forked from cory/tildefriends
		
	Compare commits
	
		
			28 Commits
		
	
	
		
			43f6a3a482
			...
			tasiaiso-h
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						5e72c9caf4
	
				 | 
					
					
						|||
| 71329c5532 | |||
| feb4bf9e87 | |||
| 5d5567e94c | |||
| 684e6fb9cb | |||
| 
						
						
							
						
						ee21fa6d03
	
				 | 
					
					
						|||
| 7a2974e54f | |||
| f4dfc1dd98 | |||
| 2eebfa9a7a | |||
| 10097ffeb8 | |||
| cbe1f54a2a | |||
| 4d8f081a59 | |||
| 29e79c9484 | |||
| ba35869b0a | |||
| 580688381e | |||
| e63d69a440 | |||
| be64fe04fb | |||
| 801ab20723 | |||
| d974a5e044 | |||
| 1be94ae0be | |||
| b883e6a485 | |||
| a0210379ae | |||
| 
						
						
							
						
						3b36496dac
	
				 | 
					
					
						|||
| 
						
						
							
						
						4ebd6c24a9
	
				 | 
					
					
						|||
| 
						
						
							
						
						05451d98b3
	
				 | 
					
					
						|||
| 
						
						
							
						
						22a4bce3c8
	
				 | 
					
					
						|||
| 76d499f00b | |||
| 
						
						
							
						
						f0772f9b99
	
				 | 
					
					
						
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -8,3 +8,4 @@ out
 | 
			
		||||
*.swo
 | 
			
		||||
*.swp
 | 
			
		||||
.zsign_cache/
 | 
			
		||||
result
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.husky/pre-commit
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.husky/pre-commit
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
make format || exit 0
 | 
			
		||||
@@ -2,6 +2,7 @@ node_modules
 | 
			
		||||
src
 | 
			
		||||
deps
 | 
			
		||||
.clang-format
 | 
			
		||||
flake.lock
 | 
			
		||||
 | 
			
		||||
# Minified files
 | 
			
		||||
