Compare commits
163 Commits
v0.2025.8
...
1381696f9b
| Author | SHA1 | Date | |
|---|---|---|---|
| 1381696f9b | |||
| 19b346cc6d | |||
| ec3064e0a1 | |||
| af8e60f8c3 | |||
| d3e06434e6 | |||
| 83fd005ded | |||
| c0dd47ba28 | |||
| e44a85c92b | |||
| 89d3e9b4fe | |||
| f4c6e2db1f | |||
| 48406bfe38 | |||
| 07c879f5f5 | |||
| cd34c127d1 | |||
| fb6e554e59 | |||
| d200e361f7 | |||
| bc3fd57d7a | |||
| fa4ef3b082 | |||
| 0827718d68 | |||
| 0ec862eaac | |||
| 7e1621dfb4 | |||
| c4d4e3822d | |||
| d2e5015eac | |||
| 510c2f81bd | |||
| 4f2e0245d3 | |||
| eecdbf6852 | |||
| ddc4603f13 | |||
| 759b522cd1 | |||
| 7ecb4a192d | |||
| d84626ac31 | |||
| 9c36e0db7b | |||
| fcd26bac1c | |||
| e8e7c98705 | |||
| b5af5cc223 | |||
| ba8253fa30 | |||
| f5bd389183 | |||
| 0c34a38e15 | |||
| 7c7857a6cd | |||
| 716bce2bb0 | |||
| 33fb96b120 | |||
| 28a4accabf | |||
| 31c7394c17 | |||
| e2974d34e2 | |||
| 4a06c84511 | |||
| 4960a1d9d6 | |||
| 75dd8889e9 | |||
| 111a6c3c6e | |||
| 775fdafa63 | |||
| dae38bbd83 | |||
| 35f374047a | |||
| aea4a14a62 | |||
| 98f7504a4c | |||
| bb52cdd7c2 | |||
| 07b660a0d6 | |||
| 2b9d712d48 | |||
| 3c1f60b62d | |||
| bb75edfd42 | |||
| c2b61cec2c | |||
| 05c3107b27 | |||
| bb67df7846 | |||
| 89ec523ea2 | |||
| f30458d953 | |||
| 42df0d830e | |||
| 50b2c0c7f4 | |||
| 0edb76b678 | |||
| 2d71af3243 | |||
| b571cd213b | |||
| b52c79ac4e | |||
| 63c6a5ab07 | |||
| 4447ea63e2 | |||
| 61200c4a7d | |||
| 62dc9d6cc0 | |||
| a28d41e1ee | |||
| 6a05e3770b | |||
| 53a93e510c | |||
| 1cb3ecf1ea | |||
| 687665cd6b | |||
| 7879ab1d50 | |||
| 24f0cdb398 | |||
| 6d5555e596 | |||
| 4052e3235f | |||
| 13302ad1c7 | |||
| 0f8687e473 | |||
| 9399ccd684 | |||
| b3604039fa | |||
| 732089da2c | |||
| b3e7e4b196 | |||
| 09a0cfd349 | |||
| 5bf7346321 | |||
| dd558c57e0 | |||
| 5647196924 | |||
| 49b1834bc6 | |||
| d111647ea8 | |||
| d7580dab9b | |||
| 28db8a8d5f | |||
| e64d5617e7 | |||
| acd114650a | |||
| 7a47ffaa61 | |||
| 42f7f66f35 | |||
| b2b4ffeeae | |||
| 26de1f7daa | |||
| 07605933dc | |||
| 4bdc7ec616 | |||
| 8ca64550e5 | |||
| 25dbac804c | |||
| 6ab3fd168b | |||
| 94858e2371 | |||
| 6d13502e94 | |||
| 77001e595c | |||
| 6fad20ffa3 | |||
| 00fb6c9839 | |||
| 97fcf72d63 | |||
| 5d8d02515d | |||
| 859fe1feb0 | |||
| 8f61d83f41 | |||
| 6423b3e479 | |||
| 2bc8cec8a2 | |||
| b49a6cd685 | |||
| 2885380f40 | |||
| 2ec3b6a249 | |||
| 3ef795452d | |||
| 479d87c8b8 | |||
| a56077dcc7 | |||
| d3f4587c3b | |||
| 623705b7a1 | |||
| 8f87f4751d | |||
| 2ac6dfde9d | |||
| 81ade7a400 | |||
| 63f7ff9f27 | |||
| 8a0fa17a79 | |||
| 0ead5ed967 | |||
| 53261a6fbc | |||
| c60ff86a4d | |||
| 83a0b017c5 | |||
| 3746622a11 | |||
| ccd50cf59f | |||
| 93680eb43d | |||
| 3ae4b7086a | |||
| 446b1f8600 | |||
| 00fd208a2c | |||
| e574d03716 | |||
| c1f3116c9d | |||
| 3aec7e6c14 | |||
| 9f0020dec8 | |||
| 6e78ad9729 | |||
| 44d84a9b2a | |||
| ac7809415c | |||
| 675cecaa20 | |||
| 5d179cc088 | |||
| d905618590 | |||
| 3fd9bc0b18 | |||
| 39abee7f73 | |||
| b770619111 | |||
| 1c44857da4 | |||
| bca4440867 | |||
| 4855543961 | |||
| cb3d6a98b9 | |||
| ada67a13d3 | |||
| f4c928f26e | |||
| 91fd515d39 | |||
| be6e841d3d | |||
| af6afa6903 | |||
| 6ab5d2a28d | |||
| 4be033f288 |
4
.gitmodules
vendored
@@ -19,10 +19,6 @@
|
|||||||
[submodule "deps/picohttpparser"]
|
[submodule "deps/picohttpparser"]
|
||||||
path = deps/picohttpparser
|
path = deps/picohttpparser
|
||||||
url = https://github.com/h2o/picohttpparser.git
|
url = https://github.com/h2o/picohttpparser.git
|
||||||
[submodule "deps/openssl_src"]
|
|
||||||
path = deps/openssl_src
|
|
||||||
url = https://github.com/openssl/openssl.git
|
|
||||||
shallow = true
|
|
||||||
[submodule "deps/c-ares"]
|
[submodule "deps/c-ares"]
|
||||||
path = deps/c-ares
|
path = deps/c-ares
|
||||||
url = https://github.com/c-ares/c-ares.git
|
url = https://github.com/c-ares/c-ares.git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ src
|
|||||||
deps
|
deps
|
||||||
.clang-format
|
.clang-format
|
||||||
flake.lock
|
flake.lock
|
||||||
|
apps/trace/speedscope/**
|
||||||
|
|
||||||
# Minified files
|
# Minified files
|
||||||
**/*.min.css
|
**/*.min.css
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ RUN apt-get update && \
|
|||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
gcc \
|
gcc \
|
||||||
libc6-dev \
|
libc6-dev \
|
||||||
perl \
|
|
||||||
make
|
make
|
||||||
|
|
||||||
COPY . /app
|
COPY . /app
|
||||||
|
|||||||
358
Doxyfile
@@ -1,4 +1,4 @@
|
|||||||
# Doxyfile 1.9.8
|
# Doxyfile 1.9.4
|
||||||
|
|
||||||
# This file describes the settings to be used by the documentation system
|
# This file describes the settings to be used by the documentation system
|
||||||
# doxygen (www.doxygen.org) for a project.
|
# doxygen (www.doxygen.org) for a project.
|
||||||
@@ -19,8 +19,7 @@
|
|||||||
# configuration file:
|
# configuration file:
|
||||||
# doxygen -x [configFile]
|
# doxygen -x [configFile]
|
||||||
# Use doxygen to compare the used configuration file with the template
|
# Use doxygen to compare the used configuration file with the template
|
||||||
# configuration file without replacing the environment variables or CMake type
|
# configuration file without replacing the environment variables:
|
||||||
# replacement variables:
|
|
||||||
# doxygen -x_noenv [configFile]
|
# doxygen -x_noenv [configFile]
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@@ -86,7 +85,7 @@ CREATE_SUBDIRS = NO
|
|||||||
# level increment doubles the number of directories, resulting in 4096
|
# level increment doubles the number of directories, resulting in 4096
|
||||||
# directories at level 8 which is the default and also the maximum value. The
|
# directories at level 8 which is the default and also the maximum value. The
|
||||||
# sub-directories are organized in 2 levels, the first level always has a fixed
|
# sub-directories are organized in 2 levels, the first level always has a fixed
|
||||||
# number of 16 directories.
|
# numer of 16 directories.
|
||||||
# Minimum value: 0, maximum value: 8, default value: 8.
|
# Minimum value: 0, maximum value: 8, default value: 8.
|
||||||
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
|
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
|
||||||
|
|
||||||
@@ -363,17 +362,6 @@ MARKDOWN_SUPPORT = YES
|
|||||||
|
|
||||||
TOC_INCLUDE_HEADINGS = 5
|
TOC_INCLUDE_HEADINGS = 5
|
||||||
|
|
||||||
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
|
|
||||||
# generate identifiers for the Markdown headings. Note: Every identifier is
|
|
||||||
# unique.
|
|
||||||
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
|
|
||||||
# sequence number starting at 0 and GITHUB use the lower case version of title
|
|
||||||
# with any whitespace replaced by '-' and punctuation characters removed.
|
|
||||||
# The default value is: DOXYGEN.
|
|
||||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
|
||||||
|
|
||||||
MARKDOWN_ID_STYLE = DOXYGEN
|
|
||||||
|
|
||||||
# When enabled doxygen tries to link words that correspond to documented
|
# When enabled doxygen tries to link words that correspond to documented
|
||||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||||
# be prevented in individual cases by putting a % sign in front of the word or
|
# be prevented in individual cases by putting a % sign in front of the word or
|
||||||
@@ -498,14 +486,6 @@ LOOKUP_CACHE_SIZE = 0
|
|||||||
|
|
||||||
NUM_PROC_THREADS = 1
|
NUM_PROC_THREADS = 1
|
||||||
|
|
||||||
# If the TIMESTAMP tag is set different from NO then each generated page will
|
|
||||||
# contain the date or date and time when the page was generated. Setting this to
|
|
||||||
# NO can help when comparing the output of multiple runs.
|
|
||||||
# Possible values are: YES, NO, DATETIME and DATE.
|
|
||||||
# The default value is: NO.
|
|
||||||
|
|
||||||
TIMESTAMP = NO
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Build related configuration options
|
# Build related configuration options
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@@ -587,8 +567,7 @@ HIDE_UNDOC_MEMBERS = NO
|
|||||||
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
|
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
|
||||||
# undocumented classes that are normally visible in the class hierarchy. If set
|
# undocumented classes that are normally visible in the class hierarchy. If set
|
||||||
# to NO, these classes will be included in the various overviews. This option
|
# to NO, these classes will be included in the various overviews. This option
|
||||||
# will also hide undocumented C++ concepts if enabled. This option has no effect
|
# has no effect if EXTRACT_ALL is enabled.
|
||||||
# if EXTRACT_ALL is enabled.
|
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
HIDE_UNDOC_CLASSES = NO
|
HIDE_UNDOC_CLASSES = NO
|
||||||
@@ -626,8 +605,7 @@ INTERNAL_DOCS = NO
|
|||||||
# Windows (including Cygwin) and MacOS, users should typically set this option
|
# Windows (including Cygwin) and MacOS, users should typically set this option
|
||||||
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
||||||
# YES.
|
# YES.
|
||||||
# Possible values are: SYSTEM, NO and YES.
|
# The default value is: system dependent.
|
||||||
# The default value is: SYSTEM.
|
|
||||||
|
|
||||||
CASE_SENSE_NAMES = YES
|
CASE_SENSE_NAMES = YES
|
||||||
|
|
||||||
@@ -879,26 +857,11 @@ WARN_IF_INCOMPLETE_DOC = YES
|
|||||||
|
|
||||||
WARN_NO_PARAMDOC = NO
|
WARN_NO_PARAMDOC = NO
|
||||||
|
|
||||||
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
|
|
||||||
# undocumented enumeration values. If set to NO, doxygen will accept
|
|
||||||
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
|
|
||||||
# will automatically be disabled.
|
|
||||||
# The default value is: NO.
|
|
||||||
|
|
||||||
WARN_IF_UNDOC_ENUM_VAL = NO
|
|
||||||
|
|
||||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||||
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
|
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||||
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
|
|
||||||
# write the warning messages in between other messages but write them at the end
|
|
||||||
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
|
|
||||||
# besides being in the defined file also be shown at the end of a run, unless
|
|
||||||
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
|
|
||||||
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
|
|
||||||
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
|
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
WARN_AS_ERROR = NO
|
WARN_AS_ERROR = NO
|
||||||
@@ -947,7 +910,6 @@ INPUT = README.md \
|
|||||||
core/app.js \
|
core/app.js \
|
||||||
core/client.js \
|
core/client.js \
|
||||||
core/core.js \
|
core/core.js \
|
||||||
core/http.js \
|
|
||||||
core/tfrpc.js \
|
core/tfrpc.js \
|
||||||
docs/ \
|
docs/ \
|
||||||
src/
|
src/
|
||||||
@@ -957,21 +919,10 @@ INPUT = README.md \
|
|||||||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||||
# documentation (see:
|
# documentation (see:
|
||||||
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
||||||
# See also: INPUT_FILE_ENCODING
|
|
||||||
# The default value is: UTF-8.
|
# The default value is: UTF-8.
|
||||||
|
|
||||||
INPUT_ENCODING = UTF-8
|
INPUT_ENCODING = UTF-8
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
|
||||||
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
|
|
||||||
# character encoding on a per file pattern basis. Doxygen will compare the file
|
|
||||||
# name with each pattern and apply the encoding instead of the default
|
|
||||||
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
|
|
||||||
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
|
|
||||||
# "INPUT_ENCODING" for further information on supported encodings.
|
|
||||||
|
|
||||||
INPUT_FILE_ENCODING =
|
|
||||||
|
|
||||||
# If the value of the INPUT tag contains directories, you can use the
|
# If the value of the INPUT tag contains directories, you can use the
|
||||||
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
|
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
|
||||||
# *.h) to filter out the source-files in the directories.
|
# *.h) to filter out the source-files in the directories.
|
||||||
@@ -983,12 +934,12 @@ INPUT_FILE_ENCODING =
|
|||||||
# Note the list of default checked file patterns might differ from the list of
|
# Note the list of default checked file patterns might differ from the list of
|
||||||
# default file extension mappings.
|
# default file extension mappings.
|
||||||
#
|
#
|
||||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
|
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||||
# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl,
|
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||||
# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php,
|
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
|
||||||
# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be
|
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
|
||||||
# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||||
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||||
|
|
||||||
FILE_PATTERNS = *.h \
|
FILE_PATTERNS = *.h \
|
||||||
*.js \
|
*.js \
|
||||||
@@ -1030,6 +981,9 @@ EXCLUDE_PATTERNS =
|
|||||||
# output. The symbol name can be a fully qualified name, a word, or if the
|
# output. The symbol name can be a fully qualified name, a word, or if the
|
||||||
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
||||||
# ANamespace::AClass, ANamespace::*Test
|
# ANamespace::AClass, ANamespace::*Test
|
||||||
|
#
|
||||||
|
# Note that the wildcards are matched against the file with absolute path, so to
|
||||||
|
# exclude all test directories use the pattern */test/*
|
||||||
|
|
||||||
EXCLUDE_SYMBOLS =
|
EXCLUDE_SYMBOLS =
|
||||||
|
|
||||||
@@ -1074,11 +1028,6 @@ IMAGE_PATH = docs/images/
|
|||||||
# code is scanned, but not when the output code is generated. If lines are added
|
# code is scanned, but not when the output code is generated. If lines are added
|
||||||
# or removed, the anchors will not be placed correctly.
|
# or removed, the anchors will not be placed correctly.
|
||||||
#
|
#
|
||||||
# Note that doxygen will use the data processed and written to standard output
|
|
||||||
# for further processing, therefore nothing else, like debug statements or used
|
|
||||||
# commands (so in case of a Windows batch file always use @echo OFF), should be
|
|
||||||
# written to standard output.
|
|
||||||
#
|
|
||||||
# Note that for custom extensions or not directly supported extensions you also
|
# Note that for custom extensions or not directly supported extensions you also
|
||||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||||
# properly processed by doxygen.
|
# properly processed by doxygen.
|
||||||
@@ -1120,15 +1069,6 @@ FILTER_SOURCE_PATTERNS =
|
|||||||
|
|
||||||
USE_MDFILE_AS_MAINPAGE = README.md
|
USE_MDFILE_AS_MAINPAGE = README.md
|
||||||
|
|
||||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
|
||||||
# characters from position 72 are to be considered as comment. A common
|
|
||||||
# extension is to allow longer lines before the automatic comment starts. The
|
|
||||||
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
|
|
||||||
# be processed before the automatic comment starts.
|
|
||||||
# Minimum value: 7, maximum value: 10000, default value: 72.
|
|
||||||
|
|
||||||
FORTRAN_COMMENT_AFTER = 72
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to source browsing
|
# Configuration options related to source browsing
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@@ -1266,11 +1206,10 @@ CLANG_DATABASE_PATH =
|
|||||||
|
|
||||||
ALPHABETICAL_INDEX = YES
|
ALPHABETICAL_INDEX = YES
|
||||||
|
|
||||||
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
|
# In case all classes in a project start with a common prefix, all classes will
|
||||||
# that should be ignored while generating the index headers. The IGNORE_PREFIX
|
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
|
||||||
# tag works for classes, function and member names. The entity will be placed in
|
# can be used to specify a prefix (or a list of prefixes) that should be ignored
|
||||||
# the alphabetical list under the first letter of the entity name that remains
|
# while generating the index headers.
|
||||||
# after removing the prefix.
|
|
||||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||||
|
|
||||||
IGNORE_PREFIX =
|
IGNORE_PREFIX =
|
||||||
@@ -1349,12 +1288,7 @@ HTML_STYLESHEET =
|
|||||||
# Doxygen will copy the style sheet files to the output directory.
|
# Doxygen will copy the style sheet files to the output directory.
|
||||||
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
||||||
# style sheet in the list overrules the setting of the previous ones in the
|
# style sheet in the list overrules the setting of the previous ones in the
|
||||||
# list).
|
# list). For an example see the documentation.
|
||||||
# Note: Since the styling of scrollbars can currently not be overruled in
|
|
||||||
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
|
|
||||||
# one or more extra stylesheets have been specified. So if scrollbar
|
|
||||||
# customization is desired it has to be added explicitly. For an example see the
|
|
||||||
# documentation.
|
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
HTML_EXTRA_STYLESHEET =
|
HTML_EXTRA_STYLESHEET =
|
||||||
@@ -1369,19 +1303,6 @@ HTML_EXTRA_STYLESHEET =
|
|||||||
|
|
||||||
HTML_EXTRA_FILES =
|
HTML_EXTRA_FILES =
|
||||||
|
|
||||||
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
|
|
||||||
# should be rendered with a dark or light theme.
|
|
||||||
# Possible values are: LIGHT always generate light mode output, DARK always
|
|
||||||
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
|
|
||||||
# the user preference, use light mode if no preference is set (the default),
|
|
||||||
# AUTO_DARK automatically set the mode according to the user preference, use
|
|
||||||
# dark mode if no preference is set and TOGGLE allow to user to switch between
|
|
||||||
# light and dark mode via a button.
|
|
||||||
# The default value is: AUTO_LIGHT.
|
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
|
||||||
|
|
||||||
HTML_COLORSTYLE = AUTO_LIGHT
|
|
||||||
|
|
||||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||||
# will adjust the colors in the style sheet and background images according to
|
# will adjust the colors in the style sheet and background images according to
|
||||||
# this color. Hue is specified as an angle on a color-wheel, see
|
# this color. Hue is specified as an angle on a color-wheel, see
|
||||||
@@ -1412,6 +1333,15 @@ HTML_COLORSTYLE_SAT = 100
|
|||||||
|
|
||||||
HTML_COLORSTYLE_GAMMA = 80
|
HTML_COLORSTYLE_GAMMA = 80
|
||||||
|
|
||||||
|
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
|
||||||
|
# page will contain the date and time when the page was generated. Setting this
|
||||||
|
# to YES can help to show when doxygen was last run and thus if the
|
||||||
|
# documentation is up to date.
|
||||||
|
# The default value is: NO.
|
||||||
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
|
#HTML_TIMESTAMP = NO
|
||||||
|
|
||||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||||
# documentation will contain a main index with vertical navigation menus that
|
# documentation will contain a main index with vertical navigation menus that
|
||||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||||
@@ -1431,13 +1361,6 @@ HTML_DYNAMIC_MENUS = YES
|
|||||||
|
|
||||||
HTML_DYNAMIC_SECTIONS = NO
|
HTML_DYNAMIC_SECTIONS = NO
|
||||||
|
|
||||||
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
|
|
||||||
# dynamically folded and expanded in the generated HTML source code.
|
|
||||||
# The default value is: YES.
|
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
|
||||||
|
|
||||||
HTML_CODE_FOLDING = YES
|
|
||||||
|
|
||||||
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
|
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
|
||||||
# shown in the various tree structured indices initially; the user can expand
|
# shown in the various tree structured indices initially; the user can expand
|
||||||
# and collapse entries dynamically later on. Doxygen will expand the tree to
|
# and collapse entries dynamically later on. Doxygen will expand the tree to
|
||||||
@@ -1568,16 +1491,6 @@ BINARY_TOC = NO
|
|||||||
|
|
||||||
TOC_EXPAND = NO
|
TOC_EXPAND = NO
|
||||||
|
|
||||||
# The SITEMAP_URL tag is used to specify the full URL of the place where the
|
|
||||||
# generated documentation will be placed on the server by the user during the
|
|
||||||
# deployment of the documentation. The generated sitemap is called sitemap.xml
|
|
||||||
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
|
|
||||||
# is specified no sitemap is generated. For information about the sitemap
|
|
||||||
# protocol see https://www.sitemaps.org
|
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
|
||||||
|
|
||||||
SITEMAP_URL =
|
|
||||||
|
|
||||||
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
|
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
|
||||||
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
|
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
|
||||||
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
|
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
|
||||||
@@ -1753,6 +1666,17 @@ HTML_FORMULA_FORMAT = png
|
|||||||
|
|
||||||
FORMULA_FONTSIZE = 10
|
FORMULA_FONTSIZE = 10
|
||||||
|
|
||||||
|
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||||
|
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||||
|
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||||
|
#
|
||||||
|
# Note that when changing this option you need to delete any form_*.png files in
|
||||||
|
# the HTML output directory before the changes have effect.
|
||||||
|
# The default value is: YES.
|
||||||
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
|
#FORMULA_TRANSPARENT = YES
|
||||||
|
|
||||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||||
# the section "Including formulas" for details.
|
# the section "Including formulas" for details.
|
||||||
@@ -1814,8 +1738,8 @@ MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
|
|||||||
|
|
||||||
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
|
||||||
# extension names that should be enabled during MathJax rendering. For example
|
# extension names that should be enabled during MathJax rendering. For example
|
||||||
# for MathJax version 2 (see
|
# for MathJax version 2 (see https://docs.mathjax.org/en/v2.7-latest/tex.html
|
||||||
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
|
# #tex-and-latex-extensions):
|
||||||
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
||||||
# For example for MathJax version 3 (see
|
# For example for MathJax version 3 (see
|
||||||
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
|
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
|
||||||
@@ -2066,16 +1990,9 @@ PDF_HYPERLINKS = YES
|
|||||||
|
|
||||||
USE_PDFLATEX = YES
|
USE_PDFLATEX = YES
|
||||||
|
|
||||||
# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
|
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||||
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
|
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||||
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
|
# if errors occur, instead of asking the user for help.
|
||||||
# hit at every error; missing files that TeX tries to input or request from
|
|
||||||
# keyboard input (\read on a not open input stream) cause the job to abort,
|
|
||||||
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
|
|
||||||
# but there is no possibility of user interaction just like in batch mode,
|
|
||||||
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
|
|
||||||
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
|
|
||||||
# each error, asking for user intervention.
|
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||||
|
|
||||||
@@ -2096,6 +2013,14 @@ LATEX_HIDE_INDICES = NO
|
|||||||
|
|
||||||
LATEX_BIB_STYLE = plain
|
LATEX_BIB_STYLE = plain
|
||||||
|
|
||||||
|
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||||
|
# page will contain the date and time when the page was generated. Setting this
|
||||||
|
# to NO can help when comparing the output of multiple runs.
|
||||||
|
# The default value is: NO.
|
||||||
|
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||||
|
|
||||||
|
#LATEX_TIMESTAMP = NO
|
||||||
|
|
||||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||||
# path from which the emoji images will be read. If a relative path is entered,
|
# path from which the emoji images will be read. If a relative path is entered,
|
||||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||||
@@ -2261,39 +2186,13 @@ DOCBOOK_OUTPUT = docbook
|
|||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||||
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
|
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
|
||||||
# the structure of the code including all documentation. Note that this feature
|
# the structure of the code including all documentation. Note that this feature
|
||||||
# is still experimental and incomplete at the moment.
|
# is still experimental and incomplete at the moment.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
GENERATE_AUTOGEN_DEF = NO
|
GENERATE_AUTOGEN_DEF = NO
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Configuration options related to Sqlite3 output
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
|
|
||||||
# database with symbols found by doxygen stored in tables.
|
|
||||||
# The default value is: NO.
|
|
||||||
|
|
||||||
#GENERATE_SQLITE3 = NO
|
|
||||||
|
|
||||||
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
|
||||||
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
|
||||||
# in front of it.
|
|
||||||
# The default directory is: sqlite3.
|
|
||||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
|
||||||
|
|
||||||
#SQLITE3_OUTPUT = sqlite3
|
|
||||||
|
|
||||||
# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
|
||||||
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
|
||||||
# will warn if an a database file is already found and not modify it.
|
|
||||||
# The default value is: YES.
|
|
||||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
|
||||||
|
|
||||||
#SQLITE3_RECREATE_DB = YES
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to the Perl module output
|
# Configuration options related to the Perl module output
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@@ -2436,15 +2335,15 @@ TAGFILES =
|
|||||||
|
|
||||||
GENERATE_TAGFILE =
|
GENERATE_TAGFILE =
|
||||||
|
|
||||||
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
|
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
|
||||||
# will be listed in the class and namespace index. If set to NO, only the
|
# the class index. If set to NO, only the inherited external classes will be
|
||||||
# inherited external classes will be listed.
|
# listed.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
ALLEXTERNALS = NO
|
ALLEXTERNALS = NO
|
||||||
|
|
||||||
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
|
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
|
||||||
# in the topic index. If set to NO, only the current project's groups will be
|
# in the modules index. If set to NO, only the current project's groups will be
|
||||||
# listed.
|
# listed.
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
|
|
||||||
@@ -2458,9 +2357,16 @@ EXTERNAL_GROUPS = YES
|
|||||||
EXTERNAL_PAGES = YES
|
EXTERNAL_PAGES = YES
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to diagram generator tools
|
# Configuration options related to the dot tool
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||||
|
# then run dia to produce the diagram and insert it in the documentation. The
|
||||||
|
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||||
|
# If left empty dia is assumed to be found in the default search path.
|
||||||
|
|
||||||
|
DIA_PATH =
|
||||||
|
|
||||||
# If set to YES the inheritance and collaboration graphs will hide inheritance
|
# If set to YES the inheritance and collaboration graphs will hide inheritance
|
||||||
# and usage relations if the target is undocumented or is not a class.
|
# and usage relations if the target is undocumented or is not a class.
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
@@ -2469,10 +2375,10 @@ HIDE_UNDOC_RELATIONS = YES
|
|||||||
|
|
||||||
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
||||||
# available from the path. This tool is part of Graphviz (see:
|
# available from the path. This tool is part of Graphviz (see:
|
||||||
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||||
# Bell Labs. The other options in this section have no effect if this option is
|
# Bell Labs. The other options in this section have no effect if this option is
|
||||||
# set to NO
|
# set to NO
|
||||||
# The default value is: YES.
|
# The default value is: NO.
|
||||||
|
|
||||||
HAVE_DOT = YES
|
HAVE_DOT = YES
|
||||||
|
|
||||||
@@ -2486,51 +2392,37 @@ HAVE_DOT = YES
|
|||||||
|
|
||||||
DOT_NUM_THREADS = 0
|
DOT_NUM_THREADS = 0
|
||||||
|
|
||||||
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
|
# When you want a differently looking font in the dot files that doxygen
|
||||||
# subgraphs. When you want a differently looking font in the dot files that
|
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||||
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
|
# sure dot is able to find the font, which can be done by putting it in a
|
||||||
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
|
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||||
# Edge and Graph Attributes specification</a> You need to make sure dot is able
|
# setting DOT_FONTPATH to the directory containing the font.
|
||||||
# to find the font, which can be done by putting it in a standard location or by
|
# The default value is: Helvetica.
|
||||||
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
|
|
||||||
# directory containing the font. Default graphviz fontsize is 14.
|
|
||||||
# The default value is: fontname=Helvetica,fontsize=10.
|
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
|
#DOT_FONTNAME = Helvetica
|
||||||
|
|
||||||
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
|
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
|
||||||
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
|
# dot graphs.
|
||||||
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
|
# Minimum value: 4, maximum value: 24, default value: 10.
|
||||||
# arrows shapes.</a>
|
|
||||||
# The default value is: labelfontname=Helvetica,labelfontsize=10.
|
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
|
#DOT_FONTSIZE = 10
|
||||||
|
|
||||||
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
|
# By default doxygen will tell dot to use the default font as specified with
|
||||||
# around nodes set 'shape=plain' or 'shape=plaintext' <a
|
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
||||||
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
|
# the path where dot can find it using this tag.
|
||||||
# The default value is: shape=box,height=0.2,width=0.4.
|
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
|
#DOT_FONTPATH =
|
||||||
|
|
||||||
# You can set the path where dot can find font specified with fontname in
|
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
|
||||||
# DOT_COMMON_ATTR and others dot attributes.
|
# graph for each documented class showing the direct and indirect inheritance
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
|
||||||
|
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
|
||||||
DOT_FONTPATH =
|
# to TEXT the direct and indirect inheritance relations will be shown as texts /
|
||||||
|
# links.
|
||||||
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
|
# Possible values are: NO, YES, TEXT and GRAPH.
|
||||||
# generate a graph for each documented class showing the direct and indirect
|
|
||||||
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
|
|
||||||
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
|
|
||||||
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
|
|
||||||
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
|
|
||||||
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
|
|
||||||
# relations will be shown as texts / links.
|
|
||||||
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
|
|
||||||
CLASS_GRAPH = YES
|
CLASS_GRAPH = YES
|
||||||
@@ -2538,21 +2430,15 @@ CLASS_GRAPH = YES
|
|||||||
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
|
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
|
||||||
# graph for each documented class showing the direct and indirect implementation
|
# graph for each documented class showing the direct and indirect implementation
|
||||||
# dependencies (inheritance, containment, and class references variables) of the
|
# dependencies (inheritance, containment, and class references variables) of the
|
||||||
# class with other documented classes. Explicit enabling a collaboration graph,
|
# class with other documented classes.
|
||||||
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
|
|
||||||
# command \collaborationgraph. Disabling a collaboration graph can be
|
|
||||||
# accomplished by means of the command \hidecollaborationgraph.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
COLLABORATION_GRAPH = YES
|
COLLABORATION_GRAPH = YES
|
||||||
|
|
||||||
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
||||||
# groups, showing the direct groups dependencies. Explicit enabling a group
|
# groups, showing the direct groups dependencies. See also the chapter Grouping
|
||||||
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
|
# in the manual.
|
||||||
# of the command \groupgraph. Disabling a directory graph can be accomplished by
|
|
||||||
# means of the command \hidegroupgraph. See also the chapter Grouping in the
|
|
||||||
# manual.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
@@ -2612,9 +2498,7 @@ TEMPLATE_RELATIONS = NO
|
|||||||
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
|
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
|
||||||
# YES then doxygen will generate a graph for each documented file showing the
|
# YES then doxygen will generate a graph for each documented file showing the
|
||||||
# direct and indirect include dependencies of the file with other documented
|
# direct and indirect include dependencies of the file with other documented
|
||||||
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
|
# files.
|
||||||
# can be accomplished by means of the command \includegraph. Disabling an
|
|
||||||
# include graph can be accomplished by means of the command \hideincludegraph.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
@@ -2623,10 +2507,7 @@ INCLUDE_GRAPH = YES
|
|||||||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
||||||
# set to YES then doxygen will generate a graph for each documented file showing
|
# set to YES then doxygen will generate a graph for each documented file showing
|
||||||
# the direct and indirect include dependencies of the file with other documented
|
# the direct and indirect include dependencies of the file with other documented
|
||||||
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
|
# files.
|
||||||
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
|
|
||||||
# an included by graph can be accomplished by means of the command
|
|
||||||
# \hideincludedbygraph.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
@@ -2666,10 +2547,7 @@ GRAPHICAL_HIERARCHY = YES
|
|||||||
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
|
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
|
||||||
# dependencies a directory has on other directories in a graphical way. The
|
# dependencies a directory has on other directories in a graphical way. The
|
||||||
# dependency relations are determined by the #include relations between the
|
# dependency relations are determined by the #include relations between the
|
||||||
# files in the directories. Explicit enabling a directory graph, when
|
# files in the directories.
|
||||||
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
|
|
||||||
# \directorygraph. Disabling a directory graph can be accomplished by means of
|
|
||||||
# the command \hidedirectorygraph.
|
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
@@ -2685,13 +2563,12 @@ DIR_GRAPH_MAX_DEPTH = 1
|
|||||||
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
||||||
# generated by dot. For an explanation of the image formats see the section
|
# generated by dot. For an explanation of the image formats see the section
|
||||||
# output formats in the documentation of the dot tool (Graphviz (see:
|
# output formats in the documentation of the dot tool (Graphviz (see:
|
||||||
# https://www.graphviz.org/)).
|
# http://www.graphviz.org/)).
|
||||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||||
# requirement).
|
# requirement).
|
||||||
# Possible values are: png, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd,
|
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
|
||||||
# gif, gif:cairo, gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd,
|
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
|
||||||
# png:cairo, png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
|
|
||||||
# png:gdiplus:gdiplus.
|
# png:gdiplus:gdiplus.
|
||||||
# The default value is: png.
|
# The default value is: png.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
@@ -2723,12 +2600,11 @@ DOT_PATH =
|
|||||||
|
|
||||||
DOTFILE_DIRS =
|
DOTFILE_DIRS =
|
||||||
|
|
||||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||||
# then run dia to produce the diagram and insert it in the documentation. The
|
# contain msc files that are included in the documentation (see the \mscfile
|
||||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
# command).
|
||||||
# If left empty dia is assumed to be found in the default search path.
|
|
||||||
|
|
||||||
DIA_PATH =
|
MSCFILE_DIRS =
|
||||||
|
|
||||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||||
# contain dia files that are included in the documentation (see the \diafile
|
# contain dia files that are included in the documentation (see the \diafile
|
||||||
@@ -2778,6 +2654,18 @@ DOT_GRAPH_MAX_NODES = 50
|
|||||||
|
|
||||||
MAX_DOT_GRAPH_DEPTH = 0
|
MAX_DOT_GRAPH_DEPTH = 0
|
||||||
|
|
||||||
|
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||||
|
# background. This is disabled by default, because dot on Windows does not seem
|
||||||
|
# to support this out of the box.
|
||||||
|
#
|
||||||
|
# Warning: Depending on the platform used, enabling this option may lead to
|
||||||
|
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
|
||||||
|
# read).
|
||||||
|
# The default value is: NO.
|
||||||
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
|
#DOT_TRANSPARENT = NO
|
||||||
|
|
||||||
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
||||||
# files in one run (i.e. multiple -o and -T options on the command line). This
|
# files in one run (i.e. multiple -o and -T options on the command line). This
|
||||||
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
||||||
@@ -2805,19 +2693,3 @@ GENERATE_LEGEND = YES
|
|||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
|
|
||||||
DOT_CLEANUP = YES
|
DOT_CLEANUP = YES
|
||||||
|
|
||||||
# You can define message sequence charts within doxygen comments using the \msc
|
|
||||||
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
|
|
||||||
# use a built-in version of mscgen tool to produce the charts. Alternatively,
|
|
||||||
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
|
|
||||||
# specifying prog as the value, doxygen will call the tool as prog -T
|
|
||||||
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
|
|
||||||
# output file formats "png", "eps", "svg", and "ismap".
|
|
||||||
|
|
||||||
MSCGEN_TOOL =
|
|
||||||
|
|
||||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
|
||||||
# contain msc files that are included in the documentation (see the \mscfile
|
|
||||||
# command).
|
|
||||||
|
|
||||||
MSCFILE_DIRS =
|
|
||||||
|
|||||||
159
GNUmakefile
@@ -16,15 +16,15 @@ MAKEFLAGS += --no-builtin-rules
|
|||||||
## LD := Linker.
|
## LD := Linker.
|
||||||
## ANDROID_SDK := Path to the Android SDK.
|
## ANDROID_SDK := Path to the Android SDK.
|
||||||
|
|
||||||
VERSION_CODE := 42
|
VERSION_CODE := 49
|
||||||
VERSION_CODE_IOS := 16
|
VERSION_CODE_IOS := 27
|
||||||
VERSION_NUMBER := 0.2025.8
|
VERSION_NUMBER := 0.2025.12-wip
|
||||||
VERSION_NAME := This program kills fascists.
|
VERSION_NAME := This program kills fascists.
|
||||||
|
|
||||||
IPHONEOS_VERSION_MIN=14.0
|
IPHONEOS_VERSION_MIN=14.5
|
||||||
|
|
||||||
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3500400.zip
|
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3510100.zip
|
||||||
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.18.2/bundletool-all-1.18.2.jar
|
||||||
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||||
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
||||||
|
|
||||||
@@ -38,7 +38,6 @@ BUNDLETOOL = out/bundletool.jar
|
|||||||
|
|
||||||
HAVE_WIN :=
|
HAVE_WIN :=
|
||||||
HAVE_CROSS_AARCH64 :=
|
HAVE_CROSS_AARCH64 :=
|
||||||
USE_SYSTEM_SSL :=
|
|
||||||
|
|
||||||
export SOURCE_DATE_EPOCH=1
|
export SOURCE_DATE_EPOCH=1
|
||||||
export TZ=UTC
|
export TZ=UTC
|
||||||
@@ -65,7 +64,6 @@ LDFLAGS += \
|
|||||||
-lbsd \
|
-lbsd \
|
||||||
-lnetwork \
|
-lnetwork \
|
||||||
-Wno-stringop-overflow
|
-Wno-stringop-overflow
|
||||||
USE_SYSTEM_SSL := 1
|
|
||||||
HAVE_ANDROID = 0
|
HAVE_ANDROID = 0
|
||||||
HAVE_LINUX_IOS = 0
|
HAVE_LINUX_IOS = 0
|
||||||
HAVE_LINUX_MACOS = 0
|
HAVE_LINUX_MACOS = 0
|
||||||
@@ -80,13 +78,12 @@ LDFLAGS += \
|
|||||||
HAVE_ANDROID :=
|
HAVE_ANDROID :=
|
||||||
HAVE_LINUX_IOS :=
|
HAVE_LINUX_IOS :=
|
||||||
HAVE_LINUX_MACOS :=
|
HAVE_LINUX_MACOS :=
|
||||||
USE_SYSTEM_SSL := 1
|
|
||||||
else
|
else
|
||||||
$(error Unexpected host platform $(UNAME_S).)
|
$(error Unexpected host platform $(UNAME_S).)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Everything is set above.
|
# 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))
|
$(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))
|
||||||
|
|
||||||
CFLAGS += \
|
CFLAGS += \
|
||||||
-std=gnu11 \
|
-std=gnu11 \
|
||||||
@@ -270,16 +267,12 @@ $(WINDOWS_TARGETS): AS = $(CC)
|
|||||||
$(WINDOWS_TARGETS): CFLAGS += \
|
$(WINDOWS_TARGETS): CFLAGS += \
|
||||||
-D_WIN32_WINNT=0x0A00 \
|
-D_WIN32_WINNT=0x0A00 \
|
||||||
-DWINVER=0x0A00 \
|
-DWINVER=0x0A00 \
|
||||||
-DNTDDI_VERSION=NTDDI_WIN10 \
|
-DNTDDI_VERSION=NTDDI_WIN10
|
||||||
-Iout/openssl/$(UNAME_S)/mingw64/usr/local/include
|
|
||||||
$(WINDOWS_TARGETS): LDFLAGS += \
|
$(WINDOWS_TARGETS): LDFLAGS += \
|
||||||
-static \
|
-static \
|
||||||
-lm \
|
-lm
|
||||||
-Lout/openssl/$(UNAME_S)/mingw64/usr/local/lib
|
|
||||||
$(AARCH64_TARGETS): CC = aarch64-linux-gnu-gcc
|
$(AARCH64_TARGETS): CC = aarch64-linux-gnu-gcc
|
||||||
$(AARCH64_TARGETS): AS = $(CC)
|
$(AARCH64_TARGETS): AS = $(CC)
|
||||||
$(AARCH64_TARGETS): CFLAGS += -Iout/openssl/Linux/aarch64/usr/local/include
|
|
||||||
$(AARCH64_TARGETS): LDFLAGS += -Lout/openssl/Linux/aarch64/usr/local/lib
|
|
||||||
ifeq ($(UNAME_S),Darwin)
|
ifeq ($(UNAME_S),Darwin)
|
||||||
$(HOST_TARGETS): CC = xcrun clang
|
$(HOST_TARGETS): CC = xcrun clang
|
||||||
$(IOS_TARGETS): IOS_SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)
|
$(IOS_TARGETS): IOS_SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)
|
||||||
@@ -304,39 +297,12 @@ $(ANDROID_TARGETS): AS = $(CC)
|
|||||||
$(ANDROID_TARGETS): CFLAGS += \
|
$(ANDROID_TARGETS): CFLAGS += \
|
||||||
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION) \
|
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION) \
|
||||||
-Wno-unknown-warning-option
|
-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_X86_TARGETS): CFLAGS += -Wno-atomic-alignment
|
$(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
|
|
||||||
$(NONMACOS_TARGETS): CFLAGS += -Wno-cast-function-type
|
$(NONMACOS_TARGETS): CFLAGS += -Wno-cast-function-type
|
||||||
$(MACOS_TARGETS): LDFLAGS += -Wl,-dead_strip
|
$(MACOS_TARGETS): LDFLAGS += -Wl,-dead_strip
|
||||||
$(NONMACOS_TARGETS): LDFLAGS += -Wl,--gc-sections -Wl,--as-needed
|
$(NONMACOS_TARGETS): LDFLAGS += -Wl,--gc-sections -Wl,--as-needed
|
||||||
$(IOS_TARGETS): CFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
|
$(IOS_TARGETS): CFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
|
||||||
$(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
|
$(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
|
||||||
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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
|
|
||||||
ifeq ($(UNAME_M),x86_64)
|
ifeq ($(UNAME_M),x86_64)
|
||||||
ifeq ($(UNAME_S),Linux)
|
ifeq ($(UNAME_S),Linux)
|
||||||
@@ -824,9 +790,6 @@ $(MINIUNZIP_OBJS): CFLAGS += \
|
|||||||
LDFLAGS += \
|
LDFLAGS += \
|
||||||
-pthread \
|
-pthread \
|
||||||
-lm
|
-lm
|
||||||
$(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS) $(AARCH64_TARGETS) $(filter-out $(HOST_TARGETS),$(MACOS_TARGETS)): LDFLAGS += \
|
|
||||||
-lssl \
|
|
||||||
-lcrypto
|
|
||||||
ifneq ($(UNAME_S),Haiku)
|
ifneq ($(UNAME_S),Haiku)
|
||||||
ifneq ($(UNAME_S),OpenBSD)
|
ifneq ($(UNAME_S),OpenBSD)
|
||||||
$(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
$(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||||
@@ -834,8 +797,6 @@ $(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
$(WINDOWS_TARGETS): LDFLAGS += \
|
$(WINDOWS_TARGETS): LDFLAGS += \
|
||||||
-lssl \
|
|
||||||
-lcrypto \
|
|
||||||
-lcrypt32 \
|
-lcrypt32 \
|
||||||
-ldbghelp \
|
-ldbghelp \
|
||||||
-liphlpapi \
|
-liphlpapi \
|
||||||
@@ -848,15 +809,15 @@ $(WINDOWS_TARGETS): LDFLAGS += \
|
|||||||
$(ANDROID_TARGETS): LDFLAGS += \
|
$(ANDROID_TARGETS): LDFLAGS += \
|
||||||
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION) \
|
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION) \
|
||||||
-ldl \
|
-ldl \
|
||||||
-llog \
|
-llog
|
||||||
-lssl \
|
|
||||||
-lcrypto
|
|
||||||
$(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): CFLAGS += \
|
$(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): CFLAGS += \
|
||||||
-Wno-unknown-warning-option
|
-Wno-unknown-warning-option
|
||||||
$(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
$(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||||
-framework Foundation \
|
-framework Foundation \
|
||||||
-framework CoreFoundation \
|
-framework CoreFoundation \
|
||||||
|
-framework CoreSpotlight \
|
||||||
-framework UIKit \
|
-framework UIKit \
|
||||||
|
-framework UniformTypeIdentifiers \
|
||||||
-framework WebKit
|
-framework WebKit
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -998,8 +959,7 @@ PACKAGE_DIRS := \
|
|||||||
core \
|
core \
|
||||||
deps/codemirror \
|
deps/codemirror \
|
||||||
deps/prettier \
|
deps/prettier \
|
||||||
deps/lit \
|
deps/lit
|
||||||
deps/speedscope
|
|
||||||
|
|
||||||
RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f -not -name '.*')))
|
RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f -not -name '.*')))
|
||||||
|
|
||||||
@@ -1193,6 +1153,7 @@ out/tildefriends-%.ipa: out/tildefriends-ios%.app/tildefriends
|
|||||||
@echo "[ipa] $@"
|
@echo "[ipa] $@"
|
||||||
@rm -rf $@.tmp $@
|
@rm -rf $@.tmp $@
|
||||||
@mkdir -p $@.tmp/Payload/tildefriends.app/
|
@mkdir -p $@.tmp/Payload/tildefriends.app/
|
||||||
|
@cp src/ios/tildefriends512.png $@.tmp/iTunesArtwork
|
||||||
@cp -R $(dir $<)/* $@.tmp/Payload/tildefriends.app/
|
@cp -R $(dir $<)/* $@.tmp/Payload/tildefriends.app/
|
||||||
@cd $@.tmp/ && zip -u ../../$@ -q -9 -r ./
|
@cd $@.tmp/ && zip -u ../../$@ -q -9 -r ./
|
||||||
@rm -rf $@.tmp/
|
@rm -rf $@.tmp/
|
||||||
@@ -1220,98 +1181,11 @@ ios%go: out/tildefriends-ios%.app/tildefriends
|
|||||||
ideviceinstaller -i $(realpath $(dir $<))
|
ideviceinstaller -i $(realpath $(dir $<))
|
||||||
|
|
||||||
iossimdebuggo: out/tildefriends-iossimdebug.app/tildefriends ## Build, install, and run an iOS debug build.
|
iossimdebuggo: out/tildefriends-iossimdebug.app/tildefriends ## Build, install, and run an iOS debug build.
|
||||||
|
xcrun -sdk iphoneos codesign -f -s 'Apple Distribution' --entitlements src/ios/Entitlements.plist --generate-entitlement-der out/tildefriends-iossimdebug.app
|
||||||
xcrun simctl install booted out/tildefriends-iossimdebug.app/
|
xcrun simctl install booted out/tildefriends-iossimdebug.app/
|
||||||
xcrun simctl launch booted com.unprompted.tildefriends
|
xcrun simctl launch --console booted com.unprompted.tildefriends
|
||||||
.PHONY: iossimdebuggo
|
.PHONY: iossimdebuggo
|
||||||
|
|
||||||
ANDROID_DEPS := out/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
|
|
||||||
$(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):
|
|
||||||
+@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):
|
|
||||||
+@OPTIONS="--cross-compile-prefix=aarch64-linux-gnu-" BUILD_TARGET=aarch64 SSL_TARGET=linux-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):
|
|
||||||
+@PATH=deps/ios_toolchain/target/bin:$$PATH \
|
|
||||||
BUILD_TARGET=ios64-cross \
|
|
||||||
SSL_TARGET=ios64-cross \
|
|
||||||
CROSS_COMPILE=../../deps/ios_toolchain/target/bin/arm-apple-darwin11- \
|
|
||||||
CROSS_TOP=../../deps/ios_toolchain/target \
|
|
||||||
CROSS_SDK=iPhoneOS18.2.sdk \
|
|
||||||
CC=clang \
|
|
||||||
OPTIONS=-miphoneos-version-min=$(IPHONEOS_VERSION_MIN) \
|
|
||||||
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):
|
|
||||||
+@tools/ssl-local
|
|
||||||
$(filter $(BUILD_DIR)/debug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/release/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(HAVE_WIN),1)
|
|
||||||
WINDOWS_DEPS := out/openssl/$(UNAME_S)/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
|
|
||||||
$(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):
|
|
||||||
+@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
|
|
||||||
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(IOS_DEPS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
out/macos%/tildefriends: out/macos%-arm/tildefriends out/macos%-x86_64/tildefriends
|
out/macos%/tildefriends: out/macos%-arm/tildefriends out/macos%-x86_64/tildefriends
|
||||||
@echo [lipo] $@
|
@echo [lipo] $@
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
@@ -1385,7 +1259,6 @@ tarball: ## Build an all-inclusive source tarball (.tar.xz).
|
|||||||
--exclude=deps/libsodium/test \
|
--exclude=deps/libsodium/test \
|
||||||
--exclude=deps/libuv/docs \
|
--exclude=deps/libuv/docs \
|
||||||
--exclude=deps/libuv/test \
|
--exclude=deps/libuv/test \
|
||||||
--exclude=deps/speedscope/*.map \
|
|
||||||
--exclude=deps/sqlite/shell.c \
|
--exclude=deps/sqlite/shell.c \
|
||||||
--exclude=deps/zlib/contrib/vstudio \
|
--exclude=deps/zlib/contrib/vstudio \
|
||||||
--exclude=deps/zlib/doc \
|
--exclude=deps/zlib/doc \
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ dependencies in the right places.
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
System OpenSSL libraries are assumed to be available on Haiku and OpenBSD.
|
|
||||||
|
|
||||||
On MacOS, Xcode's command-line tools are expected to be available.
|
On MacOS, Xcode's command-line tools are expected to be available.
|
||||||
|
|
||||||
### Build Commands
|
### Build Commands
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🎛",
|
"emoji": "🎛",
|
||||||
"previous": "&kmKNyb/uaXNb24gCinJtfS8iWx4cLUWdtl0y2DwEUas=.sha256"
|
"previous": "&bRhS1LQIH8WQjbBfQqdhjLv7tqDdHT7IEPyCmj39b+4=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,20 @@ tfrpc.register(function global_settings_set(key, value) {
|
|||||||
return core.globalSettingsSet(key, value);
|
return core.globalSettingsSet(key, value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tfrpc.register(function addBlock(id) {
|
||||||
|
return ssb.addBlock(id);
|
||||||
|
});
|
||||||
|
tfrpc.register(function removeBlock(id) {
|
||||||
|
return ssb.removeBlock(id);
|
||||||
|
});
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
let data = {
|
let data = {
|
||||||
users: {},
|
users: {},
|
||||||
granted: await core.allPermissionsGranted(),
|
granted: await core.allPermissionsGranted(),
|
||||||
settings: await core.globalSettingsDescriptions(),
|
settings: await core.globalSettingsDescriptions(),
|
||||||
|
blocks: await ssb.getBlocks(),
|
||||||
};
|
};
|
||||||
for (let user of await core.users()) {
|
for (let user of await core.users()) {
|
||||||
data.users[user] = await core.permissionsForUser(user);
|
data.users[user] = await core.permissionsForUser(user);
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ function delete_user(user) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function add_block() {
|
||||||
|
await tfrpc.rpc.addBlock(document.getElementById('add_block').value);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function remove_block(id) {
|
||||||
|
await tfrpc.rpc.removeBlock(id);
|
||||||
|
}
|
||||||
|
|
||||||
function global_settings_set(key, value) {
|
function global_settings_set(key, value) {
|
||||||
tfrpc.rpc
|
tfrpc.rpc
|
||||||
.global_settings_set(key, value)
|
.global_settings_set(key, value)
|
||||||
@@ -94,11 +102,32 @@ ${description.value}</textarea
|
|||||||
${user}: ${permissions.map((x) => permission_template(x))}
|
${user}: ${permissions.map((x) => permission_template(x))}
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
|
const block_template = (block) => html`
|
||||||
|
<li class="w3-card w3-margin">
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-action"
|
||||||
|
@click=${(e) => remove_block(block.id)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
<code>${block.id}</code>
|
||||||
|
${new Date(block.timestamp)}
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
const users_template = (users) =>
|
const users_template = (users) =>
|
||||||
html` <header class="w3-container w3-theme-l2"><h2>Users</h2></header>
|
html` <header class="w3-container w3-theme-l2"><h2>Users</h2></header>
|
||||||
<ul class="w3-ul">
|
<ul class="w3-ul">
|
||||||
${Object.entries(users).map((u) => user_template(u[0], u[1]))}
|
${Object.entries(users).map((u) => user_template(u[0], u[1]))}
|
||||||
</ul>`;
|
</ul>`;
|
||||||
|
const blocks_template = (blocks) =>
|
||||||
|
html` <header class="w3-container w3-theme-l2"><h2>Blocks</h2></header>
|
||||||
|
<div class="w3-row w3-margin">
|
||||||
|
<input type="text" class="w3-threequarter w3-input" id="add_block"></input>
|
||||||
|
<button class="w3-quarter w3-button w3-theme-action" @click=${add_block}>Add</button>
|
||||||
|
</div>
|
||||||
|
<ul class="w3-ul">
|
||||||
|
${blocks.map((b) => block_template(b))}
|
||||||
|
</ul>`;
|
||||||
const page_template = (data) =>
|
const page_template = (data) =>
|
||||||
html`<div style="padding: 0; margin: 0; width: 100%; max-width: 100%">
|
html`<div style="padding: 0; margin: 0; width: 100%; max-width: 100%">
|
||||||
<header class="w3-container w3-theme-l2"><h2>Global Settings</h2></header>
|
<header class="w3-container w3-theme-l2"><h2>Global Settings</h2></header>
|
||||||
@@ -109,7 +138,7 @@ ${description.value}</textarea
|
|||||||
.map((x) => html`${input_template(x, data.settings[x])}`)}
|
.map((x) => html`${input_template(x, data.settings[x])}`)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
${users_template(data.users)}
|
${users_template(data.users)} ${blocks_template(data.blocks)}
|
||||||
</div> `;
|
</div> `;
|
||||||
render(page_template(g_data), document.body);
|
render(page_template(g_data), document.body);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "📜",
|
"emoji": "📜",
|
||||||
"previous": "&BEf0nraBdHk/+PWqx6tOSu5rheWVaxaL7orAOz3285M=.sha256"
|
"previous": "&sJqeyYjHys6Z8IqqtZ2ij2ZC1E2xieu/FU/u2hE+O1U=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ app.setDocument(`<head>
|
|||||||
</head>
|
</head>
|
||||||
<body style="color:#fff">
|
<body style="color:#fff">
|
||||||
${markdown(docs.docs.global)}
|
${markdown(docs.docs.global)}
|
||||||
|
<!--
|
||||||
|
${Object.keys(docs.docs).filter(x => [...treeify('', globalThis)].indexOf(x) == -1).map(x => `<p>STALE: ${x}</p>`).join('')}
|
||||||
|
-->
|
||||||
${[...treeify('', globalThis)].map(x => document(x)).join('\n')}
|
${[...treeify('', globalThis)].map(x => document(x)).join('\n')}
|
||||||
<a id="Database"></a>
|
<a id="Database"></a>
|
||||||
${markdown(docs.docs.database)}
|
${markdown(docs.docs.database)}
|
||||||
|
|||||||
@@ -195,51 +195,6 @@ Call a function after some delay.
|
|||||||
* *Number* **timeout** Number of milliseconds to wait before calling the callback function.
|
* *Number* **timeout** Number of milliseconds to wait before calling the callback function.
|
||||||
`;
|
`;
|
||||||
|
|
||||||
docs['parseHttpRequest()'] = `
|
|
||||||
Parses an HTTP request.
|
|
||||||
### Parameters
|
|
||||||
* *Uint8Array* **request** The request data. Maybe be partial or contain extra data. The return value will
|
|
||||||
indicate when and where it is complete.
|
|
||||||
* *Number* **last_length** The length of the data passed on a previous attempt for the same request, or 0 initially.
|
|
||||||
### Returns
|
|
||||||
* *Integer* **-2** if the request is incomplete.
|
|
||||||
* *Integer* **-1** if the request could not be parsed.
|
|
||||||
* *Object* An object with **bytes_parsed**, **minor_version**, **path**, and **headers** fields on successful parse.
|
|
||||||
`;
|
|
||||||
|
|
||||||
docs['parseHttpResponse()'] = `
|
|
||||||
Parses an HTTP response.
|
|
||||||
### Parameters
|
|
||||||
* *Uint8Array* **response** The response data. Maybe be partial or contain extra data. The return value will
|
|
||||||
indicate when and where it is complete.
|
|
||||||
* *Number* **last_length** The length of the data passed on a previous attempt for the same response, or 0 initially.
|
|
||||||
### Returns
|
|
||||||
* *Integer* **-2** if the response is incomplete.
|
|
||||||
* *Integer* **-1** if the response could not be parsed.
|
|
||||||
* *Object* An object with **bytes_parsed**, **minor_version**, **status**, **message**, and **headers** fields on successful parse.
|
|
||||||
`;
|
|
||||||
|
|
||||||
docs['sha1Digest()'] = `
|
|
||||||
Calculates a SHA1 digest.
|
|
||||||
|
|
||||||
Completes synchronously.
|
|
||||||
### Parameters
|
|
||||||
* *String* **value** The value for which to calculate the digest.
|
|
||||||
### Returns
|
|
||||||
*String* The SHA1 digest of UTF-8 encoded \`value\`.
|
|
||||||
`;
|
|
||||||
|
|
||||||
docs['maskBytes()'] = `
|
|
||||||
Masks bytes for WebSocket communication.
|
|
||||||
|
|
||||||
Completes synchronously.
|
|
||||||
### Parameters
|
|
||||||
* *Uint8Array* **bytes** The byte array of data to mask.
|
|
||||||
* *Uint32* **mask** The mask to apply.
|
|
||||||
### Returns
|
|
||||||
*Uint32Array* The masked bytes.
|
|
||||||
`;
|
|
||||||
|
|
||||||
docs['exit()'] = `
|
docs['exit()'] = `
|
||||||
Exits the app. But why would you want to do that?
|
Exits the app. But why would you want to do that?
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🦀",
|
"emoji": "🦀",
|
||||||
"previous": "&Hd6CuhhnZIf13PdFJYZBUYLYZO84WdaKvWXLC29M7Ac=.sha256"
|
"previous": "&NdaJseW11fi9Cj4m+WITGxOF2JExm6GDhdCJKFV284Y=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as tfrpc from '/tfrpc.js';
|
|||||||
|
|
||||||
let g_database;
|
let g_database;
|
||||||
let g_hash;
|
let g_hash;
|
||||||
|
let g_sql_cache = {};
|
||||||
|
|
||||||
tfrpc.register(async function localStorageGet(key) {
|
tfrpc.register(async function localStorageGet(key) {
|
||||||
return app.localStorageGet(key);
|
return app.localStorageGet(key);
|
||||||
@@ -51,11 +52,40 @@ tfrpc.register(async function connect(token) {
|
|||||||
tfrpc.register(async function closeConnection(id) {
|
tfrpc.register(async function closeConnection(id) {
|
||||||
await ssb.closeConnection(id);
|
await ssb.closeConnection(id);
|
||||||
});
|
});
|
||||||
tfrpc.register(async function query(sql, args) {
|
tfrpc.register(async function query(sql, args, options) {
|
||||||
|
let start = new Date();
|
||||||
let result = [];
|
let result = [];
|
||||||
await ssb.sqlAsync(sql, args, function callback(row) {
|
let key = options?.cacheable ? JSON.stringify([sql, args]) : undefined;
|
||||||
result.push(row);
|
let entry = key ? g_sql_cache[key] : undefined;
|
||||||
});
|
const k_ideal_count = 64;
|
||||||
|
if (entry) {
|
||||||
|
result = entry.result;
|
||||||
|
} else {
|
||||||
|
await ssb.sqlAsync(sql, args, function callback(row) {
|
||||||
|
result.push(row);
|
||||||
|
});
|
||||||
|
if (key) {
|
||||||
|
g_sql_cache[key] = {
|
||||||
|
result: result,
|
||||||
|
time: new Date().valueOf(),
|
||||||
|
};
|
||||||
|
if (Object.keys(g_sql_cache).length > k_ideal_count * 2) {
|
||||||
|
let aged = Object.entries(g_sql_cache).map(([k, v]) => [v.time, k]);
|
||||||
|
aged.sort();
|
||||||
|
for (let i = 0; i < aged.length / 2; i++) {
|
||||||
|
delete g_sql_cache[aged[i][1]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let end = new Date();
|
||||||
|
if (end - start > 1000) {
|
||||||
|
print(
|
||||||
|
(end - start) / 1000,
|
||||||
|
entry ? 'from cache' : 'from db',
|
||||||
|
sql.replaceAll(/\s+/g, ' ').trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
tfrpc.register(async function appendMessage(id, message) {
|
tfrpc.register(async function appendMessage(id, message) {
|
||||||
@@ -76,6 +106,9 @@ tfrpc.register(function setHash(hash) {
|
|||||||
core.register('onMessage', async function (id) {
|
core.register('onMessage', async function (id) {
|
||||||
await tfrpc.rpc.notifyNewMessage(id);
|
await tfrpc.rpc.notifyNewMessage(id);
|
||||||
});
|
});
|
||||||
|
core.register('onBlob', async function (id) {
|
||||||
|
await tfrpc.rpc.notifyNewBlob(id);
|
||||||
|
});
|
||||||
tfrpc.register(async function store_blob(blob) {
|
tfrpc.register(async function store_blob(blob) {
|
||||||
if (Array.isArray(blob)) {
|
if (Array.isArray(blob)) {
|
||||||
blob = Uint8Array.from(blob);
|
blob = Uint8Array.from(blob);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import {html, render} from './lit-all.min.js';
|
import {html, render} from './lit-all.min.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
let g_emojis;
|
let g_emojis;
|
||||||
|
|
||||||
@@ -140,6 +140,9 @@ export async function picker(callback, anchor, author, recent) {
|
|||||||
<style>
|
<style>
|
||||||
${styles}
|
${styles}
|
||||||
</style>
|
</style>
|
||||||
|
<style>
|
||||||
|
${generate_theme()}
|
||||||
|
</style>
|
||||||
<div
|
<div
|
||||||
class="w3-modal"
|
class="w3-modal"
|
||||||
style="display: block; box-sizing: border-box; z-index: 10"
|
style="display: block; box-sizing: border-box; z-index: 10"
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ import * as tf_tab_news from './tf-tab-news.js';
|
|||||||
import * as tf_tab_news_feed from './tf-tab-news-feed.js';
|
import * as tf_tab_news_feed from './tf-tab-news-feed.js';
|
||||||
import * as tf_tab_search from './tf-tab-search.js';
|
import * as tf_tab_search from './tf-tab-search.js';
|
||||||
import * as tf_tab_connections from './tf-tab-connections.js';
|
import * as tf_tab_connections from './tf-tab-connections.js';
|
||||||
import * as tf_tab_query from './tf-tab-query.js';
|
|
||||||
import * as tf_tag from './tf-tag.js';
|
import * as tf_tag from './tf-tag.js';
|
||||||
import * as tf_styles from './tf-styles.js';
|
import * as tf_styles from './tf-styles.js';
|
||||||
|
|
||||||
window.addEventListener('load', function () {
|
window.addEventListener('load', function () {
|
||||||
let style = document.createElement('style');
|
let style = document.createElement('style');
|
||||||
style.innerText = tf_styles.styles;
|
style.innerText = tf_styles.styles;
|
||||||
|
Promise.resolve(tf_styles.generate_theme()).then(function (x) {
|
||||||
|
style.innerText += x;
|
||||||
|
});
|
||||||
document.body.appendChild(style);
|
document.body.appendChild(style);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {LitElement, html, css, guard, until} from './lit-all.min.js';
|
import {LitElement, html, css, guard, until} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfElement extends LitElement {
|
class TfElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -65,6 +65,17 @@ class TfElement extends LitElement {
|
|||||||
tfrpc.register(async function notifyNewMessage(id) {
|
tfrpc.register(async function notifyNewMessage(id) {
|
||||||
await self.fetch_new_message(id);
|
await self.fetch_new_message(id);
|
||||||
});
|
});
|
||||||
|
tfrpc.register(async function notifyNewBlob(id) {
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent('blob-stored', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
detail: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
tfrpc.register(function set(name, value) {
|
tfrpc.register(function set(name, value) {
|
||||||
if (name === 'broadcasts') {
|
if (name === 'broadcasts') {
|
||||||
self.broadcasts = value;
|
self.broadcasts = value;
|
||||||
@@ -174,6 +185,7 @@ class TfElement extends LitElement {
|
|||||||
'',
|
'',
|
||||||
'@',
|
'@',
|
||||||
'👍',
|
'👍',
|
||||||
|
'🚩',
|
||||||
...Object.keys(this.visible_private())
|
...Object.keys(this.visible_private())
|
||||||
.sort()
|
.sort()
|
||||||
.map((x) => '🔐' + JSON.parse(x).join(',')),
|
.map((x) => '🔐' + JSON.parse(x).join(',')),
|
||||||
@@ -195,8 +207,6 @@ class TfElement extends LitElement {
|
|||||||
this.tab = 'search';
|
this.tab = 'search';
|
||||||
} else if (this.hash === '#connections') {
|
} else if (this.hash === '#connections') {
|
||||||
this.tab = 'connections';
|
this.tab = 'connections';
|
||||||
} else if (this.hash.startsWith('#sql=')) {
|
|
||||||
this.tab = 'query';
|
|
||||||
} else {
|
} else {
|
||||||
this.tab = 'news';
|
this.tab = 'news';
|
||||||
}
|
}
|
||||||
@@ -394,14 +404,6 @@ class TfElement extends LitElement {
|
|||||||
return [cache.latest, cache.messages];
|
return [cache.latest, cache.messages];
|
||||||
}
|
}
|
||||||
|
|
||||||
async query_timed(sql, args) {
|
|
||||||
let start = new Date();
|
|
||||||
let result = await tfrpc.rpc.query(sql, args);
|
|
||||||
let end = new Date();
|
|
||||||
console.log((end - start) / 1000, sql.replaceAll(/\s+/g, ' ').trim());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async group_private_messages(messages) {
|
async group_private_messages(messages) {
|
||||||
let groups = {};
|
let groups = {};
|
||||||
let result = await this.decrypt(
|
let result = await this.decrypt(
|
||||||
@@ -433,7 +435,6 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async load_channels_latest(following) {
|
async load_channels_latest(following) {
|
||||||
let start_time = new Date();
|
|
||||||
let latest_private = this.get_latest_private(following);
|
let latest_private = this.get_latest_private(following);
|
||||||
const k_args = [
|
const k_args = [
|
||||||
JSON.stringify(this.channels),
|
JSON.stringify(this.channels),
|
||||||
@@ -443,7 +444,7 @@ class TfElement extends LitElement {
|
|||||||
];
|
];
|
||||||
let channels = (
|
let channels = (
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.query_timed(
|
tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
||||||
@@ -456,7 +457,7 @@ class TfElement extends LitElement {
|
|||||||
`,
|
`,
|
||||||
k_args
|
k_args
|
||||||
),
|
),
|
||||||
this.query_timed(
|
tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
JOIN messages_refs ON messages.id = messages_refs.message
|
JOIN messages_refs ON messages.id = messages_refs.message
|
||||||
@@ -470,7 +471,7 @@ class TfElement extends LitElement {
|
|||||||
`,
|
`,
|
||||||
k_args
|
k_args
|
||||||
),
|
),
|
||||||
this.query_timed(
|
tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
@@ -481,7 +482,7 @@ class TfElement extends LitElement {
|
|||||||
`,
|
`,
|
||||||
k_args
|
k_args
|
||||||
),
|
),
|
||||||
this.query_timed(
|
tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||||
@@ -490,9 +491,16 @@ class TfElement extends LitElement {
|
|||||||
`,
|
`,
|
||||||
k_args
|
k_args
|
||||||
),
|
),
|
||||||
|
tfrpc.rpc.query(
|
||||||
|
`
|
||||||
|
SELECT '🚩' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
|
WHERE messages.content ->> 'type' = 'flag'
|
||||||
|
`,
|
||||||
|
k_args
|
||||||
|
),
|
||||||
])
|
])
|
||||||
).flat();
|
).flat();
|
||||||
let latest = {};
|
let latest = {'🔐': undefined};
|
||||||
for (let row of channels) {
|
for (let row of channels) {
|
||||||
if (!latest[row.channel]) {
|
if (!latest[row.channel]) {
|
||||||
latest[row.channel] = row.rowid;
|
latest[row.channel] = row.rowid;
|
||||||
@@ -501,18 +509,14 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.channels_latest = latest;
|
this.channels_latest = latest;
|
||||||
console.log('channels took', (new Date() - start_time) / 1000.0);
|
|
||||||
let self = this;
|
let self = this;
|
||||||
start_time = new Date();
|
|
||||||
latest_private.then(async function (latest) {
|
latest_private.then(async function (latest) {
|
||||||
|
let grouped = await self.group_private_messages(latest[1]);
|
||||||
self.channels_latest = Object.assign({}, self.channels_latest, {
|
self.channels_latest = Object.assign({}, self.channels_latest, {
|
||||||
'🔐': latest[0],
|
'🔐': latest[0],
|
||||||
});
|
});
|
||||||
console.log('private took', (new Date() - start_time) / 1000.0);
|
|
||||||
self.private_messages = latest[1];
|
self.private_messages = latest[1];
|
||||||
self.grouped_private_messages = await self.group_private_messages(
|
self.grouped_private_messages = grouped;
|
||||||
latest[1]
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,7 +593,6 @@ class TfElement extends LitElement {
|
|||||||
this.loading_latest = true;
|
this.loading_latest = true;
|
||||||
this.reset_progress();
|
this.reset_progress();
|
||||||
try {
|
try {
|
||||||
let start_time = new Date();
|
|
||||||
let whoami = this.whoami;
|
let whoami = this.whoami;
|
||||||
let following = await tfrpc.rpc.following([whoami], 2);
|
let following = await tfrpc.rpc.following([whoami], 2);
|
||||||
let old_users = this.users ?? {};
|
let old_users = this.users ?? {};
|
||||||
@@ -614,30 +617,13 @@ class TfElement extends LitElement {
|
|||||||
(await tfrpc.rpc.databaseGet('unread')) ?? '{}'
|
(await tfrpc.rpc.databaseGet('unread')) ?? '{}'
|
||||||
);
|
);
|
||||||
this.following = Object.keys(following);
|
this.following = Object.keys(following);
|
||||||
let about_start_time = new Date();
|
|
||||||
start_time = new Date();
|
|
||||||
users = await this.fetch_user_info(users);
|
users = await this.fetch_user_info(users);
|
||||||
console.log(
|
|
||||||
'user info took',
|
|
||||||
(new Date() - start_time) / 1000.0,
|
|
||||||
'seconds'
|
|
||||||
);
|
|
||||||
this.users = users;
|
this.users = users;
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
this.fetch_about(following, users).then(function (result) {
|
this.fetch_about(following, users).then(function (result) {
|
||||||
self.users = result;
|
self.users = result;
|
||||||
console.log(
|
|
||||||
'about took',
|
|
||||||
(new Date() - about_start_time) / 1000.0,
|
|
||||||
'seconds for',
|
|
||||||
Object.keys(users).length,
|
|
||||||
'users'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
console.log(
|
|
||||||
`load finished ${whoami} => ${this.whoami} in ${(new Date() - start_time) / 1000}`
|
|
||||||
);
|
|
||||||
await reactions;
|
await reactions;
|
||||||
this.whoami = whoami;
|
this.whoami = whoami;
|
||||||
this.loaded = whoami;
|
this.loaded = whoami;
|
||||||
@@ -700,7 +686,8 @@ class TfElement extends LitElement {
|
|||||||
@closeprivatechat=${this.close_private_chat}
|
@closeprivatechat=${this.close_private_chat}
|
||||||
.connections=${this.connections}
|
.connections=${this.connections}
|
||||||
.private_messages=${this.private_messages}
|
.private_messages=${this.private_messages}
|
||||||
.grouped_private_messages=${this.visible_private()}
|
.visible_private_messages=${this.visible_private()}
|
||||||
|
.grouped_private_messages=${this.grouped_private_messages}
|
||||||
.recent_reactions=${this.recent_reactions}
|
.recent_reactions=${this.recent_reactions}
|
||||||
?is_administrator=${this.is_administrator}
|
?is_administrator=${this.is_administrator}
|
||||||
?stay_connected=${this.stay_connected}
|
?stay_connected=${this.stay_connected}
|
||||||
@@ -725,17 +712,6 @@ class TfElement extends LitElement {
|
|||||||
: null}
|
: null}
|
||||||
></tf-tab-search>
|
></tf-tab-search>
|
||||||
`;
|
`;
|
||||||
} else if (this.tab === 'query') {
|
|
||||||
return html`
|
|
||||||
<tf-tab-query
|
|
||||||
.following=${this.following}
|
|
||||||
whoami=${this.whoami}
|
|
||||||
.users=${this.users}
|
|
||||||
query=${this.hash?.startsWith('#sql=')
|
|
||||||
? decodeURIComponent(this.hash.substring(5))
|
|
||||||
: null}
|
|
||||||
></tf-tab-query>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -746,8 +722,6 @@ class TfElement extends LitElement {
|
|||||||
await tfrpc.rpc.setHash('#');
|
await tfrpc.rpc.setHash('#');
|
||||||
} else if (tab === 'connections') {
|
} else if (tab === 'connections') {
|
||||||
await tfrpc.rpc.setHash('#connections');
|
await tfrpc.rpc.setHash('#connections');
|
||||||
} else if (tab === 'query') {
|
|
||||||
await tfrpc.rpc.setHash('#sql=');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -767,6 +741,27 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async pick_color() {
|
||||||
|
let input = document.createElement('input');
|
||||||
|
input.type = 'color';
|
||||||
|
input.value = (await tfrpc.rpc.localStorageGet('color')) ?? '#ff0000';
|
||||||
|
input.addEventListener('change', async function () {
|
||||||
|
await tfrpc.rpc.localStorageSet('color', input.value);
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
search() {
|
||||||
|
this.set_hash('#q=' + this.renderRoot.getElementById('search_text').value);
|
||||||
|
}
|
||||||
|
|
||||||
|
search_keydown(event) {
|
||||||
|
if (event.keyCode == 13) {
|
||||||
|
this.search();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
@@ -780,8 +775,6 @@ class TfElement extends LitElement {
|
|||||||
const k_tabs = {
|
const k_tabs = {
|
||||||
'📰': 'news',
|
'📰': 'news',
|
||||||
'📡': 'connections',
|
'📡': 'connections',
|
||||||
'🔍': 'search',
|
|
||||||
'👩💻': 'query',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let tabs = html`
|
let tabs = html`
|
||||||
@@ -789,26 +782,27 @@ class TfElement extends LitElement {
|
|||||||
class="w3-bar w3-theme-l1"
|
class="w3-bar w3-theme-l1"
|
||||||
style="position: static; top: 0; z-index: 10"
|
style="position: static; top: 0; z-index: 10"
|
||||||
>
|
>
|
||||||
${this.is_administrator && self.tab != 'news'
|
${
|
||||||
? html`
|
this.is_administrator
|
||||||
<button
|
? html`
|
||||||
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
|
<button
|
||||||
(this.connections?.some((x) => x.flags.one_shot)
|
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
|
||||||
? ' w3-spin'
|
(this.connections?.some((x) => x.flags.one_shot)
|
||||||
: '')}
|
? ' w3-spin'
|
||||||
style="width: 1.5em; height: 1.5em; padding: 8px"
|
: '')}
|
||||||
@click=${this.refresh}
|
@click=${this.refresh}
|
||||||
>
|
>
|
||||||
↻
|
↻
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="w3-bar-item w3-button w3-ripple"
|
class="w3-bar-item w3-button w3-ripple"
|
||||||
@click=${this.toggle_stay_connected}
|
@click=${this.toggle_stay_connected}
|
||||||
>
|
>
|
||||||
${this.stay_connected ? '🔗' : '⛓️💥'}
|
${this.stay_connected ? '🔗' : '⛓️💥'}
|
||||||
</button>
|
</button>
|
||||||
`
|
`
|
||||||
: undefined}
|
: undefined
|
||||||
|
}
|
||||||
${Object.entries(k_tabs).map(
|
${Object.entries(k_tabs).map(
|
||||||
([k, v]) => html`
|
([k, v]) => html`
|
||||||
<button
|
<button
|
||||||
@@ -825,6 +819,14 @@ class TfElement extends LitElement {
|
|||||||
</button>
|
</button>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
|
<button
|
||||||
|
class="w3-bar-item w3-button w3-right"
|
||||||
|
@click=${this.pick_color}
|
||||||
|
>
|
||||||
|
🎨<span class="w3-hide-small">Color</span>
|
||||||
|
</button>
|
||||||
|
<button class="w3-bar-item w3-button w3-right" @click=${this.search}>Search</button>
|
||||||
|
<input type="text" class="w3-input w3-bar-item w3-right w3-theme-d1" placeholder="keywords, @id, #channel" id="search_text" @keydown=${this.search_keydown}></input>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
let contents = this.guest
|
let contents = this.guest
|
||||||
@@ -860,6 +862,9 @@ class TfElement extends LitElement {
|
|||||||
`
|
`
|
||||||
: undefined;
|
: undefined;
|
||||||
return html`
|
return html`
|
||||||
|
<style>
|
||||||
|
${generate_theme()}
|
||||||
|
</style>
|
||||||
<div
|
<div
|
||||||
style="width: 100vw; min-height: 100vh; height: 100vh; display: flex; flex-direction: column"
|
style="width: 100vw; min-height: 100vh; height: 100vh; display: flex; flex-direction: column"
|
||||||
class="w3-theme-dark"
|
class="w3-theme-dark"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {LitElement, html, unsafeHTML, live} from './lit-all.min.js';
|
import {LitElement, html, unsafeHTML, live} from './lit-all.min.js';
|
||||||
import * as tfutils from './tf-utils.js';
|
import * as tfutils from './tf-utils.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
import Tribute from './tribute.esm.js';
|
import Tribute from './tribute.esm.js';
|
||||||
|
|
||||||
class TfComposeElement extends LitElement {
|
class TfComposeElement extends LitElement {
|
||||||
@@ -603,6 +603,9 @@ class TfComposeElement extends LitElement {
|
|||||||
🔐 Encrypt
|
🔐 Encrypt
|
||||||
</button>`;
|
</button>`;
|
||||||
let result = html`
|
let result = html`
|
||||||
|
<style>
|
||||||
|
${generate_theme()}
|
||||||
|
</style>
|
||||||
<style>
|
<style>
|
||||||
.w3-input:empty::before {
|
.w3-input:empty::before {
|
||||||
content: attr(placeholder);
|
content: attr(placeholder);
|
||||||
@@ -625,7 +628,7 @@ class TfComposeElement extends LitElement {
|
|||||||
<div class="w3-half">
|
<div class="w3-half">
|
||||||
<span
|
<span
|
||||||
class="w3-input w3-theme-d1 w3-border"
|
class="w3-input w3-theme-d1 w3-border"
|
||||||
style="resize: vertical; width: 100%; overflow: hidden; white-space: pre-wrap"
|
style="resize: vertical; width: 100%; white-space: pre-wrap"
|
||||||
placeholder="Write a post here."
|
placeholder="Write a post here."
|
||||||
id="edit"
|
id="edit"
|
||||||
@input=${this.input}
|
@input=${this.input}
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import {
|
|||||||
html,
|
html,
|
||||||
repeat,
|
repeat,
|
||||||
render,
|
render,
|
||||||
|
unsafeCSS,
|
||||||
unsafeHTML,
|
unsafeHTML,
|
||||||
|
until,
|
||||||
} from './lit-all.min.js';
|
} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import * as tfutils from './tf-utils.js';
|
import * as tfutils from './tf-utils.js';
|
||||||
import * as emojis from './emojis.js';
|
import * as emojis from './emojis.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfMessageElement extends LitElement {
|
class TfMessageElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -24,6 +26,7 @@ class TfMessageElement extends LitElement {
|
|||||||
channel: {type: String},
|
channel: {type: String},
|
||||||
channel_unread: {type: Number},
|
channel_unread: {type: Number},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
|
depth: {type: Number},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,16 +43,20 @@ class TfMessageElement extends LitElement {
|
|||||||
this.expanded = {};
|
this.expanded = {};
|
||||||
this.channel_unread = -1;
|
this.channel_unread = -1;
|
||||||
this.recent_reactions = [];
|
this.recent_reactions = [];
|
||||||
|
this.depth = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this._click_callback = this.document_click.bind(this);
|
this._click_callback = this.document_click.bind(this);
|
||||||
|
this._blob_stored = this.blob_stored.bind(this);
|
||||||
document.body.addEventListener('mouseup', this._click_callback);
|
document.body.addEventListener('mouseup', this._click_callback);
|
||||||
|
window.addEventListener('blob-stored', this._blob_stored);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
|
window.removeEventListener('blob-stored', this._blob_stored);
|
||||||
document.body.removeEventListener('mouseup', this._click_callback);
|
document.body.removeEventListener('mouseup', this._click_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +68,16 @@ class TfMessageElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blob_stored(event) {
|
||||||
|
let search = `/${event.detail.id}/view`;
|
||||||
|
for (let img of this.shadowRoot.querySelectorAll('img')) {
|
||||||
|
if (img.src.indexOf(search) != -1) {
|
||||||
|
let src = img.src.split('?')[0];
|
||||||
|
img.src = `${src}?${new Date().valueOf()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
show_reply() {
|
show_reply() {
|
||||||
let event = new CustomEvent('tf-draft', {
|
let event = new CustomEvent('tf-draft', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@@ -179,6 +196,26 @@ class TfMessageElement extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flag(event) {
|
||||||
|
let reason = prompt(
|
||||||
|
'What is the reason for reporting this content (spam, nsfw, ...)?',
|
||||||
|
'offensive'
|
||||||
|
);
|
||||||
|
if (reason !== undefined) {
|
||||||
|
tfrpc.rpc
|
||||||
|
.appendMessage(this.whoami, {
|
||||||
|
type: 'flag',
|
||||||
|
flag: {
|
||||||
|
link: this.message.id,
|
||||||
|
reason: reason.length ? reason : undefined,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
alert(error?.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
show_image(link) {
|
show_image(link) {
|
||||||
let div = document.createElement('div');
|
let div = document.createElement('div');
|
||||||
div.style.left = 0;
|
div.style.left = 0;
|
||||||
@@ -311,7 +348,9 @@ class TfMessageElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expanded_key() {
|
expanded_key() {
|
||||||
return this.message?.id || this.messages?.map((x) => x.id).join(':');
|
return (
|
||||||
|
this.message?.id || this.message?.messages?.map((x) => x.id).join(':')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_expanded(expanded, tag) {
|
set_expanded(expanded, tag) {
|
||||||
@@ -349,12 +388,13 @@ class TfMessageElement extends LitElement {
|
|||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
return html` <div class="w3-container w3-margin-bottom">
|
return html` <ul class="w3-container w3-margin-bottom w3-ul w3-card-4">
|
||||||
${repeat(
|
${repeat(
|
||||||
this.message.child_messages || [],
|
this.message.child_messages || [],
|
||||||
(x) => x.id,
|
(x) => x.id,
|
||||||
(x) =>
|
(x) =>
|
||||||
html`<tf-message
|
html`<li style="padding: 0">
|
||||||
|
<tf-message
|
||||||
.message=${x}
|
.message=${x}
|
||||||
whoami=${this.whoami}
|
whoami=${this.whoami}
|
||||||
.users=${this.users}
|
.users=${this.users}
|
||||||
@@ -363,16 +403,20 @@ class TfMessageElement extends LitElement {
|
|||||||
channel=${this.channel}
|
channel=${this.channel}
|
||||||
channel_unread=${this.channel_unread}
|
channel_unread=${this.channel_unread}
|
||||||
.recent_reactions=${this.recent_reactions}
|
.recent_reactions=${this.recent_reactions}
|
||||||
></tf-message>`
|
depth=${this.depth + 1}
|
||||||
)}
|
></tf-message>
|
||||||
</div>
|
</li>`
|
||||||
<button
|
)}
|
||||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
<li style="padding: 0" class="w3-margin-bottom">
|
||||||
style="box-sizing: border-box"
|
<button
|
||||||
@click=${() => self.set_expanded(false)}
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
>
|
style="box-sizing: border-box"
|
||||||
Collapse
|
@click=${() => self.set_expanded(false)}
|
||||||
</button>`;
|
>
|
||||||
|
Collapse
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -475,12 +519,16 @@ class TfMessageElement extends LitElement {
|
|||||||
</button>
|
</button>
|
||||||
`
|
`
|
||||||
: undefined}
|
: undefined}
|
||||||
<button
|
<button class="w3-button w3-bar-item" @click=${this.react}>
|
||||||
class="w3-button w3-bar-item w3-border-bottom"
|
|
||||||
@click=${this.react}
|
|
||||||
>
|
|
||||||
👍 React
|
👍 React
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
id="button_flag"
|
||||||
|
class="w3-button w3-bar-item w3-border-bottom"
|
||||||
|
@click=${this.flag}
|
||||||
|
>
|
||||||
|
🚩 Flag
|
||||||
|
</button>
|
||||||
${formats.map(
|
${formats.map(
|
||||||
([format, name]) => html`
|
([format, name]) => html`
|
||||||
<button
|
<button
|
||||||
@@ -536,8 +584,11 @@ class TfMessageElement extends LitElement {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div
|
<div
|
||||||
class="w3-card-4 ${this.class_background()} w3-border-theme w3-margin-top"
|
class="w3-card-4 ${this.class_background()} w3-border-theme ${this
|
||||||
style="overflow: auto; overflow-wrap: anywhere; display: block; max-width: 100%"
|
.depth == 0
|
||||||
|
? 'w3-margin-top'
|
||||||
|
: ''}"
|
||||||
|
style="overflow-wrap: anywhere; display: block; max-width: 100%"
|
||||||
>
|
>
|
||||||
${inner}
|
${inner}
|
||||||
</div>
|
</div>
|
||||||
@@ -548,9 +599,11 @@ class TfMessageElement extends LitElement {
|
|||||||
let self = this;
|
let self = this;
|
||||||
return this.render_frame(html`
|
return this.render_frame(html`
|
||||||
${self.render_header()}
|
${self.render_header()}
|
||||||
${self.format == 'raw'
|
<div class="w3-container">
|
||||||
? html`<div class="w3-container">${self.render_raw()}</div>`
|
${self.format == 'raw'
|
||||||
: inner}
|
? html`${self.render_raw()}`
|
||||||
|
: self.render_flagged(inner)}
|
||||||
|
</div>
|
||||||
${self.render_votes()}
|
${self.render_votes()}
|
||||||
${(self.message.child_messages || []).map(
|
${(self.message.child_messages || []).map(
|
||||||
(x) => html`
|
(x) => html`
|
||||||
@@ -563,6 +616,7 @@ class TfMessageElement extends LitElement {
|
|||||||
channel=${self.channel}
|
channel=${self.channel}
|
||||||
channel_unread=${self.channel_unread}
|
channel_unread=${self.channel_unread}
|
||||||
.recent_reactions=${self.recent_reactions}
|
.recent_reactions=${self.recent_reactions}
|
||||||
|
depth=${self.depth + 1}
|
||||||
></tf-message>
|
></tf-message>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@@ -598,15 +652,17 @@ class TfMessageElement extends LitElement {
|
|||||||
let sorted = this.message.messages
|
let sorted = this.message.messages
|
||||||
.map((x) => [
|
.map((x) => [
|
||||||
x.author,
|
x.author,
|
||||||
x.content.blocking !== undefined
|
x.content.following && x.content.blocking
|
||||||
? x.content.blocking
|
? 'is following and blocking'
|
||||||
? 'is blocking'
|
: x.content.following
|
||||||
: 'is no longer blocking'
|
? 'is following'
|
||||||
: x.content.following !== undefined
|
: x.content.blocking
|
||||||
? x.content.following
|
? 'is blocking'
|
||||||
? 'is following'
|
: x.content.blocking !== undefined
|
||||||
: 'is no longer following'
|
? 'is no longer blocking'
|
||||||
: '',
|
: x.content.following !== undefined
|
||||||
|
? 'is no longer following'
|
||||||
|
: '',
|
||||||
x.content.contact,
|
x.content.contact,
|
||||||
x,
|
x,
|
||||||
])
|
])
|
||||||
@@ -631,6 +687,35 @@ class TfMessageElement extends LitElement {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channel_group_by_author() {
|
||||||
|
let sorted = this.message.messages
|
||||||
|
.map((x) => [
|
||||||
|
x.author,
|
||||||
|
x.content.subscribed ? 'subscribed to' : 'unsubscribed from',
|
||||||
|
x.content.channel,
|
||||||
|
x,
|
||||||
|
])
|
||||||
|
.sort();
|
||||||
|
let result = [];
|
||||||
|
let last;
|
||||||
|
let group;
|
||||||
|
for (let row of sorted) {
|
||||||
|
if (last && last[0] == row[0] && last[1] == row[1]) {
|
||||||
|
group.push(row[2]);
|
||||||
|
} else {
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], channels: group});
|
||||||
|
}
|
||||||
|
last = row;
|
||||||
|
group = [row[2]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], channels: group});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
allow_unread() {
|
allow_unread() {
|
||||||
return (
|
return (
|
||||||
this.channel == '@' ||
|
this.channel == '@' ||
|
||||||
@@ -644,7 +729,39 @@ class TfMessageElement extends LitElement {
|
|||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render_flagged(inner) {
|
||||||
|
if (this.message.flags) {
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="w3-panel w3-round-xlarge w3-theme-l4 w3"
|
||||||
|
style="cursor: pointer"
|
||||||
|
@click=${(x) => this.toggle_expanded(':cw')}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
${this.message.flags
|
||||||
|
? html`<p>
|
||||||
|
Caution: This message has been flagged
|
||||||
|
${this.message.flags.length}
|
||||||
|
time${this.message.flags.length == 1 ? '' : 's'}.
|
||||||
|
</p>`
|
||||||
|
: undefined}
|
||||||
|
</p>
|
||||||
|
<p class="w3-small">
|
||||||
|
${inner !== undefined
|
||||||
|
? this.is_expanded(':cw')
|
||||||
|
? 'Show less'
|
||||||
|
: 'Show more'
|
||||||
|
: undefined}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
${this.is_expanded(':cw') ? inner : undefined}
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
return inner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_render() {
|
||||||
let content = this.message?.content;
|
let content = this.message?.content;
|
||||||
if (this.message?.decrypted?.type == 'post') {
|
if (this.message?.decrypted?.type == 'post') {
|
||||||
content = this.message.decrypted;
|
content = this.message.decrypted;
|
||||||
@@ -665,6 +782,7 @@ class TfMessageElement extends LitElement {
|
|||||||
.expanded=${this.expanded}
|
.expanded=${this.expanded}
|
||||||
channel=${this.channel}
|
channel=${this.channel}
|
||||||
channel_unread=${this.channel_unread}
|
channel_unread=${this.channel_unread}
|
||||||
|
depth=${this.depth + 1}
|
||||||
></tf-message>`
|
></tf-message>`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -706,6 +824,56 @@ class TfMessageElement extends LitElement {
|
|||||||
</button>
|
</button>
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
} else if (this.message?.type === 'channel_group') {
|
||||||
|
if (this.expanded[this.expanded_key()]) {
|
||||||
|
return this.render_frame(html`
|
||||||
|
<div class="w3-padding">
|
||||||
|
${this.message.messages.map(
|
||||||
|
(x) =>
|
||||||
|
html`<tf-message
|
||||||
|
.message=${x}
|
||||||
|
whoami=${this.whoami}
|
||||||
|
.users=${this.users}
|
||||||
|
.drafts=${this.drafts}
|
||||||
|
.expanded=${this.expanded}
|
||||||
|
channel=${this.channel}
|
||||||
|
channel_unread=${this.channel_unread}
|
||||||
|
depth=${this.depth + 1}
|
||||||
|
></tf-message>`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(false)}
|
||||||
|
>
|
||||||
|
Collapse
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
return this.render_frame(html`
|
||||||
|
<div class="w3-padding">
|
||||||
|
${this.channel_group_by_author().map(
|
||||||
|
(x) => html`
|
||||||
|
<div>
|
||||||
|
<tf-user id=${x.author} .users=${this.users}></tf-user>
|
||||||
|
${x.action}
|
||||||
|
${x.channels.map(
|
||||||
|
(y) => html` <tf-tag tag=${'#' + y}></tf-tag> `
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(true)}
|
||||||
|
>
|
||||||
|
Expand
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
}
|
||||||
} else if (this.message.placeholder) {
|
} else if (this.message.placeholder) {
|
||||||
return this.render_frame(
|
return this.render_frame(
|
||||||
html`<div>
|
html`<div>
|
||||||
@@ -740,6 +908,7 @@ class TfMessageElement extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w3-container">${this.render_flagged(undefined)}</div>
|
||||||
<div>${this.render_votes()}</div>
|
<div>${this.render_votes()}</div>
|
||||||
${(this.message.child_messages || []).map(
|
${(this.message.child_messages || []).map(
|
||||||
(x) => html`
|
(x) => html`
|
||||||
@@ -751,6 +920,7 @@ class TfMessageElement extends LitElement {
|
|||||||
.expanded=${this.expanded}
|
.expanded=${this.expanded}
|
||||||
channel=${this.channel}
|
channel=${this.channel}
|
||||||
channel_unread=${this.channel_unread}
|
channel_unread=${this.channel_unread}
|
||||||
|
depth=${this.depth + 1}
|
||||||
></tf-message>
|
></tf-message>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@@ -854,7 +1024,19 @@ class TfMessageElement extends LitElement {
|
|||||||
style="cursor: pointer"
|
style="cursor: pointer"
|
||||||
@click=${(x) => this.toggle_expanded(':cw')}
|
@click=${(x) => this.toggle_expanded(':cw')}
|
||||||
>
|
>
|
||||||
<p>${content.contentWarning}</p>
|
<p>
|
||||||
|
${this.message.flags
|
||||||
|
? html`<p>
|
||||||
|
Caution: This message has been flagged
|
||||||
|
${this.message.flags.length}
|
||||||
|
time${this.message.flags.length == 1 ? '' : 's'}.
|
||||||
|
</p>`
|
||||||
|
: undefined}
|
||||||
|
${content.contentWarning
|
||||||
|
? html`<p>${content.contentWarning}</p>`
|
||||||
|
: undefined}
|
||||||
|
</p>
|
||||||
|
|
||||||
<p class="w3-small">
|
<p class="w3-small">
|
||||||
${this.is_expanded(':cw') ? 'Show less' : 'Show more'}
|
${this.is_expanded(':cw') ? 'Show less' : 'Show more'}
|
||||||
</p>
|
</p>
|
||||||
@@ -865,11 +1047,12 @@ class TfMessageElement extends LitElement {
|
|||||||
<div @click=${this.body_click}>${body}</div>
|
<div @click=${this.body_click}>${body}</div>
|
||||||
${this.render_mentions()}
|
${this.render_mentions()}
|
||||||
`;
|
`;
|
||||||
let payload = content.contentWarning
|
let payload =
|
||||||
? self.expanded[(this.message.id || '') + ':cw']
|
this.message.flags || content.contentWarning
|
||||||
? html` ${content_warning} ${content_html} `
|
? self.expanded[(this.message.id || '') + ':cw']
|
||||||
: content_warning
|
? html` ${content_warning} ${content_html} `
|
||||||
: content_html;
|
: content_warning
|
||||||
|
: content_html;
|
||||||
return this.render_frame(html`
|
return this.render_frame(html`
|
||||||
${this.render_header()}
|
${this.render_header()}
|
||||||
<div class="w3-container">${payload}</div>
|
<div class="w3-container">${payload}</div>
|
||||||
@@ -888,15 +1071,13 @@ class TfMessageElement extends LitElement {
|
|||||||
`);
|
`);
|
||||||
} else if (content.type === 'blog') {
|
} else if (content.type === 'blog') {
|
||||||
let self = this;
|
let self = this;
|
||||||
tfrpc.rpc.get_blob(content.blog).then(function (data) {
|
self.blog_data = tfrpc.rpc.get_blob(content.blog).then(function (data) {
|
||||||
self.blog_data = data;
|
return data
|
||||||
|
? unsafeHTML(tfutils.markdown(data))
|
||||||
|
: html`Blog post content unavailable.`;
|
||||||
});
|
});
|
||||||
let payload = this.expanded[(this.message.id || '') + ':blog']
|
let payload = this.expanded[(this.message.id || '') + ':blog']
|
||||||
? html`<div>
|
? until(this.blog_data, 'Loading...')
|
||||||
${this.blog_data
|
|
||||||
? unsafeHTML(tfutils.markdown(this.blog_data))
|
|
||||||
: 'Loading...'}
|
|
||||||
</div>`
|
|
||||||
: undefined;
|
: undefined;
|
||||||
let body;
|
let body;
|
||||||
switch (this.format) {
|
switch (this.format) {
|
||||||
@@ -909,15 +1090,24 @@ class TfMessageElement extends LitElement {
|
|||||||
case 'message':
|
case 'message':
|
||||||
body = html`
|
body = html`
|
||||||
<div
|
<div
|
||||||
style="border: 1px solid #fff; border-radius: 1em; padding: 8px; margin: 4px; cursor: pointer"
|
class="w3-border w3-theme-l4 w3-round-xlarge"
|
||||||
@click=${(x) => self.toggle_expanded(':blog')}>
|
style="padding: 8px; margin: 4px; cursor: pointer"
|
||||||
|
@click=${(x) => self.toggle_expanded(':blog')}
|
||||||
|
>
|
||||||
<h2>${content.title}</h2>
|
<h2>${content.title}</h2>
|
||||||
<div style="display: flex; flex-direction: row">
|
<div style="display: flex; flex-direction: row; gap: 8px">
|
||||||
<img src=/${content.thumbnail}/view></img>
|
${content.thumbnail
|
||||||
|
? html`<img src=/${content.thumbnail}/view style="max-width: 25vw; max-height: 25vw"></img>`
|
||||||
|
: undefined}
|
||||||
<span>${content.summary}</span>
|
<span>${content.summary}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="w3-small">
|
||||||
|
${this.expanded[(this.message.id || '') + ':blog']
|
||||||
|
? 'Show less'
|
||||||
|
: 'Show more'}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
${payload}
|
<div class="w3-container">${payload}</div>
|
||||||
`;
|
`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -986,6 +1176,15 @@ class TfMessageElement extends LitElement {
|
|||||||
return this.render_small_frame(this.render_raw());
|
return this.render_small_frame(this.render_raw());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
${generate_theme()}
|
||||||
|
</style>
|
||||||
|
${this._render()}
|
||||||
|
`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('tf-message', TfMessageElement);
|
customElements.define('tf-message', TfMessageElement);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {LitElement, html, unsafeHTML, repeat, until} from './lit-all.min.js';
|
import {LitElement, html, unsafeHTML, repeat, until} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfNewsElement extends LitElement {
|
class TfNewsElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -37,8 +37,6 @@ class TfNewsElement extends LitElement {
|
|||||||
let self = this;
|
let self = this;
|
||||||
let messages_by_id = {};
|
let messages_by_id = {};
|
||||||
|
|
||||||
console.log('processing', messages.length, 'messages');
|
|
||||||
|
|
||||||
function ensure_message(id, rowid) {
|
function ensure_message(id, rowid) {
|
||||||
let found = messages_by_id[id];
|
let found = messages_by_id[id];
|
||||||
if (found) {
|
if (found) {
|
||||||
@@ -66,6 +64,16 @@ class TfNewsElement extends LitElement {
|
|||||||
}
|
}
|
||||||
parent.votes.push(message);
|
parent.votes.push(message);
|
||||||
message.parent_message = message.content.vote.link;
|
message.parent_message = message.content.vote.link;
|
||||||
|
} else if (message.content.type == 'flag') {
|
||||||
|
let parent = ensure_message(message.content.flag.link, message.rowid);
|
||||||
|
if (!parent.flags) {
|
||||||
|
parent.flags = [];
|
||||||
|
}
|
||||||
|
parent.flags.push(message);
|
||||||
|
parent.flags = Object.values(
|
||||||
|
Object.fromEntries(parent.flags.map((x) => [x.id, x]))
|
||||||
|
);
|
||||||
|
message.parent_message = message.content.flag.link;
|
||||||
} else if (message.content.type == 'post') {
|
} else if (message.content.type == 'post') {
|
||||||
if (message.content.root) {
|
if (message.content.root) {
|
||||||
if (typeof message.content.root === 'string') {
|
if (typeof message.content.root === 'string') {
|
||||||
@@ -106,6 +114,7 @@ class TfNewsElement extends LitElement {
|
|||||||
message.parent_message = placeholder.parent_message;
|
message.parent_message = placeholder.parent_message;
|
||||||
message.child_messages = placeholder.child_messages;
|
message.child_messages = placeholder.child_messages;
|
||||||
message.votes = placeholder.votes;
|
message.votes = placeholder.votes;
|
||||||
|
message.flags = placeholder.flags;
|
||||||
if (
|
if (
|
||||||
placeholder.parent_message &&
|
placeholder.parent_message &&
|
||||||
messages_by_id[placeholder.parent_message]
|
messages_by_id[placeholder.parent_message]
|
||||||
@@ -160,11 +169,29 @@ class TfNewsElement extends LitElement {
|
|||||||
return recursive_sort(roots, true);
|
return recursive_sort(roots, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
group_following(messages) {
|
group_messages(messages) {
|
||||||
let result = [];
|
let result = [];
|
||||||
let group = [];
|
let group = [];
|
||||||
|
let type = undefined;
|
||||||
for (let message of messages) {
|
for (let message of messages) {
|
||||||
if (message?.content?.type === 'contact') {
|
if (
|
||||||
|
message?.content?.type === 'contact' ||
|
||||||
|
message?.content?.type === 'channel'
|
||||||
|
) {
|
||||||
|
if (type && message.content.type !== type) {
|
||||||
|
if (group.length == 1) {
|
||||||
|
result.push(group[0]);
|
||||||
|
group = [];
|
||||||
|
} else if (group.length > 1) {
|
||||||
|
result.push({
|
||||||
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
|
type: `${type}_group`,
|
||||||
|
messages: group,
|
||||||
|
});
|
||||||
|
group = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = message.content.type;
|
||||||
group.push(message);
|
group.push(message);
|
||||||
} else {
|
} else {
|
||||||
if (group.length == 1) {
|
if (group.length == 1) {
|
||||||
@@ -173,12 +200,13 @@ class TfNewsElement extends LitElement {
|
|||||||
} else if (group.length > 1) {
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: `${type}_group`,
|
||||||
messages: group,
|
messages: group,
|
||||||
});
|
});
|
||||||
group = [];
|
group = [];
|
||||||
}
|
}
|
||||||
result.push(message);
|
result.push(message);
|
||||||
|
type = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (group.length == 1) {
|
if (group.length == 1) {
|
||||||
@@ -187,7 +215,7 @@ class TfNewsElement extends LitElement {
|
|||||||
} else if (group.length > 1) {
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: `${type}_group`,
|
||||||
messages: group,
|
messages: group,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -200,7 +228,7 @@ class TfNewsElement extends LitElement {
|
|||||||
|
|
||||||
load_and_render(messages) {
|
load_and_render(messages) {
|
||||||
let messages_by_id = this.process_messages(messages);
|
let messages_by_id = this.process_messages(messages);
|
||||||
let final_messages = this.group_following(
|
let final_messages = this.group_messages(
|
||||||
this.finalize_messages(messages_by_id)
|
this.finalize_messages(messages_by_id)
|
||||||
);
|
);
|
||||||
let unread_rowid = -1;
|
let unread_rowid = -1;
|
||||||
@@ -212,6 +240,9 @@ class TfNewsElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
|
<style>
|
||||||
|
${generate_theme()}
|
||||||
|
</style>
|
||||||
<div>
|
<div>
|
||||||
${repeat(
|
${repeat(
|
||||||
final_messages,
|
final_messages,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {LitElement, html, until, unsafeHTML} from './lit-all.min.js';
|
import {LitElement, html, until, unsafeHTML} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import * as tfutils from './tf-utils.js';
|
import * as tfutils from './tf-utils.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfProfileElement extends LitElement {
|
class TfProfileElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -37,16 +37,22 @@ class TfProfileElement extends LitElement {
|
|||||||
this.following = undefined;
|
this.following = undefined;
|
||||||
this.blocking = undefined;
|
this.blocking = undefined;
|
||||||
|
|
||||||
|
let latest = (
|
||||||
|
await tfrpc.rpc.query('SELECT MAX(rowid) AS latest FROM messages')
|
||||||
|
)[0].latest;
|
||||||
|
|
||||||
let result = await tfrpc.rpc.query(
|
let result = await tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
SELECT json_extract(content, '$.following') AS following
|
SELECT json_extract(content, '$.following') AS following
|
||||||
FROM messages WHERE author = ? AND
|
FROM messages WHERE author = ? AND
|
||||||
json_extract(content, '$.type') = 'contact' AND
|
json_extract(content, '$.type') = 'contact' AND
|
||||||
json_extract(content, '$.contact') = ? AND
|
json_extract(content, '$.contact') = ? AND
|
||||||
following IS NOT NULL
|
following IS NOT NULL AND
|
||||||
|
messages.rowid <= ?
|
||||||
ORDER BY sequence DESC LIMIT 1
|
ORDER BY sequence DESC LIMIT 1
|
||||||
`,
|
`,
|
||||||
[this.whoami, this.id]
|
[this.whoami, this.id, latest],
|
||||||
|
{cacheable: true}
|
||||||
);
|
);
|
||||||
this.following = result?.[0]?.following ?? false;
|
this.following = result?.[0]?.following ?? false;
|
||||||
result = await tfrpc.rpc.query(
|
result = await tfrpc.rpc.query(
|
||||||
@@ -55,10 +61,12 @@ class TfProfileElement extends LitElement {
|
|||||||
FROM messages WHERE author = ? AND
|
FROM messages WHERE author = ? AND
|
||||||
json_extract(content, '$.type') = 'contact' AND
|
json_extract(content, '$.type') = 'contact' AND
|
||||||
json_extract(content, '$.contact') = ? AND
|
json_extract(content, '$.contact') = ? AND
|
||||||
blocking IS NOT NULL
|
blocking IS NOT NULL AND
|
||||||
|
messages.rowid <= ?
|
||||||
ORDER BY sequence DESC LIMIT 1
|
ORDER BY sequence DESC LIMIT 1
|
||||||
`,
|
`,
|
||||||
[this.whoami, this.id]
|
[this.whoami, this.id, latest],
|
||||||
|
{cacheable: true}
|
||||||
);
|
);
|
||||||
this.blocking = result?.[0]?.blocking ?? false;
|
this.blocking = result?.[0]?.blocking ?? false;
|
||||||
}
|
}
|
||||||
@@ -238,7 +246,7 @@ class TfProfileElement extends LitElement {
|
|||||||
let profile = this.users[this.id] || {};
|
let profile = this.users[this.id] || {};
|
||||||
tfrpc.rpc
|
tfrpc.rpc
|
||||||
.query(
|
.query(
|
||||||
`SELECT SUM(LENGTH(content)) AS size, MAX(sequence) AS sequence FROM messages WHERE author = ?`,
|
`SELECT size AS size, max_sequence AS sequence FROM messages_stats WHERE author = ?`,
|
||||||
[this.id]
|
[this.id]
|
||||||
)
|
)
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
@@ -316,7 +324,9 @@ class TfProfileElement extends LitElement {
|
|||||||
}
|
}
|
||||||
image = this.editing?.image ?? image;
|
image = this.editing?.image ?? image;
|
||||||
let description = this.editing?.description ?? profile.description;
|
let description = this.editing?.description ?? profile.description;
|
||||||
return html`<div class="w3-card-4 w3-container w3-theme-d3" style="box-sizing: border-box">
|
return html`
|
||||||
|
<style>${generate_theme()}</style>
|
||||||
|
<div class="w3-card-4 w3-container w3-theme-d3" style="box-sizing: border-box">
|
||||||
<header class="w3-container">
|
<header class="w3-container">
|
||||||
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfReactionsModalElement extends LitElement {
|
class TfReactionsModalElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -24,50 +24,57 @@ class TfReactionsModalElement extends LitElement {
|
|||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
return this.votes?.length
|
return this.votes?.length
|
||||||
? html` <div
|
? html` <style>
|
||||||
class="w3-modal w3-animate-opacity"
|
${generate_theme()}
|
||||||
style="display: block; box-sizing: border-box; z-index: 10"
|
</style>
|
||||||
@click=${this.clear}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="w3-modal-content w3-card-4 w3-theme-d1"
|
class="w3-modal w3-animate-opacity"
|
||||||
onclick="event.stopPropagation()"
|
style="display: block; box-sizing: border-box; z-index: 10"
|
||||||
|
@click=${this.clear}
|
||||||
>
|
>
|
||||||
<div class="w3-container w3-padding">
|
<div
|
||||||
<header class="w3-container">
|
class="w3-modal-content w3-card-4 w3-theme-d1"
|
||||||
<h2>Reactions</h2>
|
onclick="event.stopPropagation()"
|
||||||
<span class="w3-button w3-display-topright" @click=${this.clear}
|
>
|
||||||
>×</span
|
<div class="w3-container w3-padding">
|
||||||
>
|
<header class="w3-container">
|
||||||
</header>
|
<h2>Reactions</h2>
|
||||||
<ul class="w3-theme-dark w3-container w3-ul">
|
<span
|
||||||
${this.votes
|
class="w3-button w3-display-topright"
|
||||||
.sort((x, y) => y.timestamp - x.timestamp)
|
@click=${this.clear}
|
||||||
.map(
|
>×</span
|
||||||
(x) => html`
|
>
|
||||||
<li style="display: flex; flex-direction: row; gap: 4px">
|
</header>
|
||||||
<span style="flex-basis: 3em"
|
<ul class="w3-theme-dark w3-container w3-ul">
|
||||||
>${x?.content?.vote?.expression}</span
|
${this.votes
|
||||||
|
.sort((x, y) => y.timestamp - x.timestamp)
|
||||||
|
.map(
|
||||||
|
(x) => html`
|
||||||
|
<li
|
||||||
|
style="display: flex; flex-direction: row; gap: 4px"
|
||||||
>
|
>
|
||||||
<tf-user
|
<span style="flex-basis: 3em"
|
||||||
style="flex: 1 1"
|
>${x?.content?.vote?.expression}</span
|
||||||
id=${x.author}
|
>
|
||||||
.users=${this.users}
|
<tf-user
|
||||||
></tf-user>
|
style="flex: 1 1; overflow: hidden"
|
||||||
<span
|
id=${x.author}
|
||||||
style="flex-shrink: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
.users=${this.users}
|
||||||
>${new Date(x?.timestamp).toLocaleString()}</span
|
></tf-user>
|
||||||
>
|
<span
|
||||||
</li>
|
style="flex-shrink: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
||||||
`
|
>${new Date(x?.timestamp).toLocaleString()}</span
|
||||||
)}
|
>
|
||||||
</ul>
|
</li>
|
||||||
<footer class="w3-container w3-padding">
|
`
|
||||||
<button class="w3-button" @click=${this.clear}>Close</button>
|
)}
|
||||||
</footer>
|
</ul>
|
||||||
|
<footer class="w3-container w3-padding">
|
||||||
|
<button class="w3-button" @click=${this.clear}>Close</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>`
|
||||||
</div>`
|
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {css, unsafeCSS} from './lit-all.min.js';
|
import {css, unsafeCSS, until} from './lit-all.min.js';
|
||||||
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
|
|
||||||
const tf = css`
|
const tf = css`
|
||||||
img {
|
img {
|
||||||
@@ -408,16 +409,8 @@ function is_dark(hex, value) {
|
|||||||
return (r * 299 + g * 587 + b * 114) / 1000 < value;
|
return (r * 299 + g * 587 + b * 114) / 1000 < value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generated() {
|
export function generate(color) {
|
||||||
let now = new Date();
|
let [r, g, b] = hex_to_rgb(color);
|
||||||
let k_color = rgb_to_hex([
|
|
||||||
(now.getDay() * 128) / 6,
|
|
||||||
(now.getHours() * 128) / 23,
|
|
||||||
(now.getSeconds() * 128) / 59,
|
|
||||||
]);
|
|
||||||
//let k_color = '#034f84';
|
|
||||||
//let k_color = rgb_to_hex([Math.random() * 256, Math.random() * 256, Math.random() * 256]);
|
|
||||||
let [r, g, b] = hex_to_rgb(k_color);
|
|
||||||
let [h, s, l] = rgb_to_hsl(r, g, b);
|
let [h, s, l] = rgb_to_hsl(r, g, b);
|
||||||
|
|
||||||
let theme1 = {
|
let theme1 = {
|
||||||
@@ -461,4 +454,28 @@ function generated() {
|
|||||||
return unsafeCSS(result);
|
return unsafeCSS(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
export let styles = [tf, w3, generated()];
|
let g_theme;
|
||||||
|
export function generate_theme() {
|
||||||
|
return g_theme
|
||||||
|
? g_theme
|
||||||
|
: until(
|
||||||
|
tfrpc.rpc.localStorageGet('color').then(function (value) {
|
||||||
|
g_theme = generate(value ?? '#034f84');
|
||||||
|
return g_theme;
|
||||||
|
}),
|
||||||
|
generated_now()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generated_now() {
|
||||||
|
let now = new Date();
|
||||||
|
return generate(
|
||||||
|
rgb_to_hex([
|
||||||
|
(now.getDay() * 128) / 6,
|
||||||
|
(now.getHours() * 128) / 23,
|
||||||
|
(now.getSeconds() * 128) / 59,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export let styles = [tf, w3];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {LitElement, html} from './lit-all.min.js';
|
import {LitElement, html} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfTabConnectionsElement extends LitElement {
|
class TfTabConnectionsElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -15,6 +15,7 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
connect_attempt: {type: Object},
|
connect_attempt: {type: Object},
|
||||||
connect_message: {type: String},
|
connect_message: {type: String},
|
||||||
connect_success: {type: Boolean},
|
connect_success: {type: Boolean},
|
||||||
|
peer_exchange: {type: Boolean},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +48,20 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
tfrpc.rpc.getServerIdentity().then(function (identity) {
|
tfrpc.rpc.getServerIdentity().then(function (identity) {
|
||||||
self.server_identity = identity;
|
self.server_identity = identity;
|
||||||
});
|
});
|
||||||
|
this.check_peer_exchange();
|
||||||
|
}
|
||||||
|
|
||||||
|
async check_peer_exchange() {
|
||||||
|
if (await tfrpc.rpc.isAdministrator()) {
|
||||||
|
this.peer_exchange = await tfrpc.rpc.globalSettingsGet('peer_exchange');
|
||||||
|
} else {
|
||||||
|
this.peer_exchange = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async enable_peer_exchange() {
|
||||||
|
await tfrpc.rpc.globalSettingsSet('peer_exchange', true);
|
||||||
|
await this.check_peer_exchange();
|
||||||
}
|
}
|
||||||
|
|
||||||
render_connection_summary(connection) {
|
render_connection_summary(connection) {
|
||||||
@@ -251,7 +266,26 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
return html`
|
return html`
|
||||||
|
<style>
|
||||||
|
${generate_theme()}
|
||||||
|
</style>
|
||||||
<div class="w3-container" style="box-sizing: border-box">
|
<div class="w3-container" style="box-sizing: border-box">
|
||||||
|
<div
|
||||||
|
class=${'w3-panel w3-padding w3-theme-l3' +
|
||||||
|
(this.peer_exchange !== false ? ' w3-hide' : '')}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Looking for connections? Enabling this option will include publicly
|
||||||
|
advertised rooms and pubs among the list of discovered connections
|
||||||
|
to help you replicate.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1"
|
||||||
|
@click=${this.enable_peer_exchange}
|
||||||
|
>
|
||||||
|
🔍🌐 Use publicly advertised peers
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<h2>New Connection</h2>
|
<h2>New Connection</h2>
|
||||||
<textarea class="w3-input w3-theme-d1" id="code"></textarea>
|
<textarea class="w3-input w3-theme-d1" id="code"></textarea>
|
||||||
${this.render_message(this.renderRoot.getElementById('code')?.value)}
|
${this.render_message(this.renderRoot.getElementById('code')?.value)}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {LitElement, cache, html, unsafeHTML, until} from './lit-all.min.js';
|
import {LitElement, cache, html, unsafeHTML, until} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfTabNewsFeedElement extends LitElement {
|
class TfTabNewsFeedElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -84,7 +84,6 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
`,
|
`,
|
||||||
[JSON.stringify(combined.map((x) => x.id))]
|
[JSON.stringify(combined.map((x) => x.id))]
|
||||||
);
|
);
|
||||||
let t0 = new Date();
|
|
||||||
let result = [].concat(
|
let result = [].concat(
|
||||||
combined,
|
combined,
|
||||||
await tfrpc.rpc.query(
|
await tfrpc.rpc.query(
|
||||||
@@ -101,8 +100,6 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let t1 = new Date();
|
|
||||||
console.log((t1 - t0) / 1000);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +117,11 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
result = await tfrpc.rpc.query(
|
result = await tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
WITH mentions AS (SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
WITH mentions AS (SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
FROM messages_fts(?1)
|
FROM messages_refs
|
||||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
JOIN messages ON messages.id = messages_refs.message
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
WHERE
|
WHERE
|
||||||
|
messages_refs.ref = ?1 AND
|
||||||
messages.author != ?1 AND
|
messages.author != ?1 AND
|
||||||
(?3 IS NULL OR messages.timestamp >= ?3) AND messages.timestamp < ?4
|
(?3 IS NULL OR messages.timestamp >= ?3) AND messages.timestamp < ?4
|
||||||
ORDER BY timestamp DESC limit ?5)
|
ORDER BY timestamp DESC limit ?5)
|
||||||
@@ -135,7 +133,7 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
SELECT TRUE AS is_primary, * FROM mentions
|
SELECT TRUE AS is_primary, * FROM mentions
|
||||||
`,
|
`,
|
||||||
[
|
[
|
||||||
'"' + this.whoami.replace('"', '""') + '"',
|
this.whoami,
|
||||||
JSON.stringify(this.following),
|
JSON.stringify(this.following),
|
||||||
start_time,
|
start_time,
|
||||||
end_time,
|
end_time,
|
||||||
@@ -175,7 +173,6 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
[this.hash.substring(1)]
|
[this.hash.substring(1)]
|
||||||
);
|
);
|
||||||
} else if (this.hash.startsWith('##')) {
|
} else if (this.hash.startsWith('##')) {
|
||||||
let t0 = new Date();
|
|
||||||
let initial_messages = await tfrpc.rpc.query(
|
let initial_messages = await tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
WITH
|
WITH
|
||||||
@@ -203,12 +200,7 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
k_max_results,
|
k_max_results,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
let t1 = new Date();
|
|
||||||
result = await this._fetch_related_messages(initial_messages);
|
result = await this._fetch_related_messages(initial_messages);
|
||||||
let t2 = new Date();
|
|
||||||
console.log(
|
|
||||||
`load of ${result.length} rows took ${(t2 - t0) / 1000} (${(t1 - t0) / 1000} to find ${initial_messages.length} initial messages, ${(t2 - t1) / 1000} to find ${result.length} total messages) following=${this.following.length} st=${start_time} et=${end_time}`
|
|
||||||
);
|
|
||||||
} else if (this.hash.startsWith('#🔐')) {
|
} else if (this.hash.startsWith('#🔐')) {
|
||||||
let ids =
|
let ids =
|
||||||
this.hash == '#🔐' ? [] : this.hash.substring('#🔐'.length).split(',');
|
this.hash == '#🔐' ? [] : this.hash.substring('#🔐'.length).split(',');
|
||||||
@@ -233,7 +225,8 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
k_max_results,
|
k_max_results,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
let decrypted = (await this.decrypt(result)).filter((x) => x.decrypted);
|
||||||
|
result = await this._fetch_related_messages(decrypted);
|
||||||
} else if (this.hash == '#👍') {
|
} else if (this.hash == '#👍') {
|
||||||
result = await tfrpc.rpc.query(
|
result = await tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
@@ -252,25 +245,50 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
`,
|
`,
|
||||||
[JSON.stringify(this.following), start_time, end_time, k_max_results]
|
[JSON.stringify(this.following), start_time, end_time, k_max_results]
|
||||||
);
|
);
|
||||||
|
} else if (this.hash == '#🚩') {
|
||||||
|
result = await tfrpc.rpc.query(
|
||||||
|
`
|
||||||
|
WITH flags AS (SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
|
FROM messages
|
||||||
|
WHERE
|
||||||
|
messages.content ->> 'type' = 'flag' AND
|
||||||
|
(?1 IS NULL OR messages.timestamp >= ?1) AND messages.timestamp < ?2
|
||||||
|
ORDER BY timestamp DESC limit ?3)
|
||||||
|
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
|
FROM flags
|
||||||
|
JOIN messages ON messages.id = flags.content ->> '$.flag.link'
|
||||||
|
UNION
|
||||||
|
SELECT TRUE AS is_primary, * FROM flags
|
||||||
|
`,
|
||||||
|
[start_time, end_time, k_max_results]
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
let t0 = new Date();
|
|
||||||
let initial_messages = await tfrpc.rpc.query(
|
let initial_messages = await tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
|
WITH
|
||||||
|
channels AS (SELECT '#' || value AS value FROM json_each(?5))
|
||||||
SELECT TRUE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
SELECT TRUE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
FROM messages
|
FROM messages
|
||||||
JOIN json_each(?) AS following ON messages.author = following.value
|
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||||
WHERE messages.timestamp < ?3 AND (?2 IS NULL OR messages.timestamp >= ?2) AND
|
WHERE messages.timestamp < ?3 AND (?2 IS NULL OR messages.timestamp >= ?2) AND
|
||||||
messages.content ->> 'type' != 'vote'
|
messages.content ->> 'type' != 'vote' AND
|
||||||
|
(messages.content ->> 'root' IS NULL OR (
|
||||||
|
NOT EXISTS (SELECT * FROM messages root JOIN channels ON ('#' || (root.content ->> 'channel')) = channels.value WHERE root.id = messages.content ->> 'root') AND
|
||||||
|
NOT EXISTS (SELECT * FROM messages root JOIN messages_refs ON root.id = messages.content ->> 'root' JOIN channels ON messages_refs.message = root.id AND messages_refs.ref = channels.value)
|
||||||
|
)) AND
|
||||||
|
(messages.content ->> 'channel' IS NULL OR ('#' || (messages.content ->> 'channel')) NOT IN (SELECT * FROM channels)) AND
|
||||||
|
NOT EXISTS (SELECT * FROM messages_refs JOIN channels ON messages_refs.message = messages.id AND messages_refs.ref = channels.value)
|
||||||
ORDER BY timestamp DESC LIMIT ?4
|
ORDER BY timestamp DESC LIMIT ?4
|
||||||
`,
|
`,
|
||||||
[JSON.stringify(this.following), start_time, end_time, k_max_results]
|
[
|
||||||
|
JSON.stringify(this.following),
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
k_max_results,
|
||||||
|
JSON.stringify(Object.keys(this.channels_latest)),
|
||||||
|
]
|
||||||
);
|
);
|
||||||
let t1 = new Date();
|
|
||||||
result = await this._fetch_related_messages(initial_messages);
|
result = await this._fetch_related_messages(initial_messages);
|
||||||
let t2 = new Date();
|
|
||||||
console.log(
|
|
||||||
`load of ${result.length} rows took ${(t2 - t0) / 1000} (${(t1 - t0) / 1000} to find ${initial_messages.length} initial messages, ${(t2 - t1) / 1000} to find ${result.length} total messages) following=${this.following.length} st=${start_time} et=${end_time}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
this.time_loading = undefined;
|
this.time_loading = undefined;
|
||||||
return result;
|
return result;
|
||||||
@@ -378,7 +396,6 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
console.log('done loading latest messages.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async load_messages() {
|
async load_messages() {
|
||||||
@@ -393,9 +410,13 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
this._messages_hash = this.hash;
|
this._messages_hash = this.hash;
|
||||||
}
|
}
|
||||||
this._messages_following = JSON.stringify(this.following);
|
this._messages_following = JSON.stringify(this.following);
|
||||||
this._private_messages =
|
this._private_messages = JSON.stringify([
|
||||||
JSON.stringify(this.private_messages) +
|
this.private_messages,
|
||||||
JSON.stringify(this.grouped_private_messages);
|
this.grouped_private_messages,
|
||||||
|
]);
|
||||||
|
this._channels_latest = JSON.stringify(
|
||||||
|
Object.keys(this.channels_latest ?? {})
|
||||||
|
);
|
||||||
let now = new Date().valueOf();
|
let now = new Date().valueOf();
|
||||||
let start_time = now - 24 * 60 * 60 * 1000;
|
let start_time = now - 24 * 60 * 60 * 1000;
|
||||||
this.start_time = start_time;
|
this.start_time = start_time;
|
||||||
@@ -412,9 +433,6 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
this.messages = this.merge_messages(this.messages, messages);
|
this.messages = this.merge_messages(this.messages, messages);
|
||||||
}
|
}
|
||||||
this.time_loading = undefined;
|
this.time_loading = undefined;
|
||||||
console.log(
|
|
||||||
`loading ${messages.length} messages done for ${self.whoami} in ${(new Date() - start_time) / 1000}s`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_all_read() {
|
mark_all_read() {
|
||||||
@@ -470,11 +488,15 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
this._messages_hash !== this.hash ||
|
this._messages_hash !== this.hash ||
|
||||||
this._messages_following !== JSON.stringify(this.following) ||
|
this._messages_following !== JSON.stringify(this.following) ||
|
||||||
this._private_messages !==
|
this._private_messages !==
|
||||||
JSON.stringify(this.private_messages) +
|
JSON.stringify([
|
||||||
JSON.stringify(this.grouped_private_messages)
|
this.private_messages,
|
||||||
|
this.grouped_private_messages,
|
||||||
|
]) ||
|
||||||
|
this._channels_latest !==
|
||||||
|
JSON.stringify(Object.keys(this.channels_latest))
|
||||||
) {
|
) {
|
||||||
console.log(
|
console.log(
|
||||||
`loading messages for ${this.whoami} (following ${this.following.length})`
|
`loading messages for ${this.whoami} (messages=${!this.messages},${this._messages_hash != this.hash} following=${this._messages_following !== JSON.stringify(this.following)}, channels=${this._channels_latest !== JSON.stringify(Object.keys(this.channels_latest))}, private=${this._private_messages !== JSON.stringify([this.private_messages, this.grouped_private_messages])},${this.private_messages?.length},${Object.keys(this.grouped_private_messages ?? {}).length})`
|
||||||
);
|
);
|
||||||
this.load_messages();
|
this.load_messages();
|
||||||
}
|
}
|
||||||
@@ -523,6 +545,9 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return cache(html`
|
return cache(html`
|
||||||
|
<style>
|
||||||
|
${generate_theme()}
|
||||||
|
</style>
|
||||||
${this.unread_allowed()
|
${this.unread_allowed()
|
||||||
? html`<button
|
? html`<button
|
||||||
class="w3-button w3-theme-d1"
|
class="w3-button w3-theme-d1"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
until,
|
until,
|
||||||
} from './lit-all.min.js';
|
} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfTabNewsElement extends LitElement {
|
class TfTabNewsElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -25,6 +25,7 @@ class TfTabNewsElement extends LitElement {
|
|||||||
connections: {type: Array},
|
connections: {type: Array},
|
||||||
private_messages: {type: Array},
|
private_messages: {type: Array},
|
||||||
grouped_private_messages: {type: Object},
|
grouped_private_messages: {type: Object},
|
||||||
|
visible_private_messages: {type: Object},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
peer_exchange: {type: Boolean},
|
peer_exchange: {type: Boolean},
|
||||||
is_administrator: {type: Boolean},
|
is_administrator: {type: Boolean},
|
||||||
@@ -211,35 +212,6 @@ class TfTabNewsElement extends LitElement {
|
|||||||
>
|
>
|
||||||
×
|
×
|
||||||
</div>
|
</div>
|
||||||
${this.is_administrator
|
|
||||||
? html`
|
|
||||||
<button
|
|
||||||
class="w3-bar-item w3-button"
|
|
||||||
@click=${() =>
|
|
||||||
this.dispatchEvent(
|
|
||||||
new Event('refresh', {bubbles: true, composed: true})
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span style="display: inline-block; width: 1.8em">↻</span>
|
|
||||||
Sync now
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="w3-bar-item w3-button w3-ripple"
|
|
||||||
@click=${() =>
|
|
||||||
this.dispatchEvent(
|
|
||||||
new Event('toggle_stay_connected', {
|
|
||||||
bubbles: true,
|
|
||||||
composed: true,
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span style="display: inline-block; width: 1.8em"
|
|
||||||
>${this.stay_connected ? '🔗' : '⛓️💥'}</span
|
|
||||||
>
|
|
||||||
${this.stay_connected ? 'Online mode' : 'Passive mode'}
|
|
||||||
</button>
|
|
||||||
`
|
|
||||||
: undefined}
|
|
||||||
${this.hash.startsWith('##') &&
|
${this.hash.startsWith('##') &&
|
||||||
this.channels.indexOf(this.hash.substring(2)) == -1
|
this.channels.indexOf(this.hash.substring(2)) == -1
|
||||||
? html`
|
? html`
|
||||||
@@ -248,7 +220,7 @@ class TfTabNewsElement extends LitElement {
|
|||||||
href="#"
|
href="#"
|
||||||
class="w3-bar-item w3-button"
|
class="w3-bar-item w3-button"
|
||||||
style="font-weight: bold"
|
style="font-weight: bold"
|
||||||
>${this.hash.substring(2)}</a
|
>${this.hash.substring(1)}</a
|
||||||
>
|
>
|
||||||
`
|
`
|
||||||
: undefined}
|
: undefined}
|
||||||
@@ -271,7 +243,13 @@ class TfTabNewsElement extends LitElement {
|
|||||||
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
||||||
>${this.unread_status('👍')}👍votes</a
|
>${this.unread_status('👍')}👍votes</a
|
||||||
>
|
>
|
||||||
${Object.keys(this?.grouped_private_messages ?? [])
|
<a
|
||||||
|
href="#🚩"
|
||||||
|
class="w3-bar-item w3-button"
|
||||||
|
style=${this.hash == '#🚩' ? 'font-weight: bold' : undefined}
|
||||||
|
>${this.unread_status('🚩')}🚩flagged</a
|
||||||
|
>
|
||||||
|
${Object.keys(this?.visible_private_messages ?? [])
|
||||||
?.sort()
|
?.sort()
|
||||||
?.map(
|
?.map(
|
||||||
(key) => html`
|
(key) => html`
|
||||||
@@ -335,11 +313,26 @@ class TfTabNewsElement extends LitElement {
|
|||||||
↻ Sync now
|
↻ Sync now
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class=${'w3-bar-item w3-button' +
|
class="w3-bar-item w3-button w3-ripple"
|
||||||
|
@click=${() =>
|
||||||
|
this.dispatchEvent(
|
||||||
|
new Event('toggle_stay_connected', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span style="display: inline-block; width: 1.8em"
|
||||||
|
>${this.stay_connected ? '🔗' : '⛓️💥'}</span
|
||||||
|
>
|
||||||
|
${this.stay_connected ? 'Online mode' : 'Passive mode'}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class=${'w3-bar-item w3-button w3-border w3-leftbar w3-rightbar' +
|
||||||
(this.peer_exchange !== false ? ' w3-hide' : '')}
|
(this.peer_exchange !== false ? ' w3-hide' : '')}
|
||||||
@click=${this.enable_peer_exchange}
|
@click=${this.enable_peer_exchange}
|
||||||
>
|
>
|
||||||
Enable peer exchange
|
🔍🌐 Use publicly advertised peers
|
||||||
</button>
|
</button>
|
||||||
`
|
`
|
||||||
: undefined}
|
: undefined}
|
||||||
@@ -409,9 +402,12 @@ class TfTabNewsElement extends LitElement {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
return cache(html`
|
return cache(html`
|
||||||
|
<style>
|
||||||
|
${generate_theme()}
|
||||||
|
</style>
|
||||||
${this.render_sidebar()}
|
${this.render_sidebar()}
|
||||||
<div
|
<div
|
||||||
style="margin-left: 2in; padding: 0px; top: 0; max-height: 100%; overflow: auto; contain: layout"
|
style="margin-left: 2in; padding: 0px; top: 0; height: 100vh; max-height: 100%; overflow: auto; contain: layout"
|
||||||
id="main"
|
id="main"
|
||||||
class="w3-main"
|
class="w3-main"
|
||||||
>
|
>
|
||||||
@@ -438,7 +434,12 @@ class TfTabNewsElement extends LitElement {
|
|||||||
>
|
>
|
||||||
${this.unread_status()}☰
|
${this.unread_status()}☰
|
||||||
</div>
|
</div>
|
||||||
Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!
|
<span
|
||||||
|
style="display: inline-block; width: 100%; max-width: 100%; white-space: nowrap; overflow: hidden"
|
||||||
|
>
|
||||||
|
Welcome,
|
||||||
|
<tf-user id=${this.whoami} .users=${this.users}></tf-user>!
|
||||||
|
</span>
|
||||||
${edit_profile}
|
${edit_profile}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,136 +0,0 @@
|
|||||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
|
||||||
import {styles} from './tf-styles.js';
|
|
||||||
|
|
||||||
class TfTabQueryElement extends LitElement {
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
whoami: {type: String},
|
|
||||||
users: {type: Object},
|
|
||||||
following: {type: Array},
|
|
||||||
query: {type: String},
|
|
||||||
expanded: {type: Object},
|
|
||||||
results: {type: Array},
|
|
||||||
error: {type: Object},
|
|
||||||
duration: {type: Number},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = styles;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
let self = this;
|
|
||||||
this.whoami = null;
|
|
||||||
this.users = {};
|
|
||||||
this.following = [];
|
|
||||||
this.expanded = {};
|
|
||||||
this.duration = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
async search(query) {
|
|
||||||
console.log('Searching...', this.whoami, query);
|
|
||||||
this.results = [];
|
|
||||||
this.error = undefined;
|
|
||||||
this.duration = undefined;
|
|
||||||
let search = this.renderRoot.getElementById('search');
|
|
||||||
if (search) {
|
|
||||||
search.value = query;
|
|
||||||
search.focus();
|
|
||||||
}
|
|
||||||
await tfrpc.rpc.setHash('#sql=' + encodeURIComponent(query));
|
|
||||||
let start_time = new Date();
|
|
||||||
try {
|
|
||||||
this.results = await tfrpc.rpc.query(query, []);
|
|
||||||
} catch (error) {
|
|
||||||
this.error = error;
|
|
||||||
}
|
|
||||||
let end_time = new Date();
|
|
||||||
this.duration = (end_time - start_time).valueOf();
|
|
||||||
console.log('Done.');
|
|
||||||
search = this.renderRoot.getElementById('search');
|
|
||||||
if (search) {
|
|
||||||
search.value = query;
|
|
||||||
search.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
search_keydown(event) {
|
|
||||||
if (event.keyCode == 13 && event.ctrlKey) {
|
|
||||||
this.query = this.renderRoot.getElementById('search').value;
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
on_expand(event) {
|
|
||||||
if (event.detail.expanded) {
|
|
||||||
let expand = {};
|
|
||||||
expand[event.detail.id] = true;
|
|
||||||
this.expanded = Object.assign({}, this.expanded, expand);
|
|
||||||
} else {
|
|
||||||
delete this.expanded[event.detail.id];
|
|
||||||
this.expanded = Object.assign({}, this.expanded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render_results() {
|
|
||||||
if (!this.results?.length) {
|
|
||||||
return html`<div>No results.</div>`;
|
|
||||||
} else {
|
|
||||||
let keys = Object.keys(this.results[0]).sort();
|
|
||||||
return html`<table style="width: 100%; max-width: 100%">
|
|
||||||
<tr>
|
|
||||||
${keys.map((key) => html`<th>${key}</th>`)}
|
|
||||||
</tr>
|
|
||||||
${this.results.map(
|
|
||||||
(row) =>
|
|
||||||
html`<tr>
|
|
||||||
${keys.map((key) => html`<td>${row[key]}</td>`)}
|
|
||||||
</tr>`
|
|
||||||
)}
|
|
||||||
</table>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render_error() {
|
|
||||||
if (this.error) {
|
|
||||||
return html`<h2 style="color: red">${this.error.message}</h2>
|
|
||||||
<pre style="color: red">${this.error.stack}</pre>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.query !== this.last_query) {
|
|
||||||
this.last_query = this.query;
|
|
||||||
this.search(this.query);
|
|
||||||
}
|
|
||||||
let self = this;
|
|
||||||
return html`
|
|
||||||
<div style="display: flex; flex-direction: row; gap: 4px">
|
|
||||||
<textarea
|
|
||||||
id="search"
|
|
||||||
rows="8"
|
|
||||||
class="w3-input w3-theme-d1"
|
|
||||||
style="flex: 1; resize: vertical"
|
|
||||||
@keydown=${this.search_keydown}
|
|
||||||
>
|
|
||||||
${this.query}</textarea
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="w3-button w3-theme-d1"
|
|
||||||
@click=${(event) =>
|
|
||||||
self.search(self.renderRoot.getElementById('search').value)}
|
|
||||||
>
|
|
||||||
Execute
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div ?hidden=${this.duration === undefined}>
|
|
||||||
Took ${this.duration / 1000.0} seconds.
|
|
||||||
</div>
|
|
||||||
<div ?hidden=${this.duration !== undefined}>Executing...</div>
|
|
||||||
${this.render_error()} ${this.render_results()}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('tf-tab-query', TfTabQueryElement);
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfTabSearchElement extends LitElement {
|
class TfTabSearchElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -11,6 +11,9 @@ class TfTabSearchElement extends LitElement {
|
|||||||
following: {type: Array},
|
following: {type: Array},
|
||||||
query: {type: String},
|
query: {type: String},
|
||||||
expanded: {type: Object},
|
expanded: {type: Object},
|
||||||
|
messages: {type: Array},
|
||||||
|
results: {type: Array},
|
||||||
|
error: {type: Object},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,29 +41,39 @@ class TfTabSearchElement extends LitElement {
|
|||||||
search.select();
|
search.select();
|
||||||
}
|
}
|
||||||
await tfrpc.rpc.setHash('#q=' + encodeURIComponent(query));
|
await tfrpc.rpc.setHash('#q=' + encodeURIComponent(query));
|
||||||
let results = await tfrpc.rpc.query(
|
this.error = undefined;
|
||||||
`
|
this.results = [];
|
||||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
this.messages = [];
|
||||||
FROM messages_fts(?)
|
if (query.startsWith('sql:')) {
|
||||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
this.messages = [];
|
||||||
JOIN json_each(?) AS following ON messages.author = following.value
|
try {
|
||||||
ORDER BY timestamp DESC limit 100
|
this.results = await tfrpc.rpc.query(
|
||||||
`,
|
query.substring('sql:'.length),
|
||||||
['"' + query.replace('"', '""') + '"', JSON.stringify(this.following)]
|
[]
|
||||||
);
|
);
|
||||||
console.log('Done.');
|
} catch (e) {
|
||||||
search = this.renderRoot.getElementById('search');
|
this.results = [];
|
||||||
if (search) {
|
this.error = e;
|
||||||
search.value = query;
|
}
|
||||||
search.focus();
|
} else {
|
||||||
search.select();
|
let results = await tfrpc.rpc.query(
|
||||||
}
|
`
|
||||||
this.renderRoot.getElementById('news').messages = results;
|
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
}
|
FROM messages_fts(?)
|
||||||
|
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||||
search_keydown(event) {
|
JOIN json_each(?) AS following ON messages.author = following.value
|
||||||
if (event.keyCode == 13) {
|
ORDER BY timestamp DESC limit 100
|
||||||
this.query = this.renderRoot.getElementById('search').value;
|
`,
|
||||||
|
['"' + query.replace('"', '""') + '"', JSON.stringify(this.following)]
|
||||||
|
);
|
||||||
|
console.log('Done.');
|
||||||
|
search = this.renderRoot.getElementById('search');
|
||||||
|
if (search) {
|
||||||
|
search.value = query;
|
||||||
|
search.focus();
|
||||||
|
search.select();
|
||||||
|
}
|
||||||
|
this.messages = results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +100,39 @@ class TfTabSearchElement extends LitElement {
|
|||||||
tfrpc.rpc.localStorageSet('drafts', JSON.stringify(this.drafts));
|
tfrpc.rpc.localStorageSet('drafts', JSON.stringify(this.drafts));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render_results() {
|
||||||
|
if (this.error) {
|
||||||
|
return html`<h2 style="color: red">${this.error.message}</h2>
|
||||||
|
<pre style="color: red">${this.error.stack}</pre>`;
|
||||||
|
} else if (this.messages?.length) {
|
||||||
|
return html`<tf-news
|
||||||
|
id="news"
|
||||||
|
whoami=${this.whoami}
|
||||||
|
.messages=${this.messages}
|
||||||
|
.users=${this.users}
|
||||||
|
.expanded=${this.expanded}
|
||||||
|
.drafts=${this.drafts}
|
||||||
|
@tf-expand=${this.on_expand}
|
||||||
|
@tf-draft=${this.draft}
|
||||||
|
></tf-news>`;
|
||||||
|
} else if (this.results?.length) {
|
||||||
|
let keys = Object.keys(this.results[0]).sort();
|
||||||
|
return html`<table style="width: 100%; max-width: 100%">
|
||||||
|
<tr>
|
||||||
|
${keys.map((key) => html`<th>${key}</th>`)}
|
||||||
|
</tr>
|
||||||
|
${this.results.map(
|
||||||
|
(row) =>
|
||||||
|
html`<tr>
|
||||||
|
${keys.map((key) => html`<td>${row[key]}</td>`)}
|
||||||
|
</tr>`
|
||||||
|
)}
|
||||||
|
</table>`;
|
||||||
|
} else {
|
||||||
|
return html`<div>No results.</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.query !== this.last_query) {
|
if (this.query !== this.last_query) {
|
||||||
this.last_query = this.query;
|
this.last_query = this.query;
|
||||||
@@ -94,11 +140,10 @@ class TfTabSearchElement extends LitElement {
|
|||||||
}
|
}
|
||||||
let self = this;
|
let self = this;
|
||||||
return html`
|
return html`
|
||||||
<div style="display: flex; flex-direction: row; gap: 4px">
|
<style>
|
||||||
<input type="text" class="w3-input w3-theme-d1" id="search" value=${this.query} style="flex: 1" @keydown=${this.search_keydown}></input>
|
${generate_theme()}
|
||||||
<button class="w3-button w3-theme-d1" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}>Search</button>
|
</style>
|
||||||
</div>
|
<div class="w3-padding">${this.render_results()}</div>
|
||||||
<tf-news id="news" whoami=${this.whoami} .messages=${this.messages} .users=${this.users} .expanded=${this.expanded} .drafts=${this.drafts} @tf-expand=${this.on_expand} @tf-draft=${this.draft}></tf-news>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfTagElement extends LitElement {
|
class TfTagElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -17,11 +17,15 @@ class TfTagElement extends LitElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
let number = this.count ? html` (${this.count})` : undefined;
|
let number = this.count ? html` (${this.count})` : undefined;
|
||||||
return html`<a
|
return html`
|
||||||
href=${'#' + encodeURIComponent(this.tag)}
|
<style>
|
||||||
class="w3-tag w3-theme-d1 w3-round-4 w3-button"
|
${generate_theme()}</style
|
||||||
>${this.tag}${number}</a
|
><a
|
||||||
> `;
|
href=${'#' + encodeURIComponent(this.tag)}
|
||||||
|
class="w3-tag w3-theme-d1 w3-round-4 w3-button"
|
||||||
|
>${this.tag}${number}</a
|
||||||
|
>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {LitElement, html} from './lit-all.min.js';
|
import {LitElement, html} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles, generate_theme} from './tf-styles.js';
|
||||||
|
|
||||||
class TfUserElement extends LitElement {
|
class TfUserElement extends LitElement {
|
||||||
static get properties() {
|
static get properties() {
|
||||||
@@ -58,12 +58,15 @@ class TfUserElement extends LitElement {
|
|||||||
/>`;
|
/>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return html` <div
|
return html` <style>
|
||||||
style=${'display: inline-block; vertical-align: middle; text-wrap: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis' +
|
${generate_theme()}
|
||||||
(this.nolink ? '' : '; font-weight: bold')}
|
</style>
|
||||||
>
|
<div
|
||||||
${image} ${name}
|
style=${'display: inline-block; vertical-align: middle; text-wrap: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis' +
|
||||||
</div>`;
|
(this.nolink ? '' : '; font-weight: bold')}
|
||||||
|
>
|
||||||
|
${image} ${name}
|
||||||
|
</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,12 +104,12 @@ export function markdown(md) {
|
|||||||
node.destination.startsWith('@') &&
|
node.destination.startsWith('@') &&
|
||||||
node.destination.endsWith('.ed25519')
|
node.destination.endsWith('.ed25519')
|
||||||
) {
|
) {
|
||||||
node.destination = '#' + node.destination;
|
node.destination = '#' + encodeURIComponent(node.destination);
|
||||||
} else if (
|
} else if (
|
||||||
node.destination.startsWith('%') &&
|
node.destination.startsWith('%') &&
|
||||||
node.destination.endsWith('.sha256')
|
node.destination.endsWith('.sha256')
|
||||||
) {
|
) {
|
||||||
node.destination = '#' + node.destination;
|
node.destination = '#' + encodeURIComponent(node.destination);
|
||||||
} else if (
|
} else if (
|
||||||
node.destination.startsWith('&') &&
|
node.destination.startsWith('&') &&
|
||||||
node.destination.endsWith('.sha256')
|
node.destination.endsWith('.sha256')
|
||||||
|
|||||||
5
apps/trace.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"type": "tildefriends-app",
|
||||||
|
"emoji": "📦",
|
||||||
|
"previous": "&mhBOscDHiJ4VNnod27NOdRVC+4cXYZXIdYjsQBfmTYg=.sha256"
|
||||||
|
}
|
||||||
27
apps/trace/app.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
async function main() {
|
||||||
|
let speedscope_js = await utf8Decode(
|
||||||
|
getFile('speedscope/speedscope-432XE7GS.js')
|
||||||
|
);
|
||||||
|
speedscope_js = speedscope_js.replace(/alert\(`Cannot load.*?return/, '');
|
||||||
|
app.setDocument(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>speedscope</title>
|
||||||
|
<link rel="stylesheet" href="speedscope/speedscope-GHPHNKXC.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
delete window.localStorage;
|
||||||
|
window.location.hash = '#profileURL=${core.url}../../trace';
|
||||||
|
</script>
|
||||||
|
<script>${speedscope_js}</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
Before Width: | Height: | Size: 679 B After Width: | Height: | Size: 679 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -11,7 +11,7 @@
|
|||||||
<link rel="icon" type="image/x-icon" href="favicon-FOKUP5Y5.ico">
|
<link rel="icon" type="image/x-icon" href="favicon-FOKUP5Y5.ico">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script src="speedscope-HCR63FMT.js"></script>
|
<script src="speedscope-432XE7GS.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3
apps/trace/speedscope/release.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
speedscope@1.24.0
|
||||||
|
Mon Oct 20 18:11:29 PDT 2025
|
||||||
|
fc76932551754a442cd5c4f0afdba28032d14d8a
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "👋",
|
"emoji": "👋",
|
||||||
"previous": "&5NkMRSgcMqCYF3xcLOBmaytkoxfV9zx4br7JladKPTs=.sha256"
|
"previous": "&m7sF3iaAEqf6PtaTBN+upiHNNBPjrq15kZUuxRWiAag=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,10 @@
|
|||||||
<link rel="stylesheet" href="regular.min.css" />
|
<link rel="stylesheet" href="regular.min.css" />
|
||||||
<link rel="stylesheet" href="solid.min.css" />
|
<link rel="stylesheet" href="solid.min.css" />
|
||||||
<link rel="stylesheet" href="brands.min.css" />
|
<link rel="stylesheet" href="brands.min.css" />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
img {
|
img {
|
||||||
margin-bottom: -8px;
|
margin-bottom: -8px;
|
||||||
}
|
}
|
||||||
.mySlides {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<base target="_top" />
|
<base target="_top" />
|
||||||
</head>
|
</head>
|
||||||
@@ -104,14 +100,14 @@
|
|||||||
src="googleplay.svg"
|
src="googleplay.svg"
|
||||||
style="height: 2em; margin: 0"
|
style="height: 2em; margin: 0"
|
||||||
/>
|
/>
|
||||||
Get it on Google Play (Open Testing)
|
Get it on Google Play
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||||
href="https://testflight.apple.com/join/tXxgtSpE"
|
href="https://apps.apple.com/us/app/tilde-friends/id6742085604"
|
||||||
>
|
>
|
||||||
<img src="ios.svg" style="height: 2em; margin: 0" />
|
<img src="ios.svg" style="height: 2em; margin: 0" />
|
||||||
Get it on iOS (TestFlight)
|
Get it on iOS
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p>Just launch the app.</p>
|
<p>Just launch the app.</p>
|
||||||
@@ -298,7 +294,7 @@
|
|||||||
|
|
||||||
<!-- Technlology Section -->
|
<!-- Technlology Section -->
|
||||||
<div class="w3-container w3-padding-64 w3-light-grey w3-center">
|
<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>Built to Last</b></h1>
|
||||||
<p>
|
<p>
|
||||||
Tilde Friends strives to use only simple and widely adopted dependencies
|
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
|
in order to keep it easy to build for all sorts of platforms and
|
||||||
@@ -340,10 +336,6 @@
|
|||||||
<i class="fa fa-lock w3-text-purple w3-jumbo"></i>
|
<i class="fa fa-lock w3-text-purple w3-jumbo"></i>
|
||||||
<p>libsodium</p>
|
<p>libsodium</p>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/openssl/openssl/releases" class="w3-col s3">
|
|
||||||
<i class="fa fa-shield-halved w3-text-green w3-jumbo"></i>
|
|
||||||
<p>OpenSSL</p>
|
|
||||||
</a>
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/ianlancetaylor/libbacktrace"
|
href="https://github.com/ianlancetaylor/libbacktrace"
|
||||||
class="w3-col s3"
|
class="w3-col s3"
|
||||||
@@ -351,13 +343,13 @@
|
|||||||
<i class="fa fa-burst w3-text-pink w3-jumbo"></i>
|
<i class="fa fa-burst w3-text-pink w3-jumbo"></i>
|
||||||
<p>libbacktrace</p>
|
<p>libbacktrace</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="w3-row" style="margin-top: 64px">
|
|
||||||
<a href="https://codemirror.net/docs/changelog/" class="w3-col s3">
|
<a href="https://codemirror.net/docs/changelog/" class="w3-col s3">
|
||||||
<i class="fa fa-keyboard w3-text-indigo w3-jumbo"></i>
|
<i class="fa fa-keyboard w3-text-indigo w3-jumbo"></i>
|
||||||
<p>CodeMirror</p>
|
<p>CodeMirror</p>
|
||||||
</a>
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w3-row" style="margin-top: 64px">
|
||||||
<a href="https://github.com/jlfwong/speedscope/" class="w3-col s3">
|
<a href="https://github.com/jlfwong/speedscope/" class="w3-col s3">
|
||||||
<i class="fa fa-microscope w3-text-orange w3-jumbo"></i>
|
<i class="fa fa-microscope w3-text-orange w3-jumbo"></i>
|
||||||
<p>Speedscope</p>
|
<p>Speedscope</p>
|
||||||
@@ -370,9 +362,6 @@
|
|||||||
<i class="fa fa-book-atlas w3-text-purple w3-jumbo"></i>
|
<i class="fa fa-book-atlas w3-text-purple w3-jumbo"></i>
|
||||||
<p>c-ares</p>
|
<p>c-ares</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="w3-row" style="margin-top: 64px">
|
|
||||||
<a href="https://www.gnu.org/software/make/" class="w3-col s3">
|
<a href="https://www.gnu.org/software/make/" class="w3-col s3">
|
||||||
<i class="fa fa-hammer w3-text-teal w3-jumbo"></i>
|
<i class="fa fa-hammer w3-text-teal w3-jumbo"></i>
|
||||||
<p>GNU Make</p>
|
<p>GNU Make</p>
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ exports.app_socket = async function socket(request, response) {
|
|||||||
parentApp: parentApp,
|
parentApp: parentApp,
|
||||||
id: blobId,
|
id: blobId,
|
||||||
},
|
},
|
||||||
await ssb.getIdentityInfo(
|
await ssb_internal.getIdentityInfo(
|
||||||
credentials?.session?.name,
|
credentials?.session?.name,
|
||||||
packageOwner,
|
packageOwner,
|
||||||
packageName
|
packageName
|
||||||
|
|||||||
@@ -75,6 +75,10 @@
|
|||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
#code_of_conduct:has(>textarea:empty) {
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div style="display: flex; flex-direction: column; max-width: 1280px; margin: auto">
|
<div style="display: flex; flex-direction: column; max-width: 1280px; margin: auto">
|
||||||
<h1 ?hidden=${this.name}>Welcome.</h1>
|
<h1 ?hidden=${this.name}>Welcome.</h1>
|
||||||
@@ -126,8 +130,10 @@
|
|||||||
There is currently no administrator. You will be made administrator.
|
There is currently no administrator. You will be made administrator.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Code of Conduct</h2>
|
<div id="code_of_conduct">
|
||||||
<textarea readonly rows="20" cols="80" style="resize: none">${this.code_of_conduct}</textarea>
|
<h2>Code of Conduct</h2>
|
||||||
|
<textarea readonly rows="20" style="resize: none; width: 100%">${this.code_of_conduct}</textarea>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
264
core/client.js
@@ -9,18 +9,17 @@
|
|||||||
import {LitElement, html, css, svg} from '/lit/lit-all.min.js';
|
import {LitElement, html, css, svg} from '/lit/lit-all.min.js';
|
||||||
|
|
||||||
let cm6;
|
let cm6;
|
||||||
let gSocket;
|
|
||||||
|
|
||||||
let gCurrentFile;
|
let g_socket;
|
||||||
let gFiles = {};
|
let g_current_file;
|
||||||
let gApp = {files: {}, emoji: '📦'};
|
let g_files = {};
|
||||||
let gEditor;
|
let g_app = {files: {}, emoji: '📦'};
|
||||||
let gOriginalInput;
|
let g_editor;
|
||||||
let gUnloading;
|
let g_unloading;
|
||||||
|
|
||||||
let kErrorColor = '#dc322f';
|
let k_color_error = '#dc322f';
|
||||||
let kDisconnectColor = '#f00';
|
let k_color_disconnect = '#f00';
|
||||||
let kStatusColor = '#fff';
|
let k_color_status = '#fff';
|
||||||
/** \endcond */
|
/** \endcond */
|
||||||
|
|
||||||
/** Functions that server-side app code can call through the app object. */
|
/** Functions that server-side app code can call through the app object. */
|
||||||
@@ -30,7 +29,10 @@ const k_api = {
|
|||||||
error: {args: ['error'], func: api_error},
|
error: {args: ['error'], func: api_error},
|
||||||
localStorageSet: {args: ['key', 'value'], func: api_localStorageSet},
|
localStorageSet: {args: ['key', 'value'], func: api_localStorageSet},
|
||||||
localStorageGet: {args: ['key'], func: api_localStorageGet},
|
localStorageGet: {args: ['key'], func: api_localStorageGet},
|
||||||
requestPermission: {args: ['permission', 'id'], func: api_requestPermission},
|
requestPermission: {
|
||||||
|
args: ['permission', 'id', 'description'],
|
||||||
|
func: api_requestPermission,
|
||||||
|
},
|
||||||
print: {args: ['...'], func: api_print},
|
print: {args: ['...'], func: api_print},
|
||||||
setHash: {args: ['hash'], func: api_setHash},
|
setHash: {args: ['hash'], func: api_setHash},
|
||||||
};
|
};
|
||||||
@@ -370,16 +372,15 @@ class TfNavigationElement extends LitElement {
|
|||||||
>${this.version?.number}</span
|
>${this.version?.number}</span
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="w3-bar-item"
|
class="w3-bar-item w3-button w3-text-white"
|
||||||
accesskey="h"
|
accesskey="h"
|
||||||
@mouseover=${set_access_key_title}
|
@mouseover=${set_access_key_title}
|
||||||
data-tip="Open home app."
|
data-tip="Open home app."
|
||||||
href="/"
|
href="/"
|
||||||
style="color: #fff; white-space: nowrap"
|
|
||||||
>TF</a
|
>TF</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="w3-bar-item"
|
class="w3-bar-item w3-button w3-text-light-gray"
|
||||||
accesskey="a"
|
accesskey="a"
|
||||||
@mouseover=${set_access_key_title}
|
@mouseover=${set_access_key_title}
|
||||||
data-tip="Open apps list."
|
data-tip="Open apps list."
|
||||||
@@ -387,7 +388,7 @@ class TfNavigationElement extends LitElement {
|
|||||||
>apps</a
|
>apps</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="w3-bar-item"
|
class="w3-bar-item w3-button w3-text-light-gray"
|
||||||
accesskey="e"
|
accesskey="e"
|
||||||
@mouseover=${set_access_key_title}
|
@mouseover=${set_access_key_title}
|
||||||
data-tip="Toggle the app editor."
|
data-tip="Toggle the app editor."
|
||||||
@@ -396,7 +397,7 @@ class TfNavigationElement extends LitElement {
|
|||||||
>edit</a
|
>edit</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="w3-bar-item"
|
class="w3-bar-item w3-button"
|
||||||
accesskey="p"
|
accesskey="p"
|
||||||
@mouseover=${set_access_key_title}
|
@mouseover=${set_access_key_title}
|
||||||
data-tip="View and change permissions."
|
data-tip="View and change permissions."
|
||||||
@@ -410,7 +411,7 @@ class TfNavigationElement extends LitElement {
|
|||||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||||
<div
|
<div
|
||||||
class="w3-bar-item"
|
class="w3-bar-item"
|
||||||
style="color: ${this.status.color ?? kStatusColor}"
|
style="color: ${this.status.color ?? k_color_status}"
|
||||||
>
|
>
|
||||||
${this.status.message}
|
${this.status.message}
|
||||||
</div>
|
</div>
|
||||||
@@ -429,7 +430,7 @@ class TfNavigationElement extends LitElement {
|
|||||||
<div class="w3-model w3-animate-top" style="position: absolute; left: 50%; transform: translate(-50%); z-index: 1">
|
<div class="w3-model w3-animate-top" style="position: absolute; left: 50%; transform: translate(-50%); z-index: 1">
|
||||||
<dijv class="w3-modal-content w3-card-4" style="display: block; padding: 1em">
|
<dijv class="w3-modal-content w3-card-4" style="display: block; padding: 1em">
|
||||||
<span id="close_error" @click=${self.clear_error} class="w3-button w3-display-topright">×</span>
|
<span id="close_error" @click=${self.clear_error} class="w3-button w3-display-topright">×</span>
|
||||||
<div style="color: ${this.status.color ?? kErrorColor}"><b>ERROR:</b><p id="error" style="white-space: pre">${this.status.message}</p></div>
|
<div style="color: ${this.status.color ?? k_color_error}"><b>ERROR:</b><p id="error" style="white-space: pre">${this.status.message}</p></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@@ -438,6 +439,9 @@ class TfNavigationElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a tf-navigation element.
|
||||||
|
*/
|
||||||
customElements.define('tf-navigation', TfNavigationElement);
|
customElements.define('tf-navigation', TfNavigationElement);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -518,7 +522,7 @@ class TfFilesElement extends LitElement {
|
|||||||
for (let file of event.dataTransfer.files) {
|
for (let file of event.dataTransfer.files) {
|
||||||
let buffer = await file.arrayBuffer();
|
let buffer = await file.arrayBuffer();
|
||||||
let text = new TextDecoder('latin1').decode(buffer);
|
let text = new TextDecoder('latin1').decode(buffer);
|
||||||
gFiles[file.name] = {
|
g_files[file.name] = {
|
||||||
doc: cm6.EditorState.create({
|
doc: cm6.EditorState.create({
|
||||||
doc: text,
|
doc: text,
|
||||||
extensions: cm6.extensions,
|
extensions: cm6.extensions,
|
||||||
@@ -526,9 +530,9 @@ class TfFilesElement extends LitElement {
|
|||||||
buffer: buffer,
|
buffer: buffer,
|
||||||
isNew: true,
|
isNew: true,
|
||||||
};
|
};
|
||||||
gCurrentFile = file.name;
|
g_current_file = file.name;
|
||||||
}
|
}
|
||||||
openFile(gCurrentFile);
|
openFile(g_current_file);
|
||||||
updateFiles();
|
updateFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -892,11 +896,11 @@ async function edit() {
|
|||||||
: 'flex';
|
: 'flex';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!gEditor) {
|
if (!g_editor) {
|
||||||
cm6 = await import('/codemirror/cm6.js');
|
cm6 = await import('/codemirror/cm6.js');
|
||||||
gEditor = cm6.TildeFriendsEditorView(document.getElementById('editor'));
|
g_editor = cm6.TildeFriendsEditorView(document.getElementById('editor'));
|
||||||
}
|
}
|
||||||
gEditor.onDocChange = updateFiles;
|
g_editor.onDocChange = updateFiles;
|
||||||
await load();
|
await load();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert(`${error.message}\n\n${error.stack}`);
|
alert(`${error.message}\n\n${error.stack}`);
|
||||||
@@ -908,7 +912,7 @@ async function edit() {
|
|||||||
* Open a performance trace.
|
* Open a performance trace.
|
||||||
*/
|
*/
|
||||||
function trace() {
|
function trace() {
|
||||||
window.open(`/speedscope/#profileURL=${encodeURIComponent('/trace')}`);
|
window.open(`/~core/trace/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -929,13 +933,13 @@ function loadFile(name, id) {
|
|||||||
return response.text();
|
return response.text();
|
||||||
})
|
})
|
||||||
.then(function (text) {
|
.then(function (text) {
|
||||||
gFiles[name].doc = cm6.EditorState.create({
|
g_files[name].doc = cm6.EditorState.create({
|
||||||
doc: text,
|
doc: text,
|
||||||
extensions: cm6.extensions,
|
extensions: cm6.extensions,
|
||||||
});
|
});
|
||||||
gFiles[name].original = gFiles[name].doc.doc.toString();
|
g_files[name].original = g_files[name].doc.doc.toString();
|
||||||
if (!Object.values(gFiles).some((x) => !x.doc)) {
|
if (!Object.values(g_files).some((x) => !x.doc)) {
|
||||||
openFile(Object.keys(gFiles).sort()[0]);
|
openFile(Object.keys(g_files).sort()[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -953,31 +957,31 @@ async function load(path) {
|
|||||||
} else if (response.status != 404) {
|
} else if (response.status != 404) {
|
||||||
throw new Error(response.status + ' ' + response.statusText);
|
throw new Error(response.status + ' ' + response.statusText);
|
||||||
}
|
}
|
||||||
gFiles = {};
|
g_files = {};
|
||||||
let isApp = false;
|
let isApp = false;
|
||||||
let promises = [];
|
let promises = [];
|
||||||
|
|
||||||
if (json && json['type'] == 'tildefriends-app') {
|
if (json && json['type'] == 'tildefriends-app') {
|
||||||
isApp = true;
|
isApp = true;
|
||||||
Object.keys(json['files']).forEach(function (name) {
|
Object.keys(json['files']).forEach(function (name) {
|
||||||
gFiles[name] = {};
|
g_files[name] = {};
|
||||||
promises.push(loadFile(name, json['files'][name]));
|
promises.push(loadFile(name, json['files'][name]));
|
||||||
});
|
});
|
||||||
if (Object.keys(json['files']).length == 0) {
|
if (Object.keys(json['files']).length == 0) {
|
||||||
document.getElementById('editPane').style.display = 'flex';
|
document.getElementById('editPane').style.display = 'flex';
|
||||||
}
|
}
|
||||||
gApp = json;
|
g_app = json;
|
||||||
gApp.emoji = gApp.emoji || '📦';
|
g_app.emoji = g_app.emoji || '📦';
|
||||||
document.getElementById('icon').innerHTML = gApp.emoji;
|
document.getElementById('icon').innerHTML = g_app.emoji;
|
||||||
}
|
}
|
||||||
if (!isApp) {
|
if (!isApp) {
|
||||||
document.getElementById('editPane').style.display = 'flex';
|
document.getElementById('editPane').style.display = 'flex';
|
||||||
let text = '// New script.\n';
|
let text = '// New script.\n';
|
||||||
gCurrentFile = 'app.js';
|
g_current_file = 'app.js';
|
||||||
gFiles[gCurrentFile] = {
|
g_files[g_current_file] = {
|
||||||
doc: cm6.EditorState.create({doc: text, extensions: cm6.extensions}),
|
doc: cm6.EditorState.create({doc: text, extensions: cm6.extensions}),
|
||||||
};
|
};
|
||||||
openFile(gCurrentFile);
|
openFile(g_current_file);
|
||||||
}
|
}
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
@@ -998,13 +1002,14 @@ function closeEditor() {
|
|||||||
*/
|
*/
|
||||||
function save(save_to) {
|
function save(save_to) {
|
||||||
document.getElementById('save').disabled = true;
|
document.getElementById('save').disabled = true;
|
||||||
if (gCurrentFile) {
|
if (g_current_file) {
|
||||||
gFiles[gCurrentFile].doc = gEditor.state;
|
g_files[g_current_file].doc = g_editor.state;
|
||||||
if (
|
if (
|
||||||
!gFiles[gCurrentFile].isNew &&
|
!g_files[g_current_file].isNew &&
|
||||||
!gFiles[gCurrentFile].doc.doc.toString() == gFiles[gCurrentFile].original
|
!g_files[g_current_file].doc.doc.toString() ==
|
||||||
|
g_files[g_current_file].original
|
||||||
) {
|
) {
|
||||||
delete gFiles[gCurrentFile].buffer;
|
delete g_files[g_current_file].buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1019,8 +1024,8 @@ function save(save_to) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let promises = [];
|
let promises = [];
|
||||||
for (let name of Object.keys(gFiles)) {
|
for (let name of Object.keys(g_files)) {
|
||||||
let file = gFiles[name];
|
let file = g_files[name];
|
||||||
if (!file.isNew && file.doc.doc.toString() == file.original) {
|
if (!file.isNew && file.doc.doc.toString() == file.original) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1062,14 +1067,14 @@ function save(save_to) {
|
|||||||
let app = {
|
let app = {
|
||||||
type: 'tildefriends-app',
|
type: 'tildefriends-app',
|
||||||
files: Object.fromEntries(
|
files: Object.fromEntries(
|
||||||
Object.keys(gFiles).map((x) => [x, gFiles[x].id || gApp.files[x]])
|
Object.keys(g_files).map((x) => [x, g_files[x].id || g_app.files[x]])
|
||||||
),
|
),
|
||||||
emoji: gApp.emoji || '📦',
|
emoji: g_app.emoji || '📦',
|
||||||
};
|
};
|
||||||
Object.values(gFiles).forEach(function (file) {
|
Object.values(g_files).forEach(function (file) {
|
||||||
delete file.id;
|
delete file.id;
|
||||||
});
|
});
|
||||||
gApp = JSON.parse(JSON.stringify(app));
|
g_app = JSON.parse(JSON.stringify(app));
|
||||||
|
|
||||||
return fetch(save_path + 'save', {
|
return fetch(save_path + 'save', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -1084,7 +1089,7 @@ function save(save_to) {
|
|||||||
|
|
||||||
if (save_path != window.location.pathname) {
|
if (save_path != window.location.pathname) {
|
||||||
alert('Saved to ' + save_path + '.');
|
alert('Saved to ' + save_path + '.');
|
||||||
} else if (!gFiles['app.js']) {
|
} else if (!g_files['app.js']) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
reconnect(save_path);
|
reconnect(save_path);
|
||||||
@@ -1096,7 +1101,7 @@ function save(save_to) {
|
|||||||
})
|
})
|
||||||
.finally(function () {
|
.finally(function () {
|
||||||
document.getElementById('save').disabled = false;
|
document.getElementById('save').disabled = false;
|
||||||
Object.values(gFiles).forEach(function (file) {
|
Object.values(g_files).forEach(function (file) {
|
||||||
file.original = file.doc.doc.toString();
|
file.original = file.doc.doc.toString();
|
||||||
});
|
});
|
||||||
updateFiles();
|
updateFiles();
|
||||||
@@ -1109,8 +1114,8 @@ function save(save_to) {
|
|||||||
function changeIcon() {
|
function changeIcon() {
|
||||||
let value = prompt('Enter a new app icon emoji:');
|
let value = prompt('Enter a new app icon emoji:');
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
gApp.emoji = value || '📦';
|
g_app.emoji = value || '📦';
|
||||||
document.getElementById('icon').innerHTML = gApp.emoji;
|
document.getElementById('icon').innerHTML = g_app.emoji;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1187,9 +1192,12 @@ function api_postMessage(message) {
|
|||||||
function api_error(error) {
|
function api_error(error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
if (typeof error == 'string') {
|
if (typeof error == 'string') {
|
||||||
setStatusMessage('⚠️ ' + error, kErrorColor);
|
setStatusMessage('⚠️ ' + error, k_color_error);
|
||||||
} else {
|
} else {
|
||||||
setStatusMessage('⚠️ ' + error.message + '\n' + error.stack, kErrorColor);
|
setStatusMessage(
|
||||||
|
'⚠️ ' + error.message + '\n' + error.stack,
|
||||||
|
k_color_error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log('error', error);
|
console.log('error', error);
|
||||||
@@ -1216,10 +1224,10 @@ function api_localStorageGet(key) {
|
|||||||
/**
|
/**
|
||||||
* Request a permission
|
* Request a permission
|
||||||
* @param permission The permission to request.
|
* @param permission The permission to request.
|
||||||
* @param id The id requeesting the permission.
|
* @param description An optional human-readable description of the action for which the permission is being requested.
|
||||||
* @return A promise fulfilled if the permission was granted.
|
* @return A promise fulfilled if the permission was granted.
|
||||||
*/
|
*/
|
||||||
function api_requestPermission(permission, id) {
|
function api_requestPermission(permission, description) {
|
||||||
let outer = document.createElement('div');
|
let outer = document.createElement('div');
|
||||||
outer.classList.add('permissions');
|
outer.classList.add('permissions');
|
||||||
|
|
||||||
@@ -1236,6 +1244,18 @@ function api_requestPermission(permission, id) {
|
|||||||
div.appendChild(span);
|
div.appendChild(span);
|
||||||
container.appendChild(div);
|
container.appendChild(div);
|
||||||
|
|
||||||
|
if (description) {
|
||||||
|
container.appendChild(document.createTextNode('for the action:'));
|
||||||
|
let description_div = document.createElement('div');
|
||||||
|
description_div.classList.add('w3-border');
|
||||||
|
description_div.classList.add('w3-padding');
|
||||||
|
description_div.style.backgroundColor = '#666';
|
||||||
|
description_div.style.maxHeight = '3em';
|
||||||
|
description_div.style.overflow = 'auto';
|
||||||
|
description_div.appendChild(document.createTextNode(description));
|
||||||
|
container.appendChild(description_div);
|
||||||
|
}
|
||||||
|
|
||||||
div = document.createElement('div');
|
div = document.createElement('div');
|
||||||
div.style = 'padding: 1em';
|
div.style = 'padding: 1em';
|
||||||
let check = document.createElement('input');
|
let check = document.createElement('input');
|
||||||
@@ -1306,7 +1326,7 @@ function api_setHash(hash) {
|
|||||||
*/
|
*/
|
||||||
function _receive_websocket_message(message) {
|
function _receive_websocket_message(message) {
|
||||||
if (message && message.action == 'session') {
|
if (message && message.action == 'session') {
|
||||||
setStatusMessage('🟢 Executing...', kStatusColor);
|
setStatusMessage('🟢 Executing...', k_color_status);
|
||||||
let navigation = document.getElementsByTagName('tf-navigation')[0];
|
let navigation = document.getElementsByTagName('tf-navigation')[0];
|
||||||
navigation.credentials = message.credentials;
|
navigation.credentials = message.credentials;
|
||||||
navigation.identities = message.identities;
|
navigation.identities = message.identities;
|
||||||
@@ -1406,7 +1426,7 @@ function setStatusMessage(message, color) {
|
|||||||
document.getElementsByTagName('tf-navigation')[0].status = {
|
document.getElementsByTagName('tf-navigation')[0].status = {
|
||||||
message: message,
|
message: message,
|
||||||
color: color,
|
color: color,
|
||||||
is_error: color == kErrorColor,
|
is_error: color == k_color_error,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1416,11 +1436,11 @@ function setStatusMessage(message, color) {
|
|||||||
*/
|
*/
|
||||||
function send(value) {
|
function send(value) {
|
||||||
try {
|
try {
|
||||||
if (gSocket && gSocket.readyState == gSocket.OPEN) {
|
if (g_socket && g_socket.readyState == g_socket.OPEN) {
|
||||||
gSocket.send(JSON.stringify(value));
|
g_socket.send(JSON.stringify(value));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setStatusMessage('🤷 Send failed: ' + error.toString(), kErrorColor);
|
setStatusMessage('🤷 Send failed: ' + error.toString(), k_color_error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1435,7 +1455,7 @@ function hashChange() {
|
|||||||
* Make sure the app is connected on window focus, and notify the app.
|
* Make sure the app is connected on window focus, and notify the app.
|
||||||
*/
|
*/
|
||||||
function focus() {
|
function focus() {
|
||||||
if (gSocket && gSocket.readyState == gSocket.CLOSED) {
|
if (g_socket && g_socket.readyState == g_socket.CLOSED) {
|
||||||
connectSocket();
|
connectSocket();
|
||||||
} else {
|
} else {
|
||||||
send({event: 'focus'});
|
send({event: 'focus'});
|
||||||
@@ -1446,9 +1466,7 @@ function focus() {
|
|||||||
* Notify the app of lost focus.
|
* Notify the app of lost focus.
|
||||||
*/
|
*/
|
||||||
function blur() {
|
function blur() {
|
||||||
if (gSocket && gSocket.readyState == gSocket.OPEN) {
|
send({event: 'blur'});
|
||||||
send({event: 'blur'});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1505,8 +1523,8 @@ function message(event) {
|
|||||||
* @param path The path to which the WebSocket should be connected.
|
* @param path The path to which the WebSocket should be connected.
|
||||||
*/
|
*/
|
||||||
function reconnect(path) {
|
function reconnect(path) {
|
||||||
let oldSocket = gSocket;
|
let oldSocket = g_socket;
|
||||||
gSocket = null;
|
g_socket = null;
|
||||||
if (oldSocket) {
|
if (oldSocket) {
|
||||||
oldSocket.onopen = null;
|
oldSocket.onopen = null;
|
||||||
oldSocket.onclose = null;
|
oldSocket.onclose = null;
|
||||||
@@ -1521,24 +1539,24 @@ function reconnect(path) {
|
|||||||
* @param path The path to which to connect.
|
* @param path The path to which to connect.
|
||||||
*/
|
*/
|
||||||
function connectSocket(path) {
|
function connectSocket(path) {
|
||||||
if (!gSocket || gSocket.readyState != gSocket.OPEN) {
|
if (!g_socket || g_socket.readyState != g_socket.OPEN) {
|
||||||
if (gSocket) {
|
if (g_socket) {
|
||||||
gSocket.onopen = null;
|
g_socket.onopen = null;
|
||||||
gSocket.onclose = null;
|
g_socket.onclose = null;
|
||||||
gSocket.onmessage = null;
|
g_socket.onmessage = null;
|
||||||
gSocket.close();
|
g_socket.close();
|
||||||
}
|
}
|
||||||
setStatusMessage('⚪ Connecting...', kStatusColor);
|
setStatusMessage('⚪ Connecting...', k_color_status);
|
||||||
gSocket = new WebSocket(
|
g_socket = new WebSocket(
|
||||||
(window.location.protocol == 'https:' ? 'wss://' : 'ws://') +
|
(window.location.protocol == 'https:' ? 'wss://' : 'ws://') +
|
||||||
window.location.hostname +
|
window.location.hostname +
|
||||||
(window.location.port.length ? ':' + window.location.port : '') +
|
(window.location.port.length ? ':' + window.location.port : '') +
|
||||||
'/app/socket'
|
'/app/socket'
|
||||||
);
|
);
|
||||||
gSocket.onopen = function () {
|
g_socket.onopen = function () {
|
||||||
setStatusMessage('🟡 Authenticating...', kStatusColor);
|
setStatusMessage('🟡 Authenticating...', k_color_status);
|
||||||
let connect_path = path ?? window.location.pathname;
|
let connect_path = path ?? window.location.pathname;
|
||||||
gSocket.send(
|
g_socket.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
action: 'hello',
|
action: 'hello',
|
||||||
path: connect_path,
|
path: connect_path,
|
||||||
@@ -1550,12 +1568,12 @@ function connectSocket(path) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
gSocket.onmessage = function (event) {
|
g_socket.onmessage = function (event) {
|
||||||
_receive_websocket_message(JSON.parse(event.data));
|
_receive_websocket_message(JSON.parse(event.data));
|
||||||
};
|
};
|
||||||
gSocket.onclose = function (event) {
|
g_socket.onclose = function (event) {
|
||||||
if (gUnloading) {
|
if (g_unloading) {
|
||||||
setStatusMessage('⚪ Closing...', kStatusColor);
|
setStatusMessage('⚪ Closing...', k_color_status);
|
||||||
} else {
|
} else {
|
||||||
const k_codes = {
|
const k_codes = {
|
||||||
1000: 'Normal closure',
|
1000: 'Normal closure',
|
||||||
@@ -1576,37 +1594,57 @@ function connectSocket(path) {
|
|||||||
};
|
};
|
||||||
setStatusMessage(
|
setStatusMessage(
|
||||||
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
||||||
kDisconnectColor
|
k_color_disconnect
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine a CodeMirror language mode from filename.
|
||||||
|
* @param name Filename.
|
||||||
|
* @return The mode name.
|
||||||
|
*/
|
||||||
|
function modeFromName(name) {
|
||||||
|
switch (name.split('.').pop()) {
|
||||||
|
case 'md':
|
||||||
|
return 'markdown';
|
||||||
|
case 'css':
|
||||||
|
return 'css';
|
||||||
|
case 'js':
|
||||||
|
return 'javascript';
|
||||||
|
case 'xml':
|
||||||
|
case 'svg':
|
||||||
|
return 'xml';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a file by name.
|
* Open a file by name.
|
||||||
* @param name The file to open.
|
* @param name The file to open.
|
||||||
*/
|
*/
|
||||||
function openFile(name) {
|
function openFile(name) {
|
||||||
let newDoc =
|
let newDoc =
|
||||||
name && gFiles[name]
|
name && g_files[name]
|
||||||
? gFiles[name].doc
|
? g_files[name].doc
|
||||||
: cm6.EditorState.create({doc: '', extensions: cm6.extensions});
|
: cm6.EditorState.create({doc: '', extensions: cm6.extensions});
|
||||||
let oldDoc = gEditor.state;
|
let oldDoc = g_editor.state;
|
||||||
gEditor.setState(newDoc);
|
g_editor.setState(newDoc);
|
||||||
|
cm6.setEditorMode(g_editor, modeFromName(name));
|
||||||
|
|
||||||
if (gFiles[gCurrentFile]) {
|
if (g_files[g_current_file]) {
|
||||||
gFiles[gCurrentFile].doc = oldDoc;
|
g_files[g_current_file].doc = oldDoc;
|
||||||
if (
|
if (
|
||||||
!gFiles[gCurrentFile].isNew &&
|
!g_files[g_current_file].isNew &&
|
||||||
gFiles[gCurrentFile].doc.doc.toString() == oldDoc.doc.toString()
|
g_files[g_current_file].doc.doc.toString() == oldDoc.doc.toString()
|
||||||
) {
|
) {
|
||||||
delete gFiles[gCurrentFile].buffer;
|
delete g_files[g_current_file].buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gCurrentFile = name;
|
g_current_file = name;
|
||||||
updateFiles();
|
updateFiles();
|
||||||
gEditor.focus();
|
g_editor.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1616,19 +1654,19 @@ function updateFiles() {
|
|||||||
let files = document.getElementsByTagName('tf-files-pane')[0];
|
let files = document.getElementsByTagName('tf-files-pane')[0];
|
||||||
if (files) {
|
if (files) {
|
||||||
files.files = Object.fromEntries(
|
files.files = Object.fromEntries(
|
||||||
Object.keys(gFiles).map((file) => [
|
Object.keys(g_files).map((file) => [
|
||||||
file,
|
file,
|
||||||
{
|
{
|
||||||
clean:
|
clean:
|
||||||
(file == gCurrentFile
|
(file == g_current_file
|
||||||
? gEditor.state.doc.toString()
|
? g_editor.state.doc.toString()
|
||||||
: gFiles[file].doc.doc.toString()) == gFiles[file].original,
|
: g_files[file].doc.doc.toString()) == g_files[file].original,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
files.current = gCurrentFile;
|
files.current = g_current_file;
|
||||||
}
|
}
|
||||||
gEditor.focus();
|
g_editor.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1636,7 +1674,7 @@ function updateFiles() {
|
|||||||
* @param name The file's name.
|
* @param name The file's name.
|
||||||
*/
|
*/
|
||||||
function makeNewFile(name) {
|
function makeNewFile(name) {
|
||||||
gFiles[name] = {
|
g_files[name] = {
|
||||||
doc: cm6.EditorState.create({extensions: cm6.extensions}),
|
doc: cm6.EditorState.create({extensions: cm6.extensions}),
|
||||||
};
|
};
|
||||||
openFile(name);
|
openFile(name);
|
||||||
@@ -1647,7 +1685,7 @@ function makeNewFile(name) {
|
|||||||
*/
|
*/
|
||||||
function newFile() {
|
function newFile() {
|
||||||
let name = prompt('Name of new file:', 'file.js');
|
let name = prompt('Name of new file:', 'file.js');
|
||||||
if (name && !gFiles[name]) {
|
if (name && !g_files[name]) {
|
||||||
makeNewFile(name);
|
makeNewFile(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1656,9 +1694,9 @@ function newFile() {
|
|||||||
* Prompt to remove a file.
|
* Prompt to remove a file.
|
||||||
*/
|
*/
|
||||||
function removeFile() {
|
function removeFile() {
|
||||||
if (confirm('Remove ' + gCurrentFile + '?')) {
|
if (confirm('Remove ' + g_current_file + '?')) {
|
||||||
delete gFiles[gCurrentFile];
|
delete g_files[g_current_file];
|
||||||
openFile(Object.keys(gFiles)[0]);
|
openFile(Object.keys(g_files)[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1674,13 +1712,13 @@ async function appExport() {
|
|||||||
`${name}.json`,
|
`${name}.json`,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: 'tildefriends-app',
|
type: 'tildefriends-app',
|
||||||
emoji: gApp.emoji || '📦',
|
emoji: g_app.emoji || '📦',
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
for (let file of Object.keys(gFiles)) {
|
for (let file of Object.keys(g_files)) {
|
||||||
zip.file(
|
zip.file(
|
||||||
`${name}/${file}`,
|
`${name}/${file}`,
|
||||||
gFiles[file].buffer ?? gFiles[file].doc.doc.toString()
|
g_files[file].buffer ?? g_files[file].doc.doc.toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let content = await zip.generateAsync({
|
let content = await zip.generateAsync({
|
||||||
@@ -1799,9 +1837,9 @@ async function sourcePretty() {
|
|||||||
let babel = (await import('/prettier/babel.mjs')).default;
|
let babel = (await import('/prettier/babel.mjs')).default;
|
||||||
let estree = (await import('/prettier/estree.mjs')).default;
|
let estree = (await import('/prettier/estree.mjs')).default;
|
||||||
let prettier_html = (await import('/prettier/html.mjs')).default;
|
let prettier_html = (await import('/prettier/html.mjs')).default;
|
||||||
let source = gEditor.state.doc.toString();
|
let source = g_editor.state.doc.toString();
|
||||||
let formatted = await prettier.format(source, {
|
let formatted = await prettier.format(source, {
|
||||||
parser: gCurrentFile?.toLowerCase()?.endsWith('.html') ? 'html' : 'babel',
|
parser: g_current_file?.toLowerCase()?.endsWith('.html') ? 'html' : 'babel',
|
||||||
plugins: [babel, estree, prettier_html],
|
plugins: [babel, estree, prettier_html],
|
||||||
trailingComma: 'es5',
|
trailingComma: 'es5',
|
||||||
useTabs: true,
|
useTabs: true,
|
||||||
@@ -1810,10 +1848,10 @@ async function sourcePretty() {
|
|||||||
bracketSpacing: false,
|
bracketSpacing: false,
|
||||||
});
|
});
|
||||||
if (source !== formatted) {
|
if (source !== formatted) {
|
||||||
gEditor.dispatch({
|
g_editor.dispatch({
|
||||||
changes: {
|
changes: {
|
||||||
from: 0,
|
from: 0,
|
||||||
to: gEditor.state.doc.length,
|
to: g_editor.state.doc.length,
|
||||||
insert: formatted,
|
insert: formatted,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -1858,7 +1896,7 @@ window.addEventListener('load', function () {
|
|||||||
window.addEventListener('message', message, false);
|
window.addEventListener('message', message, false);
|
||||||
window.addEventListener('online', connectSocket);
|
window.addEventListener('online', connectSocket);
|
||||||
window.addEventListener('beforeunload', function () {
|
window.addEventListener('beforeunload', function () {
|
||||||
gUnloading = true;
|
g_unloading = true;
|
||||||
});
|
});
|
||||||
document.getElementById('name').value = window.location.pathname;
|
document.getElementById('name').value = window.location.pathname;
|
||||||
document
|
document
|
||||||
|
|||||||
211
core/core.js
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
/** \cond */
|
/** \cond */
|
||||||
import * as app from './app.js';
|
import * as app from './app.js';
|
||||||
import * as http from './http.js';
|
|
||||||
|
|
||||||
export {invoke, getProcessBlob};
|
export {invoke, getProcessBlob};
|
||||||
/** \endcond */
|
/** \endcond */
|
||||||
@@ -18,6 +17,8 @@ let gProcesses = {};
|
|||||||
let gStatsTimer = false;
|
let gStatsTimer = false;
|
||||||
/** Effectively a process ID. */
|
/** Effectively a process ID. */
|
||||||
let g_handler_index = 0;
|
let g_handler_index = 0;
|
||||||
|
/** Whether updating accounts information is currently scheduled. */
|
||||||
|
let g_update_accounts_scheduled;
|
||||||
/** Time between pings, in milliseconds. */
|
/** Time between pings, in milliseconds. */
|
||||||
const k_ping_interval = 60 * 1000;
|
const k_ping_interval = 60 * 1000;
|
||||||
|
|
||||||
@@ -179,6 +180,7 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
process.task = new Task();
|
process.task = new Task();
|
||||||
process.packageOwner = options.packageOwner;
|
process.packageOwner = options.packageOwner;
|
||||||
process.packageName = options.packageName;
|
process.packageName = options.packageName;
|
||||||
|
process.url = options?.url;
|
||||||
process.eventHandlers = {};
|
process.eventHandlers = {};
|
||||||
if (!options?.script || options?.script === 'app.js') {
|
if (!options?.script || options?.script === 'app.js') {
|
||||||
process.app = new app.App();
|
process.app = new app.App();
|
||||||
@@ -192,54 +194,13 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
});
|
});
|
||||||
gProcesses[key] = process;
|
gProcesses[key] = process;
|
||||||
process.task.onExit = function (exitCode, terminationSignal) {
|
process.task.onExit = function (exitCode, terminationSignal) {
|
||||||
broadcastEvent('onSessionEnd', [getUser(process, process)]);
|
|
||||||
process.task = null;
|
process.task = null;
|
||||||
delete gProcesses[key];
|
delete gProcesses[key];
|
||||||
};
|
};
|
||||||
let imports = {
|
let imports = {
|
||||||
core: {
|
core: {
|
||||||
broadcast: broadcast.bind(process),
|
broadcast: broadcast.bind(process),
|
||||||
register: function (eventName, handler) {
|
|
||||||
if (!process.eventHandlers[eventName]) {
|
|
||||||
process.eventHandlers[eventName] = [];
|
|
||||||
}
|
|
||||||
process.eventHandlers[eventName].push(handler);
|
|
||||||
},
|
|
||||||
unregister: function (eventName, handler) {
|
|
||||||
if (process.eventHandlers[eventName]) {
|
|
||||||
let index = process.eventHandlers[eventName].indexOf(handler);
|
|
||||||
if (index != -1) {
|
|
||||||
process.eventHandlers[eventName].splice(index, 1);
|
|
||||||
}
|
|
||||||
if (process.eventHandlers[eventName].length == 0) {
|
|
||||||
delete process.eventHandlers[eventName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
user: getUser(process, process),
|
user: getUser(process, process),
|
||||||
users: async function () {
|
|
||||||
try {
|
|
||||||
return JSON.parse(await new Database('auth').get('users'));
|
|
||||||
} catch {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
permissionsGranted: async function () {
|
|
||||||
let user = process?.credentials?.session?.name;
|
|
||||||
let settings = await loadSettings();
|
|
||||||
if (
|
|
||||||
user &&
|
|
||||||
options?.packageOwner &&
|
|
||||||
options?.packageName &&
|
|
||||||
settings.userPermissions &&
|
|
||||||
settings.userPermissions[user] &&
|
|
||||||
settings.userPermissions[user][options.packageOwner]
|
|
||||||
) {
|
|
||||||
return settings.userPermissions[user][options.packageOwner][
|
|
||||||
options.packageName
|
|
||||||
];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
allPermissionsGranted: async function () {
|
allPermissionsGranted: async function () {
|
||||||
let user = process?.credentials?.session?.name;
|
let user = process?.credentials?.session?.name;
|
||||||
let settings = await loadSettings();
|
let settings = await loadSettings();
|
||||||
@@ -253,12 +214,7 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
return settings.userPermissions[user];
|
return settings.userPermissions[user];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
permissionsForUser: async function (user) {
|
permissionTest: async function (permission, description) {
|
||||||
let settings = await loadSettings();
|
|
||||||
return settings?.permissions?.[user] ?? [];
|
|
||||||
},
|
|
||||||
getSockets: getSockets,
|
|
||||||
permissionTest: async function (permission) {
|
|
||||||
let user = process?.credentials?.session?.name;
|
let user = process?.credentials?.session?.name;
|
||||||
let settings = await loadSettings();
|
let settings = await loadSettings();
|
||||||
if (!user || !options?.packageOwner || !options?.packageName) {
|
if (!user || !options?.packageOwner || !options?.packageName) {
|
||||||
@@ -285,7 +241,7 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
}
|
}
|
||||||
} else if (process.app) {
|
} else if (process.app) {
|
||||||
return process.app
|
return process.app
|
||||||
.makeFunction(['requestPermission'])(permission)
|
.makeFunction(['requestPermission'])(permission, description)
|
||||||
.then(async function (value) {
|
.then(async function (value) {
|
||||||
if (value == 'allow') {
|
if (value == 'allow') {
|
||||||
await ssb.setUserPermission(
|
await ssb.setUserPermission(
|
||||||
@@ -318,26 +274,26 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
throw Error(`Permission denied: ${permission}.`);
|
throw Error(`Permission denied: ${permission}.`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
app: {
|
|
||||||
owner: options?.packageOwner,
|
|
||||||
name: options?.packageName,
|
|
||||||
},
|
|
||||||
url: options?.url,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
process.sendIdentities = async function () {
|
process.sendIdentities = async function () {
|
||||||
process.app.send(
|
let identities = await ssb_internal.getIdentityInfo(
|
||||||
Object.assign(
|
process?.credentials?.session?.name,
|
||||||
{
|
options?.packageOwner,
|
||||||
action: 'identities',
|
options?.packageName
|
||||||
},
|
|
||||||
await ssb.getIdentityInfo(
|
|
||||||
process?.credentials?.session?.name,
|
|
||||||
options?.packageOwner,
|
|
||||||
options?.packageName
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
let json = JSON.stringify(identities);
|
||||||
|
if (process._last_sent_identities !== json) {
|
||||||
|
process.app.send(
|
||||||
|
Object.assign(
|
||||||
|
{
|
||||||
|
action: 'identities',
|
||||||
|
},
|
||||||
|
identities
|
||||||
|
)
|
||||||
|
);
|
||||||
|
process._last_sent_identities = json;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
process.setActiveIdentity = async function (identity) {
|
process.setActiveIdentity = async function (identity) {
|
||||||
if (
|
if (
|
||||||
@@ -374,7 +330,7 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
options.packageName,
|
options.packageName,
|
||||||
'setActiveIdentity',
|
'setActiveIdentity',
|
||||||
[
|
[
|
||||||
await ssb.getActiveIdentity(
|
await imports.ssb.getActiveIdentity(
|
||||||
process.credentials?.session?.name,
|
process.credentials?.session?.name,
|
||||||
options.packageOwner,
|
options.packageOwner,
|
||||||
options.packageName
|
options.packageName
|
||||||
@@ -387,21 +343,11 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (process.credentials?.permissions?.administration) {
|
if (process.credentials?.permissions?.administration) {
|
||||||
imports.core.globalSettingsDescriptions = async function () {
|
|
||||||
let settings = Object.assign({}, defaultGlobalSettings());
|
|
||||||
for (let [key, value] of Object.entries(await loadSettings())) {
|
|
||||||
if (settings[key]) {
|
|
||||||
settings[key].value = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return settings;
|
|
||||||
};
|
|
||||||
imports.core.globalSettingsGet = async function (key) {
|
|
||||||
let settings = await loadSettings();
|
|
||||||
return settings?.[key];
|
|
||||||
};
|
|
||||||
imports.core.globalSettingsSet = async function (key, value) {
|
imports.core.globalSettingsSet = async function (key, value) {
|
||||||
await imports.core.permissionTest('set_global_setting');
|
await imports.core.permissionTest(
|
||||||
|
'set_global_setting',
|
||||||
|
`Set ${JSON.stringify(key)} to ${JSON.stringify(value)}.`
|
||||||
|
);
|
||||||
print('Setting', key, value);
|
print('Setting', key, value);
|
||||||
let settings = await loadSettings();
|
let settings = await loadSettings();
|
||||||
settings[key] = value;
|
settings[key] = value;
|
||||||
@@ -481,26 +427,6 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
imports.ssb.setActiveIdentity = (id) => process.setActiveIdentity(id);
|
imports.ssb.setActiveIdentity = (id) => process.setActiveIdentity(id);
|
||||||
imports.ssb.getActiveIdentity = () =>
|
|
||||||
ssb.getActiveIdentity(
|
|
||||||
process.credentials?.session?.name,
|
|
||||||
options.packageOwner,
|
|
||||||
options.packageName
|
|
||||||
);
|
|
||||||
imports.ssb.getOwnerIdentities = function () {
|
|
||||||
if (options.packageOwner) {
|
|
||||||
return ssb.getIdentities(options.packageOwner);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
imports.ssb.getIdentities = function () {
|
|
||||||
if (
|
|
||||||
process.credentials &&
|
|
||||||
process.credentials.session &&
|
|
||||||
process.credentials.session.name
|
|
||||||
) {
|
|
||||||
return ssb.getIdentities(process.credentials.session.name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
imports.ssb.getPrivateKey = function (id) {
|
imports.ssb.getPrivateKey = function (id) {
|
||||||
if (
|
if (
|
||||||
process.credentials &&
|
process.credentials &&
|
||||||
@@ -520,8 +446,18 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
process.credentials.session &&
|
process.credentials.session &&
|
||||||
process.credentials.session.name
|
process.credentials.session.name
|
||||||
) {
|
) {
|
||||||
|
let action;
|
||||||
|
try {
|
||||||
|
if (message?.type === 'vote' && message?.vote?.expression) {
|
||||||
|
action = `React with ${message?.vote?.expression}.`;
|
||||||
|
} else if (typeof message === 'string') {
|
||||||
|
action = `Post a private message.`;
|
||||||
|
} else {
|
||||||
|
action = `Publish ${'aeiou'.indexOf(message?.type?.toLowerCase()?.substring(0, 1)) != -1 ? 'an' : 'a'} "${message?.type}" message.`;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
imports.core.permissionTest('ssb_append')
|
imports.core.permissionTest('ssb_append', action)
|
||||||
).then(function () {
|
).then(function () {
|
||||||
return ssb.appendMessageWithIdentity(
|
return ssb.appendMessageWithIdentity(
|
||||||
process.credentials.session.name,
|
process.credentials.session.name,
|
||||||
@@ -558,25 +494,29 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
imports.ssb.swapWithServerIdentity = function (id) {
|
if (process.credentials?.permissions?.administration) {
|
||||||
if (
|
imports.ssb.swapWithServerIdentity = function (id) {
|
||||||
process.credentials &&
|
if (
|
||||||
process.credentials.session &&
|
process.credentials &&
|
||||||
process.credentials.session.name
|
process.credentials.session &&
|
||||||
) {
|
process.credentials.session.name
|
||||||
return ssb.swapWithServerIdentity(
|
) {
|
||||||
process.credentials.session.name,
|
return ssb.swapWithServerIdentity(
|
||||||
id
|
process.credentials.session.name,
|
||||||
);
|
id
|
||||||
}
|
);
|
||||||
};
|
}
|
||||||
imports.ssb.addEventListener = undefined;
|
};
|
||||||
imports.ssb.removeEventListener = undefined;
|
imports.ssb.addBlock = async function (id) {
|
||||||
imports.ssb.getIdentityInfo = undefined;
|
await imports.core.permissionTest('modify_blocks', `Block ${id}.`);
|
||||||
imports.fetch = async function (url, options) {
|
await ssb_internal.addBlock(id);
|
||||||
let settings = await loadSettings();
|
};
|
||||||
return http.fetch(url, options, settings?.fetch_hosts);
|
imports.ssb.removeBlock = async function (id) {
|
||||||
};
|
await imports.core.permissionTest('modify_blocks', `Unblock ${id}.`);
|
||||||
|
await ssb_internal.removeBlock(id);
|
||||||
|
};
|
||||||
|
imports.ssb.getBlocks = ssb_internal.getBlocks.bind(ssb_internal);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
process.credentials &&
|
process.credentials &&
|
||||||
@@ -680,7 +620,6 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
printError(e);
|
printError(e);
|
||||||
}
|
}
|
||||||
broadcastEvent('onSessionBegin', [getUser(process, process)]);
|
|
||||||
if (process.app) {
|
if (process.app) {
|
||||||
process.app.send({action: 'ready', version: version()});
|
process.app.send({action: 'ready', version: version()});
|
||||||
await process.sendPermissions();
|
await process.sendPermissions();
|
||||||
@@ -703,15 +642,39 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
return process;
|
return process;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssb.addEventListener('message', function () {
|
/**
|
||||||
|
* Send any changed account information.
|
||||||
|
*/
|
||||||
|
function updateAccounts() {
|
||||||
|
g_update_accounts_scheduled = false;
|
||||||
|
let promises = [];
|
||||||
|
for (let process of Object.values(gProcesses)) {
|
||||||
|
promises.push(process.sendIdentities());
|
||||||
|
}
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSB message added callback.
|
||||||
|
*/
|
||||||
|
ssb_internal.addEventListener('message', function () {
|
||||||
broadcastEvent('onMessage', [...arguments]);
|
broadcastEvent('onMessage', [...arguments]);
|
||||||
|
|
||||||
|
if (!g_update_accounts_scheduled) {
|
||||||
|
setTimeout(updateAccounts, 1000);
|
||||||
|
g_update_accounts_scheduled = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ssb.addEventListener('broadcasts', function () {
|
ssb_internal.addEventListener('blob', function () {
|
||||||
|
broadcastEvent('onBlob', [...arguments]);
|
||||||
|
});
|
||||||
|
|
||||||
|
ssb_internal.addEventListener('broadcasts', function () {
|
||||||
broadcastEvent('onBroadcastsChanged', []);
|
broadcastEvent('onBroadcastsChanged', []);
|
||||||
});
|
});
|
||||||
|
|
||||||
ssb.addEventListener('connections', function () {
|
ssb_internal.addEventListener('connections', function () {
|
||||||
broadcastEvent('onConnectionsChanged', []);
|
broadcastEvent('onConnectionsChanged', []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
67
core/eula.html
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Tilde Friends Usage Agreement</title>
|
||||||
|
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
</head>
|
||||||
|
<body class="w3-container">
|
||||||
|
<h1>Tilde Friends Usage Agreement</h1>
|
||||||
|
<p>
|
||||||
|
Tilde Friends is an app that enables communication with other users
|
||||||
|
through the
|
||||||
|
<a href="https://ssbc.github.io/scuttlebutt-protocol-guide/"
|
||||||
|
>Secure Scuttlebutt</a
|
||||||
|
>
|
||||||
|
protocol.
|
||||||
|
</p>
|
||||||
|
<h2>Your actions are your responsibility</h2>
|
||||||
|
<p>
|
||||||
|
Apple tolerates no objectionable content or abusive users on their
|
||||||
|
platforms.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You are responsible for your own actions within this app. You are
|
||||||
|
responsible for complying with all applicable rules and laws.
|
||||||
|
</p>
|
||||||
|
<h2>You choose what you see</h2>
|
||||||
|
<p>
|
||||||
|
You are in full control of the content you see in Tilde Friends. The peers
|
||||||
|
to which you choose to connect and the users you choose to follow directly
|
||||||
|
determine the content presented to you. Initially you will be following no
|
||||||
|
one with no connections and as a result see no user-generated content.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you encounter objectionable content, you can filter it from your view
|
||||||
|
by blocking the user who posted it. This also makes it so that users
|
||||||
|
following you will not see it as a consequence of following you. You
|
||||||
|
moderate for your friends and vice versa.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You can also flag a message to report to the operators of services with
|
||||||
|
which you interact that the it should be considered for removal.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The <code>admin</code> app contains a variety of settings that control the
|
||||||
|
types of connections Tilde Friends will make or accept, including whether
|
||||||
|
the app will even accept or make connections at all. This is also where a
|
||||||
|
local blocklist can be managed.
|
||||||
|
</p>
|
||||||
|
<h2>This app is not a service</h2>
|
||||||
|
<p>
|
||||||
|
Tilde Friends is an app. It relies on no servers. The author of this app
|
||||||
|
has no more ability to see or filter what you post or read than any other
|
||||||
|
user of the network.
|
||||||
|
</p>
|
||||||
|
<h2>Agreement</h2>
|
||||||
|
<p>
|
||||||
|
If you do not accept these terms, do not use this app. You may close and
|
||||||
|
delete it now.
|
||||||
|
</p>
|
||||||
|
<div class="w3-center w3-margin">
|
||||||
|
<a class="w3-button w3-blue w3-round-large" href="/eula/accept"
|
||||||
|
>Accept Agreement</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
121
core/http.js
@@ -1,121 +0,0 @@
|
|||||||
/**
|
|
||||||
* \file
|
|
||||||
* \defgroup tfhttp Tilde Friends HTTP Client JS
|
|
||||||
* Tilde Friends server-side HTTP client.
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a URL into protocol, host, path, and port parts.
|
|
||||||
* @param url
|
|
||||||
* @return An object of the URL parts.
|
|
||||||
*/
|
|
||||||
function parseUrl(url) {
|
|
||||||
// XXX: Hack.
|
|
||||||
let match = url.match(new RegExp('(\\w+)://([^/:]+)(?::(\\d+))?(.*)'));
|
|
||||||
return {
|
|
||||||
protocol: match[1],
|
|
||||||
host: match[2],
|
|
||||||
path: match[4],
|
|
||||||
port: match[3] ? parseInt(match[3]) : match[1] == 'http' ? 80 : 443,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse an HTTP response into headers and body content.
|
|
||||||
* @param data The response data, headers and body included.
|
|
||||||
* @return headers and body data.
|
|
||||||
*/
|
|
||||||
function parseResponse(data) {
|
|
||||||
let firstLine;
|
|
||||||
let headers = {};
|
|
||||||
while (true) {
|
|
||||||
let endLine = data.indexOf('\r\n');
|
|
||||||
let line = data.substring(0, endLine);
|
|
||||||
data = data.substring(endLine + 2);
|
|
||||||
if (!line.length) {
|
|
||||||
break;
|
|
||||||
} else if (!firstLine) {
|
|
||||||
firstLine = line;
|
|
||||||
} else {
|
|
||||||
let colon = line.indexOf(':');
|
|
||||||
headers[line.substring(colon)] = line.substring(colon + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {headers: headers, body: data};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make an HTTP request.
|
|
||||||
* @param url The URL.
|
|
||||||
* @param options Request options.
|
|
||||||
* @param allowed_hosts List of allowed hosts.
|
|
||||||
* @return A promise resolved with the response headers and body.
|
|
||||||
*/
|
|
||||||
export function fetch(url, options, allowed_hosts) {
|
|
||||||
let parsed = parseUrl(url);
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
if ((allowed_hosts ?? []).indexOf(parsed.host) == -1) {
|
|
||||||
throw new Error(`fetch() request to host ${parsed.host} is not allowed.`);
|
|
||||||
}
|
|
||||||
let socket = new Socket();
|
|
||||||
let buffer = new Uint8Array(0);
|
|
||||||
|
|
||||||
return socket
|
|
||||||
.connect(parsed.host, parsed.port)
|
|
||||||
.then(function () {
|
|
||||||
socket.read(function (data) {
|
|
||||||
if (data && data.length) {
|
|
||||||
let newBuffer = new Uint8Array(buffer.length + data.length);
|
|
||||||
newBuffer.set(buffer, 0);
|
|
||||||
newBuffer.set(data, buffer.length);
|
|
||||||
buffer = newBuffer;
|
|
||||||
} else {
|
|
||||||
let result = parseHttpResponse(buffer);
|
|
||||||
if (!result) {
|
|
||||||
reject(new Exception('Parse failed.'));
|
|
||||||
}
|
|
||||||
if (typeof result == 'number') {
|
|
||||||
if (result == -2) {
|
|
||||||
reject('Incomplete request.');
|
|
||||||
} else {
|
|
||||||
reject('Bad request.');
|
|
||||||
}
|
|
||||||
} else if (typeof result == 'object') {
|
|
||||||
resolve({
|
|
||||||
body: buffer.slice(result.bytes_parsed),
|
|
||||||
status: result.status,
|
|
||||||
message: result.message,
|
|
||||||
headers: result.headers,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
reject(new Exception('Unexpected parse result.'));
|
|
||||||
}
|
|
||||||
resolve(parseResponse(utf8Decode(buffer)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (parsed.port == 443) {
|
|
||||||
return socket.startTls();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(function () {
|
|
||||||
let body =
|
|
||||||
typeof options?.body == 'string'
|
|
||||||
? utf8Encode(options.body)
|
|
||||||
: options.body || new Uint8Array(0);
|
|
||||||
let headers = utf8Encode(
|
|
||||||
`${options?.method ?? 'GET'} ${parsed.path} HTTP/1.0\r\nHost: ${parsed.host}\r\nConnection: close\r\nContent-Length: ${body.length}\r\n\r\n`
|
|
||||||
);
|
|
||||||
let fullRequest = new Uint8Array(headers.length + body.length);
|
|
||||||
fullRequest.set(headers, 0);
|
|
||||||
fullRequest.set(body, headers.length);
|
|
||||||
socket.write(fullRequest);
|
|
||||||
})
|
|
||||||
.catch(function (error) {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @} */
|
|
||||||
@@ -5,7 +5,10 @@
|
|||||||
<link type="text/css" rel="stylesheet" href="/static/style.css" />
|
<link type="text/css" rel="stylesheet" href="/static/style.css" />
|
||||||
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
|
||||||
<link type="image/svg+xml" rel="icon" href="/static/tildefriends.svg" />
|
<link type="image/svg+xml" rel="icon" href="/static/tildefriends.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1, maximum-scale=1"
|
||||||
|
/>
|
||||||
<meta
|
<meta
|
||||||
name="title"
|
name="title"
|
||||||
content="Tilde Friends - Make friends and apps from your web browser."
|
content="Tilde Friends - Make friends and apps from your web browser."
|
||||||
|
|||||||
@@ -152,4 +152,5 @@ body {
|
|||||||
border-bottom: 4px solid #fff;
|
border-bottom: 4px solid #fff;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
max-width: 80%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,14 +25,14 @@
|
|||||||
}:
|
}:
|
||||||
pkgs.stdenv.mkDerivation rec {
|
pkgs.stdenv.mkDerivation rec {
|
||||||
pname = "tildefriends";
|
pname = "tildefriends";
|
||||||
version = "0.0.33";
|
version = "0.2025.11";
|
||||||
|
|
||||||
src = pkgs.fetchFromGitea {
|
src = pkgs.fetchFromGitea {
|
||||||
domain = "dev.tildefriends.net";
|
domain = "dev.tildefriends.net";
|
||||||
owner = "cory";
|
owner = "cory";
|
||||||
repo = "tildefriends";
|
repo = "tildefriends";
|
||||||
rev = "v${version}";
|
rev = "v${version}";
|
||||||
hash = "sha256-9D28gmaBTRVyXhY3zZd/W9PsXA1YZt/K69hz41aVP04=";
|
hash = "sha256-z4v4ghKOBTMv+agTUKg+HU8zfE4imluXFsozQCT4qX8=";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
2
deps/codemirror/cm6.js
vendored
46
deps/codemirror_src/editor.mjs
vendored
@@ -1,12 +1,14 @@
|
|||||||
import {EditorState} from "@codemirror/state"
|
import {EditorState, Compartment} from "@codemirror/state"
|
||||||
import {EditorView} from '@codemirror/view';
|
import {EditorView} from '@codemirror/view';
|
||||||
import {javascript} from "@codemirror/lang-javascript"
|
import {javascript} from "@codemirror/lang-javascript"
|
||||||
import {html} from "@codemirror/lang-html"
|
import {htmlLanguage, html} from "@codemirror/lang-html"
|
||||||
import {css} from "@codemirror/lang-css"
|
import {css} from "@codemirror/lang-css"
|
||||||
|
import {markdown} from "@codemirror/lang-markdown"
|
||||||
|
import {xml} from "@codemirror/lang-xml"
|
||||||
import {search} from "@codemirror/search"
|
import {search} from "@codemirror/search"
|
||||||
import {oneDark} from "./theme-tf-dark.js"
|
import {oneDark} from "./theme-tf-dark.js"
|
||||||
import {lineNumbers, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine, keymap, highlightWhitespace} from '@codemirror/view';
|
import {lineNumbers, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine, keymap, highlightWhitespace} from '@codemirror/view';
|
||||||
import {foldGutter, indentUnit, indentOnInput, syntaxHighlighting, defaultHighlightStyle, bracketMatching, foldKeymap} from '@codemirror/language';
|
import {language, foldGutter, indentUnit, indentOnInput, syntaxHighlighting, defaultHighlightStyle, bracketMatching, foldKeymap} from '@codemirror/language';
|
||||||
import {history, defaultKeymap, historyKeymap, indentWithTab} from '@codemirror/commands';
|
import {history, defaultKeymap, historyKeymap, indentWithTab} from '@codemirror/commands';
|
||||||
import {highlightSelectionMatches, searchKeymap} from '@codemirror/search';
|
import {highlightSelectionMatches, searchKeymap} from '@codemirror/search';
|
||||||
import {autocompletion, closeBracketsKeymap, completionKeymap} from '@codemirror/autocomplete';
|
import {autocompletion, closeBracketsKeymap, completionKeymap} from '@codemirror/autocomplete';
|
||||||
@@ -18,6 +20,23 @@ let updateListenerExtension = EditorView.updateListener.of((update) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* https://codemirror.net/examples/config/ */
|
||||||
|
const languageConfig = new Compartment();
|
||||||
|
|
||||||
|
const autoLanguage = EditorState.transactionExtender.of(tr => {
|
||||||
|
if (!tr.docChanged) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let doc_is_html = /\s*</.test(tr.newDoc.sliceString(0, 100));
|
||||||
|
let state_is_html = tr.startState.facet(language) == htmlLanguage;
|
||||||
|
if (doc_is_html == state_is_html) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
effects: languageConfig.reconfigure(doc_is_html ? html() : javascript()),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const extensions = [
|
const extensions = [
|
||||||
lineNumbers(),
|
lineNumbers(),
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
@@ -47,9 +66,8 @@ const extensions = [
|
|||||||
...lintKeymap,
|
...lintKeymap,
|
||||||
indentWithTab,
|
indentWithTab,
|
||||||
]),
|
]),
|
||||||
javascript(),
|
languageConfig.of(javascript()),
|
||||||
html(),
|
autoLanguage,
|
||||||
css(),
|
|
||||||
search(),
|
search(),
|
||||||
oneDark,
|
oneDark,
|
||||||
updateListenerExtension,
|
updateListenerExtension,
|
||||||
@@ -60,11 +78,25 @@ function TildeFriendsEditorView(parent) {
|
|||||||
extensions: extensions,
|
extensions: extensions,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
function setEditorMode(view, mode) {
|
||||||
|
const k_modes = {
|
||||||
|
'css': css(),
|
||||||
|
'html': html(),
|
||||||
|
'javascript': javascript(),
|
||||||
|
'markdown': markdown(),
|
||||||
|
'xml': xml(),
|
||||||
|
};
|
||||||
|
view.dispatch({
|
||||||
|
effects: languageConfig.reconfigure(k_modes[mode])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
TildeFriendsEditorView,
|
TildeFriendsEditorView,
|
||||||
EditorState,
|
EditorState,
|
||||||
EditorView,
|
EditorView,
|
||||||
extensions,
|
extensions,
|
||||||
|
setEditorMode,
|
||||||
};
|
};
|
||||||
|
|||||||
346
deps/codemirror_src/package-lock.json
generated
vendored
@@ -9,6 +9,8 @@
|
|||||||
"@codemirror/lang-html": "^6.4.8",
|
"@codemirror/lang-html": "^6.4.8",
|
||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@codemirror/lang-json": "^6.0.1",
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
|
"@codemirror/lang-markdown": "^6.0.1",
|
||||||
|
"@codemirror/lang-xml": "^6.0.1",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
@@ -19,9 +21,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/autocomplete": {
|
"node_modules/@codemirror/autocomplete": {
|
||||||
"version": "6.18.6",
|
"version": "6.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz",
|
||||||
"integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
|
"integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
@@ -31,9 +33,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/commands": {
|
"node_modules/@codemirror/commands": {
|
||||||
"version": "6.8.1",
|
"version": "6.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.0.tgz",
|
||||||
"integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
|
"integrity": "sha512-2xUIc5mHXQzT16JnyOFkh8PvfeXuIut3pslWGfsGOhxP/lpgRm9HOl/mpzLErgt5mXDovqA0d11P21gofRLb9w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
@@ -56,9 +58,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/lang-html": {
|
"node_modules/@codemirror/lang-html": {
|
||||||
"version": "6.4.9",
|
"version": "6.4.11",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
|
||||||
"integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
|
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
@@ -69,7 +71,7 @@
|
|||||||
"@codemirror/view": "^6.17.0",
|
"@codemirror/view": "^6.17.0",
|
||||||
"@lezer/common": "^1.0.0",
|
"@lezer/common": "^1.0.0",
|
||||||
"@lezer/css": "^1.1.0",
|
"@lezer/css": "^1.1.0",
|
||||||
"@lezer/html": "^1.3.0"
|
"@lezer/html": "^1.3.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/lang-javascript": {
|
"node_modules/@codemirror/lang-javascript": {
|
||||||
@@ -97,6 +99,35 @@
|
|||||||
"@lezer/json": "^1.0.0"
|
"@lezer/json": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@codemirror/lang-markdown": {
|
||||||
|
"version": "6.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz",
|
||||||
|
"integrity": "sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.7.1",
|
||||||
|
"@codemirror/lang-html": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.3.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.2.1",
|
||||||
|
"@lezer/markdown": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-xml": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.4.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/xml": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@codemirror/language": {
|
"node_modules/@codemirror/language": {
|
||||||
"version": "6.11.3",
|
"version": "6.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz",
|
||||||
@@ -112,9 +143,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/lint": {
|
"node_modules/@codemirror/lint": {
|
||||||
"version": "6.8.5",
|
"version": "6.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.2.tgz",
|
||||||
"integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
|
"integrity": "sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
@@ -155,9 +186,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.38.1",
|
"version": "6.38.8",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.8.tgz",
|
||||||
"integrity": "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==",
|
"integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
@@ -206,9 +237,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.30",
|
"version": "0.3.31",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||||
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
|
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -217,9 +248,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/common": {
|
"node_modules/@lezer/common": {
|
||||||
"version": "1.2.3",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.4.0.tgz",
|
||||||
"integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==",
|
"integrity": "sha512-DVeMRoGrgn/k45oQNu189BoW4SZwgZFzJ1+1TV5j2NJ/KFC83oa/enRqZSGshyeMk5cPWMhsKs9nx+8o0unwGg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/css": {
|
"node_modules/@lezer/css": {
|
||||||
@@ -234,18 +265,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/highlight": {
|
"node_modules/@lezer/highlight": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz",
|
||||||
"integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==",
|
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.0.0"
|
"@lezer/common": "^1.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/html": {
|
"node_modules/@lezer/html": {
|
||||||
"version": "1.3.10",
|
"version": "1.3.12",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.12.tgz",
|
||||||
"integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==",
|
"integrity": "sha512-RJ7eRWdaJe3bsiiLLHjCFT1JMk8m1YP9kaUbvu2rMLEoOnke9mcTVDyfOslsln0LtujdWespjJ39w6zo+RsQYw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.2.0",
|
"@lezer/common": "^1.2.0",
|
||||||
@@ -254,9 +285,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/javascript": {
|
"node_modules/@lezer/javascript": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz",
|
||||||
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
|
"integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.2.0",
|
"@lezer/common": "^1.2.0",
|
||||||
@@ -276,14 +307,35 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/lr": {
|
"node_modules/@lezer/lr": {
|
||||||
"version": "1.4.2",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.4.tgz",
|
||||||
"integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==",
|
"integrity": "sha512-LHL17Mq0OcFXm1pGQssuGTQFPPdxARjKM8f7GA5+sGtHi0K3R84YaSbmche0+RKWHnCsx9asEe5OWOI4FHfe4A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.0.0"
|
"@lezer/common": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@lezer/markdown": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-AXb98u3M6BEzTnreBnGtQaF7xFTiMA92Dsy5tqEjpacbjRxDSFdN4bKJo9uvU4cEEOS7D2B9MT7kvDgOEIzJSw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/highlight": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/xml": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.2.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@marijn/find-cluster-break": {
|
"node_modules/@marijn/find-cluster-break": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
|
||||||
@@ -338,9 +390,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "5.2.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
|
||||||
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
|
"integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "^1.0.0",
|
"@types/estree": "^1.0.0",
|
||||||
@@ -360,9 +412,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
|
||||||
"integrity": "sha512-B2wfzCJ+ps/OBzRjeds7DlJumCU3rXMxJJS1vzURyj7+KBHGONm7c9q1TfdBl4vCuNMkDvARn3PBl2wZzuR5mw==",
|
"integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -373,9 +425,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
|
||||||
"integrity": "sha512-FGJYXvYdn8Bs6lAlBZYT5n+4x0ciEp4cmttsvKAZc/c8/JiPaQK8u0c/86vKX8lA7OY/+37lIQSe0YoAImvBAA==",
|
"integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -386,9 +438,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
|
||||||
"integrity": "sha512-/9qwE/BM7ATw/W/OFEMTm3dmywbJyLQb4f4v5nmOjgYxPIGpw7HaxRi6LnD4Pjn/q7k55FGeHe1/OD02w63apA==",
|
"integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -399,9 +451,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
|
||||||
"integrity": "sha512-QkWfNbeRuzFnv2d0aPlrzcA3Ebq2mE8kX/5Pl7VdRShbPBjSnom7dbT8E3Jmhxo2RL784hyqGvR5KHavCJQciw==",
|
"integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -412,9 +464,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
|
||||||
"integrity": "sha512-+ToyOMYnSfV8D+ckxO6NthPln/PDNp1P6INcNypfZ7muLmEvPKXqduUiD8DlJpMMT8LxHcE5W0dK9kXfJke9Zw==",
|
"integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -425,9 +477,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
|
||||||
"integrity": "sha512-cGT6ey/W+sje6zywbLiqmkfkO210FgRz7tepWAzzEVgQU8Hn91JJmQWNqs55IuglG8sJdzk7XfNgmGRtcYlo1w==",
|
"integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -438,9 +490,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
|
||||||
"integrity": "sha512-9fhTJyOb275w5RofPSl8lpr4jFowd+H4oQKJ9XTYzD1JWgxdZKE8bA6d4npuiMemkecQOcigX01FNZNCYnQBdA==",
|
"integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -451,9 +503,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
|
||||||
"integrity": "sha512-+6kCIM5Zjvz2HwPl/udgVs07tPMIp1VU2Y0c72ezjOvSvEfAIWsUgpcSDvnC7g9NrjYR6X9bZT92mZZ90TfvXw==",
|
"integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -464,9 +516,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
|
||||||
"integrity": "sha512-SWuXdnsayCZL4lXoo6jn0yyAj7TTjWE4NwDVt9s7cmu6poMhtiras5c8h6Ih6Y0Zk6Z+8t/mLumvpdSPTWub2Q==",
|
"integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -477,9 +529,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
|
||||||
"integrity": "sha512-vDknMDqtMhrrroa5kyX6tuC0aRZZlQ+ipDfbXd2YGz5HeV2t8HOl/FDAd2ynhs7Ki5VooWiiZcCtxiZ4IjqZwQ==",
|
"integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -489,10 +541,10 @@
|
|||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
|
||||||
"integrity": "sha512-mCBkjRZWhvjtl/x+Bd4fQkWZT8canStKDxGrHlBiTnZmJnWygGcvBylzLVCZXka4dco5ymkWhZlLwKCGFF4ivw==",
|
"integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@@ -503,9 +555,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
|
||||||
"integrity": "sha512-YMdz2phOTFF+Z66dQfGf0gmeDSi5DJzY5bpZyeg9CPBkV9QDzJ1yFRlmi/j7WWRf3hYIWrOaJj5jsfwgc8GTHQ==",
|
"integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -516,9 +568,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
|
||||||
"integrity": "sha512-r0WKLSfFAK8ucG024v2yiLSJMedoWvk8yWqfNICX28NHDGeu3F/wBf8KG6mclghx4FsLePxJr/9N8rIj1PtCnw==",
|
"integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -529,9 +581,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
|
||||||
"integrity": "sha512-IaizpPP2UQU3MNyPH1u0Xxbm73D+4OupL0bjo4Hm0496e2wg3zuvoAIhubkD1NGy9fXILEExPQy87mweujEatA==",
|
"integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -542,9 +594,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
|
||||||
"integrity": "sha512-aCM29orANR0a8wk896p6UEgIfupReupnmISz6SUwMIwTGaTI8MuKdE0OD2LvEg8ondDyZdMvnaN3bW4nFbATPA==",
|
"integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -555,9 +607,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
|
||||||
"integrity": "sha512-0Xj1vZE3cbr/wda8d/m+UeuSL+TDpuozzdD4QaSzu/xSOMK0Su5RhIkF7KVHFQsobemUNHPLEcYllL7ZTCP/Cg==",
|
"integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -568,9 +620,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
|
||||||
"integrity": "sha512-kM/orjpolfA5yxsx84kI6bnK47AAZuWxglGKcNmokw2yy9i5eHY5UAjcX45jemTJnfHAWo3/hOoRqEeeTdL5hw==",
|
"integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -580,10 +632,23 @@
|
|||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||||
|
"version": "4.53.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
|
||||||
|
"integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"openharmony"
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
|
||||||
"integrity": "sha512-cNLH4psMEsWKILW0isbpQA2OvjXLbKvnkcJFmqAptPQbtLrobiapBJVj6RoIvg6UXVp5w0wnIfd/Q56cNpF+Ew==",
|
"integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -594,9 +659,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
|
||||||
"integrity": "sha512-OiEa5lRhiANpv4SfwYVgQ3opYWi/QmPDC5ve21m8G9pf6ZO+aX1g2EEF1/IFaM1xPSP7mK0msTRXlPs6mIagkg==",
|
"integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -606,10 +671,23 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||||
|
"version": "4.53.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
|
||||||
|
"integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
|
||||||
"integrity": "sha512-IKL9mewGZ5UuuX4NQlwOmxPyqielvkAPUS2s1cl6yWjjQvyN3h5JTdVFGD5Jr5xMjRC8setOfGQDVgX8V+dkjg==",
|
"integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -779,12 +857,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.10",
|
"version": "1.22.11",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
||||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
"integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-core-module": "^2.16.0",
|
"is-core-module": "^2.16.1",
|
||||||
"path-parse": "^1.0.7",
|
"path-parse": "^1.0.7",
|
||||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||||
},
|
},
|
||||||
@@ -799,9 +877,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.46.4",
|
"version": "4.53.3",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
|
||||||
"integrity": "sha512-YbxoxvoqNg9zAmw4+vzh1FkGAiZRK+LhnSrbSrSXMdZYsRPDWoshcSd/pldKRO6lWzv/e9TiJAVQyirYIeSIPQ==",
|
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.8"
|
"@types/estree": "1.0.8"
|
||||||
@@ -814,26 +892,28 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.46.4",
|
"@rollup/rollup-android-arm-eabi": "4.53.3",
|
||||||
"@rollup/rollup-android-arm64": "4.46.4",
|
"@rollup/rollup-android-arm64": "4.53.3",
|
||||||
"@rollup/rollup-darwin-arm64": "4.46.4",
|
"@rollup/rollup-darwin-arm64": "4.53.3",
|
||||||
"@rollup/rollup-darwin-x64": "4.46.4",
|
"@rollup/rollup-darwin-x64": "4.53.3",
|
||||||
"@rollup/rollup-freebsd-arm64": "4.46.4",
|
"@rollup/rollup-freebsd-arm64": "4.53.3",
|
||||||
"@rollup/rollup-freebsd-x64": "4.46.4",
|
"@rollup/rollup-freebsd-x64": "4.53.3",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.46.4",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.46.4",
|
"@rollup/rollup-linux-arm-musleabihf": "4.53.3",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.46.4",
|
"@rollup/rollup-linux-arm64-gnu": "4.53.3",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.46.4",
|
"@rollup/rollup-linux-arm64-musl": "4.53.3",
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": "4.46.4",
|
"@rollup/rollup-linux-loong64-gnu": "4.53.3",
|
||||||
"@rollup/rollup-linux-ppc64-gnu": "4.46.4",
|
"@rollup/rollup-linux-ppc64-gnu": "4.53.3",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.46.4",
|
"@rollup/rollup-linux-riscv64-gnu": "4.53.3",
|
||||||
"@rollup/rollup-linux-riscv64-musl": "4.46.4",
|
"@rollup/rollup-linux-riscv64-musl": "4.53.3",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.46.4",
|
"@rollup/rollup-linux-s390x-gnu": "4.53.3",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.46.4",
|
"@rollup/rollup-linux-x64-gnu": "4.53.3",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.46.4",
|
"@rollup/rollup-linux-x64-musl": "4.53.3",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.46.4",
|
"@rollup/rollup-openharmony-arm64": "4.53.3",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.46.4",
|
"@rollup/rollup-win32-arm64-msvc": "4.53.3",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.46.4",
|
"@rollup/rollup-win32-ia32-msvc": "4.53.3",
|
||||||
|
"@rollup/rollup-win32-x64-gnu": "4.53.3",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.53.3",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -897,9 +977,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/style-mod": {
|
"node_modules/style-mod": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
|
||||||
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
|
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/supports-preserve-symlinks-flag": {
|
"node_modules/supports-preserve-symlinks-flag": {
|
||||||
@@ -915,14 +995,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.43.1",
|
"version": "5.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz",
|
||||||
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
|
"integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
"acorn": "^8.14.0",
|
"acorn": "^8.15.0",
|
||||||
"commander": "^2.20.0",
|
"commander": "^2.20.0",
|
||||||
"source-map-support": "~0.5.20"
|
"source-map-support": "~0.5.20"
|
||||||
},
|
},
|
||||||
|
|||||||
2
deps/codemirror_src/package.json
vendored
@@ -7,6 +7,8 @@
|
|||||||
"@codemirror/lang-html": "^6.4.8",
|
"@codemirror/lang-html": "^6.4.8",
|
||||||
"@codemirror/lang-javascript": "^6.2.2",
|
"@codemirror/lang-javascript": "^6.2.2",
|
||||||
"@codemirror/lang-json": "^6.0.1",
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
|
"@codemirror/lang-markdown": "^6.0.1",
|
||||||
|
"@codemirror/lang-xml": "^6.0.1",
|
||||||
"@codemirror/theme-one-dark": "^6.1.2",
|
"@codemirror/theme-one-dark": "^6.1.2",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
|
|||||||
2
deps/libbacktrace
vendored
1
deps/openssl_src
vendored
2
deps/quickjs
vendored
3
deps/speedscope/release.txt
vendored
@@ -1,3 +0,0 @@
|
|||||||
speedscope@1.23.1
|
|
||||||
Mon Aug 11 11:43:09 PDT 2025
|
|
||||||
0cec0f82c334aed6cf19d43cabeadcda0d95e0fc
|
|
||||||
2531
deps/sqlite/shell.c
vendored
5713
deps/sqlite/sqlite3.c
vendored
415
deps/sqlite/sqlite3.h
vendored
@@ -146,9 +146,12 @@ extern "C" {
|
|||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.50.4"
|
#define SQLITE_VERSION "3.51.1"
|
||||||
#define SQLITE_VERSION_NUMBER 3050004
|
#define SQLITE_VERSION_NUMBER 3051001
|
||||||
#define SQLITE_SOURCE_ID "2025-07-30 19:33:53 4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3"
|
#define SQLITE_SOURCE_ID "2025-11-28 17:28:25 281fc0e9afc38674b9b0991943b9e9d1e64c6cbdb133d35f6f5c87ff6af38a88"
|
||||||
|
#define SQLITE_SCM_BRANCH "branch-3.51"
|
||||||
|
#define SQLITE_SCM_TAGS "release version-3.51.1"
|
||||||
|
#define SQLITE_SCM_DATETIME "2025-11-28T17:28:25.933Z"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
@@ -168,9 +171,9 @@ extern "C" {
|
|||||||
** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
|
** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
|
||||||
** </pre></blockquote>)^
|
** </pre></blockquote>)^
|
||||||
**
|
**
|
||||||
** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION]
|
** ^The sqlite3_version[] string constant contains the text of the
|
||||||
** macro. ^The sqlite3_libversion() function returns a pointer to the
|
** [SQLITE_VERSION] macro. ^The sqlite3_libversion() function returns a
|
||||||
** to the sqlite3_version[] string constant. The sqlite3_libversion()
|
** pointer to the sqlite3_version[] string constant. The sqlite3_libversion()
|
||||||
** function is provided for use in DLLs since DLL users usually do not have
|
** function is provided for use in DLLs since DLL users usually do not have
|
||||||
** direct access to string constants within the DLL. ^The
|
** direct access to string constants within the DLL. ^The
|
||||||
** sqlite3_libversion_number() function returns an integer equal to
|
** sqlite3_libversion_number() function returns an integer equal to
|
||||||
@@ -370,7 +373,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
|
|||||||
** without having to use a lot of C code.
|
** without having to use a lot of C code.
|
||||||
**
|
**
|
||||||
** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded,
|
** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded,
|
||||||
** semicolon-separate SQL statements passed into its 2nd argument,
|
** semicolon-separated SQL statements passed into its 2nd argument,
|
||||||
** in the context of the [database connection] passed in as its 1st
|
** in the context of the [database connection] passed in as its 1st
|
||||||
** argument. ^If the callback function of the 3rd argument to
|
** argument. ^If the callback function of the 3rd argument to
|
||||||
** sqlite3_exec() is not NULL, then it is invoked for each result row
|
** sqlite3_exec() is not NULL, then it is invoked for each result row
|
||||||
@@ -403,7 +406,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
|
|||||||
** result row is NULL then the corresponding string pointer for the
|
** result row is NULL then the corresponding string pointer for the
|
||||||
** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the
|
** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the
|
||||||
** sqlite3_exec() callback is an array of pointers to strings where each
|
** sqlite3_exec() callback is an array of pointers to strings where each
|
||||||
** entry represents the name of corresponding result column as obtained
|
** entry represents the name of a corresponding result column as obtained
|
||||||
** from [sqlite3_column_name()].
|
** from [sqlite3_column_name()].
|
||||||
**
|
**
|
||||||
** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer
|
** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer
|
||||||
@@ -497,6 +500,9 @@ SQLITE_API int sqlite3_exec(
|
|||||||
#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
|
#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8))
|
||||||
#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
|
#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8))
|
||||||
#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
|
#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8))
|
||||||
|
#define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8))
|
||||||
|
#define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8))
|
||||||
|
#define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8))
|
||||||
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
|
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
|
||||||
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
|
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
|
||||||
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
|
#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8))
|
||||||
@@ -531,6 +537,8 @@ SQLITE_API int sqlite3_exec(
|
|||||||
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
|
#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
|
||||||
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
|
#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8))
|
||||||
#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
|
#define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8))
|
||||||
|
#define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8))
|
||||||
|
#define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8))
|
||||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||||
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
|
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
|
||||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
||||||
@@ -589,7 +597,7 @@ SQLITE_API int sqlite3_exec(
|
|||||||
** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into
|
** Note in particular that passing the SQLITE_OPEN_EXCLUSIVE flag into
|
||||||
** [sqlite3_open_v2()] does *not* cause the underlying database file
|
** [sqlite3_open_v2()] does *not* cause the underlying database file
|
||||||
** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into
|
** to be opened using O_EXCL. Passing SQLITE_OPEN_EXCLUSIVE into
|
||||||
** [sqlite3_open_v2()] has historically be a no-op and might become an
|
** [sqlite3_open_v2()] has historically been a no-op and might become an
|
||||||
** error in future versions of SQLite.
|
** error in future versions of SQLite.
|
||||||
*/
|
*/
|
||||||
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
|
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
|
||||||
@@ -683,7 +691,7 @@ SQLITE_API int sqlite3_exec(
|
|||||||
** SQLite uses one of these integer values as the second
|
** SQLite uses one of these integer values as the second
|
||||||
** argument to calls it makes to the xLock() and xUnlock() methods
|
** argument to calls it makes to the xLock() and xUnlock() methods
|
||||||
** of an [sqlite3_io_methods] object. These values are ordered from
|
** of an [sqlite3_io_methods] object. These values are ordered from
|
||||||
** lest restrictive to most restrictive.
|
** least restrictive to most restrictive.
|
||||||
**
|
**
|
||||||
** The argument to xLock() is always SHARED or higher. The argument to
|
** The argument to xLock() is always SHARED or higher. The argument to
|
||||||
** xUnlock is either SHARED or NONE.
|
** xUnlock is either SHARED or NONE.
|
||||||
@@ -924,7 +932,7 @@ struct sqlite3_io_methods {
|
|||||||
** connection. See also [SQLITE_FCNTL_FILE_POINTER].
|
** connection. See also [SQLITE_FCNTL_FILE_POINTER].
|
||||||
**
|
**
|
||||||
** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
|
** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
|
||||||
** No longer in use.
|
** The SQLITE_FCNTL_SYNC_OMITTED file-control is no longer used.
|
||||||
**
|
**
|
||||||
** <li>[[SQLITE_FCNTL_SYNC]]
|
** <li>[[SQLITE_FCNTL_SYNC]]
|
||||||
** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and
|
** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and
|
||||||
@@ -999,7 +1007,7 @@ struct sqlite3_io_methods {
|
|||||||
**
|
**
|
||||||
** <li>[[SQLITE_FCNTL_VFSNAME]]
|
** <li>[[SQLITE_FCNTL_VFSNAME]]
|
||||||
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
|
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
|
||||||
** all [VFSes] in the VFS stack. The names are of all VFS shims and the
|
** all [VFSes] in the VFS stack. The names of all VFS shims and the
|
||||||
** final bottom-level VFS are written into memory obtained from
|
** final bottom-level VFS are written into memory obtained from
|
||||||
** [sqlite3_malloc()] and the result is stored in the char* variable
|
** [sqlite3_malloc()] and the result is stored in the char* variable
|
||||||
** that the fourth parameter of [sqlite3_file_control()] points to.
|
** that the fourth parameter of [sqlite3_file_control()] points to.
|
||||||
@@ -1013,7 +1021,7 @@ struct sqlite3_io_methods {
|
|||||||
** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
|
** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
|
||||||
** [VFSes] currently in use. ^(The argument X in
|
** [VFSes] currently in use. ^(The argument X in
|
||||||
** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be
|
** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be
|
||||||
** of type "[sqlite3_vfs] **". This opcodes will set *X
|
** of type "[sqlite3_vfs] **". This opcode will set *X
|
||||||
** to a pointer to the top-level VFS.)^
|
** to a pointer to the top-level VFS.)^
|
||||||
** ^When there are multiple VFS shims in the stack, this opcode finds the
|
** ^When there are multiple VFS shims in the stack, this opcode finds the
|
||||||
** upper-most shim only.
|
** upper-most shim only.
|
||||||
@@ -1203,7 +1211,7 @@ struct sqlite3_io_methods {
|
|||||||
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
|
** <li>[[SQLITE_FCNTL_EXTERNAL_READER]]
|
||||||
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
|
** The EXPERIMENTAL [SQLITE_FCNTL_EXTERNAL_READER] opcode is used to detect
|
||||||
** whether or not there is a database client in another process with a wal-mode
|
** whether or not there is a database client in another process with a wal-mode
|
||||||
** transaction open on the database or not. It is only available on unix.The
|
** transaction open on the database or not. It is only available on unix. The
|
||||||
** (void*) argument passed with this file-control should be a pointer to a
|
** (void*) argument passed with this file-control should be a pointer to a
|
||||||
** value of type (int). The integer value is set to 1 if the database is a wal
|
** value of type (int). The integer value is set to 1 if the database is a wal
|
||||||
** mode database and there exists at least one client in another process that
|
** mode database and there exists at least one client in another process that
|
||||||
@@ -1221,6 +1229,15 @@ struct sqlite3_io_methods {
|
|||||||
** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control
|
** database is not a temp db, then the [SQLITE_FCNTL_RESET_CACHE] file-control
|
||||||
** purges the contents of the in-memory page cache. If there is an open
|
** purges the contents of the in-memory page cache. If there is an open
|
||||||
** transaction, or if the db is a temp-db, this opcode is a no-op, not an error.
|
** transaction, or if the db is a temp-db, this opcode is a no-op, not an error.
|
||||||
|
**
|
||||||
|
** <li>[[SQLITE_FCNTL_FILESTAT]]
|
||||||
|
** The [SQLITE_FCNTL_FILESTAT] opcode returns low-level diagnostic information
|
||||||
|
** about the [sqlite3_file] objects used access the database and journal files
|
||||||
|
** for the given schema. The fourth parameter to [sqlite3_file_control()]
|
||||||
|
** should be an initialized [sqlite3_str] pointer. JSON text describing
|
||||||
|
** various aspects of the sqlite3_file object is appended to the sqlite3_str.
|
||||||
|
** The SQLITE_FCNTL_FILESTAT opcode is usually a no-op, unless compile-time
|
||||||
|
** options are used to enable it.
|
||||||
** </ul>
|
** </ul>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||||
@@ -1266,6 +1283,7 @@ struct sqlite3_io_methods {
|
|||||||
#define SQLITE_FCNTL_RESET_CACHE 42
|
#define SQLITE_FCNTL_RESET_CACHE 42
|
||||||
#define SQLITE_FCNTL_NULL_IO 43
|
#define SQLITE_FCNTL_NULL_IO 43
|
||||||
#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
|
#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
|
||||||
|
#define SQLITE_FCNTL_FILESTAT 45
|
||||||
|
|
||||||
/* deprecated names */
|
/* deprecated names */
|
||||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||||
@@ -1628,7 +1646,7 @@ struct sqlite3_vfs {
|
|||||||
** SQLite interfaces so that an application usually does not need to
|
** SQLite interfaces so that an application usually does not need to
|
||||||
** invoke sqlite3_initialize() directly. For example, [sqlite3_open()]
|
** invoke sqlite3_initialize() directly. For example, [sqlite3_open()]
|
||||||
** calls sqlite3_initialize() so the SQLite library will be automatically
|
** calls sqlite3_initialize() so the SQLite library will be automatically
|
||||||
** initialized when [sqlite3_open()] is called if it has not be initialized
|
** initialized when [sqlite3_open()] is called if it has not been initialized
|
||||||
** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT]
|
** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT]
|
||||||
** compile-time option, then the automatic calls to sqlite3_initialize()
|
** compile-time option, then the automatic calls to sqlite3_initialize()
|
||||||
** are omitted and the application must call sqlite3_initialize() directly
|
** are omitted and the application must call sqlite3_initialize() directly
|
||||||
@@ -1885,21 +1903,21 @@ struct sqlite3_mem_methods {
|
|||||||
** The [sqlite3_mem_methods]
|
** The [sqlite3_mem_methods]
|
||||||
** structure is filled with the currently defined memory allocation routines.)^
|
** structure is filled with the currently defined memory allocation routines.)^
|
||||||
** This option can be used to overload the default memory allocation
|
** This option can be used to overload the default memory allocation
|
||||||
** routines with a wrapper that simulations memory allocation failure or
|
** routines with a wrapper that simulates memory allocation failure or
|
||||||
** tracks memory usage, for example. </dd>
|
** tracks memory usage, for example. </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_SMALL_MALLOC]] <dt>SQLITE_CONFIG_SMALL_MALLOC</dt>
|
** [[SQLITE_CONFIG_SMALL_MALLOC]] <dt>SQLITE_CONFIG_SMALL_MALLOC</dt>
|
||||||
** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes single argument of
|
** <dd> ^The SQLITE_CONFIG_SMALL_MALLOC option takes a single argument of
|
||||||
** type int, interpreted as a boolean, which if true provides a hint to
|
** type int, interpreted as a boolean, which if true provides a hint to
|
||||||
** SQLite that it should avoid large memory allocations if possible.
|
** SQLite that it should avoid large memory allocations if possible.
|
||||||
** SQLite will run faster if it is free to make large memory allocations,
|
** SQLite will run faster if it is free to make large memory allocations,
|
||||||
** but some application might prefer to run slower in exchange for
|
** but some applications might prefer to run slower in exchange for
|
||||||
** guarantees about memory fragmentation that are possible if large
|
** guarantees about memory fragmentation that are possible if large
|
||||||
** allocations are avoided. This hint is normally off.
|
** allocations are avoided. This hint is normally off.
|
||||||
** </dd>
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
|
** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
|
||||||
** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int,
|
** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes a single argument of type int,
|
||||||
** interpreted as a boolean, which enables or disables the collection of
|
** interpreted as a boolean, which enables or disables the collection of
|
||||||
** memory allocation statistics. ^(When memory allocation statistics are
|
** memory allocation statistics. ^(When memory allocation statistics are
|
||||||
** disabled, the following SQLite interfaces become non-operational:
|
** disabled, the following SQLite interfaces become non-operational:
|
||||||
@@ -1944,7 +1962,7 @@ struct sqlite3_mem_methods {
|
|||||||
** ^If pMem is NULL and N is non-zero, then each database connection
|
** ^If pMem is NULL and N is non-zero, then each database connection
|
||||||
** does an initial bulk allocation for page cache memory
|
** does an initial bulk allocation for page cache memory
|
||||||
** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or
|
** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or
|
||||||
** of -1024*N bytes if N is negative, . ^If additional
|
** of -1024*N bytes if N is negative. ^If additional
|
||||||
** page cache memory is needed beyond what is provided by the initial
|
** page cache memory is needed beyond what is provided by the initial
|
||||||
** allocation, then SQLite goes to [sqlite3_malloc()] separately for each
|
** allocation, then SQLite goes to [sqlite3_malloc()] separately for each
|
||||||
** additional cache line. </dd>
|
** additional cache line. </dd>
|
||||||
@@ -1973,7 +1991,7 @@ struct sqlite3_mem_methods {
|
|||||||
** <dd> ^(The SQLITE_CONFIG_MUTEX option takes a single argument which is a
|
** <dd> ^(The SQLITE_CONFIG_MUTEX option takes a single argument which is a
|
||||||
** pointer to an instance of the [sqlite3_mutex_methods] structure.
|
** pointer to an instance of the [sqlite3_mutex_methods] structure.
|
||||||
** The argument specifies alternative low-level mutex routines to be used
|
** The argument specifies alternative low-level mutex routines to be used
|
||||||
** in place the mutex routines built into SQLite.)^ ^SQLite makes a copy of
|
** in place of the mutex routines built into SQLite.)^ ^SQLite makes a copy of
|
||||||
** the content of the [sqlite3_mutex_methods] structure before the call to
|
** the content of the [sqlite3_mutex_methods] structure before the call to
|
||||||
** [sqlite3_config()] returns. ^If SQLite is compiled with
|
** [sqlite3_config()] returns. ^If SQLite is compiled with
|
||||||
** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
|
** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
|
||||||
@@ -2015,7 +2033,7 @@ struct sqlite3_mem_methods {
|
|||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>
|
** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>
|
||||||
** <dd> ^(The SQLITE_CONFIG_GETPCACHE2 option takes a single argument which
|
** <dd> ^(The SQLITE_CONFIG_GETPCACHE2 option takes a single argument which
|
||||||
** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies of
|
** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies off
|
||||||
** the current page cache implementation into that object.)^ </dd>
|
** the current page cache implementation into that object.)^ </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
|
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
|
||||||
@@ -2032,7 +2050,7 @@ struct sqlite3_mem_methods {
|
|||||||
** the logger function is a copy of the first parameter to the corresponding
|
** the logger function is a copy of the first parameter to the corresponding
|
||||||
** [sqlite3_log()] call and is intended to be a [result code] or an
|
** [sqlite3_log()] call and is intended to be a [result code] or an
|
||||||
** [extended result code]. ^The third parameter passed to the logger is
|
** [extended result code]. ^The third parameter passed to the logger is
|
||||||
** log message after formatting via [sqlite3_snprintf()].
|
** a log message after formatting via [sqlite3_snprintf()].
|
||||||
** The SQLite logging interface is not reentrant; the logger function
|
** The SQLite logging interface is not reentrant; the logger function
|
||||||
** supplied by the application must not invoke any SQLite interface.
|
** supplied by the application must not invoke any SQLite interface.
|
||||||
** In a multi-threaded application, the application-defined logger
|
** In a multi-threaded application, the application-defined logger
|
||||||
@@ -2223,7 +2241,7 @@ struct sqlite3_mem_methods {
|
|||||||
** These constants are the available integer configuration options that
|
** These constants are the available integer configuration options that
|
||||||
** can be passed as the second parameter to the [sqlite3_db_config()] interface.
|
** 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
|
** The [sqlite3_db_config()] interface is a var-args function. It takes a
|
||||||
** variable number of parameters, though always at least two. The number of
|
** variable number of parameters, though always at least two. The number of
|
||||||
** parameters passed into sqlite3_db_config() depends on which of these
|
** parameters passed into sqlite3_db_config() depends on which of these
|
||||||
** constants is given as the second parameter. This documentation page
|
** constants is given as the second parameter. This documentation page
|
||||||
@@ -2335,17 +2353,20 @@ struct sqlite3_mem_methods {
|
|||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
|
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
|
||||||
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
|
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
|
||||||
** <dd> ^This option is used to enable or disable the
|
** <dd> ^This option is used to enable or disable using the
|
||||||
** [fts3_tokenizer()] function which is part of the
|
** [fts3_tokenizer()] function - part of the [FTS3] full-text search engine
|
||||||
** [FTS3] full-text search engine extension.
|
** extension - without using bound parameters as the parameters. Doing so
|
||||||
** There must be two additional arguments.
|
** is disabled by default. There must be two additional arguments. The first
|
||||||
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
|
** argument is an integer. If it is passed 0, then using fts3_tokenizer()
|
||||||
** positive to enable fts3_tokenizer() or negative to leave the setting
|
** without bound parameters is disabled. If it is passed a positive value,
|
||||||
** unchanged.
|
** then calling fts3_tokenizer without bound parameters is enabled. If it
|
||||||
** The second parameter is a pointer to an integer into which
|
** is passed a negative value, this setting is not modified - this can be
|
||||||
** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
|
** used to query for the current setting. The second parameter is a pointer
|
||||||
** following this call. The second parameter may be a NULL pointer, in
|
** to an integer into which is written 0 or 1 to indicate the current value
|
||||||
** which case the new setting is not reported back. </dd>
|
** of this setting (after it is modified, if applicable). The second
|
||||||
|
** parameter may be a NULL pointer, in which case the value of the setting
|
||||||
|
** is not reported back. Refer to [FTS3] documentation for further details.
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
|
** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
|
||||||
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
|
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
|
||||||
@@ -2357,8 +2378,8 @@ struct sqlite3_mem_methods {
|
|||||||
** When the first argument to this interface is 1, then only the C-API is
|
** 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
|
** 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.
|
** this interface is 0, then both the C-API and the SQL function are disabled.
|
||||||
** If the first argument is -1, then no changes are made to state of either the
|
** If the first argument is -1, then no changes are made to the state of either
|
||||||
** C-API or the SQL function.
|
** the C-API or the SQL function.
|
||||||
** The second parameter is a pointer to an integer into which
|
** The second parameter is a pointer to an integer into which
|
||||||
** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface
|
** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface
|
||||||
** is disabled or enabled following this call. The second parameter may
|
** is disabled or enabled following this call. The second parameter may
|
||||||
@@ -2476,7 +2497,7 @@ struct sqlite3_mem_methods {
|
|||||||
** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]]
|
** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]]
|
||||||
** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt>
|
** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt>
|
||||||
** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates
|
** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates
|
||||||
** the legacy behavior of the [ALTER TABLE RENAME] command such it
|
** the legacy behavior of the [ALTER TABLE RENAME] command such that it
|
||||||
** behaves as it did prior to [version 3.24.0] (2018-06-04). See the
|
** behaves as it did prior to [version 3.24.0] (2018-06-04). See the
|
||||||
** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for
|
** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for
|
||||||
** additional information. This feature can also be turned on and off
|
** additional information. This feature can also be turned on and off
|
||||||
@@ -2525,7 +2546,7 @@ struct sqlite3_mem_methods {
|
|||||||
** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
|
** <dt>SQLITE_DBCONFIG_LEGACY_FILE_FORMAT</dt>
|
||||||
** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
|
** <dd>The SQLITE_DBCONFIG_LEGACY_FILE_FORMAT option activates or deactivates
|
||||||
** the legacy file format flag. When activated, this flag causes all newly
|
** the legacy file format flag. When activated, this flag causes all newly
|
||||||
** created database file to have a schema format version number (the 4-byte
|
** created database files to have a schema format version number (the 4-byte
|
||||||
** integer found at offset 44 into the database header) of 1. This in turn
|
** integer found at offset 44 into the database header) of 1. This in turn
|
||||||
** means that the resulting database file will be readable and writable by
|
** means that the resulting database file will be readable and writable by
|
||||||
** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
|
** any SQLite version back to 3.0.0 ([dateof:3.0.0]). Without this setting,
|
||||||
@@ -2552,7 +2573,7 @@ struct sqlite3_mem_methods {
|
|||||||
** the database handle both when the SQL statement is prepared and when it
|
** the database handle both when the SQL statement is prepared and when it
|
||||||
** is stepped. The flag is set (collection of statistics is enabled)
|
** 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. <p>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
|
** an integer. The first argument is 1, 0, or -1 to enable, disable, or
|
||||||
** leave unchanged the statement scanstatus option. If the second argument
|
** leave unchanged the statement scanstatus option. If the second argument
|
||||||
** is not NULL, then the value of the statement scanstatus setting after
|
** is not NULL, then the value of the statement scanstatus setting after
|
||||||
** processing the first argument is written into the integer that the second
|
** processing the first argument is written into the integer that the second
|
||||||
@@ -2595,8 +2616,8 @@ struct sqlite3_mem_methods {
|
|||||||
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the
|
** <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.
|
** ability of the [ATTACH DATABASE] SQL command to open a database for writing.
|
||||||
** This capability is enabled by default. Applications can disable or
|
** This capability is enabled by default. Applications can disable or
|
||||||
** reenable this capability using the current DBCONFIG option. If the
|
** reenable this capability using the current DBCONFIG option. If
|
||||||
** the this capability is disabled, the [ATTACH] command will still work,
|
** this capability is disabled, the [ATTACH] command will still work,
|
||||||
** but the database will be opened read-only. If this option is disabled,
|
** 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,
|
** then the ability to create a new database using [ATTACH] is also disabled,
|
||||||
** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]
|
** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]
|
||||||
@@ -2630,7 +2651,7 @@ struct sqlite3_mem_methods {
|
|||||||
**
|
**
|
||||||
** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the
|
** <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.
|
** 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 first argument (the third parameter to sqlite3_db_config()) is an integer.
|
||||||
** The second argument is a pointer to an integer. If the first argument is 1,
|
** 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
|
** 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
|
** option is disabled. If the first argument is -1, then the option setting
|
||||||
@@ -2920,7 +2941,7 @@ SQLITE_API int sqlite3_is_interrupted(sqlite3*);
|
|||||||
** ^These routines return 0 if the statement is incomplete. ^If a
|
** ^These routines return 0 if the statement is incomplete. ^If a
|
||||||
** memory allocation fails, then SQLITE_NOMEM is returned.
|
** memory allocation fails, then SQLITE_NOMEM is returned.
|
||||||
**
|
**
|
||||||
** ^These routines do not parse the SQL statements thus
|
** ^These routines do not parse the SQL statements and thus
|
||||||
** will not detect syntactically incorrect SQL.
|
** will not detect syntactically incorrect SQL.
|
||||||
**
|
**
|
||||||
** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior
|
** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior
|
||||||
@@ -3037,7 +3058,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
|||||||
** indefinitely if possible. The results of passing any other negative value
|
** indefinitely if possible. The results of passing any other negative value
|
||||||
** are undefined.
|
** are undefined.
|
||||||
**
|
**
|
||||||
** Internally, each SQLite database handle store two timeout values - the
|
** Internally, each SQLite database handle stores two timeout values - the
|
||||||
** busy-timeout (used for rollback mode databases, or if the VFS does not
|
** busy-timeout (used for rollback mode databases, or if the VFS does not
|
||||||
** support blocking locks) and the setlk-timeout (used for blocking locks
|
** support blocking locks) and the setlk-timeout (used for blocking locks
|
||||||
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
|
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
|
||||||
@@ -3067,7 +3088,7 @@ SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
|
|||||||
** This is a legacy interface that is preserved for backwards compatibility.
|
** This is a legacy interface that is preserved for backwards compatibility.
|
||||||
** Use of this interface is not recommended.
|
** Use of this interface is not recommended.
|
||||||
**
|
**
|
||||||
** Definition: A <b>result table</b> is memory data structure created by the
|
** Definition: A <b>result table</b> is a memory data structure created by the
|
||||||
** [sqlite3_get_table()] interface. A result table records the
|
** [sqlite3_get_table()] interface. A result table records the
|
||||||
** complete query results from one or more queries.
|
** complete query results from one or more queries.
|
||||||
**
|
**
|
||||||
@@ -3210,7 +3231,7 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
|
|||||||
** ^Calling sqlite3_free() with a pointer previously returned
|
** ^Calling sqlite3_free() with a pointer previously returned
|
||||||
** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
|
** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
|
||||||
** that it might be reused. ^The sqlite3_free() routine is
|
** that it might be reused. ^The sqlite3_free() routine is
|
||||||
** a no-op if is called with a NULL pointer. Passing a NULL pointer
|
** a no-op if it is called with a NULL pointer. Passing a NULL pointer
|
||||||
** to sqlite3_free() is harmless. After being freed, memory
|
** to sqlite3_free() is harmless. After being freed, memory
|
||||||
** should neither be read nor written. Even reading previously freed
|
** should neither be read nor written. Even reading previously freed
|
||||||
** memory might result in a segmentation fault or other severe error.
|
** memory might result in a segmentation fault or other severe error.
|
||||||
@@ -3228,13 +3249,13 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
|
|||||||
** sqlite3_free(X).
|
** sqlite3_free(X).
|
||||||
** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation
|
** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation
|
||||||
** of at least N bytes in size or NULL if insufficient memory is available.
|
** of at least N bytes in size or NULL if insufficient memory is available.
|
||||||
** ^If M is the size of the prior allocation, then min(N,M) bytes
|
** ^If M is the size of the prior allocation, then min(N,M) bytes of the
|
||||||
** of the prior allocation are copied into the beginning of buffer returned
|
** prior allocation are copied into the beginning of the buffer returned
|
||||||
** by sqlite3_realloc(X,N) and the prior allocation is freed.
|
** by sqlite3_realloc(X,N) and the prior allocation is freed.
|
||||||
** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the
|
** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the
|
||||||
** prior allocation is not freed.
|
** prior allocation is not freed.
|
||||||
**
|
**
|
||||||
** ^The sqlite3_realloc64(X,N) interfaces works the same as
|
** ^The sqlite3_realloc64(X,N) interface works the same as
|
||||||
** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead
|
** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead
|
||||||
** of a 32-bit signed integer.
|
** of a 32-bit signed integer.
|
||||||
**
|
**
|
||||||
@@ -3284,7 +3305,7 @@ SQLITE_API sqlite3_uint64 sqlite3_msize(void*);
|
|||||||
** was last reset. ^The values returned by [sqlite3_memory_used()] and
|
** was last reset. ^The values returned by [sqlite3_memory_used()] and
|
||||||
** [sqlite3_memory_highwater()] include any overhead
|
** [sqlite3_memory_highwater()] include any overhead
|
||||||
** added by SQLite in its implementation of [sqlite3_malloc()],
|
** added by SQLite in its implementation of [sqlite3_malloc()],
|
||||||
** but not overhead added by the any underlying system library
|
** but not overhead added by any underlying system library
|
||||||
** routines that [sqlite3_malloc()] may call.
|
** routines that [sqlite3_malloc()] may call.
|
||||||
**
|
**
|
||||||
** ^The memory high-water mark is reset to the current value of
|
** ^The memory high-water mark is reset to the current value of
|
||||||
@@ -3736,7 +3757,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
|||||||
** there is no harm in trying.)
|
** there is no harm in trying.)
|
||||||
**
|
**
|
||||||
** ^(<dt>[SQLITE_OPEN_SHAREDCACHE]</dt>
|
** ^(<dt>[SQLITE_OPEN_SHAREDCACHE]</dt>
|
||||||
** <dd>The database is opened [shared cache] enabled, overriding
|
** <dd>The database is opened with [shared cache] enabled, overriding
|
||||||
** the default shared cache setting provided by
|
** the default shared cache setting provided by
|
||||||
** [sqlite3_enable_shared_cache()].)^
|
** [sqlite3_enable_shared_cache()].)^
|
||||||
** The [use of shared cache mode is discouraged] and hence shared cache
|
** The [use of shared cache mode is discouraged] and hence shared cache
|
||||||
@@ -3744,7 +3765,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
|
|||||||
** this option is a no-op.
|
** this option is a no-op.
|
||||||
**
|
**
|
||||||
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
|
** ^(<dt>[SQLITE_OPEN_PRIVATECACHE]</dt>
|
||||||
** <dd>The database is opened [shared cache] disabled, overriding
|
** <dd>The database is opened with [shared cache] disabled, overriding
|
||||||
** the default shared cache setting provided by
|
** the default shared cache setting provided by
|
||||||
** [sqlite3_enable_shared_cache()].)^
|
** [sqlite3_enable_shared_cache()].)^
|
||||||
**
|
**
|
||||||
@@ -4162,7 +4183,7 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename);
|
|||||||
** subsequent calls to other SQLite interface functions.)^
|
** subsequent calls to other SQLite interface functions.)^
|
||||||
**
|
**
|
||||||
** ^The sqlite3_errstr(E) interface returns the English-language text
|
** ^The sqlite3_errstr(E) interface returns the English-language text
|
||||||
** that describes the [result code] E, as UTF-8, or NULL if E is not an
|
** that describes the [result code] E, as UTF-8, or NULL if E is not a
|
||||||
** result code for which a text error message is available.
|
** result code for which a text error message is available.
|
||||||
** ^(Memory to hold the error message string is managed internally
|
** ^(Memory to hold the error message string is managed internally
|
||||||
** and must not be freed by the application)^.
|
** and must not be freed by the application)^.
|
||||||
@@ -4170,7 +4191,7 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename);
|
|||||||
** ^If the most recent error references a specific token in the input
|
** ^If the most recent error references a specific token in the input
|
||||||
** SQL, the sqlite3_error_offset() interface returns the byte offset
|
** SQL, the sqlite3_error_offset() interface returns the byte offset
|
||||||
** of the start of that token. ^The byte offset returned by
|
** of the start of that token. ^The byte offset returned by
|
||||||
** sqlite3_error_offset() assumes that the input SQL is UTF8.
|
** sqlite3_error_offset() assumes that the input SQL is UTF-8.
|
||||||
** ^If the most recent error does not reference a specific token in the input
|
** ^If the most recent error does not reference a specific token in the input
|
||||||
** SQL, then the sqlite3_error_offset() function returns -1.
|
** SQL, then the sqlite3_error_offset() function returns -1.
|
||||||
**
|
**
|
||||||
@@ -4195,6 +4216,34 @@ SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
|
|||||||
SQLITE_API const char *sqlite3_errstr(int);
|
SQLITE_API const char *sqlite3_errstr(int);
|
||||||
SQLITE_API int sqlite3_error_offset(sqlite3 *db);
|
SQLITE_API int sqlite3_error_offset(sqlite3 *db);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Set Error Codes And Message
|
||||||
|
** METHOD: sqlite3
|
||||||
|
**
|
||||||
|
** Set the error code of the database handle passed as the first argument
|
||||||
|
** to errcode, and the error message to a copy of nul-terminated string
|
||||||
|
** zErrMsg. If zErrMsg is passed NULL, then the error message is set to
|
||||||
|
** the default message associated with the supplied error code. Subsequent
|
||||||
|
** calls to [sqlite3_errcode()] and [sqlite3_errmsg()] and similar will
|
||||||
|
** return the values set by this routine in place of what was previously
|
||||||
|
** set by SQLite itself.
|
||||||
|
**
|
||||||
|
** This function returns SQLITE_OK if the error code and error message are
|
||||||
|
** successfully set, SQLITE_NOMEM if an OOM occurs, and SQLITE_MISUSE if
|
||||||
|
** the database handle is NULL or invalid.
|
||||||
|
**
|
||||||
|
** The error code and message set by this routine remains in effect until
|
||||||
|
** they are changed, either by another call to this routine or until they are
|
||||||
|
** changed to by SQLite itself to reflect the result of some subsquent
|
||||||
|
** API call.
|
||||||
|
**
|
||||||
|
** This function is intended for use by SQLite extensions or wrappers. The
|
||||||
|
** idea is that an extension or wrapper can use this routine to set error
|
||||||
|
** messages and error codes and thus behave more like a core SQLite
|
||||||
|
** feature from the point of view of an application.
|
||||||
|
*/
|
||||||
|
SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zErrMsg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Prepared Statement Object
|
** CAPI3REF: Prepared Statement Object
|
||||||
** KEYWORDS: {prepared statement} {prepared statements}
|
** KEYWORDS: {prepared statement} {prepared statements}
|
||||||
@@ -4269,8 +4318,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
|
|||||||
**
|
**
|
||||||
** These constants define various performance limits
|
** These constants define various performance limits
|
||||||
** that can be lowered at run-time using [sqlite3_limit()].
|
** that can be lowered at run-time using [sqlite3_limit()].
|
||||||
** The synopsis of the meanings of the various limits is shown below.
|
** A concise description of these limits follows, and additional information
|
||||||
** Additional information is available at [limits | Limits in SQLite].
|
** is available at [limits | Limits in SQLite].
|
||||||
**
|
**
|
||||||
** <dl>
|
** <dl>
|
||||||
** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt>
|
** [[SQLITE_LIMIT_LENGTH]] ^(<dt>SQLITE_LIMIT_LENGTH</dt>
|
||||||
@@ -4335,7 +4384,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
|
|||||||
/*
|
/*
|
||||||
** CAPI3REF: Prepare Flags
|
** CAPI3REF: Prepare Flags
|
||||||
**
|
**
|
||||||
** These constants define various flags that can be passed into
|
** These constants define various flags that can be passed into the
|
||||||
** "prepFlags" parameter of the [sqlite3_prepare_v3()] and
|
** "prepFlags" parameter of the [sqlite3_prepare_v3()] and
|
||||||
** [sqlite3_prepare16_v3()] interfaces.
|
** [sqlite3_prepare16_v3()] interfaces.
|
||||||
**
|
**
|
||||||
@@ -4422,7 +4471,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
|
|||||||
** there is a small performance advantage to passing an nByte parameter that
|
** there is a small performance advantage to passing an nByte parameter that
|
||||||
** is the number of bytes in the input string <i>including</i>
|
** is the number of bytes in the input string <i>including</i>
|
||||||
** the nul-terminator.
|
** the nul-terminator.
|
||||||
** Note that nByte measure the length of the input in bytes, not
|
** Note that nByte measures the length of the input in bytes, not
|
||||||
** characters, even for the UTF-16 interfaces.
|
** characters, even for the UTF-16 interfaces.
|
||||||
**
|
**
|
||||||
** ^If pzTail is not NULL then *pzTail is made to point to the first byte
|
** ^If pzTail is not NULL then *pzTail is made to point to the first byte
|
||||||
@@ -4556,7 +4605,7 @@ SQLITE_API int sqlite3_prepare16_v3(
|
|||||||
**
|
**
|
||||||
** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory
|
** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory
|
||||||
** is available to hold the result, or if the result would exceed the
|
** is available to hold the result, or if the result would exceed the
|
||||||
** the maximum string length determined by the [SQLITE_LIMIT_LENGTH].
|
** maximum string length determined by the [SQLITE_LIMIT_LENGTH].
|
||||||
**
|
**
|
||||||
** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of
|
** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of
|
||||||
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
|
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
|
||||||
@@ -4744,7 +4793,7 @@ typedef struct sqlite3_value sqlite3_value;
|
|||||||
**
|
**
|
||||||
** The context in which an SQL function executes is stored in an
|
** The context in which an SQL function executes is stored in an
|
||||||
** sqlite3_context object. ^A pointer to an sqlite3_context object
|
** sqlite3_context object. ^A pointer to an sqlite3_context object
|
||||||
** is always first parameter to [application-defined SQL functions].
|
** is always the first parameter to [application-defined SQL functions].
|
||||||
** The application-defined SQL function implementation will pass this
|
** The application-defined SQL function implementation will pass this
|
||||||
** pointer through into calls to [sqlite3_result_int | sqlite3_result()],
|
** pointer through into calls to [sqlite3_result_int | sqlite3_result()],
|
||||||
** [sqlite3_aggregate_context()], [sqlite3_user_data()],
|
** [sqlite3_aggregate_context()], [sqlite3_user_data()],
|
||||||
@@ -4868,9 +4917,11 @@ typedef struct sqlite3_context sqlite3_context;
|
|||||||
** associated with the pointer P of type T. ^D is either a NULL pointer or
|
** associated with the pointer P of type T. ^D is either a NULL pointer or
|
||||||
** a pointer to a destructor function for P. ^SQLite will invoke the
|
** a pointer to a destructor function for P. ^SQLite will invoke the
|
||||||
** destructor D with a single argument of P when it is finished using
|
** destructor D with a single argument of P when it is finished using
|
||||||
** P. The T parameter should be a static string, preferably a string
|
** P, even if the call to sqlite3_bind_pointer() fails. Due to a
|
||||||
** literal. The sqlite3_bind_pointer() routine is part of the
|
** historical design quirk, results are undefined if D is
|
||||||
** [pointer passing interface] added for SQLite 3.20.0.
|
** SQLITE_TRANSIENT. The T parameter should be a static string,
|
||||||
|
** preferably a string literal. The sqlite3_bind_pointer() routine is
|
||||||
|
** part of the [pointer passing interface] added for SQLite 3.20.0.
|
||||||
**
|
**
|
||||||
** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer
|
** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer
|
||||||
** for the [prepared statement] or with a prepared statement for which
|
** for the [prepared statement] or with a prepared statement for which
|
||||||
@@ -5481,7 +5532,7 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol);
|
|||||||
**
|
**
|
||||||
** ^The sqlite3_finalize() function is called to delete a [prepared statement].
|
** ^The sqlite3_finalize() function is called to delete a [prepared statement].
|
||||||
** ^If the most recent evaluation of the statement encountered no errors
|
** ^If the most recent evaluation of the statement encountered no errors
|
||||||
** or if the statement is never been evaluated, then sqlite3_finalize() returns
|
** or if the statement has never been evaluated, then sqlite3_finalize() returns
|
||||||
** SQLITE_OK. ^If the most recent evaluation of statement S failed, then
|
** SQLITE_OK. ^If the most recent evaluation of statement S failed, then
|
||||||
** sqlite3_finalize(S) returns the appropriate [error code] or
|
** sqlite3_finalize(S) returns the appropriate [error code] or
|
||||||
** [extended error code].
|
** [extended error code].
|
||||||
@@ -5713,7 +5764,7 @@ SQLITE_API int sqlite3_create_window_function(
|
|||||||
/*
|
/*
|
||||||
** CAPI3REF: Text Encodings
|
** CAPI3REF: Text Encodings
|
||||||
**
|
**
|
||||||
** These constant define integer codes that represent the various
|
** These constants define integer codes that represent the various
|
||||||
** text encodings supported by SQLite.
|
** text encodings supported by SQLite.
|
||||||
*/
|
*/
|
||||||
#define SQLITE_UTF8 1 /* IMP: R-37514-35566 */
|
#define SQLITE_UTF8 1 /* IMP: R-37514-35566 */
|
||||||
@@ -5805,7 +5856,7 @@ SQLITE_API int sqlite3_create_window_function(
|
|||||||
** result.
|
** result.
|
||||||
** Every function that invokes [sqlite3_result_subtype()] should have this
|
** Every function that invokes [sqlite3_result_subtype()] should have this
|
||||||
** property. If it does not, then the call to [sqlite3_result_subtype()]
|
** property. If it does not, then the call to [sqlite3_result_subtype()]
|
||||||
** might become a no-op if the function is used as term in an
|
** might become a no-op if the function is used as a term in an
|
||||||
** [expression index]. On the other hand, SQL functions that never invoke
|
** [expression index]. On the other hand, SQL functions that never invoke
|
||||||
** [sqlite3_result_subtype()] should avoid setting this property, as the
|
** [sqlite3_result_subtype()] should avoid setting this property, as the
|
||||||
** purpose of this property is to disable certain optimizations that are
|
** purpose of this property is to disable certain optimizations that are
|
||||||
@@ -5932,7 +5983,7 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
|
|||||||
** sqlite3_value_nochange(X) interface returns true if and only if
|
** sqlite3_value_nochange(X) interface returns true if and only if
|
||||||
** the column corresponding to X is unchanged by the UPDATE operation
|
** the column corresponding to X is unchanged by the UPDATE operation
|
||||||
** that the xUpdate method call was invoked to implement and if
|
** that the xUpdate method call was invoked to implement and if
|
||||||
** and the prior [xColumn] method call that was invoked to extracted
|
** the prior [xColumn] method call that was invoked to extract
|
||||||
** the value for that column returned without setting a result (probably
|
** the value for that column returned without setting a result (probably
|
||||||
** because it queried [sqlite3_vtab_nochange()] and found that the column
|
** because it queried [sqlite3_vtab_nochange()] and found that the column
|
||||||
** was unchanging). ^Within an [xUpdate] method, any value for which
|
** was unchanging). ^Within an [xUpdate] method, any value for which
|
||||||
@@ -6205,6 +6256,7 @@ SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(voi
|
|||||||
** or a NULL pointer if there were no prior calls to
|
** or a NULL pointer if there were no prior calls to
|
||||||
** sqlite3_set_clientdata() with the same values of D and N.
|
** sqlite3_set_clientdata() with the same values of D and N.
|
||||||
** Names are compared using strcmp() and are thus case sensitive.
|
** Names are compared using strcmp() and are thus case sensitive.
|
||||||
|
** It returns 0 on success and SQLITE_NOMEM on allocation failure.
|
||||||
**
|
**
|
||||||
** If P and X are both non-NULL, then the destructor X is invoked with
|
** If P and X are both non-NULL, then the destructor X is invoked with
|
||||||
** argument P on the first of the following occurrences:
|
** argument P on the first of the following occurrences:
|
||||||
@@ -8881,9 +8933,18 @@ SQLITE_API int sqlite3_status64(
|
|||||||
** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
|
** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
|
||||||
** non-zero [error code] on failure.
|
** non-zero [error code] on failure.
|
||||||
**
|
**
|
||||||
|
** ^The sqlite3_db_status64(D,O,C,H,R) routine works exactly the same
|
||||||
|
** way as sqlite3_db_status(D,O,C,H,R) routine except that the C and H
|
||||||
|
** parameters are pointer to 64-bit integers (type: sqlite3_int64) instead
|
||||||
|
** of pointers to 32-bit integers, which allows larger status values
|
||||||
|
** to be returned. If a status value exceeds 2,147,483,647 then
|
||||||
|
** sqlite3_db_status() will truncate the value whereas sqlite3_db_status64()
|
||||||
|
** will return the full value.
|
||||||
|
**
|
||||||
** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
|
** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
|
||||||
|
SQLITE_API int sqlite3_db_status64(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Status Parameters for database connections
|
** CAPI3REF: Status Parameters for database connections
|
||||||
@@ -8980,6 +9041,10 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
|
|||||||
** If an IO or other error occurs while writing a page to disk, the effect
|
** If an IO or other error occurs while writing a page to disk, the effect
|
||||||
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
|
** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
|
||||||
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
|
** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
|
||||||
|
** <p>
|
||||||
|
** ^(There is overlap between the quantities measured by this parameter
|
||||||
|
** (SQLITE_DBSTATUS_CACHE_WRITE) and SQLITE_DBSTATUS_TEMPBUF_SPILL.
|
||||||
|
** Resetting one will reduce the other.)^
|
||||||
** </dd>
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
|
** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt>
|
||||||
@@ -8995,6 +9060,18 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
|
|||||||
** <dd>This parameter returns zero for the current value if and only if
|
** <dd>This parameter returns zero for the current value if and only if
|
||||||
** all foreign key constraints (deferred or immediate) have been
|
** all foreign key constraints (deferred or immediate) have been
|
||||||
** resolved.)^ ^The highwater mark is always 0.
|
** resolved.)^ ^The highwater mark is always 0.
|
||||||
|
**
|
||||||
|
** [[SQLITE_DBSTATUS_TEMPBUF_SPILL] ^(<dt>SQLITE_DBSTATUS_TEMPBUF_SPILL</dt>
|
||||||
|
** <dd>^(This parameter returns the number of bytes written to temporary
|
||||||
|
** files on disk that could have been kept in memory had sufficient memory
|
||||||
|
** been available. This value includes writes to intermediate tables that
|
||||||
|
** are part of complex queries, external sorts that spill to disk, and
|
||||||
|
** writes to TEMP tables.)^
|
||||||
|
** ^The highwater mark is always 0.
|
||||||
|
** <p>
|
||||||
|
** ^(There is overlap between the quantities measured by this parameter
|
||||||
|
** (SQLITE_DBSTATUS_TEMPBUF_SPILL) and SQLITE_DBSTATUS_CACHE_WRITE.
|
||||||
|
** Resetting one will reduce the other.)^
|
||||||
** </dd>
|
** </dd>
|
||||||
** </dl>
|
** </dl>
|
||||||
*/
|
*/
|
||||||
@@ -9011,7 +9088,8 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
|
|||||||
#define SQLITE_DBSTATUS_DEFERRED_FKS 10
|
#define SQLITE_DBSTATUS_DEFERRED_FKS 10
|
||||||
#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
|
#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
|
||||||
#define SQLITE_DBSTATUS_CACHE_SPILL 12
|
#define SQLITE_DBSTATUS_CACHE_SPILL 12
|
||||||
#define SQLITE_DBSTATUS_MAX 12 /* Largest defined DBSTATUS */
|
#define SQLITE_DBSTATUS_TEMPBUF_SPILL 13
|
||||||
|
#define SQLITE_DBSTATUS_MAX 13 /* Largest defined DBSTATUS */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -9776,7 +9854,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
|
|||||||
** is the number of pages currently in the write-ahead log file,
|
** is the number of pages currently in the write-ahead log file,
|
||||||
** including those that were just committed.
|
** including those that were just committed.
|
||||||
**
|
**
|
||||||
** The callback function should normally return [SQLITE_OK]. ^If an error
|
** ^The callback function should normally return [SQLITE_OK]. ^If an error
|
||||||
** code is returned, that error will propagate back up through the
|
** code is returned, that error will propagate back up through the
|
||||||
** SQLite code base to cause the statement that provoked the callback
|
** SQLite code base to cause the statement that provoked the callback
|
||||||
** to report an error, though the commit will have still occurred. If the
|
** to report an error, though the commit will have still occurred. If the
|
||||||
@@ -9784,13 +9862,26 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
|
|||||||
** that does not correspond to any valid SQLite error code, the results
|
** that does not correspond to any valid SQLite error code, the results
|
||||||
** are undefined.
|
** are undefined.
|
||||||
**
|
**
|
||||||
** A single database handle may have at most a single write-ahead log callback
|
** ^A single database handle may have at most a single write-ahead log
|
||||||
** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any
|
** callback registered at one time. ^Calling [sqlite3_wal_hook()]
|
||||||
** previously registered write-ahead log callback. ^The return value is
|
** replaces the default behavior or previously registered write-ahead
|
||||||
** a copy of the third parameter from the previous call, if any, or 0.
|
** log callback.
|
||||||
** ^Note that the [sqlite3_wal_autocheckpoint()] interface and the
|
**
|
||||||
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
|
** ^The return value is a copy of the third parameter from the
|
||||||
** overwrite any prior [sqlite3_wal_hook()] settings.
|
** previous call, if any, or 0.
|
||||||
|
**
|
||||||
|
** ^The [sqlite3_wal_autocheckpoint()] interface and the
|
||||||
|
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and
|
||||||
|
** will overwrite any prior [sqlite3_wal_hook()] settings.
|
||||||
|
**
|
||||||
|
** ^If a write-ahead log callback is set using this function then
|
||||||
|
** [sqlite3_wal_checkpoint_v2()] or [PRAGMA wal_checkpoint]
|
||||||
|
** should be invoked periodically to keep the write-ahead log file
|
||||||
|
** from growing without bound.
|
||||||
|
**
|
||||||
|
** ^Passing a NULL pointer for the callback disables automatic
|
||||||
|
** checkpointing entirely. To re-enable the default behavior, call
|
||||||
|
** sqlite3_wal_autocheckpoint(db,1000) or use [PRAGMA wal_checkpoint].
|
||||||
*/
|
*/
|
||||||
SQLITE_API void *sqlite3_wal_hook(
|
SQLITE_API void *sqlite3_wal_hook(
|
||||||
sqlite3*,
|
sqlite3*,
|
||||||
@@ -9807,7 +9898,7 @@ SQLITE_API void *sqlite3_wal_hook(
|
|||||||
** to automatically [checkpoint]
|
** to automatically [checkpoint]
|
||||||
** after committing a transaction if there are N or
|
** after committing a transaction if there are N or
|
||||||
** more frames in the [write-ahead log] file. ^Passing zero or
|
** more frames in the [write-ahead log] file. ^Passing zero or
|
||||||
** a negative value as the nFrame parameter disables automatic
|
** a negative value as the N parameter disables automatic
|
||||||
** checkpoints entirely.
|
** checkpoints entirely.
|
||||||
**
|
**
|
||||||
** ^The callback registered by this function replaces any existing callback
|
** ^The callback registered by this function replaces any existing callback
|
||||||
@@ -9823,9 +9914,10 @@ SQLITE_API void *sqlite3_wal_hook(
|
|||||||
**
|
**
|
||||||
** ^Every new [database connection] defaults to having the auto-checkpoint
|
** ^Every new [database connection] defaults to having the auto-checkpoint
|
||||||
** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
|
** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
|
||||||
** pages. The use of this interface
|
** pages.
|
||||||
** is only necessary if the default setting is found to be suboptimal
|
**
|
||||||
** for a particular application.
|
** ^The use of this interface is only necessary if the default setting
|
||||||
|
** is found to be suboptimal for a particular application.
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
|
SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
|
||||||
|
|
||||||
@@ -9890,6 +9982,11 @@ SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
|
|||||||
** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
|
** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
|
||||||
** addition that it also truncates the log file to zero bytes just prior
|
** addition that it also truncates the log file to zero bytes just prior
|
||||||
** to a successful return.
|
** to a successful return.
|
||||||
|
**
|
||||||
|
** <dt>SQLITE_CHECKPOINT_NOOP<dd>
|
||||||
|
** ^This mode always checkpoints zero frames. The only reason to invoke
|
||||||
|
** a NOOP checkpoint is to access the values returned by
|
||||||
|
** sqlite3_wal_checkpoint_v2() via output parameters *pnLog and *pnCkpt.
|
||||||
** </dl>
|
** </dl>
|
||||||
**
|
**
|
||||||
** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
|
** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
|
||||||
@@ -9960,6 +10057,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
|
|||||||
** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
|
** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
|
||||||
** meaning of each of these checkpoint modes.
|
** meaning of each of these checkpoint modes.
|
||||||
*/
|
*/
|
||||||
|
#define SQLITE_CHECKPOINT_NOOP -1 /* Do no work at all */
|
||||||
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
|
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
|
||||||
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
|
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
|
||||||
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
|
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
|
||||||
@@ -10328,7 +10426,7 @@ SQLITE_API int sqlite3_vtab_in(sqlite3_index_info*, int iCons, int bHandle);
|
|||||||
** ){
|
** ){
|
||||||
** // do something with pVal
|
** // do something with pVal
|
||||||
** }
|
** }
|
||||||
** if( rc!=SQLITE_OK ){
|
** if( rc!=SQLITE_DONE ){
|
||||||
** // an error has occurred
|
** // an error has occurred
|
||||||
** }
|
** }
|
||||||
** </pre></blockquote>)^
|
** </pre></blockquote>)^
|
||||||
@@ -10787,7 +10885,7 @@ typedef struct sqlite3_snapshot {
|
|||||||
** The [sqlite3_snapshot_get()] interface is only available when the
|
** The [sqlite3_snapshot_get()] interface is only available when the
|
||||||
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
||||||
*/
|
*/
|
||||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
|
SQLITE_API int sqlite3_snapshot_get(
|
||||||
sqlite3 *db,
|
sqlite3 *db,
|
||||||
const char *zSchema,
|
const char *zSchema,
|
||||||
sqlite3_snapshot **ppSnapshot
|
sqlite3_snapshot **ppSnapshot
|
||||||
@@ -10836,7 +10934,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
|
|||||||
** The [sqlite3_snapshot_open()] interface is only available when the
|
** The [sqlite3_snapshot_open()] interface is only available when the
|
||||||
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
||||||
*/
|
*/
|
||||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
|
SQLITE_API int sqlite3_snapshot_open(
|
||||||
sqlite3 *db,
|
sqlite3 *db,
|
||||||
const char *zSchema,
|
const char *zSchema,
|
||||||
sqlite3_snapshot *pSnapshot
|
sqlite3_snapshot *pSnapshot
|
||||||
@@ -10853,7 +10951,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
|
|||||||
** The [sqlite3_snapshot_free()] interface is only available when the
|
** The [sqlite3_snapshot_free()] interface is only available when the
|
||||||
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used.
|
||||||
*/
|
*/
|
||||||
SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
|
SQLITE_API void sqlite3_snapshot_free(sqlite3_snapshot*);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Compare the ages of two snapshot handles.
|
** CAPI3REF: Compare the ages of two snapshot handles.
|
||||||
@@ -10880,7 +10978,7 @@ SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
|
|||||||
** This interface is only available if SQLite is compiled with the
|
** This interface is only available if SQLite is compiled with the
|
||||||
** [SQLITE_ENABLE_SNAPSHOT] option.
|
** [SQLITE_ENABLE_SNAPSHOT] option.
|
||||||
*/
|
*/
|
||||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
|
SQLITE_API int sqlite3_snapshot_cmp(
|
||||||
sqlite3_snapshot *p1,
|
sqlite3_snapshot *p1,
|
||||||
sqlite3_snapshot *p2
|
sqlite3_snapshot *p2
|
||||||
);
|
);
|
||||||
@@ -10908,7 +11006,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
|
|||||||
** This interface is only available if SQLite is compiled with the
|
** This interface is only available if SQLite is compiled with the
|
||||||
** [SQLITE_ENABLE_SNAPSHOT] option.
|
** [SQLITE_ENABLE_SNAPSHOT] option.
|
||||||
*/
|
*/
|
||||||
SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
|
SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Serialize a database
|
** CAPI3REF: Serialize a database
|
||||||
@@ -10982,12 +11080,13 @@ SQLITE_API unsigned char *sqlite3_serialize(
|
|||||||
**
|
**
|
||||||
** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
|
** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the
|
||||||
** [database connection] D to disconnect from database S and then
|
** [database connection] D to disconnect from database S and then
|
||||||
** reopen S as an in-memory database based on the serialization contained
|
** reopen S as an in-memory database based on the serialization
|
||||||
** in P. The serialized database P is N bytes in size. M is the size of
|
** contained in P. If S is a NULL pointer, the main database is
|
||||||
** the buffer P, which might be larger than N. If M is larger than N, and
|
** used. The serialized database P is N bytes in size. M is the size
|
||||||
** the SQLITE_DESERIALIZE_READONLY bit is not set in F, then SQLite is
|
** of the buffer P, which might be larger than N. If M is larger than
|
||||||
** permitted to add content to the in-memory database as long as the total
|
** N, and the SQLITE_DESERIALIZE_READONLY bit is not set in F, then
|
||||||
** size does not exceed M bytes.
|
** SQLite is permitted to add content to the in-memory database as
|
||||||
|
** long as the total size does not exceed M bytes.
|
||||||
**
|
**
|
||||||
** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
|
** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will
|
||||||
** invoke sqlite3_free() on the serialization buffer when the database
|
** invoke sqlite3_free() on the serialization buffer when the database
|
||||||
@@ -11054,6 +11153,54 @@ SQLITE_API int sqlite3_deserialize(
|
|||||||
#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
|
#define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */
|
||||||
#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
|
#define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Bind array values to the CARRAY table-valued function
|
||||||
|
**
|
||||||
|
** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to
|
||||||
|
** one of the first argument of the [carray() table-valued function]. The
|
||||||
|
** S parameter is a pointer to the [prepared statement] that uses the carray()
|
||||||
|
** functions. I is the parameter index to be bound. P is a pointer to the
|
||||||
|
** array to be bound, and N is the number of eements in the array. The
|
||||||
|
** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64],
|
||||||
|
** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to
|
||||||
|
** indicate the datatype of the array being bound. The X argument is not a
|
||||||
|
** NULL pointer, then SQLite will invoke the function X on the P parameter
|
||||||
|
** after it has finished using P, even if the call to
|
||||||
|
** sqlite3_carray_bind() fails. The special-case finalizer
|
||||||
|
** SQLITE_TRANSIENT has no effect here.
|
||||||
|
*/
|
||||||
|
SQLITE_API int sqlite3_carray_bind(
|
||||||
|
sqlite3_stmt *pStmt, /* Statement to be bound */
|
||||||
|
int i, /* Parameter index */
|
||||||
|
void *aData, /* Pointer to array data */
|
||||||
|
int nData, /* Number of data elements */
|
||||||
|
int mFlags, /* CARRAY flags */
|
||||||
|
void (*xDel)(void*) /* Destructor for aData */
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Datatypes for the CARRAY table-valued function
|
||||||
|
**
|
||||||
|
** The fifth argument to the [sqlite3_carray_bind()] interface musts be
|
||||||
|
** one of the following constants, to specify the datatype of the array
|
||||||
|
** that is being bound into the [carray table-valued function].
|
||||||
|
*/
|
||||||
|
#define SQLITE_CARRAY_INT32 0 /* Data is 32-bit signed integers */
|
||||||
|
#define SQLITE_CARRAY_INT64 1 /* Data is 64-bit signed integers */
|
||||||
|
#define SQLITE_CARRAY_DOUBLE 2 /* Data is doubles */
|
||||||
|
#define SQLITE_CARRAY_TEXT 3 /* Data is char* */
|
||||||
|
#define SQLITE_CARRAY_BLOB 4 /* Data is struct iovec */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Versions of the above #defines that omit the initial SQLITE_, for
|
||||||
|
** legacy compatibility.
|
||||||
|
*/
|
||||||
|
#define CARRAY_INT32 0 /* Data is 32-bit signed integers */
|
||||||
|
#define CARRAY_INT64 1 /* Data is 64-bit signed integers */
|
||||||
|
#define CARRAY_DOUBLE 2 /* Data is doubles */
|
||||||
|
#define CARRAY_TEXT 3 /* Data is char* */
|
||||||
|
#define CARRAY_BLOB 4 /* Data is struct iovec */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Undo the hack that converts floating point types to integer for
|
** Undo the hack that converts floating point types to integer for
|
||||||
** builds on processors without floating point support.
|
** builds on processors without floating point support.
|
||||||
@@ -12313,14 +12460,32 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
|
|||||||
** update the "main" database attached to handle db with the changes found in
|
** update the "main" database attached to handle db with the changes found in
|
||||||
** the changeset passed via the second and third arguments.
|
** the changeset passed via the second and third arguments.
|
||||||
**
|
**
|
||||||
|
** All changes made by these functions are enclosed in a savepoint transaction.
|
||||||
|
** If any other error (aside from a constraint failure when attempting to
|
||||||
|
** write to the target database) occurs, then the savepoint transaction is
|
||||||
|
** rolled back, restoring the target database to its original state, and an
|
||||||
|
** SQLite error code returned. Additionally, starting with version 3.51.0,
|
||||||
|
** an error code and error message that may be accessed using the
|
||||||
|
** [sqlite3_errcode()] and [sqlite3_errmsg()] APIs are left in the database
|
||||||
|
** handle.
|
||||||
|
**
|
||||||
** The fourth argument (xFilter) passed to these functions is the "filter
|
** The fourth argument (xFilter) passed to these functions is the "filter
|
||||||
** callback". If it is not NULL, then for each table affected by at least one
|
** callback". This may be passed NULL, in which case all changes in the
|
||||||
** change in the changeset, the filter callback is invoked with
|
** changeset are applied to the database. For sqlite3changeset_apply() and
|
||||||
** the table name as the second argument, and a copy of the context pointer
|
** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once
|
||||||
** passed as the sixth argument as the first. If the "filter callback"
|
** for each table affected by at least one change in the changeset. In this
|
||||||
** returns zero, then no attempt is made to apply any changes to the table.
|
** case the table name is passed as the second argument, and a copy of
|
||||||
** Otherwise, if the return value is non-zero or the xFilter argument to
|
** the context pointer passed as the sixth argument to apply() or apply_v2()
|
||||||
** is NULL, all changes related to the table are attempted.
|
** as the first. If the "filter callback" returns zero, then no attempt is
|
||||||
|
** made to apply any changes to the table. Otherwise, if the return value is
|
||||||
|
** non-zero, all changes related to the table are attempted.
|
||||||
|
**
|
||||||
|
** For sqlite3_changeset_apply_v3(), the xFilter callback is invoked once
|
||||||
|
** per change. The second argument in this case is an sqlite3_changeset_iter
|
||||||
|
** that may be queried using the usual APIs for the details of the current
|
||||||
|
** change. If the "filter callback" returns zero in this case, then no attempt
|
||||||
|
** is made to apply the current change. If it returns non-zero, the change
|
||||||
|
** is applied.
|
||||||
**
|
**
|
||||||
** For each table that is not excluded by the filter callback, this function
|
** For each table that is not excluded by the filter callback, this function
|
||||||
** tests that the target database contains a compatible table. A table is
|
** tests that the target database contains a compatible table. A table is
|
||||||
@@ -12341,11 +12506,11 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
|
|||||||
** one such warning is issued for each table in the changeset.
|
** one such warning is issued for each table in the changeset.
|
||||||
**
|
**
|
||||||
** For each change for which there is a compatible table, an attempt is made
|
** For each change for which there is a compatible table, an attempt is made
|
||||||
** to modify the table contents according to the UPDATE, INSERT or DELETE
|
** to modify the table contents according to each UPDATE, INSERT or DELETE
|
||||||
** change. If a change cannot be applied cleanly, the conflict handler
|
** change that is not excluded by a filter callback. If a change cannot be
|
||||||
** function passed as the fifth argument to sqlite3changeset_apply() may be
|
** applied cleanly, the conflict handler function passed as the fifth argument
|
||||||
** invoked. A description of exactly when the conflict handler is invoked for
|
** to sqlite3changeset_apply() may be invoked. A description of exactly when
|
||||||
** each type of change is below.
|
** the conflict handler is invoked for each type of change is below.
|
||||||
**
|
**
|
||||||
** Unlike the xFilter argument, xConflict may not be passed NULL. The results
|
** Unlike the xFilter argument, xConflict may not be passed NULL. The results
|
||||||
** of passing anything other than a valid function pointer as the xConflict
|
** of passing anything other than a valid function pointer as the xConflict
|
||||||
@@ -12441,12 +12606,6 @@ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup*);
|
|||||||
** This can be used to further customize the application's conflict
|
** This can be used to further customize the application's conflict
|
||||||
** resolution strategy.
|
** resolution strategy.
|
||||||
**
|
**
|
||||||
** All changes made by these functions are enclosed in a savepoint transaction.
|
|
||||||
** If any other error (aside from a constraint failure when attempting to
|
|
||||||
** write to the target database) occurs, then the savepoint transaction is
|
|
||||||
** rolled back, restoring the target database to its original state, and an
|
|
||||||
** SQLite error code returned.
|
|
||||||
**
|
|
||||||
** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
|
** If the output parameters (ppRebase) and (pnRebase) are non-NULL and
|
||||||
** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
|
** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2()
|
||||||
** may set (*ppRebase) to point to a "rebase" that may be used with the
|
** may set (*ppRebase) to point to a "rebase" that may be used with the
|
||||||
@@ -12496,6 +12655,23 @@ SQLITE_API int sqlite3changeset_apply_v2(
|
|||||||
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
||||||
int flags /* SESSION_CHANGESETAPPLY_* flags */
|
int flags /* SESSION_CHANGESETAPPLY_* flags */
|
||||||
);
|
);
|
||||||
|
SQLITE_API int sqlite3changeset_apply_v3(
|
||||||
|
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||||
|
int nChangeset, /* Size of changeset in bytes */
|
||||||
|
void *pChangeset, /* Changeset blob */
|
||||||
|
int(*xFilter)(
|
||||||
|
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||||
|
sqlite3_changeset_iter *p /* Handle describing change */
|
||||||
|
),
|
||||||
|
int(*xConflict)(
|
||||||
|
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||||
|
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||||
|
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||||
|
),
|
||||||
|
void *pCtx, /* First argument passed to xConflict */
|
||||||
|
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
|
||||||
|
int flags /* SESSION_CHANGESETAPPLY_* flags */
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Flags for sqlite3changeset_apply_v2
|
** CAPI3REF: Flags for sqlite3changeset_apply_v2
|
||||||
@@ -12915,6 +13091,23 @@ SQLITE_API int sqlite3changeset_apply_v2_strm(
|
|||||||
void **ppRebase, int *pnRebase,
|
void **ppRebase, int *pnRebase,
|
||||||
int flags
|
int flags
|
||||||
);
|
);
|
||||||
|
SQLITE_API int sqlite3changeset_apply_v3_strm(
|
||||||
|
sqlite3 *db, /* Apply change to "main" db of this handle */
|
||||||
|
int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
|
||||||
|
void *pIn, /* First arg for xInput */
|
||||||
|
int(*xFilter)(
|
||||||
|
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||||
|
sqlite3_changeset_iter *p
|
||||||
|
),
|
||||||
|
int(*xConflict)(
|
||||||
|
void *pCtx, /* Copy of sixth arg to _apply() */
|
||||||
|
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
|
||||||
|
sqlite3_changeset_iter *p /* Handle describing change and conflict */
|
||||||
|
),
|
||||||
|
void *pCtx, /* First argument passed to xConflict */
|
||||||
|
void **ppRebase, int *pnRebase,
|
||||||
|
int flags
|
||||||
|
);
|
||||||
SQLITE_API int sqlite3changeset_concat_strm(
|
SQLITE_API int sqlite3changeset_concat_strm(
|
||||||
int (*xInputA)(void *pIn, void *pData, int *pnData),
|
int (*xInputA)(void *pIn, void *pData, int *pnData),
|
||||||
void *pInA,
|
void *pInA,
|
||||||
|
|||||||
7
deps/sqlite/sqlite3ext.h
vendored
@@ -368,6 +368,10 @@ struct sqlite3_api_routines {
|
|||||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||||
/* Version 3.50.0 and later */
|
/* Version 3.50.0 and later */
|
||||||
int (*setlk_timeout)(sqlite3*,int,int);
|
int (*setlk_timeout)(sqlite3*,int,int);
|
||||||
|
/* Version 3.51.0 and later */
|
||||||
|
int (*set_errmsg)(sqlite3*,int,const char*);
|
||||||
|
int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -703,6 +707,9 @@ typedef int (*sqlite3_loadext_entry)(
|
|||||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||||
/* Version 3.50.0 and later */
|
/* Version 3.50.0 and later */
|
||||||
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
|
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
|
||||||
|
/* Version 3.51.0 and later */
|
||||||
|
#define sqlite3_set_errmsg sqlite3_api->set_errmsg
|
||||||
|
#define sqlite3_db_status64 sqlite3_api->db_status64
|
||||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||||
|
|
||||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
|||||||
32
docs/model.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Model
|
||||||
|
|
||||||
|
A reasonable mental model of Tilde Friends is as a virtual computer. User
|
||||||
|
interace is through a web browser. Communication with the outside world is
|
||||||
|
through the Secure Scuttlebutt (SSB) network protocol. Persistence is through
|
||||||
|
an SSB store and an additional key-value store in an sqlite database.
|
||||||
|
|
||||||
|
The schema for the sqlite database is primarily a `messages` table and a
|
||||||
|
`blobs` table, which are what one would expect from the SSB specifications.
|
||||||
|
|
||||||
|
```dot
|
||||||
|
digraph {
|
||||||
|
web_browser -> tilde_friends_web_interface [dir=both];
|
||||||
|
web_browser [shape=rect,label="Web Browser"];
|
||||||
|
subgraph cluster_tilde_friends {
|
||||||
|
label = "Tilde Friends";
|
||||||
|
tilde_friends_web_interface -> example_app [dir=both];
|
||||||
|
subgraph cluster_sandbox {
|
||||||
|
label = "app sandbox";
|
||||||
|
example_app;
|
||||||
|
}
|
||||||
|
example_app -> tilde_friends_core;
|
||||||
|
tilde_friends_core -> example_app;
|
||||||
|
tilde_friends_web_interface -> tilde_friends_core;
|
||||||
|
tilde_friends_core -> "db.sqlite";
|
||||||
|
tilde_friends_core -> ssb;
|
||||||
|
"db.sqlite" [shape=cylinder];
|
||||||
|
}
|
||||||
|
ssb -> other_ssb_clients [label="Secure Handshake",dir=both];
|
||||||
|
other_ssb_clients [shape=rect,label="SSB Peers"];
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -41,11 +41,9 @@ options:
|
|||||||
ssb_port (default: 8008): Port on which to listen for SSB secure handshake connections.
|
ssb_port (default: 8008): Port on which to listen for SSB secure handshake connections.
|
||||||
http_local_only (default: false): Whether to bind http(s) to the loopback address. Otherwise any.
|
http_local_only (default: false): Whether to bind http(s) to the loopback address. Otherwise any.
|
||||||
http_port (default: 12345): Port on which to listen for HTTP connections.
|
http_port (default: 12345): Port on which to listen for HTTP connections.
|
||||||
https_port (default: 0): Port on which to listen for secure HTTP connections.
|
|
||||||
out_http_port_file (default: ""): File to which to write bound HTTP port.
|
out_http_port_file (default: ""): File to which to write bound HTTP port.
|
||||||
blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched.
|
blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched.
|
||||||
blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted.
|
blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted.
|
||||||
fetch_hosts (default: ""): Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty.
|
|
||||||
http_redirect (default: ""): If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "http://example.com")
|
http_redirect (default: ""): If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "http://example.com")
|
||||||
index (default: "/~core/intro/"): Default path.
|
index (default: "/~core/intro/"): Default path.
|
||||||
index_map (default: ""): Mappings from hostname to redirect path, one per line, as in: "www.tildefriends.net=/~core/index/"
|
index_map (default: ""): Mappings from hostname to redirect path, one per line, as in: "www.tildefriends.net=/~core/index/"
|
||||||
@@ -62,6 +60,7 @@ options:
|
|||||||
broadcast (default: true): Send network discovery broadcasts.
|
broadcast (default: true): Send network discovery broadcasts.
|
||||||
discovery (default: true): Receive network discovery broadcasts.
|
discovery (default: true): Receive network discovery broadcasts.
|
||||||
stay_connected (default: false): Whether to attempt to keep several peer connections open.
|
stay_connected (default: false): Whether to attempt to keep several peer connections open.
|
||||||
|
accepted_eula_version (default: 0): The version of the last accepted EULA.
|
||||||
-o, --one-proc Run everything in one process (unsafely!).
|
-o, --one-proc Run everything in one process (unsafely!).
|
||||||
-z, --zip path Zip archive from which to load files.
|
-z, --zip path Zip archive from which to load files.
|
||||||
-v, --verbose Log raw messages.
|
-v, --verbose Log raw messages.
|
||||||
|
|||||||
6
flake.lock
generated
@@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1753749649,
|
"lastModified": 1763948260,
|
||||||
"narHash": "sha256-+jkEZxs7bfOKfBIk430K+tK9IvXlwzqQQnppC2ZKFj4=",
|
"narHash": "sha256-dY9qLD0H0zOUgU3vWacPY6Qc421BeQAfm8kBuBtPVE0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "1f08a4df998e21f4e8be8fb6fbf61d11a1a5076a",
|
"rev": "1c8ba8d3f7634acac4a2094eef7c32ad9106532c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
8
metadata/en-US/changelogs/43.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
* Fixed multiple issues with blob replication.
|
||||||
|
* Fixed some link encoding issues.
|
||||||
|
* Fixed some context menus being cut off.
|
||||||
|
* Minor Android fixes.
|
||||||
|
* Updates:
|
||||||
|
* CodeMirror
|
||||||
|
* OpenSSL 3.5.3
|
||||||
|
* QuickJS 2025-09-13
|
||||||
10
metadata/en-US/changelogs/44.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
* Faster load times.
|
||||||
|
* Private message fixes.
|
||||||
|
* Fix contact groups expanding/collapsing.
|
||||||
|
* Manual SSB theme color picker.
|
||||||
|
* Give channel subscribe/unsubscribe similar grouping treatment to follows/blocks.
|
||||||
|
* Slightly improved following/blocking message display.
|
||||||
|
* Remove the query tab in favor of searching for "sql:SELECT ...".
|
||||||
|
* Exclude messages for subscribed channels from the general feed.
|
||||||
|
* Remove OpenSSL.
|
||||||
|
* Updates: CodeMirror, libbacktrace, and speedscope 1.24.0.
|
||||||
9
metadata/en-US/changelogs/48.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
* Fixed disagreement in identity information.
|
||||||
|
* Show more context when prompting for permissions.
|
||||||
|
* Reduce redundant queries to improve load times.
|
||||||
|
* Improved profile load times.
|
||||||
|
* Don't show an empty code of conduct.
|
||||||
|
* Resolve ambiguity when a user is both followed and blocked (they're blocked).
|
||||||
|
* Move perf tracing viewer into a trace app.
|
||||||
|
* Minor UI improvements.
|
||||||
|
* Updates: CodeMirror, emojis, libbacktrace, sqlite 3.51.0.
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.unprompted.tildefriends"
|
package="com.unprompted.tildefriends"
|
||||||
android:versionCode="42"
|
android:versionCode="49"
|
||||||
android:versionName="0.2025.8">
|
android:versionName="0.2025.12-wip">
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<application
|
<application
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import android.os.RemoteException;
|
|||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -44,6 +43,7 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class TildeFriendsActivity extends Activity {
|
public class TildeFriendsActivity extends Activity {
|
||||||
@@ -53,29 +53,42 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
String port_file_path;
|
String port_file_path;
|
||||||
Thread create_thread;
|
Thread create_thread;
|
||||||
Thread server_thread;
|
Thread server_thread;
|
||||||
|
Thread log_thread;
|
||||||
ServiceConnection service_connection;
|
ServiceConnection service_connection;
|
||||||
FileObserver observer;
|
FileObserver observer;
|
||||||
|
LinkedBlockingQueue<String> log_queue = new LinkedBlockingQueue<String>();
|
||||||
|
|
||||||
private ValueCallback<Uri[]> upload_message;
|
private ValueCallback<Uri[]> upload_message;
|
||||||
private final static int FILECHOOSER_RESULT = 1;
|
private final static int FILECHOOSER_RESULT = 1;
|
||||||
private float touch_down_y;
|
private float touch_down_y;
|
||||||
private boolean ready = false;
|
private boolean ready = false;
|
||||||
private boolean loaded = false;
|
private boolean loaded = false;
|
||||||
|
private boolean shutting_down = false;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Log.w("tildefriends", "Calling system.loadLibrary().");
|
log("Calling system.loadLibrary().");
|
||||||
System.loadLibrary("tildefriends");
|
System.loadLibrary("tildefriends");
|
||||||
Log.w("tildefriends", "system.loadLibrary() completed.");
|
log("system.loadLibrary() completed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static native int tf_server_main(String files_dir, String apk_path, String out_port_file_path, ConnectivityManager connectivity_manager);
|
public static native int tf_server_main(String files_dir, String apk_path, String out_port_file_path, ConnectivityManager connectivity_manager);
|
||||||
public static native int tf_sandbox_main(int pipe_fd);
|
public static native int tf_sandbox_main(int pipe_fd);
|
||||||
|
|
||||||
|
public static void log(String message) {
|
||||||
|
if (s_activity != null && s_activity.log_queue != null && message != null) {
|
||||||
|
try {
|
||||||
|
s_activity.log_queue.put(message);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
android.util.Log.w("tildefriends", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createThread() {
|
private void createThread() {
|
||||||
web_view = (TildeFriendsWebView)findViewById(R.id.web);
|
web_view = (TildeFriendsWebView)findViewById(R.id.web);
|
||||||
Log.w("tildefriends", String.format("getFilesDir() is %s", getFilesDir().toString()));
|
log(String.format("getFilesDir() is %s", getFilesDir().toString()));
|
||||||
Log.w("tildefriends", String.format("getPackageResourcePath() is %s", getPackageResourcePath().toString()));
|
log(String.format("getPackageResourcePath() is %s", getPackageResourcePath().toString()));
|
||||||
Log.w("tildefriends", String.format("nativeLibraryDir is %s", getApplicationInfo().nativeLibraryDir));
|
log(String.format("nativeLibraryDir is %s", getApplicationInfo().nativeLibraryDir));
|
||||||
|
|
||||||
port_file_path = getFilesDir().toString() + "/port.txt";
|
port_file_path = getFilesDir().toString() + "/port.txt";
|
||||||
new File(port_file_path).delete();
|
new File(port_file_path).delete();
|
||||||
@@ -86,17 +99,17 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
server_thread = new Thread(new Runnable() {
|
server_thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.w("tildefriends", "Watching for changes in: " + getFilesDir().toString());
|
log("Watching for changes in: " + getFilesDir().toString());
|
||||||
observer = make_file_observer(getFilesDir().toString(), port_file_path);
|
observer = make_file_observer(getFilesDir().toString(), port_file_path);
|
||||||
observer.startWatching();
|
observer.startWatching();
|
||||||
|
|
||||||
Log.w("tildefriends", "Calling tf_server_main.");
|
log("Calling tf_server_main.");
|
||||||
int result = tf_server_main(
|
int result = tf_server_main(
|
||||||
getFilesDir().toString(),
|
getFilesDir().toString(),
|
||||||
getPackageResourcePath().toString(),
|
getPackageResourcePath().toString(),
|
||||||
port_file_path,
|
port_file_path,
|
||||||
(ConnectivityManager)getApplicationContext().getSystemService(CONNECTIVITY_SERVICE));
|
(ConnectivityManager)getApplicationContext().getSystemService(CONNECTIVITY_SERVICE));
|
||||||
Log.w("tildefriends", "tf_server_main returned " + result + ".");
|
log("tf_server_main returned " + result + ".");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
server_thread.start();
|
server_thread.start();
|
||||||
@@ -110,17 +123,17 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
|
|
||||||
web_view.setDownloadListener(new DownloadListener() {
|
web_view.setDownloadListener(new DownloadListener() {
|
||||||
public void onDownloadStart(String url, String userAgent, String content_disposition, String mime_type, long content_length) {
|
public void onDownloadStart(String url, String userAgent, String content_disposition, String mime_type, long content_length) {
|
||||||
Log.w("tildefriends", "Let's download: " + url + " (" + content_disposition + ")");
|
log("Let's download: " + url + " (" + content_disposition + ")");
|
||||||
String file_name = URLUtil.guessFileName(url, content_disposition, mime_type);
|
String file_name = URLUtil.guessFileName(url, content_disposition, mime_type);
|
||||||
if (url.startsWith("data:") && url.indexOf(',') != -1) {
|
if (url.startsWith("data:") && url.indexOf(',') != -1) {
|
||||||
String b64 = url.substring(url.indexOf(',') + 1);
|
String b64 = url.substring(url.indexOf(',') + 1);
|
||||||
byte[] data = Base64.decode(b64, Base64.DEFAULT);
|
byte[] data = Base64.decode(b64, Base64.DEFAULT);
|
||||||
Log.w("tildefriends", "Downloaded " + data.length + " bytes.");
|
log("Downloaded " + data.length + " bytes.");
|
||||||
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||||
try (OutputStream stream = new FileOutputStream(new File(path, file_name))) {
|
try (OutputStream stream = new FileOutputStream(new File(path, file_name))) {
|
||||||
stream.write(data);
|
stream.write(data);
|
||||||
} catch (java.io.IOException e) {
|
} catch (java.io.IOException e) {
|
||||||
Log.w("tildefriends", "IOException: " + e.toString());
|
log("IOException: " + e.toString());
|
||||||
}
|
}
|
||||||
Toast.makeText(getApplicationContext(), "Downloaded File", Toast.LENGTH_LONG).show();
|
Toast.makeText(getApplicationContext(), "Downloaded File", Toast.LENGTH_LONG).show();
|
||||||
} else {
|
} else {
|
||||||
@@ -228,7 +241,7 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onConsoleMessage(android.webkit.ConsoleMessage consoleMessage) {
|
public boolean onConsoleMessage(android.webkit.ConsoleMessage consoleMessage) {
|
||||||
Log.d("tildefriends", consoleMessage.message() + " -- From line " + consoleMessage.lineNumber() + " of " + consoleMessage.sourceId());
|
log(consoleMessage.message() + " -- From line " + consoleMessage.lineNumber() + " of " + consoleMessage.sourceId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -259,10 +272,27 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
s_activity = this;
|
s_activity = this;
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
log_thread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!s_activity.shutting_down) {
|
||||||
|
try {
|
||||||
|
String message = log_queue.take();
|
||||||
|
if (message != null) {
|
||||||
|
android.util.Log.w("tildefriends", message);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
log_thread.start();
|
||||||
StrictMode.setThreadPolicy(
|
StrictMode.setThreadPolicy(
|
||||||
new StrictMode.ThreadPolicy.Builder()
|
new StrictMode.ThreadPolicy.Builder()
|
||||||
.detectAll()
|
.detectAll()
|
||||||
.penaltyDialog()
|
//.penaltyDialog()
|
||||||
.penaltyLog()
|
.penaltyLog()
|
||||||
.build());
|
.build());
|
||||||
StrictMode.setVmPolicy(
|
StrictMode.setVmPolicy(
|
||||||
@@ -305,8 +335,17 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onDestroy()
|
protected void onDestroy()
|
||||||
{
|
{
|
||||||
super.onDestroy();
|
try {
|
||||||
|
shutting_down = true;
|
||||||
|
if (log_queue != null) {
|
||||||
|
log_queue.put("Goodbye.");
|
||||||
|
}
|
||||||
|
log_thread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
log_thread = null;
|
||||||
s_activity = null;
|
s_activity = null;
|
||||||
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -399,32 +438,32 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void start_sandbox(int pipe_fd) {
|
public static void start_sandbox(int pipe_fd) {
|
||||||
Log.w("tildefriends", "starting service with fd: " + pipe_fd);
|
log("starting service with fd: " + pipe_fd);
|
||||||
Intent intent = new Intent(s_activity, TildeFriendsSandboxService.class);
|
Intent intent = new Intent(s_activity, TildeFriendsSandboxService.class);
|
||||||
s_activity.service_connection = new ServiceConnection() {
|
s_activity.service_connection = new ServiceConnection() {
|
||||||
@Override
|
@Override
|
||||||
public void onBindingDied(ComponentName name) {
|
public void onBindingDied(ComponentName name) {
|
||||||
Log.w("tildefriends", "onBindingDied");
|
log("onBindingDied");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNullBinding(ComponentName name) {
|
public void onNullBinding(ComponentName name) {
|
||||||
Log.w("tildefriends", "onNullBinding");
|
log("onNullBinding");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||||
Log.w("tildefriends", "onServiceConnected");
|
log("onServiceConnected");
|
||||||
Parcel data = Parcel.obtain();
|
Parcel data = Parcel.obtain();
|
||||||
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(pipe_fd)) {
|
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromFd(pipe_fd)) {
|
||||||
data.writeParcelable(pfd, 0);
|
data.writeParcelable(pfd, 0);
|
||||||
} catch (java.io.IOException e) {
|
} catch (java.io.IOException e) {
|
||||||
Log.w("tildefriends", "IOException: " + e);
|
log("IOException: " + e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
binder.transact(TildeFriendsSandboxService.START_CALL, data, null, IBinder.FLAG_ONEWAY);
|
binder.transact(TildeFriendsSandboxService.START_CALL, data, null, IBinder.FLAG_ONEWAY);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.w("tildefriends", "RemoteException");
|
log("RemoteException");
|
||||||
} finally {
|
} finally {
|
||||||
data.recycle();
|
data.recycle();
|
||||||
}
|
}
|
||||||
@@ -432,14 +471,14 @@ public class TildeFriendsActivity extends Activity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
Log.w("tildefriends", "onServiceDisconnected");
|
log("onServiceDisconnected");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
s_activity.bindService(intent, s_activity.service_connection, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND);
|
s_activity.bindService(intent, s_activity.service_connection, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void stop_sandbox() {
|
public static void stop_sandbox() {
|
||||||
Log.w("tildefriends", "stop_sandbox");
|
log("stop_sandbox");
|
||||||
if (s_activity.service_connection != null) {
|
if (s_activity.service_connection != null) {
|
||||||
s_activity.unbindService(s_activity.service_connection);
|
s_activity.unbindService(s_activity.service_connection);
|
||||||
s_activity.service_connection = null;
|
s_activity.service_connection = null;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import android.os.Binder;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
public class TildeFriendsSandboxService extends Service {
|
public class TildeFriendsSandboxService extends Service {
|
||||||
public static final int START_CALL = IBinder.FIRST_CALL_TRANSACTION;
|
public static final int START_CALL = IBinder.FIRST_CALL_TRANSACTION;
|
||||||
@@ -14,12 +13,12 @@ public class TildeFriendsSandboxService extends Service {
|
|||||||
Thread thread;
|
Thread thread;
|
||||||
|
|
||||||
public int onStartCommand(Intent intent, int flags, int start_id) {
|
public int onStartCommand(Intent intent, int flags, int start_id) {
|
||||||
Log.w("tildefriends", "TildeFriendsSandboxService: onStartCommand");
|
TildeFriendsActivity.log("TildeFriendsSandboxService: onStartCommand");
|
||||||
return super.onStartCommand(intent, flags, start_id);
|
return super.onStartCommand(intent, flags, start_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
Log.w("tildefriends", "TildeFriendsSandboxService: onDestroy");
|
TildeFriendsActivity.log("TildeFriendsSandboxService: onDestroy");
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,9 +26,9 @@ public class TildeFriendsSandboxService extends Service {
|
|||||||
thread = new Thread(new Runnable() {
|
thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Log.w("tildefriends", "Calling tf_sandbox_main.");
|
TildeFriendsActivity.log("Calling tf_sandbox_main.");
|
||||||
int result = TildeFriendsActivity.tf_sandbox_main(pipe_fd);
|
int result = TildeFriendsActivity.tf_sandbox_main(pipe_fd);
|
||||||
Log.w("tildefriends", "tf_sandbox_main returned " + result + ".");
|
TildeFriendsActivity.log("tf_sandbox_main returned " + result + ".");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
thread.start();
|
thread.start();
|
||||||
@@ -43,7 +42,7 @@ public class TildeFriendsSandboxService extends Service {
|
|||||||
if (code == START_CALL) {
|
if (code == START_CALL) {
|
||||||
ParcelFileDescriptor pfd = read_pfd(data);
|
ParcelFileDescriptor pfd = read_pfd(data);
|
||||||
if (pfd != null) {
|
if (pfd != null) {
|
||||||
Log.w("tildefriends", "fd is " + pfd.getFd());
|
TildeFriendsActivity.log("fd is " + pfd.getFd());
|
||||||
start_thread(pfd.detachFd());
|
start_thread(pfd.detachFd());
|
||||||
try {
|
try {
|
||||||
pfd.close();
|
pfd.close();
|
||||||
|
|||||||
685
src/api.js.c
@@ -74,7 +74,7 @@ static void _tf_api_core_apps_after_work(tf_ssb_t* ssb, int status, void* user_d
|
|||||||
JSValue result = JS_NewObject(context);
|
JSValue result = JS_NewObject(context);
|
||||||
for (int i = 0; i < work->count; i++)
|
for (int i = 0; i < work->count; i++)
|
||||||
{
|
{
|
||||||
JS_SetPropertyStr(context, result, work->apps[i].app, JS_NewString(context, work->apps[i].path));
|
JS_SetPropertyStr(context, result, work->apps[i].app, JS_NewString(context, work->apps[i].path ? work->apps[i].path : ""));
|
||||||
}
|
}
|
||||||
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
||||||
tf_util_report_error(context, error);
|
tf_util_report_error(context, error);
|
||||||
@@ -147,12 +147,695 @@ static JSValue _tf_api_core_apps(JSContext* context, JSValueConst this_val, int
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _users_t
|
||||||
|
{
|
||||||
|
const char* users;
|
||||||
|
JSContext* context;
|
||||||
|
JSValue promise[2];
|
||||||
|
} users_t;
|
||||||
|
|
||||||
|
static void _tf_api_core_users_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
users_t* work = user_data;
|
||||||
|
work->users = tf_ssb_db_get_property(ssb, "auth", "users");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_api_core_users_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
users_t* work = user_data;
|
||||||
|
JSContext* context = work->context;
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
if (work->users)
|
||||||
|
{
|
||||||
|
result = JS_ParseJSON(context, work->users, strlen(work->users), NULL);
|
||||||
|
tf_free((void*)work->users);
|
||||||
|
}
|
||||||
|
if (JS_IsUndefined(result))
|
||||||
|
{
|
||||||
|
result = JS_NewArray(context);
|
||||||
|
}
|
||||||
|
JSValue error = JS_Call(context, JS_IsArray(context, result) ? work->promise[0] : work->promise[1], JS_UNDEFINED, 1, &result);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
JS_FreeValue(context, work->promise[0]);
|
||||||
|
JS_FreeValue(context, work->promise[1]);
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_api_core_users(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
users_t* work = tf_malloc(sizeof(users_t));
|
||||||
|
*work = (users_t) {
|
||||||
|
.context = context,
|
||||||
|
};
|
||||||
|
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
tf_ssb_run_work(ssb, _tf_api_core_users_work, _tf_api_core_users_after_work, work);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_api_core_register(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue event_name = argv[0];
|
||||||
|
JSValue handler = argv[1];
|
||||||
|
JSValue process = data[0];
|
||||||
|
JSValue event_handlers = JS_GetPropertyStr(context, process, "eventHandlers");
|
||||||
|
JSAtom atom = JS_ValueToAtom(context, event_name);
|
||||||
|
JSValue array = JS_GetProperty(context, event_handlers, atom);
|
||||||
|
if (!JS_IsArray(context, array))
|
||||||
|
{
|
||||||
|
JS_FreeValue(context, array);
|
||||||
|
array = JS_NewArray(context);
|
||||||
|
JS_SetProperty(context, event_handlers, atom, JS_DupValue(context, array));
|
||||||
|
}
|
||||||
|
JS_SetPropertyUint32(context, array, tf_util_get_length(context, array), JS_DupValue(context, handler));
|
||||||
|
JS_FreeValue(context, array);
|
||||||
|
JS_FreeAtom(context, atom);
|
||||||
|
JS_FreeValue(context, event_handlers);
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_api_core_unregister(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue event_name = argv[0];
|
||||||
|
JSValue handler = argv[1];
|
||||||
|
JSValue process = data[0];
|
||||||
|
JSValue event_handlers = JS_GetPropertyStr(context, process, "eventHandlers");
|
||||||
|
JSAtom atom = JS_ValueToAtom(context, event_name);
|
||||||
|
JSValue array = JS_GetProperty(context, event_handlers, atom);
|
||||||
|
|
||||||
|
if (JS_IsArray(context, array))
|
||||||
|
{
|
||||||
|
JSValue index_of = JS_GetPropertyStr(context, array, "indexOf");
|
||||||
|
|
||||||
|
JSValue index = JS_Call(context, index_of, array, 1, &handler);
|
||||||
|
int int_index = -1;
|
||||||
|
JS_ToInt32(context, &int_index, index);
|
||||||
|
if (int_index != -1)
|
||||||
|
{
|
||||||
|
JSValue splice = JS_GetPropertyStr(context, array, "splice");
|
||||||
|
JSValue splice_args[] = {
|
||||||
|
index,
|
||||||
|
JS_NewInt32(context, 1),
|
||||||
|
};
|
||||||
|
JSValue result = JS_Call(context, splice, array, 2, splice_args);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
JS_FreeValue(context, splice);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, index);
|
||||||
|
JS_FreeValue(context, index_of);
|
||||||
|
|
||||||
|
if (tf_util_get_length(context, array) == 0)
|
||||||
|
{
|
||||||
|
JS_DeleteProperty(context, event_handlers, atom, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_FreeValue(context, array);
|
||||||
|
JS_FreeAtom(context, atom);
|
||||||
|
JS_FreeValue(context, event_handlers);
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _permissions_for_user_t
|
||||||
|
{
|
||||||
|
const char* user;
|
||||||
|
const char* settings;
|
||||||
|
JSContext* context;
|
||||||
|
JSValue promise[2];
|
||||||
|
} permissions_for_user_t;
|
||||||
|
|
||||||
|
static void _tf_api_core_permissions_for_user_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
permissions_for_user_t* work = user_data;
|
||||||
|
work->settings = tf_ssb_db_get_property(ssb, "core", "settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_api_core_permissions_for_user_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
permissions_for_user_t* work = user_data;
|
||||||
|
JSContext* context = work->context;
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
if (work->settings)
|
||||||
|
{
|
||||||
|
JSValue json = JS_ParseJSON(context, work->settings, strlen(work->settings), NULL);
|
||||||
|
if (JS_IsObject(json))
|
||||||
|
{
|
||||||
|
JSValue permissions = JS_GetPropertyStr(context, json, "permissions");
|
||||||
|
if (JS_IsObject(permissions))
|
||||||
|
{
|
||||||
|
result = JS_GetPropertyStr(context, permissions, work->user);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, permissions);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, json);
|
||||||
|
tf_free((void*)work->settings);
|
||||||
|
}
|
||||||
|
if (JS_IsUndefined(result))
|
||||||
|
{
|
||||||
|
result = JS_NewArray(context);
|
||||||
|
}
|
||||||
|
JSValue error = JS_Call(context, JS_IsArray(context, result) ? work->promise[0] : work->promise[1], JS_UNDEFINED, 1, &result);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
JS_FreeValue(context, work->promise[0]);
|
||||||
|
JS_FreeValue(context, work->promise[1]);
|
||||||
|
JS_FreeCString(context, work->user);
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_api_core_permissionsForUser(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
permissions_for_user_t* work = tf_malloc(sizeof(permissions_for_user_t));
|
||||||
|
*work = (permissions_for_user_t) {
|
||||||
|
.context = context,
|
||||||
|
.user = JS_ToCString(context, argv[0]),
|
||||||
|
};
|
||||||
|
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
tf_ssb_run_work(ssb, _tf_api_core_permissions_for_user_work, _tf_api_core_permissions_for_user_after_work, work);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _permissions_granted_t
|
||||||
|
{
|
||||||
|
JSContext* context;
|
||||||
|
const char* user;
|
||||||
|
const char* package_owner;
|
||||||
|
const char* package_name;
|
||||||
|
const char* settings;
|
||||||
|
JSValue promise[2];
|
||||||
|
} permissions_granted_t;
|
||||||
|
|
||||||
|
static void _tf_api_core_permissions_granted_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
permissions_granted_t* work = user_data;
|
||||||
|
work->settings = tf_ssb_db_get_property(ssb, "core", "settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_api_core_permissions_granted_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
permissions_granted_t* work = user_data;
|
||||||
|
JSContext* context = work->context;
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
if (work->settings)
|
||||||
|
{
|
||||||
|
JSValue json = JS_ParseJSON(context, work->settings, strlen(work->settings), NULL);
|
||||||
|
if (JS_IsObject(json) && work->user && work->package_owner && work->package_name)
|
||||||
|
{
|
||||||
|
JSValue user_permissions = JS_GetPropertyStr(context, json, "userPermissions");
|
||||||
|
if (JS_IsObject(user_permissions))
|
||||||
|
{
|
||||||
|
JSValue user = JS_GetPropertyStr(context, user_permissions, work->user);
|
||||||
|
if (JS_IsObject(user))
|
||||||
|
{
|
||||||
|
JSValue package_owner = JS_GetPropertyStr(context, user, work->package_owner);
|
||||||
|
if (JS_IsObject(package_owner))
|
||||||
|
{
|
||||||
|
result = JS_GetPropertyStr(context, package_owner, work->package_name);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, package_owner);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, user);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, user_permissions);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, json);
|
||||||
|
tf_free((void*)work->settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
JS_FreeValue(context, work->promise[0]);
|
||||||
|
JS_FreeValue(context, work->promise[1]);
|
||||||
|
tf_free((void*)work->user);
|
||||||
|
tf_free((void*)work->package_owner);
|
||||||
|
tf_free((void*)work->package_name);
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* _tf_ssb_get_process_credentials_session_name(JSContext* context, JSValue process)
|
||||||
|
{
|
||||||
|
JSValue credentials = JS_IsObject(process) ? JS_GetPropertyStr(context, process, "credentials") : JS_UNDEFINED;
|
||||||
|
JSValue session = JS_IsObject(credentials) ? JS_GetPropertyStr(context, credentials, "session") : JS_UNDEFINED;
|
||||||
|
JSValue name_value = JS_IsObject(session) ? JS_GetPropertyStr(context, session, "name") : JS_UNDEFINED;
|
||||||
|
const char* name = JS_IsString(name_value) ? JS_ToCString(context, name_value) : NULL;
|
||||||
|
const char* result = tf_strdup(name);
|
||||||
|
JS_FreeCString(context, name);
|
||||||
|
JS_FreeValue(context, name_value);
|
||||||
|
JS_FreeValue(context, session);
|
||||||
|
JS_FreeValue(context, credentials);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_api_core_permissionsGranted(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
JSValue process = data[0];
|
||||||
|
JSValue package_owner_value = JS_GetPropertyStr(context, process, "packageOwner");
|
||||||
|
JSValue package_name_value = JS_GetPropertyStr(context, process, "packageName");
|
||||||
|
const char* package_owner = JS_ToCString(context, package_owner_value);
|
||||||
|
const char* package_name = JS_ToCString(context, package_name_value);
|
||||||
|
permissions_granted_t* work = tf_malloc(sizeof(permissions_granted_t));
|
||||||
|
*work = (permissions_granted_t) {
|
||||||
|
.context = context,
|
||||||
|
.user = _tf_ssb_get_process_credentials_session_name(context, process),
|
||||||
|
.package_owner = tf_strdup(package_owner),
|
||||||
|
.package_name = tf_strdup(package_name),
|
||||||
|
};
|
||||||
|
JS_FreeCString(context, package_owner);
|
||||||
|
JS_FreeCString(context, package_name);
|
||||||
|
JS_FreeValue(context, package_owner_value);
|
||||||
|
JS_FreeValue(context, package_name_value);
|
||||||
|
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
tf_ssb_run_work(ssb, _tf_api_core_permissions_granted_work, _tf_api_core_permissions_granted_after_work, work);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _active_identity_work_t
|
||||||
|
{
|
||||||
|
JSContext* context;
|
||||||
|
const char* name;
|
||||||
|
const char* package_owner;
|
||||||
|
const char* package_name;
|
||||||
|
char identity[k_id_base64_len];
|
||||||
|
int result;
|
||||||
|
JSValue promise[2];
|
||||||
|
} active_identity_work_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_getActiveIdentity_visit(const char* identity, void* user_data)
|
||||||
|
{
|
||||||
|
active_identity_work_t* request = user_data;
|
||||||
|
if (!*request->identity)
|
||||||
|
{
|
||||||
|
snprintf(request->identity, sizeof(request->identity), "@%s", identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_getActiveIdentity_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
active_identity_work_t* request = user_data;
|
||||||
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
|
tf_ssb_db_identity_get_active(db, request->name, request->package_owner, request->package_name, request->identity, sizeof(request->identity));
|
||||||
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
|
||||||
|
if (!*request->identity && tf_ssb_db_user_has_permission(ssb, NULL, request->name, "administration"))
|
||||||
|
{
|
||||||
|
tf_ssb_whoami(ssb, request->identity, sizeof(request->identity));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*request->identity)
|
||||||
|
{
|
||||||
|
tf_ssb_db_identity_visit(ssb, request->name, _tf_ssb_getActiveIdentity_visit, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_getActiveIdentity_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
active_identity_work_t* request = user_data;
|
||||||
|
JSContext* context = request->context;
|
||||||
|
if (request->result == 0)
|
||||||
|
{
|
||||||
|
JSValue identity = JS_NewString(context, request->identity);
|
||||||
|
JSValue error = JS_Call(context, request->promise[0], JS_UNDEFINED, 1, &identity);
|
||||||
|
JS_FreeValue(context, identity);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JSValue error = JS_Call(context, request->promise[1], JS_UNDEFINED, 0, NULL);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, request->promise[0]);
|
||||||
|
JS_FreeValue(context, request->promise[1]);
|
||||||
|
tf_free((void*)request->name);
|
||||||
|
tf_free((void*)request->package_owner);
|
||||||
|
tf_free((void*)request->package_name);
|
||||||
|
tf_free(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_getActiveIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
JSValue process = data[0];
|
||||||
|
JSValue package_owner_value = JS_GetPropertyStr(context, process, "packageOwner");
|
||||||
|
JSValue package_name_value = JS_GetPropertyStr(context, process, "packageName");
|
||||||
|
|
||||||
|
const char* name = _tf_ssb_get_process_credentials_session_name(context, process);
|
||||||
|
const char* package_owner = JS_ToCString(context, package_owner_value);
|
||||||
|
const char* package_name = JS_ToCString(context, package_name_value);
|
||||||
|
active_identity_work_t* work = tf_malloc(sizeof(active_identity_work_t));
|
||||||
|
*work = (active_identity_work_t) {
|
||||||
|
.context = context,
|
||||||
|
.name = tf_strdup(name),
|
||||||
|
.package_owner = tf_strdup(package_owner),
|
||||||
|
.package_name = tf_strdup(package_name),
|
||||||
|
};
|
||||||
|
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
tf_free((void*)name);
|
||||||
|
JS_FreeCString(context, package_owner);
|
||||||
|
JS_FreeCString(context, package_name);
|
||||||
|
|
||||||
|
JS_FreeValue(context, package_owner_value);
|
||||||
|
JS_FreeValue(context, package_name_value);
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_getActiveIdentity_work, _tf_ssb_getActiveIdentity_after_work, work);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _identities_visit_t
|
||||||
|
{
|
||||||
|
JSContext* context;
|
||||||
|
JSValue promise[2];
|
||||||
|
const char** identities;
|
||||||
|
int count;
|
||||||
|
char user[];
|
||||||
|
} identities_visit_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_getIdentities_visit(const char* identity, void* user_data)
|
||||||
|
{
|
||||||
|
identities_visit_t* work = user_data;
|
||||||
|
work->identities = tf_resize_vec(work->identities, (work->count + 1) * sizeof(const char*));
|
||||||
|
char id[k_id_base64_len];
|
||||||
|
snprintf(id, sizeof(id), "@%s", identity);
|
||||||
|
work->identities[work->count++] = tf_strdup(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_get_all_identities_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
tf_ssb_db_identity_visit_all(ssb, _tf_ssb_getIdentities_visit, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_get_identities_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
identities_visit_t* work = user_data;
|
||||||
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
|
JSValue result = JS_NewArray(context);
|
||||||
|
for (int i = 0; i < work->count; i++)
|
||||||
|
{
|
||||||
|
JS_SetPropertyUint32(context, result, i, JS_NewString(context, work->identities[i]));
|
||||||
|
tf_free((void*)work->identities[i]);
|
||||||
|
}
|
||||||
|
tf_free(work->identities);
|
||||||
|
|
||||||
|
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
JS_FreeValue(context, work->promise[0]);
|
||||||
|
JS_FreeValue(context, work->promise[1]);
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_getAllIdentities(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
|
{
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
if (ssb)
|
||||||
|
{
|
||||||
|
identities_visit_t* work = tf_malloc(sizeof(identities_visit_t));
|
||||||
|
*work = (identities_visit_t) {
|
||||||
|
.context = context,
|
||||||
|
};
|
||||||
|
|
||||||
|
result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_get_all_identities_work, _tf_ssb_get_identities_after_work, work);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_get_identities_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
identities_visit_t* work = user_data;
|
||||||
|
if (tf_ssb_db_user_has_permission(ssb, NULL, work->user, "administration"))
|
||||||
|
{
|
||||||
|
char id[k_id_base64_len] = "";
|
||||||
|
if (tf_ssb_whoami(ssb, id, sizeof(id)))
|
||||||
|
{
|
||||||
|
_tf_ssb_getIdentities_visit(*id == '@' ? id + 1 : id, work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tf_ssb_db_identity_visit(ssb, work->user, _tf_ssb_getIdentities_visit, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_getIdentities(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
JSValue process = data[0];
|
||||||
|
if (ssb)
|
||||||
|
{
|
||||||
|
const char* user = _tf_ssb_get_process_credentials_session_name(context, process);
|
||||||
|
if (user)
|
||||||
|
{
|
||||||
|
size_t user_length = user ? strlen(user) : 0;
|
||||||
|
identities_visit_t* work = tf_malloc(sizeof(identities_visit_t) + user_length + 1);
|
||||||
|
*work = (identities_visit_t) {
|
||||||
|
.context = context,
|
||||||
|
};
|
||||||
|
memcpy(work->user, user, user_length + 1);
|
||||||
|
tf_free((void*)user);
|
||||||
|
|
||||||
|
result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_get_identities_work, _tf_ssb_get_identities_after_work, work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_getOwnerIdentities(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||||
|
{
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
JSValue process = data[0];
|
||||||
|
if (ssb)
|
||||||
|
{
|
||||||
|
JSValue value = JS_GetPropertyStr(context, process, "packageOwner");
|
||||||
|
const char* user = JS_ToCString(context, value);
|
||||||
|
size_t user_length = user ? strlen(user) : 0;
|
||||||
|
identities_visit_t* work = tf_malloc(sizeof(identities_visit_t) + user_length + 1);
|
||||||
|
*work = (identities_visit_t) {
|
||||||
|
.context = context,
|
||||||
|
};
|
||||||
|
memcpy(work->user, user, user_length + 1);
|
||||||
|
JS_FreeCString(context, user);
|
||||||
|
JS_FreeValue(context, value);
|
||||||
|
|
||||||
|
result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_get_identities_work, _tf_ssb_get_identities_after_work, work);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _settings_descriptions_get_t
|
||||||
|
{
|
||||||
|
const char* settings;
|
||||||
|
JSContext* context;
|
||||||
|
JSValue promise[2];
|
||||||
|
} settings_descriptions_get_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_get_settings_descriptions_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
settings_descriptions_get_t* work = user_data;
|
||||||
|
work->settings = tf_ssb_db_get_property(ssb, "core", "settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_get_settings_descriptions_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
settings_descriptions_get_t* work = user_data;
|
||||||
|
JSContext* context = work->context;
|
||||||
|
JSValue result = JS_NewObject(context);
|
||||||
|
JSValue settings = JS_ParseJSON(context, work->settings ? work->settings : "", work->settings ? strlen(work->settings) : 0, NULL);
|
||||||
|
const char* name;
|
||||||
|
const char* type;
|
||||||
|
tf_setting_kind_t kind;
|
||||||
|
const char* description;
|
||||||
|
for (int i = 0; tf_util_get_global_setting_by_index(i, &name, &type, &kind, &description); i++)
|
||||||
|
{
|
||||||
|
JSValue entry = JS_NewObject(context);
|
||||||
|
JS_SetPropertyStr(context, entry, "type", JS_NewString(context, type));
|
||||||
|
JS_SetPropertyStr(context, entry, "description", JS_NewString(context, description));
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
case k_kind_unknown:
|
||||||
|
break;
|
||||||
|
case k_kind_bool:
|
||||||
|
JS_SetPropertyStr(context, entry, "default_value", JS_NewBool(context, tf_util_get_default_global_setting_bool(name)));
|
||||||
|
break;
|
||||||
|
case k_kind_int:
|
||||||
|
JS_SetPropertyStr(context, entry, "default_value", JS_NewInt32(context, tf_util_get_default_global_setting_int(name)));
|
||||||
|
break;
|
||||||
|
case k_kind_string:
|
||||||
|
JS_SetPropertyStr(context, entry, "default_value", JS_NewString(context, tf_util_get_default_global_setting_string(name)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (JS_IsObject(settings))
|
||||||
|
{
|
||||||
|
JS_SetPropertyStr(context, entry, "value", JS_GetPropertyStr(context, settings, name));
|
||||||
|
}
|
||||||
|
JS_SetPropertyStr(context, result, name, entry);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, settings);
|
||||||
|
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
JS_FreeValue(context, work->promise[0]);
|
||||||
|
JS_FreeValue(context, work->promise[1]);
|
||||||
|
tf_free((void*)work->settings);
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_globalSettingsDescriptions(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
|
{
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
settings_descriptions_get_t* work = tf_malloc(sizeof(settings_descriptions_get_t));
|
||||||
|
*work = (settings_descriptions_get_t) { .context = context };
|
||||||
|
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_get_settings_descriptions_work, _tf_ssb_get_settings_descriptions_after_work, work);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _settings_get_t
|
||||||
|
{
|
||||||
|
const char* key;
|
||||||
|
tf_setting_kind_t kind;
|
||||||
|
void* value;
|
||||||
|
JSContext* context;
|
||||||
|
JSValue promise[2];
|
||||||
|
} settings_get_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_settings_get_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
settings_get_t* work = user_data;
|
||||||
|
work->kind = tf_util_get_global_setting_kind(work->key);
|
||||||
|
switch (work->kind)
|
||||||
|
{
|
||||||
|
case k_kind_unknown:
|
||||||
|
break;
|
||||||
|
case k_kind_bool:
|
||||||
|
{
|
||||||
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
|
bool value = false;
|
||||||
|
tf_ssb_db_get_global_setting_bool(db, work->key, &value);
|
||||||
|
work->value = (void*)(intptr_t)value;
|
||||||
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case k_kind_int:
|
||||||
|
{
|
||||||
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
|
int64_t value = 0;
|
||||||
|
tf_ssb_db_get_global_setting_int64(db, work->key, &value);
|
||||||
|
work->value = (void*)(intptr_t)value;
|
||||||
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case k_kind_string:
|
||||||
|
{
|
||||||
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
|
work->value = (void*)tf_ssb_db_get_global_setting_string_alloc(db, work->key);
|
||||||
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_settings_get_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
settings_get_t* work = user_data;
|
||||||
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
switch (work->kind)
|
||||||
|
{
|
||||||
|
case k_kind_unknown:
|
||||||
|
break;
|
||||||
|
case k_kind_bool:
|
||||||
|
result = work->value ? JS_TRUE : JS_FALSE;
|
||||||
|
break;
|
||||||
|
case k_kind_int:
|
||||||
|
result = JS_NewInt64(context, (int64_t)(intptr_t)work->value);
|
||||||
|
break;
|
||||||
|
case k_kind_string:
|
||||||
|
result = JS_NewString(context, (const char*)work->value);
|
||||||
|
tf_free(work->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
|
||||||
|
tf_util_report_error(context, error);
|
||||||
|
JS_FreeValue(context, error);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
JS_FreeValue(context, work->promise[0]);
|
||||||
|
JS_FreeValue(context, work->promise[1]);
|
||||||
|
JS_FreeCString(context, work->key);
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_globalSettingsGet(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
|
{
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
settings_get_t* work = tf_malloc(sizeof(settings_get_t));
|
||||||
|
*work = (settings_get_t) { .context = context, .key = JS_ToCString(context, argv[0]) };
|
||||||
|
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||||
|
tf_ssb_run_work(ssb, _tf_ssb_settings_get_work, _tf_ssb_settings_get_after_work, work);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
JSValue imports = argv[0];
|
JSValue imports = argv[0];
|
||||||
JSValue process = argv[1];
|
JSValue process = argv[1];
|
||||||
JSValue core = JS_GetPropertyStr(context, imports, "core");
|
JSValue core = JS_GetPropertyStr(context, imports, "core");
|
||||||
JS_SetPropertyStr(context, core, "apps", JS_NewCFunctionData(context, _tf_api_core_apps, 1, 0, 1, &process));
|
JS_SetPropertyStr(context, core, "apps", JS_NewCFunctionData(context, _tf_api_core_apps, 1, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, core, "register", JS_NewCFunctionData(context, _tf_api_core_register, 2, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, core, "unregister", JS_NewCFunctionData(context, _tf_api_core_unregister, 2, 0, 1, &process));
|
||||||
|
|
||||||
|
JS_SetPropertyStr(context, core, "users", JS_NewCFunctionData(context, _tf_api_core_users, 0, 0, 1, &process));
|
||||||
|
|
||||||
|
JS_SetPropertyStr(context, core, "permissionsForUser", JS_NewCFunctionData(context, _tf_api_core_permissionsForUser, 1, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, core, "permissionsGranted", JS_NewCFunctionData(context, _tf_api_core_permissionsGranted, 0, 0, 1, &process));
|
||||||
|
|
||||||
|
JSValue app = JS_NewObject(context);
|
||||||
|
JS_SetPropertyStr(context, app, "owner", JS_GetPropertyStr(context, process, "packageOwner"));
|
||||||
|
JS_SetPropertyStr(context, app, "name", JS_GetPropertyStr(context, process, "packageName"));
|
||||||
|
JS_SetPropertyStr(context, core, "app", app);
|
||||||
|
|
||||||
|
JS_SetPropertyStr(context, core, "url", JS_GetPropertyStr(context, process, "url"));
|
||||||
|
|
||||||
|
JSValue ssb = JS_GetPropertyStr(context, imports, "ssb");
|
||||||
|
JS_SetPropertyStr(context, ssb, "getAllIdentities", JS_NewCFunction(context, _tf_ssb_getAllIdentities, "getAllIdentities", 0));
|
||||||
|
JS_SetPropertyStr(context, ssb, "getActiveIdentity", JS_NewCFunctionData(context, _tf_ssb_getActiveIdentity, 0, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, ssb, "getIdentities", JS_NewCFunctionData(context, _tf_ssb_getIdentities, 0, 0, 1, &process));
|
||||||
|
JS_SetPropertyStr(context, ssb, "getOwnerIdentities", JS_NewCFunctionData(context, _tf_ssb_getOwnerIdentities, 0, 0, 1, &process));
|
||||||
|
JS_FreeValue(context, ssb);
|
||||||
|
|
||||||
|
JSValue credentials = JS_GetPropertyStr(context, process, "credentials");
|
||||||
|
JSValue permissions = JS_IsObject(credentials) ? JS_GetPropertyStr(context, credentials, "permissions") : JS_UNDEFINED;
|
||||||
|
JSValue administration = JS_IsObject(permissions) ? JS_GetPropertyStr(context, permissions, "administration") : JS_UNDEFINED;
|
||||||
|
if (JS_ToBool(context, administration) > 0)
|
||||||
|
{
|
||||||
|
JS_SetPropertyStr(context, core, "globalSettingsDescriptions", JS_NewCFunction(context, _tf_ssb_globalSettingsDescriptions, "globalSettingsDescriptions", 0));
|
||||||
|
JS_SetPropertyStr(context, core, "globalSettingsGet", JS_NewCFunction(context, _tf_ssb_globalSettingsGet, "globalSettingsGet", 1));
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, administration);
|
||||||
|
JS_FreeValue(context, permissions);
|
||||||
|
JS_FreeValue(context, credentials);
|
||||||
|
|
||||||
JS_FreeValue(context, core);
|
JS_FreeValue(context, core);
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
#include "bcrypt.js.h"
|
|
||||||
|
|
||||||
#include "task.h"
|
|
||||||
|
|
||||||
#include "ow-crypt.h"
|
|
||||||
#include "quickjs.h"
|
|
||||||
#include "uv.h"
|
|
||||||
|
|
||||||
static JSValue _crypt_hashpw(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
const char* key = JS_ToCString(context, argv[0]);
|
|
||||||
const char* salt = JS_ToCString(context, argv[1]);
|
|
||||||
char output[7 + 22 + 31 + 1];
|
|
||||||
char* hash = crypt_rn(key, salt, output, sizeof(output));
|
|
||||||
JSValue result = JS_NewString(context, hash);
|
|
||||||
JS_FreeCString(context, key);
|
|
||||||
JS_FreeCString(context, salt);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _crypt_gensalt(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
int length = 0;
|
|
||||||
JS_ToInt32(context, &length, argv[0]);
|
|
||||||
char buffer[16];
|
|
||||||
tf_task_t* task = tf_task_get(context);
|
|
||||||
size_t bytes = uv_random(tf_task_get_loop(task), &(uv_random_t) { 0 }, buffer, sizeof(buffer), 0, NULL) == 0 ? sizeof(buffer) : 0;
|
|
||||||
char output[7 + 22 + 1];
|
|
||||||
char* salt = crypt_gensalt_rn("$2b$", length, buffer, bytes, output, sizeof(output));
|
|
||||||
JSValue result = JS_NewString(context, salt);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void tf_bcrypt_register(JSContext* context)
|
|
||||||
{
|
|
||||||
JSValue global = JS_GetGlobalObject(context);
|
|
||||||
JSValue bcrypt = JS_NewObject(context);
|
|
||||||
JS_SetPropertyStr(context, global, "bCrypt", bcrypt);
|
|
||||||
JS_SetPropertyStr(context, bcrypt, "hashpw", JS_NewCFunction(context, _crypt_hashpw, "hashpw", 2));
|
|
||||||
JS_SetPropertyStr(context, bcrypt, "gensalt", JS_NewCFunction(context, _crypt_gensalt, "gensalt", 1));
|
|
||||||
JS_FreeValue(context, global);
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/**
|
|
||||||
** \defgroup bcrypt_js bCrypt
|
|
||||||
** Exposes bcrypt to script, where it is used for hashing and verifying
|
|
||||||
** passwords.
|
|
||||||
** @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** A JS context. */
|
|
||||||
typedef struct JSContext JSContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
** Register the bcrypt script interface.
|
|
||||||
** @param context The JS context.
|
|
||||||
*/
|
|
||||||
void tf_bcrypt_register(JSContext* context);
|
|
||||||
|
|
||||||
/** @} */
|
|
||||||
144
src/http.c
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "tls.h"
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
|
|
||||||
@@ -20,10 +19,12 @@
|
|||||||
|
|
||||||
static const int k_timeout_ms = 60000;
|
static const int k_timeout_ms = 60000;
|
||||||
|
|
||||||
|
static tf_http_t** s_http_instances;
|
||||||
|
int s_http_instance_count;
|
||||||
|
|
||||||
typedef struct _tf_http_connection_t
|
typedef struct _tf_http_connection_t
|
||||||
{
|
{
|
||||||
tf_http_t* http;
|
tf_http_t* http;
|
||||||
tf_tls_session_t* tls;
|
|
||||||
uv_tcp_t tcp;
|
uv_tcp_t tcp;
|
||||||
uv_shutdown_t shutdown;
|
uv_shutdown_t shutdown;
|
||||||
uv_timer_t timeout;
|
uv_timer_t timeout;
|
||||||
@@ -75,7 +76,6 @@ typedef struct _tf_http_handler_t
|
|||||||
typedef struct _tf_http_listener_t
|
typedef struct _tf_http_listener_t
|
||||||
{
|
{
|
||||||
tf_http_t* http;
|
tf_http_t* http;
|
||||||
tf_tls_context_t* tls;
|
|
||||||
uv_tcp_t tcp;
|
uv_tcp_t tcp;
|
||||||
tf_http_cleanup_t* cleanup;
|
tf_http_cleanup_t* cleanup;
|
||||||
void* user_data;
|
void* user_data;
|
||||||
@@ -106,7 +106,6 @@ typedef struct _tf_http_t
|
|||||||
static const char* _http_connection_get_header(const tf_http_connection_t* connection, const char* name);
|
static const char* _http_connection_get_header(const tf_http_connection_t* connection, const char* name);
|
||||||
static void _http_connection_destroy(tf_http_connection_t* connection, const char* reason);
|
static void _http_connection_destroy(tf_http_connection_t* connection, const char* reason);
|
||||||
static void _http_timer_reset(tf_http_connection_t* connection);
|
static void _http_timer_reset(tf_http_connection_t* connection);
|
||||||
static void _http_tls_update(tf_http_connection_t* connection);
|
|
||||||
static void _http_builtin_404_handler(tf_http_request_t* request);
|
static void _http_builtin_404_handler(tf_http_request_t* request);
|
||||||
|
|
||||||
tf_http_t* tf_http_create(uv_loop_t* loop)
|
tf_http_t* tf_http_create(uv_loop_t* loop)
|
||||||
@@ -115,6 +114,8 @@ tf_http_t* tf_http_create(uv_loop_t* loop)
|
|||||||
*http = (tf_http_t) {
|
*http = (tf_http_t) {
|
||||||
.loop = loop,
|
.loop = loop,
|
||||||
};
|
};
|
||||||
|
s_http_instances = tf_resize_vec(s_http_instances, sizeof(tf_http_t*) * (s_http_instance_count + 1));
|
||||||
|
s_http_instances[s_http_instance_count++] = http;
|
||||||
return http;
|
return http;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,11 +256,6 @@ static void _http_connection_destroy(tf_http_connection_t* connection, const cha
|
|||||||
{
|
{
|
||||||
uv_close((uv_handle_t*)&connection->timeout, _http_connection_on_close);
|
uv_close((uv_handle_t*)&connection->timeout, _http_connection_on_close);
|
||||||
}
|
}
|
||||||
if (connection->tls)
|
|
||||||
{
|
|
||||||
tf_tls_session_destroy(connection->tls);
|
|
||||||
connection->tls = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection->ref_count == 0 && !connection->tcp.data && !connection->shutdown.data && !connection->timeout.data)
|
if (connection->ref_count == 0 && !connection->tcp.data && !connection->shutdown.data && !connection->timeout.data)
|
||||||
{
|
{
|
||||||
@@ -441,7 +437,6 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
|
|||||||
*request = (tf_http_request_t) {
|
*request = (tf_http_request_t) {
|
||||||
.http = connection->http,
|
.http = connection->http,
|
||||||
.connection = connection,
|
.connection = connection,
|
||||||
.is_tls = connection->tls != NULL,
|
|
||||||
.method = connection->method,
|
.method = connection->method,
|
||||||
.path = connection->path,
|
.path = connection->path,
|
||||||
.query = connection->query,
|
.query = connection->query,
|
||||||
@@ -582,21 +577,7 @@ static void _http_on_read(uv_stream_t* stream, ssize_t read_size, const uv_buf_t
|
|||||||
_http_timer_reset(connection);
|
_http_timer_reset(connection);
|
||||||
if (read_size > 0)
|
if (read_size > 0)
|
||||||
{
|
{
|
||||||
if (connection->tls)
|
_http_on_read_plain(connection, buffer->base, read_size);
|
||||||
{
|
|
||||||
if (tf_tls_session_write_encrypted(connection->tls, buffer->base, read_size) < 0)
|
|
||||||
{
|
|
||||||
_http_connection_destroy(connection, "tf_tls_session_write_encrypted");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_http_tls_update(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_http_on_read_plain(connection, buffer->base, read_size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (read_size < 0)
|
else if (read_size < 0)
|
||||||
{
|
{
|
||||||
@@ -638,17 +619,6 @@ static void _http_on_connection(uv_stream_t* stream, int status)
|
|||||||
|
|
||||||
tf_http_connection_t* connection = tf_malloc(sizeof(tf_http_connection_t));
|
tf_http_connection_t* connection = tf_malloc(sizeof(tf_http_connection_t));
|
||||||
*connection = (tf_http_connection_t) { .http = http, .tcp = { .data = connection }, .is_receiving_headers = true };
|
*connection = (tf_http_connection_t) { .http = http, .tcp = { .data = connection }, .is_receiving_headers = true };
|
||||||
if (listener->tls)
|
|
||||||
{
|
|
||||||
connection->tls = tf_tls_context_create_session(listener->tls);
|
|
||||||
if (!connection->tls)
|
|
||||||
{
|
|
||||||
_http_connection_destroy(connection, "tf_tls_context_create_session");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tf_tls_session_start_accept(connection->tls);
|
|
||||||
connection->is_handshaking = true;
|
|
||||||
}
|
|
||||||
int r = uv_tcp_init(connection->http->loop, &connection->tcp);
|
int r = uv_tcp_init(connection->http->loop, &connection->tcp);
|
||||||
if (r)
|
if (r)
|
||||||
{
|
{
|
||||||
@@ -689,21 +659,15 @@ static void _http_on_connection(uv_stream_t* stream, int status)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connection->tls)
|
|
||||||
{
|
|
||||||
_http_tls_update(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
http->connections = tf_resize_vec(http->connections, sizeof(tf_http_connection_t*) * (http->connections_count + 1));
|
http->connections = tf_resize_vec(http->connections, sizeof(tf_http_connection_t*) * (http->connections_count + 1));
|
||||||
http->connections[http->connections_count++] = connection;
|
http->connections[http->connections_count++] = connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tf_http_listen(tf_http_t* http, int port, bool local_only, tf_tls_context_t* tls, tf_http_cleanup_t* cleanup, void* user_data)
|
int tf_http_listen(tf_http_t* http, int port, bool local_only, tf_http_cleanup_t* cleanup, void* user_data)
|
||||||
{
|
{
|
||||||
tf_http_listener_t* listener = tf_malloc(sizeof(tf_http_listener_t));
|
tf_http_listener_t* listener = tf_malloc(sizeof(tf_http_listener_t));
|
||||||
*listener = (tf_http_listener_t) {
|
*listener = (tf_http_listener_t) {
|
||||||
.http = http,
|
.http = http,
|
||||||
.tls = tls,
|
|
||||||
.tcp = { .data = listener },
|
.tcp = { .data = listener },
|
||||||
.cleanup = cleanup,
|
.cleanup = cleanup,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
@@ -872,6 +836,21 @@ void tf_http_destroy(tf_http_t* http)
|
|||||||
http->handlers_count = 0;
|
http->handlers_count = 0;
|
||||||
|
|
||||||
tf_free(http);
|
tf_free(http);
|
||||||
|
|
||||||
|
for (int i = 0; i < s_http_instance_count; i++)
|
||||||
|
{
|
||||||
|
if (s_http_instances[i] == http)
|
||||||
|
{
|
||||||
|
s_http_instances[i] = s_http_instances[--s_http_instance_count];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (s_http_instance_count == 0)
|
||||||
|
{
|
||||||
|
tf_free(s_http_instances);
|
||||||
|
s_http_instances = NULL;
|
||||||
|
}
|
||||||
|
tf_printf("http %p destroyed\n", http);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -928,71 +907,10 @@ static void _http_write_internal(tf_http_connection_t* connection, const void* d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _http_tls_update(tf_http_connection_t* connection)
|
|
||||||
{
|
|
||||||
bool again = true;
|
|
||||||
while (again)
|
|
||||||
{
|
|
||||||
again = false;
|
|
||||||
|
|
||||||
if (connection->is_handshaking && connection->tls)
|
|
||||||
{
|
|
||||||
switch (tf_tls_session_handshake(connection->tls))
|
|
||||||
{
|
|
||||||
case k_tls_handshake_done:
|
|
||||||
connection->is_handshaking = false;
|
|
||||||
break;
|
|
||||||
case k_tls_handshake_more:
|
|
||||||
break;
|
|
||||||
case k_tls_handshake_failed:
|
|
||||||
_http_connection_destroy(connection, "tf_tls_session_handshake");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Maybe we became disconnected and cleaned up our TLS session. */
|
|
||||||
if (connection->tls)
|
|
||||||
{
|
|
||||||
char buffer[8192];
|
|
||||||
int r = tf_tls_session_read_encrypted(connection->tls, buffer, sizeof(buffer));
|
|
||||||
if (r > 0)
|
|
||||||
{
|
|
||||||
_http_write_internal(connection, buffer, r);
|
|
||||||
again = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection->tls)
|
|
||||||
{
|
|
||||||
char buffer[8192];
|
|
||||||
int r = tf_tls_session_read_plain(connection->tls, buffer, sizeof(buffer));
|
|
||||||
if (r > 0)
|
|
||||||
{
|
|
||||||
_http_on_read_plain(connection, buffer, r);
|
|
||||||
again = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _http_write(tf_http_connection_t* connection, const void* data, size_t size)
|
static void _http_write(tf_http_connection_t* connection, const void* data, size_t size)
|
||||||
{
|
{
|
||||||
_http_timer_reset(connection);
|
_http_timer_reset(connection);
|
||||||
if (connection->tls)
|
_http_write_internal(connection, data, size);
|
||||||
{
|
|
||||||
int r = tf_tls_session_write_plain(connection->tls, data, size);
|
|
||||||
if (r < (ssize_t)size)
|
|
||||||
{
|
|
||||||
char buffer[8192];
|
|
||||||
tf_tls_session_get_error(connection->tls, buffer, sizeof(buffer));
|
|
||||||
tf_printf("tf_tls_session_write_plain: %s\n", buffer);
|
|
||||||
}
|
|
||||||
_http_tls_update(connection);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_http_write_internal(connection, data, size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size)
|
void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size)
|
||||||
@@ -1215,3 +1133,19 @@ const char* tf_http_get_cookie(const char* cookie_header, const char* name)
|
|||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tf_http_debug_destroy()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < s_http_instance_count; i++)
|
||||||
|
{
|
||||||
|
tf_http_t* http = s_http_instances[i];
|
||||||
|
tf_printf("http %p[%d]\n", http, i);
|
||||||
|
tf_printf(" connections = %d\n", http->connections_count);
|
||||||
|
for (int j = 0; j < http->connections_count; j++)
|
||||||
|
{
|
||||||
|
tf_http_connection_t* connection = http->connections[j];
|
||||||
|
tf_printf(" connection %p[%d] %s tcp=%p timeout=%p shutdown=%p rc=%d\n", connection, j, connection->trace_name, connection->tcp.data, connection->timeout.data,
|
||||||
|
connection->shutdown.data, connection->ref_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
13
src/http.h
@@ -23,9 +23,6 @@ typedef struct _tf_http_request_t tf_http_request_t;
|
|||||||
/** An HTTP instance. */
|
/** An HTTP instance. */
|
||||||
typedef struct _tf_http_t tf_http_t;
|
typedef struct _tf_http_t tf_http_t;
|
||||||
|
|
||||||
/** A TLS context. */
|
|
||||||
typedef struct _tf_tls_context_t tf_tls_context_t;
|
|
||||||
|
|
||||||
/** A trace instance. */
|
/** A trace instance. */
|
||||||
typedef struct _tf_trace_t tf_trace_t;
|
typedef struct _tf_trace_t tf_trace_t;
|
||||||
|
|
||||||
@@ -68,8 +65,6 @@ typedef struct _tf_http_request_t
|
|||||||
tf_http_t* http;
|
tf_http_t* http;
|
||||||
/** The HTTP connection associated with this request. */
|
/** The HTTP connection associated with this request. */
|
||||||
tf_http_connection_t* connection;
|
tf_http_connection_t* connection;
|
||||||
/** True if this is an HTTPS session. */
|
|
||||||
bool is_tls;
|
|
||||||
/** The HTTP method of the request (GET/POST/...). */
|
/** The HTTP method of the request (GET/POST/...). */
|
||||||
const char* method;
|
const char* method;
|
||||||
/** The HTTP request path. */
|
/** The HTTP request path. */
|
||||||
@@ -117,12 +112,11 @@ void tf_http_set_trace(tf_http_t* http, tf_trace_t* trace);
|
|||||||
** @param http The HTTP instance.
|
** @param http The HTTP instance.
|
||||||
** @param port The port on which to listen, or 0 to assign a free port.
|
** @param port The port on which to listen, or 0 to assign a free port.
|
||||||
** @param local_only Only access connections on localhost, otherwise any address.
|
** @param local_only Only access connections on localhost, otherwise any address.
|
||||||
** @param tls An optional TLS context to use for HTTPS requests.
|
|
||||||
** @param cleanup A function called when the HTTP instance is being cleaned up.
|
** @param cleanup A function called when the HTTP instance is being cleaned up.
|
||||||
** @param user_data User data passed to the cleanup callback.
|
** @param user_data User data passed to the cleanup callback.
|
||||||
** @return The port number on which the HTTP instance is now listening.
|
** @return The port number on which the HTTP instance is now listening.
|
||||||
*/
|
*/
|
||||||
int tf_http_listen(tf_http_t* http, int port, bool local_only, tf_tls_context_t* tls, tf_http_cleanup_t* cleanup, void* user_data);
|
int tf_http_listen(tf_http_t* http, int port, bool local_only, tf_http_cleanup_t* cleanup, void* user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Add an HTTP request handler.
|
** Add an HTTP request handler.
|
||||||
@@ -238,4 +232,9 @@ const char* tf_http_status_text(int status);
|
|||||||
*/
|
*/
|
||||||
bool tf_http_pattern_matches(const char* pattern, const char* path);
|
bool tf_http_pattern_matches(const char* pattern, const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Log debug information to diagnose shutdown problems.
|
||||||
|
*/
|
||||||
|
void tf_http_debug_destroy();
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "httpd.js.h"
|
#include "httpd.js.h"
|
||||||
|
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "ssb.db.h"
|
#include "ssb.db.h"
|
||||||
#include "ssb.h"
|
#include "ssb.h"
|
||||||
@@ -8,6 +9,7 @@
|
|||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
|
|
||||||
#include "picohttpparser.h"
|
#include "picohttpparser.h"
|
||||||
|
#include "uv.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@@ -211,7 +213,12 @@ void tf_httpd_endpoint_app(tf_http_request_t* request)
|
|||||||
tf_ssb_run_work(ssb, _httpd_endpoint_app_blob_work, _httpd_endpoint_app_blob_after_work, data);
|
tf_ssb_run_work(ssb, _httpd_endpoint_app_blob_work, _httpd_endpoint_app_blob_after_work, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_httpd_endpoint_app_socket(tf_http_request_t* request)
|
static void _tf_httpd_endpoint_app_socket_c(tf_http_request_t* request)
|
||||||
|
{
|
||||||
|
tf_printf("Unimplemented.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_httpd_endpoint_app_socket_js(tf_http_request_t* request)
|
||||||
{
|
{
|
||||||
tf_task_t* task = request->user_data;
|
tf_task_t* task = request->user_data;
|
||||||
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
@@ -250,3 +257,25 @@ void tf_httpd_endpoint_app_socket(tf_http_request_t* request)
|
|||||||
JS_FreeValue(context, exports);
|
JS_FreeValue(context, exports);
|
||||||
JS_FreeValue(context, global);
|
JS_FreeValue(context, global);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tf_httpd_endpoint_app_socket(tf_http_request_t* request)
|
||||||
|
{
|
||||||
|
static bool checked_env;
|
||||||
|
static bool use_c;
|
||||||
|
if (!checked_env)
|
||||||
|
{
|
||||||
|
char buffer[8] = { 0 };
|
||||||
|
size_t buffer_size = sizeof(buffer);
|
||||||
|
use_c = uv_os_getenv("TF_APP_C", buffer, &buffer_size) == 0 && strcmp(buffer, "1") == 0;
|
||||||
|
checked_env = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_c)
|
||||||
|
{
|
||||||
|
_tf_httpd_endpoint_app_socket_c(request);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_tf_httpd_endpoint_app_socket_js(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
190
src/httpd.js.c
@@ -4,9 +4,9 @@
|
|||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "sha1.h"
|
||||||
#include "ssb.db.h"
|
#include "ssb.db.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include "tls.h"
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@@ -14,13 +14,17 @@
|
|||||||
#include "sodium/crypto_sign.h"
|
#include "sodium/crypto_sign.h"
|
||||||
#include "sodium/utils.h"
|
#include "sodium/utils.h"
|
||||||
|
|
||||||
#include <openssl/sha.h>
|
#if defined(__APPLE__)
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define CYAN "\e[1;36m"
|
#define CYAN "\e[1;36m"
|
||||||
#define MAGENTA "\e[1;35m"
|
#define MAGENTA "\e[1;35m"
|
||||||
#define YELLOW "\e[1;33m"
|
#define YELLOW "\e[1;33m"
|
||||||
#define RESET "\e[0m"
|
#define RESET "\e[0m"
|
||||||
|
|
||||||
|
static const int k_eula_version = 2;
|
||||||
|
|
||||||
static JSClassID _httpd_request_class_id;
|
static JSClassID _httpd_request_class_id;
|
||||||
|
|
||||||
typedef struct _http_user_data_t
|
typedef struct _http_user_data_t
|
||||||
@@ -169,8 +173,13 @@ static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_va
|
|||||||
uint8_t* key_magic = alloca(size);
|
uint8_t* key_magic = alloca(size);
|
||||||
memcpy(key_magic, header_sec_websocket_key, key_length);
|
memcpy(key_magic, header_sec_websocket_key, key_length);
|
||||||
memcpy(key_magic + key_length, k_magic, 36);
|
memcpy(key_magic + key_length, k_magic, 36);
|
||||||
|
|
||||||
uint8_t digest[20];
|
uint8_t digest[20];
|
||||||
SHA1(key_magic, size, digest);
|
SHA1_CTX sha1 = { 0 };
|
||||||
|
SHA1Init(&sha1);
|
||||||
|
SHA1Update(&sha1, key_magic, size);
|
||||||
|
SHA1Final(digest, &sha1);
|
||||||
|
|
||||||
char key[41] = { 0 };
|
char key[41] = { 0 };
|
||||||
tf_base64_encode(digest, sizeof(digest), key, sizeof(key));
|
tf_base64_encode(digest, sizeof(digest), key, sizeof(key));
|
||||||
|
|
||||||
@@ -253,11 +262,6 @@ JSValue tf_httpd_make_response_object(JSContext* context, tf_http_request_t* req
|
|||||||
|
|
||||||
bool tf_httpd_redirect(tf_http_request_t* request)
|
bool tf_httpd_redirect(tf_http_request_t* request)
|
||||||
{
|
{
|
||||||
if (request->is_tls)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
http_user_data_t* user_data = tf_http_get_user_data(request->http);
|
http_user_data_t* user_data = tf_http_get_user_data(request->http);
|
||||||
if (!user_data || !*user_data->redirect)
|
if (!user_data || !*user_data->redirect)
|
||||||
{
|
{
|
||||||
@@ -272,16 +276,12 @@ bool tf_httpd_redirect(tf_http_request_t* request)
|
|||||||
|
|
||||||
typedef struct _httpd_listener_t
|
typedef struct _httpd_listener_t
|
||||||
{
|
{
|
||||||
tf_tls_context_t* tls;
|
int padding;
|
||||||
} httpd_listener_t;
|
} httpd_listener_t;
|
||||||
|
|
||||||
static void _httpd_listener_cleanup(void* user_data)
|
static void _httpd_listener_cleanup(void* user_data)
|
||||||
{
|
{
|
||||||
httpd_listener_t* listener = user_data;
|
httpd_listener_t* listener = user_data;
|
||||||
if (listener->tls)
|
|
||||||
{
|
|
||||||
tf_tls_context_destroy(listener->tls);
|
|
||||||
}
|
|
||||||
tf_free(listener);
|
tf_free(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,7 +570,7 @@ static void _httpd_endpoint_add_slash(tf_http_request_t* request)
|
|||||||
host = tf_http_request_get_header(request, "host");
|
host = tf_http_request_get_header(request, "host");
|
||||||
}
|
}
|
||||||
char url[1024];
|
char url[1024];
|
||||||
snprintf(url, sizeof(url), "%s%s%s/", request->is_tls ? "https://" : "http://", host, request->path);
|
snprintf(url, sizeof(url), "%s%s%s/", "http://", host, request->path);
|
||||||
const char* headers[] = {
|
const char* headers[] = {
|
||||||
"Location",
|
"Location",
|
||||||
url,
|
url,
|
||||||
@@ -622,28 +622,97 @@ tf_httpd_user_app_t* tf_httpd_parse_user_app_from_path(const char* path, const c
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_root_callback(const char* path, void* user_data)
|
typedef struct _root_t
|
||||||
{
|
{
|
||||||
tf_http_request_t* request = user_data;
|
tf_http_request_t* request;
|
||||||
const char* headers[] = {
|
const char* path;
|
||||||
"Location",
|
} root_t;
|
||||||
path ? path : "/~core/apps/",
|
|
||||||
};
|
|
||||||
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
|
|
||||||
tf_http_request_unref(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _httpd_endpoint_root(tf_http_request_t* request)
|
static void _httpd_root_work(tf_ssb_t* ssb, void* user_data)
|
||||||
{
|
{
|
||||||
|
root_t* root = user_data;
|
||||||
|
tf_http_request_t* request = root->request;
|
||||||
const char* host = tf_http_request_get_header(request, "x-forwarded-host");
|
const char* host = tf_http_request_get_header(request, "x-forwarded-host");
|
||||||
if (!host)
|
if (!host)
|
||||||
{
|
{
|
||||||
host = tf_http_request_get_header(request, "host");
|
host = tf_http_request_get_header(request, "host");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool require_eula =
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int64_t accepted_eula_version = 0;
|
||||||
|
|
||||||
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
|
tf_ssb_db_get_global_setting_int64(db, "accepted_eula_version", &accepted_eula_version);
|
||||||
|
if (require_eula && accepted_eula_version != k_eula_version)
|
||||||
|
{
|
||||||
|
root->path = tf_strdup("/static/eula.html");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
root->path = tf_ssb_db_resolve_index(db, host);
|
||||||
|
}
|
||||||
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_root_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
root_t* root = user_data;
|
||||||
|
tf_http_request_t* request = root->request;
|
||||||
|
const char* headers[] = {
|
||||||
|
"Location",
|
||||||
|
root->path ? root->path : "/~core/apps/",
|
||||||
|
};
|
||||||
|
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
|
||||||
|
tf_http_request_unref(request);
|
||||||
|
tf_free((void*)root->path);
|
||||||
|
tf_free(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_endpoint_root(tf_http_request_t* request)
|
||||||
|
{
|
||||||
|
root_t* root = tf_malloc(sizeof(root_t));
|
||||||
|
*root = (root_t) {
|
||||||
|
.request = request,
|
||||||
|
};
|
||||||
|
tf_http_request_ref(request);
|
||||||
tf_task_t* task = request->user_data;
|
tf_task_t* task = request->user_data;
|
||||||
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
tf_ssb_run_work(ssb, _httpd_root_work, _httpd_root_after_work, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_accept_eula_work(tf_ssb_t* ssb, void* user_data)
|
||||||
|
{
|
||||||
|
char buffer[64];
|
||||||
|
snprintf(buffer, sizeof(buffer), "%d", k_eula_version);
|
||||||
|
|
||||||
|
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||||
|
tf_ssb_db_set_global_setting_from_string(db, "accepted_eula_version", buffer);
|
||||||
|
tf_ssb_release_db_writer(ssb, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_accept_eula_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
|
{
|
||||||
|
tf_http_request_t* request = user_data;
|
||||||
|
const char* headers[] = {
|
||||||
|
"Location",
|
||||||
|
"/",
|
||||||
|
};
|
||||||
|
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
|
||||||
|
tf_http_request_unref(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_endpoint_accept_eula(tf_http_request_t* request)
|
||||||
|
{
|
||||||
tf_http_request_ref(request);
|
tf_http_request_ref(request);
|
||||||
tf_ssb_db_resolve_index_async(ssb, host, _httpd_endpoint_root_callback, request);
|
tf_task_t* task = request->user_data;
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
tf_ssb_run_work(ssb, _httpd_accept_eula_work, _httpd_accept_eula_after_work, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_robots_txt(tf_http_request_t* request)
|
static void _httpd_endpoint_robots_txt(tf_http_request_t* request)
|
||||||
@@ -856,31 +925,6 @@ bool tf_httpd_is_name_valid(const char* name)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
void tf_httpd_register(JSContext* context)
|
||||||
{
|
{
|
||||||
JS_NewClassID(&_httpd_request_class_id);
|
JS_NewClassID(&_httpd_request_class_id);
|
||||||
@@ -909,41 +953,18 @@ tf_http_t* tf_httpd_create(JSContext* context)
|
|||||||
tf_http_set_trace(http, tf_task_get_trace(task));
|
tf_http_set_trace(http, tf_task_get_trace(task));
|
||||||
|
|
||||||
int64_t http_port = 0;
|
int64_t http_port = 0;
|
||||||
int64_t https_port = 0;
|
|
||||||
char out_http_port_file[512] = "";
|
char out_http_port_file[512] = "";
|
||||||
bool local_only = false;
|
bool local_only = false;
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
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, "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_db_get_global_setting_string(db, "out_http_port_file", out_http_port_file, sizeof(out_http_port_file));
|
||||||
tf_ssb_db_get_global_setting_bool(db, "http_local_only", &local_only);
|
tf_ssb_db_get_global_setting_bool(db, "http_local_only", &local_only);
|
||||||
tf_ssb_release_db_reader(ssb, db);
|
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, "/", _httpd_endpoint_root, NULL, task);
|
||||||
tf_http_add_handler(http, "/codemirror/*", tf_httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/codemirror/*", tf_httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/lit/*", tf_httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/lit/*", tf_httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/prettier/*", tf_httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/prettier/*", tf_httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/speedscope/*", tf_httpd_endpoint_static, NULL, task);
|
|
||||||
tf_http_add_handler(http, "/static/*", tf_httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/static/*", tf_httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/.well-known/*", tf_httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/.well-known/*", tf_httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/&*.sha256", _httpd_endpoint_add_slash, NULL, task);
|
tf_http_add_handler(http, "/&*.sha256", _httpd_endpoint_add_slash, NULL, task);
|
||||||
@@ -966,6 +987,7 @@ tf_http_t* tf_httpd_create(JSContext* context)
|
|||||||
tf_http_add_handler(http, "/login/logout", tf_httpd_endpoint_logout, NULL, task);
|
tf_http_add_handler(http, "/login/logout", tf_httpd_endpoint_logout, NULL, task);
|
||||||
tf_http_add_handler(http, "/login/auto", tf_httpd_endpoint_login_auto, NULL, task);
|
tf_http_add_handler(http, "/login/auto", tf_httpd_endpoint_login_auto, NULL, task);
|
||||||
tf_http_add_handler(http, "/login", tf_httpd_endpoint_login, NULL, task);
|
tf_http_add_handler(http, "/login", tf_httpd_endpoint_login, NULL, task);
|
||||||
|
tf_http_add_handler(http, "/eula/accept", _httpd_endpoint_accept_eula, NULL, task);
|
||||||
|
|
||||||
tf_http_add_handler(http, "/app/socket", tf_httpd_endpoint_app_socket, NULL, task);
|
tf_http_add_handler(http, "/app/socket", tf_httpd_endpoint_app_socket, NULL, task);
|
||||||
|
|
||||||
@@ -973,7 +995,7 @@ tf_http_t* tf_httpd_create(JSContext* context)
|
|||||||
{
|
{
|
||||||
httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
|
httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
|
||||||
*listener = (httpd_listener_t) { 0 };
|
*listener = (httpd_listener_t) { 0 };
|
||||||
int assigned_port = tf_http_listen(http, http_port, local_only, NULL, _httpd_listener_cleanup, listener);
|
int assigned_port = tf_http_listen(http, http_port, local_only, _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);
|
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)
|
if (*out_http_port_file)
|
||||||
@@ -992,26 +1014,6 @@ tf_http_t* tf_httpd_create(JSContext* context)
|
|||||||
}
|
}
|
||||||
tf_free((char*)actual_http_port_file);
|
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, local_only, 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return http;
|
return http;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ typedef struct _login_request_t
|
|||||||
const char* tf_httpd_make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie)
|
const char* tf_httpd_make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie)
|
||||||
{
|
{
|
||||||
const char* k_pattern = "session=%s; path=/; Max-Age=%" PRId64 "; %sSameSite=Strict; HttpOnly";
|
const char* k_pattern = "session=%s; path=/; Max-Age=%" PRId64 "; %sSameSite=Strict; HttpOnly";
|
||||||
int length = session_cookie ? snprintf(NULL, 0, k_pattern, session_cookie, k_httpd_auth_refresh_interval, request->is_tls ? "Secure; " : "") : 0;
|
int length = session_cookie ? snprintf(NULL, 0, k_pattern, session_cookie, k_httpd_auth_refresh_interval, "") : 0;
|
||||||
char* cookie = length ? tf_malloc(length + 1) : NULL;
|
char* cookie = length ? tf_malloc(length + 1) : NULL;
|
||||||
if (cookie)
|
if (cookie)
|
||||||
{
|
{
|
||||||
snprintf(cookie, length + 1, k_pattern, session_cookie, k_httpd_auth_refresh_interval, request->is_tls ? "Secure; " : "");
|
snprintf(cookie, length + 1, k_pattern, session_cookie, k_httpd_auth_refresh_interval, "");
|
||||||
}
|
}
|
||||||
return cookie;
|
return cookie;
|
||||||
}
|
}
|
||||||
@@ -226,7 +226,7 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf(login->location_header, sizeof(login->location_header), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host"));
|
snprintf(login->location_header, sizeof(login->location_header), "%s%s/", "http://", tf_http_request_get_header(request, "host"));
|
||||||
}
|
}
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
@@ -332,7 +332,7 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf(login->location_header, sizeof(login->location_header), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host"));
|
snprintf(login->location_header, sizeof(login->location_header), "%s%s/", "http://", tf_http_request_get_header(request, "host"));
|
||||||
}
|
}
|
||||||
login->set_cookie_header = tf_httpd_make_set_session_cookie_header(request, send_session);
|
login->set_cookie_header = tf_httpd_make_set_session_cookie_header(request, send_session);
|
||||||
tf_free((void*)send_session);
|
tf_free((void*)send_session);
|
||||||
@@ -352,7 +352,7 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
{
|
{
|
||||||
JSValue settings_value = JS_ParseJSON(context, login->settings, strlen(login->settings), NULL);
|
JSValue settings_value = JS_ParseJSON(context, login->settings, strlen(login->settings), NULL);
|
||||||
JSValue code_of_conduct_value = JS_GetPropertyStr(context, settings_value, "code_of_conduct");
|
JSValue code_of_conduct_value = JS_GetPropertyStr(context, settings_value, "code_of_conduct");
|
||||||
const char* code_of_conduct = JS_ToCString(context, code_of_conduct_value);
|
const char* code_of_conduct = JS_IsString(code_of_conduct_value) ? JS_ToCString(context, code_of_conduct_value) : NULL;
|
||||||
const char* result = tf_strdup(code_of_conduct);
|
const char* result = tf_strdup(code_of_conduct);
|
||||||
JS_FreeCString(context, code_of_conduct);
|
JS_FreeCString(context, code_of_conduct);
|
||||||
JS_FreeValue(context, code_of_conduct_value);
|
JS_FreeValue(context, code_of_conduct_value);
|
||||||
@@ -416,8 +416,7 @@ void tf_httpd_endpoint_login(tf_http_request_t* request)
|
|||||||
|
|
||||||
void tf_httpd_endpoint_logout(tf_http_request_t* request)
|
void tf_httpd_endpoint_logout(tf_http_request_t* request)
|
||||||
{
|
{
|
||||||
const char* k_set_cookie = request->is_tls ? "session=; path=/; Secure; SameSite=Strict; expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly"
|
const char* k_set_cookie = "session=; path=/; SameSite=Strict; expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly";
|
||||||
: "session=; path=/; SameSite=Strict; expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly";
|
|
||||||
const char* k_location_format = "/login%s%s";
|
const char* k_location_format = "/login%s%s";
|
||||||
int length = snprintf(NULL, 0, k_location_format, request->query ? "?" : "", request->query);
|
int length = snprintf(NULL, 0, k_location_format, request->query ? "?" : "", request->query);
|
||||||
char* location = alloca(length + 1);
|
char* location = alloca(length + 1);
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ void tf_httpd_endpoint_static(tf_http_request_t* request)
|
|||||||
|
|
||||||
const char* k_static_files[] = {
|
const char* k_static_files[] = {
|
||||||
"index.html",
|
"index.html",
|
||||||
|
"eula.html",
|
||||||
"client.js",
|
"client.js",
|
||||||
"tildefriends.svg",
|
"tildefriends.svg",
|
||||||
"jszip.min.js",
|
"jszip.min.js",
|
||||||
@@ -137,7 +138,6 @@ void tf_httpd_endpoint_static(tf_http_request_t* request)
|
|||||||
{ "/lit/", "deps/lit/" },
|
{ "/lit/", "deps/lit/" },
|
||||||
{ "/codemirror/", "deps/codemirror/" },
|
{ "/codemirror/", "deps/codemirror/" },
|
||||||
{ "/prettier/", "deps/prettier/" },
|
{ "/prettier/", "deps/prettier/" },
|
||||||
{ "/speedscope/", "deps/speedscope/" },
|
|
||||||
{ "/.well-known/", "data/global/.well-known/" },
|
{ "/.well-known/", "data/global/.well-known/" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
144
src/ios.m
@@ -1,19 +1,28 @@
|
|||||||
|
#import <CoreSpotlight/CSSearchableIndex.h>
|
||||||
|
#import <CoreSpotlight/CSSearchableItem.h>
|
||||||
|
#import <CoreSpotlight/CSSearchableItemAttributeSet.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
#import <WebKit/WKDownload.h>
|
||||||
|
#import <WebKit/WKDownloadDelegate.h>
|
||||||
|
#import <WebKit/WKNavigationAction.h>
|
||||||
#import <WebKit/WKNavigationDelegate.h>
|
#import <WebKit/WKNavigationDelegate.h>
|
||||||
|
#import <WebKit/WKNavigationResponse.h>
|
||||||
#import <WebKit/WKUIDelegate.h>
|
#import <WebKit/WKUIDelegate.h>
|
||||||
#import <WebKit/WKWebView.h>
|
#import <WebKit/WKWebView.h>
|
||||||
#import <WebKit/WKWebViewConfiguration.h>
|
#import <WebKit/WKWebViewConfiguration.h>
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "util.js.h"
|
||||||
|
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void tf_run_thread_start(const char* zip_path);
|
void tf_run_thread_start(const char* zip_path);
|
||||||
|
|
||||||
@interface ViewController : UINavigationController <WKUIDelegate, WKNavigationDelegate>
|
@interface ViewController : UINavigationController <WKUIDelegate, WKNavigationDelegate, WKDownloadDelegate, UIDocumentPickerDelegate>
|
||||||
@property (strong, nonatomic) WKWebView* web_view;
|
@property (strong, nonatomic) WKWebView* web_view;
|
||||||
@property bool initial_load_complete;
|
@property bool initial_load_complete;
|
||||||
|
@property (retain) NSURL* download_url;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static void _start_initial_load(WKWebView* web_view)
|
static void _start_initial_load(WKWebView* web_view)
|
||||||
@@ -26,20 +35,17 @@ static void _start_initial_load(WKWebView* web_view)
|
|||||||
{
|
{
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
|
||||||
[self setToolbarHidden:false animated:false];
|
|
||||||
self.toolbar.items = @[
|
|
||||||
[[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStylePlain target:self action:@selector(goBack)],
|
|
||||||
[[UIBarButtonItem alloc] initWithTitle:@"Forward" style:UIBarButtonItemStylePlain target:self action:@selector(goForward)],
|
|
||||||
[[UIBarButtonItem alloc] initWithTitle:@"Refresh" style:UIBarButtonItemStylePlain target:self action:@selector(reload)]
|
|
||||||
];
|
|
||||||
|
|
||||||
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
|
WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
|
||||||
self.web_view = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
|
self.web_view = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
|
||||||
self.web_view.UIDelegate = self;
|
self.web_view.UIDelegate = self;
|
||||||
self.web_view.navigationDelegate = self;
|
self.web_view.navigationDelegate = self;
|
||||||
self.web_view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
self.web_view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
||||||
self.web_view.translatesAutoresizingMaskIntoConstraints = false;
|
self.web_view.translatesAutoresizingMaskIntoConstraints = false;
|
||||||
|
self.web_view.allowsBackForwardNavigationGestures = true;
|
||||||
[self.view addSubview:self.web_view];
|
[self.view addSubview:self.web_view];
|
||||||
|
UIRefreshControl* refresh = [[UIRefreshControl alloc] init];
|
||||||
|
[refresh addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
|
||||||
|
self.web_view.scrollView.refreshControl = refresh;
|
||||||
|
|
||||||
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view
|
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.web_view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view
|
||||||
attribute:NSLayoutAttributeTop
|
attribute:NSLayoutAttributeTop
|
||||||
@@ -61,30 +67,21 @@ static void _start_initial_load(WKWebView* web_view)
|
|||||||
_start_initial_load(self.web_view);
|
_start_initial_load(self.web_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)goBack
|
- (void)handleRefresh:(id)sender
|
||||||
{
|
|
||||||
if (self.web_view.canGoBack)
|
|
||||||
{
|
|
||||||
[self.web_view goBack];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)goForward
|
|
||||||
{
|
|
||||||
if (self.web_view.canGoForward)
|
|
||||||
{
|
|
||||||
[self.web_view goForward];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)reload
|
|
||||||
{
|
{
|
||||||
|
tf_printf("refresh\n");
|
||||||
[self.web_view reload];
|
[self.web_view reload];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation
|
- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation
|
||||||
{
|
{
|
||||||
self.initial_load_complete = true;
|
if (!self.initial_load_complete)
|
||||||
|
{
|
||||||
|
tf_printf("initial load complete\n");
|
||||||
|
self.initial_load_complete = true;
|
||||||
|
}
|
||||||
|
self.navigationController.interactivePopGestureRecognizer.enabled = self.web_view.canGoBack;
|
||||||
|
[self.web_view.scrollView.refreshControl endRefreshing];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)webView:(WKWebView*)webView didFailProvisionalNavigation:(WKNavigation*)navigation withError:(NSError*)error
|
- (void)webView:(WKWebView*)webView didFailProvisionalNavigation:(WKNavigation*)navigation withError:(NSError*)error
|
||||||
@@ -133,6 +130,59 @@ static void _start_initial_load(WKWebView* web_view)
|
|||||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) { completionHandler(nil); }]];
|
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action) { completionHandler(nil); }]];
|
||||||
[self presentViewController:alertController animated:YES completion:^ {}];
|
[self presentViewController:alertController animated:YES completion:^ {}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)webView:(WKWebView*)webView decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction decisionHandler:(void (^)(enum WKNavigationActionPolicy))decisionHandler
|
||||||
|
{
|
||||||
|
decisionHandler(navigationAction.shouldPerformDownload ? WKNavigationActionPolicyDownload : WKNavigationActionPolicyAllow);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)webView:(WKWebView*)webView
|
||||||
|
decidePolicyForNavigationResponse:(WKNavigationResponse*)navigationResponse
|
||||||
|
decisionHandler:(void (^)(enum WKNavigationResponsePolicy))decisionHandler
|
||||||
|
{
|
||||||
|
decisionHandler(navigationResponse.canShowMIMEType ? WKNavigationResponsePolicyAllow : WKNavigationResponsePolicyDownload);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)webView:(WKWebView*)webView navigationAction:(WKNavigationAction*)navigationAction didBecomeDownload:(WKDownload*)download
|
||||||
|
{
|
||||||
|
download.delegate = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)webView:(WKWebView*)webView navigationResponse:(WKNavigationResponse*)navigationResponse didBecomeDownload:(WKDownload*)download
|
||||||
|
{
|
||||||
|
download.delegate = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)download:(WKDownload*)download
|
||||||
|
decideDestinationUsingResponse:(NSURLResponse*)response
|
||||||
|
suggestedFilename:(NSString*)suggestedFilename
|
||||||
|
completionHandler:(void (^)(NSURL*))completionHandler
|
||||||
|
{
|
||||||
|
self.download_url = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:suggestedFilename];
|
||||||
|
completionHandler(self.download_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)downloadDidFinish:(WKDownload*)download
|
||||||
|
{
|
||||||
|
UIDocumentPickerViewController* picker = [[UIDocumentPickerViewController alloc] initForExportingURLs:@[ self.download_url ]];
|
||||||
|
picker.delegate = self;
|
||||||
|
[self presentViewController:picker animated:YES completion:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)download:(WKDownload*)download didFailWithError:(NSError*)error resumeData:(NSData*)resumeData
|
||||||
|
{
|
||||||
|
tf_printf("download didFailWithError:%s\n", [error.localizedDescription UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)documentPicker:(UIDocumentPickerViewController*)controller didPickDocumentAtURLs:(NSArray<NSURL*>*)urls
|
||||||
|
{
|
||||||
|
[[NSFileManager defaultManager] removeItemAtURL:self.download_url error:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController*)controller
|
||||||
|
{
|
||||||
|
[[NSFileManager defaultManager] removeItemAtURL:self.download_url error:nil];
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||||
@@ -148,8 +198,50 @@ static void _start_initial_load(WKWebView* web_view)
|
|||||||
[self.window makeKeyAndVisible];
|
[self.window makeKeyAndVisible];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication*)application
|
||||||
|
continueUserActivity:(NSUserActivity*)activity
|
||||||
|
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>>*))restorationHandler
|
||||||
|
{
|
||||||
|
if ([activity.activityType isEqual:CSSearchableItemActionType])
|
||||||
|
{
|
||||||
|
const char* identifier = [[activity.userInfo valueForKey:CSSearchableItemActivityIdentifier] UTF8String];
|
||||||
|
tf_printf("Jumping to search result: %s.\n", identifier);
|
||||||
|
|
||||||
|
char url[1024];
|
||||||
|
snprintf(url, sizeof(url), "http://localhost:12345/~core/ssb/#%s", identifier);
|
||||||
|
tf_printf("Navigating to %s.", url);
|
||||||
|
ViewController* view_controller = (ViewController*)self.window.rootViewController;
|
||||||
|
[view_controller.web_view loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]]];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_printf("no search\n");
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
void tf_notify_message_added_ios(const char* identifier, const char* title, const char* content)
|
||||||
|
{
|
||||||
|
tf_printf("indexing: identifier=%s title=%s content=%s\n", identifier, title, content);
|
||||||
|
CSSearchableItemAttributeSet* attribute_set = [[CSSearchableItemAttributeSet alloc] initWithContentType:UTTypeText];
|
||||||
|
attribute_set.title = [NSString stringWithUTF8String:content];
|
||||||
|
attribute_set.contentDescription = [NSString stringWithUTF8String:title];
|
||||||
|
CSSearchableItem* item = [[CSSearchableItem alloc] initWithUniqueIdentifier:[NSString stringWithUTF8String:identifier] domainIdentifier:@"com.unprompted.tildefriends.messages"
|
||||||
|
attributeSet:attribute_set];
|
||||||
|
[[CSSearchableIndex defaultSearchableIndex] indexSearchableItems:@[ item ] completionHandler:^(NSError* _Nullable error) {
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
tf_printf("indexing error: %s.\n", [error.localizedDescription UTF8String]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_printf("indexed successfully.\n");
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
NSFileManager* file_manager = [NSFileManager defaultManager];
|
NSFileManager* file_manager = [NSFileManager defaultManager];
|
||||||
|
|||||||
@@ -13,19 +13,19 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.2025.8</string>
|
<string>0.2025.12</string>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>16</string>
|
<string>27</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>iphoneos</string>
|
<string>iphoneos</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
<string>14.0</string>
|
<string>14.5</string>
|
||||||
<key>UIDeviceFamily</key>
|
<key>UIDeviceFamily</key>
|
||||||
<array>
|
<array>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
@@ -83,5 +83,13 @@
|
|||||||
</dict>
|
</dict>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>Camera access is used to take pictures to add to posts.</string>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>Microphone access is used to capture audio to add to posts.</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>Photo library access is used to select images to add to posts.</string>
|
||||||
|
<key>NSDownlodasFolderUsageDescription</key>
|
||||||
|
<string>Downloads folder access is used to export and import apps.</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
BIN
src/ios/tildefriends512.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
@@ -17,12 +17,15 @@
|
|||||||
#include <TargetConditionals.h>
|
#include <TargetConditionals.h>
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#include <os/log.h>
|
#include <os/log.h>
|
||||||
|
#include <stdio.h>
|
||||||
#define tf_printf(...) \
|
#define tf_printf(...) \
|
||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
char buffer##__LINE__[2048]; \
|
char buffer##__LINE__[2048]; \
|
||||||
snprintf(buffer##__LINE__, sizeof(buffer##__LINE__), __VA_ARGS__); \
|
snprintf(buffer##__LINE__, sizeof(buffer##__LINE__), __VA_ARGS__); \
|
||||||
os_log(OS_LOG_DEFAULT, "%{public}s", buffer##__LINE__); \
|
os_log(OS_LOG_DEFAULT, "%{public}s", buffer##__LINE__); \
|
||||||
|
fputs(buffer##__LINE__, stdout); \
|
||||||
|
fflush(stdout); \
|
||||||
} while (0)
|
} while (0)
|
||||||
#else
|
#else
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|||||||
23
src/main.c
@@ -1502,13 +1502,11 @@ static int _tf_run_task(const tf_run_args_t* args, int index)
|
|||||||
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
int64_t http_port = 0;
|
int64_t http_port = 0;
|
||||||
int64_t https_port = 0;
|
|
||||||
char out_http_port_file[512] = "";
|
char out_http_port_file[512] = "";
|
||||||
tf_ssb_db_get_global_setting_int64(db, "http_port", &http_port);
|
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_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);
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
if (http_port || https_port || *out_http_port_file)
|
if (http_port || *out_http_port_file)
|
||||||
{
|
{
|
||||||
if (args->zip)
|
if (args->zip)
|
||||||
{
|
{
|
||||||
@@ -1578,28 +1576,28 @@ static void _shed_privileges()
|
|||||||
if (setrlimit(RLIMIT_FSIZE, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_FSIZE, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_FSIZE, {0, 0})");
|
perror("setrlimit(RLIMIT_FSIZE, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (setrlimit(RLIMIT_NOFILE, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_NOFILE, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_NOFILE, {0, 0})");
|
perror("setrlimit(RLIMIT_NOFILE, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (setrlimit(RLIMIT_NPROC, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_NPROC, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_NPROC, {0, 0})");
|
perror("setrlimit(RLIMIT_NPROC, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#if !defined(__MACH__) && !defined(__OpenBSD__)
|
#if !defined(__MACH__) && !defined(__OpenBSD__)
|
||||||
if (setrlimit(RLIMIT_LOCKS, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_LOCKS, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_LOCKS, {0, 0})");
|
perror("setrlimit(RLIMIT_LOCKS, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (setrlimit(RLIMIT_MSGQUEUE, &zeroLimit) != 0)
|
if (setrlimit(RLIMIT_MSGQUEUE, &zeroLimit) != 0)
|
||||||
{
|
{
|
||||||
perror("setrlimit(RLIMIT_MSGQUEUE, {0, 0})");
|
perror("setrlimit(RLIMIT_MSGQUEUE, {0, 0})");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -1609,12 +1607,12 @@ static void _shed_privileges()
|
|||||||
if (unveil("/dev/null", "r") || unveil(NULL, NULL))
|
if (unveil("/dev/null", "r") || unveil(NULL, NULL))
|
||||||
{
|
{
|
||||||
perror("unveil");
|
perror("unveil");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
if (pledge("stdio unveil", NULL))
|
if (pledge("stdio unveil", NULL))
|
||||||
{
|
{
|
||||||
perror("pledge");
|
perror("pledge");
|
||||||
exit(-1);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1831,7 +1829,7 @@ static void _error_handler(int sig)
|
|||||||
const char* stack = tf_util_backtrace_string();
|
const char* stack = tf_util_backtrace_string();
|
||||||
tf_printf("ERROR:\n%s\n", stack);
|
tf_printf("ERROR:\n%s\n", stack);
|
||||||
tf_free((void*)stack);
|
tf_free((void*)stack);
|
||||||
_exit(1);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
@@ -1843,7 +1841,7 @@ static LONG WINAPI _win32_exception_handler(EXCEPTION_POINTERS* info)
|
|||||||
const char* stack = tf_util_backtrace_string();
|
const char* stack = tf_util_backtrace_string();
|
||||||
tf_printf("ERROR:\n%s\n", stack);
|
tf_printf("ERROR:\n%s\n", stack);
|
||||||
tf_free((void*)stack);
|
tf_free((void*)stack);
|
||||||
_exit(1);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
@@ -1869,7 +1867,6 @@ static void _startup(int argc, char* argv[])
|
|||||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||||
#endif
|
#endif
|
||||||
tf_mem_replace_uv_allocator();
|
tf_mem_replace_uv_allocator();
|
||||||
tf_mem_replace_tls_allocator();
|
|
||||||
tf_mem_replace_sqlite_allocator();
|
tf_mem_replace_sqlite_allocator();
|
||||||
uv_setup_args(argc, argv);
|
uv_setup_args(argc, argv);
|
||||||
tf_taskstub_startup();
|
tf_taskstub_startup();
|
||||||
|
|||||||
28
src/mem.c
@@ -7,8 +7,6 @@
|
|||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
|
|
||||||
#include <openssl/crypto.h>
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -19,7 +17,6 @@ static bool s_mem_tracking;
|
|||||||
static tf_mem_node_t* s_mem_tracked;
|
static tf_mem_node_t* s_mem_tracked;
|
||||||
static int64_t s_tf_malloc_size;
|
static int64_t s_tf_malloc_size;
|
||||||
static int64_t s_uv_malloc_size;
|
static int64_t s_uv_malloc_size;
|
||||||
static int64_t s_tls_malloc_size;
|
|
||||||
static int64_t s_js_malloc_size;
|
static int64_t s_js_malloc_size;
|
||||||
static int64_t s_sqlite_malloc_size;
|
static int64_t s_sqlite_malloc_size;
|
||||||
|
|
||||||
@@ -387,31 +384,6 @@ size_t tf_mem_get_uv_malloc_size()
|
|||||||
return s_uv_malloc_size;
|
return s_uv_malloc_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* _tf_tls_alloc(size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
return _tf_alloc(&s_tls_malloc_size, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void* _tf_tls_realloc(void* ptr, size_t size, const char* file, int line)
|
|
||||||
{
|
|
||||||
return _tf_realloc(&s_tls_malloc_size, ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tf_tls_free(void* ptr, const char* file, int line)
|
|
||||||
{
|
|
||||||
_tf_free(&s_tls_malloc_size, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tf_mem_replace_tls_allocator()
|
|
||||||
{
|
|
||||||
CRYPTO_set_mem_functions(_tf_tls_alloc, _tf_tls_realloc, _tf_tls_free);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t tf_mem_get_tls_malloc_size()
|
|
||||||
{
|
|
||||||
return s_tls_malloc_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* tf_malloc(size_t size)
|
void* tf_malloc(size_t size)
|
||||||
{
|
{
|
||||||
return _tf_alloc(&s_tf_malloc_size, size);
|
return _tf_alloc(&s_tf_malloc_size, size);
|
||||||
|
|||||||
13
src/mem.h
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
** \defgroup mem Memory management
|
** \defgroup mem Memory management
|
||||||
** tf_malloc() and friends use malloc() behind the scenes but optionally
|
** tf_malloc() and friends use malloc() behind the scenes but optionally
|
||||||
** track memory per system (OpenSSL, sqlite, libuv, ...) and store callstacks
|
** track memory per system (sqlite, libuv, ...) and store callstacks
|
||||||
** to help debug leaks.
|
** to help debug leaks.
|
||||||
** @{
|
** @{
|
||||||
*/
|
*/
|
||||||
@@ -38,17 +38,6 @@ void tf_mem_replace_uv_allocator();
|
|||||||
*/
|
*/
|
||||||
size_t tf_mem_get_uv_malloc_size();
|
size_t tf_mem_get_uv_malloc_size();
|
||||||
|
|
||||||
/**
|
|
||||||
** Register a custom allocator with OpenSSL.
|
|
||||||
*/
|
|
||||||
void tf_mem_replace_tls_allocator();
|
|
||||||
|
|
||||||
/**
|
|
||||||
** Get the number of bytes currently allocated by OpenSSL.
|
|
||||||
** @return The allocated size in bytes.
|
|
||||||
*/
|
|
||||||
size_t tf_mem_get_tls_malloc_size();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Register a custom allocator with SQLite.
|
** Register a custom allocator with SQLite.
|
||||||
*/
|
*/
|
||||||
|
|||||||
329
src/sha1.c
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
* SHA1 hash implementation and interface functions
|
||||||
|
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sha1.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ===== start - public domain SHA1 implementation ===== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
SHA-1 in C
|
||||||
|
By Steve Reid <sreid@sea-to-sky.net>
|
||||||
|
100% Public Domain
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Modified 7/98
|
||||||
|
By James H. Brown <jbrown@burgoyne.com>
|
||||||
|
Still 100% Public Domain
|
||||||
|
|
||||||
|
Corrected a problem which generated improper hash values on 16 bit machines
|
||||||
|
Routine SHA1Update changed from
|
||||||
|
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
||||||
|
len)
|
||||||
|
to
|
||||||
|
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
||||||
|
long len)
|
||||||
|
|
||||||
|
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
||||||
|
However, on 16 bit machines an int is too small for the shifts being done
|
||||||
|
against it. This caused the hash function to generate incorrect values if len
|
||||||
|
was greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
|
||||||
|
|
||||||
|
Since the file IO in main() reads 16K at a time, any file 8K or larger would be
|
||||||
|
guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s).
|
||||||
|
|
||||||
|
I also changed the declaration of variables i & j in SHA1Update to unsigned
|
||||||
|
long from unsigned int for the same reason.
|
||||||
|
|
||||||
|
These changes should make no difference to any 32 bit implementations since an
|
||||||
|
int and a long are the same size in those environments.
|
||||||
|
|
||||||
|
--
|
||||||
|
I also corrected a few compiler warnings generated by Borland C.
|
||||||
|
1. Added #include <process.h> for exit() prototype
|
||||||
|
2. Removed unused variable 'j' in SHA1Final
|
||||||
|
3. Changed exit(0) to return(0) at end of main.
|
||||||
|
|
||||||
|
ALL changes I made can be located by searching for comments containing 'JHB'
|
||||||
|
-----------------
|
||||||
|
Modified 8/98
|
||||||
|
By Steve Reid <sreid@sea-to-sky.net>
|
||||||
|
Still 100% public domain
|
||||||
|
|
||||||
|
1- Removed #include <process.h> and used return() instead of exit()
|
||||||
|
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
|
||||||
|
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Modified 4/01
|
||||||
|
By Saul Kravitz <Saul.Kravitz@celera.com>
|
||||||
|
Still 100% PD
|
||||||
|
Modified to run on Compaq Alpha hardware.
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Modified 4/01
|
||||||
|
By Jouni Malinen <j@w1.fi>
|
||||||
|
Minor changes to match the coding style used in Dynamics.
|
||||||
|
|
||||||
|
Modified September 24, 2004
|
||||||
|
By Jouni Malinen <j@w1.fi>
|
||||||
|
Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined.
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
Modified September 29, 2025
|
||||||
|
By Cory McWilliams <cory@tildefriends.net>
|
||||||
|
Adapted from
|
||||||
|
https://web.mit.edu/freebsd/head/contrib/wpa/src/crypto/sha1-internal.c.
|
||||||
|
Modified to build outside of FreeBSD. Updated with clang-format.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test Vectors (from FIPS PUB 180-1)
|
||||||
|
"abc"
|
||||||
|
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||||
|
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||||
|
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||||
|
A million repetitions of "a"
|
||||||
|
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SHA1HANDSOFF
|
||||||
|
|
||||||
|
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||||
|
|
||||||
|
/* blk0() and blk() perform the initial expand. */
|
||||||
|
/* I got the idea of expanding during the round function from SSLeay */
|
||||||
|
#ifndef WORDS_BIGENDIAN
|
||||||
|
#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF))
|
||||||
|
#else
|
||||||
|
#define blk0(i) block->l[i]
|
||||||
|
#endif
|
||||||
|
#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
|
||||||
|
|
||||||
|
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||||
|
#define R0(v, w, x, y, z, i) \
|
||||||
|
z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R1(v, w, x, y, z, i) \
|
||||||
|
z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R2(v, w, x, y, z, i) \
|
||||||
|
z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R3(v, w, x, y, z, i) \
|
||||||
|
z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
#define R4(v, w, x, y, z, i) \
|
||||||
|
z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
|
||||||
|
w = rol(w, 30);
|
||||||
|
|
||||||
|
#ifdef VERBOSE /* SAK */
|
||||||
|
void SHAPrintContext(SHA1_CTX* context, char* msg)
|
||||||
|
{
|
||||||
|
printf("%s (%d,%d) %x %x %x %x %x\n", msg, context->count[0], context->count[1], context->state[0], context->state[1], context->state[2], context->state[3], context->state[4]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||||
|
|
||||||
|
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
|
||||||
|
{
|
||||||
|
uint32_t a, b, c, d, e;
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
unsigned char c[64];
|
||||||
|
uint32_t l[16];
|
||||||
|
} CHAR64LONG16;
|
||||||
|
CHAR64LONG16* block;
|
||||||
|
#ifdef SHA1HANDSOFF
|
||||||
|
CHAR64LONG16 workspace;
|
||||||
|
block = &workspace;
|
||||||
|
memcpy(block, buffer, 64);
|
||||||
|
#else
|
||||||
|
block = (CHAR64LONG16*)buffer;
|
||||||
|
#endif
|
||||||
|
/* Copy context->state[] to working vars */
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
e = state[4];
|
||||||
|
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||||
|
R0(a, b, c, d, e, 0);
|
||||||
|
R0(e, a, b, c, d, 1);
|
||||||
|
R0(d, e, a, b, c, 2);
|
||||||
|
R0(c, d, e, a, b, 3);
|
||||||
|
R0(b, c, d, e, a, 4);
|
||||||
|
R0(a, b, c, d, e, 5);
|
||||||
|
R0(e, a, b, c, d, 6);
|
||||||
|
R0(d, e, a, b, c, 7);
|
||||||
|
R0(c, d, e, a, b, 8);
|
||||||
|
R0(b, c, d, e, a, 9);
|
||||||
|
R0(a, b, c, d, e, 10);
|
||||||
|
R0(e, a, b, c, d, 11);
|
||||||
|
R0(d, e, a, b, c, 12);
|
||||||
|
R0(c, d, e, a, b, 13);
|
||||||
|
R0(b, c, d, e, a, 14);
|
||||||
|
R0(a, b, c, d, e, 15);
|
||||||
|
R1(e, a, b, c, d, 16);
|
||||||
|
R1(d, e, a, b, c, 17);
|
||||||
|
R1(c, d, e, a, b, 18);
|
||||||
|
R1(b, c, d, e, a, 19);
|
||||||
|
R2(a, b, c, d, e, 20);
|
||||||
|
R2(e, a, b, c, d, 21);
|
||||||
|
R2(d, e, a, b, c, 22);
|
||||||
|
R2(c, d, e, a, b, 23);
|
||||||
|
R2(b, c, d, e, a, 24);
|
||||||
|
R2(a, b, c, d, e, 25);
|
||||||
|
R2(e, a, b, c, d, 26);
|
||||||
|
R2(d, e, a, b, c, 27);
|
||||||
|
R2(c, d, e, a, b, 28);
|
||||||
|
R2(b, c, d, e, a, 29);
|
||||||
|
R2(a, b, c, d, e, 30);
|
||||||
|
R2(e, a, b, c, d, 31);
|
||||||
|
R2(d, e, a, b, c, 32);
|
||||||
|
R2(c, d, e, a, b, 33);
|
||||||
|
R2(b, c, d, e, a, 34);
|
||||||
|
R2(a, b, c, d, e, 35);
|
||||||
|
R2(e, a, b, c, d, 36);
|
||||||
|
R2(d, e, a, b, c, 37);
|
||||||
|
R2(c, d, e, a, b, 38);
|
||||||
|
R2(b, c, d, e, a, 39);
|
||||||
|
R3(a, b, c, d, e, 40);
|
||||||
|
R3(e, a, b, c, d, 41);
|
||||||
|
R3(d, e, a, b, c, 42);
|
||||||
|
R3(c, d, e, a, b, 43);
|
||||||
|
R3(b, c, d, e, a, 44);
|
||||||
|
R3(a, b, c, d, e, 45);
|
||||||
|
R3(e, a, b, c, d, 46);
|
||||||
|
R3(d, e, a, b, c, 47);
|
||||||
|
R3(c, d, e, a, b, 48);
|
||||||
|
R3(b, c, d, e, a, 49);
|
||||||
|
R3(a, b, c, d, e, 50);
|
||||||
|
R3(e, a, b, c, d, 51);
|
||||||
|
R3(d, e, a, b, c, 52);
|
||||||
|
R3(c, d, e, a, b, 53);
|
||||||
|
R3(b, c, d, e, a, 54);
|
||||||
|
R3(a, b, c, d, e, 55);
|
||||||
|
R3(e, a, b, c, d, 56);
|
||||||
|
R3(d, e, a, b, c, 57);
|
||||||
|
R3(c, d, e, a, b, 58);
|
||||||
|
R3(b, c, d, e, a, 59);
|
||||||
|
R4(a, b, c, d, e, 60);
|
||||||
|
R4(e, a, b, c, d, 61);
|
||||||
|
R4(d, e, a, b, c, 62);
|
||||||
|
R4(c, d, e, a, b, 63);
|
||||||
|
R4(b, c, d, e, a, 64);
|
||||||
|
R4(a, b, c, d, e, 65);
|
||||||
|
R4(e, a, b, c, d, 66);
|
||||||
|
R4(d, e, a, b, c, 67);
|
||||||
|
R4(c, d, e, a, b, 68);
|
||||||
|
R4(b, c, d, e, a, 69);
|
||||||
|
R4(a, b, c, d, e, 70);
|
||||||
|
R4(e, a, b, c, d, 71);
|
||||||
|
R4(d, e, a, b, c, 72);
|
||||||
|
R4(c, d, e, a, b, 73);
|
||||||
|
R4(b, c, d, e, a, 74);
|
||||||
|
R4(a, b, c, d, e, 75);
|
||||||
|
R4(e, a, b, c, d, 76);
|
||||||
|
R4(d, e, a, b, c, 77);
|
||||||
|
R4(c, d, e, a, b, 78);
|
||||||
|
R4(b, c, d, e, a, 79);
|
||||||
|
/* Add the working vars back into context.state[] */
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
state[4] += e;
|
||||||
|
/* Wipe variables */
|
||||||
|
a = b = c = d = e = 0;
|
||||||
|
#ifdef SHA1HANDSOFF
|
||||||
|
memset(block, 0, 64);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SHA1Init - Initialize new context */
|
||||||
|
|
||||||
|
void SHA1Init(SHA1_CTX* context)
|
||||||
|
{
|
||||||
|
/* SHA1 initialization constants */
|
||||||
|
context->state[0] = 0x67452301;
|
||||||
|
context->state[1] = 0xEFCDAB89;
|
||||||
|
context->state[2] = 0x98BADCFE;
|
||||||
|
context->state[3] = 0x10325476;
|
||||||
|
context->state[4] = 0xC3D2E1F0;
|
||||||
|
context->count[0] = context->count[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run your data through this. */
|
||||||
|
|
||||||
|
void SHA1Update(SHA1_CTX* context, const void* _data, uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t i, j;
|
||||||
|
const unsigned char* data = _data;
|
||||||
|
|
||||||
|
#ifdef VERBOSE
|
||||||
|
SHAPrintContext(context, "before");
|
||||||
|
#endif
|
||||||
|
j = (context->count[0] >> 3) & 63;
|
||||||
|
if ((context->count[0] += len << 3) < (len << 3))
|
||||||
|
context->count[1]++;
|
||||||
|
context->count[1] += (len >> 29);
|
||||||
|
if ((j + len) > 63)
|
||||||
|
{
|
||||||
|
memcpy(&context->buffer[j], data, (i = 64 - j));
|
||||||
|
SHA1Transform(context->state, context->buffer);
|
||||||
|
for (; i + 63 < len; i += 64)
|
||||||
|
{
|
||||||
|
SHA1Transform(context->state, &data[i]);
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i = 0;
|
||||||
|
memcpy(&context->buffer[j], &data[i], len - i);
|
||||||
|
#ifdef VERBOSE
|
||||||
|
SHAPrintContext(context, "after ");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add padding and return the message digest. */
|
||||||
|
|
||||||
|
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
unsigned char finalcount[8];
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
/* Endian independent */
|
||||||
|
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255);
|
||||||
|
}
|
||||||
|
SHA1Update(context, (unsigned char*)"\200", 1);
|
||||||
|
while ((context->count[0] & 504) != 448)
|
||||||
|
{
|
||||||
|
SHA1Update(context, (unsigned char*)"\0", 1);
|
||||||
|
}
|
||||||
|
/* Should cause a SHA1Transform() */
|
||||||
|
SHA1Update(context, finalcount, 8);
|
||||||
|
for (i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
digest[i] = (unsigned char)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||||
|
}
|
||||||
|
/* Wipe variables */
|
||||||
|
i = 0;
|
||||||
|
memset(context->buffer, 0, 64);
|
||||||
|
memset(context->state, 0, 20);
|
||||||
|
memset(context->count, 0, 8);
|
||||||
|
memset(finalcount, 0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== end - public domain SHA1 implementation ===== */
|
||||||
71
src/sha1.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* SHA1 internal definitions
|
||||||
|
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
|
||||||
|
*
|
||||||
|
* This software may be distributed under the terms of the BSD license.
|
||||||
|
* See README for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
** \defgroup sha1 SHA1
|
||||||
|
** SHA1 API.
|
||||||
|
** Adapted from
|
||||||
|
** https://web.mit.edu/freebsd/head/contrib/wpa/src/crypto/sha1_i.h by Cory
|
||||||
|
** McWilliams 2025-09-28.
|
||||||
|
** @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SHA1_I_H
|
||||||
|
#define SHA1_I_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
** SHA1 context struct.
|
||||||
|
*/
|
||||||
|
struct SHA1Context
|
||||||
|
{
|
||||||
|
/** SHA1 state. */
|
||||||
|
uint32_t state[5];
|
||||||
|
/** SHA1 count. */
|
||||||
|
uint32_t count[2];
|
||||||
|
/** SHA1 buffer. */
|
||||||
|
unsigned char buffer[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
** SHA1 context.
|
||||||
|
*/
|
||||||
|
typedef struct SHA1Context SHA1_CTX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Initialize a SHA1 context.
|
||||||
|
** @param context The context.
|
||||||
|
*/
|
||||||
|
void SHA1Init(struct SHA1Context* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Calculate an ongoing hash for a block of data.
|
||||||
|
** @param context The SHA1 context.
|
||||||
|
** @param data The data to hash.
|
||||||
|
** @param len The length of data.
|
||||||
|
*/
|
||||||
|
void SHA1Update(struct SHA1Context* context, const void* data, uint32_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Calculate the final hash digest.
|
||||||
|
** @param digest Populated with the digest.
|
||||||
|
** @param context The SHA1 context.
|
||||||
|
*/
|
||||||
|
void SHA1Final(unsigned char digest[20], struct SHA1Context* context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Perform a SHA1 transformation.
|
||||||
|
** @param state The SHA1 state.
|
||||||
|
** @param buffer The data.
|
||||||
|
*/
|
||||||
|
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
|
||||||
|
|
||||||
|
#endif /* SHA1_I_H */
|
||||||
|
|
||||||
|
/** @} */
|
||||||