Compare commits

..

No commits in common. "main" and "v0.0.27" have entirely different histories.

122 changed files with 3215 additions and 4215 deletions

@ -1,3 +1,4 @@
.git
db.sqlite*
out/
.svn
db.sqlite
out/**/*.o
out/**/*.d

@ -6,51 +6,15 @@ jobs:
Build-All:
runs-on: ubuntu-latest
container:
image: node:23-bookworm-slim
valid_volumes:
- '/opt/keys'
- '/opt/deps'
valid_volumes: ['/opt/keys']
volumes:
- /opt/keys:/opt/keys
- /opt/deps:/opt/deps
steps:
- name: Install build dependencies
run: >
apt update && apt install -y \
build-essential \
clang-19 \
cmake \
curl \
docker.io \
doxygen \
file \
gcc-aarch64-linux-gnu \
git \
graphviz \
libgpgme11 \
libssl-dev \
mingw-w64 \
rsync \
unzip \
zip \
zlib1g-dev
- name: Get code
- name: check out code
uses: actions/checkout@v4
with:
submodules: true
- name: Setup environment
run: |
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100
ln -s /opt/keys .keys
ln -sf /opt/deps/ios_toolchain deps/
ln -sf /opt/deps/macos_toolchain deps/
- name: Build documentation
run: |
mkdir -p out/html/ ~/.ssh/
make docs
echo 'pildefriends ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKD3Kde5vDO0TrMBDK0IGGeNGe/XinWAZkSQ/rXxwUjt' >> ~/.ssh/known_hosts
rsync -avP --delete -e "ssh -i /opt/keys/ssh.ed25519" out/html/ tfdocs@pildefriends:docs/html/
- run: ln -s /opt/keys .keys
- name: Setup JDK
uses: actions/setup-java@v3
with:
@ -60,12 +24,14 @@ jobs:
uses: android-actions/setup-android@v3
with:
packages: 'tools platform-tools build-tools;34.0.0 platforms;android-34 ndk;26.3.11579264'
- name: Docker build
run: DOCKER_BUILDKIT=1 docker build .
- name: Build
run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all dist docs
- name: Upload artifacts
uses: actions/upload-artifact@v3
- run: sudo apt update && sudo apt install -y doxygen graphviz mingw-w64 libgpgme11 gcc-aarch64-linux-gnu
- run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all docs
- run: docker build .
- uses: actions/upload-artifact@v3
with:
name: dist
path: dist/*
path: |
out/TildeFriends-release.fdroid.apk
out/winrelease/tildefriends.standalone.exe
out/tildefriends-x86_64.AppImage
out/release/tildefriends.standalone
out/armrelease/tildefriends.standalone

4
.gitignore vendored

@ -1,13 +1,11 @@
build/
*.core
db.*
deps/ios_toolchain
deps/macos_toolchain
deps/ios_toolchain/
deps/openssl/
dist/
.flatpak-builder
.keys
**/.DS_Store
logs/
**/node_modules
out

6
.gitmodules vendored

@ -26,6 +26,6 @@
[submodule "deps/c-ares"]
path = deps/c-ares
url = https://github.com/c-ares/c-ares.git
[submodule "deps/zsign"]
path = deps/zsign
url = https://github.com/zhlynn/zsign.git
[submodule "docs"]
path = docs
url = https://dev.tildefriends.net/cory/tildefriends.wiki.git

@ -1,16 +1,19 @@
FROM bitnami/minideb:bookworm AS build
FROM bitnami/minideb:bullseye AS build
RUN apt-get update && \
apt-get install -y --no-install-recommends \
gcc \
libc6-dev \
perl \
libssl-dev \
make
COPY . /app
RUN make -C /app -j $(nproc) release
FROM bitnami/minideb:bookworm
FROM bitnami/minideb:bullseye
RUN apt-get update && \
apt-get install -y --no-install-recommends \
libssl1.1
COPY --from=build /app/out/release/tildefriends /app/out/release/tildefriends
COPY --from=build /app/apps /app/apps

@ -943,7 +943,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = README.md docs/ src/
INPUT = src/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -1110,7 +1110,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE = README.md
USE_MDFILE_AS_MAINPAGE =
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
@ -1680,7 +1680,7 @@ DISABLE_INDEX = NO
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = YES
GENERATE_TREEVIEW = NO
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview

