@volter/teams 0.2.37 → 0.2.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/teams",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.39",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Volter Teams, the neutral team plane: machine identity and daemon core, the local door and server link, admission from machine grants, product doors, and the Teams server's neutral routes (company RFC 0008).",
|
|
6
6
|
"license": "MIT",
|
package/server/core.mjs
CHANGED
|
@@ -64,7 +64,7 @@ export async function maintain(api,{budgetMs=MAINTAIN_BUDGET_MS}={}){const now=a
|
|
|
64
64
|
const failed=[];let more=false;const counts={};
|
|
65
65
|
const left=()=>Date.now()<deadline;
|
|
66
66
|
const q=(sql,params=[])=>api.store.query(sql,params),one=(sql,params=[])=>api.store.one(sql,params),tx=(statements)=>api.store.transaction(statements);
|
|
67
|
-
const step=async(name,work)=>{if(!left()){more=true;return;}try{await work();}catch(error){failed.push(name);console.error(`maintain ${name}: ${error.message}`);}};
|
|
67
|
+
const step=async(name,work)=>{if(!left()){more=true;return;}try{await (api.work?api.work(`maintain.${name}`,work):work());}catch(error){failed.push(name);console.error(`maintain ${name}: ${error.message}`);}};
|
|
68
68
|
const count=(name,n)=>{if(n>0)counts[name]=(counts[name]??0)+n;};
|
|
69
69
|
// Deletes go first, each in a transaction of its own: on a full database an insert fails, and a run that begins with
|
|
70
70
|
// one never reaches the deletes that would make room.
|
|
@@ -4,7 +4,7 @@ import { writeSse, holdStream } from './events.mjs';
|
|
|
4
4
|
export function machineTransition(api, team, machine, linked) {
|
|
5
5
|
// Serialize the read/change/event across asynchronous store calls. A replaced
|
|
6
6
|
// link remains linked; only the current link's close records a departure.
|
|
7
|
-
const
|
|
7
|
+
const work = async () => {
|
|
8
8
|
const row = await api.store.one('SELECT name,revoked_at FROM machines WHERE team_id=? AND id=?', [team, machine]);
|
|
9
9
|
if (!row) return;
|
|
10
10
|
const present = linked ?? api.machineLinks?.has(`${team}\0${machine}`);
|
|
@@ -17,7 +17,8 @@ export function machineTransition(api, team, machine, linked) {
|
|
|
17
17
|
{ sql: 'INSERT INTO machine_presence_events(team_id,machine_id,name,linked,created_at) VALUES (?,?,?,?,?)', params: [team,machine,row.name,value,at] },
|
|
18
18
|
]);
|
|
19
19
|
api.events.publish(team, { type: 'machine_transition' });
|
|
20
|
-
}
|
|
20
|
+
};
|
|
21
|
+
const next = (api.machineTransitionWork ?? Promise.resolve()).then(() => api.work ? api.work('machineTransition', work) : work());
|
|
21
22
|
api.machineTransitionWork = next.catch(error => { console.error(`machine transition ${machine}: ${error.message}`); });
|
|
22
23
|
return next;
|
|
23
24
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Capture prefixes are server-owned SHA-256 continuations. Preparing an old session
|
|
2
|
+
// is resumable; a request never reopens its entire transcript. No message body is copied.
|
|
3
|
+
export default `
|
|
4
|
+
CREATE TABLE capture_prefix_state (
|
|
5
|
+
team_id TEXT NOT NULL, session_id TEXT NOT NULL, source_id TEXT NOT NULL,
|
|
6
|
+
revision INTEGER NOT NULL, generation TEXT NOT NULL,
|
|
7
|
+
count INTEGER NOT NULL, target_count INTEGER NOT NULL, sha_state TEXT NOT NULL,
|
|
8
|
+
checkpoint_count INTEGER NOT NULL, checkpoint_bytes INTEGER NOT NULL,
|
|
9
|
+
invalid INTEGER NOT NULL DEFAULT 0,
|
|
10
|
+
PRIMARY KEY(team_id,session_id,source_id)
|
|
11
|
+
) STRICT;
|
|
12
|
+
CREATE TABLE capture_prefix_checkpoints (
|
|
13
|
+
team_id TEXT NOT NULL, session_id TEXT NOT NULL, source_id TEXT NOT NULL,
|
|
14
|
+
generation TEXT NOT NULL, count INTEGER NOT NULL, digest TEXT NOT NULL, sha_state TEXT NOT NULL,
|
|
15
|
+
PRIMARY KEY(team_id,session_id,source_id,generation,count)
|
|
16
|
+
) STRICT;
|
|
17
|
+
CREATE TABLE capture_prefix_keys (
|
|
18
|
+
team_id TEXT NOT NULL, session_id TEXT NOT NULL, source_id TEXT NOT NULL,
|
|
19
|
+
generation TEXT NOT NULL, native_key TEXT NOT NULL, ordinal INTEGER NOT NULL,
|
|
20
|
+
PRIMARY KEY(team_id,session_id,source_id,generation,native_key)
|
|
21
|
+
) STRICT;
|
|
22
|
+
CREATE INDEX capture_prefix_keys_ordinal ON capture_prefix_keys(team_id,session_id,source_id,generation,ordinal);
|
|
23
|
+
CREATE TABLE capture_prefix_garbage (
|
|
24
|
+
team_id TEXT NOT NULL, session_id TEXT NOT NULL, source_id TEXT NOT NULL, generation TEXT NOT NULL,
|
|
25
|
+
PRIMARY KEY(team_id,session_id,source_id,generation)
|
|
26
|
+
) STRICT;
|
|
27
|
+
CREATE TABLE capture_prefix_warm_queue (
|
|
28
|
+
ticket INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
+
team_id TEXT NOT NULL, session_id TEXT NOT NULL, source_id TEXT NOT NULL,
|
|
30
|
+
UNIQUE(team_id,session_id,source_id)
|
|
31
|
+
) STRICT;
|
|
32
|
+
`;
|
|
@@ -24,7 +24,8 @@ import m022 from './022.mjs';
|
|
|
24
24
|
import m023 from './023.mjs';
|
|
25
25
|
import m024 from './024.mjs';
|
|
26
26
|
import m025 from './025.mjs';
|
|
27
|
-
|
|
27
|
+
import m026 from './026.mjs';
|
|
28
|
+
export const MIGRATIONS = [m001, m002, m003, m004, m005, m006, m007, m008, m009, m010, m011, m012, m013, m014, m015, m016, m017, m018, m019, m020, m021, m022, m023, m024, m025, m026];
|
|
28
29
|
/** A message row's `until_revision` while its session's current version still holds it (schema 9). */
|
|
29
30
|
export const OPEN_REVISION = Number.MAX_SAFE_INTEGER;
|
|
30
31
|
/** SQL: message row `m` belongs to the version at revision ?1 (bind the revision twice). */
|
|
@@ -132,6 +132,9 @@ export async function publishedEventRoute(ctx) {
|
|
|
132
132
|
for(const session of new Set(body.sessions)){
|
|
133
133
|
const natives=(await api.store.query("SELECT DISTINCT json_extract(descriptor_json,'$.native_session_id') AS native FROM bindings WHERE team_id=? AND session_id=?",[auth.team_id,session])).map(row=>row.native).filter(Boolean);
|
|
134
134
|
const statements=[];
|
|
135
|
+
// Purging text also removes every continuation buffer that can contain
|
|
136
|
+
// its last SHA block, including retired generations. Never retain a redacted byte there.
|
|
137
|
+
for(const table of ['capture_prefix_state','capture_prefix_checkpoints','capture_prefix_keys','capture_prefix_garbage','capture_prefix_warm_queue'])statements.push(stmt(`DELETE FROM ${table} WHERE team_id=? AND session_id=?`,auth.team_id,session));
|
|
135
138
|
for(const value of body.values){
|
|
136
139
|
statements.push(stmt('UPDATE messages SET value_json=replace(value_json,?,?),search_text=replace(search_text,?,?) WHERE team_id=? AND session_id=? AND (instr(value_json,?)>0 OR instr(search_text,?)>0)',value,marker,value,marker,auth.team_id,session,value,value));
|
|
137
140
|
if(natives.length)statements.push(stmt(`UPDATE capture_parts SET payload_json=replace(payload_json,?,?) WHERE team_id=? AND instr(payload_json,?)>0 AND capture_id IN (SELECT id FROM captures WHERE team_id=? AND native_id IN (${natives.map(()=>'?').join(',')}))`,value,marker,auth.team_id,value,auth.team_id,...natives));
|