diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 1ed1181..0000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -# Use the [Choice] comment to indicate option arguments that should appear in VS Code UX -# -# [Choice] Haskell version: 9, 8 -ARG VARIANT=9 -FROM haskell:${VARIANT} - -# Use the [Option] comment to specify true/false arguments that should appear in VS Code UX -# -# [Option] Install zsh -ARG INSTALL_ZSH="true" -# [Option] Upgrade OS packages to their latest versions -ARG UPGRADE_PACKAGES="false" - -# Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies -ARG USERNAME=vscode -ARG USER_UID=1000 -ARG USER_GID=$USER_UID -COPY library-scripts/*.sh /tmp/library-scripts/ -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true"\ - && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts - -# Setup ghcup, rbenv, and ruby dependencies. -USER vscode -RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 BOOTSTRAP_HASKELL_MINIMAL=1 sh \ - && echo '[ -f "/home/vscode/.ghcup/env" ] && source "/home/vscode/.ghcup/env" # ghcup-env' >> /home/vscode/.zshrc \ - && git clone https://github.com/rbenv/rbenv.git /home/vscode/.rbenv \ - && echo '[ -f "/home/vscode/.rbenv/bin/rbenv" ] && eval "$(rbenv init - bash)" # rbenv' >> /home/vscode/.zshrc \ - && echo 'export PATH="/home/vscode/.rbenv/bin:$PATH"' >> /home/vscode/.zshrc \ - && echo 'export PATH="/home/vscode/.rbenv/bin:$PATH"' >> /home/vscode/.bashrc \ - && mkdir -p /home/vscode/.rbenv/versions \ - && mkdir -p /home/vscode/.rbenv/plugins \ - && git clone https://github.com/rbenv/ruby-build.git /home/vscode/.rbenv/plugins/ruby-build diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 042f7a5..0000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,31 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: -// https://github.com/microsoft/vscode-dev-containers/tree/v0.208.0/containers/haskell -{ - "name": "Haskell (Community)", - - // Update the 'dockerfile' property if you aren't using the standard 'Dockerfile' filename. - "build": { - "dockerfile": "Dockerfile", - "args": { - // Update 'VARIANT' to pick a Haskell version: 9, 8 - "VARIANT": "9" - } - }, - - // Set *default* container specific settings.json values on container create. - "settings": {}, - - // Add the IDs of extensions you want installed when the container is created. - "extensions": ["haskell.haskell"], - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "uname -a", - // Run this command everytime the container is being prebuilt. - "updateContentCommand": ".devcontainer/update.sh", - - // Comment out connect as root instead. To add a non-root user, see: https://aka.ms/vscode-remote/containers/non-root. - "remoteUser": "vscode" -} diff --git a/.devcontainer/library-scripts/common-debian.sh b/.devcontainer/library-scripts/common-debian.sh deleted file mode 100644 index f453a6b..0000000 --- a/.devcontainer/library-scripts/common-debian.sh +++ /dev/null @@ -1,454 +0,0 @@ -#!/usr/bin/env bash -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- -# -# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/common.md -# Maintainer: The VS Code and Codespaces Teams -# -# Syntax: ./common-debian.sh [install zsh flag] [username] [user UID] [user GID] [upgrade packages flag] [install Oh My Zsh! flag] [Add non-free packages] - -set -e - -INSTALL_ZSH=${1:-"true"} -USERNAME=${2:-"automatic"} -USER_UID=${3:-"automatic"} -USER_GID=${4:-"automatic"} -UPGRADE_PACKAGES=${5:-"true"} -INSTALL_OH_MYS=${6:-"true"} -ADD_NON_FREE_PACKAGES=${7:-"false"} -SCRIPT_DIR="$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)" -MARKER_FILE="/usr/local/etc/vscode-dev-containers/common" - -if [ "$(id -u)" -ne 0 ]; then - echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' - exit 1 -fi - -# Ensure that login shells get the correct path if the user updated the PATH using ENV. -rm -f /etc/profile.d/00-restore-env.sh -echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh -chmod +x /etc/profile.d/00-restore-env.sh - -# If in automatic mode, determine if a user already exists, if not use vscode -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in ${POSSIBLE_USERS[@]}; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=vscode - fi -elif [ "${USERNAME}" = "none" ]; then - USERNAME=root - USER_UID=0 - USER_GID=0 -fi - -# Load markers to see which steps have already run -if [ -f "${MARKER_FILE}" ]; then - echo "Marker file found:" - cat "${MARKER_FILE}" - source "${MARKER_FILE}" -fi - -# Ensure apt is in non-interactive to avoid prompts -export DEBIAN_FRONTEND=noninteractive - -# Function to call apt-get if needed -apt_get_update_if_needed() -{ - if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then - echo "Running apt-get update..." - apt-get update - else - echo "Skipping apt-get update." - fi -} - -# Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies -if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then - - package_list="apt-utils \ - openssh-client \ - gnupg2 \ - dirmngr \ - iproute2 \ - procps \ - lsof \ - htop \ - net-tools \ - psmisc \ - curl \ - wget \ - rsync \ - ca-certificates \ - unzip \ - zip \ - nano \ - vim-tiny \ - less \ - jq \ - lsb-release \ - apt-transport-https \ - dialog \ - libc6 \ - libgcc1 \ - libkrb5-3 \ - libgssapi-krb5-2 \ - libicu[0-9][0-9] \ - liblttng-ust0 \ - libstdc++6 \ - zlib1g \ - locales \ - sudo \ - ncdu \ - man-db \ - strace \ - manpages \ - manpages-dev \ - init-system-helpers" - - # Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian - if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then - # Bring in variables from /etc/os-release like VERSION_CODENAME - . /etc/os-release - sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list - sed -i -E "s/deb-src http:\/\/(deb|httredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list - sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list - sed -i -E "s/deb-src http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list - sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list - sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list - sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list - sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list - # Handle bullseye location for security https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html - sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list - sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list - echo "Running apt-get update..." - apt-get update - package_list="${package_list} manpages-posix manpages-posix-dev" - else - apt_get_update_if_needed - fi - - # Install libssl1.1 if available - if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then - package_list="${package_list} libssl1.1" - fi - - # Install appropriate version of libssl1.0.x if available - libssl_package=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '') - if [ "$(echo "$LIlibssl_packageBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then - if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then - # Debian 9 - package_list="${package_list} libssl1.0.2" - elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then - # Ubuntu 18.04, 16.04, earlier - package_list="${package_list} libssl1.0.0" - fi - fi - - echo "Packages to verify are installed: ${package_list}" - apt-get -y install --no-install-recommends ${package_list} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 ) - - # Install git if not already installed (may be more recent than distro version) - if ! type git > /dev/null 2>&1; then - apt-get -y install --no-install-recommends git - fi - - PACKAGES_ALREADY_INSTALLED="true" -fi - -# Get to latest versions of all packages -if [ "${UPGRADE_PACKAGES}" = "true" ]; then - apt_get_update_if_needed - apt-get -y upgrade --no-install-recommends - apt-get autoremove -y -fi - -# Ensure at least the en_US.UTF-8 UTF-8 locale is available. -# Common need for both applications and things like the agnoster ZSH theme. -if [ "${LOCALE_ALREADY_SET}" != "true" ] && ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then - echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen - locale-gen - LOCALE_ALREADY_SET="true" -fi - -# Create or update a non-root user to match UID/GID. -group_name="${USERNAME}" -if id -u ${USERNAME} > /dev/null 2>&1; then - # User exists, update if needed - if [ "${USER_GID}" != "automatic" ] && [ "$USER_GID" != "$(id -g $USERNAME)" ]; then - group_name="$(id -gn $USERNAME)" - groupmod --gid $USER_GID ${group_name} - usermod --gid $USER_GID $USERNAME - fi - if [ "${USER_UID}" != "automatic" ] && [ "$USER_UID" != "$(id -u $USERNAME)" ]; then - usermod --uid $USER_UID $USERNAME - fi -else - # Create user - if [ "${USER_GID}" = "automatic" ]; then - groupadd $USERNAME - else - groupadd --gid $USER_GID $USERNAME - fi - if [ "${USER_UID}" = "automatic" ]; then - useradd -s /bin/bash --gid $USERNAME -m $USERNAME - else - useradd -s /bin/bash --uid $USER_UID --gid $USERNAME -m $USERNAME - fi -fi - -# Add add sudo support for non-root user -if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then - echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME - chmod 0440 /etc/sudoers.d/$USERNAME - EXISTING_NON_ROOT_USER="${USERNAME}" -fi - -# ** Shell customization section ** -if [ "${USERNAME}" = "root" ]; then - user_rc_path="/root" -else - user_rc_path="/home/${USERNAME}" -fi - -# Restore user .bashrc defaults from skeleton file if it doesn't exist or is empty -if [ ! -f "${user_rc_path}/.bashrc" ] || [ ! -s "${user_rc_path}/.bashrc" ] ; then - cp /etc/skel/.bashrc "${user_rc_path}/.bashrc" -fi - -# Restore user .profile defaults from skeleton file if it doesn't exist or is empty -if [ ! -f "${user_rc_path}/.profile" ] || [ ! -s "${user_rc_path}/.profile" ] ; then - cp /etc/skel/.profile "${user_rc_path}/.profile" -fi - -# .bashrc/.zshrc snippet -rc_snippet="$(cat << 'EOF' - -if [ -z "${USER}" ]; then export USER=$(whoami); fi -if [[ "${PATH}" != *"$HOME/.local/bin"* ]]; then export PATH="${PATH}:$HOME/.local/bin"; fi - -# Display optional first run image specific notice if configured and terminal is interactive -if [ -t 1 ] && [[ "${TERM_PROGRAM}" = "vscode" || "${TERM_PROGRAM}" = "codespaces" ]] && [ ! -f "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed" ]; then - if [ -f "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" ]; then - cat "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" - elif [ -f "/workspaces/.codespaces/shared/first-run-notice.txt" ]; then - cat "/workspaces/.codespaces/shared/first-run-notice.txt" - fi - mkdir -p "$HOME/.config/vscode-dev-containers" - # Mark first run notice as displayed after 10s to avoid problems with fast terminal refreshes hiding it - ((sleep 10s; touch "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed") &) -fi - -# Set the default git editor if not already set -if [ -z "$(git config --get core.editor)" ] && [ -z "${GIT_EDITOR}" ]; then - if [ "${TERM_PROGRAM}" = "vscode" ]; then - if [[ -n $(command -v code-insiders) && -z $(command -v code) ]]; then - export GIT_EDITOR="code-insiders --wait" - else - export GIT_EDITOR="code --wait" - fi - fi -fi - -EOF -)" - -# code shim, it fallbacks to code-insiders if code is not available -cat << 'EOF' > /usr/local/bin/code -#!/bin/sh - -get_in_path_except_current() { - which -a "$1" | grep -A1 "$0" | grep -v "$0" -} - -code="$(get_in_path_except_current code)" - -if [ -n "$code" ]; then - exec "$code" "$@" -elif [ "$(command -v code-insiders)" ]; then - exec code-insiders "$@" -else - echo "code or code-insiders is not installed" >&2 - exit 127 -fi -EOF -chmod +x /usr/local/bin/code - -# systemctl shim - tells people to use 'service' if systemd is not running -cat << 'EOF' > /usr/local/bin/systemctl -#!/bin/sh -set -e -if [ -d "/run/systemd/system" ]; then - exec /bin/systemctl/systemctl "$@" -else - echo '\n"systemd" is not running in this container due to its overhead.\nUse the "service" command to start services intead. e.g.: \n\nservice --status-all' -fi -EOF -chmod +x /usr/local/bin/systemctl - -# Codespaces bash and OMZ themes - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme -codespaces_bash="$(cat \ -<<'EOF' - -# Codespaces bash prompt theme -__bash_prompt() { - local userpart='`export XIT=$? \ - && [ ! -z "${GITHUB_USER}" ] && echo -n "\[\033[0;32m\]@${GITHUB_USER} " || echo -n "\[\033[0;32m\]\u " \ - && [ "$XIT" -ne "0" ] && echo -n "\[\033[1;31m\]➜" || echo -n "\[\033[0m\]➜"`' - local gitbranch='`\ - if [ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ]; then \ - export BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null); \ - if [ "${BRANCH}" != "" ]; then \ - echo -n "\[\033[0;36m\](\[\033[1;31m\]${BRANCH}" \ - && if git ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \ - echo -n " \[\033[1;33m\]✗"; \ - fi \ - && echo -n "\[\033[0;36m\]) "; \ - fi; \ - fi`' - local lightblue='\[\033[1;34m\]' - local removecolor='\[\033[0m\]' - PS1="${userpart} ${lightblue}\w ${gitbranch}${removecolor}\$ " - unset -f __bash_prompt -} -__bash_prompt - -EOF -)" - -codespaces_zsh="$(cat \ -<<'EOF' -# Codespaces zsh prompt theme -__zsh_prompt() { - local prompt_username - if [ ! -z "${GITHUB_USER}" ]; then - prompt_username="@${GITHUB_USER}" - else - prompt_username="%n" - fi - PROMPT="%{$fg[green]%}${prompt_username} %(?:%{$reset_color%}➜ :%{$fg_bold[red]%}➜ )" # User/exit code arrow - PROMPT+='%{$fg_bold[blue]%}%(5~|%-1~/…/%3~|%4~)%{$reset_color%} ' # cwd - PROMPT+='$([ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ] && git_prompt_info)' # Git status - PROMPT+='%{$fg[white]%}$ %{$reset_color%}' - unset -f __zsh_prompt -} -ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[cyan]%}(%{$fg_bold[red]%}" -ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} " -ZSH_THEME_GIT_PROMPT_DIRTY=" %{$fg_bold[yellow]%}✗%{$fg_bold[cyan]%})" -ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg_bold[cyan]%})" -__zsh_prompt - -EOF -)" - -# Add RC snippet and custom bash prompt -if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then - echo "${rc_snippet}" >> /etc/bash.bashrc - echo "${codespaces_bash}" >> "${user_rc_path}/.bashrc" - echo 'export PROMPT_DIRTRIM=4' >> "${user_rc_path}/.bashrc" - if [ "${USERNAME}" != "root" ]; then - echo "${codespaces_bash}" >> "/root/.bashrc" - echo 'export PROMPT_DIRTRIM=4' >> "/root/.bashrc" - fi - chown ${USERNAME}:${group_name} "${user_rc_path}/.bashrc" - RC_SNIPPET_ALREADY_ADDED="true" -fi - -# Optionally install and configure zsh and Oh My Zsh! -if [ "${INSTALL_ZSH}" = "true" ]; then - if ! type zsh > /dev/null 2>&1; then - apt_get_update_if_needed - apt-get install -y zsh - fi - if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then - echo "${rc_snippet}" >> /etc/zsh/zshrc - ZSH_ALREADY_INSTALLED="true" - fi - - # Adapted, simplified inline Oh My Zsh! install steps that adds, defaults to a codespaces theme. - # See https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh for official script. - oh_my_install_dir="${user_rc_path}/.oh-my-zsh" - if [ ! -d "${oh_my_install_dir}" ] && [ "${INSTALL_OH_MYS}" = "true" ]; then - template_path="${oh_my_install_dir}/templates/zshrc.zsh-template" - user_rc_file="${user_rc_path}/.zshrc" - umask g-w,o-w - mkdir -p ${oh_my_install_dir} - git clone --depth=1 \ - -c core.eol=lf \ - -c core.autocrlf=false \ - -c fsck.zeroPaddedFilemode=ignore \ - -c fetch.fsck.zeroPaddedFilemode=ignore \ - -c receive.fsck.zeroPaddedFilemode=ignore \ - "https://github.com/ohmyzsh/ohmyzsh" "${oh_my_install_dir}" 2>&1 - echo -e "$(cat "${template_path}")\nDISABLE_AUTO_UPDATE=true\nDISABLE_UPDATE_PROMPT=true" > ${user_rc_file} - sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="codespaces"/g' ${user_rc_file} - - mkdir -p ${oh_my_install_dir}/custom/themes - echo "${codespaces_zsh}" > "${oh_my_install_dir}/custom/themes/codespaces.zsh-theme" - # Shrink git while still enabling updates - cd "${oh_my_install_dir}" - git repack -a -d -f --depth=1 --window=1 - # Copy to non-root user if one is specified - if [ "${USERNAME}" != "root" ]; then - cp -rf "${user_rc_file}" "${oh_my_install_dir}" /root - chown -R ${USERNAME}:${group_name} "${user_rc_path}" - fi - fi -fi - -# Persist image metadata info, script if meta.env found in same directory -meta_info_script="$(cat << 'EOF' -#!/bin/sh -. /usr/local/etc/vscode-dev-containers/meta.env - -# Minimal output -if [ "$1" = "version" ] || [ "$1" = "image-version" ]; then - echo "${VERSION}" - exit 0 -elif [ "$1" = "release" ]; then - echo "${GIT_REPOSITORY_RELEASE}" - exit 0 -elif [ "$1" = "content" ] || [ "$1" = "content-url" ] || [ "$1" = "contents" ] || [ "$1" = "contents-url" ]; then - echo "${CONTENTS_URL}" - exit 0 -fi - -#Full output -echo -echo "Development container image information" -echo -if [ ! -z "${VERSION}" ]; then echo "- Image version: ${VERSION}"; fi -if [ ! -z "${DEFINITION_ID}" ]; then echo "- Definition ID: ${DEFINITION_ID}"; fi -if [ ! -z "${VARIANT}" ]; then echo "- Variant: ${VARIANT}"; fi -if [ ! -z "${GIT_REPOSITORY}" ]; then echo "- Source code repository: ${GIT_REPOSITORY}"; fi -if [ ! -z "${GIT_REPOSITORY_RELEASE}" ]; then echo "- Source code release/branch: ${GIT_REPOSITORY_RELEASE}"; fi -if [ ! -z "${BUILD_TIMESTAMP}" ]; then echo "- Timestamp: ${BUILD_TIMESTAMP}"; fi -if [ ! -z "${CONTENTS_URL}" ]; then echo && echo "More info: ${CONTENTS_URL}"; fi -echo -EOF -)" -if [ -f "${SCRIPT_DIR}/meta.env" ]; then - mkdir -p /usr/local/etc/vscode-dev-containers/ - cp -f "${SCRIPT_DIR}/meta.env" /usr/local/etc/vscode-dev-containers/meta.env - echo "${meta_info_script}" > /usr/local/bin/devcontainer-info - chmod +x /usr/local/bin/devcontainer-info -fi - -# Write marker file -mkdir -p "$(dirname "${MARKER_FILE}")" -echo -e "\ - PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\ - LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\ - EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\ - RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\ - ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}" - -echo "Done!" diff --git a/.devcontainer/update.sh b/.devcontainer/update.sh deleted file mode 100755 index 5a3eb28..0000000 --- a/.devcontainer/update.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -whoami -pwd - -rbenv install 2.6.5 -rbenv global 2.6.5 - -gem install compass - -ghcup install ghc 9.2.5 -ghcup set ghc 9.2.5 - -stack upgrade -stack setup diff --git a/.ghci b/.ghci deleted file mode 100644 index edfda70..0000000 --- a/.ghci +++ /dev/null @@ -1,5 +0,0 @@ -:set -XOverloadedStrings -:set -fwarn-unused-binds -fwarn-unused-imports -:set -isrc -:load Main -import Hakyll diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml deleted file mode 100644 index 432eff5..0000000 --- a/.github/workflows/cd.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: "CD: Deploy site" - -# Check out https://help.github.com/en/articles/workflow-syntax-for-github-actions for documentation on Actions. -on: - workflow_dispatch: - release: - types: [released] - -concurrency: production - -defaults: - run: - shell: bash # Set the default shell to bash. - -jobs: - cd: - runs-on: ubuntu-latest - environment: - name: production - url: https://codetalk.io - steps: - - uses: actions/checkout@v3 - - - name: Download latest release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release download --pattern "_site.tar.gz" --dir ./ - tar -zxvf _site.tar.gz - - - name: Upload site to S3 - env: - AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - run: | - aws s3 sync _site s3://codetalk.io --delete - - - name: Purge CloudFront cache - env: - CLOUDFLARE_ZONE: ${{ secrets.CLOUDFLARE_ZONE }} - CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_TOKEN }} - run: | - curl -X DELETE "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE}/purge_cache" \ - -H "Authorization: Bearer ${CLOUDFLARE_TOKEN}" \ - -H "Content-Type: application/json" \ - --data '{"purge_everything":true}' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index c731cd2..0000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,74 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ "master" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "master" ] - schedule: - - cron: '44 12 * * 6' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'javascript' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" diff --git a/.github/workflows/cron-cache-deps.yml b/.github/workflows/cron-cache-deps.yml deleted file mode 100644 index 661b096..0000000 --- a/.github/workflows/cron-cache-deps.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: "CRON: Cache Dependencies" - -# Check out https://help.github.com/en/articles/workflow-syntax-for-github-actions for documentation on Actions. -on: - schedule: - # Run every Monday at 01:00 in the night. - - cron: "0 1 * * 1" - -defaults: - run: - shell: bash # Set the default shell to bash. - -jobs: - ci: - runs-on: ubuntu-20.04 - environment: - name: ci - env: - GHC_VERSION: "9.2.5" - CABAL_VERSION: "3.6.2.0" - STACK_VERSION: "2.9.3" - steps: - - uses: actions/checkout@v1 - - # Setup tooling - - uses: haskell/actions/setup@v1 - name: Setup Haskell Stack - with: - ghc-version: ${{env.GHC_VERSION}} - cabal-version: ${{env.CABAL_VERSION}} - stack-version: ${{env.STACK_VERSION}} - enable-stack: true - - # Enable cache for cabal and stack - - uses: actions/cache@v2 - name: Cache ~/.cabal/packages, ~/.cabal/store and dist-newstyle - with: - path: | - ~/.cabal/packages - ~/.cabal/store - dist-newstyle - key: ${{ runner.os }}-${{env.GHC_VERSION}}-cabal-store-${{ hashFiles('*.cabal') }} - - uses: actions/cache@v1 - name: Cache ~/.stack - with: - path: ~/.stack - key: ${{ runner.os }}-${{env.GHC_VERSION}}-stack-${{ hashFiles('stack.yaml.lock') }} - - - name: Install dependencies - run: | - stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --only-dependencies diff --git a/.gitignore b/.gitignore index 1feb004..8e074fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +_dist dist cabal-dev *.o diff --git a/.htmlhintrc b/.htmlhintrc deleted file mode 100644 index f4621ba..0000000 --- a/.htmlhintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "doctype-first": false -} diff --git a/.pages.yml b/.pages.yml index 7b0fff1..cb4dba5 100644 --- a/.pages.yml +++ b/.pages.yml @@ -1,11 +1,11 @@ media: - input: resources/images - output: /resources/images + input: static/content-images + output: /static/content-images content: - name: posts label: Posts - path: posts + path: content/posts type: collection filename: '{year}-{month}-{day}-{primary}.md' view: @@ -42,7 +42,7 @@ content: required: true - name: drafts label: Drafts - path: drafts + path: content/drafts type: collection filename: '{year}-{month}-{day}-{primary}.md' view: @@ -76,4 +76,4 @@ content: type: rich-text options: format: markdown - required: true \ No newline at end of file + required: true diff --git a/.sass-lint.yml b/.sass-lint.yml deleted file mode 100644 index 47ebbac..0000000 --- a/.sass-lint.yml +++ /dev/null @@ -1,95 +0,0 @@ -options: - formatter: stylish -files: - include: '**/*.s+(a|c)ss' -rules: - # Extends - extends-before-mixins: 1 - extends-before-declarations: 1 - placeholder-in-extend: 1 - - # Mixins - mixins-before-declarations: 1 - - # Line Spacing - one-declaration-per-line: 1 - empty-line-between-blocks: 1 - single-line-per-selector: 1 - - # Disallows - no-attribute-selectors: 0 - no-color-hex: 0 - no-color-keywords: 1 - no-color-literals: 1 - no-combinators: 0 - no-css-comments: 1 - no-debug: 1 - no-disallowed-properties: 0 - no-duplicate-properties: 1 - no-empty-rulesets: 1 - no-extends: 0 - no-ids: 0 - no-important: 1 - no-invalid-hex: 1 - no-mergeable-selectors: 1 - no-misspelled-properties: 1 - no-qualifying-elements: 0 - no-trailing-whitespace: 1 - no-trailing-zero: 1 - no-transition-all: 1 - no-universal-selectors: 0 - no-url-domains: 1 - no-url-protocols: 1 - no-vendor-prefixes: 0 - no-warn: 1 - property-units: 0 - - # Nesting - declarations-before-nesting: 1 - force-attribute-nesting: 1 - force-element-nesting: 1 - force-pseudo-nesting: 1 - - # Name Formats - class-name-format: 0 - function-name-format: 1 - id-name-format: 0 - mixin-name-format: 1 - placeholder-name-format: 1 - variable-name-format: 1 - - # Style Guide - attribute-quotes: 1 - bem-depth: 0 - border-zero: 1 - brace-style: 1 - clean-import-paths: 1 - empty-args: 1 - hex-length: 1 - hex-notation: 1 - indentation: 1 - leading-zero: 0 - max-line-length: 0 - max-file-line-count: 0 - nesting-depth: 1 - property-sort-order: 0 - pseudo-element: 1 - quotes: 1 - shorthand-values: 1 - url-quotes: 1 - variable-for-property: 1 - zero-unit: 1 - - # Inner Spacing - space-after-comma: 1 - space-before-colon: 1 - space-after-colon: 1 - space-before-brace: 1 - space-before-bang: 1 - space-after-bang: 1 - space-between-parens: 1 - space-around-operator: 1 - - # Final Items - trailing-semicolon: 1 - final-newline: 1 diff --git a/.stylish-haskell.yaml b/.stylish-haskell.yaml deleted file mode 100644 index fad1657..0000000 --- a/.stylish-haskell.yaml +++ /dev/null @@ -1,25 +0,0 @@ -steps: - - imports: - align: none - list_align: with_module_name - pad_module_names: false - long_list_align: new_line_multiline - empty_list_align: inherit - list_padding: 7 # length "import " - separate_lists: false - space_surround: false - - language_pragmas: - style: vertical - align: false - remove_redundant: true - - simple_align: - cases: false - top_level_patterns: false - records: false - - trailing_whitespace: {} - -# You need to put any language extensions that's enabled for the entire project here. -language_extensions: [] - -# This is up to personal preference, but 80 is the right answer. -columns: 80 diff --git a/LICENSE b/LICENSE index 7b95fc1..520ada1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright Christian Kjær Laustsen (c) 2016 +Copyright Christian Kjær Laustsen (c) 2024 All rights reserved. diff --git a/README.md b/README.md index dfad159..41cdecf 100644 --- a/README.md +++ b/README.md @@ -1,61 +1 @@ -# [codetalk.io](https://codetalk.io) site [![CI: Build](https://github.com/codetalkio/codetalk.io/actions/workflows/ci.yml/badge.svg)](https://github.com/codetalkio/codetalk.io/actions/workflows/ci.yml) [![CD: Deploy](https://github.com/codetalkio/codetalk.io/actions/workflows/cd.yml/badge.svg)](https://github.com/codetalkio/codetalk.io/actions/workflows/cd.yml) [![Codespaces: Prebuild](https://github.com/codetalkio/codetalk.io/actions/workflows/codespaces/create_codespaces_prebuilds/badge.svg)](https://github.com/codetalkio/codetalk.io/actions/workflows/codespaces/create_codespaces_prebuilds) - -## Dependencies - -You'll need sass/compass to compile the (S)CSS files, else `hakyll` will complain in the build phase. - -```bash -$ npm i -g sass -``` - -To avoid needing to rebuild the entire Haskell application, you can use the ready-made binary (Linux only), - -```bash -$ gh release download --pattern "hakyll-site" --dir ./dist -$ chmod +x ./dist/hakyll-site -$ ./dist/hakyll-site watch -``` - -## Quick walkthrough of Hakyll - -If you change something in `site.hs`, then it needs to be recompiled, using GHC. Everything else, should just need a rebuild via Hakyll. - -```bash -$ stack build -``` - -After this, rebuilding the site is as simple as, - -```bash -$ stack exec -- hakyll-site rebuild -``` - -or, alternatively use `watch` to launch a preview server while developing, - -```bash -$ stack exec -- hakyll-site watch -$ sass resources/scss/app.scss:_site/app.css --style compressed --watch -``` - -## Image Optimization - -```bash -$ brew install imagemagick -``` - -Either put the images in `resources/images/unoptimized`, or copy them with the following script, - -```bash -$ cd resources/images -$ mkdir -p unoptimized -$ for f in *.png; do cp "$f" "unoptimized/${f%.png}.thumbnail.png"; done -``` - -Now that the images you want to generate optimized versions for are in `resources/images/unoptimized`, run the optimization script inside this folder, - -```bash -$ cd unoptimized -$ mogrify -resize 50% *.thumbnail.png -``` - -Finally, copy them out to images again. +# codethoughts diff --git a/Setup.hs b/Setup.hs deleted file mode 100644 index ebdc00e..0000000 --- a/Setup.hs +++ /dev/null @@ -1,3 +0,0 @@ -import Distribution.Simple - -main = defaultMain diff --git a/codetalk-io.cabal b/codetalk-io.cabal deleted file mode 100644 index aa8a8c4..0000000 --- a/codetalk-io.cabal +++ /dev/null @@ -1,34 +0,0 @@ -name: codetalk-io -version: 1.3.0 -synopsis: The codetalk.io blog, generated using Hakyll -description: Please see README.md -homepage: https://github.com/codetalkio/codetalk.io#readme -license: BSD3 -license-file: LICENSE -author: Christian Kjær Laustsen -maintainer: ckl@codetalk.io -copyright: codetalk -category: Web -build-type: Simple -cabal-version: >=1.10 - -executable hakyll-site - main-is: Main.hs - hs-source-dirs: src - default-language: Haskell2010 - ghc-options: -Wall -threaded - other-modules: Site.Compiler - , Site.Context - , Site.Feed - build-depends: base - , hakyll - , pandoc - , text - , bytestring - , aeson - , hxt - , HandsomeSoup - -source-repository head - type: git - location: https://github.com/codetalkio/codetalk.io diff --git a/codetalk.code-workspace b/codetalk.code-workspace index a3d32be..5c5d501 100644 --- a/codetalk.code-workspace +++ b/codetalk.code-workspace @@ -1,24 +1,12 @@ { "folders": [ - { - "name": "drafts", - "path": "drafts" - }, { "name": "posts", - "path": "posts" - }, - { - "name": "importable", - "path": "importable" - }, - { - "name": "images", - "path": "resources/images" + "path": "content/posts" }, { - "name": "thumbnails", - "path": "resources/images/unoptimized" + "name": "drafts", + "path": "content/drafts" }, { "name": "project", @@ -28,30 +16,36 @@ "settings": { "editor.formatOnSave": true, "[yaml]": { - "editor.formatOnSave": false + "editor.formatOnSave": true }, "[markdown]": { + "editor.formatOnSave": true + }, + "[html]": { "editor.formatOnSave": false }, - "workspaceKeybindings.codetalk.optimize-images.enabled": true + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + "**/.ruby-lsp": true, + "_dist": true, + "legacy": true + }, + "search.exclude": { + "**/node_modules": true, + "**/bower_components": true, + "**/*.code-search": true, + "**/.ruby-lsp": true, + "static/styles/output.css": true, + "static/js/**": true + } }, "tasks": { "version": "2.0.0", - "tasks": [ - { - "label": "Optimize Images", - "type": "shell", - // First install brew install rename. Then drop a file in resources/images/unoptimized and run this task. - // - Copy all PNG/JPG files to ./thumbnails - // - Move all PNG/JPG files to the images folder at ../ - // - Rename all PNG/JPG files in ./thumbnails to have a .thumbnail.png/.thumbnail.jpg extension - // - Resize all PNG/JPG files in ./thumbnails to 70% of their original size - // - Move all PNG/JPG files in ./thumbnails to the images folder at ../../ - "command": "(cp *.png ./thumbnails || true) && (cp *.jpg ./thumbnails || true) && (mv *.png ../ || true) && (mv *.jpg ../ || true) && cd ./thumbnails && ls && (rename 's/.png/.thumbnail.png/' *.png || true) && (rename 's/.jpg/.thumbnail.jpg/' *.jpg || true) && (mogrify -resize 70% *.thumbnail.png || true) && (mogrify -resize 70% *.thumbnail.jpg || true) && (mv *.png ../../ || true) && (mv *.jpg ../../ || true)", - "options": { - "cwd": "${workspaceFolder}/../resources/images/unoptimized" - } - } - ] + "tasks": [] } } diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..13028e8 --- /dev/null +++ b/config.toml @@ -0,0 +1,79 @@ +# The URL the site will be built for +base_url = "/" + +# Whether to automatically compile all Sass files in the sass directory +compile_sass = false # We use TailwindCSS instead. + +# When set to "true", the generated HTML files are minified. +minify_html = true + +# For overriding the default output directory `public`, set it to another value (e.g.: "docs") +output_dir = "_dist" + +# Whether to build a search index to be used later on by a JavaScript library +build_search_index = true + +# When set to "true", a feed is automatically generated. +generate_feed = true + +# The filename to use for the feed. Used as the template filename, too. +# Defaults to "atom.xml", which has a built-in template that renders an Atom 1.0 feed. +# There is also a built-in template "rss.xml" that renders an RSS 2.0 feed. +feed_filename = "atom.xml" + +# The default author for pages +author = "Christian Kjær" + +# The taxonomies to be rendered for the site and their configuration of the default languages +# Example: +# taxonomies = [ +# {name = "tags", feed = true}, # each tag will have its own feed +# {name = "tags"}, # you can have taxonomies with the same name in multiple languages +# {name = "categories", paginate_by = 5}, # 5 items per page for a term +# {name = "authors"}, # Basic definition: no feed or pagination +# ] +# +taxonomies = [ + { name = "tags", feed = true }, # each tag will have its own feed +] + +[markdown] +# Whether to do syntax highlighting +# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola +highlight_code = true + +# Use CSS for styling instead of inline styles. +highlight_theme = "css" + +# Set the themes for both dark and light mode. +highlight_themes_css = [ + { theme = "OneHalfDark", filename = "syntax-theme-dark.css" }, # Or visual-studio-dark + { theme = "OneHalfLight", filename = "syntax-theme-light.css" }, +] + +# Whether external links are to be opened in a new tab +# If this is true, a `rel="noopener"` will always automatically be added for security reasons +external_links_target_blank = true +# Whether to set rel="nofollow" for all external links +external_links_no_follow = true +# Whether to set rel="noreferrer" for all external links +external_links_no_referrer = true + +# Whether to set decoding="async" and loading="lazy" for all images +# When turned on, the alt text must be plain text. +# For example, `![xx](...)` is ok but `![*x*x](...)` isn’t ok +lazy_async_image = true + +# Whether footnotes are rendered in the GitHub-style (at the bottom, with back references) or plain (in the place, where they are defined) +bottom_footnotes = true + +# Various slugification strategies, see below for details +# Defaults to everything being a slug +[slugify] +# Whether to remove date prefixes for page path slugs. +# For example, content/posts/2016-10-08_a-post-with-dates.md => posts/a-post-with-dates +# When true, content/posts/2016-10-08_a-post-with-dates.md => posts/2016-10-08-a-post-with-dates +paths_keep_dates = true + +[extra] +# Put all your custom variables here diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..73e90f1 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,4 @@ ++++ +sort_by = "date" +paginate_by = 7 ++++ diff --git a/drafts/2022-01-01-api-migration-and-modernization.md b/content/drafts/2022-01-01-api-migration-and-modernization.md similarity index 100% rename from drafts/2022-01-01-api-migration-and-modernization.md rename to content/drafts/2022-01-01-api-migration-and-modernization.md diff --git a/drafts/2022-01-01-your-aws-bill-tells-a-story.md b/content/drafts/2022-01-01-your-aws-bill-tells-a-story.md similarity index 100% rename from drafts/2022-01-01-your-aws-bill-tells-a-story.md rename to content/drafts/2022-01-01-your-aws-bill-tells-a-story.md diff --git a/drafts/2023-01-01-the-stack.md b/content/drafts/2023-01-01-the-stack.md similarity index 100% rename from drafts/2023-01-01-the-stack.md rename to content/drafts/2023-01-01-the-stack.md diff --git a/drafts/2023-10-17-the-stack-part-4.md b/content/drafts/2023-10-17-the-stack-part-4.md similarity index 95% rename from drafts/2023-10-17-the-stack-part-4.md rename to content/drafts/2023-10-17-the-stack-part-4.md index a965d91..75a7dd3 100644 --- a/drafts/2023-10-17-the-stack-part-4.md +++ b/content/drafts/2023-10-17-the-stack-part-4.md @@ -15,21 +15,9 @@ At the end of this post we will have: There is quite a lot to cover. My recommendation is to clone down the Part 4 branch in the [GitHub repository](https://github.com/codetalkio/the-stack/tree/part-4-api) and use this post as an explanation of what is set up. -
- -- [Prelude: Federated GraphQL](#prelude-federated-graphql) -- [Our GraphQL Schema](#our-graphql-schema) -- [Subgraph: Products, Users, and Reviews](#subgraph-products-users-and-reviews) - - [Local Development](#local-development) - - [Rust GraphQL Server](#rust-graphql-server) - - [Performance \& Pricing](#performance-pricing) -- [Apollo Router](#apollo-router) - - [Performance \& Pricing: App Runner v.s. Lambda](#performance-pricing-app-runner-vs-lambda) -- [Automating Deployments via CDK](#automating-deployments-via-cdk) - - [Building artifacts in CI](#building-artifacts-in-ci) - - [Deploying to App Runner](#deploying-to-app-runner) - - [Deploying to AWS Lambda](#deploying-to-aws-lambda) -- [Next Steps](#next-steps) +
+ +{{ toc() }} ## Prelude: Federated GraphQL diff --git a/content/drafts/_index.md b/content/drafts/_index.md new file mode 100644 index 0000000..35b3e6c --- /dev/null +++ b/content/drafts/_index.md @@ -0,0 +1,8 @@ ++++ +title = "List of draft posts" +sort_by = "date" +template = "posts.html" +page_template = "drafts-post-page.html" +in_search_index = false +insert_anchor_links = "heading" ++++ diff --git a/content/drafts/component-overview/example.ts b/content/drafts/component-overview/example.ts new file mode 100644 index 0000000..7ee4459 --- /dev/null +++ b/content/drafts/component-overview/example.ts @@ -0,0 +1,13 @@ +// @ts-check + +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: "export", + trailingSlash: true, + experimental: { + // Statically type links to prevent typos and other errors when using next/link, improving type safety when navigating between pages. + typedRoutes: true, + }, +}; + +module.exports = nextConfig; diff --git a/content/drafts/component-overview/index.md b/content/drafts/component-overview/index.md new file mode 100644 index 0000000..a865909 --- /dev/null +++ b/content/drafts/component-overview/index.md @@ -0,0 +1,238 @@ ++++ +title = "Component overview" +date = 2023-11-29 ++++ + +Generated Table of Contents by inserting a `toc()` shortcode wrapped in `{{ }}`: + +{{ toc() }} + +Summary cutoff point by placing `
` at the desired location. + +
+ +## Lists + +- First level item no. 1 +- First level item no. 2 + - Second level item no. 1 + - Second level item no. 2 + - Third level item no. 1 + - Third level item no. 2 + - Second level item no. 3 +- First level item no. 3 + + +## Footnotes + +Here's a simple footnote,[^1] and here's a longer one.[^bignote] + +[^1]: This is the first footnote. + +[^bignote]: Multiline footnotes are supported ([#1282](https://github.com/getzola/zola/issues/1282#issuecomment-753445777)), and will end up wrapping everything in a code block outside the footnote `
`. + + Indent paragraphs to include them in the footnote. + + `{ my code }` + + Add as many paragraphs as you like. + +## Code blocks + +Code blocks support the following annotations: + +- `linenos`: Display line numbers column +- `hl_lines`: Highlight specific lines, e.g. `hl_lines=1 3-5 9` +- `linenostart`: Start line number, e.g. `linenostart=20` +- `hide_lines`: Hide specific lines, e.g. `hide_lines=1-2` + +It's placed after the language, which will look like this: + +```` +```rust,linenos,hl_lines=1 3-5 9 +println!("Hello, world!"); +``` +```` + +### Examples + +Plain, without line numbers: + +```typescript +// @ts-check + +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: "export", + trailingSlash: true, + experimental: { + // Statically type links to prevent typos and other errors when using next/link, improving type safety when navigating between pages. + typedRoutes: true, + }, +}; + +module.exports = nextConfig; +``` + +With line numbers: + +```typescript,linenos +// @ts-check + +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: "export", + trailingSlash: true, + experimental: { + // Statically type links to prevent typos and other errors when using next/link, improving type safety when navigating between pages. + typedRoutes: true, + }, +}; + +module.exports = nextConfig; +``` + +With line numbers and highlighting: + +```typescript,linenos,hl_lines=1 3-5 9 +// @ts-check + +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: "export", + trailingSlash: true, + experimental: { + // Statically type links to prevent typos and other errors when using next/link, improving type safety when navigating between pages. + typedRoutes: true, + }, +}; + +module.exports = nextConfig; +``` + +Without line numbers but with highlighting: + +```typescript,hl_lines=1 3-5 9 13 +// @ts-check + +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: "export", + trailingSlash: true, + experimental: { + // Statically type links to prevent typos and other errors when using next/link, improving type safety when navigating between pages. + typedRoutes: true, + }, +}; + +module.exports = nextConfig; +``` + +## Code from files + +Loading code from a colocated file, and hiding specific lines: + +```typescript,hide_lines=1-5 10-15 +{{ load_code(path="example.ts") }} +``` + +## Inline code + +We're using [leptos_i18n](https://github.com/Baptistemontan/leptos_i18n) for localization in Leptos, which supports an API that's very close to the one we used in Next.js. We already pulled in the dependency when we updated our `Cargo.toml` file earlier, so let's get the rest of it set up. + +We'll create a `ui-internal/messages/` folder where our locales will live.... + +## Images + +Pulling in a colocated image: + +{{ image(path="logo.png", alt="codethoughts logo", width=200) }} + +Pulling in a colocated image with caption: + +{{ image(path="logo.png", caption="codethoughts", width=200) }} + +Showing multiple images: + +{{ images(paths=["logo.png", "logo.png", "logo.png"], captions=["codethoughts logo 1", "logo 2"], widths=[100, 200, 100], defaultAlt="The default alt", defaultWidth=200) }} + +## Tables + +A simple table with a header, defaulting to centered alignment: + +| | Header 2 | Header 3 | +|----------|----------|----------| +| Row 1 | Row 1 | Row 1 | +| Row 2 | Row 2 | Row 2 | +| Row 3 | Row 3 | Row 3 | + +A table with different text alignment: + +| Longer header 1 | Longer header 2 | Longer header 3 | +| :--- | :--: | ---: | +| Row 1 | Row 1 | Row 1 | +| Row 2 | Row 2 | Row 2 | + +A left-aligned table (uses `...` HTML tags): + + + +| Header 1 | Header 2 | Header 3 | +|----------|----------|----------| +| Row 1 | Row 1 | Row 1 | +| Row 2 | Row 2 | Row 2 | + + + +A right-aligned table (uses `...` HTML tags): + + + +| Header 1 | Header 2 | Header 3 | +|----------|----------|----------| +| Row 1 | Row 1 | Row 1 | +| Row 2 | Row 2 | Row 2 | + + + + +A simple table without header (empty headers hidden with CSS): + +|||| +|----------|----------|----------| +| Row 1 | Row 1 | Row 1 | +| Row 2 | Row 2 | Row 2 | +| Row 3 | Row 3 | Row 3 | + +This is a clever little trick of convoluted logic (lots of negation!) which checks: + +- If there's a `thead` element +- That do **not** have `th` elements +- That are **not** empty +- Then hide it + +```css +thead:not(thead:has(th:not(:empty))) { + display: none; +} +``` + +## Quotes + +A single line quote: + +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet. + +And a multi-line quote: + +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet. +> +> And it can span multiple lines... + +## Callouts + +A callout: + +{% aside() %} + This is meant to be a tip or `call` out some [additonal info](#). +{% end %} diff --git a/content/drafts/component-overview/logo.png b/content/drafts/component-overview/logo.png new file mode 100644 index 0000000..a29a6ae Binary files /dev/null and b/content/drafts/component-overview/logo.png differ diff --git a/posts/2023-10-16-the-stack-part-3.md b/content/importable-posts/2023-10-16-the-stack-part-3.md similarity index 98% rename from posts/2023-10-16-the-stack-part-3.md rename to content/importable-posts/2023-10-16-the-stack-part-3.md index 430cfd3..a392cf3 100644 --- a/posts/2023-10-16-the-stack-part-3.md +++ b/content/importable-posts/2023-10-16-the-stack-part-3.md @@ -1,8 +1,10 @@ ---- -title: "The Stack Part 3: Building a Frontend" -tags: rust, typescript, wasm, nextjs, leptos, aws, cloud, infrastructure, cdk, ci -date: 2023-10-16 ---- ++++ +title = "The Stack Part 3: Building a Frontend" +date = 2023-10-16 +in_search_index = false +[taxonomies] +tags = ["rust", "typescript", "wasm", "nextjs", "leptos", "aws", "cloud", "infrastructure", "cdk", "ci"] ++++ In [the last post](/posts/2023-10-07-the-stack-part-2.html) we set up our deployment, fully automated on merge to our `main` branch. In this post we will be building our UI (Frontend) applications. See the full overview of posts [here](/posts/2023-01-29-the-stack.html#what-will-we-be-covering). @@ -16,24 +18,9 @@ We are essentially hedging our bets by building both a JavaScript-based Frontend There is quite a lot to cover. My recommendation is to clone down the Part 3 branch in the [GitHub repository](https://github.com/codetalkio/the-stack/tree/part-3-frontend) and use this post as an explanation of what is set up. -
- -- [Prelude: Static Site Generation](#prelude-static-site-generation) -- [Next.js](#next.js) - - [Setting up our Next.js App](#setting-up-our-next.js-app) - - [Building Static Files](#building-static-files) - - [Setting up Localization](#setting-up-localization) -- [Leptos (Rust/WASM)](#leptos-rustwasm) - - [Setting up our Leptos App](#setting-up-our-leptos-app) - - [Setting up Tailwind CSS](#setting-up-tailwind-css) - - [Setting up Localization](#setting-up-localization-1) -- [Bonus: End-to-End Tests](#bonus-end-to-end-tests) -- [Bonus: DevEx Improvements](#bonus-devex-improvements) -- [Automating Deployments via CDK](#automating-deployments-via-cdk) - - [Building artifacts in CI](#building-artifacts-in-ci) - - [Deploying to S3 + CloudFront](#deploying-to-s3-cloudfront) -- [Next Steps](#next-steps) +
+{{ toc() }} ## Prelude: Static Site Generation @@ -1335,6 +1322,7 @@ This means that it will fit into our `Global` stack. Cross-stack references in C Some examples of issues/annoyances and workarounds with `Exports`: [Here](https://github.com/aws/aws-cdk/issues/17741), [here](https://chariotsolutions.com/blog/post/limiting-cross-stack-references-in-cdk/), [here](https://github.com/aws/aws-cdk/issues/5304), and [here](https://www.endoflineblog.com/cdk-tips-03-how-to-unblock-cross-stack-references).
+ Instead, we will rely on the approach outlined in this nice article from AWS on how to [Read parameters across AWS Regions with AWS CloudFormation custom resources](https://aws.amazon.com/blogs/infrastructure-and-automation/read-parameters-across-aws-regions-with-aws-cloudformation-custom-resources/). We will essentially: diff --git a/content/importable-posts/_index.md b/content/importable-posts/_index.md new file mode 100644 index 0000000..f251468 --- /dev/null +++ b/content/importable-posts/_index.md @@ -0,0 +1,8 @@ ++++ +title = "Importable Posts" +sort_by = "date" +template = "posts.html" +page_template = "importable-post-page.html" +in_search_index = false +insert_anchor_links = "heading" ++++ diff --git a/content/pages/_index.md b/content/pages/_index.md new file mode 100644 index 0000000..ee573c7 --- /dev/null +++ b/content/pages/_index.md @@ -0,0 +1,3 @@ ++++ +page_template = "content-page.html" ++++ diff --git a/content/pages/about.md b/content/pages/about.md new file mode 100644 index 0000000..f676d24 --- /dev/null +++ b/content/pages/about.md @@ -0,0 +1,6 @@ ++++ +title = "About" + +[extra] +parent = "posts/_index.md" ++++ diff --git a/content/pages/archive.md b/content/pages/archive.md new file mode 100644 index 0000000..d892634 --- /dev/null +++ b/content/pages/archive.md @@ -0,0 +1,8 @@ ++++ +title = "Archive" + +[extra] +parent = "posts/_index.md" ++++ + +{{ archive() }} diff --git a/posts/2014-01-31-what-is-this-about.md b/content/posts/2014-01-31-what-is-this-about.md similarity index 94% rename from posts/2014-01-31-what-is-this-about.md rename to content/posts/2014-01-31-what-is-this-about.md index 2660fa1..5c47630 100644 --- a/posts/2014-01-31-what-is-this-about.md +++ b/content/posts/2014-01-31-what-is-this-about.md @@ -1,8 +1,9 @@ ---- -title: What is this about? -tags: ramblings -date: 2014-01-31 ---- ++++ +title = "What is this about?" +date = 2014-01-31 +[taxonomies] +tags = ["ramblings"] ++++ This blog will mostly serve as a personal memory pad. I often discover a process, learn something new or just am plain forgetful. Therefore I'll try to write down various things that I learn during my journey as a programmer and enthusiast on all things technical. @@ -10,11 +11,11 @@ With that in mind, the idea is to make the content posted here general enough so What will be on here? Mostly things related to programming and technology. What I learn is almost always related to some hobby project I got going on. -
+
Another thing I'll try to do is post upfront whatever version of tools or libraries I might be using throughout that post. Far too often has it bothered me that I didn't know what version an article or tutorial applied to, or had in mind, and because of that, one might be doing something that was solved in later versions of said library or tool. -# A little background on me +## A little background on me _Updated on 21st of March, 2020._ diff --git a/posts/2014-02-13-deploying-with-vagrant.md b/content/posts/2014-02-13-deploying-with-vagrant.md similarity index 92% rename from posts/2014-02-13-deploying-with-vagrant.md rename to content/posts/2014-02-13-deploying-with-vagrant.md index d2f15cd..9b88aae 100644 --- a/posts/2014-02-13-deploying-with-vagrant.md +++ b/content/posts/2014-02-13-deploying-with-vagrant.md @@ -1,36 +1,25 @@ ---- -title: Deploying with Vagrant -tags: vagrant -date: 2014-02-13 -versions: -- Vagrant 1.4.3 ---- ++++ +title = "Deploying with Vagrant" +date = 2014-02-13 +[taxonomies] +tags = ["vagrant"] +[extra] +versions = ["Vagrant 1.4.3"] ++++ -_UPDATE: I wouldn't really recommend this approach anymore! In stead you should look into [Docker](https://www.docker.com) for containerization and [Stack](http://docs.haskellstack.org/en/stable/README/) for the Haskell side_ +_UPDATE: I wouldn't really recommend this approach anymore! Instead you should look into [Docker](https://www.docker.com) for containerization and [Stack](http://docs.haskellstack.org/en/stable/README/) for the Haskell side_ I recently started looking into ways that I could improve my current deployment workflow. Since my server doesn't have much RAM, I currently build the binaries locally in a Virtual Machine (VM from here on out) and then send them to the server using `scp`. Although I can't do much about the server part (except buying a bigger server), I can do something about what I do locally. I set out to check what possibilities I had, and ended up looking at Vagrant. -
+
While it doesn't cut hugely from the local part of my deployment hassles, it could at least streamline it a bit, and help remove the cabal hell that I'd sometimes run into with my current setup (having multiple applications that I deploy from the same VM). A quick overview of the article: -* [What is Vagrant?](#whatisvagrant) -* [Getting started with Vagrant](#gettingstartedwithvagrant) -* [Replicating your server](#replicatingyourserver) -* [Rolling your own Vagrant box](#rollingyourownvagrantbox) -* [Setting up the VM](#settingupthevm) - * [Sudo access](#sudoaccess) - * [Installing VirtualBox's Guest Additions](#installingvirtualboxsguestadditions) - * [Installing packages](#installingpackages) - * [Custom packages](#custompackages) - * [SSH keys](#sshkeys) -* [Exporting the VM box](#exportingthevmbox) -* [But how do we use it?](#buthowdoweuseit) -* [Concluding thoughts](#concludingthoughts) +{{ toc() }} ## What is Vagrant? Vagrant basically manages your VMs in a manner that is local to each project you use it in (although you can use it in a more general way). As standard it uses VirtualBox, but you can also use it with other providers, such as VMWare. diff --git a/posts/2014-09-24-local-notifications-from-irssi-on-a-remote-server.md b/content/posts/2014-09-24-local-notifications-from-irssi-on-a-remote-server.md similarity index 95% rename from posts/2014-09-24-local-notifications-from-irssi-on-a-remote-server.md rename to content/posts/2014-09-24-local-notifications-from-irssi-on-a-remote-server.md index 03a7b10..eb2d1e3 100644 --- a/posts/2014-09-24-local-notifications-from-irssi-on-a-remote-server.md +++ b/content/posts/2014-09-24-local-notifications-from-irssi-on-a-remote-server.md @@ -1,13 +1,11 @@ ---- -title: Local notifications from irssi on a remote server -tags: irssi -date: 2014-09-24 -versions: -- OS X 10.9 Mavericks (should work on all, unless they change launchtl plists) -- irssi v0.8.15 -- autossh v1.4c -- terminal-notifier v1.6.1 ---- ++++ +title = "Local notifications from irssi on a remote server" +date = 2014-09-24 +[taxonomies] +tags = ["irssi"] +[extra] +versions = ["OS X 10.9 Mavericks (should work on all, unless they change launchtl plists)", "irssi v0.8.15", "autossh v1.4c", "terminal-notifier v1.6.1"] ++++ So, if you're like me and like to have your IRC client (in this instance [irssi](http://www.irssi.org)) running on a server in a `tmux` or `screen` session to never miss out on the conversation, you might feel like you're missing some of the benefits of running a local IRC client. @@ -15,8 +13,9 @@ In particular, I was missing local notifications when someone would highlight my So, how does one solve this problem? -
+
+{{ toc() }} ## The setup The final setup will look something like this, diff --git a/posts/2014-09-25-irssi-notifications-on-ios-and-android.md b/content/posts/2014-09-25-irssi-notifications-on-ios-and-android/index.md similarity index 90% rename from posts/2014-09-25-irssi-notifications-on-ios-and-android.md rename to content/posts/2014-09-25-irssi-notifications-on-ios-and-android/index.md index 5bec5c1..8ed8fd1 100644 --- a/posts/2014-09-25-irssi-notifications-on-ios-and-android.md +++ b/content/posts/2014-09-25-irssi-notifications-on-ios-and-android/index.md @@ -1,15 +1,18 @@ ---- -title: Irssi notifications on iOS and Android -tags: irssi -date: 2014-09-25 ---- ++++ +title = "Irssi notifications on iOS and Android" +date = 2014-09-25 +[taxonomies] +tags = ["irssi"] ++++ Or really anywhere that [https://pushover.net](https://pushover.net) supports. In light of the [earlier article](/posts/2014-09-24-local-notifications-from-irssi-on-a-remote-server.html), I thought I'd might as well supercharge my IRC setup. So, now we're gonna get some notifications for our mobile devices via pushover. -
+
+ +{{ toc() }} ## Setting up pushover There really isn't much to do here. First, go create an account and download the app on the device you want notifications on. @@ -17,7 +20,7 @@ There really isn't much to do here. First, go create an account and download the Then, register a pushover application. You can do this from the dashboard, or directly from [https://pushover.net/apps/build](https://pushover.net/apps/build). It will look something like this, -Pushover Application Registration +{{ image(path="pushover-config.png", caption="Pushover Application Registration", width=600) }} ## Setting up the script @@ -27,7 +30,8 @@ So, like the article mentioned before, I'm assuming you're using [irssi](http:// Create a file called something like `irssi_pushover.sh` with the contents below,
irssi_pushover.sh
-```bash + +```bash,linenos #!/bin/bash # Replace `YOUR_APP_TOKEN` and `YOUR_USER_TOKEN` with your values from pushover diff --git a/resources/images/pushover-config.png b/content/posts/2014-09-25-irssi-notifications-on-ios-and-android/pushover-config.png similarity index 100% rename from resources/images/pushover-config.png rename to content/posts/2014-09-25-irssi-notifications-on-ios-and-android/pushover-config.png diff --git a/resources/images/S3-create-policy.png b/content/posts/2015-11-18-s3-bucket-specific-policy/S3-create-policy.png similarity index 100% rename from resources/images/S3-create-policy.png rename to content/posts/2015-11-18-s3-bucket-specific-policy/S3-create-policy.png diff --git a/resources/images/S3-edit-permissions-added.png b/content/posts/2015-11-18-s3-bucket-specific-policy/S3-edit-permissions-added.png similarity index 100% rename from resources/images/S3-edit-permissions-added.png rename to content/posts/2015-11-18-s3-bucket-specific-policy/S3-edit-permissions-added.png diff --git a/resources/images/S3-edit-permissions.png b/content/posts/2015-11-18-s3-bucket-specific-policy/S3-edit-permissions.png similarity index 100% rename from resources/images/S3-edit-permissions.png rename to content/posts/2015-11-18-s3-bucket-specific-policy/S3-edit-permissions.png diff --git a/resources/images/S3-policy-generator.png b/content/posts/2015-11-18-s3-bucket-specific-policy/S3-policy-generator.png similarity index 100% rename from resources/images/S3-policy-generator.png rename to content/posts/2015-11-18-s3-bucket-specific-policy/S3-policy-generator.png diff --git a/resources/images/S3-review-policy.png b/content/posts/2015-11-18-s3-bucket-specific-policy/S3-review-policy.png similarity index 100% rename from resources/images/S3-review-policy.png rename to content/posts/2015-11-18-s3-bucket-specific-policy/S3-review-policy.png diff --git a/posts/2015-11-18-s3-bucket-specific-policy.md b/content/posts/2015-11-18-s3-bucket-specific-policy/index.md similarity index 63% rename from posts/2015-11-18-s3-bucket-specific-policy.md rename to content/posts/2015-11-18-s3-bucket-specific-policy/index.md index c672452..e3d9870 100644 --- a/posts/2015-11-18-s3-bucket-specific-policy.md +++ b/content/posts/2015-11-18-s3-bucket-specific-policy/index.md @@ -1,8 +1,9 @@ ---- -title: S3 bucket specific policy -tags: aws -date: 2015-11-18 ---- ++++ +title = "S3 bucket specific policy" +date = 2015-11-18 +[taxonomies] +tags = ["aws"] ++++ I have recently started caring a bit more about security on my AWS applications, and to this end Identity & Access Management (IAM) users are a great way to limit access to @@ -14,21 +15,17 @@ bucket, and for this an IAM role was created. There are two ways to generate policies: -* Using the policy generator -* Manually creating a policy +{{ toc() }} I will generally advise to either use the generator completely or at least use it for the basis of the policy you want to create. -
+
## Using the policy generator To access the generator go into IAM -> Policies -> Create Policy -> Policy Generator. -
- S3 Create Policy - S3 Select Policy Generator -
+{{ images(paths=["S3-create-policy.png", "S3-policy-generator.png"], captions=["S3 Create Policy", "S3 Select Policy Generator"], widths=[400, 400]) }} You will then be able to create the policy. To do this, first set the AWS service to `Amazon S3` and then choose the actions that you want to support. @@ -39,11 +36,7 @@ Finally, you need to set the `ARN` of your bucket. This will be something like `arn:aws:s3:::YourBucketName/*` to allow access to all elements in your S3 bucket named `YourBucketName`. It'll look a little something like in the left figure below. -
- S3 Edit Permissions - S3 Review Policy -
+{{ images(paths=["S3-edit-permissions.png", "S3-review-policy.png"], captions=["S3 Edit Permissions", "S3 Review Policy"], widths=[400, 400]) }} Finally add the statement, and you can click next which should present you with something like the right image above. @@ -59,8 +52,8 @@ You can either use the generator as a basis for this document or craft your own. The following is the one generated from the policy generator which can be directly used here. -
S3 bucket policy
-```javascript + +```javascript,linenos { "Version": "2012-10-17", "Statement": [ diff --git a/posts/2015-11-18-setting-up-unrealircd-and-anope-on-ec2.md b/content/posts/2015-11-18-setting-up-unrealircd-and-anope-on-ec2.md similarity index 91% rename from posts/2015-11-18-setting-up-unrealircd-and-anope-on-ec2.md rename to content/posts/2015-11-18-setting-up-unrealircd-and-anope-on-ec2.md index ad5831b..cff607e 100644 --- a/posts/2015-11-18-setting-up-unrealircd-and-anope-on-ec2.md +++ b/content/posts/2015-11-18-setting-up-unrealircd-and-anope-on-ec2.md @@ -1,11 +1,11 @@ ---- -title: Setting up UnrealIRCd and Anope IRC Services on EC2 -tags: irc, aws, unrealircd, anope -date: 2015-11-18 -versions: -- UnrealIRCd 4.0.0-rc3 -- Anope 2.0.2 ---- ++++ +title = "Setting up UnrealIRCd and Anope IRC Services on EC2" +date = 2015-11-18 +[taxonomies] +tags = ["irc", "aws", "unrealircd", "anope"] +[extra] +versions = ["UnrealIRCd 4.0.0-rc3", "Anope 2.0.2"] ++++ Having recently discovered sameroom.io I wanted to update the codetalk IRC server to be compliant with their authentication method. This basically just meant enabling `SASL` support, but @@ -15,21 +15,15 @@ set up on AWS using `EC2` and `S3`. This will go through the process of doing exactly that, by talking about: -* enabling SASL support -* installing `UnrealIRCd` -* installing `Anope` -* a little about automating the launch of the `EC2` instance that the IRC server will run on, using the `user-data` field as input for a setup -script. -* all the important files gathered in a github repo +
-
+{{ toc() }} If you just want to throw yourself in the deep end, you can fork/clone the github repository, alter the configuration and script variables to fit your need and quickly be on with it. I recommend though that you skim through most of this to get an overview of what is happening. - ## SASL support SASL (Simple Authentication and Security Layer) is a framework for authentication and data security. For all intents and purposes we won't bother @@ -48,8 +42,8 @@ a block for Anope. __UnrealIRCd__: The following links the Anope services to the IRC server and sets the SASL server to the services server. -
services.conf
-```bash +```bash,linenos +/* FILE: services.conf */ link services.myircserver.org { incoming { mask *; @@ -77,8 +71,8 @@ set { __Anope__: Enabling SASL is fairly simple, and just requires that the module is loaded. The rest is handled by Anope. -
modules.conf
-```bash +```bash,linenos +/* FILE: modules.conf */ /* Load the SASL module m_sasl */ module { name = "m_sasl" } ``` @@ -95,8 +89,8 @@ Since we are building UnrealIRCd from source, we will need some build tools. These can be found in the `Development Tools` group. Other than that, there are some curl, ssl and other libraries that needs to be installed. -
install-unrealircd.sh
-```bash +```bash,linenos +# FILE: install-unrealircd.sh yum -yq groupinstall "Development Tools" \ && yum -yq install \ curl \ @@ -111,8 +105,9 @@ yum -yq groupinstall "Development Tools" \ Now we are able to compile UnrealIRCd from source. We will do all of this in one giant step: -
install-unrealircd.sh
-```bash + +```bash,linenos +# FILE: install-unrealircd.sh # UnrealIRCd version UNREAL_VERSION="unrealircd-4.0.0-rc3" @@ -143,8 +138,9 @@ have SSL enabled, we will also need an SSL certificate! Luckily, this can also be done through without any user input. It does require some information though, so you should substitute the variables with your information. -
install-unrealircd.sh
-```bash + +```bash,linenos +# FILE: install-unrealircd.sh # SSL certificate information # The two-letter ISO abbreviation for your country SSL_CERTIFICATE_COUNTRY="DK" @@ -186,16 +182,16 @@ the tools (namely the `Development Tools` packages). Anope uses (or at least we use it here) cmake to build. This means we have to install cmake before doing anything else. -
install-anope.sh
-```bash +```bash,linenos +# FILE: install-anope.sh yum -y install cmake ``` Now we can compile Anope IRC services from source. We will fetch it and compile it in one step: -
install-anope.sh
-```bash +```bash,linenos +# FILE: install-anope.sh # Anope version ANOPE_VERSION="2.0.2" @@ -253,8 +249,8 @@ into place, using the install scripts mentioned earlier. By having a IAM role with S3 read access attached to it, we can download objects from the S3 bucket directly. -
init-ec2.sh
-```bash +```bash,linenos +# FILE: init-ec2.sh #!/bin/bash # Bucket location export AWS_S3_BUCKET="YourBucket/install" @@ -288,8 +284,9 @@ While launching stuff from the console is indeed very fun...the first couple of times, it quickly gets tedious. Therefore we will utilize the AWS API, to create an EC2 instance, tag it and associate an elastic IP to it. -
launch-ec2-instance.sh
-```bash + +```bash,linenos +# FILE: launch-ec2-instance.sh #!/bin/bash # AWS user credentials @@ -345,7 +342,7 @@ to match what you need. The AMI ID can be found in the AMI store (you can start launching an instance and stop after the first screen). -## Github repo, ready to fork! +## GitHub repo, ready to fork! All of the scripts and configuration files to set it all up can be found in this [github repo](https://github.com/codetalkio/AWS-IRC-Setup). You'll want to change the configuration files in the `config` folder to fit your server diff --git a/posts/2015-11-28-briefly-on-the-purpose-of-functors-applicatives-and-monads.md b/content/posts/2015-11-28-briefly-on-the-purpose-of-functors-applicatives-and-monads.md similarity index 86% rename from posts/2015-11-28-briefly-on-the-purpose-of-functors-applicatives-and-monads.md rename to content/posts/2015-11-28-briefly-on-the-purpose-of-functors-applicatives-and-monads.md index f1e7861..68a9807 100644 --- a/posts/2015-11-28-briefly-on-the-purpose-of-functors-applicatives-and-monads.md +++ b/content/posts/2015-11-28-briefly-on-the-purpose-of-functors-applicatives-and-monads.md @@ -1,13 +1,16 @@ ---- -title: Briefly on the purpose of Functors, Applicatives and Monads -tags: haskell -date: 2015-11-28 ---- ++++ +title = "Briefly on the purpose of Functors, Applicatives and Monads" +date = 2015-11-28 +[taxonomies] +tags = ["haskell"] ++++ In a recent thread on [/r/haskell](https://www.reddit.com/r/haskell/) about how to [motivate the AMP proposal in teaching](https://www.reddit.com/r/haskell/comments/3tpom7/amp_how_do_you_motivate_this_in_teaching/), I read a comment that finally helped me understand the purpose of `Functor`s, `Applicative`s and `Monad`s. -
+
+ +{{ toc() }} ## Wait, what is AMP? @@ -18,8 +21,8 @@ a `Monad` is always an `Applicative`. The hierachy of `Functor`, `Applicative` and `Monad` is thus changed to, -
Typeclass hierachy
-```haskell + +```haskell,linenos class Functor f where -- ... @@ -48,26 +51,24 @@ I'm mostly going to paraphrase [/u/ForTheFunctionGod's](https://www.reddit.com/u One can naturally extend the intuition of a `Functor` to an `Applicative` and from there to a `Monad`. -__An explanation of the first two might go as follows:__ +**An explanation of the first two might go as follows:** -
-Let's say I have a list of numbers and want to add `2` to each of these -numbers. You can write `fmap (+2) [1..10]` (using `Functor`). -But what if I had two lists of numbers and wanted to add each number from the -first list to each number from the second list? With `Applicative` you can do -just that - it's like `fmap` only it can take multiple arguments. You can write -`(+) <$> [1,5,10] <*> [11..13]`. -
+> Let's say I have a list of numbers and want to add `2` to each of these +> numbers. You can write `fmap (+2) [1..10]` (using `Functor`). +> +> But what if I had two lists of numbers and wanted to add each number from the +> first list to each number from the second list? With `Applicative` you can do +> just that - it's like `fmap` only it can take multiple arguments. You can write +> `(+) <$> [1,5,10] <*> [11..13]`.[^1] -__From here, one can motivate `Monad` by asking:__ -
-What if I wanted to abort midway through - say, if I encountered a problem? You can then write: -
+**From here, one can motivate `Monad` by asking:** +> What if I wanted to abort midway through - say, if I encountered a problem? You can then write:[^1] -```haskell + +```haskell,linenos add xs ys = do x <- xs if x < 0 then [] @@ -178,8 +179,8 @@ because on each step the bind function `>>=` (or `=<<` for the other direction) To understand this better, let's take a look at the `Maybe` instance, -
Maybe Monad typeclass instance
-```haskell + +```haskell,linenos instance Monad Maybe where return x = Just x Nothing >>= f = Nothing @@ -209,3 +210,5 @@ Finally, I can deeply recommend the book [Haskell Programming - from first principle](http://haskellbook.com). It is still in development as of this date, but already features 700 pages of quality content. Simply the best Haskell book I have ever read (or, I'm still reading it as of writing). + +[^1]: https://www.reddit.com/r/haskell/comments/3tpom7/amp_how_do_you_motivate_this_in_teaching/cx8an8b diff --git a/posts/2016-05-10-compiling-scss-and-js-in-hakyll.md b/content/posts/2016-05-10-compiling-scss-and-js-in-hakyll.md similarity index 91% rename from posts/2016-05-10-compiling-scss-and-js-in-hakyll.md rename to content/posts/2016-05-10-compiling-scss-and-js-in-hakyll.md index ab33c3a..8c6bd90 100644 --- a/posts/2016-05-10-compiling-scss-and-js-in-hakyll.md +++ b/content/posts/2016-05-10-compiling-scss-and-js-in-hakyll.md @@ -1,30 +1,29 @@ ---- -title: Compiling SCSS and JavaScript in Hakyll -tags: haskell, hakyll -date: 2016-05-10 -versions: -- Hakyll 4.8.3.0 -- hjsmin 0.2.0.1 -- Sass 3.4.18 -- Stackage LTS 5.15 ---- ++++ +title = "Compiling SCSS and JavaScript in Hakyll" +date = 2016-05-10 +[taxonomies] +tags = ["haskell", "hakyll"] +[extra] +versions = ["Hakyll 4.8.3.0", "hjsmin 0.2.0.1", "Sass 3.4.18", "Stackage LTS 5.15"] ++++ This seems to be an often asked question, so I thought I'd try and share the approach that I've arrived at after having explored a couple of solutions to the problem. If you want to see the full code in action, check out the [repo for the codetalk.io site](https://github.com/codetalkio/codetalk.io/blob/v1.0.0/site.hs#L9) (linking to v1.0.0 is intended, in case the code changes later on). -
+
+ +{{ toc() }} ## Compiling and minifying JavaScript For some reason `Hakyll` does not include its own JavaScript compiler, which makes little sense. Luckily there is a package called `hjsmin` giving us `Text.Jasmine`, which we will use to both compile and minify our JavaScript files. -
-
💡
+{% aside() %} From personal experience an earlier version of `hjsmin`, e.g. 0.1.5.3, would throw a parse error on some files such as jQuery. This has later been fixed in 0.2.0.1, but unfortunately Stackage is using 0.1.5.3 in the current LTS 5.15. -
+{% end %} To get `hjsmin` 0.2.0.1 working with `stack`, add `hjsmin == 0.2.*` as a dependency in the projects cabal file, and the following to the `stack.yaml` file in the project, -```yaml +```yaml ,linenos ... extra-deps: [ hjsmin-0.2.0.1 , language-javascript-0.6.0.4 @@ -35,7 +34,7 @@ extra-deps: [ hjsmin-0.2.0.1 Now we are ready to construct the compiler itself, by jumping into `site.hs`, -```haskell +```haskell ,linenos import qualified Data.ByteString.Lazy.Char8 as C import Text.Jasmine @@ -51,7 +50,7 @@ The code is fairly straightforward. We use the `Text.Jasmine` provided function Later on inside the main `Hakyll` function in `site.hs`, we use it as we would the other compilers, -```haskell +```haskell ,linenos -- | Define the rules for the site/hakyll compiler main :: IO () main = hakyll $ do @@ -69,15 +68,13 @@ For those who aren't aware, there are other ways to write CSS than CSS. Sass and This time we rely on external dependencies, namely the `sass` tool, which can be installed with `gem install sass`. -
-
💡
+{% aside() %} There is a library called [`hsass`](https://hackage.haskell.org/package/hsass), which provides a Haskell interface, but I've been running into problems with linking to the underlying C API. As such, I've opted for the external dependency for now. -
- +{% end %} Same as with the JavaScript minification, we add a compiler in `site.hs`, -```haskell +```haskell ,linenos -- | Create a SCSS compiler that transpiles the SCSS to CSS and -- minifies it (relying on the external 'sass' tool) compressScssCompiler :: Compiler (Item String) @@ -102,7 +99,7 @@ This time we have no library dependencies, and use the `Hakyll` provided functio Much like with the JavaScript compiler, we use it in the main `Hakyll` function inside `site.hs` as such: -```haskell +```haskell ,linenos -- | Define the rules for the site/hakyll compiler main :: IO () main = hakyll $ do diff --git a/posts/2016-05-11-using-electron-with-haskell.md b/content/posts/2016-05-11-using-electron-with-haskell.md similarity index 79% rename from posts/2016-05-11-using-electron-with-haskell.md rename to content/posts/2016-05-11-using-electron-with-haskell.md index faa402e..3b0ae43 100644 --- a/posts/2016-05-11-using-electron-with-haskell.md +++ b/content/posts/2016-05-11-using-electron-with-haskell.md @@ -1,26 +1,23 @@ ---- -title: Using Electron with Haskell -tags: haskell, electron -date: 2016-05-11 -versions: -- 'Electron 1.0.1' -- 'Stackage LTS 5.15' -- 'servant 0.4.4.7' ---- - -_If you want to grab the whole code from this post, it can be found at [codetalkio/Haskell-Electron-app](https://github.com/codetalkio/Haskell-Electron-app){target="_blank" rel="noopener noreferrer"}._ - -Not much literature exist on using `Electron` as a GUI tool for Haskell development, so I thought I'd explore the space a little. Being initially a bit clueless on how `Electron` would launch the Haskell web server, I was watching [the Electron meetup talk by Mike Craig from Wagon HG](https://youtu.be/mUAu7lcgYWE?t=6m54s){target="_blank" rel="noopener noreferrer"} (they use `Electron` with Haskell) and noticed they actually mention it on the slides: - -
- -
- * Statically compiled Haskell executable - * Shipped in Electron app bundle - * main.js in Electron spawns the subprocess - * Executable creates a localhost HTTP server - * JS talks to Haskell over AJAX -
++++ +title =" Using Electron with Haskell" +date = 2016-05-11 +[taxonomies] +tags = ["haskell", "electron"] +[extra] +versions = ["Electron 1.0.1", "Stackage LTS 5.15", "servant 0.4.4.7"] ++++ + +_If you want to grab the whole code from this post, it can be found at [codetalkio/Haskell-Electron-app](https://github.com/codetalkio/Haskell-Electron-app)._ + +Not much literature exist on using `Electron` as a GUI tool for Haskell development, so I thought I'd explore the space a little. Being initially a bit clueless on how `Electron` would launch the Haskell web server, I was watching [the Electron meetup talk by Mike Craig from Wagon HG](https://youtu.be/mUAu7lcgYWE?t=6m54s) (they use `Electron` with Haskell) and noticed they actually mention it on the slides: + +
+ +> * Statically compiled Haskell executable +> * Shipped in Electron app bundle +> * main.js in Electron spawns the subprocess +> * Executable creates a localhost HTTP server +> * JS talks to Haskell over AJAX Importantly the bit _main.js in Electron spawns the subprocess_ is the part that was somehow missing in my mental model of how this would be structured (my JavaScript experience mainly lies in webdev and not really with Node.js and server-side/desktop JS). @@ -38,8 +35,12 @@ Haskell-Electron-app/ ...servant files ``` +Let's dive into it: + +{{ toc() }} + ## Setting up Electron -`Electron` has a nice [quick start guide](http://electron.atom.io/docs/latest/tutorial/quick-start/){target="_blank" rel="noopener noreferrer"}, which helps you get going fairly, well, quick. For our purposes, the following will set up the initial app we will use throughout. +`Electron` has a nice [quick start guide](http://electron.atom.io/docs/latest/tutorial/quick-start/), which helps you get going fairly, well, quick. For our purposes, the following will set up the initial app we will use throughout. ```bash $ cd Haskell-Electron-app @@ -52,7 +53,7 @@ And that's it really. You've now got a basic `Electron` app running locally. The ## Setting up the Haskell webserver -We'll be using [servant](http://haskell-servant.readthedocs.io/en/stable/){target="_blank" rel="noopener noreferrer"} for a minimal application, but you could really use anything that will run a web server (such as [Yesod](http://www.yesodweb.com){target="_blank" rel="noopener noreferrer"}, [WAI](https://www.stackage.org/package/wai){target="_blank" rel="noopener noreferrer"}, [Snap](http://snapframework.com){target="_blank" rel="noopener noreferrer"}, [Happstack](http://www.happstack.com){target="_blank" rel="noopener noreferrer"} etc, you get the idea :). +We'll be using [servant](http://haskell-servant.readthedocs.io/en/stable/) for a minimal application, but you could really use anything that will run a web server (such as [Yesod](http://www.yesodweb.com), [WAI](https://www.stackage.org/package/wai), [Snap](http://snapframework.com), [Happstack](http://www.happstack.com) etc, you get the idea :). Assuming that `stack` is installed, you can set up a new `servant` project with @@ -63,9 +64,9 @@ $ cd backend $ stack build ``` -which will download the `servant` project template for you (from the [stack templates repo](https://github.com/commercialhaskell/stack-templates){target="_blank" rel="noopener noreferrer"}) and build it. +which will download the `servant` project template for you (from the [stack templates repo](https://github.com/commercialhaskell/stack-templates)) and build it. -To test that it works run `stack exec backend-exe` which will start the executable that `stack build` produced. You now have a web server running at `127.0.0.1:8080` - try and navigate to [127.0.0.1:8080/users](http://127.0.0.1:8080/users){target="_blank" rel="noopener noreferrer"} and check it out! :) +To test that it works run `stack exec backend-exe` which will start the executable that `stack build` produced. You now have a web server running at `127.0.0.1:8080` - try and navigate to [127.0.0.1:8080/users](http://127.0.0.1:8080/users) and check it out! :) For the lack of a better named I have called the application _backend_, but it could really be anything you fancy. @@ -77,7 +78,7 @@ Since the `servant` template project has given us the endpoint `127.0.0.1:8080/u By default the chromium developer tools are enabled in `Electron`. I suggest you keep them enabled while debugging, since that makes it a lot easier to see if anything went wrong. If you want to disable it, you just need to comment/remove a line in `Haskell-Electron-app/haskell-app/main.js`: -```javascript +```typescript ,linenos ... function createWindow () { // Create the browser window, @@ -91,11 +92,11 @@ function createWindow () { ... ``` -Short interlude: we'll be a bit lazy and download [jQuery 2.2.3 minified](https://code.jquery.com/jquery-2.2.3.min.js){target="_blank" rel="noopener noreferrer"}. Put that into `Haskell-Electron-app/haskell-app/resources/jQuery-2.2.3.min.js` so we can include it later on and get the nice AJAX functionality it provides. +Short interlude: we'll be a bit lazy and download [jQuery 2.2.3 minified](https://code.jquery.com/jquery-2.2.3.min.js). Put that into `Haskell-Electron-app/haskell-app/resources/jQuery-2.2.3.min.js` so we can include it later on and get the nice AJAX functionality it provides. Back to work in `Haskell-Electron-app/haskell-app`, lets change the `index.html` page and prepare it for our list of users. -```html +```html ,linenos @@ -126,7 +127,7 @@ Back to work in `Haskell-Electron-app/haskell-app`, lets change the `index.html` And finally we'll implement the logic in `renderer.js`, -```javascript +```typescript ,linenos // Backend and endpoint details. const host = 'http://127.0.0.1:8080' const endpoint = '/users' @@ -172,7 +173,7 @@ fetchUserList(waitTimeBetweenAttempt, maxNoOfAttempts) We simply request the `JSON` data at `http://127.0.0.1:8080/users`, with `$.getJSON(...)`, and display it if we received the data. If the request failed, we keep retrying until we either get a response or reach the maximum number of attempts (here set to 50 via `maxNoOfAttempts`). -The real purpose behind the retry will become apparent later on, when we might need to wait for the server to become available. Normally you will use a status endpoint that you are 100% sure is correct and not failing to check for the availability (inspired by the answer [Mike from Wagon HQ gave here](https://www.reddit.com/r/haskell/comments/4ipah2/resources_for_electron_haskell/d30mupm){target="_blank" rel="noopener noreferrer"}). +The real purpose behind the retry will become apparent later on, when we might need to wait for the server to become available. Normally you will use a status endpoint that you are 100% sure is correct and not failing to check for the availability (inspired by the answer [Mike from Wagon HQ gave here](https://www.reddit.com/r/haskell/comments/4ipah2/resources_for_electron_haskell/d30mupm)). ## Launching the Haskell web server from Electron @@ -180,7 +181,7 @@ Now to the interesting part, let's try to launch the Haskell web server from ins First though, let us set the `haskell-app/resources` folder as the target for our binary, in the `stack.yaml` file, with the `local-bin-path` configuration value. -```yaml +```yaml ,linenos resolver: lts-5.15 local-bin-path: ../haskell-app/resources ... @@ -199,7 +200,7 @@ After that it is surprisingly easy to launch the executable from within `Electro Since there are bits and pieces that are added I'll include the whole `Haskell-Electron-app/haskell-app/main.js` file, with most of the comments removed. -```javascript +```typescript ,linenos const electron = require('electron') // Used to spawn processes. const child_process = require('child_process') @@ -246,13 +247,13 @@ app.on('activate', function () { Let's briefly go through what is happening: -* We are using the [child_process.spawn](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options){target="_blank" rel="noopener noreferrer"} command to launch our backend web server -* We imported the `child_process` module with `const child_process = require('child_process')` -* Defined a variable `let backendServer` that'll let us keep the backend server from being garbage collected -* Added a function `createBackendServer` that runs `child_process.spawn('./resources/backend-exe')` to spawn the process -* Added the `createBackendServer` function to the `ready` hook with `app.on('ready', createBackendServer)` -* Close the `backendServer` when the event `will-quit` occurs +- We are using the [child_process.spawn](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) command to launch our backend web server +- We imported the `child_process` module with `const child_process = require('child_process')` +- Defined a variable `let backendServer` that'll let us keep the backend server from being garbage collected +- Added a function `createBackendServer` that runs `child_process.spawn('./resources/backend-exe')` to spawn the process +- Added the `createBackendServer` function to the `ready` hook with `app.on('ready', createBackendServer)` +- Close the `backendServer` when the event `will-quit` occurs __And voila!__ We now have Electron spawning a process that runs a Haskell web server! :) -Next step would be to package the app up for distribution to see if that affects anything, but I'll save that for another time (and `Electron` already has [a page on distribution here](http://electron.atom.io/docs/v0.37.8/tutorial/application-distribution/){target="_blank" rel="noopener noreferrer"}). +Next step would be to package the app up for distribution to see if that affects anything, but I'll save that for another time (and `Electron` already has [a page on distribution here](http://electron.atom.io/docs/v0.37.8/tutorial/application-distribution/)). diff --git a/posts/2018-02-07-Mobile-Haskell.md b/content/posts/2018-02-07-mobile-haskell/index.md similarity index 64% rename from posts/2018-02-07-Mobile-Haskell.md rename to content/posts/2018-02-07-mobile-haskell/index.md index 30e6383..ac5ebf9 100644 --- a/posts/2018-02-07-Mobile-Haskell.md +++ b/content/posts/2018-02-07-mobile-haskell/index.md @@ -1,34 +1,36 @@ ---- -title: Mobile Haskell (iOS) -tags: haskell, mobile, iOS -date: 2018-02-07 -versions: -- 'Xcode Version 9.2 (9C40b)' -- 'Cabal HEAD (commit [94a7374](https://github.com/haskell/cabal/commit/94a7374d7b1a9b55454209e92d5057ba81be7d6a){target="_blank" rel="noopener noreferrer"})' -- 'Stack Version 1.6.3' -- 'LLVM Version 5.0.1' ---- ++++ +title = "Mobile Haskell (iOS)" +date = 2018-02-07 +[taxonomies] +tags = ["haskell", "mobile", "iOS"] +[extra] +versions = ["Xcode Version 9.2 (9C40b)", "Cabal HEAD (commit [94a7374](https://github.com/haskell/cabal/commit/94a7374d7b1a9b55454209e92d5057ba81be7d6a){target=\"_blank\" rel=\"noopener noreferrer\"})", "Stack Version 1.6.3", "LLVM Version 5.0.1"] ++++ A lot of progress has been going on to make Haskell work on mobile natively, instead of e.g. generating JavaScript via GHCJS and using that. Unfortunately, not much documentation exists yet on how to build a project using these tools all together. This post will be an attempt to piece together the tools and various attempts into a coherent step-by-step guide. We will start by setting up the tools needed, and then build an iOS app that runs in both the simulator and on the device itself (i.e. a x86 build and an arm build). -
+
For the impatient and brave, simply, -- clone down the [MobileHaskellFun](https://github.com/Tehnix/MobileHaskellFun){target="_blank" rel="noopener noreferrer"} repository, +- clone down the [MobileHaskellFun](https://github.com/Tehnix/MobileHaskellFun) repository, - run `./setup-tools.sh` to set up the tools, - `cd` into `Offie/hs-src/` - build the package index `./call x86_64-apple-ios-cabal new-update --allow-newer`, - run `./call make iOS` to compile the program for iOS, - and finally launch Xcode and start the simulator. +Otherwise, let's go through the steps in detail: + +{{ toc() }} + ## Setting up the Tools A bunch of tools are needed, so we will set these up first. You might have some of these, but I will go through them anyways, for good measure. The steps will assume that we are on macOS for some parts, but it should not be part to adapt these to your system (all steps using `brew`). -If you don't have [stack](https://docs.haskellstack.org/en/stable/README/){target="_blank" rel="noopener noreferrer"} installed already, set it up with, +If you don't have [stack](https://docs.haskellstack.org/en/stable/README/) installed already, set it up with, ```bash $ curl -sSL https://get.haskellstack.org/ | sh @@ -40,7 +42,7 @@ We will collect all our tools and GHC versions in a folder in `$HOME`—for conv $ mkdir -p ~/.mobile-haskell ``` -Next step is cloning down [cabal](github.com/haskell/cabal){target="_blank" rel="noopener noreferrer"} and building cabal-install. This is necessary until `new-update` lands. +Next step is cloning down [cabal](github.com/haskell/cabal) and building cabal-install. This is necessary until `new-update` lands. ```bash $ cd ~/.mobile-haskell @@ -60,7 +62,7 @@ $ brew install llvm This should set up LLVM in `/usr/local/opt/llvm@5/bin` (or just `/usr/local/opt/llvm/bin`), remember this path for later. -We'll now set up the tools from [http://hackage.mobilehaskell.org](http://hackage.mobilehaskell.org){target="_blank" rel="noopener noreferrer"}, namely the toolchain-wrapper and the different GHC versions we will use. +We'll now set up the tools from [http://hackage.mobilehaskell.org](http://hackage.mobilehaskell.org), namely the toolchain-wrapper and the different GHC versions we will use. Let's start off with getting our GHCs, by downloading `ghc-8.4.0.20180109-x86_64-apple-ios.tar.xz` and `ghc-8.4.0.20180109-aarch64-apple-ios.tar.xz`, for the simulator and device respectively. You can download the by cliking their links on the website, or curl them down with (the links are probably outdated soon, so replace the links with the ones on the site), @@ -120,7 +122,7 @@ $ make && make install We should now have our libffi files for the two targets living in `~/.mobile-haskell/lffi/aarch64-apple-ios` and `~/.mobile-haskell/lffi/x86_64-apple-ios` respectively. --> -And that's it! We have now set up all the tools we need for later. If you want all the steps as a single script, check out the [setup script in the MobileHaskellFun repo](https://github.com/Tehnix/MobileHaskellFun/blob/master/setup-tools.sh){target="_blank" rel="noopener noreferrer"}. +And that's it! We have now set up all the tools we need for later. If you want all the steps as a single script, check out the [setup script in the MobileHaskellFun repo](https://github.com/Tehnix/MobileHaskellFun/blob/master/setup-tools.sh). ## Setting up the Xcode Project @@ -128,25 +130,20 @@ Setting up Xcode is a bit of a visual process, so I'll augment these steps with First, let's set up our Xcode project, by creating a new project. -1. Create Project +{{ image(path="mobile-haskell-1. Create Project.png", caption="1. Create Project", width=600) }} Choose `Single View Application`, -1.1. Create Project - Single View Application +{{ image(path="mobile-haskell-1.1. Create Project - Single View Application.png", caption="1.1. Create Project - Single View Application", width=600) }} And set the name and location of your project, -
- 1.2. Create Project - Name - 1.3. Create Project - Set Location -
-Now, let's add a folder to keep our Haskell code in and call it `hs-src`, by right-clicking our project and adding a `New Group`, +{{ images(paths=["mobile-haskell-1.2. Create Project - Name.png", "mobile-haskell-1.3. Create Project - Set Location.png"], captions=["1.2. Create Project - Name", "1.3. Create Project - Set Location"], widths=[400, 400]) }} -
-2. Add Source Folder for Haskell Code -
+Now, let's add a folder to keep our Haskell code in and call it `hs-src`, by right-clicking our project and adding a `New Group`, +{{ image(path="mobile-haskell-2. Add Source Folder for Haskell Code.png", caption="2. Add Source Folder for Haskell Code", width=300) }} ## Interlude: Set up the Haskell Code @@ -162,7 +159,7 @@ $ touch MobileFun.cabal cabal.project Makefile call LICENSE src/Lib.hs We use the features of `cabal.project` to set our package repository to use the hackage.mobilehaskell.org overlay. -```haskell +```haskell ,linenos packages: . repository hackage.mobilehaskell @@ -178,7 +175,7 @@ repository hackage.mobilehaskell Just a simple cabal package setup. -```haskell +```haskell ,linenos name: MobileFun version: 0.1.0.0 license: BSD3 @@ -202,7 +199,7 @@ library The Makefile simplifies a lot of the compilation process and passes the flags we need to use. -```makefile +```makefile ,linenos LIB=MobileFun ARCHIVE=libHS${LIB} @@ -250,7 +247,7 @@ clean: Our Haskell code for now, is simply some C FFI that sets up a small toy function. -```haskell +```haskell ,linenos module Lib where import Foreign.C (CString, newCString) @@ -270,7 +267,7 @@ hello = "Hello from Haskell" We use the `call` script to set up the various path variables that point to our tools, so we don't need these polluting our global command space. If you've followed the setup so far, the paths should match out-of-the-box. -```bash +```bash ,linenos #!/usr/bin/env bash # Path to LLVM (this is the default when installing via `brew`) export PATH=/usr/local/opt/llvm@5/bin:$PATH @@ -325,23 +322,15 @@ We should now have our library file at `hs-src/binaries/iOS/libHSMobileFun.a`. I Now we need to tie together the Haskell code with Xcode. Drag-and-drop the newly created files into the `hs-src` group in Xcode (if it hasn't found it by itself). -3. Drag the files to Xcode +And set the name and location of your project, + +{{ image(path="mobile-haskell-3. Drag the files to Xcode.png", caption="3. Drag the files to Xcode", width=600) }} Since we are using Swift, we need a bridging header to bring our C prototypes into Swift. We'll do this by adding an Objective-C file to the project, `tmp.m`, which will make Xcode ask if we want to create a bridging header, `Offie-Bridging-Header.h`, for which we will answer yes. -
-4. Create Objective-C File -
- -
- 4.1. Create Objective-C File - Choose Filetype - 4.2. Create Objective-C File - Set Name -
+{{ image(path="mobile-haskell-4. Create Objective-C File.png", caption="4. Create Objective-C File", width=300) }} -
- 4.3. Create Objective-C File - Set Location - 4.4. Create Objective-C File - Create Bridging Header -
+{{ images(paths=["mobile-haskell-4.1. Create Objective-C File - Choose Filetype.png", "mobile-haskell-4.2. Create Objective-C File - Set Name.png"], captions=["4.1. Create Objective-C File - Choose Filetype", "4.2. Create Objective-C File - Set Name"], widths=[400, 400]) }}
@@ -349,7 +338,7 @@ Since we are using Swift, we need a bridging header to bring our C prototypes in In our bridging file, `Offie-Bridging-Header.h`, we add our prototypes that we need to glue in the Haskell code, -```c +```c ,linenos extern void hs_init(int * argc, char ** argv[]); extern char * hello(); ``` @@ -358,7 +347,7 @@ extern char * hello(); Now let's go into `AppDelegate.swift` and call `hs_init` to initialize the Haskell code, -```objective-c +```objective-c ,linenos import UIKit @UIApplicationMain @@ -388,15 +377,15 @@ Next, we will set up a label in a view controller. You can either set this up in First go into the `Main.storyboard` and create a label element somewhere on the screen. -7. Add Label +{{ image(path="mobile-haskell-7. Add Label.png", caption="7. Add Label", width=600) }} Then enable the `Assistant Editor` in the top right cornor, and ctrl-click on the label, dragging it over to the `ViewController.swift` and name `helloWorldLabel`. -7.1. Add Label - Connect IBOutlet +{{ image(path="mobile-haskell-7.1. Add Label - Connect IBOutlet.png", caption="7.1. Add Label - Connect IBOutlet", width=600) }} We can now set the text of the label by calling our Haskell function with `cString: hello()`, making our `ViewController.swift` look like, -```objective-c +```objective-c ,linenos import UIKit class ViewController: UIViewController { @@ -420,46 +409,43 @@ The final step we need to do, is linking in our library that we built earlier, ` We do this by going into `Build Phases`, which is exposed under the Xcode project settings, and click the `+` to add a new library, -5. Build Phases +{{ image(path="mobile-haskell-5. Build Phases.png", caption="5. Build Phases", width=650) }} Choose `Add Other...` to locate the library, -5.1. Build Phases - Add New +{{ image(path="mobile-haskell-5.1. Build Phases - Add New.png", caption="5.1. Build Phases - Add New", width=650) }} and finally locate the library file in `hs-src/binaries/iOS/libHSMobileFun.a`, -5.2. Build Phases - Locate the Library +{{ image(path="mobile-haskell-5.2. Build Phases - Locate the Library.png", caption="5.2. Build Phases - Locate the Library", width=650) }} We also need to set the build to not generate bytecode, because we are using the external GHC library. This is done under `Build Settings`, locating `Enable Bitcode` (e.g. via the search) and setting it to `No`. -6. Build Settings +{{ image(path="mobile-haskell-6. Build Settings.png", caption="6. Build Settings", width=650) }} ## Run the Code! Final step, let's run our code in the simulator -
- 9. Run Simulator -
+{{ image(path="mobile-haskell-9. Run Simulator.png", caption="9. Run Simulator", width=300) }} Congratulations! You're now calling Haskell code from Swift and running it in an iOS simulator. -
-
💡
+{% aside() %} You might run into a problem like `could not create compact unwind for _ffi_call_unix64: does not use RBP or RSP based frame` in your Xcode builds. You can fix this by adding `libconv` to your libraries in `Build Phase`. -
+{% end %} -8. Add libconv to libraries +{{ image(path="mobile-haskell-8. Add libconv to libraries.png", caption="8. Add libconv to libraries", width=650) }} ## Resources Most of this is gathered from: -- [A Haskell Cross Compiler for iOS](https://medium.com/@zw3rk/a-haskell-cross-compiler-for-ios-7cc009abe208){target="_blank" rel="noopener noreferrer"} and some of the other medium posts. -- Various issues on the [mobile-haskell/hackage-overlay](https://github.com/mobilehaskell/hackage-overlay){target="_blank" rel="noopener noreferrer"} ([#5](https://github.com/mobilehaskell/hackage-overlay/issues/5){target="_blank" rel="noopener noreferrer"}, [#2](https://github.com/mobilehaskell/hackage-overlay/issues/2){target="_blank" rel="noopener noreferrer"}). +- [A Haskell Cross Compiler for iOS](https://medium.com/@zw3rk/a-haskell-cross-compiler-for-ios-7cc009abe208) and some of the other medium posts. +- Various issues on the [mobile-haskell/hackage-overlay](https://github.com/mobilehaskell/hackage-overlay) ([#5](https://github.com/mobilehaskell/hackage-overlay/issues/5), [#2](https://github.com/mobilehaskell/hackage-overlay/issues/2)). - The [preliminary user guide](http://mobile-haskell-user-guide.readthedocs.io/en/latest/). -If you are interested in following the development of Haskell in the mobile space, I recommend following [\@zw3rktech](https://twitter.com/zw3rktech){target="_blank" rel="noopener noreferrer"} and [\@mobilehaskell](https://twitter.com/mobilehaskell){target="_blank" rel="noopener noreferrer"}. +If you are interested in following the development of Haskell in the mobile space, I recommend following [\@zw3rktech](https://twitter.com/zw3rktech) and [\@mobilehaskell](https://twitter.com/mobilehaskell). -Finally, let me know if something is not working with the [MobileHaskellFun repository](https://github.com/Tehnix/MobileHaskellFun){target="_blank" rel="noopener noreferrer"}. I haven't dealt that much with setting up Xcode projects for sharing, so I'm a bit unclear on what settings follow the repository around. +Finally, let me know if something is not working with the [MobileHaskellFun repository](https://github.com/Tehnix/MobileHaskellFun). I haven't dealt that much with setting up Xcode projects for sharing, so I'm a bit unclear on what settings follow the repository around. diff --git a/resources/images/mobile-haskell-1. Create Project.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-1. Create Project.png similarity index 100% rename from resources/images/mobile-haskell-1. Create Project.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-1. Create Project.png diff --git a/resources/images/mobile-haskell-1.1. Create Project - Single View Application.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-1.1. Create Project - Single View Application.png similarity index 100% rename from resources/images/mobile-haskell-1.1. Create Project - Single View Application.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-1.1. Create Project - Single View Application.png diff --git a/resources/images/mobile-haskell-1.2. Create Project - Name.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-1.2. Create Project - Name.png similarity index 100% rename from resources/images/mobile-haskell-1.2. Create Project - Name.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-1.2. Create Project - Name.png diff --git a/resources/images/mobile-haskell-1.3. Create Project - Set Location.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-1.3. Create Project - Set Location.png similarity index 100% rename from resources/images/mobile-haskell-1.3. Create Project - Set Location.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-1.3. Create Project - Set Location.png diff --git a/resources/images/mobile-haskell-2. Add Source Folder for Haskell Code.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-2. Add Source Folder for Haskell Code.png similarity index 100% rename from resources/images/mobile-haskell-2. Add Source Folder for Haskell Code.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-2. Add Source Folder for Haskell Code.png diff --git a/resources/images/mobile-haskell-2.1. Add Source Folder for Haskell Code.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-2.1. Add Source Folder for Haskell Code.png similarity index 100% rename from resources/images/mobile-haskell-2.1. Add Source Folder for Haskell Code.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-2.1. Add Source Folder for Haskell Code.png diff --git a/resources/images/mobile-haskell-3. Drag the files to Xcode.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-3. Drag the files to Xcode.png similarity index 100% rename from resources/images/mobile-haskell-3. Drag the files to Xcode.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-3. Drag the files to Xcode.png diff --git a/resources/images/mobile-haskell-4. Create Objective-C File.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-4. Create Objective-C File.png similarity index 100% rename from resources/images/mobile-haskell-4. Create Objective-C File.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-4. Create Objective-C File.png diff --git a/resources/images/mobile-haskell-4.1. Create Objective-C File - Choose Filetype.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-4.1. Create Objective-C File - Choose Filetype.png similarity index 100% rename from resources/images/mobile-haskell-4.1. Create Objective-C File - Choose Filetype.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-4.1. Create Objective-C File - Choose Filetype.png diff --git a/resources/images/mobile-haskell-4.2. Create Objective-C File - Set Name.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-4.2. Create Objective-C File - Set Name.png similarity index 100% rename from resources/images/mobile-haskell-4.2. Create Objective-C File - Set Name.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-4.2. Create Objective-C File - Set Name.png diff --git a/resources/images/mobile-haskell-4.3. Create Objective-C File - Set Location.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-4.3. Create Objective-C File - Set Location.png similarity index 100% rename from resources/images/mobile-haskell-4.3. Create Objective-C File - Set Location.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-4.3. Create Objective-C File - Set Location.png diff --git a/resources/images/mobile-haskell-4.4. Create Objective-C File - Create Bridging Header.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-4.4. Create Objective-C File - Create Bridging Header.png similarity index 100% rename from resources/images/mobile-haskell-4.4. Create Objective-C File - Create Bridging Header.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-4.4. Create Objective-C File - Create Bridging Header.png diff --git a/resources/images/mobile-haskell-5. Build Phases.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-5. Build Phases.png similarity index 100% rename from resources/images/mobile-haskell-5. Build Phases.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-5. Build Phases.png diff --git a/resources/images/mobile-haskell-5.1. Build Phases - Add New.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-5.1. Build Phases - Add New.png similarity index 100% rename from resources/images/mobile-haskell-5.1. Build Phases - Add New.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-5.1. Build Phases - Add New.png diff --git a/resources/images/mobile-haskell-5.2. Build Phases - Locate the Library.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-5.2. Build Phases - Locate the Library.png similarity index 100% rename from resources/images/mobile-haskell-5.2. Build Phases - Locate the Library.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-5.2. Build Phases - Locate the Library.png diff --git a/resources/images/mobile-haskell-6. Build Settings.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-6. Build Settings.png similarity index 100% rename from resources/images/mobile-haskell-6. Build Settings.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-6. Build Settings.png diff --git a/resources/images/mobile-haskell-6. Build Settings.thumbnail.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-6. Build Settings.thumbnail.png similarity index 100% rename from resources/images/mobile-haskell-6. Build Settings.thumbnail.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-6. Build Settings.thumbnail.png diff --git a/resources/images/mobile-haskell-7. Add Label.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-7. Add Label.png similarity index 100% rename from resources/images/mobile-haskell-7. Add Label.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-7. Add Label.png diff --git a/resources/images/mobile-haskell-7. Add Label.thumbnail.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-7. Add Label.thumbnail.png similarity index 100% rename from resources/images/mobile-haskell-7. Add Label.thumbnail.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-7. Add Label.thumbnail.png diff --git a/resources/images/mobile-haskell-7.1. Add Label - Connect IBOutlet.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-7.1. Add Label - Connect IBOutlet.png similarity index 100% rename from resources/images/mobile-haskell-7.1. Add Label - Connect IBOutlet.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-7.1. Add Label - Connect IBOutlet.png diff --git a/resources/images/mobile-haskell-7.1. Add Label - Connect IBOutlet.thumbnail.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-7.1. Add Label - Connect IBOutlet.thumbnail.png similarity index 100% rename from resources/images/mobile-haskell-7.1. Add Label - Connect IBOutlet.thumbnail.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-7.1. Add Label - Connect IBOutlet.thumbnail.png diff --git a/resources/images/mobile-haskell-8. Add libconv to libraries.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-8. Add libconv to libraries.png similarity index 100% rename from resources/images/mobile-haskell-8. Add libconv to libraries.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-8. Add libconv to libraries.png diff --git a/resources/images/mobile-haskell-8. Add libconv to libraries.thumbnail.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-8. Add libconv to libraries.thumbnail.png similarity index 100% rename from resources/images/mobile-haskell-8. Add libconv to libraries.thumbnail.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-8. Add libconv to libraries.thumbnail.png diff --git a/resources/images/mobile-haskell-9. Run Simulator.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-9. Run Simulator.png similarity index 100% rename from resources/images/mobile-haskell-9. Run Simulator.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-9. Run Simulator.png diff --git a/resources/images/mobile-haskell-9. Run Simulator.thumbnail.png b/content/posts/2018-02-07-mobile-haskell/mobile-haskell-9. Run Simulator.thumbnail.png similarity index 100% rename from resources/images/mobile-haskell-9. Run Simulator.thumbnail.png rename to content/posts/2018-02-07-mobile-haskell/mobile-haskell-9. Run Simulator.thumbnail.png diff --git a/resources/images/dynamodb-migration-phase-1-background.png b/content/posts/2020-03-19-live-migration-of-dynamodb-tables/dynamodb-migration-phase-1-background.png similarity index 100% rename from resources/images/dynamodb-migration-phase-1-background.png rename to content/posts/2020-03-19-live-migration-of-dynamodb-tables/dynamodb-migration-phase-1-background.png diff --git a/resources/images/dynamodb-migration-phase-1.png b/content/posts/2020-03-19-live-migration-of-dynamodb-tables/dynamodb-migration-phase-1.png similarity index 100% rename from resources/images/dynamodb-migration-phase-1.png rename to content/posts/2020-03-19-live-migration-of-dynamodb-tables/dynamodb-migration-phase-1.png diff --git a/resources/images/dynamodb-migration-phase-2-background.png b/content/posts/2020-03-19-live-migration-of-dynamodb-tables/dynamodb-migration-phase-2-background.png similarity index 100% rename from resources/images/dynamodb-migration-phase-2-background.png rename to content/posts/2020-03-19-live-migration-of-dynamodb-tables/dynamodb-migration-phase-2-background.png diff --git a/resources/images/dynamodb-migration-phase-2.png b/content/posts/2020-03-19-live-migration-of-dynamodb-tables/dynamodb-migration-phase-2.png similarity index 100% rename from resources/images/dynamodb-migration-phase-2.png rename to content/posts/2020-03-19-live-migration-of-dynamodb-tables/dynamodb-migration-phase-2.png diff --git a/posts/2020-03-19-Live-Migration-of-DynamoDB-Tables.md b/content/posts/2020-03-19-live-migration-of-dynamodb-tables/index.md similarity index 76% rename from posts/2020-03-19-Live-Migration-of-DynamoDB-Tables.md rename to content/posts/2020-03-19-live-migration-of-dynamodb-tables/index.md index e2297cb..6a0e040 100644 --- a/posts/2020-03-19-Live-Migration-of-DynamoDB-Tables.md +++ b/content/posts/2020-03-19-live-migration-of-dynamodb-tables/index.md @@ -1,8 +1,9 @@ ---- -title: Live Migration of DynamoDB Tables -tags: aws, dynamodb, sqs, migration -date: 2020-03-19 ---- ++++ +title = "Live Migration of DynamoDB Tables" +date = 2020-03-19 +[taxonomies] +tags = ["aws", "dynamodb", "sqs", "migration"] ++++ Recently I was faced with the challenge of having to migrate a set of AWS DynamoDB tables to completely new tables. We wanted to achieve this without affecting any of our users, and without having a maintenance window while migrating data from the old table to the new ones. @@ -14,24 +15,27 @@ The following will be a very high-level overview of how you: - Restart the migration if you made an error - Complete the switchover after validating everything looks OK -
+
-
-
💡
- This won't be detailed walkthrough but will instead outline a method for people that are in the same situation we were. -
+{% aside() %} + This won't be a detailed walkthrough, but will instead outline a method for people that are in the same situation we were. +{% end %} + +We'll go through the following: + +{{ toc() }} ## Context To put this migration into context, our entire cloud and setup are automated via CloudFormation, deploying many micro-services. -To better orchestrate the deployment of these and have better handling of dependencies between the order of deployments, we had been working on a lot of infrastructure changes, switching around the tools that manage CloudFormation (from [serverless](https://serverless.com) to [AWS CDK](https://github.com/aws/aws-cdk){target="_blank" rel="noopener noreferrer"}). +To better orchestrate the deployment of these and have better handling of dependencies between the order of deployments, we had been working on a lot of infrastructure changes, switching around the tools that manage CloudFormation (from [serverless](https://serverless.com) to [AWS CDK](https://github.com/aws/aws-cdk)). Internally these tools generate unique references (logical ids), each in their own way, to the resources they generate CloudFormation for. This meant that we needed a way to change the logical ids of a resource for us to have the new tools adopt and manage the existing resources. -Unfortunately, this is not possible for DynamoDB tables^[As mentioned in [serverless#1677](https://github.com/serverless/serverless/issues/1677){target="_blank" rel="noopener noreferrer"}, this is not supported.], without recreating the resources ☹️ +Unfortunately, this is not possible for DynamoDB tables^[As mentioned in [serverless#1677](https://github.com/serverless/serverless/issues/1677), this is not supported.], without recreating the resources ☹️ ## Approach -The idea came after AWS announced support for importing existing resources into a CloudFormation stack^[[https://aws.amazon.com/blogs/aws/new-import-existing-resources-into-a-cloudformation-stack/](https://aws.amazon.com/blogs/aws/new-import-existing-resources-into-a-cloudformation-stack/){target="_blank" rel="noopener noreferrer"}.], and was further motivated by the later support for restoring a DynamoDB table to another region^[[https://aws.amazon.com/blogs/database/restore-amazon-dynamodb-backups-to-different-aws-regions-and-with-custom-table-settings/](https://aws.amazon.com/blogs/database/restore-amazon-dynamodb-backups-to-different-aws-regions-and-with-custom-table-settings/){target="_blank" rel="noopener noreferrer"}.]. +The idea came after AWS announced support for importing existing resources into a CloudFormation stack[^1], and was further motivated by the later support for restoring a DynamoDB table to another region[^2]. The concept is simple and can be divided into two phases. @@ -43,7 +47,7 @@ In the first phase, we will: - Set up a Lambda function that will receive the events from these streams - Set up a FIFO SQS queue which the Lambda function will put all of the events on -Migration Phase 1 +{{ image(path="dynamodb-migration-phase-1.png", caption="Migration Phase 1", width=650) }} After this is set up, all DynamoDB events will exist on the SQS queue. We will now create a backup for each of the tables. The time of the backups will be important later on. You don't have to note them down, since they are visible in the table backup overview. @@ -58,7 +62,7 @@ We will now: - Set up a Lambda Function that consumes from you SQS queue - The consumer Lambda should only act on events that have happened on or after your backup time -Migration Phase 2 +{{ image(path="dynamodb-migration-phase-2.png", caption="Migration Phase 2", width=650) }} Since DynamoDB stream events contain all the information about a record, we can break them into the following: @@ -83,7 +87,7 @@ When we felt comfortable that the new tables were correct, and had finalized the Pointing the API to the new DynamoDB tables means that no more operations are done on the old tables, and thus no further events are put on the SQS. This had the effect that there was no rush to clean up the two Lambdas and SQS queue since they would simply not be performing any actions anymore. -### Utilizing this approach for data transformation +## Utilizing this approach for data transformation We've only talked about migrating tables, without really touching the data. Still, this approach allows you to perform a data transformation along with your migration without needing to coordinate a complicated API switch or support both formats of data simultaneously. You could do the following instead: @@ -101,3 +105,7 @@ This way, you perform live transformations on your data and have ample time to s While very high-level, this approach is a great tool to have in your toolbox when you run into those once-in-a-while cases where you need to perform these migrations. If you are interested in me expanding more on the approach, please don't hesitate to leave a comment 🙂 + +[^1]: [https://aws.amazon.com/blogs/aws/new-import-existing-resources-into-a-cloudformation-stack/](https://aws.amazon.com/blogs/aws/new-import-existing-resources-into-a-cloudformation-stack/) + +[^2]: [https://aws.amazon.com/blogs/database/restore-amazon-dynamodb-backups-to-different-aws-regions-and-with-custom-table-settings/](https://aws.amazon.com/blogs/database/restore-amazon-dynamodb-backups-to-different-aws-regions-and-with-custom-table-settings/) diff --git a/posts/2020-03-20-Visual-Studio-Code-on-iPad.md b/content/posts/2020-03-20-visual-studio-code-on-ipad/index.md similarity index 68% rename from posts/2020-03-20-Visual-Studio-Code-on-iPad.md rename to content/posts/2020-03-20-visual-studio-code-on-ipad/index.md index aae6f8a..6506e75 100644 --- a/posts/2020-03-20-Visual-Studio-Code-on-iPad.md +++ b/content/posts/2020-03-20-visual-studio-code-on-ipad/index.md @@ -1,33 +1,34 @@ ---- -title: Visual Studio Code on iPad -tags: vscode, ipad, digital nomad -date: 2020-03-20 -versions: -- '`code-server` version 3.4.1' -- 'Ubuntu 18.04 (should fit with most distros)' -- 'Your own server' -- 'Your own domain' ---- - -With Apple increasing their focus^[[https://www.apple.com/newsroom/2020/03/apple-unveils-new-ipad-pro-with-lidar-scanner-and-trackpad-support-in-ipados/](https://www.apple.com/newsroom/2020/03/apple-unveils-new-ipad-pro-with-lidar-scanner-and-trackpad-support-in-ipados/){target="_blank" rel="noopener noreferrer"}.] on making the iPad a viable device for work, it is time to revisit using my iPad as a workstation for programming. ++++ +title = "Visual Studio Code on iPad" +date = 2020-03-20 +[taxonomies] +tags = ["vscode", "ipad", "digital nomad"] +[extra] +versions = ["`code-server` version 3.4.1", "Ubuntu 18.04 (should fit with most distros)", "Your own server", "Your own domain"] ++++ + +With Apple increasing their focus[^1] on making the iPad a viable device for work, it is time to revisit using my iPad as a workstation for programming. I rely heavily on command-line tools and language-specific tools (rust-analyser, node, ghcide, etc.) for my day-to-day programming, and my current setup features: -- [Blink](https://blink.sh){target="_blank" rel="noopener noreferrer"} with mosh to a remote server. -- Neovim and [wemux](https://github.com/zolrath/wemux){target="_blank" rel="noopener noreferrer"} on the remote server. -- [iSH](https://ish.app){target="_blank" rel="noopener noreferrer"} to play around with very simple CLI needs locally on the iPad. +- [Blink](https://blink.sh) with mosh to a remote server. +- Neovim and [wemux](https://github.com/zolrath/wemux) on the remote server. +- [iSH](https://ish.app) to play around with very simple CLI needs locally on the iPad. On my computer, I use Visual Studio Code, and it's long been a wish to get that running somehow on my iPad. This is an attempt to make VS Code available on the iPad under the restrictions that we have to deal with. -
+
-
-
💡
+{% aside() %} This setup unfortunately doesn't eliminate the need for a server yet! We'll have to dream of that day to come. -
+{% end %} + +We'll go through the following: + +{{ toc() }} ## Enter code-server -[code-server](https://github.com/cdr/code-server){target="_blank" rel="noopener noreferrer"} enables you to run VS Code on a remote server, and access it through a browser. While not ideal, this is at least one way to get VS Code onto an iPad. +[code-server](https://github.com/cdr/code-server) enables you to run VS Code on a remote server, and access it through a browser. While not ideal, this is at least one way to get VS Code onto an iPad. First, SSH into your server, so that we can set up `code-server`, @@ -41,7 +42,7 @@ Neat! 🙂 This was previously multiple steps, but code-server's recent addition ## Securing the setup for remote access So far, `code-server` is only listening for local connections, but we'd like to be able to use it on the go, from a browser on the iPad. This means we have to do a little extra work to secure our setup. -`code-server` covers how to do this [in their FAQ](https://github.com/cdr/code-server/blob/master/doc/FAQ.md#how-should-i-expose-code-server-to-the-internet){target="_blank" rel="noopener noreferrer"}, but skips the specific steps. Due to an issue with self-signed certificates on iOS, we cannot use these^[See issue [code-server#1122](https://github.com/cdr/code-server/issues/1122){target="_blank" rel="noopener noreferrer"} covering this.], so instead we will opt for [letsencrypt](https://letsencrypt.org){target="_blank" rel="noopener noreferrer"}! +`code-server` covers how to do this [in their FAQ](https://github.com/cdr/code-server/blob/master/doc/FAQ.md#how-should-i-expose-code-server-to-the-internet), but skips the specific steps. Due to an issue with self-signed certificates on iOS, we cannot use these^[See issue [code-server#1122](https://github.com/cdr/code-server/issues/1122) covering this.], so instead we will opt for [letsencrypt](https://letsencrypt.org)! +
**Table of Contents:** @@ -45,9 +46,9 @@ First, we will set up our data structures and a few examples, which we will use **TypeScript** -Let us first set up our reference Object in TypeScript. Save the following in `house.ts` (or check out [typescript-json](https://github.com/codetalkio/codetalk.io/tree/master/resources/code/common-json-patterns/typescript-json){target="_blank" rel="noopener noreferrer"}): +Let us first set up our reference Object in TypeScript. Save the following in `house.ts` (or check out [typescript-json](https://github.com/codetalkio/codethoughts.io/tree/master/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json)): -```typescript +```typescript ,linenos interface Address { country: string; address: string; @@ -86,13 +87,13 @@ The included snippet serves to give you an idea of the data structures, types, a You can find the setup for each specific solution in: -- [haskell-lens](https://github.com/codetalkio/codetalk.io/tree/master/resources/code/common-json-patterns/haskell-lens){target="_blank" rel="noopener noreferrer"}: Contains the Lens apporach. -- [haskell-record-dot](https://github.com/codetalkio/codetalk.io/tree/master/resources/code/common-json-patterns/haskell-record-dot){target="_blank" rel="noopener noreferrer"}: Contains the Record Dot Syntax apporach. +- [haskell-lens](https://github.com/codetalkio/codethoughts.io/tree/master/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/haskell-lens): Contains the Lens apporach. +- [haskell-record-dot](https://github.com/codetalkio/codethoughts.io/tree/master/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/haskell-record-dot): Contains the Record Dot Syntax apporach. Check out `src/House.hs` for the data structures, and `src/Main.hs` for all the examples throughout this post. -```haskell +```haskell ,linenos data Address = Address { country :: String , address :: String @@ -129,15 +130,15 @@ house = Household son = Person { id = 3, firstname = "Eric", lastname = "Swanson" } ``` -To allow overlapping record fields, we use [DuplicateRecordFields](https://github.com/adamgundry/ghc-proposals/blob/overloaded-record-fields/proposals/0000-overloaded-record-fields.rst#recap-duplicaterecordfields){target="_blank" rel="noopener noreferrer"} along with [OverloadedLabels](https://github.com/adamgundry/ghc-proposals/blob/overloaded-record-fields/proposals/0000-overloaded-record-fields.rst#recap-overloadedlabels){target="_blank" rel="noopener noreferrer"} (only in the Lens version), and a bunch of other extensions for deriving things via generics. +To allow overlapping record fields, we use [DuplicateRecordFields](https://github.com/adamgundry/ghc-proposals/blob/overloaded-record-fields/proposals/0000-overloaded-record-fields.rst#recap-duplicaterecordfields) along with [OverloadedLabels](https://github.com/adamgundry/ghc-proposals/blob/overloaded-record-fields/proposals/0000-overloaded-record-fields.rst#recap-overloadedlabels) (only in the Lens version), and a bunch of other extensions for deriving things via generics. We control the details of the JSON serialization / deserialization using the [derive-aeson](https://hackage.haskell.org/package/deriving-aeson) package + the [`DerivingVia`](https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0120-deriving-via.rst) language extension. **Rust** -The full setup can be found in [rust-serde](https://github.com/codetalkio/codetalk.io/tree/master/resources/code/common-json-patterns/rust-serde){target="_blank" rel="noopener noreferrer"}. Check out `src/house.rs` for the data structures, and `src/main.rs` for all the examples throughout this post. +The full setup can be found in [rust-serde](https://github.com/codetalkio/codethoughts.io/tree/master/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/rust-serde). Check out `src/house.rs` for the data structures, and `src/main.rs` for all the examples throughout this post. -```rust +```rust ,linenos #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Address { pub country: String, @@ -183,10 +184,9 @@ pub fn house() -> Household { If you wish to follow along, you can fire up a REPL for each approach. For the TypeScript and Rust versions, where we utilize mutability, we will clone the objects each time, to keep them consistent across examples and in our REPL. -
-
💡
- In TypeScript this would more commonly be done using the spread operator, `...`, or using something like [`_.cloneDeep(value)`](https://lodash.com/docs/#cloneDeep){target="_blank" rel="noopener noreferrer"}. -
+{% aside() %} + In TypeScript this would more commonly be done using the spread operator, `...`, or using something like [`_.cloneDeep(value)`](https://lodash.com/docs/#cloneDeep). +{% end %} **TypeScript** ```bash @@ -393,7 +393,7 @@ new_house.owner = new_ariel; --> Household { /* Full Household object... */ } ``` -Alternatively we could use Rust's [Struct Update syntax](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax){target="_blank" rel="noopener noreferrer"}, `..`, which works much like the spread syntax (`...`) in JavaScript. It would look something like `Household { owner: new_ariel, ..house }`. +Alternatively we could use Rust's [Struct Update syntax](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax), `..`, which works much like the spread syntax (`...`) in JavaScript. It would look something like `Household { owner: new_ariel, ..house }`. ### Set a nested field Now it gets a bit more tricky. @@ -419,7 +419,7 @@ house{ owner.firstname = "New Ariel"} --> Household { {- Full Household object... -} } ``` -Note that the lack of spacing in `house{` is actually important, at least in the current state of [RecordDotSyntax](https://github.com/shayne-fletcher-da/ghc-proposals/blob/record-dot-syntax/proposals/0000-record-dot-syntax.md#3-examples){target="_blank" rel="noopener noreferrer"}. +Note that the lack of spacing in `house{` is actually important, at least in the current state of [RecordDotSyntax](https://github.com/shayne-fletcher-da/ghc-proposals/blob/record-dot-syntax/proposals/0000-record-dot-syntax.md#3-examples). **Rust:** ```rust @@ -556,8 +556,6 @@ Like with Haskell, we let Rust know what data type we are trying to convert our
---- - Have other common patterns you'd like to see? Feel like some of the approaches could be improved? Leave a comment, and I will try to expand this list to be more comprehensive! ## Changelog @@ -574,12 +572,12 @@ Thanks to all the feedback from the [/r/rust](https://www.reddit.com/r/rust/comm - Switched the Rust approaches to use mutations instead of the unidiomatic immutable style it was written in. -[^moreOptions]: There are of course more options, like [Optics](https://www.well-typed.com/blog/2019/09/announcing-the-optics-library/){target="_blank" rel="noopener noreferrer"} ([usage example](https://www.reddit.com/r/haskell/comments/cyo4o2/welltyped_announcing_the_optics_library/eywc9ya?utm_source=share&utm_medium=web2x){target="_blank" rel="noopener noreferrer"}), but I won't cover them all here. +[^derivingAeson]: Along with [aeson](https://hackage.haskell.org/package/aeson), we will use the new [deriving-aeson](https://hackage.haskell.org/package/deriving-aeson) library to derive our instances. -[^derivingAeson]: Along with [aeson](https://hackage.haskell.org/package/aeson){target="_blank" rel="noopener noreferrer"}, we will use the new [deriving-aeson](https://hackage.haskell.org/package/deriving-aeson){target="_blank" rel="noopener noreferrer"} library to derive our instances. +[^moreOptions]: There are of course more options, like [Optics](https://www.well-typed.com/blog/2019/09/announcing-the-optics-library/) ([usage example](https://www.reddit.com/r/haskell/comments/cyo4o2/welltyped_announcing_the_optics_library/eywc9ya?utm_source=share&utm_medium=web2x)), but I won't cover them all here. -[^genericLens]: We use [generic-lens](https://github.com/kcsongor/generic-lens){target="_blank" rel="noopener noreferrer"} for Lens derivations instead of TemplateHaskell. +[^genericLens]: We use [generic-lens](https://github.com/kcsongor/generic-lens) for Lens derivations instead of TemplateHaskell. -[^recordDotSyntax]: It will take a bit of time before it is merged and available in GHC, so we will use the [record-dot-preprocessor](https://hackage.haskell.org/package/record-dot-preprocessor){target="_blank" rel="noopener noreferrer"} plugin to get a sneak peak. +[^recordDotSyntax]: It will take a bit of time before it is merged and available in GHC, so we will use the [record-dot-preprocessor](https://hackage.haskell.org/package/record-dot-preprocessor) plugin to get a sneak peak. [^dataMaybe]: `maybe` from Data.Maybe has the type signature `maybe :: b -> (a -> b) -> Maybe a -> b`, taking in as argument (1) a default value (2) a function to run if the value is `Just` and (3) the `Maybe` value we want to operate on. diff --git a/resources/code/common-json-patterns/rust-serde/Cargo.lock b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/rust-serde/Cargo.lock similarity index 100% rename from resources/code/common-json-patterns/rust-serde/Cargo.lock rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/rust-serde/Cargo.lock diff --git a/resources/code/common-json-patterns/rust-serde/Cargo.toml b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/rust-serde/Cargo.toml similarity index 100% rename from resources/code/common-json-patterns/rust-serde/Cargo.toml rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/rust-serde/Cargo.toml diff --git a/resources/code/common-json-patterns/rust-serde/src/house.rs b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/rust-serde/src/house.rs similarity index 100% rename from resources/code/common-json-patterns/rust-serde/src/house.rs rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/rust-serde/src/house.rs diff --git a/resources/code/common-json-patterns/rust-serde/src/main.rs b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/rust-serde/src/main.rs similarity index 100% rename from resources/code/common-json-patterns/rust-serde/src/main.rs rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/rust-serde/src/main.rs diff --git a/resources/code/common-json-patterns/typescript-json/.node-version b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/.node-version similarity index 100% rename from resources/code/common-json-patterns/typescript-json/.node-version rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/.node-version diff --git a/resources/code/common-json-patterns/typescript-json/.vscode/settings.json b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/.vscode/settings.json similarity index 100% rename from resources/code/common-json-patterns/typescript-json/.vscode/settings.json rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/.vscode/settings.json diff --git a/resources/code/common-json-patterns/typescript-json/house.ts b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/house.ts similarity index 100% rename from resources/code/common-json-patterns/typescript-json/house.ts rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/house.ts diff --git a/resources/code/common-json-patterns/typescript-json/main.ts b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/main.ts similarity index 100% rename from resources/code/common-json-patterns/typescript-json/main.ts rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/main.ts diff --git a/resources/code/common-json-patterns/typescript-json/package-lock.json b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/package-lock.json similarity index 100% rename from resources/code/common-json-patterns/typescript-json/package-lock.json rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/package-lock.json diff --git a/resources/code/common-json-patterns/typescript-json/package.json b/content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/package.json similarity index 100% rename from resources/code/common-json-patterns/typescript-json/package.json rename to content/posts/2020-04-05-common-json-patterns-in-haskell-rust-and-javascript/typescript-json/package.json diff --git a/posts/2023-01-29-the-stack.md b/content/posts/2023-01-29-the-stack.md similarity index 95% rename from posts/2023-01-29-the-stack.md rename to content/posts/2023-01-29-the-stack.md index bdc1d39..f8175cb 100644 --- a/posts/2023-01-29-the-stack.md +++ b/content/posts/2023-01-29-the-stack.md @@ -1,8 +1,9 @@ ---- -title: "\"The Stack\": Everything you'll need" -tags: aws, cloud, infrastructure, cdk, rust -date: 2023-01-29 ---- ++++ +title = "\"The Stack\": Everything you'll need" +date = 2023-01-29 +[taxonomies] +tags = ["aws", "cloud", "infrastructure", "cdk", "rust"] ++++ This will be a series of blog posts where we will build up the perfect infrastructure setup for the majority of usecase, aka "The Stack". We'll be building everything on top of AWS. @@ -23,11 +24,19 @@ Some technology choices upfront: - We'll be using [Rust](https://www.rust-lang.org) throughout for each service, as it allows us to squeeze out the most performance while still giving us a nice developer experience. - [Federated GraphQL](https://www.apollographql.com/docs/federation/) will be our way of facilitating microservices. -
+
-# What will we be covering? +Here's an overview before we get into the details: -**You can see a sneak peak of the final setup below**. All of this will be covered in parts: +{{ toc() }} + +## What will we be covering? + +{% aside() %} + You can see a sneak peak of the final setup below. +{% end %} + +All of this will be covered in parts: - [Part 0: The introduction and goals (this post)](#what-will-we-be-covering) - The goals of "The Stack" and architecture overview @@ -62,7 +71,7 @@ Some technology choices upfront: - Part 15 (Bonus): Billing breakdown - Forming an overview of service costs via Billing Tags and the Cost Explorer -# Account Structure and Governance +## Account Structure and Governance Reorganizing your AWS Account structure is a pain, so let's see if we can get this right from the beginning. There are a few things that direct our choices here: @@ -131,7 +140,7 @@ graph TD class ControlTower,AWSProdMultiTenantAccount,AWSProdSingleTenantAccount,AWSIntegrationTestAccount,AWSPreviewAccount,AWSIndividualDeveloperAccount,AWSMonitoringAccount,AWSLogsAccount container; -# Service Infrastructure +## Service Infrastructure Each of the infrastructure accounts (Production, Integration, Developer) all hold the same services and follow the same setup. The infrastructure we will make might seem complex at first, and it is, but as we go through each piece everything will start to make sense. @@ -142,7 +151,7 @@ The diagram gets quite large, so we will split it up into three parts: - Asynchronous Work and Media -#### Client to Frontend +### Client to Frontend Let's focus first on the Client to Frontend paths: @@ -192,7 +201,7 @@ graph TD -#### Client to API +### Client to API As we can see, the two Frontends need something to talk to, let's check out the APIs: @@ -249,7 +258,7 @@ graph TD class Certificate,Frontend,Internal,API,Media,Database,Notification,Async,Monitoring container; -#### Asynchronous Work and Media +### Asynchronous Work and Media And finally we can see the Media and Async work (the Database and some APIs reappear here as well): @@ -309,7 +318,7 @@ graph TD -#### One diagram to rule them all +### One diagram to rule them all If we combine all the individual diagrams, we get: @@ -412,6 +421,6 @@ graph TD -# Next Steps +## Next Steps -Next up is to start building! Follow along in [Part 1 of the series here](/posts/2023-01-29-the-stack-part-1.html). +Next up is to start building! Follow along in [Part 1 of the series here](@/posts/2023-10-07-the-stack-part-1/index.md). diff --git a/posts/2023-10-07-the-stack-part-1.md b/content/posts/2023-10-07-the-stack-part-1/index.md similarity index 86% rename from posts/2023-10-07-the-stack-part-1.md rename to content/posts/2023-10-07-the-stack-part-1/index.md index a6ef950..d06496a 100644 --- a/posts/2023-10-07-the-stack-part-1.md +++ b/content/posts/2023-10-07-the-stack-part-1/index.md @@ -1,22 +1,13 @@ ---- -title: "The Stack Part 1: Setting up your AWS Account Structure" -tags: aws, cloud, infrastructure -date: 2023-10-07 ---- ++++ +title = "The Stack Part 1: Setting up your AWS Account Structure" +date = 2023-10-07 +[taxonomies] +tags = ["aws", "cloud", "infrastructure"] ++++ In [the last post](/posts/2023-01-29-the-stack.html) we went over the overall goals of "The Stack" and what we will be building. In this post we'll be setting up our AWS Account structure. See the full overview of posts [here](/posts/2023-01-29-the-stack.html#what-will-we-be-covering). -- [Set up Control Tower](#set-up-control-tower) - - [Step 1](#step-1) - - [Step 2](#step-2) - - [Step 3](#step-3) - - [Step 4](#step-4) - - [Step 5](#step-5) - - [Cleanup](#cleanup) -- [Adjusting Account Factory defaults](#adjusting-account-factory-defaults) -- [Set up our Development Organizational Unit](#set-up-our-development-organizational-unit) -- [Set up our AWS Accounts](#set-up-our-aws-accounts) -- [Next Steps](#next-steps) +{{ toc() }} As a reminder, here is the structure we are aiming for: @@ -29,7 +20,7 @@ As a reminder, here is the structure we are aiming for: - **Monitoring**: Centralize monitoring and observability into one account, allowing access to insights without access to sensitive logs or infrastructure from the other accounts - **Logs**: Centralized storage of logs, which may require different access considerations than metrics and traces -
+
Or, for the visual learners: @@ -147,9 +138,7 @@ We'll immediately start our good habit of adding billing tags whereever we can,
👈 Open the toggle for an example -
-KMS Configuration -
+{{ image(path="the-stack-part-1-kms-step-1.png", caption="KMS Configuration", width=600) }}
@@ -157,9 +146,7 @@ Skip through Step 3, Step 4, and click "Finish" on the review step. Once the key is created we'll immediately edit it now that it has gotten a Key ID. If we don't we'll run into this nice error later on: -
-KMS error -
+{{ image(path="the-stack-part-1-kms-error.png", caption="KMS error", width=650) }} To avoid this: @@ -172,7 +159,8 @@ To avoid this: Replace `AWS_REGION`, `AWS_ACCOUNT_ID`, and `KMS_KEY_ID` and insert the following: -```json + +```json ,linenos { "Sid": "Allow Config to use KMS for encryption", "Effect": "Allow", @@ -221,10 +209,9 @@ Finally, we'll also cleanup the VPCs that were created in our Control Tower: 2. Click on **Your VPCs** in the menu on the left, and click on your Control Tower VPC. 3. Choose **Actions** and then choose **Delete VPC** as well as confirming the choice. -
-
💡
+{% aside() %} If you missed the KMS policy adjustment in Step 4 and the creation fails, you can go into CloudFormation and delete the stack called `AWSControlTowerBP-BASELINE-CLOUDTRAIL-MASTER`. Once that is done, you can go back to the Control Tower console and click "Retry" to try again. -
+{% end %} ## Adjusting Account Factory defaults @@ -259,9 +246,7 @@ Find the User portal URL: 3. Copy the **User portal URL** from the **Federated access management** card. 4. Bookmark this URL, you're gonna need it a lot. -
-User portal URL -
+{{ image(path="the-stack-part-1-user-portal-url.png", caption="User portal URL", width=600) }} Once you're logged into the Control Tower account using the portal, jump into the Account Factory again: @@ -290,10 +275,8 @@ Pick the appropriate Organizational unit according to the list above, and click You should end up with an overall structure like the following (Preview environment missing, but should be in the Development OU): -
-Account overview -
+{{ image(path="the-stack-part-1-account-overview.png", caption="Account overview", width=300) }} ## Next Steps -Next up, we will be looking at how we can set up and automate our deployments to these environments so that once we start building it will update automatically. Follow along in [Part 2 of the series](/posts/2023-01-29-the-stack-part-2.html). +Next up, we will be looking at how we can set up and automate our deployments to these environments so that once we start building it will update automatically. Follow along in [Part 2 of the series](@/posts/2023-10-08-the-stack-part-2/index.md). diff --git a/resources/images/the-stack-part-1-account-overview 2.png b/content/posts/2023-10-07-the-stack-part-1/the-stack-part-1-account-overview.png similarity index 100% rename from resources/images/the-stack-part-1-account-overview 2.png rename to content/posts/2023-10-07-the-stack-part-1/the-stack-part-1-account-overview.png diff --git a/resources/images/the-stack-part-1-kms-error.png b/content/posts/2023-10-07-the-stack-part-1/the-stack-part-1-kms-error.png similarity index 100% rename from resources/images/the-stack-part-1-kms-error.png rename to content/posts/2023-10-07-the-stack-part-1/the-stack-part-1-kms-error.png diff --git a/resources/images/the-stack-part-1-kms-step-1.png b/content/posts/2023-10-07-the-stack-part-1/the-stack-part-1-kms-step-1.png similarity index 100% rename from resources/images/the-stack-part-1-kms-step-1.png rename to content/posts/2023-10-07-the-stack-part-1/the-stack-part-1-kms-step-1.png diff --git a/resources/images/the-stack-part-1-user-portal-url 2.png b/content/posts/2023-10-07-the-stack-part-1/the-stack-part-1-user-portal-url.png similarity index 100% rename from resources/images/the-stack-part-1-user-portal-url 2.png rename to content/posts/2023-10-07-the-stack-part-1/the-stack-part-1-user-portal-url.png diff --git a/posts/2023-10-07-the-stack-part-2.md b/content/posts/2023-10-08-the-stack-part-2/index.md similarity index 89% rename from posts/2023-10-07-the-stack-part-2.md rename to content/posts/2023-10-08-the-stack-part-2/index.md index 9f237bc..9b5ace8 100644 --- a/posts/2023-10-07-the-stack-part-2.md +++ b/content/posts/2023-10-08-the-stack-part-2/index.md @@ -1,8 +1,9 @@ ---- -title: "The Stack Part 2: Automating Deployments via CI" -tags: aws, cloud, infrastructure, cdk, ci -date: 2023-10-07 ---- ++++ +title = "The Stack Part 2: Automating Deployments via CI" +date = 2023-10-08 +[taxonomies] +tags = ["aws", "cloud", "infrastructure", "cdk", "ci"] ++++ In [the last post](/posts/2023-10-07-the-stack-part-1.html) we created our Control Tower structure with all of our AWS Accounts in it. In this post we will be automating our deployment process for each of these environments. See the full overview of posts [here](/posts/2023-01-29-the-stack.html#what-will-we-be-covering). @@ -17,19 +18,9 @@ If you want to jump straight to the code, you can find it in the [GitHub reposit Otherwise, let's jump in! -
+
-- [AWS: Seting up Credentials](#aws-seting-up-credentials) -- [GitHub: Setting up Environments](#github-setting-up-environments) -- [CDK: Infrastructure as Code](#cdk-infrastructure-as-code) -- [Automated Deployments via GitHub Actions](#automated-deployments-via-github-actions) - - [Boostrap Workflow](#boostrap-workflow) - - [Deployment Workflow](#deployment-workflow) - - [Trigger the Workflows](#trigger-the-workflows) -- [Manual alternative: Bootstrapping our Accounts](#manual-alternative-bootstrapping-our-accounts) -- [Manual alternative: Deployments](#manual-alternative-deployments) -- [Bonus: Justfile and just](#bonus-justfile-and-just) -- [Next Steps](#next-steps) +{{ toc() }} ## AWS: Seting up Credentials @@ -42,10 +33,9 @@ We will need to set up credentials for our GitHub Actions to be able to deploy t For each of these we will need to set up IAM credentials that we can use in GitHub Actions to deploy our resources into each of these accounts. -
-
💡
+{% aside() %} GitHub also supports authenticating to AWS via OpenID. Check out their docs on that [here](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services). -
+{% end %} Let's get set up. First we'll define a group for the user to go into, create the user, and then create the access keys for the user: @@ -86,15 +76,11 @@ Repeat those steps with the relevant values for each of the environments `Integr Your `Environment` overview will look like this: -
-Overview of Environments -
+{{ image(path="the-stack-part-2-environment-overview.png", caption="Overview of Environments", width=600) }} And each environment will roughly look like this: -
-Configuration, secrets, and variables of an environment -
+{{ image(path="the-stack-part-2-environment-configuration.png", caption="Configuration, secrets, and variables of an environment", width=600) }} ## CDK: Infrastructure as Code @@ -128,7 +114,8 @@ In `deployment.ts` you'll see the root of our CDK stack. This is where we will d For now, we will only define the `Global` layer: -```typescript + +```typescript ,linenos // ...imports const app = new cdk.App(); @@ -160,7 +147,8 @@ Our `GlobalStack` is then defined in `lib/global/stack.ts`, and more or less jus The interesting bit here is the call to `new domain.Stack` which is what actually kicks off the provisioning of resources, which are defined inside the `lib/global/domain.ts` file on layer deeper: -```typescript + +```typescript ,linenos // ...imports interface StackProps extends cdk.StackProps, domain.StackProps {} @@ -183,7 +171,8 @@ We create our Hosted Zone via `new route53.HostedZone` and our ACM certificate v Let's get our resources defined: -```typescript + +```typescript ,linenos // ...imports export interface StackProps extends cdk.StackProps { /** @@ -235,7 +224,8 @@ The GitHub Actions YAML might feel a bit verbose, so let's break it down a bit. We first define our name and the trigger. Because we only want this to be triggered manually (bootstrapping just needs to run once) we can use `workflow_dispatch` which allows us to trigger it from the GitHub Actions UI ([docs here](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch)): -```yaml + +```yaml ,linenos name: "Deployment: Bootstrap" on: @@ -249,7 +239,8 @@ A neat way to "do the same thing" over a set of different things is to utilize t This is what the `strategy` section does, and it then feeds this into the `environment` which tells GitHub Actions which environment variables and secrets are available, as well as automatically tracks our deployments in the GitHub UI: -```yaml + +```yaml ,linenos # ... jobs: bootstrap: @@ -271,7 +262,8 @@ Now, what would happen if we ran this multiple times in parallel on the same env To prevent this, we can tell GitHub to only allow one job to run at a time, given a group identifier. We do this by adding a `concurrency` control to our workflow ([docs here](https://docs.github.com/en/actions/using-jobs/using-concurrency)): -```yaml + +```yaml ,linenos # ... # Limit to only one concurrent deployment per environment. concurrency: @@ -282,7 +274,8 @@ To prevent this, we can tell GitHub to only allow one job to run at a time, give And finally, we get to the actual steps of logic we are performing. First we'll checkout our code, set up bun, and then use bun to install our dependencies: -```yaml + +```yaml ,linenos # ... steps: - uses: actions/checkout@v3 @@ -298,7 +291,8 @@ Now we're ready to bootstrap! We use the variables and secrets we defined previo We pull in what we need, and then run the `cdk bootstrap` command via bun: -```yaml + +```yaml ,linenos # ... - name: Bootstrap account working-directory: ./deployment @@ -316,9 +310,7 @@ Our deployment flow gets a bit more complicated. We're building for the future h We will be building up the following flow, illustrated in the diagram below: -
-Deployment flow -
+{{ image(path="the-stack-part-2-deployment-flow.png", caption="Deployment flow", width=700) }} This is what is called a "staggered deployment", but across our environments: @@ -326,8 +318,8 @@ This is what is called a "staggered deployment", but across our environments: - We validate that our IaC actually works. 2. Once the deployment is done, we perform checks against it to validate the health of the deployment (e.g. End-to-End tests, smoke tests, etc) - We validate that our application actually works with our changes to our infrastructure. -4. If everything looks good, we proceed to **Stage 2** which deploys both our `Production Single-tenant` and `Production Multi-tenant` environments in parallel. -5. We do a final check that all is good, and then we're done! +3. If everything looks good, we proceed to **Stage 2** which deploys both our `Production Single-tenant` and `Production Multi-tenant` environments in parallel. +4. We do a final check that all is good, and then we're done! This helps us build confidence that our deployments work, since our aim is to deploy changes immediately as they are merged into our `main` branch. @@ -335,7 +327,8 @@ This helps us build confidence that our deployments work, since our aim is to de Let's take a look at how we do this. First, we'll set up our triggers. We want to both allow manually triggering a deployment, again via `workflow_dispatch`, but we also want to immediately deploy when changes are merged to our `main` branch: -```yaml + +```yaml ,linenos name: "Deployment: Deploy to AWS" on: @@ -348,7 +341,8 @@ on: All good and well, so let's start defining our jobs. Ignore the `uses` for now, that's a reuseable workflow which we'll get back to later in **Part 2** of this section: -```yaml + +```yaml ,linenos # ... jobs: # Stage 1 tests that the deployment is working correctly. @@ -365,7 +359,8 @@ We first initiate our **Stage 1** deployment, specifying that it's the `Integrat Next, we want to run our checks, but only after our **Stage 1** job has finished running successfully. To do this we use `needs` to define a dependency on a previous job ([docs here](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds)). -```yaml + +```yaml ,linenos # ... # Run tests against the environments from Stage 1. stage-1-check: @@ -385,7 +380,8 @@ We aren't doing much interesting for now in our test job, since we are only depl Similarly, we use `needs` again to specify how we move into **Stage 2**. We first set up `Production Single-tenant`: -```yaml + +```yaml ,linenos # ... # Stage 2 is our more critical environments, and only proceeds if prior stages succeed. stage-2-single: @@ -411,7 +407,8 @@ Similarly, we use `needs` again to specify how we move into **Stage 2**. We firs And do the same for our `Production Multi-tenant` environment: -```yaml + +```yaml ,linenos # ... stage-2-multi: name: "Stage 2: Multi-tenant" @@ -438,9 +435,8 @@ We could have been using using build `matrix`'s again, but that would mean that Voila 🎉 We've now set the skeleton for the deployment flow we pictured earlier: -
-Deployment flow -
+ +{{ image(path="the-stack-part-2-deployment-flow.png", caption="Deployment flow", width=700) }} **Part 2: Reuseable workflow** @@ -450,7 +446,8 @@ We've done this in our `wf-deploy.yml` workflow, which we use in our `stage-1`, First, we will need to define which inputs this workflow takes. Remember the `with` and `secrets` that we used earlier? That's how we pass information to our resuseable workflow. We define these in the `inputs` section: -```yaml + +```yaml ,linenos name: Deploy on: @@ -466,7 +463,8 @@ Here we simply specify that we require an `environment` to be passed along. We w We can now proceed to the logic, which resembles the `cd-bootstrap` workflow we looked at earlier. We first set up our environment, concurrency group, and then install our dependencies: -```yaml + +```yaml ,linenos # ... jobs: deploy: @@ -489,7 +487,8 @@ jobs: Before we proceed to actually deploying anything, we want to sanity check that our deployment looks valid. We do this by trying to first synthesize the whole deployment ([some info on synth here](https://docs.aws.amazon.com/cdk/v2/guide/hello_world.html)), and then run whatever test suite we have: -```yaml + +```yaml ,linenos # ... - name: Synthesize the whole stack working-directory: ./deployment @@ -513,7 +512,8 @@ Before we proceed to actually deploying anything, we want to sanity check that o Everything should now be good, so let's run our actual deployment: -```yaml + +```yaml ,linenos # ... - name: Deploy to ${{ inputs.environment }} working-directory: ./deployment @@ -534,9 +534,7 @@ Push your project to GitHub. You now have access to the workflows and can trigge If you haven't done it already, let's run the `Deployment: Bootstrap` workflow first, to set up CDK on all accounts. Alternatively, jump to the section [Manual alternative: Bootstrapping our Accounts](#manual-alternative-bootstrapping-our-accounts) for how to do this manually. -
-Manually trigger the bootstrap workflow -
+{{ image(path="the-stack-part-2-trigger-bootstrap-workflow.png", caption="Manually trigger the bootstrap workflow", width=700) }} Next up, before we initiate the deployment it's recommended to be logged into your Domain Registrar that controls the DNS of your domain, so that you can quickly update your name servers to point to the Hosted Zone that we will be creating. This is necessary to DNS validate our ACM certificates. @@ -559,9 +557,8 @@ This can easily take 5-15 minutes to complete the first time, depending on how q You can go and see the generated CloudFormation stacks in the [AWS Console -> CloudFormation](https://console.aws.amazon.com/cloudformation/) which will look something like this: -
-Cloudformation stacks -
+ +{{ image(path="the-stack-part-2-cloudformation.png", caption="Cloudformation stacks", width=600) }} We've now set up the foundation for all of our future deployments of applications and services 🥳 @@ -635,7 +632,8 @@ $ curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s This allows us to set up a `justfile` in the root of our project, which we can then use to define shortcuts to our commands. For example, we can define a shortcut to run our CDK commands: -```makefile + +```makefile ,linenos # Display help information. help: @ just --list @@ -691,4 +689,4 @@ $ just deploy # or just deploy Global ## Next Steps -Next up is to add our first Frontend! Follow along in [Part 3 of the series](/posts/2023-10-16-the-stack-part-3.html). +Next up is to add our first Frontend! Follow along in [Part 3 of the series](@/posts/2023-10-16-the-stack-part-3/index.md). diff --git a/resources/images/the-stack-part-2-cloudformation.png b/content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-cloudformation.png similarity index 100% rename from resources/images/the-stack-part-2-cloudformation.png rename to content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-cloudformation.png diff --git a/resources/images/the-stack-part-2-deployment-flow.png b/content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-deployment-flow.png similarity index 100% rename from resources/images/the-stack-part-2-deployment-flow.png rename to content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-deployment-flow.png diff --git a/resources/images/the-stack-part-2-environment-configuration.png b/content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-environment-configuration.png similarity index 100% rename from resources/images/the-stack-part-2-environment-configuration.png rename to content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-environment-configuration.png diff --git a/resources/images/the-stack-part-2-environment-overview.png b/content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-environment-overview.png similarity index 100% rename from resources/images/the-stack-part-2-environment-overview.png rename to content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-environment-overview.png diff --git a/resources/images/the-stack-part-2-trigger-bootstrap-workflow.png b/content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-trigger-bootstrap-workflow.png similarity index 100% rename from resources/images/the-stack-part-2-trigger-bootstrap-workflow.png rename to content/posts/2023-10-08-the-stack-part-2/the-stack-part-2-trigger-bootstrap-workflow.png diff --git a/content/posts/2023-10-16-the-stack-part-3/index.md b/content/posts/2023-10-16-the-stack-part-3/index.md new file mode 100644 index 0000000..5c757c4 --- /dev/null +++ b/content/posts/2023-10-16-the-stack-part-3/index.md @@ -0,0 +1,1881 @@ ++++ +title = "The Stack Part 3: Building a Frontend" +date = 2023-10-16 +[taxonomies] +tags = ["rust", "typescript", "wasm", "nextjs", "leptos", "aws", "cloud", "infrastructure", "cdk", "ci"] ++++ + +In [the last post](/posts/2023-10-07-the-stack-part-2.html) we set up our deployment, fully automated on merge to our `main` branch. In this post we will be building our UI (Frontend) applications. See the full overview of posts [here](/posts/2023-01-29-the-stack.html#what-will-we-be-covering). + +At the end of this post we will have: + +- A [Next.js](https://nextjs.org/) Frontend app with support for localization, using Tailwind CSS. +- A [Leptos](https://github.com/leptos-rs/leptos) Rust/WASM Frontend app with support for localization, using Tailwind CSS. +- Automatic deployment of our Apps AWS using CDK, statically hosted using S3 + CloudFront. + +We are essentially hedging our bets by building both a JavaScript-based Frontend, which is the safe bet, and a Rust/WASM-based Frontend, which is the future bet. We will be using the same GraphQL API for both, so we can easily switch between them. + +There is quite a lot to cover. My recommendation is to clone down the Part 3 branch in the [GitHub repository](https://github.com/codetalkio/the-stack/tree/part-3-frontend) and use this post as an explanation of what is set up. + +
+ +{{ toc() }} + + +## Prelude: Static Site Generation + +While SSR (Server Side Rendering) is seeing a renaissance these days, we are intentionally avoiding this functionality. If you remember the architecture outlined in [our introduction to the series](/posts/2023-01-29-the-stack.html#what-will-we-be-covering), we want to be able to serve our Frontend entirely using static file hosting. + +There are multiple reasons for this, going back to our core design goals: + +- **Low cost**: Static file hosting is extremely cheap, and scales as well as S3/CloudFront does (i.e. *very well*). +- **Low operational overhead**: Static file hosting is extremely simple to operate—there are no servers for us to worry about and scale up/down as needed, no need for any orchestration yet. +- **Performant**: Static file hosting is extremely performant, as the files are served directly from the edge, and not from a server. + +We do sacrifice the ability to do SSR and the various User Experiences that can potentially bring, but the benefits are far outweighed by the downsides in this tradeoff. + +## Next.js + +Next.js is one of the most popular React frameworks at the moment, and supports a lot of niceties as well as sets us on a path of a good structure from the get-go. It has a lot of momentum behind it, a strong focus on the Developer Experience, and uses React which makes it a very familiar option for a large majority of Frontend Engineers out there. + +We'll once again use [Bun](https://bun.sh/), which you can install via: + +```bash +$ curl -fsSL https://bun.sh/install | bash +``` + +While [bun unfortunately doesn't fully support the Next.js App Router yet](https://bun.sh/guides/ecosystem/nextjs) we will still rely on it for installing dependencies and being our general go-to tool for running anything JS related. + +#### Setting up our Next.js App + +Let's get our Next.js app set up, which we will call `ui-app`: + +```bash +$ bun create next-app # Call it ui-app +✔ What is your project named? … ui-app +✔ Would you like to use TypeScript? … Yes +✔ Would you like to use ESLint? … Yes +✔ Would you like to use Tailwind CSS? … Yes +✔ Would you like to use `src/` directory? … Yes +✔ Would you like to use App Router? (recommended) … Yes +✔ Would you like to customize the default import alias (@/*)? … No +``` + +This gets us quite far, we now have an App we can run, Tailwind CSS is already set up, and we got a lot of the structure set up for us: + +```bash +$ cd ui-app +$ bun run dev # or bun run build +next dev + ▲ Next.js 13.5.4 + - Local: http://localhost:3000 + + ✓ Ready in 3s +``` + +Voila, we've got a little Hello World Next.js app! + +#### Building Static Files + +We need to do just one small change to our Next.js setup to make it output static files for us. We'll do this by adding `output: "export"` to our `next.config.js` file at the root of `ui-app/`: + + +```typescript ,linenos +// @ts-check + +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: "export", + trailingSlash: true, + experimental: { + // Statically type links to prevent typos and other errors when using next/link, improving type safety when navigating between pages. + typedRoutes: true, + }, +}; + +module.exports = nextConfig; +``` + +We also enabled `trailingSlash` ([docs here](https://nextjs.org/docs/pages/api-reference/next-config-js/trailingSlash)) to make Next.js work nicely with CloudFront. + +This tells Next.js that we want to to [Statically Export](https://nextjs.org/docs/app/building-your-application/deploying/static-exports) our files, which will generate an HTML file per route, which allows each route to serve the minimal content it needs, enabling faster page loads, instead of the traditional SPA approach of serving one large file upfront. + +We get the best of both worlds here, as we still get the reduced bundle sizes typical of SSR, but can retain the static file advantage of SPAs. + +#### Setting up Localization + +As our customer-base grows, we will inevitably run into the need to localization. To do this, we will restructure our App with basic support for this, as well as bring in a dependency to help us with this, namely [next-intl](https://www.npmjs.com/package/next-intl). Vercel also has some good documentation on how to [Get Started](https://next-intl-docs.vercel.app/docs/getting-started/app-router-client-components) here. + +Let's start by adding the dependency: + +```bash +$ bun add next-intl +``` + +We'll also create a folder that will contain our localization files, which we'll call `messages/` in the root of the `ui-app/` project: + +```bash +$ mkdir messages +``` + +This allows us to set up some text for our first languages. Create an English locale, `messages/en.json`, with the following: + + +```json ,linenos +{ + "home": { + "intro": "Welcome!" + } +} +``` + +And also a French locale, in `messages/fr.json`: + + +```json ,linenos +{ + "home": { + "intro": "Bienvenue!" + } +} +``` + +To make this a bit nicer to work with, we'll also [add typesafety](https://next-intl-docs.vercel.app/docs/workflows/typescript) by letting TypeScript know what keys we support in our localization function. Create a `ui-app/global.d.ts` file with the following: + + +```typescript ,linenos +// Use type safe message keys with `next-intl`, based on the contents/keys of +// our default english locale. +type Messages = typeof import("./messages/en.json"); +declare interface IntlMessages extends Messages {} +``` + +This ensures that if we misspell a key, or even remove one later on, we will be highlighted of the incorrect usage by TypeScript. + +We can now set up a route using the App Router to pick up our locale. We'll want the locale to be part of our URL as a prefix on all routes, so that we can pick it up as a [dynamic segment](https://nextjs.org/docs/app/building-your-application/routing/defining-routes#creating-routes) and use it to load the correct localization file. + +First we will create a folder where our localized pages will live in, and also clean up the default files that Next.js created for us: + +```bash +$ mkdir "src/app/[locale]" +$ rm src/app/page.tsx src/app/layout.tsx +``` + +Let's create a simply page in here at `src/app/[locale]/page.tsx`, and get our welcome text from the localization file: + + +```typescript ,linenos +"use client"; + +import { useTranslations } from "next-intl"; + +export default function Home() { + const t = useTranslations("home"); + + return ( + // Name="grid place-content-center content-center h-screen"> +
{t("intro")} +

+
Go to fr + Go to en +
+

+ ); +} +``` + +We'll need to mark the component as `'use client'` for now, while [next-intl is working on server-side support](https://next-intl-docs.vercel.app/docs/getting-started/app-router-server-components). + +Since we removed existing layout file, we need to define a new one that also handles setting up our localization at the root of our components. We'll create a `src/app/[locale]/layout.tsx` file with the following: + + +```typescript ,linenos +import "../globals.css"; +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; +import { NextIntlClientProvider } from "next-intl"; +import { notFound } from "next/navigation"; +import { ReactNode } from 'react'; +import { promises as fs } from 'fs'; + +export const metadata: Metadata = { + title: "Hello, World!", + description: "Ready to set things up", +} + +const inter = Inter({ subsets: ["latin"] }); + +/** + * Set up all supported locales as static parameters, which ensures we generate + * static pages for each possible value of `locale`. + */ +export async function generateStaticParams() { + // Construct an array of all supported locales based on the files in the `messages/` directory. + const localeFiles = await fs.readdir(`${process.cwd()}/messages`); + return localeFiles.map((f) => ({locale: f.replace(/\.json$/, "")})); +} + +/** + * Load the contents of a given locale's messages file. + */ +async function messagesContent(locale: string) { + try { + return (await import(`../../../messages/${locale}.json`)).default; + } catch (error) { + console.error("Something went wrong", error); + notFound(); + } +} + +type Props = { + children: ReactNode; + params: { + locale: string; + }; +}; + +export default async function Layout({ children, params: { locale } }: Props) { + const messages = await messagesContent(locale); + return ( + + // Name={inter.className}> + + {children} + + + + ); +} +``` + +To avoid needing to maintain a hardcoded list of locales, we dynamically find all the locales defined in our `messages/` folder during build time, and construct a list of supported locales from this. We then pass the contents of this into `NextIntlClientProvider`. + +We could imagine later on, once our translation file becomes massive, that we split up `en.json` into smaller segments such as `en/home.json`, and load these parts specifically in a `app/[locale]/home/layout.tsx` file. For now though, we'll keep it simple. + +As the final piece of this puzzle, we need a way to let Next.js know where it should route to by default, since we removed the default root pages. + +We unfortunately cannot use [middlewares](https://next-intl-docs.vercel.app/docs/routing/middleware) when statically exporting our site, so we will instead redirect the user upon loading the page. Create a `src/app/page.tsx` file with the following: + + +```typescript ,linenos +import { redirect } from "next/navigation"; + +export default function Root() { + redirect("/en"); +} +``` + +Along with a root layout file at `src/app/layout.tsx`: + +```typescript +import { ReactNode } from "react"; + +type Props = { + children: ReactNode; +}; + +export default function Layout({ children }: Props) { + return children; +} +``` + +You should now have a structure that looks like this: + +```bash +. +├── README.md +├── bun.lockb +├── global.d.ts +├── messages +│   └── en.json +│   └── fr.json +├── next.config.js +├── package.json +├── postcss.config.js +├── public +│   └── favicon.ico +├── src +│   └── app +│   ├── [locale] +│   │   ├── layout.tsx +│   │   └── page.tsx +│   ├── favicon.ico +│   ├── globals.css +│   ├── layout.tsx +│   └── page.tsx +├── tailwind.config.ts +└── tsconfig.json +``` + +And that's it! We're now able to run our app and check it out in the browser: + +```bash +$ bun run dev +``` + +{{ image(path="the-stack-part-3-ui-app-skeleton.png", caption="Screenshot of our ui-app", width=500) }} + +It may not look like much, but we've implemented a lot of the core functionality we need to get started, such as static builds and localization. + +As the final step we will add our commands to just, [extending our existing justfile](/posts/2023-10-07-the-stack-part-2.html#bonus-just): + + +```makefile ,linenos +_setup-ui-app: + #!/usr/bin/env bash + set -euxo pipefail + cd ui-app + bun install +``` + +We'll also set up a new command for running our development server: + + +```makefile ,linenos +# Run development server, e.g. `just dev ui-app`. +dev project: + just _dev-{{project}} + +_dev-ui-app: + #!/usr/bin/env bash + set -euxo pipefail + cd ui-app + bun dev +``` + +## Leptos (Rust/WASM) + +Leptos is one of the newer entries on the Rust/WASM scene. It has a radical focus on performance and scalability of your codebase, so that as your codebase grows your App doesn't just start to become slower which is a typical issue in React and VDOM-based frameworks. + +Leptos should feel somewhat familiar, although it is more closely related to something like [Solid.js](https://www.solidjs.com/) which is based on Signals and not using a VDOM. Leptos has a good quick overview of features [here](https://github.com/leptos-rs/leptos#what-does-that-mean) and a nice [FAQ here](https://github.com/leptos-rs/leptos#faqs). + +#### Setting up our Leptos App + +We will be using Trunk for developing and building our Leptos App. Trunk is a great tool for developing Rust/WASM Apps, and is very similar to Bun (in a sense) in that it is a wrapper around the underlying tools. Let's install it first: + +```bash +# Install dependencies +$ cargo install trunk +``` + +We can then set up our project, which we'll call `ui-internal`: + +```bash +$ cargo init ui-internal +$ cd ui-internal +``` + +We'll immediately adjust our `Cargo.toml` file with the dependencies we'll need, as well as a few common WASM optimizations for our release builds: + + +```toml ,linenos +[package] +name = "ui-internal" +version = "0.1.0" +edition = "2021" + +# Define our supported locales. +[package.metadata.leptos-i18n] +default = "en" +locales = ["en"] +locales-dir = "./messages" + +# Optimize for WASM binary size. +[profile.release] +opt-level = 'z' +lto = true +codegen-units = 1 + +[dependencies] +# Core leptos library. +leptos = { version = "0.5.1", features = ["csr", "nightly"] } +# Leptos Meta adds support for adjusting from within components. +leptos_meta = { version = "0.5.1", features = ["csr", "nightly"] } +# Router and Route state management. +leptos_router = { version = "0.5.1", features = ["csr", "nightly"] } +# Leptos support for i18n localization. +leptos_i18n = { version = "0.2", features = ["csr", "nightly"] } +# Lightweight logging support. +log = "0.4" + +# Common WASM libraries. +wasm-bindgen = { version = "0.2" } +console_log = { version = "1" } +console_error_panic_hook = { version = "0.1" } +``` + +And finally, we'll use Rust Nightly to develop our App, which gives us a few better ergonomics: + +```bash +$ rustup toolchain install nightly +$ rustup default nightly +$ rustup target add wasm32-unknown-unknown +``` + +Let's create a quick `index.html` file in the root of the `ui-internal/` folder, just to get started: + + +```html ,linenos + + + + + +``` + +And replace the contents of our `src/main.rs`: + + +```rust ,linenos +use leptos::*; + +mod app; + +pub fn main() { + // Register log and panich handlers. + let _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + mount_to_body(|| { + view! { } + }); +} +``` + +We'll also create a `src/app.rs` file with the following (we'll update this file later): + + +```rust ,linenos +use leptos::*; + +#[component] +pub fn Layout() -> impl IntoView { + view! {

"Hello, world!"

} +} +``` + +We can now run our App using Trunk: + +```bash +$ trunk serve --open +``` + +Voila, we've got a little Hello World Leptos app! + +#### Setting up Tailwind CSS + +Let's configure Tailwind CSS for our Leptos App. First, we need to tell Tailwind where to look for files that might contain our CSS classes. Create a `ui-internal/tailwind.config.ts` file with the following: + + +```typescript ,linenos +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: { + files: ["*.html", "./src/**/*.rs"], + }, + theme: { + extend: {}, + }, + plugins: [], +}; +``` + +We also need to tell `trunk` to build Tailwind CSS as part of its build process. We can do this by creating a `ui-internal/Trunk.toml` file: + + +```toml ,linenos +[[hooks]] +stage = "pre_build" +command = "sh" +command_arguments = [ + "-c", + "bunx tailwindcss --input resources/input.css --output public/output.css", +] +``` + +This let's `trunk` know that before it builds our WASM App, it should run the `bunx tailwindcss ...` command, which will generate our Tailwind CSS file, which it puts into `public/output.css`. + +Now, you might have noticed we also have an input file. Let's get that set up, along with a `resources/` folder: + +```bash +$ mkdir ui-internal/resources +``` + +We'll then create our base Tailwind CSS file at `ui-internal/resources/input.css`, mimicing our Next.js setup: + + +```css ,linenos +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); +} + +``` + +Final step, we need to pull in our Tailwind CSS file in our `index.html`. Update the contents to the following: + + +```html ,linenos + + + + + + + + Hello, World! + + + + +``` + +And that's it! We've now integrated Tailwind CSS into our Leptos App. + +#### Setting up Localization + +We're using [leptos_i18n](https://github.com/Baptistemontan/leptos_i18n) for localization in Leptos, which supports an API that's very close to the one we used in Next.js. We already pulled in the dependency when we updated our `Cargo.toml` file earlier, so let's get the rest of it set up. + +We'll create a `ui-internal/messages/` folder where our locales will live: + +```bash +$ mkdir ui-internal/messages +``` + +We'll define our first locale, English, in a `messages/en.json` file: + + +```json ,linenos +{ + "home": { + "intro": "Welcome!" + } +} +``` + +And also a French locale, in a `messages/fr.json` file: + + +```json ,linenos +{ + "home": { + "intro": "Bienvenue!" + } +} +``` + +[leptos_i18n](https://github.com/Baptistemontan/leptos_i18n#loading-the-locales) exposes a macro `load_locales!()` that looks for our configuration and generates code specific for our project that we can load in our App. + +Let's update `src/main.rs`, and also pull in a new module `home` in anticipation of creating splitting our code out from the current `app.rs` file: + + +```rust ,linenos +use leptos::*; + +mod app; +mod home; + +// Load our locales from the files defined in `Cargo.toml`. +leptos_i18n::load_locales!(); + +pub fn main() { + // Register log and panich handlers. + let _ = console_log::init_with_level(log::Level::Debug); + console_error_panic_hook::set_once(); + + mount_to_body(|| { + view! { } + }); +} +``` + +Let's create a `src/home.rs` in which will use our locales: + + +```rust ,linenos +use crate::i18n::*; +use leptos::*; +use leptos_router::*; + +#[component] +pub fn Page() -> impl IntoView { + let i18n = use_i18n(); + + view! { +
+

{t!(i18n, home.intro)}

+ +
+ } +} +``` + +The magic here comes from the `crate::i18n::*` which got generated by `leptos_i18n::load_locales!()`, and the `use_i18n` hook that we now got access to. Very similar to our Next.js App, we then call the macro `t!` to get the correct translation for the current locale, given a JSON key. + +We're not entirely done yet, we need to tell our Leptos App about the `I18nContext` at the root of our application. We also still need to add support for routing between locales. + +Let's update `src/app.rs` to do this: + + +```rust ,linenos +use crate::i18n::*; +use leptos::*; +use leptos_meta::*; +use leptos_router::*; + +use crate::home; + +const DEFAULT_LOCALE: &str = "en"; + +#[derive(Params, PartialEq, Clone, Debug)] +pub struct LayoutParams { + locale: String, +} + +#[component(transparent)] +fn LocalizedRoute(path: P, view: F) -> impl IntoView +where + P: std::fmt::Display, + F: Fn() -> IV + 'static, + IV: IntoView, +{ + view! { + (); + let chosen_locale = move || params().map(|params| params.locale).unwrap_or(DEFAULT_LOCALE.to_string()); + + create_effect(move |_| { + // Figure out what the current locale is, and if it matches the chosen locale from path. + let current_locale = i18n(); + let new_locale = match chosen_locale().as_str() { + "fr" => Locale::fr, + "en" => Locale::en, + _ => Locale::en, + }; + // Update the locale if necessary. + if current_locale != new_locale { + i18n(new_locale); + } + }); + + view! { + {view()} + } + }/> + } +} + +#[component] +pub fn Layout() -> impl IntoView { + provide_meta_context(); + provide_i18n_context(); + + view! { + +
+ + }/> + }/> + +
+
+ } +} +``` + +There's a lot to unpack here, so let's go through it step by step. + +- In our `pub fn Layout() -> impl IntoView` component we set up a normal `Router` component, which will handle routing for us. +- We introduce a special route, `LocalizedRoute`, which handles detecting our locale and switching the active locale if the path changes. +- In our `fn LocalizedRoute` function we wrap the normal `Route` component, and inject a bit of logic before returning the `view` that was otherwise passed into our `LocalizedRoute`. + +The last part is the most interesting, so let's break down what we are doing inside the `Route` we are setting up for `LocalizedRoute`. + +First we get the current parameters, which we know will contain a `locale` key: + + +```rust ,linenos +// Extract the locale from the path. +let i18n = use_i18n(); +let params = use_params::(); +let chosen_locale = move || params().map(|params| params.locale).unwrap_or(DEFAULT_LOCALE.to_string()); +``` + +We then create an effect that will run every time the parameters change, which will be every time the path changes: + + +```rust ,linenos +create_effect(move |_| { + // Figure out what the current locale is, and if it matches the chosen locale from path. + let current_locale = i18n(); + let new_locale = match chosen_locale().as_str() { + "fr" => Locale::fr, + "en" => Locale::en, + _ => Locale::en, + }; + // Update the locale if necessary. + if current_locale != new_locale { + i18n(new_locale); + } +}); +``` + +The thing that makes our effect rerun is our usage of `i18n()` which subscribes us to the signal, and thus reruns the effect every time the locale changes. + + +You should now have a structure that looks like this: + +```bash +. +├── Cargo.lock +├── Cargo.toml +├── Trunk.toml +├── index.html +├── messages +│   ├── en.json +│   └── fr.json +├── public +│   └── favicon.ico +├── resources +│   └── input.css +├── src +│   ├── app.rs +│   ├── home.rs +│   └── main.rs +├── tailwind.config.ts +``` + +And that's it! We're now able to run our app and check it out in the browser: + +```bash +$ trunk serve --open +``` + +{{ image(path="the-stack-part-3-ui-internal-skeleton.png", caption="Screenshot of our ui-internal", width=500) }} + +Again, it may not look like much, but we've implemented a lot of the core functionality we need to get started! + +As the final step we will add our commands to just, [extending our existing justfile](/posts/2023-10-07-the-stack-part-2.html#bonus-just): + + +```makefile ,linenos +_setup-ui-internal: + #!/usr/bin/env bash + set -euxo pipefail + cd ui-internal + rustup toolchain install nightly + rustup default nightly + rustup target add wasm32-unknown-unknown + +_dev-ui-internal: + #!/usr/bin/env bash + set -euxo pipefail + cd ui-internal + trunk serve +``` + +## Bonus: End-to-End Tests + +End-to-End tests are a great way to ensure that our App is working as expected, and that we don't accidentally break something when we make changes. We'll use [Playwright](https://playwright.dev/) for this, which is a great tool for writing End-to-End tests. + +We want three different test suites to cover: + +- **ui-app**: Test our Next.js App. +- **ui-internal**: Test our Leptos App. +- **deployment**.: Test our deployed App to verify it is working as expected. + +Let's start by setting up our folder structure. Many of our configuration files will be the same across all three test suites, let's create an `end2end` folder for our projects: + +```bash +$ mkdir -p deployment/end2end/tests +$ mkdir -p ui-app/end2end/tests +$ mkdir -p ui-internal/end2end/tests +``` + +We intentionally make a distinction between `end2end/` and unit/integration tests which will live in `tests/`. These have very different requirements for how to run them, and we often want to run them at different times. + +Before we can run anything, we will need a couple of other files to set up Playwright as well as support for TypeScript. + +Let's create a `tsconfig.json` for all for all three projects (`ui-app`, `ui-internal`, and `deployment`). We'll place it at `/end2end/tsconfig.json`: + + +```json ,linenos +{ + "compilerOptions": { + "lib": ["esnext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "noEmit": true, + "allowImportingTsExtensions": true, + "moduleDetection": "force", + "allowJs": true, // allow importing `.js` from `.ts` + "esModuleInterop": true, // allow default imports for CommonJS modules + "strict": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "noImplicitAny": false + } +} +``` + +Now, let's configure Playwright for all for all three projects (`ui-app`, `ui-internal`, and `deployment`). We'll place it at `/end2end/playwright.config.ts`: + + +```typescript ,linenos +import type { PlaywrightTestConfig } from "@playwright/test"; +import { devices } from "@playwright/test"; + +const SERVER = `http://localhost:8080`; + +const config: PlaywrightTestConfig = { + testDir: "./tests", + // Maximum time one test can run for. + timeout: 30 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000, + }, + // Run tests in files in parallel. + fullyParallel: true, + // Fail the build on CI if you accidentally left test.only in the source code. + forbidOnly: !!process.env.CI, + // Retry on CI only. + retries: process.env.CI ? 2 : 0, + // [Optional] Opt out of parallel tests on CI. + // workers: process.env.CI ? 1 : undefined, + // Limit the number of failures on CI to save resources + maxFailures: process.env.CI ? 10 : undefined, + + reporter: "html", + use: { + // Base URL to use in actions like `await page.goto('/')`. + baseURL: SERVER, + // Maximum time each action such as `click()` can take. Defaults to 0 (no limit). + actionTimeout: 0, + // Collect trace when retrying the failed test. + trace: "on-first-retry", + }, + + // Configure which browsers to test against. + projects: [ + { + name: "chromium", + use: { + ...devices["Desktop Chrome"], + }, + }, + ], + webServer: { + command: "just dev ui-internal", + url: SERVER, + reuseExistingServer: true, + stdout: "ignore", + stderr: "pipe", + }, +}; + +export default config; +``` + +There are some minor adjustments we want to do in the above configuration for each project: + +- **ui-app**: Set the `SERVER` variable to `http://localhost:3000` and command to `just dev ui-app`. +- **ui-internal**: Set the `SERVER` variable to `http://localhost:8080` and command to `just dev ui-internal`. +- **deployment**: Remove the `webServer` block, and set the `SERVER` variable to `http://${process.env.DOMAIN}`. + + +And a `package.json` in `/end2end/package.json`: + + +```json ,linenos +{ + "name": "end2end", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "setup": "playwright install", + "e2e": "playwright test" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "bun-types": "latest", + "@types/node": "^20.5.0", + "typescript": "^5", + "@playwright/test": "^1.38.1" + } +} +``` + +We are now ready to add our first test! Since we have just added localization, let's make sure that it works and doesn't regress. + +For all three projects `ui-app`, `deployment`, and `ui-internal` we'll create a test in `/end2end/tests/localization.spec.ts`: + + +```typescript ,linenos +import { test, expect } from "@playwright/test"; + +test("localization translates text when changing language", async ({ page }) => { + await page.goto("/"); + await expect(page.locator("h1")).toHaveText( + "Welcome!" + ); + + await page.getByText('Go to fr').dblclick(); + await expect(page.locator("h1")).toHaveText( + "Bienvenue!" + ); +}); + +test("localization loads correct text from URL", async ({ page }) => { + await page.goto("/fr"); + await expect(page.locator("h1")).toHaveText( + "Bienvenue!" + ); +}); +``` + +This same test works for both apps since we've set them up with the same functionality. + +Let's try and run them: + +```bash +$ cd ui-app/end2end # or cd ui-internal/end2end +$ bun run e2e +``` + +And for `deployment` we can test it locally by starting up `just dev ui-app` in another terminal, and then running: + +```bash +$ cd deployment/end2end # or cd ui-internal/end2end +$ DOMAIN="localhost:3000" bun run e2e +``` + +NOTE: You might want to add the following to your `.gitignore`: + + +``` ,linenos +playwright-report +test-results +``` + +And that's it! We've now got an easy way to run End-to-End tests. Let's do our final step and add this to our `justfile`: + + +```makefile ,linenos +# Run End-to-End tests for , e.g. `just e2e ui-internal`. +e2e project: + just _e2e-{{project}} + +_e2e-deployment: + cd deployment/end2end && bun run e2e + +_e2e-ui-app: + cd ui-app/end2end && bun run e2e + +_e2e-ui-internal: + cd ui-internal/end2end && bun run e2e +``` + +And we'll also update our `_setup-project` commands to setup the Playwright dependencies: + + +```makefile ,linenos +_setup-deployment: + #!/usr/bin/env bash + set -euxo pipefail + cd deployment + bun install + cd end2end + bun install + bun run setup + +_setup-ui-app: + #!/usr/bin/env bash + set -euxo pipefail + cd ui-app + bun install + cd end2end + bun install + bun run setup + +_setup-ui-internal: + #!/usr/bin/env bash + set -euxo pipefail + cd ui-internal + rustup toolchain install nightly + rustup default nightly + rustup target add wasm32-unknown-unknown + cd end2end + bun install + bun run setup +``` + +## Bonus: DevEx Improvements + +There are a few Editor improvements [that are recommended](https://leptos-rs.github.io/leptos/appendix_dx.html) for working with Leptos and Tailwind CSS if you are using VS Code. + +Add to your settings: + + +```json ,linenos +{ + "rust-analyzer.procMacro.ignored": { + "leptos_macro": ["server", "component"] + }, + "emmet.includeLanguages": { + "rust": "html", + "*.rs": "html" + }, + "tailwindCSS.includeLanguages": { + "rust": "html", + "*.rs": "html" + }, + "files.associations": { + "*.rs": "rust" + }, + "editor.quickSuggestions": { + "other": "on", + "comments": "on", + "strings": true + }, + "css.validate": false +} +``` + +Another nice tool is [leptosfmt](https://github.com/bram209/leptosfmt), which helps keep our Leptos View macro code nicely formatted. + +You can install it via: + +```bash +$ cargo install leptosfmt +``` + +And then add this to your settings: + + +```json ,linenos +{ + "rust-analyzer.rustfmt.overrideCommand": ["leptosfmt", "--stdin", "--rustfmt"] +} +``` + +## Automating Deployments via CDK + +Since we are now generating artifacts that we want to deploy, from multiple projects, we need to restructure our existing deployment pipeline slightly. + +We still want to retain staggered deployments, but we need a bit of coordination to make sure we have all relevant artifacts before we deploy. + +Our new flow will look like this: + +{{ image(path="the-stack-part-3-updated-flow.png", caption="Updated deployment pipeline", width=700) }} + +#### Building artifacts in CI + +In this part we will be doing the following: + +- Extend our existing `cd-deploy.yml` workflow to build artifacts for `ui-app` and `ui-internal` via a reuseable workflow. +- Extend our existing `wf-deploy.yml` workflow to download artifacts so it can use it during deployments. +- Set up a reuseable workflow, `wf-build.yml`, that will build our artifacts. +- Set up a reuseable workflows for both `ui-app` and `ui-internal` that will do the actual building. + + +Let's start with our `wf-build-ui-app.yml` workflow: + + +```yaml ,linenos +name: "Build: ui-app" + +on: + workflow_call: + inputs: + release: + type: boolean + default: false + upload-artifact: + type: boolean + default: false + +jobs: + ui-app: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + - uses: oven-sh/setup-bun@v1 + - uses: extractions/setup-just@v1 + + - name: Install dependencies + working-directory: ./ui-app + run: bun install + + - name: Build Next.js project + run: just build ui-app + + - uses: actions/upload-artifact@v3 + if: ${{ inputs.upload-artifact }} + with: + name: ui-app + path: ui-app/out + if-no-files-found: error + retention-days: 1 +``` + +And our `wf-build-ui-internal.yml` workflow: + + +```yaml ,linenos +name: "Build: ui-internal" + +on: + workflow_call: + inputs: + release: + type: boolean + default: false + upload-artifact: + type: boolean + default: false + +jobs: + ui-internal: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: oven-sh/setup-bun@v1 + - uses: extractions/setup-just@v1 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2023-10-11 + targets: wasm32-unknown-unknown + + - name: Install trunk + uses: jaxxstorm/action-install-gh-release@v1.9.0 + with: + repo: thedodd/trunk + platform: linux # Other valid options: 'windows' or 'darwin'. + arch: x86_64 + + - uses: actions/cache@v3 + continue-on-error: false + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ui-internal/target + key: cargo-${{ hashFiles('Cargo.lock') }}-${{ hashFiles('ui-internal/Cargo.lock') }}-ui-internal + restore-keys: cargo- + + - name: Build release build + if: ${{ inputs.release }} + run: just build ui-internal + + - name: Build debug build + if: ${{ !inputs.release }} + run: just build ui-internal true + + - uses: actions/upload-artifact@v3 + if: ${{ inputs.upload-artifact }} + with: + name: ui-internal + path: ui-internal/dist + if-no-files-found: error + retention-days: 1 +``` + +Both of these workflows take two optional arguments: + +- `release`: Whether or not to build a release build. +- `upload-artifact`: Whether or not to upload the artifact to GitHub. + +This means we can easily reuse these builds from our CI workflows. Once our jobs are building, we'll see these artifacts in the **Summary** view of our workflow, which will look something like this: + + +{{ image(path="the-stack-part-3-artifacts.png", caption="Deployment artifacts", width=700) }} + +With these in place we can now stitch them together in a `wf-build.yml`: + + +```yaml ,linenos +name: "Build Artifacts" + +on: + workflow_call: + +jobs: + ui-app: + uses: ./.github/workflows/wf-build-ui-app.yml + with: + release: true + upload-artifact: true + + ui-internal: + uses: ./.github/workflows/wf-build-ui-internal.yml + with: + release: true + upload-artifact: true +``` + +Not much going on here, we are simply calling our previously defined reuseable workflows. + +We can now update our `cd-deploy.yml` workflow to call our new `wf-build.yml` workflow. To do this, we extend the existing file by adding a `build-artifacts` job as well as mark our `stage-1` job as `needs: [build-artifacts]`: + + +```yaml ,linenos +# ... +jobs: + # Build deployment artifacts + build-artifacts: + name: "Build Artifacts" + uses: ./.github/workflows/wf-build.yml + + # Stage 1 tests that the deployment is working correctly. + stage-1: + name: "Stage 1" + needs: [build-artifacts] + uses: ./.github/workflows/wf-deploy.yml + # ...the rest is the same +``` + +The final change we need to make is to make our `wf-deploy.yml` workflow download the artifacts we just built: + + +```yaml ,linenos +# ... +jobs: + deploy: + name: "Deploy" + runs-on: ubuntu-latest + environment: ${{ inputs.environment }} + # Limit to only one concurrent deployment per environment. + concurrency: + group: ${{ inputs.environment }} + cancel-in-progress: false + steps: + - uses: actions/checkout@v3 + - uses: oven-sh/setup-bun@v1 + - uses: extractions/setup-just@v1 + + - name: Setup artifact directory + run: mkdir -p ./deployment/artifacts + + # Download all artifacts from previous jobs. + - uses: actions/download-artifact@v3 + with: + path: ./deployment/artifacts/ + + - name: Display structure of downloaded files + working-directory: ./deployment/artifacts/ + run: ls -R + + - name: Validate artifacts + working-directory: ./deployment/artifacts + run: just deploy-validate-artifacts + + - name: Install dependencies + working-directory: ./deployment + run: bun install + # ...the rest is the same +``` + +The new additions here are the steps: + +- `extractions/setup-just@v1`: Make `just` available in our workflow. +- `Setup artifact directory`: Creating our artifacts folder. +- `actions/download-artifact@v3y`: Download all uploaded assets into this folder. +- `Display structure of downloaded files`: Very helpful for any debugging. +- `Validate artifacts`: Make sure we have all the artifacts we need. + +The `deploy-validate-artifacts` command is defined in our `justfile`: + + +```makefile ,linenos +# Validate that all deployment artifacts are present. +deploy-validate-artifacts: + @ [ -d "./deployment/artifacts/ui-app" ] && echo "ui-app exists" || exit 1 + @ [ -d "./deployment/artifacts/ui-internal" ] && echo "ui-internal exists" || exit 1 +``` + + +#### Deploying to S3 + CloudFront + +If we remember our three groupings, we see that `ui-app` and `ui-internal` would fit into `Services`: + +- `Global`: "Global" (often `us-east-1`) specific things such as ACM Certificates for CloudFront, and we'll also put Hosted Zones here +- `Cloud`: Region specific infrequently changing things such as VPC, Region-specific Certificates, etc +- `Platform`: DynamoDB, Cache, SQS +- `Services`: Lambdas, API Gateway, etc + +##### Preparing for CloudFront + +We'd like our CloudFront distribution to use its own domain name and HTTPS, and to do this requires a bit of extra work on our end, since CloudFront needs our ACM Certificate to live in `us-east-1` specifically. + +This means that it will fit into our `Global` stack. Cross-stack references in CDK/CloudFormation is a bit finicky, and we generally want to avoid relying on `Exports` which are incredibly frustrating to work with and often gets you into sticky situations where you have to carefully destroy or update your stacks in a certain order. Once an export is used, it cannot change, leading to very tight coupling between stacks. + +{% aside() %} + Some examples of issues/annoyances and workarounds with `Exports`: [Here](https://github.com/aws/aws-cdk/issues/17741), [here](https://chariotsolutions.com/blog/post/limiting-cross-stack-references-in-cdk/), [here](https://github.com/aws/aws-cdk/issues/5304), and [here](https://www.endoflineblog.com/cdk-tips-03-how-to-unblock-cross-stack-references). +{% end %} + +Instead, we will rely on the approach outlined in this nice article from AWS on how to [Read parameters across AWS Regions with AWS CloudFormation custom resources](https://aws.amazon.com/blogs/infrastructure-and-automation/read-parameters-across-aws-regions-with-aws-cloudformation-custom-resources/). + +We will essentially: + +- Store the Certificate ARN in the SSM Parameter Store in `us-east-1`. +- Set up a new construct via `AwsCustomResource` and `AwsSdkCall` that can read parameters from a specific region. +- Use this construct in our `Services` stack to read the Certificate ARN from `us-east-1`. + +Let's set up our new Certificate first. We'll adjust the existing `GlobalStack` slightly in `bin/deployment.ts`: + + +```typescript ,linenos +/** + * SSM Parameter name for the global certificate ARN used by CloudFront. + */ +const GLOBAL_CERTIFICATE_SSM = "/global/acm/certificate/arn"; + +/** + * Define our 'Global' stack that provisions the infrastructure for our application, such + * as domain names, certificates, and other resources that are shared across all regions. + * + * ```bash + * bun run cdk deploy --concurrency 6 'Global/**' + * ``` + */ +const globalStackName = "Global"; +if (matchesStack(app, globalStackName)) { + // Some of our global resources need to live in us-east-1 (e.g. CloudFront certificates), + // so we set that as the region for all global resources. + new GlobalStack(app, globalStackName, { + env: { + account: process.env.AWS_ACCOUNT_ID || process.env.CDK_DEFAULT_ACCOUNT, + region: "us-east-1", + }, + domain: validateEnv("DOMAIN", globalStackName), + certificateArnSsm: GLOBAL_CERTIFICATE_SSM, + }); +} +``` + +We've introduced `GLOBAL_CERTIFICATE_SSM` which will be how we share the name of the parameter across stacks, and `certificateArnSsm` as a property to our `GlobalStack`. + +Let's set up the certificate before we stitch it into our `GlobalStack`. We'll create a new file `lib/global/certificate.ts`: + + +```typescript ,linenos +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as route53 from 'aws-cdk-lib/aws-route53'; +import * as acm from 'aws-cdk-lib/aws-certificatemanager'; +import * as ssm from 'aws-cdk-lib/aws-ssm'; + +export interface StackProps extends cdk.StackProps { + /** + * The domain name the application is hosted under. + */ + readonly domain: string; + + /** + * SSM Parameter name for the global certificate ARN used by CloudFront. + */ + readonly certificateArnSsm: string; +} + +export class Stack extends cdk.Stack { + constructor(scope: Construct, id: string, props: StackProps) { + super(scope, id, props); + + const hostedZone = route53.HostedZone.fromLookup(this, 'HostedZone', { + domainName: props.domain, + }); + + // Set up an ACM certificate for the domain + subdomains, and validate it using DNS. + // NOTE: This has to live in us-east-1 for CloudFront to be able to use it with CloudFront. + const cert = new acm.Certificate(this, 'Certificate', { + domainName: props.domain, + subjectAlternativeNames: [`*.${props.domain}`], + validation: acm.CertificateValidation.fromDns(hostedZone), + }); + + new cdk.CfnOutput(this, `CertificateArn`, { + value: cert.certificateArn, + description: 'The ARN of the ACM Certificate to be used with CloudFront.', + }); + + // Store the Certificate ARN in SSM so that we can reference it from other regions + // without creating cross-stack references. + new ssm.StringParameter(this, 'CertificateARN', { + parameterName: props.certificateArnSsm, + description: 'Certificate ARN to be used with Cloudfront', + stringValue: cert.certificateArn, + }); + } +} +``` + +The last step in the stack stores the `certificateArn` in the SSM Parameter Store. + +Finally, we adjust `lib/global/stack.ts` to now look like: + + +```typescript ,linenos +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as domain from './domain'; +import * as certificate from './certificate'; + +interface StackProps extends cdk.StackProps, domain.StackProps, certificate.StackProps {} + +export class Stack extends cdk.Stack { + constructor(scope: Construct, id: string, props: StackProps) { + super(scope, id, props); + + // Set up our domain stack. + const domainStack = new domain.Stack(this, 'Domain', props); + + // Set up our Certificate stack. + const certificateStack = new certificate.Stack(this, 'Certificate', props); + certificateStack.addDependency(domainStack); + } +} +``` + +Instead of passing the Hosted Zone into the certificate stack, we explicitly mark the certificate as dependent on the domain stack to ensure the hosted zone exists before we try to access it. Again, avoiding exports. + +Normally SSM doesn't take the region as a parameter, so to access the parameter from `us-east-1` we'll set up a new construct in `lib/services/ssm-global.ts`: + + +```typescript ,linenos +import { Arn, Stack } from 'aws-cdk-lib'; +import * as CustomResource from 'aws-cdk-lib/custom-resources'; +import { Construct } from 'constructs'; + +interface SsmGlobalProps { + /** + * The name of the parameter to retrieve. + */ + parameterName: string; + + /** + * The region the parameter is stored in, when it was created. + */ + region: string; +} + +/** + * Remove any leading slashes from the resource `parameterName`. + */ +const removeLeadingSlash = (parameterName: string): string => + parameterName.slice(0, 1) == '/' ? parameterName.slice(1) : parameterName; + +/** + * Custom resource to retrieve a global SSM parameter. See https://aws.amazon.com/blogs/infrastructure-and-automation/read-parameters-across-aws-regions-with-aws-cloudformation-custom-resources/ for more information. + * + * You store your SSM Parameter as normal in any region: + * ```ts + * import { StringParameter } from 'aws-cdk-lib/aws-ssm'; + * + * const cert = ... + * + * new StringParameter(this, 'CertificateARN', { + * parameterName: 'CloudFrontCertificateArn', + * description: 'Certificate ARN to be used with Cloudfront', + * stringValue: cert.certificateArn, + * }); + * ``` + * + * Example of retrieving it from another region: + * ```ts + * import { SsmGlobal } from './ssm-global'; + * + * const certificateArnReader = new SsmGlobal(this, 'SsmCertificateArn', { + * parameterName: "CloudFrontCertificateArn", + * region: 'us-east-1' + * }); + * + * // Get the value itself. + * certificateArnReader.value(); + * ``` + */ +export class SsmGlobal extends CustomResource.AwsCustomResource { + constructor(scope: Construct, name: string, props: SsmGlobalProps) { + const { parameterName, region } = props; + + const ssmAwsSdkCall: CustomResource.AwsSdkCall = { + service: 'SSM', + action: 'getParameter', + parameters: { + Name: parameterName, + }, + region, + physicalResourceId: CustomResource.PhysicalResourceId.of(Date.now().toString()), + }; + + const ssmCrPolicy = CustomResource.AwsCustomResourcePolicy.fromSdkCalls({ + resources: [ + Arn.format( + { + service: 'ssm', + region: props.region, + resource: 'parameter', + resourceName: removeLeadingSlash(parameterName), + }, + Stack.of(scope), + ), + ], + }); + + super(scope, name, { onUpdate: ssmAwsSdkCall, policy: ssmCrPolicy }); + } + + /** + * Get the parameter value from the store. + */ + public value(): string { + return this.getResponseField('Parameter.Value').toString(); + } +} +``` + +We now have everything we need to create our services. + +##### Services + +Now we are ready to get our `Services` stack set up! + +All files will live in the `deployment/` folder. We'll first adjust our `bin/deployment.ts`, adding our `Services` stack. Append the following at the end: + + +```typescript ,linenos +// ... +import { Stack as ServicesStack } from "../lib/services/stack"; +// ... + +/** + * Define our 'Services' stack that provisions our applications and services, such as our + * UI applications and APIs. + * + * ```bash + * bun run cdk deploy --concurrency 6 'Services/**' + * ``` + */ +const servicesStackName = "Services"; +if (matchesStack(app, servicesStackName)) { + // Set up our service resources. + new ServicesStack(app, servicesStackName, { + env: { + account: process.env.AWS_ACCOUNT_ID || process.env.CDK_DEFAULT_ACCOUNT, + region: + process.env.AWS_REGION || + process.env.AWS_DEFAULT_REGION || + process.env.CDK_DEFAULT_REGION, + }, + domain: validateEnv("DOMAIN", servicesStackName), + certificateArnSsm: GLOBAL_CERTIFICATE_SSM, + }); +} +``` + +And our `ServicesStack` is defined in `lib/services/stack.ts`: + + +```typescript ,linenos +import * as cdk from "aws-cdk-lib"; +import { Construct } from "constructs"; + +import * as s3Website from "./s3-website"; +import { SsmGlobal } from './ssm-global'; + +interface StackProps extends cdk.StackProps { + /** + * The domain name the application is hosted under. + */ + readonly domain: string; + + /** + * The ACM Certificate ARN to use with CloudFront. + */ + readonly certificate: acm.Certificate; +} + +export class Stack extends cdk.Stack { + constructor(scope: Construct, id: string, props: StackProps) { + super(scope, id, props); + + // Fetch the ARN of our CloudFront ACM Certificate from us-east-1. + const certificateArnReader = new SsmGlobal(this, 'SsmCertificateArn', { + parameterName: props.certificateArnSsm, + region: 'us-east-1', + }); + const certificateArn = certificateArnReader.value(); + + // Set up our s3 website for ui-app. + new s3Website.Stack(this, "UiApp", { + ...props, + assets: "artifacts/ui-app", + index: "index.html", + error: "404.html", + domain: props.domain, + hostedZone: props.domain, + billingGroup: "ui-app", + rewriteUrls: true, + certificateArn: certificateArn, + }); + + // Set up our s3 website for ui-internal. + new s3Website.Stack(this, "UiInternal", { + ...props, + assets: "artifacts/ui-internal", + index: "index.html", + error: "index.html", + domain: `internal.${props.domain}`, + hostedZone: props.domain, + billingGroup: "ui-internal", + certificateArn: certificateArn, + }); + } +} +``` + +In here we deploy both `ui-app` and `ui-internal` the same way, but do some minor adjustments to the props we pass on to the stack to ensure it gets the right assets and also the right domain. + +This brings us to our final part, which is the most lengthy, our `lib/services/s3-website.ts`: + + +```typescript ,linenos +import * as cdk from "aws-cdk-lib"; +import { Construct } from "constructs"; +import * as route53 from "aws-cdk-lib/aws-route53"; +import * as route53Targets from "aws-cdk-lib/aws-route53-targets"; +import * as acm from "aws-cdk-lib/aws-certificatemanager"; +import * as lambda from "aws-cdk-lib/aws-lambda"; +import * as s3deploy from "aws-cdk-lib/aws-s3-deployment"; +import * as cloudfront from "aws-cdk-lib/aws-cloudfront"; +import * as cloudfrontOrigins from "aws-cdk-lib/aws-cloudfront-origins"; +import * as s3 from "aws-cdk-lib/aws-s3"; +import * as path from "path"; + +export interface StackProps extends cdk.StackProps { + /** + * The path to the assets we are deploying. + */ + readonly assets: string; + + /** + * The file to use as the root/index page. + */ + readonly index: string; + + /** + * The file to redirect upon errors or 404s. + */ + readonly error: string; + + /** + * The domain name the application is hosted under. + */ + readonly domain: string; + + /** + * The hosted zone that controls the DNS for the domain. + */ + readonly hostedZone: string; + + /** + * The billing group to associate with this stack. + */ + readonly billingGroup: string; + + /** + * The ACM Certificate ARN. + */ + readonly certificateArn: string; + + /** + * Whether to rewrite URLs to /folder/ -> /folder/index.html. + */ + readonly rewriteUrls?: boolean; +} + +/** + * Set up an S3 bucket, hosting our assets, with CloudFront in front of it. + */ +export class Stack extends cdk.Stack { + constructor(scope: Construct, id: string, props: StackProps) { + super(scope, id, props); + + // Create our S3 Bucket, making it private and secure. + const bucket = new s3.Bucket(this, "WebsiteBucket", { + publicReadAccess: false, + accessControl: s3.BucketAccessControl.PRIVATE, + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, + encryption: s3.BucketEncryption.S3_MANAGED, + removalPolicy: cdk.RemovalPolicy.RETAIN, + }); + cdk.Tags.of(bucket).add("billing", `${props.billingGroup}-s3`); + cdk.Tags.of(bucket).add("billing-group", `${props.billingGroup}`); + + // Set up access between CloudFront and our S3 Bucket. + const originAccessIdentity = new cloudfront.OriginAccessIdentity( + this, + "OriginAccessIdentity" + ); + bucket.grantRead(originAccessIdentity); + + // Rewrite requests to /folder/ -> /folder/index.html. + let rewriteUrl: cloudfront.experimental.EdgeFunction | undefined; + if (props.rewriteUrls) { + rewriteUrl = new cloudfront.experimental.EdgeFunction(this, "RewriteFn", { + runtime: lambda.Runtime.NODEJS_LATEST, + handler: "rewrite-urls.handler", + code: lambda.Code.fromAsset(path.resolve("edge-functions")), + }); + } + + // Configure our CloudFront distribution. + const distribution = new cloudfront.Distribution(this, "Distribution", { + domainNames: [props.domain], + certificate: acm.Certificate.fromCertificateArn( + this, + "Certificate", + props.certificateArn + ), + // Allow both HTTP 2 and 3. + httpVersion: cloudfront.HttpVersion.HTTP2_AND_3, + // Our default behavior is to redirect to our index page. + defaultRootObject: props.index, + defaultBehavior: { + // Set our S3 bucket as the origin. + origin: new cloudfrontOrigins.S3Origin(bucket, { + originAccessIdentity, + }), + // Redirect users from HTTP to HTTPs. + viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + edgeLambdas: + rewriteUrl !== undefined + ? [ + { + functionVersion: rewriteUrl.currentVersion, + eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST, + }, + ] + : undefined, + }, + // Set up redirects when a user hits a 404 or 403. + errorResponses: [ + { + httpStatus: 403, + responsePagePath: `/${props.error}`, + responseHttpStatus: 200, + }, + { + httpStatus: 404, + responsePagePath: `/${props.error}`, + responseHttpStatus: 200, + }, + ], + }); + cdk.Tags.of(distribution).add( + "billing", + `${props.billingGroup}-cloudfront` + ); + cdk.Tags.of(distribution).add("billing-group", `${props.billingGroup}`); + + // Upload our assets to our bucket, and connect it to our distribution. + new s3deploy.BucketDeployment(this, "WebsiteDeployment", { + destinationBucket: bucket, + sources: [s3deploy.Source.asset(path.resolve(props.assets))], + // Invalidate the cache for / and index.html when we deploy so that cloudfront serves latest site + distribution, + distributionPaths: ["/", `/${props.index}`], + }); + + // Set up our DNS records that points to our CloudFront distribution. + const hostedZone = route53.HostedZone.fromLookup(this, "HostedZone", { + domainName: props.hostedZone, + }); + + new route53.ARecord(this, "Alias", { + zone: hostedZone, + recordName: props.domain, + target: route53.RecordTarget.fromAlias( + new route53Targets.CloudFrontTarget(distribution) + ), + }); + } +} +``` + +And our Lambda@Edge function to rewrite urls is defined in `edge-functions/rewrite-urls.js`: + + +```typescript ,linenos +exports.handler = (event, _, callback) => { + let request = event.Records[0].cf.request; + + // Check whether the URI is missing a file name. + if (request.uri.endsWith("/")) { + request.uri += "index.html"; + } else if (!request.uri.includes(".")) { + // Check whether the URI is missing a file extension. + request.uri += "/index.html"; + } + + return callback(null, request); +}; +``` + + +There is quite a bit going on here. A rough overview of what is happening: + +- We create an S3 Bucket, which will host our assets. +- The S3 Bucket will be configured to with encryption and to block public read access. +- We create a CloudFront distribution, which will serve our assets. +- The CloudFront distribution will also redirect HTTP to HTTPS, use our domain name and certificate, as well as support HTTP 2 and 3. +- If enabled via `rewriteUrls`, we will also set up a Lambda@Edge function that will rewrite URLs to `/folder/` -> `/folder/index.html`. This is necessary to support the way Next.js generates its files. + +And that's it! Your `ui-app` will now live at the root of your domain, e.g. `app.example.com`, and `ui-internal` will live at the subdomain `internal` e.g. `internal.app.example.com`. + +## Next Steps + +Next up is to set up our Federated GraphQL API! Follow along in Part 4 of the series when that arrives!. diff --git a/resources/images/the-stack-part-3-artifacts.png b/content/posts/2023-10-16-the-stack-part-3/the-stack-part-3-artifacts.png similarity index 100% rename from resources/images/the-stack-part-3-artifacts.png rename to content/posts/2023-10-16-the-stack-part-3/the-stack-part-3-artifacts.png diff --git a/resources/images/the-stack-part-3-ui-app-skeleton.png b/content/posts/2023-10-16-the-stack-part-3/the-stack-part-3-ui-app-skeleton.png similarity index 100% rename from resources/images/the-stack-part-3-ui-app-skeleton.png rename to content/posts/2023-10-16-the-stack-part-3/the-stack-part-3-ui-app-skeleton.png diff --git a/resources/images/the-stack-part-3-ui-internal-skeleton.png b/content/posts/2023-10-16-the-stack-part-3/the-stack-part-3-ui-internal-skeleton.png similarity index 100% rename from resources/images/the-stack-part-3-ui-internal-skeleton.png rename to content/posts/2023-10-16-the-stack-part-3/the-stack-part-3-ui-internal-skeleton.png diff --git a/resources/images/the-stack-part-3-updated-flow.png b/content/posts/2023-10-16-the-stack-part-3/the-stack-part-3-updated-flow.png similarity index 100% rename from resources/images/the-stack-part-3-updated-flow.png rename to content/posts/2023-10-16-the-stack-part-3/the-stack-part-3-updated-flow.png diff --git a/posts/2024-06-24-setting-up-uniffi-for-ios-simulators-and-watchos.md b/content/posts/2024-06-24-setting-up-uniffi-for-ios-simulators-and-watchos.md similarity index 64% rename from posts/2024-06-24-setting-up-uniffi-for-ios-simulators-and-watchos.md rename to content/posts/2024-06-24-setting-up-uniffi-for-ios-simulators-and-watchos.md index cef0f51..cf72350 100644 --- a/posts/2024-06-24-setting-up-uniffi-for-ios-simulators-and-watchos.md +++ b/content/posts/2024-06-24-setting-up-uniffi-for-ios-simulators-and-watchos.md @@ -1,12 +1,13 @@ ---- -title: "Setting up UniFFI for iOS, Simulators, and watchOS" -tags: rust, wasm, leptos, mobile -date: 2024-06-24 ---- ++++ +title = "Setting up UniFFI for iOS, Simulators, and watchOS" +date = 2024-06-24 +[taxonomies] +tags = ["rust", "wasm", "leptos", "mobile"] ++++ -This is a part of the post [Mobile: A different way using Rust?](/posts/2024-06-25-mobile-a-different-way.html). +This is a part of the post [Mobile: A different way using Rust?](@/posts/2024-06-25-mobile-a-different-way/index.md). -There are some great resources out there on UniFFI already such as [this post](https://forgen.tech/en/blog/post/building-an-ios-app-with-rust-using-uniffi), but it doesn’t cover watchOS, so let’s take a quick tour through what I’ve set up in the example repository [https://github.com/Tehnix/template-mobile-wasm](https://github.com/Tehnix/template-mobile-wasm)[.](https://github.com/Tehnix/playground-mobile-wasm?tab=readme-ov-file) +There are some great resources out there on UniFFI already such as [this post](https://forgen.tech/en/blog/post/building-an-ios-app-with-rust-using-uniffi), but it doesn’t cover watchOS, so let’s take a quick tour through what I’ve set up in the example repository [https://github.com/Tehnix/template-mobile-wasm](https://github.com/Tehnix/template-mobile-wasm). We've set up four crates: @@ -15,19 +16,24 @@ We've set up four crates: - `shared`: Our shared code that we might use in `appy`, and also want to expose in Swift to use in our Widgets or watchOS App - `mobile`: Where we will generate the Swift bindings from via UniFFI, reexporting everything from `shared` that’s made available to UniFFI via the macros -I won’t go over the details to get these to play nicely with Cargo and Workspaces, check out the repository for that. Let’s instead focus on what `mobile` does (the rest assumes you’re in the `mobile/` directory). +I won’t go over the details to get these to play nicely with Cargo and Workspaces, check out the repository for that. Let’s instead focus on a simplified version of what `mobile` does (the rest assumes you’re in the `mobile/` directory). -
+
+ +{% aside() %} + Looking for Android support? This is already added in the template repository above. Otherwise check out [this post](https://forgen.tech/en/blog/post/building-an-android-app-with-rust-using-uniffi) for more details about it. +{% end %} If starting from scratch, create a new cargo project: ```bash -cargo init mobile --lib +$ cargo init mobile --lib ``` Update your `Cargo.toml`: -```toml + +```toml ,linenos [package] name = "mobile" version = "0.1.0" @@ -57,7 +63,8 @@ uniffi = { version = "0.28.0", features = ["cli"] } Update your `src/lib.rs`: -```rust + +```rust ,linenos uniffi::setup_scaffolding!(); #[derive(uniffi::Enum)] @@ -79,14 +86,14 @@ pub fn eat_fruit(fruit: Fruits) -> String { We’re using `setup_scaffolding` to avoid needing to manually construct headers, modulemaps, and the UDL files (check out the [docs here](https://mozilla.github.io/uniffi-rs/0.27/tutorial/Rust_scaffolding.html#setup-for-crates-using-only-proc-macros)). -
-
💡
+{% aside() %} If you are rexporting another crate, like I am in the example repository, you can replace all of the contents of `src/lib.rs` with this line pointing to your other crate, e.g. “shared”, `shared::uniffi_reexport_scaffolding!();` ([docs here](https://mozilla.github.io/uniffi-rs/0.27/tutorial/Rust_scaffolding.html#libraries-that-depend-on-uniffi-components)). -
+{% end %} And finally, create a new file `src/bin/uniffi-bindgen.rs`: -```rust + +```rust ,linenos fn main() { uniffi::uniffi_bindgen_main() } @@ -96,36 +103,32 @@ We’re now ready to build the binary for generating our bindings, and then use ```bash # Build the uniffi-bindgen binary and our initial library file. -cargo build -cargo run --bin uniffi-bindgen generate --library ./target/debug/libmobile.a --language swift --out-dir ./bindings - +$ cargo build +$ cargo run --bin uniffi-bindgen generate --library ./target/debug/libmobile.a --language swift --out-dir ./bindings ``` We also need to rename the FFI file to `module.modulemap` so that XCFramework will find it: ```bash -mv ./bindings/sharedFFI.modulemap ./bindings/module.modulemap - +$ mv ./bindings/sharedFFI.modulemap ./bindings/module.modulemap ``` Now, let's add support for iOS, the Simulator and macOS via rustup: ```bash -rustup target add aarch64-apple-darwin -rustup target add aarch64-apple-ios -rustup target add aarch64-apple-ios-sim -rustup target add x86_64-apple-ios # iOS simulator, also needed on Arm Macs. - +$ rustup target add aarch64-apple-darwin +$ rustup target add aarch64-apple-ios +$ rustup target add aarch64-apple-ios-sim +$ rustup target add x86_64-apple-ios # iOS simulator, also needed on Arm Macs. ``` and then build the library for all of our targets: ```bash -carbo build --release --target=aarch64-apple-darwin -carbo build --release --target=aarch64-apple-ios -carbo build --release --target=aarch64-apple-ios-sim -carbo build --release --target=x86_64-apple-ios - +$ carbo build --release --target=aarch64-apple-darwin +$ carbo build --release --target=aarch64-apple-ios +$ carbo build --release --target=aarch64-apple-ios-sim +$ carbo build --release --target=x86_64-apple-ios ``` We'll combine `x86_64-apple-ios` and `aarch64-apple-ios-sim` into a single binary later on, but for now we keep them separate. @@ -133,12 +136,11 @@ We'll combine `x86_64-apple-ios` and `aarch64-apple-ios-sim` into a single binar If we want watchOS we need to handle things a bit differently, since these are Tier 3 targets (i.e. rustup won't have their stdlib): ```bash -cargo +nightly build -Zbuild-std=std,panic_abort --release --target=aarch64-apple-watchos-sim -cargo +nightly build -Zbuild-std=std,panic_abort --release --target=x86_64-apple-watchos-sim -cargo +nightly build -Zbuild-std=std,panic_abort --release --target=aarch64-apple-watchos -cargo +nightly build -Zbuild-std=std,panic_abort --release --target=armv7k-apple-watchos -cargo +nightly build -Zbuild-std=std,panic_abort --release --target=arm64_32-apple-watchos - +$ cargo +nightly build -Zbuild-std=std,panic_abort --release --target=aarch64-apple-watchos-sim +$ cargo +nightly build -Zbuild-std=std,panic_abort --release --target=x86_64-apple-watchos-sim +$ cargo +nightly build -Zbuild-std=std,panic_abort --release --target=aarch64-apple-watchos +$ cargo +nightly build -Zbuild-std=std,panic_abort --release --target=armv7k-apple-watchos +$ cargo +nightly build -Zbuild-std=std,panic_abort --release --target=arm64_32-apple-watchos ``` That's a lot of targets, which represent all the various Watch models, as well as the simulators (we always need both ARM and x86). @@ -147,35 +149,33 @@ That's a lot of targets, which represent all the various Watch models, as well a ```bash # Combine the watchOS simulator libraries into a single file using lipo. -mkdir -p target/watchOS-sim/release -lipo -create target/aarch64-apple-watchos-sim/release/libmobile.a \\ -target/x86_64-apple-watchos-sim/release/libmobile.a \\ +$ mkdir -p target/watchOS-sim/release +$ lipo -create target/aarch64-apple-watchos-sim/release/libmobile.a \\ + target/x86_64-apple-watchos-sim/release/libmobile.a \\ -o target/watchOS-sim/release/libmobile.a # Confirm the architectures. -lipo -info target/watchOS-sim/release/libmobile.a +$ lipo -info target/watchOS-sim/release/libmobile.a # Combine the watchOS libraries into a single file using lipo. -mkdir -p target/watchOS/release -lipo -create target/aarch64-apple-watchos/release/libmobile.a \\ +$ mkdir -p target/watchOS/release +$ lipo -create target/aarch64-apple-watchos/release/libmobile.a \\ target/arm64_32-apple-watchos/release/libmobile.a \\ target/armv7k-apple-watchos/release/libmobile.a \\ -o target/watchOS/release/libmobile.a # Confirm the architectures. -lipo -info target/watchOS/release/libmobile.a - +$ lipo -info target/watchOS/release/libmobile.a ``` We can then create our XCFramework: ```bash -xcodebuild -create-xcframework \\ +$ xcodebuild -create-xcframework \\ -library ./target/aarch64-apple-ios-sim/release/libmobile.a -headers ./bindings \\ -library ./target/aarch64-apple-ios/release/libmobile.a -headers ./bindings \\ -library ./target/aarch64-apple-darwin/release/libmobile.a -headers ./bindings \\ -library ./target/watchOS-sim/release/libmobile.a -headers ./bindings \\ -library ./target/watchOS/release/libmobile.a -headers ./bindings \\ -output "ios/Shared.xcframework" - ``` And finally, we'll combine `x86_64-apple-ios` and `aarch64-apple-ios-sim` into a single binary. If we included both of these in the XCFramework, `xcodebuild` would complain that these are the same, and not generate our XCFramework file. Oddly enough, it will not be able to build the project without both, so we let `xcodebuild` generate the XCFramework first, and then replace the binary with the fat binary: @@ -184,18 +184,17 @@ And finally, we'll combine `x86_64-apple-ios` and `aarch64-apple-ios-sim` into a # We need to combine the architectures for the iOS Simulator libraries after we've # constructed the XCFramework, otherwise it will complain about them being the same, # while also failing because of missing x86_64 if we omit it. -mkdir -p target/iOS-sim/release -lipo -create target/aarch64-apple-ios-sim/release/libmobile.a \\ - target/x86_64-apple-ios/release/libmobile.a \\ - -o target/iOS-sim/release/libmobile.a +$ mkdir -p target/iOS-sim/release +$ lipo -create target/aarch64-apple-ios-sim/release/libmobile.a \\ + target/x86_64-apple-ios/release/libmobile.a \\ + -o target/iOS-sim/release/libmobile.a # Confirm the architectures. -lipo -info target/iOS-sim/release/libmobile.a +$ lipo -info target/iOS-sim/release/libmobile.a # Move it into place. -rm ios/Shared.xcframework/ios-arm64-simulator/libmobile.a -cp target/iOS-sim/release/libmobile.a ios/Shared.xcframework/ios-arm64-simulator/libmobile.a - +$ rm ios/Shared.xcframework/ios-arm64-simulator/libmobile.a +$ cp target/iOS-sim/release/libmobile.a ios/Shared.xcframework/ios-arm64-simulator/libmobile.a ``` Done! -As the final step we drag-n-drop `./ios/Shared.xcframework` and `./bindings/shared.swift` into the XCode project whereever you want them. I personally like to create a new group (folder) called `Generated` for them (the `build-ios.sh` script assumes that's the case).. +As the final step we *drag-n-drop* `./ios/Shared.xcframework` and `./bindings/shared.swift` into the XCode project whereever you want them. I personally like to create a new group (folder) called `Generated` for them (the `build-ios.sh` script assumes that's the case). diff --git a/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm.md b/content/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm/index.md similarity index 88% rename from posts/2024-06-24-using-capacitor-plugins-from-rust-wasm.md rename to content/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm/index.md index b362727..79e62f2 100644 --- a/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm.md +++ b/content/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm/index.md @@ -1,16 +1,17 @@ ---- -title: "Using Capacitor Plugins from Rust/WASM" -tags: rust, wasm, leptos, mobile -date: 2024-06-24 ---- ++++ +title = "Using Capacitor Plugins from Rust/WASM" +date = 2024-06-24 +[taxonomies] +tags = ["rust", "wasm", "leptos", "mobile"] ++++ -This is a part of the post [Mobile: A different way using Rust?](/posts/2024-06-25-mobile-a-different-way.html). +This is a part of the post [Mobile: A different way using Rust?](@/posts/2024-06-25-mobile-a-different-way/index.md). Capacitor is normally used in combination with JavaScript projects, so there is little help available if you want to use it in a Rust/WASM project. Worry not! That's exactly what we'll take a look at in this post. -Since Capacitor is a JS framework there will be a little extra work involved in interacting with any plugins, but it’s honestly not a lot we need to do. Let’s take a quick tour through what I’ve set up in the example repository [https://github.com/Tehnix/template-mobile-wasm](https://github.com/Tehnix/template-mobile-wasm)[.](https://github.com/Tehnix/playground-mobile-wasm?tab=readme-ov-file) +Since Capacitor is a JS framework there will be a little extra work involved in interacting with any plugins, but it’s honestly not a lot we need to do. Let’s take a quick tour through what I’ve set up in the example repository [https://github.com/Tehnix/template-mobile-wasm](https://github.com/Tehnix/template-mobile-wasm). -
+
We've set up four crates: @@ -21,6 +22,12 @@ We've set up four crates: In this post we'll focus on the `capacitor-rs` and `appy` crates, which contain our Capacitor bridge and our app where we'll use it respectively. +An overview of what we'll cover: + +{{ toc() }} + +## Setting up `capacitor-rs` + First, let’s create a new library where we’ll keep the Capacitor bridge related code in: ```bash @@ -51,13 +58,15 @@ mkdir src/plugins Let’s set up our `capacitor-rs/src/lib.rs` to expose the plugins folder: -```rust + +```rust ,linenos pub mod plugins; ``` And finally, let’s update our dependencies of the Rust project in `./capacitor-rs/Cargo.toml`: -```toml + +```toml ,linenos [package] name = "capacitor-rs" version = "0.1.0" @@ -71,6 +80,8 @@ wasm-bindgen-futures = { version = "0.4" } js-sys = { version = "0.3" } ``` +## Bridging a CapacitorJS plugin + For this example we’ll set up the [Haptics](https://capacitorjs.com/docs/apis/haptics) plugin, which will allow us to provide tactile feedback from our App in response to user interactions. Our approach will be: @@ -84,7 +95,8 @@ Our approach will be: Let’s add the TypeScript side of things in `./js/haptics.ts` which quite simply just wraps and exposes the Capacitor functionality: -```tsx + +```tsx ,linenos /** * https://capacitorjs.com/docs/apis/haptics */ @@ -130,7 +142,8 @@ Which gives us a corresponding bundled file in `js-dist/haptics.js` that we can As the final step, we’ll bridge this into our Rust code by setting up `src/plugins/haptics.rs`: -```rust + +```rust ,linenos use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -216,13 +229,17 @@ Let’s break down what’s going on here: We also need to add a `src/plugins/mod.rs` file to expose our new plugin: -```rust + +```rust ,linenos pub mod haptics; ``` +## Calling it from our App + We’re now ready to use it in our App by adding the new `capacitor-rs` crate to our dependencies in our WASM app: -```toml + +```toml ,linenos # ... [dependencies] capacitor-rs = { version = "0.1.0", path = "../capacitor-rs" } @@ -231,7 +248,8 @@ capacitor-rs = { version = "0.1.0", path = "../capacitor-rs" } And then using it like you like a normal Rust function: -```rust + +```rust ,linenos use log::info; use capacitor_rs::plugins::haptics; use leptos::*; @@ -260,8 +278,4 @@ And that’s it! Since I can’t exactly screenshot a haptic vibration, here’s an example where we use the [Capacitor](https://capacitorjs.com/docs/basics/utilities) utility function to determine which platform we are on, set up in the same way: -
- Screenshot of Capacitor running on iPhone - Screenshot of Capacitor running in Web -
-
+{{ images(paths=["using-capacitor-plugins-from-rust-wasm-iphone.png", "using-capacitor-plugins-from-rust-wasm-web.png"], captions=["Screenshot of Capacitor running on iPhone", "Screenshot of Capacitor running in Web"], widths=[400, 400]) }} diff --git a/resources/images/using-capacitor-plugins-from-rust-wasm-iphone.png b/content/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm/using-capacitor-plugins-from-rust-wasm-iphone.png similarity index 100% rename from resources/images/using-capacitor-plugins-from-rust-wasm-iphone.png rename to content/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm/using-capacitor-plugins-from-rust-wasm-iphone.png diff --git a/resources/images/using-capacitor-plugins-from-rust-wasm-web.png b/content/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm/using-capacitor-plugins-from-rust-wasm-web.png similarity index 100% rename from resources/images/using-capacitor-plugins-from-rust-wasm-web.png rename to content/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm/using-capacitor-plugins-from-rust-wasm-web.png diff --git a/importable/2024-06-25-mobile-a-different-way.md b/content/posts/2024-06-25-mobile-a-different-way/index.md similarity index 58% rename from importable/2024-06-25-mobile-a-different-way.md rename to content/posts/2024-06-25-mobile-a-different-way/index.md index 52914d1..bbeba33 100644 --- a/importable/2024-06-25-mobile-a-different-way.md +++ b/content/posts/2024-06-25-mobile-a-different-way/index.md @@ -1,10 +1,11 @@ ---- -title: "Mobile: A different way using Rust?" -tags: rust, wasm, leptos, mobile, capacitor, swift, xcode -date: 2024-06-25 ---- ++++ +title = "Mobile: A different way using Rust?" +date = 2024-06-25 +[taxonomies] +tags = ["rust", "wasm", "leptos", "mobile", "capacitor", "swift", "xcode"] ++++ -After having spent the better part of two months deep in the trenches of iOS development with Swift, I can comfortably say: Web Development simple is more mature. +After having spent the better part of two months deep in the trenches of iOS development with Swift, I can comfortably say: Web Development simply is more mature. Let’s just run over a few things I ran into that’s bad, broken, or made for a poor experience: @@ -19,16 +20,19 @@ Let’s just run over a few things I ran into that’s bad, broken, or made for - UI performance issues are very easy to sneak in, and very hard to track down - Apple’s frameworks sure do have a lot of bugs and you’ll commonly see stuff being broken almost permanently -Admittedly, these might be trivial grips for someone that has spent years in the Mobile ecosystem, but they certainly didn’t make me fall in love with it. More importantly, it wasn’t really providing a great user experience either. +Admittedly, these might be trivial gripes for someone that has spent years in the Mobile ecosystem, but they certainly didn’t make me fall in love with it. More importantly, it wasn’t really providing a great user experience either. ….so what’s the alternative? -
+
-
-
💡
- Looking for the TL;DR? Checkout [https://github.com/Tehnix/template-mobile-wasm](https://github.com/Tehnix/template-mobile-wasm) -
+{% aside() %} + Looking for the TL;DR? Checkout [https://github.com/Tehnix/template-mobile-wasm](https://github.com/Tehnix/template-mobile-wasm) for a ready setup with Rust/Leptos, i18n, TailwindCSS, Capacitor + bindings, iOS/Android, Widget, watchOS, and code sharing to Swift + Kotlin. +{% end %} + +Here's an overview of what we'll cover: + +{{ toc() }} ## The goals @@ -52,18 +56,22 @@ Rust supports Frontend development via WASM, supported in all major browsers, an Good alternatives for a full-stack language would be TypeScript and Node.js/Bun. For reasons we’ll see later, this is not the choice we go with in the end, but for now let’s consider it a viable alternative (it might be for you!). -Now, how do we get our Rust or TypeScript code onto our different deployment targets? Let’s make an overview of how you’d typically do: +Now, how do we get our Rust or TypeScript code onto our different deployment targets? Let’s make an overview of how you’d typically do. For good measure, let’s also compare with Dart/Flutter: + -| Language / Target | Backend Options | Frontend Options | Mobile Options | -| --- | --- | --- | --- | -| Rust | - Native Support | - Leptos (WASM)
- Dioxus (WASM)
- Yew (WASM)
- …etc | - Capacitor (WASM in WebView)
- Tauri (Beta, WASM in WebView) | -| TypeScript | - Node.js
- Bun | - Native Support (React, Vue, Svelte, Solid, etc) | - Capacitor (JS in WebView) | +| Language / Target | Backend Options | Frontend Options | Mobile Options | +|-------------|------------------|--------------------------|-------------------------------| +| Rust | - Native Support | - Leptos (WASM)
- Dioxus (WASM)
- Yew (WASM)
- …etc | - Capacitor (WASM in WebView)
- Tauri (Beta, WASM in WebView) | +| TypeScript | - Node.js
- Bun | - Native Support (React, Vue, Svelte, Solid, etc) | - Capacitor (JS in WebView) | +| Dart/Flutter| - Native Support | - Native for Dart
- Flutter (HTML or Canvas) | - Flutter | For Rust there are some interesting projects brewing such as [Robius](https://robius.rs) which is quite ambitious and hopefully gains traction. +The choice is not yet super clear here, and all could work so far. One concern I have with Dart/Flutter specifically is the lack of maturity in its Backend ecosystem currently. It you’ll quickly run into missing or unmaintained libraries. + **Choice**: Rust & WASM -**Alternative**: TypeScript & Node.js/Bun +**Alternative**: TypeScript + Node.js/Bun or Dart/Flutter ## **All Targets** @@ -77,9 +85,7 @@ If you want to develop an iOS Widget ([those little things](https://support.appl Here’s an example of a Widget for a Todo App, providing interactivity from the Home Screen: -
-Screenshot of Capacitor an iOS Widget -
+{{ image(path="mobile-a-different-way-widget-screenshot.jpg", caption="Screenshot of Capacitor an iOS Widget", width=450) }} This seemingly innocuous example is also where most of our options get limited and we’ll have to get a bit creative with how we solve it. @@ -87,14 +93,15 @@ So how does support look like for our Mobile options? (we’ll simplify for a mo | Mobile Options / Target Support | iOS App | macOS App | Widget Extension | watchOS | watchOS Complication | | --- | --- | --- | --- | --- | --- | -| Tauri | ✅ (sorta)* | ✅ (same App as the iOS App)*** | ❌ | ❌ | ❌ | -| Capacitor | ✅ (sorta)** | ✅ (same App as the iOS App)*** | ✅⚠️ (via Native code) | ✅⚠️ (via Native code) | ✅⚠️ (via Native code) | +| Tauri | ✅ (sorta)[^1] | ✅ (same App as the iOS App)[^2] | ❌ | ❌ | ❌ | +| Capacitor | ✅ (sorta)[^3] | ✅ (same App as the iOS App)[^2] | ✅⚠️ (via Native code) | ✅⚠️ (via Native code) | ✅⚠️ (via Native code) | +| Flutter | ✅ | ✅ | ✅⚠️ (via Native code) | ✅⚠️ (via Native code) | ✅⚠️ (via Native code) | -*: Platform support overview here [https://v2.tauri.app/plugin/](https://v2.tauri.app/plugin/) +[^1]: Platform support overview here [https://v2.tauri.app/plugin/](https://v2.tauri.app/plugin/) -**: Platform support overview here [https://capacitorjs.com/docs/apis](https://capacitorjs.com/docs/apis) +[^2]: This is the “Designed for iPad” type of App which is essentially an iOS App that runs completely native on ARM-based Macbooks. Alternatively you can also deploy it as an Electron App using [https://github.com/capacitor-community/electron](https://github.com/capacitor-community/electron) for other targets. -***: This is the “Designed for iPad” type of App which is essentially an iOS App that runs completely native on ARM-based Macbooks. Alternatively you can also deploy it as an Electron App using [https://github.com/capacitor-community/electron](https://github.com/capacitor-community/electron) for other targets. +[^3]: Platform support overview here [https://capacitorjs.com/docs/apis](https://capacitorjs.com/docs/apis) Before continuing, let’s talk about that ✅⚠️ score though—what does that mean exactly? @@ -104,23 +111,19 @@ But will our chosen framework support that, or will we get completely blocked ev For Tauri, the answer is: We are blocked ([#9766](https://github.com/tauri-apps/tauri/issues/9766) is still open in the Tauri repo), and cannot proceed. -Luckily, for Capacitor, that’s not entirely the case. We can open XCode and add our own Targets that will build alongside our App, for anything that we want, such as Widgets and our watchOS App. +Luckily, for both Capacitor and Dart/Flutter, that’s not entirely the case. We can open XCode and add our own Targets that will build alongside our App, for anything that we want, such as Widgets and our watchOS App. That looks like this in XCode: -
-Adding additional Targets in Xcode -
- -The limitation here is that these additional targets only support native code (i.e. Swift), which neatly brings us onto our next section. +{{ image(path="mobile-a-different-way-xcode-adding-targets.png", caption="Adding additional Targets in Xcode", width=650) }} -Now, getting back to the topic of the main app, how do we actually communicate with and/or use the native capabilities that Capacitor supports? +The limitation here is that these additional targets only support native code (i.e. Swift or Kotlin/Java). -I’ve written up a guide on how exactly to do that in detail here [Using Capacitor Plugins from Rust/WASM](/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm.html). +**Before jumping into the next section where we’ll look at code sharing**, how do we actually communicate with and/or use the native capabilities that Capacitor supports? I’ve written up a guide on how exactly to do that in detail here: [Using Capacitor Plugins from Rust/WASM](/posts/2024-06-24-using-capacitor-plugins-from-rust-wasm.html). **Choice**: Capacitor -**Alternative**: None +**Alternative**: Dart/Flutter ## **Code Sharing** @@ -132,12 +135,15 @@ This is where we get into the differences in our language choice. Our options ar - Calling Rust using [UniFFI](https://mozilla.github.io/uniffi-rs/latest/) and generated Swift bindings - Calling JavaScript by evaluating it using [JavaScriptCore](https://developer.apple.com/documentation/javascriptcore) +- Calling Flutter via FFI generated using [add-to-app](https://docs.flutter.dev/add-to-app) We can quickly exclude the [JavaScriptCore](https://developer.apple.com/documentation/javascriptcore) option as a viable route, it’s simply not gonna be even remotely ergonomic. You essentially import your JavaScript file, or put the code in a String. Then you evaluate it, and can get the output. There’s not much interop between the host language (Swift) and the guest language (JavaScript), providing for poor ergonomics in our usecase (some examples [here](https://stackoverflow.com/a/37435316) and [here](https://douglashill.co/javascript-in-swift/)). -How does the Rust side of things then look like? +Dart/Flutter does support calling [Objective-C/Swift from within Dart](https://dart.dev/interop/objective-c-interop#callbacks-and-multithreading-limitations), and with a bit of work it does seem to be able to generate FFI bindings via Flutter to go the other way as well, with a few examples given in [their documentation](https://docs.flutter.dev/add-to-app). + +How does the Rust side of things then look like? Very much like Dart/Flutter actually! 1. Use the [uniffi crate](https://crates.io/crates/uniffi) 2. Annotate the functions, enums, records, etc that you want to export with a macro @@ -146,56 +152,67 @@ How does the Rust side of things then look like? 5. Import the generated Swift and `.xcframework` into XCode 6. Done! -I’ve written up a guide on how exactly to do that in detail here [Setting up UniFFI for iOS, Simulators, and watchOS](/posts/2024-06-24-setting-up-uniffi-for-ios-simulators-and-watchos.html). +I’ve written up a guide on how exactly to do that in detail here: [Setting up UniFFI for iOS, Simulators, and watchOS](/posts/2024-06-24-setting-up-uniffi-for-ios-simulators-and-watchos.html). Once you’ve done the initial project setup (for building the targets and generating the `.xcframework` file) you don’t really touch that again, and you only need to concern yourself with which things to expose to Swift. Let’s make a small example of code using `uniffi` would look like: -https://gist.github.com/Tehnix/2a7c3059816f04fe2ccbc5390c96e3ed.js?file=uniffi-example.rs + +```rust ,linenos +uniffi::setup_scaffolding!(); + +#[derive(uniffi::Enum)] +pub enum Fruits { + Watermelon, + Cranberry, + Cherry, +} + +#[uniffi::export] +pub fn eat_fruit(fruit: Fruits) -> String { + match fruit { + Fruits::Watermelon => "Eating Watermelon".to_string(), + Fruits::Cranberry => "Eating Cranberry".to_string(), + Fruits::Cherry => "Eating Cherry".to_string(), + } +} +``` How do we call the generated code? As you would any other Swift code! -https://gist.github.com/Tehnix/2a7c3059816f04fe2ccbc5390c96e3ed.js?file=Example.swift + +```swift ,linenos +// Calling our Rust function with our Rust enum as an argument. +eatFruit(fruit: Fruits.watermelon) +``` -https://gist.github.com/Tehnix/2a7c3059816f04fe2ccbc5390c96e3ed.js?file=Example.swift%20(**Code%20Sharing**).swift + +```swift ,linenos +// It'll work everywhere you'd expect it to, e.g. in String interpolation here. +Text("Ready? \(eatFruit(fruit: Fruits.watermelon))") +``` -Final result in the iOS Simulator, running our Web App as a Mobile App: +Final result in the iOS and Android Simulators, running our Web App as a Mobile App: -Final result in the iOS Simulator, running our Web App as a Mobile App: +{{ images(paths=["mobile-a-different-way-ios-simulator.png", "mobile-a-different-way-android-simulator.png"], captions=["Final result in the iOS Simulator", "Final result in the Android Simulator"], widths=[400, 400]) }} -
-Final result in the iOS Simulator -
- -In the Android Simulator: - -
-Final result in the Android Simulator -
- Same application on the Web: -
-Final result on the Web -
+{{ image(path="mobile-a-different-way-web.png", caption="Final result on the Web", width=500) }} The Widget Extension in the XCode Preview: -
-Final result as a Widget Preview in Xcode -
+{{ image(path="mobile-a-different-way-widget-preview.png", caption="Final result as a Widget Preview in Xcode", width=600) }} The watchOS App in the XCode Preview: -
-Final result as a watchOS Preview in Xcode -
+{{ image(path="mobile-a-different-way-watchos-preview.png", caption="Final result as a watchOS Preview in Xcode", width=600) }} **Choice**: Rust with [UniFFI](https://mozilla.github.io/uniffi-rs/latest/) -**Alternative**: None +**Alternative**: Dart/Flutter ## Conclusion @@ -211,4 +228,8 @@ Let’s revisit our original goals with our final results: - **Code Sharing**: If we must drop into native code (Swift/Kotlin) we should be able to use code from our “core” language - Expose our Rust code to our native Swift code using UniFFI +Why not Dart/Flutter? My main holdback stems from the maturity the language and ecosystem has on the Backend, which seems to be quite lacking. Rust definitely has it beat here, with an extremely vibrant ecosystem. + I’m personally pretty happy with the solution. Most people probably won’t need the watchOS and Widgets, so they won’t have to touch Swift code, but it’s at least nice to know that you haven’t closed off that option for yourself down the road, as some options would leave you. + +[👉 Let me know what you think over on Medium](https://codethoughts.medium.com/mobile-a-different-way-using-rust-0e7b1dfa8cbf) or down in the comments below. diff --git a/resources/images/mobile-a-different-way-android-simulator.png b/content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-android-simulator.png similarity index 100% rename from resources/images/mobile-a-different-way-android-simulator.png rename to content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-android-simulator.png diff --git a/resources/images/mobile-a-different-way-ios-simulator.png b/content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-ios-simulator.png similarity index 100% rename from resources/images/mobile-a-different-way-ios-simulator.png rename to content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-ios-simulator.png diff --git a/resources/images/mobile-a-different-way-watchos-preview.png b/content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-watchos-preview.png similarity index 100% rename from resources/images/mobile-a-different-way-watchos-preview.png rename to content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-watchos-preview.png diff --git a/resources/images/mobile-a-different-way-web.png b/content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-web.png similarity index 100% rename from resources/images/mobile-a-different-way-web.png rename to content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-web.png diff --git a/resources/images/mobile-a-different-way-widget-preview.png b/content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-widget-preview.png similarity index 100% rename from resources/images/mobile-a-different-way-widget-preview.png rename to content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-widget-preview.png diff --git a/resources/images/mobile-a-different-way-widget-screenshot.jpeg b/content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-widget-screenshot.jpg similarity index 100% rename from resources/images/mobile-a-different-way-widget-screenshot.jpeg rename to content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-widget-screenshot.jpg diff --git a/resources/images/mobile-a-different-way-xcode-adding-targets.png b/content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-xcode-adding-targets.png similarity index 100% rename from resources/images/mobile-a-different-way-xcode-adding-targets.png rename to content/posts/2024-06-25-mobile-a-different-way/mobile-a-different-way-xcode-adding-targets.png diff --git a/resources/images/how-i-structure-my-work-as-a-solor-founder-goals.png b/content/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder/how-i-structure-my-work-as-a-solor-founder-goals.png similarity index 100% rename from resources/images/how-i-structure-my-work-as-a-solor-founder-goals.png rename to content/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder/how-i-structure-my-work-as-a-solor-founder-goals.png diff --git a/resources/images/how-i-structure-my-work-as-a-solor-founder-initiatives.png b/content/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder/how-i-structure-my-work-as-a-solor-founder-initiatives.png similarity index 100% rename from resources/images/how-i-structure-my-work-as-a-solor-founder-initiatives.png rename to content/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder/how-i-structure-my-work-as-a-solor-founder-initiatives.png diff --git a/resources/images/how-i-structure-my-work-as-a-solor-founder-issues.png b/content/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder/how-i-structure-my-work-as-a-solor-founder-issues.png similarity index 100% rename from resources/images/how-i-structure-my-work-as-a-solor-founder-issues.png rename to content/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder/how-i-structure-my-work-as-a-solor-founder-issues.png diff --git a/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder.md b/content/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder/index.md similarity index 84% rename from posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder.md rename to content/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder/index.md index 7e1df24..39d3d45 100644 --- a/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder.md +++ b/content/posts/2024-06-26-how-i-structure-my-work-as-a-solor-founder/index.md @@ -1,8 +1,9 @@ ---- -title: "How I structure work as a Solo Founder" -tags: project management, solo founder, codetalk -date: 2024-06-26 ---- ++++ +title = "How I structure work as a Solo Founder" +date = 2024-06-26 +[taxonomies] +tags = ["project management", "solo founder", "codetalk"] ++++ This is for sure something that will keep evolving over time, but I got inspired by [this Hacker News post](https://news.ycombinator.com/item?id=40742831) to share in more detail how I organize myself and my work (you can see my short answer [here](https://news.ycombinator.com/item?id=40743669)). @@ -16,9 +17,11 @@ You could rephrase this slightly to: How do I run *my own* project management? For most Engineers, this is not actually something they’ve needed to do often, especially if working in larger corporations. The people that have worked at Startups are a mixed bag, where some learn the value of structure, and others managed to get by without since the fast-paced environment changes things around all the time anyways. -I’ve personally landed in the camp of: Structure is valuable, even crucial, but it should never get in my way. Best of both worlds sorta. +I’ve personally landed in the camp of: **Structure is valuable, even crucial, but it should never get in my way. Best of both worlds sorta.** -
+
+ +{{ toc() }} ## What is my context? @@ -64,9 +67,7 @@ At the time of writing, I currently have two focuses for the month of July: In Linear, I’ve structure the monthly goals using their new [Initiatives](https://linear.app/docs/initiatives), which gives me a great way to write a high-level summary of the goal, and gather the related projects that will get me there into each of them. -
-A view of my Initiatives in Linear for July 2024 -
+{{ image(path="how-i-structure-my-work-as-a-solor-founder-initiatives.png", caption="A view of my Initiatives in Linear for July 2024", width=700) }} A view of my Initiatives in Linear for July 2024. @@ -90,9 +91,7 @@ I then use this prioritization as the input for my daily goals, and it also make In Linear I’ve set up a Custom View with a simple filter on the Initiatives I have active at the moment, named “Initiative Issues” in the screenshot below. -
-A Custom View with a filter on the currently active Initiatives -
+{{ image(path="how-i-structure-my-work-as-a-solor-founder-issues.png", caption="A Custom View with a filter on the currently active Initiatives", width=700) }} A Custom View with a filter on the currently active Initiatives. @@ -102,9 +101,7 @@ Arriving at the final step, I start from the “Initiative Issues” list and pi These issues then go into another Custom View called “Goal of the day” which just gathers all issues with the `Goal` label on them, as well as having myself assigned to them. -
-A Custom View with a filter on “Goals” label being added -
+{{ image(path="how-i-structure-my-work-as-a-solor-founder-goals.png", caption="A Custom View with a filter on “Goals", width=700) }} A Custom View with a filter on “Goals” label being added. @@ -150,4 +147,6 @@ Honestly, most things would work, and you can even just do it with pen-and-paper As an alternative to Linear, I’d probably recommend Notion. You will need to set up the structure more yourself, but you can wrangle their [Projects](https://www.notion.so/product/projects) functionality into something that will allow you a very similar setup. -I always love to get inspiration, so I’d love to hear from you about your own preferred way of organizing yourself. [Let me know what you think over on Medium.](https://codethoughts.medium.com/how-i-structure-work-as-a-solo-founder-4944afcfc2f2) +I always love to get inspiration, so I’d love to hear from you about your own preferred way of organizing yourself. + +[👉 Let me know what you think over on Medium](https://codethoughts.medium.com/how-i-structure-work-as-a-solo-founder-4944afcfc2f2) or in the comments below. diff --git a/content/posts/_index.md b/content/posts/_index.md new file mode 100644 index 0000000..d2f87a6 --- /dev/null +++ b/content/posts/_index.md @@ -0,0 +1,8 @@ ++++ +title = "Posts" +sort_by = "date" +template = "posts.html" +page_template = "post-page.html" +transparent = true +insert_anchor_links = "heading" ++++ diff --git a/deploy_rsa.enc b/deploy_rsa.enc deleted file mode 100644 index c9e7747..0000000 Binary files a/deploy_rsa.enc and /dev/null differ diff --git a/justfile b/justfile index ae54085..d20787e 100644 --- a/justfile +++ b/justfile @@ -12,49 +12,41 @@ code: [linux] install-tooling: @ just _install-tooling-all-platforms - # Install imagemagick for mogrify. - command -v mogrify >/dev/null 2>&1 || sudo apt install imagemagick - gh release download --clobber --pattern "hakyll-site" --dir ./dist - chmod +x ./dist/hakyll-site + echo "Currently unsupported, see https://www.getzola.org/documentation/getting-started/installation/ for installation instructions." # Install tooling for working with the codetalk blog. [macos] install-tooling: @ just _install-tooling-all-platforms - # Install imagemagick for mogrify. - command -v mogrify >/dev/null 2>&1 || brew install imagemagick + # Install zola. + brew install zola _install-tooling-all-platforms: - # Install stack. - command -v stack >/dev/null 2>&1 || curl -sSL https://get.haskellstack.org/ | sh - # Install ghcup. - command -v ghcup >/dev/null 2>&1 || curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh - # Install sass. - command -v sass >/dev/null 2>&1 || npm i -g sass + echo "Setting up tools..." -# Setup dependencies and build the hakyll executable. -setup: - stack build - -# Deploy the blog to S3 and invalidate CloudFront cache. -deploy: - #!/usr/bin/env bash - set -euxo pipefail - just build - # Sync files to S3. - aws s3 sync _site s3://codetalk.io --delete - # Invalidate Cloudflare cache. - curl -X DELETE "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE}/purge_cache" \ - -H "Authorization: Bearer ${CLOUDFLARE_TOKEN}" \ - -H "Content-Type: application/json" \ - --data '{"purge_everything":true}' - -# Run hakyll development server in watch mode. +# Run zola development server in watch mode. dev: - stack exec -- hakyll-site watch --port 4566 + bunx concurrently --names tailwind,zola "bunx tailwindcss --input styles/input.css --output static/styles/output.css --watch" "zola serve --port 4566" # Build blog artifacts and static files. build: - ./dist/hakyll-site clean || stack exec -- hakyll-site clean - ./dist/hakyll-site clean || stack exec -- hakyll-site build - sass resources/scss/app.scss:_site/app.css --style compressed + bunx tailwindcss --input styles/input.css --output static/styles/output.css --minify + zola build + +# Generate posts with Gists instead of code blocks. +generate-gists: + cd tools && bun run index.ts + +# TODO: Move deployment to CloudFlare Pages? +# Deploy the blog to S3 and invalidate CloudFront cache. +# deploy: +# #!/usr/bin/env bash +# set -euxo pipefail +# just build +# # Sync files to S3. +# aws s3 sync _site s3://codetalk.io --delete +# # Invalidate Cloudflare cache. +# curl -X DELETE "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE}/purge_cache" \ +# -H "Authorization: Bearer ${CLOUDFLARE_TOKEN}" \ +# -H "Content-Type: application/json" \ +# --data '{"purge_everything":true}' diff --git a/resources/bookmarks/April.html b/resources/bookmarks/April.html deleted file mode 100644 index c8e9a50..0000000 --- a/resources/bookmarks/April.html +++ /dev/null @@ -1,307 +0,0 @@ -

April

-

-

Blog Rewrite with Hakyll and Purescript · in Code -
https://www.rsync.net/resources/notices/canary.txt -
Deploying to amazon is broken by sse4_crc32 · Issue #1050 · ParsePlatform/parse-server · GitHub -
Dwarf Fortress' creator on how he's 42% towards simulating existence | Hacker News -
You can now install a GSM network using apt-get | LaForge's home page -
Apricity OS -
Stream PirateBay movies directly from CLI | Hacker News -
Full Linux-on-PS4 hits Github • The Register -
Full Linux-on-PS4 hits GitHub | Hacker News -
Save Netflix! | Electronic Frontier Foundation -
register-machine-typelevel: A computationally universal register machine implementation at the type-level | Hackage -
Well, that escalated quickly. : haskell -
Deploying Haskell for Mac projects on OS X and Linux · Haskell for Mac -
Deploying Haskell for Mac projects on OS X and Linux using Stack : haskell -
Abandoning gitflow and GitHub | Hacker News -
XenialXerus/ReleaseNotes - Ubuntu Wiki -
At this time you should not upgrade a production desktop from 14.04 to 16.04 | Hacker News -
Being a ‘Dumb’ Girl in Computer Science | Rewriting the Code -
Tokamak: A Rust IDE for Atom | Hacker News -
Issue 326539 - chromium - OS X Notification Center should be used on Mac - Monorail -
Native Mac Notifications Comes to Chrome Stable | Hacker News -
Tor exit node operator gets raided by police | Hacker News -
When A Dark Web Volunteer Gets Raided By The Police : All Tech Considered : NPR -
How We Saved $132k a Year With an IT Infrastructure Audit | Hacker News -
Nostalrius Begins -
Blizzard shutting down Nostalrius, popular WoW private server | Hacker News -
WebTorrent Desktop -
WebTorrent Desktop – Open source streaming torrent client | Hacker News -
GitHub · Where software is built -
Vis: A Vim Like Text Editor | Hacker News -
Workflow automation : haskell -
Internet hyperlinks do not infringe copyright, EU court advised | Reuters -
Internet hyperlinks do not infringe copyright, EU court advised | Hacker News -
Why Erlang Matters -
Why Erlang Matters | Hacker News -
Android Studio 2.0 | Android Developers Blog -
Android Studio 2.0 Released | Hacker News -
Design better databases | Hacker News -
Open Whisper Systems >> Blog >> Signal Desktop beta now publicly available -
Hidden latency in the Linux network stack | Hacker News -
React v15.0 | React -
React v15.0 | Hacker News -
Don't wrap text in <span> elements by mwiencek · Pull Request #5753 · facebook/react -
Reddit: The Official App For iPhone and Android. Meow. -
React v15.0 | Hacker News -
Computer paints 'new Rembrandt' after old works analysis - BBC News -
EX-99.1 -
Jeff Bezos’ letter to shareholders | Hacker News -
Erlang 19.0 Garbage Collector | Hacker News -
Why does Go seem to be the most heavily criticised among the newer programming languages? - Quora -
Why Everyone Hates Go · npf.io -
Erlang Garbage Collection Details and Why They Matter | Hacker News -
Building a Better Custom Haskell Prelude | Hacker News -
Knuth versus Email -
stack support for yesod devel -
LiveReload with Yesod + Stack on existing project? : haskell -
Yesod quick start guide -
yesodweb/yesod: A RESTful Haskell web framework built on WAI. -
numeric-prelude: An experimental alternative hierarchy of numeric type classes | Hackage -
Building a Better Custom Haskell Prelude -
Performance comparison between s3fs and riofs · Issue #6 · skoobe/riofs -
Efficient Integer Overflow Checking in LLVM – Embedded in Academia -
Efficient Integer Overflow Checking in LLVM | Hacker News -
Congratulations! You’ve Been Fired - The New York Times -
Could the String problem be solved as an compiler optimisation ? : haskell -
It's the IO type, not IO monad. : haskell -
Domino's: Pizza and Payments -
Domino's: Pizza and Payments | Hacker News -
Haskell Diary #2 - Algebra of Data Types -
Haskell Diary #2 - Algebra of Data Types : haskell -
HP Claims Innovation Over Apple With 'World's Thinnest Laptop' - Mac Rumors -
Apple Seeds First OS X 10.11.5 El Capitan Beta to Developers - Mac Rumors -
Open-sourcing Haxl, a library for Haskell | Engineering Blog | Facebook Code -
A year after its launch, it’s now clear that pretty much no one needs an Apple Watch - Quartz -
Nginx Caching - Servers for Hackers -
Stumbled across SublimeHaskell when setting up a new laptop for dev. Very nice Haskell integration with plenty of features. : haskell -
Jyt -
LLVM based Just-in-Time-Compiler for C++ | Hacker News -
Arq 5: Massively faster backup and restore - Arq Backup Blog : Arq Backup Blog -
Arq 5: Massively faster backup and restore for Mac and Windows | Hacker News -
Neil Mitchell's Haskell Blog: GHCid 0.6 Released -
Facebook Messenger Platform | Hacker News -
GitHub’s CSP journey - GitHub Engineering -
Haskell for all: Worst practices should be hard -
Worst practices should be hard : haskell -
Forbes Site, After Begging You Turn Off Adblocker, Serves Up A Steaming Pile Of Malware 'Ads' | Techdirt -
Forbes Site, After Begging You Turn Off Adblocker, Serves Up Malware 'Ads' | Hacker News -
Haskell vs. Erlang, Reloaded | Lambda the Ultimate -
functional programming - Haskell vs Erlang for web services - Programmers Stack Exchange -
Erlang 19.0 Garbage Collector | Hacker News -
Erlang Solutions - Erlang/OTP 19 GC -
DevOps is dead. Long live DevOps | Hacker News -
Managed services killed DevOps | TechCrunch -
vim/version8.txt at master · vim/vim · GitHub -
Vim 8.0 is coming | Hacker News -
Third-Party Reddit Apps Pulled From App Store for NSFW Content [Updated] - Mac Rumors -
Facebook Introduces Chatbots for Messenger, Pushes Further Into Live Video - Mac Rumors -
9.7" iPad Pro's 'Excellent' Display is 'Major Upgrade' Over iPad Air 2 - Mac Rumors -
bye — Medium -
Min | A smarter, faster web browser -
Show HN: Min – web browser with better search and built-in ad blocking | Hacker News -
3 Approaches to Monadic API Design in Haskell - Pusher Blog -
How the 'FIFA Soccer' Video Games Get Such Awesome Soundtracks | GQ -
Apple Jumps On the WebRTC Bandwagon - Post - No Jitter -
Apple Jumps on the WebRTC Bandwagon | Hacker News -
QuakeNet IRC Network- Article - Trust is not transitive: or why IRC over SSL is pointless. -
Why IRC over SSL is pointless (2009) | Hacker News -
Post-Mortem for Google Compute Engine’s Global Outage on April 11 | Hacker News -
CaptionBot by Microsoft | Hacker News -
The WHATWG Blog — Adding JavaScript modules to the web platform -
Adding JavaScript modules to the web platform | Hacker News -
Docker 1.11: The first OCI-compliant runtime, built on containerd | Hacker News -
PlayTBC.com | Experience the finest - World of Warcraft: The Burning Crusade -
Atom text editor 1.7.0 released | Hacker News -
Creating signed GitHub releases - Debian Wiki -
Announcing Rust 1.8 - The Rust Programming Language Blog -
Announcing Rust 1.8 | Hacker News -
Warcraft fans' fury at Blizzard over server closure - BBC News -
Warcraft fans' fury at Blizzard over server closure | Hacker News -
Nostalrius Memories : wow -
In Loving Memory - WoWWiki - Wikia -
Google App Engine Silently Stopped Sending Email 5 Weeks Ago | Hacker News -
Renderings vs. Reality: The Improbable Rise of Tree-Covered Skyscrapers - 99% Invisible -
Renderings vs. Reality: The Improbable Rise of Tree-Covered Skyscrapers | Hacker News -
The Famous Photo of Chernobyl's Most Dangerous Radioactive Material Was a Selfie | Atlas Obscura -
Famous Photo of Chernobyl's Most Radioactive Material Was a Selfie | Hacker News -
How (and why) to make your software faster | Hacker News -
How (and why) to make your software faster -
1031899 – Github spy on user reading emails -
Two-factor authentication for Apple ID | Hacker News -
Keeping secrecy the exception, not the rule: An issue for both consumers and businesses - Microsoft on the Issues -
Keeping secrecy the exception, not the rule | Hacker News -
A New Map for America | Hacker News -
When Lobbying was Illegal -
When Lobbying was Illegal | Hacker News -
Sorry, You Can’t Speed Read - The New York Times -
jaspy/README.rst at master · koehlma/jaspy -
Jaspy – A Python VM written in JavaScript | Hacker News -
Challenges of micro-service deployments -
Challenges of micro-service deployments | Hacker News -
Why does Haskell, in your opinion, suck? : haskell -
Advanced DDoS Protection and Mitigation | CloudFlare | The web performance & security company -
Checked exceptions with open-signals : haskell -
Shtetl-Optimized » Blog Archive » Grading Trudeau on quantum computing -
Grading Trudeau on quantum computing | Hacker News -
Why aren’t we using SSH for everything? | Hacker News -
Why learning Haskell/Python makes you a worse programmer - All Unkept -
Why learning Haskell/Python makes you a worse programmer (2006) | Hacker News -
How a Diode Changed My Life | Hacker News -
An interesting html5-animated clock | Hacker News -
What are row types exactly? : haskell -
Google CDN Beta is here ... and it's already one of the fastest CDNs out there | Hacker News -
[ANNOUNCE] GHC 8.0.1 release candidate 3 available -
[ANNOUNCE] GHC 8.0.1 release candidate 3 available : haskell -
Deepjazz: AI-generated 'jazz' | Hacker News -
Static website on S3, CloudFront and Route 53, the right way! – Un blog sur tout et rien -
Does async syntax have any advantages over do notation? : haskell -
Attoparsec & Line Numbers : haskell -
When does one consider using Arrows? : haskell -
Mad scientist shrinks Arduino to size of an AA battery | TechCrunch -
Intel Releases the Arduino 101 Source Code | Hacker News -
Ravens score just as high as big-brained chimps on cognitive tests | Hacker News -
When it comes to brains, size doesn’t matter | Ars Technica -
Coding Inspiration: 3 Teen Sisters Crush Coding Records, Stereotypes -
Can we have -XHaskell2016 which turns on the most commonly used and safe extensions? : haskell -
Reducing garbage-collection pause time in a Haskell program : haskell -
Reducing garbage-collection pause time in a Haskell program - Stack Overflow -
why Haskell cannot do pattern match ? : haskell -
Apple pays $25M to a university—and the patent troll it cut a deal with | Ars Technica -
Thoughts on using _ as syntactic sugar for partially applied functions. : haskell -
FBI Paid More Than $1 Million to Hack San Bernardino iPhone - WSJ -
FBI Paid More Than $1M to Hack San Bernardino iPhone | Hacker News -
How can I do this in hamlet? : haskell -
Performance These Days | Hacker News -
How fast can we make interpreted Python? (2013) | Hacker News -
Why is haskell.org powered by some much PHP? : haskell -
Performance issues in my factoring function? : haskell -
Haskell.org News : haskell -
(Newbie) Certain I'm making a very basic mistake, but not entirely sure where in this Haskell Text Editor : haskell -
[ANN] Stratosphere: AWS CloudFormation EDSL in Haskell : haskell -
[Haskell-cafe] [ANN] Stratosphere: AWS CloudFormation EDSL in Haskell -
Haskell's Type Classes: We Can Do Better – John A De Goes -
Reducing garbage-collection pause time in a Haskell program : haskell -
The average size of Web pages is now the average size of a Doom install | Hacker News -
The web is Doom - mobiForge -
Edward Snowden: The Internet Is Broken | Popular Science -
Edward Snowden: The Internet Is Broken | Hacker News -
Why Write in English? by Tim Parks | NYR Daily | The New York Review of Books -
Why Write in English? | Hacker News -
One Easy Way to Make Wikipedia Better - The Atlantic -
Yakuza 3 reviewed by Yakuza / Boing Boing -
Haskell's Typeclasses: We Can Do Better : haskell -
So, you want to learn bookkeeping | Hacker News -
One Easy Way to Make Wikipedia Better | Hacker News -
The impact of Prince’s death on Wikipedia | Hacker News -
William Shakespeare, Playwright and Poet, Is Dead at 52 | Hacker News -
Cached deriving : haskell -
simple Haskell question!!! Please help me!! : haskell -
Web framework for building browser UI for an app running LAN/localhost : haskell -
Use OpenGL to get more RAM | Hacker News -
GitHub - graphitemaster/moreram: Get more system memory -
Something Secret This Way Comes — Llogiq on stuff -
[Rust] Something Secret This Way Comes | Hacker News -
Show HN: QBE – a new compiler back end | Hacker News -
Functors, Applicatives, and Monads in Plain English | Hacker News -
Russ Bishop - Functors, Applicatives, and Monads in Plain English -
William Shakespeare, Playwright and Poet, Is Dead at 52 - The New York Times -
The Science of Making Friends | Hacker News -
CloudFlare Now Supporting More Ports -
A web based dashboard for monitoring and controlling Philips Hue lights : haskell -
blitzcode/hue-dashboard: A web based dashboard for monitoring and controlling Philips Hue lights -
mtl: Monad classes, using functional dependencies | Hackage -
monad-log: A simple and fast logging monad | Hackage -
[announcement] monad-log: A simple and fast logging monad : haskell -
Problems installing (starting?) Haskell Platform on OSX : haskell -
The Dark Side of Haskell : haskell -
Newbie question about map and evaluation : haskell -
Dealing with error messages/codes returned within IO : haskell -
Is there any guarantee that a version of Stack will be same tomorrow as it is today? : haskell -
brew/Analytics.md at master · Homebrew/brew -
Homebrew now sends usage information to Google Analytics | Hacker News -
Half Your Brain Is Alert The First Night In A New Place : Shots - Health News : NPR -
Half Your Brain Stands Guard When Sleeping in a New Place | Hacker News -
Docker for Mac Beta Review | Hacker News -
BYOT Bring your own team -
The CIA Waterboarded the Wrong Man 83 Times in 1 Month | Hacker News -
Kidnap A Soviet? Well . . . Shiite Moslems Tried It In Lebanon - Once - philly-archives -
Effectiveness of torture for interrogation - Wikipedia, the free encyclopedia -
Summary of the -XHaskell2016 feedback : haskell -
Basic Syntax Extensions - School of Haskell | School of Haskell -
ANN: ghc-8.0.1-rc4 (quiet update) : haskell -
λ Bubble Pop!(A simple game that illustrates functional computation) : haskell -
λ Bubble Pop! -
A revolutionary new way to access all your files | | Dropbox Business Blog -
Dropbox Project Infinite | Hacker News -
Elixir RAM and the Template of Doom -
Elixir RAM and the Template of Doom | Hacker News -
Lionsgate is making its films available on Steam | Gadgette -
Lionsgate is making its films available on Steam | Hacker News -
World’s Fastest Growing Open Source Platform Pushes Out New Release | Node.js -
Node.js v6.0 Released | Hacker News -
Updating Our Prefixing Policy | WebKit -
WebKit is moving away from prefixes | Hacker News -
GitHub · Where software is built -
Xi editor: A modern editor with a backend written in Rust | Hacker News -
Long Compile Time in Swift? Could be the Literal Parsing Bug -
GHC may be slow, but it's not this slow! : haskell -
Jakub Jelinek - GCC 6.1 Released -
GCC 6.1 Released | Hacker News -
Good IDE for Haskell Stack? : haskell -
Wrangling State In Haskell : haskell -
Wrangling State In Haskell · Deliberate Software -
Xamarin Open Source SDK -
Xamarin Open-Sourced | Hacker News -
Introducing MIR - The Rust Programming Language Blog -
Introducing MIR | Hacker News -
-XOverloadedLists? : haskell -
Difference between haskell-lang/haskell-lang and haskell-infra/hl? · Issue #2 · haskell-lang/haskell-lang -
[Haskell-community] On the design used for haskell.org -
Haskell Incredibly Slow : haskell -
haskell-lang/announcements.md at master · haskell-lang/haskell-lang -
The content of the haskell.org Downloads page - Google Forms -
ANN: Hackage.haskell.org is down : haskell -
How do I download a package's source code with Stack? : haskell -
ANN: Haskell Prime 2020 committee has formed -
ANN: Haskell Prime 2020 committee has formed : haskell -
How we found a bug in Amazon ELB | Hacker News -
Load Your Steam Wallet Using Bitcoin -
Is there any dependently typed language that can actually be used in practice? : haskell -
#1582 (LLVM optimisation passes / tables next to code) – Haskell.org GSOC -
#1608 (Implement 2-3 concurrent data structures from the literature) – Haskell.org GSOC -
#1654 (Make the GHC performance monitoring build bot production ready) – Haskell.org GSOC -
#1681 (Develop cross platform mobile applications using GHCJS/GHC) – Haskell.org GSOC -
#1607 (Haskell for the "real-time web") – Haskell.org GSOC -
#1601 (Library for the Arduino platform) – Haskell.org GSOC -
Design input opportunity: specializing types in GHCi : haskell -
-XGlasgow2016 as an IDE feature? : haskell -
Instagram insists littergram app is renamed - BBC News -
Instagram insists littergram app is renamed | Hacker News -
the objective package is a neat little thing : haskell -
Freelance Haskell Work? : haskell -
Haskell's GC: Anything Special? : haskell -
The GHC Runtime System -
alternatives to firing — Ask a Manager -
Alternatives to Firing | Hacker News -
Hello Lua | Hacker News -
"Haskell programmer seeks forgiveness for using unsafePerformIO at meetup" : haskell -
Who's downloading pirated papers? Everyone | Hacker News -
Infinit announces Project Dropboxe -
Infinit - Decentralized Software‑Based File Storage Platform -
In Reaction to Dropbox's Project Infinite, Infinit Announced Project Dropboxe | Hacker News -
Haskell and GUIs / Vulkan : haskell -
Snowden’s Rubik’s Cube — Matt Davis [da5is.com] -
Snowden’s Rubik’s Cube | Hacker News -
What happened to Haskell Cast? : haskell -
CS Syd - A functor is not a box : haskell -
Path Finding One Liner : haskell -
Simon Peyton Jones elected into the Royal Society Fellowship | Lambda the Ultimate -
CS Syd - A functor is not a box -
Generalizing function application : haskell -
Show HN: Task-Based Electron Web Browser | Hacker News -
Alloy – Task-based web browser -
Warn HN: stacks made executable on GNU by mere presence of assembly code | Hacker News -
Deploy Haskell + Stack Application on Raspberry Pi : haskell -

\ No newline at end of file diff --git a/resources/bookmarks/February.html b/resources/bookmarks/February.html deleted file mode 100644 index b2463da..0000000 --- a/resources/bookmarks/February.html +++ /dev/null @@ -1,239 +0,0 @@ -

February

-

-

Vim and Haskell in 2016 -
Vim and Haskell in 2016 : haskell -
ghcjs + ghc-7.10.3 + lts-5.1 : haskell -
How can the use of State simplify this genetic algorithm-based traveling salesman code? : haskell -
Suggestions about an 1 hour talk of Haskell? : haskell -
Memory profiling in Haskell : haskell -
Top tips and tools for optimising Haskell - Pusher Blog -
Memory profiling in Haskell - Pusher Blog -
Shout — The self-hosted web IRC client -
Shout: The self-hosted web IRC client | Hacker News -
Please don't use Slack for FOSS projects -
Please don't use Slack for FOSS projects | Hacker News -
This Is What Happens When You Reply to Spam Email | James Veitch | TED Talks - YouTube -
Google plans to beam 5G internet from solar drones | Hacker News -
The Power of Privacy – film | Media Network | The Guardian -
15 years of VLC and VideoLAN - Yet another blog for JBKempf -
Alphabet Becomes the Most Valuable Public Company in the World | Hacker News -
Use Atom to edit in Chrome | Hacker News -
Haskell for commercial software development - Wagon -
Haskell in industry : haskell -
Neil Mitchell's Haskell Blog: New HLint version and API features -
PageSpeed Examples Directory -
Nginx module by Google for rewriting web pages to reduce latency and bandwidth | Hacker News -
Dropbox Hack Week: GraphQL Server in Haskell : haskell -
Dropbox Hack Week: GraphQL Server in Haskell – Chad Austin -
Is there a human-oriented JSON/YAML API? : haskell -
GitHub: January 28th Incident Report | Hacker News -
Plain Email – An app concept for efficient one-touch email processing workflow -
Show HN: Plain Email – An app for one-touch email processing | Hacker News -
h-atom -
basile-henry/HaskellSweeper: A Minesweeper clone with infinite grid written in Haskell. -
A Minesweeper clone with infinite grid written in Haskell. Would love some feedback! : haskell -
Programmer Hero | Devpost -
x86 Assembly – A crash course tutorial I | Mark's blog -
Learn Haskell in Y Minutes -
Microsoft Open Source Challenge - Microsoft Research -
Apple's declining software quality - Sudophilosophical -
Apple’s declining software quality | Hacker News -
Month in Haskell Mode January 2016 · haskell/haskell-mode Wiki -
Introducing the Keybase filesystem -
Introducing the Keybase filesystem | Hacker News -
Does 'argument do' have a future? : haskell -
Learn Enough Git to Be Dangerous | Hacker News -
Posting successful SSH logins to Slack -
iPhones 'disabled' if Apple detects third-party repairs - BBC News -
iPhones 'disabled' if Apple detects third-party repairs | Hacker News -
Rebuild & restart program on file change : haskell -
lukexi/halive: Live recompiler for Haskell -
Monoids and their efficiency in practice | -
Monoids and their efficiency in practice : haskell -
haskell-ide-engine/Report-2016-01.md at master · haskell/haskell-ide-engine -
Month in haskell-ide-engine January 2016 : haskell -
Which web framework should I choose? : haskell -
Composition vs Higher Order Functions? : haskell -
New type of ($) operator in GHC 8.0 is problematic -
Haskell for all: How to make your Haskell code more readable to non-Haskell programmers -
[ghc-devs] New type of ($) operator in GHC 8.0 is problematic : haskell -
ARM Cloud - RunAbove -
Why cross compile when you can just compile? : haskell -
Parser Combinators: Parsing for Haskell Beginners : haskell -
Easy universal Haskell development environment -
Easy universal Haskell development environment (using Stack and Atom) : haskell -
Announcing servant-swagger and swagger2 : haskell -
Haskell is not trivial, but it's not unfair like Dark Souls either : haskell -
bitemyapp - Haskell is not trivial, but it's not unfair like Dark Souls either -
I am a puts debuggerer | Hacker News -
Learn c in Y Minutes -
haskelliseasy -
http://haskelliseasy.com : haskell -
[Intermediate] Recommend a learning path? : haskell -
[ANNOUNCE] GHC 8.0.1 release candidate 2 -
[ANNOUNCE] GHC 8.0.1 release candidate 2 : haskell -
Is this an okay usage of unsafePerformIO? : haskell -
Trouble with loading stack project into REPL : haskell -
More fun to build an interpreter in Haskell or node.JS? : haskell -
Records: Python library for making raw SQL queries to Postgres databases | Hacker News -
Getting Started Pains : haskell -
Origin of "(Learn|Write) You(rself)? (a|an|Some) [language]" idiom? : haskell -
Is the title "Learn you a Haskell for Great Good" a reference to something? : haskell -
What Haskell book would you recommend for an experienced 'C' programmer. I would appreciate recommendations that covers the language features in a comprehensive manner. I am OK with recommendations that are of formal/academic bent. TIA. : haskell -
Monads as Adjectives : haskell -
[Haskell-cafe] [ANN] aeson 0.11.0.0 -
[ANN] aeson 0.11.0.0 : haskell -
Funding testimonial: Divio | Weblog | Django -
Funding Django is not an act of charity, it's an investment | Hacker News -
Hacker Publishes Personal Info of 20,000 FBI Agents | Motherboard -
Hacker Publishes Personal Info of 20,000 FBI Agents | Hacker News -
What Keeps Couples Happy Long Term | Hacker News -
The Genius of WordPress (and why it’s doomed) — Medium -
Printing to the terminal and the browser. (Question) : haskell -
Can someone explain the basics of IO and >> to me? : haskell -
Getting Started with the LLVM System — LLVM 3.9 documentation -
Is it just me, or does `IO`s definition give you the chills? : haskell -
How to write this type of expressions more elegantly? : haskell -
why this program run significantly faster when profiling is enabled?? : haskell -
GHC performance over time : haskell -
Google is banning Flash from its display ads | Hacker News -
Idea: Syntax Highlighting for variables : haskell -
GHC performance over time | Mostly nerdless -
How to get rid of unsafePerformIO in this interpreter? : haskell -
What It Feels Like to Freeze on National Television | Dr Benjamin Habib -
What It Feels Like to Freeze on National Television | Hacker News -
A modest Haskell critique from the Cap'n Proto author : haskell -
Cap'n Proto: Cap'n Proto v0.2: Compiler rewritten Haskell -> C++ -
Adblock via /etc/hosts | Hacker News -
Swift Protocols and the Promised Land, with Michele Titolo - Realm is a mobile database: a replacement for SQLite & Core Data -
Swift Protocols and the Promised Land | Hacker News -
Answer - Quora -
On-site Haskell/Python(!) opportunity at Positive Technologies (Moscow) : haskell -
JohnReedLOL/HaskellPrintDebugger: Prints lines with IDE friendly stack traces in Haskell. -
Haskell Print Debugger with IDE friendly Stack Trace : haskell -
Lightweight http server options : haskell -
Initial response from GitHub · bkeepers/dear-github@4afa490 -
GitHub responds to Dear GitHub letter | Hacker News -
Lesbians tend to earn more than heterosexual women | Hacker News -
I3ck/HGE2D: 2D game engine written in Haskell -
Moved the game engine out of my game -> HGE2D : haskell -
Stop lying about Haskell : haskell -
For a non-expert, what's different between Bayesian and frequentist approaches? | Hacker News -
Stack traces in GHCi, coming in GHC 8.0.1 · Simon Marlow -
Stack traces in GHCi, coming in GHC 8.0.1 · Simon Marlow : haskell -
QuickPlot: Quick and easy data visualization with Haskell | Hackage -
Twitter: Years After the Alphabet Acquisition — Medium -
Mitigating DDoS Attacks with NGINX and NGINX Plus - NGINX -
Mitigating DoS Attacks with Nginx | Hacker News -
Tests vs Types : haskell -
atemerev/skynet: Skynet 1M threads microbenchmark -
Scala, Haskell, Erlang, and Go compared on a million threads : haskell -
I no longer understand my PhD dissertation | Hacker News -
Is anything being done to remedy the soul crushing compile times of GHC? : haskell -
Cofun with Cofree Comonads by David Laing - YOW! Lambda Jam 2015 | Eventer -
Haskell for all: Why free monads matter -
Learn X in Y Minutes: Scenic Programming Language Tours -
Download FFmpeg -
FFmpeg 3.0 released | Hacker News -
How to Distribute Binaries for OS X Using Homebrew -
How to Distribute Binaries for OS X Using Homebrew | Hacker News -
Why I No Longer Use MVC Frameworks | Hacker News -
Adrian Sampson: Run an LLVM Pass Automatically with Clang -
When a Self-Declared Genius Asks You to Read His Masterpiece | Literary Hub -
When a Self-Declared Genius Asks You to Read His Masterpiece | Hacker News -
Search Hillary Clinton’s Emails | Hacker News -
Uber's Atomic Meltdown — Eli Schiff -
Uber's Atomic Meltdown | Hacker News -
Chromabits - Serving a Hakyll site with Warp/Wai -
Serving a Hakyll site with Warp/Wai : haskell -
Vulkan is Here | Hacker News -
A Message to Our Customers | Hacker News -
Customer Letter - Apple -
No, A Judge Did Not Just Order Apple To Break Encryption On San Bernardino Shooter's iPhone, But To Create A New Backdoor | Techdirt -
Apple ordered to bypass auto-erase on San Bernadino shooter's iPhone | Hacker News -
Code quality metrics for Haskell : haskell -
Best practices for modifiable state in Haskell? : haskell -
GitHub launches templates for new issues and pull requests -
What do you mean ‘we need more time’?? | Dropbox Tech Blog -
What do you mean ‘we need more time’? Project schedule estimation in software | Hacker News -
Deutsche Bank: Germany's financial colossus stumbles | Business | The Guardian -
Deutsche Bank: Germany's Financial Colossus Stumbles | Hacker News -
For gifted children, being intelligent can have dark implications | Calgary Herald -
For gifted children, being intelligent can have dark implications (2015) | Hacker News -
A Haskell vs Perl Performance Case | Cagesong on Earth -
A Haskell vs Perl Performance Case : haskell -
Resource management problem while trying to design a monadic graphics rendering library. Advices? : haskell -
Hython - a toy Python 3 interpreter written in Haskell : haskell -
iTerm2 Version 3 Now in Beta | Hacker News -
Apple Fixes iPhones Disabled By Error 53 Caused By Unofficial Repairs -
Apple Apologizes and Updates iOS to Restore iPhones Disabled by Error 53 | Hacker News -
literally words — Graphing when your Facebook friends are awake -
Graphing when your Facebook friends are awake | Hacker News -
Stack v1.0.4 : haskell -
Release v1.0.4 · commercialhaskell/stack -
The Joy and Agony of Haskell in Production -
The Joy and Agony of Haskell in Production : haskell -
Graphics and FPL: Pure API vs. IO-bound API for graphics frameworks -
Ayn Rand Lamp | Hacker News -
Ayn Rand Lamp: Video : JACK RYAN -
WINE 1.9.4 Released | Hacker News -
Snowden: FBI obscuring crucial details in Apple case | Hacker News -
State of the Haskell Ecosystem – February 2016 | Hacker News -
Haskell for all: State of the Haskell Ecosystem - February 2016 Edition -
Summary of Haskell recommendations I received ... Thanks for all those who took time to respond. : haskell -
Haskell for all: State of the Haskell Ecosystem Feb 2016 Edition : haskell -
Inside Noma, the restaurant cooking up the future of food (Wired UK) -
Noma's taste of tomorrow: creating the future of food | Hacker News -
Joshua Browder's Bot for parking tickets -
A bot has successfully appealed $3M worth of parking tickets in the UK | Hacker News -
AI generated music to improve focus, relaxation and sleep | Hacker News -
Proving With Types : haskell -
It Is What It Is (And Nothing Else), Robert Harper : haskell -
Mark Zuckerberg Backs Apple in Its Refusal to Unlock iPhone - The New York Times -
Mark Zuckerberg Backs Apple in Its Refusal to Unlock iPhone | Hacker News -
Justice Department Seeks to Force Apple to Extract Data From About 12 Other iPhones - WSJ -
Draft.js | Rich Text Editor Framework for React -
Draft.js – Rich Text Editor Framework for React | Hacker News -
WikiLeaks reveals the NSA spied on Berlusconi and his closest advisors - l'Espresso -
WikiLeaks reveals the NSA spied on Berlusconi and his closest advisors | Hacker News -
A New Breed of Trader on Wall Street: Coders with a Ph.D | Hacker News -
How to stop functional programming - BAM Weblog -
How to stop functional programming | Hacker News -
Bill Gates calls for terror data debate - BBC News -
Bill Gates calls for terror data debate | Hacker News -
Which book should I buy ? : haskell -
Remote Haskell developer role : haskell -
What's in a Haskell parsing combinator library. : haskell -
Megaparsec 4 and 5 : haskell -
Megaparsec 4 and 5 by Mark Karpov -
Justice Department Wants Apple to Unlock Nine More iPhones | Hacker News -
Need advice using Haskell to parse HTML/CSS to optimize CSS files. : haskell -
Fake girlfriend, revisited - BBC News -
Fake girlfriend, revisited | Hacker News -
Announcing Spotify Infrastructure’s Googley Future | News -
Spotify moves its back end to Google Cloud | Hacker News -
Dear Skype/Microsoft -
Skype keeps ignoring people who complain or are having issues with Linux client | Hacker News -
How to Disable Ads on Your Windows 10 Lock Screen -
Stripe: Atlas -
Stripe Atlas | Hacker News -
Windows Now Showing Full-Screen Ads | Hacker News -
Apple Is Said to Be Working on an iPhone Even It Can’t Hack | Hacker News -
[Advice/request] Intern/Short-Term working with haskell. : haskell -
Overview of all Haskell repos in Github - Post your interesting finds! : haskell -
Haskell developer roles at Standard Chartered [London, Singapore] | Control.Monad.Writer -
Six (6) Haskell developer roles at Standard Chartered [London, Singapore] : haskell -
PHP 7 – The next big thing for Magento -
PHP 7 – The next big thing for Magento | MGT-Commerce Blog -
"Haskell is Not For Production and Other Tales" - Katie Miller @ Linux.conf.au 2016 (Haxl@Facebook talk) : haskell -
Microsoft Acquires Xamarin | Hacker News -
Apple Hires Developer Behind Signal, Edward Snowden’s Favorite Secure Chat App | Hacker News -
vulkan 1.0: bindings to the Vulkan graphics API : haskell -
Report from the VMware GPL court hearing | Hacker News -
Upgrading from React 0.11.2 to 0.14.7 in 347 easy steps | Hacker News -
Space leak in a simple attoparsec parser : haskell -
Haskell for all: Auto-generate a command line interface from a data type -
Leksah 0.15.2.0 - Many UI improvements and some stack support. : haskell -
Haskell for all: Auto-generate a command line interface from a data type : haskell -
Ten years on LinkedIn | Hacker News -

\ No newline at end of file diff --git a/resources/bookmarks/Have Read, 2016.html b/resources/bookmarks/Have Read, 2016.html deleted file mode 100644 index 8dbdc36..0000000 --- a/resources/bookmarks/Have Read, 2016.html +++ /dev/null @@ -1,2982 +0,0 @@ - - - - Bookmarks -

Bookmarks

-

Bookmarks Menu

-

-

Have Read

-

-

January, 2016

-

-

The Performance of Open Source Software | Warp -
The Architecture of Open Source Applications (Volume 2): The Glasgow Haskell Compiler -
Serving an API - haskell-servant -
Data-Pipelines in Haskell -
Accidentally Quadratic — GHC Derived Foldable and Traversable Instances -
The convergence of compilers, build systems and package managers : Inside 736-131 : haskell -
Towards general techniques in programming - the second diagonal principle - Correct Software -
Using applicative functors for reactive web interfaces: purescript-flare : haskell -
The convergence of compilers, build systems and package managers : Inside 736-131 -
Neil Mitchell's Haskell Blog: What's the point of Stackage LTS? -
Is there any point in having Stackage LTS? : haskell -
[ANNOUNCE] Glasgow Haskell Compiler version 7.10.3 -
1.7. Release notes for version 7.10.3 -
GHC 7.10.3 is here! : haskell -
Neil Mitchell's Haskell Blog: MinGHC is Dead, Long Live Stack -
MinGHC is Dead, Long Live Stack : haskell -
Django CMS 3.2 released | Hacker News -
django CMS 3.2 - Blog - django-cms.org -
Facebook to open-source AI hardware design | Hacker News -
Facebook to open-source AI hardware design | Engineering Blog | Facebook Code -
Stack + Nix = portable reproducible builds -
Stack and Nix = portable reproducible builds | Hacker News -
What happened to Haskell on the web framework benchmark game? : haskell -
How stack can use Docker under the hood | FP Complete -
Anyone using Neovim ? : haskell -
Haskell Skyline -
Phabricator: Add kind equalities to GHC. -
Kind equalities are now in GHC HEAD! : haskell -
New release of Haskell bindings to libclang! : haskell -
Using Stack with GHC 7.10.3 | FP Complete -
Sufficiently Smart Compiler -
osa1 - On sufficiently smart compilers -
On sufficiently smart compilers : haskell -
[1512.01896] In the Age of Web: Typed Functional-First Programming Revisited -
In the Age of Web: Typed Functional-First Programming Revisited : haskell -
How Elon Musk and Y Combinator Plan to Stop Computers From Taking Over — Backchannel — Medium -
How Elon Musk and Y Combinator Plan to Stop Computers from Taking Over | Hacker News -
Foldable scans : haskell -
AutoPar: Automating the Parallelization of Functional Programs -
Chris Lattner on Swift and dynamic dispatch | Hacker News -
Elm by Example | Hacker News -
[swift-evolution] Proposal: Universal dynamic dispatch for method calls -
Haste thesis: A Distributed Haskell for the Modern Web : haskell -
Exploratory Programming with Haskell : haskell -
Exploratory Haskell -
Haskell for all: How to contribute to the Haskell ecosystem : haskell -
Haskell for all: How to contribute to the Haskell ecosystem -
mpickering - Pattern Synonyms in GHC 8.0 -
Pattern Synonyms in GHC 8.0 : haskell -
Stackage Nightly and LTS moving soon to ghc-7.10.3 -
Haddock for GHC 8.0 -
Help bring beautiful documentation to GHC 8.0 — Volunteers needed to get Haddock ready for release : haskell -
Finite Automata -
Context-Free Grammars -
Does this library exist or should I try and make it? : haskell -
Philips Hue blocks 3rd party lights | Hacker News -
Why is the first random value produced from a StdGen so predictable? : haskell -
The Slack Platform Launch | Several People Are Typing -
How to set up your open source Haskell project on github with automated testing via Travis (example commit) : haskell -
Efficient parsing of large text files, part 1 | hbtvl -
Efficient parsing of large text files, part 2 | hbtvl -
Efficient parsing of large text files, part 3 | hbtvl -
Split into sum : haskell -
High Performance Log Parsing in Haskell: Part Two : haskell -
Efficient parsing of large text files, part 4 and conclusion | hbtvl -
Why Python 3 Exists | Hacker News -
Brett Cannon - Why Python 3 exists -
Compiling to WebAssembly: It’s Happening! ★ Mozilla Hacks – the Web developer blog -
Compiling to WebAssembly: It’s Happening | Hacker News -
Georgia Tech Researchers Demonstrate How the Brain Can Handle So Much Data | Georgia Tech - College of Computing -
Georgia Tech Researchers Demonstrate How the Brain Can Handle So Much Data | Hacker News -
Updated WebGL Benchmark Results – Unity Blog -
Updated WebGL Benchmark Results | Hacker News -
Algebraic Data Types Demystified · Haskell for Mac -
Intro PureScript for a Haskeller : haskell -
Reflecting on Haskell in 2015 -
The long process of creating a Chinese font | Hacker News -
Does Haskell make promises it can't keep? The problem with wrapping numeric types : haskell -
Promises.md -
What's the best way to create GUI for Haskell program? : haskell -
Typeclass counter examples : haskell -
Thomas Byttebier - The best icon is a text label -
The best icon is a text label | Hacker News -
Adam Bergmark, Stackage update and plans for LTS Haskell 4 -
Stackage update and plans for LTS Haskell 4 : haskell -
ARoW.info Blog -- PureScript for the Haskeller -
PostgreSQL: PostgreSQL 9.5 RC1 Released -
PostgreSQL 9.5 RC1 Released | Hacker News -
Big company vs. startup work and compensation -
Beating the Averages -
An article about the power of Lisp. Where does Haskell fall on this "spectrum"? I know Haskell is very high-level, but is Lisp in some sense still more powerful than Haskell? Also what do you think about Haskell vs. Lisp for a startup in 2015? (That article is from 2003 so things may have changed.) : haskell -
Report more than one syntax error in Parsec? : haskell -
tel/serv -
Serv — Dependently typed API servers, clients, and documentation : haskell -
Big Company vs. Startup Work and Compensation | Hacker News -
articles/tag-dont-type.md at master · quchen/articles -
Tag, don't type : haskell -
andyfriesen.com - Haskell Basics: How to Loop -
Haskell Basics: How to Loop : haskell -
Kubernetes for Haskell Services | FP Complete -
mnot’s blog: Why 451? -
Why 451? | Hacker News -
CS Syd - Haphviz: Graphviz code generation with Haskell : haskell -
Welcome to MkLinux.org -
MkLinux: Apple-Funded Port of Linux to the Power Mac / Mach Microkernel | Hacker News -
Real-time GIF images | Hacker News -
Two Wrongs – Using withPtr From inline-c in Haskell -
Using withPtr From inline-c in Haskell : haskell -
The Procrastination Matrix - Wait But Why -
The Hacks of Life: The Dangers of Super Smart Compilers -
The Dangers of Super Smart Compilers | Hacker News -
Juniper screenOS authentication backdoor - master ssh password posted | Hacker News -
How do I learn to write code like the post doc Haskell programmer? : haskell -
Ask-Elle: an adaptable programming tutor for Haskell giving automated feedback [PDF] : haskell -
idea for tryhaskell - mine data for common beginner errors : haskell -
A philosophical difference between Haskell and Lisp : haskell -
A philosophical difference between Haskell and Lisp -
Stream fusion and composability (Java 8 and Haskell) for newbies -
Install Plex Media Server on Raspberry Pi 2 • -
Haskell and Lisp Comparisons in Podcast : haskell -
Write You a Haskell ( Stephen Diehl ) Introduction -
Kwm, tiling window manager for OS X | Hacker News -
koekeishiya/kwm -
Vagrant 1.8 - HashiCorp -
The biggest mystery in mathematics: Shinichi Mochizuki and the impenetrable proof : Nature News & Comment -
On the Juniper backdoor | Hacker News -
A Few Thoughts on Cryptographic Engineering: On the Juniper backdoor -
Vagrant 1.8 released | Hacker News -
Nikita Churaev / dialog · GitLab -
Dialog: 21st century style replacement for putStrLn (my first package) : haskell -
Front Row Education Engineering — Servant style handlers for Yesod -
Servant style handlers for Yesod : haskell -
[Haskell-community] (no subject) -
haskell.org is now a 501(c)(3) charity in its own right : haskell -
I analyzed 250 SaaS pricing pages — here’s what I found -
I analyzed 250 SaaS pricing pages | Hacker News -
Electron at Wagon - Wagon -
My First Two Weeks of Haskell at Wagon - Wagon -
GitLab 8.3 released with Auto-merge and GitLab Pages | Hacker News -
Kim Dotcom loses extradition case, files immediate appeal | Stuff.co.nz -
A Python programmer discovers Haskell : haskell -
haskell in 8th grade : haskell -
ANN: stack-1.0.0 : haskell -
[Haskell-cafe] ANN: stack-1.0.0 -
Porting xv6 to Haskell : haskell -
State considered harmful - A proposal for a stateless laptop (new paper) -
"State considered harmful": A proposal for a stateless laptop (?!) : haskell -
Kim Dotcom loses extradition case | Hacker News -
How to setup Leksah? : haskell -
Haskell on Topcoder : haskell -
Why Copenhagen Has Such Great Water - CityLab -
Why Copenhagen Has Almost Perfect Water | Hacker News -
Unison: How laziness brings good query performance without an insane black box optimizer -
A Haskell Compiler (61) -
Can't build GHCJS with Stack on Ubuntu 15.10 : haskell -
How come refined isn't in base? : haskell -
[help] performance problem : haskell -
What would you reccomend for a non programmer to delve into haskell ? : haskell -
What Code Does vs What Code Means -
Much faster incremental apt updates | Blog of Julian Andres Klode -
Much faster incremental apt updates | Hacker News -
TLDR pages -
TLDR pages | Hacker News -
What happens when you apply length to an infinite list. : haskell -
Predicate on length of infinite list : haskell -
Some thoughts/questions on writing typesafe and secure web APIs : haskell -
Predicate Higher Order Functions : haskell -
Logical Graphs LLC: Trading Analytics -
Some Haskell libraries on trading analytics I couldn't find on Hackage. Who made these? : haskell -
What are Haskellers' opinion/critiques of Perl6? : haskell -
The Future of Node Is in Microsoft’s Fork | Hacker News -
Тех-Детали: nginx module to enable haskell binding to nginx configuration files -
Re: Testing Micro Services - Google Groups -
nginx module to enable haskell binding to nginx configuration files : haskell -
Looking for help to trace/improve memory/GC performance of something? : haskell -
How T-Mobile wanted to change itself but ended up changing the wireless industry | Hacker News -
Blog series: Typed up CRUD SPA with Haskell and Elm : haskell -
Interview with Jesper Louis Andersen about Erlang, Haskell, OCaml, Go, Idris, the JVM, software and protocol design — PART I : haskell -
Categories for the Working Mathematician : haskell -
Month in Haskell Mode December 2015 · haskell/haskell-mode Wiki -
Designing a GUI library purely in Haskell : haskell -
Haskell stack, Yesod and Docker : haskell -
Best Haskell Books for 2015 -
Best Haskell Books for 2015 : haskell -
Haskell for all: Compile-time memory safety using Liquid Haskell : haskell -
Why Age of Empires 2 is still growing | Hacker News -
haskell-ide-engine/Report-2015-12.md at master · haskell/haskell-ide-engine -
Difference between . and $ : haskell -
openage by [SFT]Technologies -
Openage – Free Age of Empires 2 engine clone | Hacker News -
jonathanslenders/pymux -
Pymux: a tmux clone in pure Python | Hacker News -
Will GHC's multiple-passes or advanced optimizations ever rewrite the contents of the enclosed files such that they'll result in the same binary? : haskell -
A Modern Architecture for FP : haskell -
Peter Naur - Wikipedia, the free encyclopedia -
Peter Naur has died | Hacker News -
ghc-mod 5.5.0.0 pre-release, testers wanted : haskell -
What's the major benefit of everything being immutable, anyway? : haskell -
Wi-Fi Alliance® introduces low power, long range Wi-Fi HaLow™ | Wi-Fi Alliance -
Wi-Fi Alliance introduces low power, long range Wi-Fi HaLow | Hacker News -
New Adventures for Elm -
New Adventures for Elm | Hacker News -
Visible type application will be part of GHC 8.0 : haskell -
I worked at Amazon to see if anything's changed | Hacker News -
Haskell LTS-4.0 is here! : haskell -
Are StrictHaskell and OverloadedRecordFields the beginning of Haskell 2.0? : haskell -
Status of School of Haskell 2.0 : haskell -
Status of School of Haskell 2.0 | FP Complete -
Question regarding performance : haskell -
Is there any hope to see the "import problem" solved ? : haskell -
GHC/QualifiedModuleExport - HaskellWiki -
Prelude> 2 ^ 3 ^ 4 ^ 5 hangs stack ghci : haskell -
Convolutionnal Neural Network in Haskell? : haskell -
RBM and Backprop implementation : haskell -
#1681 (Develop cross platform mobile applications using GHCJS/GHC) – Haskell.org GSOC -
Develop cross platform mobile applications using GHCJS/GHC : haskell -
How export/import a list of submodules; project organization : haskell -
ANN: QuickLift, a Haskell/PureScript single page app : haskell -
Optimizing list comprehensions : haskell -
Do Free Monads Actually Matter? : haskell -
CodeReddit: Cover UI for Reddit at Work | Hacker News -
[Haskell-cafe] AMP - how do you motivate this in teaching? -
Paypal froze our funds, then offered us a business loan — Medium -
Paypal froze our funds, then offered us a business loan | Hacker News -
Using the OS X 10.10 Hypervisor Framework: A Simple DOS Emulator | Hacker News -
Announcing: C◦mp◦se Unconference and Exchange : haskell -
ghc-shake: Reimplementing ghc –make : Inside 736-131 -
Reimplementing ghc --make with Shake : haskell -
Why I love Snapchat | Hacker News -
Haskell Backpack and GHC 8.x : haskell -
Convert IO String to String : haskell -
Would /r/haskell be interested in funding those projects? : haskell -
bloomberg/ocamlscript -
OCamlScript – a JavaScript back end for OCaml | Hacker News -
Install and setup IHaskell using Stack (on Ubuntu) step-by-step : haskell -
Install IHaskell on Ubuntu 14.04 with Stack — Pythux -
how to make it work so that I can do two things after a maybe -> : haskell -
wandernauta/viskell -
Viskell: Visual programming meets Haskell. Visual programming environment for a typed (Haskell-like) functional programming language. : haskell -
Telegram Bot API bindings : haskell -
Detecting type errors with GHCi commands? : haskell -
Your favourite language extension? : haskell -
What do you call this kind of type inference : haskell -
What is the motivation for extensible effects? : haskell -
What is the current state of ghci or any editor documentation tools? : haskell -
Catamorphisms in 15 Minutes : haskell -
How Optimizely Almost Got Me Fired (2014) | Hacker News -
Recent Spam : haskell -
Cushion - Expenses -
A short fable of software engineering vs. regular engineering | The Lab Lunch -
A short fable of software engineering vs. regular engineering | Hacker News -
Running costs for running a web app | Hacker News -
1233970 – YouTube no longer serving MSE/webm to Firefox 43 users. -
YouTube change served lower-quality video to Firefox 43 for 2 weeks | Hacker News -
Why Americans Work So Much Despite Economist John Maynard Keynes's Prediction - The Atlantic -
Why Americans Work So Much Despite Keynes's Prediction | Hacker News -
Linux-Kernel Archive: Linux 4.4 -
Viskell: Visual programming meets Haskell | Hacker News -
Why We Hire Great Developers, Not Great Mobile Developers — Runkeeper: Everyone. Every Run. — Medium -
Why We Hire Great Developers, Not Great Mobile Developers | Hacker News -
Stephen Hawking's New Black-Hole Paper, Translated: An Interview | Hacker News -
Emacs development -
CλasH: From Haskell To Hardware : haskell -
New release of the book Haskell Programming from first principles : haskell -
Access files one at a time? : haskell -
Canada and Denmark fight over uninhabited island by leaving bottles of alcohol | Hacker News -
Canada and Denmark whiskey war over Hans Island -
String manipulation Guide? : haskell -
Haskell books to be published in 2015? : haskell -
Plugin system for an e-commerce/CMS website? (XPOST from /r/haskellquestions) : haskell -
A Haskell program for beginners? : haskell -
Shutting down persona.org in November 2016 | Hacker News -
H.265/HEVC vs H.264/AVC: 50% bit rate savings verified - BBC R&D -
H.265/HEVC vs. H.264/AVC: 50% bit rate savings verified | Hacker News -
.py in the sky -
Code that will break in Python 4 | Hacker News -
Why Would Anyone Use Perl 6? | Hacker News -
EFF To Court: Cisco Must Be Held Accountable For Aiding China’s Human Rights Abuses | Electronic Frontier Foundation -
Cisco Must Be Held Accountable for Aiding China’s Human Rights Abuses | Hacker News -
T-Mobile CEO to EFF: ‘Who the Fuck Are You?’ | Hacker News -
Elm in the Real World | Hacker News -
Can I refractor this out and how do I do that. : haskell -
ghc-mod not finding stack project dependencies : haskell -
HaSKI: Haskell-to-FPGA SKI calculus computer : haskell -
Typed up CRUD SPA with Haskell and Elm - Part 2: Persistence up and running : haskell -
Core Spec : haskell -
I run a SV startup but refuse to own a cellphone | Hacker News -
Project Rider – A C# IDE | Hacker News -
Qt Licensing Changes | Hacker News -
[ANNOUNCE] Glasgow Haskell Compiler 8.0.1, release candidate 1 -
GHC 8.0 release candidate 1 is out! : haskell -
Hiring: Haskell at Biotech Startup! : haskell -
How we used category theory to solve a problem in Java : haskell -
ChakraCore GitHub repository is now open | Hacker News -
Merge sort complexity problem : haskell -
Exploring S3 based filesystems S3FS and S3Backer | TurnKey GNU/Linux Blog -
Encapsulating unsafe IORefs : haskell -
Encapsulating unsafe IORefs · Karius Engineering Blog -
Learn you Func Prog on five minute quick! (El Reg's parody on Functional Programming) : haskell -
Facebook Messenger protocol in pidgin | Hacker News -
Release 1.4.0 · atom/atom -
Atom text editor 1.4 released | Hacker News -
OpenSSH: client bug CVE-0216-0778 -
OpenSSH: client bug CVE-2016-0777 | Hacker News -
Announcing serversession | Felipe Lessa -
Imba: The new programming language for web apps | Hacker News -
I'm learning calculus for the first time. If you were me, how would you use Haskell to get calculus-fu and increase Haskell-fu? : haskell -
Netflix to block proxy access to content not available locally | Hacker News -
What to do with aeson-0.10? | Unknown Parallel -
What to do with aeson-0.10? : haskell -
Help 25 to 30 girls learn computer programming in Mountain View, CA! | Sententia cdsmithus -
You could help 25 to 30 kids learn Haskell in Mountain View, CA : haskell -
Is there an intuitive interface for MySQL? : haskell -
dear-github/dear-github: An open letter to GitHub from the maintainers of open source projects -
Dear GitHub | Hacker News -
Amazon Has Just Registered to Sell Ocean Freight | Hacker News -
Why Kubernetes isn't using Docker's libnetwork | Hacker News -
The resolution of the Bitcoin experiment | Hacker News -
A New Code License: The MIT, this time with Attribution Required - Meta Stack Exchange -
A New Code License: The MIT, This Time with Attribution Required | Hacker News -
New blog post: Efficient Amortised and Real-Time Queues in Haskell : haskell -
Using Lua with Haskell : haskell -
Attempt at a modern Haddock theme : haskell -
An open letter of gratitude to GitHub | Hacker News -
To ship `stack.yaml` or not to? : haskell -
Vim Galore: everything you need to know about Vim | Hacker News -
Well-Typed - The Haskell Consultants: Efficient Amortised and Real-Time Queues in Haskell -
Why, oh WHY, do those #?@! nutheads use vi? -
transcranial/atom-transparency - CSS -
How to make the Atom editor transparent | Hacker News -
lukasmartinelli/hwk - Haskell -
Haskell based awk and sed alternative : haskell -
What sort of candidates are shops like Jane Street looking for for summer internships? : haskell -
What is Type-In-Type? : haskell -
Idris: Type safe printf - YouTube -
DependentHaskell/Phase1 – GHC -
Is there a way to have a function that returns a random number? : haskell -
Yesod for reactive web apps - Google Groups -
ANN: stack-1.0.2 - Google Groups -
lts-4 with aeson-0.10 is being discontinued, lts-5 will use aeson-0.9 : haskell -
aeson intentionally held back to 0.9.* · Issue #1155 · fpco/stackage -
Haskell for Finance resources : haskell -
Types Don't Lie, But They Won't Reveal Everything – cli-nerd.com -
WebTorrent – BitTorrent over WebRTC | Hacker News -
Haskell developer roles at Standard Chartered [London, Singapore] : haskell -
Haskell developer roles at Standard Chartered [London, Singapore] | Control.Monad.Writer -
Making GitLab Better for Large Open Source Projects | GitLab -
Dear open source maintainers, a letter from GitLab | Hacker News -
Christian's Rants and Ravings -
Fun With RULES : haskell -
darcs advanced revision control system: darcs hacking sprint 10 report -
Haskell Meets the Central Dogma · SavinaRoja -
Haskell Meets the Central Dogma (of molecular biology) : haskell -
Did European Court Just Outlaw “Massive Monitoring of Communications” in Europe? | Hacker News -
Apple May Be Using Congo Cobalt Mined by Children, Amnesty Says | Hacker News -
GHC panic! in ghc-mod + Atom when using QuasiQuotes : haskell -
"Clean Code" vs. Haskell : haskell -
Data.Tagged -
haskell - What is the purpose of `Data.Proxy`? - Stack Overflow -
Typed Holes for beginners -
GHC/Typed holes - HaskellWiki -
[Haskell-cafe] [ANN] ghc-mod-5.5.0.0: Happy Haskell Hacking -
The Monad Challenges: A new take on an old theme : haskell -
lettier/gifcurry: Create animated GIFs, overlaid with optional text, from movies. -
Haskell GIF creation utility using FFmpeg, ImageMagick, and GTK. : haskell -
The State Of Meteor Part 1: What Went Wrong - Discover Meteor -
ANN: logging-effect - a new mtl-style monad transformer for general purpose & compositional logging : haskell -
Control.Monad.Log -
Microsoft Philanthropies announces commitment to donate $1 billion in cloud computing resources to serve the public good | News Center -
Microsoft giving cloud to charities and researchers - think haskell.org would benefit? : haskell -
"Haskell Design Patterns" any good? : haskell -
Can this be ported to Haskell : haskell -
joegallo/doric: Clojure table layout -
PostgreSQL Simple FromField help : haskell -
status of `servant-foreign`? : haskell -
Show HN: Super Mail Forward, an HTML email that evolves as you forward it | Hacker News -
Timestamps done right | Kerf blog -
Timestamps done right | Hacker News -
An introduction to building GHC type-checker plugins : haskell -
Docker Acquires Unikernel Systems As It Looks Beyond Containers | TechCrunch -
Docker Acquires Unikernel Systems as It Looks Beyond Containers | Hacker News -
Unikernel Systems Joins Docker | Docker Blog -
Unikernel Systems Joins Docker | Hacker News -
I missed Nim - Roads Less Taken -
I missed Nim (2014) | Hacker News -
Announcing Rust 1.6 - The Rust Programming Language Blog -
Announcing Rust 1.6 | Hacker News -
Haskell: Why use Proxy? - Stack Overflow -
The meaning of tilde in Haskell types (Type equality) - Stack Overflow -
Posting successful SSH logins to Slack -
Posting successful SSH logins to Slack | Hacker News -
Microsoft silenty adds Amazon root certificates to its CTL · HA -
Microsoft silently adds Amazon root certificates to its CTL | Hacker News -
GHC type checker plugins: adding new type-level operations · Christiaan Baaij -
An introduction to building GHC type-checker plugins : haskell -
The Apple iPad Pro Review | Hacker News -
CλaSH FPGA Starter · Christiaan Baaij -
Any high-level way to abstract away networking / communication ? : haskell -
glasgowmoocadventures -
Haskell MOOC project : haskell -
Why didn't France adopt the longbow like England did? | Hacker News -
The State of Meteor Part 1: What Went Wrong | Hacker News -
A Beginner's Guide to Scaling to 11 Million+ Users on Amazon's AWS - High Scalability - -
How to add polymorphic type signature in where clause? : haskell -
ANN: generics-eot-0.2 : haskell -
[ANNOUNCE] Shaking up GHC -
Shaking up GHC : haskell -
Does this exist in base? : haskell -
Maybe/Either vs exceptions [Video] : haskell -
Servant or other library for a largish API? : haskell -
Stack users: multiple executables in one cabal file, or multiple packages in stack.yaml? : haskell -
Why Erlang looks like it does | Hacker News -
Parsing Bitstreams with haskell : haskell -
Haskell as a gradually typed dynamic language | Types and Kinds -
Haskell as a gradually typed dynamic language : haskell -
haskell - Can I use gloss on Android? - Stack Overflow -
Can I use gloss on Android? : haskell -
9 things every React.js beginner should know - Cam Jackson -
Things every React.js beginner should know | Hacker News -
Read Me | Redux -
[erlang-questions] State Management Problem -
Local documentation : haskell -
Integrating slack with emacs : haskell -
github/swift-style-guide: Style guide & coding conventions for Swift projects -
Swift Style Guide by GitHub | Hacker News -
Calling OCaml from Objective-C on the iPhone - Edgar Aroutiounian -
Call OCaml from Objective-C, run on iOS, code sample and build steps | Hacker News -
State of HaLVM? : haskell -
Opam-ios: an OCaml compiler for iOS via opam, with example server code | Hacker News -
YBlog - Category Theory Presentation -
Why, oh WHY, do those #?@! nutheads use vi? -
Adrian Sampson: LLVM for Grad Students -
Magnus web site | Random stuff -
Magnus web site | Random stuff -
Cofun with Cofree Comonads: The Video : haskell -
mpm/transition - Mercurial -
Extensible, reusable parsers? : haskell -
Is GHC's popularity dangerous? : haskell -
Response to: Dynamic Typing > Static Typing? : haskell -
Multiple security vulnerabilities in Rails | Hacker News -
gereeter/hsdecomp: A decompiler for GHC-compiled Haskell -
A decompiler for GHC-compiled Haskell : haskell -
An attempt at a modern Haskell theme (continued) : haskell -
performance - Why is Haskell (GHC) so darn fast? - Stack Overflow -
Why is Haskell (GHC) so darn fast? : haskell -
Just released on Free site: Section 8 of happylearnhaskelltutorial.com Vol1 : haskell -
Stackage LTS 5.0 released : haskell -
OCaml roles at microservices startup StackHut [London] : haskell -
GHC 8.0.1 plans -
Question about optimizing code with free monads and monad transformers. : haskell -
Chrome Cleanup Tool | Hacker News -
If you are looking for something to do, here is a small/medium and well made library you can help fix. : haskell -
How do people feel about modules that only exist to provide instance? : haskell -
rebase: A more progressive alternative to the "base" package | Hackage -
"rebase", a more progressive alternative to the "base" package : haskell -
Is there a way to access an Amazon RDS database via Haskell and be able run queries via an API of sorts? Is there a Haskell library that allows me to do that? : haskell -
Automatic bug-repair system fixes 10 times as many errors as its predecessors | Hacker News -
Simon Meier: A primer to commercial Haskell programming [1:20:48] : haskell -
Options for a Haskell computing environment, Mac OS X or Linux : haskell -
Challenge: Write a compile-time type-level quine : haskell -
nh2/call-haskell-from-anything -
It is now easier to extend or replace non-Haskell code with Haskell code compiled with GHC 7.10.3 : haskell -
Why doesn't the Monad class use Constraints? : haskell -
100 Days of Fibonacci - Day 9, Haskell Types · Mads Buch -

-

February, 2016

-

-

Vim and Haskell in 2016 -
Vim and Haskell in 2016 : haskell -
ghcjs + ghc-7.10.3 + lts-5.1 : haskell -
How can the use of State simplify this genetic algorithm-based traveling salesman code? : haskell -
Suggestions about an 1 hour talk of Haskell? : haskell -
Memory profiling in Haskell : haskell -
Top tips and tools for optimising Haskell - Pusher Blog -
Memory profiling in Haskell - Pusher Blog -
Shout — The self-hosted web IRC client -
Shout: The self-hosted web IRC client | Hacker News -
Please don't use Slack for FOSS projects -
Please don't use Slack for FOSS projects | Hacker News -
This Is What Happens When You Reply to Spam Email | James Veitch | TED Talks - YouTube -
Google plans to beam 5G internet from solar drones | Hacker News -
The Power of Privacy – film | Media Network | The Guardian -
15 years of VLC and VideoLAN - Yet another blog for JBKempf -
Alphabet Becomes the Most Valuable Public Company in the World | Hacker News -
Use Atom to edit in Chrome | Hacker News -
Haskell for commercial software development - Wagon -
Haskell in industry : haskell -
Neil Mitchell's Haskell Blog: New HLint version and API features -
PageSpeed Examples Directory -
Nginx module by Google for rewriting web pages to reduce latency and bandwidth | Hacker News -
Dropbox Hack Week: GraphQL Server in Haskell : haskell -
Dropbox Hack Week: GraphQL Server in Haskell – Chad Austin -
Is there a human-oriented JSON/YAML API? : haskell -
GitHub: January 28th Incident Report | Hacker News -
Plain Email – An app concept for efficient one-touch email processing workflow -
Show HN: Plain Email – An app for one-touch email processing | Hacker News -
h-atom -
basile-henry/HaskellSweeper: A Minesweeper clone with infinite grid written in Haskell. -
A Minesweeper clone with infinite grid written in Haskell. Would love some feedback! : haskell -
Programmer Hero | Devpost -
x86 Assembly – A crash course tutorial I | Mark's blog -
Learn Haskell in Y Minutes -
Microsoft Open Source Challenge - Microsoft Research -
Apple's declining software quality - Sudophilosophical -
Apple’s declining software quality | Hacker News -
Month in Haskell Mode January 2016 · haskell/haskell-mode Wiki -
Introducing the Keybase filesystem -
Introducing the Keybase filesystem | Hacker News -
Does 'argument do' have a future? : haskell -
Learn Enough Git to Be Dangerous | Hacker News -
Posting successful SSH logins to Slack -
iPhones 'disabled' if Apple detects third-party repairs - BBC News -
iPhones 'disabled' if Apple detects third-party repairs | Hacker News -
Rebuild & restart program on file change : haskell -
lukexi/halive: Live recompiler for Haskell -
Monoids and their efficiency in practice | -
Monoids and their efficiency in practice : haskell -
haskell-ide-engine/Report-2016-01.md at master · haskell/haskell-ide-engine -
Month in haskell-ide-engine January 2016 : haskell -
Which web framework should I choose? : haskell -
Composition vs Higher Order Functions? : haskell -
New type of ($) operator in GHC 8.0 is problematic -
Haskell for all: How to make your Haskell code more readable to non-Haskell programmers -
[ghc-devs] New type of ($) operator in GHC 8.0 is problematic : haskell -
ARM Cloud - RunAbove -
Why cross compile when you can just compile? : haskell -
Parser Combinators: Parsing for Haskell Beginners : haskell -
Easy universal Haskell development environment -
Easy universal Haskell development environment (using Stack and Atom) : haskell -
Announcing servant-swagger and swagger2 : haskell -
Haskell is not trivial, but it's not unfair like Dark Souls either : haskell -
bitemyapp - Haskell is not trivial, but it's not unfair like Dark Souls either -
I am a puts debuggerer | Hacker News -
Learn c in Y Minutes -
haskelliseasy -
http://haskelliseasy.com : haskell -
[Intermediate] Recommend a learning path? : haskell -
[ANNOUNCE] GHC 8.0.1 release candidate 2 -
[ANNOUNCE] GHC 8.0.1 release candidate 2 : haskell -
Is this an okay usage of unsafePerformIO? : haskell -
Trouble with loading stack project into REPL : haskell -
More fun to build an interpreter in Haskell or node.JS? : haskell -
Records: Python library for making raw SQL queries to Postgres databases | Hacker News -
Getting Started Pains : haskell -
Origin of "(Learn|Write) You(rself)? (a|an|Some) [language]" idiom? : haskell -
Is the title "Learn you a Haskell for Great Good" a reference to something? : haskell -
What Haskell book would you recommend for an experienced 'C' programmer. I would appreciate recommendations that covers the language features in a comprehensive manner. I am OK with recommendations that are of formal/academic bent. TIA. : haskell -
Monads as Adjectives : haskell -
[Haskell-cafe] [ANN] aeson 0.11.0.0 -
[ANN] aeson 0.11.0.0 : haskell -
Funding testimonial: Divio | Weblog | Django -
Funding Django is not an act of charity, it's an investment | Hacker News -
Hacker Publishes Personal Info of 20,000 FBI Agents | Motherboard -
Hacker Publishes Personal Info of 20,000 FBI Agents | Hacker News -
What Keeps Couples Happy Long Term | Hacker News -
The Genius of WordPress (and why it’s doomed) — Medium -
Printing to the terminal and the browser. (Question) : haskell -
Can someone explain the basics of IO and >> to me? : haskell -
Getting Started with the LLVM System — LLVM 3.9 documentation -
Is it just me, or does `IO`s definition give you the chills? : haskell -
How to write this type of expressions more elegantly? : haskell -
why this program run significantly faster when profiling is enabled?? : haskell -
GHC performance over time : haskell -
Google is banning Flash from its display ads | Hacker News -
Idea: Syntax Highlighting for variables : haskell -
GHC performance over time | Mostly nerdless -
How to get rid of unsafePerformIO in this interpreter? : haskell -
What It Feels Like to Freeze on National Television | Dr Benjamin Habib -
What It Feels Like to Freeze on National Television | Hacker News -
A modest Haskell critique from the Cap'n Proto author : haskell -
Cap'n Proto: Cap'n Proto v0.2: Compiler rewritten Haskell -> C++ -
Adblock via /etc/hosts | Hacker News -
Swift Protocols and the Promised Land, with Michele Titolo - Realm is a mobile database: a replacement for SQLite & Core Data -
Swift Protocols and the Promised Land | Hacker News -
Answer - Quora -
On-site Haskell/Python(!) opportunity at Positive Technologies (Moscow) : haskell -
JohnReedLOL/HaskellPrintDebugger: Prints lines with IDE friendly stack traces in Haskell. -
Haskell Print Debugger with IDE friendly Stack Trace : haskell -
Lightweight http server options : haskell -
Initial response from GitHub · bkeepers/dear-github@4afa490 -
GitHub responds to Dear GitHub letter | Hacker News -
Lesbians tend to earn more than heterosexual women | Hacker News -
I3ck/HGE2D: 2D game engine written in Haskell -
Moved the game engine out of my game -> HGE2D : haskell -
Stop lying about Haskell : haskell -
For a non-expert, what's different between Bayesian and frequentist approaches? | Hacker News -
Stack traces in GHCi, coming in GHC 8.0.1 · Simon Marlow -
Stack traces in GHCi, coming in GHC 8.0.1 · Simon Marlow : haskell -
QuickPlot: Quick and easy data visualization with Haskell | Hackage -
Twitter: Years After the Alphabet Acquisition — Medium -
Mitigating DDoS Attacks with NGINX and NGINX Plus - NGINX -
Mitigating DoS Attacks with Nginx | Hacker News -
Tests vs Types : haskell -
atemerev/skynet: Skynet 1M threads microbenchmark -
Scala, Haskell, Erlang, and Go compared on a million threads : haskell -
I no longer understand my PhD dissertation | Hacker News -
Is anything being done to remedy the soul crushing compile times of GHC? : haskell -
Cofun with Cofree Comonads by David Laing - YOW! Lambda Jam 2015 | Eventer -
Haskell for all: Why free monads matter -
Learn X in Y Minutes: Scenic Programming Language Tours -
Download FFmpeg -
FFmpeg 3.0 released | Hacker News -
How to Distribute Binaries for OS X Using Homebrew -
How to Distribute Binaries for OS X Using Homebrew | Hacker News -
Why I No Longer Use MVC Frameworks | Hacker News -
Adrian Sampson: Run an LLVM Pass Automatically with Clang -
When a Self-Declared Genius Asks You to Read His Masterpiece | Literary Hub -
When a Self-Declared Genius Asks You to Read His Masterpiece | Hacker News -
Search Hillary Clinton’s Emails | Hacker News -
Uber's Atomic Meltdown — Eli Schiff -
Uber's Atomic Meltdown | Hacker News -
Chromabits - Serving a Hakyll site with Warp/Wai -
Serving a Hakyll site with Warp/Wai : haskell -
Vulkan is Here | Hacker News -
A Message to Our Customers | Hacker News -
Customer Letter - Apple -
No, A Judge Did Not Just Order Apple To Break Encryption On San Bernardino Shooter's iPhone, But To Create A New Backdoor | Techdirt -
Apple ordered to bypass auto-erase on San Bernadino shooter's iPhone | Hacker News -
Code quality metrics for Haskell : haskell -
Best practices for modifiable state in Haskell? : haskell -
GitHub launches templates for new issues and pull requests -
What do you mean ‘we need more time’?? | Dropbox Tech Blog -
What do you mean ‘we need more time’? Project schedule estimation in software | Hacker News -
Deutsche Bank: Germany's financial colossus stumbles | Business | The Guardian -
Deutsche Bank: Germany's Financial Colossus Stumbles | Hacker News -
For gifted children, being intelligent can have dark implications | Calgary Herald -
For gifted children, being intelligent can have dark implications (2015) | Hacker News -
A Haskell vs Perl Performance Case | Cagesong on Earth -
A Haskell vs Perl Performance Case : haskell -
Resource management problem while trying to design a monadic graphics rendering library. Advices? : haskell -
Hython - a toy Python 3 interpreter written in Haskell : haskell -
iTerm2 Version 3 Now in Beta | Hacker News -
Apple Fixes iPhones Disabled By Error 53 Caused By Unofficial Repairs -
Apple Apologizes and Updates iOS to Restore iPhones Disabled by Error 53 | Hacker News -
literally words — Graphing when your Facebook friends are awake -
Graphing when your Facebook friends are awake | Hacker News -
Stack v1.0.4 : haskell -
Release v1.0.4 · commercialhaskell/stack -
The Joy and Agony of Haskell in Production -
The Joy and Agony of Haskell in Production : haskell -
Graphics and FPL: Pure API vs. IO-bound API for graphics frameworks -
Ayn Rand Lamp | Hacker News -
Ayn Rand Lamp: Video : JACK RYAN -
WINE 1.9.4 Released | Hacker News -
Snowden: FBI obscuring crucial details in Apple case | Hacker News -
State of the Haskell Ecosystem – February 2016 | Hacker News -
Haskell for all: State of the Haskell Ecosystem - February 2016 Edition -
Summary of Haskell recommendations I received ... Thanks for all those who took time to respond. : haskell -
Haskell for all: State of the Haskell Ecosystem Feb 2016 Edition : haskell -
Inside Noma, the restaurant cooking up the future of food (Wired UK) -
Noma's taste of tomorrow: creating the future of food | Hacker News -
Joshua Browder's Bot for parking tickets -
A bot has successfully appealed $3M worth of parking tickets in the UK | Hacker News -
AI generated music to improve focus, relaxation and sleep | Hacker News -
Proving With Types : haskell -
It Is What It Is (And Nothing Else), Robert Harper : haskell -
Mark Zuckerberg Backs Apple in Its Refusal to Unlock iPhone - The New York Times -
Mark Zuckerberg Backs Apple in Its Refusal to Unlock iPhone | Hacker News -
Justice Department Seeks to Force Apple to Extract Data From About 12 Other iPhones - WSJ -
Draft.js | Rich Text Editor Framework for React -
Draft.js – Rich Text Editor Framework for React | Hacker News -
WikiLeaks reveals the NSA spied on Berlusconi and his closest advisors - l'Espresso -
WikiLeaks reveals the NSA spied on Berlusconi and his closest advisors | Hacker News -
A New Breed of Trader on Wall Street: Coders with a Ph.D | Hacker News -
How to stop functional programming - BAM Weblog -
How to stop functional programming | Hacker News -
Bill Gates calls for terror data debate - BBC News -
Bill Gates calls for terror data debate | Hacker News -
Which book should I buy ? : haskell -
Remote Haskell developer role : haskell -
What's in a Haskell parsing combinator library. : haskell -
Megaparsec 4 and 5 : haskell -
Megaparsec 4 and 5 by Mark Karpov -
Justice Department Wants Apple to Unlock Nine More iPhones | Hacker News -
Need advice using Haskell to parse HTML/CSS to optimize CSS files. : haskell -
Fake girlfriend, revisited - BBC News -
Fake girlfriend, revisited | Hacker News -
Announcing Spotify Infrastructure’s Googley Future | News -
Spotify moves its back end to Google Cloud | Hacker News -
Dear Skype/Microsoft -
Skype keeps ignoring people who complain or are having issues with Linux client | Hacker News -
How to Disable Ads on Your Windows 10 Lock Screen -
Stripe: Atlas -
Stripe Atlas | Hacker News -
Windows Now Showing Full-Screen Ads | Hacker News -
Apple Is Said to Be Working on an iPhone Even It Can’t Hack | Hacker News -
[Advice/request] Intern/Short-Term working with haskell. : haskell -
Overview of all Haskell repos in Github - Post your interesting finds! : haskell -
Haskell developer roles at Standard Chartered [London, Singapore] | Control.Monad.Writer -
Six (6) Haskell developer roles at Standard Chartered [London, Singapore] : haskell -
PHP 7 – The next big thing for Magento -
PHP 7 – The next big thing for Magento | MGT-Commerce Blog -
"Haskell is Not For Production and Other Tales" - Katie Miller @ Linux.conf.au 2016 (Haxl@Facebook talk) : haskell -
Microsoft Acquires Xamarin | Hacker News -
Apple Hires Developer Behind Signal, Edward Snowden’s Favorite Secure Chat App | Hacker News -
vulkan 1.0: bindings to the Vulkan graphics API : haskell -
Report from the VMware GPL court hearing | Hacker News -
Upgrading from React 0.11.2 to 0.14.7 in 347 easy steps | Hacker News -
Space leak in a simple attoparsec parser : haskell -
Haskell for all: Auto-generate a command line interface from a data type -
Leksah 0.15.2.0 - Many UI improvements and some stack support. : haskell -
Haskell for all: Auto-generate a command line interface from a data type : haskell -
Ten years on LinkedIn | Hacker News -

-

March, 2016

-

-

DROWN Attack -
Comparing Haskell and Rust: which to choose, when? : haskell -
Microsoft, Google, Facebook Back Apple in Blocked Phone Case | Hacker News -
Ghost in the Shell remains our most challenging film about technology | Hacker News -
Haskell Summer of Code : haskell -
The Haskell Cheatsheet -
The Haskell Cheatsheet (A Repost) : haskell -
A 19 basis point portfolio beats the average of every college endowment | Hacker News -
Why is Google mysteriously allowing some very popular apps to break many Google Play rules (developer policies)? : Android -
Why is Google allowing this popular app to violate so many Google Play rules? | Hacker News -
Haskell job posting: Join 'Stage N Software' to help build Unison, a revolutionary new platform for software development : haskell -
WxWidgets 3.1.0 Brings Better HiDPI Support, WxQt with Qt5 | Hacker News -
Free React.js Fundamentals Course | Hacker News -
Malthe Borch's Homepage: A Blog About Computer Systems and Language Design -
The Sign Up with Google Mistake You Can't Fix | Hacker News -
Slack will soon start testing voice and video chat | TechCrunch -
Slack will soon start testing voice and video chat | Hacker News -
OCaml 4.03: Everything else :: Jane Street Tech Blogs -
OCaml 4.03: Everything else | Hacker News -
The design of the Strict Haskell pragma | Johan Tibell -
Kanye West caught using Pirate Bay to download music software | Ars Technica UK -
Spotify's Chromecast approach is the start of an unfortunate new chapter | Android Central -
Write You a Haskell ( Stephen Diehl ) : haskell -
Does Haskell have a function that let's you apply an argument to a function? : haskell -
LaTeX diagrams in Hakyll : haskell -
What Features Would You Like to Find in a Haskell IDE? : haskell -
Status of Stream Fusion in GHC libraries : haskell -
What features would you like to find in a Haskell IDE? (part 2) : haskell -
Adding Redis to Yesod : haskell -
Laziness in ghci-7.10 : haskell -
Dead code elimination : haskell -
Amazon Echo Dot | Hacker News -
Source: Microsoft mulled an $8B bid for Slack, will focus on Skype instead | Hacker News -
Real benefit of Servant : haskell -
How's your experience with full stack haskell been? : haskell -
How sharing code between front/back is relevant in practice ? : haskell -
apt 1.0 | Michael Vogt's blog -
Bitcoin transaction processing takes up to 10 hours | Hacker News -
Frege/frege: Frege is a Haskell for the JVM. It brings purely functional programing to the Java platform. -
As 4th trial nears, Samsung asks judge: Make Apple stop talking about Korea | Ars Technica -
San Bernardino DA says seized iPhone may hold “dormant cyber pathogen” [Update] | Ars Technica -
Mr. Fart’s Favorite Colors — Medium -
Mr. Fart’s Favorite Colors | Hacker News -
OSX.KeRanger.A malware in 2.90? - Transmission -
Transmission BitTorrent app contained malware | Hacker News -
Is group chat making you sweat? — Signal v. Noise — Medium -
Is group chat making you sweat? | Hacker News -
10 years on LinkedIn -
Coffee Drip Printer - RIT: College of Imaging Arts & Sciences -
Coffee Drip Printer | Hacker News -
So, you want to learn bookkeeping | Hacker News -
[NYHUG] Translating Haskell to Hardware : haskell -
Translating Haskell to Hardware - YouTube -
LLVM 3.8 Release Notes | Hacker News -
LenzBlog - Full stack web Haskell with Servant and GHCJS -
Hacking with Latex | Hacker News -
WebAssembly & Haskell : haskell -
hint is alive again! : haskell -
America's High School Graduates Look Like Other Countries' High School Dropouts | WAMC -
America's High School Graduates Look Like Other Countries' High School Dropouts | Hacker News -
How FPGAs work, and why you'll buy one (2013) | Hacker News -
Alchemy Micro-Service Framework: Using RabbitMQ Instead of HTTP | Hacker News -
Amazon Echo, home alone with NPR on, got confused and hijacked a thermostat - Quartz -
Measuring GHC's "soul crushing" compile times / feedback? : haskell -
Nerd-Fonts – Patched Fonts for Vim | Hacker News -
I made my own clear plastic tooth aligners and they worked | Hacker News -
The case expression syntactic sugar can usually be represented in terms of an ordinary higher-order function. How can this be done for GADT's? : haskell -
Dropbox’s Exodus from the Amazon Cloud | Hacker News -
GitHub · Where software is built -
Show HN: Using Google AMP to build a Medium-style Jekyll site that loads in 65ms | Hacker News -
V8 JavaScript Engine: V8 Release 5.0 -
V8 Release 5.0 (upcoming) | Hacker News -
On asking job candidates to code -
The future of dependent Haskell : haskell -
Google nabs Apple as a cloud customer - Business Insider -
Google nabs Apple as a cloud customer | Hacker News -
Atom-haskell now implements case-splitting : haskell -
Facebook is the new Excel | Hacker News -
Unmasking Startup L. Jackson | Hacker News -
Atom 1.6 and 1.7 beta -
Atom 1.6 Released with Pending Pane Items, Async Git and Top and Bottom Bar API | Hacker News -
A slightly better error message : haskell -
US government pushed tech firms to hand over source code | Hacker News -
Apple Encryption Engineers, if Ordered to Unlock iPhone, Might Resist - The New York Times -
Apple Encryption Engineers, If Ordered to Unlock iPhone, Might Resist | Hacker News -
Microsoft won't stop forcing companies to pay for its bogus Android patents | Hacker News -
Apple vs. The F.B.I.: How the Case Could Play Out | Hacker News -
Diablo - Game Concept by Condor, Inc. -
Thank you for ad blocking | Hacker News -
Swift.org - Swift 2.2 Released! -
Swift 2.2 Released | Hacker News -
iPhone SE | Hacker News -
Autocomplete from Stack Overflow -
Dragon: A distributed graph query engine | Hacker News -
Autocomplete from Stack Overflow | Hacker News -
How to do a PhD: top 10 tips | James Hayton, PhD -
How to do a PhD | Hacker News -
Any ideas for a Haskell-related senior thesis? : haskell -
Help with Aeson : haskell -
Siri and iAd Restricted by Apple 'Policy Czars' to Limit Customer Data Collection - Mac Rumors -
Apple Said to Ship New 'Ultra-Thin' 13" and 15" MacBooks by June-July - Mac Rumors -
iPad Air 2 Smart Covers Not Recommended for 9.7-inch iPad Pro - Mac Rumors -
I’ve Just Liberated My Modules — Medium -
I've Just Liberated My Modules | Hacker News -
In foam-arrow patent fracas, Newegg swoops in to aid LARPer defendant | Ars Technica -
In foam-arrow patent fracas, Newegg swoops in to aid LARPer defendant | Hacker News -
Study: People Want Power Because They Want Autonomy - The Atlantic -
Study: People Want Power Because They Want Autonomy | Hacker News -
NPM & left-pad: Have We Forgotten How To Program? | Haney Codes .NET -
Require-from-Twitter | Hacker News -
Tinder like application for gifs built with elm | Hacker News -
Docker for Mac and Windows Beta: the simplest way to use Docker on your laptop | Docker Blog -
Docker for Mac and Windows Beta: the simplest way to use Docker on your laptop | Hacker News -
Citus Unforks from PostgreSQL, Goes Open Source | Hacker News -
BLE (Bluetooth Low Energy) in Haskell? : haskell -
Justin Time - Y Combinator Posthaven -
Justin Time | Hacker News -
Valve releases Steam Controller CAD geometry so you can mod it - PC Gamer -
Valve releases Steam Controller CAD geometry so you can mod it | Hacker News -
An administrator accidentally deleted the production database | Hacker News -
Internet Archive Seeks to Defend Against Wrongful Copyright Takedowns - TorrentFreak -
Internet Archive Seeks to Defend Against Wrongful Copyright Takedowns | Hacker News -
That awkward moment when Apple mocked good hardware and poor people -
Chris Thoburn -
The Chrome Distortion: how Chrome negatively alters our expectations | Hacker News -
parro-it/canibekiked: Tool to check if any of your NPM repositories name is trademarked -
How one developer just broke Node, Babel and thousands of projects in 11 lines of JavaScript • The Register -
How do we prevent the recent Node.js npm catastrophe in Haskell? Do Stack's snapshots (carefully vetted by FP complete I presume) fix the problem? What if one package developer takes his package down? Is this possible in Stack? : haskell -
A discussion about the breaking of the Internet — Medium -
Patent that cost Microsoft millions gets invalidated | Ars Technica -
An introduction to web development? : haskell -
My dad hid his depression. I won’t hide mine | Hacker News -
How to Get Out of Bed | Hacker News -
How Scandinavian design took over the world | Hacker News -
Babel 6 – useless by default | Hacker News -
Change GitBucket user interface to make difference with GitHub -
GitHub forces Gitbucket to change their UI | Hacker News -
Need help to make Stack and Yesod work together (especially test). : haskell -
Follow the money: Apple vs. the FBI | Hacker News -
FBI Breaks into iPhone. We Have Some Questions. | Electronic Frontier Foundation -
FBI Breaks into iPhone. We Have Some Questions | Hacker News -
U.S. Drops California Case Against Apple After Accessing iPhone | Hacker News -
My Heroic and Lazy Stand Against IFTTT (Pinboard Blog) -
My Heroic and Lazy Stand Against IFTTT | Hacker News -
The npm Blog — changes to npm’s unpublish policy -
Ember FastBoot -
Ember Fastboot | Hacker News -
GitHub - evanw/thinscript: A low-level programming language inspired by TypeScript -
Thinscript: language that compiles to both WebAssembly and JavaScript | Hacker News -
Spotify raises $1 billion in debt with devilish terms to fight Apple Music | TechCrunch -
Spotify raises $1B in debt with devilish terms to fight Apple Music | Hacker News -
ptmt/react-native-desktop: React Native for OS X -
Saved replies -
React Native for OS X | Hacker News -
Git 2.8.0 released | Hacker News -
Introducing Safari Technology Preview | WebKit -
Introducing Safari Technology Preview | Hacker News -
Introducing Safari Technology Preview | Hacker News -
Sunsetting "What I Wish I Knew" -
The 6 types of Haskell users - Rick Dzekman -
Sunsetting "What I Wish I Knew" : haskell -
(Rumor) Facebook to migrate all systems to Haskell by 2018 : haskell -
Best csv library ? : haskell -
Initial compile time benchmarks -
[ghc-devs] Initial compile time benchmarks : haskell -
Late sleepers are tired of being discriminated against. And science has their back. - Vox -
Reddit removed NSL canary from 2015 Transparency Report | Hacker News -
Poor man's IDE : haskell -
It's 2016 people, we should fix our Monad instance for Either! : haskell -
Fixing the Monad instance for Either -

-

April, 2016

-

-

Blog Rewrite with Hakyll and Purescript · in Code -
https://www.rsync.net/resources/notices/canary.txt -
Deploying to amazon is broken by sse4_crc32 · Issue #1050 · ParsePlatform/parse-server · GitHub -
Dwarf Fortress' creator on how he's 42% towards simulating existence | Hacker News -
You can now install a GSM network using apt-get | LaForge's home page -
Apricity OS -
Stream PirateBay movies directly from CLI | Hacker News -
Full Linux-on-PS4 hits Github • The Register -
Full Linux-on-PS4 hits GitHub | Hacker News -
Save Netflix! | Electronic Frontier Foundation -
register-machine-typelevel: A computationally universal register machine implementation at the type-level | Hackage -
Well, that escalated quickly. : haskell -
Deploying Haskell for Mac projects on OS X and Linux · Haskell for Mac -
Deploying Haskell for Mac projects on OS X and Linux using Stack : haskell -
Abandoning gitflow and GitHub | Hacker News -
XenialXerus/ReleaseNotes - Ubuntu Wiki -
At this time you should not upgrade a production desktop from 14.04 to 16.04 | Hacker News -
Being a ‘Dumb’ Girl in Computer Science | Rewriting the Code -
Tokamak: A Rust IDE for Atom | Hacker News -
Issue 326539 - chromium - OS X Notification Center should be used on Mac - Monorail -
Native Mac Notifications Comes to Chrome Stable | Hacker News -
Tor exit node operator gets raided by police | Hacker News -
When A Dark Web Volunteer Gets Raided By The Police : All Tech Considered : NPR -
How We Saved $132k a Year With an IT Infrastructure Audit | Hacker News -
Nostalrius Begins -
Blizzard shutting down Nostalrius, popular WoW private server | Hacker News -
WebTorrent Desktop -
WebTorrent Desktop – Open source streaming torrent client | Hacker News -
GitHub · Where software is built -
Vis: A Vim Like Text Editor | Hacker News -
Workflow automation : haskell -
Internet hyperlinks do not infringe copyright, EU court advised | Reuters -
Internet hyperlinks do not infringe copyright, EU court advised | Hacker News -
Why Erlang Matters -
Why Erlang Matters | Hacker News -
Android Studio 2.0 | Android Developers Blog -
Android Studio 2.0 Released | Hacker News -
Design better databases | Hacker News -
Open Whisper Systems >> Blog >> Signal Desktop beta now publicly available -
Hidden latency in the Linux network stack | Hacker News -
React v15.0 | React -
React v15.0 | Hacker News -
Don't wrap text in <span> elements by mwiencek · Pull Request #5753 · facebook/react -
Reddit: The Official App For iPhone and Android. Meow. -
React v15.0 | Hacker News -
Computer paints 'new Rembrandt' after old works analysis - BBC News -
EX-99.1 -
Jeff Bezos’ letter to shareholders | Hacker News -
Erlang 19.0 Garbage Collector | Hacker News -
Why does Go seem to be the most heavily criticised among the newer programming languages? - Quora -
Why Everyone Hates Go · npf.io -
Erlang Garbage Collection Details and Why They Matter | Hacker News -
Building a Better Custom Haskell Prelude | Hacker News -
Knuth versus Email -
stack support for yesod devel -
LiveReload with Yesod + Stack on existing project? : haskell -
Yesod quick start guide -
yesodweb/yesod: A RESTful Haskell web framework built on WAI. -
numeric-prelude: An experimental alternative hierarchy of numeric type classes | Hackage -
Building a Better Custom Haskell Prelude -
Performance comparison between s3fs and riofs · Issue #6 · skoobe/riofs -
Efficient Integer Overflow Checking in LLVM – Embedded in Academia -
Efficient Integer Overflow Checking in LLVM | Hacker News -
Congratulations! You’ve Been Fired - The New York Times -
Could the String problem be solved as an compiler optimisation ? : haskell -
It's the IO type, not IO monad. : haskell -
Domino's: Pizza and Payments -
Domino's: Pizza and Payments | Hacker News -
Haskell Diary #2 - Algebra of Data Types -
Haskell Diary #2 - Algebra of Data Types : haskell -
HP Claims Innovation Over Apple With 'World's Thinnest Laptop' - Mac Rumors -
Apple Seeds First OS X 10.11.5 El Capitan Beta to Developers - Mac Rumors -
Open-sourcing Haxl, a library for Haskell | Engineering Blog | Facebook Code -
A year after its launch, it’s now clear that pretty much no one needs an Apple Watch - Quartz -
Nginx Caching - Servers for Hackers -
Stumbled across SublimeHaskell when setting up a new laptop for dev. Very nice Haskell integration with plenty of features. : haskell -
Jyt -
LLVM based Just-in-Time-Compiler for C++ | Hacker News -
Arq 5: Massively faster backup and restore - Arq Backup Blog : Arq Backup Blog -
Arq 5: Massively faster backup and restore for Mac and Windows | Hacker News -
Neil Mitchell's Haskell Blog: GHCid 0.6 Released -
Facebook Messenger Platform | Hacker News -
GitHub’s CSP journey - GitHub Engineering -
Haskell for all: Worst practices should be hard -
Worst practices should be hard : haskell -
Forbes Site, After Begging You Turn Off Adblocker, Serves Up A Steaming Pile Of Malware 'Ads' | Techdirt -
Forbes Site, After Begging You Turn Off Adblocker, Serves Up Malware 'Ads' | Hacker News -
Haskell vs. Erlang, Reloaded | Lambda the Ultimate -
functional programming - Haskell vs Erlang for web services - Programmers Stack Exchange -
Erlang 19.0 Garbage Collector | Hacker News -
Erlang Solutions - Erlang/OTP 19 GC -
DevOps is dead. Long live DevOps | Hacker News -
Managed services killed DevOps | TechCrunch -
vim/version8.txt at master · vim/vim · GitHub -
Vim 8.0 is coming | Hacker News -
Third-Party Reddit Apps Pulled From App Store for NSFW Content [Updated] - Mac Rumors -
Facebook Introduces Chatbots for Messenger, Pushes Further Into Live Video - Mac Rumors -
9.7" iPad Pro's 'Excellent' Display is 'Major Upgrade' Over iPad Air 2 - Mac Rumors -
bye — Medium -
Min | A smarter, faster web browser -
Show HN: Min – web browser with better search and built-in ad blocking | Hacker News -
3 Approaches to Monadic API Design in Haskell - Pusher Blog -
How the 'FIFA Soccer' Video Games Get Such Awesome Soundtracks | GQ -
Apple Jumps On the WebRTC Bandwagon - Post - No Jitter -
Apple Jumps on the WebRTC Bandwagon | Hacker News -
QuakeNet IRC Network- Article - Trust is not transitive: or why IRC over SSL is pointless. -
Why IRC over SSL is pointless (2009) | Hacker News -
Post-Mortem for Google Compute Engine’s Global Outage on April 11 | Hacker News -
CaptionBot by Microsoft | Hacker News -
The WHATWG Blog — Adding JavaScript modules to the web platform -
Adding JavaScript modules to the web platform | Hacker News -
Docker 1.11: The first OCI-compliant runtime, built on containerd | Hacker News -
PlayTBC.com | Experience the finest - World of Warcraft: The Burning Crusade -
Atom text editor 1.7.0 released | Hacker News -
Creating signed GitHub releases - Debian Wiki -
Announcing Rust 1.8 - The Rust Programming Language Blog -
Announcing Rust 1.8 | Hacker News -
Warcraft fans' fury at Blizzard over server closure - BBC News -
Warcraft fans' fury at Blizzard over server closure | Hacker News -
Nostalrius Memories : wow -
In Loving Memory - WoWWiki - Wikia -
Google App Engine Silently Stopped Sending Email 5 Weeks Ago | Hacker News -
Renderings vs. Reality: The Improbable Rise of Tree-Covered Skyscrapers - 99% Invisible -
Renderings vs. Reality: The Improbable Rise of Tree-Covered Skyscrapers | Hacker News -
The Famous Photo of Chernobyl's Most Dangerous Radioactive Material Was a Selfie | Atlas Obscura -
Famous Photo of Chernobyl's Most Radioactive Material Was a Selfie | Hacker News -
How (and why) to make your software faster | Hacker News -
How (and why) to make your software faster -
1031899 – Github spy on user reading emails -
Two-factor authentication for Apple ID | Hacker News -
Keeping secrecy the exception, not the rule: An issue for both consumers and businesses - Microsoft on the Issues -
Keeping secrecy the exception, not the rule | Hacker News -
A New Map for America | Hacker News -
When Lobbying was Illegal -
When Lobbying was Illegal | Hacker News -
Sorry, You Can’t Speed Read - The New York Times -
jaspy/README.rst at master · koehlma/jaspy -
Jaspy – A Python VM written in JavaScript | Hacker News -
Challenges of micro-service deployments -
Challenges of micro-service deployments | Hacker News -
Why does Haskell, in your opinion, suck? : haskell -
Advanced DDoS Protection and Mitigation | CloudFlare | The web performance & security company -
Checked exceptions with open-signals : haskell -
Shtetl-Optimized » Blog Archive » Grading Trudeau on quantum computing -
Grading Trudeau on quantum computing | Hacker News -
Why aren’t we using SSH for everything? | Hacker News -
Why learning Haskell/Python makes you a worse programmer - All Unkept -
Why learning Haskell/Python makes you a worse programmer (2006) | Hacker News -
How a Diode Changed My Life | Hacker News -
An interesting html5-animated clock | Hacker News -
What are row types exactly? : haskell -
Google CDN Beta is here ... and it's already one of the fastest CDNs out there | Hacker News -
[ANNOUNCE] GHC 8.0.1 release candidate 3 available -
[ANNOUNCE] GHC 8.0.1 release candidate 3 available : haskell -
Deepjazz: AI-generated 'jazz' | Hacker News -
Static website on S3, CloudFront and Route 53, the right way! – Un blog sur tout et rien -
Does async syntax have any advantages over do notation? : haskell -
Attoparsec & Line Numbers : haskell -
When does one consider using Arrows? : haskell -
Mad scientist shrinks Arduino to size of an AA battery | TechCrunch -
Intel Releases the Arduino 101 Source Code | Hacker News -
Ravens score just as high as big-brained chimps on cognitive tests | Hacker News -
When it comes to brains, size doesn’t matter | Ars Technica -
Coding Inspiration: 3 Teen Sisters Crush Coding Records, Stereotypes -
Can we have -XHaskell2016 which turns on the most commonly used and safe extensions? : haskell -
Reducing garbage-collection pause time in a Haskell program : haskell -
Reducing garbage-collection pause time in a Haskell program - Stack Overflow -
why Haskell cannot do pattern match ? : haskell -
Apple pays $25M to a university—and the patent troll it cut a deal with | Ars Technica -
Thoughts on using _ as syntactic sugar for partially applied functions. : haskell -
FBI Paid More Than $1 Million to Hack San Bernardino iPhone - WSJ -
FBI Paid More Than $1M to Hack San Bernardino iPhone | Hacker News -
How can I do this in hamlet? : haskell -
Performance These Days | Hacker News -
How fast can we make interpreted Python? (2013) | Hacker News -
Why is haskell.org powered by some much PHP? : haskell -
Performance issues in my factoring function? : haskell -
Haskell.org News : haskell -
(Newbie) Certain I'm making a very basic mistake, but not entirely sure where in this Haskell Text Editor : haskell -
[ANN] Stratosphere: AWS CloudFormation EDSL in Haskell : haskell -
[Haskell-cafe] [ANN] Stratosphere: AWS CloudFormation EDSL in Haskell -
Haskell's Type Classes: We Can Do Better – John A De Goes -
Reducing garbage-collection pause time in a Haskell program : haskell -
The average size of Web pages is now the average size of a Doom install | Hacker News -
The web is Doom - mobiForge -
Edward Snowden: The Internet Is Broken | Popular Science -
Edward Snowden: The Internet Is Broken | Hacker News -
Why Write in English? by Tim Parks | NYR Daily | The New York Review of Books -
Why Write in English? | Hacker News -
One Easy Way to Make Wikipedia Better - The Atlantic -
Yakuza 3 reviewed by Yakuza / Boing Boing -
Haskell's Typeclasses: We Can Do Better : haskell -
So, you want to learn bookkeeping | Hacker News -
One Easy Way to Make Wikipedia Better | Hacker News -
The impact of Prince’s death on Wikipedia | Hacker News -
William Shakespeare, Playwright and Poet, Is Dead at 52 | Hacker News -
Cached deriving : haskell -
simple Haskell question!!! Please help me!! : haskell -
Web framework for building browser UI for an app running LAN/localhost : haskell -
Use OpenGL to get more RAM | Hacker News -
GitHub - graphitemaster/moreram: Get more system memory -
Something Secret This Way Comes — Llogiq on stuff -
[Rust] Something Secret This Way Comes | Hacker News -
Show HN: QBE – a new compiler back end | Hacker News -
Functors, Applicatives, and Monads in Plain English | Hacker News -
Russ Bishop - Functors, Applicatives, and Monads in Plain English -
William Shakespeare, Playwright and Poet, Is Dead at 52 - The New York Times -
The Science of Making Friends | Hacker News -
CloudFlare Now Supporting More Ports -
A web based dashboard for monitoring and controlling Philips Hue lights : haskell -
blitzcode/hue-dashboard: A web based dashboard for monitoring and controlling Philips Hue lights -
mtl: Monad classes, using functional dependencies | Hackage -
monad-log: A simple and fast logging monad | Hackage -
[announcement] monad-log: A simple and fast logging monad : haskell -
Problems installing (starting?) Haskell Platform on OSX : haskell -
The Dark Side of Haskell : haskell -
Newbie question about map and evaluation : haskell -
Dealing with error messages/codes returned within IO : haskell -
Is there any guarantee that a version of Stack will be same tomorrow as it is today? : haskell -
brew/Analytics.md at master · Homebrew/brew -
Homebrew now sends usage information to Google Analytics | Hacker News -
Half Your Brain Is Alert The First Night In A New Place : Shots - Health News : NPR -
Half Your Brain Stands Guard When Sleeping in a New Place | Hacker News -
Docker for Mac Beta Review | Hacker News -
BYOT Bring your own team -
The CIA Waterboarded the Wrong Man 83 Times in 1 Month | Hacker News -
Kidnap A Soviet? Well . . . Shiite Moslems Tried It In Lebanon - Once - philly-archives -
Effectiveness of torture for interrogation - Wikipedia, the free encyclopedia -
Summary of the -XHaskell2016 feedback : haskell -
Basic Syntax Extensions - School of Haskell | School of Haskell -
ANN: ghc-8.0.1-rc4 (quiet update) : haskell -
λ Bubble Pop!(A simple game that illustrates functional computation) : haskell -
λ Bubble Pop! -
A revolutionary new way to access all your files | | Dropbox Business Blog -
Dropbox Project Infinite | Hacker News -
Elixir RAM and the Template of Doom -
Elixir RAM and the Template of Doom | Hacker News -
Lionsgate is making its films available on Steam | Gadgette -
Lionsgate is making its films available on Steam | Hacker News -
World’s Fastest Growing Open Source Platform Pushes Out New Release | Node.js -
Node.js v6.0 Released | Hacker News -
Updating Our Prefixing Policy | WebKit -
WebKit is moving away from prefixes | Hacker News -
GitHub · Where software is built -
Xi editor: A modern editor with a backend written in Rust | Hacker News -
Long Compile Time in Swift? Could be the Literal Parsing Bug -
GHC may be slow, but it's not this slow! : haskell -
Jakub Jelinek - GCC 6.1 Released -
GCC 6.1 Released | Hacker News -
Good IDE for Haskell Stack? : haskell -
Wrangling State In Haskell : haskell -
Wrangling State In Haskell · Deliberate Software -
Xamarin Open Source SDK -
Xamarin Open-Sourced | Hacker News -
Introducing MIR - The Rust Programming Language Blog -
Introducing MIR | Hacker News -
-XOverloadedLists? : haskell -
Difference between haskell-lang/haskell-lang and haskell-infra/hl? · Issue #2 · haskell-lang/haskell-lang -
[Haskell-community] On the design used for haskell.org -
Haskell Incredibly Slow : haskell -
haskell-lang/announcements.md at master · haskell-lang/haskell-lang -
The content of the haskell.org Downloads page - Google Forms -
ANN: Hackage.haskell.org is down : haskell -
How do I download a package's source code with Stack? : haskell -
ANN: Haskell Prime 2020 committee has formed -
ANN: Haskell Prime 2020 committee has formed : haskell -
How we found a bug in Amazon ELB | Hacker News -
Load Your Steam Wallet Using Bitcoin -
Is there any dependently typed language that can actually be used in practice? : haskell -
#1582 (LLVM optimisation passes / tables next to code) – Haskell.org GSOC -
#1608 (Implement 2-3 concurrent data structures from the literature) – Haskell.org GSOC -
#1654 (Make the GHC performance monitoring build bot production ready) – Haskell.org GSOC -
#1681 (Develop cross platform mobile applications using GHCJS/GHC) – Haskell.org GSOC -
#1607 (Haskell for the "real-time web") – Haskell.org GSOC -
#1601 (Library for the Arduino platform) – Haskell.org GSOC -
Design input opportunity: specializing types in GHCi : haskell -
-XGlasgow2016 as an IDE feature? : haskell -
Instagram insists littergram app is renamed - BBC News -
Instagram insists littergram app is renamed | Hacker News -
the objective package is a neat little thing : haskell -
Freelance Haskell Work? : haskell -
Haskell's GC: Anything Special? : haskell -
The GHC Runtime System -
alternatives to firing — Ask a Manager -
Alternatives to Firing | Hacker News -
Hello Lua | Hacker News -
"Haskell programmer seeks forgiveness for using unsafePerformIO at meetup" : haskell -
Who's downloading pirated papers? Everyone | Hacker News -
Infinit announces Project Dropboxe -
Infinit - Decentralized Software‑Based File Storage Platform -
In Reaction to Dropbox's Project Infinite, Infinit Announced Project Dropboxe | Hacker News -
Haskell and GUIs / Vulkan : haskell -
Snowden’s Rubik’s Cube — Matt Davis [da5is.com] -
Snowden’s Rubik’s Cube | Hacker News -
What happened to Haskell Cast? : haskell -
CS Syd - A functor is not a box : haskell -
Path Finding One Liner : haskell -
Simon Peyton Jones elected into the Royal Society Fellowship | Lambda the Ultimate -
CS Syd - A functor is not a box -
Generalizing function application : haskell -
Show HN: Task-Based Electron Web Browser | Hacker News -
Alloy – Task-based web browser -
Warn HN: stacks made executable on GNU by mere presence of assembly code | Hacker News -
Deploy Haskell + Stack Application on Raspberry Pi : haskell -
Programming by poking: why MIT stopped teaching SICP | posterior science -

-

May, 2016

-

-

Simon Peyton Jones Elected into the Royal Society Fellowship | Hacker News -
Why I'm Not Going To Stop Posting Go Links - Tim Kellogg -
Haskell, Vectors, and Implicit Knowledge -
Haskell, Vectors and Implicit Knowledge : haskell -
Go best practices, six years in | Hacker News -
Extremely Angry -
My first haskell project - ApiAI bindings : haskell -
“Extremely angry with the state of academic CS research right now” | Hacker News -
Creator of Bitcoin digital cash reveals identity - BBC News -
Creator of Bitcoin digital cash reveals identity | Hacker News -
GitHub - patio11/wrightverification: (Failed) verification of the signature posted by Craig Wright on May 2, 2016 -
Correlation implies Causation (2009) | Hacker News -
Guide for, or common standard way to structure your Haskell projects? : haskell -
Superdesk – An End-To-End Platform for News | Hacker News -
Adrian Sampson: Weep for Graphics Programming -
Haskell Jobs @ AlphaSheets (coding in a spreadsheet, in any language) : haskell -
[RANT] Can we stop calling every little discomfort, Hell ? : haskell -
Announcing cabal new-build: Nix-style local builds : Inside 736-131 : haskell -
relax/relax: New generation CMS on top of React, Redux and GraphQL -
Relax – A New Generation CMS on Top of React, Redux, and GraphQL | Hacker News -
"This Week in Haskell Docs"? : haskell -
Mentor for a JVM backend for GHC : haskell -
Mentor for a JVM backend for GHC -
Haskell equivalent of Rust's Vulkano? : haskell -
vulkan: Bindings to the Vulkan graphics API. | Hackage -
Learning Haskell : haskell -
Is there any way to get my hands on Intel's Haskell Research Compiler? : haskell -
Improving Angular performance with 1 line of code — Medium -
Programming by poking: why MIT stopped teaching SICP | Hacker News -
Make /packages page · Issue #27 · haskell-lang/haskell-lang -
Craig Wright: “I'm Sorry” -
Bitcoin 'creator' backs out of Satoshi coin move 'proof' - BBC News -
Bitcoin 'creator' backs out of Satoshi coin move 'proof' | Hacker News -
Release v1.1.0 · commercialhaskell/stack -
Apple Stole My Music. No, Seriously. | vellumatlanta -
Apple Stole My Music. No, Seriously | Hacker News -
How to uninstall snapshots and older ghc from stack : haskell -
[ANN] stack 1.1.0 released : haskell -
My first DDoS attack for a $200 ransom – Symfony @ Singapore -
Introducing a new, advanced Visual C++ code optimizer | Hacker News -
Introducing a new, advanced Visual C++ code optimizer | Hacker News -
No one should have to use proprietary software to communicate with their government — Free Software Foundation — working together for free software -
Haskell Sucks! - Paul Bone -
Count me as a systemd convert | Hacker News -
The Mypy Blog: Mypy 0.4 Released -
Typed Python: new Mypy release | Hacker News -
XKCD Plots in Matplotlib: Going the Whole Way -
Looking for GHC compile-time performance tests -
Looking for GHC compile-time performance tests : haskell -
A presentation for those who dislike Haskell. : haskell -
Demonstration of how to Backpack'ify this package. · Issue #18 · yihuang/tagstream-conduit · GitHub -
Demonstration of how to Backpack'ify tagstream-conduit : haskell -
Texas Hold'em (the little haskeller) : haskell -
Tell HN: Winners of Apply HN for YC Fellowship 3 | Hacker News -
2 Years Open Source - Atom Blog -
Atom: 2 Years Open Source | Hacker News -
Tell HN: Apply HN apology and revision | Hacker News -
GHC + Stack + ARM is a flaming car crash, but read this if you want your app to run on an Raspberry Pi : haskell -
map - Haskell Ambiguous Occurrences -- how to avoid? - Stack Overflow -
Can Boys Beat Girls in Reading? - WSJ -
Boys outscore girls on reading tests – if they're told it's a game | Hacker News -
Rails 5.0.0.rc1 | Hacker News -
Swift.org - Swift 3.0 Release Process -
Swift 3.0 Release Process | Hacker News -
Swift 3.0 Release Process | Hacker News -
Swift for Windows - Home -
Swift for Windows | Hacker News -
Facebook's newest privacy problem: 'Faceprint' data - CNET -
Your Brain Limits You to Just Five BFFs | Hacker News -
Working with IO UTCTime : haskell -
What is the relationship/difference between determinism and functional purity? : haskell -
Medical Equipment Crashes During Heart Procedure Because of Antivirus Scan -
Medical Equipment Crashes During Heart Procedure Because of Antivirus Scan | Hacker News -
Kel: An Open-Source, Kubernetes-Based PaaS Built in Python and Go | Hacker News -
Show HN: Phone verification at no cost | Hacker News -
Cay Horstmann's Unblog -
Code Page 437 Refuses to Die | Hacker News -
In High School, looking to learn functional programming after being primarily a imperative programmer for the last few years. : haskell -
Coderwall | Store your private data inside a git repository -
PostgreSQL Scalability: Towards Millions TPS - Alexander Korotkov's blog -
How Breakfast Became a Thing | Hacker News -
PostgreSQL Scalability: Towards Millions TPS | Hacker News -
fltkhs-compose-conference-2016-talk/Talk.org at master · deech/fltkhs-compose-conference-2016-talk -
Easy Native GUIs in Haskell, Today! - YouTube -
Make Japanese Iced Coffee Instead of Cold-Brewing for the Best Flavor -
Ideas for the success of Elm - Google Groups -
farewell-to-frp -
A Farewell to FRP | Hacker News -
State Dept.: Clinton IT aide's email archive is lost | Hacker News -
GHC Development: OutsideIn -
Wadler's Law - HaskellWiki -
#10478 (Shorter import syntax) – GHC -
ShorterImportSyntax – GHC -
Using Electron with Haskell : haskell -
Using Electron with Haskell -
Electron 1.0 is here · GitHub -
Electron 1.0 -
Devtron -
Electron 1.0 is here | Hacker News -
SS7 MITM Attack Against WhatsApp and Telegram | Hacker News -
facebook/fbctf: Platform to host Capture the Flag competitions -
Facebook CTF Is Now Open Source | Hacker News -
BBC - Earth - The bizarre beasts living in Romania's poison cave -
A cave which was sealed for 5.5M years discovered in Romania | Hacker News -
It’s a Tough Job Market for the Young Without College Degrees | Hacker News -
Low latency, large working set, and GHC’s garbage collector: pick two of three - Pusher Blog -
performance - Reducing garbage-collection pause time in a Haskell program - Stack Overflow -
GHC cannot achieve low latency with a large working set : haskell -
SQL vs. NoSQL Databases: What’s the Difference? | Hacker News -
Password-less email authentication in a Haskell web framework? : haskell -
Not a hacker or a hipster: How I got my first start-up job — Tech London — Medium -
Not a Hacker or a Hipster – How I Got My First Startup Job | Hacker News -
An Open Letter to Members of the W3C Advisory Committee | Electronic Frontier Foundation -
An Open Letter to Members of the W3C Advisory Committee | Hacker News -
Anders Hejlsberg on Modern Compiler Construction [video] | Hacker News -
What we learned from Google: code reviews aren’t just for catching bugs | FullStory Blog -
Code reviews aren’t just for catching bugs | Hacker News -
Why aren't you using Frege? : haskell -
Why I Haven’t Fixed Your Issue Yet -
Why I Haven’t Fixed Your Issue Yet | Hacker News -
No Sane Compiler Would Optimize Atomics | Hacker News -
Is Haskell able to deal with big data structures as effectively as C/C++ is? : haskell -
What's new with GHC generics in 8.0 - Ryan Scott -
What's new with GHC generics in 8.0 : haskell -
xpost from r/mac: Mac ports: GHC and Haskell platform are out of date : haskell -
Mocking an Automotive Backend with Haskell and Servant: Episode 1 — Detroit Labs -
Whoever does not understand LISP, is doomed to reinvent it. | Lambda the Ultimate -
Whoever does not understand Lisp is doomed to reinvent it (2007) | Hacker News -
Vulkan Tutorial | Hacker News -
Announcing Megaparsec 5 by Mark Karpov -
Announcing Megaparsec 5 : haskell -
Torn Between Two YouTubes - Bloomberg Gadfly -
Torn Between Two YouTubes | Hacker News -
Constant lists of Int/Double/whatever as compile-time arrays? : haskell -
"Jeg måtte snige mig ud på toilettet og ringe hjem og dæmpe gemytterne.” -
JavaScript async/await implemented in V8 | Hacker News -
JavaScript async/await implemented in V8 | Hacker News -
FP shops : haskell -
ReactOS 0.4.1 Released | ReactOS Project -
ReactOS 0.4.1 Released (Activation Context, USB, UniATA, BTRFS and Etc) | Hacker News -
Question about turning code point-free : haskell -
Is there any research related to reasoning about code performance using type systems? : functionalprogramming -
What has Rust borrowed? : haskell -
Compose Conference: Fun with GHCJSi : haskell -
Why dollar sign was chosen for function application? : haskell -
Why don't popular libraries instanciate common type classes? : haskell -
What is the Difference Between GHC and the HaLVM? : haskell -
Reason | Hacker News -
Apple Sent Two Men to My House. No, They Weren’t Assassins | Hacker News -
Apple Sent Two Men to My House. No, They Weren’t Assassins. | vellumatlanta -
A Ukrainian Hacker Who Became the FBI’s Best Weapon and Worst Nightmare | Hacker News -
Why use newtype? : haskell -
Software Licenses and Failed States - 250bpm -
Newtype - HaskellWiki -
Send variable from servant server to client? : haskell -
In-Ear Device That Translates Foreign Languages In Real Time | Bored Panda -
AWS X1 instances – 1.9 TB of memory | Hacker News -
In-Ear Device That Translates Foreign Languages in Real Time | Hacker News -
Are unused import warnings harmful? -
Are unused import warnings harmful? : haskell -
Commentary/Compiler/RelaxedUnusedImports – GHC -
Open Whisper Systems Partners with Google on End-To-end Encryption for Allo | Hacker News -
Open Whisper Systems Partners with Google on End-To-end Encryption for Allo | Hacker News -
Open Whisper Systems >> Blog >> Open Whisper Systems partners with Google on end-to-end encryption for Allo -
Atom + Stack = no globally installed GHC/packages - Nikos Baxevanis -
Atom + Stack = no globally installed GHC/packages : haskell -
Depending on GHCJS risky? : haskell -
Haskell Community and Activity Report #30 is now published : haskell -
Parsing GHC Core in ghc-7.10 ? : haskell -
haskell - Parsing GHC Core in ghc-7.10 - Stack Overflow -
Change your LinkedIn password NOW | Kaspersky Lab Official Blog -
LinkedIn password leak | Hacker News -
Issue 608016 - chromium - Can't navigate back using the backspace key - Monorail -
Chrome removes Backspace to go back | Hacker News -
A language that compiles to Bash and Windows Batch | Hacker News -
Authentication · Issue #70 · haskell-servant/servant -
nvdv/vprof: Visual Python profiler -
Visual profiler for Python | Hacker News -
Bootcamps vs. College | Hacker News -
Bootcamps vs. College - Triplebyte -
Don't Use Allo | Motherboard -
Don't Use Allo | Hacker News -
Fox 'Stole' a Game Clip, Used it in Family Guy & DMCA'd the Original - TorrentFreak -
Google moves to replace Flash with HTML5 - Bitmovin -
Google blocks Flash and moves to HTML5, what does this mean for video | Hacker News -
career advice : haskell -
Haskell, Stack and IntelliJ IDEA IDE setup tutorial how to get started : haskell -
Data Declarations in Where Statements : haskell -
Cutestrap: 8k CSS framework | Hacker News -
“Eat, sleep, code, repeat” is such bullshit — Signal v. Noise -
“Eat, sleep, code, repeat” is such bullshit | Hacker News -
Top developers can have a life outside coding | Hacker News -
Blog: GHC 8.0.1 is available! – GHC -
Migration/8.0 – GHC -
GHC 8.0.1 is available | Hacker News -
[ANNOUNCE] It's official: GHC 8.0.1 has been released : haskell -
Is a stackage snapshot for ghc 8.0 on the way? : haskell -
It would be nice if "?" could be part of a symbol : haskell -
Woman's blindness apparently reversed by stem cell treatment | Hacker News -
For World’s Newest Scrabble Stars, SHORT Tops SHORTER - WSJ -
Nigerians Dominate Scrabble Tournaments Using Five-Letter Word Strategy | Hacker News -
Nigerians Dominate Scrabble Tournaments Using Five-Letter Word Strategy | Hacker News -
My time with Rails is up | Hacker News -
Exhaustive list of all CPP macros : haskell -
Dynamic Swift | Hacker News -
Fraudulent claims made by IBM about Watson and AI | Roger Schank -
3 Reasons AWS Lambda Is Not Ready for Prime Time | Hacker News -
3 Reasons AWS Lambda Is Not Ready for Prime Time | @datawireio -
Electric cars are no longer held back by crappy, expensive batteries | Hacker News -
Stackage LTS and GHC 8.0 | Unknown Parallel -
Stackage LTS and GHC 8.0 : haskell -
Solving the biggest problems now - before Haskell 2020 hits : haskell -
How to manage exceptions : haskell -
Zach Holman joins GitLab as advisor | Hacker News -
Fired -
Firing People -
GitLab Container Registry | Hacker News -
Summer of Haskell 2016 - Accepted Projects : haskell -
Hanami – web framework for Ruby | Hacker News -
Pebble 2, Time 2 + All-New Pebble Core | Hacker News -
Japan adding mandatory programming education to all elementary schools | Hacker News -
jQuery 3.0 Release Candidate...Released! | Official jQuery Blog -
JQuery 3.0 Release Candidate | Hacker News -
Programmers are not different, they need simple UIs | Hacker News -
Going deeper with Project Infinite | Dropbox Tech Blog -
Dropbox: Going Deeper with Project Infinite | Hacker News -
MacVim vs. (neo)vim — Medium -
because people asked: my irssi advanced windowlist setup | die-welt.net -
Airbnb: Building a Visual Language, Behind the scenes of our new design system | Hacker News -
Mathematicians Bridge Finite-Infinite Divide | Hacker News -
What is the point of Haskell packages managed by system-wide package managers? : haskell -
Practical Dependent Types in Haskell -- Type Safe Neural Networks : haskell -
Ethereum is the Forefront of Digital Currency — Stories From Coinbase — Medium -
Coinbase Co-founder: Ethereum Is the Forefront of Digital Currency | Hacker News -
OpenAI Team Update | Hacker News -
Why does MonadZip need to be a Monad? : haskell -
Four hundred miles with Tesla’s autopilot forced me to trust the machine | Ars Technica -
Show HN: Automatic private time tracking for OS X | Hacker News -
GitHub · Where software is built -
IDE for building React Native apps is now free and open source | Hacker News -
LTS Haskell 6.0 (ghc-7.10.3) : haskell -
React Tutorial: Cloning Yelp | Hacker News -
Moving Stackage Nightly to GHC 8.0 | FP Complete -
hackage downtime : haskell -
Haskell gets a job helping everyone get a job! -
A presentation on using Haskell in a company. : haskell -
What would be required to get GHCJS into an npm package, like PureScript and Elm are? : haskell -
Package versioning policy - HaskellWiki -
The Haskell Package Versioning Policy -
Michael Snoyman's personal take on PVP version upper bounds -
"Michael Snoyman's personal take on PVP version upper bounds" : haskell -
WordPress is now 13 years old | Hacker News -
Orphan instance - HaskellWiki -
FaceTime, iMessages hang in the balance after Apple loss to patent troll | Ars Technica -
weigh: Measuring allocations in Haskell : haskell -
weigh: Measuring allocations in Haskell | FP Complete -
Memory footprint of Haskell data types - Stack Overflow -
All European scientific publicly funded articles to be freely accessible by 2020 | Hacker News -
Experience with PornHub's bug bounty: Scornhub -
Experience with PornHub's bug bounty: Scornhub | Hacker News -
Ethereum’s $150-Million Blockchain-Powered Fund Opens Just as Researchers Call For a Halt - IEEE Spectrum -
Ethereum’s $150M DAO Opens Just as Researchers Call for a Halt | Hacker News -
Common Lisp: The Untold Story (2008) | Hacker News -
store: a new and efficient binary serialization library | FP Complete -
store: a new and efficient binary serialization library : haskell -
The Plan-9 Effect or why you should not fix it if it ain't broken -
What Does It Mean to Be Poor in Germany? | Hacker News -
take 100 $ filter (<10) [0..] ~~ need help understanding laziness : haskell -
Recommended queue package? (x-post from /r/haskellquestions) : haskell -
Flask 0.11 Released | Hacker News -
You can’t replace your face, says face recognition | Hacker News -
What UX designers can learn from 1990s Japanese video games | TechCrunch -
What UX designers can learn from 1990s Japanese video games | Hacker News -
The real responsive design challenge? RSS. -
The real responsive design challenge is RSS | Hacker News -
SSA vs. CPS (and ANF?) | Lambda the Ultimate -
[MLton-devel] Re: CPS vs SSA -
CPS, SSA, and ANF – Heidy Khlaaf -
Why Most Unit Testing is Waste [pdf] | Hacker News -
mrb: Real-Time Garbage Collection Is Real -
Measuring GC latencies in Haskell, OCaml, Racket -
Help with TShow (Type level Show class) : haskell -
Why is the F# version of this program 6x faster than the Haskell one? - Stack Overflow -
64bit ARM / ARMv8 GHC? : haskell -
Quick Question: jQuery in Haskell : haskell -
Microlight.js, a code highlighting library | Hacker News -
Sublime Text 3 has been released | Hacker News -
MichaelXavier.net - Enterprise Haskell Pattern: Lensed Reader -
Terminology? "type class" or "typeclass" : haskell -

-

June, 2016

-

-

Library request: validate JSON structure with nice structured errors : haskell -
How to build static checking systems using orders of magnitude less code : haskell -
Screenshot utility : haskell -
Weigh your Haskell code -
What do normal and abnormal heart rhythms look like on Apple Watch? — Cardiogram -
What normal and abnormal heart rhythms look like on an Apple watch | Hacker News -
Blizzard Exempt from iOS and MacOS Security Sandbox | Hacker News -
What Haskell projects can expert beginners contribute to? : haskell -
Pragmatic Haskell Lecture 2: Databases : haskell -
Why is (Int ~ Bool) rejected with an error, not a warning? : haskell -
SyntaxDB – Quickly look up syntax for programming languages | Hacker News -
run functions by giving their names in a string : haskell -
Preprocessor Or Template Haskell for generating Haskell? : haskell -
Apple cloud services outage | Hacker News -
Programming Sucks -
Elizabeth Holmes - Wikipedia, the free encyclopedia -
In Defense of Erlang: Through Magic of Offence | Hacker News -
Facebook disabling messaging in its mobile web app to push people to Messenger | Hacker News -
SublimeText 3 & Haskell in 9 steps – Alex Collins – Blog -
SublimeText 3 & Haskell in 9 steps : haskell -
TeamViewer users are being hacked in bulk, and we still don’t know how | Hacker News -
Vote results: June 5, 2016 - SWI swissinfo.ch -
Swiss Overwhelmingly Reject Basic Income Guarantee | Hacker News -
What is a best way to add private code to a project ? : haskell -
[Taler] GNU Taler 0.0.0 -
[Jobs] AlphaSheets, collaborative/programmable spreadsheet startup : haskell -
Hierarchy of documentation dysfunction : haskell -
Creating a Stack Machine with Haskell : haskell -
Happy Learn Haskell Tutorial V1 book now totally FREE! (And Beta for Vol 2 on the way) : haskell -
Multiple new Haskell developer roles in Strats at Standard Chartered : haskell -
Announce: arithmoi 0.4.2.0 : haskell -
Understanding haskell inlining/fusion hints : haskell -
Removing Python's GIL: The Gilectomy [video] | Hacker News -
Putting Down Elm /// -
How do you avoid repeating mtl-style instances? : haskell -
Is it good to lift IO actions when publishing a package? : haskell -
Intero for Emacs -
What is the point of Typeable? : haskell -
Intero for Emacs : haskell -
Seven months later, Valve’s Steam Machines look dead in the water | Ars Technica -
Seven months later, Valve’s Steam Machines look dead in the water | Hacker News -
Valve Steam Machine selling very poorly | Hacker News -
A case against missing upper bounds : haskell -
Haskell Tutorials, a tutorial : haskell -
Show HN: New calendar app idea | Hacker News -
Understanding the Elm type system | Adam Waselnuk - Front End Web Developer -
Getting Started | Nuclide -
Getting Started – React Native | A framework for building native apps using React -
Understanding the Elm type system | Hacker News -
Elm package design guidelines : haskell -
Design Guidelines -
Just started learning Haskell and its the most awesome thing ever : haskell -
PSA: Make sure to use uuid >= 1.3.12 : haskell -
An unofficial WWDC app for OS X | Hacker News -
MaiaVictor/unknown_halting_status: Small programs with unknown halting status. -
Shortest programs with unknown halting status : haskell -
Video: “I am being sued for using the Google Play Store.” | Hacker News -
How Celery fixed Python's GIL problem -
How Celery Fixed Python's GIL Problem | Hacker News -
How to use the Application object of Android | -
Open-Sourcing CloudFlare’s UI Framework | Hacker News -
Read Me | Redux -
Motivation | Redux -
Three Principles | Redux -
Prior Art | Redux -
Ecosystem | Redux -
Examples | Redux -
Basics | Redux -
Getting Started with React, Redux and Immutable: a Test-Driven Tutorial (Part 1) | Theodo -
Actions | Redux -
Reducers | Redux -
Store | Redux -
Data Flow | Redux -
facebook/react-native-fbsdk: A React Native wrapper around the Facebook SDKs for Android and iOS. Provides access to Facebook login, sharing, graph requests, app events etc. -
It’s cheaper to build multiple native applications than one responsive web app | Hacker News -
Using React Native: One Year Later — Discord Engineering -
Usage with React | Redux -
Example: Todo List | Redux -
Building Efficient UI with React and Redux | Toptal -
hexena: Haskell EXEcutable aNAlyser : haskell -
Chrome DevTools in 2016 and Beyond | Hacker News -
Gawker Files for Bankruptcy, Will Be Put Up for Auction - WSJ -
Apple Starts to Woo Its App Developers - The New York Times -
Apple Starts to Woo Its App Developers | Hacker News -
[Nix-dev] Stackage Support Will Be Discontinued -
PSA: NixOS to discontinue Stackage Support : haskell -
jQuery 3.0 Released | Hacker News -
{·ᴥ·} -
Show HN: Create instantly shareable native app with a single JSON markup | Hacker News -
GHCforMacOSX looking for a new maintainer : haskell -
Safe way to fork something into background within a Servant REST handler? : haskell -
GHC 8.0 · Issue #49 · ghcformacosx/ghc-dot-app · GitHub -
The Day we hired a Blind Coder — The MomoCentral Times — Medium -
We Hired a Blind Coder | Hacker News -
How bad is the Windows command line really? -
How bad is the Windows command line really? | Hacker News -
Myth debunking: WebViews suck, everything should be native | Hacker News -
Baby Steps: Slowly Porting musl to Rust -
Baby Steps: Slowly Porting Musl to Rust | Hacker News -
haskell-gets-a-job/Haskell-gets-a-job.pdf at master · screamish/haskell-gets-a-job · GitHub -
Distributed Systems in Haskell :: Will Yager -
John Carmack - I have been advocating this for many years, but the... -
John Carmack: The case for memory mapped GPU assets. Again | Hacker News -
Microsoft to acquire LinkedIn for $26.2bn | Hacker News -
Four months with Haskell : haskell -
Four months with Haskell | Hacker News -
Working on a Haskell Tutorial -- Looking for Feedback! : haskell -
Stable Haskell development environment : haskell -
Ghostbuster: A Tool for Simplifying and Converting GADTs : haskell -
Text editor plugin for automatically adding language extensions? : haskell -
Newbie! A few questions on mutability and FFI with C. : haskell -
iMessage - Apple Developer -
iMessage + Apps | Hacker News -
OS X is now macOS and gets support for Siri, auto unlock | Hacker News -
Apple announces iOS 10 | Hacker News -
Apple File System | Hacker News -
Stripe: Apple Pay — Frictionless iOS payments -
Apple Pay on the Web | Hacker News -
Best practice for Facebook login flow with the JavaScript SDK and PHP SDK v4.1 :: Sammy Kaye Powers -
The Story of Haskell at IMVU – Chad Austin -
The Story of Haskell at IMVU | Hacker News -
K-Nearest Neighbors from Scratch | Hacker News -
Emacs gets Haskell IDE • The Register -
GitHub Security Update: Reused password attack -
The Story of Haskell at IMVU : haskell -
Contextual Identities on the Web | Tanvi's Blog -
Contextual Identities on the Web | Hacker News -
The First Big Company to Say It’s Serving the Legal Marijuana Trade? Microsoft. - The New York Times -
Microsoft is the first big company to say it's serving the legal marijuana trade | Hacker News -
Sådan håndterer DTU den ændrede fremdriftsreform - DTU -
How Yahoo derailed Tumblr -
Git 2.9 released | Hacker News -
Haddock at your fingertips -
Ten years on, revisiting YouTube’s first viral sensation | Hacker News -
Gangnam Style overflows INT_MAX, forces YouTube to go 64-bit | Ars Technica -
The DAO is currently being attacked, over 2M Ethereum missing so far | Hacker News -
CRITICAL UPDATE Re: DAO Vulnerability - Ethereum Blog -
Critical Update on DAO Vulnerability | Hacker News -
Introducing postgresql-transactional — Helium Blog -
Deconstructing theDAO Attack: A Brief Code Tour -
Would the smart formal methods people here mind chiming in with some calm advice on this thread? : haskell -
Appropriate forum for servant usage questions? : haskell -
How Frankenstein’s Monster Became Human | New Republic -
How Frankenstein’s Monster Became Human | Hacker News -
Haskell in Industry, Startups and Business: 2016 report (with images) · realtalktech · Storify -
Haskell in Industry, Startups and Business: 2016 report : haskell -
Haskell in Industry, Startups and Business: 2016 report : haskell -
Servant - Optional Response Headers : haskell -
Simple token authentication with Servant : haskell -
Blog: Contributing to GHC – GHC -
How should I update this version number for PVP? : haskell -
How SQLite Is Tested | Hacker News -
Monoid/Semigroup instance for Encoding leads to misuse · Issue #434 · bos/aeson -
async exceptions, STM, and deadlocks | FP Complete -
async exceptions, STM, and deadlocks : haskell -
async exceptions, STM, and deadlocks | FP Complete -
Erlang/OTP 19.0 has been released | Hacker News -
Erlang Programming Language -
Dotty -
What language(s) do you use for work (to make money)? : haskell -
Dotty: a next generation compiler for Scala | Hacker News -
Mozilla Awards $385,000 to Open Source Projects | Hacker News -
Apple confirms iOS kernel code left unencrypted intentionally | TechCrunch -
Unicode Power Symbol | We did it! How a comment on HackerNews lead to 4 ½ new Unicode characters -
Apple confirms iOS kernel code left unencrypted intentionally | Hacker News -
How a comment on Hacker News led to 4½ new Unicode characters | Hacker News -
Why we challenge Microsoft’s battery test -
Why we challenge Microsoft’s battery test | Hacker News -
Coconut Programming Language -
Crispr Wins Key Approval to Fight Cancer in Human Trials | Hacker News -
Chicago Haskell: Getting started with GHCJS dev : haskell -
Brexit: David Cameron to quit after UK votes to leave EU - BBC News -
UK votes to leave EU | Hacker News -
Young voters wanted Brexit the least – and will have to live with it the longest | Hacker News -
I made a cli tool to give me the current day. is there anything that can be imrpoved? : haskell -
What do you start without : haskell -
Stack binary release for aRMv7 : haskell -
My God, it's full of yaks!! -
My God, it's full of yaks | Hacker News -
iOS 10 and MacOS Sierra: Networking for the Modern Internet | Hacker News -
Networking for the Modern Internet - WWDC 2016 - Videos - Apple Developer -
Over one million people sign UK petition for second EU referendum | Hacker News -
STGi: Visual STG implementation to help understand Haskell's execution model | Hacker News -
Over one million people sign UK petition for second EU referendum | world-news | Hindustan Times -
what is the point of inferred typing if you are just going to annotate everything anyways? : haskell -
To mitigate poverty, Y Combinator set to launch minimum income plan | Ars Technica -
Norway–European Union relations | Hacker News -
A ZFS developer’s analysis of the good and bad in Apple’s new APFS file system | Ars Technica -
Why Poor Web Performance for Haskell? : haskell -

-

July, 2016

-

-

OpenLTE: An open source 3GPP LTE implementation | Hacker News -
The State of SourceForge Since Its Acquisition in January : sysadmin -
The Tcl War (1994) | Hacker News -
Why is Python slow | Hacker News -
A ZFS developer’s analysis of Apple’s new APFS file system | Hacker News -
Css is more powerful today and you can do a lot of thing dont need js | Hacker News -
BSD vs. Linux (2005) | Hacker News -
BSD For Linux Users :: Intro -
Haskell ArgumentDo Proposal | Hacker News -
ArgumentDo – GHC -
ArgumentDo proposal : haskell -
ghcJS @ lts-6.6 : haskell -
Is Haskell is a fairly robust language for Web application in a sensitive area : haskell -
Plugin based Architectures in Haskell : haskell -
Why does this simple program consume all available CPU resources? : haskell -
Make the Web Work For Everyone ★ Mozilla Hacks – the Web developer blog -
Make the Web Work for Everyone | Hacker News -
Go’s march to low-latency GC — Twitch Blog -
Go’s march to low-latency GC | Hacker News -
LambCI – A continuous integration system built on AWS Lambda | Hacker News -
Luakit: Fast, small, WebKit-based browser framework extensible by Lua | Hacker News -
There are Guards and Guards — Medium -
An Erlang WAT-story about guards and guards | Hacker News -
Apple Open-Sources its New Compression Algorithm LZFSE -
Apple Open-Sources its Compression Algorithm LZFSE | Hacker News -
Refining Rust's RFCs (editorial: as GHC Haskell seeks better RFC process, this is interesting to read) : haskell -
Apollo 11 Guidance Computer source code | Hacker News -
Russ Olsen - To the Moon! - YouTube -
Pokémon Go has already taught me so much | TechCrunch -
Pauseless Garbage Collection for Java - Azul Systems, Inc. Azul Systems, Inc. -
A JVM Does That? (2011) [pdf] | Hacker News -
Mozilla could walk away from Yahoo deal and get more than $1B | Hacker News -
Facebook ‘glitch’ that deleted the Castile shooting video: It was the police | Hacker News -
Open Whisper Systems >> Blog >> Facebook Messenger deploys Signal Protocol for end to end encryption -
Facebook Messenger begins testing end-to-end encryption using Signal Protocol | Hacker News -
jameysharp/corrode: C to Rust translator -
Corrode: C to Rust translator written in Haskell | Hacker News -
Our nightmare on Amazon ECS | Hacker News -
Securing a travel iPhone -
Securing a travel iPhone | Hacker News -
New Haskell community nexus site launched. : haskell -
Refining Rust's RFCs · Aaron Turon -
servant-persistent updated -
servant-persistent updated: now a more useful starter pack! : haskell -
The Brexit hangover has left UK startups drained – Are the brains next? | Hacker News -
Blog: Rethinking GHC's approach to managing proposals – GHC -
ANN: intero-neovim (alpha quality, feedback requested) : haskell -
GHC Blog: Rethinking GHC's approach to managing proposals : haskell -
https://turingjump.com/posts/tech-empower.html -
servant on TechEmpower benchmarks : haskell -
What does * in a typeclass instance mean? : haskell -
New haskell-lang.org | Hacker News -
FP Complete, haskell-lang.org, Stack, and Stackage · GitHub -
What's the current agreed upon solution to Records? : haskell -
Why we use the Linux kernel's TCP stack -
Why we use the Linux kernel's TCP stack | Hacker News -
how-to-use-elm-at-work -
Examples of why lens is useful? : haskell -
Interesting / useful / neat applications of id function : haskell -
What’s the best way to create pre-built binaries of my Haskell program? : haskell -
Is there a library for creation / manipulation of docx? : haskell -
Shorter develop-compile-debug cycles? (or how to speed up compile times) : haskell -
Haskel newbie here: So many Infix operators? : haskell -
Marp: Markdown Presentation Writer | Hacker News -
Haskell: The Bad Parts : haskell -
Shipping Rust in Firefox ★ Mozilla Hacks – the Web developer blog -
Shipping Rust code in Firefox | Hacker News -
HTML Templating : haskell -
Deep dive into the murky waters of script loading - HTML5 Rocks -
Startup Technical Diligence Is a Waste of Time | Hacker News -
Tell Us your Haskell Hiring Experience : haskell -
Haskell Symposium accepted papers : haskell -
React: Mixins Considered Harmful | Hacker News -
Starcraft Html5 project revived on GitHub without all copyrighted materials | Hacker News -
Blind-tested soloists unable to tell Stradivarius violins from modern instruments - The Strad -
Blind-tested soloists unable to tell Stradivarius from modern violins | Hacker News -
Hammerspoon – Powerful automation of OS X with Lua | Hacker News -
GitHub - rgcr/m-cli:  Swiss Army Knife for Mac OS X -
Show HN: Swiss Army Knife for Mac OS X | Hacker News -
Multiple Bugs in OpenBSD Kernel | Hacker News -
I remade a compiler that beginners can use to create their own mini functional language with : haskell -
Mr Robot S02E01 easter egg | Hacker News -
Visual Haskell programming: representing pure functions as Composable lego blocks : haskell -
Servant Performance : haskell -
CloudFlare, We Have A Problem - joepie91's Ramblings -
CloudFlare, We Have a Problem | Hacker News -
Hablog – High-availability, distributed, lightweight, static site with comments | Hacker News -
Destroy All Ifs — A Perspective from Functional Programming – John A De Goes -
Destroy All Ifs – A Perspective from Functional Programming | Hacker News -
Destroy All Ifs – A Perspective from Functional Programming : haskell -
Release Wallet and Mist Beta with Hard Fork choice 0.8.1 · ethereum/mist -
#ethereum fork: Mist browser now available with hardfork choice | Hacker News -
hack.css -
Hack – CSS framework for Hackers | Hacker News -
Can you not create a type "synonym" for records? : haskell -
FESCo Approves Latest Round Of Fedora 25 Features - Phoronix -
Why is Fedora only putting in GHC 7.10 for F25? : haskell -
Why most “clever” code ain’t so clever after all by Carlo Pescio, 2016 : programming -
Why most "clever" code ain't so clever after all [x-post from r/programming] : haskell -
Crypto flaw made it easy for attackers to snoop on Juniper customers | Hacker News -
Exponential time complexity in the Swift type checker | Hacker News -
Serverless Architectures | Hacker News -
Typeclasses and Run-Time Dependency Management -
Typeclasses and Run-Time Dependency Management : haskell -
Examples of real-world haskell usage where correctness is important (not just type-safety) : haskell -
IamA (I built a fusion reactor in my bedroom) AMA! : IAmA -
I built a fusion reactor in my bedroom – AMA | Hacker News -
Are type families a suitable solution to the record problem : haskell -
Show HN: Language Evolution Simulation | Hacker News -
Stick a Fork in Ethereum | Elaine's Idle Mind -
Stick a Fork in Ethereum | Hacker News -
BT Broadband users hit by fault - BBC News -
BT Broadband outage in the UK | Hacker News -
Datatilsynet: Anbefalet brev afleveret til en forkert modtager -
Danish authorities deliver 5m personal ID# to China by accident (danish link) | Hacker News -
Neil Mitchell's Haskell Blog: Making sequence/mapM for IO take O(1) stack -
Making sequence/mapM for IO take O(1) stack : haskell -
What Template Haskell gets wrong and Racket gets right : haskell -
Encryption added to ZFS -
Native encryption added to ZFS on Linux | Hacker News -
Stick a Fork in Ethereum | Hacker News -
Alleged founder of world’s largest BitTorrent distribution site arrested | Ars Technica -
Alleged founder of world’s largest BitTorrent distribution site arrested | Hacker News -
Proposal process status -
you may need a Slack Team for Haskell : haskell -
Does Servant run On Multi-core CPU? : haskell -
New Features in Ruby 2.4 | Hacker News -
Programming Language Rankings: June 2016 | Hacker News -
Free Monads - Haskell Language - Stack Overflow -
StackOverflow Documentation is now public beta and there are haskell topics that could use your attention : haskell -
Why I’m Suing the US Government « bunnie's blog -
Why I’m Suing the US Government | Hacker News -
China is challenging the idea that censorship thwarts online innovation | Hacker News -
Statement on DMCA Lawsuit | Hacker News -
A Few Thoughts on Cryptographic Engineering: Statement on DMCA lawsuit -
Zenzizenzizenzic | Hacker News -
Introducing Stack Overflow Documentation Beta – Stack Overflow Blog – A destination for all things related to development at Stack Overflow -
Introducing Stack Overflow Documentation Beta | Hacker News -
Superformula | Hacker News -
Reddit is still in turmoil | TechCrunch -
Reddit is still in turmoil | Hacker News -
FallibleInc/security-guide-for-developers: Security Guide for Developers -
Automatic Parallelisation at runtime : haskell -
security-guide-for-developers/security-checklist.md at master · FallibleInc/security-guide-for-developers -
A practical security guide for web developers | Hacker News -
LLVM Programmer’s Manual | Hacker News -
KickassTorrents resurfaces online, as all piracy sites do | The Verge -
Sculpture of Housing Prices Ripping San Francisco Apart | Doug McCune -
Sculpture of Housing Prices Ripping San Francisco Apart | Hacker News -
Pattern Synonyms in Template Haskell : haskell -
Apple says Pokémon Go is the most downloaded app in its first week ever | Hacker News -
Create Apps with No Configuration | React -
Create React Apps with No Configuration | Hacker News -
Dead googlecode URLs on GitHub | Hacker News -
David Chang’s Unified Theory of Deliciousness | Hacker News -
// Object.map: returns a new object with the predicate applied to each value Th... | Hacker News -
Google tags Wikileaks as a dangerous site | Hacker News -
HGE2D : A 2D game engine (many updates since last post, also with a full game as example now) : haskell -
Rust: The New LLVM | &Notepad -
Any suggestions about haskell + uI + openGL? : haskell -
multithreading - What's the relationship between forkOn and the -qm RTS flag? - Stack Overflow -
Rust: The New LLVM | Hacker News -
How we broke PHP, hacked Pornhub and earned $20k | Hacker News -
Pokémon Go Is Teaching Americans the Metric System | Hacker News -
Countdown to fully dependent types? : haskell -
Functional Programming Jargon | Hacker News -
I am new to haskell, and need some help on an assignment. : haskell -
Goodbye, Object Oriented Programming — Medium -
Goodbye, Object Oriented Programming | Hacker News -
Dependent types in Haskell: Progress Report | Types and Kinds -
Dependent types in Haskell: Progress Report : haskell -
Badness 10.000 - Algebraic patterns - Semigroup -
Badness 10.000 - Algebraic patterns - Identity element -
Badness 10.000 - Algebraic patterns - Monoid -
Algebraic Patterns - Monoid : haskell -
Documentation priorities : haskell_lang -
Has learning Haskell paid off for you? : haskell -
A surprising (?) GHC Type Inference Omission : haskell -
Help with Servant-client-0.7.1 : haskell -
Can Programming Be Liberated From The Von Neumann Style? (1977) [pdf] | Hacker News -
NIST declares the age of SMS-based 2-factor authentication over | Hacker News -
The Apple Goes Mushy Part I: OS X's Interface Decline | Hacker News -
Bitcoin’s not money, judge rules as she tosses money-laundering charge - The Washington Post -
[Question] Is the newtype keyword like inheritance in OOP ? : haskell -
Why Uber Engineering Switched from Postgres to MySQL | Hacker News -
React in patterns | Hacker News -
Web Design in 4 minutes -
Show HN: Web Design in 4 minutes | Hacker News -
We have an epidemic of bad posture -
We have an epidemic of bad posture | Hacker News -
Intel Begins Shipping First Kaby Lake Processors, but Most Macs Won't Get Them Until 2017 - Mac Rumors -
iPhone, iPad and Mac sales are down, but Apple only cares about services now | Hacker News -
Onward from the Hard Fork | Hacker News -
Xiaomi’s first laptop is a Macbook Air rival that’s as cheap as $540 | TechCrunch -
Xiaomi’s first laptop is a Macbook Air rival | Hacker News -
Ask HN: Anonymous person sent proof of SSH access to our production server | Hacker News -
Salary transparency at Stack Overflow – Stack Overflow Blog – A destination for all things related to development at Stack Overflow -
Salary transparency at Stack Overflow | Hacker News -
Proposal: tracking security holes and major bugs with Stackage and Stack : haskell -
Haskell jobs (Full-time and contractor) : haskell -
If you're interested in NLP, and want to contribute to open source, consider Hext: a text classification library : haskell -
Opiniosn on Hack(language) ? : haskell -
What are the Top things you miss from other languages? : haskell -
Haskell DSLs vs. Java template engines (e.g. BlazeHtml vs. Apache Velocity) : haskell -
Large Primes! : haskell -
Very first release of Raaz a cryptographic library for Haskell. : haskell -
What will Dependant Types add to general programming? : haskell -
Elm vs PureScript I -
Elm vs PureScript -- I've typed this or something similar enough times to just stick it in a Gist : ] · GitHub -
An Introduction to Elm - GitBook -
Core Language · An Introduction to Elm -
The Elm Architecture · An Introduction to Elm -
User Input · An Introduction to Elm -
Buttons · An Introduction to Elm -
Text Fields · An Introduction to Elm -
Forms · An Introduction to Elm -
Effects · An Introduction to Elm -
Bringing ChakraCore to Linux and OS X | Hacker News -
The Rust Platform · Aaron Turon -
The Rust Platform | Hacker News -
The Rust Platform : haskell -
[Haskell-community] Haskell Platform as the default recommendation considered harmful -
Citibank IT guy deliberately wiped routers, shut down 90% of networks -
Citibank IT guy deliberately wiped routers, shut down 90% of firm’s US networks | Hacker News -
Second call for help: StackOverflow Documentation Beta has plenty of activity on its Haskell topic - come help! : haskell -
Show HN: RESTful Bitcoin payment channel server (written in Haskell) | Hacker News -
How to completely Delete Haskell [Windows] : haskell -
Harvard Study: Biggest Factor in Divorce Is Husband's Employment Status | Hacker News -
Brazil Freezes Facebook Funds | Hacker News -
Haskell Data Analysis and Machine Learning Cookbook | Hacker News -
Docker for Mac and Windows is Now Generally Available and Ready for Production | Docker Blog -
Protecting Free Speech: Why Yelp is Marking Businesses That Sue Their Customers - Yelp -
Protecting Free Speech: Why Yelp Is Marking Businesses That Sue Their Customers | Hacker News -
There are limits to 2FA | Hacker News -

-

August, 2016

-

-

There are limits to 2FA | Hacker News -
How do you deploy your haskell web server : haskell -
Contracts in haskell : haskell -
Commentary/Contracts – GHC -
Haskell apprenticeship : haskell -
There are limits to 2FA and it can be near-crippling to your digital life | Ars Technica -
How to use your full brain when writing code | Hacker News -
A founder's perspective on 4 years with Haskell -
A founder's perspective on 4 years with Haskell : haskell -
Atom 1.9 and 1.10 beta - Atom Blog -
Atom 1.9 and 1.10 beta | Hacker News -
Django 1.10 released | Weblog | Django -
I Love Go; I Hate Go | Hacker News -
what's the best libraries to use in a new GHCJS project? : haskell -
Concurrent Haskell in the real world -
Show HN: Learn Functional Programming Using Haskell | Hacker News -
Play: A P2P Distributed Torrent Site That's Impossible to Shut Down - TorrentFreak -
Facebook could owe $5B in back taxes | Hacker News -
Software Simply: How to Get a Haskell Job -
haskell - Parsec: Applicatives vs Monads - Stack Overflow -
The difference between Applicative and Monad is that between batch mode and interactive mode : haskell -
Show HN: Git Confirm. Git hook to catch accidentally committed code (TODO/Skip) | Hacker News -
pimterry/git-confirm: Git hook to catch placeholders and temporary changes (TODO / @ignore) before you commit them. -
How to Get a Haskell Job : haskell -
Macbook charger teardown: The surprising complexity inside Apple's power adapter -
Macbook charger teardown: Complexity inside Apple's power adapter (2015) | Hacker News -
Making django CMS as easy to install as WordPress - Blog - django CMS 2016 -
Making Django CMS as easy to install as WordPress | Hacker News -
Haskell, But Quickly -
Haskell, But Quickly - ship actual software instead of just another useless Fibbonacci exercise : haskell -
Idiom brackets in GHC : haskell -
Idiom brackets - HaskellWiki -
This is what Apple should tell you when you lose your iPhone — Hacker Noon -
What Apple should tell you when you lose your iPhone | Hacker News -
We need an impactful haskell application : haskell -
Torrentz Shuts Down, Largest Torrent Meta-Search Engine Says Farewell - TorrentFreak -
Why is the Haskell core libraries lacking of essential utility functions, such as split? : haskell -
JQuery++ | Hacker News -
Let's Write Documentation for Haskell : haskell -
React Fiber Architecture | Hacker News -
[Haskell-cafe] [ANN] aeson 1.0.0.0 -
TLS Everywhere, not https: URIs (2015) | Hacker News -
Snap: A Haskell Web Framework: Blog: Announcing: Snap 1.0 -
Announcing: Snap 1.0 : haskell -
Snap: A Haskell Web Framework: Blog: Announcing: Snap 1.0 -
heist: An Haskell template system supporting both HTML5 and XML. -
Moving 12 years of email from GMail to FastMail | Hacker News -
announcing hasky extensions for emacs : haskell -
Intero for Emacs: Changes June–July : haskell -
Announcements -
Thoughts on Uber’s List of Postgres Limitations | Hacker News -
Building fast.com | Hacker News -
Example haskell rest api project? : haskell -
GitHub · Where software is built -
Compile Go programs 7x faster | Hacker News -
‎lcamtuf.coredump.cx/squirrel/ -
The Life of a Phone Sex Operator | Hacker News -
Intero for Emacs: complete interactive development program for Haskell | Hacker News -
React Server -
Fuchsia, a new operating system | Hacker News -
React Server | Hacker News -
What I learned as a hired consultant to autodidact physicists | Aeon Ideas -
What I learned as a hired consultant to autodidact physicists | Hacker News -
ghc-proposals/0000-backpack.rst at 9bdb9de01b2853298efa9b154f7af94db8bbc215 · ezyang/ghc-proposals -
Specification for Backpack, proposal for mix-in packages (work in progress) : haskell -
A Glimpse Into The Future With React Native For Web – Smashing Magazine -
Encrypted Magento extensions on Connect - yes or no? -
Don’t need no education: What Danes consider healthy children’s television | The Economist -
What Danes consider healthy childrens’ television | Hacker News -
Happy 10th birthday, pandoc! : haskell -
Why building web-apps in Haskell is harder than it ought to be : haskell -
Ask HN: Is it possible to run your own mail server for personal use? | Hacker News -
Why building web-apps in Haskell is harder than it ought to be — Medium -
Intero for Emacs: complete interactive development program for Haskell | Hacker News -
From Vim to Emacs in Fourteen Days -
135M messages a second between processes in Java (2013) | Hacker News -
SW-delta: an incremental cache for the web | Hacker News -
[Tutorial/Workshop] Implementing a shell-based Slack client from scratch : haskell -
Quick start -
Spacemacs Conventions -
Neil Mitchell's Haskell Blog: The Four Flaws of Haskell -
GHC from a software security perspective : haskell -
Neil Mitchell's Haskell Blog: The Four Flaws of Haskell : haskell -
Introducing Webpack Dashboard | Hacker News -
Go 1.7 is released - The Go Blog -
Vacancies | Myrtle Software Ltd -
Haskell Job In Cambridge UK Working On Compilers, Machine Learning And Computer Vision! : haskell -
Snowden: Hack of an NSA server is not unprecedented, publication of the take is | Hacker News -
LDN Functionals #8: OCaml the Ultimate Config Format - YouTube -
OCaml the Ultimate Config Format : haskell -
Is there a read/show for text or bytestring out there somewhere? : haskell -
We’re Bullish on AMP — Track Changes -
We're Bullish on AMP | Hacker News -
Twitch is acquiring popular video game community and software maker Curse | TechCrunch -
Twitch is acquiring popular video game community and software maker Curse | Hacker News -
What Great Listeners Actually Do -
It’s The Future - CircleCI -
Heroku is dead – no-one uses it anymore. You need to use Docker now | Hacker News -
Vim plugin for Chrome (Surfingkeys) | Hacker News -
Haskell devops/dev tools role at Standard Chartered (London) | Control.Monad.Writer -
Haskell devops/dev tools role at Standard Chartered (London) : haskell -
benchmarksgame haskell nbody, is written imperative : haskell -
Bitcoin 0.13.0 Binary Safety Warning | Hacker News -
PHP - The Wrong Way -
PHP the Wrong Way | Hacker News -
initialsession: remove curl and wget aliases · Issue #1901 · PowerShell/PowerShell -
Goodbye Mac OS Forge, hello GitHub -
Vim helper For Plugin For Writing Haskell, Without Any Dependencies (Link In Post) : haskell -
Pitfalls in Haskell | Hacker News -
Pitfalls in Haskell : haskell -
The state of mySQL in haskell? : haskell -
Typelevel.scala | Higher-kinded types: the difference between giving up, and moving forward -
What exactly do companies use Haskell for? : haskell -
Sharing Coloring Books with Friends in Rust -
Records – GHC -
Well-Typed - The Haskell Consultants: OverloadedRecordFields revived -
Is there an alternative to records in Haskell? - Stack Overflow -
Why is OCaml's (+) not polymorphic? - Stack Overflow -
ekmett/lens: Lenses, Folds, and Traversals - Join us on freenode #haskell-lens -
A literate Haskell example of embedding Servant into a Yesod Application : haskell -
Python JITs are coming | Hacker News -
Stop the inner struggle over whether your thoughts and feelings are “right” | Hacker News -
3 years out of date on haskell. Looking for tutor : haskell -
Haskell positions at Facebook : haskell -
Round 13 results - TechEmpower Framework Benchmarks -
servant on TechEmpower benchmarks : haskell -
Problems understanding haskell compile Error : haskell -
A few HTML tips ★ Mozilla Hacks – the Web developer blog -
A few HTML tips | Hacker News -
Does a compiler use all x86 instructions? - Wishful Coding -
Does a compiler use all x86 instructions? | Hacker News -
Baidu Takes FPGA Approach to Accelerating SQL at Scale -
iOS remote exploit discovered in the wild attacking high-value targets | Hacker News -
How to make Slack less bad for you | Hacker News -
[ANN] neovim-ghcid : haskell -
Why Haskell doesn't have error signatures? : haskell -
Use It Too Much and Lose It? The Effect of Working Hours on Cognitive Ability [pdf] | Hacker News -
HackedThat: Breaking in to a hardened server via the back door | Hacker News -
Dropbox prompts password resets | Hacker News -
Top Books Found in Comments on HackerNews | Hacker News -
What would make this Yesod code 15% faster than Servant? : haskell -
Announcing The GitLab Issue Board | GitLab -
mmstick/parallel: Inspired by GNU Parallel, a command-line CPU load balancer written in Rust. -
Parallel – A command-line CPU load balancer written in Rust | Hacker News -
#597050 - moreutils: parallel is obsolete - Debian Bug report logs -
A short introduction to the MVVM-C design pattern | Hacker News -
Another Denuvo-protected game cracked just weeks after release | Ars Technica -
“Silicon Valley is hostile to diversity,” says Slack Director of Engineering Leslie Miley | Ars Technica -
Safari 10 brings fast, native App Extensions to the macOS browser, web content -
[ANN] rawr-0.0.0.0: Yet Another Record Library : haskell -
Data.Rawr -
Baidu Takes FPGA Approach to Accelerating SQL at Scale | Hacker News -
C++ IDEs – a rant | Hacker News -
What Slack might learn from its Open Source alternative | Mattermost -
What Slack might learn from its Open Source alternative | Hacker News -
Optimizing incremental compilation : Inside 736-131 -
Optimizing incremental compilation : haskell -
Breaking a myth: Data shows you don’t actually need a co-founder | Hacker News -
The Strange Reason Nearly Every Film Ends by Saying It’s Fiction (You Guessed It: Rasputin!) -
haskell.org and the Evil Cabal - Michael Snoyman's blog -
haskell.org and the Evil Cabal : haskell -
indentation in EMACS : haskell -
Compiles with tabs, but not with Spaces? : haskell -
[Haskell-community] haskell.org download page : haskell -
Post: Docker Not Ready for Prime Time -
GitHub - koush/electron-chrome -
Run Chrome apps in Electron | Hacker News -
Show HN: In-view.js – Get notified when DOM elements enter or exit the viewport | Hacker News -
Resignation : haskell -
Switching from lists to mutable vectors slowed things down. How can I restructure this? : haskell -
Follow up: haskell.org and the Evil Cabal - Michael Snoyman's blog -
Make stack the primary download method on the Downloads page by snoyberg · Pull Request #130 · haskell-infra/hl -
Indian ISPs Speed Up BitTorrent by ‘Peering’ with a Torrent Site | Hacker News -
hindent 5: One style to rule them all -
hIndent 5: One style to rule them all : haskell -
hIndent 5: One style to rule them all : haskell -
Doodle poll: Which tab width do you prefer for Haskell code? : haskell -
Google Chrome Blog: Google Cast is now built-in to Chrome -
Last call for merge requests for 8.0.2 -
Last call for merge requests for GHC 8.0.2 : haskell -
janestreet/ecaml: Writing Emacs plugin in OCaml -
cabal new-build is a package manager : Inside 736-131 -
cabal new-build is a package manager! : haskell -
Overture: A Haskell Prelude – Medium -
[Bikeshedding] Rename Haskell Platform Minimal : haskell -
AWS S3 open source alternative written in Go | Hacker News -
Pointless Haskell -
Write emacs (25) extensions in Haskell : haskell -
GitHub - knupfer/haskell-emacs: Write Emacs extensions in Haskell -
Stack in HP does not find installed msys when using system ghc · Issue #251 · haskell/haskell-platform · GitHub -
Dropbox employee’s password reuse led to theft of 60M+ user credentials | TechCrunch -
The Dropbox hack is real | Hacker News -
Troy Hunt: The Dropbox hack is real -
Dropbox employee’s password reuse led to theft of 60M+ user credentials | Hacker News -
[Haskell-cafe] Announcing containers 0.5.8.1 -
Announcing containers 0.5.8.1 (new functions, speedups, and more!) : haskell -
Google Takes on Uber With New Ride-Share Service - WSJ -
Google Takes on Uber with New Ride-Share Service | Hacker News -
DataHaskell - An Open Source Haskell Data Science Organization : haskell -
Stitch – A small Python library for writing reproducible reports in Markdown | Hacker News -

-

September, 2016

-

-

Wagon Joins Box - Wagon -
So long Wagon! We'll miss you. : haskell -
Speed-up your application with inlined id : haskell -
Performance over Data.Vector · Issue #23 · haskell/bytestring -
How good is Yi for haskell development at the moment? : haskell -
Backpack and separate compilation : Inside 736-131 : haskell -
Transmission -
Transmission BitTorrent client download was compromised for 2 days | Hacker News -
Foldable - Haskell Language - Stack Overflow -
Lens - Haskell Language - Stack Overflow -
Backpack and separate compilation : Inside 736-131 -
Is it possible to promote the length of an input to the type level? : haskell -
This snippet of code is syntactically valid in both PHP and Java, and produces the same output in both. -
polyglot.[cob|pas|f77|c|ps|sh|com] -
Code that is valid in both PHP and Java, and produces the same output in both | Hacker News -
OpenBSD 6.0 released | Hacker News -
Does anyone use literate haskell for a medium-sized project? what are your experiences with it or literate programming in general? : haskell -
Backpack and separate compilation : haskell -
LinearTypes – GHC -
Linear types coming to GHC? : haskell -
[Haskell-community] Next Steps in Downloads Page Discussion -
[Haskell-community] Next Steps in Downloads Page Discussion : haskell -
Canadian surgeons urge people to throw out bristle BBQ brushes - Health - CBC News -
Canadian surgeons urge people to throw out bristle BBQ brushes | Hacker News -
43M passwords hacked in Last.fm breach | Hacker News -
DIY: Haskell docs -
Exclusive: Google shelves plan for phone with interchangeable parts - sources | Reuters -
Google has reportedly suspended Project Ara, its modular smartphone effort | Hacker News -
A SpaceX Falcon 9 rocket just exploded at Cape Canaveral, destroying Facebook’s Internet.org satellite | TechCrunch -
A SpaceX Falcon 9 rocket has exploded at Cape Canaveral | Hacker News -
Typeable with variables? : haskell -
functional and typed filesystem : haskell -
Comparison of Programming Language Package Managers | Hacker News -
Finansmand og iværksætter: Skat, vi har et problem... | Berlingske Business -
golang – encoding/csv: Reading is slower than python | Hacker News -
How do you use HN? | Hacker News -
So parsec and attoparsec are length units... : haskell -
In Defense of Douglas Crockford -
How to Tell a Mother Her Child Is Dead | Hacker News -
Import Ordering : haskell -
Skypeopensource2 – Selfmade Skype client based on reversing Skype 5.5 client | Hacker News -
This blind Apple engineer is transforming the tech world at only 22 -
Blind Apple engineer is transforming the tech world at only 22 | Hacker News -
Dependency graphs of Go web frameworks | Hacker News -
IoT’s killer app is not home security | Hacker News -
[Haskell-cafe] [ANN] brittany - haskell source code formatting tool - experimental/alpha -
[ANN] Alpha/Experimental release of brittany - haskell source code formatting tool : haskell -
You’re How Old? We’ll Be in Touch | Hacker News -
Mosh: the mobile shell | Hacker News -
HacBook Elite – A Fully-functional Mac for $329 | Hacker News -
Warner Bros. Flags Its Own Website as a Piracy Portal - TorrentFreak -
Warner Bros. Flags Its Own Website as a Piracy Portal : technology -
how much of Haskell do you think is needed to competently use it without running into show-stopping roadblocks? : haskell -
Announcing ‘text-metrics’ : haskell -
Call for Nominations: GHC Steering Committee : haskell -
ghc-proposals/ghc-proposals: Proposed compiler and language changes for GHC and GHC/Haskell -
Ask HN: Why is inter-device file sharing still a hassle? | Hacker News -
In Norway, everyone can know how much you earn [video] | Hacker News -
Cabal User's Guide Migrated to RST/Sphinx (MuniHac2016 project) : haskell -
Europe announces that all scientific papers should be free by 2020 - ScienceAlert -
Europe announces that all scientific papers should be free by 2020 | Hacker News -
Multi-process Firefox brings 400-700% improvement in responsiveness | TechCrunch -
Multi-process Firefox brings 400-700% improvement in responsiveness | Hacker News -
GHC proposal for OverloadedRecordFields (plus some amendments to OverloadedLabels) : haskell -
tonsky/FiraCode: Monospaced font with programming ligatures -
If you haven't yet : Check out FiraCode. Awesome font for Haskell : haskell -
Whatever happened to automatic vectorization in GHC? : haskell -
atemerev/skynet: Skynet 1M threads microbenchmark -
A concurrency speed test across different languages : haskell -
Lessons from a 45-year Study of Super-Smart Children | Hacker News -
iPhone 7 | Hacker News -
I am a fast webpage | Hacker News -
Show HN: cookies.js, making cookies a delight to work with on the front-end | Hacker News -
Magento localization with Transifex - Yireo -
Sophisticated OS X Backdoor Discovered | Hacker News -
[Python-Dev] Python 3.6 dict becomes compact and gets a private version; and keywords become ordered -
Python 3.6 dict becomes compact and keywords become ordered | Hacker News -
With the iPhone 7, Apple Changed the Camera Industry | Hacker News -
How Dropbox Hacks Your Mac | Hacker News -
Incremental Compilation | Hacker News -
Time crystals might exist after all | Hacker News -
Have propagators been Implemented in Haskell? : haskell -
New to Haskell, wondering how to make this code more concise. : haskell -
Is This a Confusing Compile Error, or what Am I doing wrong? : haskell -
How many keywords haskell truly needs? : haskell -
maxigit/crew: Command rewriter -
Paginators are Mealy Machines in disguise : haskell -
Ask HN: Is web programming just a series of hacks on hacks? | Hacker News -
Vim 8.0 Release | Hacker News -
Plugin UI architecture · neovim/neovim Wiki -
“it's my fault that Google shut down Google Reader” | Hacker News -
Nick Baum on Twitter: "I realized this weekend that it's my fault that @Google shut down Google Reader. /1" -
Libtiff goes offline | Mad File Format Science -
Libtiff goes offline | Hacker News -
(De)constructing the Front End : haskell -
Performance In the Large - Benchmark : haskell -
GitLab Master Plan | GitLab -
GitLab Master Plan | Hacker News -
[coreboot] Experiments with disabling the ME on Sandybridge x230 -
Logo Pizza - Logos for sale -
Logo Pizza: Hot and ready logos for sale | Hacker News -
Is there a neovim distribution similar to Spacemacs? : neovim -
Techniques for debugging Haskell : haskell -
Chelsea Manning Told She Can Have Gender Reassignment Surgery, Lawyer Says - NYTimes.com -
A whole new Universe -
GitHub gets built-in project management tools and support for formal reviews | TechCrunch -
GitHub Universe Live Stream [video] | Hacker News -
Stackage LTS 7 is released -
Stackage 7.0 released! : haskell -
A whole new GitHub Universe: announcing new tools, forums, and features | Hacker News -
ZenHub - Agile project management for GitHub issues -
Into the Core - Squeezing Haskell into Nine Constructors by Simon Peyton Jones : haskell -
The state of indentation support In Emacs : haskell -
Aggressively prune nightly collection · Issue #1872 · fpco/stackage -
Aggressively prune Stackage nightly collection : haskell -
About pull request reviews - User Documentation -
Microsoft researchers achieve speech recognition milestone - Next at Microsoft -
Researchers achieve speech recognition milestone | Hacker News -
Web2Web Bootstrap | Hacker News -
Matt Stone and Trey Parker Spill the Secret to Keeping South Park Cool | Vanity Fair -
South Park's creators on how the series has evolved | Hacker News -
FBI director: Cover up your webcam | Hacker News -
[Haskell-cafe] technical thoughts on stack -
IPv6 Wall of Shame | Hacker News -
How to Play with Stack -
A thought-provoking experiment showed what happens when children don’t have the internet for a whole day -
What happens when children don’t have the internet for a whole day | Hacker News -
One port to rule them all: Thunderbolt 3 and USB Type-C join forces - CNET -
Elon Musk on How to Build the Future | Hacker News -
Run your haskell code on the browser : haskell -
Release v1.2.0 · commercialhaskell/stack -
Microservices – Please, don’t | Basho Technologies -
Microservices | Hacker News -
Torrent Site Founder, Moderator and Users Receive Prison Sentences - TorrentFreak -
The Programmer’s Guide to a Sane Workweek | Hacker News -
So you think you can fake your own death? | Elizabeth Greenwood | Pulse | LinkedIn -
Is your Chrome bigger than mine? How Chrome ate 21GB of storage – Blog de François Maillet -
Googles HTML Styleguide – Omit Optional Tags | Hacker News -
Using command-line history - Vim Tips Wiki - Wikia -
Shortcut for moving between Vim windows - Stack Overflow -
Vim script cheatsheet -
Mapping keys in Vim - Tutorial (Part 1) - Vim Tips Wiki - Wikia -
Scripting the Vim editor, Part 1: Variables, values, and expressions -
Variable Scoping / Learn Vimscript the Hard Way -
Autoloading / Learn Vimscript the Hard Way -
How does vim8 compare to neovim? : neovim -
Tutorials for setting up Neovim as an IDE? : neovim -
How will the Vim 8 release affect Noevim? (OTOH, how was it affected by Neovim?) : neovim -
I gave commit rights to someone I didn't know, I could never have guessed what happened next! · Jakewins -
I gave commit rights to someone I didn't know | Hacker News -
Neovim, Deoplete, JSPC, UltiSnips and Tern: A config for kickass autocompletion | gregJS -
Neovim, Deoplete, JSPC, UltiSnips and Tern: A config for kickass autocompletion : neovim -
MacOS Sierra | Hacker News -
Haskell on macOS sierra, any problems? : haskell -
Mutable constructor fields by simonmar · Pull Request #8 · ghc-proposals/ghc-proposals -
#12479 (build fail of commercialhaskell.com with stack build on mac os x sierra beta 4) – GHC -
Warning: Microsoft Signature PC program now requires that you can't run Linux. Lenovo's recent Ultrabooks among affected systems. -
Homebrew 1.0.0 | Hacker News -
Linux can’t be installed on a recent Lenovo laptop | Hacker News -
Conspiracy! The Reddit rundown on the man who deleted Clinton e-mails | Ars Technica -
Servant authentication and sessions via cookies - Tutorials -
Servant authentication and sessions via cookies : haskell -
Sublime Text 3 Build 3124 | Hacker News -
Gallium – Build Desktop Applications in Go and HTML | Hacker News -
Battle.net’s name is changing—just in time for its 20th anniversary | Ars Technica -
Bug that hit Firefox and Tor browsers was hard to spot—now we know why | Ars Technica -
Disqus now compatible with Google AMP | Hacker News -
Question About SPJ's Publications : haskell -
Am I Introverted, or Just Rude? | Hacker News -
UnGoogled Chromium: Chromium with enhanced privacy, control and transparency | Hacker News -
Google Allo – First Impression – prodissues -
Google Allo – First Impression | Hacker News -
Body fat link to bacteria in feces | Hacker News -
Rebase and merge pull requests -
Rebase and merge pull requests | Hacker News -
readable error messages like in elm? : haskell -
Exponent: Making React Native Easier | Hacker News -
Official YouTube Blog: YouTube Go: YouTube reimagined for the next generation of YouTube viewers -
YouTube Go: YouTube reimagined for the next generation of YouTube viewers | Hacker News -
Why I'm not a React Native Developer -
Why I'm Not a React Native Developer | Hacker News -
[luatex] Luatex 1.0.0 announcement -
LuaTeX 1.0.0 | Hacker News -
Re: [Haskell-cafe] [Haskell] GHC is a monopoly compiler - Google Groups -
GHC is a monopoly compiler : haskell -
[Haskell] Respect -
[Haskell] Respect (SPJ) : haskell -
Making Humans a Multiplanetary Species [video] | Hacker News -
Google Allo’s limitations explained in one word: “India” | Ars Technica -
What I Wish I Had Known Before Scaling Uber to 1000 Services [video] | Hacker News -
An Architecture for Modern Functional Programming: Part 2 : haskell -
Musk’s Mars moment: Audacity, madness, brilliance—or maybe all three | Ars Technica -
Supporting Idris' !-notation in Haskell : haskell -
Bringing Pokémon GO to life on Google Cloud | Hacker News -
ORWL – The first open source, physically secure computer | Hacker News -
PostgreSQL 9.6 Released | Hacker News -
BBC - Future - Our IQs have never been higher – but it hasn’t made us smart -
Our IQs have never been higher – but it hasn’t made us smart | Hacker News -
New blog post: Sharing, Memory Leaks, and Conduit and friends : haskell -
Announcing Project Mortar - Google Groups -
Show /r/haskell: dib, a simple, forward build system : haskell -
Watching Pirate Streams Isn't Illegal, EU Commission Argues - TorrentFreak -
Introduction to 9front | Hacker News -
A Nonlinear History of Time Travel | Hacker News -
Is there anything new on Intel and Haskell compilation? : haskell -
More than 400 malicious apps infiltrate Google Play | Ars Technica -
EU wants Google to stop anti-competitive Android practices, fine expected | Hacker News -
Learn ruby in Y Minutes -
Ready to Try Ruby? An Awesome Rails Cheat Sheet -

-

October, 2016

-

-

EU wants Google to stop anti-competitive Android practices, fine expected | Hacker News -
Exclusive: EU wants Google to stop anti-competitive Android practices, fine expected | Reuters -
'Re: LLVM license change' - MARC -
Theo de Raadt on LLVM license change | Hacker News -
Contribution opportunities: help upgrade to haskell-src-exts 1.18 : haskell -
Rails Beginner Cheat Sheet -
Dropbox Hasn't Learned their Lesson -
Dropbox Hasn't Learned Their Lesson | Hacker News -
Heartbreaking essay by Robin Williams' wife about what happened to him | Hacker News -
Apple loses patent retrial to the litigious VirnetX, ordered to pay $300M | Hacker News -
EU Allows Copyright Owners to Demand Open Wifi Networks Be Password Protected | Hacker News -
: Purposes, Concepts, Misfits, and a Redesign of Git -
Purposes, Concepts, Misfits, and a Redesign of Git | Hacker News -
Gitless -
What would you want to see in a yi tutorial? : haskell -
NoScript is harmful and promotes Malware! - liltinkerer -
Never accept a counter-offer | Hacker News -
The Deletion of gcj | Hacker News -
#12479 (build fail of commercialhaskell.com with stack build on mac os x sierra beta 4) – GHC -
Qtah - Haskell Qt bindings : haskell -
Pixel – Highest-rated smartphone camera | Hacker News -
skedgeme/wrecker: An HTTP Benchmarking Library -
[ANN] wrecker - An HTTP Benchmarking Library : haskell -
What's the Longest Humans Can Live? | Hacker News -
Apple Has Removed Dash from the App Store - Kapeli Blog -
Apple Has Removed Dash from the App Store | Hacker News -
RethinkDB is shutting down - RethinkDB -
RethinkDB is shutting down | Hacker News -
Opposition to Galileo was scientific, not just religious | Aeon Ideas -
Opposition to Galileo was scientific, not just religious | Hacker News -
Erratum to "Sharing, Memory Leaks, and Conduit and friends" : haskell -
Unix as IDE | Hacker News -
Google Noto Fonts | Hacker News -
A Javascript journey with only six characters | Hacker News -
Lawsuit: Yahoo CEO Marissa Mayer led illegal purge of male workers -
Lawsuit: Yahoo CEO Marissa Mayer led illegal purge of male workers | Hacker News -
More Details Emerge About the Soylent Food Bars Making People Sick -
More Details Emerge About the Soylent Food Bars Making People Sick | Hacker News -
Hacker From Jakarta Faces 12 Years In Jail After Broadcasting Porn On a Billboard | Fresh As F#ck -
Latency numbers every programmer should know -
Latency numbers every programmer should know (2012) | Hacker News -
Calculating Correct Compilers | SkillsCast : haskell -
Welcome - IRCv3 -
IRC v3 | Hacker News -
Introduction :: Yesod Web Framework Book- Version 1.4 -
Haskell :: Yesod Web Framework Book- Version 1.4 -
Basics :: Yesod Web Framework Book- Version 1.4 -
My impressions on moving to Haskell : haskell -
Haskell High Performance Programming by Samuli Thomasson, any feedbacks ? : haskell -
Git-Repo The ultimate utility for managing services · Guyzmo's got nothing to blog. -
Deploying your Webapp :: Yesod Web Framework Book- Version 1.4 -
Git-repo – Manage Gitlab, GitHub and Bitbucket from the command line | Hacker News -
http-client library -
Environment variables for configuration :: Yesod Web Framework Book- Version 1.4 -
You Might Not Need Redux – Medium -
React Implementation Notes | Hacker News -
Visual Studio Code 1.6 | Hacker News -
Visual Studio Code September 2016 1.6 -
poll on mac 10.12 is broken | daniel.haxx.se -
Atom 1.11 | Hacker News -
Atom 1.11 - Atom Blog -
ResinOS, run Docker containers on embedded devices | Hacker News -
Open Guide to Amazon Web Services | Hacker News -
Marathono, a GUI tool for managing long-running processes in macOS -
Show HN: Marathono – A GUI tool for managing long-running processes in macOS | Hacker News -
Open Whisper Systems >> Blog >> Disappearing messages for Signal -
Disappearing messages for Signal | Hacker News -
N Haskell Tools & Packages I wish someone had told me about -
Tools and Libraries I really wish someone had told me about before I started : haskell -
High Performance Client Side Web Applications through Haskell EDSLs And Asm.js. As Fast As Hand Rolled Javascript. : haskell -
Yarn: A new package manager for JavaScript | Engineering Blog | Facebook Code -
Yarn: Fast, reliable, and secure dependency management for JavaScript | Hacker News -
It's time to reconsider going on-prem | Hacker News -
Internationalization :: Yesod Web Framework Book- Version 1.4 -
Static types in Python, oh my(py)! -
Static types in Python | Hacker News -
How did managers get introduced in 2014, and did it really change everything? · Issue #800 · holman/ama -
Taking PHP Seriously | Hacker News -
Taking PHP Seriously -
Haskell Code Standards : haskell -
New Conduit Tutorial - Michael Snoyman's blog -
New Conduit Tutorial : haskell -
Change TypeSig printing for textmate compatability by duog · Pull Request #288 · chrisdone/hindent -
Community feedback wanted: Type-sig indentation in HIndent : haskell -
PHP 7 deployment at Dailymotion -
PHP 7 deployment at Dailymotion | Hacker News -
The State of GHC : haskell -
4os -
Haskell - Jedi Knight : haskell -
Be Kind — Brian Gilham -
Be Kind | Hacker News -
ghc-proposals/0000-relax-qualified-import-syntax.rst at patch-2 · cblp/ghc-proposals -
Relax qualified import syntax by cblp · Pull Request #21 · ghc-proposals/ghc-proposals -
Books Programmers Don't Really Read (2008) | Hacker News -
Jobs that no longer exist | Hacker News -
Github just censored my research data -
channelcat/sanic: Python 3.5+ web server that's written to go fast -
Sanic – Python 3.5+ web server that's written to go fast | Hacker News -
5900 online stores found skimming [analysis] -
GitLab reinstates list of servers that have malware | GitLab -
Jumper - Your personal exercise partner -
TFW an obituary you wrote five years ago goes viral | Ars Technica -
"Constraint to Bool" wired-in type family by hsyl20 · Pull Request #22 · ghc-proposals/ghc-proposals -
Elm and Phoenix/Elixir in production for France TV | Vince's -
Caddy - The HTTP/2 Web Server with Fully Managed TLS -
Caddy – HTTP/2 Web Server with Automatic HTTPS | Hacker News -
From functor to functor or: How I learned to stop worrying and love the C++ : haskell -
Report: Tech investor Peter Thiel will donate $1.25M to Trump campaign | Ars Technica -
The Cryptopals Crypto Challenges -
The cryptopals crypto challenges | Hacker News -
Google May Be Stealing Your Mobile Traffic -
Haskell code reloading - Has it been solved? : haskell -
[Haskell] [ANN] HyperHaskell -- the strongly hyped Haskell interpreter -
[ANN] HyperHaskell — the strongly hyped Haskell interpreter : haskell -
Researchers reach human parity in conversational speech recognition | Hacker News -
Report: Apple will introduce new Macs at October 27th event [Updated] | Ars Technica -
T-Mobile will pay a $48 million fine for throttling 'unlimited data' plans - The Verge -
Why Do These Straight Men Want Nothing to Do With Women? – Narratively -
Why Do These Straight Men Want Nothing to Do with Women? | Hacker News -
Coding Standards in haskell : haskell -
Fixing Python Performance with Rust | Hacker News -
How Silicon Valley Treats a Trump Backer: Peter Thiel - The New York Times -
I went to Nigeria to meet the man who scammed me - BBC News -
I went to Nigeria to meet a man who scammed me | Hacker News -
jfischoff/reuse-port-example: A Demo of how to use Warp and SO_REUSEPORT to create a zero downtime deploy. -
Zero downtime deploys with SO_REUSEPORT and Warp. : haskell -
Electric Ponies - Code your JS app like it's 86 -
How to avoid rewriting your JavaScript application | Hacker News -
Show HN: hnlive.tk – A tool to estimate activity on HN before posting | Hacker News -
“Most serious” Linux privilege-escalation bug ever is under active exploit | Hacker News -
ocr with haskell : haskell -
HN comments are underrated | Hacker News -
How does GHC compare to other compilers? : haskell -
Building a Bank with Kubernetes | Hacker News -
Varnish Explained | Hacker News -
IHaskell – A Haskell kernel for Jupyter | Hacker News -
A Taste of Haskell · HookRace Blog -
A Taste of Haskell | Hacker News -
GHC panic when building Stack on macOS Sierra · Issue #2577 · commercialhaskell/stack -
Add `--dynlibdir` by christiaanb · Pull Request #3979 · haskell/cabal -
Update on Vanilla/Classic Server Status - News - Icy Veins Forums -
Deploying a Haskell application to AWS Elastic Beanstalk : haskell -
CPP: A Standardized Alternative to AMP | Hacker News -
Swift.org - Server APIs Project -
Server APIs Project | Hacker News -
A Haskell-like language on the Erlang VM (2015 Thesis) : haskell -
Backport 8.0.2 Sierra fixes · Issue #2743 · commercialhaskell/stack -
Falsehoods programmers believe about networks | Hacker News -
Augeas – A Configuration API | Hacker News -
Why is the lazy Text version of this function an order of magnitude faster than the pipes version? : haskell -
MacBook Pro | Hacker News -
Apple, this time you made a mistake | Hacker News -
New MacBook Pro Is Not a Laptop for Developers Anymore | Hacker News -
Apple just told the world it has no idea who the Mac is for | Hacker News -
15 hours with the 13” MacBook Pro, and how Apple’s T1 bridges ARM and Intel | Ars Technica -
Sub-millisecond GC pauses in Go 1.8 | Hacker News -
When should I use type Signatures? Should I rely on type inference whenever I can? : haskell -
Total Nightmare: USB-C and Thunderbolt 3 - Stephen Foskett, Pack Rat -
Total Nightmare: USB-C and Thunderbolt 3 | Hacker News -
Buttery Smooth Emacs -
Master LATEX in minutes | Hacker News -
UX Myths -
UX Myths (2014) | Hacker News -
Learn Me a Haskell -
Learn Me a Haskell: An OOP Developer's First Impressions : haskell -
Sonder Design -
Sonder E-Ink Keyboard | Hacker News -
I don't understand Python's Asyncio | Hacker News -
No more ransom | Hacker News -
Show HN: A fork of sudo with Touch ID support | Hacker News -
How a Fake British Accent Took Old Hollywood by Storm | Hacker News -
Daring Fireball: Who's to Blame for the 16 GB RAM Limit on the New MacBook Pros: Apple or Intel? -
Who's to Blame for the 16 GB RAM Limit on the New MacBook Pros: Apple or Intel? | Hacker News -

-

November, 2016

-

-

“Design Patterns” Aren't | Hacker News -
The Mirai Botnet is Proof the Security Industry is Broken - Appcanary -
HTML 5.1 is a W3C Recommendation | Hacker News -
The Mirai Botnet Is Proof the Security Industry Is Broken | Hacker News -
How Apple could have avoided much of the controversy -
How Apple could have avoided much of the controversy | Hacker News -
Gameroom | Hacker News -
YouTube's seven-year stand-off ends | Hacker News -
[Haskell-cafe] PSA: gcc-6.2.0 breaks linking in ghc 7.10.3/8.0.1 -
[Haskell-cafe] PSA: gcc-6.2.0 breaks linking in ghc 7.10.3/8.0.1 : haskell -
Does your mind jump around, stay on task or get stuck? | Berkeley News -
Does your mind jump around, stay on task or get stuck? | Hacker News -
BugReplay -
Pornhub Bypasses Ad Blockers with WebSockets | Hacker News -
How to Advertise on a Porn Website | Eat24 Blog -
Switching from macOS: The Basics | Hacker News -
Open Whisper Systems >> Blog >> Signal and GIPHY -
Signal and Giphy | Hacker News -
Introducing TJSON, a stricter, typed form of JSON | Hacker News -
Review: The $1,499 2016 MacBook Pro is an expensive MacBook Air on the inside | Ars Technica -
Docker & Haskell Stack : haskell -
Using Haskell to Find Unused Spring MVC Code | Small Improvements -
Why a federal high-tech start-up is a money loser - The Washington Post -
18F, the Government in-house tech startup, losing money | Hacker News -
18F: Digital service delivery | Hacking inclusion: How we customized a bot to gently correct people who use the word 'guys' -
Visual Studio Code 1.7 | Hacker News -
Bilibili/flv.js: HTML5 FLV Player -
Flv.js – An HTML5 Flash Video Player | Hacker News -
Why Slack may live to regret its smarmy letter to Microsoft - The Verge -
Slack may regret its letter to Microsoft | Hacker News -
Browsers, not apps, are the future of mobile - Inside Intercom -
Browsers, not apps, are the future of mobile | Hacker News -
Web fonts, boy, I don't know – Monica Dinculescu -
Web fonts, boy, I don't know | Hacker News -
drathier/stack-overflow-import: Import arbitrary code from Stack Overflow as Python modules. -
The People's Code | Hacker News -
Why 2016 is such a terrible year for the Mac | Hacker News -
Docker in Production: A retort - Sysadmin 4 lyfe -
Docker in Production: A retort | Hacker News -
State of Haskell on macOS & ARM : haskell -
State of Haskell on macOS & ARM : haskell -
Why I won't recommend Signal anymore - Sander Venema -
Why I won't recommend Signal anymore | Hacker News -
Nyancat on the touchbar | Hacker News -
Online PHP Coding Standards Fixer | Hacker News -
Powerline-hs - a Powerline clone that is 15x faster (x-post /r/linux) : haskell -
22 years in, Magic: The Gathering is the brainiest it’s ever been · For Our Consideration · The A.V. Club -
Fighting Loneliness with Public Living Rooms | Hacker News -
22 years in, Magic: The Gathering is the brainiest it’s ever been (2015) | Hacker News -
22 years in, Magic: The Gathering is the brainiest it’s ever been (2015) | Hacker News -
:syntax off - Frederik Dudzik -
Turning off syntax highlighting | Hacker News -
intercooler-js/README.md at master · LeadDyno/intercooler-js · GitHub -
iOS App Store flooded with fake retail apps | Hacker News -
Some thoughts on asynchronous Python API design in a post-async/await world | Hacker News -
Time to Dump Time Zones | Hacker News -
Use MySQL Safely in Yesod Applications : haskell -
Homebrew 1.1.0 | Hacker News -
The Macintosh GUI in 1984 | Hacker News -
Software Developers: how to get a raise without changing jobs | Hacker News -
IoT Goes Nuclear - Creating a ZigBee Chain Reaction -
IoT Goes Nuclear: Creating a ZigBee Chain Reaction | Hacker News -
Julia is the hottest new language on Wall Street | Hacker News -
TIOBE: November Headline: Is Haskell finally going to hit the top 20? : haskell -
A neat way to name parameters in signatures : haskell -
Benefits of using tmux – streamlining your development environment | Hacker News -
Live Presidential Forecast (with priors) | Hacker News -
So you think you know C? -
So you think you know C? | Hacker News -
Why Twitter Must Be Saved | Hacker News -
Elm Is Wrong :: Reasonably Polymorphic -
Elm is Wrong | Hacker News -
Introducing the Nextcloud Box | Hacker News -
Elm from a Business Perspective | Hacker News -
https://assets.donaldjtrump.com/_landings/contract/O-TRU-102316-Contractv02.pdf -
Donald Trump’s Contract with the American Voter [pdf] | Hacker News -
Beyond 10,000 Lines -
Beyond 10,000 Lines: Lessons Learned from a Large Phoenix Project | Hacker News -
The SSD Endurance Experiment: They're All Dead (2015) | Hacker News -
The One Method I’ve Used to Eliminate Bad Tech Hires - Mattermark -
Tumblr Engineering — PHP 7 at Tumblr -
Atom 1.12 - Atom Blog -
Introducing Atom benchmarks by as-cii · Pull Request #12984 · atom/atom -
Seven years of Go | Hacker News -
PHP 7 at Tumblr | Hacker News -
Remove shadow DOM from `atom-text-editor` by as-cii · Pull Request #12903 · atom/atom -
Atom 1.12 released | Hacker News -
Show HN: Blink Shell for iOS, an Open-Source, Mosh and SSH Terminal for Pros | Hacker News -
Why did ProtonMail vanish from Google search results for months? | Hacker News -
Nintendowned: Amazon sells out of the NES Classic Edition in (null) seconds | TechCrunch -
Apple is doubling down on open source | Hacker News -
Quant Job Interview Questions [pdf] | Hacker News -
Why 30 is the decade friends disappear | Hacker News -
porting intero to VSCode : haskell -
If Prisoners Ran Prisons | The Marshall Project -
If Prisoners Ran Prisons | Hacker News -
'Reply-all' email chain sent to all 1.2 million NHS employees - Business Insider -
The NHS's 1.2M employees are trapped in a 'reply-all' email hell | Hacker News -
China: We’ll block iPhone sales if Trump imposes tariffs | Ars Technica -
Review: Touch Bar MacBook Pros give an expensive glimpse at the Mac’s future | Ars Technica -
Do you really want an SPA framework? – Michael S. Mikowski – SPA (UI/UX/server) architect and author -
Do you really want an SPA framework? – Michael S. Mikowski | Hacker News -
Primitive for macOS -
Show HN: Primitive for macOS | Hacker News -
Movie pirate? Don’t trust Plex Cloud | TechCrunch -
What's the difference between Stackage and Hackage? : haskell -
Facebook Says It Found More Miscalculated Metrics | Hacker News -
Quick, Draw | Hacker News -
React 15.4 | Hacker News -
Preact | Preact: Fast 3kb React alternative with the same ES6 API. Components & Virtual DOM. -
React Distribution · Issue #8294 · facebook/react -
Email Markup  |  Google Developers -
Email Markup in Gmail | Hacker News -
On 2016 Apple: What do “Pro” users want? | Hacker News -
What do "Pro" users want? -
Officer charged with manslaughter in live-streamed death of motorist | Ars Technica -
What makes for great IoT product development? - Telecom Tech News -
Choosing Ember over React in 2016 -
Choosing Ember over React in 2016 | Hacker News -
Don’t Mess with The Google | Hacker News -
json-msg-pack/README.md at master · eborden/json-msg-pack · GitHub -
Haskell, can we REST? JSON and Message Pack : haskell -
Global warming: The state of the climate in 2016 | Hacker News -
Building a cross-platform desktop app with Electron and Elm – Medium -
Finally, Our Own OS – Oh Yes! | Nota Bene: Eugene Kaspersky's Official Blog -
Kaspersky OS | Hacker News -
vgrep – a drop-in pager for Grep : haskell -
FiB | Devpost -
alx741/vim-yesod: Haskell Yesod web framework vim plugin -
Yesod - Vim plugin : haskell -
Add ifThenElse and (?) to Data.Bool -
Add ifThenElse and (?) to Data.Bool : haskell -
Exploratory Haskell (2015) | Hacker News -
My new Haskell book "Haskell Tutorial and Cookbook" is now available : haskell -
Haskell Tutorial and... by Mark Watson [Leanpub PDF/iPad/Kindle] -
Multiline strings don't line up · Issue #219 · avh4/elm-format -
Google Cloud is 50% cheaper than AWS – The HFT Guy -
Google Cloud is 50% cheaper than AWS | Hacker News -
Hindley-Milner: an {article about, interactive tutorial for, implementation of} the famous type inference algorithm : haskell -
Why the MacBook Pro Is Limited to 16GB of RAM | Hacker News -
Learning to Superoptimize Programs | Hacker News -
Julia vs Haskell type system. : haskell -
Smalltalk vs. Scheme, JavaScript, and Java | Hacker News -
Gettting started with Haskell, stack, intero-emacs : haskell -
Hvad sker der når du drikker en cola light? - Slut med forbudt -
elm-decode-pipeline 3.0.0 -
Jobs as a Haskell Programmer : haskell -
Attacking discrimination with smarter machine learning | Hacker News -
Show HN: Publish a Markdown website in 5 minutes | Hacker News -
Favourites -
Introduction · An Introduction to Elm -
Mastering Time-to-Market with Haskell :: FP Complete -
Mastering Time-to-Market with Haskell | Hacker News -
Scripting in Haskell : haskell -
ElvishJerricco comments on Haskell's Missing Concurrency Basics -
Spreading the Gospel of Haskell : haskell -
Spreading the Gospel of Haskell - Michael Snoyman's blog -
Mastering Time-to-Market with Haskell :: FP Complete : haskell -
Haskell for Dummies - Michael Snoyman's blog -
Facebook Said to Create Censorship Tool to Get Back into China | Hacker News -
GitLab 8.14 Released with Time Tracking Beta and Chat Commands | GitLab -
GitLab 8.14 Released with Time Tracking Beta and Chat Commands – GitLab | Hacker News -
How to speed up the Rust compiler some more | Hacker News -
Haskell Opportunities At Wrinkl, Inc. in NYC : haskell -
Show HN: A DigitalOcean-like support community for your website in 3 minutes | Hacker News -
Why is Firefox still not supporting .webp? : firefox -
Why is Firefox still not supporting .webp? | Hacker News -
Signal for Beginners | Hacker News -
Twitter accidentally suspends its own CEO's account - Nov. 23, 2016 -
Twitter accidentally suspends its own CEO's account | Hacker News -
What's the best way to install and upgrade Haskell? : haskell -
A practical guide to securing macOS | Hacker News -
The Manhattan Project Fallacy | Hacker News -
Snowden can be asked to testify in person in German NSA probe | Ars Technica -
lua.vm.js -
The Lua VM, on the Web | Hacker News -
MacOS 10.12 Open Source | Hacker News -
The New (Experimental) Yesod Development Server - Feedback Requested! -
Stack based devel server · Issue #1305 · yesodweb/yesod -
The sparrow with four sexes | Hacker News -
We Tracked Down a Fake-News Creator in the Suburbs | Hacker News -
Neil Mitchell's Haskell Blog: The Haskell.org Website Working Group (HWWG) -
The Haskell.org Website Working Group (HWWG) : haskell -
Show HN: A visual guide to the most popular CSS properties | Hacker News -
Looks like "join points" come to GHC (nice wins) : haskell -
Data is being send to Google every time you execute a command with brew | Hacker News -
My First Programming Language.. Hard Choice... : haskell -
Of course Zuckerberg wants to bow to Chinese censorship | Hacker News -
Deleting the /r/golang subreddit - Google Groups -
Deleting the golang subreddit | Hacker News -
Joyent | The Power of a Pronoun -
Proposal to delete the r/golang subreddit | Hacker News -
Reddit’s CEO edited comments that criticized him -
Latest additions to Megaparsec : haskell -
Resources for writing high-performance code? : haskell -
yishan comments on The accuracy of Voat regarding Reddit: SRS admins? -
“No, I'm the ex-CEO of Reddit because eventually there was too much bullshit.” | Hacker News -
Thoughts on Haskell -
Locked doors, headaches, and intellectual need | Affording Play -
Cards Against Humanity is digging a hole in the earth | Hacker News -
We're digging a hole. -
Issues with Plex cloud invites — Plex Forums -
What is haskell good for? : haskell -
I made an iPhone game with PhoneGap and won't do it again | Hacker News -
A Crypto Challenge for the Telegram Developers (2013) | Hacker News -
The new MacBook Pro is kind of great for hackers – Medium -
The new MacBook Pro is kind of great for hackers | Hacker News -
Trying to understand what Pipes/Conduits solve that withXXX pattern doesn't solve? : haskell -
New XPS 13 Developer Edition Lands in Europe, United States and Canada | Hacker News -
tonsky/AnyBar: OS X menubar status indicator -
AnyBar: OS X menubar status indicator | Hacker News -
Best Practices for Time Travelers (Idle Words) -
[ANNOUNCE] GHC 8.0.2 release candidate 1 -
Well-Typed - The Haskell Consultants: Implementing a minimal version of haskell-servant -
How we use Slack in our family | Hacker News -
My family uses Slack. It’s pretty interesting. | Labs -
Announcing servant-swagger and swagger2 - haskell-servant -
Futurism - Building the future together -
New finding may explain heat loss in fusion reactors | Hacker News -
Any GHCJS tutorials out there? : haskell -
Circle CI configuration with yesod (haskell) backend and javascript frontend -
Circle CI continuous integration configuration with Yesod as a backend and javascript as a frontend : haskell -
Haskell's Typeclasses: We Can Do Better : haskell -
Haskell's Typeclasses: We Can Do Better : haskell -
The infamous editor/IDE situation : haskell -
[Bikeshedding] Rename Haskell Platform Minimal : haskell -
psibi/yesod-rest: Yesod Rest Scaffolding with React -
yesod-rest: Boilerplate code with Yesod as an API server and React for frontend development. Looking for Feedback/Suggestions. : haskell -
Git 2.10 has been released -
Git 2.10 has been released | Hacker News -
The Macintosh Endgame | Hacker News -
Japan plans 130-petaflops China-beating number-crunching supercomputer | Ars Technica -
silky/literate-readme: README files used to be able to lie. Now they can't! Woo! -
literate-readme - A README.md that is also a Literate Haskell program : haskell -
iPhone battery drain: iOS 10 update problem leaves users furious -
iPhone update leaves users furious due to battery drain issue | Hacker News -
Wrong signal | Hacker News -
Vellvm: Verified LLVM -
Static Site Hosting with S3 and CloudFlare | William Vincent -
4M gmail addresses with passwords leaked | Hacker News -
CyberChef – A Cyber Swiss Army Knife | Hacker News -
We turned $140k on Kickstarter into $40k in debt and broke even | Hacker News -
How we turned $140k on Kickstarter into $40k in debt. And how we broke even. – Medium -
Haskell Documentation, 2016 Update - Michael Snoyman's blog -
.net core vs go - Google Groups -
Net core vs. golang | Hacker News -
Slack client for Commodore 64 | Hacker News -
ptmt/react-native-touchbar: TouchBar for react-native-macos -
A tutorial on GHC optimization and fusion : haskell -
Should I use Haskell for small game project? : haskell -
Why can’t humans regenerate body parts? We’ve got the genes | Hacker News -
Egg Freckles | The Last Mac Pro -
The Last Mac Pro | Hacker News -
Bugsnag Blog - Building native macOS applications with Rust -

-

December, 2016

-

-

Towards Version 1.0 | Idris -
Towards Idris Version 1.0 : haskell -
Fighting against CDN libraries | Hacker News -
Fighting against CDN libraries | Hacker News -
What are valid programs in the lambda calculus, that Haskell doesn't allow. : haskell -
Endomorphism - Wikipedia -
Homomorphism - Wikipedia -
Isomorphism - Wikipedia -
What are valid programs in the lambda calculus, that Haskell doesn't allow. : haskell -
Is “Isomorphic JavaScript” a good term? -
Elm FAQ -
9to5mac.com -
South Korea Passes Bill to Directly Punish Hack Makers - PVP Live -
South Korea Passes Bill to Directly Punish Hack Makers | Hacker News -
how to create linux screensaver with haskell? : haskell -
nikita-volkov/rerebase: Reexports from "base" with a bunch of other standard libraries -
ANN: A richer drop-in replacement for "base" and its Prelude : haskell -
The Django Project Debates User Tracking | Hacker News -
HaskellerZ/haskell_and_fp_in_industry.md at 8d84b08f6ccf489ae2101521d17f2c4f625810f7 · meiersi/HaskellerZ -
This AI Boom Will Also Bust | Hacker News -
Chrome 55 uses 30% less memory than 54 | Hacker News -
Chrome 54 vs. Chrome 55 memory usage -
Improved Authentication for Email Encryption and Security - ProtonMail Blog -
Improved Authentication for Email Encryption and Security | Hacker News -
Idris 0.99 Released | Idris -
Disassembling Sublime Text | Hacker News -
A fast multi-producer, multi-consumer lock-free concurrent queue for C++11 | Hacker News -
Jury deadlocked in trial of cop filmed killing fleeing suspect | Ars Technica -
Golang's Real-time GC in Theory and Practice (From Haskell to Go) : haskell -
Measuring GC latencies in Haskell, OCaml, Racket | Hacker News -
Troy Hunt: Here's 1.4 billion records from Have I been pwned for you to analyse -
1.4B records from “Have I been pwned” for analysis | Hacker News -
Tell HN: Political Detox Week – No politics on HN for one week | Hacker News -
Data.Compact -
Missing packages that needs to be written? : haskell -
Apple to Start Publishing AI Research | Hacker News -
I'm giving up on PGP -
Closure Compiler in JavaScript | Hacker News -
Pebble – Pebble’s Next Step -
Pebble's next step | Hacker News -
What is warp-tls performance and failure modes like? : haskell -
Bluetooth 5 Now Available | Bluetooth Technology Website -
Bluetooth 5 Now Available | Hacker News -
Yoga: A cross-platform layout engine | Engineering Blog | Facebook Code -
Yoga: A cross-platform layout engine | Hacker News -
Mr. Robot Killed the Hollywood Hacker | Hacker News -
Concurrency and Node :: FP Complete -
Review: Comparing Google Wifi to other mesh networking heavyweights | Ars Technica -
Trump’s “Mad Dog” stays as Theranos cuts figureheads and outs investors | Ars Technica -
A Simple Explanation: VLC.js | Hacker News -
Intel x86 documentation has more pages than the 6502 has transistors -
Intel x86 documentation has more pages than the 6502 has transistors | Hacker News -
more indirection: Thoughts on node.js and Haskell -
Comparing Haskell and Node concurrency performance | Hacker News -
[RFC] Using DC in amdgpu for upcoming GPU -
Linux kernel maintainer says 'No.' to AMDGPU patch | Hacker News -
Concurrency and Node :: FP Complete : haskell -
GHC now runs in Windows Subsystem for Linux! : haskell -
Haskelling without remorse - HSOC evaluation for live profiling server -
Go on till you come to the end · FunBlocks -
HSoC Testimonial -
Strelka - the new simple, flexible and composable web-router : haskell -
PewDiePie claims he will delete his YouTube channel today | Ars Technica -
Chromium Blog: Chrome 56 Beta: “Not Secure” warning, Web Bluetooth, and CSS position: sticky -
Chrome 56 Beta: “Not Secure” Warning, Web Bluetooth, and CSS Position: Sticky | Hacker News -
I am not a self-made man. -
Dorothea Lange’s Censored Photographs of FDR’s Japanese Concentration Camps — Anchor Editions -
Censored Photographs of Japanese Internment Camps – Dorothea Lange | Hacker News -
Bose Hearphones | Hacker News -
Saving the Internet 2000TB a Day: Fixing Font Awesome’s Fonts | Hacker News -
Developers’ side projects – Joel on Software -
Developers’ side projects | Hacker News -
Why I'm Making Python 2.8 -
Why I'm Making Python 2.8 | Hacker News -
WineHQ - News - Wine 2.0-rc1 Released -
Wine 2.0 RC1 Released | Hacker News -
An FBI Error Opens A Window Into Government Demands For Private Info | FiveThirtyEight -
Any more vim plugins? : haskell -
My Pixel has a manufacturing defect, and Google wants me to drive to another state to get it fixed | 9to5Google -
Python 3.6 – Asynchronous Comprehensions | Hacker News -
Equivalent to Rust's type-level macros? : haskell -
I am not a self-made man | Hacker News -
Algebraic blindness : haskell -
articles/algebraic-blindness.md at master · quchen/articles -
Levity-Polymorphic Type Classes · Issue #30 · ghc-proposals/ghc-proposals -
Ask HN: 57% of Sites Don't Use a CMS Like Wordpress – What Non-CMS Do You Use? | Hacker News -
German judges explain why Adblock Plus is legal | Ars Technica -
German judges explain why Adblock Plus is legal | Hacker News -
[Haskell-cafe] [Ann] Haskell Ecosystem Proposals -
[Haskell-cafe] [Ann] Haskell Ecosystem Proposals : haskell -
Proposal for managing implicit/speculative version bounds by alanz · Pull Request #1 · haskell/ecosystem-proposals -
MacBook Pro Launch: Perplexing -
The MacBook Pro's new Force Touch Trackpad is great. Pity about the name - The Verge -
Apple quietly releases delayed AirPods; shipping times slip to four weeks | Ars Technica -
AirPods are now available | Hacker News -
A Backdoor in Skype for Mac OS X | Hacker News -
Slack Calls: Now with 100% more video -
Slack Calls: Now with video | Hacker News -
Angular 4 will be the next version of Angular | Hacker News -
Quick Tip: How to Use HTML5 “picture” for Responsive Images -
What Kubernetes users should know about the rkt container engine | Hacker News -
Release v1.3.0 · commercialhaskell/stack -
Haskell in the Datacentre · Simon Marlow -
Haskell in the Datacentre · Simon Marlow : haskell -
Haskell in the Datacentre | Hacker News -
Waymo: Google's self-driving car company | Hacker News -
LLVM Project Blog: LLVM's New Versioning Scheme -
Webpack 2.2: The Release Candidate | Hacker News -
Yahoo discloses hack of 1 billion accounts | TechCrunch -
Yahoo discloses hack of 1B accounts | Hacker News -
Visual Studio Code 1.8 | Hacker News -
Uber ordered to stop self-driving vehicle service in San Francisco | TechCrunch -
Uber ordered to stop self-driving vehicle service in San Francisco | Hacker News -
Don't Panic: Learning Magento 2 As A Developer | ParadoxLabs -
Moody's: U.S. Telecoms are underinvesting in broadband | Hacker News -
GitHub lost $66M in nine months of 2016 | Hacker News -
Xilinx Pynq board documentation lists Haskell as one of its supported languages : haskell -
Servant without boilerplate. Example of fully functional static Servant server in 1 line of code and CLI app in 5 lines of code. : haskell -
MacOS FileVault2 Password Retrieval | Hacker News -
Show HN: 30 Days of React | Hacker News -
Thoughts about Spec-ulation (Rich Hickey) : Inside 736-131 -
Explaining the battery life problems with the new MacBook Pros | Ars Technica -
The Incompatible Food Triad | Hacker News -
shopify-scripts: Bug Bounty Program - Get Rewards through HackerOne -
Shopify has paid over $300k in security exploit bounties | Hacker News -
How popular is C-- and is it a good idea to target it when making a compiler. : haskell -
Corentin Dupont - Haskell is hard. Aaaaaaaaaaahhhhhhhhh!!!!! -
[Blog post] Haskell is hard. Aaaaaaaaaaahhhhhhhhh!!!!! : haskell -
Swift: Challenges and Opportunity for Language and Compiler Research [pdf] | Hacker News -
The problem of reusable and composable specifications : Inside 736-131 : haskell -
Does anyone have any suggestions on how to get the most out of the book Haskell Programming from First Principles? : haskell -
How should Haskell filenames be structured? [poll] : haskell -
Uber, defiant, says it won’t apply for an autonomous car permit in California [Updated] | Ars Technica -
Call for new Stackage Curator - Michael Snoyman's blog -
The Epic Guide to Bootstrapping a SaaS Startup from Scratch — By Yourself (Part 1) – Medium -
Bootstrapping a SaaS Startup from Scratch  | Hacker News -
Haskell Pitfalls : haskell -
Uber's Loss Exceeds $800M in Q3 on $1.7B in Net Revenue | Hacker News -
The New (Experimental) Yesod Development Server - Feedback Requested! -
Tim Cook assures employees that Apple is committed to the Mac | Hacker News -
Modern garbage collection – Medium -
Modern garbage collection | Hacker News -
Apple temporarily cuts prices on all USB-C cables and accessories [Updated] | Ars Technica -
Haskell-like languages. : haskell -
Supporting Haskell.org in 2016 : haskell -
Could GHC support off-heap storage (like Erlang's ETS)? : haskell -
Mdmath – LaTeX Math for Markdown inside of Visual Studio Code | Hacker News -
Building Jarvis -
Building Jarvis | Hacker News -
Finally, a Fortran Web Framework | Hacker News -
Backpack to Work: Towards Backpack in Practice - YouTube -
A Git catastrophe cleaned up | Hacker News -
Why are browsers so slow? -
Why are browsers so slow? | Hacker News -
PostgreSQL anti-patterns: read-modify-write cycles (2014) | Hacker News -
Update on Multi-Process Firefox | Hacker News -
Bootstrap 4 drops IE9 support and goes full flexbox | Hacker News -
Four years with Rust | Hacker News -
Beware of readFile - Michael Snoyman's blog -
Don't use flexbox for page layout | Hacker News -
haskell - Yesod.Auth.Email setting password always returns "Passwords did not match, please try again" - Stack Overflow -
SES: Sending Mail From Different Regions · Issue #28 · snoyberg/mime-mail -
Authentication and Authorization :: Yesod Web Framework Book- Version 1.4 -
GitLab 8.15 Released | Hacker News -
Beware of readFile - Michael Snoyman's blog : haskell -
[ANNOUNCE] GHC 8.0.2 release candidate 2 -
[ANNOUNCE] GHC 8.0.2 release candidate 2 : haskell -
How Long Have You Used Haskell? - haskell -
Presentable Types · Haskell for Mac -
Keter 1.0.1 and email auth - Google Groups -
authEmail CSRF token problem - Google Groups -
Basic email/password auth: why separate user, email tables? - Google Groups -
Yesod - the best way to create users on the web site? - Google Groups -
Sing-in page customization - Google Groups -
Show HN: 50X Faster Than Redis, Open Source P2P Firebase | Hacker News -
Things I learnt making a fast website | Hacker News -
Why time management is ruining our lives | Hacker News -
Can I specify an encoding when going from ByteString to String? - haskell -
Retro-Haskell: can we get seq somewhat under control? -
Announcing amazonka-s3-streaming-0.1 - efficient streaming and concurrent uploads to S3 based on Amazonka - haskell -
Announcing amazonka-s3-streaming-0.1 - Axman6.com -
Cyanogen services shutting down -
Cyanogen services shutting down | Hacker News -
Getting Started with Tokio | Hacker News -
What is the current state of JavaScript problem? : haskell -
Reflecting on Haskell in 2016 -
Reflecting on Haskell in 2016 | Hacker News -
» ELBs are great for HA but not for balancing load -
Ways to do load balancing wrong | Hacker News -
Are You Load Balancing Wrong? - ACM Queue -
Have more famous people died in 2016? | Hacker News -
Raspberry Pi releases an OS to breathe new life into old PCs | Hacker News -
What are the best cell phone options in Korea for English-speakers? - Quora -
Raspberry Pi releases an OS to breathe new life into old PCs -
First trailer for Alien: Covenant goes for the gore | Ars Technica -
Review: The AirPods are fine wireless headphones for a certain type of person | Ars Technica -
No tipping means better business | Hacker News -
Software Simply: On Haskell Documentation -
On Haskell Documentation : haskell -
Everyone quotes command line arguments the wrong way | Hacker News -
Carrie Fisher has died | Hacker News -
Carrie Fisher has died | Hacker News -
haskus/haskus-system: Haskus system -
Haskus, a Haskell Framework for Systems Programming | Hacker News -
Markdown.css - make HTML look like plain-text -
Markdown.css – Make HTML look like plain-text markdown | Hacker News -
UDP vs TCP | Hacker News -
facebooknuclide/atom-in-orbit: Putting Atom in the browser -
Atom-in-orbit: Putting Atom in the browser | Hacker News -
Kwang's Haskell Blog - Type inference algorithms of Haskell-like languages -
Rust is literally Haskell : rust -

-

-

- diff --git a/resources/bookmarks/January.html b/resources/bookmarks/January.html deleted file mode 100644 index 64fd6d0..0000000 --- a/resources/bookmarks/January.html +++ /dev/null @@ -1,467 +0,0 @@ -

January

-

-

The Performance of Open Source Software | Warp -
The Architecture of Open Source Applications (Volume 2): The Glasgow Haskell Compiler -
Serving an API - haskell-servant -
Data-Pipelines in Haskell -
Accidentally Quadratic — GHC Derived Foldable and Traversable Instances -
The convergence of compilers, build systems and package managers : Inside 736-131 : haskell -
Towards general techniques in programming - the second diagonal principle - Correct Software -
Using applicative functors for reactive web interfaces: purescript-flare : haskell -
The convergence of compilers, build systems and package managers : Inside 736-131 -
Neil Mitchell's Haskell Blog: What's the point of Stackage LTS? -
Is there any point in having Stackage LTS? : haskell -
[ANNOUNCE] Glasgow Haskell Compiler version 7.10.3 -
1.7. Release notes for version 7.10.3 -
GHC 7.10.3 is here! : haskell -
Neil Mitchell's Haskell Blog: MinGHC is Dead, Long Live Stack -
MinGHC is Dead, Long Live Stack : haskell -
Django CMS 3.2 released | Hacker News -
django CMS 3.2 - Blog - django-cms.org -
Facebook to open-source AI hardware design | Hacker News -
Facebook to open-source AI hardware design | Engineering Blog | Facebook Code -
Stack + Nix = portable reproducible builds -
Stack and Nix = portable reproducible builds | Hacker News -
What happened to Haskell on the web framework benchmark game? : haskell -
How stack can use Docker under the hood | FP Complete -
Anyone using Neovim ? : haskell -
Haskell Skyline -
Phabricator: Add kind equalities to GHC. -
Kind equalities are now in GHC HEAD! : haskell -
New release of Haskell bindings to libclang! : haskell -
Using Stack with GHC 7.10.3 | FP Complete -
Sufficiently Smart Compiler -
osa1 - On sufficiently smart compilers -
On sufficiently smart compilers : haskell -
[1512.01896] In the Age of Web: Typed Functional-First Programming Revisited -
In the Age of Web: Typed Functional-First Programming Revisited : haskell -
How Elon Musk and Y Combinator Plan to Stop Computers From Taking Over — Backchannel — Medium -
How Elon Musk and Y Combinator Plan to Stop Computers from Taking Over | Hacker News -
Foldable scans : haskell -
AutoPar: Automating the Parallelization of Functional Programs -
Chris Lattner on Swift and dynamic dispatch | Hacker News -
Elm by Example | Hacker News -
[swift-evolution] Proposal: Universal dynamic dispatch for method calls -
Haste thesis: A Distributed Haskell for the Modern Web : haskell -
Exploratory Programming with Haskell : haskell -
Exploratory Haskell -
Haskell for all: How to contribute to the Haskell ecosystem : haskell -
Haskell for all: How to contribute to the Haskell ecosystem -
mpickering - Pattern Synonyms in GHC 8.0 -
Pattern Synonyms in GHC 8.0 : haskell -
Stackage Nightly and LTS moving soon to ghc-7.10.3 -
Haddock for GHC 8.0 -
Help bring beautiful documentation to GHC 8.0 — Volunteers needed to get Haddock ready for release : haskell -
Finite Automata -
Context-Free Grammars -
Does this library exist or should I try and make it? : haskell -
Philips Hue blocks 3rd party lights | Hacker News -
Why is the first random value produced from a StdGen so predictable? : haskell -
The Slack Platform Launch | Several People Are Typing -
How to set up your open source Haskell project on github with automated testing via Travis (example commit) : haskell -
Efficient parsing of large text files, part 1 | hbtvl -
Efficient parsing of large text files, part 2 | hbtvl -
Efficient parsing of large text files, part 3 | hbtvl -
Split into sum : haskell -
High Performance Log Parsing in Haskell: Part Two : haskell -
Efficient parsing of large text files, part 4 and conclusion | hbtvl -
Why Python 3 Exists | Hacker News -
Brett Cannon - Why Python 3 exists -
Compiling to WebAssembly: It’s Happening! ★ Mozilla Hacks – the Web developer blog -
Compiling to WebAssembly: It’s Happening | Hacker News -
Georgia Tech Researchers Demonstrate How the Brain Can Handle So Much Data | Georgia Tech - College of Computing -
Georgia Tech Researchers Demonstrate How the Brain Can Handle So Much Data | Hacker News -
Updated WebGL Benchmark Results – Unity Blog -
Updated WebGL Benchmark Results | Hacker News -
Algebraic Data Types Demystified · Haskell for Mac -
Intro PureScript for a Haskeller : haskell -
Reflecting on Haskell in 2015 -
The long process of creating a Chinese font | Hacker News -
Does Haskell make promises it can't keep? The problem with wrapping numeric types : haskell -
Promises.md -
What's the best way to create GUI for Haskell program? : haskell -
Typeclass counter examples : haskell -
Thomas Byttebier - The best icon is a text label -
The best icon is a text label | Hacker News -
Adam Bergmark, Stackage update and plans for LTS Haskell 4 -
Stackage update and plans for LTS Haskell 4 : haskell -
ARoW.info Blog -- PureScript for the Haskeller -
PostgreSQL: PostgreSQL 9.5 RC1 Released -
PostgreSQL 9.5 RC1 Released | Hacker News -
Big company vs. startup work and compensation -
Beating the Averages -
An article about the power of Lisp. Where does Haskell fall on this "spectrum"? I know Haskell is very high-level, but is Lisp in some sense still more powerful than Haskell? Also what do you think about Haskell vs. Lisp for a startup in 2015? (That article is from 2003 so things may have changed.) : haskell -
Report more than one syntax error in Parsec? : haskell -
tel/serv -
Serv — Dependently typed API servers, clients, and documentation : haskell -
Big Company vs. Startup Work and Compensation | Hacker News -
articles/tag-dont-type.md at master · quchen/articles -
Tag, don't type : haskell -
andyfriesen.com - Haskell Basics: How to Loop -
Haskell Basics: How to Loop : haskell -
Kubernetes for Haskell Services | FP Complete -
mnot’s blog: Why 451? -
Why 451? | Hacker News -
CS Syd - Haphviz: Graphviz code generation with Haskell : haskell -
Welcome to MkLinux.org -
MkLinux: Apple-Funded Port of Linux to the Power Mac / Mach Microkernel | Hacker News -
Real-time GIF images | Hacker News -
Two Wrongs – Using withPtr From inline-c in Haskell -
Using withPtr From inline-c in Haskell : haskell -
The Procrastination Matrix - Wait But Why -
The Hacks of Life: The Dangers of Super Smart Compilers -
The Dangers of Super Smart Compilers | Hacker News -
Juniper screenOS authentication backdoor - master ssh password posted | Hacker News -
How do I learn to write code like the post doc Haskell programmer? : haskell -
Ask-Elle: an adaptable programming tutor for Haskell giving automated feedback [PDF] : haskell -
idea for tryhaskell - mine data for common beginner errors : haskell -
A philosophical difference between Haskell and Lisp : haskell -
A philosophical difference between Haskell and Lisp -
Stream fusion and composability (Java 8 and Haskell) for newbies -
Install Plex Media Server on Raspberry Pi 2 • -
Haskell and Lisp Comparisons in Podcast : haskell -
Write You a Haskell ( Stephen Diehl ) Introduction -
Kwm, tiling window manager for OS X | Hacker News -
koekeishiya/kwm -
Vagrant 1.8 - HashiCorp -
The biggest mystery in mathematics: Shinichi Mochizuki and the impenetrable proof : Nature News & Comment -
On the Juniper backdoor | Hacker News -
A Few Thoughts on Cryptographic Engineering: On the Juniper backdoor -
Vagrant 1.8 released | Hacker News -
Nikita Churaev / dialog · GitLab -
Dialog: 21st century style replacement for putStrLn (my first package) : haskell -
Front Row Education Engineering — Servant style handlers for Yesod -
Servant style handlers for Yesod : haskell -
[Haskell-community] (no subject) -
haskell.org is now a 501(c)(3) charity in its own right : haskell -
I analyzed 250 SaaS pricing pages — here’s what I found -
I analyzed 250 SaaS pricing pages | Hacker News -
Electron at Wagon - Wagon -
My First Two Weeks of Haskell at Wagon - Wagon -
GitLab 8.3 released with Auto-merge and GitLab Pages | Hacker News -
Kim Dotcom loses extradition case, files immediate appeal | Stuff.co.nz -
A Python programmer discovers Haskell : haskell -
haskell in 8th grade : haskell -
ANN: stack-1.0.0 : haskell -
[Haskell-cafe] ANN: stack-1.0.0 -
Porting xv6 to Haskell : haskell -
State considered harmful - A proposal for a stateless laptop (new paper) -
"State considered harmful": A proposal for a stateless laptop (?!) : haskell -
Kim Dotcom loses extradition case | Hacker News -
How to setup Leksah? : haskell -
Haskell on Topcoder : haskell -
Why Copenhagen Has Such Great Water - CityLab -
Why Copenhagen Has Almost Perfect Water | Hacker News -
Unison: How laziness brings good query performance without an insane black box optimizer -
A Haskell Compiler (61) -
Can't build GHCJS with Stack on Ubuntu 15.10 : haskell -
How come refined isn't in base? : haskell -
[help] performance problem : haskell -
What would you reccomend for a non programmer to delve into haskell ? : haskell -
What Code Does vs What Code Means -
Much faster incremental apt updates | Blog of Julian Andres Klode -
Much faster incremental apt updates | Hacker News -
TLDR pages -
TLDR pages | Hacker News -
What happens when you apply length to an infinite list. : haskell -
Predicate on length of infinite list : haskell -
Some thoughts/questions on writing typesafe and secure web APIs : haskell -
Predicate Higher Order Functions : haskell -
Logical Graphs LLC: Trading Analytics -
Some Haskell libraries on trading analytics I couldn't find on Hackage. Who made these? : haskell -
What are Haskellers' opinion/critiques of Perl6? : haskell -
The Future of Node Is in Microsoft’s Fork | Hacker News -
Тех-Детали: nginx module to enable haskell binding to nginx configuration files -
Re: Testing Micro Services - Google Groups -
nginx module to enable haskell binding to nginx configuration files : haskell -
Looking for help to trace/improve memory/GC performance of something? : haskell -
How T-Mobile wanted to change itself but ended up changing the wireless industry | Hacker News -
Blog series: Typed up CRUD SPA with Haskell and Elm : haskell -
Interview with Jesper Louis Andersen about Erlang, Haskell, OCaml, Go, Idris, the JVM, software and protocol design — PART I : haskell -
Categories for the Working Mathematician : haskell -
Month in Haskell Mode December 2015 · haskell/haskell-mode Wiki -
Designing a GUI library purely in Haskell : haskell -
Haskell stack, Yesod and Docker : haskell -
Best Haskell Books for 2015 -
Best Haskell Books for 2015 : haskell -
Haskell for all: Compile-time memory safety using Liquid Haskell : haskell -
Why Age of Empires 2 is still growing | Hacker News -
haskell-ide-engine/Report-2015-12.md at master · haskell/haskell-ide-engine -
Difference between . and $ : haskell -
openage by [SFT]Technologies -
Openage – Free Age of Empires 2 engine clone | Hacker News -
jonathanslenders/pymux -
Pymux: a tmux clone in pure Python | Hacker News -
Will GHC's multiple-passes or advanced optimizations ever rewrite the contents of the enclosed files such that they'll result in the same binary? : haskell -
A Modern Architecture for FP : haskell -
Peter Naur - Wikipedia, the free encyclopedia -
Peter Naur has died | Hacker News -
ghc-mod 5.5.0.0 pre-release, testers wanted : haskell -
What's the major benefit of everything being immutable, anyway? : haskell -
Wi-Fi Alliance® introduces low power, long range Wi-Fi HaLow™ | Wi-Fi Alliance -
Wi-Fi Alliance introduces low power, long range Wi-Fi HaLow | Hacker News -
New Adventures for Elm -
New Adventures for Elm | Hacker News -
Visible type application will be part of GHC 8.0 : haskell -
I worked at Amazon to see if anything's changed | Hacker News -
Haskell LTS-4.0 is here! : haskell -
Are StrictHaskell and OverloadedRecordFields the beginning of Haskell 2.0? : haskell -
Status of School of Haskell 2.0 : haskell -
Status of School of Haskell 2.0 | FP Complete -
Question regarding performance : haskell -
Is there any hope to see the "import problem" solved ? : haskell -
GHC/QualifiedModuleExport - HaskellWiki -
Prelude> 2 ^ 3 ^ 4 ^ 5 hangs stack ghci : haskell -
Convolutionnal Neural Network in Haskell? : haskell -
RBM and Backprop implementation : haskell -
#1681 (Develop cross platform mobile applications using GHCJS/GHC) – Haskell.org GSOC -
Develop cross platform mobile applications using GHCJS/GHC : haskell -
How export/import a list of submodules; project organization : haskell -
ANN: QuickLift, a Haskell/PureScript single page app : haskell -
Optimizing list comprehensions : haskell -
Do Free Monads Actually Matter? : haskell -
CodeReddit: Cover UI for Reddit at Work | Hacker News -
[Haskell-cafe] AMP - how do you motivate this in teaching? -
Paypal froze our funds, then offered us a business loan — Medium -
Paypal froze our funds, then offered us a business loan | Hacker News -
Using the OS X 10.10 Hypervisor Framework: A Simple DOS Emulator | Hacker News -
Announcing: C◦mp◦se Unconference and Exchange : haskell -
ghc-shake: Reimplementing ghc –make : Inside 736-131 -
Reimplementing ghc --make with Shake : haskell -
Why I love Snapchat | Hacker News -
Haskell Backpack and GHC 8.x : haskell -
Convert IO String to String : haskell -
Would /r/haskell be interested in funding those projects? : haskell -
bloomberg/ocamlscript -
OCamlScript – a JavaScript back end for OCaml | Hacker News -
Install and setup IHaskell using Stack (on Ubuntu) step-by-step : haskell -
Install IHaskell on Ubuntu 14.04 with Stack — Pythux -
how to make it work so that I can do two things after a maybe -> : haskell -
wandernauta/viskell -
Viskell: Visual programming meets Haskell. Visual programming environment for a typed (Haskell-like) functional programming language. : haskell -
Telegram Bot API bindings : haskell -
Detecting type errors with GHCi commands? : haskell -
Your favourite language extension? : haskell -
What do you call this kind of type inference : haskell -
What is the motivation for extensible effects? : haskell -
What is the current state of ghci or any editor documentation tools? : haskell -
Catamorphisms in 15 Minutes : haskell -
How Optimizely Almost Got Me Fired (2014) | Hacker News -
Recent Spam : haskell -
Cushion - Expenses -
A short fable of software engineering vs. regular engineering | The Lab Lunch -
A short fable of software engineering vs. regular engineering | Hacker News -
Running costs for running a web app | Hacker News -
1233970 – YouTube no longer serving MSE/webm to Firefox 43 users. -
YouTube change served lower-quality video to Firefox 43 for 2 weeks | Hacker News -
Why Americans Work So Much Despite Economist John Maynard Keynes's Prediction - The Atlantic -
Why Americans Work So Much Despite Keynes's Prediction | Hacker News -
Linux-Kernel Archive: Linux 4.4 -
Viskell: Visual programming meets Haskell | Hacker News -
Why We Hire Great Developers, Not Great Mobile Developers — Runkeeper: Everyone. Every Run. — Medium -
Why We Hire Great Developers, Not Great Mobile Developers | Hacker News -
Stephen Hawking's New Black-Hole Paper, Translated: An Interview | Hacker News -
Emacs development -
CλasH: From Haskell To Hardware : haskell -
New release of the book Haskell Programming from first principles : haskell -
Access files one at a time? : haskell -
Canada and Denmark fight over uninhabited island by leaving bottles of alcohol | Hacker News -
Canada and Denmark whiskey war over Hans Island -
String manipulation Guide? : haskell -
Haskell books to be published in 2015? : haskell -
Plugin system for an e-commerce/CMS website? (XPOST from /r/haskellquestions) : haskell -
A Haskell program for beginners? : haskell -
Shutting down persona.org in November 2016 | Hacker News -
H.265/HEVC vs H.264/AVC: 50% bit rate savings verified - BBC R&D -
H.265/HEVC vs. H.264/AVC: 50% bit rate savings verified | Hacker News -
.py in the sky -
Code that will break in Python 4 | Hacker News -
Why Would Anyone Use Perl 6? | Hacker News -
EFF To Court: Cisco Must Be Held Accountable For Aiding China’s Human Rights Abuses | Electronic Frontier Foundation -
Cisco Must Be Held Accountable for Aiding China’s Human Rights Abuses | Hacker News -
T-Mobile CEO to EFF: ‘Who the Fuck Are You?’ | Hacker News -
Elm in the Real World | Hacker News -
Can I refractor this out and how do I do that. : haskell -
ghc-mod not finding stack project dependencies : haskell -
HaSKI: Haskell-to-FPGA SKI calculus computer : haskell -
Typed up CRUD SPA with Haskell and Elm - Part 2: Persistence up and running : haskell -
Core Spec : haskell -
I run a SV startup but refuse to own a cellphone | Hacker News -
Project Rider – A C# IDE | Hacker News -
Qt Licensing Changes | Hacker News -
[ANNOUNCE] Glasgow Haskell Compiler 8.0.1, release candidate 1 -
GHC 8.0 release candidate 1 is out! : haskell -
Hiring: Haskell at Biotech Startup! : haskell -
How we used category theory to solve a problem in Java : haskell -
ChakraCore GitHub repository is now open | Hacker News -
Merge sort complexity problem : haskell -
Exploring S3 based filesystems S3FS and S3Backer | TurnKey GNU/Linux Blog -
Encapsulating unsafe IORefs : haskell -
Encapsulating unsafe IORefs · Karius Engineering Blog -
Learn you Func Prog on five minute quick! (El Reg's parody on Functional Programming) : haskell -
Facebook Messenger protocol in pidgin | Hacker News -
Release 1.4.0 · atom/atom -
Atom text editor 1.4 released | Hacker News -
OpenSSH: client bug CVE-0216-0778 -
OpenSSH: client bug CVE-2016-0777 | Hacker News -
Announcing serversession | Felipe Lessa -
Imba: The new programming language for web apps | Hacker News -
I'm learning calculus for the first time. If you were me, how would you use Haskell to get calculus-fu and increase Haskell-fu? : haskell -
Netflix to block proxy access to content not available locally | Hacker News -
What to do with aeson-0.10? | Unknown Parallel -
What to do with aeson-0.10? : haskell -
Help 25 to 30 girls learn computer programming in Mountain View, CA! | Sententia cdsmithus -
You could help 25 to 30 kids learn Haskell in Mountain View, CA : haskell -
Is there an intuitive interface for MySQL? : haskell -
dear-github/dear-github: An open letter to GitHub from the maintainers of open source projects -
Dear GitHub | Hacker News -
Amazon Has Just Registered to Sell Ocean Freight | Hacker News -
Why Kubernetes isn't using Docker's libnetwork | Hacker News -
The resolution of the Bitcoin experiment | Hacker News -
A New Code License: The MIT, this time with Attribution Required - Meta Stack Exchange -
A New Code License: The MIT, This Time with Attribution Required | Hacker News -
New blog post: Efficient Amortised and Real-Time Queues in Haskell : haskell -
Using Lua with Haskell : haskell -
Attempt at a modern Haddock theme : haskell -
An open letter of gratitude to GitHub | Hacker News -
To ship `stack.yaml` or not to? : haskell -
Vim Galore: everything you need to know about Vim | Hacker News -
Well-Typed - The Haskell Consultants: Efficient Amortised and Real-Time Queues in Haskell -
Why, oh WHY, do those #?@! nutheads use vi? -
transcranial/atom-transparency - CSS -
How to make the Atom editor transparent | Hacker News -
lukasmartinelli/hwk - Haskell -
Haskell based awk and sed alternative : haskell -
What sort of candidates are shops like Jane Street looking for for summer internships? : haskell -
What is Type-In-Type? : haskell -
Idris: Type safe printf - YouTube -
DependentHaskell/Phase1 – GHC -
Is there a way to have a function that returns a random number? : haskell -
Yesod for reactive web apps - Google Groups -
ANN: stack-1.0.2 - Google Groups -
lts-4 with aeson-0.10 is being discontinued, lts-5 will use aeson-0.9 : haskell -
aeson intentionally held back to 0.9.* · Issue #1155 · fpco/stackage -
Haskell for Finance resources : haskell -
Types Don't Lie, But They Won't Reveal Everything – cli-nerd.com -
WebTorrent – BitTorrent over WebRTC | Hacker News -
Haskell developer roles at Standard Chartered [London, Singapore] : haskell -
Haskell developer roles at Standard Chartered [London, Singapore] | Control.Monad.Writer -
Making GitLab Better for Large Open Source Projects | GitLab -
Dear open source maintainers, a letter from GitLab | Hacker News -
Christian's Rants and Ravings -
Fun With RULES : haskell -
darcs advanced revision control system: darcs hacking sprint 10 report -
Haskell Meets the Central Dogma · SavinaRoja -
Haskell Meets the Central Dogma (of molecular biology) : haskell -
Did European Court Just Outlaw “Massive Monitoring of Communications” in Europe? | Hacker News -
Apple May Be Using Congo Cobalt Mined by Children, Amnesty Says | Hacker News -
GHC panic! in ghc-mod + Atom when using QuasiQuotes : haskell -
"Clean Code" vs. Haskell : haskell -
Data.Tagged -
haskell - What is the purpose of `Data.Proxy`? - Stack Overflow -
Typed Holes for beginners -
GHC/Typed holes - HaskellWiki -
[Haskell-cafe] [ANN] ghc-mod-5.5.0.0: Happy Haskell Hacking -
The Monad Challenges: A new take on an old theme : haskell -
lettier/gifcurry: Create animated GIFs, overlaid with optional text, from movies. -
Haskell GIF creation utility using FFmpeg, ImageMagick, and GTK. : haskell -
The State Of Meteor Part 1: What Went Wrong - Discover Meteor -
ANN: logging-effect - a new mtl-style monad transformer for general purpose & compositional logging : haskell -
Control.Monad.Log -
Microsoft Philanthropies announces commitment to donate $1 billion in cloud computing resources to serve the public good | News Center -
Microsoft giving cloud to charities and researchers - think haskell.org would benefit? : haskell -
"Haskell Design Patterns" any good? : haskell -
Can this be ported to Haskell : haskell -
joegallo/doric: Clojure table layout -
PostgreSQL Simple FromField help : haskell -
status of `servant-foreign`? : haskell -
Show HN: Super Mail Forward, an HTML email that evolves as you forward it | Hacker News -
Timestamps done right | Kerf blog -
Timestamps done right | Hacker News -
An introduction to building GHC type-checker plugins : haskell -
Docker Acquires Unikernel Systems As It Looks Beyond Containers | TechCrunch -
Docker Acquires Unikernel Systems as It Looks Beyond Containers | Hacker News -
Unikernel Systems Joins Docker | Docker Blog -
Unikernel Systems Joins Docker | Hacker News -
I missed Nim - Roads Less Taken -
I missed Nim (2014) | Hacker News -
Announcing Rust 1.6 - The Rust Programming Language Blog -
Announcing Rust 1.6 | Hacker News -
Haskell: Why use Proxy? - Stack Overflow -
The meaning of tilde in Haskell types (Type equality) - Stack Overflow -
Posting successful SSH logins to Slack -
Posting successful SSH logins to Slack | Hacker News -
Microsoft silenty adds Amazon root certificates to its CTL · HA -
Microsoft silently adds Amazon root certificates to its CTL | Hacker News -
GHC type checker plugins: adding new type-level operations · Christiaan Baaij -
An introduction to building GHC type-checker plugins : haskell -
The Apple iPad Pro Review | Hacker News -
CλaSH FPGA Starter · Christiaan Baaij -
Any high-level way to abstract away networking / communication ? : haskell -
glasgowmoocadventures -
Haskell MOOC project : haskell -
Why didn't France adopt the longbow like England did? | Hacker News -
The State of Meteor Part 1: What Went Wrong | Hacker News -
A Beginner's Guide to Scaling to 11 Million+ Users on Amazon's AWS - High Scalability - -
How to add polymorphic type signature in where clause? : haskell -
ANN: generics-eot-0.2 : haskell -
[ANNOUNCE] Shaking up GHC -
Shaking up GHC : haskell -
Does this exist in base? : haskell -
Maybe/Either vs exceptions [Video] : haskell -
Servant or other library for a largish API? : haskell -
Stack users: multiple executables in one cabal file, or multiple packages in stack.yaml? : haskell -
Why Erlang looks like it does | Hacker News -
Parsing Bitstreams with haskell : haskell -
Haskell as a gradually typed dynamic language | Types and Kinds -
Haskell as a gradually typed dynamic language : haskell -
haskell - Can I use gloss on Android? - Stack Overflow -
Can I use gloss on Android? : haskell -
9 things every React.js beginner should know - Cam Jackson -
Things every React.js beginner should know | Hacker News -
Read Me | Redux -
[erlang-questions] State Management Problem -
Local documentation : haskell -
Integrating slack with emacs : haskell -
github/swift-style-guide: Style guide & coding conventions for Swift projects -
Swift Style Guide by GitHub | Hacker News -
Calling OCaml from Objective-C on the iPhone - Edgar Aroutiounian -
Call OCaml from Objective-C, run on iOS, code sample and build steps | Hacker News -
State of HaLVM? : haskell -
Opam-ios: an OCaml compiler for iOS via opam, with example server code | Hacker News -
YBlog - Category Theory Presentation -
Why, oh WHY, do those #?@! nutheads use vi? -
Adrian Sampson: LLVM for Grad Students -
Magnus web site | Random stuff -
Magnus web site | Random stuff -
Cofun with Cofree Comonads: The Video : haskell -
mpm/transition - Mercurial -
Extensible, reusable parsers? : haskell -
Is GHC's popularity dangerous? : haskell -
Response to: Dynamic Typing > Static Typing? : haskell -
Multiple security vulnerabilities in Rails | Hacker News -
gereeter/hsdecomp: A decompiler for GHC-compiled Haskell -
A decompiler for GHC-compiled Haskell : haskell -
An attempt at a modern Haskell theme (continued) : haskell -
performance - Why is Haskell (GHC) so darn fast? - Stack Overflow -
Why is Haskell (GHC) so darn fast? : haskell -
Just released on Free site: Section 8 of happylearnhaskelltutorial.com Vol1 : haskell -
Stackage LTS 5.0 released : haskell -
OCaml roles at microservices startup StackHut [London] : haskell -
GHC 8.0.1 plans -
Question about optimizing code with free monads and monad transformers. : haskell -
Chrome Cleanup Tool | Hacker News -
If you are looking for something to do, here is a small/medium and well made library you can help fix. : haskell -
How do people feel about modules that only exist to provide instance? : haskell -
rebase: A more progressive alternative to the "base" package | Hackage -
"rebase", a more progressive alternative to the "base" package : haskell -
Is there a way to access an Amazon RDS database via Haskell and be able run queries via an API of sorts? Is there a Haskell library that allows me to do that? : haskell -
Automatic bug-repair system fixes 10 times as many errors as its predecessors | Hacker News -
Simon Meier: A primer to commercial Haskell programming [1:20:48] : haskell -
Options for a Haskell computing environment, Mac OS X or Linux : haskell -
Challenge: Write a compile-time type-level quine : haskell -
nh2/call-haskell-from-anything -
It is now easier to extend or replace non-Haskell code with Haskell code compiled with GHC 7.10.3 : haskell -
Why doesn't the Monad class use Constraints? : haskell -
100 Days of Fibonacci - Day 9, Haskell Types · Mads Buch -

\ No newline at end of file diff --git a/resources/bookmarks/March.html b/resources/bookmarks/March.html deleted file mode 100644 index 8ac8065..0000000 --- a/resources/bookmarks/March.html +++ /dev/null @@ -1,173 +0,0 @@ -

March

-

-

DROWN Attack -
Comparing Haskell and Rust: which to choose, when? : haskell -
Microsoft, Google, Facebook Back Apple in Blocked Phone Case | Hacker News -
Ghost in the Shell remains our most challenging film about technology | Hacker News -
Haskell Summer of Code : haskell -
The Haskell Cheatsheet -
The Haskell Cheatsheet (A Repost) : haskell -
A 19 basis point portfolio beats the average of every college endowment | Hacker News -
Why is Google mysteriously allowing some very popular apps to break many Google Play rules (developer policies)? : Android -
Why is Google allowing this popular app to violate so many Google Play rules? | Hacker News -
Haskell job posting: Join 'Stage N Software' to help build Unison, a revolutionary new platform for software development : haskell -
WxWidgets 3.1.0 Brings Better HiDPI Support, WxQt with Qt5 | Hacker News -
Free React.js Fundamentals Course | Hacker News -
Malthe Borch's Homepage: A Blog About Computer Systems and Language Design -
The Sign Up with Google Mistake You Can't Fix | Hacker News -
Slack will soon start testing voice and video chat | TechCrunch -
Slack will soon start testing voice and video chat | Hacker News -
OCaml 4.03: Everything else :: Jane Street Tech Blogs -
OCaml 4.03: Everything else | Hacker News -
The design of the Strict Haskell pragma | Johan Tibell -
Kanye West caught using Pirate Bay to download music software | Ars Technica UK -
Spotify's Chromecast approach is the start of an unfortunate new chapter | Android Central -
Write You a Haskell ( Stephen Diehl ) : haskell -
Does Haskell have a function that let's you apply an argument to a function? : haskell -
LaTeX diagrams in Hakyll : haskell -
What Features Would You Like to Find in a Haskell IDE? : haskell -
Status of Stream Fusion in GHC libraries : haskell -
What features would you like to find in a Haskell IDE? (part 2) : haskell -
Adding Redis to Yesod : haskell -
Laziness in ghci-7.10 : haskell -
Dead code elimination : haskell -
Amazon Echo Dot | Hacker News -
Source: Microsoft mulled an $8B bid for Slack, will focus on Skype instead | Hacker News -
Real benefit of Servant : haskell -
How's your experience with full stack haskell been? : haskell -
How sharing code between front/back is relevant in practice ? : haskell -
apt 1.0 | Michael Vogt's blog -
Bitcoin transaction processing takes up to 10 hours | Hacker News -
Frege/frege: Frege is a Haskell for the JVM. It brings purely functional programing to the Java platform. -
As 4th trial nears, Samsung asks judge: Make Apple stop talking about Korea | Ars Technica -
San Bernardino DA says seized iPhone may hold “dormant cyber pathogen” [Update] | Ars Technica -
Mr. Fart’s Favorite Colors — Medium -
Mr. Fart’s Favorite Colors | Hacker News -
OSX.KeRanger.A malware in 2.90? - Transmission -
Transmission BitTorrent app contained malware | Hacker News -
Is group chat making you sweat? — Signal v. Noise — Medium -
Is group chat making you sweat? | Hacker News -
10 years on LinkedIn -
Coffee Drip Printer - RIT: College of Imaging Arts & Sciences -
Coffee Drip Printer | Hacker News -
So, you want to learn bookkeeping | Hacker News -
[NYHUG] Translating Haskell to Hardware : haskell -
Translating Haskell to Hardware - YouTube -
LLVM 3.8 Release Notes | Hacker News -
LenzBlog - Full stack web Haskell with Servant and GHCJS -
Hacking with Latex | Hacker News -
WebAssembly & Haskell : haskell -
hint is alive again! : haskell -
America's High School Graduates Look Like Other Countries' High School Dropouts | WAMC -
America's High School Graduates Look Like Other Countries' High School Dropouts | Hacker News -
How FPGAs work, and why you'll buy one (2013) | Hacker News -
Alchemy Micro-Service Framework: Using RabbitMQ Instead of HTTP | Hacker News -
Amazon Echo, home alone with NPR on, got confused and hijacked a thermostat - Quartz -
Measuring GHC's "soul crushing" compile times / feedback? : haskell -
Nerd-Fonts – Patched Fonts for Vim | Hacker News -
I made my own clear plastic tooth aligners and they worked | Hacker News -
The case expression syntactic sugar can usually be represented in terms of an ordinary higher-order function. How can this be done for GADT's? : haskell -
Dropbox’s Exodus from the Amazon Cloud | Hacker News -
GitHub · Where software is built -
Show HN: Using Google AMP to build a Medium-style Jekyll site that loads in 65ms | Hacker News -
V8 JavaScript Engine: V8 Release 5.0 -
V8 Release 5.0 (upcoming) | Hacker News -
On asking job candidates to code -
The future of dependent Haskell : haskell -
Google nabs Apple as a cloud customer - Business Insider -
Google nabs Apple as a cloud customer | Hacker News -
Atom-haskell now implements case-splitting : haskell -
Facebook is the new Excel | Hacker News -
Unmasking Startup L. Jackson | Hacker News -
Atom 1.6 and 1.7 beta -
Atom 1.6 Released with Pending Pane Items, Async Git and Top and Bottom Bar API | Hacker News -
A slightly better error message : haskell -
US government pushed tech firms to hand over source code | Hacker News -
Apple Encryption Engineers, if Ordered to Unlock iPhone, Might Resist - The New York Times -
Apple Encryption Engineers, If Ordered to Unlock iPhone, Might Resist | Hacker News -
Microsoft won't stop forcing companies to pay for its bogus Android patents | Hacker News -
Apple vs. The F.B.I.: How the Case Could Play Out | Hacker News -
Diablo - Game Concept by Condor, Inc. -
Thank you for ad blocking | Hacker News -
Swift.org - Swift 2.2 Released! -
Swift 2.2 Released | Hacker News -
iPhone SE | Hacker News -
Autocomplete from Stack Overflow -
Dragon: A distributed graph query engine | Hacker News -
Autocomplete from Stack Overflow | Hacker News -
How to do a PhD: top 10 tips | James Hayton, PhD -
How to do a PhD | Hacker News -
Any ideas for a Haskell-related senior thesis? : haskell -
Help with Aeson : haskell -
Siri and iAd Restricted by Apple 'Policy Czars' to Limit Customer Data Collection - Mac Rumors -
Apple Said to Ship New 'Ultra-Thin' 13" and 15" MacBooks by June-July - Mac Rumors -
iPad Air 2 Smart Covers Not Recommended for 9.7-inch iPad Pro - Mac Rumors -
I’ve Just Liberated My Modules — Medium -
I've Just Liberated My Modules | Hacker News -
In foam-arrow patent fracas, Newegg swoops in to aid LARPer defendant | Ars Technica -
In foam-arrow patent fracas, Newegg swoops in to aid LARPer defendant | Hacker News -
Study: People Want Power Because They Want Autonomy - The Atlantic -
Study: People Want Power Because They Want Autonomy | Hacker News -
NPM & left-pad: Have We Forgotten How To Program? | Haney Codes .NET -
Require-from-Twitter | Hacker News -
Tinder like application for gifs built with elm | Hacker News -
Docker for Mac and Windows Beta: the simplest way to use Docker on your laptop | Docker Blog -
Docker for Mac and Windows Beta: the simplest way to use Docker on your laptop | Hacker News -
Citus Unforks from PostgreSQL, Goes Open Source | Hacker News -
BLE (Bluetooth Low Energy) in Haskell? : haskell -
Justin Time - Y Combinator Posthaven -
Justin Time | Hacker News -
Valve releases Steam Controller CAD geometry so you can mod it - PC Gamer -
Valve releases Steam Controller CAD geometry so you can mod it | Hacker News -
An administrator accidentally deleted the production database | Hacker News -
Internet Archive Seeks to Defend Against Wrongful Copyright Takedowns - TorrentFreak -
Internet Archive Seeks to Defend Against Wrongful Copyright Takedowns | Hacker News -
That awkward moment when Apple mocked good hardware and poor people -
Chris Thoburn -
The Chrome Distortion: how Chrome negatively alters our expectations | Hacker News -
parro-it/canibekiked: Tool to check if any of your NPM repositories name is trademarked -
How one developer just broke Node, Babel and thousands of projects in 11 lines of JavaScript • The Register -
How do we prevent the recent Node.js npm catastrophe in Haskell? Do Stack's snapshots (carefully vetted by FP complete I presume) fix the problem? What if one package developer takes his package down? Is this possible in Stack? : haskell -
A discussion about the breaking of the Internet — Medium -
Patent that cost Microsoft millions gets invalidated | Ars Technica -
An introduction to web development? : haskell -
My dad hid his depression. I won’t hide mine | Hacker News -
How to Get Out of Bed | Hacker News -
How Scandinavian design took over the world | Hacker News -
Babel 6 – useless by default | Hacker News -
Change GitBucket user interface to make difference with GitHub -
GitHub forces Gitbucket to change their UI | Hacker News -
Need help to make Stack and Yesod work together (especially test). : haskell -
Follow the money: Apple vs. the FBI | Hacker News -
FBI Breaks into iPhone. We Have Some Questions. | Electronic Frontier Foundation -
FBI Breaks into iPhone. We Have Some Questions | Hacker News -
U.S. Drops California Case Against Apple After Accessing iPhone | Hacker News -
My Heroic and Lazy Stand Against IFTTT (Pinboard Blog) -
My Heroic and Lazy Stand Against IFTTT | Hacker News -
The npm Blog — changes to npm’s unpublish policy -
Ember FastBoot -
Ember Fastboot | Hacker News -
GitHub - evanw/thinscript: A low-level programming language inspired by TypeScript -
Thinscript: language that compiles to both WebAssembly and JavaScript | Hacker News -
Spotify raises $1 billion in debt with devilish terms to fight Apple Music | TechCrunch -
Spotify raises $1B in debt with devilish terms to fight Apple Music | Hacker News -
ptmt/react-native-desktop: React Native for OS X -
Saved replies -
React Native for OS X | Hacker News -
Git 2.8.0 released | Hacker News -
Introducing Safari Technology Preview | WebKit -
Introducing Safari Technology Preview | Hacker News -
Introducing Safari Technology Preview | Hacker News -
Sunsetting "What I Wish I Knew" -
The 6 types of Haskell users - Rick Dzekman -
Sunsetting "What I Wish I Knew" : haskell -
(Rumor) Facebook to migrate all systems to Haskell by 2018 : haskell -
Best csv library ? : haskell -
Initial compile time benchmarks -
[ghc-devs] Initial compile time benchmarks : haskell -
Late sleepers are tired of being discriminated against. And science has their back. - Vox -
Reddit removed NSL canary from 2015 Transparency Report | Hacker News -
Poor man's IDE : haskell -
It's 2016 people, we should fix our Monad instance for Either! : haskell -
Fixing the Monad instance for Either -

\ No newline at end of file diff --git a/resources/favicon.ico b/resources/favicon.ico deleted file mode 100644 index 8c4b8fa..0000000 Binary files a/resources/favicon.ico and /dev/null differ diff --git a/resources/favicon/android-icon-144x144.png b/resources/favicon/android-icon-144x144.png deleted file mode 100644 index ed201ae..0000000 Binary files a/resources/favicon/android-icon-144x144.png and /dev/null differ diff --git a/resources/favicon/android-icon-192x192.png b/resources/favicon/android-icon-192x192.png deleted file mode 100644 index 62ffccd..0000000 Binary files a/resources/favicon/android-icon-192x192.png and /dev/null differ diff --git a/resources/favicon/android-icon-36x36.png b/resources/favicon/android-icon-36x36.png deleted file mode 100644 index 4d6c376..0000000 Binary files a/resources/favicon/android-icon-36x36.png and /dev/null differ diff --git a/resources/favicon/android-icon-48x48.png b/resources/favicon/android-icon-48x48.png deleted file mode 100644 index decc1ec..0000000 Binary files a/resources/favicon/android-icon-48x48.png and /dev/null differ diff --git a/resources/favicon/android-icon-72x72.png b/resources/favicon/android-icon-72x72.png deleted file mode 100644 index eb48700..0000000 Binary files a/resources/favicon/android-icon-72x72.png and /dev/null differ diff --git a/resources/favicon/android-icon-96x96.png b/resources/favicon/android-icon-96x96.png deleted file mode 100644 index e16d5ab..0000000 Binary files a/resources/favicon/android-icon-96x96.png and /dev/null differ diff --git a/resources/favicon/apple-icon-114x114.png b/resources/favicon/apple-icon-114x114.png deleted file mode 100644 index a960c4b..0000000 Binary files a/resources/favicon/apple-icon-114x114.png and /dev/null differ diff --git a/resources/favicon/apple-icon-120x120.png b/resources/favicon/apple-icon-120x120.png deleted file mode 100644 index 064b3f0..0000000 Binary files a/resources/favicon/apple-icon-120x120.png and /dev/null differ diff --git a/resources/favicon/apple-icon-144x144.png b/resources/favicon/apple-icon-144x144.png deleted file mode 100644 index ed201ae..0000000 Binary files a/resources/favicon/apple-icon-144x144.png and /dev/null differ diff --git a/resources/favicon/apple-icon-152x152.png b/resources/favicon/apple-icon-152x152.png deleted file mode 100644 index d1172ac..0000000 Binary files a/resources/favicon/apple-icon-152x152.png and /dev/null differ diff --git a/resources/favicon/apple-icon-180x180.png b/resources/favicon/apple-icon-180x180.png deleted file mode 100644 index ae974ce..0000000 Binary files a/resources/favicon/apple-icon-180x180.png and /dev/null differ diff --git a/resources/favicon/apple-icon-57x57.png b/resources/favicon/apple-icon-57x57.png deleted file mode 100644 index ea71a32..0000000 Binary files a/resources/favicon/apple-icon-57x57.png and /dev/null differ diff --git a/resources/favicon/apple-icon-60x60.png b/resources/favicon/apple-icon-60x60.png deleted file mode 100644 index d3d8616..0000000 Binary files a/resources/favicon/apple-icon-60x60.png and /dev/null differ diff --git a/resources/favicon/apple-icon-72x72.png b/resources/favicon/apple-icon-72x72.png deleted file mode 100644 index eb48700..0000000 Binary files a/resources/favicon/apple-icon-72x72.png and /dev/null differ diff --git a/resources/favicon/apple-icon-76x76.png b/resources/favicon/apple-icon-76x76.png deleted file mode 100644 index 9a6e51d..0000000 Binary files a/resources/favicon/apple-icon-76x76.png and /dev/null differ diff --git a/resources/favicon/apple-icon-precomposed.png b/resources/favicon/apple-icon-precomposed.png deleted file mode 100644 index 771880c..0000000 Binary files a/resources/favicon/apple-icon-precomposed.png and /dev/null differ diff --git a/resources/favicon/apple-icon.png b/resources/favicon/apple-icon.png deleted file mode 100644 index 771880c..0000000 Binary files a/resources/favicon/apple-icon.png and /dev/null differ diff --git a/resources/favicon/browserconfig.xml b/resources/favicon/browserconfig.xml deleted file mode 100644 index c554148..0000000 --- a/resources/favicon/browserconfig.xml +++ /dev/null @@ -1,2 +0,0 @@ - -#ffffff \ No newline at end of file diff --git a/resources/favicon/favicon-16x16.png b/resources/favicon/favicon-16x16.png deleted file mode 100644 index 3ae0e21..0000000 Binary files a/resources/favicon/favicon-16x16.png and /dev/null differ diff --git a/resources/favicon/favicon-32x32.png b/resources/favicon/favicon-32x32.png deleted file mode 100644 index 5d98087..0000000 Binary files a/resources/favicon/favicon-32x32.png and /dev/null differ diff --git a/resources/favicon/favicon-96x96.png b/resources/favicon/favicon-96x96.png deleted file mode 100644 index e16d5ab..0000000 Binary files a/resources/favicon/favicon-96x96.png and /dev/null differ diff --git a/resources/favicon/favicon.ico b/resources/favicon/favicon.ico deleted file mode 100644 index 8c4b8fa..0000000 Binary files a/resources/favicon/favicon.ico and /dev/null differ diff --git a/resources/favicon/manifest.json b/resources/favicon/manifest.json deleted file mode 100644 index 013d4a6..0000000 --- a/resources/favicon/manifest.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "App", - "icons": [ - { - "src": "\/android-icon-36x36.png", - "sizes": "36x36", - "type": "image\/png", - "density": "0.75" - }, - { - "src": "\/android-icon-48x48.png", - "sizes": "48x48", - "type": "image\/png", - "density": "1.0" - }, - { - "src": "\/android-icon-72x72.png", - "sizes": "72x72", - "type": "image\/png", - "density": "1.5" - }, - { - "src": "\/android-icon-96x96.png", - "sizes": "96x96", - "type": "image\/png", - "density": "2.0" - }, - { - "src": "\/android-icon-144x144.png", - "sizes": "144x144", - "type": "image\/png", - "density": "3.0" - }, - { - "src": "\/android-icon-192x192.png", - "sizes": "192x192", - "type": "image\/png", - "density": "4.0" - } - ] -} \ No newline at end of file diff --git a/resources/favicon/ms-icon-144x144.png b/resources/favicon/ms-icon-144x144.png deleted file mode 100644 index ed201ae..0000000 Binary files a/resources/favicon/ms-icon-144x144.png and /dev/null differ diff --git a/resources/favicon/ms-icon-150x150.png b/resources/favicon/ms-icon-150x150.png deleted file mode 100644 index 7f26e4d..0000000 Binary files a/resources/favicon/ms-icon-150x150.png and /dev/null differ diff --git a/resources/favicon/ms-icon-310x310.png b/resources/favicon/ms-icon-310x310.png deleted file mode 100644 index 97996d9..0000000 Binary files a/resources/favicon/ms-icon-310x310.png and /dev/null differ diff --git a/resources/favicon/ms-icon-70x70.png b/resources/favicon/ms-icon-70x70.png deleted file mode 100644 index b8f19e9..0000000 Binary files a/resources/favicon/ms-icon-70x70.png and /dev/null differ diff --git a/resources/images/EC2-add-storage.thumbnail.png b/resources/images/EC2-add-storage.thumbnail.png deleted file mode 100644 index fab767b..0000000 Binary files a/resources/images/EC2-add-storage.thumbnail.png and /dev/null differ diff --git a/resources/images/EC2-choose-instance-type.thumbnail.png b/resources/images/EC2-choose-instance-type.thumbnail.png deleted file mode 100644 index e9469ab..0000000 Binary files a/resources/images/EC2-choose-instance-type.thumbnail.png and /dev/null differ diff --git a/resources/images/EC2-configure-instance-details.thumbnail.png b/resources/images/EC2-configure-instance-details.thumbnail.png deleted file mode 100644 index 653facf..0000000 Binary files a/resources/images/EC2-configure-instance-details.thumbnail.png and /dev/null differ diff --git a/resources/images/EC2-configure-security-group.thumbnail.png b/resources/images/EC2-configure-security-group.thumbnail.png deleted file mode 100644 index 2098bfd..0000000 Binary files a/resources/images/EC2-configure-security-group.thumbnail.png and /dev/null differ diff --git a/resources/images/EC2-launch-status.thumbnail.png b/resources/images/EC2-launch-status.thumbnail.png deleted file mode 100644 index d4fb355..0000000 Binary files a/resources/images/EC2-launch-status.thumbnail.png and /dev/null differ diff --git a/resources/images/EC2-review-instance-launch-1.thumbnail.png b/resources/images/EC2-review-instance-launch-1.thumbnail.png deleted file mode 100644 index 1995a61..0000000 Binary files a/resources/images/EC2-review-instance-launch-1.thumbnail.png and /dev/null differ diff --git a/resources/images/EC2-review-instance-launch-2.thumbnail.png b/resources/images/EC2-review-instance-launch-2.thumbnail.png deleted file mode 100644 index 6ff48b5..0000000 Binary files a/resources/images/EC2-review-instance-launch-2.thumbnail.png and /dev/null differ diff --git a/resources/images/EC2-select-ami.thumbnail.png b/resources/images/EC2-select-ami.thumbnail.png deleted file mode 100644 index e475f0d..0000000 Binary files a/resources/images/EC2-select-ami.thumbnail.png and /dev/null differ diff --git a/resources/images/EC2-select-keypair.thumbnail.png b/resources/images/EC2-select-keypair.thumbnail.png deleted file mode 100644 index 0c10fdb..0000000 Binary files a/resources/images/EC2-select-keypair.thumbnail.png and /dev/null differ diff --git a/resources/images/EC2-tag-instance.thumbnail.png b/resources/images/EC2-tag-instance.thumbnail.png deleted file mode 100644 index 3285c7f..0000000 Binary files a/resources/images/EC2-tag-instance.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-create-policy.thumbnail.png b/resources/images/S3-create-policy.thumbnail.png deleted file mode 100644 index e2ca6d4..0000000 Binary files a/resources/images/S3-create-policy.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-create-policy.thumbnail.thumbnail.png b/resources/images/S3-create-policy.thumbnail.thumbnail.png deleted file mode 100644 index 3eee90b..0000000 Binary files a/resources/images/S3-create-policy.thumbnail.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-edit-permissions-added.thumbnail.png b/resources/images/S3-edit-permissions-added.thumbnail.png deleted file mode 100644 index f920789..0000000 Binary files a/resources/images/S3-edit-permissions-added.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-edit-permissions-added.thumbnail.thumbnail.png b/resources/images/S3-edit-permissions-added.thumbnail.thumbnail.png deleted file mode 100644 index d2a451e..0000000 Binary files a/resources/images/S3-edit-permissions-added.thumbnail.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-edit-permissions.thumbnail.png b/resources/images/S3-edit-permissions.thumbnail.png deleted file mode 100644 index 0c1d42e..0000000 Binary files a/resources/images/S3-edit-permissions.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-edit-permissions.thumbnail.thumbnail.png b/resources/images/S3-edit-permissions.thumbnail.thumbnail.png deleted file mode 100644 index 991a2df..0000000 Binary files a/resources/images/S3-edit-permissions.thumbnail.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-policy-generator.thumbnail.png b/resources/images/S3-policy-generator.thumbnail.png deleted file mode 100644 index 95a7ddd..0000000 Binary files a/resources/images/S3-policy-generator.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-policy-generator.thumbnail.thumbnail.png b/resources/images/S3-policy-generator.thumbnail.thumbnail.png deleted file mode 100644 index 95abb0f..0000000 Binary files a/resources/images/S3-policy-generator.thumbnail.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-review-policy.thumbnail.png b/resources/images/S3-review-policy.thumbnail.png deleted file mode 100644 index 3112e6a..0000000 Binary files a/resources/images/S3-review-policy.thumbnail.png and /dev/null differ diff --git a/resources/images/S3-review-policy.thumbnail.thumbnail.png b/resources/images/S3-review-policy.thumbnail.thumbnail.png deleted file mode 100644 index deeaea8..0000000 Binary files a/resources/images/S3-review-policy.thumbnail.thumbnail.png and /dev/null differ diff --git a/resources/images/codetalk-logo-signature.png b/resources/images/codetalk-logo-signature.png deleted file mode 100755 index 020ad99..0000000 Binary files a/resources/images/codetalk-logo-signature.png and /dev/null differ diff --git a/resources/images/codetalk-logo-white.png b/resources/images/codetalk-logo-white.png deleted file mode 100644 index c0ea1db..0000000 Binary files a/resources/images/codetalk-logo-white.png and /dev/null differ diff --git a/resources/images/codetalk-logo.png b/resources/images/codetalk-logo.png deleted file mode 100644 index f60d518..0000000 Binary files a/resources/images/codetalk-logo.png and /dev/null differ diff --git a/resources/images/dynamodb-migration-phase-1-background.thumbnail.png b/resources/images/dynamodb-migration-phase-1-background.thumbnail.png deleted file mode 100644 index 1c39490..0000000 Binary files a/resources/images/dynamodb-migration-phase-1-background.thumbnail.png and /dev/null differ diff --git a/resources/images/dynamodb-migration-phase-1.thumbnail.png b/resources/images/dynamodb-migration-phase-1.thumbnail.png deleted file mode 100644 index 30c709d..0000000 Binary files a/resources/images/dynamodb-migration-phase-1.thumbnail.png and /dev/null differ diff --git a/resources/images/dynamodb-migration-phase-2-background.thumbnail.png b/resources/images/dynamodb-migration-phase-2-background.thumbnail.png deleted file mode 100644 index a1dcb31..0000000 Binary files a/resources/images/dynamodb-migration-phase-2-background.thumbnail.png and /dev/null differ diff --git a/resources/images/dynamodb-migration-phase-2.thumbnail.png b/resources/images/dynamodb-migration-phase-2.thumbnail.png deleted file mode 100644 index d155c53..0000000 Binary files a/resources/images/dynamodb-migration-phase-2.thumbnail.png and /dev/null differ diff --git a/resources/images/how-i-structure-my-work-as-a-solor-founder-goals.thumbnail.png b/resources/images/how-i-structure-my-work-as-a-solor-founder-goals.thumbnail.png deleted file mode 100644 index c8274d3..0000000 Binary files a/resources/images/how-i-structure-my-work-as-a-solor-founder-goals.thumbnail.png and /dev/null differ diff --git a/resources/images/how-i-structure-my-work-as-a-solor-founder-initiatives.thumbnail.png b/resources/images/how-i-structure-my-work-as-a-solor-founder-initiatives.thumbnail.png deleted file mode 100644 index c0f1389..0000000 Binary files a/resources/images/how-i-structure-my-work-as-a-solor-founder-initiatives.thumbnail.png and /dev/null differ diff --git a/resources/images/how-i-structure-my-work-as-a-solor-founder-issues.thumbnail.png b/resources/images/how-i-structure-my-work-as-a-solor-founder-issues.thumbnail.png deleted file mode 100644 index 9e46706..0000000 Binary files a/resources/images/how-i-structure-my-work-as-a-solor-founder-issues.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-a-different-way-android-simulator.thumbnail.png b/resources/images/mobile-a-different-way-android-simulator.thumbnail.png deleted file mode 100644 index 2bfce22..0000000 Binary files a/resources/images/mobile-a-different-way-android-simulator.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-a-different-way-ios-simulator.thumbnail.png b/resources/images/mobile-a-different-way-ios-simulator.thumbnail.png deleted file mode 100644 index 43c9701..0000000 Binary files a/resources/images/mobile-a-different-way-ios-simulator.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-a-different-way-watchos-preview.thumbnail.png b/resources/images/mobile-a-different-way-watchos-preview.thumbnail.png deleted file mode 100644 index ebd49a0..0000000 Binary files a/resources/images/mobile-a-different-way-watchos-preview.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-a-different-way-web.thumbnail.png b/resources/images/mobile-a-different-way-web.thumbnail.png deleted file mode 100644 index 80f8146..0000000 Binary files a/resources/images/mobile-a-different-way-web.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-a-different-way-widget-preview.thumbnail.png b/resources/images/mobile-a-different-way-widget-preview.thumbnail.png deleted file mode 100644 index fa8ce47..0000000 Binary files a/resources/images/mobile-a-different-way-widget-preview.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-a-different-way-widget-screenshot.thumbnail.jpeg b/resources/images/mobile-a-different-way-widget-screenshot.thumbnail.jpeg deleted file mode 100644 index 280c34d..0000000 Binary files a/resources/images/mobile-a-different-way-widget-screenshot.thumbnail.jpeg and /dev/null differ diff --git a/resources/images/mobile-a-different-way-xcode-adding-targets.thumbnail.png b/resources/images/mobile-a-different-way-xcode-adding-targets.thumbnail.png deleted file mode 100644 index 6405397..0000000 Binary files a/resources/images/mobile-a-different-way-xcode-adding-targets.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-1. Create Project.thumbnail.png b/resources/images/mobile-haskell-1. Create Project.thumbnail.png deleted file mode 100644 index 541a09a..0000000 Binary files a/resources/images/mobile-haskell-1. Create Project.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-1.1. Create Project - Single View Application.thumbnail.png b/resources/images/mobile-haskell-1.1. Create Project - Single View Application.thumbnail.png deleted file mode 100644 index e15406b..0000000 Binary files a/resources/images/mobile-haskell-1.1. Create Project - Single View Application.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-1.2. Create Project - Name.thumbnail.png b/resources/images/mobile-haskell-1.2. Create Project - Name.thumbnail.png deleted file mode 100644 index dd82255..0000000 Binary files a/resources/images/mobile-haskell-1.2. Create Project - Name.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-1.3. Create Project - Set Location.thumbnail.png b/resources/images/mobile-haskell-1.3. Create Project - Set Location.thumbnail.png deleted file mode 100644 index 603928a..0000000 Binary files a/resources/images/mobile-haskell-1.3. Create Project - Set Location.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-2. Add Source Folder for Haskell Code.thumbnail.png b/resources/images/mobile-haskell-2. Add Source Folder for Haskell Code.thumbnail.png deleted file mode 100644 index 65156d2..0000000 Binary files a/resources/images/mobile-haskell-2. Add Source Folder for Haskell Code.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-2.1. Add Source Folder for Haskell Code.thumbnail.png b/resources/images/mobile-haskell-2.1. Add Source Folder for Haskell Code.thumbnail.png deleted file mode 100644 index a96e474..0000000 Binary files a/resources/images/mobile-haskell-2.1. Add Source Folder for Haskell Code.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-3. Drag the files to Xcode.thumbnail.png b/resources/images/mobile-haskell-3. Drag the files to Xcode.thumbnail.png deleted file mode 100644 index bd128dd..0000000 Binary files a/resources/images/mobile-haskell-3. Drag the files to Xcode.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-4. Create Objective-C File.thumbnail.png b/resources/images/mobile-haskell-4. Create Objective-C File.thumbnail.png deleted file mode 100644 index f188d19..0000000 Binary files a/resources/images/mobile-haskell-4. Create Objective-C File.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-4.1. Create Objective-C File - Choose Filetype.thumbnail.png b/resources/images/mobile-haskell-4.1. Create Objective-C File - Choose Filetype.thumbnail.png deleted file mode 100644 index 8949257..0000000 Binary files a/resources/images/mobile-haskell-4.1. Create Objective-C File - Choose Filetype.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-4.2. Create Objective-C File - Set Name.thumbnail.png b/resources/images/mobile-haskell-4.2. Create Objective-C File - Set Name.thumbnail.png deleted file mode 100644 index d644762..0000000 Binary files a/resources/images/mobile-haskell-4.2. Create Objective-C File - Set Name.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-4.3. Create Objective-C File - Set Location.thumbnail.png b/resources/images/mobile-haskell-4.3. Create Objective-C File - Set Location.thumbnail.png deleted file mode 100644 index 4cc4570..0000000 Binary files a/resources/images/mobile-haskell-4.3. Create Objective-C File - Set Location.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-4.4. Create Objective-C File - Create Bridging Header.thumbnail.png b/resources/images/mobile-haskell-4.4. Create Objective-C File - Create Bridging Header.thumbnail.png deleted file mode 100644 index 1026ba9..0000000 Binary files a/resources/images/mobile-haskell-4.4. Create Objective-C File - Create Bridging Header.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-5. Build Phases.thumbnail.png b/resources/images/mobile-haskell-5. Build Phases.thumbnail.png deleted file mode 100644 index 9a772bb..0000000 Binary files a/resources/images/mobile-haskell-5. Build Phases.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-5.1. Build Phases - Add New.thumbnail.png b/resources/images/mobile-haskell-5.1. Build Phases - Add New.thumbnail.png deleted file mode 100644 index b417902..0000000 Binary files a/resources/images/mobile-haskell-5.1. Build Phases - Add New.thumbnail.png and /dev/null differ diff --git a/resources/images/mobile-haskell-5.2. Build Phases - Locate the Library.thumbnail.png b/resources/images/mobile-haskell-5.2. Build Phases - Locate the Library.thumbnail.png deleted file mode 100644 index 40c2c6a..0000000 Binary files a/resources/images/mobile-haskell-5.2. Build Phases - Locate the Library.thumbnail.png and /dev/null differ diff --git a/resources/images/pushover-config.thumbnail.png b/resources/images/pushover-config.thumbnail.png deleted file mode 100644 index 7194f2b..0000000 Binary files a/resources/images/pushover-config.thumbnail.png and /dev/null differ diff --git a/resources/images/pushover-config.thumbnail.thumbnail.png b/resources/images/pushover-config.thumbnail.thumbnail.png deleted file mode 100644 index 46a4dce..0000000 Binary files a/resources/images/pushover-config.thumbnail.thumbnail.png and /dev/null differ diff --git a/resources/images/serverless-rust-cold-starts-overview.thumbnail.png b/resources/images/serverless-rust-cold-starts-overview.thumbnail.png deleted file mode 100644 index aaccae3..0000000 Binary files a/resources/images/serverless-rust-cold-starts-overview.thumbnail.png and /dev/null differ diff --git a/resources/images/serverless-rust-trace-view-cold-start.thumbnail.png b/resources/images/serverless-rust-trace-view-cold-start.thumbnail.png deleted file mode 100644 index 9966361..0000000 Binary files a/resources/images/serverless-rust-trace-view-cold-start.thumbnail.png and /dev/null differ diff --git a/resources/images/serverless-rust-trace-view-warm-function-1.thumbnail.png b/resources/images/serverless-rust-trace-view-warm-function-1.thumbnail.png deleted file mode 100644 index 8cd76aa..0000000 Binary files a/resources/images/serverless-rust-trace-view-warm-function-1.thumbnail.png and /dev/null differ diff --git a/resources/images/serverless-rust-trace-view-warm-function-2.thumbnail.png b/resources/images/serverless-rust-trace-view-warm-function-2.thumbnail.png deleted file mode 100644 index ba00cac..0000000 Binary files a/resources/images/serverless-rust-trace-view-warm-function-2.thumbnail.png and /dev/null differ diff --git a/resources/images/serverless-rust-usual-traces-overview.thumbnail.png b/resources/images/serverless-rust-usual-traces-overview.thumbnail.png deleted file mode 100644 index 050efe1..0000000 Binary files a/resources/images/serverless-rust-usual-traces-overview.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-1-account-overview.thumbnail.png b/resources/images/the-stack-part-1-account-overview.thumbnail.png deleted file mode 100644 index f5b4a35..0000000 Binary files a/resources/images/the-stack-part-1-account-overview.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-1-kms-error.thumbnail.png b/resources/images/the-stack-part-1-kms-error.thumbnail.png deleted file mode 100644 index df19518..0000000 Binary files a/resources/images/the-stack-part-1-kms-error.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-1-kms-step-1.thumbnail.png b/resources/images/the-stack-part-1-kms-step-1.thumbnail.png deleted file mode 100644 index 9478c41..0000000 Binary files a/resources/images/the-stack-part-1-kms-step-1.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-1-user-portal-url.thumbnail.png b/resources/images/the-stack-part-1-user-portal-url.thumbnail.png deleted file mode 100644 index cf94e1b..0000000 Binary files a/resources/images/the-stack-part-1-user-portal-url.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-2-cloudformation.thumbnail.png b/resources/images/the-stack-part-2-cloudformation.thumbnail.png deleted file mode 100644 index 51a9825..0000000 Binary files a/resources/images/the-stack-part-2-cloudformation.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-2-deployment-flow.thumbnail.png b/resources/images/the-stack-part-2-deployment-flow.thumbnail.png deleted file mode 100644 index 4fa06db..0000000 Binary files a/resources/images/the-stack-part-2-deployment-flow.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-2-environment-configuration.thumbnail.png b/resources/images/the-stack-part-2-environment-configuration.thumbnail.png deleted file mode 100644 index 0fb05d6..0000000 Binary files a/resources/images/the-stack-part-2-environment-configuration.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-2-environment-overview.thumbnail.png b/resources/images/the-stack-part-2-environment-overview.thumbnail.png deleted file mode 100644 index cff7baa..0000000 Binary files a/resources/images/the-stack-part-2-environment-overview.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-2-trigger-bootstrap-workflow.thumbnail.png b/resources/images/the-stack-part-2-trigger-bootstrap-workflow.thumbnail.png deleted file mode 100644 index e5ab5f2..0000000 Binary files a/resources/images/the-stack-part-2-trigger-bootstrap-workflow.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-3-artifacts.thumbnail.png b/resources/images/the-stack-part-3-artifacts.thumbnail.png deleted file mode 100644 index d2bb4a0..0000000 Binary files a/resources/images/the-stack-part-3-artifacts.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-3-ui-app-skeleton.thumbnail.png b/resources/images/the-stack-part-3-ui-app-skeleton.thumbnail.png deleted file mode 100644 index fa5c442..0000000 Binary files a/resources/images/the-stack-part-3-ui-app-skeleton.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-3-ui-internal-skeleton.thumbnail.png b/resources/images/the-stack-part-3-ui-internal-skeleton.thumbnail.png deleted file mode 100644 index 639d047..0000000 Binary files a/resources/images/the-stack-part-3-ui-internal-skeleton.thumbnail.png and /dev/null differ diff --git a/resources/images/the-stack-part-3-updated-flow.thumbnail.png b/resources/images/the-stack-part-3-updated-flow.thumbnail.png deleted file mode 100644 index eef3f72..0000000 Binary files a/resources/images/the-stack-part-3-updated-flow.thumbnail.png and /dev/null differ diff --git a/resources/images/unoptimized/.gitkeep b/resources/images/unoptimized/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/resources/images/unoptimized/thumbnails/.gitkeep b/resources/images/unoptimized/thumbnails/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/resources/images/using-capacitor-plugins-from-rust-wasm-iphone.thumbnail.png b/resources/images/using-capacitor-plugins-from-rust-wasm-iphone.thumbnail.png deleted file mode 100644 index 868bcf2..0000000 Binary files a/resources/images/using-capacitor-plugins-from-rust-wasm-iphone.thumbnail.png and /dev/null differ diff --git a/resources/images/using-capacitor-plugins-from-rust-wasm-web.thumbnail.png b/resources/images/using-capacitor-plugins-from-rust-wasm-web.thumbnail.png deleted file mode 100644 index aae16f3..0000000 Binary files a/resources/images/using-capacitor-plugins-from-rust-wasm-web.thumbnail.png and /dev/null differ diff --git a/resources/images/visual-studio-on-ipad-code-file.thumbnail.png b/resources/images/visual-studio-on-ipad-code-file.thumbnail.png deleted file mode 100644 index 5c5538d..0000000 Binary files a/resources/images/visual-studio-on-ipad-code-file.thumbnail.png and /dev/null differ diff --git a/resources/images/visual-studio-on-ipad-welcome.thumbnail.png b/resources/images/visual-studio-on-ipad-welcome.thumbnail.png deleted file mode 100644 index 78dcbe2..0000000 Binary files a/resources/images/visual-studio-on-ipad-welcome.thumbnail.png and /dev/null differ diff --git a/resources/js/mermaid.min.js b/resources/js/mermaid.min.js deleted file mode 100644 index 1d3c0fb..0000000 --- a/resources/js/mermaid.min.js +++ /dev/null @@ -1,1284 +0,0 @@ -(function(ln,Wr){typeof exports=="object"&&typeof module<"u"?module.exports=Wr():typeof define=="function"&&define.amd?define(Wr):(ln=typeof globalThis<"u"?globalThis:ln||self,ln.mermaid=Wr())})(this,function(){"use strict";var XY=Object.defineProperty;var KY=(ln,Wr,Bi)=>Wr in ln?XY(ln,Wr,{enumerable:!0,configurable:!0,writable:!0,value:Bi}):ln[Wr]=Bi;var za=(ln,Wr,Bi)=>(KY(ln,typeof Wr!="symbol"?Wr+"":Wr,Bi),Bi);var ln=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function Wr(t){throw new Error('Could not dynamically require "'+t+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var Bi={exports:{}};(function(t,e){(function(r,n){t.exports=n()})(ln,function(){var r;function n(){return r.apply(null,arguments)}function i(h){return h instanceof Array||Object.prototype.toString.call(h)==="[object Array]"}function s(h){return h!=null&&Object.prototype.toString.call(h)==="[object Object]"}function a(h,y){return Object.prototype.hasOwnProperty.call(h,y)}function o(h){if(Object.getOwnPropertyNames)return Object.getOwnPropertyNames(h).length===0;for(var y in h)if(a(h,y))return;return 1}function l(h){return h===void 0}function u(h){return typeof h=="number"||Object.prototype.toString.call(h)==="[object Number]"}function d(h){return h instanceof Date||Object.prototype.toString.call(h)==="[object Date]"}function f(h,y){for(var _=[],S=h.length,R=0;R>>0,S=0;S<_;S++)if(S in y&&h.call(this,y[S],S,y))return!0;return!1},E=n.momentProperties=[],I=!1;function V(h,y){var _,S,R,it=E.length;if(l(y._isAMomentObject)||(h._isAMomentObject=y._isAMomentObject),l(y._i)||(h._i=y._i),l(y._f)||(h._f=y._f),l(y._l)||(h._l=y._l),l(y._strict)||(h._strict=y._strict),l(y._tzm)||(h._tzm=y._tzm),l(y._isUTC)||(h._isUTC=y._isUTC),l(y._offset)||(h._offset=y._offset),l(y._pf)||(h._pf=b(y)),l(y._locale)||(h._locale=y._locale),0Jt(h)?(it=h+1,mt-Jt(h)):(it=h,mt);return{year:it,dayOfYear:Ct}}function Be(h,y,_){var S,R,it=De(h.year(),y,_),it=Math.floor((h.dayOfYear()-it-1)/7)+1;return it<1?S=it+Re(R=h.year()-1,y,_):it>Re(h.year(),y,_)?(S=it-Re(h.year(),y,_),R=h.year()+1):(R=h.year(),S=it),{week:S,year:R}}function Re(h,R,_){var S=De(h,R,_),R=De(h+1,R,_);return(Jt(h)-S+R)/7}j("w",["ww",2],"wo","week"),j("W",["WW",2],"Wo","isoWeek"),ut("week","w"),ut("isoWeek","W"),P("week",5),P("isoWeek",5),st("w",U),st("ww",U,ce),st("W",U),st("WW",U,ce),Ee(["w","ww","W","WW"],function(h,y,_,S){y[S.substr(0,1)]=F(h)});function Pe(h,y){return h.slice(y,7).concat(h.slice(0,y))}j("d",0,"do","day"),j("dd",0,0,function(h){return this.localeData().weekdaysMin(this,h)}),j("ddd",0,0,function(h){return this.localeData().weekdaysShort(this,h)}),j("dddd",0,0,function(h){return this.localeData().weekdays(this,h)}),j("e",0,0,"weekday"),j("E",0,0,"isoWeekday"),ut("day","d"),ut("weekday","e"),ut("isoWeekday","E"),P("day",11),P("weekday",11),P("isoWeekday",11),st("d",U),st("e",U),st("E",U),st("dd",function(h,y){return y.weekdaysMinRegex(h)}),st("ddd",function(h,y){return y.weekdaysShortRegex(h)}),st("dddd",function(h,y){return y.weekdaysRegex(h)}),Ee(["dd","ddd","dddd"],function(h,y,_,S){S=_._locale.weekdaysParse(h,S,_._strict),S!=null?y.d=S:b(_).invalidWeekday=h}),Ee(["d","e","E"],function(h,y,_,S){y[S]=F(h)});var Ye="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Ie="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),$e="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ar=pt,zt=pt,Z=pt;function At(){function h(It,ue){return ue.length-It.length}for(var y,_,S,R=[],it=[],mt=[],Ct=[],Pt=0;Pt<7;Pt++)S=g([2e3,1]).day(Pt),y=Xt(this.weekdaysMin(S,"")),_=Xt(this.weekdaysShort(S,"")),S=Xt(this.weekdays(S,"")),R.push(y),it.push(_),mt.push(S),Ct.push(y),Ct.push(_),Ct.push(S);R.sort(h),it.sort(h),mt.sort(h),Ct.sort(h),this._weekdaysRegex=new RegExp("^("+Ct.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+mt.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+it.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+R.join("|")+")","i")}function Nt(){return this.hours()%12||12}function pe(h,y){j(h,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),y)})}function Me(h,y){return y._meridiemParse}j("H",["HH",2],0,"hour"),j("h",["hh",2],0,Nt),j("k",["kk",2],0,function(){return this.hours()||24}),j("hmm",0,0,function(){return""+Nt.apply(this)+Q(this.minutes(),2)}),j("hmmss",0,0,function(){return""+Nt.apply(this)+Q(this.minutes(),2)+Q(this.seconds(),2)}),j("Hmm",0,0,function(){return""+this.hours()+Q(this.minutes(),2)}),j("Hmmss",0,0,function(){return""+this.hours()+Q(this.minutes(),2)+Q(this.seconds(),2)}),pe("a",!0),pe("A",!1),ut("hour","h"),P("hour",13),st("a",Me),st("A",Me),st("H",U),st("h",U),st("k",U),st("HH",U,ce),st("hh",U,ce),st("kk",U,ce),st("hmm",Yt),st("hmmss",et),st("Hmm",Yt),st("Hmmss",et),Gt(["H","HH"],ft),Gt(["k","kk"],function(h,y,_){h=F(h),y[ft]=h===24?0:h}),Gt(["a","A"],function(h,y,_){_._isPm=_._locale.isPM(h),_._meridiem=h}),Gt(["h","hh"],function(h,y,_){y[ft]=F(h),b(_).bigHour=!0}),Gt("hmm",function(h,y,_){var S=h.length-2;y[ft]=F(h.substr(0,S)),y[Lt]=F(h.substr(S)),b(_).bigHour=!0}),Gt("hmmss",function(h,y,_){var S=h.length-4,R=h.length-2;y[ft]=F(h.substr(0,S)),y[Lt]=F(h.substr(S,2)),y[bt]=F(h.substr(R)),b(_).bigHour=!0}),Gt("Hmm",function(h,y,_){var S=h.length-2;y[ft]=F(h.substr(0,S)),y[Lt]=F(h.substr(S))}),Gt("Hmmss",function(h,y,_){var S=h.length-4,R=h.length-2;y[ft]=F(h.substr(0,S)),y[Lt]=F(h.substr(S,2)),y[bt]=F(h.substr(R))}),pt=O("Hours",!0);var Le,We={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:ee,monthsShort:fe,week:{dow:0,doy:6},weekdays:Ye,weekdaysMin:$e,weekdaysShort:Ie,meridiemParse:/[ap]\.?m?\.?/i},ve={},Ne={};function Xi(h){return h&&h.toLowerCase().replace("_","-")}function Ki(h){for(var y,_,S,R,it=0;it=y&&function(mt,Ct){for(var Pt=Math.min(mt.length,Ct.length),It=0;It=y-1)break;y--}it++}return Le}function zr(h){var y;if(ve[h]===void 0&&!0&&t&&t.exports&&h.match("^[^/\\\\]*$")!=null)try{y=Le._abbr,Wr("./locale/"+h),qt(y)}catch{ve[h]=null}return ve[h]}function qt(h,y){return h&&((y=l(y)?oe(h):te(h,y))?Le=y:typeof console<"u"&&console.warn&&console.warn("Locale "+h+" not found. Did you forget to load it?")),Le._abbr}function te(h,y){if(y===null)return delete ve[h],null;var _,S=We;if(y.abbr=h,ve[h]!=null)D("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),S=ve[h]._config;else if(y.parentLocale!=null)if(ve[y.parentLocale]!=null)S=ve[y.parentLocale]._config;else{if((_=zr(y.parentLocale))==null)return Ne[y.parentLocale]||(Ne[y.parentLocale]=[]),Ne[y.parentLocale].push({name:h,config:y}),null;S=_._config}return ve[h]=new M(v(S,y)),Ne[h]&&Ne[h].forEach(function(R){te(R.name,R.config)}),qt(h),ve[h]}function oe(h){var y;if(!(h=h&&h._locale&&h._locale._abbr?h._locale._abbr:h))return Le;if(!i(h)){if(y=zr(h))return y;h=[h]}return Ki(h)}function Rt(h){var y=h._a;return y&&b(h).overflow===-2&&(y=y[Ft]<0||11dt(y[Vt],y[Ft])?xt:y[ft]<0||24Re(it,Pt,It)?b(S)._overflowWeeks=!0:ue!=null?b(S)._overflowWeekday=!0:(re=ae(it,mt,Ct,Pt,It),S._a[Vt]=re.year,S._dayOfYear=re.dayOfYear)),h._dayOfYear!=null&&(R=Qn(h._a[Vt],_[Vt]),(h._dayOfYear>Jt(R)||h._dayOfYear===0)&&(b(h)._overflowDayOfYear=!0),ue=Xr(R,0,h._dayOfYear),h._a[Ft]=ue.getUTCMonth(),h._a[xt]=ue.getUTCDate()),y=0;y<3&&h._a[y]==null;++y)h._a[y]=He[y]=_[y];for(;y<7;y++)h._a[y]=He[y]=h._a[y]==null?y===2?1:0:h._a[y];h._a[ft]===24&&h._a[Lt]===0&&h._a[bt]===0&&h._a[Ut]===0&&(h._nextDay=!0,h._a[ft]=0),h._d=(h._useUTC?Xr:Nn).apply(null,He),it=h._useUTC?h._d.getUTCDay():h._d.getDay(),h._tzm!=null&&h._d.setUTCMinutes(h._d.getUTCMinutes()-h._tzm),h._nextDay&&(h._a[ft]=24),h._w&&h._w.d!==void 0&&h._w.d!==it&&(b(h).weekdayMismatch=!0)}}function ll(h){if(h._f===n.ISO_8601)Gu(h);else if(h._f===n.RFC_2822)Zn(h);else{h._a=[],b(h).empty=!0;for(var y,_,S,R,it,mt=""+h._i,Ct=mt.length,Pt=0,It=rt(h._f,h._locale).match(x)||[],ue=It.length,re=0;reh.valueOf():h.valueOf()"}),C.toJSON=function(){return this.isValid()?this.toISOString():null},C.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},C.unix=function(){return Math.floor(this.valueOf()/1e3)},C.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},C.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},C.eraName=function(){for(var h,y=this.localeData().eras(),_=0,S=y.length;_this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},C.isLocal=function(){return!!this.isValid()&&!this._isUTC},C.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},C.isUtc=cb,C.isUTC=cb,C.zoneAbbr=function(){return this._isUTC?"UTC":""},C.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},C.dates=W("dates accessor is deprecated. Use date instead.",ts),C.months=W("months accessor is deprecated. Use month instead",se),C.years=W("years accessor is deprecated. Use year instead",Ku),C.zone=W("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(h,y){return h!=null?(this.utcOffset(h=typeof h!="string"?-h:h,y),this):-this.utcOffset()}),C.isDSTShifted=W("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var h,y={};return V(y,this),(y=qu(y))._a?(h=(y._isUTC?g:Se)(y._a),this._isDSTShifted=this.isValid()&&0{},debug:(...t)=>{},info:(...t)=>{},warn:(...t)=>{},error:(...t)=>{},fatal:(...t)=>{}},Qu=function(t="fatal"){let e=Jn.fatal;typeof t=="string"?(t=t.toLowerCase(),t in Jn&&(e=Jn[t])):typeof t=="number"&&(e=t),B.trace=()=>{},B.debug=()=>{},B.info=()=>{},B.warn=()=>{},B.error=()=>{},B.fatal=()=>{},e<=Jn.fatal&&(B.fatal=console.error?console.error.bind(console,Qr("FATAL"),"color: orange"):console.log.bind(console,"\x1B[35m",Qr("FATAL"))),e<=Jn.error&&(B.error=console.error?console.error.bind(console,Qr("ERROR"),"color: orange"):console.log.bind(console,"\x1B[31m",Qr("ERROR"))),e<=Jn.warn&&(B.warn=console.warn?console.warn.bind(console,Qr("WARN"),"color: orange"):console.log.bind(console,"\x1B[33m",Qr("WARN"))),e<=Jn.info&&(B.info=console.info?console.info.bind(console,Qr("INFO"),"color: lightblue"):console.log.bind(console,"\x1B[34m",Qr("INFO"))),e<=Jn.debug&&(B.debug=console.debug?console.debug.bind(console,Qr("DEBUG"),"color: lightgreen"):console.log.bind(console,"\x1B[32m",Qr("DEBUG"))),e<=Jn.trace&&(B.trace=console.debug?console.debug.bind(console,Qr("TRACE"),"color: lightgreen"):console.log.bind(console,"\x1B[32m",Qr("TRACE")))},Qr=t=>`%c${cn().format("ss.SSS")} : ${t} : `;var Ju={};Object.defineProperty(Ju,"__esModule",{value:!0});var ti=Ju.sanitizeUrl=void 0,Eb=/^([^\w]*)(javascript|data|vbscript)/im,Cb=/&#(\w+)(^\w|;)?/g,Sb=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim,Ab=/^([^:]+):/gm,Lb=[".","/"];function Db(t){return Lb.indexOf(t[0])>-1}function Mb(t){return t.replace(Cb,function(e,r){return String.fromCharCode(r)})}function Nb(t){var e=Mb(t||"").replace(Sb,"").trim();if(!e)return"about:blank";if(Db(e))return e;var r=e.match(Ab);if(!r)return e;var n=r[0];return Eb.test(n)?"about:blank":e}ti=Ju.sanitizeUrl=Nb;function ul(t,e){return t==null||e==null?NaN:te?1:t>=e?0:NaN}function Bb(t,e){return t==null||e==null?NaN:et?1:e>=t?0:NaN}function th(t){let e,r,n;t.length!==2?(e=ul,r=(o,l)=>ul(t(o),l),n=(o,l)=>t(o)-l):(e=t===ul||t===Bb?t:Ib,r=t,n=t);function i(o,l,u=0,d=o.length){if(u>>1;r(o[f],l)<0?u=f+1:d=f}while(u>>1;r(o[f],l)<=0?u=f+1:d=f}while(uu&&n(o[f-1],l)>-n(o[f],l)?f-1:f}return{left:i,center:a,right:s}}function Ib(){return 0}function Ob(t){return t===null?NaN:+t}const Rb=th(ul).right;th(Ob).center;const Fb=Rb;class Hd extends Map{constructor(e,r=Vb){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:r}}),e!=null)for(const[n,i]of e)this.set(n,i)}get(e){return super.get(Gd(this,e))}has(e){return super.has(Gd(this,e))}set(e,r){return super.set(Pb(this,e),r)}delete(e){return super.delete(Yb(this,e))}}function Gd({_intern:t,_key:e},r){const n=e(r);return t.has(n)?t.get(n):r}function Pb({_intern:t,_key:e},r){const n=e(r);return t.has(n)?t.get(n):(t.set(n,r),r)}function Yb({_intern:t,_key:e},r){const n=e(r);return t.has(n)&&(r=t.get(n),t.delete(n)),r}function Vb(t){return t!==null&&typeof t=="object"?t.valueOf():t}var eh=Math.sqrt(50),rh=Math.sqrt(10),nh=Math.sqrt(2);function Ub(t,e,r){var n,i=-1,s,a,o;if(e=+e,t=+t,r=+r,t===e&&r>0)return[t];if((n=e0){let l=Math.round(t/o),u=Math.round(e/o);for(l*oe&&--u,a=new Array(s=u-l+1);++ie&&--u,a=new Array(s=u-l+1);++i=0?(s>=eh?10:s>=rh?5:s>=nh?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(s>=eh?10:s>=rh?5:s>=nh?2:1)}function ih(t,e,r){var n=Math.abs(e-t)/Math.max(0,r),i=Math.pow(10,Math.floor(Math.log(n)/Math.LN10)),s=n/i;return s>=eh?i*=10:s>=rh?i*=5:s>=nh&&(i*=2),e=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r=i)&&(r=i)}return r}function Wb(t,e){let r;if(e===void 0)for(const n of t)n!=null&&(r>n||r===void 0&&n>=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r>i||r===void 0&&i>=i)&&(r=i)}return r}function Hb(t){return t}var hl=1,sh=2,ah=3,dl=4,jd=1e-6;function Gb(t){return"translate("+t+",0)"}function qb(t){return"translate(0,"+t+")"}function jb(t){return e=>+t(e)}function $b(t,e){return e=Math.max(0,t.bandwidth()-e*2)/2,t.round()&&(e=Math.round(e)),r=>+t(r)+e}function Xb(){return!this.__axis}function $d(t,e){var r=[],n=null,i=null,s=6,a=6,o=3,l=typeof window<"u"&&window.devicePixelRatio>1?0:.5,u=t===hl||t===dl?-1:1,d=t===dl||t===sh?"x":"y",f=t===hl||t===ah?Gb:qb;function p(g){var b=n==null?e.ticks?e.ticks.apply(e,r):e.domain():n,k=i==null?e.tickFormat?e.tickFormat.apply(e,r):Hb:i,T=Math.max(s,0)+o,L=e.range(),E=+L[0]+l,I=+L[L.length-1]+l,V=(e.bandwidth?$b:jb)(e.copy(),l),H=g.selection?g.selection():g,G=H.selectAll(".domain").data([null]),Y=H.selectAll(".tick").data(b,e).order(),W=Y.exit(),N=Y.enter().append("g").attr("class","tick"),D=Y.select("line"),w=Y.select("text");G=G.merge(G.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),Y=Y.merge(N),D=D.merge(N.append("line").attr("stroke","currentColor").attr(d+"2",u*s)),w=w.merge(N.append("text").attr("fill","currentColor").attr(d,u*T).attr("dy",t===hl?"0em":t===ah?"0.71em":"0.32em")),g!==H&&(G=G.transition(g),Y=Y.transition(g),D=D.transition(g),w=w.transition(g),W=W.transition(g).attr("opacity",jd).attr("transform",function(v){return isFinite(v=V(v))?f(v+l):this.getAttribute("transform")}),N.attr("opacity",jd).attr("transform",function(v){var M=this.parentNode.__axis;return f((M&&isFinite(M=M(v))?M:V(v))+l)})),W.remove(),G.attr("d",t===dl||t===sh?a?"M"+u*a+","+E+"H"+l+"V"+I+"H"+u*a:"M"+l+","+E+"V"+I:a?"M"+E+","+u*a+"V"+l+"H"+I+"V"+u*a:"M"+E+","+l+"H"+I),Y.attr("opacity",1).attr("transform",function(v){return f(V(v)+l)}),D.attr(d+"2",u*s),w.attr(d,u*T).text(k),H.filter(Xb).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===sh?"start":t===dl?"end":"middle"),H.each(function(){this.__axis=V})}return p.scale=function(g){return arguments.length?(e=g,p):e},p.ticks=function(){return r=Array.from(arguments),p},p.tickArguments=function(g){return arguments.length?(r=g==null?[]:Array.from(g),p):r.slice()},p.tickValues=function(g){return arguments.length?(n=g==null?null:Array.from(g),p):n&&n.slice()},p.tickFormat=function(g){return arguments.length?(i=g,p):i},p.tickSize=function(g){return arguments.length?(s=a=+g,p):s},p.tickSizeInner=function(g){return arguments.length?(s=+g,p):s},p.tickSizeOuter=function(g){return arguments.length?(a=+g,p):a},p.tickPadding=function(g){return arguments.length?(o=+g,p):o},p.offset=function(g){return arguments.length?(l=+g,p):l},p}function Kb(t){return $d(hl,t)}function Zb(t){return $d(ah,t)}var Qb={value:()=>{}};function Xd(){for(var t=0,e=arguments.length,r={},n;t=0&&(n=r.slice(i+1),r=r.slice(0,i)),r&&!e.hasOwnProperty(r))throw new Error("unknown type: "+r);return{type:r,name:n}})}fl.prototype=Xd.prototype={constructor:fl,on:function(t,e){var r=this._,n=Jb(t+"",r),i,s=-1,a=n.length;if(arguments.length<2){for(;++s0)for(var r=new Array(i),n=0,i,s;n=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),Zd.hasOwnProperty(e)?{space:Zd[e],local:t}:t}function e5(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===oh&&e.documentElement.namespaceURI===oh?e.createElement(t):e.createElementNS(r,t)}}function r5(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Qd(t){var e=pl(t);return(e.local?r5:e5)(e)}function n5(){}function lh(t){return t==null?n5:function(){return this.querySelector(t)}}function i5(t){typeof t!="function"&&(t=lh(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i=I&&(I=E+1);!(H=T[I])&&++I=0;)(a=n[i])&&(s&&a.compareDocumentPosition(s)^4&&s.parentNode.insertBefore(a,s),s=a);return this}function A5(t){t||(t=L5);function e(f,p){return f&&p?t(f.__data__,p.__data__):!f-!p}for(var r=this._groups,n=r.length,i=new Array(n),s=0;se?1:t>=e?0:NaN}function D5(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function M5(){return Array.from(this)}function N5(){for(var t=this._groups,e=0,r=t.length;e1?this.each((e==null?W5:typeof e=="function"?G5:H5)(t,e,r==null?"":r)):Vs(this.node(),t)}function Vs(t,e){return t.style.getPropertyValue(e)||sf(t).getComputedStyle(t,null).getPropertyValue(e)}function j5(t){return function(){delete this[t]}}function $5(t,e){return function(){this[t]=e}}function X5(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function K5(t,e){return arguments.length>1?this.each((e==null?j5:typeof e=="function"?X5:$5)(t,e)):this.node()[t]}function af(t){return t.trim().split(/^|\s+/)}function ch(t){return t.classList||new of(t)}function of(t){this._node=t,this._names=af(t.getAttribute("class")||"")}of.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function lf(t,e){for(var r=ch(t),n=-1,i=e.length;++n=0&&(r=e.slice(n+1),e=e.slice(0,n)),{type:e,name:r}})}function T_(t){return function(){var e=this.__on;if(!!e){for(var r=0,n=-1,i=e.length,s;r>8&15|e>>4&240,e>>4&15|e&240,(e&15)<<4|e&15,1):r===8?bl(e>>24&255,e>>16&255,e>>8&255,(e&255)/255):r===4?bl(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|e&240,((e&15)<<4|e&15)/255):null):(e=B_.exec(t))?new vr(e[1],e[2],e[3],1):(e=I_.exec(t))?new vr(e[1]*255/100,e[2]*255/100,e[3]*255/100,1):(e=O_.exec(t))?bl(e[1],e[2],e[3],e[4]):(e=R_.exec(t))?bl(e[1]*255/100,e[2]*255/100,e[3]*255/100,e[4]):(e=F_.exec(t))?bf(e[1],e[2]/100,e[3]/100,1):(e=P_.exec(t))?bf(e[1],e[2]/100,e[3]/100,e[4]):hf.hasOwnProperty(t)?pf(hf[t]):t==="transparent"?new vr(NaN,NaN,NaN,0):null}function pf(t){return new vr(t>>16&255,t>>8&255,t&255,1)}function bl(t,e,r,n){return n<=0&&(t=e=r=NaN),new vr(t,e,r,n)}function gf(t){return t instanceof es||(t=rs(t)),t?(t=t.rgb(),new vr(t.r,t.g,t.b,t.opacity)):new vr}function dh(t,e,r,n){return arguments.length===1?gf(t):new vr(t,e,r,n==null?1:n)}function vr(t,e,r,n){this.r=+t,this.g=+e,this.b=+r,this.opacity=+n}Ha(vr,dh,yl(es,{brighter(t){return t=t==null?ml:Math.pow(ml,t),new vr(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=t==null?Ga:Math.pow(Ga,t),new vr(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new vr(ns(this.r),ns(this.g),ns(this.b),_l(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:yf,formatHex:yf,formatHex8:U_,formatRgb:mf,toString:mf}));function yf(){return`#${is(this.r)}${is(this.g)}${is(this.b)}`}function U_(){return`#${is(this.r)}${is(this.g)}${is(this.b)}${is((isNaN(this.opacity)?1:this.opacity)*255)}`}function mf(){const t=_l(this.opacity);return`${t===1?"rgb(":"rgba("}${ns(this.r)}, ${ns(this.g)}, ${ns(this.b)}${t===1?")":`, ${t})`}`}function _l(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function ns(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function is(t){return t=ns(t),(t<16?"0":"")+t.toString(16)}function bf(t,e,r,n){return n<=0?t=e=r=NaN:r<=0||r>=1?t=e=NaN:e<=0&&(t=NaN),new un(t,e,r,n)}function _f(t){if(t instanceof un)return new un(t.h,t.s,t.l,t.opacity);if(t instanceof es||(t=rs(t)),!t)return new un;if(t instanceof un)return t;t=t.rgb();var e=t.r/255,r=t.g/255,n=t.b/255,i=Math.min(e,r,n),s=Math.max(e,r,n),a=NaN,o=s-i,l=(s+i)/2;return o?(e===s?a=(r-n)/o+(r0&&l<1?0:a,new un(a,o,l,t.opacity)}function z_(t,e,r,n){return arguments.length===1?_f(t):new un(t,e,r,n==null?1:n)}function un(t,e,r,n){this.h=+t,this.s=+e,this.l=+r,this.opacity=+n}Ha(un,z_,yl(es,{brighter(t){return t=t==null?ml:Math.pow(ml,t),new un(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=t==null?Ga:Math.pow(Ga,t),new un(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,e=isNaN(t)||isNaN(this.s)?0:this.s,r=this.l,n=r+(r<.5?r:1-r)*e,i=2*r-n;return new vr(fh(t>=240?t-240:t+120,i,n),fh(t,i,n),fh(t<120?t+240:t-120,i,n),this.opacity)},clamp(){return new un(xf(this.h),xl(this.s),xl(this.l),_l(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=_l(this.opacity);return`${t===1?"hsl(":"hsla("}${xf(this.h)}, ${xl(this.s)*100}%, ${xl(this.l)*100}%${t===1?")":`, ${t})`}`}}));function xf(t){return t=(t||0)%360,t<0?t+360:t}function xl(t){return Math.max(0,Math.min(1,t||0))}function fh(t,e,r){return(t<60?e+(r-e)*t/60:t<180?r:t<240?e+(r-e)*(240-t)/60:e)*255}const W_=Math.PI/180,H_=180/Math.PI,vl=18,vf=.96422,kf=1,wf=.82521,Tf=4/29,zs=6/29,Ef=3*zs*zs,G_=zs*zs*zs;function Cf(t){if(t instanceof Fn)return new Fn(t.l,t.a,t.b,t.opacity);if(t instanceof ei)return Sf(t);t instanceof vr||(t=gf(t));var e=mh(t.r),r=mh(t.g),n=mh(t.b),i=ph((.2225045*e+.7168786*r+.0606169*n)/kf),s,a;return e===r&&r===n?s=a=i:(s=ph((.4360747*e+.3850649*r+.1430804*n)/vf),a=ph((.0139322*e+.0971045*r+.7141733*n)/wf)),new Fn(116*i-16,500*(s-i),200*(i-a),t.opacity)}function q_(t,e,r,n){return arguments.length===1?Cf(t):new Fn(t,e,r,n==null?1:n)}function Fn(t,e,r,n){this.l=+t,this.a=+e,this.b=+r,this.opacity=+n}Ha(Fn,q_,yl(es,{brighter(t){return new Fn(this.l+vl*(t==null?1:t),this.a,this.b,this.opacity)},darker(t){return new Fn(this.l-vl*(t==null?1:t),this.a,this.b,this.opacity)},rgb(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,r=isNaN(this.b)?t:t-this.b/200;return e=vf*gh(e),t=kf*gh(t),r=wf*gh(r),new vr(yh(3.1338561*e-1.6168667*t-.4906146*r),yh(-.9787684*e+1.9161415*t+.033454*r),yh(.0719453*e-.2289914*t+1.4052427*r),this.opacity)}}));function ph(t){return t>G_?Math.pow(t,1/3):t/Ef+Tf}function gh(t){return t>zs?t*t*t:Ef*(t-Tf)}function yh(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function mh(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function j_(t){if(t instanceof ei)return new ei(t.h,t.c,t.l,t.opacity);if(t instanceof Fn||(t=Cf(t)),t.a===0&&t.b===0)return new ei(NaN,0()=>t;function Af(t,e){return function(r){return t+r*e}}function $_(t,e,r){return t=Math.pow(t,r),e=Math.pow(e,r)-t,r=1/r,function(n){return Math.pow(t+n*e,r)}}function X_(t,e){var r=e-t;return r?Af(t,r>180||r<-180?r-360*Math.round(r/360):r):kl(isNaN(t)?e:t)}function K_(t){return(t=+t)==1?ja:function(e,r){return r-e?$_(e,r,t):kl(isNaN(e)?r:e)}}function ja(t,e){var r=e-t;return r?Af(t,r):kl(isNaN(t)?e:t)}const wl=function t(e){var r=K_(e);function n(i,s){var a=r((i=dh(i)).r,(s=dh(s)).r),o=r(i.g,s.g),l=r(i.b,s.b),u=ja(i.opacity,s.opacity);return function(d){return i.r=a(d),i.g=o(d),i.b=l(d),i.opacity=u(d),i+""}}return n.gamma=t,n}(1);function Z_(t,e){e||(e=[]);var r=t?Math.min(e.length,t.length):0,n=e.slice(),i;return function(s){for(i=0;ir&&(s=e.slice(r,s),o[a]?o[a]+=s:o[++a]=s),(n=n[0])===(i=i[0])?o[a]?o[a]+=i:o[++a]=i:(o[++a]=null,l.push({i:a,x:hn(n,i)})),r=xh.lastIndex;return r180?d+=360:d-u>180&&(u+=360),p.push({i:f.push(i(f)+"rotate(",null,n)-2,x:hn(u,d)})):d&&f.push(i(f)+"rotate("+d+n)}function o(u,d,f,p){u!==d?p.push({i:f.push(i(f)+"skewX(",null,n)-2,x:hn(u,d)}):d&&f.push(i(f)+"skewX("+d+n)}function l(u,d,f,p,g,b){if(u!==f||d!==p){var k=g.push(i(g)+"scale(",null,",",null,")");b.push({i:k-4,x:hn(u,f)},{i:k-2,x:hn(d,p)})}else(f!==1||p!==1)&&g.push(i(g)+"scale("+f+","+p+")")}return function(u,d){var f=[],p=[];return u=t(u),d=t(d),s(u.translateX,u.translateY,d.translateX,d.translateY,f,p),a(u.rotate,d.rotate,f,p),o(u.skewX,d.skewX,f,p),l(u.scaleX,u.scaleY,d.scaleX,d.scaleY,f,p),u=d=null,function(g){for(var b=-1,k=p.length,T;++b=0&&t._call.call(void 0,e),t=t._next;--Ws}function Rf(){ss=(Cl=Za.now())+Sl,Ws=$a=0;try{dx()}finally{Ws=0,px(),ss=0}}function fx(){var t=Za.now(),e=t-Cl;e>Bf&&(Sl-=e,Cl=t)}function px(){for(var t,e=El,r,n=1/0;e;)e._call?(n>e._time&&(n=e._time),t=e,e=e._next):(r=e._next,e._next=null,e=t?t._next=r:El=r);Ka=t,Th(n)}function Th(t){if(!Ws){$a&&($a=clearTimeout($a));var e=t-ss;e>24?(t<1/0&&($a=setTimeout(Rf,t-Za.now()-Sl)),Xa&&(Xa=clearInterval(Xa))):(Xa||(Cl=Za.now(),Xa=setInterval(fx,Bf)),Ws=1,If(Rf))}}function Ff(t,e,r){var n=new Al;return e=e==null?0:+e,n.restart(i=>{n.stop(),t(i+e)},e,r),n}var gx=Xd("start","end","cancel","interrupt"),yx=[],Pf=0,Yf=1,Eh=2,Ll=3,Vf=4,Ch=5,Dl=6;function Ml(t,e,r,n,i,s){var a=t.__transition;if(!a)t.__transition={};else if(r in a)return;mx(t,r,{name:e,index:n,group:i,on:gx,tween:yx,time:s.time,delay:s.delay,duration:s.duration,ease:s.ease,timer:null,state:Pf})}function Sh(t,e){var r=dn(t,e);if(r.state>Pf)throw new Error("too late; already scheduled");return r}function Pn(t,e){var r=dn(t,e);if(r.state>Ll)throw new Error("too late; already running");return r}function dn(t,e){var r=t.__transition;if(!r||!(r=r[e]))throw new Error("transition not found");return r}function mx(t,e,r){var n=t.__transition,i;n[e]=r,r.timer=Of(s,0,r.time);function s(u){r.state=Yf,r.timer.restart(a,r.delay,r.time),r.delay<=u&&a(u-r.delay)}function a(u){var d,f,p,g;if(r.state!==Yf)return l();for(d in n)if(g=n[d],g.name===r.name){if(g.state===Ll)return Ff(a);g.state===Vf?(g.state=Dl,g.timer.stop(),g.on.call("interrupt",t,t.__data__,g.index,g.group),delete n[d]):+dEh&&n.state=0&&(e=e.slice(0,r)),!e||e==="start"})}function $x(t,e,r){var n,i,s=jx(e)?Sh:Pn;return function(){var a=s(this,t),o=a.on;o!==n&&(i=(n=o).copy()).on(e,r),a.on=i}}function Xx(t,e){var r=this._id;return arguments.length<2?dn(this.node(),r).on.on(t):this.each($x(r,t,e))}function Kx(t){return function(){var e=this.parentNode;for(var r in this.__transition)if(+r!==t)return;e&&e.removeChild(this)}}function Zx(){return this.on("end.remove",Kx(this._id))}function Qx(t){var e=this._name,r=this._id;typeof t!="function"&&(t=lh(t));for(var n=this._groups,i=n.length,s=new Array(i),a=0;aas)if(!(Math.abs(d*o-l*u)>as)||!i)this._+="L"+(this._x1=t)+","+(this._y1=e);else{var p=r-s,g=n-a,b=o*o+l*l,k=p*p+g*g,T=Math.sqrt(b),L=Math.sqrt(f),E=i*Math.tan((Lh-Math.acos((b+f-k)/(2*T*L)))/2),I=E/L,V=E/T;Math.abs(I-1)>as&&(this._+="L"+(t+I*u)+","+(e+I*d)),this._+="A"+i+","+i+",0,0,"+ +(d*p>u*g)+","+(this._x1=t+V*o)+","+(this._y1=e+V*l)}},arc:function(t,e,r,n,i,s){t=+t,e=+e,r=+r,s=!!s;var a=r*Math.cos(n),o=r*Math.sin(n),l=t+a,u=e+o,d=1^s,f=s?n-i:i-n;if(r<0)throw new Error("negative radius: "+r);this._x1===null?this._+="M"+l+","+u:(Math.abs(this._x1-l)>as||Math.abs(this._y1-u)>as)&&(this._+="L"+l+","+u),r&&(f<0&&(f=f%Dh+Dh),f>w6?this._+="A"+r+","+r+",0,1,"+d+","+(t-a)+","+(e-o)+"A"+r+","+r+",0,1,"+d+","+(this._x1=l)+","+(this._y1=u):f>as&&(this._+="A"+r+","+r+",0,"+ +(f>=Lh)+","+d+","+(this._x1=t+r*Math.cos(i))+","+(this._y1=e+r*Math.sin(i))))},rect:function(t,e,r,n){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +r+"v"+ +n+"h"+-r+"Z"},toString:function(){return this._}};function T6(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.text()}function E6(t,e){return fetch(t,e).then(T6)}function C6(t){return(e,r)=>E6(e,r).then(n=>new DOMParser().parseFromString(n,t))}var S6=C6("image/svg+xml");function A6(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function Nl(t,e){if((r=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var r,n=t.slice(0,r);return[n.length>1?n[0]+n.slice(2):n,+t.slice(r+1)]}function Hs(t){return t=Nl(Math.abs(t)),t?t[1]:NaN}function L6(t,e){return function(r,n){for(var i=r.length,s=[],a=0,o=t[0],l=0;i>0&&o>0&&(l+o+1>n&&(o=Math.max(1,n-l)),s.push(r.substring(i-=o,i+o)),!((l+=o+1)>n));)o=t[a=(a+1)%t.length];return s.reverse().join(e)}}function D6(t){return function(e){return e.replace(/[0-9]/g,function(r){return t[+r]})}}var M6=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Bl(t){if(!(e=M6.exec(t)))throw new Error("invalid format: "+t);var e;return new Bh({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}Bl.prototype=Bh.prototype;function Bh(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}Bh.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function N6(t){t:for(var e=t.length,r=1,n=-1,i;r0&&(n=0);break}return n>0?t.slice(0,n)+t.slice(i+1):t}var Hf;function B6(t,e){var r=Nl(t,e);if(!r)return t+"";var n=r[0],i=r[1],s=i-(Hf=Math.max(-8,Math.min(8,Math.floor(i/3)))*3)+1,a=n.length;return s===a?n:s>a?n+new Array(s-a+1).join("0"):s>0?n.slice(0,s)+"."+n.slice(s):"0."+new Array(1-s).join("0")+Nl(t,Math.max(0,e+s-1))[0]}function Gf(t,e){var r=Nl(t,e);if(!r)return t+"";var n=r[0],i=r[1];return i<0?"0."+new Array(-i).join("0")+n:n.length>i+1?n.slice(0,i+1)+"."+n.slice(i+1):n+new Array(i-n.length+2).join("0")}const qf={"%":(t,e)=>(t*100).toFixed(e),b:t=>Math.round(t).toString(2),c:t=>t+"",d:A6,e:(t,e)=>t.toExponential(e),f:(t,e)=>t.toFixed(e),g:(t,e)=>t.toPrecision(e),o:t=>Math.round(t).toString(8),p:(t,e)=>Gf(t*100,e),r:Gf,s:B6,X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function jf(t){return t}var $f=Array.prototype.map,Xf=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function I6(t){var e=t.grouping===void 0||t.thousands===void 0?jf:L6($f.call(t.grouping,Number),t.thousands+""),r=t.currency===void 0?"":t.currency[0]+"",n=t.currency===void 0?"":t.currency[1]+"",i=t.decimal===void 0?".":t.decimal+"",s=t.numerals===void 0?jf:D6($f.call(t.numerals,String)),a=t.percent===void 0?"%":t.percent+"",o=t.minus===void 0?"\u2212":t.minus+"",l=t.nan===void 0?"NaN":t.nan+"";function u(f){f=Bl(f);var p=f.fill,g=f.align,b=f.sign,k=f.symbol,T=f.zero,L=f.width,E=f.comma,I=f.precision,V=f.trim,H=f.type;H==="n"?(E=!0,H="g"):qf[H]||(I===void 0&&(I=12),V=!0,H="g"),(T||p==="0"&&g==="=")&&(T=!0,p="0",g="=");var G=k==="$"?r:k==="#"&&/[boxX]/.test(H)?"0"+H.toLowerCase():"",Y=k==="$"?n:/[%p]/.test(H)?a:"",W=qf[H],N=/[defgprs%]/.test(H);I=I===void 0?6:/[gprs]/.test(H)?Math.max(1,Math.min(21,I)):Math.max(0,Math.min(20,I));function D(w){var v=G,M=Y,m,Q,x;if(H==="c")M=W(w)+M,w="";else{w=+w;var ct=w<0||1/w<0;if(w=isNaN(w)?l:W(Math.abs(w),I),V&&(w=N6(w)),ct&&+w==0&&b!=="+"&&(ct=!1),v=(ct?b==="("?b:o:b==="-"||b==="("?"":b)+v,M=(H==="s"?Xf[8+Hf/3]:"")+M+(ct&&b==="("?")":""),N){for(m=-1,Q=w.length;++mx||x>57){M=(x===46?i+w.slice(m+1):w.slice(m))+M,w=w.slice(0,m);break}}}E&&!T&&(w=e(w,1/0));var Dt=v.length+w.length+M.length,kt=Dt>1)+v+w+M+kt.slice(Dt);break;default:w=kt+v+w+M;break}return s(w)}return D.toString=function(){return f+""},D}function d(f,p){var g=u((f=Bl(f),f.type="f",f)),b=Math.max(-8,Math.min(8,Math.floor(Hs(p)/3)))*3,k=Math.pow(10,-b),T=Xf[8+b/3];return function(L){return g(k*L)+T}}return{format:u,formatPrefix:d}}var Il,Kf,Zf;O6({thousands:",",grouping:[3],currency:["$",""]});function O6(t){return Il=I6(t),Kf=Il.format,Zf=Il.formatPrefix,Il}function R6(t){return Math.max(0,-Hs(Math.abs(t)))}function F6(t,e){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(Hs(e)/3)))*3-Hs(Math.abs(t)))}function P6(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,Hs(e)-Hs(t))+1}function Ih(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}const Qf=Symbol("implicit");function Jf(){var t=new Hd,e=[],r=[],n=Qf;function i(s){let a=t.get(s);if(a===void 0){if(n!==Qf)return n;t.set(s,a=e.push(s)-1)}return r[a%r.length]}return i.domain=function(s){if(!arguments.length)return e.slice();e=[],t=new Hd;for(const a of s)t.has(a)||t.set(a,e.push(a)-1);return i},i.range=function(s){return arguments.length?(r=Array.from(s),i):r.slice()},i.unknown=function(s){return arguments.length?(n=s,i):n},i.copy=function(){return Jf(e,r).unknown(n)},Ih.apply(i,arguments),i}function Y6(t){return function(){return t}}function V6(t){return+t}var t2=[0,1];function Gs(t){return t}function Oh(t,e){return(e-=t=+t)?function(r){return(r-t)/e}:Y6(isNaN(e)?NaN:.5)}function U6(t,e){var r;return t>e&&(r=t,t=e,e=r),function(n){return Math.max(t,Math.min(e,n))}}function z6(t,e,r){var n=t[0],i=t[1],s=e[0],a=e[1];return i2?W6:z6,l=u=null,f}function f(p){return p==null||isNaN(p=+p)?s:(l||(l=o(t.map(n),e,r)))(n(a(p)))}return f.invert=function(p){return a(i((u||(u=o(e,t.map(n),hn)))(p)))},f.domain=function(p){return arguments.length?(t=Array.from(p,V6),d()):t.slice()},f.range=function(p){return arguments.length?(e=Array.from(p),d()):e.slice()},f.rangeRound=function(p){return e=Array.from(p),r=ix,d()},f.clamp=function(p){return arguments.length?(a=p?!0:Gs,d()):a!==Gs},f.interpolate=function(p){return arguments.length?(r=p,d()):r},f.unknown=function(p){return arguments.length?(s=p,f):s},function(p,g){return n=p,i=g,d()}}function r2(){return H6()(Gs,Gs)}function G6(t,e,r,n){var i=ih(t,e,r),s;switch(n=Bl(n==null?",f":n),n.type){case"s":{var a=Math.max(Math.abs(t),Math.abs(e));return n.precision==null&&!isNaN(s=F6(i,a))&&(n.precision=s),Zf(n,a)}case"":case"e":case"g":case"p":case"r":{n.precision==null&&!isNaN(s=P6(i,Math.max(Math.abs(t),Math.abs(e))))&&(n.precision=s-(n.type==="e"));break}case"f":case"%":{n.precision==null&&!isNaN(s=R6(i))&&(n.precision=s-(n.type==="%")*2);break}}return Kf(n)}function q6(t){var e=t.domain;return t.ticks=function(r){var n=e();return Ub(n[0],n[n.length-1],r==null?10:r)},t.tickFormat=function(r,n){var i=e();return G6(i[0],i[i.length-1],r==null?10:r,n)},t.nice=function(r){r==null&&(r=10);var n=e(),i=0,s=n.length-1,a=n[i],o=n[s],l,u,d=10;for(o0;){if(u=qd(a,o,r),u===l)return n[i]=a,n[s]=o,e(n);if(u>0)a=Math.floor(a/u)*u,o=Math.ceil(o/u)*u;else if(u<0)a=Math.ceil(a*u)/u,o=Math.floor(o*u)/u;else break;l=u}return t},t}function n2(){var t=r2();return t.copy=function(){return e2(t,n2())},Ih.apply(t,arguments),q6(t)}function j6(t,e){t=t.slice();var r=0,n=t.length-1,i=t[r],s=t[n],a;return s0))return l;do l.push(u=new Date(+s)),e(s,o),t(s);while(u=a)for(;t(a),!s(a);)a.setTime(a-1)},function(a,o){if(a>=a)if(o<0)for(;++o<=0;)for(;e(a,-1),!s(a););else for(;--o>=0;)for(;e(a,1),!s(a););})},r&&(i.count=function(s,a){return Rh.setTime(+s),Fh.setTime(+a),t(Rh),t(Fh),Math.floor(r(Rh,Fh))},i.every=function(s){return s=Math.floor(s),!isFinite(s)||!(s>0)?null:s>1?i.filter(n?function(a){return n(a)%s===0}:function(a){return i.count(0,a)%s===0}):i}),i}var Ol=dr(function(){},function(t,e){t.setTime(+t+e)},function(t,e){return e-t});Ol.every=function(t){return t=Math.floor(t),!isFinite(t)||!(t>0)?null:t>1?dr(function(e){e.setTime(Math.floor(e/t)*t)},function(e,r){e.setTime(+e+r*t)},function(e,r){return(r-e)/t}):Ol};const $6=Ol;Ol.range;const ii=1e3,Jr=ii*60,si=Jr*60,os=si*24,Ph=os*7,i2=os*30,Yh=os*365;var s2=dr(function(t){t.setTime(t-t.getMilliseconds())},function(t,e){t.setTime(+t+e*ii)},function(t,e){return(e-t)/ii},function(t){return t.getUTCSeconds()});const Qa=s2;s2.range;var a2=dr(function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*ii)},function(t,e){t.setTime(+t+e*Jr)},function(t,e){return(e-t)/Jr},function(t){return t.getMinutes()});const Rl=a2;a2.range;var o2=dr(function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*ii-t.getMinutes()*Jr)},function(t,e){t.setTime(+t+e*si)},function(t,e){return(e-t)/si},function(t){return t.getHours()});const Fl=o2;o2.range;var l2=dr(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*Jr)/os,t=>t.getDate()-1);const qs=l2;l2.range;function ls(t){return dr(function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},function(e,r){e.setDate(e.getDate()+r*7)},function(e,r){return(r-e-(r.getTimezoneOffset()-e.getTimezoneOffset())*Jr)/Ph})}var js=ls(0),Pl=ls(1),X6=ls(2),K6=ls(3),$s=ls(4),Z6=ls(5),Q6=ls(6);js.range,Pl.range,X6.range,K6.range,$s.range,Z6.range,Q6.range;var c2=dr(function(t){t.setDate(1),t.setHours(0,0,0,0)},function(t,e){t.setMonth(t.getMonth()+e)},function(t,e){return e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12},function(t){return t.getMonth()});const Yl=c2;c2.range;var Vh=dr(function(t){t.setMonth(0,1),t.setHours(0,0,0,0)},function(t,e){t.setFullYear(t.getFullYear()+e)},function(t,e){return e.getFullYear()-t.getFullYear()},function(t){return t.getFullYear()});Vh.every=function(t){return!isFinite(t=Math.floor(t))||!(t>0)?null:dr(function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},function(e,r){e.setFullYear(e.getFullYear()+r*t)})};const cs=Vh;Vh.range;var u2=dr(function(t){t.setUTCSeconds(0,0)},function(t,e){t.setTime(+t+e*Jr)},function(t,e){return(e-t)/Jr},function(t){return t.getUTCMinutes()});const J6=u2;u2.range;var h2=dr(function(t){t.setUTCMinutes(0,0,0)},function(t,e){t.setTime(+t+e*si)},function(t,e){return(e-t)/si},function(t){return t.getUTCHours()});const tv=h2;h2.range;var d2=dr(function(t){t.setUTCHours(0,0,0,0)},function(t,e){t.setUTCDate(t.getUTCDate()+e)},function(t,e){return(e-t)/os},function(t){return t.getUTCDate()-1});const Uh=d2;d2.range;function us(t){return dr(function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},function(e,r){e.setUTCDate(e.getUTCDate()+r*7)},function(e,r){return(r-e)/Ph})}var zh=us(0),Vl=us(1),ev=us(2),rv=us(3),Xs=us(4),nv=us(5),iv=us(6);zh.range,Vl.range,ev.range,rv.range,Xs.range,nv.range,iv.range;var f2=dr(function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)},function(t,e){t.setUTCMonth(t.getUTCMonth()+e)},function(t,e){return e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12},function(t){return t.getUTCMonth()});const sv=f2;f2.range;var Wh=dr(function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)},function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()},function(t){return t.getUTCFullYear()});Wh.every=function(t){return!isFinite(t=Math.floor(t))||!(t>0)?null:dr(function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},function(e,r){e.setUTCFullYear(e.getUTCFullYear()+r*t)})};const Ks=Wh;Wh.range;function p2(t,e,r,n,i,s){const a=[[Qa,1,ii],[Qa,5,5*ii],[Qa,15,15*ii],[Qa,30,30*ii],[s,1,Jr],[s,5,5*Jr],[s,15,15*Jr],[s,30,30*Jr],[i,1,si],[i,3,3*si],[i,6,6*si],[i,12,12*si],[n,1,os],[n,2,2*os],[r,1,Ph],[e,1,i2],[e,3,3*i2],[t,1,Yh]];function o(u,d,f){const p=dT).right(a,p);if(g===a.length)return t.every(ih(u/Yh,d/Yh,f));if(g===0)return $6.every(Math.max(ih(u,d,f),1));const[b,k]=a[p/a[g-1][2]53)return null;"w"in O||(O.w=1),"Z"in O?(J=Gh(Ja(O.y,0,1)),C=J.getUTCDay(),J=C>4||C===0?Vl.ceil(J):Vl(J),J=Uh.offset(J,(O.V-1)*7),O.y=J.getUTCFullYear(),O.m=J.getUTCMonth(),O.d=J.getUTCDate()+(O.w+6)%7):(J=Hh(Ja(O.y,0,1)),C=J.getDay(),J=C>4||C===0?Pl.ceil(J):Pl(J),J=qs.offset(J,(O.V-1)*7),O.y=J.getFullYear(),O.m=J.getMonth(),O.d=J.getDate()+(O.w+6)%7)}else("W"in O||"U"in O)&&("w"in O||(O.w="u"in O?O.u%7:"W"in O?1:0),C="Z"in O?Gh(Ja(O.y,0,1)).getUTCDay():Hh(Ja(O.y,0,1)).getDay(),O.m=0,O.d="W"in O?(O.w+6)%7+O.W*7-(C+5)%7:O.w+O.U*7-(C+6)%7);return"Z"in O?(O.H+=O.Z/100|0,O.M+=O.Z%100,Gh(O)):Hh(O)}}function W(z,X,F,O){for(var A=0,J=X.length,C=F.length,ht,U;A=C)return-1;if(ht=X.charCodeAt(A++),ht===37){if(ht=X.charAt(A++),U=H[ht in g2?X.charAt(A++):ht],!U||(O=U(z,F,O))<0)return-1}else if(ht!=F.charCodeAt(O++))return-1}return O}function N(z,X,F){var O=u.exec(X.slice(F));return O?(z.p=d.get(O[0].toLowerCase()),F+O[0].length):-1}function D(z,X,F){var O=g.exec(X.slice(F));return O?(z.w=b.get(O[0].toLowerCase()),F+O[0].length):-1}function w(z,X,F){var O=f.exec(X.slice(F));return O?(z.w=p.get(O[0].toLowerCase()),F+O[0].length):-1}function v(z,X,F){var O=L.exec(X.slice(F));return O?(z.m=E.get(O[0].toLowerCase()),F+O[0].length):-1}function M(z,X,F){var O=k.exec(X.slice(F));return O?(z.m=T.get(O[0].toLowerCase()),F+O[0].length):-1}function m(z,X,F){return W(z,e,X,F)}function Q(z,X,F){return W(z,r,X,F)}function x(z,X,F){return W(z,n,X,F)}function ct(z){return a[z.getDay()]}function Dt(z){return s[z.getDay()]}function kt(z){return l[z.getMonth()]}function j(z){return o[z.getMonth()]}function ot(z){return i[+(z.getHours()>=12)]}function rt(z){return 1+~~(z.getMonth()/3)}function nt(z){return a[z.getUTCDay()]}function ut(z){return s[z.getUTCDay()]}function _t(z){return l[z.getUTCMonth()]}function K(z){return o[z.getUTCMonth()]}function Ot(z){return i[+(z.getUTCHours()>=12)]}function P(z){return 1+~~(z.getUTCMonth()/3)}return{format:function(z){var X=G(z+="",I);return X.toString=function(){return z},X},parse:function(z){var X=Y(z+="",!1);return X.toString=function(){return z},X},utcFormat:function(z){var X=G(z+="",V);return X.toString=function(){return z},X},utcParse:function(z){var X=Y(z+="",!0);return X.toString=function(){return z},X}}}var g2={"-":"",_:" ",0:"0"},fr=/^\s*\d+/,cv=/^%/,uv=/[\\^$*+?|[\]().{}]/g;function Ae(t,e,r){var n=t<0?"-":"",i=(n?-t:t)+"",s=i.length;return n+(s[e.toLowerCase(),r]))}function dv(t,e,r){var n=fr.exec(e.slice(r,r+1));return n?(t.w=+n[0],r+n[0].length):-1}function fv(t,e,r){var n=fr.exec(e.slice(r,r+1));return n?(t.u=+n[0],r+n[0].length):-1}function pv(t,e,r){var n=fr.exec(e.slice(r,r+2));return n?(t.U=+n[0],r+n[0].length):-1}function gv(t,e,r){var n=fr.exec(e.slice(r,r+2));return n?(t.V=+n[0],r+n[0].length):-1}function yv(t,e,r){var n=fr.exec(e.slice(r,r+2));return n?(t.W=+n[0],r+n[0].length):-1}function y2(t,e,r){var n=fr.exec(e.slice(r,r+4));return n?(t.y=+n[0],r+n[0].length):-1}function m2(t,e,r){var n=fr.exec(e.slice(r,r+2));return n?(t.y=+n[0]+(+n[0]>68?1900:2e3),r+n[0].length):-1}function mv(t,e,r){var n=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(r,r+6));return n?(t.Z=n[1]?0:-(n[2]+(n[3]||"00")),r+n[0].length):-1}function bv(t,e,r){var n=fr.exec(e.slice(r,r+1));return n?(t.q=n[0]*3-3,r+n[0].length):-1}function _v(t,e,r){var n=fr.exec(e.slice(r,r+2));return n?(t.m=n[0]-1,r+n[0].length):-1}function b2(t,e,r){var n=fr.exec(e.slice(r,r+2));return n?(t.d=+n[0],r+n[0].length):-1}function xv(t,e,r){var n=fr.exec(e.slice(r,r+3));return n?(t.m=0,t.d=+n[0],r+n[0].length):-1}function _2(t,e,r){var n=fr.exec(e.slice(r,r+2));return n?(t.H=+n[0],r+n[0].length):-1}function vv(t,e,r){var n=fr.exec(e.slice(r,r+2));return n?(t.M=+n[0],r+n[0].length):-1}function kv(t,e,r){var n=fr.exec(e.slice(r,r+2));return n?(t.S=+n[0],r+n[0].length):-1}function wv(t,e,r){var n=fr.exec(e.slice(r,r+3));return n?(t.L=+n[0],r+n[0].length):-1}function Tv(t,e,r){var n=fr.exec(e.slice(r,r+6));return n?(t.L=Math.floor(n[0]/1e3),r+n[0].length):-1}function Ev(t,e,r){var n=cv.exec(e.slice(r,r+1));return n?r+n[0].length:-1}function Cv(t,e,r){var n=fr.exec(e.slice(r));return n?(t.Q=+n[0],r+n[0].length):-1}function Sv(t,e,r){var n=fr.exec(e.slice(r));return n?(t.s=+n[0],r+n[0].length):-1}function x2(t,e){return Ae(t.getDate(),e,2)}function Av(t,e){return Ae(t.getHours(),e,2)}function Lv(t,e){return Ae(t.getHours()%12||12,e,2)}function Dv(t,e){return Ae(1+qs.count(cs(t),t),e,3)}function v2(t,e){return Ae(t.getMilliseconds(),e,3)}function Mv(t,e){return v2(t,e)+"000"}function Nv(t,e){return Ae(t.getMonth()+1,e,2)}function Bv(t,e){return Ae(t.getMinutes(),e,2)}function Iv(t,e){return Ae(t.getSeconds(),e,2)}function Ov(t){var e=t.getDay();return e===0?7:e}function Rv(t,e){return Ae(js.count(cs(t)-1,t),e,2)}function k2(t){var e=t.getDay();return e>=4||e===0?$s(t):$s.ceil(t)}function Fv(t,e){return t=k2(t),Ae($s.count(cs(t),t)+(cs(t).getDay()===4),e,2)}function Pv(t){return t.getDay()}function Yv(t,e){return Ae(Pl.count(cs(t)-1,t),e,2)}function Vv(t,e){return Ae(t.getFullYear()%100,e,2)}function Uv(t,e){return t=k2(t),Ae(t.getFullYear()%100,e,2)}function zv(t,e){return Ae(t.getFullYear()%1e4,e,4)}function Wv(t,e){var r=t.getDay();return t=r>=4||r===0?$s(t):$s.ceil(t),Ae(t.getFullYear()%1e4,e,4)}function Hv(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+Ae(e/60|0,"0",2)+Ae(e%60,"0",2)}function w2(t,e){return Ae(t.getUTCDate(),e,2)}function Gv(t,e){return Ae(t.getUTCHours(),e,2)}function qv(t,e){return Ae(t.getUTCHours()%12||12,e,2)}function jv(t,e){return Ae(1+Uh.count(Ks(t),t),e,3)}function T2(t,e){return Ae(t.getUTCMilliseconds(),e,3)}function $v(t,e){return T2(t,e)+"000"}function Xv(t,e){return Ae(t.getUTCMonth()+1,e,2)}function Kv(t,e){return Ae(t.getUTCMinutes(),e,2)}function Zv(t,e){return Ae(t.getUTCSeconds(),e,2)}function Qv(t){var e=t.getUTCDay();return e===0?7:e}function Jv(t,e){return Ae(zh.count(Ks(t)-1,t),e,2)}function E2(t){var e=t.getUTCDay();return e>=4||e===0?Xs(t):Xs.ceil(t)}function tk(t,e){return t=E2(t),Ae(Xs.count(Ks(t),t)+(Ks(t).getUTCDay()===4),e,2)}function ek(t){return t.getUTCDay()}function rk(t,e){return Ae(Vl.count(Ks(t)-1,t),e,2)}function nk(t,e){return Ae(t.getUTCFullYear()%100,e,2)}function ik(t,e){return t=E2(t),Ae(t.getUTCFullYear()%100,e,2)}function sk(t,e){return Ae(t.getUTCFullYear()%1e4,e,4)}function ak(t,e){var r=t.getUTCDay();return t=r>=4||r===0?Xs(t):Xs.ceil(t),Ae(t.getUTCFullYear()%1e4,e,4)}function ok(){return"+0000"}function C2(){return"%"}function S2(t){return+t}function A2(t){return Math.floor(+t/1e3)}var Zs,Ul;lk({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function lk(t){return Zs=lv(t),Ul=Zs.format,Zs.parse,Zs.utcFormat,Zs.utcParse,Zs}function ck(t){return new Date(t)}function uk(t){return t instanceof Date?+t:+new Date(+t)}function L2(t,e,r,n,i,s,a,o,l,u){var d=r2(),f=d.invert,p=d.domain,g=u(".%L"),b=u(":%S"),k=u("%I:%M"),T=u("%I %p"),L=u("%a %d"),E=u("%b %d"),I=u("%B"),V=u("%Y");function H(G){return(l(G)1?0:t<-1?ro:Math.acos(t)}function M2(t){return t>=1?zl:t<=-1?-zl:Math.asin(t)}function pk(t){return t.innerRadius}function gk(t){return t.outerRadius}function yk(t){return t.startAngle}function mk(t){return t.endAngle}function bk(t){return t&&t.padAngle}function _k(t,e,r,n,i,s,a,o){var l=r-t,u=n-e,d=a-i,f=o-s,p=f*l-d*u;if(!(p*pm*m+Q*Q&&(W=D,N=w),{cx:W,cy:N,x01:-d,y01:-f,x11:W*(i/H-1),y11:N*(i/H-1)}}function jh(){var t=pk,e=gk,r=Xe(0),n=null,i=yk,s=mk,a=bk,o=null;function l(){var u,d,f=+t.apply(this,arguments),p=+e.apply(this,arguments),g=i.apply(this,arguments)-zl,b=s.apply(this,arguments)-zl,k=D2(b-g),T=b>g;if(o||(o=u=Nh()),pRr))o.moveTo(0,0);else if(k>Wl-Rr)o.moveTo(p*hs(g),p*Yn(g)),o.arc(0,0,p,g,b,!T),f>Rr&&(o.moveTo(f*hs(b),f*Yn(b)),o.arc(0,0,f,b,g,T));else{var L=g,E=b,I=g,V=b,H=k,G=k,Y=a.apply(this,arguments)/2,W=Y>Rr&&(n?+n.apply(this,arguments):Qs(f*f+p*p)),N=qh(D2(p-f)/2,+r.apply(this,arguments)),D=N,w=N,v,M;if(W>Rr){var m=M2(W/f*Yn(Y)),Q=M2(W/p*Yn(Y));(H-=m*2)>Rr?(m*=T?1:-1,I+=m,V-=m):(H=0,I=V=(g+b)/2),(G-=Q*2)>Rr?(Q*=T?1:-1,L+=Q,E-=Q):(G=0,L=E=(g+b)/2)}var x=p*hs(L),ct=p*Yn(L),Dt=f*hs(V),kt=f*Yn(V);if(N>Rr){var j=p*hs(E),ot=p*Yn(E),rt=f*hs(I),nt=f*Yn(I),ut;if(kRr?w>Rr?(v=Hl(rt,nt,x,ct,p,w,T),M=Hl(j,ot,Dt,kt,p,w,T),o.moveTo(v.cx+v.x01,v.cy+v.y01),wRr)||!(H>Rr)?o.lineTo(Dt,kt):D>Rr?(v=Hl(Dt,kt,j,ot,f,-D,T),M=Hl(x,ct,rt,nt,f,-D,T),o.lineTo(v.cx+v.x01,v.cy+v.y01),Dt?1:e>=t?0:NaN}function wk(t){return t}function Tk(){var t=wk,e=kk,r=null,n=Xe(0),i=Xe(Wl),s=Xe(0);function a(o){var l,u=(o=N2(o)).length,d,f,p=0,g=new Array(u),b=new Array(u),k=+n.apply(this,arguments),T=Math.min(Wl,Math.max(-Wl,i.apply(this,arguments)-k)),L,E=Math.min(Math.abs(T)/u,s.apply(this,arguments)),I=E*(T<0?-1:1),V;for(l=0;l0&&(p+=V);for(e!=null?g.sort(function(H,G){return e(b[H],b[G])}):r!=null&&g.sort(function(H,G){return r(o[H],o[G])}),l=0,f=p?(T-u*I)/p:0;l0?V*f:0)+I,b[d]={data:o[d],index:l,value:V,startAngle:k,endAngle:L,padAngle:E};return b}return a.value=function(o){return arguments.length?(t=typeof o=="function"?o:Xe(+o),a):t},a.sortValues=function(o){return arguments.length?(e=o,r=null,a):e},a.sort=function(o){return arguments.length?(r=o,e=null,a):r},a.startAngle=function(o){return arguments.length?(n=typeof o=="function"?o:Xe(+o),a):n},a.endAngle=function(o){return arguments.length?(i=typeof o=="function"?o:Xe(+o),a):i},a.padAngle=function(o){return arguments.length?(s=typeof o=="function"?o:Xe(+o),a):s},a}function Gl(){}function ql(t,e,r){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+r)/6)}function I2(t){this._context=t}I2.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:ql(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:ql(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function ta(t){return new I2(t)}function O2(t){this._context=t}O2.prototype={areaStart:Gl,areaEnd:Gl,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:ql(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function Ek(t){return new O2(t)}function R2(t){this._context=t}R2.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var r=(this._x0+4*this._x1+t)/6,n=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(r,n):this._context.moveTo(r,n);break;case 3:this._point=4;default:ql(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function Ck(t){return new R2(t)}function F2(t){this._context=t}F2.prototype={areaStart:Gl,areaEnd:Gl,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}};function Sk(t){return new F2(t)}function P2(t){return t<0?-1:1}function Y2(t,e,r){var n=t._x1-t._x0,i=e-t._x1,s=(t._y1-t._y0)/(n||i<0&&-0),a=(r-t._y1)/(i||n<0&&-0),o=(s*i+a*n)/(n+i);return(P2(s)+P2(a))*Math.min(Math.abs(s),Math.abs(a),.5*Math.abs(o))||0}function V2(t,e){var r=t._x1-t._x0;return r?(3*(t._y1-t._y0)/r-e)/2:e}function $h(t,e,r){var n=t._x0,i=t._y0,s=t._x1,a=t._y1,o=(s-n)/3;t._context.bezierCurveTo(n+o,i+o*e,s-o,a-o*r,s,a)}function jl(t){this._context=t}jl.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:$h(this,this._t0,V2(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var r=NaN;if(t=+t,e=+e,!(t===this._x1&&e===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,$h(this,V2(this,r=Y2(this,t,e)),r);break;default:$h(this,this._t0,r=Y2(this,t,e));break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=r}}};function U2(t){this._context=new z2(t)}(U2.prototype=Object.create(jl.prototype)).point=function(t,e){jl.prototype.point.call(this,e,t)};function z2(t){this._context=t}z2.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,r,n,i,s){this._context.bezierCurveTo(e,t,n,r,s,i)}};function Ak(t){return new jl(t)}function Lk(t){return new U2(t)}function W2(t){this._context=t}W2.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,r=t.length;if(r)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),r===2)this._context.lineTo(t[1],e[1]);else for(var n=H2(t),i=H2(e),s=0,a=1;a=0;--e)i[e]=(a[e]-i[e+1])/s[e];for(s[r-1]=(t[r]+i[r-1])/2,e=0;e=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var r=this._x*(1-this._t)+t*this._t;this._context.lineTo(r,this._y),this._context.lineTo(r,e)}break}}this._x=t,this._y=e}};function Mk(t){return new $l(t,.5)}function Nk(t){return new $l(t,0)}function Bk(t){return new $l(t,1)}function ea(t,e,r){this.k=t,this.x=e,this.y=r}ea.prototype={constructor:ea,scale:function(t){return t===1?this:new ea(this.k*t,this.x,this.y)},translate:function(t,e){return t===0&e===0?this:new ea(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}},new ea(1,0,0),ea.prototype;/*! @license DOMPurify 2.4.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.4.1/LICENSE */function Ii(t){return Ii=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(e){return typeof e}:function(e){return e&&typeof Symbol=="function"&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Ii(t)}function Xh(t,e){return Xh=Object.setPrototypeOf||function(n,i){return n.__proto__=i,n},Xh(t,e)}function Ik(){if(typeof Reflect>"u"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch{return!1}}function Xl(t,e,r){return Ik()?Xl=Reflect.construct:Xl=function(i,s,a){var o=[null];o.push.apply(o,s);var l=Function.bind.apply(i,o),u=new l;return a&&Xh(u,a.prototype),u},Xl.apply(null,arguments)}function fn(t){return Ok(t)||Rk(t)||Fk(t)||Pk()}function Ok(t){if(Array.isArray(t))return Kh(t)}function Rk(t){if(typeof Symbol<"u"&&t[Symbol.iterator]!=null||t["@@iterator"]!=null)return Array.from(t)}function Fk(t,e){if(!!t){if(typeof t=="string")return Kh(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);if(r==="Object"&&t.constructor&&(r=t.constructor.name),r==="Map"||r==="Set")return Array.from(t);if(r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return Kh(t,e)}}function Kh(t,e){(e==null||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r1?r-1:0),i=1;i/gm),Jk=pn(/\${[\w\W]*}/gm),t8=pn(/^data-[\-\w.\u00B7-\uFFFF]/),e8=pn(/^aria-[\-\w]+$/),r8=pn(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),n8=pn(/^(?:\w+script|data):/i),i8=pn(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),s8=pn(/^html$/i),a8=function(){return typeof window>"u"?null:window},o8=function(e,r){if(Ii(e)!=="object"||typeof e.createPolicy!="function")return null;var n=null,i="data-tt-policy-suffix";r.currentScript&&r.currentScript.hasAttribute(i)&&(n=r.currentScript.getAttribute(i));var s="dompurify"+(n?"#"+n:"");try{return e.createPolicy(s,{createHTML:function(o){return o},createScriptURL:function(o){return o}})}catch{return console.warn("TrustedTypes policy "+s+" could not be created."),null}};function Q2(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:a8(),e=function(Z){return Q2(Z)};if(e.version="2.4.1",e.removed=[],!t||!t.document||t.document.nodeType!==9)return e.isSupported=!1,e;var r=t.document,n=t.document,i=t.DocumentFragment,s=t.HTMLTemplateElement,a=t.Node,o=t.Element,l=t.NodeFilter,u=t.NamedNodeMap,d=u===void 0?t.NamedNodeMap||t.MozNamedAttrMap:u,f=t.HTMLFormElement,p=t.DOMParser,g=t.trustedTypes,b=o.prototype,k=Ql(b,"cloneNode"),T=Ql(b,"nextSibling"),L=Ql(b,"childNodes"),E=Ql(b,"parentNode");if(typeof s=="function"){var I=n.createElement("template");I.content&&I.content.ownerDocument&&(n=I.content.ownerDocument)}var V=o8(g,r),H=V?V.createHTML(""):"",G=n,Y=G.implementation,W=G.createNodeIterator,N=G.createDocumentFragment,D=G.getElementsByTagName,w=r.importNode,v={};try{v=ds(n).documentMode?n.documentMode:{}}catch{}var M={};e.isSupported=typeof E=="function"&&Y&&typeof Y.createHTMLDocument<"u"&&v!==9;var m=Zk,Q=Qk,x=Jk,ct=t8,Dt=e8,kt=n8,j=i8,ot=r8,rt=null,nt=we({},[].concat(fn($2),fn(t1),fn(e1),fn(r1),fn(X2))),ut=null,_t=we({},[].concat(fn(K2),fn(n1),fn(Z2),fn(Jl))),K=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Ot=null,P=null,z=!0,X=!0,F=!1,O=!1,A=!1,J=!1,C=!1,ht=!1,U=!1,Yt=!1,et=!0,Mt=!1,Tt="user-content-",gt=!0,Et=!1,wt={},pt=null,st=we({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),le=null,Xt=we({},["audio","video","img","source","image","track"]),jt=null,Kt=we({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Gt="http://www.w3.org/1998/Math/MathML",Ee="http://www.w3.org/2000/svg",Ht="http://www.w3.org/1999/xhtml",Vt=Ht,Ft=!1,xt=null,ft=we({},[Gt,Ee,Ht],Qh),Lt,bt=["application/xhtml+xml","text/html"],Ut="text/html",at,ie=null,dt=n.createElement("form"),ee=function(Z){return Z instanceof RegExp||Z instanceof Function},fe=function(Z){ie&&ie===Z||((!Z||Ii(Z)!=="object")&&(Z={}),Z=ds(Z),Lt=bt.indexOf(Z.PARSER_MEDIA_TYPE)===-1?Lt=Ut:Lt=Z.PARSER_MEDIA_TYPE,at=Lt==="application/xhtml+xml"?Qh:Zl,rt="ALLOWED_TAGS"in Z?we({},Z.ALLOWED_TAGS,at):nt,ut="ALLOWED_ATTR"in Z?we({},Z.ALLOWED_ATTR,at):_t,xt="ALLOWED_NAMESPACES"in Z?we({},Z.ALLOWED_NAMESPACES,Qh):ft,jt="ADD_URI_SAFE_ATTR"in Z?we(ds(Kt),Z.ADD_URI_SAFE_ATTR,at):Kt,le="ADD_DATA_URI_TAGS"in Z?we(ds(Xt),Z.ADD_DATA_URI_TAGS,at):Xt,pt="FORBID_CONTENTS"in Z?we({},Z.FORBID_CONTENTS,at):st,Ot="FORBID_TAGS"in Z?we({},Z.FORBID_TAGS,at):{},P="FORBID_ATTR"in Z?we({},Z.FORBID_ATTR,at):{},wt="USE_PROFILES"in Z?Z.USE_PROFILES:!1,z=Z.ALLOW_ARIA_ATTR!==!1,X=Z.ALLOW_DATA_ATTR!==!1,F=Z.ALLOW_UNKNOWN_PROTOCOLS||!1,O=Z.SAFE_FOR_TEMPLATES||!1,A=Z.WHOLE_DOCUMENT||!1,ht=Z.RETURN_DOM||!1,U=Z.RETURN_DOM_FRAGMENT||!1,Yt=Z.RETURN_TRUSTED_TYPE||!1,C=Z.FORCE_BODY||!1,et=Z.SANITIZE_DOM!==!1,Mt=Z.SANITIZE_NAMED_PROPS||!1,gt=Z.KEEP_CONTENT!==!1,Et=Z.IN_PLACE||!1,ot=Z.ALLOWED_URI_REGEXP||ot,Vt=Z.NAMESPACE||Ht,Z.CUSTOM_ELEMENT_HANDLING&&ee(Z.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(K.tagNameCheck=Z.CUSTOM_ELEMENT_HANDLING.tagNameCheck),Z.CUSTOM_ELEMENT_HANDLING&&ee(Z.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(K.attributeNameCheck=Z.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),Z.CUSTOM_ELEMENT_HANDLING&&typeof Z.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(K.allowCustomizedBuiltInElements=Z.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),O&&(X=!1),U&&(ht=!0),wt&&(rt=we({},fn(X2)),ut=[],wt.html===!0&&(we(rt,$2),we(ut,K2)),wt.svg===!0&&(we(rt,t1),we(ut,n1),we(ut,Jl)),wt.svgFilters===!0&&(we(rt,e1),we(ut,n1),we(ut,Jl)),wt.mathMl===!0&&(we(rt,r1),we(ut,Z2),we(ut,Jl))),Z.ADD_TAGS&&(rt===nt&&(rt=ds(rt)),we(rt,Z.ADD_TAGS,at)),Z.ADD_ATTR&&(ut===_t&&(ut=ds(ut)),we(ut,Z.ADD_ATTR,at)),Z.ADD_URI_SAFE_ATTR&&we(jt,Z.ADD_URI_SAFE_ATTR,at),Z.FORBID_CONTENTS&&(pt===st&&(pt=ds(pt)),we(pt,Z.FORBID_CONTENTS,at)),gt&&(rt["#text"]=!0),A&&we(rt,["html","head","body"]),rt.table&&(we(rt,["tbody"]),delete Ot.tbody),Lr&&Lr(Z),ie=Z)},be=we({},["mi","mo","mn","ms","mtext"]),_e=we({},["foreignobject","desc","title","annotation-xml"]),ke=we({},["title","style","font","a","script"]),$t=we({},t1);we($t,e1),we($t,Xk);var se=we({},r1);we(se,Kk);var ye=function(Z){var At=E(Z);(!At||!At.tagName)&&(At={namespaceURI:Vt,tagName:"template"});var Nt=Zl(Z.tagName),pe=Zl(At.tagName);return xt[Z.namespaceURI]?Z.namespaceURI===Ee?At.namespaceURI===Ht?Nt==="svg":At.namespaceURI===Gt?Nt==="svg"&&(pe==="annotation-xml"||be[pe]):Boolean($t[Nt]):Z.namespaceURI===Gt?At.namespaceURI===Ht?Nt==="math":At.namespaceURI===Ee?Nt==="math"&&_e[pe]:Boolean(se[Nt]):Z.namespaceURI===Ht?At.namespaceURI===Ee&&!_e[pe]||At.namespaceURI===Gt&&!be[pe]?!1:!se[Nt]&&(ke[Nt]||!$t[Nt]):!!(Lt==="application/xhtml+xml"&&xt[Z.namespaceURI]):!1},Jt=function(Z){no(e.removed,{element:Z});try{Z.parentNode.removeChild(Z)}catch{try{Z.outerHTML=H}catch{Z.remove()}}},Nn=function(Z,At){try{no(e.removed,{attribute:At.getAttributeNode(Z),from:At})}catch{no(e.removed,{attribute:null,from:At})}if(At.removeAttribute(Z),Z==="is"&&!ut[Z])if(ht||U)try{Jt(At)}catch{}else try{At.setAttribute(Z,"")}catch{}},Xr=function(Z){var At,Nt;if(C)Z=""+Z;else{var pe=Gk(Z,/^[\r\n\t ]+/);Nt=pe&&pe[0]}Lt==="application/xhtml+xml"&&Vt===Ht&&(Z=''+Z+"");var Me=V?V.createHTML(Z):Z;if(Vt===Ht)try{At=new p().parseFromString(Me,Lt)}catch{}if(!At||!At.documentElement){At=Y.createDocument(Vt,"template",null);try{At.documentElement.innerHTML=Ft?"":Me}catch{}}var Le=At.body||At.documentElement;return Z&&Nt&&Le.insertBefore(n.createTextNode(Nt),Le.childNodes[0]||null),Vt===Ht?D.call(At,A?"html":"body")[0]:A?At.documentElement:Le},De=function(Z){return W.call(Z.ownerDocument||Z,Z,l.SHOW_ELEMENT|l.SHOW_COMMENT|l.SHOW_TEXT,null,!1)},ae=function(Z){return Z instanceof f&&(typeof Z.nodeName!="string"||typeof Z.textContent!="string"||typeof Z.removeChild!="function"||!(Z.attributes instanceof d)||typeof Z.removeAttribute!="function"||typeof Z.setAttribute!="function"||typeof Z.namespaceURI!="string"||typeof Z.insertBefore!="function"||typeof Z.hasChildNodes!="function")},Be=function(Z){return Ii(a)==="object"?Z instanceof a:Z&&Ii(Z)==="object"&&typeof Z.nodeType=="number"&&typeof Z.nodeName=="string"},Re=function(Z,At,Nt){!M[Z]||Hk(M[Z],function(pe){pe.call(e,At,Nt,ie)})},Pe=function(Z){var At;if(Re("beforeSanitizeElements",Z,null),ae(Z)||Dr(/[\u0080-\uFFFF]/,Z.nodeName))return Jt(Z),!0;var Nt=at(Z.nodeName);if(Re("uponSanitizeElement",Z,{tagName:Nt,allowedTags:rt}),Z.hasChildNodes()&&!Be(Z.firstElementChild)&&(!Be(Z.content)||!Be(Z.content.firstElementChild))&&Dr(/<[/\w]/g,Z.innerHTML)&&Dr(/<[/\w]/g,Z.textContent)||Nt==="select"&&Dr(/