@ -16,14 +16,11 @@ MAKEFLAGS += --no-builtin-rules
## LD := Linker.
## ANDROID_SDK := Path to the Android SDK.
VERSION_CODE := 33
VERSION_CODE_IOS := 8
VERSION_NUMBER := 0.0.28-wip
VERSION_CODE := 32
VERSION_NUMBER := 0.0.27
VERSION_NAME := This program kills fascists.
IPHONEOS_VERSION_MIN=14.0
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3490100.zip
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3480000.zip
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
@ -36,23 +33,21 @@ UNAME_M := $(shell uname -m)
ANDROID_SDK ?= ~/Android/Sdk
BUNDLETOOL = out/bundletool.jar
HAVE_WIN :=
HAVE_CROSS_AARCH64 :=
USE_SYSTEM_SSL :=
HAVE_WIN := 0
HAVE_CROSS_AARCH64 := 0
export SOURCE_DATE_EPOCH=1
export TZ=UTC
ifeq ($(UNAME_S),Darwin)
BUILD_TYPES := debug release iosdebug iosrelease iossimdebug iossimrelease
BUILD_TYPES := macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease
else ifeq ($(UNAME_S),Linux)
BUILD_TYPES := debug release
HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1)
HAVE_LINUX_IOS = $(if $(shell which deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang),1)
HAVE_LINUX_MACOS = $(if $(shell which deps/macos_toolchain/bin/oa64-clang),1)
HAVE_WIN = $(if $(shell which x86_64-w64-mingw32-gcc-win32),1)
HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1,0)
HAVE_LINUX_IOS = $(if $(shell which deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang),1,0)
HAVE_WIN = $(if $(shell which x86_64-w64-mingw32-gcc-win32),1,0)
ifneq ($(UNAME_M),aarch64)
HAVE_CROSS_AARCH64 = $(if $(shell which aarch64-linux-gnu-gcc),1)
HAVE_CROSS_AARCH64 = $(if $(shell which aarch64-linux-gnu-gcc),1,0)
endif
else ifeq ($(UNAME_S),Haiku)
BUILD_TYPES := debug release
@ -61,7 +56,6 @@ LDFLAGS += \
-lbsd \
-lnetwork \
-Wno-stringop-overflow
USE_SYSTEM_SSL := 1
else ifeq ($(UNAME_S),OpenBSD)
BUILD_TYPES := debug release
CFLAGS += \
@ -69,24 +63,18 @@ CFLAGS += \
LDFLAGS += \
-lexecinfo \
-lc++abi
HAVE_ANDROID :=
HAVE_LINUX_IOS :=
HAVE_LINUX_MACOS :=
USE_SYSTEM_SSL := 1
HAVE_ANDROID := 0
HAVE_LINUX_IOS := 0
else
$(error Unexpected host platform $(UNAME_S).)
endif
# Everything is set above.
$(info Building Tilde Friends $(VERSION_NUMBER) android=$(if $(HAVE_ANDROID),1,0) win=$(if $(HAVE_WIN),1,0) cross_aarch64=$(if $(HAVE_CROSS_AARCH64),1,0) cross_ios=$(if $(HAVE_LINUX_IOS),1,0) cross_macos=$(if $(HAVE_LINUX_MACOS),1,0) system_ssl=$(if $(USE_SYSTEM_SSL),1,0))
CFLAGS += \
-std=gnu11 \
-Wall \
-Wextra \
-Wno-cast-function-type-mismatch \
-Wno-unknown-warning-option \
-Wno-unused-parameter \
-Wno-unknown-warning-option \
-MMD \
-MP \
-ffunction-sections \
@ -148,28 +136,12 @@ ifeq ($(HAVE_CROSS_AARCH64),1)
BUILD_TYPES += armdebug armrelease
endif
HOST_TARGETS := \
LINUX_TARGETS := \
out/debug/tildefriends \
out/release/tildefriends
ifeq ($(UNAME_S),Darwin)
MACOS_TARGETS := \
out/debug/tildefriends \
out/release/tildefriends
else ifeq ($(UNAME_S),Linux)
ifeq ($(HAVE_LINUX_MACOS),1)
MACOS_TARGETS := \
out/macosdebug-arm/tildefriends \
out/macosrelease-arm/tildefriends \
out/macosdebug-x86_64/tildefriends \
out/macosrelease-x86_64/tildefriends
all: out/macosdebug/tildefriends.standalone
all: out/macosrelease/tildefriends.standalone
else
MACOS_TARGETS :=
endif
else
MACOS_TARGETS :=
endif
out/macosdebug/tildefriends \
out/macosrelease/tildefriends
IOS_TARGETS := \
out/iosdebug/tildefriends \
out/iosrelease/tildefriends
@ -183,14 +155,6 @@ ifeq ($(HAVE_LINUX_IOS),1)
BUILD_TYPES += iosdebug iosrelease
all: $(IOS_APPS)
endif
ifeq ($(HAVE_LINUX_MACOS),1)
BUILD_TYPES += \
macosdebug-arm \
macosrelease-arm \
macosdebug-x86_64 \
macosrelease-x86_64
all: $(IOS_APPS)
endif
ifeq ($(UNAME_S),Darwin)
all: $(IOS_APPS) \
out/tildefriends-iossimdebug.app/tildefriends \
@ -205,38 +169,35 @@ DEBUG_TARGETS := \
out/windebug/tildefriends.exe \
out/iosdebug/tildefriends \
out/iossimdebug/tildefriends \
out/macosdebug/tildefriends \
out/androiddebug/tildefriends \
out/androiddebug-armv7a/tildefriends \
out/androiddebug-x86_64/tildefriends \
out/androiddebug-x86/tildefriends \
out/armdebug/tildefriends \
out/macosdebug-arm/tildefriends \
out/macosdebug-x86_64/tildefriends
out/armdebug/tildefriends
RELEASE_TARGETS := \
out/release/tildefriends \
out/winrelease/tildefriends.exe \
out/iosrelease/tildefriends \
out/iossimrelease/tildefriends \
out/macosrelease/tildefriends \
out/androidrelease/tildefriends \
out/androidrelease-armv7a/tildefriends \
out/androidrelease-x86_64/tildefriends \
out/androidrelease-x86/tildefriends \
out/armrelease/tildefriends \
out/macosrelease-arm/tildefriends \
out/macosrelease-x86_64/tildefriends
out/armrelease/tildefriends
ALL_TARGETS = $(DEBUG_TARGETS) $(RELEASE_TARGETS)
ANDROID_RELEASE_TARGETS := $(filter-out $(DEBUG_TARGETS),$(ANDROID_TARGETS))
NONANDROID_RELEASE_TARGETS := $(filter-out $(ANDROID_ARM64_TARGETS),$(RELEASE_TARGETS))
NONANDROID_TARGETS := $(filter-out $(ANDROID_TARGETS),$(ALL_TARGETS))
NONMACOS_TARGETS := $(filter-out $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS),$(ALL_TARGETS))
DEADSTRIP_TARGETS := $(filter-out $(ANDROID_TARGETS),$(NONMACOS_TARGETS))
ifneq ($(UNAME_S),OpenBSD)
$(NONMACOS_TARGETS): LDFLAGS += -static-libgcc
endif
$(NONANDROID_TARGETS): CFLAGS += -fno-omit-frame-pointer
$(filter-out $(WINDOWS_TARGETS),$(ALL_TARGETS)): LDFLAGS += \
-rdynamic \
-gz=zlib
$(filter-out $(WINDOWS_TARGETS),$(ALL_TARGETS)): LDFLAGS += -rdynamic
$(ANDROID_TARGETS): CFLAGS += \
--sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
-fPIC \
@ -260,29 +221,25 @@ $(WINDOWS_TARGETS): CFLAGS += \
-D_WIN32_WINNT=0x0A00 \
-DWINVER=0x0A00 \
-DNTDDI_VERSION=NTDDI_WIN10 \
-Iout/openssl/$(UNAME_S)/mingw64/usr/local/include
-Ideps/openssl/mingw64/usr/local/include
$(WINDOWS_TARGETS): LDFLAGS += \
-static \
-lm \
-Lout/openssl/$(UNAME_S)/mingw64/usr/local/lib
-Ldeps/openssl/mingw64/usr/local/lib
$(AARCH64_TARGETS): CC = aarch64-linux-gnu-gcc
$(AARCH64_TARGETS): AS = $(CC)
$(AARCH64_TARGETS): CFLAGS += -Iout/openssl/Linux/aarch64/usr/local/include
$(AARCH64_TARGETS): LDFLAGS += -Lout/openssl/Linux/aarch64/usr/local/lib
$(AARCH64_TARGETS): CFLAGS += -Ideps/openssl/Linux/aarch64/usr/local/include
$(AARCH64_TARGETS): LDFLAGS += -Ldeps/openssl/Linux/aarch64/usr/local/lib
ifeq ($(UNAME_S),Darwin)
$(HOST_TARGETS): CC = xcrun clang
$(MACOS_TARGETS): CC = xcrun clang
$(IOS_TARGETS): IOS_SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)
$(IOS_TARGETS): CC = xcrun --sdk iphoneos clang -isysroot $(IOS_SYSROOT) -arch arm64
$(IOSSIM_TARGETS): IOSSIM_SYSROOT := $(shell xcrun --sdk iphonesimulator --show-sdk-path)
$(IOSSIM_TARGETS): CC = xcrun --sdk iphonesimulator clang -isysroot $(IOSSIM_SYSROOT) -arch x86_64
else ifeq ($(UNAME_S),Linux)
$(IOS_TARGETS): CFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk -arch arm64 -DTARGET_OS_IPHONE=1
$(IOS_TARGETS): CFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk -arch arm64
$(IOS_TARGETS): LDFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk
$(IOS_TARGETS): CC = PATH=$$PATH:deps/ios_toolchain/target/bin LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/ios_toolchain/target/lib deps/ios_toolchain/target/bin/arm-apple-darwin11-clang
$(filter $(BUILD_DIR)/macosdebug-x86_64/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/o64-clang
$(filter $(BUILD_DIR)/macosdebug-arm/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/oa64-clang
$(filter $(BUILD_DIR)/macosrelease-x86_64/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/o64-clang
$(filter $(BUILD_DIR)/macosrelease-arm/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/oa64-clang
$(IOS_TARGETS): CC = PATH=$$PATH:deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang
endif
$(ANDROID_X86_64_TARGETS): ANDROID_NDK_TARGET_TRIPLE := x86_64-linux-android
$(ANDROID_X86_TARGETS): ANDROID_NDK_TARGET_TRIPLE := i686-linux-android
@ -293,39 +250,30 @@ $(ANDROID_TARGETS): AS = $(CC)
$(ANDROID_TARGETS): CFLAGS += \
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION) \
-Wno-unknown-warning-option
$(ANDROID_ARMV7A_TARGETS): CFLAGS += -Iout/openssl/android/armeabi-v7a/usr/local/include
$(ANDROID_ARMV7A_TARGETS): LDFLAGS += -Lout/openssl/android/armeabi-v7a/usr/local/lib
$(ANDROID_ARM64_TARGETS): CFLAGS += -Iout/openssl/android/arm64-v8a/usr/local/include
$(ANDROID_ARM64_TARGETS): LDFLAGS += -Lout/openssl/android/arm64-v8a/usr/local/lib
$(ANDROID_X86_TARGETS): CFLAGS += -Iout/openssl/android/x86/usr/local/include
$(ANDROID_ARMV7A_TARGETS): CFLAGS += -Ideps/openssl/android/armeabi-v7a/usr/local/include
$(ANDROID_ARMV7A_TARGETS): LDFLAGS += -Ldeps/openssl/android/armeabi-v7a/usr/local/lib
$(ANDROID_ARM64_TARGETS): CFLAGS += -Ideps/openssl/android/arm64-v8a/usr/local/include
$(ANDROID_ARM64_TARGETS): LDFLAGS += -Ldeps/openssl/android/arm64-v8a/usr/local/lib
$(ANDROID_X86_TARGETS): CFLAGS += -Ideps/openssl/android/x86/usr/local/include
$(ANDROID_X86_TARGETS): CFLAGS += -Wno-atomic-alignment
$(ANDROID_X86_TARGETS): LDFLAGS += -Lout/openssl/android/x86/usr/local/lib
$(ANDROID_X86_64_TARGETS): CFLAGS += -Iout/openssl/android/x86_64/usr/local/include
$(ANDROID_X86_64_TARGETS): LDFLAGS += -Lout/openssl/android/x86_64/usr/local/lib
$(ANDROID_X86_TARGETS): LDFLAGS += -Ldeps/openssl/android/x86/usr/local/lib
$(ANDROID_X86_64_TARGETS): CFLAGS += -Ideps/openssl/android/x86_64/usr/local/include
$(ANDROID_X86_64_TARGETS): LDFLAGS += -Ldeps/openssl/android/x86_64/usr/local/lib
$(NONMACOS_TARGETS): CFLAGS += -Wno-cast-function-type
$(MACOS_TARGETS): LDFLAGS += -Wl,-dead_strip
$(NONMACOS_TARGETS): LDFLAGS += -Wl,--gc-sections -Wl,--as-needed
$(IOS_TARGETS): CFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
$(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
$(DEADSTRIP_TARGETS): LDFLAGS += -Wl,--gc-sections
$(IOS_TARGETS): CFLAGS += -miphoneos-version-min=9.0
$(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=9.0
ifeq ($(UNAME_S),Darwin)
$(IOS_TARGETS): CFLAGS += -Iout/openssl/ios/ios64-xcrun/usr/local/include
$(IOS_TARGETS): LDFLAGS += -Lout/openssl/ios/ios64-xcrun/usr/local/lib
$(IOS_TARGETS): CFLAGS += -Ideps/openssl/ios/ios64-xcrun/usr/local/include
$(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib
else
$(IOS_TARGETS): CFLAGS += -Iout/openssl/$(UNAME_S)/ios64-cross/usr/local/include
$(IOS_TARGETS): LDFLAGS += -Lout/openssl/$(UNAME_S)/ios64-cross/usr/local/lib
$(filter $(BUILD_DIR)/macosdebug-x86_64/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-x86_64/usr/local/include
$(filter $(BUILD_DIR)/macosdebug-arm/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-arm/usr/local/include
$(filter $(BUILD_DIR)/macosdebug-x86_64/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-x86_64/usr/local/lib
$(filter $(BUILD_DIR)/macosdebug-arm/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-arm/usr/local/lib
$(filter $(BUILD_DIR)/macosrelease-x86_64/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-x86_64/usr/local/include
$(filter $(BUILD_DIR)/macosrelease-arm/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-arm/usr/local/include
$(filter $(BUILD_DIR)/macosrelease-x86_64/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-x86_64/usr/local/lib
$(filter $(BUILD_DIR)/macosrelease-arm/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-arm/usr/local/lib
$(IOS_TARGETS): CFLAGS += -Ideps/openssl/$(UNAME_S)/ios64-cross/usr/local/include
$(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/$(UNAME_S)/ios64-cross/usr/local/lib
endif
$(IOSSIM_TARGETS): CFLAGS += -Iout/openssl/ios/iossimulator-xcrun/usr/local/include
$(IOSSIM_TARGETS): LDFLAGS += -Lout/openssl/ios/iossimulator-xcrun/usr/local/lib
$(HOST_TARGETS): CFLAGS += -Iout/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/include
$(HOST_TARGETS): LDFLAGS += -Lout/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib
$(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include
$(IOSSIM_TARGETS): LDFLAGS += -Ldeps/openssl/ios/iossimulator-xcrun/usr/local/lib
$(LINUX_TARGETS) $(MACOS_TARGETS): CFLAGS += -Ideps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/include
$(LINUX_TARGETS) $(MACOS_TARGETS): LDFLAGS += -Ldeps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib
ifeq ($(UNAME_M),x86_64)
ifeq ($(UNAME_S),Linux)
@ -344,14 +292,13 @@ endif
get_objs = \
$(foreach build_type,$(BUILD_TYPES),$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)))))) \
$(foreach build_type,debug release armdebug armrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \
$(foreach build_type,windebug winrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_win))))) \
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androidrelease-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_android))))) \
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androiddebug-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_android))))) \
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androidrelease-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \
$(foreach build_type,macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_macos))))) \
$(foreach build_type,iosdebug iosrelease iossimdebug iossimrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_ios))))) \
$(foreach build_type,iosdebug iosrelease iossimdebug iossimrelease macosdebug-arm macosrelease-arm macosdebug-x86_64 macosrelease-x86_64,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_macos))))) \
$(foreach build_type,androiddebug-x86 androidrelease-x86,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_x86))))) \
$(if $(findstring Darwin,$(UNAME_S)),$(foreach build_type,debug release,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_macos)))))) \
$(if $(findstring Darwin,$(UNAME_S)),,$(foreach build_type,debug release armdebug armrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))))
$(foreach build_type,androiddebug-x86 androidrelease-x86,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_x86)))))
APP_SOURCES := $(wildcard src/*.c)
APP_SOURCES_ios := $(wildcard src/*.m)
@ -373,12 +320,10 @@ $(APP_OBJS): CFLAGS += \
-Ideps/valgrind \
-Wdouble-promotion \
-Werror
ifneq ($(UNAME_S),Darwin)
ifeq ($(UNAME_M),x86_64)
$(filter-out $(BUILD_DIR)/android% $(BUILD_DIR)/ios% $(BUILD_DIR)/macos%,$(APP_OBJS)): CFLAGS += \
$(filter-out $(BUILD_DIR)/android% $(BUILD_DIR)/macos% $(BUILD_DIR)/ios%,$(APP_OBJS)): CFLAGS += \
-fanalyzer
endif
endif
ARES_SOURCES := \
deps/c-ares/src/lib/ares_addrinfo2hostent.c \
@ -811,12 +756,12 @@ $(MINIUNZIP_OBJS): CFLAGS += \
LDFLAGS += \
-pthread \
-lm
$(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS) $(AARCH64_TARGETS) $(filter-out $(HOST_TARGETS),$(MACOS_TARGETS)): LDFLAGS += \
$(LINUX_TARGETS) $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS) $(AARCH64_TARGETS): LDFLAGS += \
-lssl \
-lcrypto
ifneq ($(UNAME_S),Haiku)
ifneq ($(UNAME_S),OpenBSD)
$(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
debug release $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
-ldl
endif
endif
@ -849,15 +794,27 @@ $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
##
## Common targets:
##
all: $(BUILD_TYPES) ## Build all targets that appear possible to build on this machine.
debug: ## Build a debug executable for the current host platform.
release: ## Build a release executable for the current host platform.
debug: ## Build a debug executable for the current platform.
release: ## Build a release executable for the current platform.
armdebug: ## Cross-compile aarch64 debug on Linux.
armrelease: ## Cross-compile aarch64 release on Linux.
windebug: ## Cross-compile a debug win32 executable on Linux.
winrelease: ## Cross-compile a release win32 executable on Linux.
all: $(BUILD_TYPES) ## Build all targets that appear possible to build on this machine.
unix: debug release ## Build all UNIX targets.
win: windebug winrelease ## Build all Windows targets.
.PHONY: all win unix
##
## Windows targets:
##
windebug: ## Build a debug win32 executable.
winrelease: ## Build a release win32 executable.
##
## MacOS targets:
##
macosdebug: ## Build a MacOS debug executable.
macosrelease: ## Build a MacOS release executable.
ALL_APP_OBJS := \
$(APP_OBJS) \
$(ARES_OBJS) \
@ -913,17 +870,6 @@ src/android/AndroidManifest.xml : $(firstword $(MAKEFILE_LIST))
-e 's/android:targetSdkVersion="[[:digit:]]*"/android:targetSdkVersion="$(ANDROID_TARGET_SDK_VERSION)"/' \
$@
src/ios/Info.plist : $(firstword $(MAKEFILE_LIST))
@echo "[ios_version] $@"
@cat $@ | \
tr '\n' '^' | \
sed -r \
-e 's@(<key>CFBundleShortVersionString</key>\^[[:space:]]*<string>)[0-9.]*(</string>)@\1$(VERSION_NUMBER:%-wip=%)\2@' \
-e 's@(<key>CFBundleVersion</key>\^[[:space:]]*<string>)[[:digit:]]+(</string>)@\1$(VERSION_CODE_IOS)\2@' \
-e 's@(<key>MinimumOSVersion</key>\^[[:space:]]*<string>)[0-9.]*(</string>)@\1$(IPHONEOS_VERSION_MIN)\2@' | \
tr '^' '\n' > \
$@.tmp && mv $@.tmp $@ || rm -f $@.tmp
##
## Android targets:
##
@ -941,30 +887,29 @@ out/res/layout_activity_main.xml.flat: src/android/res/layout/activity_main.xml
@echo "[aapt2] $@"
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ src/android/res/layout/activity_main.xml
out/res/drawable_%.xml.flat: src/android/res/drawable/%.xml
out/res/drawable_icon.xml.flat: src/android/res/drawable/icon.xml
@mkdir -p $(dir $@)
@echo "[aapt2] $@"
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ $(<)
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ src/android/res/drawable/icon.xml
out/apk/res.apk out/gen/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat out/res/drawable_logo.xml.flat src/android/AndroidManifest.xml
out/apk/res.apk out/gen/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat src/android/AndroidManifest.xml
@echo [aapt2 link] res.apk
@mkdir -p out/apk/
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat out/res/drawable_logo.xml.flat \
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat \
--min-sdk-version $(ANDROID_MIN_SDK_VERSION) \
--target-sdk-version $(ANDROID_TARGET_SDK_VERSION) \
--manifest src/android/AndroidManifest.xml \
-o out/apk/res.apk \
--java out/gen/
out/apk/res.fdroid.apk out/gen_fdroid/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon_fdroid.xml.flat out/res/drawable_logo.xml.flat src/android/AndroidManifest.xml
out/apk/res.fdroid.apk out/gen_fdroid/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat src/android/AndroidManifest.xml
@echo [aapt2 link] res.fdroid.apk
@mkdir -p out/apk/
@sed -e 's@drawable/icon@drawable/icon_fdroid@' src/android/AndroidManifest.xml > out/apk/AndroidManifest.fdroid.xml
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon_fdroid.xml.flat out/res/drawable_logo.xml.flat \
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat \
--min-sdk-version $(ANDROID_MIN_SDK_VERSION) \
--target-sdk-version $(ANDROID_TARGET_SDK_VERSION) \
--rename-manifest-package com.unprompted.tildefriends.fdroid \
--manifest out/apk/AndroidManifest.fdroid.xml \
--manifest src/android/AndroidManifest.xml \
-o out/apk/res.fdroid.apk \
--java out/gen_fdroid/
@ -981,11 +926,11 @@ out/apk/classes.dex: $(CLASS_FILES)
@$(ANDROID_BUILD_TOOLS)/d8 --lib $(ANDROID_PLATFORM)/android.jar --output $(dir $@) out/classes/com/unprompted/tildefriends/*.class
PACKAGE_DIRS := \
apps \
core \
deps/codemirror \
deps/prettier \
deps/lit
apps/ \
core/ \
deps/codemirror/ \
deps/prettier/ \
deps/lit/
RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f -not -name '.*')))
@ -1015,7 +960,6 @@ out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGET
--manifest src/android/AndroidManifest.xml \
-R out/res/layout_activity_main.xml.flat \
-R out/res/drawable_icon.xml.flat \
-R out/res/drawable_logo.xml.flat \
--auto-add-overlay
@unzip out/aab/temporary.apk -d out/aab/staging/
@mkdir -p out/aab/staging/root/deps
@ -1042,7 +986,7 @@ out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGET
@cp -r deps/codemirror/ out/aab/staging/root/deps/
@cd out/aab/staging/; zip -r ../base.zip *; cd ../../../
@java -jar $(BUNDLETOOL) build-bundle --overwrite --config=src/android/BundleConfig.json --modules=out/aab/base.zip --output=$@
@$(ANDROID_BUILD_TOOLS)/apksigner sign -ks .keys/android.jks --ks-key-alias androidKey -ks-pass pass:android --min-sdk-version=$(ANDROID_MIN_SDK_VERSION) $@
@jarsigner -keystore .keys/android.jks $@ androidKey -storepass android
aab: out/TildeFriends.aab ## Build an Android App Bundle.
.PHONY: aab
@ -1148,18 +1092,12 @@ out/data.zip: $(RAW_FILES)
@echo [zip] $@
@zip -u $@ -q -9 $(RAW_FILES)
out/zsign_build/zsign: $(wildcard deps/zsign/*.cpp deps/zsign/*.h deps/zsign/*.txt deps/zsign/common/*)
@+echo [cmake] $@
@cmake -B out/zsign_build deps/zsign
@cmake --build out/zsign_build -- COLOR=0 VERBOSE=0 MAKESILENT=-s
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip $(if $(HAVE_LINUX_IOS),out/zsign_build/zsign)
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip
@mkdir -p $(dir $@)
@cp -v $(filter-out out/zsign%,$<) $@
@cp -v $< $@
@cp -v out/data.zip $(@D)/
ifeq ($(HAVE_LINUX_IOS),1)
@mkdir -p $(realpath $(dir $@))/_CodeSignature
@out/zsign_build/zsign -q -k .keys/apple.p12 -f -m src/ios/embedded.mobileprovision $(realpath $(dir $@))
@zsign -q -k .keys/apple.p12 -f -m src/ios/embedded.mobileprovision $(realpath $(dir $@))
endif
.SECONDARY:
out/tildefriends-%.ipa: out/tildefriends-ios%.app/tildefriends
@ -1197,34 +1135,26 @@ iossimdebuggo: out/tildefriends-iossimdebug.app/tildefriends ## Build, install,
xcrun simctl launch booted com.unprompted.tildefriends
.PHONY: iossimdebuggo
ANDROID_DEPS := out/openssl/android/arm64-v8a/usr/local/lib/libssl.a
ANDROID_DEPS := deps/openssl/android/arm64-v8a/usr/local/lib/libssl.a
$(ANDROID_DEPS):
+@export ANDROID_NDK_ROOT=$(ANDROID_NDK)
+@export BUILD_PLATFORM=android
+@export TOOLCHAIN=$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64
+@PATH="$$TOOLCHAIN/x86_64-linux-android/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=x86_64 SSL_TARGET=android-x86_64 OPTIONS="-D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
+@PATH="$$TOOLCHAIN/i686-linux-android/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=x86 SSL_TARGET=android-x86 OPTIONS="-D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
+@PATH="$$TOOLCHAIN/arm-linux-androideabi/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=armeabi-v7a SSL_TARGET=android-arm OPTIONS="--target=armv7a-linux-androideabi -Wl,--fix-cortex-a8 -D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
+@PATH="$$TOOLCHAIN/aarch64-linux-android/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=arm64-v8a SSL_TARGET=android-arm64 OPTIONS="-D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
+@ANDROID_NDK_ROOT=$(ANDROID_NDK) tools/ssl-android
$(filter $(BUILD_DIR)/android%,$(APP_OBJS)): | $(ANDROID_DEPS)
ifeq ($(UNAME_S),Linux)
ifneq ($(USE_SYSTEM_SSL),1)
LOCAL_DEPS := out/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
LOCAL_DEPS := deps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@tools/ssl-local
+@/usr/bin/env bash tools/ssl-local
$(filter $(BUILD_DIR)/debug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/release/%,$(APP_OBJS)): | $(LOCAL_DEPS)
endif
ifeq ($(HAVE_CROSS_AARCH64),1)
LOCAL_DEPS := out/openssl/$(UNAME_S)/aarch64/usr/local/lib/libssl.a
LOCAL_DEPS := deps/openssl/$(UNAME_S)/aarch64/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@OPTIONS="--cross-compile-prefix=aarch64-linux-gnu-" BUILD_TARGET=aarch64 SSL_TARGET=linux-aarch64 tools/ssl-local
+@OPTIONS="--cross-compile-prefix=aarch64-linux-gnu-" BUILD_TARGET=aarch64 tools/ssl-local
$(filter $(BUILD_DIR)/armdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/armrelease/%,$(APP_OBJS)): | $(LOCAL_DEPS)
endif
ifeq ($(HAVE_LINUX_IOS),1)
LOCAL_DEPS := out/openssl/$(UNAME_S)/ios64-cross/usr/local/lib/libssl.a
LOCAL_DEPS := deps/openssl/$(UNAME_S)/ios64-cross/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@PATH=deps/ios_toolchain/target/bin:$$PATH \
BUILD_TARGET=ios64-cross \
@ -1233,63 +1163,33 @@ $(LOCAL_DEPS):
CROSS_TOP=../../deps/ios_toolchain/target \
CROSS_SDK=iPhoneOS18.2.sdk \
CC=clang \
OPTIONS=-miphoneos-version-min=$(IPHONEOS_VERSION_MIN) \
OPTIONS=-miphoneos-version-min=9.0 \
tools/ssl-local
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(LOCAL_DEPS)
endif
ifeq ($(HAVE_LINUX_MACOS),1)
LOCAL_DEPS := out/openssl/$(UNAME_S)/macos-arm/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@PATH=../../deps/macos_toolchain/bin:$$PATH \
BUILD_TARGET=macos-arm \
SSL_TARGET=darwin64-arm64 \
CC=../../deps/macos_toolchain/bin/oa64-clang \
RANLIB=../../deps/macos_toolchain/bin/x86_64-apple-darwin24-ranlib \
AR=../../deps/macos_toolchain/bin/arm64-apple-darwin24-ar \
tools/ssl-local
$(filter $(BUILD_DIR)/macosrelease-arm/% $(BUILD_DIR)/macosdebug-arm/%,$(APP_OBJS)): | $(LOCAL_DEPS)
LOCAL_DEPS := out/openssl/$(UNAME_S)/macos-x86_64/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@PATH=../../deps/macos_toolchain/bin:$$PATH \
BUILD_TARGET=macos-x86_64 \
SSL_TARGET=darwin64-x86_64 \
CC=../../deps/macos_toolchain/bin/o64-clang \
RANLIB=../../deps/macos_toolchain/bin/x86_64-apple-darwin24-ranlib \
AR=../../deps/macos_toolchain/bin/x86_64-apple-darwin24-ar \
tools/ssl-local
$(filter $(BUILD_DIR)/macosrelease-x86_64/% $(BUILD_DIR)/macosdebug-x86_64/%,$(APP_OBJS)): | $(LOCAL_DEPS)
endif
endif
ifeq ($(UNAME_S),Darwin)
LOCAL_DEPS := out/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
LOCAL_DEPS := deps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@tools/ssl-local
$(filter $(BUILD_DIR)/debug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/release/%,$(APP_OBJS)): | $(LOCAL_DEPS)
$(filter $(BUILD_DIR)/macosdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/macosrelease/%,$(APP_OBJS)): | $(LOCAL_DEPS)
endif
ifeq ($(HAVE_WIN),1)
WINDOWS_DEPS := out/openssl/$(UNAME_S)/mingw64/usr/local/lib/libssl.a
WINDOWS_DEPS := deps/openssl/mingw64/usr/local/lib/libssl.a
$(WINDOWS_DEPS):
+@BUILD_TARGET=mingw64 SSL_TARGET=mingw64 OPTIONS="--cross-compile-prefix=x86_64-w64-mingw32-" tools/ssl-local
+@tools/ssl-mingw64
$(filter $(BUILD_DIR)/win%,$(APP_OBJS)): | $(WINDOWS_DEPS)
endif
ifeq ($(UNAME_S),Darwin)
IOS_DEPS := out/openssl/ios/ios64-xcrun/usr/local/lib/libssl.a
IOS_DEPS := deps/openssl/ios/ios64-xcrun/usr/local/lib/libssl.a
$(IOS_DEPS):
+@BUILD_PLATFORM=ios BUILD_TARGET=ios64-xcrun SSL_TARGET=ios64-xcrun OPTIONS="-fPIC -Wno-macro-redefined -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)" tools/ssl-local
+@BUILD_PLATFORM=ios BUILD_TARGET=iossimulator-xcrun SSL_TARGET=iossimulator-xcrun OPTIONS="-fPIC -Wno-macro-redefined" tools/ssl-local
+@tools/ssl-ios
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(IOS_DEPS)
endif
out/macos%/tildefriends: out/macos%-arm/tildefriends out/macos%-x86_64/tildefriends
@echo [lipo] $@
@mkdir -p $(@D)
@deps/macos_toolchain/bin/lipo -create -output $@ $^
##
## Linux package targets:
##
@ -1358,6 +1258,7 @@ tarball: ## Build an all-inclusive source tarball (.tar.xz).
--exclude=deps/libsodium/test \
--exclude=deps/libuv/docs \
--exclude=deps/libuv/test \
--exclude=deps/openssl \
--exclude=deps/speedscope/*.map \
--exclude=deps/sqlite/shell.c \
--exclude=deps/zlib/contrib/vstudio \
@ -1368,20 +1269,7 @@ tarball: ## Build an all-inclusive source tarball (.tar.xz).
.PHONY: tarball
dist: ## Build versions of all distributables for release.
dist: release-apk aab out/TildeFriends-release.fdroid.apk appimage tarball out/release/tildefriends.standalone
ifeq ($(HAVE_LINUX_IOS),1)
dist: iosrelease-ipa
endif
ifeq ($(HAVE_LINUX_MACOS),1)
dist: out/macosrelease/tildefriends.standalone
endif
ifeq ($(HAVE_WIN),1)
dist: out/winrelease/tildefriends.standalone.exe
endif
ifeq ($(HAVE_CROSS_AARCH64),1)
dist: out/armrelease/tildefriends.standalone
endif
dist:
dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) out/TildeFriends-release.fdroid.apk appimage tarball out/release/tildefriends.standalone $(if $(HAVE_CROSS_AARCH64), out/armrelease/tildefriends.standalone)
@mkdir -p dist/
@echo "[cp] tildefriends-$(VERSION_NUMBER).tar.xz"
@cp out/tildefriends-$(VERSION_NUMBER).tar.xz dist/tildefriends-$(VERSION_NUMBER).tar.xz
@ -1389,10 +1277,8 @@ dist:
@cp out/TildeFriends-x86-release.zopfli.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk
@echo "[cp] TildeFriends-arm-$(VERSION_NUMBER).apk"
@cp out/TildeFriends-arm-release.zopfli.apk dist/TildeFriends-arm-$(VERSION_NUMBER).apk
@test $(HAVE_LINUX_IOS) && echo "[cp] TildeFriends-$(VERSION_NUMBER).ipa"
@test $(HAVE_LINUX_IOS) && cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
@test $(HAVE_LINUX_MACOS) && echo "[cp] tildefriends-macos-$(VERSION_NUMBER)"
@test $(HAVE_LINUX_MACOS) && cp out/macosrelease/tildefriends.standalone dist/tildefriends-macos-$(VERSION_NUMBER)
@echo "[cp] TildeFriends-$(VERSION_NUMBER).ipa"
@cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
@test $(HAVE_WIN) && echo "[cp] tildefriends-$(VERSION_NUMBER).exe"
@test $(HAVE_WIN) && cp out/winrelease/tildefriends.standalone.exe dist/tildefriends-$(VERSION_NUMBER).exe
@echo "[cp] TildeFriends-$(VERSION_NUMBER).aab"
@ -1414,17 +1300,6 @@ dist-test: dist ## Exercise some built distributable files, making sure they wor
@rm -rf tildefriends-$(VERSION_NUMBER)
.PHONY: dist-test
dist-ios: iosrelease-app
rm -rfv out/Payload out/tildefriends.ipa
mkdir -p out/Payload/tildefriends.app
cp -avR out/tildefriends-iosrelease.app/* out/Payload/tildefriends.app/
cp src/ios/tildefriends.png out/Payload/tildefriends.app/
cp src/ios/icons/Assets.car out/Payload/tildefriends.app/
cp src/ios/distribution.mobileprovision out/Payload/tildefriends.app/embedded.mobileprovision
xcrun -sdk iphoneos codesign -f -s 'Apple Distribution' --entitlements src/ios/Entitlements.plist --generate-entitlement-der out/Payload/tildefriends.app
cd out; zip -r tildefriends.ipa Payload; cd ..
xcrun -sdk iphoneos altool --upload-app -f out/tildefriends.ipa -t ios -u $$(cat .keys/altool-user) -p $$(cat .keys/altool-password)
##
## Targets for tidying up:
##

@ -1,16 +1,18 @@
# Tilde Friends
Tilde Friends participates in the Secure Scuttlebutt decentralized social
network while also functioning as a platform for making, sharing, and running
web applications.
Tilde Friends is a tool for making and sharing.
A public instance lives at https://www.tildefriends.net/.
It is both a peer-to-peer social network client, participating in Secure
Scuttlebutt, as well as a platform for writing and running web applications.
## Goals
1. Be the fanciest, best-maintained Secure Scuttlebutt client in town.
1. Make it easy to make, share, and run all sorts of applications while
respecting the privacy and safety of your data.
1. Make it easy and fun to run all sorts of web applications.
2. Provide security that is easy to understand and protects your data.
3. Make creating and sharing web applications accessible to anyone with a
browser.
## Getting the Source
@ -38,7 +40,8 @@ dependencies in the right places.
### Requirements
System OpenSSL libraries are assumed to be available on Haiku and OpenSSL.
On Linux only, system OpenSSL libraries (`libssl-dev`, in debian-speak) are
assumed to be available.
On MacOS, Xcode's command-line tools are expected to be available.
@ -60,12 +63,13 @@ repository root as the current working directory. `tildefriends -h` lists
further options.
The first user to create an account and log in will be granted administrative
privileges. Further administration can be done in the `admin` app at
privileges. Further administration can be done at
<http://localhost:12345/~core/admin/>.
## Documentation
Docs live here: <https://docs.tildefriends.net/>.
Docs are a work in progress:
<https://dev.tildefriends.net/cory/tildefriends/wiki>.
## License

@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "🎛",
"previous": "&kmKNyb/uaXNb24gCinJtfS8iWx4cLUWdtl0y2DwEUas=.sha256"
"previous": "&R49FywYF8CXPhoSEydLbSCgvCddeyTiBwGuDU/gqY+M=.sha256"
}

@ -72,7 +72,7 @@ ${description.value}</textarea
</button>
</li>
`;
} else if (description.type != 'hidden') {
} else {
return html`
<li class="w3-row">
<label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold">${title_case(key)}</label>

@ -1,9 +1,7 @@
async function main() {
print(core.url);
let host = core.url.match(/.*?\/\/([^:/]*)/)[1];
let port = await ssb.port();
let host = core.url.match(/.*\/\/(.*?)\//)[1];
let id = (await ssb.getServerIdentity()).substring(1);
let room = `net:${host}:${port}~shs:${id}:SSB+Room+SK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
let room = `net:${host}:${ssb.port}~shs:${id}:SSB+Room+SK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
await app.setDocument(`
<body style="color: #fff">
<h1>Server</h1>

@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "🦀",
"previous": "&jAAzd36Nmpw0sRA1Dx9wLiIwGX+q//+S/Han+RLlEOw=.sha256"
"previous": "&m5ZrkzoyAjv7AY6AukzVoWIyUFUL97uuDDWCtxCloAU=.sha256"
}

@ -20,7 +20,6 @@ class TfElement extends LitElement {
channels_latest: {type: Object},
guest: {type: Boolean},
url: {type: String},
private_messages: {type: Array},
};
}
@ -335,7 +334,7 @@ class TfElement extends LitElement {
JSON.stringify(cache)
);
}
return [cache.latest, cache.messages];
return cache.latest;
}
async load_channels_latest(following) {
@ -379,11 +378,9 @@ class TfElement extends LitElement {
start_time = new Date();
latest_private.then(function (latest) {
self.channels_latest = Object.assign({}, self.channels_latest, {
'🔐': latest[0],
'🔐': latest,
});
console.log('private took', (new Date() - start_time) / 1000.0);
console.log(latest);
self.private_messages = latest[1];
});
}
@ -516,7 +513,6 @@ class TfElement extends LitElement {
.channels_unread=${this.channels_unread}
@channelsetunread=${this.channel_set_unread}
.connections=${this.connections}
.private_messages=${this.private_messages}
></tf-tab-news>
`;
} else if (this.tab === 'connections') {

@ -775,9 +775,7 @@ class TfMessageElement extends LitElement {
}
} else {
return this.render_small_frame(
html`<div class="w3-container">
<p><b>type</b>: ${content.type}</p>
</div>`
html`<div class="w3-container"><b>type</b>: ${content.type}</div>`
);
}
} else if (typeof this.message.content == 'string') {

@ -62,7 +62,6 @@ class TfProfileElement extends LitElement {
}
modify(change) {
let self = this;
tfrpc.rpc
.appendMessage(
this.whoami,
@ -74,10 +73,6 @@ class TfProfileElement extends LitElement {
change
)
)
.then(function () {
self._follow_whoami = undefined;
self.load();
})
.catch(function (error) {
alert(error?.message);
});

@ -17,7 +17,6 @@ class TfTabNewsFeedElement extends LitElement {
loading: {type: Number},
time_range: {type: Array},
time_loading: {type: Array},
private_messages: {type: Array},
};
}
@ -149,13 +148,13 @@ class TfTabNewsFeedElement extends LitElement {
`
SELECT TRUE AS is_primary, messages.rowid, messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
FROM messages
JOIN json_each(?1) AS private_messages ON messages.id = private_messages.value
JOIN json_each(?1) AS following ON messages.author = following.value
WHERE
(?2 IS NULL OR (messages.timestamp >= ?2)) AND messages.timestamp < ?3 AND
json(messages.content) LIKE '"%'
ORDER BY messages.sequence DESC LIMIT 20
`,
[JSON.stringify(this.private_messages), start_time, end_time]
[JSON.stringify(this.following), start_time, end_time]
);
result = (await this.decrypt(result)).filter((x) => x.decrypted);
} else {
@ -299,7 +298,7 @@ class TfTabNewsFeedElement extends LitElement {
let now = new Date().valueOf();
let start_time = now - 24 * 60 * 60 * 1000;
this.start_time = start_time;
this.time_range = [now + 24 * 60 * 60 * 1000, now + 24 * 60 * 60 * 1000];
this.time_range = [this.start_time, now + 24 * 60 * 60 * 1000];
messages = await this.fetch_messages(null, this.time_range[1]);
this.update_time_range_from_messages(
messages.filter((x) => x.timestamp < this.time_range[1])

@ -23,7 +23,6 @@ class TfTabNewsElement extends LitElement {
channels_unread: {type: Object},
channels_latest: {type: Object},
connections: {type: Array},
private_messages: {type: Array},
};
}
@ -338,7 +337,6 @@ class TfTabNewsElement extends LitElement {
@tf-expand=${this.on_expand}
.channels_unread=${this.channels_unread}
.channels_latest=${this.channels_latest}
.private_messages=${this.private_messages}
></tf-tab-news-feed>
</div>
</div>

@ -21,9 +21,7 @@ class TfUserElement extends LitElement {
render() {
let user = this.users[this.id];
let shape =
user?.follow_depth === undefined || user.follow_depth >= 2
? 'w3-circle'
: 'w3-round';
!user?.follow_depth || user.follow_depth >= 2 ? 'w3-circle' : 'w3-round';
let image = html`<span
class=${'w3-theme-l4 ' + shape}
style="display: inline-block; width: 2em; height: 2em; text-align: center; line-height: 2em"

@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "👋",
"previous": "&wAb7J6E35xEXpiXsQ6t1RaWTGIvlatUnyH8ipF6pVic=.sha256"
"previous": "&7gFmLW5zSMhmxWWY1+jeRcHdullgujSqGJg94lVgr1k=.sha256"
}

@ -10,6 +10,17 @@
<link rel="stylesheet" href="brands.min.css" />
<style>
body,
h1,
h2,
h3,
h4,
h5 {
font-family: 'Poppins', sans-serif;
}
body {
font-size: 16px;
}
img {
margin-bottom: -8px;
}
@ -28,14 +39,11 @@
<b>😎 Tilde Friends</b>
</h1>
<h1 class="w3-xxlarge w3-text-green">
<b
>the Secure Scuttlebutt decentralized social network client that's
<i>fancy🎩</i></b
>
<b>Make apps and friends from the comfort of your web browser.</b>
</h1>
<p>
In addition to participating in Secure Scuttlebutt, Tilde Friends is
a platform for building, running, and sharing applications.
Tilde Friends is a platform for building, running, and sharing web
applications.
</p>
<p>
Available for lots of devices:
@ -52,7 +60,7 @@
>
<a
class="w3-button w3-black w3-padding-large"
href="https://www.tildefriends.net/~core/ssb/"
href="https://www.tildefriends.net/~cory/apps/"
><i class="fa fa-link"></i> Try It</a
>
<a
@ -60,11 +68,6 @@
href="https://dev.tildefriends.net/"
><i class="fa fa-mug-hot"></i> Development</a
>
<a
class="w3-button w3-black w3-padding-large"
href="https://docs.tildefriends.net/"
><i class="fa fa-book"></i> Documentation</a
>
<p>
<a
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
@ -220,15 +223,16 @@
<!-- Technlology Section -->
<div class="w3-container w3-padding-64 w3-light-grey w3-center">
<h1 class="w3-jumbo"><b>Built the Old Fashioned Way</b></h1>
<h1 class="w3-jumbo"><b>Boring Technology</b></h1>
<p>
Tilde Friends strives to use only simple and widely adopted dependencies
in order to keep it easy to build for all sorts of platforms and
maintainable for a very long time.
Tilde Friends is built using boring, trusted tech. Unless a better
reason presents itself, it strives to use only simple and widely adopted
dependencies in order to keep it easy to build for all sorts of
platforms and maintainable for a very long time.
</p>
<p>
Though of course for building Tilde Friends apps, you are free to use
whatever fits on top.
whatever fits.
</p>
<div class="w3-row" style="margin-top: 64px">

@ -81,8 +81,9 @@ App.prototype.send = function (message) {
* TODOC
* @param {*} request
* @param {*} response
* @param {*} client
*/
exports.app_socket = async function socket(request, response) {
async function socket(request, response, client) {
let process;
let options = {};
let credentials = await httpd.auth_query(request.headers);
@ -244,6 +245,6 @@ exports.app_socket = async function socket(request, response) {
};
response.upgrade(100, {});
};
}
export {App};
export {socket, App};

@ -1,4 +1,5 @@
import * as app from './app.js';
import * as form from './form.js';
import * as http from './http.js';
let gProcesses = {};
@ -470,6 +471,7 @@ async function getProcessBlob(blobId, key, options) {
imports.ssb = Object.fromEntries(
Object.keys(ssb).map((key) => [key, ssb[key].bind(ssb)])
);
imports.ssb.port = tildefriends.ssb_port;
imports.ssb.createIdentity = () => process.createIdentity();
imports.ssb.addIdentity = function (id) {
if (
@ -833,4 +835,59 @@ exports.callAppHandler = async function callAppHandler(
response.end(answer?.data);
};
/**
* TODOC
*/
loadSettings()
.then(function (settings) {
if (tildefriends.https_port && settings.http_redirect) {
httpd.set_http_redirect(settings.http_redirect);
}
httpd.all('/app/socket', app.socket);
let port = httpd.start(tildefriends.http_port);
if (tildefriends.args.out_http_port_file) {
print('Writing the port file.');
File.writeFile(
tildefriends.args.out_http_port_file,
port.toString() + '\n'
)
.then(function (r) {
print(
'Wrote the port file:',
tildefriends.args.out_http_port_file,
r
);
})
.catch(function () {
print('Failed to write the port file.');
});
}
if (tildefriends.https_port) {
async function start_tls() {
const kCertificatePath = 'data/httpd/certificate.pem';
const kPrivateKeyPath = 'data/httpd/privatekey.pem';
let privateKey;
let certificate;
try {
privateKey = utf8Decode(await File.readFile(kPrivateKeyPath));
certificate = utf8Decode(await File.readFile(kCertificatePath));
} catch (e) {
print(`TLS disabled (${e.message}).`);
return;
}
let context = new TlsContext();
context.setPrivateKey(privateKey);
context.setCertificate(certificate);
httpd.start(tildefriends.https_port, context);
}
start_tls();
}
})
.catch(function (error) {
print('Failed to load settings.');
printError({print: print}, error);
exit(1);
});
export {invoke, getProcessBlob};

44
core/form.js Normal file

@ -0,0 +1,44 @@
/**
* TODOC
* @param {*} encoded
* @returns
*/
function decode(encoded) {
let result = '';
for (let i = 0; i < encoded.length; i++) {
let c = encoded[i];
if (c == '+') {
result += ' ';
} else if (c == '%') {
result += String.fromCharCode(parseInt(encoded.slice(i + 1, i + 3), 16));
i += 2;
} else {
result += c;
}
}
return result;
}
/**
* TODOC
* @param {*} encoded
* @param {*} initial
* @returns
*/
function decodeForm(encoded, initial) {
let result = initial || {};
if (encoded) {
encoded = encoded.trim();
let items = encoded.split('&');
for (let i = 0; i < items.length; i++) {
let item = items[i];
let equals = item.indexOf('=');
let key = decode(item.slice(0, equals));
let value = decode(item.slice(equals + 1));
result[key] = value;
}
}
return result;
}
export {decodeForm};

@ -1,8 +1,4 @@
# How to upgrade to a newer version
# - On the june and december release, you'll have to update nixpkgs to the current branch
# Change `nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";`
# to the latest release (see https://nixos.org/)
# - Run `$ nix flake update`
# - Comment `src.hash`
# - Change `version`
# - Run `$ nix build`
@ -25,32 +21,31 @@
}:
pkgs.stdenv.mkDerivation rec {
pname = "tildefriends";
version = "0.0.27.1";
version = "0.0.27";
src = pkgs.fetchFromGitea {
domain = "dev.tildefriends.net";
owner = "cory";
repo = "tildefriends";
rev = "v${version}";
hash = "sha256-3t1m9ZomQF3DteWyALJWrnCq0EAROEK8shKXh6Ao38c=";
hash = "sha256-NhoTBWYsWc206f1+M9haxHSJU6ZSGoP5nPStymAIyRk=";
fetchSubmodules = true;
};
nativeBuildInputs = with pkgs; [
bash
glibc
gnumake
openssl
which
];
buildInputs = with pkgs; [
glibc
openssl
which
];
buildPhase = ''
make -j $NIX_BUILD_CORES release USE_SYSTEM_SSL=1
make -j $NIX_BUILD_CORES release
'';
installPhase = ''

File diff suppressed because one or more lines are too long

194
deps/codemirror_src/package-lock.json generated vendored

@ -19,9 +19,9 @@
}
},
"node_modules/@codemirror/autocomplete": {
"version": "6.18.6",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
"integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
"version": "6.18.4",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.4.tgz",
"integrity": "sha512-sFAphGQIqyQZfP2ZBsSHV7xQvo9Py0rV0dW7W3IMRdS+zDuNb2l3no78CvUaWKGfzFjI4FTrLdUSj86IGb2hRA==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
@ -69,9 +69,9 @@
}
},
"node_modules/@codemirror/lang-javascript": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.3.tgz",
"integrity": "sha512-8PR3vIWg7pSu7ur8A07pGiYHgy3hHj+mRYRCSG8q+mPIrl0F02rgpGv+DsQTHRTc30rydOsf5PZ7yjKFg2Ackw==",
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz",
"integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.6.0",
@ -115,9 +115,9 @@
}
},
"node_modules/@codemirror/search": {
"version": "6.5.9",
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.9.tgz",
"integrity": "sha512-7DdQ9aaZMMxuWB1u6IIFWWuK9NocVZwvo4nG8QjJTS6oZGvteoLSiXw3EbVZVlO08Ri2ltO89JVInMpfcJxhtg==",
"version": "6.5.8",
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.8.tgz",
"integrity": "sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
@ -125,9 +125,9 @@
}
},
"node_modules/@codemirror/state": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.1.tgz",
"integrity": "sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==",
"dependencies": {
"@marijn/find-cluster-break": "^1.0.0"
}
@ -144,9 +144,9 @@
}
},
"node_modules/@codemirror/view": {
"version": "6.36.3",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.3.tgz",
"integrity": "sha512-N2bilM47QWC8Hnx0rMdDxO2x2ImJ1FvZWXubwKgjeoOrWwEiFrtpA7SFHcuZ+o2Ze2VzbkgbzWVj4+V18LVkeg==",
"version": "6.36.2",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.2.tgz",
"integrity": "sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==",
"dependencies": {
"@codemirror/state": "^6.5.0",
"style-mod": "^4.1.0",
@ -344,9 +344,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz",
"integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz",
"integrity": "sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==",
"cpu": [
"arm"
],
@ -356,9 +356,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz",
"integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.1.tgz",
"integrity": "sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==",
"cpu": [
"arm64"
],
@ -368,9 +368,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz",
"integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.1.tgz",
"integrity": "sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==",
"cpu": [
"arm64"
],
@ -380,9 +380,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz",
"integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz",
"integrity": "sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==",
"cpu": [
"x64"
],
@ -392,9 +392,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz",
"integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.1.tgz",
"integrity": "sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==",
"cpu": [
"arm64"
],
@ -404,9 +404,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz",
"integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.1.tgz",
"integrity": "sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==",
"cpu": [
"x64"
],
@ -416,9 +416,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz",
"integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.1.tgz",
"integrity": "sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==",
"cpu": [
"arm"
],
@ -428,9 +428,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz",
"integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.1.tgz",
"integrity": "sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==",
"cpu": [
"arm"
],
@ -440,9 +440,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz",
"integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.1.tgz",
"integrity": "sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==",
"cpu": [
"arm64"
],
@ -452,9 +452,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz",
"integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.1.tgz",
"integrity": "sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==",
"cpu": [
"arm64"
],
@ -464,9 +464,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz",
"integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.1.tgz",
"integrity": "sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==",
"cpu": [
"loong64"
],
@ -476,9 +476,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz",
"integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.1.tgz",
"integrity": "sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==",
"cpu": [
"ppc64"
],
@ -488,9 +488,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz",
"integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.1.tgz",
"integrity": "sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==",
"cpu": [
"riscv64"
],
@ -500,9 +500,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz",
"integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.1.tgz",
"integrity": "sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==",
"cpu": [
"s390x"
],
@ -512,9 +512,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz",
"integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz",
"integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==",
"cpu": [
"x64"
],
@ -524,9 +524,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz",
"integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.1.tgz",
"integrity": "sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==",
"cpu": [
"x64"
],
@ -536,9 +536,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz",
"integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.1.tgz",
"integrity": "sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==",
"cpu": [
"arm64"
],
@ -548,9 +548,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz",
"integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.1.tgz",
"integrity": "sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==",
"cpu": [
"ia32"
],
@ -560,9 +560,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz",
"integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.1.tgz",
"integrity": "sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==",
"cpu": [
"x64"
],
@ -733,9 +733,9 @@
}
},
"node_modules/rollup": {
"version": "4.34.8",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz",
"integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==",
"version": "4.32.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.1.tgz",
"integrity": "sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==",
"dependencies": {
"@types/estree": "1.0.6"
},
@ -747,25 +747,25 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.34.8",
"@rollup/rollup-android-arm64": "4.34.8",
"@rollup/rollup-darwin-arm64": "4.34.8",
"@rollup/rollup-darwin-x64": "4.34.8",
"@rollup/rollup-freebsd-arm64": "4.34.8",
"@rollup/rollup-freebsd-x64": "4.34.8",
"@rollup/rollup-linux-arm-gnueabihf": "4.34.8",
"@rollup/rollup-linux-arm-musleabihf": "4.34.8",
"@rollup/rollup-linux-arm64-gnu": "4.34.8",
"@rollup/rollup-linux-arm64-musl": "4.34.8",
"@rollup/rollup-linux-loongarch64-gnu": "4.34.8",
"@rollup/rollup-linux-powerpc64le-gnu": "4.34.8",
"@rollup/rollup-linux-riscv64-gnu": "4.34.8",
"@rollup/rollup-linux-s390x-gnu": "4.34.8",
"@rollup/rollup-linux-x64-gnu": "4.34.8",
"@rollup/rollup-linux-x64-musl": "4.34.8",
"@rollup/rollup-win32-arm64-msvc": "4.34.8",
"@rollup/rollup-win32-ia32-msvc": "4.34.8",
"@rollup/rollup-win32-x64-msvc": "4.34.8",
"@rollup/rollup-android-arm-eabi": "4.32.1",
"@rollup/rollup-android-arm64": "4.32.1",
"@rollup/rollup-darwin-arm64": "4.32.1",
"@rollup/rollup-darwin-x64": "4.32.1",
"@rollup/rollup-freebsd-arm64": "4.32.1",
"@rollup/rollup-freebsd-x64": "4.32.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.32.1",
"@rollup/rollup-linux-arm-musleabihf": "4.32.1",
"@rollup/rollup-linux-arm64-gnu": "4.32.1",
"@rollup/rollup-linux-arm64-musl": "4.32.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.32.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.32.1",
"@rollup/rollup-linux-riscv64-gnu": "4.32.1",
"@rollup/rollup-linux-s390x-gnu": "4.32.1",
"@rollup/rollup-linux-x64-gnu": "4.32.1",
"@rollup/rollup-linux-x64-musl": "4.32.1",
"@rollup/rollup-win32-arm64-msvc": "4.32.1",
"@rollup/rollup-win32-ia32-msvc": "4.32.1",
"@rollup/rollup-win32-x64-msvc": "4.32.1",
"fsevents": "~2.3.2"
}
},
@ -840,9 +840,9 @@
}
},
"node_modules/terser": {
"version": "5.39.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
"integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
"version": "5.37.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz",
"integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==",
"dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",

2
deps/libbacktrace vendored

@ -1 +1 @@
Subproject commit 0034e33946824057b48c5e686a3aefc761b37384
Subproject commit d48f84034ce3e53e501d10593710d025cb1121db

2
deps/openssl_src vendored

@ -1 +1 @@
Subproject commit a26d85337dbdcd33c971f38eb3fa5150e75cea87
Subproject commit 98acb6b02839c609ef5b837794e08d906d965335

@ -11,7 +11,7 @@
<link rel="icon" type="image/x-icon" href="favicon-FOKUP5Y5.ico">
</head>
<body>
<script src="speedscope-VHEG2FVF.js"></script>
<script src="speedscope-CAEVGCWN.js"></script>

@ -1,3 +1,3 @@
speedscope@1.22.2
Sat Feb 15 13:02:38 PST 2025
1c254dcb3e2b4f6d921340d20e972d9d27b788f4
speedscope@1.22.0
Thu Jan 16 16:49:47 PST 2025
bd7b44a0a7d63375ee6ea0a0d1b96e65a456642f

File diff suppressed because one or more lines are too long

24
deps/sqlite/shell.c vendored

@ -357,11 +357,6 @@ void sqlite3_fsetmode(FILE *stream, int mode);
** use O_U8TEXT when writing to the Windows console (or anything
** else for which _isatty() returns true) and to use O_BINARY or O_TEXT
** for all other output channels.
**
** The SQLITE_USE_W32_FOR_CONSOLE_IO macro is also available. If
** defined, it forces the use of Win32 APIs for all console I/O, both
** input and output. This is necessary for some non-Microsoft run-times
** that implement stdio differently from Microsoft/Visual-Studio.
*/
#if defined(SQLITE_U8TEXT_ONLY)
# define UseWtextForOutput(fd) 1
@ -464,10 +459,10 @@ char *sqlite3_fgets(char *buf, int sz, FILE *in){
*/
wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
if( b1==0 ) return 0;
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
#ifndef SQLITE_USE_STDIO_FOR_CONSOLE
DWORD nRead = 0;
if( IsConsole(in)
&& ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz-1, &nRead, 0)
&& ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz, &nRead, 0)
){
b1[nRead] = 0;
}else
@ -542,7 +537,7 @@ int sqlite3_fputs(const char *z, FILE *out){
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
b1[sz] = 0;
#ifdef SQLITE_USE_W32_FOR_CONSOLE_IO
#ifndef SQLITE_STDIO_FOR_CONSOLE
DWORD nWr = 0;
if( IsConsole(out)
&& WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),b1,sz,&nWr,0)
@ -552,9 +547,8 @@ int sqlite3_fputs(const char *z, FILE *out){
}else
#endif
{
/* As long as SQLITE_USE_W32_FOR_CONSOLE_IO is not defined, or for
** non-console I/O even if that macro is defined, write using the
** standard library. */
/* For non-console I/O, or if SQLITE_USE_STDIO_FOR_CONSOLE is defined
** then write using the standard library. */
_setmode(_fileno(out), _O_U8TEXT);
if( UseBinaryWText(out) ){
piecemealOutput(b1, sz, out);
@ -5320,7 +5314,7 @@ static u8* fromBase64( char *pIn, int ncIn, u8 *pOut ){
deliberate_fall_through; /* FALLTHRU */
case 1:
pOut[0] = (qv>>16) & 0xff;
break;
deliberate_fall_through; /* FALLTHRU */
}
pOut += nbo;
}
@ -17205,7 +17199,7 @@ static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_DLCLOSE);
vfstrace_printf(pInfo, "%s.xDlClose()\n", pInfo->zVfsName);
vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName);
pRoot->xDlClose(pRoot, pHandle);
}
@ -28891,9 +28885,6 @@ static int do_meta_command(char *zLine, ShellState *p){
const char *zName;
int op;
} aDbConfig[] = {
{ "attach_create", SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE },
{ "attach_write", SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE },
{ "comments", SQLITE_DBCONFIG_ENABLE_COMMENTS },
{ "defensive", SQLITE_DBCONFIG_DEFENSIVE },
{ "dqs_ddl", SQLITE_DBCONFIG_DQS_DDL },
{ "dqs_dml", SQLITE_DBCONFIG_DQS_DML },
@ -31644,7 +31635,6 @@ static int do_meta_command(char *zLine, ShellState *p){
{ 0x04000000, 1, "NullUnusedCols" },
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0x20000000, 1, "StarQuery" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;

3968
deps/sqlite/sqlite3.c vendored

File diff suppressed because it is too large Load Diff

153
deps/sqlite/sqlite3.h vendored

@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.49.1"
#define SQLITE_VERSION_NUMBER 3049001
#define SQLITE_SOURCE_ID "2025-02-18 13:38:58 873d4e274b4988d260ba8354a9718324a1c26187a4ab4c1cc0227c03d0f10e70"
#define SQLITE_VERSION "3.48.0"
#define SQLITE_VERSION_NUMBER 3048000
#define SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010"
/*
** CAPI3REF: Run-Time Library Version Numbers
@ -2211,15 +2211,7 @@ struct sqlite3_mem_methods {
** CAPI3REF: Database Connection Configuration Options
**
** These constants are the available integer configuration options that
** can be passed as the second parameter to the [sqlite3_db_config()] interface.
**
** The [sqlite3_db_config()] interface is a var-args functions. It takes a
** variable number of parameters, though always at least two. The number of
** parameters passed into sqlite3_db_config() depends on which of these
** constants is given as the second parameter. This documentation page
** refers to parameters beyond the second as "arguments". Thus, when this
** page says "the N-th argument" it means "the N-th parameter past the
** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()".
** can be passed as the second argument to the [sqlite3_db_config()] interface.
**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
@ -2231,14 +2223,8 @@ struct sqlite3_mem_methods {
** <dl>
** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
** configuration of the lookaside memory allocator within a database
** connection.
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
** in the [DBCONFIG arguments|usual format].
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
** should have a total of five parameters.
** <dd> ^This option takes three additional arguments that determine the
** [lookaside memory allocator] configuration for the [database connection].
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
** pointer to a memory buffer to use for lookaside memory.
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
@ -2261,8 +2247,7 @@ struct sqlite3_mem_methods {
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
** <dd> ^This option is used to enable or disable the enforcement of
** [foreign key constraints]. This is the same setting that is
** enabled or disabled by the [PRAGMA foreign_keys] statement.
** [foreign key constraints]. There should be two additional arguments.
** The first argument is an integer which is 0 to disable FK enforcement,
** positive to enable FK enforcement or negative to leave FK enforcement
** unchanged. The second parameter is a pointer to an integer into which
@ -2284,13 +2269,13 @@ struct sqlite3_mem_methods {
** <p>Originally this option disabled all triggers. ^(However, since
** SQLite version 3.35.0, TEMP triggers are still allowed even if
** this option is off. So, in other words, this option now only disables
** triggers in the main database schema or in the schemas of [ATTACH]-ed
** triggers in the main database schema or in the schemas of ATTACH-ed
** databases.)^ </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
** There must be two additional arguments.
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable views,
** positive to enable views or negative to leave the setting unchanged.
** The second parameter is a pointer to an integer into which
@ -2309,7 +2294,7 @@ struct sqlite3_mem_methods {
** <dd> ^This option is used to enable or disable the
** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension.
** There must be two additional arguments.
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
** positive to enable fts3_tokenizer() or negative to leave the setting
** unchanged.
@ -2324,7 +2309,7 @@ struct sqlite3_mem_methods {
** interface independently of the [load_extension()] SQL function.
** The [sqlite3_enable_load_extension()] API enables or disables both the
** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
** There must be two additional arguments.
** There should be two additional arguments.
** When the first argument to this interface is 1, then only the C-API is
** enabled and the SQL function remains disabled. If the first argument to
** this interface is 0, then both the C-API and the SQL function are disabled.
@ -2338,30 +2323,23 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
** <dd> ^This option is used to change the name of the "main" database
** schema. This option does not follow the
** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format].
** This option takes exactly one additional argument so that the
** [sqlite3_db_config()] call has a total of three parameters. The
** extra argument must be a pointer to a constant UTF8 string which
** will become the new schema name in place of "main". ^SQLite does
** not make a copy of the new main schema name string, so the application
** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME
** is unchanged until after the database connection closes.
** schema. ^The sole argument is a pointer to a constant UTF8 string
** which will become the new schema name in place of "main". ^SQLite
** does not make a copy of the new main schema name string, so the application
** must ensure that the argument passed into this DBCONFIG option is unchanged
** until after the database connection closes.
** </dd>
**
** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
** <dd> Usually, when a database in [WAL mode] is closed or detached from a
** database handle, SQLite checks if if there are other connections to the
** same database, and if there are no other database connection (if the
** connection being closed is the last open connection to the database),
** then SQLite performs a [checkpoint] before closing the connection and
** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can
** be used to override that behavior. The first argument passed to this
** operation (the third parameter to [sqlite3_db_config()]) is an integer
** which is positive to disable checkpoints-on-close, or zero (the default)
** to enable them, and negative to leave the setting unchanged.
** The second argument (the fourth parameter) is a pointer to an integer
** <dd> Usually, when a database in wal mode is closed or detached from a
** database handle, SQLite checks if this will mean that there are now no
** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
** override this behavior. The first parameter passed to this operation
** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer
** into which is written 0 or 1 to indicate whether checkpoints-on-close
** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd>
@ -2522,7 +2500,7 @@ struct sqlite3_mem_methods {
** statistics. For statistics to be collected, the flag must be set on
** the database handle both when the SQL statement is prepared and when it
** is stepped. The flag is set (collection of statistics is enabled)
** by default. <p>This option takes two arguments: an integer and a pointer to
** by default. This option takes two arguments: an integer and a pointer to
** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the statement scanstatus option. If the second argument
** is not NULL, then the value of the statement scanstatus setting after
@ -2536,7 +2514,7 @@ struct sqlite3_mem_methods {
** in which tables and indexes are scanned so that the scans start at the end
** and work toward the beginning rather than starting at the beginning and
** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
** same as setting [PRAGMA reverse_unordered_selects]. <p>This option takes
** same as setting [PRAGMA reverse_unordered_selects]. This option takes
** two arguments which are an integer and a pointer to an integer. The first
** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
** reverse scan order flag, respectively. If the second argument is not NULL,
@ -2545,76 +2523,7 @@ struct sqlite3_mem_methods {
** first argument.
** </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]]
** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE</dt>
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables
** the ability of the [ATTACH DATABASE] SQL command to create a new database
** file if the database filed named in the ATTACH command does not already
** exist. This ability of ATTACH to create a new database is enabled by
** default. Applications can disable or reenable the ability for ATTACH to
** create new database files using this DBCONFIG option.<p>
** This option takes two arguments which are an integer and a pointer
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the attach-create flag, respectively. If the second
** argument is not NULL, then 0 or 1 is written into the integer that the
** second argument points to depending on if the attach-create flag is set
** after processing the first argument.
** </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]]
** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE</dt>
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the
** ability of the [ATTACH DATABASE] SQL command to open a database for writing.
** This capability is enabled by default. Applications can disable or
** reenable this capability using the current DBCONFIG option. If the
** the this capability is disabled, the [ATTACH] command will still work,
** but the database will be opened read-only. If this option is disabled,
** then the ability to create a new database using [ATTACH] is also disabled,
** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]
** option.<p>
** This option takes two arguments which are an integer and a pointer
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the ability to ATTACH another database for writing,
** respectively. If the second argument is not NULL, then 0 or 1 is written
** into the integer to which the second argument points, depending on whether
** the ability to ATTACH a read/write database is enabled or disabled
** after processing the first argument.
** </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]]
** <dt>SQLITE_DBCONFIG_ENABLE_COMMENTS</dt>
** <dd>The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the
** ability to include comments in SQL text. Comments are enabled by default.
** An application can disable or reenable comments in SQL text using this
** DBCONFIG option.<p>
** This option takes two arguments which are an integer and a pointer
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the ability to use comments in SQL text,
** respectively. If the second argument is not NULL, then 0 or 1 is written
** into the integer that the second argument points to depending on if
** comments are allowed in SQL text after processing the first argument.
** </dd>
**
** </dl>
**
** [[DBCONFIG arguments]] <h3>Arguments To SQLITE_DBCONFIG Options</h3>
**
** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the
** overall call to [sqlite3_db_config()] has a total of four parameters.
** The first argument (the third parameter to sqlite3_db_config()) is a integer.
** The second argument is a pointer to an integer. If the first argument is 1,
** then the option becomes enabled. If the first integer argument is 0, then the
** option is disabled. If the first argument is -1, then the option setting
** is unchanged. The second argument, the pointer to an integer, may be NULL.
** If the second argument is not NULL, then a value of 0 or 1 is written into
** the integer to which the second argument points, depending on whether the
** setting is disabled or enabled after applying any changes specified by
** the first argument.
**
** <p>While most SQLITE_DBCONFIG options use the argument format
** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME]
** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the
** documentation of those exceptional options for details.
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
@ -2636,10 +2545,7 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */
#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */
#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@ -10842,9 +10748,8 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
/*
** CAPI3REF: Serialize a database
**
** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
** memory that is a serialization of the S database on
** [database connection] D. If S is a NULL pointer, the main database is used.
** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory
** that is a serialization of the S database on [database connection] D.
** If P is not a NULL pointer, then the size of the database in bytes
** is written into *P.
**

1
deps/zsign vendored

@ -1 +0,0 @@
Subproject commit d995d539ff16f28d985f5b2a1c62dd4eb9f029ea

1
docs Submodule

@ -0,0 +1 @@
Subproject commit a40758cc4bef5ded4d214c2acf1c95e554e20564

@ -1,63 +0,0 @@
# App Development Cheat Sheet
Making apps for the impatient tilde friend.
## Prerequisites
- either run your own instance or use [tildefriends.net](https://www.tildefriends.net/)
- register and login
- [optional] use the `ssb` app to create yourself an SSB identity
## Development Process
1. hit the `edit` link from any app or new app URL
2. make sure the path in the text box is under your username: `/~username/app/`
3. write server-side code in `app.js`
4. click the `save` button or press the save hotkey (Alt+S or _[browser-specific modifiers]_+S)
5. see the app reload on the right side
## Output
- `app.setDocument(html)` - send HTML to the browser
- `print(...)` - send values to the browser's developer console
## Persistence
- `app.localStorageGet(key)` -> `value`
- `app.localStorageSet(key, value)`
- `database()`, `shared_database(key)`, `my_shared_database(package, key)`
- `db.get(key)` -> `value`
- `db.set(key, value)`
- `db.exchange(key, expected, value)` -> `exchanged`
- `db.remove(key)`
- `db.getAll()` -> `[key1, ...]`
- `db.getLike(pattern)` -> `{key1: value1, ...}`
## SSB
- `ssb.createIdentity()` -> `id`
- `ssb.getIdentities()` -> `[id1, ...]`
- `ssb.appendMessageWithIdentity(id, content)` -> `message_id`
- `ssb.blobStore(blob)` -> `blob_id`
- `ssb.blobGet(id)` -> `blob`
- `ssb.sqlAsync(query, args, row_callback)`
## TF-RPC
Stock helper code for calling functions across the web server and browser boundary.
- on the server: `import * as tfrpc from "/tfrpc.js";`
- in the browser: `import * as tfrpc from "/static/tfrpc.js";`
- either direction:
- register a function: `tfrpc.register(function my_function() {});`
- call a remote function: `let promise = tfrpc.rpc.my_function();`
## Share
- give out web links: [https://www.tildefriends.net/~cory/screwble/](https://www.tildefriends.net/~cory/screwble/)
- use the `Attach App` button when composing a post in [the SSB app](https://www.tildefriends.net/~core/ssb/)
## More Docs
- [api reference](https://www.tildefriends.net/~cory/api/)
- [source code](https://dev.tildefriends.net/cory/tildefriends/releases)

@ -1,166 +0,0 @@
# App Development Guide
A Tilde Friends application starts with code that runs on a Tilde Friends server, possibly far away from where you wrote it, in a little JavaScript environment, in its own restricted process, with the only access to the outside world being the ability to send messages to the server. This document gives some recipes showing how that can be used to build a functional user-facing application in light of the unique constraints present.
## Example 1: Hello, world!
Of course we must start with a classic.
### app.js
```js
app.setDocument('<h1 style="color: #fff">Hello, world!</h1>');
```
### Output
<h1 style="color: #fff">Hello, world!</h1>
### Explanation
At a glance, this might seem mundane, but for it to work:
- the server starts a real process for your app and loads your code into it
- your code runs
- `app.setDocument()` sends a message back to the server
- the server interprets the message and redirects it to the browser
- `core/client.js` in the browser receives the message and puts your HTML into an iframe
- your HTML is presented by the browser in an iframe sandbox
But you don't have to think about all that. Call a function, and you see the result.
## Example 2: Hit Counter
Let's take advantage of code running on the server and create a little hit counter using a key value store shared between all visitors.
### app.js
```js
async function main() {
let db = await shared_database('visitors');
let count = parseInt((await db.get('visitors')) ?? '0') + 1;
await db.set('visitors', count.toString());
await app.setDocument(`
<h1 style="color: #fff">Welcome, visitor #${count}!</h1>
`);
}
main();
```
### Output
<h1 style="color: #fff">Welcome, visitor #1!</h1>
### Explanation
Just as pure browser apps have access to `localStorage`, Tilde Friends apps have access to key-value storage on the server.
The interface is a bit clunky and will likely change someday, but this example gets a database object, from which you can get and set string values by key. There are various on `shared_database` that let you store data that is private to the user or shared by different criteria.
Also, even though any browser-side code is sandboxed, it is allowed to access browser local storage by going through Tilde Friends API, because sometimes that is useful.
## Example 3: Files
Suppose you don't want to create your entire app in a single server-side file as we've done with the previous examples. There are some tools to allow you to begin to organize.
### app.js
```js
async function main() {
let html = utf8Decode(await getFile('index.html'));
app.setDocument(html);
}
main();
```
### index.html
```html
<html>
<head>
<script type="module" src="script.js"></script>
</head>
<body style="color: #fff">
<h1>File Test</h1>
</body>
</html>
```
### script.js
```js
window.addEventListener('load', function() {
document.body.appendChild(document.createTextNode('Hello, world');
});
```
### Output
<h1>File Test</h1><p>Hello, world!</p>
### Explanation
On the server, `utf8Decode(await getFile(fileName))` lets you load a file from your app. In the browser, your app files are made available by HTTP, so you can `<script src="my_script.js"></script>` and such to access them.
## Example 4: Remote Procedure Call
While making calls between the client and the server, it is possible to pass functions across that boundary. `tfrpc.js` is a tiny script which builds on that feature to try to hide some of the complexities.
### app.js
```js
import * as tf from '/tfrpc.js';
function sum() {
let s = 0;
for (let x of arguments) {
s += x;
}
return s;
}
tf.register(sum);
async function main() {
app.setDocument(utf8Decode(await getFile('index.html')));
}
main();
```
### index.html
```html
<html>
<body>
<h1 id="result">Calculating...</h1>
</body>
<script type="module" src="script.js"></script>
</html>
```
### script.js
```js
import * as tf from '/static/tfrpc.js';
window.addEventListener('load', async function () {
document.getElementById('result').innerText = await tf.rpc.sum(1, 2, 3);
});
```
### Output
<h1>6</h1>
### Explanation
Here the browser makes an asynchronous call to the server to do some basic math and update its DOM with the result.
With your favorite Vue/Lit/React/... library on the client-side and your favorite Tilde Friends API calls registered with tfrpc, it becomes pretty easy to start extracting interesting information from, say, SQL queries over Secure Scuttlebutt data, and generating complicated, dynamic user interface. These are the building blocks I used to make the current Tilde Friends SSB client interface.
## Conclusion
Tilde Friends is currently a pile of all the parts that I thought I needed to build interesting web applications, tied together by code that tries to walk the fine line between being secure enough to let us safely run code on the same device and being usable enough that you can open a tab in your browser and start building just by typing code.
I don't claim it thoroughly accomplishes either yet, but I believe it is at a stage where it is showing how promising this approach can be, and I am excited for you to take it for a spin and share.

@ -1,15 +0,0 @@
# Inspiration
This is an ever-growing list of software that is similar to what Tilde Friends tries to be but as far as I can tell don't quite fit the same niche.
- Secure Scuttlebutt Clients
- [Manyverse](https://www.manyver.se/)
- [Patchwork](https://github.com/ssbc/patchwork)
- [Patchfox](https://patchfox.org/#/)
- [Habitat](https://gitlab.com/quickdudley/habitat)
- [Āhau](https://gitlab.com/ahau/ahau/)
- [erlbutt](https://github.com/cmoid/erlbutt/)
- Web Application Platforms
- [Glitch](https://glitch.com/)
- [Val Town](https://www.val.town/)
- [Clace](https://clace.io/)

@ -1,24 +0,0 @@
# Release Checklist
- make sure ci is passing
- run the tests
- format + prettier
- update metadata/en-US/changelogs
- git tag v1.2.3
- git tag -f latest_release
- push
- make a release on gitea
- upload the artifacts
- upload the AppImage and zsyncmake
- upload to Google
- upload to Apple with dist-ios on macos
- nix
- june and december: update release version
- run `nix flake update`
- comment out the hash in default.nix
- update the version
- run `nix-build`
- update the hash
- bump the versions in GNUmakefile for the next release
- make
- commit

@ -1,64 +0,0 @@
# Vision
Tilde Friends is a tool for making and sharing.
It is both a peer-to-peer social network client, participating in Secure
Scuttlebutt, and an environment for creating and running web applications.
## Why
This is a thing that I wanted to exist and wanted to work on. No other reason.
There is not a business model. I believe it is interesting and unique.
## Goals
1. Make it **easy and fun** to run all sorts of web applications.
2. Provide **security** that is easy to understand and protects your data.
3. Make **creating and sharing** web applications accessible to anyone with a
browser.
## Ways to Use Tilde Friends
1. **Social Network User**: This is a social network first. You are just here,
because your friends are. Or you like how we limit your message length or
short videos or whatever the trend is. If you are ambitious, you click links
and see interactive experiences (apps) that you wouldn't see elsewhere.
2. **Web Visitor**: You get links from a friend to meeting invites, polls, games,
lists, wiki pages, ..., and you interact with them as though they were
cloud-hosted by a megacorporation. They just work, and you don't think twice.
3. **Group leader**: You host or use a small public instance, installing apps for
a group of friends to use as web visitors.
4. **Developer**: You like to write code and make or improve apps for fun or to
solve problems. When you encounter a Tilde Friends app on a strange server,
you know you can trivially modify it or download it to your own instance.
## Future Goals / Endgame
1. Mobile apps. This can run on your old phone. Maybe you won't be hosting
the web interface publicly, but you can sync, install and edit apps, and
otherwise get the full experience from a tiny touch screen.
2. The universal application runtime. The web browser is the universal
platform, but even for the simplest application that you might want to host
for your friends, cloud hosting, containers, and complicated dependencies might
all enter the mix. Tilde Friends, though it is yet another thing to host,
includes everything you need out of the box to run a vast variety of interesting
apps.
Tilde Friends will be built out, gradually providing safe access to host
resources and client resources the same way web browsers extended access to
resources like GPU, persistent storage, cameras, ... over the years.
Not much effort has been put forward yet to having a robust, long-lasting API,
but since the client side longevity is already handled by web browsers, it
seems possible that the server-side API can be managed in a similar way.
3. An awesome development environment. Right now it runs JavaScript from the
first embeddable text editor I could poorly configure enough to edit code,
but it could incorporate a debugger, source control integration a la ssb-git,
merge tools, and transpiling from all sorts of different languages.

14
flake.lock generated

@ -5,11 +5,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
@ -20,16 +20,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1739758141,
"narHash": "sha256-uq6A2L7o1/tR6VfmYhZWoVAwb3gTy7j4Jx30MIrH0rE=",
"lastModified": 1717281328,
"narHash": "sha256-evZPzpf59oNcDUXxh2GHcxHkTEG4fjae2ytWP85jXRo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c618e28f70257593de75a7044438efc1c1fc0791",
"rev": "b3b2b28c1daa04fe2ae47c21bb76fd226eac4ca1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.11",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}

@ -2,7 +2,7 @@
description = "Tilde Friends is a platform for making, running, and sharing web applications.";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
flake-utils.url = "github:numtide/flake-utils";
};

@ -1,14 +0,0 @@
* An iOS build is on TestFlight.
* macOS targets are debug and release like everywhere else.
* Running from a subdirectory is fine.
* Patchwork compatibility improvements.
* Invite fixes.
* Follow/block UI fixes.
* Mobile automatically logs in.
* Allow specifying all global settings from the command-line.
* UpdateS:
* CodeMirror
* OpenSSL 3.4.1
* libbacktrace
* sqlite 3.49.1
* speedscope 1.22.2

6
package-lock.json generated

@ -11,9 +11,9 @@
}
},
"node_modules/prettier": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
"integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
"bin": {
"prettier": "bin/prettier.cjs"
},

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unprompted.tildefriends"
android:versionCode="33"
android:versionName="0.0.28-wip">
android:versionCode="32"
android:versionName="0.0.27">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application

@ -421,7 +421,7 @@ public class TildeFriendsActivity extends Activity {
base_url = "http://127.0.0.1:" + String.valueOf(port) + "/";
runOnUiThread(() -> {
hide_status();
web_view.loadUrl(base_url + "login/auto");
web_view.loadUrl(base_url);
});
observer = null;
} else {

@ -1,16 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:gravity="center"
android:width="256dp"
android:height="256dp">
<shape>
<solid
android:color="#0af"/>
</shape>
</item>
<item android:drawable="@drawable/logo"
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="65dp"
android:height="65dp"
android:gravity="center"/>
</layer-list>
android:viewportWidth="61"
android:viewportHeight="65">
<path
android:pathData="M6,0h49a8,8 45,0 1,8 8v49a8,8 135,0 1,-8 8H6a8,8 45,0 1,-8 -8V8a8,8 135,0 1,8 -8Z"
android:strokeWidth=".712717"
android:fillColor="#0af"
android:fillAlpha="1"/>
<path
android:pathData="m1.6762,36.6891v-4.0039q2.0703,-2.3438 5.4297,-2.3438 1.1719,0 2.4609,0.3516 1.2891,0.332 3.6719,1.3477 1.3477,0.5664 2.0117,0.7422 0.6836,0.1758 1.3672,0.1758 1.2695,0 2.6172,-0.7617 1.3672,-0.7617 2.4219,-1.9141v4.1406q-1.25,1.1719 -2.5391,1.6992 -1.2695,0.5273 -2.8711,0.5273 -1.1719,0 -2.2461,-0.2734 -1.0547,-0.2734 -3.3789,-1.3086 -2.3047,-1.0352 -3.8477,-1.0352 -1.25,0 -2.3633,0.5469 -1.0938,0.5273 -2.7344,2.1094z"
android:fillColor="#000000"/>
<path
android:pathData="M42.4653,32.2273m-16.7723,0a16.7723,16.7723 0,1 1,33.5446 0a16.7723,16.7723 0,1 1,-33.5446 0"
android:fillColor="#fcea2b"/>
<path
android:pathData="M49.2697,34.097c2.8899,0 5.2344,-2.0871 5.2344,-4.6591 0,-1.2871 0.3267,-2.5742 -0.6213,-3.4164 -0.9473,-0.843 -3.1685,-1.2426 -4.6131,-1.2426 -1.7188,0 -3.7504,0.1043 -4.7043,1.2426 -0.6519,0.7766 -0.5302,2.3722 -0.5302,3.4164 0,2.572 2.343,4.6591 5.2344,4.6591zM34.9819,34.097c2.8899,0 5.2351,-2.0871 5.2351,-4.6591 0,-1.2871 0.326,-2.5742 -0.6213,-3.4164 -0.948,-0.843 -3.1685,-1.2426 -4.6138,-1.2426 -1.7181,0 -3.7497,0.1043 -4.7043,1.2426 -0.6512,0.7766 -0.5302,2.3722 -0.5302,3.4164 0,2.572 2.343,4.6591 5.2344,4.6591z"
android:fillColor="#3f3f3f"/>
<path
android:pathData="M42.3829,32.2681m-16.7723,0a16.7723,16.7723 0,1 1,33.5446 0a16.7723,16.7723 0,1 1,-33.5446 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M49.5403,38.6897c-4.794,2.5705 -10.242,2.6675 -14.3148,0M29.983,28.1903s-0.695,6.2349 5.0025,5.774c1.9106,-0.1546 5.7004,-0.474 5.7369,-6.0832 0.0036,-0.509 -0.0051,-1.1668 -0.5907,-1.9179 -0.7766,-0.9969 -2.6048,-1.4373 -7.2522,-1.037 0,0 -2.5129,-0.0729 -2.8965,3.264z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="m30.0341,27.8016 l-0.3158,-2.459 2.7951,-0.3843M54.6733,28.1903s0.695,6.2349 -5.0025,5.774c-1.9106,-0.1546 -5.7004,-0.474 -5.7376,-6.0832 -0.0029,-0.509 0.0058,-1.1668 0.5914,-1.9179 0.7766,-0.9969 2.6048,-1.4373 7.2522,-1.037 0,0 2.5129,-0.0729 2.8965,3.264z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M39.1874,25.2383s3.0073,1.8479 6.3129,0M40.6685,28.813s1.6058,-2.7353 3.3078,0M54.6172,27.803l0.3158,-2.4582 -2.7951,-0.385"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M40.974,27.8716s1.309,-2.7346 2.6974,0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
</vector>

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:gravity="center"
android:width="256dp"
android:height="256dp">
<shape>
<solid
android:color="#0fa"/>
</shape>
</item>
<item android:drawable="@drawable/logo"
android:width="65dp"
android:height="65dp"
android:gravity="center"/>
</layer-list>

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="65dp"
android:height="65dp"
android:viewportWidth="65"
android:viewportHeight="65">
<path
android:pathData="m1.6762,36.6891v-4.0039q2.0703,-2.3438 5.4297,-2.3438 1.1719,0 2.4609,0.3516 1.2891,0.332 3.6719,1.3477 1.3477,0.5664 2.0117,0.7422 0.6836,0.1758 1.3672,0.1758 1.2695,0 2.6172,-0.7617 1.3672,-0.7617 2.4219,-1.9141v4.1406q-1.25,1.1719 -2.5391,1.6992 -1.2695,0.5273 -2.8711,0.5273 -1.1719,0 -2.2461,-0.2734 -1.0547,-0.2734 -3.3789,-1.3086 -2.3047,-1.0352 -3.8477,-1.0352 -1.25,0 -2.3633,0.5469 -1.0938,0.5273 -2.7344,2.1094z"
android:fillColor="#000000"/>
<path
android:pathData="M42.4653,32.2273m-16.7723,0a16.7723,16.7723 0,1 1,33.5446 0a16.7723,16.7723 0,1 1,-33.5446 0"
android:fillColor="#fcea2b"/>
<path
android:pathData="M49.2697,34.097c2.8899,0 5.2344,-2.0871 5.2344,-4.6591 0,-1.2871 0.3267,-2.5742 -0.6213,-3.4164 -0.9473,-0.843 -3.1685,-1.2426 -4.6131,-1.2426 -1.7188,0 -3.7504,0.1043 -4.7043,1.2426 -0.6519,0.7766 -0.5302,2.3722 -0.5302,3.4164 0,2.572 2.343,4.6591 5.2344,4.6591zM34.9819,34.097c2.8899,0 5.2351,-2.0871 5.2351,-4.6591 0,-1.2871 0.326,-2.5742 -0.6213,-3.4164 -0.948,-0.843 -3.1685,-1.2426 -4.6138,-1.2426 -1.7181,0 -3.7497,0.1043 -4.7043,1.2426 -0.6512,0.7766 -0.5302,2.3722 -0.5302,3.4164 0,2.572 2.343,4.6591 5.2344,4.6591z"
android:fillColor="#3f3f3f"/>
<path
android:pathData="M42.3829,32.2681m-16.7723,0a16.7723,16.7723 0,1 1,33.5446 0a16.7723,16.7723 0,1 1,-33.5446 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M49.5403,38.6897c-4.794,2.5705 -10.242,2.6675 -14.3148,0M29.983,28.1903s-0.695,6.2349 5.0025,5.774c1.9106,-0.1546 5.7004,-0.474 5.7369,-6.0832 0.0036,-0.509 -0.0051,-1.1668 -0.5907,-1.9179 -0.7766,-0.9969 -2.6048,-1.4373 -7.2522,-1.037 0,0 -2.5129,-0.0729 -2.8965,3.264z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="m30.0341,27.8016 l-0.3158,-2.459 2.7951,-0.3843M54.6733,28.1903s0.695,6.2349 -5.0025,5.774c-1.9106,-0.1546 -5.7004,-0.474 -5.7376,-6.0832 -0.0029,-0.509 0.0058,-1.1668 0.5914,-1.9179 0.7766,-0.9969 2.6048,-1.4373 7.2522,-1.037 0,0 2.5129,-0.0729 2.8965,3.264z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M39.1874,25.2383s3.0073,1.8479 6.3129,0M40.6685,28.813s1.6058,-2.7353 3.3078,0M54.6172,27.803l0.3158,-2.4582 -2.7951,-0.385"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
<path
android:pathData="M40.974,27.8716s1.309,-2.7346 2.6974,0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
</vector>

@ -18,6 +18,20 @@
static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
static JSValue _file_read_file_zip(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
static JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
static JSValue _file_stat_zip(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
static double _time_spec_to_double(const uv_timespec_t* time_spec);
static void _file_on_stat_complete(uv_fs_t* request);
typedef struct file_stat_t
{
void* _task;
JSContext* _context;
promiseid_t _promise;
uv_fs_t _request;
} file_stat_t;
typedef struct fs_req_t
{
@ -31,10 +45,12 @@ void tf_file_register(JSContext* context)
{
JSValue global = JS_GetGlobalObject(context);
JSValue file = JS_NewObject(context);
tf_task_t* task = JS_GetContextOpaque(context);
void* task = JS_GetContextOpaque(context);
const char* zip = tf_task_get_zip_path(task);
JS_SetPropertyStr(context, global, "File", file);
JS_SetPropertyStr(context, file, "readFile", JS_NewCFunction(context, zip ? _file_read_file_zip : _file_read_file, "readFile", 1));
JS_SetPropertyStr(context, file, "writeFile", JS_NewCFunction(context, _file_write_file, "writeFile", 2));
JS_SetPropertyStr(context, file, "stat", JS_NewCFunction(context, zip ? _file_stat_zip : _file_stat, "stat", 1));
JS_FreeValue(context, global);
}
@ -49,6 +65,105 @@ static void _file_async_close_callback(uv_fs_t* req)
tf_free(req);
}
static void _file_write_write_callback(uv_fs_t* req)
{
uv_fs_req_cleanup(req);
fs_req_t* fsreq = (fs_req_t*)req;
tf_task_t* task = req->loop->data;
JSContext* context = tf_task_get_context(task);
promiseid_t promise = (promiseid_t)(intptr_t)req->data;
if (req->result >= 0)
{
tf_task_resolve_promise(task, promise, JS_NewInt64(context, req->result));
}
else
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, "Failed to write %s: %s", req->path, uv_strerror(req->result)));
}
int result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback);
if (result < 0)
{
uv_fs_req_cleanup(req);
tf_free(fsreq);
}
}
static void _file_write_open_callback(uv_fs_t* req)
{
fs_req_t* fsreq = (fs_req_t*)req;
const char* path = tf_strdup(req->path);
uv_fs_req_cleanup(req);
tf_task_t* task = req->loop->data;
JSContext* context = tf_task_get_context(task);
promiseid_t promise = (promiseid_t)(intptr_t)req->data;
if (req->result >= 0)
{
uv_buf_t buf = { .base = fsreq->buffer, .len = fsreq->size };
fsreq->file = req->result;
int result = uv_fs_write(req->loop, req, fsreq->file, &buf, 1, 0, _file_write_write_callback);
if (result < 0)
{
uv_fs_req_cleanup(req);
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, "Failed to write %s: %s", path, uv_strerror(result)));
result = uv_fs_close(req->loop, req, fsreq->file, _file_async_close_callback);
if (result < 0)
{
uv_fs_req_cleanup(req);
tf_free(fsreq);
}
}
}
else
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, "%s", uv_strerror(req->result)));
tf_free(req);
}
tf_free((void*)path);
}
static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
void* task = JS_GetContextOpaque(context);
const char* file_name = JS_ToCString(context, argv[0]);
size_t size;
uint8_t* buffer = tf_util_try_get_array_buffer(context, &size, argv[1]);
bool is_array_buffer = false;
if (buffer)
{
is_array_buffer = true;
}
else
{
buffer = (uint8_t*)JS_ToCStringLen(context, &size, argv[1]);
}
promiseid_t promise = -1;
JSValue promise_value = tf_task_allocate_promise(task, &promise);
fs_req_t* req = tf_malloc(sizeof(fs_req_t) + size);
*req = (fs_req_t)
{
.fs =
{
.data = (void*)(intptr_t)promise,
},
.size = size,
};
memcpy(req->buffer, buffer, size);
if (!is_array_buffer)
{
JS_FreeCString(context, (const char*)buffer);
}
int result = uv_fs_open(tf_task_get_loop(task), &req->fs, file_name, UV_FS_O_CREAT | UV_FS_O_WRONLY, 0644, _file_write_open_callback);
if (result < 0)
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, "Failed to open %s for write: %s", file_name, uv_strerror(result)));
}
JS_FreeCString(context, file_name);
return promise_value;
}
static void _file_read_read_callback(uv_fs_t* req)
{
fs_req_t* fsreq = (fs_req_t*)req;
@ -109,9 +224,8 @@ static void _file_read_open_callback(uv_fs_t* req)
static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
tf_task_t* task = JS_GetContextOpaque(context);
void* task = JS_GetContextOpaque(context);
const char* file_name = JS_ToCString(context, argv[0]);
const char* actual = tf_task_get_path_with_root(task, file_name);
promiseid_t promise = -1;
JSValue promise_value = tf_task_allocate_promise(task, &promise);
@ -125,7 +239,7 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar
.size = k_file_read_max,
};
memset(req + 1, 0, k_file_read_max);
int result = uv_fs_open(tf_task_get_loop(task), &req->fs, actual, UV_FS_O_RDONLY, 0, _file_read_open_callback);
int result = uv_fs_open(tf_task_get_loop(task), &req->fs, file_name, UV_FS_O_RDONLY, 0, _file_read_open_callback);
if (result < 0)
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, "Failed to open %s for read: %s", file_name, uv_strerror(result)));
@ -133,7 +247,6 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar
tf_free(req);
}
JS_FreeCString(context, file_name);
tf_free((char*)actual);
return promise_value;
}
@ -252,6 +365,81 @@ static JSValue _file_read_file_zip(JSContext* context, JSValueConst this_val, in
return promise_value;
}
static JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
void* task = JS_GetContextOpaque(context);
const char* path = JS_ToCString(context, argv[0]);
promiseid_t promise = -1;
JSValue promise_value = tf_task_allocate_promise(task, &promise);
file_stat_t* data = tf_malloc(sizeof(file_stat_t));
data->_task = task;
data->_promise = promise;
data->_request.data = data;
data->_context = context;
int result = uv_fs_stat(tf_task_get_loop(task), &data->_request, path, _file_on_stat_complete);
if (result)
{
tf_task_reject_promise(task, promise, JS_NewInt32(context, result));
uv_fs_req_cleanup(&data->_request);
tf_free(data);
}
JS_FreeCString(context, path);
return promise_value;
}
static JSValue _file_stat_zip(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
void* task = JS_GetContextOpaque(context);
promiseid_t promise = -1;
JSValue promise_value = tf_task_allocate_promise(task, &promise);
file_stat_t* data = tf_malloc(sizeof(file_stat_t));
data->_task = task;
data->_promise = promise;
data->_request.data = data;
data->_context = context;
/* Ignore the requested path and stat the zip itself. */
int result = uv_fs_stat(tf_task_get_loop(task), &data->_request, tf_task_get_zip_path(task), _file_on_stat_complete);
if (result)
{
tf_task_reject_promise(task, promise, JS_NewInt32(context, result));
uv_fs_req_cleanup(&data->_request);
tf_free(data);
}
return promise_value;
}
static double _time_spec_to_double(const uv_timespec_t* time_spec)
{
return time_spec->tv_sec + (double)(time_spec->tv_nsec) / 1e9;
}
static void _file_on_stat_complete(uv_fs_t* request)
{
file_stat_t* data = (file_stat_t*)(request->data);
JSContext* context = data->_context;
if (request->result)
{
tf_task_reject_promise(data->_task, data->_promise, JS_NewInt32(context, request->result));
}
else
{
JSValue result = JS_NewObject(context);
JS_SetPropertyStr(context, result, "mtime", JS_NewFloat64(context, _time_spec_to_double(&request->statbuf.st_mtim)));
JS_SetPropertyStr(context, result, "ctime", JS_NewFloat64(context, _time_spec_to_double(&request->statbuf.st_ctim)));
JS_SetPropertyStr(context, result, "atime", JS_NewFloat64(context, _time_spec_to_double(&request->statbuf.st_atim)));
JS_SetPropertyStr(context, result, "size", JS_NewFloat64(context, request->statbuf.st_size));
tf_task_resolve_promise(data->_task, data->_promise, result);
JS_FreeValue(context, result);
}
uv_fs_req_cleanup(request);
tf_free(data);
}
typedef struct _stat_t
{
uv_fs_t request;

@ -7,7 +7,6 @@
#include "ssb.db.h"
#include "ssb.h"
#include "task.h"
#include "tls.h"
#include "tlscontext.js.h"
#include "trace.h"
#include "util.js.h"
@ -234,6 +233,46 @@ static JSValue _httpd_make_response_object(JSContext* context, tf_http_request_t
return response_object;
}
static void _httpd_callback_internal(tf_http_request_t* request, bool is_websocket)
{
http_handler_data_t* data = request->user_data;
JSContext* context = data->context;
JSValue request_object = JS_NewObject(context);
JS_SetPropertyStr(context, request_object, "method", JS_NewString(context, request->method));
JS_SetPropertyStr(context, request_object, "uri", JS_NewString(context, request->path));
JSValue headers = JS_NewObject(context);
for (int i = 0; i < request->headers_count; i++)
{
JS_SetPropertyStr(context, headers, request->headers[i].name, JS_NewString(context, request->headers[i].value));
}
JS_SetPropertyStr(context, request_object, "headers", headers);
if (request->query)
{
JS_SetPropertyStr(context, request_object, "query", JS_NewString(context, request->query));
}
if (request->body)
{
JS_SetPropertyStr(context, request_object, "body", tf_util_new_uint8_array(context, request->body, request->content_length));
}
JSValue client = JS_NewObject(context);
JS_SetPropertyStr(context, client, "tls", request->is_tls ? JS_TRUE : JS_FALSE);
JS_SetPropertyStr(context, request_object, "client", client);
JSValue response_object = _httpd_make_response_object(context, request);
/* The ref is owned by the JS object and will be released by the finalizer. */
tf_http_request_ref(request);
JSValue args[] = {
request_object,
response_object,
};
JSValue response = JS_Call(context, data->callback, JS_UNDEFINED, 2, args);
tf_util_report_error(context, response);
JS_FreeValue(context, request_object);
JS_FreeValue(context, response);
JS_FreeValue(context, response_object);
}
static bool _httpd_redirect(tf_http_request_t* request)
{
if (request->is_tls)
@ -253,6 +292,16 @@ static bool _httpd_redirect(tf_http_request_t* request)
return true;
}
static void _httpd_callback(tf_http_request_t* request)
{
if (_httpd_redirect(request))
{
return;
}
_httpd_callback_internal(request, false);
}
static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
@ -339,21 +388,73 @@ static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_va
return JS_UNDEFINED;
}
static void _httpd_cleanup_callback(void* user_data)
{
http_handler_data_t* data = user_data;
JS_FreeValue(data->context, data->callback);
tf_free(data);
}
static JSValue _httpd_endpoint_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
const char* pattern = JS_ToCString(context, argv[0]);
http_handler_data_t* data = tf_malloc(sizeof(http_handler_data_t));
*data = (http_handler_data_t) { .context = context, .callback = JS_DupValue(context, argv[1]) };
tf_http_add_handler(http, pattern, _httpd_callback, _httpd_cleanup_callback, data);
JS_FreeCString(context, pattern);
return JS_UNDEFINED;
}
typedef struct _httpd_listener_t
{
tf_tls_context_t* tls;
JSContext* context;
JSValue tls;
} httpd_listener_t;
static void _httpd_listener_cleanup(void* user_data)
{
httpd_listener_t* listener = user_data;
if (listener->tls)
{
tf_tls_context_destroy(listener->tls);
}
JS_FreeValue(listener->context, listener->tls);
tf_free(listener);
}
static JSValue _httpd_endpoint_start(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
int port = 0;
JS_ToInt32(context, &port, argv[0]);
httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
*listener = (httpd_listener_t) { .context = context, .tls = JS_DupValue(context, argv[1]) };
tf_tls_context_t* tls = tf_tls_context_get(listener->tls);
int assigned_port = tf_http_listen(http, port, tls, _httpd_listener_cleanup, listener);
tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "http%s://127.0.0.1:%d/" RESET ".\n", tls ? "s" : "", assigned_port);
return JS_NewInt32(context, assigned_port);
}
static void _httpd_free_user_data(void* user_data)
{
tf_free(user_data);
}
static JSValue _httpd_set_http_redirect(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
http_user_data_t* user_data = tf_http_get_user_data(http);
if (!user_data)
{
user_data = tf_malloc(sizeof(http_user_data_t));
memset(user_data, 0, sizeof(http_user_data_t));
tf_http_set_user_data(http, user_data, _httpd_free_user_data);
}
const char* redirect = JS_ToCString(context, argv[0]);
snprintf(user_data->redirect, sizeof(user_data->redirect), "%s", redirect ? redirect : "");
JS_FreeCString(context, redirect);
return JS_UNDEFINED;
}
typedef struct _auth_query_work_t
{
const char* settings;
@ -682,24 +783,13 @@ typedef struct _http_file_t
char etag[512];
} http_file_t;
static bool _ends_with(const char* a, const char* suffix)
{
if (!a || !suffix)
{
return false;
}
size_t alen = strlen(a);
size_t suffixlen = strlen(suffix);
return alen >= suffixlen && strcmp(a + alen - suffixlen, suffix) == 0;
}
static void _httpd_endpoint_static_read(tf_task_t* task, const char* path, int result, const void* data, void* user_data)
{
http_file_t* file = user_data;
tf_http_request_t* request = file->request;
if (result >= 0)
{
if (strcmp(path, "core/tfrpc.js") == 0 || _ends_with(path, "core/tfrpc.js"))
if (strcmp(path, "core/tfrpc.js") == 0)
{
const char* content_type = _ext_to_content_type(strrchr(path, '.'), true);
const char* headers[] = {
@ -841,10 +931,9 @@ static void _httpd_endpoint_static(tf_http_request_t* request)
}
tf_task_t* task = request->user_data;
const char* root_path = tf_task_get_root_path(task);
size_t size = (root_path ? strlen(root_path) + 1 : 0) + strlen(file_path) + strlen(after) + 1;
size_t size = strlen(file_path) + strlen(after) + 1;
char* path = alloca(size);
snprintf(path, size, "%s%s%s%s", root_path ? root_path : "", root_path ? "/" : "", file_path, after);
snprintf(path, size, "%s%s", file_path, after);
tf_http_request_ref(request);
tf_file_stat(task, path, _httpd_endpoint_static_stat, request);
}
@ -2166,176 +2255,6 @@ static void _httpd_endpoint_logout(tf_http_request_t* request)
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
}
typedef struct _auto_login_t
{
tf_http_request_t* request;
bool autologin;
const char* users;
} auto_login_t;
static void _httpd_auto_login_work(tf_ssb_t* ssb, void* user_data)
{
auto_login_t* request = user_data;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
tf_ssb_db_get_global_setting_bool(db, "autologin", &request->autologin);
tf_ssb_release_db_reader(ssb, db);
if (request->autologin)
{
request->users = tf_ssb_db_get_property(ssb, "auth", "users");
if (request->users && strcmp(request->users, "[]") == 0)
{
tf_free((void*)request->users);
request->users = NULL;
}
if (!request->users)
{
JSMallocFunctions funcs = { 0 };
tf_get_js_malloc_functions(&funcs);
JSRuntime* runtime = JS_NewRuntime2(&funcs, NULL);
JSContext* context = JS_NewContext(runtime);
static const char* k_account_name = "mobile";
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
bool registered = tf_ssb_db_register_account(tf_ssb_get_loop(ssb), db, context, k_account_name, k_account_name);
tf_ssb_release_db_writer(ssb, db);
if (registered)
{
_make_administrator_if_first(ssb, context, k_account_name, true);
}
JS_FreeContext(context);
JS_FreeRuntime(runtime);
request->users = tf_ssb_db_get_property(ssb, "auth", "users");
}
}
}
static void _httpd_auto_login_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
auto_login_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
const char* session_token = NULL;
if (!work->autologin)
{
const char* k_payload = tf_http_status_text(404);
tf_http_respond(work->request, 404, NULL, 0, k_payload, strlen(k_payload));
}
else
{
if (work->users)
{
JSValue json = JS_ParseJSON(context, work->users, strlen(work->users), NULL);
JSValue user = JS_GetPropertyUint32(context, json, 0);
const char* user_string = JS_ToCString(context, user);
session_token = _make_session_jwt(context, ssb, user_string);
JS_FreeCString(context, user_string);
JS_FreeValue(context, user);
JS_FreeValue(context, json);
}
if (session_token)
{
const char* cookie = _make_set_session_cookie_header(work->request, session_token);
tf_free((void*)session_token);
const char* headers[] = {
"Set-Cookie",
cookie,
"Location",
"/",
};
tf_http_respond(work->request, 303, headers, tf_countof(headers) / 2, NULL, 0);
tf_free((void*)cookie);
}
else
{
const char* headers[] = {
"Location",
"/",
};
tf_http_respond(work->request, 303, headers, tf_countof(headers) / 2, NULL, 0);
}
}
tf_http_request_unref(work->request);
tf_free((void*)work->users);
tf_free(work);
}
static void _httpd_endpoint_login_auto(tf_http_request_t* request)
{
tf_task_t* task = request->user_data;
tf_http_request_ref(request);
tf_ssb_t* ssb = tf_task_get_ssb(task);
auto_login_t* work = tf_malloc(sizeof(auto_login_t));
*work = (auto_login_t) { .request = request };
tf_ssb_run_work(ssb, _httpd_auto_login_work, _httpd_auto_login_after_work, work);
}
static void _httpd_endpoint_app_socket(tf_http_request_t* request)
{
tf_task_t* task = request->user_data;
tf_ssb_t* ssb = tf_task_get_ssb(task);
JSContext* context = tf_ssb_get_context(ssb);
JSValue global = JS_GetGlobalObject(context);
JSValue exports = JS_GetPropertyStr(context, global, "exports");
JSValue app_socket = JS_GetPropertyStr(context, exports, "app_socket");
JSValue request_object = JS_NewObject(context);
JSValue headers = JS_NewObject(context);
for (int i = 0; i < request->headers_count; i++)
{
JS_SetPropertyStr(context, headers, request->headers[i].name, JS_NewString(context, request->headers[i].value));
}
JS_SetPropertyStr(context, request_object, "headers", headers);
JSValue response = _httpd_make_response_object(context, request);
tf_http_request_ref(request);
JSValue args[] = {
request_object,
response,
};
JSValue result = JS_Call(context, app_socket, JS_NULL, tf_countof(args), args);
tf_util_report_error(context, result);
JS_FreeValue(context, result);
for (int i = 0; i < tf_countof(args); i++)
{
JS_FreeValue(context, args[i]);
}
JS_FreeValue(context, app_socket);
JS_FreeValue(context, exports);
JS_FreeValue(context, global);
}
static void _httpd_free_user_data(void* user_data)
{
tf_free(user_data);
}
static const char* _httpd_read_file(tf_task_t* task, const char* path)
{
const char* actual = tf_task_get_path_with_root(task, path);
const size_t k_max_read = 8 * 1024 * 1024;
char* result = NULL;
char* buffer = tf_malloc(k_max_read);
FILE* file = fopen(actual, "rb");
if (file)
{
size_t size = fread(buffer, 1, k_max_read, file);
result = tf_malloc(size + 1);
memcpy(result, buffer, size);
result[size] = '\0';
fclose(file);
}
tf_free(buffer);
tf_free((char*)actual);
return result;
}
void tf_httpd_register(JSContext* context)
{
JS_NewClassID(&_httpd_class_id);
@ -2356,46 +2275,15 @@ void tf_httpd_register(JSContext* context)
{
fprintf(stderr, "Failed to register Request.\n");
}
JSValue global = JS_GetGlobalObject(context);
JSValue httpd = JS_NewObjectClass(context, _httpd_class_id);
tf_task_t* task = tf_task_get(context);
tf_ssb_t* ssb = tf_task_get_ssb(task);
uv_loop_t* loop = tf_task_get_loop(task);
tf_http_t* http = tf_http_create(loop);
tf_http_set_trace(http, tf_task_get_trace(task));
JS_SetOpaque(httpd, http);
int64_t http_port = 0;
int64_t https_port = 0;
char out_http_port_file[512] = "";
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
tf_ssb_db_get_global_setting_int64(db, "http_port", &http_port);
tf_ssb_db_get_global_setting_int64(db, "https_port", &https_port);
tf_ssb_db_get_global_setting_string(db, "out_http_port_file", out_http_port_file, sizeof(out_http_port_file));
tf_ssb_release_db_reader(ssb, db);
if (https_port)
{
http_user_data_t* user_data = tf_http_get_user_data(http);
if (!user_data)
{
user_data = tf_malloc(sizeof(http_user_data_t));
memset(user_data, 0, sizeof(http_user_data_t));
tf_http_set_user_data(http, user_data, _httpd_free_user_data);
}
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
tf_ssb_db_get_global_setting_string(db, "http_redirect", user_data->redirect, sizeof(user_data->redirect));
tf_ssb_release_db_reader(ssb, db);
/* Workaround. */
if (strcmp(user_data->redirect, "0") == 0)
{
*user_data->redirect = '\0';
}
}
tf_http_add_handler(http, "/", _httpd_endpoint_root, NULL, task);
tf_http_add_handler(http, "/codemirror/*", _httpd_endpoint_static, NULL, task);
tf_http_add_handler(http, "/lit/*", _httpd_endpoint_static, NULL, task);
@ -2422,57 +2310,12 @@ void tf_httpd_register(JSContext* context)
tf_http_add_handler(http, "/trace", _httpd_endpoint_trace, NULL, task);
tf_http_add_handler(http, "/login/logout", _httpd_endpoint_logout, NULL, task);
tf_http_add_handler(http, "/login/auto", _httpd_endpoint_login_auto, NULL, task);
tf_http_add_handler(http, "/login", _httpd_endpoint_login, NULL, task);
tf_http_add_handler(http, "/app/socket", _httpd_endpoint_app_socket, NULL, task);
JS_SetPropertyStr(context, httpd, "all", JS_NewCFunction(context, _httpd_endpoint_all, "all", 2));
JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_endpoint_start, "start", 2));
JS_SetPropertyStr(context, httpd, "set_http_redirect", JS_NewCFunction(context, _httpd_set_http_redirect, "set_http_redirect", 1));
JS_SetPropertyStr(context, httpd, "auth_query", JS_NewCFunction(context, _httpd_auth_query, "auth_query", 1));
JS_SetPropertyStr(context, global, "httpd", httpd);
JS_FreeValue(context, global);
if (http_port > 0 || *out_http_port_file)
{
httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
*listener = (httpd_listener_t) { 0 };
int assigned_port = tf_http_listen(http, http_port, NULL, _httpd_listener_cleanup, listener);
tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "http://127.0.0.1:%d/" RESET ".\n", assigned_port);
if (*out_http_port_file)
{
const char* actual_http_port_file = tf_task_get_path_with_root(task, out_http_port_file);
FILE* file = fopen(actual_http_port_file, "wb");
if (file)
{
fprintf(file, "%d", assigned_port);
fclose(file);
tf_printf("Wrote the port file: %s.\n", out_http_port_file);
}
else
{
tf_printf("Failed to open %s for write: %s.\n", out_http_port_file, strerror(errno));
}
tf_free((char*)actual_http_port_file);
}
if (https_port)
{
const char* k_certificate = "data/httpd/certificate.pem";
const char* k_private_key = "data/httpd/privatekey.pem";
const char* certificate = _httpd_read_file(task, k_certificate);
const char* private_key = _httpd_read_file(task, k_private_key);
if (certificate && private_key)
{
tf_tls_context_t* tls = tf_tls_context_create();
tf_tls_context_set_certificate(tls, certificate);
tf_tls_context_set_private_key(tls, private_key);
httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
*listener = (httpd_listener_t) { .tls = tls };
int assigned_port = tf_http_listen(http, https_port, tls, _httpd_listener_cleanup, listener);
tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "https://127.0.0.1:%d/" RESET ".\n", assigned_port);
}
tf_free((char*)certificate);
tf_free((char*)private_key);
}
}
}