**/*.min.css
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								GNUmakefile
									
									
									
									
									
								
							@@ -3,11 +3,11 @@
 | 
			
		||||
MAKEFLAGS += --warn-undefined-variables
 | 
			
		||||
MAKEFLAGS += --no-builtin-rules
 | 
			
		||||
 | 
			
		||||
VERSION_CODE := 19
 | 
			
		||||
VERSION_NUMBER := 0.0.19-wip
 | 
			
		||||
VERSION_NAME := Don't let your loyalty become a burden.
 | 
			
		||||
VERSION_CODE := 20
 | 
			
		||||
VERSION_NUMBER := 0.0.20-wip
 | 
			
		||||
VERSION_NAME := One word all lowercase four words all uppercase.
 | 
			
		||||
 | 
			
		||||
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3450300.zip
 | 
			
		||||
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3460000.zip
 | 
			
		||||
LIBUV_URL := https://dist.libuv.org/dist/v1.48.0/libuv-v1.48.0.tar.gz
 | 
			
		||||
 | 
			
		||||
PROJECT = tildefriends
 | 
			
		||||
@@ -616,7 +616,7 @@ $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
 | 
			
		||||
 | 
			
		||||
unix: debug release
 | 
			
		||||
win: windebug winrelease
 | 
			
		||||
all: $(BUILD_TYPES)
 | 
			
		||||
all: $(BUILD_TYPES) default.nix
 | 
			
		||||
.PHONY: all win unix
 | 
			
		||||
 | 
			
		||||
ALL_APP_OBJS := \
 | 
			
		||||
@@ -673,6 +673,10 @@ src/android/AndroidManifest.xml : $(firstword $(MAKEFILE_LIST))
 | 
			
		||||
		-e 's/android:targetSdkVersion="[[:digit:]]*"/android:targetSdkVersion="$(ANDROID_TARGET_SDK_VERSION)"/' \
 | 
			
		||||
		$@
 | 
			
		||||
 | 
			
		||||
default.nix : $(firstword $(MAKEFILE_LIST))
 | 
			
		||||
	@echo "[version] $@"
 | 
			
		||||
	@sed -i -e 's/version = ".*";/version = "$(VERSION_NUMBER)";/' $@
 | 
			
		||||
 | 
			
		||||
# Android support.
 | 
			
		||||
out/res/layout_activity_main.xml.flat: src/android/res/layout/activity_main.xml
 | 
			
		||||
	@mkdir -p $(dir $@)
 | 
			
		||||
@@ -858,7 +862,7 @@ clean:
 | 
			
		||||
	rm -rf $(BUILD_DIR)
 | 
			
		||||
.PHONY: clean
 | 
			
		||||
 | 
			
		||||
dist: release-apk iosrelease-ipa $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe)
 | 
			
		||||
dist: release-apk iosrelease-ipa $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) default.nix
 | 
			
		||||
	@echo [archive] dist/tildefriends-$(VERSION_NUMBER).tar.xz
 | 
			
		||||
	@rm -rf out/tildefriends-$(VERSION_NUMBER)
 | 
			
		||||
	@mkdir -p dist/ out/tildefriends-$(VERSION_NUMBER)
 | 
			
		||||
@@ -898,7 +902,7 @@ dist-test: dist
 | 
			
		||||
	@rm -rf tildefriends-$(VERSION_NUMBER)
 | 
			
		||||
.PHONY: dist-test
 | 
			
		||||
 | 
			
		||||
format:
 | 
			
		||||
format: prettier
 | 
			
		||||
	@clang-format -i $(wildcard src/*.c src/*.h src/*.m)
 | 
			
		||||
.PHONY: format
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -67,9 +67,6 @@ tfrpc.register(function getHash(id, message) {
 | 
			
		||||
tfrpc.register(function setHash(hash) {
 | 
			
		||||
	return app.setHash(hash);
 | 
			
		||||
});
 | 
			
		||||
ssb.addEventListener('message', async function (id) {
 | 
			
		||||
	await tfrpc.rpc.notifyNewMessage(id);
 | 
			
		||||
});
 | 
			
		||||
tfrpc.register(async function store_blob(blob) {
 | 
			
		||||
	if (Array.isArray(blob)) {
 | 
			
		||||
		blob = Uint8Array.from(blob);
 | 
			
		||||
@@ -91,10 +88,12 @@ tfrpc.register(function getActiveIdentity() {
 | 
			
		||||
tfrpc.register(async function try_decrypt(id, content) {
 | 
			
		||||
	return await ssb.privateMessageDecrypt(id, content);
 | 
			
		||||
});
 | 
			
		||||
ssb.addEventListener('broadcasts', async function () {
 | 
			
		||||
core.register('onMessage', async function (id) {
 | 
			
		||||
	await tfrpc.rpc.notifyNewMessage(id);
 | 
			
		||||
});
 | 
			
		||||
core.register('onBroadcastsChanged', async function () {
 | 
			
		||||
	await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
core.register('onConnectionsChanged', async function () {
 | 
			
		||||
	await tfrpc.rpc.set('connections', await ssb.connections());
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ function new_message() {
 | 
			
		||||
	return g_new_message_promise;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ssb.addEventListener('message', function (id) {
 | 
			
		||||
core.register('onMessage', function (id) {
 | 
			
		||||
	let resolve = g_new_message_resolve;
 | 
			
		||||
	g_new_message_promise = new Promise(function (resolve, reject) {
 | 
			
		||||
		g_new_message_resolve = resolve;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
	"type": "tildefriends-app",
 | 
			
		||||
	"emoji": "🐌",
 | 
			
		||||
	"previous": "&wA6sLaDxtYeFdVCCu8jyhPsGYtGZEjbWQHeGOn0Yifg=.sha256"
 | 
			
		||||
	"previous": "&zRv7YNZBT/NoliiTS7Jn/Q+3przdFZljUl8yPBIpSSE=.sha256"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ tfrpc.register(function getHash(id, message) {
 | 
			
		||||
tfrpc.register(function setHash(hash) {
 | 
			
		||||
	return app.setHash(hash);
 | 
			
		||||
});
 | 
			
		||||
ssb.addEventListener('message', async function (id) {
 | 
			
		||||
core.register('onMessage', async function (id) {
 | 
			
		||||
	await tfrpc.rpc.notifyNewMessage(id);
 | 
			
		||||
});
 | 
			
		||||
tfrpc.register(async function store_blob(blob) {
 | 
			
		||||
@@ -103,7 +103,7 @@ tfrpc.register(async function encrypt(id, recipients, content) {
 | 
			
		||||
tfrpc.register(async function getActiveIdentity() {
 | 
			
		||||
	return await ssb.getActiveIdentity();
 | 
			
		||||
});
 | 
			
		||||
ssb.addEventListener('broadcasts', async function () {
 | 
			
		||||
core.register('onBroadcastsChanged', async function () {
 | 
			
		||||
	await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
import * as tfrpc from '/static/tfrpc.js';
 | 
			
		||||
import {html, render} from './lit-all.min.js';
 | 
			
		||||
import {styles} from './tf-styles.js';
 | 
			
		||||
 | 
			
		||||
let g_emojis;
 | 
			
		||||
 | 
			
		||||
@@ -36,11 +38,6 @@ export async function picker(callback, anchor, author) {
 | 
			
		||||
	div.style.background = '#fff';
 | 
			
		||||
	div.style.border = '1px solid #000';
 | 
			
		||||
	div.style.display = 'block';
 | 
			
		||||
	div.style.position = 'absolute';
 | 
			
		||||
	div.style.minWidth = 'min(16em, 90vw)';
 | 
			
		||||
	div.style.width = 'min(16em, 90vw)';
 | 
			
		||||
	div.style.maxWidth = 'min(16em, 90vw)';
 | 
			
		||||
	div.style.maxHeight = '16em';
 | 
			
		||||
	div.style.overflow = 'scroll';
 | 
			
		||||
	div.style.fontWeight = 'bold';
 | 
			
		||||
	div.style.fontSize = 'xx-large';
 | 
			
		||||
@@ -58,14 +55,6 @@ export async function picker(callback, anchor, author) {
 | 
			
		||||
		event.stopPropagation();
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	function cleanup() {
 | 
			
		||||
		console.log('emoji cleanup');
 | 
			
		||||
		div.parentElement.removeChild(div);
 | 
			
		||||
		window.removeEventListener('keydown', key_down);
 | 
			
		||||
		console.log('removing click');
 | 
			
		||||
		document.body.removeEventListener('mousedown', cleanup);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function key_down(event) {
 | 
			
		||||
		if (event.key == 'Escape') {
 | 
			
		||||
			cleanup();
 | 
			
		||||
@@ -153,13 +142,23 @@ export async function picker(callback, anchor, author) {
 | 
			
		||||
	}
 | 
			
		||||
	refresh();
 | 
			
		||||
	input.oninput = refresh;
 | 
			
		||||
	document.body.appendChild(div);
 | 
			
		||||
	div.style.position = 'fixed';
 | 
			
		||||
	div.style.top = '50%';
 | 
			
		||||
	div.style.left = '50%';
 | 
			
		||||
	div.style.transform = 'translate(-50%, -50%)';
 | 
			
		||||
	let modal = html`
 | 
			
		||||
		<style>
 | 
			
		||||
			${styles}
 | 
			
		||||
		</style>
 | 
			
		||||
		<div class="w3-modal" style="display: block">
 | 
			
		||||
			<div class="w3-modal-content w3-card-4">${div}</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	`;
 | 
			
		||||
	let parent = document.createElement('div');
 | 
			
		||||
	document.body.appendChild(parent);
 | 
			
		||||
	function cleanup() {
 | 
			
		||||
		parent.parentElement.removeChild(parent);
 | 
			
		||||
		window.removeEventListener('keydown', key_down);
 | 
			
		||||
		document.body.removeEventListener('mousedown', cleanup);
 | 
			
		||||
	}
 | 
			
		||||
	render(modal, parent);
 | 
			
		||||
	input.focus();
 | 
			
		||||
	console.log('adding click');
 | 
			
		||||
	document.body.addEventListener('mousedown', cleanup);
 | 
			
		||||
	window.addEventListener('keydown', key_down);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -172,7 +172,7 @@ class TfMessageElement extends LitElement {
 | 
			
		||||
			event.srcElement.classList.contains('img_caption')
 | 
			
		||||
		) {
 | 
			
		||||
			let next = event.srcElement.nextSibling;
 | 
			
		||||
			if (next.style.display == 'block') {
 | 
			
		||||
			if (next.style.display != 'none') {
 | 
			
		||||
				next.style.display = 'none';
 | 
			
		||||
			} else {
 | 
			
		||||
				next.style.display = 'block';
 | 
			
		||||
 
 | 
			
		||||
@@ -136,7 +136,7 @@ class TfTabNewsElement extends LitElement {
 | 
			
		||||
					${this.new_messages_text()}
 | 
			
		||||
				</button>
 | 
			
		||||
			</p>
 | 
			
		||||
			<div>
 | 
			
		||||
			<div class="w3-bar">
 | 
			
		||||
				Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!
 | 
			
		||||
				${edit_profile}
 | 
			
		||||
			</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -50,7 +50,7 @@ function new_message() {
 | 
			
		||||
	return g_new_message_promise;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ssb.addEventListener('message', function (id) {
 | 
			
		||||
core.register('onMessage', function (id) {
 | 
			
		||||
	let resolve = g_new_message_resolve;
 | 
			
		||||
	g_new_message_promise = new Promise(function (resolve, reject) {
 | 
			
		||||
		g_new_message_resolve = resolve;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ let gEditor;
 | 
			
		||||
let gOriginalInput;
 | 
			
		||||
 | 
			
		||||
let kErrorColor = '#dc322f';
 | 
			
		||||
let kDisconnectColor = '#f00';
 | 
			
		||||
let kStatusColor = '#fff';
 | 
			
		||||
 | 
			
		||||
// Functions that server-side app code can call through the app object.
 | 
			
		||||
@@ -1548,7 +1549,7 @@ function connectSocket(path) {
 | 
			
		||||
			};
 | 
			
		||||
			setStatusMessage(
 | 
			
		||||
				'🔴 Closed: ' + (k_codes[event.code] || event.code),
 | 
			
		||||
				kErrorColor
 | 
			
		||||
				kDisconnectColor
 | 
			
		||||
			);
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -678,6 +678,8 @@ async function getProcessBlob(blobId, key, options) {
 | 
			
		||||
					);
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
			imports.ssb.addEventListener = undefined;
 | 
			
		||||
			imports.ssb.removeEventListener = undefined;
 | 
			
		||||
			imports.ssb.getIdentityInfo = undefined;
 | 
			
		||||
			imports.fetch = function (url, options) {
 | 
			
		||||
				return http.fetch(url, options, gGlobalSettings.fetch_hosts);
 | 
			
		||||
@@ -1228,6 +1230,10 @@ async function blobHandler(request, response, blobId, uri) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ssb.addEventListener('message', function () {
 | 
			
		||||
	broadcastEvent('onMessage', [...arguments]);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
ssb.addEventListener('broadcasts', function () {
 | 
			
		||||
	broadcastEvent('onBroadcastsChanged', []);
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										64
									
								
								default.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								default.nix
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
# How to upgrade to a newer version
 | 
			
		||||
# - Comment `src.hash`
 | 
			
		||||
# - Change `version`
 | 
			
		||||
# - Run `$ nix build`
 | 
			
		||||
# This will fetch the source code
 | 
			
		||||
# Since `hash` is not provided, nix will stop building and throw an error:
 | 
			
		||||
#
 | 
			
		||||
# error: hash mismatch in fixed-output derivation '/nix/store/fghi3ljs6fhz8pwm3dh73j5fwjpq5wbz-source.drv':
 | 
			
		||||
#          specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
 | 
			
		||||
#             got:    sha256-+uthA1w8CmZfW+WOK9wYGl2fUl/k10ufOc8W+Pwa9iQ=
 | 
			
		||||
# error: 1 dependencies of derivation '/nix/store/imcwsw5r74vkd8r0qa2k7cys2xfgraaz-tildefriends-0.0.18.drv' failed to build
 | 
			
		||||
#
 | 
			
		||||
# - Change `src.hash` to the new one, ie `sha256-+uthA1w8CmZfW+WOK9wYGl2fUl/k10ufOc8W+Pwa9iQ=`
 | 
			
		||||
# - Uncomment `src.hash`
 | 
			
		||||
# - Build again, this time it should work.
 | 
			
		||||
# - Check the release notes, if there's a new dependency or a change to `GNUMakefile`, this file might need to be changed too.
 | 
			
		||||
# For more details, contact tasiaiso @ https://tilde.club/~tasiaiso/
 | 
			
		||||
{
 | 
			
		||||
  pkgs ? import <nixpkgs> {},
 | 
			
		||||
  lib ? import <nixpkgs/lib>,
 | 
			
		||||
}:
 | 
			
		||||
pkgs.stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "tildefriends";
 | 
			
		||||
  version = "0.0.19";
 | 
			
		||||
 | 
			
		||||
  src = pkgs.fetchFromGitea {
 | 
			
		||||
    domain = "dev.tildefriends.net";
 | 
			
		||||
    owner = "cory";
 | 
			
		||||
    repo = "tildefriends";
 | 
			
		||||
    rev = "v${version}";
 | 
			
		||||
    hash = "sha256-ttqL2wz06Jvn2f6kKIAGpF0nSSle+g4nSlj4jL0D+Fk=";
 | 
			
		||||
    fetchSubmodules = true;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  nativeBuildInputs = with pkgs; [
 | 
			
		||||
    gnumake
 | 
			
		||||
    openssl
 | 
			
		||||
    which
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  buildInputs = with pkgs; [
 | 
			
		||||
    openssl
 | 
			
		||||
    which
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  buildPhase = ''
 | 
			
		||||
    make -j $NIX_BUILD_CORES release
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
  installPhase = ''
 | 
			
		||||
    mkdir -p $out/bin
 | 
			
		||||
    cp -r out/release/tildefriends $out/bin
 | 
			
		||||
  '';
 | 
			
		||||
 | 
			
		||||
  doCheck = false;
 | 
			
		||||
 | 
			
		||||
  meta = with pkgs; {
 | 
			
		||||
    homepage = "https://tildefriends.net";
 | 
			
		||||
    description = "Make apps and friends from the comfort of your web browser.";
 | 
			
		||||
    mainProgram = "tildefriends";
 | 
			
		||||
    license = with lib.licenses; [mit];
 | 
			
		||||
    platforms = lib.platforms.all;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								deps/codemirror/cm6.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								deps/codemirror/cm6.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										146
									
								
								deps/codemirror_src/package-lock.json
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										146
									
								
								deps/codemirror_src/package-lock.json
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -111,9 +111,9 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@codemirror/lint": {
 | 
			
		||||
      "version": "6.7.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.7.0.tgz",
 | 
			
		||||
      "integrity": "sha512-LTLOL2nT41ADNSCCCCw8Q/UmdAFzB23OUYSjsHTdsVaH0XEo+orhuqbDNWzrzodm14w6FOxqxpmy4LF8Lixqjw==",
 | 
			
		||||
      "version": "6.8.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.0.tgz",
 | 
			
		||||
      "integrity": "sha512-lsFofvaw0lnPRJlQylNsC4IRt/1lI4OD/yYslrSGVndOJfStc58v+8p9dgGiD90ktOfL7OhBWns1ZETYgz0EJA==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@codemirror/state": "^6.0.0",
 | 
			
		||||
        "@codemirror/view": "^6.0.0",
 | 
			
		||||
@@ -248,9 +248,9 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@lezer/javascript": {
 | 
			
		||||
      "version": "1.4.15",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.15.tgz",
 | 
			
		||||
      "integrity": "sha512-B082ZdjI0vo2AgLqD834GlRTE9gwRX8NzHzKq5uDwEnQ9Dq+A/CEhd3nf68tiNA2f9O+8jS1NeSTUYT9IAqcTw==",
 | 
			
		||||
      "version": "1.4.16",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.16.tgz",
 | 
			
		||||
      "integrity": "sha512-84UXR3N7s11MPQHWgMnjb9571fr19MmXnr5zTv2XX0gHXXUvW3uPJ8GCjKrfTXmSdfktjRK0ayKklw+A13rk4g==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@lezer/common": "^1.2.0",
 | 
			
		||||
        "@lezer/highlight": "^1.1.3",
 | 
			
		||||
@@ -343,9 +343,9 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-android-arm-eabi": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "arm"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -355,9 +355,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-android-arm64": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "arm64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -367,9 +367,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-darwin-arm64": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "arm64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -379,9 +379,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-darwin-x64": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "x64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -391,9 +391,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "arm"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -403,9 +403,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "arm"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -415,9 +415,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-linux-arm64-gnu": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "arm64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -427,9 +427,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-linux-arm64-musl": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "arm64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -439,9 +439,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "ppc64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -451,9 +451,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "riscv64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -463,9 +463,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-linux-s390x-gnu": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "s390x"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -475,9 +475,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-linux-x64-gnu": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "x64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -487,9 +487,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-linux-x64-musl": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "x64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -499,9 +499,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-win32-arm64-msvc": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "arm64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -511,9 +511,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-win32-ia32-msvc": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "ia32"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -523,9 +523,9 @@
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@rollup/rollup-win32-x64-msvc": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==",
 | 
			
		||||
      "cpu": [
 | 
			
		||||
        "x64"
 | 
			
		||||
      ],
 | 
			
		||||
@@ -715,9 +715,9 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/rollup": {
 | 
			
		||||
      "version": "4.17.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz",
 | 
			
		||||
      "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==",
 | 
			
		||||
      "version": "4.18.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
 | 
			
		||||
      "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/estree": "1.0.5"
 | 
			
		||||
      },
 | 
			
		||||
@@ -729,22 +729,22 @@
 | 
			
		||||
        "npm": ">=8.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "optionalDependencies": {
 | 
			
		||||
        "@rollup/rollup-android-arm-eabi": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-android-arm64": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-darwin-arm64": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-darwin-x64": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-linux-arm-gnueabihf": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-linux-arm-musleabihf": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-linux-arm64-gnu": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-linux-arm64-musl": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-linux-riscv64-gnu": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-linux-s390x-gnu": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-linux-x64-gnu": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-linux-x64-musl": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-win32-arm64-msvc": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-win32-ia32-msvc": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-win32-x64-msvc": "4.17.2",
 | 
			
		||||
        "@rollup/rollup-android-arm-eabi": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-android-arm64": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-darwin-arm64": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-darwin-x64": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-linux-arm-gnueabihf": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-linux-arm-musleabihf": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-linux-arm64-gnu": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-linux-arm64-musl": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-linux-riscv64-gnu": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-linux-s390x-gnu": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-linux-x64-gnu": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-linux-x64-musl": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-win32-arm64-msvc": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-win32-ia32-msvc": "4.18.0",
 | 
			
		||||
        "@rollup/rollup-win32-x64-msvc": "4.18.0",
 | 
			
		||||
        "fsevents": "~2.3.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								deps/libsodium
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								deps/libsodium
									
									
									
									
										vendored
									
									
								
							 Submodule deps/libsodium updated: fb4533b0a9...9511c982fb
									
								
							
							
								
								
									
										1679
									
								
								deps/sqlite/shell.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1679
									
								
								deps/sqlite/shell.c
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										8471
									
								
								deps/sqlite/sqlite3.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8471
									
								
								deps/sqlite/sqlite3.c
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										97
									
								
								deps/sqlite/sqlite3.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										97
									
								
								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.45.3"
 | 
			
		||||
#define SQLITE_VERSION_NUMBER 3045003
 | 
			
		||||
#define SQLITE_SOURCE_ID      "2024-04-15 13:34:05 8653b758870e6ef0c98d46b3ace27849054af85da891eb121e9aaa537f1e8355"
 | 
			
		||||
#define SQLITE_VERSION        "3.46.0"
 | 
			
		||||
#define SQLITE_VERSION_NUMBER 3046000
 | 
			
		||||
#define SQLITE_SOURCE_ID      "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
** CAPI3REF: Run-Time Library Version Numbers
 | 
			
		||||
@@ -764,11 +764,11 @@ struct sqlite3_file {
 | 
			
		||||
** </ul>
 | 
			
		||||
** xLock() upgrades the database file lock.  In other words, xLock() moves the
 | 
			
		||||
** database file lock in the direction NONE toward EXCLUSIVE. The argument to
 | 
			
		||||
** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
 | 
			
		||||
** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
 | 
			
		||||
** SQLITE_LOCK_NONE.  If the database file lock is already at or above the
 | 
			
		||||
** requested lock, then the call to xLock() is a no-op.
 | 
			
		||||
** xUnlock() downgrades the database file lock to either SHARED or NONE.
 | 
			
		||||
*  If the lock is already at or below the requested lock state, then the call
 | 
			
		||||
** If the lock is already at or below the requested lock state, then the call
 | 
			
		||||
** to xUnlock() is a no-op.
 | 
			
		||||
** The xCheckReservedLock() method checks whether any database connection,
 | 
			
		||||
** either in this process or in some other process, is holding a RESERVED,
 | 
			
		||||
@@ -3305,8 +3305,8 @@ SQLITE_API int sqlite3_set_authorizer(
 | 
			
		||||
#define SQLITE_RECURSIVE            33   /* NULL            NULL            */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
** CAPI3REF: Tracing And Profiling Functions
 | 
			
		||||
