or3-provider-sqlite 0.0.9 → 0.0.11
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/README.md +50 -5
- package/dist/module.json +1 -1
- package/dist/runtime/server/admin/stores/sqlite-store.js +16 -0
- package/dist/runtime/server/background-jobs/sqlite-provider.d.ts +10 -1
- package/dist/runtime/server/background-jobs/sqlite-provider.js +316 -18
- package/dist/runtime/server/db/migrate.js +10 -2
- package/dist/runtime/server/db/migrations/018_background_admission_cancels.d.ts +7 -0
- package/dist/runtime/server/db/migrations/018_background_admission_cancels.js +18 -0
- package/dist/runtime/server/db/migrations/019_background_generation_history.d.ts +7 -0
- package/dist/runtime/server/db/migrations/019_background_generation_history.js +25 -0
- package/dist/runtime/server/db/migrations/020_background_generation_receipts.d.ts +4 -0
- package/dist/runtime/server/db/migrations/020_background_generation_receipts.js +19 -0
- package/dist/runtime/server/db/schema.d.ts +15 -0
- package/dist/runtime/server/plugin-connections/sqlite-connection-store.d.ts +7 -0
- package/dist/runtime/server/plugin-connections/sqlite-connection-store.js +209 -0
- package/dist/runtime/server/plugins/register.js +13 -0
- package/dist/runtime/server/sync/history-gc-policy.d.ts +8 -0
- package/dist/runtime/server/sync/history-gc-policy.js +7 -0
- package/dist/runtime/server/sync/sqlite-sync-gateway-adapter.d.ts +13 -1
- package/dist/runtime/server/sync/sqlite-sync-gateway-adapter.js +722 -96
- package/package.json +1 -1
|
@@ -16,6 +16,9 @@ import * as m014 from "./migrations/014_connect_retention_activation.js";
|
|
|
16
16
|
import * as m015 from "./migrations/015_rate_limits.js";
|
|
17
17
|
import * as m016 from "./migrations/016_connect_runtime_binding.js";
|
|
18
18
|
import * as m017 from "./migrations/017_background_jobs.js";
|
|
19
|
+
import * as m018 from "./migrations/018_background_admission_cancels.js";
|
|
20
|
+
import * as m019 from "./migrations/019_background_generation_history.js";
|
|
21
|
+
import * as m020 from "./migrations/020_background_generation_receipts.js";
|
|
19
22
|
const REQUIRED_SCHEMA_TABLES = [
|
|
20
23
|
"users",
|
|
21
24
|
"auth_accounts",
|
|
@@ -41,7 +44,9 @@ const REQUIRED_SCHEMA_TABLES = [
|
|
|
41
44
|
"connect_device_authorizations",
|
|
42
45
|
"connect_environments",
|
|
43
46
|
"rate_limits",
|
|
44
|
-
"background_jobs"
|
|
47
|
+
"background_jobs",
|
|
48
|
+
"background_admission_cancels",
|
|
49
|
+
"background_generation_receipts"
|
|
45
50
|
];
|
|
46
51
|
const migrations = {
|
|
47
52
|
"001_init": m001,
|
|
@@ -60,7 +65,10 @@ const migrations = {
|
|
|
60
65
|
"014_connect_retention_activation": m014,
|
|
61
66
|
"015_rate_limits": m015,
|
|
62
67
|
"016_connect_runtime_binding": m016,
|
|
63
|
-
"017_background_jobs": m017
|
|
68
|
+
"017_background_jobs": m017,
|
|
69
|
+
"018_background_admission_cancels": m018,
|
|
70
|
+
"019_background_generation_history": m019,
|
|
71
|
+
"020_background_generation_receipts": m020
|
|
64
72
|
};
|
|
65
73
|
class StaticMigrationProvider {
|
|
66
74
|
async getMigrations() {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Durable admission cancellations. Creation and cancellation both check this
|
|
4
|
+
* table so a Stop that arrives before the job row commits cannot launch work.
|
|
5
|
+
*/
|
|
6
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
7
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
2
|
+
export async function up(db) {
|
|
3
|
+
await sql`
|
|
4
|
+
CREATE TABLE IF NOT EXISTS background_admission_cancels (
|
|
5
|
+
user_id TEXT NOT NULL,
|
|
6
|
+
admission_id TEXT NOT NULL,
|
|
7
|
+
expires_at INTEGER NOT NULL,
|
|
8
|
+
PRIMARY KEY (user_id, admission_id)
|
|
9
|
+
)
|
|
10
|
+
`.execute(db);
|
|
11
|
+
await sql`
|
|
12
|
+
CREATE INDEX IF NOT EXISTS background_admission_cancels_expiry
|
|
13
|
+
ON background_admission_cancels(expires_at)
|
|
14
|
+
`.execute(db);
|
|
15
|
+
}
|
|
16
|
+
export async function down(db) {
|
|
17
|
+
await sql`DROP TABLE IF EXISTS background_admission_cancels`.execute(db);
|
|
18
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Generation identity, durable history phase, and accumulated reasoning on
|
|
4
|
+
* background jobs. Reasoning is kept distinct from request reasoning config.
|
|
5
|
+
*/
|
|
6
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
7
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
2
|
+
export async function up(db) {
|
|
3
|
+
await sql`
|
|
4
|
+
ALTER TABLE background_jobs
|
|
5
|
+
ADD COLUMN reasoning TEXT NOT NULL DEFAULT ''
|
|
6
|
+
`.execute(db);
|
|
7
|
+
await sql`
|
|
8
|
+
ALTER TABLE background_jobs
|
|
9
|
+
ADD COLUMN generation_id TEXT
|
|
10
|
+
`.execute(db);
|
|
11
|
+
await sql`
|
|
12
|
+
ALTER TABLE background_jobs
|
|
13
|
+
ADD COLUMN history_phase TEXT
|
|
14
|
+
`.execute(db);
|
|
15
|
+
await sql`
|
|
16
|
+
ALTER TABLE background_jobs
|
|
17
|
+
ADD COLUMN sync_provider_id TEXT
|
|
18
|
+
`.execute(db);
|
|
19
|
+
}
|
|
20
|
+
export async function down(db) {
|
|
21
|
+
await sql`ALTER TABLE background_jobs DROP COLUMN sync_provider_id`.execute(db);
|
|
22
|
+
await sql`ALTER TABLE background_jobs DROP COLUMN history_phase`.execute(db);
|
|
23
|
+
await sql`ALTER TABLE background_jobs DROP COLUMN generation_id`.execute(db);
|
|
24
|
+
await sql`ALTER TABLE background_jobs DROP COLUMN reasoning`.execute(db);
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
2
|
+
export async function up(db) {
|
|
3
|
+
await sql`
|
|
4
|
+
CREATE TABLE background_generation_receipts (
|
|
5
|
+
id TEXT PRIMARY KEY,
|
|
6
|
+
workspace_id TEXT NOT NULL,
|
|
7
|
+
generation_id TEXT NOT NULL,
|
|
8
|
+
stage TEXT NOT NULL CHECK(stage IN ('admission', 'finalization')),
|
|
9
|
+
fingerprint TEXT NOT NULL,
|
|
10
|
+
outcome TEXT NOT NULL,
|
|
11
|
+
server_version INTEGER,
|
|
12
|
+
created_at INTEGER NOT NULL,
|
|
13
|
+
UNIQUE(workspace_id, generation_id, stage)
|
|
14
|
+
)
|
|
15
|
+
`.execute(db);
|
|
16
|
+
}
|
|
17
|
+
export async function down(db) {
|
|
18
|
+
await sql`DROP TABLE IF EXISTS background_generation_receipts`.execute(db);
|
|
19
|
+
}
|
|
@@ -197,6 +197,10 @@ export interface BackgroundJobsTable {
|
|
|
197
197
|
kind: string | null;
|
|
198
198
|
status: 'streaming' | 'complete' | 'error' | 'aborted';
|
|
199
199
|
content: string;
|
|
200
|
+
reasoning: string;
|
|
201
|
+
generation_id: string | null;
|
|
202
|
+
history_phase: string | null;
|
|
203
|
+
sync_provider_id: string | null;
|
|
200
204
|
chunks_received: number;
|
|
201
205
|
started_at: number;
|
|
202
206
|
last_activity_at: number;
|
|
@@ -210,6 +214,16 @@ export interface BackgroundJobsTable {
|
|
|
210
214
|
lease_expires_at: number | null;
|
|
211
215
|
attempts: number;
|
|
212
216
|
}
|
|
217
|
+
export interface BackgroundGenerationReceiptsTable {
|
|
218
|
+
id: string;
|
|
219
|
+
workspace_id: string;
|
|
220
|
+
generation_id: string;
|
|
221
|
+
stage: 'admission' | 'finalization';
|
|
222
|
+
fingerprint: string;
|
|
223
|
+
outcome: string;
|
|
224
|
+
server_version: number | null;
|
|
225
|
+
created_at: number;
|
|
226
|
+
}
|
|
213
227
|
export interface AdminUsersTable {
|
|
214
228
|
user_id: string;
|
|
215
229
|
created_at: number;
|
|
@@ -239,6 +253,7 @@ export interface Or3SqliteDb {
|
|
|
239
253
|
connect_environments: ConnectEnvironmentsTable;
|
|
240
254
|
rate_limits: RateLimitsTable;
|
|
241
255
|
background_jobs: BackgroundJobsTable;
|
|
256
|
+
background_generation_receipts: BackgroundGenerationReceiptsTable;
|
|
242
257
|
s_threads: SyncedEntityTable;
|
|
243
258
|
s_messages: SyncedEntityTable;
|
|
244
259
|
s_projects: SyncedEntityTable;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PluginConnectionStore } from '~~/server/utils/plugins/connections/store/registry';
|
|
2
|
+
import { type SqliteRawDatabase } from '../db/kysely.js';
|
|
3
|
+
type SqliteDatabase = SqliteRawDatabase;
|
|
4
|
+
export declare function createSqlitePluginConnectionStore(options?: {
|
|
5
|
+
database?: SqliteDatabase;
|
|
6
|
+
}): PluginConnectionStore;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getRawDb,
|
|
3
|
+
getSqliteDriver
|
|
4
|
+
} from "../db/kysely.js";
|
|
5
|
+
function parseStringArray(raw) {
|
|
6
|
+
try {
|
|
7
|
+
const parsed = JSON.parse(raw);
|
|
8
|
+
if (!Array.isArray(parsed)) return [];
|
|
9
|
+
return parsed.filter((value) => typeof value === "string");
|
|
10
|
+
} catch {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function toConnection(row) {
|
|
15
|
+
return {
|
|
16
|
+
id: row.id,
|
|
17
|
+
ownerUserId: row.owner_user_id,
|
|
18
|
+
workspaceId: row.workspace_id,
|
|
19
|
+
pluginId: row.plugin_id,
|
|
20
|
+
providerId: row.provider_id,
|
|
21
|
+
...row.slot_id === null ? {} : { slotId: row.slot_id },
|
|
22
|
+
label: row.label,
|
|
23
|
+
scopes: parseStringArray(row.scopes),
|
|
24
|
+
revision: row.revision,
|
|
25
|
+
secretCiphertext: row.secret_ciphertext,
|
|
26
|
+
createdAt: row.created_at,
|
|
27
|
+
updatedAt: row.updated_at
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function toEvidence(row) {
|
|
31
|
+
return {
|
|
32
|
+
connectionId: row.connection_id,
|
|
33
|
+
revision: row.revision,
|
|
34
|
+
operationId: row.operation_id,
|
|
35
|
+
ok: row.ok === 1,
|
|
36
|
+
...row.code === null ? {} : { code: row.code },
|
|
37
|
+
checkedAt: row.checked_at,
|
|
38
|
+
...row.detail === null ? {} : { detail: row.detail }
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
class SqlitePluginConnectionStore {
|
|
42
|
+
constructor(db) {
|
|
43
|
+
this.db = db;
|
|
44
|
+
this.#initialize();
|
|
45
|
+
}
|
|
46
|
+
#initialize() {
|
|
47
|
+
if (getSqliteDriver() !== "turso") {
|
|
48
|
+
this.db.pragma?.("journal_mode = WAL");
|
|
49
|
+
this.db.pragma?.("synchronous = NORMAL");
|
|
50
|
+
this.db.pragma?.("foreign_keys = ON");
|
|
51
|
+
}
|
|
52
|
+
this.db.exec?.(`
|
|
53
|
+
CREATE TABLE IF NOT EXISTS plugin_connections (
|
|
54
|
+
id TEXT PRIMARY KEY,
|
|
55
|
+
owner_user_id TEXT NOT NULL,
|
|
56
|
+
workspace_id TEXT NOT NULL,
|
|
57
|
+
plugin_id TEXT NOT NULL,
|
|
58
|
+
provider_id TEXT NOT NULL,
|
|
59
|
+
slot_id TEXT,
|
|
60
|
+
label TEXT NOT NULL DEFAULT '',
|
|
61
|
+
scopes TEXT NOT NULL DEFAULT '[]',
|
|
62
|
+
revision INTEGER NOT NULL DEFAULT 1,
|
|
63
|
+
secret_ciphertext TEXT NOT NULL,
|
|
64
|
+
created_at INTEGER NOT NULL,
|
|
65
|
+
updated_at INTEGER NOT NULL
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
CREATE TABLE IF NOT EXISTS plugin_connection_tests (
|
|
69
|
+
connection_id TEXT PRIMARY KEY,
|
|
70
|
+
revision INTEGER NOT NULL,
|
|
71
|
+
operation_id TEXT NOT NULL,
|
|
72
|
+
ok INTEGER NOT NULL,
|
|
73
|
+
code TEXT,
|
|
74
|
+
checked_at INTEGER NOT NULL,
|
|
75
|
+
detail TEXT,
|
|
76
|
+
FOREIGN KEY (connection_id) REFERENCES plugin_connections(id) ON DELETE CASCADE
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
CREATE INDEX IF NOT EXISTS idx_plugin_connections_workspace_plugin
|
|
80
|
+
ON plugin_connections (workspace_id, plugin_id);
|
|
81
|
+
CREATE INDEX IF NOT EXISTS idx_plugin_connections_owner
|
|
82
|
+
ON plugin_connections (owner_user_id);
|
|
83
|
+
`);
|
|
84
|
+
try {
|
|
85
|
+
this.db.prepare(`ALTER TABLE plugin_connections ADD COLUMN slot_id TEXT`).run();
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Owner-scoped listing. Ownership is part of the SQL predicate so a workspace
|
|
91
|
+
* peer cannot discover another user's connections or their references.
|
|
92
|
+
*/
|
|
93
|
+
async list(input) {
|
|
94
|
+
const rows = input.pluginId === void 0 ? this.db.prepare(
|
|
95
|
+
`SELECT * FROM plugin_connections WHERE owner_user_id = ? AND workspace_id = ? ORDER BY created_at ASC`
|
|
96
|
+
).all(input.ownerUserId, input.workspaceId) : this.db.prepare(
|
|
97
|
+
`SELECT * FROM plugin_connections WHERE owner_user_id = ? AND workspace_id = ? AND plugin_id = ? ORDER BY created_at ASC`
|
|
98
|
+
).all(input.ownerUserId, input.workspaceId, input.pluginId);
|
|
99
|
+
return rows.map(toConnection);
|
|
100
|
+
}
|
|
101
|
+
async get(id) {
|
|
102
|
+
const row = this.db.prepare(`SELECT * FROM plugin_connections WHERE id = ?`).get(id);
|
|
103
|
+
return row ? toConnection(row) : null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Insert-only creation. `DO NOTHING` is deliberate: an upsert here could
|
|
107
|
+
* reassign an existing record's owner, workspace, plugin or provider when two
|
|
108
|
+
* processes happen to generate the same id.
|
|
109
|
+
*/
|
|
110
|
+
async insert(connection) {
|
|
111
|
+
const result = this.db.prepare(
|
|
112
|
+
`INSERT INTO plugin_connections (
|
|
113
|
+
id, owner_user_id, workspace_id, plugin_id, provider_id, slot_id,
|
|
114
|
+
label, scopes, revision, secret_ciphertext, created_at, updated_at
|
|
115
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
116
|
+
ON CONFLICT(id) DO NOTHING`
|
|
117
|
+
).run(
|
|
118
|
+
connection.id,
|
|
119
|
+
connection.ownerUserId,
|
|
120
|
+
connection.workspaceId,
|
|
121
|
+
connection.pluginId,
|
|
122
|
+
connection.providerId,
|
|
123
|
+
connection.slotId ?? null,
|
|
124
|
+
connection.label,
|
|
125
|
+
JSON.stringify(connection.scopes),
|
|
126
|
+
connection.revision,
|
|
127
|
+
connection.secretCiphertext,
|
|
128
|
+
connection.createdAt,
|
|
129
|
+
connection.updatedAt
|
|
130
|
+
);
|
|
131
|
+
return result.changes > 0;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Compare-and-swap update. Identity columns are not in the SET list, so a
|
|
135
|
+
* retry cannot move the record; the revision predicate is the whole
|
|
136
|
+
* concurrency control.
|
|
137
|
+
*/
|
|
138
|
+
async update(update) {
|
|
139
|
+
const result = this.db.prepare(
|
|
140
|
+
`UPDATE plugin_connections
|
|
141
|
+
SET revision = ?,
|
|
142
|
+
secret_ciphertext = ?,
|
|
143
|
+
updated_at = ?,
|
|
144
|
+
scopes = COALESCE(?, scopes),
|
|
145
|
+
label = COALESCE(?, label)
|
|
146
|
+
WHERE id = ? AND revision = ?`
|
|
147
|
+
).run(
|
|
148
|
+
update.revision,
|
|
149
|
+
update.secretCiphertext,
|
|
150
|
+
update.updatedAt,
|
|
151
|
+
update.scopes === void 0 ? null : JSON.stringify(update.scopes),
|
|
152
|
+
update.label ?? null,
|
|
153
|
+
update.id,
|
|
154
|
+
update.expectedRevision
|
|
155
|
+
);
|
|
156
|
+
return result.changes > 0;
|
|
157
|
+
}
|
|
158
|
+
async delete(id) {
|
|
159
|
+
this.db.prepare(`DELETE FROM plugin_connections WHERE id = ?`).run(id);
|
|
160
|
+
this.db.prepare(`DELETE FROM plugin_connection_tests WHERE connection_id = ?`).run(id);
|
|
161
|
+
}
|
|
162
|
+
async getTestEvidence(connectionId) {
|
|
163
|
+
const row = this.db.prepare(`SELECT * FROM plugin_connection_tests WHERE connection_id = ?`).get(connectionId);
|
|
164
|
+
return row ? toEvidence(row) : null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Stores evidence only when it belongs to the connection's current revision
|
|
168
|
+
* and is not older than what is already stored. A slow test that finishes
|
|
169
|
+
* after a credential rotation therefore cannot reinstate itself as current
|
|
170
|
+
* evidence, and an older completion cannot replace a newer result.
|
|
171
|
+
*/
|
|
172
|
+
async setTestEvidence(evidence) {
|
|
173
|
+
const result = this.db.prepare(
|
|
174
|
+
`INSERT INTO plugin_connection_tests (
|
|
175
|
+
connection_id, revision, operation_id, ok, code, checked_at, detail
|
|
176
|
+
)
|
|
177
|
+
SELECT ?, ?, ?, ?, ?, ?, ?
|
|
178
|
+
WHERE EXISTS (
|
|
179
|
+
SELECT 1 FROM plugin_connections
|
|
180
|
+
WHERE id = ? AND revision = ?
|
|
181
|
+
)
|
|
182
|
+
ON CONFLICT(connection_id) DO UPDATE SET
|
|
183
|
+
revision = excluded.revision,
|
|
184
|
+
operation_id = excluded.operation_id,
|
|
185
|
+
ok = excluded.ok,
|
|
186
|
+
code = excluded.code,
|
|
187
|
+
checked_at = excluded.checked_at,
|
|
188
|
+
detail = excluded.detail
|
|
189
|
+
WHERE excluded.revision > plugin_connection_tests.revision
|
|
190
|
+
OR (excluded.revision = plugin_connection_tests.revision
|
|
191
|
+
AND excluded.checked_at >= plugin_connection_tests.checked_at)`
|
|
192
|
+
).run(
|
|
193
|
+
evidence.connectionId,
|
|
194
|
+
evidence.revision,
|
|
195
|
+
evidence.operationId,
|
|
196
|
+
evidence.ok ? 1 : 0,
|
|
197
|
+
evidence.code ?? null,
|
|
198
|
+
evidence.checkedAt,
|
|
199
|
+
evidence.detail ?? null,
|
|
200
|
+
evidence.connectionId,
|
|
201
|
+
evidence.revision
|
|
202
|
+
);
|
|
203
|
+
return result.changes > 0;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
export function createSqlitePluginConnectionStore(options = {}) {
|
|
207
|
+
const db = options.database ?? getRawDb();
|
|
208
|
+
return new SqlitePluginConnectionStore(db);
|
|
209
|
+
}
|
|
@@ -5,6 +5,7 @@ import { registerAdminStoreProvider } from "~~/server/admin/stores/registry";
|
|
|
5
5
|
import { registerSyncGatewayAdapter } from "~~/server/sync/gateway/registry";
|
|
6
6
|
import { registerWebhookStore } from "~~/server/utils/webhooks/store/registry";
|
|
7
7
|
import { registerConnectStore } from "~~/server/connect/store/registry";
|
|
8
|
+
import { registerPluginConnectionStore } from "~~/server/utils/plugins/connections/store/registry";
|
|
8
9
|
import { registerRateLimitProvider } from "~~/server/utils/rate-limit/registry";
|
|
9
10
|
import { registerBackgroundJobProvider } from "~~/server/utils/background-jobs/registry";
|
|
10
11
|
import { createSqliteAuthWorkspaceStore } from "../auth/sqlite-auth-workspace-store.js";
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
} from "../admin/stores/sqlite-store.js";
|
|
18
19
|
import { sqliteSyncAdminAdapter } from "../admin/adapters/sync-sqlite.js";
|
|
19
20
|
import { createSqliteConnectStore } from "../connect/sqlite-connect-store.js";
|
|
21
|
+
import { createSqlitePluginConnectionStore } from "../plugin-connections/sqlite-connection-store.js";
|
|
20
22
|
import { getSqliteDriver, initializeSqliteDb } from "../db/kysely.js";
|
|
21
23
|
import { runMigrations } from "../db/migrate.js";
|
|
22
24
|
import { sqliteRateLimitProvider } from "../rate-limit/sqlite-provider.js";
|
|
@@ -72,6 +74,17 @@ export default defineNitroPlugin(async (nitroApp) => {
|
|
|
72
74
|
"[or3-sqlite] Webhook persistence is unavailable with Cloudflare D1."
|
|
73
75
|
);
|
|
74
76
|
}
|
|
77
|
+
if (driver !== "d1") {
|
|
78
|
+
registerPluginConnectionStore({
|
|
79
|
+
id: SQLITE_PROVIDER_ID,
|
|
80
|
+
order: 100,
|
|
81
|
+
create: createSqlitePluginConnectionStore
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
console.warn(
|
|
85
|
+
"[or3-sqlite] Plugin connection persistence is unavailable with Cloudflare D1."
|
|
86
|
+
);
|
|
87
|
+
}
|
|
75
88
|
registerAdminStoreProvider({
|
|
76
89
|
id: SQLITE_PROVIDER_ID,
|
|
77
90
|
createWorkspaceAccessStore: createSqliteWorkspaceAccessStore,
|
|
@@ -11,3 +11,11 @@ export declare const SYNC_HISTORY_GC_POLICY: Readonly<{
|
|
|
11
11
|
reason: "SQLite history GC is guarded by the verified snapshot-v1 retention contract.";
|
|
12
12
|
}>;
|
|
13
13
|
export declare function canRunSyncHistoryGc(): boolean;
|
|
14
|
+
export declare function computePullRetention(input: {
|
|
15
|
+
cursor: number;
|
|
16
|
+
oldestLogVersion: number | null;
|
|
17
|
+
highWatermark: number;
|
|
18
|
+
}): {
|
|
19
|
+
oldestRetainedVersion: number;
|
|
20
|
+
requiresSnapshot: boolean;
|
|
21
|
+
};
|
|
@@ -6,3 +6,10 @@ export const SYNC_HISTORY_GC_POLICY = Object.freeze({
|
|
|
6
6
|
export function canRunSyncHistoryGc() {
|
|
7
7
|
return SYNC_HISTORY_GC_POLICY.enabled && SYNC_HISTORY_GC_POLICY.snapshotBootstrapVerified;
|
|
8
8
|
}
|
|
9
|
+
export function computePullRetention(input) {
|
|
10
|
+
const oldestRetainedVersion = input.oldestLogVersion ?? Math.max(0, input.highWatermark) + 1;
|
|
11
|
+
return {
|
|
12
|
+
oldestRetainedVersion,
|
|
13
|
+
requiresSnapshot: oldestRetainedVersion > 0 && input.cursor < Math.max(0, oldestRetainedVersion - 1)
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -7,14 +7,26 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { H3Event } from 'h3';
|
|
9
9
|
import type { CanonicalStorageQueryRequest, CanonicalStorageQueryResponse, SyncGatewayAdapter, SyncMaintenanceState, UploadIntentConsumptionRequest, UploadIntentReservationRequest } from '~~/server/sync/gateway/types';
|
|
10
|
+
import type { AdmitChatGenerationResult, CanonicalGenerationSnapshot, CanonicalHistoryActor, ChatGenerationAdmissionEnvelope, FinalizeChatGenerationResult } from '~~/shared/chat/background-history';
|
|
10
11
|
import type { PullRequest, PullResponse, PushBatch, PushResult, SnapshotRequest, SnapshotResponse } from '~~/shared/sync/types';
|
|
11
12
|
export declare class SqliteSyncGatewayAdapter implements SyncGatewayAdapter {
|
|
12
13
|
id: string;
|
|
13
|
-
|
|
14
|
+
get capabilities(): {
|
|
14
15
|
readonly snapshotBootstrap: "snapshot-v1";
|
|
15
16
|
readonly historyRetention: "snapshot-v1";
|
|
17
|
+
readonly backgroundGenerationHistory?: undefined;
|
|
18
|
+
} | {
|
|
19
|
+
readonly snapshotBootstrap: "snapshot-v1";
|
|
20
|
+
readonly historyRetention: "snapshot-v1";
|
|
21
|
+
readonly backgroundGenerationHistory: "v1";
|
|
16
22
|
};
|
|
17
23
|
private get db();
|
|
24
|
+
private assertCanonicalActor;
|
|
25
|
+
admitChatGeneration(actor: CanonicalHistoryActor, input: ChatGenerationAdmissionEnvelope): Promise<AdmitChatGenerationResult>;
|
|
26
|
+
finalizeChatGeneration(actor: CanonicalHistoryActor, input: {
|
|
27
|
+
admission: ChatGenerationAdmissionEnvelope;
|
|
28
|
+
snapshot: CanonicalGenerationSnapshot;
|
|
29
|
+
}): Promise<FinalizeChatGenerationResult>;
|
|
18
30
|
reserveUploadIntent(event: H3Event, input: UploadIntentReservationRequest): Promise<void>;
|
|
19
31
|
consumeUploadIntent(event: H3Event, input: UploadIntentConsumptionRequest): Promise<void>;
|
|
20
32
|
cancelUploadIntent(event: H3Event, input: {
|