1
src/index.md Normal file

@ -0,0 +1 @@
\mainpage Tilde Friends Source Documentation

@ -16,7 +16,7 @@ void tf_run_thread_start(const char* zip_path);
static void _start_initial_load(WKWebView* web_view)
{
[web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:12345/login/auto"]]];
[web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://localhost:12345/"]]];
}
@implementation ViewController : UIViewController

@ -7,6 +7,6 @@
<key>com.apple.developer.team-identifier</key>
<string>EDVXQ27EB5</string>
<key>get-task-allow</key>
<false/>
<true/>
</dict>
</plist>

@ -2,86 +2,34 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>Tilde Friends</string>
<key>CFBundleExecutable</key>
<string>tildefriends</string>
<key>CFBundleIdentifier</key>
<string>com.unprompted.tildefriends</string>
<key>CFBundleName</key>
<string>tildefriends</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.0.28</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>iPhoneOS</string>
</array>
<key>CFBundleVersion</key>
<string>8</string>
<key>DTPlatformName</key>
<string>iphoneos</string>
<key>CFBundleExecutable</key>
<string>tildefriends</string>
<key>CFBundleIdentifier</key>
<string>com.unprompted.tildefriends</string>
<key>CFBundleResourceSpecification</key>
<string>ResourceRules.plist</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>14.0</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>CFBundleDisplayName</key>
<string>Tilde Friends</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIconFile</key>
<string>tildefriends.png</string>
<key>UILaunchScreen</key>
<dict>
<key>UIImageName</key>
<string>tildefriends.png</string>
</dict>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>BuildMachineOSBuild</key>
<string>24D70</string>
<key>DTPlatformBuild</key>
<string>22C146</string>
<key>DTPlatformVersion</key>
<string>18.2</string>
<key>DTSDKBuild</key>
<string>22C146</string>
<key>DTSDKName</key>
<string>iphoneos18.2</string>
<key>DTXcode</key>
<string>1620</string>
<key>DTXcodeBuild</key>
<string>16C5032a</string>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~iphone</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>CFBundleIcons</key>
<dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>AppIcon60x60</string>
</array>
<key>CFBundleIconName</key>
<string>AppIcon</string>
</dict>
</dict>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
</dict>
</plist>

Binary file not shown.

Binary file not shown.

@ -1 +0,0 @@
{"images":[{"idiom":"ios-marketing","scale":"1x","size":"1024x1024","filename":"icon-ios-marketing-1-1024-1024.png"}],"info":{"author":"xcode","version":1}}

Binary file not shown.

Before

(image error) Size: 101 KiB

@ -1 +0,0 @@
{"info": {"version": 1, "author": "xcode"}}

Binary file not shown.

Before

(image error) Size: 4.2 KiB

Binary file not shown.

Before

(image error) Size: 1.8 KiB

Binary file not shown.

Before

(image error) Size: 2.6 KiB

Binary file not shown.

Before

(image error) Size: 5.9 KiB

Binary file not shown.

Before

(image error) Size: 9.6 KiB

Binary file not shown.

Before

(image error) Size: 13 KiB

Binary file not shown.

Before

(image error) Size: 6.2 KiB

Binary file not shown.

Before

(image error) Size: 7.2 KiB

Binary file not shown.

Before

(image error) Size: 8.3 KiB

Binary file not shown.

Before

(image error) Size: 639 B

Binary file not shown.

Before

(image error) Size: 12 KiB

Binary file not shown.

Before

(image error) Size: 12 KiB

Binary file not shown.

Before

(image error) Size: 14 KiB

Binary file not shown.

Before

(image error) Size: 15 KiB

Binary file not shown.

Before

(image error) Size: 17 KiB

Binary file not shown.

Before

(image error) Size: 1.5 KiB

Binary file not shown.

Before

(image error) Size: 2.6 KiB

Binary file not shown.

Before

(image error) Size: 2.7 KiB

Binary file not shown.

Before

(image error) Size: 38 KiB

Binary file not shown.

Before

(image error) Size: 3.1 KiB

Binary file not shown.

Before

(image error) Size: 3.7 KiB

Binary file not shown.

Before

(image error) Size: 5.3 KiB

Binary file not shown.

Before

(image error) Size: 7.6 KiB

Binary file not shown.

Before

(image error) Size: 9.6 KiB

Binary file not shown.

Before

(image error) Size: 10 KiB

Binary file not shown.

Before

(image error) Size: 11 KiB

Binary file not shown.

Before

(image error) Size: 4.2 KiB

Binary file not shown.

Before

(image error) Size: 4.5 KiB

Binary file not shown.

Before

(image error) Size: 858 B

Binary file not shown.

Before

(image error) Size: 2.1 KiB

Binary file not shown.

Before

(image error) Size: 3.4 KiB

Binary file not shown.

Before

(image error) Size: 7.6 KiB

Binary file not shown.

Before

(image error) Size: 2.1 KiB

Binary file not shown.

Before

(image error) Size: 4.8 KiB

Binary file not shown.

Before

(image error) Size: 1.4 KiB

Binary file not shown.

Before

(image error) Size: 3.2 KiB

Binary file not shown.

Before

(image error) Size: 5.2 KiB

Binary file not shown.

Before

(image error) Size: 101 KiB

Some files were not shown because too many files have changed in this diff Show More