** METHOD: sqlite3
 | 
			
		||||
** CAPI3REF: Deprecated Tracing And Profiling Functions
 | 
			
		||||
** DEPRECATED
 | 
			
		||||
**
 | 
			
		||||
** These routines are deprecated. Use the [sqlite3_trace_v2()] interface
 | 
			
		||||
** instead of the routines described here.
 | 
			
		||||
@@ -6887,6 +6887,12 @@ SQLITE_API int sqlite3_autovacuum_pages(
 | 
			
		||||
** The exceptions defined in this paragraph might change in a future
 | 
			
		||||
** release of SQLite.
 | 
			
		||||
**
 | 
			
		||||
** Whether the update hook is invoked before or after the
 | 
			
		||||
** corresponding change is currently unspecified and may differ
 | 
			
		||||
** depending on the type of change. Do not rely on the order of the
 | 
			
		||||
** hook call with regards to the final result of the operation which
 | 
			
		||||
** triggers the hook.
 | 
			
		||||
**
 | 
			
		||||
** The update hook implementation must not do anything that will modify
 | 
			
		||||
** the database connection that invoked the update hook.  Any actions
 | 
			
		||||
** to modify the database connection must be deferred until after the
 | 
			
		||||
@@ -8357,7 +8363,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
 | 
			
		||||
** The sqlite3_keyword_count() interface returns the number of distinct
 | 
			
		||||
** keywords understood by SQLite.
 | 
			
		||||
**
 | 
			
		||||
** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and
 | 
			
		||||
** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and
 | 
			
		||||
** makes *Z point to that keyword expressed as UTF8 and writes the number
 | 
			
		||||
** of bytes in the keyword into *L.  The string that *Z points to is not
 | 
			
		||||
** zero-terminated.  The sqlite3_keyword_name(N,Z,L) routine returns
 | 
			
		||||
@@ -9936,24 +9942,45 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
 | 
			
		||||
** <li value="2"><p>
 | 
			
		||||
** ^(If the sqlite3_vtab_distinct() interface returns 2, that means
 | 
			
		||||
** that the query planner does not need the rows returned in any particular
 | 
			
		||||
** order, as long as rows with the same values in all "aOrderBy" columns
 | 
			
		||||
** are adjacent.)^  ^(Furthermore, only a single row for each particular
 | 
			
		||||
** combination of values in the columns identified by the "aOrderBy" field
 | 
			
		||||
** needs to be returned.)^  ^It is always ok for two or more rows with the same
 | 
			
		||||
** values in all "aOrderBy" columns to be returned, as long as all such rows
 | 
			
		||||
** are adjacent.  ^The virtual table may, if it chooses, omit extra rows
 | 
			
		||||
** that have the same value for all columns identified by "aOrderBy".
 | 
			
		||||
** ^However omitting the extra rows is optional.
 | 
			
		||||
** order, as long as rows with the same values in all columns identified
 | 
			
		||||
** by "aOrderBy" are adjacent.)^  ^(Furthermore, when two or more rows
 | 
			
		||||
** contain the same values for all columns identified by "colUsed", all but
 | 
			
		||||
** one such row may optionally be omitted from the result.)^
 | 
			
		||||
