Compare commits
44 Commits
v0.0.31
...
3c17810747
Author | SHA1 | Date | |
---|---|---|---|
3c17810747 | |||
bea7a2e9ed | |||
2f0a2ac6b0 | |||
18908b6b56 | |||
b135a210cc | |||
3a2a829940 | |||
e56dd2dd2d | |||
3f41a48bc7 | |||
65ed53281a | |||
1121557a2e | |||
d4a7b86ee7 | |||
626c18b04e | |||
bfa97ed7c7 | |||
deae4d5367 | |||
899605c860 | |||
dc9a279991 | |||
2a53892581 | |||
6bef0eb764 | |||
462b40640c | |||
72e1b2025c | |||
fc7c4b1257 | |||
6c22c59056 | |||
94c2b1184f | |||
45231d703d | |||
7882fcbe8f | |||
3bbc8c4d35 | |||
8ae10dc80b | |||
9b11c2c629 | |||
e2a231fb4a | |||
8a9502d1f2 | |||
534438df63 | |||
45a4feec96 | |||
aa7a32395e | |||
ab9f57f044 | |||
4040d6aa08 | |||
1c96f5c35e | |||
4d3e42812d | |||
f7b3711d4f | |||
2408e076ff | |||
6f71ffb477 | |||
214433f36a | |||
309b22732e | |||
6fe7687b2a | |||
a8cbf757ff |
2
Doxyfile
@@ -1051,7 +1051,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH = docs/images/
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
|
@@ -16,14 +16,14 @@ MAKEFLAGS += --no-builtin-rules
|
||||
## LD := Linker.
|
||||
## ANDROID_SDK := Path to the Android SDK.
|
||||
|
||||
VERSION_CODE := 37
|
||||
VERSION_CODE_IOS := 13
|
||||
VERSION_NUMBER := 0.0.31
|
||||
VERSION_CODE := 38
|
||||
VERSION_CODE_IOS := 14
|
||||
VERSION_NUMBER := 0.0.32-wip
|
||||
VERSION_NAME := This program kills fascists.
|
||||
|
||||
IPHONEOS_VERSION_MIN=14.0
|
||||
|
||||
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3490200.zip
|
||||
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3500100.zip
|
||||
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
||||
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🚪",
|
||||
"previous": "&HXCdDG8gGYXElTyEFbg85jqa6lDXNL2ENPIA9UoJNbI=.sha256"
|
||||
"previous": "&DJwkqNfYWtW9yBtJQMseEXm7l04Enpi+yAxZulLq9Vk=.sha256"
|
||||
}
|
||||
|
@@ -2,8 +2,8 @@ async function main() {
|
||||
print(core.url);
|
||||
let host = core.url.match(/.*?\/\/([^:/]*)/)[1];
|
||||
let port = await ssb.port();
|
||||
let id = (await ssb.getServerIdentity()).substring(1);
|
||||
let room = `net:${host}:${port}~shs:${id}:SSB+Room+SK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
||||
let id = (await ssb.getServerIdentity()).substring(1).split('.')[0];
|
||||
let room = `net:${host}:${port}~shs:${id}:SSB+Room+PSK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
||||
await app.setDocument(`
|
||||
<body style="color: #fff">
|
||||
<h1>Server</h1>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🦀",
|
||||
"previous": "&0Lxm4IgS3mpvSccP3bg7wNPACtLKMTbie51ea/vJbeg=.sha256"
|
||||
"previous": "&Bpf8wmdsVYGPSBduytapHM7XHz2iCGfaysVHfEKYK/s=.sha256"
|
||||
}
|
||||
|
@@ -128,7 +128,13 @@ class TfElement extends LitElement {
|
||||
}
|
||||
|
||||
next_channel(delta) {
|
||||
let channel_names = ['', '@', '🔐', ...this.channels.map((x) => '#' + x)];
|
||||
let channel_names = [
|
||||
'',
|
||||
'@',
|
||||
'🔐',
|
||||
'👍',
|
||||
...this.channels.map((x) => '#' + x),
|
||||
];
|
||||
let index = channel_names.indexOf(this.hash.substring(1));
|
||||
index = index != -1 ? index + delta : 0;
|
||||
tfrpc.rpc.setHash(
|
||||
@@ -302,11 +308,7 @@ class TfElement extends LitElement {
|
||||
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
|
||||
}
|
||||
for (let i = cache.range[0]; i >= 0; i -= k_chunk_size) {
|
||||
ranges.push([
|
||||
Math.max(i - k_chunk_size, 0),
|
||||
Math.min(cache.range[0], i + k_chunk_size),
|
||||
false,
|
||||
]);
|
||||
ranges.push([Math.max(i - k_chunk_size, 0), i, false]);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < latest; i += k_chunk_size) {
|
||||
@@ -322,7 +324,7 @@ class TfElement extends LitElement {
|
||||
messages.rowid > ?1 AND
|
||||
messages.rowid <= ?2 AND
|
||||
json(messages.content) LIKE '"%'
|
||||
ORDER BY sequence DESC
|
||||
ORDER BY messages.rowid DESC
|
||||
`,
|
||||
[range[0], range[1]]
|
||||
);
|
||||
@@ -353,27 +355,43 @@ class TfElement extends LitElement {
|
||||
let latest_private = this.get_latest_private(following);
|
||||
let channels = await tfrpc.rpc.query(
|
||||
`
|
||||
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(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
UNION
|
||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
UNION
|
||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE messages.author != ?4
|
||||
`,
|
||||
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(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
UNION
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN messages_refs ON messages.id = messages_refs.message
|
||||
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
UNION
|
||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
UNION
|
||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE messages.author != ?4
|
||||
UNION
|
||||
SELECT '👍' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'vote' AND
|
||||
messages.author != ?4
|
||||
`,
|
||||
[
|
||||
JSON.stringify(this.channels),
|
||||
JSON.stringify(following),
|
||||
@@ -381,9 +399,15 @@ class TfElement extends LitElement {
|
||||
this.whoami,
|
||||
]
|
||||
);
|
||||
this.channels_latest = Object.fromEntries(
|
||||
channels.map((x) => [x.channel, x.rowid])
|
||||
);
|
||||
let latest = {};
|
||||
for (let row of channels) {
|
||||
if (!latest[row.channel]) {
|
||||
latest[row.channel] = row.rowid;
|
||||
} else {
|
||||
latest[row.channel] = Math.max(row.rowid, latest[row.channel]);
|
||||
}
|
||||
}
|
||||
this.channels_latest = latest;
|
||||
console.log('channels took', (new Date() - start_time) / 1000.0);
|
||||
let self = this;
|
||||
start_time = new Date();
|
||||
|
@@ -446,12 +446,15 @@ class TfComposeElement extends LitElement {
|
||||
self.apps = await tfrpc.rpc.apps();
|
||||
}
|
||||
if (!this.apps) {
|
||||
return html`<button class="w3-button w3-theme-d1" @click=${attach_app}>
|
||||
return html`<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${attach_app}
|
||||
>
|
||||
Attach App
|
||||
</button>`;
|
||||
} else {
|
||||
return html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => (this.apps = null)}
|
||||
>
|
||||
Discard App
|
||||
@@ -472,18 +475,9 @@ class TfComposeElement extends LitElement {
|
||||
if (draft.content_warning !== undefined) {
|
||||
return html`
|
||||
<div class="w3-container w3-padding">
|
||||
<p>
|
||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning(undefined)} checked="checked"></input>
|
||||
<label for="cw">CW</label>
|
||||
</p>
|
||||
<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${self.input} value=${draft.content_warning}></input>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
return html`
|
||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning('')}></input>
|
||||
<label for="cw">CW</label>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,6 +540,31 @@ class TfComposeElement extends LitElement {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
toggle_menu(event) {
|
||||
event.srcElement.parentNode
|
||||
.querySelector('.w3-dropdown-content')
|
||||
.classList.toggle('w3-show');
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._click_callback = this.document_click.bind(this);
|
||||
document.body.addEventListener('mouseup', this._click_callback);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.body.removeEventListener('mouseup', this._click_callback);
|
||||
}
|
||||
|
||||
document_click(event) {
|
||||
let content = this.renderRoot.querySelector('.w3-dropdown-content');
|
||||
let target = event.target;
|
||||
if (content && !content.contains(target)) {
|
||||
content.classList.remove('w3-show');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
let draft = self.get_draft();
|
||||
@@ -559,7 +578,7 @@ class TfComposeElement extends LitElement {
|
||||
draft.encrypt_to !== undefined
|
||||
? undefined
|
||||
: html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => this.set_encrypt([])}
|
||||
>
|
||||
🔐
|
||||
@@ -614,13 +633,43 @@ class TfComposeElement extends LitElement {
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.attach}>
|
||||
Attach
|
||||
</button>
|
||||
${this.render_attach_app_button()} ${encrypt}
|
||||
<button class="w3-button w3-theme-d1" @click=${this.discard}>
|
||||
Discard
|
||||
</button>
|
||||
<div class="w3-dropdown-click">
|
||||
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||
⚙️
|
||||
</button>
|
||||
<div class="w3-dropdown-content w3-bar-block">
|
||||
${this.get_draft().content_warning === undefined
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => self.set_content_warning('')}
|
||||
>
|
||||
Add Content Warning
|
||||
</button>
|
||||
`
|
||||
: html`
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => self.set_content_warning(undefined)}
|
||||
>
|
||||
Remove Content Warning
|
||||
</button>
|
||||
`}
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${this.attach}
|
||||
>
|
||||
Attach
|
||||
</button>
|
||||
${this.render_attach_app_button()} ${encrypt}
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${this.discard}
|
||||
>
|
||||
Discard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
|
@@ -1,4 +1,11 @@
|
||||
import {LitElement, html, repeat, render, unsafeHTML} from './lit-all.min.js';
|
||||
import {
|
||||
LitElement,
|
||||
css,
|
||||
html,
|
||||
repeat,
|
||||
render,
|
||||
unsafeHTML,
|
||||
} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import * as tfutils from './tf-utils.js';
|
||||
import * as emojis from './emojis.js';
|
||||
@@ -86,12 +93,18 @@ class TfMessageElement extends LitElement {
|
||||
|
||||
render_votes() {
|
||||
function normalize_expression(expression) {
|
||||
if (expression === 'Like' || expression === 'like' || !expression) {
|
||||
return '👍';
|
||||
} else if (expression === 'Unlike' || expression === 'unlike') {
|
||||
if (
|
||||
expression === 'Unlike' ||
|
||||
expression === 'unlike' ||
|
||||
expression == 'undig'
|
||||
) {
|
||||
return '👎';
|
||||
} else if (expression === 'heart') {
|
||||
return '❤️';
|
||||
} else if (
|
||||
(expression ?? '').split('').every((x) => x.charCodeAt(0) < 256)
|
||||
) {
|
||||
return '👍';
|
||||
} else {
|
||||
return expression;
|
||||
}
|
||||
@@ -297,31 +310,35 @@ class TfMessageElement extends LitElement {
|
||||
return total;
|
||||
}
|
||||
|
||||
expanded_key() {
|
||||
return this.message?.id || this.messages?.map((x) => x.id).join(':');
|
||||
}
|
||||
|
||||
set_expanded(expanded, tag) {
|
||||
let key = this.expanded_key();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('tf-expand', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {id: (this.message.id || '') + (tag || ''), expanded: expanded},
|
||||
detail: {id: key + (tag || ''), expanded: expanded},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
toggle_expanded(tag) {
|
||||
this.set_expanded(
|
||||
!this.expanded[(this.message.id || '') + (tag || '')],
|
||||
tag
|
||||
);
|
||||
let key = this.expanded_key();
|
||||
this.set_expanded(!this.expanded[key + (tag || '')], tag);
|
||||
}
|
||||
|
||||
is_expanded(tag) {
|
||||
return this.expanded[(this.message.id || '') + (tag || '')];
|
||||
let key = this.expanded_key();
|
||||
return this.expanded[key + (tag || '')];
|
||||
}
|
||||
|
||||
render_children() {
|
||||
let self = this;
|
||||
if (this.message.child_messages?.length) {
|
||||
if (!this.expanded[this.message.id]) {
|
||||
if (!this.expanded[this.expanded_key()]) {
|
||||
return html`
|
||||
<button
|
||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||
@@ -397,7 +414,7 @@ class TfMessageElement extends LitElement {
|
||||
class_background() {
|
||||
return this.message?.decrypted
|
||||
? 'w3-pale-red'
|
||||
: this.message?.rowid >= this.channel_unread
|
||||
: this.allow_unread() && this.message?.rowid >= this.channel_unread
|
||||
? 'w3-theme-d2'
|
||||
: 'w3-theme-d4';
|
||||
}
|
||||
@@ -489,7 +506,10 @@ class TfMessageElement extends LitElement {
|
||||
return html`
|
||||
<header class="w3-bar">
|
||||
<span class="w3-bar-item">
|
||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||
${this.render_unread_icon()}<tf-user
|
||||
id=${this.message.author}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
</span>
|
||||
${is_encrypted} ${this.render_menu()}
|
||||
<div class="w3-bar-item w3-right" style="text-wrap: nowrap">
|
||||
@@ -574,6 +594,54 @@ class TfMessageElement extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
content_group_by_author() {
|
||||
let sorted = this.message.messages
|
||||
.map((x) => [
|
||||
x.author,
|
||||
x.content.blocking !== undefined
|
||||
? x.content.blocking
|
||||
? 'is blocking'
|
||||
: 'is no longer blocking'
|
||||
: x.content.following !== undefined
|
||||
? x.content.following
|
||||
? 'is following'
|
||||
: 'is no longer following'
|
||||
: '',
|
||||
x.content.contact,
|
||||
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], users: group});
|
||||
}
|
||||
last = row;
|
||||
group = [row[2]];
|
||||
}
|
||||
}
|
||||
if (group) {
|
||||
result.push({author: last[0], action: last[1], users: group});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
allow_unread() {
|
||||
return;
|
||||
!this.channel.startsWith('@') && !this.channel.startsWith('%');
|
||||
}
|
||||
|
||||
render_unread_icon() {
|
||||
return this.allow_unread() && this.message?.rowid >= this.channel_unread
|
||||
? html`✉️`
|
||||
: undefined;
|
||||
}
|
||||
|
||||
render() {
|
||||
let content = this.message?.content;
|
||||
if (this.message?.decrypted?.type == 'post') {
|
||||
@@ -582,29 +650,90 @@ class TfMessageElement extends LitElement {
|
||||
let class_background = this.class_background();
|
||||
let self = this;
|
||||
if (this.message?.type === 'contact_group') {
|
||||
return this.render_frame(
|
||||
html` ${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}
|
||||
></tf-message>`
|
||||
)}`
|
||||
);
|
||||
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}
|
||||
></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.content_group_by_author().map(
|
||||
(x) => html`
|
||||
<div>
|
||||
<tf-user id=${x.author} .users=${this.users}></tf-user>
|
||||
${x.action}
|
||||
${x.users.map(
|
||||
(y) => html`
|
||||
<tf-user id=${y} .users=${this.users}></tf-user>
|
||||
`
|
||||
)}
|
||||
</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) {
|
||||
return this.render_frame(
|
||||
html`<div class="w3-padding">
|
||||
<p>
|
||||
<a target="_top" href=${'#' + encodeURIComponent(this.message.id)}
|
||||
>${this.message.id}</a
|
||||
html`<div>
|
||||
<div class="w3-bar">
|
||||
<a
|
||||
class="w3-bar-item w3-panel w3-round-xlarge w3-theme-d1 w3-margin w3-button"
|
||||
target="_top"
|
||||
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||
>
|
||||
(placeholder)
|
||||
</p>
|
||||
This message is not currently available.
|
||||
</a>
|
||||
<div class="w3-bar-item w3-right">
|
||||
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||
%
|
||||
</button>
|
||||
<div
|
||||
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||
style="right: 48px"
|
||||
>
|
||||
<a
|
||||
target="_top"
|
||||
class="w3-button w3-bar-item"
|
||||
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||
>View Message</a
|
||||
>
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-border-bottom"
|
||||
@click=${this.copy_id}
|
||||
>
|
||||
Copy ID
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>${this.render_votes()}</div>
|
||||
${(this.message.child_messages || []).map(
|
||||
(x) => html`
|
||||
@@ -631,7 +760,7 @@ class TfMessageElement extends LitElement {
|
||||
}
|
||||
if (content.image !== undefined) {
|
||||
image = html`
|
||||
<div><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
||||
<div @click=${this.body_click}><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
||||
`;
|
||||
}
|
||||
if (content.description !== undefined) {
|
||||
@@ -654,25 +783,60 @@ class TfMessageElement extends LitElement {
|
||||
</div>
|
||||
`);
|
||||
} else if (content.type == 'contact') {
|
||||
return html`
|
||||
<div class="w3-padding">
|
||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||
is
|
||||
${content.blocking === true
|
||||
? 'blocking'
|
||||
: content.blocking === false
|
||||
? 'no longer blocking'
|
||||
: content.following === true
|
||||
? 'following'
|
||||
: content.following === false
|
||||
? 'no longer following'
|
||||
: '?'}
|
||||
<tf-user
|
||||
id=${this.message.content.contact}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
return this.render_frame(html`
|
||||
<div class="w3-bar">
|
||||
<div class="w3-bar-item">
|
||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||
is
|
||||
${content.blocking === true
|
||||
? 'blocking'
|
||||
: content.blocking === false
|
||||
? 'no longer blocking'
|
||||
: content.following === true
|
||||
? 'following'
|
||||
: content.following === false
|
||||
? 'no longer following'
|
||||
: '?'}
|
||||
<tf-user
|
||||
id=${this.message.content.contact}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
</div>
|
||||
<div class="w3-bar-item w3-right">
|
||||
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||
%
|
||||
</button>
|
||||
<div
|
||||
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||
style="right: 48px"
|
||||
>
|
||||
<a
|
||||
target="_top"
|
||||
class="w3-button w3-bar-item"
|
||||
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||
>View Message</a
|
||||
>
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-border-bottom"
|
||||
@click=${this.copy_id}
|
||||
>
|
||||
Copy ID
|
||||
</button>
|
||||
${this.drafts[this.message?.id] === undefined
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-bar-item"
|
||||
@click=${this.show_reply}
|
||||
>
|
||||
↩️ Reply
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
</div>
|
||||
</div>
|
||||
${this.render_votes()} ${this.render_actions()}
|
||||
</div>
|
||||
`;
|
||||
`);
|
||||
} else if (content.type == 'post') {
|
||||
let self = this;
|
||||
let body;
|
||||
|
@@ -166,7 +166,10 @@ class TfNewsElement extends LitElement {
|
||||
if (message?.content?.type === 'contact') {
|
||||
group.push(message);
|
||||
} else {
|
||||
if (group.length > 0) {
|
||||
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: 'contact_group',
|
||||
@@ -177,7 +180,10 @@ class TfNewsElement extends LitElement {
|
||||
result.push(message);
|
||||
}
|
||||
}
|
||||
if (group.length > 0) {
|
||||
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: 'contact_group',
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import {LitElement, html, 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 tfutils from './tf-utils.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
@@ -166,6 +166,74 @@ class TfProfileElement extends LitElement {
|
||||
navigator.clipboard.writeText(this.id);
|
||||
}
|
||||
|
||||
show_image(link) {
|
||||
let div = document.createElement('div');
|
||||
div.style.left = 0;
|
||||
div.style.top = 0;
|
||||
div.style.width = '100%';
|
||||
div.style.height = '100%';
|
||||
div.style.position = 'fixed';
|
||||
div.style.background = '#000';
|
||||
div.style.zIndex = 100;
|
||||
div.style.display = 'grid';
|
||||
let img = document.createElement('img');
|
||||
img.src = link;
|
||||
img.style.maxWidth = '100%';
|
||||
img.style.maxHeight = '100%';
|
||||
img.style.display = 'block';
|
||||
img.style.margin = 'auto';
|
||||
img.style.objectFit = 'contain';
|
||||
img.style.width = '100%';
|
||||
div.appendChild(img);
|
||||
function image_close(event) {
|
||||
document.body.removeChild(div);
|
||||
window.removeEventListener('keydown', image_close);
|
||||
}
|
||||
div.onclick = image_close;
|
||||
window.addEventListener('keydown', image_close);
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
|
||||
body_click(event) {
|
||||
if (event.srcElement.tagName == 'IMG') {
|
||||
this.show_image(event.srcElement.src);
|
||||
}
|
||||
}
|
||||
|
||||
toggle_account_list(event) {
|
||||
let content = event.srcElement.nextElementSibling;
|
||||
if (content.classList.toggle('w3-hide')) {
|
||||
event.srcElement.innerText = 'Show Followed Accounts';
|
||||
} else {
|
||||
event.srcElement.innerText = 'Hide Followed Accounts';
|
||||
}
|
||||
}
|
||||
|
||||
async load_follows() {
|
||||
let accounts = await tfrpc.rpc.following([this.id], 1);
|
||||
return html`
|
||||
<div class="w3-container">
|
||||
<button
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${this.toggle_account_list}
|
||||
>
|
||||
Show Followed Accounts
|
||||
</button>
|
||||
<div class="w3-hide w3-card">
|
||||
<ul class="w3-ul w3-theme-d4 w3-border-theme">
|
||||
${Object.keys(accounts).map(
|
||||
(x) => html`
|
||||
<li class="w3-border-theme">
|
||||
<tf-user id=${x} .users=${this.users}></tf-user>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.load();
|
||||
let self = this;
|
||||
@@ -254,7 +322,7 @@ class TfProfileElement extends LitElement {
|
||||
<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>
|
||||
</header>
|
||||
<div class="w3-container">
|
||||
<div class="w3-container" @click=${this.body_click}>
|
||||
<div class="w3-margin-bottom" style="display: flex; flex-direction: row">
|
||||
<input type="text" class="w3-input w3-border w3-theme-d1" style="display: flex 1 1" readonly value=${this.id}></input>
|
||||
<button class="w3-button w3-theme-d1 w3-ripple" style="flex: 0 0 auto" @click=${this.copy_id}>Copy</button>
|
||||
@@ -280,6 +348,7 @@ class TfProfileElement extends LitElement {
|
||||
Blocked by ${profile.blocked} identities.
|
||||
</div>
|
||||
</div>
|
||||
${until(this.load_follows(), html`<p>Loading accounts followed...</p>`)}
|
||||
<footer class="w3-container">
|
||||
<p>
|
||||
${edit}
|
||||
|
@@ -308,6 +308,12 @@ class TfTabConnectionsElement extends LitElement {
|
||||
<div class="w3-bar-item">
|
||||
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
||||
<div><small>${x.address}:${x.port}</small></div>
|
||||
<div>
|
||||
<small
|
||||
>Last connection:
|
||||
${new Date(x.last_success * 1000)}</small
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${this.render_message(x)}
|
||||
|
@@ -177,10 +177,10 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
WHERE messages.content ->> 'channel' = ?4
|
||||
UNION
|
||||
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(?5)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
FROM messages_refs
|
||||
JOIN messages ON messages.id = messages_refs.message
|
||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||
JOIN json_tree(messages.content, '$.mentions') AS mention ON mention.value = '#' || ?4
|
||||
WHERE messages_refs.ref = '#' || ?4
|
||||
)
|
||||
SELECT TRUE AS is_primary, all_news.* FROM all_news
|
||||
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
|
||||
@@ -191,7 +191,6 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
start_time,
|
||||
end_time,
|
||||
this.hash.substring(2),
|
||||
'"#' + this.hash.substring(2).replace('"', '""') + '"',
|
||||
]
|
||||
);
|
||||
let t1 = new Date();
|
||||
@@ -209,11 +208,29 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
WHERE
|
||||
(?2 IS NULL OR (messages.timestamp >= ?2)) AND messages.timestamp < ?3 AND
|
||||
json(messages.content) LIKE '"%'
|
||||
ORDER BY messages.sequence DESC LIMIT 20
|
||||
ORDER BY messages.rowid DESC LIMIT 20
|
||||
`,
|
||||
[JSON.stringify(this.private_messages), start_time, end_time]
|
||||
);
|
||||
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
||||
} else if (this.hash == '#👍') {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH votes 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
|
||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'vote' AND
|
||||
(?2 IS NULL OR messages.timestamp >= ?2) AND messages.timestamp < ?3
|
||||
ORDER BY timestamp DESC limit 20)
|
||||
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 votes
|
||||
JOIN messages ON messages.id = votes.content ->> '$.vote.link'
|
||||
UNION
|
||||
SELECT TRUE AS is_primary, * FROM votes
|
||||
`,
|
||||
[JSON.stringify(this.following), start_time, end_time]
|
||||
);
|
||||
} else {
|
||||
let t0 = new Date();
|
||||
let initial_messages = await tfrpc.rpc.query(
|
||||
@@ -441,9 +458,14 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
`;
|
||||
}
|
||||
return cache(html`
|
||||
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
|
||||
Mark All Read
|
||||
</button>
|
||||
${!this.hash.startsWith('#%')
|
||||
? html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.mark_all_read}
|
||||
>
|
||||
Mark All Read
|
||||
</button>`
|
||||
: undefined}
|
||||
<tf-news
|
||||
id="news"
|
||||
whoami=${this.whoami}
|
||||
|
@@ -202,6 +202,12 @@ class TfTabNewsElement extends LitElement {
|
||||
style=${this.hash == '#@' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('@')}@mentions</a
|
||||
>
|
||||
<a
|
||||
href="#👍"
|
||||
class="w3-bar-item w3-button"
|
||||
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('👍')}👍votes</a
|
||||
>
|
||||
<a
|
||||
href="#🔐"
|
||||
class="w3-bar-item w3-button"
|
||||
@@ -235,12 +241,18 @@ class TfTabNewsElement extends LitElement {
|
||||
<h4 style="margin: 0">Connections</h4>
|
||||
</a>
|
||||
${this.connections
|
||||
.filter((x) => x.id && !x.destroy_reason)
|
||||
.filter((x) => x.id)
|
||||
.map(
|
||||
(x) => html`
|
||||
<tf-user
|
||||
class="w3-bar-item"
|
||||
style="max-width: 100%"
|
||||
style=${x.destroy_reason
|
||||
? 'border-left: 4px solid red; border-right: 4px solid red'
|
||||
: x.connected
|
||||
? x.flags?.one_shot
|
||||
? 'border-left: 4px solid blue; border-right: 4px solid blue'
|
||||
: 'border-left: 4px solid green; border-right: 4px solid green'
|
||||
: ''}
|
||||
id=${x.id}
|
||||
fallback_name=${x.host}
|
||||
.users=${this.users}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "👋",
|
||||
"previous": "&1o8MrBHfH42NnO+ruajwCmW/DUCb+IT1qtnAZI/agyo=.sha256"
|
||||
"previous": "&lqnpIZjz89faC8vMHQiW3mDI5UsaByC3/x8diBr0EtI=.sha256"
|
||||
}
|
||||
|
@@ -28,23 +28,12 @@
|
||||
<b>😎 Tilde Friends</b>
|
||||
</h1>
|
||||
<h1 class="w3-xxlarge w3-text-green">
|
||||
<b
|
||||
>the Secure Scuttlebutt decentralized social network client that's
|
||||
<i>fancy🎩</i></b
|
||||
>
|
||||
<b>a Secure Scuttlebutt decentralized social network client</b>
|
||||
</h1>
|
||||
<p>
|
||||
In addition to participating in Secure Scuttlebutt, Tilde Friends is
|
||||
a platform for building, running, and sharing applications.
|
||||
</p>
|
||||
<p>
|
||||
Available for lots of devices:
|
||||
<i class="fa-brands fa-linux w3-xlarge"></i>
|
||||
<i class="fa-brands fa-android w3-xlarge"></i>
|
||||
<i class="fa-brands fa-apple w3-xlarge"></i>
|
||||
<i class="fa fa-mobile-screen w3-xlarge"></i>
|
||||
<i class="fa-brands fa-windows w3-xlarge"></i>
|
||||
</p>
|
||||
<a
|
||||
class="w3-button w3-blue w3-padding-large"
|
||||
href="https://www.tildefriends.net/~core/ssb/"
|
||||
@@ -70,35 +59,6 @@
|
||||
href="https://www.tildefriends.net/~cory/tildeblog/"
|
||||
><i class="fa fa-solid fa-square-rss"></i> Blog</a
|
||||
>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
||||
><img src="f-droid.svg" style="height: 2em; margin: 0" /> Get it
|
||||
on F-Droid</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage"
|
||||
>
|
||||
<img src="appimage.svg" style="height: 2em; margin: 0" />
|
||||
Get Linux 64-bit AppImage
|
||||
</a>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends"
|
||||
>
|
||||
<img src="googleplay.svg" style="height: 2em; margin: 0" />
|
||||
Get it on Google Play (Open Testing)
|
||||
</a>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://testflight.apple.com/join/tXxgtSpE"
|
||||
>
|
||||
<img src="ios.svg" style="height: 2em; margin: 0" />
|
||||
Get it on iOS (TestFlight)
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="w3-col l4 m6">
|
||||
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
|
||||
@@ -123,8 +83,100 @@
|
||||
<a href="https://www.tildefriends.net/"
|
||||
>https://www.tildefriends.net/</a
|
||||
>.
|
||||
<div class="w3-cell-row">
|
||||
<div class="w3-container w3-cell">
|
||||
<h3>Mobile</h3>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
||||
><img src="f-droid.svg" style="height: 2em; margin: 0" />
|
||||
Get it on F-Droid</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends"
|
||||
>
|
||||
<img
|
||||
src="googleplay.svg"
|
||||
style="height: 2em; margin: 0"
|
||||
/>
|
||||
Get it on Google Play (Open Testing)
|
||||
</a>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://testflight.apple.com/join/tXxgtSpE"
|
||||
>
|
||||
<img src="ios.svg" style="height: 2em; margin: 0" />
|
||||
Get it on iOS (TestFlight)
|
||||
</a>
|
||||
</p>
|
||||
<p>Just launch the app.</p>
|
||||
</div>
|
||||
<div class="w3-container w3-cell">
|
||||
<h3>Web</h3>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-blue w3-padding-large"
|
||||
href="https://www.tildefriends.net/~core/ssb/"
|
||||
>🦀 Try It</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/login?return=/~core/intro"
|
||||
>Register an account with tildefriends.net</a
|
||||
>
|
||||
to take it for a spin right away.
|
||||
</p>
|
||||
</div>
|
||||
<div class="w3-container w3-cell">
|
||||
<h3>Desktop</h3>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-black w3-padding-large"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
><i class="fa fa-download"></i> Download</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray"
|
||||
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage"
|
||||
>
|
||||
<img src="appimage.svg" style="height: 2em; margin: 0" />
|
||||
Get Linux 64-bit AppImage
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
Tilde Friends is distributed as a single executable file (or
|
||||
source that you can
|
||||
<a href="http://dev.tildefriends.net">build yourself</a>)
|
||||
and stores all of its data in a single
|
||||
file(<code>db.sqlite</code>). You can generally download the
|
||||
latest executable from
|
||||
<a
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
>releases</a
|
||||
>
|
||||
for your platform, mark it as executable (<code
|
||||
>chmod +x tildefriends*</code
|
||||
>
|
||||
on macOS and Linux), and run. Run with <code>-h</code> to
|
||||
learn more.
|
||||
</p>
|
||||
<p>
|
||||
Tilde Friends will run in the console and provide a web
|
||||
interface at
|
||||
<a href="http://localhost:12345/">http://localhost:12345/</a
|
||||
>. You will have to register a username and password to sign
|
||||
into your instance.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
After a <a href="/~core/intro">brief introduction</a>, Tilde
|
||||
Friends will take you to the Secure Scuttlebutt social network
|
||||
app.
|
||||
</p>
|
||||
</li>
|
||||
<li>Create an account to identify yourself with that instance.</li>
|
||||
<li>
|
||||
Describe yourself in your profile in the <b>ssb</b> app. Give
|
||||
yourself a name and an avatar if you like.
|
||||
|
@@ -8,6 +8,7 @@ let gFiles = {};
|
||||
let gApp = {files: {}, emoji: '📦'};
|
||||
let gEditor;
|
||||
let gOriginalInput;
|
||||
let gUnloading;
|
||||
|
||||
let kErrorColor = '#dc322f';
|
||||
let kDisconnectColor = '#f00';
|
||||
@@ -1560,27 +1561,31 @@ function connectSocket(path) {
|
||||
_receive_websocket_message(JSON.parse(event.data));
|
||||
};
|
||||
gSocket.onclose = function (event) {
|
||||
const k_codes = {
|
||||
1000: 'Normal closure',
|
||||
1001: 'Going away',
|
||||
1002: 'Protocol error',
|
||||
1003: 'Unsupported data',
|
||||
1005: 'No status received',
|
||||
1006: 'Abnormal closure',
|
||||
1007: 'Invalid frame payload data',
|
||||
1008: 'Policy violation',
|
||||
1009: 'Message too big',
|
||||
1010: 'Missing extension',
|
||||
1011: 'Internal error',
|
||||
1012: 'Service restart',
|
||||
1013: 'Try again later',
|
||||
1014: 'Bad gateway',
|
||||
1015: 'TLS handshake',
|
||||
};
|
||||
setStatusMessage(
|
||||
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
||||
kDisconnectColor
|
||||
);
|
||||
if (gUnloading) {
|
||||
setStatusMessage('⚪ Closing...', kStatusColor);
|
||||
} else {
|
||||
const k_codes = {
|
||||
1000: 'Normal closure',
|
||||
1001: 'Going away',
|
||||
1002: 'Protocol error',
|
||||
1003: 'Unsupported data',
|
||||
1005: 'No status received',
|
||||
1006: 'Abnormal closure',
|
||||
1007: 'Invalid frame payload data',
|
||||
1008: 'Policy violation',
|
||||
1009: 'Message too big',
|
||||
1010: 'Missing extension',
|
||||
1011: 'Internal error',
|
||||
1012: 'Service restart',
|
||||
1013: 'Try again later',
|
||||
1014: 'Bad gateway',
|
||||
1015: 'TLS handshake',
|
||||
};
|
||||
setStatusMessage(
|
||||
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
||||
kDisconnectColor
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1854,6 +1859,9 @@ window.addEventListener('load', function () {
|
||||
window.addEventListener('blur', blur);
|
||||
window.addEventListener('message', message, false);
|
||||
window.addEventListener('online', connectSocket);
|
||||
window.addEventListener('beforeunload', function () {
|
||||
gUnloading = true;
|
||||
});
|
||||
document.getElementById('name').value = window.location.pathname;
|
||||
document
|
||||
.getElementById('closeEditor')
|
||||
|
@@ -25,14 +25,14 @@
|
||||
}:
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
pname = "tildefriends";
|
||||
version = "0.0.30";
|
||||
version = "0.0.31";
|
||||
|
||||
src = pkgs.fetchFromGitea {
|
||||
domain = "dev.tildefriends.net";
|
||||
owner = "cory";
|
||||
repo = "tildefriends";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-t5yvouzSL2j/ge1VHLqzIZ+Avqj4iEDt7L+yrHoTZAQ=";
|
||||
hash = "sha256-c2ZKVNikI5jN5GQuvp7S53qqnRZniSrJMF1FUZdVNPI=";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
|
||||
|
2
deps/codemirror/cm6.js
vendored
196
deps/codemirror_src/package-lock.json
generated
vendored
@@ -92,9 +92,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz",
|
||||
"integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==",
|
||||
"version": "6.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.1.tgz",
|
||||
"integrity": "sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
@@ -144,11 +144,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.36.8",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz",
|
||||
"integrity": "sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==",
|
||||
"version": "6.37.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.1.tgz",
|
||||
"integrity": "sha512-Qy4CAUwngy/VQkEz0XzMKVRcckQuqLYWKqVpDDDghBe5FSXSqfVrJn49nw3ePZHxRUz4nRmb05Lgi+9csWo4eg==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.5.0",
|
||||
"crelt": "^1.0.6",
|
||||
"style-mod": "^4.1.0",
|
||||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
@@ -344,9 +345,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz",
|
||||
"integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz",
|
||||
"integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -356,9 +357,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz",
|
||||
"integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz",
|
||||
"integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -368,9 +369,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz",
|
||||
"integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz",
|
||||
"integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -380,9 +381,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz",
|
||||
"integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz",
|
||||
"integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -392,9 +393,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz",
|
||||
"integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz",
|
||||
"integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -404,9 +405,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz",
|
||||
"integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz",
|
||||
"integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -416,9 +417,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz",
|
||||
"integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz",
|
||||
"integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -428,9 +429,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz",
|
||||
"integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz",
|
||||
"integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -440,9 +441,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz",
|
||||
"integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -452,9 +453,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz",
|
||||
"integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz",
|
||||
"integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -464,9 +465,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz",
|
||||
"integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -476,9 +477,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz",
|
||||
"integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -488,9 +489,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz",
|
||||
"integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -500,9 +501,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz",
|
||||
"integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz",
|
||||
"integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -512,9 +513,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz",
|
||||
"integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -524,9 +525,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz",
|
||||
"integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz",
|
||||
"integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -536,9 +537,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz",
|
||||
"integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz",
|
||||
"integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -548,9 +549,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz",
|
||||
"integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz",
|
||||
"integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -560,9 +561,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz",
|
||||
"integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz",
|
||||
"integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -572,9 +573,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz",
|
||||
"integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz",
|
||||
"integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -584,9 +585,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.2",
|
||||
@@ -745,9 +746,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz",
|
||||
"integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==",
|
||||
"version": "4.42.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz",
|
||||
"integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.7"
|
||||
},
|
||||
@@ -759,29 +760,34 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.41.1",
|
||||
"@rollup/rollup-android-arm64": "4.41.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.41.1",
|
||||
"@rollup/rollup-darwin-x64": "4.41.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.41.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.41.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.41.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.41.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.41.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.41.1",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.41.1",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.41.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.41.1",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.41.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.41.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.41.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.41.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.41.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.41.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.41.1",
|
||||
"@rollup/rollup-android-arm-eabi": "4.42.0",
|
||||
"@rollup/rollup-android-arm64": "4.42.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.42.0",
|
||||
"@rollup/rollup-darwin-x64": "4.42.0",
|
||||
"@rollup/rollup-freebsd-arm64": "4.42.0",
|
||||
"@rollup/rollup-freebsd-x64": "4.42.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.42.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.42.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.42.0",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.42.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.42.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.42.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.42.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.42.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.42.0",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup/node_modules/@types/estree": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
@@ -853,9 +859,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.40.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.40.0.tgz",
|
||||
"integrity": "sha512-cfeKl/jjwSR5ar7d0FGmave9hFGJT8obyo0z+CrQOylLDbk7X81nPU6vq9VORa5jU30SkDnT2FXjLbR8HLP+xA==",
|
||||
"version": "5.41.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.41.0.tgz",
|
||||
"integrity": "sha512-H406eLPXpZbAX14+B8psIuvIr8+3c+2hkuYzpMkoE0ij+NdsVATbA78vb8neA/eqrj7rywa2pIkdmWRsXW6wmw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
|
953
deps/sqlite/shell.c
vendored
4284
deps/sqlite/sqlite3.c
vendored
146
deps/sqlite/sqlite3.h
vendored
@@ -133,7 +133,7 @@ extern "C" {
|
||||
**
|
||||
** Since [version 3.6.18] ([dateof:3.6.18]),
|
||||
** SQLite source code has been stored in the
|
||||
** <a href="http://www.fossil-scm.org/">Fossil configuration management
|
||||
** <a href="http://fossil-scm.org/">Fossil configuration management
|
||||
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
||||
** a string which identifies a particular check-in of SQLite
|
||||
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
||||
@@ -146,9 +146,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.49.2"
|
||||
#define SQLITE_VERSION_NUMBER 3049002
|
||||
#define SQLITE_SOURCE_ID "2025-05-07 10:39:52 17144570b0d96ae63cd6f3edca39e27ebd74925252bbaf6723bcb2f6b4861fb1"
|
||||
#define SQLITE_VERSION "3.50.1"
|
||||
#define SQLITE_VERSION_NUMBER 3050001
|
||||
#define SQLITE_SOURCE_ID "2025-06-06 14:52:32 b77dc5e0f596d2140d9ac682b2893ff65d3a4140aa86067a3efebe29dc914c95"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
@@ -1163,6 +1163,12 @@ struct sqlite3_io_methods {
|
||||
** the value that M is to be set to. Before returning, the 32-bit signed
|
||||
** integer is overwritten with the previous value of M.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
|
||||
** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
|
||||
** VFS to block when taking a SHARED lock to connect to a wal mode database.
|
||||
** This is used to implement the functionality associated with
|
||||
** SQLITE_SETLK_BLOCK_ON_CONNECT.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
||||
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
||||
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
||||
@@ -1259,6 +1265,7 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_CKSM_FILE 41
|
||||
#define SQLITE_FCNTL_RESET_CACHE 42
|
||||
#define SQLITE_FCNTL_NULL_IO 43
|
||||
#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
|
||||
|
||||
/* deprecated names */
|
||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||
@@ -1989,13 +1996,16 @@ struct sqlite3_mem_methods {
|
||||
**
|
||||
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
||||
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
||||
** the default size of lookaside memory on each [database connection].
|
||||
** the default size of [lookaside memory] on each [database connection].
|
||||
** The first argument is the
|
||||
** size of each lookaside buffer slot and the second is the number of
|
||||
** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
|
||||
** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
|
||||
** option to [sqlite3_db_config()] can be used to change the lookaside
|
||||
** configuration on individual connections.)^ </dd>
|
||||
** size of each lookaside buffer slot ("sz") and the second is the number of
|
||||
** slots allocated to each database connection ("cnt").)^
|
||||
** ^(SQLITE_CONFIG_LOOKASIDE sets the <i>default</i> lookaside size.
|
||||
** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can
|
||||
** be used to change the lookaside configuration on individual connections.)^
|
||||
** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the
|
||||
** default lookaside configuration at compile-time.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
||||
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
||||
@@ -2232,31 +2242,50 @@ struct sqlite3_mem_methods {
|
||||
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
||||
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
||||
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
||||
** configuration of the lookaside memory allocator within a database
|
||||
** configuration of the [lookaside memory allocator] within a database
|
||||
** connection.
|
||||
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
||||
** in the [DBCONFIG arguments|usual format].
|
||||
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
||||
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
||||
** should have a total of five parameters.
|
||||
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
|
||||
** <ol>
|
||||
** <li><p>The first argument ("buf") is a
|
||||
** pointer to a memory buffer to use for lookaside memory.
|
||||
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
|
||||
** may be NULL in which case SQLite will allocate the
|
||||
** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
|
||||
** size of each lookaside buffer slot. ^The third argument is the number of
|
||||
** slots. The size of the buffer in the first argument must be greater than
|
||||
** or equal to the product of the second and third arguments. The buffer
|
||||
** must be aligned to an 8-byte boundary. ^If the second argument to
|
||||
** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
|
||||
** rounded down to the next smaller multiple of 8. ^(The lookaside memory
|
||||
** The first argument may be NULL in which case SQLite will allocate the
|
||||
** lookaside buffer itself using [sqlite3_malloc()].
|
||||
** <li><P>The second argument ("sz") is the
|
||||
** size of each lookaside buffer slot. Lookaside is disabled if "sz"
|
||||
** is less than 8. The "sz" argument should be a multiple of 8 less than
|
||||
** 65536. If "sz" does not meet this constraint, it is reduced in size until
|
||||
** it does.
|
||||
** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
|
||||
** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so
|
||||
** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
|
||||
** parameter is usually chosen so that the product of "sz" and "cnt" is less
|
||||
** than 1,000,000.
|
||||
** </ol>
|
||||
** <p>If the "buf" argument is not NULL, then it must
|
||||
** point to a memory buffer with a size that is greater than
|
||||
** or equal to the product of "sz" and "cnt".
|
||||
** The buffer must be aligned to an 8-byte boundary.
|
||||
** The lookaside memory
|
||||
** configuration for a database connection can only be changed when that
|
||||
** connection is not currently using lookaside memory, or in other words
|
||||
** when the "current value" returned by
|
||||
** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
|
||||
** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero.
|
||||
** Any attempt to change the lookaside memory configuration when lookaside
|
||||
** memory is in use leaves the configuration unchanged and returns
|
||||
** [SQLITE_BUSY].)^</dd>
|
||||
** [SQLITE_BUSY].
|
||||
** If the "buf" argument is NULL and an attempt
|
||||
** to allocate memory based on "sz" and "cnt" fails, then
|
||||
** lookaside is silently disabled.
|
||||
** <p>
|
||||
** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the
|
||||
** default lookaside configuration at initialization. The
|
||||
** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside
|
||||
** configuration at compile-time. Typical values for lookaside are 1200 for
|
||||
** "sz" and 40 to 100 for "cnt".
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
||||
@@ -2993,6 +3022,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
|
||||
*/
|
||||
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Set the Setlk Timeout
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
|
||||
** the VFS supports blocking locks, it sets the timeout in ms used by
|
||||
** eligible locks taken on wal mode databases by the specified database
|
||||
** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
|
||||
** not support blocking locks, this function is a no-op.
|
||||
**
|
||||
** Passing 0 to this function disables blocking locks altogether. Passing
|
||||
** -1 to this function requests that the VFS blocks for a long time -
|
||||
** indefinitely if possible. The results of passing any other negative value
|
||||
** are undefined.
|
||||
**
|
||||
** Internally, each SQLite database handle store two timeout values - the
|
||||
** busy-timeout (used for rollback mode databases, or if the VFS does not
|
||||
** support blocking locks) and the setlk-timeout (used for blocking locks
|
||||
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
|
||||
** values, this function sets only the setlk-timeout value. Therefore,
|
||||
** to configure separate busy-timeout and setlk-timeout values for a single
|
||||
** database handle, call sqlite3_busy_timeout() followed by this function.
|
||||
**
|
||||
** Whenever the number of connections to a wal mode database falls from
|
||||
** 1 to 0, the last connection takes an exclusive lock on the database,
|
||||
** then checkpoints and deletes the wal file. While it is doing this, any
|
||||
** new connection that tries to read from the database fails with an
|
||||
** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
|
||||
** passed to this API, the new connection blocks until the exclusive lock
|
||||
** has been released.
|
||||
*/
|
||||
SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flags for sqlite3_setlk_timeout()
|
||||
*/
|
||||
#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01
|
||||
|
||||
/*
|
||||
** CAPI3REF: Convenience Routines For Running Queries
|
||||
** METHOD: sqlite3
|
||||
@@ -5108,7 +5175,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
||||
** other than [SQLITE_ROW] before any subsequent invocation of
|
||||
** sqlite3_step(). Failure to reset the prepared statement using
|
||||
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
|
||||
** sqlite3_step() began
|
||||
** calling [sqlite3_reset()] automatically in this circumstance rather
|
||||
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
||||
@@ -7004,6 +7071,8 @@ SQLITE_API int sqlite3_autovacuum_pages(
|
||||
**
|
||||
** ^The second argument is a pointer to the function to invoke when a
|
||||
** row is updated, inserted or deleted in a rowid table.
|
||||
** ^The update hook is disabled by invoking sqlite3_update_hook()
|
||||
** with a NULL pointer as the second parameter.
|
||||
** ^The first argument to the callback is a copy of the third argument
|
||||
** to sqlite3_update_hook().
|
||||
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
||||
@@ -11486,9 +11555,10 @@ SQLITE_API void sqlite3session_table_filter(
|
||||
** is inserted while a session object is enabled, then later deleted while
|
||||
** the same session object is disabled, no INSERT record will appear in the
|
||||
** changeset, even though the delete took place while the session was disabled.
|
||||
** Or, if one field of a row is updated while a session is disabled, and
|
||||
** another field of the same row is updated while the session is enabled, the
|
||||
** resulting changeset will contain an UPDATE change that updates both fields.
|
||||
** Or, if one field of a row is updated while a session is enabled, and
|
||||
** then another field of the same row is updated while the session is disabled,
|
||||
** the resulting changeset will contain an UPDATE change that updates both
|
||||
** fields.
|
||||
*/
|
||||
SQLITE_API int sqlite3session_changeset(
|
||||
sqlite3_session *pSession, /* Session object */
|
||||
@@ -11560,8 +11630,9 @@ SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession
|
||||
** database zFrom the contents of the two compatible tables would be
|
||||
** identical.
|
||||
**
|
||||
** It an error if database zFrom does not exist or does not contain the
|
||||
** required compatible table.
|
||||
** Unless the call to this function is a no-op as described above, it is an
|
||||
** error if database zFrom does not exist or does not contain the required
|
||||
** compatible table.
|
||||
**
|
||||
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
||||
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
||||
@@ -11696,7 +11767,7 @@ SQLITE_API int sqlite3changeset_start_v2(
|
||||
** The following flags may passed via the 4th parameter to
|
||||
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
||||
**
|
||||
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
||||
** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
|
||||
** Invert the changeset while iterating through it. This is equivalent to
|
||||
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
||||
** It is an error to specify this flag with a patchset.
|
||||
@@ -12011,19 +12082,6 @@ SQLITE_API int sqlite3changeset_concat(
|
||||
void **ppOut /* OUT: Buffer containing output changeset */
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
|
||||
*/
|
||||
SQLITE_API int sqlite3changeset_upgrade(
|
||||
sqlite3 *db,
|
||||
const char *zDb,
|
||||
int nIn, const void *pIn, /* Input changeset */
|
||||
int *pnOut, void **ppOut /* OUT: Inverse of input */
|
||||
);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Changegroup Handle
|
||||
**
|
||||
|
4
deps/sqlite/sqlite3ext.h
vendored
@@ -366,6 +366,8 @@ struct sqlite3_api_routines {
|
||||
/* Version 3.44.0 and later */
|
||||
void *(*get_clientdata)(sqlite3*,const char*);
|
||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||
/* Version 3.50.0 and later */
|
||||
int (*setlk_timeout)(sqlite3*,int,int);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -699,6 +701,8 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
/* Version 3.44.0 and later */
|
||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||
/* Version 3.50.0 and later */
|
||||
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
40
docs/connecting_manyverse.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Connecting with Manyverse
|
||||
|
||||
Communication with [Manyverse](https://www.manyver.se/) should Just Work (tm).
|
||||
|
||||
This document is intended as a cheat sheet for the instances where it doesn't.
|
||||
If your experience differs, please share so we can make things better.
|
||||
|
||||
## Connecting Manyverse to the tildefriends.net room
|
||||
|
||||
Open the `Connections` tab. This is from the desktop app, but mobile is similar.
|
||||
|
||||

|
||||
|
||||
Open the `Connections Panel`.
|
||||
|
||||

|
||||
|
||||
Use the `Add Connection` button at the bottom right to open the dialog to enter
|
||||
a connections string to add a new connection.
|
||||
|
||||

|
||||
|
||||
Copy the tildefriends.net room code from https://www.tildefriends.net/~cory/room/.
|
||||
|
||||

|
||||
|
||||
Paste.
|
||||
|
||||
On mobile especially, make sure the full text is pasted without modification.
|
||||
|
||||

|
||||
|
||||
Click `Done`, and you should be connected successfully. tildefriends.net is
|
||||
all things: a room, a pub, and a client, so you should be able to start replicating
|
||||
immediately as well as find other similarly connected people with whom to establish
|
||||
further connections.
|
||||
|
||||
When logged into tildefriends.net, active connections it sees can be found on
|
||||
the `Connections` tab: https://www.tildefriends.net/~core/ssb/#connections,
|
||||
which may indicate errors if you find yourself disconnecting.
|
BIN
docs/images/manyverse_code.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
docs/images/manyverse_connections_panel.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
docs/images/manyverse_connections_tab.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
docs/images/manyverse_paste_invite_code.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
docs/images/tildefriends_room_app.png
Normal file
After Width: | Height: | Size: 136 KiB |
@@ -14,7 +14,7 @@
|
||||
- upload to Apple with dist-ios on macos
|
||||
- nix
|
||||
- june and december: update release version
|
||||
- run `nix flake update`
|
||||
- run `nix --extra-experimental-features nix-command --extra-experimental-features flakes flake update`
|
||||
- comment out the hash in default.nix
|
||||
- update the version
|
||||
- run `nix-build`
|
||||
|
6
flake.lock
generated
@@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1745279238,
|
||||
"narHash": "sha256-AQ7M9wTa/Pa/kK5pcGTgX/DGqMHyzsyINfN7ktsI7Fo=",
|
||||
"lastModified": 1748037224,
|
||||
"narHash": "sha256-92vihpZr6dwEMV6g98M5kHZIttrWahb9iRPBm1atcPk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9684b53175fc6c09581e94cc85f05ab77464c7e3",
|
||||
"rev": "f09dede81861f3a83f7f06641ead34f02f37597f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
Before Width: | Height: | Size: 275 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 108 KiB |
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.unprompted.tildefriends"
|
||||
android:versionCode="37"
|
||||
android:versionName="0.0.31">
|
||||
android:versionCode="38"
|
||||
android:versionName="0.0.32-wip">
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<application
|
||||
|
@@ -813,6 +813,11 @@ void tf_http_destroy(tf_http_t* http)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!http->is_shutting_down)
|
||||
{
|
||||
tf_printf("tf_http_destroy\n");
|
||||
}
|
||||
|
||||
http->is_shutting_down = true;
|
||||
http->is_in_destroy = true;
|
||||
|
||||
|
@@ -75,7 +75,6 @@ static int _object_to_headers(JSContext* context, JSValue object, const char** h
|
||||
JS_GetOwnPropertyNames(context, &ptab, &plen, object, JS_GPN_STRING_MASK);
|
||||
for (; count < (int)plen && count < headers_length / 2; ++count)
|
||||
{
|
||||
JSValue key = JS_AtomToString(context, ptab[count].atom);
|
||||
JSPropertyDescriptor desc;
|
||||
JSValue key_value = JS_NULL;
|
||||
if (JS_GetOwnProperty(context, &desc, object, ptab[count].atom) == 1)
|
||||
@@ -84,9 +83,8 @@ static int _object_to_headers(JSContext* context, JSValue object, const char** h
|
||||
JS_FreeValue(context, desc.setter);
|
||||
JS_FreeValue(context, desc.getter);
|
||||
}
|
||||
headers[count * 2 + 0] = JS_ToCString(context, key);
|
||||
headers[count * 2 + 0] = JS_AtomToCString(context, ptab[count].atom);
|
||||
headers[count * 2 + 1] = JS_ToCString(context, key_value);
|
||||
JS_FreeValue(context, key);
|
||||
JS_FreeValue(context, key_value);
|
||||
}
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
@@ -640,25 +638,6 @@ static void _httpd_endpoint_mem(tf_http_request_t* request)
|
||||
tf_free(response);
|
||||
}
|
||||
|
||||
static void _httpd_endpoint_hitches(tf_http_request_t* request)
|
||||
{
|
||||
if (_httpd_redirect(request))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tf_task_t* task = request->user_data;
|
||||
char* response = tf_task_get_hitches(task);
|
||||
const char* headers[] = {
|
||||
"Content-Type",
|
||||
"application/json; charset=utf-8",
|
||||
"Access-Control-Allow-Origin",
|
||||
"*",
|
||||
};
|
||||
tf_http_respond(request, 200, headers, tf_countof(headers) / 2, response, response ? strlen(response) : 0);
|
||||
tf_free(response);
|
||||
}
|
||||
|
||||
static const char* _after(const char* text, const char* prefix)
|
||||
{
|
||||
if (!text || !prefix)
|
||||
@@ -950,7 +929,7 @@ static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data)
|
||||
char* app_path = tf_malloc(path_length);
|
||||
snprintf(app_path, path_length, "path:%s", data->user_app->app);
|
||||
const char* value = tf_ssb_db_get_property(ssb, data->user_app->user, app_path);
|
||||
snprintf(data->app_blob_id, sizeof(data->app_blob_id), "%s", value);
|
||||
tf_string_set(data->app_blob_id, sizeof(data->app_blob_id), value);
|
||||
tf_free(app_path);
|
||||
tf_free((void*)value);
|
||||
data->file = last_slash + 1;
|
||||
@@ -1149,7 +1128,7 @@ static void _httpd_endpoint_view_work(tf_ssb_t* ssb, void* user_data)
|
||||
char* app_path = tf_malloc(app_path_length);
|
||||
snprintf(app_path, app_path_length, "path:%s", user_app->app);
|
||||
const char* value = tf_ssb_db_get_property(ssb, user_app->user, app_path);
|
||||
snprintf(blob_id, sizeof(blob_id), "%s", value);
|
||||
tf_string_set(blob_id, sizeof(blob_id), value);
|
||||
tf_free(app_path);
|
||||
tf_free((void*)value);
|
||||
}
|
||||
@@ -1176,7 +1155,7 @@ static void _httpd_endpoint_view_work(tf_ssb_t* ssb, void* user_data)
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
tf_ssb_db_add_blob_wants(db, blob_id);
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
snprintf(view->notify_want_blob_id, sizeof(view->notify_want_blob_id), "%s", blob_id);
|
||||
tf_string_set(view->notify_want_blob_id, sizeof(view->notify_want_blob_id), blob_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1327,7 +1306,7 @@ static void _httpd_endpoint_save_work(tf_ssb_t* ssb, void* user_data)
|
||||
tf_ssb_db_set_property(ssb, user_app->user, app_path, blob_id))
|
||||
{
|
||||
tf_ssb_db_add_value_to_array_property(ssb, user_app->user, "apps", user_app->app);
|
||||
snprintf(save->blob_id, sizeof(save->blob_id), "%s", blob_id);
|
||||
tf_string_set(save->blob_id, sizeof(save->blob_id), blob_id);
|
||||
save->response = 200;
|
||||
}
|
||||
else
|
||||
@@ -1360,7 +1339,7 @@ static void _httpd_endpoint_save_work(tf_ssb_t* ssb, void* user_data)
|
||||
char blob_id[k_blob_id_len] = { 0 };
|
||||
if (tf_ssb_db_blob_store(ssb, request->body, request->content_length, blob_id, sizeof(blob_id), NULL))
|
||||
{
|
||||
snprintf(save->blob_id, sizeof(save->blob_id), "%s", blob_id);
|
||||
tf_string_set(save->blob_id, sizeof(save->blob_id), blob_id);
|
||||
save->response = 200;
|
||||
}
|
||||
else
|
||||
@@ -1980,7 +1959,7 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
|
||||
const char* return_url = _form_data_get(form_data, "return");
|
||||
if (return_url)
|
||||
{
|
||||
snprintf(login->location_header, sizeof(login->location_header), "%s", return_url);
|
||||
tf_string_set(login->location_header, sizeof(login->location_header), return_url);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2085,7 +2064,7 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
|
||||
const char* return_url = _form_data_get(form_data, "return");
|
||||
if (return_url)
|
||||
{
|
||||
snprintf(login->location_header, sizeof(login->location_header), "%s", return_url);
|
||||
tf_string_set(login->location_header, sizeof(login->location_header), return_url);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2437,7 +2416,6 @@ tf_http_t* tf_httpd_create(JSContext* context)
|
||||
|
||||
tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL);
|
||||
tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task);
|
||||
tf_http_add_handler(http, "/hitches", _httpd_endpoint_hitches, NULL, task);
|
||||
tf_http_add_handler(http, "/mem", _httpd_endpoint_mem, NULL, task);
|
||||
tf_http_add_handler(http, "/trace", _httpd_endpoint_trace, NULL, task);
|
||||
tf_http_add_handler(http, "/ebt", _httpd_endpoint_ebt, NULL, task);
|
||||
|
@@ -13,13 +13,13 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.0.31</string>
|
||||
<string>0.0.32</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>iPhoneOS</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>13</string>
|
||||
<string>14</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>iphoneos</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
@@ -2026,6 +2026,7 @@ void tf_run_thread_start(const char* zip_path)
|
||||
#else
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
_startup(argc, argv);
|
||||
ares_library_init(0);
|
||||
|
||||
|
@@ -489,7 +489,7 @@ static JSValue _socket_connect(JSContext* context, JSValueConst this_val, int ar
|
||||
const char* node = JS_ToCString(context, argv[0]);
|
||||
const char* port = JS_ToCString(context, argv[1]);
|
||||
|
||||
snprintf(socket->_peerName, sizeof(socket->_peerName), "%s", node);
|
||||
tf_string_set(socket->_peerName, sizeof(socket->_peerName), node);
|
||||
|
||||
socket_resolve_data_t* data = tf_malloc(sizeof(socket_resolve_data_t));
|
||||
memset(data, 0, sizeof(*data));
|
||||
@@ -1062,9 +1062,7 @@ static void _socket_onShutdown(uv_shutdown_t* request, int status)
|
||||
}
|
||||
else
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_shutdown: %s", uv_strerror(status));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "%s", error));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "uv_shutdown: %s", uv_strerror(status)));
|
||||
}
|
||||
tf_free(request);
|
||||
}
|
||||
|
107
src/ssb.c
@@ -34,9 +34,6 @@
|
||||
#define CYAN "\e[1;36m"
|
||||
#define RESET "\e[0m"
|
||||
|
||||
#define PRE_CALLBACK(ssb, cb) uint64_t pre_callback_hrtime_ns = _tf_ssb_callback_pre(ssb)
|
||||
#define POST_CALLBACK(ssb, cb) _tf_ssb_callback_post(ssb, cb, pre_callback_hrtime_ns)
|
||||
|
||||
const int k_read_back_pressure_threshold = 256;
|
||||
|
||||
static_assert(k_id_base64_len == sodium_base64_ENCODED_LEN(9 + crypto_box_PUBLICKEYBYTES, sodium_base64_VARIANT_ORIGINAL), "k_id_base64_len");
|
||||
@@ -183,6 +180,7 @@ typedef struct _tf_ssb_t
|
||||
sqlite3* db_writer;
|
||||
sqlite3** db_readers;
|
||||
int db_readers_count;
|
||||
int db_ref_count;
|
||||
|
||||
uv_loop_t own_loop;
|
||||
uv_loop_t* loop;
|
||||
@@ -237,9 +235,6 @@ typedef struct _tf_ssb_t
|
||||
int32_t thread_busy_count;
|
||||
int32_t thread_busy_max;
|
||||
|
||||
void (*hitch_callback)(const char* name, uint64_t duration, void* user_data);
|
||||
void* hitch_user_data;
|
||||
|
||||
tf_ssb_store_queue_t store_queue;
|
||||
int ref_count;
|
||||
|
||||
@@ -368,8 +363,6 @@ static int s_connection_index;
|
||||
static int s_tunnel_index;
|
||||
|
||||
static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broadcast, int expires_seconds);
|
||||
static uint64_t _tf_ssb_callback_pre(tf_ssb_t* ssb);
|
||||
static void _tf_ssb_callback_post(tf_ssb_t* ssb, void* callback, uint64_t pre);
|
||||
static void _tf_ssb_connection_client_send_hello(tf_ssb_connection_t* connection);
|
||||
static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* reason);
|
||||
static void _tf_ssb_connection_finalizer(JSRuntime* runtime, JSValue value);
|
||||
@@ -645,9 +638,7 @@ static void _tf_ssb_connection_dispatch_scheduled(tf_ssb_connection_t* connectio
|
||||
connection->scheduled_count--;
|
||||
|
||||
tf_trace_begin(connection->ssb->trace, "scheduled callback");
|
||||
PRE_CALLBACK(connection->ssb, scheduled.callback);
|
||||
scheduled.callback(connection, false, scheduled.user_data);
|
||||
POST_CALLBACK(connection->ssb, scheduled.callback);
|
||||
tf_trace_end(connection->ssb->trace);
|
||||
}
|
||||
}
|
||||
@@ -666,9 +657,7 @@ void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, const char
|
||||
{
|
||||
/* Skip the new request. */
|
||||
tf_trace_begin(connection->ssb->trace, "scheduled callback (skip)");
|
||||
PRE_CALLBACK(connection->ssb, callback);
|
||||
callback(connection, true, user_data);
|
||||
POST_CALLBACK(connection->ssb, callback);
|
||||
tf_trace_end(connection->ssb->trace);
|
||||
}
|
||||
else
|
||||
@@ -679,7 +668,7 @@ void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, const char
|
||||
.callback = callback,
|
||||
.user_data = user_data,
|
||||
};
|
||||
snprintf(connection->scheduled[index].key, sizeof(connection->scheduled[index].key), "%s", key);
|
||||
tf_string_set(connection->scheduled[index].key, sizeof(connection->scheduled[index].key), key);
|
||||
connection->scheduled_count++;
|
||||
|
||||
uv_async_send(&connection->scheduled_async);
|
||||
@@ -800,7 +789,7 @@ void tf_ssb_connection_add_request(tf_ssb_connection_t* connection, int32_t requ
|
||||
.dependent_connection = dependent_connection,
|
||||
.last_active = now_ms,
|
||||
};
|
||||
snprintf(request.name, sizeof(request.name), "%s", name);
|
||||
tf_string_set(request.name, sizeof(request.name), name);
|
||||
int index = tf_util_insert_index(&request_number, connection->requests, connection->requests_count, sizeof(tf_ssb_request_t), _request_compare);
|
||||
connection->requests = tf_resize_vec(connection->requests, sizeof(tf_ssb_request_t) * (connection->requests_count + 1));
|
||||
if (connection->requests_count - index)
|
||||
@@ -853,7 +842,7 @@ void tf_ssb_connection_add_new_message_request(tf_ssb_connection_t* connection,
|
||||
.request_number = request_number,
|
||||
.keys = keys,
|
||||
};
|
||||
snprintf(connection->message_requests[index].author, sizeof(connection->message_requests[index].author), "%s", author);
|
||||
tf_string_set(connection->message_requests[index].author, sizeof(connection->message_requests[index].author), author);
|
||||
connection->message_requests_count++;
|
||||
}
|
||||
|
||||
@@ -1249,22 +1238,6 @@ bool tf_ssb_id_str_to_bin(uint8_t* bin, const char* str)
|
||||
return author_id && type ? tf_base64_decode(author_id, type - author_id, bin, crypto_box_PUBLICKEYBYTES) != 0 : false;
|
||||
}
|
||||
|
||||
static uint64_t _tf_ssb_callback_pre(tf_ssb_t* ssb)
|
||||
{
|
||||
return uv_hrtime();
|
||||
}
|
||||
|
||||
static void _tf_ssb_callback_post(tf_ssb_t* ssb, void* callback, uint64_t pre)
|
||||
{
|
||||
if (ssb->hitch_callback)
|
||||
{
|
||||
uint64_t post = uv_hrtime();
|
||||
const char* name = tf_util_function_to_string(callback);
|
||||
ssb->hitch_callback(name, post - pre, ssb->hitch_user_data);
|
||||
tf_free((void*)name);
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_ssb_notify_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection)
|
||||
{
|
||||
tf_ssb_connections_changed_callback_node_t* next = NULL;
|
||||
@@ -1272,9 +1245,7 @@ static void _tf_ssb_notify_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t ch
|
||||
{
|
||||
next = node->next;
|
||||
tf_trace_begin(ssb->trace, "connections_changed");
|
||||
PRE_CALLBACK(ssb, node->callback);
|
||||
node->callback(ssb, change, connection, node->user_data);
|
||||
POST_CALLBACK(ssb, node->callback);
|
||||
tf_trace_end(ssb->trace);
|
||||
}
|
||||
}
|
||||
@@ -1383,9 +1354,7 @@ static void _tf_ssb_connection_verify_identity(tf_ssb_connection_t* connection,
|
||||
connection->state = k_tf_ssb_state_verified;
|
||||
if (connection->connect_callback)
|
||||
{
|
||||
PRE_CALLBACK(connection->ssb, connection->connect_callback);
|
||||
connection->connect_callback(connection, NULL, connection->connect_callback_user_data);
|
||||
POST_CALLBACK(connection->ssb, connection->connect_callback);
|
||||
connection->connect_callback = NULL;
|
||||
connection->connect_callback_user_data = NULL;
|
||||
}
|
||||
@@ -1734,7 +1703,7 @@ static void _tf_ssb_name_to_string(JSContext* context, JSValue object, char* buf
|
||||
else if (JS_IsString(name))
|
||||
{
|
||||
const char* part_str = JS_ToCString(context, name);
|
||||
snprintf(buffer, size, "%s", part_str);
|
||||
tf_string_set(buffer, size, part_str);
|
||||
JS_FreeCString(context, part_str);
|
||||
}
|
||||
JS_FreeValue(context, name);
|
||||
@@ -1777,9 +1746,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "request %s:%d", request_name, request_number);
|
||||
tf_trace_begin(connection->ssb->trace, buffer);
|
||||
PRE_CALLBACK(connection->ssb, callback);
|
||||
callback(connection, flags, request_number, val, message, size, user_data);
|
||||
POST_CALLBACK(connection->ssb, callback);
|
||||
tf_trace_end(connection->ssb->trace);
|
||||
if (!(flags & k_ssb_rpc_flag_stream))
|
||||
{
|
||||
@@ -1798,9 +1765,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
|
||||
{
|
||||
tf_ssb_connection_add_request(connection, -request_number, name, NULL, NULL, NULL, NULL);
|
||||
tf_trace_begin(connection->ssb->trace, it->name);
|
||||
PRE_CALLBACK(connection->ssb, it->callback);
|
||||
it->callback(connection, flags, request_number, val, message, size, it->user_data);
|
||||
POST_CALLBACK(connection->ssb, it->callback);
|
||||
tf_trace_end(connection->ssb->trace);
|
||||
found = true;
|
||||
break;
|
||||
@@ -1840,9 +1805,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "request %s:%d", request_name, request_number);
|
||||
tf_trace_begin(connection->ssb->trace, buffer);
|
||||
PRE_CALLBACK(connection->ssb, callback);
|
||||
callback(connection, flags, request_number, JS_UNDEFINED, message, size, user_data);
|
||||
POST_CALLBACK(connection->ssb, callback);
|
||||
tf_trace_end(connection->ssb->trace);
|
||||
}
|
||||
}
|
||||
@@ -1962,7 +1925,7 @@ JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* pr
|
||||
if (previous_id)
|
||||
{
|
||||
have_previous = *previous_id && previous_sequence > 0;
|
||||
snprintf(actual_previous_id, sizeof(actual_previous_id), "%s", previous_id);
|
||||
tf_string_set(actual_previous_id, sizeof(actual_previous_id), previous_id);
|
||||
actual_previous_sequence = previous_sequence;
|
||||
}
|
||||
else
|
||||
@@ -2029,9 +1992,7 @@ static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const ch
|
||||
}
|
||||
if (connection->connect_callback)
|
||||
{
|
||||
PRE_CALLBACK(connection->ssb, connection->connect_callback);
|
||||
connection->connect_callback(NULL, reason, connection->connect_callback_user_data);
|
||||
POST_CALLBACK(connection->ssb, connection->connect_callback);
|
||||
connection->connect_callback = NULL;
|
||||
connection->connect_callback_user_data = NULL;
|
||||
}
|
||||
@@ -2519,6 +2480,7 @@ sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb)
|
||||
tf_ssb_db_init_reader(db);
|
||||
}
|
||||
tf_trace_sqlite(ssb->trace, db);
|
||||
ssb->db_ref_count++;
|
||||
uv_mutex_unlock(&ssb->db_readers_lock);
|
||||
sqlite3_set_authorizer(db, NULL, NULL);
|
||||
return db;
|
||||
@@ -2535,10 +2497,17 @@ void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db)
|
||||
{
|
||||
sqlite3_db_release_memory(db);
|
||||
uv_mutex_lock(&ssb->db_readers_lock);
|
||||
ssb->db_ref_count--;
|
||||
bool destroy = ssb->shutting_down_deferred && ssb->db_ref_count == 0;
|
||||
ssb->db_readers = tf_resize_vec(ssb->db_readers, sizeof(sqlite3*) * (ssb->db_readers_count + 1));
|
||||
ssb->db_readers[ssb->db_readers_count++] = db;
|
||||
uv_mutex_unlock(&ssb->db_readers_lock);
|
||||
tf_trace_end(ssb->trace);
|
||||
|
||||
if (destroy)
|
||||
{
|
||||
tf_ssb_destroy(ssb);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3* tf_ssb_acquire_db_writer(tf_ssb_t* ssb)
|
||||
@@ -2832,15 +2801,6 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
||||
JS_FreeRuntime(ssb->runtime);
|
||||
ssb->own_context = false;
|
||||
}
|
||||
if (ssb->db_writer)
|
||||
{
|
||||
int r = sqlite3_close(ssb->db_writer);
|
||||
if (r != SQLITE_OK)
|
||||
{
|
||||
tf_printf("sqlite3_close: %s\n", sqlite3_errstr(r));
|
||||
}
|
||||
ssb->db_writer = NULL;
|
||||
}
|
||||
while (ssb->broadcasts)
|
||||
{
|
||||
tf_ssb_broadcast_t* broadcast = ssb->broadcasts;
|
||||
@@ -2856,6 +2816,15 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
||||
tf_printf("sqlite3_close: %s\n", sqlite3_errstr(r));
|
||||
}
|
||||
}
|
||||
if (ssb->db_writer)
|
||||
{
|
||||
int r = sqlite3_close(ssb->db_writer);
|
||||
if (r != SQLITE_OK)
|
||||
{
|
||||
tf_printf("sqlite3_close: %s\n", sqlite3_errstr(r));
|
||||
}
|
||||
ssb->db_writer = NULL;
|
||||
}
|
||||
ssb->db_readers_count = 0;
|
||||
if (ssb->db_readers)
|
||||
{
|
||||
@@ -2874,7 +2843,7 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
||||
}
|
||||
|
||||
ssb->shutting_down_deferred = true;
|
||||
if (ssb->connection_ref_count == 0)
|
||||
if (ssb->connection_ref_count == 0 && ssb->db_ref_count == 0)
|
||||
{
|
||||
uv_mutex_destroy(&ssb->db_readers_lock);
|
||||
uv_mutex_destroy(&ssb->db_writer_lock);
|
||||
@@ -2996,7 +2965,7 @@ static tf_ssb_connection_t* _tf_ssb_connection_create(
|
||||
tf_ssb_connection_t* connection = _tf_ssb_connection_create_internal(ssb, "cli", s_connection_index++);
|
||||
connection->connect.data = connection;
|
||||
|
||||
snprintf(connection->host, sizeof(connection->host), "%s", host);
|
||||
tf_string_set(connection->host, sizeof(connection->host), host);
|
||||
connection->port = ntohs(addr->sin_port);
|
||||
connection->connect_callback = callback;
|
||||
connection->connect_callback_user_data = user_data;
|
||||
@@ -3171,7 +3140,7 @@ void tf_ssb_connect(tf_ssb_t* ssb, const char* host, int port, const uint8_t* ke
|
||||
{
|
||||
tf_ssb_connections_store(ssb->connections_tracker, host, port, id);
|
||||
}
|
||||
snprintf(connect->host, sizeof(connect->host), "%s", host);
|
||||
tf_string_set(connect->host, sizeof(connect->host), host);
|
||||
memcpy(connect->key, key, k_id_bin_len);
|
||||
tf_ssb_ref(ssb);
|
||||
int r = uv_getaddrinfo(ssb->loop, &connect->req, _tf_on_connect_getaddrinfo, host, NULL, &(struct addrinfo) { .ai_family = AF_INET });
|
||||
@@ -3220,7 +3189,7 @@ static void _tf_ssb_connect_with_invite(
|
||||
{
|
||||
tf_ssb_connections_store(ssb->connections_tracker, host, port, id);
|
||||
}
|
||||
snprintf(connect->host, sizeof(connect->host), "%s", host);
|
||||
tf_string_set(connect->host, sizeof(connect->host), host);
|
||||
memcpy(connect->key, key, k_id_bin_len);
|
||||
memcpy(connect->invite, invite, sizeof(connect->invite));
|
||||
tf_ssb_ref(ssb);
|
||||
@@ -3551,9 +3520,7 @@ static void _tf_ssb_notify_broadcasts_changed(tf_ssb_t* ssb)
|
||||
if (node->callback)
|
||||
{
|
||||
tf_trace_begin(ssb->trace, "broadcasts changed");
|
||||
PRE_CALLBACK(ssb, node->callback);
|
||||
node->callback(ssb, node->user_data);
|
||||
POST_CALLBACK(ssb, node->callback);
|
||||
tf_trace_end(ssb->trace);
|
||||
}
|
||||
}
|
||||
@@ -3657,9 +3624,7 @@ void tf_ssb_visit_broadcasts(tf_ssb_t* ssb,
|
||||
if (node->mtime - now < 60)
|
||||
{
|
||||
tf_trace_begin(ssb->trace, "broadcast");
|
||||
PRE_CALLBACK(ssb, callback);
|
||||
callback(node->host, &node->addr, node->origin, node->tunnel_connection, node->pub, user_data);
|
||||
POST_CALLBACK(ssb, callback);
|
||||
tf_trace_end(ssb->trace);
|
||||
}
|
||||
}
|
||||
@@ -3966,9 +3931,7 @@ void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int64_t sequ
|
||||
{
|
||||
next = node->next;
|
||||
tf_trace_begin(ssb->trace, "message added callback");
|
||||
PRE_CALLBACK(ssb, node->callback);
|
||||
node->callback(ssb, author, sequence, id, node->user_data);
|
||||
POST_CALLBACK(ssb, node->callback);
|
||||
tf_trace_end(ssb->trace);
|
||||
}
|
||||
|
||||
@@ -4044,9 +4007,7 @@ void tf_ssb_notify_blob_want_added(tf_ssb_t* ssb, const char* id)
|
||||
{
|
||||
next = node->next;
|
||||
tf_trace_begin(ssb->trace, "blob want added callback");
|
||||
PRE_CALLBACK(ssb, node->callback);
|
||||
node->callback(ssb, id, node->user_data);
|
||||
POST_CALLBACK(ssb, node->callback);
|
||||
tf_trace_end(ssb->trace);
|
||||
}
|
||||
}
|
||||
@@ -4231,12 +4192,6 @@ float tf_ssb_get_average_thread_percent(tf_ssb_t* ssb)
|
||||
return 100.0f * ssb->thread_busy_count / ssb->thread_busy_max;
|
||||
}
|
||||
|
||||
void tf_ssb_set_hitch_callback(tf_ssb_t* ssb, void (*callback)(const char* name, uint64_t duration_ns, void* user_data), void* user_data)
|
||||
{
|
||||
ssb->hitch_callback = callback;
|
||||
ssb->hitch_user_data = user_data;
|
||||
}
|
||||
|
||||
tf_ssb_store_queue_t* tf_ssb_get_store_queue(tf_ssb_t* ssb)
|
||||
{
|
||||
return &ssb->store_queue;
|
||||
@@ -4292,9 +4247,7 @@ static void _tf_ssb_connection_after_work_callback(uv_work_t* work, int status)
|
||||
if (data->after_work_callback)
|
||||
{
|
||||
tf_trace_begin(data->connection->ssb->trace, data->after_name);
|
||||
PRE_CALLBACK(data->connection->ssb, data->after_work_callback);
|
||||
data->after_work_callback(data->connection, status, data->user_data);
|
||||
POST_CALLBACK(data->connection->ssb, data->after_work_callback);
|
||||
tf_trace_end(data->connection->ssb->trace);
|
||||
}
|
||||
data->connection->ref_count--;
|
||||
@@ -4361,9 +4314,7 @@ static void _tf_ssb_after_work_callback(uv_work_t* work, int status)
|
||||
if (data->after_work_callback)
|
||||
{
|
||||
tf_trace_begin(data->ssb->trace, data->after_name);
|
||||
PRE_CALLBACK(data->ssb, data->after_work_callback);
|
||||
data->after_work_callback(data->ssb, status, data->user_data);
|
||||
POST_CALLBACK(data->ssb, data->after_work_callback);
|
||||
tf_trace_end(data->ssb->trace);
|
||||
}
|
||||
tf_ssb_unref(data->ssb);
|
||||
@@ -4485,7 +4436,7 @@ static void _tf_ssb_update_settings_after_work(tf_ssb_t* ssb, int result, void*
|
||||
uv_timer_start(&ssb->broadcast_timer, _tf_ssb_broadcast_timer, 2000, 2000);
|
||||
}
|
||||
ssb->discovery = update->discovery;
|
||||
snprintf(ssb->seeds_host, sizeof(ssb->seeds_host), "%s", update->seeds_host);
|
||||
tf_string_set(ssb->seeds_host, sizeof(ssb->seeds_host), update->seeds_host);
|
||||
_tf_ssb_start_update_settings(ssb);
|
||||
tf_free(update);
|
||||
}
|
||||
@@ -4526,9 +4477,7 @@ void tf_ssb_set_quiet(tf_ssb_t* ssb, bool quiet)
|
||||
static void _tf_ssb_scheduled_timer(uv_timer_t* handle)
|
||||
{
|
||||
tf_ssb_timer_t* timer = handle->data;
|
||||
PRE_CALLBACK(timer->ssb, timer->callback);
|
||||
timer->callback(timer->ssb, timer->user_data);
|
||||
POST_CALLBACK(timer->ssb, timer->callback);
|
||||
uv_close((uv_handle_t*)handle, _tf_ssb_on_timer_close);
|
||||
}
|
||||
|
||||
|
@@ -61,9 +61,9 @@ static bool _tf_ssb_connections_get_next_connection(tf_ssb_connections_t* connec
|
||||
{
|
||||
if (sqlite3_bind_int(statement, 1, 60000) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
snprintf(host, host_size, "%s", sqlite3_column_text(statement, 0));
|
||||
tf_string_set(host, host_size, (const char*)sqlite3_column_text(statement, 0));
|
||||
*port = sqlite3_column_int(statement, 1);
|
||||
snprintf(key, key_size, "%s", sqlite3_column_text(statement, 2));
|
||||
tf_string_set(key, key_size, (const char*)sqlite3_column_text(statement, 2));
|
||||
result = true;
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
@@ -246,8 +246,8 @@ void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* hos
|
||||
*update = (tf_ssb_connections_update_t) {
|
||||
.port = port,
|
||||
};
|
||||
snprintf(update->host, sizeof(update->host), "%s", host);
|
||||
snprintf(update->key, sizeof(update->key), "%s", key);
|
||||
tf_string_set(update->host, sizeof(update->host), host);
|
||||
tf_string_set(update->key, sizeof(update->key), key);
|
||||
_tf_ssb_connections_queue_update(connections, update);
|
||||
}
|
||||
|
||||
@@ -258,8 +258,8 @@ void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const c
|
||||
.port = port,
|
||||
.attempted = true,
|
||||
};
|
||||
snprintf(update->host, sizeof(update->host), "%s", host);
|
||||
snprintf(update->key, sizeof(update->key), "%s", key);
|
||||
tf_string_set(update->host, sizeof(update->host), host);
|
||||
tf_string_set(update->key, sizeof(update->key), key);
|
||||
_tf_ssb_connections_queue_update(connections, update);
|
||||
}
|
||||
|
||||
@@ -270,8 +270,8 @@ void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const c
|
||||
.port = port,
|
||||
.succeeded = true,
|
||||
};
|
||||
snprintf(update->host, sizeof(update->host), "%s", host);
|
||||
snprintf(update->key, sizeof(update->key), "%s", key);
|
||||
tf_string_set(update->host, sizeof(update->host), host);
|
||||
tf_string_set(update->key, sizeof(update->key), key);
|
||||
_tf_ssb_connections_queue_update(connections, update);
|
||||
}
|
||||
|
||||
|
95
src/ssb.db.c
@@ -31,6 +31,10 @@ static int _tf_ssb_db_try_exec(sqlite3* db, const char* statement)
|
||||
{
|
||||
tf_printf("Error running '%s': %s.\n", statement, error ? error : sqlite3_errmsg(db));
|
||||
}
|
||||
if (error)
|
||||
{
|
||||
sqlite3_free(error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -41,6 +45,13 @@ static void _tf_ssb_db_exec(sqlite3* db, const char* statement)
|
||||
if (result != SQLITE_OK)
|
||||
{
|
||||
tf_printf("Error running '%s': %s.\n", statement, error ? error : sqlite3_errmsg(db));
|
||||
}
|
||||
if (error)
|
||||
{
|
||||
sqlite3_free(error);
|
||||
}
|
||||
if (result != SQLITE_OK)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -76,6 +87,26 @@ static int _tf_ssb_db_busy_handler(void* user_data, int count)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _tf_ssb_db_wal_hook(void* user_data, sqlite3* db, const char* db_name, int log_pages)
|
||||
{
|
||||
/* Keeps the log below about 64MB with default 4096 byte pages. */
|
||||
if (log_pages >= 16384)
|
||||
{
|
||||
int log = 0;
|
||||
int checkpointed = 0;
|
||||
uint64_t checkpoint_start_ns = uv_hrtime();
|
||||
if (sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_TRUNCATE, &log, &checkpointed) == SQLITE_OK)
|
||||
{
|
||||
tf_printf("Checkpointed %d pages in %d ms. Log is now %d frames.\n", log_pages, (int)((uv_hrtime() - checkpoint_start_ns) / 1000000LL), log);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("Checkpoint: %s.\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
static void _tf_ssb_db_init_internal(sqlite3* db)
|
||||
{
|
||||
sqlite3_extended_result_codes(db, 1);
|
||||
@@ -93,6 +124,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
{
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
_tf_ssb_db_init_internal(db);
|
||||
sqlite3_wal_hook(db, _tf_ssb_db_wal_hook, NULL);
|
||||
|
||||
sqlite3_stmt* statement = NULL;
|
||||
int auto_vacuum = 0;
|
||||
@@ -277,6 +309,13 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
"CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN INSERT INTO messages_fts(messages_fts, rowid, content) VALUES ('delete', old.rowid, "
|
||||
"old.content); END");
|
||||
|
||||
if (_tf_ssb_db_has_rows(db, "SELECT * FROM sqlite_schema WHERE type = 'trigger' AND name = 'messages_ai_refs' AND NOT sql LIKE '%ltrim%'"))
|
||||
{
|
||||
tf_printf("Deleting incorrect messages_refs...\n");
|
||||
_tf_ssb_db_exec(db, "DROP TABLE IF EXISTS messages_refs");
|
||||
tf_printf("Done.\n");
|
||||
}
|
||||
|
||||
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_refs')"))
|
||||
{
|
||||
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
||||
@@ -291,8 +330,9 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
"INSERT INTO messages_refs(message, ref) "
|
||||
"SELECT messages.id, j.value FROM messages, json_tree(messages.content) AS j WHERE "
|
||||
"j.value LIKE '&%.sha256' OR "
|
||||
"j.value LIKE '%%%.sha256' OR "
|
||||
"j.value LIKE '@%.ed25519' "
|
||||
"j.value LIKE '!%%.sha256' ESCAPE '!' OR "
|
||||
"j.value LIKE '@%.ed25519' OR "
|
||||
"(j.value LIKE '#%' AND ltrim(substr(j.value, 2), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') = '') "
|
||||
"ON CONFLICT DO NOTHING");
|
||||
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
||||
tf_printf("Done.\n");
|
||||
@@ -304,8 +344,9 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
"INSERT INTO messages_refs(message, ref) "
|
||||
"SELECT DISTINCT new.id, j.value FROM json_tree(new.content) AS j WHERE "
|
||||
"j.value LIKE '&%.sha256' OR "
|
||||
"j.value LIKE '%%%.sha256' OR "
|
||||
"j.value LIKE '@%.ed25519' "
|
||||
"j.value LIKE '!%%.sha256' ESCAPE '!' OR "
|
||||
"j.value LIKE '@%.ed25519' OR "
|
||||
"(j.value LIKE '#%' AND ltrim(substr(j.value, 2), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') = '') "
|
||||
"ON CONFLICT DO NOTHING; END");
|
||||
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ad_refs");
|
||||
_tf_ssb_db_exec(db, "CREATE TRIGGER IF NOT EXISTS messages_ad_refs AFTER DELETE ON messages BEGIN DELETE FROM messages_refs WHERE messages_refs.message = old.id; END");
|
||||
@@ -541,7 +582,7 @@ static char* _tf_ssb_db_get_message_blob_wants(tf_ssb_t* ssb, int64_t rowid)
|
||||
|
||||
if (sqlite3_prepare_v2(db,
|
||||
"SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND "
|
||||
"json.value LIKE '&%%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL",
|
||||
"json.value LIKE '&%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL",
|
||||
-1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_int64(statement, 1, rowid) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
|
||||
@@ -748,10 +789,10 @@ void tf_ssb_db_store_message(
|
||||
.callback = callback,
|
||||
.user_data = user_data,
|
||||
};
|
||||
snprintf(store->id, sizeof(store->id), "%s", id);
|
||||
snprintf(store->previous, sizeof(store->previous), "%s", previous ? previous : "");
|
||||
snprintf(store->author, sizeof(store->author), "%s", author);
|
||||
snprintf(store->signature, sizeof(store->signature), "%s", signature);
|
||||
tf_string_set(store->id, sizeof(store->id), id);
|
||||
tf_string_set(store->previous, sizeof(store->previous), previous ? previous : "");
|
||||
tf_string_set(store->author, sizeof(store->author), author);
|
||||
tf_string_set(store->signature, sizeof(store->signature), signature);
|
||||
JS_FreeCString(context, author);
|
||||
JS_FreeCString(context, previous);
|
||||
|
||||
@@ -908,7 +949,7 @@ void tf_ssb_db_blob_get_async(tf_ssb_t* ssb, const char* id, tf_ssb_db_blob_get_
|
||||
.callback = callback,
|
||||
.user_data = user_data,
|
||||
};
|
||||
snprintf(async->id, sizeof(async->id), "%s", id);
|
||||
tf_string_set(async->id, sizeof(async->id), id);
|
||||
tf_ssb_run_work(ssb, _tf_ssb_db_blob_get_async_work, _tf_ssb_db_blob_get_async_after_work, async);
|
||||
}
|
||||
|
||||
@@ -1005,7 +1046,7 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
|
||||
|
||||
if (result && out_id)
|
||||
{
|
||||
snprintf(out_id, out_id_size, "%s", id);
|
||||
tf_string_set(out_id, out_id_size, id);
|
||||
}
|
||||
if (out_new)
|
||||
{
|
||||
@@ -1027,7 +1068,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
||||
{
|
||||
if (out_message_id)
|
||||
{
|
||||
snprintf(out_message_id, out_message_id_size, "%s", (const char*)sqlite3_column_text(statement, 0));
|
||||
tf_string_set(out_message_id, out_message_id_size, (const char*)sqlite3_column_text(statement, 0));
|
||||
}
|
||||
if (out_previous)
|
||||
{
|
||||
@@ -1040,7 +1081,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(out_previous, out_previous_size, "%s", (const char*)sqlite3_column_text(statement, 1));
|
||||
tf_string_set(out_previous, out_previous_size, (const char*)sqlite3_column_text(statement, 1));
|
||||
}
|
||||
}
|
||||
if (out_timestamp)
|
||||
@@ -1053,11 +1094,11 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
||||
}
|
||||
if (out_hash)
|
||||
{
|
||||
snprintf(out_hash, out_hash_size, "%s", (const char*)sqlite3_column_text(statement, 4));
|
||||
tf_string_set(out_hash, out_hash_size, (const char*)sqlite3_column_text(statement, 4));
|
||||
}
|
||||
if (out_signature)
|
||||
{
|
||||
snprintf(out_signature, out_signature_size, "%s", (const char*)sqlite3_column_text(statement, 5));
|
||||
tf_string_set(out_signature, out_signature_size, (const char*)sqlite3_column_text(statement, 5));
|
||||
}
|
||||
if (out_flags)
|
||||
{
|
||||
@@ -1610,7 +1651,7 @@ static following_t* _make_following_node(const char* id, following_t*** followin
|
||||
(*following_count)++;
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
entry->depth = INT_MAX;
|
||||
snprintf(entry->id, sizeof(entry->id), "%s", id);
|
||||
tf_string_set(entry->id, sizeof(entry->id), id);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
@@ -1744,7 +1785,7 @@ tf_ssb_following_t* tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, in
|
||||
{
|
||||
if (following[i]->ref_count > 0 || include_blocks)
|
||||
{
|
||||
snprintf(result[write_index].id, sizeof(result[write_index].id), "%s", following[i]->id);
|
||||
tf_string_set(result[write_index].id, sizeof(result[write_index].id), following[i]->id);
|
||||
result[write_index].following_count = following[i]->following_count;
|
||||
result[write_index].blocking_count = following[i]->blocking_count;
|
||||
result[write_index].followed_by_count = following[i]->ref_count;
|
||||
@@ -1799,7 +1840,7 @@ const char** tf_ssb_db_following_deep_ids(tf_ssb_t* ssb, const char** ids, int c
|
||||
if (following[i]->ref_count > 0)
|
||||
{
|
||||
result[write_index] = result_ids + k_id_base64_len * write_index;
|
||||
snprintf(result[write_index], k_id_base64_len, "%s", following[i]->id);
|
||||
tf_string_set(result[write_index], k_id_base64_len, following[i]->id);
|
||||
write_index++;
|
||||
}
|
||||
}
|
||||
@@ -1888,16 +1929,18 @@ tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, i
|
||||
int count = 0;
|
||||
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare_v2(db, "SELECT host, port, key FROM connections ORDER BY host, port, key", -1, &statement, NULL) == SQLITE_OK)
|
||||
if (sqlite3_prepare_v2(db, "SELECT host, port, key, last_attempt, last_success FROM connections ORDER BY host, port, key", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
result = tf_resize_vec(result, sizeof(tf_ssb_db_stored_connection_t) * (count + 1));
|
||||
result[count] = (tf_ssb_db_stored_connection_t) {
|
||||
.port = sqlite3_column_int(statement, 1),
|
||||
.last_attempt = sqlite3_column_int64(statement, 3),
|
||||
.last_success = sqlite3_column_int64(statement, 4),
|
||||
};
|
||||
snprintf(result[count].address, sizeof(result[count].address), "%s", (const char*)sqlite3_column_text(statement, 0));
|
||||
snprintf(result[count].pubkey, sizeof(result[count].pubkey), "%s", (const char*)sqlite3_column_text(statement, 2));
|
||||
tf_string_set(result[count].address, sizeof(result[count].address), (const char*)sqlite3_column_text(statement, 0));
|
||||
tf_string_set(result[count].pubkey, sizeof(result[count].pubkey), (const char*)sqlite3_column_text(statement, 2));
|
||||
count++;
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
@@ -1935,7 +1978,7 @@ bool tf_ssb_db_get_account_password_hash(tf_ssb_t* ssb, const char* name, char*
|
||||
{
|
||||
if (sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
snprintf(out_password, password_size, "%s", (const char*)sqlite3_column_text(statement, 0));
|
||||
tf_string_set(out_password, password_size, (const char*)sqlite3_column_text(statement, 0));
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
@@ -2133,7 +2176,7 @@ bool tf_ssb_db_identity_get_active(sqlite3* db, const char* user, const char* pa
|
||||
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, package_owner, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, package_name, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
snprintf(out_identity, out_identity_size, "%s", (const char*)sqlite3_column_text(statement, 0));
|
||||
tf_string_set(out_identity, out_identity_size, (const char*)sqlite3_column_text(statement, 0));
|
||||
found = true;
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
@@ -2403,7 +2446,7 @@ bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* ou
|
||||
{
|
||||
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
|
||||
{
|
||||
snprintf(out_value, size, "%s", sqlite3_column_text(statement, 0));
|
||||
tf_string_set(out_value, size, (const char*)sqlite3_column_text(statement, 0));
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
@@ -2415,7 +2458,7 @@ bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* ou
|
||||
}
|
||||
if (!result)
|
||||
{
|
||||
snprintf(out_value, size, "%s", tf_util_get_default_global_setting_string(name));
|
||||
tf_string_set(out_value, size, tf_util_get_default_global_setting_string(name));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2697,7 +2740,7 @@ tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* u
|
||||
tf_ssb_db_identity_get_active(db, user, package_owner, package_name, info->active_identity, sizeof(info->active_identity));
|
||||
if (!*info->active_identity && info->count)
|
||||
{
|
||||
snprintf(info->active_identity, sizeof(info->active_identity), "%s", info->identity[0]);
|
||||
tf_string_set(info->active_identity, sizeof(info->active_identity), info->identity[0]);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
|
||||
|
@@ -340,6 +340,10 @@ typedef struct _tf_ssb_db_stored_connection_t
|
||||
int port;
|
||||
/** The identity. */
|
||||
char pubkey[k_id_base64_len];
|
||||
/** Time of last attempted connection. */
|
||||
int64_t last_attempt;
|
||||
/** Time of last successful connection. */
|
||||
int64_t last_success;
|
||||
} tf_ssb_db_stored_connection_t;
|
||||
|
||||
/**
|
||||
|
@@ -102,7 +102,7 @@ static ebt_entry_t* _ebt_get_entry(tf_ssb_ebt_t* ebt, const char* id)
|
||||
.in = -1,
|
||||
.out = -1,
|
||||
};
|
||||
snprintf(ebt->entries[index].id, sizeof(ebt->entries[index].id), "%s", id);
|
||||
tf_string_set(ebt->entries[index].id, sizeof(ebt->entries[index].id), id);
|
||||
ebt->entries_count++;
|
||||
return &ebt->entries[index];
|
||||
}
|
||||
@@ -127,8 +127,7 @@ void tf_ssb_ebt_receive_clock(tf_ssb_ebt_t* ebt, JSContext* context, JSValue clo
|
||||
}
|
||||
if (!JS_IsUndefined(in_clock))
|
||||
{
|
||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
||||
const char* author = JS_ToCString(context, key);
|
||||
const char* author = JS_AtomToCString(context, ptab[i].atom);
|
||||
int64_t sequence = -1;
|
||||
JS_ToInt64(context, &sequence, in_clock);
|
||||
|
||||
@@ -153,7 +152,6 @@ void tf_ssb_ebt_receive_clock(tf_ssb_ebt_t* ebt, JSContext* context, JSValue clo
|
||||
}
|
||||
}
|
||||
JS_FreeCString(context, author);
|
||||
JS_FreeValue(context, key);
|
||||
}
|
||||
JS_FreeValue(context, in_clock);
|
||||
}
|
||||
@@ -212,7 +210,7 @@ static void _ebt_add_to_clock(ebt_get_clock_t* work, const char* id, int64_t val
|
||||
memmove(work->clock->entries + index + 1, work->clock->entries + index, (count - index) * sizeof(tf_ssb_ebt_clock_entry_t));
|
||||
}
|
||||
work->clock->entries[index] = (tf_ssb_ebt_clock_entry_t) { .value = out_value };
|
||||
snprintf(work->clock->entries[index].id, sizeof(work->clock->entries[index].id), "%s", id);
|
||||
tf_string_set(work->clock->entries[index].id, sizeof(work->clock->entries[index].id), id);
|
||||
work->clock->count = count + 1;
|
||||
}
|
||||
}
|
||||
@@ -279,7 +277,7 @@ static void _tf_ssb_ebt_get_send_clock_work(tf_ssb_connection_t* connection, voi
|
||||
{
|
||||
requested = tf_resize_vec(requested, (requested_count + 1) * sizeof(tf_ssb_ebt_clock_entry_t));
|
||||
requested[requested_count] = (tf_ssb_ebt_clock_entry_t) { .value = -1 };
|
||||
snprintf(requested[requested_count].id, sizeof(requested[requested_count].id), "%s", entry->id);
|
||||
tf_string_set(requested[requested_count].id, sizeof(requested[requested_count].id), entry->id);
|
||||
requested_count++;
|
||||
}
|
||||
}
|
||||
@@ -342,7 +340,7 @@ tf_ssb_ebt_clock_t* tf_ssb_ebt_get_messages_to_send(tf_ssb_ebt_t* ebt)
|
||||
{
|
||||
clock = tf_resize_vec(clock, sizeof(tf_ssb_ebt_clock_t) + (count + 1) * sizeof(tf_ssb_ebt_clock_entry_t));
|
||||
clock->entries[count] = (tf_ssb_ebt_clock_entry_t) { .value = entry->in };
|
||||
snprintf(clock->entries[count].id, sizeof(clock->entries[count].id), "%s", entry->id);
|
||||
tf_string_set(clock->entries[count].id, sizeof(clock->entries[count].id), entry->id);
|
||||
clock->count = ++count;
|
||||
}
|
||||
}
|
||||
|
@@ -142,8 +142,7 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
||||
JSPropertyDescriptor desc;
|
||||
if (JS_GetOwnProperty(context, &desc, files, ptab[i].atom) == 1)
|
||||
{
|
||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
||||
const char* file_name = JS_ToCString(context, key);
|
||||
const char* file_name = JS_AtomToCString(context, ptab[i].atom);
|
||||
const char* blob_id = JS_ToCString(context, desc.value);
|
||||
|
||||
uint8_t* file_blob = NULL;
|
||||
@@ -156,7 +155,6 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
||||
}
|
||||
|
||||
JS_FreeCString(context, file_name);
|
||||
JS_FreeValue(context, key);
|
||||
JS_FreeCString(context, blob_id);
|
||||
JS_FreeValue(context, desc.value);
|
||||
JS_FreeValue(context, desc.setter);
|
||||
|
@@ -1006,15 +1006,6 @@ void tf_ssb_record_thread_busy(tf_ssb_t* ssb, bool busy);
|
||||
*/
|
||||
float tf_ssb_get_average_thread_percent(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Register a callback to be called when the main thread blocks for an
|
||||
** unreasonable amount of time.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback to call.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_set_hitch_callback(tf_ssb_t* ssb, void (*callback)(const char* name, uint64_t duration_ns, void* user_data), void* user_data);
|
||||
|
||||
/**
|
||||
** Get the queue of messages in the progress of being stored.
|
||||
** @param ssb The SSB instance.
|
||||
|
24
src/ssb.js.c
@@ -241,7 +241,7 @@ static JSValue _tf_ssb_deleteIdentity(JSContext* context, JSValueConst this_val,
|
||||
{
|
||||
delete_identity_t* work = tf_malloc(sizeof(delete_identity_t) + user_length + 1);
|
||||
*work = (delete_identity_t) { 0 };
|
||||
snprintf(work->id, sizeof(work->id), "%s", *id == '@' ? id + 1 : id);
|
||||
tf_string_set(work->id, sizeof(work->id), *id == '@' ? id + 1 : id);
|
||||
memcpy(work->user, user, user_length + 1);
|
||||
result = JS_NewPromiseCapability(context, work->promise);
|
||||
tf_ssb_run_work(ssb, _tf_ssb_delete_identity_work, _tf_ssb_delete_identity_after_work, work);
|
||||
@@ -279,10 +279,14 @@ static void _tf_ssb_swap_with_server_identity_work(tf_ssb_t* ssb, void* user_dat
|
||||
sqlite3_bind_text(statement, 2, work->user, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 3, work->id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_DONE && sqlite3_changes(db) == 1)
|
||||
{
|
||||
error = NULL;
|
||||
if (sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, &error) != SQLITE_OK)
|
||||
char* commit_error = NULL;
|
||||
if (sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, &commit_error) != SQLITE_OK)
|
||||
{
|
||||
work->error = error ? tf_strdup(error) : tf_strdup(sqlite3_errmsg(db));
|
||||
work->error = commit_error ? tf_strdup(commit_error) : tf_strdup(sqlite3_errmsg(db));
|
||||
}
|
||||
if (commit_error)
|
||||
{
|
||||
sqlite3_free(commit_error);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -300,6 +304,10 @@ static void _tf_ssb_swap_with_server_identity_work(tf_ssb_t* ssb, void* user_dat
|
||||
{
|
||||
work->error = error ? tf_strdup(error) : tf_strdup(sqlite3_errmsg(db));
|
||||
}
|
||||
if (error)
|
||||
{
|
||||
sqlite3_free(error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -345,7 +353,7 @@ static JSValue _tf_ssb_swap_with_server_identity(JSContext* context, JSValueCons
|
||||
swap_with_server_identity_t* work = tf_malloc(sizeof(swap_with_server_identity_t) + user_length + 1);
|
||||
*work = (swap_with_server_identity_t) { 0 };
|
||||
tf_ssb_whoami(ssb, work->server_id, sizeof(work->server_id));
|
||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
||||
tf_string_set(work->id, sizeof(work->id), id);
|
||||
memcpy(work->user, user, user_length + 1);
|
||||
result = JS_NewPromiseCapability(context, work->promise);
|
||||
tf_ssb_run_work(ssb, _tf_ssb_swap_with_server_identity_work, _tf_ssb_swap_with_server_identity_after_work, work);
|
||||
@@ -481,7 +489,7 @@ static JSValue _tf_ssb_getPrivateKey(JSContext* context, JSValueConst this_val,
|
||||
get_private_key_t* work = tf_malloc(sizeof(get_private_key_t) + user_length + 1);
|
||||
*work = (get_private_key_t) { .context = context };
|
||||
memcpy(work->user, user, user_length + 1);
|
||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
||||
tf_string_set(work->id, sizeof(work->id), id);
|
||||
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||
tf_ssb_run_work(ssb, _tf_ssb_get_private_key_work, _tf_ssb_get_private_key_after_work, work);
|
||||
|
||||
@@ -758,7 +766,7 @@ static JSValue _tf_ssb_appendMessageWithIdentity(JSContext* context, JSValueCons
|
||||
append_message_t* work = tf_malloc(sizeof(append_message_t) + user_length + 1);
|
||||
*work = (append_message_t) { .context = context, .message = JS_DupValue(context, argv[2]) };
|
||||
memcpy(work->user, user, user_length + 1);
|
||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
||||
tf_string_set(work->id, sizeof(work->id), id);
|
||||
|
||||
JS_FreeCString(context, id);
|
||||
JS_FreeCString(context, user);
|
||||
@@ -988,6 +996,8 @@ static void _tf_ssb_stored_connections_after_work(tf_ssb_t* ssb, int status, voi
|
||||
JS_SetPropertyStr(context, connection, "address", JS_NewString(context, work->connections[i].address));
|
||||
JS_SetPropertyStr(context, connection, "port", JS_NewInt32(context, work->connections[i].port));
|
||||
JS_SetPropertyStr(context, connection, "pubkey", JS_NewString(context, work->connections[i].pubkey));
|
||||
JS_SetPropertyStr(context, connection, "last_attempt", JS_NewInt64(context, work->connections[i].last_attempt));
|
||||
JS_SetPropertyStr(context, connection, "last_success", JS_NewInt64(context, work->connections[i].last_success));
|
||||
JS_SetPropertyUint32(context, result, i, connection);
|
||||
}
|
||||
tf_free(work->connections);
|
||||
|
147
src/ssb.rpc.c
@@ -134,7 +134,7 @@ static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags
|
||||
*work = (blobs_get_work_t) {
|
||||
.request_number = request_number,
|
||||
};
|
||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
||||
tf_string_set(work->id, sizeof(work->id), id);
|
||||
tf_ssb_connection_schedule_idle(connection, id, _tf_ssb_blobs_get_callback, work);
|
||||
|
||||
JS_FreeCString(context, id);
|
||||
@@ -184,7 +184,7 @@ static void _tf_ssb_rpc_blobs_has(tf_ssb_connection_t* connection, uint8_t flags
|
||||
*work = (blobs_has_work_t) {
|
||||
.request_number = request_number,
|
||||
};
|
||||
snprintf(work->id, sizeof(work->id), "%s", id_str);
|
||||
tf_string_set(work->id, sizeof(work->id), id_str);
|
||||
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_blobs_has_work, _tf_ssb_rpc_blobs_has_after_work, work);
|
||||
|
||||
JS_FreeCString(context, id_str);
|
||||
@@ -239,7 +239,7 @@ static void _tf_ssb_request_blob_wants_work(tf_ssb_connection_t* connection, voi
|
||||
{
|
||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
snprintf(work->out_id[work->out_id_count], sizeof(work->out_id[work->out_id_count]), "%s", (const char*)sqlite3_column_text(statement, 0));
|
||||
tf_string_set(work->out_id[work->out_id_count], sizeof(work->out_id[work->out_id_count]), (const char*)sqlite3_column_text(statement, 0));
|
||||
work->out_id_count++;
|
||||
}
|
||||
}
|
||||
@@ -270,7 +270,7 @@ static void _tf_ssb_request_blob_wants_after_work(tf_ssb_connection_t* connectio
|
||||
}
|
||||
if (work->out_id_count)
|
||||
{
|
||||
snprintf(blob_wants->last_id, sizeof(blob_wants->last_id), "%s", work->out_id[work->out_id_count - 1]);
|
||||
tf_string_set(blob_wants->last_id, sizeof(blob_wants->last_id), work->out_id[work->out_id_count - 1]);
|
||||
}
|
||||
}
|
||||
tf_free(work);
|
||||
@@ -349,11 +349,6 @@ static void _tf_ssb_rpc_tunnel_cleanup(tf_ssb_t* ssb, void* user_data)
|
||||
static void _tf_ssb_rpc_tunnel_connect(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
if (!tf_ssb_is_room(ssb))
|
||||
{
|
||||
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "tunnel.connect");
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* context = tf_ssb_connection_get_context(connection);
|
||||
JSValue arg_array = JS_GetPropertyStr(context, args, "args");
|
||||
@@ -364,54 +359,62 @@ static void _tf_ssb_rpc_tunnel_connect(tf_ssb_connection_t* connection, uint8_t
|
||||
|
||||
if (JS_IsUndefined(origin) && !JS_IsUndefined(portal) && !JS_IsUndefined(target))
|
||||
{
|
||||
const char* target_str = JS_ToCString(context, target);
|
||||
|
||||
tf_ssb_connection_t* target_connection = tf_ssb_connection_get(ssb, target_str);
|
||||
if (target_connection)
|
||||
if (!tf_ssb_is_room(ssb))
|
||||
{
|
||||
int32_t tunnel_request_number = tf_ssb_connection_next_request_number(target_connection);
|
||||
const char* portal_str = JS_ToCString(context, portal);
|
||||
|
||||
JSValue message = JS_NewObject(context);
|
||||
JSValue name = JS_NewArray(context);
|
||||
JS_SetPropertyUint32(context, name, 0, JS_NewString(context, "tunnel"));
|
||||
JS_SetPropertyUint32(context, name, 1, JS_NewString(context, "connect"));
|
||||
JS_SetPropertyStr(context, message, "name", name);
|
||||
JSValue arg_obj = JS_NewObject(context);
|
||||
char origin_str[k_id_base64_len] = "";
|
||||
tf_ssb_connection_get_id(connection, origin_str, sizeof(origin_str));
|
||||
JS_SetPropertyStr(context, arg_obj, "origin", JS_NewString(context, origin_str));
|
||||
JS_SetPropertyStr(context, arg_obj, "portal", JS_NewString(context, portal_str));
|
||||
JS_SetPropertyStr(context, arg_obj, "target", JS_NewString(context, target_str));
|
||||
JSValue arg_array = JS_NewArray(context);
|
||||
JS_SetPropertyUint32(context, arg_array, 0, arg_obj);
|
||||
JS_SetPropertyStr(context, message, "args", arg_array);
|
||||
JS_SetPropertyStr(context, message, "type", JS_NewString(context, "duplex"));
|
||||
|
||||
tf_ssb_connection_rpc_send_json(
|
||||
target_connection, k_ssb_rpc_flag_stream | k_ssb_rpc_flag_new_request, tunnel_request_number, "tunnel.connect", message, NULL, NULL, NULL);
|
||||
|
||||
tunnel_t* data0 = tf_malloc(sizeof(tunnel_t));
|
||||
*data0 = (tunnel_t) {
|
||||
.connection = target_connection,
|
||||
.request_number = tunnel_request_number,
|
||||
};
|
||||
tunnel_t* data1 = tf_malloc(sizeof(tunnel_t));
|
||||
*data1 = (tunnel_t) {
|
||||
.connection = connection,
|
||||
.request_number = -request_number,
|
||||
};
|
||||
tf_ssb_connection_add_request(connection, -request_number, "tunnel.connect", _tf_ssb_rpc_tunnel_callback, _tf_ssb_rpc_tunnel_cleanup, data0, target_connection);
|
||||
tf_ssb_connection_add_request(target_connection, tunnel_request_number, "tunnel.connect", _tf_ssb_rpc_tunnel_callback, _tf_ssb_rpc_tunnel_cleanup, data1, connection);
|
||||
|
||||
JS_FreeValue(context, message);
|
||||
JS_FreeCString(context, portal_str);
|
||||
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "tunnel.connect");
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_ssb_connection_rpc_send_error(connection, flags, -request_number, "Connection not found.");
|
||||
const char* target_str = JS_ToCString(context, target);
|
||||
|
||||
tf_ssb_connection_t* target_connection = tf_ssb_connection_get(ssb, target_str);
|
||||
if (target_connection)
|
||||
{
|
||||
int32_t tunnel_request_number = tf_ssb_connection_next_request_number(target_connection);
|
||||
const char* portal_str = JS_ToCString(context, portal);
|
||||
|
||||
JSValue message = JS_NewObject(context);
|
||||
JSValue name = JS_NewArray(context);
|
||||
JS_SetPropertyUint32(context, name, 0, JS_NewString(context, "tunnel"));
|
||||
JS_SetPropertyUint32(context, name, 1, JS_NewString(context, "connect"));
|
||||
JS_SetPropertyStr(context, message, "name", name);
|
||||
JSValue arg_obj = JS_NewObject(context);
|
||||
char origin_str[k_id_base64_len] = "";
|
||||
tf_ssb_connection_get_id(connection, origin_str, sizeof(origin_str));
|
||||
JS_SetPropertyStr(context, arg_obj, "origin", JS_NewString(context, origin_str));
|
||||
JS_SetPropertyStr(context, arg_obj, "portal", JS_NewString(context, portal_str));
|
||||
JS_SetPropertyStr(context, arg_obj, "target", JS_NewString(context, target_str));
|
||||
JSValue arg_array = JS_NewArray(context);
|
||||
JS_SetPropertyUint32(context, arg_array, 0, arg_obj);
|
||||
JS_SetPropertyStr(context, message, "args", arg_array);
|
||||
JS_SetPropertyStr(context, message, "type", JS_NewString(context, "duplex"));
|
||||
|
||||
tf_ssb_connection_rpc_send_json(
|
||||
target_connection, k_ssb_rpc_flag_stream | k_ssb_rpc_flag_new_request, tunnel_request_number, "tunnel.connect", message, NULL, NULL, NULL);
|
||||
|
||||
tunnel_t* data0 = tf_malloc(sizeof(tunnel_t));
|
||||
*data0 = (tunnel_t) {
|
||||
.connection = target_connection,
|
||||
.request_number = tunnel_request_number,
|
||||
};
|
||||
tunnel_t* data1 = tf_malloc(sizeof(tunnel_t));
|
||||
*data1 = (tunnel_t) {
|
||||
.connection = connection,
|
||||
.request_number = -request_number,
|
||||
};
|
||||
tf_ssb_connection_add_request(connection, -request_number, "tunnel.connect", _tf_ssb_rpc_tunnel_callback, _tf_ssb_rpc_tunnel_cleanup, data0, target_connection);
|
||||
tf_ssb_connection_add_request(
|
||||
target_connection, tunnel_request_number, "tunnel.connect", _tf_ssb_rpc_tunnel_callback, _tf_ssb_rpc_tunnel_cleanup, data1, connection);
|
||||
|
||||
JS_FreeValue(context, message);
|
||||
JS_FreeCString(context, portal_str);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_ssb_connection_rpc_send_error(connection, flags, -request_number, "Connection not found.");
|
||||
}
|
||||
JS_FreeCString(context, target_str);
|
||||
}
|
||||
JS_FreeCString(context, target_str);
|
||||
}
|
||||
else if (!JS_IsUndefined(origin) && !JS_IsUndefined(portal) && !JS_IsUndefined(target))
|
||||
{
|
||||
@@ -611,7 +614,7 @@ static void _tf_ssb_rpc_connection_blobs_get(tf_ssb_connection_t* connection, co
|
||||
{
|
||||
blobs_get_t* get = tf_malloc(sizeof(blobs_get_t) + size);
|
||||
*get = (blobs_get_t) { .ssb = tf_ssb_connection_get_ssb(connection), .connection = connection, .expected_size = size };
|
||||
snprintf(get->id, sizeof(get->id), "%s", blob_id);
|
||||
tf_string_set(get->id, sizeof(get->id), blob_id);
|
||||
memset(get->buffer, 0, size);
|
||||
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
@@ -713,7 +716,7 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
||||
{
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
||||
const char* blob_id = JS_AtomToCString(context, ptab[i].atom);
|
||||
JSPropertyDescriptor desc;
|
||||
JSValue key_value = JS_NULL;
|
||||
if (JS_GetOwnProperty(context, &desc, args, ptab[i].atom) == 1)
|
||||
@@ -722,7 +725,6 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
||||
JS_FreeValue(context, desc.setter);
|
||||
JS_FreeValue(context, desc.getter);
|
||||
}
|
||||
const char* blob_id = JS_ToCString(context, key);
|
||||
int64_t size = 0;
|
||||
JS_ToInt64(context, &size, key_value);
|
||||
if (--blob_wants->wants_sent == 0)
|
||||
@@ -736,18 +738,17 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
||||
.connection = connection,
|
||||
.size = size,
|
||||
};
|
||||
snprintf(work->blob_id, sizeof(work->blob_id), "%s", blob_id);
|
||||
tf_string_set(work->blob_id, sizeof(work->blob_id), blob_id);
|
||||
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_connection_blobs_create_wants_work, _tf_ssb_rpc_connection_blobs_create_wants_after_work, work);
|
||||
}
|
||||
else
|
||||
{
|
||||
blob_get_t* get = tf_malloc(sizeof(blob_get_t));
|
||||
*get = (blob_get_t) { .size = size };
|
||||
snprintf(get->id, sizeof(get->id), "%s", blob_id);
|
||||
tf_string_set(get->id, sizeof(get->id), blob_id);
|
||||
tf_ssb_connection_schedule_idle(connection, blob_id, _tf_ssb_rpc_connection_blobs_get_idle, get);
|
||||
}
|
||||
JS_FreeCString(context, blob_id);
|
||||
JS_FreeValue(context, key);
|
||||
JS_FreeValue(context, key_value);
|
||||
}
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
@@ -1008,7 +1009,7 @@ static void _tf_ssb_connection_send_history_stream(
|
||||
.live = live,
|
||||
.end_request = end_request,
|
||||
};
|
||||
snprintf(async->author, sizeof(async->author), "%s", author);
|
||||
tf_string_set(async->author, sizeof(async->author), author);
|
||||
tf_ssb_connection_schedule_idle(connection, author, _tf_ssb_connection_send_history_stream_callback, async);
|
||||
}
|
||||
}
|
||||
@@ -1337,7 +1338,7 @@ static void _tf_ssb_rpc_invite_use_callback(
|
||||
.ssb = ssb,
|
||||
.port = tf_ssb_connection_get_port(connection),
|
||||
};
|
||||
snprintf(work->host, sizeof(work->host), "%s", tf_ssb_connection_get_host(connection));
|
||||
tf_string_set(work->host, sizeof(work->host), tf_ssb_connection_get_host(connection));
|
||||
tf_ssb_whoami(ssb, work->author, sizeof(work->author));
|
||||
tf_ssb_get_private_key(ssb, work->private_key, sizeof(work->private_key));
|
||||
tf_ssb_connection_get_id(connection, work->pub, sizeof(work->pub));
|
||||
@@ -1463,23 +1464,6 @@ static void _tf_ssb_rpc_broadcasts_changed_callback(tf_ssb_t* ssb, void* user_da
|
||||
tf_ssb_visit_broadcasts(ssb, _tf_ssb_rpc_broadcasts_changed_visit, ssb);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_checkpoint(tf_ssb_t* ssb)
|
||||
{
|
||||
int64_t checkpoint_start_ms = uv_hrtime();
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
int log = 0;
|
||||
int checkpointed = 0;
|
||||
if (sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_PASSIVE, &log, &checkpointed) == SQLITE_OK)
|
||||
{
|
||||
tf_printf("Checkpointed %d frames in %d ms. Log is now %d frames.\n", checkpointed, (int)((uv_hrtime() - checkpoint_start_ms) / 1000000LL), log);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("Checkpoint: %s.\n", sqlite3_errmsg(db));
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
}
|
||||
|
||||
typedef struct _delete_t
|
||||
{
|
||||
int deleted;
|
||||
@@ -1495,7 +1479,6 @@ static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* user_data)
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
if (age <= 0)
|
||||
{
|
||||
_tf_ssb_rpc_checkpoint(ssb);
|
||||
return;
|
||||
}
|
||||
int64_t start_ns = uv_hrtime();
|
||||
@@ -1546,7 +1529,6 @@ static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* user_data)
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
delete->duration_ms = (uv_hrtime() - start_ns) / 1000000LL;
|
||||
tf_printf("Deleted %d blobs in %d ms.\n", deleted, (int)delete->duration_ms);
|
||||
_tf_ssb_rpc_checkpoint(ssb);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_delete_blobs_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||
@@ -1632,7 +1614,6 @@ static void _tf_ssb_rpc_delete_feeds_work(tf_ssb_t* ssb, void* user_data)
|
||||
|
||||
delete->duration_ms = (uv_hrtime() - start_ns) / 1000000LL;
|
||||
tf_printf("Deleted %d message in %d ms.\n", delete->deleted, (int)delete->duration_ms);
|
||||
_tf_ssb_rpc_checkpoint(ssb);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_delete_feeds_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||
@@ -1736,7 +1717,7 @@ static void _tf_ssb_rpc_peers_exchange_internal(
|
||||
{
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
||||
const char* connection = JS_AtomToCString(context, ptab[i].atom);
|
||||
JSPropertyDescriptor desc;
|
||||
JSValue key_value = JS_NULL;
|
||||
if (JS_GetOwnProperty(context, &desc, args, ptab[i].atom) == 1)
|
||||
@@ -1745,12 +1726,10 @@ static void _tf_ssb_rpc_peers_exchange_internal(
|
||||
JS_FreeValue(context, desc.setter);
|
||||
JS_FreeValue(context, desc.getter);
|
||||
}
|
||||
const char* connection = JS_ToCString(context, key);
|
||||
int64_t timestamp = 0;
|
||||
JS_ToInt64(context, ×tamp, key_value);
|
||||
/* ADD BROADCAST connection: timestamp */
|
||||
JS_FreeCString(context, connection);
|
||||
JS_FreeValue(context, key);
|
||||
JS_FreeValue(context, key_value);
|
||||
}
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
@@ -1895,7 +1874,7 @@ static void _tf_ssb_rpc_invite_use(tf_ssb_connection_t* connection, uint8_t flag
|
||||
JSValue feed = JS_GetPropertyStr(context, object, "feed");
|
||||
tf_ssb_connection_get_id(connection, work->invite_public_key, sizeof(work->invite_public_key));
|
||||
const char* id = JS_ToCString(context, feed);
|
||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
||||
tf_string_set(work->id, sizeof(work->id), id);
|
||||
JS_FreeCString(context, id);
|
||||
JS_FreeValue(context, feed);
|
||||
JS_FreeValue(context, object);
|
||||
|
@@ -811,7 +811,7 @@ static void _break_in_a_bit(tf_ssb_t* ssb, tf_ssb_connection_t* connection, cons
|
||||
.data = data,
|
||||
},
|
||||
};
|
||||
snprintf(data->id, sizeof(data->id), "%s", id);
|
||||
tf_string_set(data->id, sizeof(data->id), id);
|
||||
uv_timer_init(tf_ssb_get_loop(ssb), &data->timer);
|
||||
uv_timer_start(&data->timer, _close_callback, 3000, 0);
|
||||
}
|
||||
@@ -1154,7 +1154,7 @@ void tf_ssb_test_replicate(const tf_test_options_t* options)
|
||||
JSValue obj = JS_NewObject(context1);
|
||||
JS_SetPropertyStr(context1, obj, "type", JS_NewString(context1, "contact"));
|
||||
char self[k_id_base64_len];
|
||||
snprintf(self, sizeof(self), "%s", id1);
|
||||
tf_string_set(self, sizeof(self), id1);
|
||||
char contact[k_id_base64_len];
|
||||
snprintf(contact, sizeof(contact), "@%s", public[0]);
|
||||
JS_SetPropertyStr(context1, obj, "contact", JS_NewString(context1, contact));
|
||||
@@ -1377,6 +1377,8 @@ void tf_ssb_test_invite(const tf_test_options_t* options)
|
||||
while (count0 != 3 || count1 != 3)
|
||||
{
|
||||
uv_run(&loop, UV_RUN_ONCE);
|
||||
|
||||
tf_printf("count0=%d count1=%d\n", count0, count1);
|
||||
}
|
||||
tf_ssb_set_main_thread(ssb0, false);
|
||||
tf_ssb_set_main_thread(ssb1, false);
|
||||
|
81
src/task.c
@@ -88,12 +88,6 @@ typedef struct _promise_stack_t
|
||||
int cstack_count;
|
||||
} promise_stack_t;
|
||||
|
||||
typedef struct _hitch_t
|
||||
{
|
||||
char name[256];
|
||||
uint64_t duration_ns;
|
||||
} hitch_t;
|
||||
|
||||
typedef struct _timeout_t timeout_t;
|
||||
|
||||
typedef struct _timeout_t
|
||||
@@ -170,8 +164,6 @@ typedef struct _tf_task_t
|
||||
|
||||
timeout_t* timeouts;
|
||||
|
||||
hitch_t hitches[32];
|
||||
|
||||
uint64_t last_gc_ns;
|
||||
int64_t last_gc_duration_ns;
|
||||
} tf_task_t;
|
||||
@@ -930,28 +922,6 @@ char* tf_task_get_debug(tf_task_t* task)
|
||||
return result;
|
||||
}
|
||||
|
||||
char* tf_task_get_hitches(tf_task_t* task)
|
||||
{
|
||||
JSContext* context = task->_context;
|
||||
tf_trace_begin(task->_trace, __func__);
|
||||
JSValue object = JS_NewObject(context);
|
||||
for (int i = 0; i < tf_countof(task->hitches); i++)
|
||||
{
|
||||
if (*task->hitches[i].name)
|
||||
{
|
||||
JS_SetPropertyStr(context, object, task->hitches[i].name, JS_NewFloat64(context, task->hitches[i].duration_ns / 1e9));
|
||||
}
|
||||
}
|
||||
JSValue json = JS_JSONStringify(context, object, JS_NULL, JS_NewInt32(context, 2));
|
||||
const char* string = JS_ToCString(context, json);
|
||||
char* result = tf_strdup(string);
|
||||
JS_FreeCString(context, string);
|
||||
JS_FreeValue(context, json);
|
||||
JS_FreeValue(context, object);
|
||||
tf_trace_end(task->_trace);
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue _tf_task_getFile(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_task_t* task = JS_GetContextOpaque(context);
|
||||
@@ -1196,7 +1166,7 @@ static JSValue _tf_task_executeSource(tf_task_t* task, const char* source, const
|
||||
JSValue result = JS_Eval(task->_context, source, strlen(source), name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_ASYNC);
|
||||
if (!*task->_scriptName)
|
||||
{
|
||||
snprintf(task->_scriptName, sizeof(task->_scriptName), "%s", name);
|
||||
tf_string_set(task->_scriptName, sizeof(task->_scriptName), name);
|
||||
}
|
||||
tf_trace_end(task->_trace);
|
||||
return result;
|
||||
@@ -1301,19 +1271,19 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
||||
JS_FreeValue(task->_context, error);
|
||||
}
|
||||
|
||||
promiseid_t promiseId;
|
||||
promiseid_t promise_id;
|
||||
do
|
||||
{
|
||||
promiseId = task->_nextPromise++;
|
||||
} while (_tf_task_find_promise(task, promiseId) || !promiseId);
|
||||
promise_id = task->_nextPromise++;
|
||||
} while (_tf_task_find_promise(task, promise_id) || !promise_id);
|
||||
|
||||
promise_t promise = {
|
||||
.id = promiseId,
|
||||
.id = promise_id,
|
||||
.values = { JS_NULL, JS_NULL },
|
||||
.stack_hash = stack_hash,
|
||||
};
|
||||
JSValue result = JS_NewPromiseCapability(task->_context, promise.values);
|
||||
int index = tf_util_insert_index((void*)(intptr_t)promiseId, task->_promises, task->_promise_count, sizeof(promise_t), _promise_compare);
|
||||
int index = tf_util_insert_index((void*)(intptr_t)promise_id, task->_promises, task->_promise_count, sizeof(promise_t), _promise_compare);
|
||||
task->_promises = tf_resize_vec(task->_promises, sizeof(promise_t) * (task->_promise_count + 1));
|
||||
if (task->_promise_count - index)
|
||||
{
|
||||
@@ -1321,7 +1291,12 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
||||
}
|
||||
task->_promises[index] = promise;
|
||||
task->_promise_count++;
|
||||
*out_promise = promiseId;
|
||||
*out_promise = promise_id;
|
||||
|
||||
if (task->_shutting_down)
|
||||
{
|
||||
tf_task_reject_promise(task, promise_id, JS_ThrowInternalError(task->_context, "Shutting down"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1383,7 +1358,7 @@ static void _promise_release_for_task(tf_task_t* task, taskid_t task_id)
|
||||
const promise_t* promise = &task->_promises[i];
|
||||
if (promise->task == task_id)
|
||||
{
|
||||
tf_task_reject_promise(task, promise->id, JS_ThrowInternalError(task->_context, "Task is gone."));
|
||||
tf_task_reject_promise(task, promise->id, JS_ThrowInternalError(task->_context, "Task is gone"));
|
||||
more = true;
|
||||
}
|
||||
}
|
||||
@@ -1661,24 +1636,6 @@ static void _tf_task_trace_to_parent(tf_trace_t* trace, const char* buffer, size
|
||||
tf_packetstream_send(tf_taskstub_get_stream(task->_parent), kTaskTrace, buffer, size);
|
||||
}
|
||||
|
||||
static void _tf_task_record_hitch(const char* name, uint64_t duration_ns, void* user_data)
|
||||
{
|
||||
tf_task_t* task = user_data;
|
||||
for (int i = 0; i < tf_countof(task->hitches); i++)
|
||||
{
|
||||
if (duration_ns > task->hitches[i].duration_ns)
|
||||
{
|
||||
if (i + 1 < tf_countof(task->hitches))
|
||||
{
|
||||
memmove(task->hitches + i + 1, task->hitches + i, sizeof(hitch_t) * (tf_countof(task->hitches) - i - 1));
|
||||
}
|
||||
snprintf(task->hitches[i].name, sizeof(task->hitches[i].name), "%s", name);
|
||||
task->hitches[i].duration_ns = duration_ns;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tf_task_activate(tf_task_t* task)
|
||||
{
|
||||
assert(!task->_activated);
|
||||
@@ -1712,7 +1669,6 @@ void tf_task_activate(tf_task_t* task)
|
||||
tf_ssb_set_trace(task->_ssb, task->_trace);
|
||||
tf_ssb_register(context, task->_ssb);
|
||||
tf_api_register(context);
|
||||
tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task);
|
||||
|
||||
if (task->_args)
|
||||
{
|
||||
@@ -1819,6 +1775,11 @@ JSValue tf_taskstub_kill(tf_taskstub_t* stub);
|
||||
|
||||
void tf_task_destroy(tf_task_t* task)
|
||||
{
|
||||
if (!task->_shutting_down)
|
||||
{
|
||||
tf_printf("tf_task_destroy\n");
|
||||
}
|
||||
|
||||
task->_shutting_down = true;
|
||||
|
||||
while (task->_children)
|
||||
@@ -2005,7 +1966,7 @@ void tf_task_set_ssb_network_key(tf_task_t* task, const char* network_key)
|
||||
|
||||
void tf_task_set_db_path(tf_task_t* task, const char* db_path)
|
||||
{
|
||||
snprintf(task->_db_path, sizeof(task->_db_path), "%s", db_path);
|
||||
tf_string_set(task->_db_path, sizeof(task->_db_path), db_path);
|
||||
}
|
||||
|
||||
void tf_task_set_zip_path(tf_task_t* task, const char* zip_path)
|
||||
@@ -2015,7 +1976,7 @@ void tf_task_set_zip_path(tf_task_t* task, const char* zip_path)
|
||||
unzClose(task->_zip);
|
||||
task->_zip = NULL;
|
||||
}
|
||||
snprintf(task->_zip_path, sizeof(task->_zip_path), "%s", zip_path);
|
||||
tf_string_set(task->_zip_path, sizeof(task->_zip_path), zip_path);
|
||||
if (zip_path)
|
||||
{
|
||||
task->_zip = unzOpen(zip_path);
|
||||
@@ -2025,7 +1986,7 @@ void tf_task_set_zip_path(tf_task_t* task, const char* zip_path)
|
||||
|
||||
void tf_task_set_root_path(tf_task_t* task, const char* path)
|
||||
{
|
||||
snprintf(task->_root_path, sizeof(task->_root_path), "%s", path ? path : "");
|
||||
tf_string_set(task->_root_path, sizeof(task->_root_path), path ? path : "");
|
||||
}
|
||||
|
||||
const char* tf_task_get_zip_path(tf_task_t* task)
|
||||
|
@@ -319,13 +319,6 @@ bool tf_task_send_error_to_parent(tf_task_t* task, JSValue error);
|
||||
*/
|
||||
char* tf_task_get_debug(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Get a report of hitches that occurred.
|
||||
** @param task The task.
|
||||
** @return A JSON report of recent hitches that must be freed with tf_free().
|
||||
*/
|
||||
char* tf_task_get_hitches(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** A callback used to start an Android service.
|
||||
** @param pipe_fd A file descriptor with which to communicate with the invoking
|
||||
|
@@ -104,7 +104,7 @@ void tf_trace_destroy(tf_trace_t* trace)
|
||||
|
||||
void tf_trace_set_process_name(tf_trace_t* trace, const char* name)
|
||||
{
|
||||
snprintf(trace->process_name, sizeof(trace->process_name), "%s", name);
|
||||
tf_string_set(trace->process_name, sizeof(trace->process_name), name);
|
||||
}
|
||||
|
||||
void tf_trace_raw(tf_trace_t* trace, const char* buffer, size_t size)
|
||||
|
@@ -500,11 +500,13 @@ void tf_util_document_settings(const char* line_prefix)
|
||||
JSValue tf_util_new_uint8_array(JSContext* context, const uint8_t* data, size_t size)
|
||||
{
|
||||
JSValue array_buffer = JS_NewArrayBufferCopy(context, data, size);
|
||||
JSValue global = JS_GetGlobalObject(context);
|
||||
JSValue constructor = JS_GetPropertyStr(context, global, "Uint8Array");
|
||||
JSValue result = JS_CallConstructor(context, constructor, 1, &array_buffer);
|
||||
JS_FreeValue(context, constructor);
|
||||
JS_FreeValue(context, global);
|
||||
JSValue args[] =
|
||||
{
|
||||
array_buffer,
|
||||
JS_NewInt64(context, 0),
|
||||
JS_NewInt64(context, size),
|
||||
};
|
||||
JSValue result = JS_NewTypedArray(context, tf_countof(args), args, JS_TYPED_ARRAY_UINT8C);
|
||||
JS_FreeValue(context, array_buffer);
|
||||
return result;
|
||||
}
|
||||
@@ -612,7 +614,7 @@ static int _tf_util_backtrace_single_callback(void* data, uintptr_t pc, const ch
|
||||
{
|
||||
char** stack = data;
|
||||
char line[256];
|
||||
int length = snprintf(line, sizeof(line), "%s", function);
|
||||
int length = (int)tf_string_set(line, sizeof(line), function);
|
||||
int current = *stack ? strlen(*stack) : 0;
|
||||
*stack = tf_resize_vec(*stack, current + length + 1);
|
||||
memcpy(*stack + current, line, length + 1);
|
||||
@@ -700,3 +702,18 @@ uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t tf_string_set(char* buffer, size_t size, const char* string)
|
||||
{
|
||||
size_t length = string ? strlen(string) : 0;
|
||||
length = tf_min(length, size - 1);
|
||||
if (size)
|
||||
{
|
||||
if (length)
|
||||
{
|
||||
memcpy(buffer, string, length);
|
||||
}
|
||||
buffer[length] = 0;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
@@ -233,4 +233,13 @@ bool tf_util_is_mobile();
|
||||
*/
|
||||
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start);
|
||||
|
||||
/**
|
||||
** Populate a string buffer, truncating if necessary.
|
||||
** @param buffer The buffer.
|
||||
** @param size The size of the buffer.
|
||||
** @param string The value to set.
|
||||
** @return The number of bytes set, not including the NULL terminator.
|
||||
*/
|
||||
size_t tf_string_set(char* buffer, size_t size, const char* string);
|
||||
|
||||
/** @} */
|
||||
|
@@ -1,2 +1,2 @@
|
||||
#define VERSION_NUMBER "0.0.31"
|
||||
#define VERSION_NUMBER "0.0.32-wip"
|
||||
#define VERSION_NAME "This program kills fascists."
|
||||
|
@@ -124,7 +124,7 @@ try:
|
||||
select(driver, ['tf-navigation', 'shadow_root', '#close_error'], ('click',))
|
||||
select(driver, ['tf-navigation', 'shadow_root', '=edit'], ('click',))
|
||||
select(driver, ['#editor', '.cm-content'], ('click',))
|
||||
select(driver, ['#editor', '.cm-content'], ('send_keys', 'app.setDocument(\n\t"<div id=\'test-div\'>Hello, world!</div>"\n);'))
|
||||
select(driver, ['#editor', '.cm-content'], ('send_keys', 'app.setDocument(\n\t`<div id=\'test-div\' style=\'color: white; font-size: xx-large\'>\n\t\tHello, world!\n\t</div>`\n);'))
|
||||
select(driver, ['#save'], ('click',))
|
||||
|
||||
select(driver, ['#document', 'frame', '#test-div'])
|
||||
|