** The virtual table is not required to omit rows that are duplicates
 | 
			
		||||
** over the "colUsed" columns, but if the virtual table can do that without
 | 
			
		||||
** too much extra effort, it could potentially help the query to run faster.
 | 
			
		||||
** This mode is used for a DISTINCT query.
 | 
			
		||||
** <li value="3"><p>
 | 
			
		||||
** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
 | 
			
		||||
** that the query planner needs only distinct rows but it does need the
 | 
			
		||||
** rows to be sorted.)^ ^The virtual table implementation is free to omit
 | 
			
		||||
** rows that are identical in all aOrderBy columns, if it wants to, but
 | 
			
		||||
** it is not required to omit any rows.  This mode is used for queries
 | 
			
		||||
** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the
 | 
			
		||||
** virtual table must return rows in the order defined by "aOrderBy" as
 | 
			
		||||
** if the sqlite3_vtab_distinct() interface had returned 0.  However if
 | 
			
		||||
** two or more rows in the result have the same values for all columns
 | 
			
		||||
** identified by "colUsed", then all but one such row may optionally be
 | 
			
		||||
** omitted.)^  Like when the return value is 2, the virtual table
 | 
			
		||||
** is not required to omit rows that are duplicates over the "colUsed"
 | 
			
		||||
** columns, but if the virtual table can do that without
 | 
			
		||||
** too much extra effort, it could potentially help the query to run faster.
 | 
			
		||||
** This mode is used for queries
 | 
			
		||||
** that have both DISTINCT and ORDER BY clauses.
 | 
			
		||||
** </ol>
 | 
			
		||||
**
 | 
			
		||||
** <p>The following table summarizes the conditions under which the
 | 
			
		||||
** virtual table is allowed to set the "orderByConsumed" flag based on
 | 
			
		||||
** the value returned by sqlite3_vtab_distinct().  This table is a
 | 
			
		||||
** restatement of the previous four paragraphs:
 | 
			
		||||
**
 | 
			
		||||
** <table border=1 cellspacing=0 cellpadding=10 width="90%">
 | 
			
		||||
** <tr>
 | 
			
		||||
** <td valign="top">sqlite3_vtab_distinct() return value
 | 
			
		||||
** <td valign="top">Rows are returned in aOrderBy order
 | 
			
		||||
** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent
 | 
			
		||||
** <td valign="top">Duplicates over all colUsed columns may be omitted
 | 
			
		||||
** <tr><td>0<td>yes<td>yes<td>no
 | 
			
		||||
** <tr><td>1<td>no<td>yes<td>no
 | 
			
		||||
** <tr><td>2<td>no<td>yes<td>yes
 | 
			
		||||
** <tr><td>3<td>yes<td>yes<td>yes
 | 
			
		||||
** </table>
 | 
			
		||||
**
 | 
			
		||||
** ^For the purposes of comparing virtual table output values to see if the
 | 
			
		||||
** values are same value for sorting purposes, two NULL values are considered
 | 
			
		||||
** to be the same.  In other words, the comparison operator is "IS"
 | 
			
		||||
@@ -11998,6 +12025,30 @@ SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const c
 | 
			
		||||
*/
 | 
			
		||||
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
** CAPI3REF: Add A Single Change To A Changegroup
 | 
			
		||||
** METHOD: sqlite3_changegroup
 | 
			
		||||
**
 | 
			
		||||
** This function adds the single change currently indicated by the iterator
 | 
			
		||||
** passed as the second argument to the changegroup object. The rules for
 | 
			
		||||
** adding the change are just as described for [sqlite3changegroup_add()].
 | 
			
		||||
**
 | 
			
		||||
** If the change is successfully added to the changegroup, SQLITE_OK is
 | 
			
		||||
** returned. Otherwise, an SQLite error code is returned.
 | 
			
		||||
**
 | 
			
		||||
** The iterator must point to a valid entry when this function is called.
 | 
			
		||||
** If it does not, SQLITE_ERROR is returned and no change is added to the
 | 
			
		||||
** changegroup. Additionally, the iterator must not have been opened with
 | 
			
		||||
** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also
 | 
			
		||||
** returned.
 | 
			
		||||
*/
 | 
			
		||||
SQLITE_API int sqlite3changegroup_add_change(
 | 
			
		||||
  sqlite3_changegroup*,
 | 
			
		||||
  sqlite3_changeset_iter*
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
 | 
			
		||||
** METHOD: sqlite3_changegroup
 | 
			
		||||
@@ -12802,8 +12853,8 @@ struct Fts5PhraseIter {
 | 
			
		||||
** EXTENSION API FUNCTIONS
 | 
			
		||||
**
 | 
			
		||||
** xUserData(pFts):
 | 
			
		||||
**   Return a copy of the context pointer the extension function was
 | 
			
		||||
**   registered with.
 | 
			
		||||
**   Return a copy of the pUserData pointer passed to the xCreateFunction()
 | 
			
		||||
**   API when the extension function was registered.
 | 
			
		||||
**
 | 
			
		||||
** xColumnTotalSize(pFts, iCol, pnToken):
 | 
			
		||||
**   If parameter iCol is less than zero, set output variable *pnToken
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								flake.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								flake.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
{
 | 
			
		||||
	"nodes": {
 | 
			
		||||
		"flake-utils": {
 | 
			
		||||
			"inputs": {
 | 
			
		||||
				"systems": "systems"
 | 
			
		||||
			},
 | 
			
		||||
			"locked": {
 | 
			
		||||
				"lastModified": 1710146030,
 | 
			
		||||
				"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
 | 
			
		||||
				"owner": "numtide",
 | 
			
		||||
				"repo": "flake-utils",
 | 
			
		||||
				"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
 | 
			
		||||
				"type": "github"
 | 
			
		||||
			},
 | 
			
		||||
			"original": {
 | 
			
		||||
				"owner": "numtide",
 | 
			
		||||
				"repo": "flake-utils",
 | 
			
		||||
				"type": "github"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"nixpkgs": {
 | 
			
		||||
			"locked": {
 | 
			
		||||
				"lastModified": 1715395895,
 | 
			
		||||
				"narHash": "sha256-DreMqi6+qa21ffLQqhMQL2XRUkAGt3N7iVB5FhJKie4=",
 | 
			
		||||
				"owner": "NixOS",
 | 
			
		||||
				"repo": "nixpkgs",
 | 
			
		||||
				"rev": "71bae31b7dbc335528ca7e96f479ec93462323ff",
 | 
			
		||||
				"type": "github"
 | 
			
		||||
			},
 | 
			
		||||
			"original": {
 | 
			
		||||
				"owner": "NixOS",
 | 
			
		||||
				"ref": "nixos-23.11",
 | 
			
		||||
				"repo": "nixpkgs",
 | 
			
		||||
				"type": "github"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"root": {
 | 
			
		||||
			"inputs": {
 | 
			
		||||
				"flake-utils": "flake-utils",
 | 
			
		||||
				"nixpkgs": "nixpkgs"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"systems": {
 | 
			
		||||
			"locked": {
 | 
			
		||||
				"lastModified": 1681028828,
 | 
			
		||||
				"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
 | 
			
		||||
				"owner": "nix-systems",
 | 
			
		||||
				"repo": "default",
 | 
			
		||||
				"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
 | 
			
		||||
				"type": "github"
 | 
			
		||||
			},
 | 
			
		||||
			"original": {
 | 
			
		||||
				"owner": "nix-systems",
 | 
			
		||||
				"repo": "default",
 | 
			
		||||
				"type": "github"
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	"root": "root",
 | 
			
		||||
	"version": 7
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								flake.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								flake.nix
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
{
 | 
			
		||||
  description = "Tilde Friends is a platform for making, running, and sharing web applications.";
 | 
			
		||||
 | 
			
		||||
  inputs = {
 | 
			
		||||
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
 | 
			
		||||
    flake-utils.url = "github:numtide/flake-utils";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  outputs = {
 | 
			
		||||
    self,
 | 
			
		||||
    nixpkgs,
 | 
			
		||||
    flake-utils,
 | 
			
		||||
  }:
 | 
			
		||||
    flake-utils.lib.eachDefaultSystem (system: let
 | 
			
		||||
        pkgs = import nixpkgs {
 | 
			
		||||
          inherit system;
 | 
			
		||||
        };
 | 
			
		||||
      in rec
 | 
			
		||||
      {
 | 
			
		||||
        # Nix formatter, run using `$ nix fmt`
 | 
			
		||||
        formatter = pkgs.alejandra;
 | 
			
		||||
 | 
			
		||||
        # Exports the tildefriends package
 | 
			
		||||
        # Build with `$ nix build`
 | 
			
		||||
        packages.default = pkgs.callPackage ./default.nix {};
 | 
			
		||||
 | 
			
		||||
        # Creates a shell with the necessary dependencies
 | 
			
		||||
        # Enter using `$ nix develop`
 | 
			
		||||
        devShell = pkgs.mkShell {
 | 
			
		||||
          buildInputs = with pkgs; [
 | 
			
		||||
            openssl
 | 
			
		||||
            llvmPackages_17.clang-unwrapped
 | 
			
		||||
            unzip
 | 
			
		||||
          ];
 | 
			
		||||
        };
 | 
			
		||||
      });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -6,12 +6,29 @@
 | 
			
		||||
		"": {
 | 
			
		||||
			"name": "tildefriends",
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"dependencies": {
 | 
			
		||||
				"prettier": "^3.2.5"
 | 
			
		||||
			"devDependencies": {
 | 
			
		||||
				"husky": "9.0.11",
 | 
			
		||||
				"prettier": "3.2.5"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/husky": {
 | 
			
		||||
			"version": "9.0.11",
 | 
			
		||||
			"resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz",
 | 
			
		||||
			"integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"bin": {
 | 
			
		||||
				"husky": "bin.mjs"
 | 
			
		||||
			},
 | 
			
		||||
			"engines": {
 | 
			
		||||
				"node": ">=18"
 | 
			
		||||
			},
 | 
			
		||||
			"funding": {
 | 
			
		||||
				"url": "https://github.com/sponsors/typicode"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"node_modules/prettier": {
 | 
			
		||||
			"version": "3.2.5",
 | 
			
		||||
			"dev": true,
 | 
			
		||||
			"license": "MIT",
 | 
			
		||||
			"bin": {
 | 
			
		||||
				"prettier": "bin/prettier.cjs"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,13 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "tildefriends",
 | 
			
		||||
	"scripts": {
 | 
			
		||||
		"prettier": "prettier . --check --cache --write"
 | 
			
		||||
		"prettier": "prettier . --check --cache --write",
 | 
			
		||||
		"prepare": "husky"
 | 
			
		||||
	},
 | 
			
		||||
	"author": "Cory McWilliams",
 | 
			
		||||
	"license": "MIT",
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"prettier": "^3.2.5"
 | 
			
		||||
	"devDependencies": {
 | 
			
		||||
		"prettier": "3.2.5",
 | 
			
		||||
		"husky": "9.0.11"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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="19"
 | 
			
		||||
	android:versionName="0.0.19-wip">
 | 
			
		||||
	android:versionCode="20"
 | 
			
		||||
	android:versionName="0.0.20-wip">
 | 
			
		||||
	<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="34"/>
 | 
			
		||||
	<uses-permission android:name="android.permission.INTERNET"/>
 | 
			
		||||
	<application
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										111
									
								
								src/ssb.c
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								src/ssb.c
									
									
									
									
									
								
							@@ -342,6 +342,9 @@ typedef struct _tf_ssb_connection_t
 | 
			
		||||
 | 
			
		||||
	tf_ssb_debug_message_t* debug_messages[k_debug_close_message_count];
 | 
			
		||||
	int ref_count;
 | 
			
		||||
 | 
			
		||||
	int read_back_pressure;
 | 
			
		||||
	int active_write_count;
 | 
			
		||||
} tf_ssb_connection_t;
 | 
			
		||||
 | 
			
		||||
static JSClassID _connection_class_id;
 | 
			
		||||
@@ -458,9 +461,10 @@ static void _tf_ssb_connection_on_tcp_alloc(uv_handle_t* handle, size_t suggeste
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_connection_on_write(uv_write_t* req, int status)
 | 
			
		||||
{
 | 
			
		||||
	tf_ssb_connection_t* connection = req->data;
 | 
			
		||||
	tf_ssb_connection_adjust_write_count(connection, -1);
 | 
			
		||||
	if (status)
 | 
			
		||||
	{
 | 
			
		||||
		tf_ssb_connection_t* connection = req->data;
 | 
			
		||||
		char buffer[256];
 | 
			
		||||
		snprintf(buffer, sizeof(buffer), "write failed asynchronously: %s", uv_strerror(status));
 | 
			
		||||
		_tf_ssb_connection_close(connection, buffer);
 | 
			
		||||
@@ -475,9 +479,11 @@ static void _tf_ssb_write(tf_ssb_connection_t* connection, void* data, size_t si
 | 
			
		||||
		uv_write_t* write = tf_malloc(sizeof(uv_write_t) + size);
 | 
			
		||||
		*write = (uv_write_t) { .data = connection };
 | 
			
		||||
		memcpy(write + 1, data, size);
 | 
			
		||||
		tf_ssb_connection_adjust_write_count(connection, 1);
 | 
			
		||||
		int result = uv_write(write, (uv_stream_t*)&connection->tcp, &(uv_buf_t) { .base = (char*)(write + 1), .len = size }, 1, _tf_ssb_connection_on_write);
 | 
			
		||||
		if (result)
 | 
			
		||||
		{
 | 
			
		||||
			tf_ssb_connection_adjust_write_count(connection, -1);
 | 
			
		||||
			_tf_ssb_connection_close(connection, "write failed");
 | 
			
		||||
			tf_free(write);
 | 
			
		||||
		}
 | 
			
		||||
@@ -604,6 +610,19 @@ static void _tf_ssb_connection_box_stream_send(tf_ssb_connection_t* connection,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_connection_dispatch_scheduled(tf_ssb_connection_t* connection)
 | 
			
		||||
{
 | 
			
		||||
	while ((connection->active_write_count == 0 || connection->closing) && connection->scheduled_count && connection->scheduled)
 | 
			
		||||
	{
 | 
			
		||||
		tf_ssb_connection_scheduled_t scheduled = connection->scheduled[0];
 | 
			
		||||
		memmove(connection->scheduled, connection->scheduled + 1, sizeof(tf_ssb_connection_scheduled_t) * (connection->scheduled_count - 1));
 | 
			
		||||
		connection->scheduled_count--;
 | 
			
		||||
		tf_trace_begin(connection->ssb->trace, "scheduled callback");
 | 
			
		||||
		scheduled.callback(connection, scheduled.user_data);
 | 
			
		||||
		tf_trace_end(connection->ssb->trace);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, tf_ssb_scheduled_callback_t* callback, void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	connection->scheduled = tf_resize_vec(connection->scheduled, sizeof(tf_ssb_connection_scheduled_t) * (connection->scheduled_count + 1));
 | 
			
		||||
@@ -611,7 +630,7 @@ void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, tf_ssb_sch
 | 
			
		||||
		.callback = callback,
 | 
			
		||||
		.user_data = user_data,
 | 
			
		||||
	};
 | 
			
		||||
	uv_async_send(&connection->async);
 | 
			
		||||
	_tf_ssb_connection_dispatch_scheduled(connection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _request_compare(const void* a, const void* b)
 | 
			
		||||
@@ -1618,6 +1637,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
 | 
			
		||||
				}
 | 
			
		||||
				if (!found && !_tf_ssb_name_equals(context, val, (const char*[]) { "Error", NULL }))
 | 
			
		||||
				{
 | 
			
		||||
					tf_ssb_connection_add_request(connection, -request_number, namebuf, NULL, NULL, NULL, NULL);
 | 
			
		||||
					char buffer[256];
 | 
			
		||||
					_tf_ssb_name_to_string(context, val, buffer, sizeof(buffer));
 | 
			
		||||
					tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, buffer);
 | 
			
		||||
@@ -1812,24 +1832,6 @@ JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* pr
 | 
			
		||||
	return root;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_connection_dispatch_scheduled(tf_ssb_connection_t* connection)
 | 
			
		||||
{
 | 
			
		||||
	const int k_scheduled_batch_count = 8;
 | 
			
		||||
	for (int i = 0; i < k_scheduled_batch_count && connection->scheduled_count && connection->scheduled; i++)
 | 
			
		||||
	{
 | 
			
		||||
		tf_ssb_connection_scheduled_t scheduled = connection->scheduled[0];
 | 
			
		||||
		memmove(connection->scheduled, connection->scheduled + 1, sizeof(tf_ssb_connection_scheduled_t) * (connection->scheduled_count - 1));
 | 
			
		||||
		connection->scheduled_count--;
 | 
			
		||||
		tf_trace_begin(connection->ssb->trace, "scheduled callback");
 | 
			
		||||
		scheduled.callback(connection, scheduled.user_data);
 | 
			
		||||
		tf_trace_end(connection->ssb->trace);
 | 
			
		||||
	}
 | 
			
		||||
	if (connection->scheduled_count)
 | 
			
		||||
	{
 | 
			
		||||
		uv_async_send(&connection->async);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* reason)
 | 
			
		||||
{
 | 
			
		||||
	tf_ssb_t* ssb = connection->ssb;
 | 
			
		||||
@@ -1838,10 +1840,7 @@ static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const ch
 | 
			
		||||
	{
 | 
			
		||||
		connection->destroy_reason = reason;
 | 
			
		||||
	}
 | 
			
		||||
	while (connection->scheduled_count)
 | 
			
		||||
	{
 | 
			
		||||
		_tf_ssb_connection_dispatch_scheduled(connection);
 | 
			
		||||
	}
 | 
			
		||||
	_tf_ssb_connection_dispatch_scheduled(connection);
 | 
			
		||||
	tf_free(connection->scheduled);
 | 
			
		||||
	connection->scheduled = NULL;
 | 
			
		||||
	while (connection->requests)
 | 
			
		||||
@@ -2061,6 +2060,30 @@ static void _tf_ssb_connection_client_send_hello(tf_ssb_connection_t* connection
 | 
			
		||||
	connection->state = k_tf_ssb_state_sent_hello;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _tf_ssb_connection_read_start(tf_ssb_connection_t* connection)
 | 
			
		||||
{
 | 
			
		||||
	int result = uv_read_start((uv_stream_t*)&connection->tcp, _tf_ssb_connection_on_tcp_alloc, _tf_ssb_connection_on_tcp_recv);
 | 
			
		||||
	if (result && result != UV_EALREADY)
 | 
			
		||||
	{
 | 
			
		||||
		tf_printf("uv_read_start => %s\n", uv_strerror(result));
 | 
			
		||||
		_tf_ssb_connection_close(connection, "uv_read_start failed");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool _tf_ssb_connection_read_stop(tf_ssb_connection_t* connection)
 | 
			
		||||
{
 | 
			
		||||
	int result = uv_read_stop((uv_stream_t*)&connection->tcp);
 | 
			
		||||
	if (result && result != UV_EALREADY)
 | 
			
		||||
	{
 | 
			
		||||
		tf_printf("uv_read_stop => %s\n", uv_strerror(result));
 | 
			
		||||
		_tf_ssb_connection_close(connection, "uv_read_stop failed");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_connection_on_connect(uv_connect_t* connect, int status)
 | 
			
		||||
{
 | 
			
		||||
	tf_ssb_connection_t* connection = connect->data;
 | 
			
		||||
@@ -2068,13 +2091,7 @@ static void _tf_ssb_connection_on_connect(uv_connect_t* connect, int status)
 | 
			
		||||
	if (status == 0)
 | 
			
		||||
	{
 | 
			
		||||
		connection->state = k_tf_ssb_state_connected;
 | 
			
		||||
		int result = uv_read_start(connect->handle, _tf_ssb_connection_on_tcp_alloc, _tf_ssb_connection_on_tcp_recv);
 | 
			
		||||
		if (result)
 | 
			
		||||
		{
 | 
			
		||||
			tf_printf("uv_read_start => %s\n", uv_strerror(status));
 | 
			
		||||
			_tf_ssb_connection_close(connection, "uv_read_start failed");
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		if (_tf_ssb_connection_read_start(connection))
 | 
			
		||||
		{
 | 
			
		||||
			_tf_ssb_connection_client_send_hello(connection);
 | 
			
		||||
		}
 | 
			
		||||
@@ -2592,7 +2609,6 @@ static void _tf_ssb_connection_process_message_async(uv_async_t* async)
 | 
			
		||||
	{
 | 
			
		||||
		uv_async_send(&connection->async);
 | 
			
		||||
	}
 | 
			
		||||
	_tf_ssb_connection_dispatch_scheduled(connection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tf_ssb_connection_t* tf_ssb_connection_create(tf_ssb_t* ssb, const char* host, const struct sockaddr_in* addr, const uint8_t* public_key)
 | 
			
		||||
@@ -2825,7 +2841,7 @@ static void _tf_ssb_on_connection(uv_stream_t* stream, int status)
 | 
			
		||||
	_tf_ssb_notify_connections_changed(ssb, k_tf_ssb_change_create, connection);
 | 
			
		||||
 | 
			
		||||
	connection->state = k_tf_ssb_state_server_wait_hello;
 | 
			
		||||
	uv_read_start((uv_stream_t*)&connection->tcp, _tf_ssb_connection_on_tcp_alloc, _tf_ssb_connection_on_tcp_recv);
 | 
			
		||||
	_tf_ssb_connection_read_start(connection);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, struct sockaddr_in* netmask)
 | 
			
		||||
@@ -4064,3 +4080,32 @@ JSValue tf_ssb_connection_requests_to_object(tf_ssb_connection_t* connection)
 | 
			
		||||
	}
 | 
			
		||||
	return object;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tf_ssb_connection_adjust_read_backpressure(tf_ssb_connection_t* connection, int delta)
 | 
			
		||||
{
 | 
			
		||||
	const int k_threshold = 256;
 | 
			
		||||
	int old_pressure = connection->read_back_pressure;
 | 
			
		||||
	connection->read_back_pressure += delta;
 | 
			
		||||
	if (!connection->closing)
 | 
			
		||||
	{
 | 
			
		||||
		if (old_pressure < k_threshold && connection->read_back_pressure >= k_threshold)
 | 
			
		||||
		{
 | 
			
		||||
			_tf_ssb_connection_read_stop(connection);
 | 
			
		||||
		}
 | 
			
		||||
		else if (old_pressure >= k_threshold && connection->read_back_pressure < k_threshold)
 | 
			
		||||
		{
 | 
			
		||||
			_tf_ssb_connection_read_start(connection);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	connection->ref_count += delta;
 | 
			
		||||
	if (connection->ref_count == 0 && connection->closing)
 | 
			
		||||
	{
 | 
			
		||||
		_tf_ssb_connection_destroy(connection, "backpressure released");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tf_ssb_connection_adjust_write_count(tf_ssb_connection_t* connection, int delta)
 | 
			
		||||
{
 | 
			
		||||
	connection->active_write_count += delta;
 | 
			
		||||
	_tf_ssb_connection_dispatch_scheduled(connection);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								src/ssb.h
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/ssb.h
									
									
									
									
									
								
							@@ -745,7 +745,7 @@ JSValue tf_ssb_connection_requests_to_object(tf_ssb_connection_t* connection);
 | 
			
		||||
typedef void(tf_ssb_scheduled_callback_t)(tf_ssb_connection_t* connection, void* user_data);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
** Schedule work to be run when the server is next idle.
 | 
			
		||||
** Schedule work to be run when the connection is next idle.
 | 
			
		||||
** @param connection The owning connection.
 | 
			
		||||
** @param callback The callback to call.
 | 
			
		||||
** @param user_data User data to pass to the callback.
 | 
			
		||||
@@ -996,4 +996,22 @@ void tf_ssb_schedule_work(tf_ssb_t* ssb, int delay_ms, void (*callback)(tf_ssb_t
 | 
			
		||||
*/
 | 
			
		||||
bool tf_ssb_hmacsha256_verify(const char* public_key, const void* payload, size_t payload_length, const char* signature, bool signature_is_urlb64);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
** Adjust read backpressure.  If it gets too high, TCP receive will be paused
 | 
			
		||||
** until it lowers.
 | 
			
		||||
** @param connection The connection on which to affect backpressure.
 | 
			
		||||
** @param delta The change in backpressure.  Higher will eventually pause
 | 
			
		||||
** receive.  Lower will resume it.
 | 
			
		||||
*/
 | 
			
		||||
void tf_ssb_connection_adjust_read_backpressure(tf_ssb_connection_t* connection, int delta);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
** Adjust write count.  Work scheduled by tf_ssb_connection_schedule_idle will
 | 
			
		||||
** only start when this reaches zero.
 | 
			
		||||
** @param connection The connection on which to affect backpressure.
 | 
			
		||||
** @param delta The change in write count.  Higher will pause processing
 | 
			
		||||
** scheduled idle work queue.  Lower will resume it.
 | 
			
		||||
*/
 | 
			
		||||
void tf_ssb_connection_adjust_write_count(tf_ssb_connection_t* connection, int delta);
 | 
			
		||||
 | 
			
		||||
/** @} */
 | 
			
		||||
 
 | 
			
		||||
@@ -1889,7 +1889,7 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
 | 
			
		||||
	JS_SetPropertyStr(context, object, "storeMessage", JS_NewCFunction(context, _tf_ssb_storeMessage, "storeMessage", 1));
 | 
			
		||||
	JS_SetPropertyStr(context, object, "blobStore", JS_NewCFunction(context, _tf_ssb_blobStore, "blobStore", 1));
 | 
			
		||||
 | 
			
		||||
	/* Should be trusted only. */
 | 
			
		||||
	/* Trusted only. */
 | 
			
		||||
	JS_SetPropertyStr(context, object, "addEventListener", JS_NewCFunction(context, _tf_ssb_add_event_listener, "addEventListener", 2));
 | 
			
		||||
	JS_SetPropertyStr(context, object, "removeEventListener", JS_NewCFunction(context, _tf_ssb_remove_event_listener, "removeEventListener", 2));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -404,6 +404,7 @@ typedef struct _blobs_get_t
 | 
			
		||||
	bool done;
 | 
			
		||||
	bool storing;
 | 
			
		||||
	tf_ssb_t* ssb;
 | 
			
		||||
	tf_ssb_connection_t* connection;
 | 
			
		||||
	uint8_t buffer[];
 | 
			
		||||
} blobs_get_t;
 | 
			
		||||
 | 
			
		||||
@@ -411,6 +412,7 @@ static void _tf_ssb_rpc_blob_store_callback(const char* id, bool is_new, void* u
 | 
			
		||||
{
 | 
			
		||||
	blobs_get_t* get = user_data;
 | 
			
		||||
	get->storing = false;
 | 
			
		||||
	tf_ssb_connection_adjust_read_backpressure(get->connection, -1);
 | 
			
		||||
	if (get->done)
 | 
			
		||||
	{
 | 
			
		||||
		tf_free(get);
 | 
			
		||||
@@ -433,6 +435,7 @@ static void _tf_ssb_rpc_connection_blobs_get_callback(
 | 
			
		||||
		if (JS_ToBool(context, args))
 | 
			
		||||
		{
 | 
			
		||||
			get->storing = true;
 | 
			
		||||
			tf_ssb_connection_adjust_read_backpressure(connection, 1);
 | 
			
		||||
			tf_ssb_db_blob_store_async(ssb, get->buffer, get->received, _tf_ssb_rpc_blob_store_callback, get);
 | 
			
		||||
		}
 | 
			
		||||
		/* TODO: Should we send the response in the callback? */
 | 
			
		||||
@@ -455,7 +458,7 @@ static void _tf_ssb_rpc_connection_blobs_get_cleanup(tf_ssb_t* ssb, void* user_d
 | 
			
		||||
static void _tf_ssb_rpc_connection_blobs_get(tf_ssb_connection_t* connection, const char* blob_id, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	blobs_get_t* get = tf_malloc(sizeof(blobs_get_t) + size);
 | 
			
		||||
	*get = (blobs_get_t) { .ssb = tf_ssb_connection_get_ssb(connection), .expected_size = size };
 | 
			
		||||
	*get = (blobs_get_t) { .ssb = tf_ssb_connection_get_ssb(connection), .connection = connection, .expected_size = size };
 | 
			
		||||
	snprintf(get->id, sizeof(get->id), "%s", blob_id);
 | 
			
		||||
	memset(get->buffer, 0, size);
 | 
			
		||||
 | 
			
		||||
@@ -741,6 +744,7 @@ static void _tf_ssb_connection_send_history_stream_work(tf_ssb_connection_t* con
 | 
			
		||||
static void _tf_ssb_connection_send_history_stream_after_work(tf_ssb_connection_t* connection, int result, void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	tf_ssb_connection_send_history_stream_t* request = user_data;
 | 
			
		||||
	tf_ssb_connection_adjust_write_count(connection, -1);
 | 
			
		||||
	if (tf_ssb_connection_is_connected(connection))
 | 
			
		||||
	{
 | 
			
		||||
		for (int i = 0; i < request->out_messages_count; i++)
 | 
			
		||||
@@ -765,6 +769,15 @@ static void _tf_ssb_connection_send_history_stream_after_work(tf_ssb_connection_
 | 
			
		||||
	tf_free(request);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_connection_send_history_stream_callback(tf_ssb_connection_t* connection, void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	tf_ssb_connection_adjust_write_count(connection, 1);
 | 
			
		||||
	if (tf_ssb_connection_is_connected(connection))
 | 
			
		||||
	{
 | 
			
		||||
		tf_ssb_connection_run_work(connection, _tf_ssb_connection_send_history_stream_work, _tf_ssb_connection_send_history_stream_after_work, user_data);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_connection_send_history_stream(tf_ssb_connection_t* connection, int32_t request_number, const char* author, int64_t sequence, bool keys, bool live)
 | 
			
		||||
{
 | 
			
		||||
	tf_ssb_connection_send_history_stream_t* async = tf_malloc(sizeof(tf_ssb_connection_send_history_stream_t));
 | 
			
		||||
@@ -775,7 +788,7 @@ static void _tf_ssb_connection_send_history_stream(tf_ssb_connection_t* connecti
 | 
			
		||||
		.live = live,
 | 
			
		||||
	};
 | 
			
		||||
	snprintf(async->author, sizeof(async->author), "%s", author);
 | 
			
		||||
	tf_ssb_connection_run_work(connection, _tf_ssb_connection_send_history_stream_work, _tf_ssb_connection_send_history_stream_after_work, async);
 | 
			
		||||
	tf_ssb_connection_schedule_idle(connection, _tf_ssb_connection_send_history_stream_callback, async);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_rpc_createHistoryStream(
 | 
			
		||||
@@ -1000,6 +1013,12 @@ static void _tf_ssb_rpc_ebt_replicate_send_messages(tf_ssb_connection_t* connect
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_rpc_ebt_replicate_store_callback(const char* id, bool verified, bool is_new, void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	tf_ssb_connection_t* connection = user_data;
 | 
			
		||||
	tf_ssb_connection_adjust_read_backpressure(connection, -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
 | 
			
		||||
@@ -1022,7 +1041,8 @@ static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t f
 | 
			
		||||
	if (!JS_IsUndefined(author))
 | 
			
		||||
	{
 | 
			
		||||
		/* Looks like a message. */
 | 
			
		||||
		tf_ssb_verify_strip_and_store_message(ssb, args, NULL, NULL);
 | 
			
		||||
		tf_ssb_connection_adjust_read_backpressure(connection, 1);
 | 
			
		||||
		tf_ssb_verify_strip_and_store_message(ssb, args, _tf_ssb_rpc_ebt_replicate_store_callback, connection);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,2 @@
 | 
			
		||||
#define VERSION_NUMBER "0.0.19-wip"
 | 
			
		||||
#define VERSION_NAME "Don't let your loyalty become a burden."
 | 
			
		||||
#define VERSION_NUMBER "0.0.20-wip"
 | 
			
		||||
#define VERSION_NAME "One word all lowercase four words all uppercase."
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user