@g9tm/syncdata 1.2.1
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/LICENSE.md +21 -0
- package/PARITY.md +71 -0
- package/README.md +71 -0
- package/THIRD-PARTY-NOTICES.md +31 -0
- package/dist/abstractions/digest.d.ts +27 -0
- package/dist/abstractions/digest.js +78 -0
- package/dist/abstractions/digest.js.map +1 -0
- package/dist/abstractions/enums.d.ts +42 -0
- package/dist/abstractions/enums.js +60 -0
- package/dist/abstractions/enums.js.map +1 -0
- package/dist/abstractions/errors.d.ts +72 -0
- package/dist/abstractions/errors.js +90 -0
- package/dist/abstractions/errors.js.map +1 -0
- package/dist/abstractions/guid.d.ts +24 -0
- package/dist/abstractions/guid.js +93 -0
- package/dist/abstractions/guid.js.map +1 -0
- package/dist/abstractions/key.d.ts +16 -0
- package/dist/abstractions/key.js +47 -0
- package/dist/abstractions/key.js.map +1 -0
- package/dist/abstractions/long.d.ts +103 -0
- package/dist/abstractions/long.js +351 -0
- package/dist/abstractions/long.js.map +1 -0
- package/dist/abstractions/parameters.d.ts +49 -0
- package/dist/abstractions/parameters.js +153 -0
- package/dist/abstractions/parameters.js.map +1 -0
- package/dist/brotli-js/decode.d.ts +8 -0
- package/dist/brotli-js/decode.js +2004 -0
- package/dist/brotli-js/decode.js.map +1 -0
- package/dist/brotli-js/index.d.ts +5 -0
- package/dist/brotli-js/index.js +19 -0
- package/dist/brotli-js/index.js.map +1 -0
- package/dist/client/engine.d.ts +110 -0
- package/dist/client/engine.js +1158 -0
- package/dist/client/engine.js.map +1 -0
- package/dist/client/model.d.ts +61 -0
- package/dist/client/model.js +154 -0
- package/dist/client/model.js.map +1 -0
- package/dist/client/progress.d.ts +101 -0
- package/dist/client/progress.js +251 -0
- package/dist/client/progress.js.map +1 -0
- package/dist/client/runs.d.ts +219 -0
- package/dist/client/runs.js +239 -0
- package/dist/client/runs.js.map +1 -0
- package/dist/client/storeTypes.d.ts +428 -0
- package/dist/client/storeTypes.js +256 -0
- package/dist/client/storeTypes.js.map +1 -0
- package/dist/client/transport.d.ts +19 -0
- package/dist/client/transport.js +2 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/abort.d.ts +21 -0
- package/dist/internal/abort.js +125 -0
- package/dist/internal/abort.js.map +1 -0
- package/dist/internal/base64.d.ts +8 -0
- package/dist/internal/base64.js +85 -0
- package/dist/internal/base64.js.map +1 -0
- package/dist/internal/dotnetFormat.d.ts +32 -0
- package/dist/internal/dotnetFormat.js +207 -0
- package/dist/internal/dotnetFormat.js.map +1 -0
- package/dist/internal/md5.d.ts +2 -0
- package/dist/internal/md5.js +97 -0
- package/dist/internal/md5.js.map +1 -0
- package/dist/internal/sha256.d.ts +20 -0
- package/dist/internal/sha256.js +153 -0
- package/dist/internal/sha256.js.map +1 -0
- package/dist/internal/utf8.d.ts +8 -0
- package/dist/internal/utf8.js +143 -0
- package/dist/internal/utf8.js.map +1 -0
- package/dist/node/index.d.ts +9 -0
- package/dist/node/index.js +35 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/sqliteDriver.d.ts +11 -0
- package/dist/node/sqliteDriver.js +252 -0
- package/dist/node/sqliteDriver.js.map +1 -0
- package/dist/protocol/dtos.d.ts +280 -0
- package/dist/protocol/dtos.js +33 -0
- package/dist/protocol/dtos.js.map +1 -0
- package/dist/protocol/protocol.d.ts +36 -0
- package/dist/protocol/protocol.js +63 -0
- package/dist/protocol/protocol.js.map +1 -0
- package/dist/protocol/rowBatch/columnLayout.d.ts +4 -0
- package/dist/protocol/rowBatch/columnLayout.js +309 -0
- package/dist/protocol/rowBatch/columnLayout.js.map +1 -0
- package/dist/protocol/rowBatch/format.d.ts +74 -0
- package/dist/protocol/rowBatch/format.js +163 -0
- package/dist/protocol/rowBatch/format.js.map +1 -0
- package/dist/protocol/rowBatch/reader.d.ts +119 -0
- package/dist/protocol/rowBatch/reader.js +459 -0
- package/dist/protocol/rowBatch/reader.js.map +1 -0
- package/dist/protocol/rowBatch/writer.d.ts +101 -0
- package/dist/protocol/rowBatch/writer.js +412 -0
- package/dist/protocol/rowBatch/writer.js.map +1 -0
- package/dist/protocol/wire.d.ts +32 -0
- package/dist/protocol/wire.js +329 -0
- package/dist/protocol/wire.js.map +1 -0
- package/dist/signalr/index.d.ts +2 -0
- package/dist/signalr/index.js +5 -0
- package/dist/signalr/index.js.map +1 -0
- package/dist/signalr/protocols.d.ts +17 -0
- package/dist/signalr/protocols.js +109 -0
- package/dist/signalr/protocols.js.map +1 -0
- package/dist/signalr/transport.d.ts +67 -0
- package/dist/signalr/transport.js +286 -0
- package/dist/signalr/transport.js.map +1 -0
- package/dist/sqlite/apply.d.ts +91 -0
- package/dist/sqlite/apply.js +657 -0
- package/dist/sqlite/apply.js.map +1 -0
- package/dist/sqlite/commands.d.ts +24 -0
- package/dist/sqlite/commands.js +167 -0
- package/dist/sqlite/commands.js.map +1 -0
- package/dist/sqlite/connection.d.ts +81 -0
- package/dist/sqlite/connection.js +322 -0
- package/dist/sqlite/connection.js.map +1 -0
- package/dist/sqlite/driver.d.ts +63 -0
- package/dist/sqlite/driver.js +9 -0
- package/dist/sqlite/driver.js.map +1 -0
- package/dist/sqlite/index.d.ts +6 -0
- package/dist/sqlite/index.js +7 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/internals.d.ts +63 -0
- package/dist/sqlite/internals.js +194 -0
- package/dist/sqlite/internals.js.map +1 -0
- package/dist/sqlite/options.d.ts +78 -0
- package/dist/sqlite/options.js +78 -0
- package/dist/sqlite/options.js.map +1 -0
- package/dist/sqlite/push.d.ts +9 -0
- package/dist/sqlite/push.js +272 -0
- package/dist/sqlite/push.js.map +1 -0
- package/dist/sqlite/query.d.ts +38 -0
- package/dist/sqlite/query.js +476 -0
- package/dist/sqlite/query.js.map +1 -0
- package/dist/sqlite/revocation.d.ts +28 -0
- package/dist/sqlite/revocation.js +337 -0
- package/dist/sqlite/revocation.js.map +1 -0
- package/dist/sqlite/sql.d.ts +60 -0
- package/dist/sqlite/sql.js +324 -0
- package/dist/sqlite/sql.js.map +1 -0
- package/dist/sqlite/store.d.ts +116 -0
- package/dist/sqlite/store.js +664 -0
- package/dist/sqlite/store.js.map +1 -0
- package/dist/sqlite/values.d.ts +29 -0
- package/dist/sqlite/values.js +288 -0
- package/dist/sqlite/values.js.map +1 -0
- package/dist/testing/index.d.ts +61 -0
- package/dist/testing/index.js +175 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
// Twin of G9SyncData.Client.Sqlite/G9CSqliteSyncStore.Query.cs: staging, reconciliation reads, pending counts, change
|
|
2
|
+
// feed, rejections, run reports and statistics.
|
|
3
|
+
import { G9SyncDigest } from '../abstractions/digest.js';
|
|
4
|
+
import { G9SyncKey } from '../abstractions/key.js';
|
|
5
|
+
import { G9Long, parseJson } from '../abstractions/long.js';
|
|
6
|
+
import { guidToN, tryNormalizeGuid } from '../abstractions/guid.js';
|
|
7
|
+
import { readContinuation, readPullPlan } from '../protocol/wire.js';
|
|
8
|
+
import { asLong, asNumber, asOptionalString, asString } from './connection.js';
|
|
9
|
+
import { findByServerName, getLocal, persistedJson, readSubscriptions } from './internals.js';
|
|
10
|
+
import { allColumnsMask, bit, keyExpression, literal, NOW_MS, quote } from './sql.js';
|
|
11
|
+
import { formatTicks, ticksOf, ticksToUnixMs } from './values.js';
|
|
12
|
+
// ================================================================ staging
|
|
13
|
+
export async function readStagedRun(connection, runId) {
|
|
14
|
+
const rows = await connection.query('SELECT plan_json, backfills_json, created_utc, generation, stage_seq, from_cursors_json FROM g9_stage_runs WHERE run_id = $r', {
|
|
15
|
+
$r: runId,
|
|
16
|
+
});
|
|
17
|
+
const r = rows[0];
|
|
18
|
+
if (!r)
|
|
19
|
+
return null;
|
|
20
|
+
const backfills = r[1] === null ? [] : parseJson(asString(r[1]));
|
|
21
|
+
const fromCursors = r[5] === null ? {} : JSON.parse(asString(r[5]));
|
|
22
|
+
return {
|
|
23
|
+
runId,
|
|
24
|
+
plan: readPullPlan(parseJson(asString(r[0]))),
|
|
25
|
+
backfills: (Array.isArray(backfills) ? backfills : []).map((b) => { var _a; return ({ tableId: Number((_a = b.tableId) !== null && _a !== void 0 ? _a : 0), columns: Array.isArray(b.columns) ? b.columns.map(String) : [] }); }),
|
|
26
|
+
createdUtcMs: asNumber(r[2]),
|
|
27
|
+
generation: asOptionalString(r[3]),
|
|
28
|
+
stageSequence: asNumber(r[4]),
|
|
29
|
+
plannedFromCursors: fromCursors !== null && fromCursors !== void 0 ? fromCursors : {},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export async function getStagedPull(store, generation) {
|
|
33
|
+
const connection = store.reader();
|
|
34
|
+
const runId = generation === null
|
|
35
|
+
? await connection.scalar('SELECT run_id FROM g9_stage_runs ORDER BY created_utc DESC LIMIT 1')
|
|
36
|
+
: await connection.scalar('SELECT run_id FROM g9_stage_runs WHERE generation = $g ORDER BY created_utc DESC LIMIT 1', { $g: generation });
|
|
37
|
+
return runId === null ? null : readStagedRun(connection, asString(runId));
|
|
38
|
+
}
|
|
39
|
+
export async function pruneStagedPulls(store, olderThanUtcMs, maxBytes, keepGeneration) {
|
|
40
|
+
return store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
41
|
+
const connection = store.writer();
|
|
42
|
+
const doomed = (await connection.query('SELECT run_id FROM g9_stage_runs WHERE created_utc < $c', { $c: olderThanUtcMs })).map((r) => asString(r[0]));
|
|
43
|
+
// The byte cap is for the stages nobody is about to resume: newest first, past the cap they go (ADR-0060).
|
|
44
|
+
let kept = 0;
|
|
45
|
+
for (const r of await connection.query('SELECT r.run_id, (SELECT coalesce(sum(length(payload)), 0) FROM g9_stage s WHERE s.run_id = r.run_id) FROM g9_stage_runs r ' +
|
|
46
|
+
'WHERE r.created_utc >= $c AND (r.generation IS NULL OR r.generation <> $g) ORDER BY r.created_utc DESC', { $c: olderThanUtcMs, $g: keepGeneration !== null && keepGeneration !== void 0 ? keepGeneration : '' })) {
|
|
47
|
+
kept += asNumber(r[1]);
|
|
48
|
+
if (kept > Math.max(0, maxBytes))
|
|
49
|
+
doomed.push(asString(r[0]));
|
|
50
|
+
}
|
|
51
|
+
for (const runId of doomed)
|
|
52
|
+
await connection.exec('DELETE FROM g9_stage WHERE run_id = $r; DELETE FROM g9_stage_runs WHERE run_id = $r', { $r: runId });
|
|
53
|
+
return doomed.length;
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
export async function discardAllStagedPulls(store) {
|
|
57
|
+
return store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
58
|
+
const connection = store.writer();
|
|
59
|
+
const count = asNumber(await connection.scalar('SELECT count(*) FROM g9_stage_runs'));
|
|
60
|
+
await connection.exec('DELETE FROM g9_stage; DELETE FROM g9_stage_runs;');
|
|
61
|
+
return count;
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
export async function beginStagedPull(store, pull) {
|
|
65
|
+
await store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
66
|
+
var _a;
|
|
67
|
+
const connection = store.writer();
|
|
68
|
+
// Only the stage this one replaces goes: the same generation (ADR-0060).
|
|
69
|
+
await connection.exec('DELETE FROM g9_stage WHERE run_id IN (SELECT run_id FROM g9_stage_runs WHERE generation IS $g); DELETE FROM g9_stage_runs WHERE generation IS $g', { $g: pull.generation });
|
|
70
|
+
// Read inside the transaction that inserts the run: rows written after it are newer than the stage (ADR-0046).
|
|
71
|
+
const stageSequence = asNumber(await connection.scalar('SELECT seq FROM g9_clock WHERE id = 1'));
|
|
72
|
+
const fromCursors = {};
|
|
73
|
+
for (const key of Object.keys(pull.plannedFromCursors))
|
|
74
|
+
fromCursors[key] = (_a = pull.plannedFromCursors[key]) !== null && _a !== void 0 ? _a : null;
|
|
75
|
+
await connection.exec('INSERT INTO g9_stage_runs (run_id, plan_json, backfills_json, created_utc, complete, generation, stage_seq, from_cursors_json) VALUES ($r, $p, $b, $c, 0, $g, $s, $f)', {
|
|
76
|
+
$r: pull.runId,
|
|
77
|
+
$p: persistedJson(pull.plan),
|
|
78
|
+
$b: persistedJson(pull.backfills),
|
|
79
|
+
$c: pull.createdUtcMs,
|
|
80
|
+
$g: pull.generation,
|
|
81
|
+
$s: stageSequence,
|
|
82
|
+
// Dictionary<string, string?>: a null value stays (DefaultIgnoreCondition only drops null PROPERTIES).
|
|
83
|
+
$f: JSON.stringify(fromCursors),
|
|
84
|
+
});
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
export async function stageChunk(store, runId, chunk, durability) {
|
|
88
|
+
await store.writeLock.run(async () => {
|
|
89
|
+
const connection = store.writer();
|
|
90
|
+
const restore = await durability(connection);
|
|
91
|
+
try {
|
|
92
|
+
await connection.transaction('immediate', async () => {
|
|
93
|
+
var _a, _b;
|
|
94
|
+
await connection.exec('INSERT OR REPLACE INTO g9_stage (run_id, seq, subscription_key, row_count, continuation_json, payload) VALUES ($r, $s, $k, $n, $c, $p)', {
|
|
95
|
+
$r: runId,
|
|
96
|
+
$s: chunk.sequence,
|
|
97
|
+
$k: (_a = chunk.subscriptionKey) !== null && _a !== void 0 ? _a : null,
|
|
98
|
+
$n: chunk.rowCount,
|
|
99
|
+
$c: chunk.continuation ? persistedJson(chunk.continuation) : null,
|
|
100
|
+
$p: (_b = chunk.payload) !== null && _b !== void 0 ? _b : null,
|
|
101
|
+
});
|
|
102
|
+
if (chunk.isFinal)
|
|
103
|
+
await connection.exec('UPDATE g9_stage_runs SET complete = 1 WHERE run_id = $r', { $r: runId });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
if (restore)
|
|
108
|
+
await restore();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export async function getResumePoint(store, runId) {
|
|
113
|
+
return store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
114
|
+
const connection = store.writer();
|
|
115
|
+
const flag = await connection.scalar('SELECT complete FROM g9_stage_runs WHERE run_id = $r', { $r: runId });
|
|
116
|
+
const complete = typeof flag === 'number' && flag !== 0;
|
|
117
|
+
let continuation = null;
|
|
118
|
+
let lastSequence = -1;
|
|
119
|
+
if (!complete) {
|
|
120
|
+
const rows = await connection.query('SELECT seq, continuation_json FROM g9_stage WHERE run_id = $r AND continuation_json IS NOT NULL ORDER BY seq DESC LIMIT 1', { $r: runId });
|
|
121
|
+
const r = rows[0];
|
|
122
|
+
if (r) {
|
|
123
|
+
lastSequence = asNumber(r[0]);
|
|
124
|
+
continuation = readContinuation(parseJson(asString(r[1])));
|
|
125
|
+
}
|
|
126
|
+
await connection.exec('DELETE FROM g9_stage WHERE run_id = $r AND seq > $s', { $r: runId, $s: lastSequence });
|
|
127
|
+
}
|
|
128
|
+
const totals = (await connection.query('SELECT coalesce(sum(row_count), 0), coalesce(sum(length(payload)), 0) FROM g9_stage WHERE run_id = $r', { $r: runId }))[0];
|
|
129
|
+
return { continuation, stagedRows: totals ? asNumber(totals[0]) : 0, stagedBytes: totals ? asNumber(totals[1]) : 0, complete };
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
export async function discardStagedPull(store, runId) {
|
|
133
|
+
await store.writeLock.run(() => store.writer().exec('DELETE FROM g9_stage WHERE run_id = $r; DELETE FROM g9_stage_runs WHERE run_id = $r', { $r: runId }));
|
|
134
|
+
}
|
|
135
|
+
// ================================================================ reconciliation reads
|
|
136
|
+
function computedColumns(local, table) {
|
|
137
|
+
return table.computedColumns.filter((c) => local.model.indexOf(c) >= 0);
|
|
138
|
+
}
|
|
139
|
+
async function memberRows(connection, local, subscriptionKey) {
|
|
140
|
+
const records = await readSubscriptions(connection, subscriptionKey);
|
|
141
|
+
const record = records[0];
|
|
142
|
+
if (!record)
|
|
143
|
+
return null;
|
|
144
|
+
return record.ephemeral
|
|
145
|
+
? connection.query('SELECT r.pk, r.server_ver, r.dirty FROM g9_rows r WHERE r.tid = $t AND r.deleted = 0 AND r.dirty <> 2 ' +
|
|
146
|
+
'AND EXISTS (SELECT 1 FROM g9_ephemeral_rows e WHERE e.sub_key = $k AND e.tid = r.tid AND e.pk = r.pk)', { $t: local.tid, $k: subscriptionKey })
|
|
147
|
+
: connection.query('SELECT r.pk, r.server_ver, r.dirty FROM g9_rows r WHERE r.tid = $t AND r.deleted = 0 AND r.dirty <> 2 AND (r.subs & $b) <> 0', {
|
|
148
|
+
$t: local.tid,
|
|
149
|
+
$b: bit(record.slot),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
export async function computeDigest(store, subscriptionKey, table) {
|
|
153
|
+
var _a;
|
|
154
|
+
store.ensureInitialized();
|
|
155
|
+
const local = findByServerName(store, table.name);
|
|
156
|
+
if (!local)
|
|
157
|
+
throw new RangeError(`'${table.name}' is not a synced table.`);
|
|
158
|
+
const computed = computedColumns(local, table);
|
|
159
|
+
const digests = [];
|
|
160
|
+
const counts = [];
|
|
161
|
+
for (let i = 0; i < 256; i++) {
|
|
162
|
+
digests.push(G9Long.ZERO);
|
|
163
|
+
counts.push(0);
|
|
164
|
+
}
|
|
165
|
+
const connection = store.reader();
|
|
166
|
+
if (computed.length === 0) {
|
|
167
|
+
for (const r of (_a = (await memberRows(connection, local, subscriptionKey))) !== null && _a !== void 0 ? _a : []) {
|
|
168
|
+
const key = asString(r[0]);
|
|
169
|
+
const bucket = G9SyncDigest.getBucket(key);
|
|
170
|
+
counts[bucket] = counts[bucket] + 1;
|
|
171
|
+
digests[bucket] = digests[bucket].xor(G9SyncDigest.getRowHash(key, asLong(r[1])));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
for (const entry of await readMemberEntries(connection, local, subscriptionKey, computed)) {
|
|
176
|
+
counts[entry.bucket] = counts[entry.bucket] + 1;
|
|
177
|
+
digests[entry.bucket] = digests[entry.bucket].xor(entry.hash);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { tableId: table.tableId, computedColumns: computed, digests, counts };
|
|
181
|
+
}
|
|
182
|
+
export async function readLocalEntries(store, subscriptionKey, table, buckets, serverKeys) {
|
|
183
|
+
var _a;
|
|
184
|
+
store.ensureInitialized();
|
|
185
|
+
const local = findByServerName(store, table.name);
|
|
186
|
+
if (!local)
|
|
187
|
+
throw new RangeError(`'${table.name}' is not a synced table.`);
|
|
188
|
+
const computed = computedColumns(local, table);
|
|
189
|
+
const entries = new Map();
|
|
190
|
+
const connection = store.reader();
|
|
191
|
+
if (computed.length === 0) {
|
|
192
|
+
for (const r of (_a = (await memberRows(connection, local, subscriptionKey))) !== null && _a !== void 0 ? _a : []) {
|
|
193
|
+
const key = asString(r[0]);
|
|
194
|
+
const bucket = G9SyncDigest.getBucket(key);
|
|
195
|
+
if (!buckets.has(bucket))
|
|
196
|
+
continue;
|
|
197
|
+
const version = asLong(r[1]);
|
|
198
|
+
entries.set(key, { key, bucket, version: version.toNumber(), hash: G9SyncDigest.getRowHash(key, version), member: true, dirty: asNumber(r[2]) !== 0 });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
for (const entry of await readMemberEntries(connection, local, subscriptionKey, computed)) {
|
|
203
|
+
if (buckets.has(entry.bucket))
|
|
204
|
+
entries.set(entry.key, entry);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const missing = [];
|
|
208
|
+
serverKeys.forEach((key) => {
|
|
209
|
+
if (!entries.has(key))
|
|
210
|
+
missing.push(key);
|
|
211
|
+
});
|
|
212
|
+
for (let i = 0; i < missing.length; i += 5000) {
|
|
213
|
+
const json = JSON.stringify(missing.slice(i, i + 5000));
|
|
214
|
+
for (const entry of await readEntries(connection, local, computed, 'r.deleted = 0 AND r.pk IN (SELECT value FROM json_each($keys))', false, { $keys: json })) {
|
|
215
|
+
entries.set(entry.key, entry);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return Array.from(entries.values());
|
|
219
|
+
}
|
|
220
|
+
async function readMemberEntries(connection, local, subscriptionKey, computed) {
|
|
221
|
+
const record = (await readSubscriptions(connection, subscriptionKey))[0];
|
|
222
|
+
if (!record)
|
|
223
|
+
return [];
|
|
224
|
+
return record.ephemeral
|
|
225
|
+
? readEntries(connection, local, computed, 'r.deleted = 0 AND r.dirty <> 2 AND EXISTS (SELECT 1 FROM g9_ephemeral_rows e WHERE e.sub_key = $sub AND e.tid = r.tid AND e.pk = r.pk)', true, { $sub: subscriptionKey })
|
|
226
|
+
: readEntries(connection, local, computed, 'r.deleted = 0 AND r.dirty <> 2 AND (r.subs & $bit) <> 0', true, { $bit: bit(record.slot) });
|
|
227
|
+
}
|
|
228
|
+
async function readEntries(connection, local, computed, where, member, params) {
|
|
229
|
+
const computedTypes = computed.map((name) => local.model.columns[local.model.indexOf(name)].type);
|
|
230
|
+
// typeof() next to each computed value: JavaScript cannot tell an INTEGER 5 from a REAL 5.0, and their canonical texts
|
|
231
|
+
// can differ (1E+15 vs 1000000000000000).
|
|
232
|
+
const select = 'SELECT r.pk, r.server_ver, r.dirty' +
|
|
233
|
+
computed.map((c) => ', b.' + quote(c) + ', typeof(b.' + quote(c) + ')').join('') +
|
|
234
|
+
' FROM g9_rows r' +
|
|
235
|
+
(computed.length === 0 ? '' : ` LEFT JOIN ${local.quoted} b ON b.${local.keyQuoted} = r.pk`) +
|
|
236
|
+
' WHERE r.tid = $tid AND ' +
|
|
237
|
+
where;
|
|
238
|
+
const rows = await connection.query(select, { $tid: local.tid, ...params });
|
|
239
|
+
return rows.map((r) => {
|
|
240
|
+
const key = asString(r[0]);
|
|
241
|
+
const version = asLong(r[1]);
|
|
242
|
+
const values = [];
|
|
243
|
+
for (let i = 0; i < computed.length; i++) {
|
|
244
|
+
const value = r[3 + i * 2];
|
|
245
|
+
const storage = asString(r[4 + i * 2]);
|
|
246
|
+
values.push(value === null ? null : G9SyncDigest.canonicalText(computedTypes[i], value, storage === 'real' ? 'real' : storage === 'integer' ? 'integer' : undefined));
|
|
247
|
+
}
|
|
248
|
+
return { key, bucket: G9SyncDigest.getBucket(key), version: version.toNumber(), hash: G9SyncDigest.getRowHash(key, version, values), member, dirty: asNumber(r[2]) !== 0 };
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
// ================================================================ queries
|
|
252
|
+
export async function getPendingSummary(store) {
|
|
253
|
+
const connection = store.reader();
|
|
254
|
+
const tables = (await connection.query('SELECT t.local_name, ' +
|
|
255
|
+
'sum(CASE WHEN r.dirty = 1 AND r.rejected = 0 THEN 1 ELSE 0 END), ' +
|
|
256
|
+
'sum(CASE WHEN r.dirty = 2 AND r.rejected = 0 THEN 1 ELSE 0 END), ' +
|
|
257
|
+
'sum(CASE WHEN r.rejected = 1 THEN 1 ELSE 0 END) ' +
|
|
258
|
+
'FROM g9_rows r JOIN g9_tables t ON t.tid = r.tid WHERE r.dirty <> 0 GROUP BY t.local_name ORDER BY t.local_name')).map((r) => ({ localTable: asString(r[0]), upserts: asNumber(r[1]), deletes: asNumber(r[2]), rejected: asNumber(r[3]) }));
|
|
259
|
+
const pendingCommands = asNumber(await connection.scalar('SELECT count(*) FROM g9_commands WHERE state = 0'));
|
|
260
|
+
let totalRows = 0;
|
|
261
|
+
let rejectedRows = 0;
|
|
262
|
+
for (const t of tables) {
|
|
263
|
+
totalRows += t.upserts + t.deletes;
|
|
264
|
+
rejectedRows += t.rejected;
|
|
265
|
+
}
|
|
266
|
+
return { tables, totalRows, rejectedRows, pendingCommands };
|
|
267
|
+
}
|
|
268
|
+
export async function readChangeFeed(store, localTable, afterSequence, limit) {
|
|
269
|
+
store.ensureInitialized();
|
|
270
|
+
const table = getLocal(store, localTable);
|
|
271
|
+
const rows = await store
|
|
272
|
+
.reader()
|
|
273
|
+
.query('SELECT local_seq, pk, (deleted = 1 OR dirty = 2), dirty <> 0 FROM g9_rows WHERE tid = $t AND local_seq > $a ORDER BY local_seq LIMIT $l', {
|
|
274
|
+
$t: table.tid,
|
|
275
|
+
$a: afterSequence,
|
|
276
|
+
$l: Math.max(1, limit),
|
|
277
|
+
});
|
|
278
|
+
return rows.map((r) => ({ sequence: asNumber(r[0]), key: asString(r[1]), deleted: asNumber(r[2]) !== 0, localPending: asNumber(r[3]) !== 0 }));
|
|
279
|
+
}
|
|
280
|
+
export async function getChangeSequence(store) {
|
|
281
|
+
store.ensureInitialized();
|
|
282
|
+
return asNumber(await store.reader().scalar('SELECT seq FROM g9_clock WHERE id = 1'));
|
|
283
|
+
}
|
|
284
|
+
export async function getRejections(store) {
|
|
285
|
+
const rows = await store
|
|
286
|
+
.reader()
|
|
287
|
+
.query('SELECT t.local_name, j.pk, j.code, j.message, j.sql_state, j.constraint_name, j.column_name, j.details, j.at_utc ' +
|
|
288
|
+
'FROM g9_rejections j JOIN g9_tables t ON t.tid = j.tid JOIN g9_rows r ON r.tid = j.tid AND r.pk = j.pk WHERE r.rejected = 1 ORDER BY j.at_utc');
|
|
289
|
+
return rows.map((r) => ({
|
|
290
|
+
localTable: asString(r[0]),
|
|
291
|
+
key: asString(r[1]),
|
|
292
|
+
code: asString(r[2]),
|
|
293
|
+
message: asOptionalString(r[3]),
|
|
294
|
+
sqlState: asOptionalString(r[4]),
|
|
295
|
+
constraint: asOptionalString(r[5]),
|
|
296
|
+
column: asOptionalString(r[6]),
|
|
297
|
+
details: asOptionalString(r[7]),
|
|
298
|
+
atUtcMs: asNumber(r[8]),
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
export async function retryRejected(store, localTable, key) {
|
|
302
|
+
store.ensureInitialized();
|
|
303
|
+
let where = 'rejected = 1';
|
|
304
|
+
const params = {};
|
|
305
|
+
if (localTable !== null) {
|
|
306
|
+
where += ' AND tid = $t';
|
|
307
|
+
params.$t = getLocal(store, localTable).tid;
|
|
308
|
+
}
|
|
309
|
+
if (key !== null) {
|
|
310
|
+
where += ' AND pk = $pk';
|
|
311
|
+
params.$pk = key;
|
|
312
|
+
}
|
|
313
|
+
return store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
314
|
+
const connection = store.writer();
|
|
315
|
+
await connection.exec('DELETE FROM g9_rejections WHERE (tid, pk) IN (SELECT tid, pk FROM g9_rows WHERE ' + where + ')', params);
|
|
316
|
+
return connection.exec('UPDATE g9_rows SET rejected = 0 WHERE ' + where, params);
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
export async function markDirty(store, localTable, keys) {
|
|
320
|
+
store.ensureInitialized();
|
|
321
|
+
const table = getLocal(store, localTable);
|
|
322
|
+
const normalized = keys.map((k) => { var _a; return (_a = G9SyncKey.tryNormalize(table.model.keyType, k)) !== null && _a !== void 0 ? _a : k; });
|
|
323
|
+
return store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
324
|
+
let marked = 0;
|
|
325
|
+
for (let i = 0; i < normalized.length; i += 5000) {
|
|
326
|
+
const json = JSON.stringify(normalized.slice(i, i + 5000));
|
|
327
|
+
marked += await markPending(store.writer(), table, 'SELECT value AS pk FROM json_each($keys)', { $keys: json }, 'dirty = 1, dirty_mask = ' + literal(allColumnsMask(table.model.columns.length)));
|
|
328
|
+
}
|
|
329
|
+
return marked;
|
|
330
|
+
}));
|
|
331
|
+
}
|
|
332
|
+
/** Marks the rows `keysSql` selects as pending, giving EACH its own local sequence (LES-0039). */
|
|
333
|
+
export async function markPending(connection, table, keysSql, params, set) {
|
|
334
|
+
await connection.exec('CREATE TEMP TABLE IF NOT EXISTS g9_mark (pk TEXT NOT NULL PRIMARY KEY, n INTEGER NOT NULL); DELETE FROM temp.g9_mark;');
|
|
335
|
+
const count = await connection.exec(`INSERT INTO temp.g9_mark (pk, n) SELECT pk, row_number() OVER (ORDER BY pk) FROM (SELECT DISTINCT pk FROM (${keysSql}) WHERE pk IS NOT NULL)`, params);
|
|
336
|
+
if (count === 0)
|
|
337
|
+
return 0;
|
|
338
|
+
const first = asNumber(await connection.scalar('SELECT seq FROM g9_clock WHERE id = 1'));
|
|
339
|
+
await connection.exec('INSERT OR IGNORE INTO g9_rows (tid, pk) SELECT $t, pk FROM temp.g9_mark', { $t: table.tid });
|
|
340
|
+
const marked = await connection.exec(`UPDATE g9_rows SET ${set}, local_seq = $first + m.n, hlc_ms = ${NOW_MS}, rejected = 0, deleted = 0 FROM temp.g9_mark AS m WHERE g9_rows.tid = $t AND g9_rows.pk = m.pk`, { $first: first, $t: table.tid });
|
|
341
|
+
await connection.exec('UPDATE g9_clock SET seq = seq + $n WHERE id = 1; DELETE FROM temp.g9_mark;', { $n: count });
|
|
342
|
+
return marked;
|
|
343
|
+
}
|
|
344
|
+
export async function adoptExistingRows(store) {
|
|
345
|
+
store.ensureInitialized();
|
|
346
|
+
return store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
347
|
+
let adopted = 0;
|
|
348
|
+
for (const table of store.byLocal.values()) {
|
|
349
|
+
adopted += await store.writer().exec(`INSERT OR IGNORE INTO g9_rows (tid, pk) SELECT $t, ${keyExpression(table.model, 'b')} FROM ${table.quoted} AS b`, { $t: table.tid });
|
|
350
|
+
}
|
|
351
|
+
return adopted;
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
// ================================================================ reports and statistics
|
|
355
|
+
/** The run report as .NET's G9SyncClientJsonContext writes it (camelCase, nulls omitted, DateTime/Guid in .NET text). */
|
|
356
|
+
function reportToJson(report) {
|
|
357
|
+
const startedTicks = ticksOf(1970, 1, 1, 0, 0, 0, 0).add(G9Long.fromNumber(Math.floor(report.startedUtcMs)).multiplySmall(10000));
|
|
358
|
+
return persistedJson({
|
|
359
|
+
runId: report.runId,
|
|
360
|
+
startedUtc: formatTicks(startedTicks) + 'Z',
|
|
361
|
+
durationMs: report.durationMs,
|
|
362
|
+
outcome: report.outcome,
|
|
363
|
+
priority: report.priority,
|
|
364
|
+
errorCode: report.errorCode,
|
|
365
|
+
errorMessage: report.errorMessage,
|
|
366
|
+
pushed: report.pushed,
|
|
367
|
+
rejected: report.rejected,
|
|
368
|
+
conflicts: report.conflicts,
|
|
369
|
+
downloaded: report.downloaded,
|
|
370
|
+
applied: report.applied,
|
|
371
|
+
downloadedBytes: report.downloadedBytes,
|
|
372
|
+
subscriptions: report.subscriptions,
|
|
373
|
+
phaseMs: report.phaseMs,
|
|
374
|
+
triggersSkipped: report.triggersSkipped,
|
|
375
|
+
foreignTriggers: report.foreignTriggers,
|
|
376
|
+
applyTransactions: report.applyTransactions,
|
|
377
|
+
indexesDeferred: report.indexesDeferred,
|
|
378
|
+
indexRebuildMs: report.indexRebuildMs,
|
|
379
|
+
liveIndexReasons: report.liveIndexReasons,
|
|
380
|
+
unchangedRows: report.unchangedRows,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
function reportFromJson(json) {
|
|
384
|
+
var _a, _b;
|
|
385
|
+
const v = parseJson(json);
|
|
386
|
+
if (!v || typeof v !== 'object')
|
|
387
|
+
return null;
|
|
388
|
+
const num = (x) => (typeof x === 'number' ? x : x instanceof G9Long ? x.toNumber() : 0);
|
|
389
|
+
const started = typeof v.startedUtc === 'string' ? Date.parse(v.startedUtc.replace(/(\.\d{3})\d+/, '$1')) : NaN;
|
|
390
|
+
return {
|
|
391
|
+
runId: typeof v.runId === 'string' ? v.runId : '',
|
|
392
|
+
startedUtcMs: isNaN(started) ? 0 : started,
|
|
393
|
+
durationMs: num(v.durationMs),
|
|
394
|
+
outcome: typeof v.outcome === 'string' ? v.outcome : '',
|
|
395
|
+
priority: typeof v.priority === 'string' ? v.priority : '',
|
|
396
|
+
errorCode: typeof v.errorCode === 'string' ? v.errorCode : null,
|
|
397
|
+
errorMessage: typeof v.errorMessage === 'string' ? v.errorMessage : null,
|
|
398
|
+
pushed: num(v.pushed),
|
|
399
|
+
rejected: num(v.rejected),
|
|
400
|
+
conflicts: num(v.conflicts),
|
|
401
|
+
downloaded: num(v.downloaded),
|
|
402
|
+
applied: num(v.applied),
|
|
403
|
+
downloadedBytes: num(v.downloadedBytes),
|
|
404
|
+
subscriptions: Array.isArray(v.subscriptions) ? v.subscriptions.map(String) : [],
|
|
405
|
+
phaseMs: (_a = v.phaseMs) !== null && _a !== void 0 ? _a : {},
|
|
406
|
+
triggersSkipped: typeof v.triggersSkipped === 'boolean' ? v.triggersSkipped : null,
|
|
407
|
+
foreignTriggers: Array.isArray(v.foreignTriggers) ? v.foreignTriggers.map(String) : [],
|
|
408
|
+
applyTransactions: num(v.applyTransactions),
|
|
409
|
+
indexesDeferred: num(v.indexesDeferred),
|
|
410
|
+
indexRebuildMs: num(v.indexRebuildMs),
|
|
411
|
+
liveIndexReasons: (_b = v.liveIndexReasons) !== null && _b !== void 0 ? _b : {},
|
|
412
|
+
unchangedRows: num(v.unchangedRows),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
export async function saveRunReport(store, report, keep) {
|
|
416
|
+
const runId = tryNormalizeGuid(report.runId) ? guidToN(report.runId) : report.runId;
|
|
417
|
+
await store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
418
|
+
const connection = store.writer();
|
|
419
|
+
await connection.exec('INSERT OR REPLACE INTO g9_runs (run_id, started_utc, report_json) VALUES ($r, $s, $j)', { $r: runId, $s: report.startedUtcMs, $j: reportToJson(report) });
|
|
420
|
+
await connection.exec('DELETE FROM g9_runs WHERE run_id NOT IN (SELECT run_id FROM g9_runs ORDER BY started_utc DESC LIMIT $k)', { $k: Math.max(1, keep) });
|
|
421
|
+
}));
|
|
422
|
+
}
|
|
423
|
+
export async function getRecentRunReports(store, count) {
|
|
424
|
+
const rows = await store.reader().query('SELECT report_json FROM g9_runs ORDER BY started_utc DESC LIMIT $c', { $c: Math.max(1, count) });
|
|
425
|
+
const reports = [];
|
|
426
|
+
for (const r of rows) {
|
|
427
|
+
const report = reportFromJson(asString(r[0]));
|
|
428
|
+
if (report)
|
|
429
|
+
reports.push(report);
|
|
430
|
+
}
|
|
431
|
+
return reports;
|
|
432
|
+
}
|
|
433
|
+
export async function getPhaseStatistics(store) {
|
|
434
|
+
const statistics = {};
|
|
435
|
+
for (const r of await store.reader().query('SELECT key, value FROM g9_stats'))
|
|
436
|
+
statistics[asString(r[0])] = asNumber(r[1]);
|
|
437
|
+
return statistics;
|
|
438
|
+
}
|
|
439
|
+
export async function recordPhaseStatistics(store, samples) {
|
|
440
|
+
await store.writeLock.run(() => store.writer().transaction('immediate', async () => {
|
|
441
|
+
for (const key of Object.keys(samples)) {
|
|
442
|
+
const value = samples[key];
|
|
443
|
+
if (!isFinite(value) || value < 0)
|
|
444
|
+
continue;
|
|
445
|
+
await store
|
|
446
|
+
.writer()
|
|
447
|
+
.exec('INSERT INTO g9_stats (key, value, samples) VALUES ($k, $v, 1) ON CONFLICT(key) DO UPDATE SET value = value * 0.7 + excluded.value * 0.3, samples = samples + 1', { $k: key, $v: value });
|
|
448
|
+
}
|
|
449
|
+
}));
|
|
450
|
+
}
|
|
451
|
+
export async function purgeDeletedFeed(store, keep) {
|
|
452
|
+
return store.writeLock.run(async () => {
|
|
453
|
+
const connection = store.writer();
|
|
454
|
+
const threshold = await connection.scalar('SELECT local_seq FROM g9_rows WHERE deleted = 1 ORDER BY local_seq DESC LIMIT 1 OFFSET $k', { $k: Math.max(0, keep) });
|
|
455
|
+
if (threshold === null)
|
|
456
|
+
return 0;
|
|
457
|
+
return connection.transaction('immediate', async () => {
|
|
458
|
+
// The floor moves in the same transaction as the purge (ADR-0059).
|
|
459
|
+
const purgedUpTo = await connection.scalar('SELECT max(local_seq) FROM g9_rows WHERE deleted = 1 AND dirty = 0 AND local_seq < $x', { $x: threshold });
|
|
460
|
+
if (purgedUpTo === null)
|
|
461
|
+
return 0;
|
|
462
|
+
const purged = await connection.exec('DELETE FROM g9_rows WHERE deleted = 1 AND dirty = 0 AND local_seq < $x', { $x: threshold });
|
|
463
|
+
await connection.exec("INSERT INTO g9_meta (key, value) VALUES ('feed_floor_seq', $f) ON CONFLICT(key) DO UPDATE SET value = excluded.value WHERE CAST(value AS INTEGER) < CAST(excluded.value AS INTEGER)", { $f: asLong(purgedUpTo).toString() });
|
|
464
|
+
return purged;
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
export async function getChangeFeedFloor(store) {
|
|
469
|
+
store.ensureInitialized();
|
|
470
|
+
const text = await store.reader().scalar("SELECT value FROM g9_meta WHERE key = 'feed_floor_seq'");
|
|
471
|
+
if (typeof text !== 'string' || !/^[+-]?\d+$/.test(text.trim()))
|
|
472
|
+
return 0;
|
|
473
|
+
return Number(text.trim());
|
|
474
|
+
}
|
|
475
|
+
export { ticksToUnixMs };
|
|
476
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/sqlite/query.ts"],"names":[],"mappings":"AAAA,sHAAsH;AACtH,gDAAgD;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAYpE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAA2B,MAAM,iBAAiB,CAAC;AAExG,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,iBAAiB,EAAwC,MAAM,gBAAgB,CAAC;AACpI,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAElE,2EAA2E;AAE3E,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,UAA8B,EAAE,KAAa;IAC/E,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,8HAA8H,EAAE;QAClK,EAAE,EAAE,KAAK;KACV,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAgD,CAAC;IACjH,MAAM,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAsC,CAAC;IAC1G,OAAO;QACL,KAAK;QACL,IAAI,EAAE,YAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7C,SAAS,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,WAAC,OAAA,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAA,CAAC,CAAC,OAAO,mCAAI,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA,EAAA,CAAC;QACxK,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5B,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,kBAAkB,EAAE,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,EAAE;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAqB,EAAE,UAAyB;IAClF,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IAClC,MAAM,KAAK,GACT,UAAU,KAAK,IAAI;QACjB,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,oEAAoE,CAAC;QAC/F,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,0FAA0F,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IAC9I,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAqB,EAAE,cAAsB,EAAE,QAAgB,EAAE,cAA6B;IACnI,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC9B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,yDAAyD,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtJ,2GAA2G;QAC3G,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,MAAM,CAAC,IAAI,MAAM,UAAU,CAAC,KAAK,CACpC,6HAA6H;YAC3H,wGAAwG,EAC1G,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,aAAd,cAAc,cAAd,cAAc,GAAI,EAAE,EAAE,CACjD,EAAE,CAAC;YACF,IAAI,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACvB,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,MAAM;YAAE,MAAM,UAAU,CAAC,IAAI,CAAC,qFAAqF,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACxJ,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,KAAqB;IAC/D,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC9B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,oCAAoC,CAAC,CAAC,CAAC;QACtF,MAAM,UAAU,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QAC1E,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAqB,EAAE,IAAkB;IAC7E,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC7B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;;QACjD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,yEAAyE;QACzE,MAAM,UAAU,CAAC,IAAI,CACnB,kJAAkJ,EAClJ,EAAE,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,CACxB,CAAC;QACF,+GAA+G;QAC/G,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,uCAAuC,CAAC,CAAC,CAAC;QACjG,MAAM,WAAW,GAAqC,EAAE,CAAC;QACzD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC;YAAE,WAAW,CAAC,GAAG,CAAC,GAAG,MAAA,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,mCAAI,IAAI,CAAC;QAChH,MAAM,UAAU,CAAC,IAAI,CACnB,uKAAuK,EACvK;YACE,EAAE,EAAE,IAAI,CAAC,KAAK;YACd,EAAE,EAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YAC5B,EAAE,EAAE,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC;YACjC,EAAE,EAAE,IAAI,CAAC,YAAY;YACrB,EAAE,EAAE,IAAI,CAAC,UAAU;YACnB,EAAE,EAAE,aAAa;YACjB,uGAAuG;YACvG,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;SAChC,CACF,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAqB,EAAE,KAAa,EAAE,KAAoB,EAAE,UAAqF;IAChL,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;QACnC,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,CAAC;YACH,MAAM,UAAU,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;;gBACnD,MAAM,UAAU,CAAC,IAAI,CACnB,wIAAwI,EACxI;oBACE,EAAE,EAAE,KAAK;oBACT,EAAE,EAAE,KAAK,CAAC,QAAQ;oBAClB,EAAE,EAAE,MAAA,KAAK,CAAC,eAAe,mCAAI,IAAI;oBACjC,EAAE,EAAE,KAAK,CAAC,QAAQ;oBAClB,EAAE,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI;oBACjE,EAAE,EAAE,MAAA,KAAK,CAAC,OAAO,mCAAI,IAAI;iBAC1B,CACF,CAAC;gBACF,IAAI,KAAK,CAAC,OAAO;oBAAE,MAAM,UAAU,CAAC,IAAI,CAAC,yDAAyD,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YACrH,CAAC,CAAC,CAAC;QACL,CAAC;gBAAS,CAAC;YACT,IAAI,OAAO;gBAAE,MAAM,OAAO,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAqB,EAAE,KAAa;IACvE,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC9B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,sDAAsD,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5G,MAAM,QAAQ,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,CAAC,CAAC;QACxD,IAAI,YAAY,GAAG,IAAI,CAAC;QACxB,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,2HAA2H,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YAChL,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,IAAI,CAAC,EAAE,CAAC;gBACN,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9B,YAAY,GAAG,gBAAgB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7D,CAAC;YACD,MAAM,UAAU,CAAC,IAAI,CAAC,qDAAqD,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;QAChH,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,uGAAuG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnK,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;IACjI,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAqB,EAAE,KAAa;IAC1E,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,qFAAqF,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAC7J,CAAC;AAED,wFAAwF;AAExF,SAAS,eAAe,CAAC,KAAiB,EAAE,KAA0B;IACpE,OAAO,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1E,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,UAA8B,EAAE,KAAiB,EAAE,eAAuB;IAClG,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,MAAM,CAAC,SAAS;QACrB,CAAC,CAAC,UAAU,CAAC,KAAK,CACd,wGAAwG;YACtG,uGAAuG,EACzG,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,EAAE,eAAe,EAAE,CACvC;QACH,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,8HAA8H,EAAE;YAC/I,EAAE,EAAE,KAAK,CAAC,GAAG;YACb,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;SACrB,CAAC,CAAC;AACT,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAqB,EAAE,eAAuB,EAAE,KAA0B;;IAC5G,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,IAAI,0BAA0B,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IAClC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,IAAI,MAAA,CAAC,MAAM,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,mCAAI,EAAE,EAAE,CAAC;YAC7E,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,GAAI,MAAM,CAAC,MAAM,CAAY,GAAG,CAAC,CAAC;YAChD,OAAO,CAAC,MAAM,CAAC,GAAI,OAAO,CAAC,MAAM,CAAY,CAAC,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChG,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,KAAK,IAAI,MAAM,iBAAiB,CAAC,UAAU,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC1F,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAY,GAAG,CAAC,CAAC;YAC5D,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,GAAI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAChF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAqB,EACrB,eAAuB,EACvB,KAA0B,EAC1B,OAA4B,EAC5B,UAA+B;;IAE/B,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC,IAAI,0BAA0B,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;IACnD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IAClC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,IAAI,MAAA,CAAC,MAAM,UAAU,CAAC,UAAU,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,mCAAI,EAAE,EAAE,CAAC;YAC7E,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,SAAS;YACnC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzJ,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,KAAK,IAAI,MAAM,iBAAiB,CAAC,UAAU,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,CAAC;YAC1F,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACxD,KAAK,MAAM,KAAK,IAAI,MAAM,WAAW,CAAC,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,gEAAgE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAC7J,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,UAA8B,EAAE,KAAiB,EAAE,eAAuB,EAAE,QAAkB;IAC7H,MAAM,MAAM,GAAG,CAAC,MAAM,iBAAiB,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,OAAO,MAAM,CAAC,SAAS;QACrB,CAAC,CAAC,WAAW,CACT,UAAU,EACV,KAAK,EACL,QAAQ,EACR,wIAAwI,EACxI,IAAI,EACJ,EAAE,IAAI,EAAE,eAAe,EAAE,CAC1B;QACH,CAAC,CAAC,WAAW,CAAC,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,yDAAyD,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC5I,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,UAA8B,EAC9B,KAAiB,EACjB,QAAkB,EAClB,KAAa,EACb,MAAe,EACf,MAA+C;IAE/C,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAsB,CAAC,IAAI,CAAC,CAAC;IACxH,uHAAuH;IACvH,0CAA0C;IAC1C,MAAM,MAAM,GACV,oCAAoC;QACpC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QAChF,iBAAiB;QACjB,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,SAAS,SAAS,CAAC;QAC5F,0BAA0B;QAC1B,KAAK,CAAC;IACR,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACpB,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,MAAM,GAAsB,EAAE,CAAC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAe,CAAC;YACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAW,EAAE,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAClL,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;IAC7K,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2EAA2E;AAE3E,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAqB;IAC3D,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IAClC,MAAM,MAAM,GAAqB,CAC/B,MAAM,UAAU,CAAC,KAAK,CACpB,uBAAuB;QACrB,mEAAmE;QACnE,mEAAmE;QACnE,kDAAkD;QAClD,iHAAiH,CACpH,CACF,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3H,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,kDAAkD,CAAC,CAAC,CAAC;IAC9G,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,SAAS,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QACnC,YAAY,IAAI,CAAC,CAAC,QAAQ,CAAC;IAC7B,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAqB,EAAE,UAAkB,EAAE,aAAqB,EAAE,KAAa;IAClH,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,MAAM,KAAK;SACrB,MAAM,EAAE;SACR,KAAK,CAAC,yIAAyI,EAAE;QAChJ,EAAE,EAAE,KAAK,CAAC,GAAG;QACb,EAAE,EAAE,aAAa;QACjB,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC;KACvB,CAAC,CAAC;IACL,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;AACjJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAqB;IAC3D,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,OAAO,QAAQ,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,uCAAuC,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAqB;IACvD,MAAM,IAAI,GAAG,MAAM,KAAK;SACrB,MAAM,EAAE;SACR,KAAK,CACJ,mHAAmH;QACjH,+IAA+I,CAClJ,CAAC;IACJ,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpB,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9B,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACxB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAqB,EAAE,UAAyB,EAAE,GAAkB;IACtG,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,IAAI,KAAK,GAAG,cAAc,CAAC;IAC3B,MAAM,MAAM,GAAmC,EAAE,CAAC;IAClD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,KAAK,IAAI,eAAe,CAAC;QACzB,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC;IAC9C,CAAC;IACD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,KAAK,IAAI,eAAe,CAAC;QACzB,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC9B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,UAAU,CAAC,IAAI,CAAC,kFAAkF,GAAG,KAAK,GAAG,GAAG,EAAE,MAAM,CAAC,CAAC;QAChI,OAAO,UAAU,CAAC,IAAI,CAAC,wCAAwC,GAAG,KAAK,EAAE,MAAM,CAAC,CAAC;IACnF,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAqB,EAAE,UAAkB,EAAE,IAAuB;IAChG,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,WAAC,OAAA,MAAA,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,mCAAI,CAAC,CAAA,EAAA,CAAC,CAAC;IACxF,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC9B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;YAC3D,MAAM,IAAI,MAAM,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,0CAA0C,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,0BAA0B,GAAG,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpM,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,kGAAkG;AAClG,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAA8B,EAAE,KAAiB,EAAE,OAAe,EAAE,MAAsC,EAAE,GAAW;IACvJ,MAAM,UAAU,CAAC,IAAI,CAAC,uHAAuH,CAAC,CAAC;IAC/I,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,8GAA8G,OAAO,yBAAyB,EAAE,MAAM,CAAC,CAAC;IAC5L,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,uCAAuC,CAAC,CAAC,CAAC;IACzF,MAAM,UAAU,CAAC,IAAI,CAAC,yEAAyE,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IACpH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAClC,sBAAsB,GAAG,wCAAwC,MAAM,iGAAiG,EACxK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,CACjC,CAAC;IACF,MAAM,UAAU,CAAC,IAAI,CAAC,4EAA4E,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACnH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAqB;IAC3D,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC9B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,OAAO,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,sDAAsD,aAAa,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,SAAS,KAAK,CAAC,MAAM,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7K,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,0FAA0F;AAE1F,yHAAyH;AACzH,SAAS,YAAY,CAAC,MAAuB;IAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IAClI,OAAO,aAAa,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,UAAU,EAAE,WAAW,CAAC,YAAY,CAAC,GAAG,GAAG;QAC3C,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,iBAAiB,EAAE,MAAM,CAAC,iBAAiB;QAC3C,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,aAAa,EAAE,MAAM,CAAC,aAAa;KACpC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;;IAClC,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,CAAsC,CAAC;IAC/D,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC7C,MAAM,GAAG,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjG,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAChH,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QACjD,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO;QAC1C,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC;QAC7B,OAAO,EAAE,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;QACvD,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;QAC1D,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;QAC/D,YAAY,EAAE,OAAO,CAAC,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI;QACxE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;QACrB,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;QACzB,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3B,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC;QAC7B,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;QACvB,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC;QACvC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;QAChF,OAAO,EAAE,MAAC,CAAC,CAAC,OAAuC,mCAAI,EAAE;QACzD,eAAe,EAAE,OAAO,CAAC,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI;QAClF,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE;QACtF,iBAAiB,EAAE,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC;QAC3C,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC;QACvC,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC;QACrC,gBAAgB,EAAE,MAAC,CAAC,CAAC,gBAAgD,mCAAI,EAAE;QAC3E,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAqB,EAAE,MAAuB,EAAE,IAAY;IAC9F,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;IACpF,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC7B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,UAAU,CAAC,IAAI,CAAC,uFAAuF,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,CAAC,YAAY,EAAE,EAAE,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjL,MAAM,UAAU,CAAC,IAAI,CAAC,yGAAyG,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9J,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,KAAqB,EAAE,KAAa;IAC5E,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,oEAAoE,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IAC1I,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,IAAI,MAAM;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAqB;IAC5D,MAAM,UAAU,GAA8B,EAAE,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,iCAAiC,CAAC;QAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3H,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,KAAqB,EAAE,OAAkC;IACnG,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC7B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAW,CAAC;YACrC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;gBAAE,SAAS;YAC5C,MAAM,KAAK;iBACR,MAAM,EAAE;iBACR,IAAI,CACH,gKAAgK,EAChK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,CACvB,CAAC;QACN,CAAC;IACH,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAqB,EAAE,IAAY;IACxE,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;QACpC,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,2FAA2F,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAClK,IAAI,SAAS,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC;QACjC,OAAO,UAAU,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;YACpD,mEAAmE;YACnE,MAAM,UAAU,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,uFAAuF,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;YACvJ,IAAI,UAAU,KAAK,IAAI;gBAAE,OAAO,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,wEAAwE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;YAClI,MAAM,UAAU,CAAC,IAAI,CACnB,qLAAqL,EACrL,EAAE,EAAE,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE,EAAE,CACtC,CAAC;YACF,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAqB;IAC5D,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,wDAAwD,CAAC,CAAC;IACnG,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1E,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,CAAC","sourcesContent":["// Twin of G9SyncData.Client.Sqlite/G9CSqliteSyncStore.Query.cs: staging, reconciliation reads, pending counts, change\n// feed, rejections, run reports and statistics.\nimport { G9SyncDigest } from '../abstractions/digest.js';\nimport { G9SyncKey } from '../abstractions/key.js';\nimport { G9Long, parseJson } from '../abstractions/long.js';\nimport { guidToN, tryNormalizeGuid } from '../abstractions/guid.js';\nimport type {\n G9ChangeFeedEntry,\n G9LocalRowEntry,\n G9PendingSummary,\n G9PendingTable,\n G9ResumePoint,\n G9StagedPull,\n G9SyncRejection,\n G9SyncRunReport,\n} from '../client/storeTypes.js';\nimport type { G9DtNegotiatedTable, G9DtSyncChunk, G9DtTableDigest } from '../protocol/dtos.js';\nimport { readContinuation, readPullPlan } from '../protocol/wire.js';\nimport { asLong, asNumber, asOptionalString, asString, type G9SqliteConnection } from './connection.js';\nimport type { G9SqlValue } from './driver.js';\nimport { findByServerName, getLocal, persistedJson, readSubscriptions, type LocalTable, type StoreInternals } from './internals.js';\nimport { allColumnsMask, bit, keyExpression, literal, NOW_MS, quote } from './sql.js';\nimport { formatTicks, ticksOf, ticksToUnixMs } from './values.js';\n\n// ================================================================ staging\n\nexport async function readStagedRun(connection: G9SqliteConnection, runId: string): Promise<G9StagedPull | null> {\n const rows = await connection.query('SELECT plan_json, backfills_json, created_utc, generation, stage_seq, from_cursors_json FROM g9_stage_runs WHERE run_id = $r', {\n $r: runId,\n });\n const r = rows[0];\n if (!r) return null;\n const backfills = r[1] === null ? [] : (parseJson(asString(r[1])) as { tableId?: number; columns?: string[] }[]);\n const fromCursors = r[5] === null ? {} : (JSON.parse(asString(r[5])) as { [key: string]: string | null });\n return {\n runId,\n plan: readPullPlan(parseJson(asString(r[0]))),\n backfills: (Array.isArray(backfills) ? backfills : []).map((b) => ({ tableId: Number(b.tableId ?? 0), columns: Array.isArray(b.columns) ? b.columns.map(String) : [] })),\n createdUtcMs: asNumber(r[2]),\n generation: asOptionalString(r[3]),\n stageSequence: asNumber(r[4]),\n plannedFromCursors: fromCursors ?? {},\n };\n}\n\nexport async function getStagedPull(store: StoreInternals, generation: string | null): Promise<G9StagedPull | null> {\n const connection = store.reader();\n const runId =\n generation === null\n ? await connection.scalar('SELECT run_id FROM g9_stage_runs ORDER BY created_utc DESC LIMIT 1')\n : await connection.scalar('SELECT run_id FROM g9_stage_runs WHERE generation = $g ORDER BY created_utc DESC LIMIT 1', { $g: generation });\n return runId === null ? null : readStagedRun(connection, asString(runId));\n}\n\nexport async function pruneStagedPulls(store: StoreInternals, olderThanUtcMs: number, maxBytes: number, keepGeneration: string | null): Promise<number> {\n return store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n const connection = store.writer();\n const doomed = (await connection.query('SELECT run_id FROM g9_stage_runs WHERE created_utc < $c', { $c: olderThanUtcMs })).map((r) => asString(r[0]));\n // The byte cap is for the stages nobody is about to resume: newest first, past the cap they go (ADR-0060).\n let kept = 0;\n for (const r of await connection.query(\n 'SELECT r.run_id, (SELECT coalesce(sum(length(payload)), 0) FROM g9_stage s WHERE s.run_id = r.run_id) FROM g9_stage_runs r ' +\n 'WHERE r.created_utc >= $c AND (r.generation IS NULL OR r.generation <> $g) ORDER BY r.created_utc DESC',\n { $c: olderThanUtcMs, $g: keepGeneration ?? '' },\n )) {\n kept += asNumber(r[1]);\n if (kept > Math.max(0, maxBytes)) doomed.push(asString(r[0]));\n }\n for (const runId of doomed) await connection.exec('DELETE FROM g9_stage WHERE run_id = $r; DELETE FROM g9_stage_runs WHERE run_id = $r', { $r: runId });\n return doomed.length;\n }),\n );\n}\n\nexport async function discardAllStagedPulls(store: StoreInternals): Promise<number> {\n return store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n const connection = store.writer();\n const count = asNumber(await connection.scalar('SELECT count(*) FROM g9_stage_runs'));\n await connection.exec('DELETE FROM g9_stage; DELETE FROM g9_stage_runs;');\n return count;\n }),\n );\n}\n\nexport async function beginStagedPull(store: StoreInternals, pull: G9StagedPull): Promise<void> {\n await store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n const connection = store.writer();\n // Only the stage this one replaces goes: the same generation (ADR-0060).\n await connection.exec(\n 'DELETE FROM g9_stage WHERE run_id IN (SELECT run_id FROM g9_stage_runs WHERE generation IS $g); DELETE FROM g9_stage_runs WHERE generation IS $g',\n { $g: pull.generation },\n );\n // Read inside the transaction that inserts the run: rows written after it are newer than the stage (ADR-0046).\n const stageSequence = asNumber(await connection.scalar('SELECT seq FROM g9_clock WHERE id = 1'));\n const fromCursors: { [key: string]: string | null } = {};\n for (const key of Object.keys(pull.plannedFromCursors)) fromCursors[key] = pull.plannedFromCursors[key] ?? null;\n await connection.exec(\n 'INSERT INTO g9_stage_runs (run_id, plan_json, backfills_json, created_utc, complete, generation, stage_seq, from_cursors_json) VALUES ($r, $p, $b, $c, 0, $g, $s, $f)',\n {\n $r: pull.runId,\n $p: persistedJson(pull.plan),\n $b: persistedJson(pull.backfills),\n $c: pull.createdUtcMs,\n $g: pull.generation,\n $s: stageSequence,\n // Dictionary<string, string?>: a null value stays (DefaultIgnoreCondition only drops null PROPERTIES).\n $f: JSON.stringify(fromCursors),\n },\n );\n }),\n );\n}\n\nexport async function stageChunk(store: StoreInternals, runId: string, chunk: G9DtSyncChunk, durability: (connection: G9SqliteConnection) => Promise<(() => Promise<void>) | null>): Promise<void> {\n await store.writeLock.run(async () => {\n const connection = store.writer();\n const restore = await durability(connection);\n try {\n await connection.transaction('immediate', async () => {\n await connection.exec(\n 'INSERT OR REPLACE INTO g9_stage (run_id, seq, subscription_key, row_count, continuation_json, payload) VALUES ($r, $s, $k, $n, $c, $p)',\n {\n $r: runId,\n $s: chunk.sequence,\n $k: chunk.subscriptionKey ?? null,\n $n: chunk.rowCount,\n $c: chunk.continuation ? persistedJson(chunk.continuation) : null,\n $p: chunk.payload ?? null,\n },\n );\n if (chunk.isFinal) await connection.exec('UPDATE g9_stage_runs SET complete = 1 WHERE run_id = $r', { $r: runId });\n });\n } finally {\n if (restore) await restore();\n }\n });\n}\n\nexport async function getResumePoint(store: StoreInternals, runId: string): Promise<G9ResumePoint> {\n return store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n const connection = store.writer();\n const flag = await connection.scalar('SELECT complete FROM g9_stage_runs WHERE run_id = $r', { $r: runId });\n const complete = typeof flag === 'number' && flag !== 0;\n let continuation = null;\n let lastSequence = -1;\n if (!complete) {\n const rows = await connection.query('SELECT seq, continuation_json FROM g9_stage WHERE run_id = $r AND continuation_json IS NOT NULL ORDER BY seq DESC LIMIT 1', { $r: runId });\n const r = rows[0];\n if (r) {\n lastSequence = asNumber(r[0]);\n continuation = readContinuation(parseJson(asString(r[1])));\n }\n await connection.exec('DELETE FROM g9_stage WHERE run_id = $r AND seq > $s', { $r: runId, $s: lastSequence });\n }\n const totals = (await connection.query('SELECT coalesce(sum(row_count), 0), coalesce(sum(length(payload)), 0) FROM g9_stage WHERE run_id = $r', { $r: runId }))[0];\n return { continuation, stagedRows: totals ? asNumber(totals[0]) : 0, stagedBytes: totals ? asNumber(totals[1]) : 0, complete };\n }),\n );\n}\n\nexport async function discardStagedPull(store: StoreInternals, runId: string): Promise<void> {\n await store.writeLock.run(() => store.writer().exec('DELETE FROM g9_stage WHERE run_id = $r; DELETE FROM g9_stage_runs WHERE run_id = $r', { $r: runId }));\n}\n\n// ================================================================ reconciliation reads\n\nfunction computedColumns(local: LocalTable, table: G9DtNegotiatedTable): string[] {\n return table.computedColumns.filter((c) => local.model.indexOf(c) >= 0);\n}\n\nasync function memberRows(connection: G9SqliteConnection, local: LocalTable, subscriptionKey: string): Promise<(readonly G9SqlValue[])[] | null> {\n const records = await readSubscriptions(connection, subscriptionKey);\n const record = records[0];\n if (!record) return null;\n return record.ephemeral\n ? connection.query(\n 'SELECT r.pk, r.server_ver, r.dirty FROM g9_rows r WHERE r.tid = $t AND r.deleted = 0 AND r.dirty <> 2 ' +\n 'AND EXISTS (SELECT 1 FROM g9_ephemeral_rows e WHERE e.sub_key = $k AND e.tid = r.tid AND e.pk = r.pk)',\n { $t: local.tid, $k: subscriptionKey },\n )\n : connection.query('SELECT r.pk, r.server_ver, r.dirty FROM g9_rows r WHERE r.tid = $t AND r.deleted = 0 AND r.dirty <> 2 AND (r.subs & $b) <> 0', {\n $t: local.tid,\n $b: bit(record.slot),\n });\n}\n\nexport async function computeDigest(store: StoreInternals, subscriptionKey: string, table: G9DtNegotiatedTable): Promise<G9DtTableDigest> {\n store.ensureInitialized();\n const local = findByServerName(store, table.name);\n if (!local) throw new RangeError(`'${table.name}' is not a synced table.`);\n const computed = computedColumns(local, table);\n const digests: G9Long[] = [];\n const counts: number[] = [];\n for (let i = 0; i < 256; i++) {\n digests.push(G9Long.ZERO);\n counts.push(0);\n }\n const connection = store.reader();\n if (computed.length === 0) {\n for (const r of (await memberRows(connection, local, subscriptionKey)) ?? []) {\n const key = asString(r[0]);\n const bucket = G9SyncDigest.getBucket(key);\n counts[bucket] = (counts[bucket] as number) + 1;\n digests[bucket] = (digests[bucket] as G9Long).xor(G9SyncDigest.getRowHash(key, asLong(r[1])));\n }\n } else {\n for (const entry of await readMemberEntries(connection, local, subscriptionKey, computed)) {\n counts[entry.bucket] = (counts[entry.bucket] as number) + 1;\n digests[entry.bucket] = (digests[entry.bucket] as G9Long).xor(entry.hash);\n }\n }\n return { tableId: table.tableId, computedColumns: computed, digests, counts };\n}\n\nexport async function readLocalEntries(\n store: StoreInternals,\n subscriptionKey: string,\n table: G9DtNegotiatedTable,\n buckets: ReadonlySet<number>,\n serverKeys: ReadonlySet<string>,\n): Promise<G9LocalRowEntry[]> {\n store.ensureInitialized();\n const local = findByServerName(store, table.name);\n if (!local) throw new RangeError(`'${table.name}' is not a synced table.`);\n const computed = computedColumns(local, table);\n const entries = new Map<string, G9LocalRowEntry>();\n const connection = store.reader();\n if (computed.length === 0) {\n for (const r of (await memberRows(connection, local, subscriptionKey)) ?? []) {\n const key = asString(r[0]);\n const bucket = G9SyncDigest.getBucket(key);\n if (!buckets.has(bucket)) continue;\n const version = asLong(r[1]);\n entries.set(key, { key, bucket, version: version.toNumber(), hash: G9SyncDigest.getRowHash(key, version), member: true, dirty: asNumber(r[2]) !== 0 });\n }\n } else {\n for (const entry of await readMemberEntries(connection, local, subscriptionKey, computed)) {\n if (buckets.has(entry.bucket)) entries.set(entry.key, entry);\n }\n }\n const missing: string[] = [];\n serverKeys.forEach((key) => {\n if (!entries.has(key)) missing.push(key);\n });\n for (let i = 0; i < missing.length; i += 5000) {\n const json = JSON.stringify(missing.slice(i, i + 5000));\n for (const entry of await readEntries(connection, local, computed, 'r.deleted = 0 AND r.pk IN (SELECT value FROM json_each($keys))', false, { $keys: json })) {\n entries.set(entry.key, entry);\n }\n }\n return Array.from(entries.values());\n}\n\nasync function readMemberEntries(connection: G9SqliteConnection, local: LocalTable, subscriptionKey: string, computed: string[]): Promise<G9LocalRowEntry[]> {\n const record = (await readSubscriptions(connection, subscriptionKey))[0];\n if (!record) return [];\n return record.ephemeral\n ? readEntries(\n connection,\n local,\n computed,\n 'r.deleted = 0 AND r.dirty <> 2 AND EXISTS (SELECT 1 FROM g9_ephemeral_rows e WHERE e.sub_key = $sub AND e.tid = r.tid AND e.pk = r.pk)',\n true,\n { $sub: subscriptionKey },\n )\n : readEntries(connection, local, computed, 'r.deleted = 0 AND r.dirty <> 2 AND (r.subs & $bit) <> 0', true, { $bit: bit(record.slot) });\n}\n\nasync function readEntries(\n connection: G9SqliteConnection,\n local: LocalTable,\n computed: string[],\n where: string,\n member: boolean,\n params: { [name: string]: G9SqlValue | G9Long },\n): Promise<G9LocalRowEntry[]> {\n const computedTypes = computed.map((name) => (local.model.columns[local.model.indexOf(name)] as { type: number }).type);\n // typeof() next to each computed value: JavaScript cannot tell an INTEGER 5 from a REAL 5.0, and their canonical texts\n // can differ (1E+15 vs 1000000000000000).\n const select =\n 'SELECT r.pk, r.server_ver, r.dirty' +\n computed.map((c) => ', b.' + quote(c) + ', typeof(b.' + quote(c) + ')').join('') +\n ' FROM g9_rows r' +\n (computed.length === 0 ? '' : ` LEFT JOIN ${local.quoted} b ON b.${local.keyQuoted} = r.pk`) +\n ' WHERE r.tid = $tid AND ' +\n where;\n const rows = await connection.query(select, { $tid: local.tid, ...params });\n return rows.map((r) => {\n const key = asString(r[0]);\n const version = asLong(r[1]);\n const values: (string | null)[] = [];\n for (let i = 0; i < computed.length; i++) {\n const value = r[3 + i * 2] as G9SqlValue;\n const storage = asString(r[4 + i * 2]);\n values.push(value === null ? null : G9SyncDigest.canonicalText(computedTypes[i] as number, value, storage === 'real' ? 'real' : storage === 'integer' ? 'integer' : undefined));\n }\n return { key, bucket: G9SyncDigest.getBucket(key), version: version.toNumber(), hash: G9SyncDigest.getRowHash(key, version, values), member, dirty: asNumber(r[2]) !== 0 };\n });\n}\n\n// ================================================================ queries\n\nexport async function getPendingSummary(store: StoreInternals): Promise<G9PendingSummary> {\n const connection = store.reader();\n const tables: G9PendingTable[] = (\n await connection.query(\n 'SELECT t.local_name, ' +\n 'sum(CASE WHEN r.dirty = 1 AND r.rejected = 0 THEN 1 ELSE 0 END), ' +\n 'sum(CASE WHEN r.dirty = 2 AND r.rejected = 0 THEN 1 ELSE 0 END), ' +\n 'sum(CASE WHEN r.rejected = 1 THEN 1 ELSE 0 END) ' +\n 'FROM g9_rows r JOIN g9_tables t ON t.tid = r.tid WHERE r.dirty <> 0 GROUP BY t.local_name ORDER BY t.local_name',\n )\n ).map((r) => ({ localTable: asString(r[0]), upserts: asNumber(r[1]), deletes: asNumber(r[2]), rejected: asNumber(r[3]) }));\n const pendingCommands = asNumber(await connection.scalar('SELECT count(*) FROM g9_commands WHERE state = 0'));\n let totalRows = 0;\n let rejectedRows = 0;\n for (const t of tables) {\n totalRows += t.upserts + t.deletes;\n rejectedRows += t.rejected;\n }\n return { tables, totalRows, rejectedRows, pendingCommands };\n}\n\nexport async function readChangeFeed(store: StoreInternals, localTable: string, afterSequence: number, limit: number): Promise<G9ChangeFeedEntry[]> {\n store.ensureInitialized();\n const table = getLocal(store, localTable);\n const rows = await store\n .reader()\n .query('SELECT local_seq, pk, (deleted = 1 OR dirty = 2), dirty <> 0 FROM g9_rows WHERE tid = $t AND local_seq > $a ORDER BY local_seq LIMIT $l', {\n $t: table.tid,\n $a: afterSequence,\n $l: Math.max(1, limit),\n });\n return rows.map((r) => ({ sequence: asNumber(r[0]), key: asString(r[1]), deleted: asNumber(r[2]) !== 0, localPending: asNumber(r[3]) !== 0 }));\n}\n\nexport async function getChangeSequence(store: StoreInternals): Promise<number> {\n store.ensureInitialized();\n return asNumber(await store.reader().scalar('SELECT seq FROM g9_clock WHERE id = 1'));\n}\n\nexport async function getRejections(store: StoreInternals): Promise<G9SyncRejection[]> {\n const rows = await store\n .reader()\n .query(\n 'SELECT t.local_name, j.pk, j.code, j.message, j.sql_state, j.constraint_name, j.column_name, j.details, j.at_utc ' +\n 'FROM g9_rejections j JOIN g9_tables t ON t.tid = j.tid JOIN g9_rows r ON r.tid = j.tid AND r.pk = j.pk WHERE r.rejected = 1 ORDER BY j.at_utc',\n );\n return rows.map((r) => ({\n localTable: asString(r[0]),\n key: asString(r[1]),\n code: asString(r[2]),\n message: asOptionalString(r[3]),\n sqlState: asOptionalString(r[4]),\n constraint: asOptionalString(r[5]),\n column: asOptionalString(r[6]),\n details: asOptionalString(r[7]),\n atUtcMs: asNumber(r[8]),\n }));\n}\n\nexport async function retryRejected(store: StoreInternals, localTable: string | null, key: string | null): Promise<number> {\n store.ensureInitialized();\n let where = 'rejected = 1';\n const params: { [name: string]: G9SqlValue } = {};\n if (localTable !== null) {\n where += ' AND tid = $t';\n params.$t = getLocal(store, localTable).tid;\n }\n if (key !== null) {\n where += ' AND pk = $pk';\n params.$pk = key;\n }\n return store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n const connection = store.writer();\n await connection.exec('DELETE FROM g9_rejections WHERE (tid, pk) IN (SELECT tid, pk FROM g9_rows WHERE ' + where + ')', params);\n return connection.exec('UPDATE g9_rows SET rejected = 0 WHERE ' + where, params);\n }),\n );\n}\n\nexport async function markDirty(store: StoreInternals, localTable: string, keys: readonly string[]): Promise<number> {\n store.ensureInitialized();\n const table = getLocal(store, localTable);\n const normalized = keys.map((k) => G9SyncKey.tryNormalize(table.model.keyType, k) ?? k);\n return store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n let marked = 0;\n for (let i = 0; i < normalized.length; i += 5000) {\n const json = JSON.stringify(normalized.slice(i, i + 5000));\n marked += await markPending(store.writer(), table, 'SELECT value AS pk FROM json_each($keys)', { $keys: json }, 'dirty = 1, dirty_mask = ' + literal(allColumnsMask(table.model.columns.length)));\n }\n return marked;\n }),\n );\n}\n\n/** Marks the rows `keysSql` selects as pending, giving EACH its own local sequence (LES-0039). */\nexport async function markPending(connection: G9SqliteConnection, table: LocalTable, keysSql: string, params: { [name: string]: G9SqlValue }, set: string): Promise<number> {\n await connection.exec('CREATE TEMP TABLE IF NOT EXISTS g9_mark (pk TEXT NOT NULL PRIMARY KEY, n INTEGER NOT NULL); DELETE FROM temp.g9_mark;');\n const count = await connection.exec(`INSERT INTO temp.g9_mark (pk, n) SELECT pk, row_number() OVER (ORDER BY pk) FROM (SELECT DISTINCT pk FROM (${keysSql}) WHERE pk IS NOT NULL)`, params);\n if (count === 0) return 0;\n const first = asNumber(await connection.scalar('SELECT seq FROM g9_clock WHERE id = 1'));\n await connection.exec('INSERT OR IGNORE INTO g9_rows (tid, pk) SELECT $t, pk FROM temp.g9_mark', { $t: table.tid });\n const marked = await connection.exec(\n `UPDATE g9_rows SET ${set}, local_seq = $first + m.n, hlc_ms = ${NOW_MS}, rejected = 0, deleted = 0 FROM temp.g9_mark AS m WHERE g9_rows.tid = $t AND g9_rows.pk = m.pk`,\n { $first: first, $t: table.tid },\n );\n await connection.exec('UPDATE g9_clock SET seq = seq + $n WHERE id = 1; DELETE FROM temp.g9_mark;', { $n: count });\n return marked;\n}\n\nexport async function adoptExistingRows(store: StoreInternals): Promise<number> {\n store.ensureInitialized();\n return store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n let adopted = 0;\n for (const table of store.byLocal.values()) {\n adopted += await store.writer().exec(`INSERT OR IGNORE INTO g9_rows (tid, pk) SELECT $t, ${keyExpression(table.model, 'b')} FROM ${table.quoted} AS b`, { $t: table.tid });\n }\n return adopted;\n }),\n );\n}\n\n// ================================================================ reports and statistics\n\n/** The run report as .NET's G9SyncClientJsonContext writes it (camelCase, nulls omitted, DateTime/Guid in .NET text). */\nfunction reportToJson(report: G9SyncRunReport): string {\n const startedTicks = ticksOf(1970, 1, 1, 0, 0, 0, 0).add(G9Long.fromNumber(Math.floor(report.startedUtcMs)).multiplySmall(10000));\n return persistedJson({\n runId: report.runId,\n startedUtc: formatTicks(startedTicks) + 'Z',\n durationMs: report.durationMs,\n outcome: report.outcome,\n priority: report.priority,\n errorCode: report.errorCode,\n errorMessage: report.errorMessage,\n pushed: report.pushed,\n rejected: report.rejected,\n conflicts: report.conflicts,\n downloaded: report.downloaded,\n applied: report.applied,\n downloadedBytes: report.downloadedBytes,\n subscriptions: report.subscriptions,\n phaseMs: report.phaseMs,\n triggersSkipped: report.triggersSkipped,\n foreignTriggers: report.foreignTriggers,\n applyTransactions: report.applyTransactions,\n indexesDeferred: report.indexesDeferred,\n indexRebuildMs: report.indexRebuildMs,\n liveIndexReasons: report.liveIndexReasons,\n unchangedRows: report.unchangedRows,\n });\n}\n\nfunction reportFromJson(json: string): G9SyncRunReport | null {\n const v = parseJson(json) as { [key: string]: unknown } | null;\n if (!v || typeof v !== 'object') return null;\n const num = (x: unknown) => (typeof x === 'number' ? x : x instanceof G9Long ? x.toNumber() : 0);\n const started = typeof v.startedUtc === 'string' ? Date.parse(v.startedUtc.replace(/(\\.\\d{3})\\d+/, '$1')) : NaN;\n return {\n runId: typeof v.runId === 'string' ? v.runId : '',\n startedUtcMs: isNaN(started) ? 0 : started,\n durationMs: num(v.durationMs),\n outcome: typeof v.outcome === 'string' ? v.outcome : '',\n priority: typeof v.priority === 'string' ? v.priority : '',\n errorCode: typeof v.errorCode === 'string' ? v.errorCode : null,\n errorMessage: typeof v.errorMessage === 'string' ? v.errorMessage : null,\n pushed: num(v.pushed),\n rejected: num(v.rejected),\n conflicts: num(v.conflicts),\n downloaded: num(v.downloaded),\n applied: num(v.applied),\n downloadedBytes: num(v.downloadedBytes),\n subscriptions: Array.isArray(v.subscriptions) ? v.subscriptions.map(String) : [],\n phaseMs: (v.phaseMs as { [phase: string]: number }) ?? {},\n triggersSkipped: typeof v.triggersSkipped === 'boolean' ? v.triggersSkipped : null,\n foreignTriggers: Array.isArray(v.foreignTriggers) ? v.foreignTriggers.map(String) : [],\n applyTransactions: num(v.applyTransactions),\n indexesDeferred: num(v.indexesDeferred),\n indexRebuildMs: num(v.indexRebuildMs),\n liveIndexReasons: (v.liveIndexReasons as { [table: string]: string }) ?? {},\n unchangedRows: num(v.unchangedRows),\n };\n}\n\nexport async function saveRunReport(store: StoreInternals, report: G9SyncRunReport, keep: number): Promise<void> {\n const runId = tryNormalizeGuid(report.runId) ? guidToN(report.runId) : report.runId;\n await store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n const connection = store.writer();\n await connection.exec('INSERT OR REPLACE INTO g9_runs (run_id, started_utc, report_json) VALUES ($r, $s, $j)', { $r: runId, $s: report.startedUtcMs, $j: reportToJson(report) });\n await connection.exec('DELETE FROM g9_runs WHERE run_id NOT IN (SELECT run_id FROM g9_runs ORDER BY started_utc DESC LIMIT $k)', { $k: Math.max(1, keep) });\n }),\n );\n}\n\nexport async function getRecentRunReports(store: StoreInternals, count: number): Promise<G9SyncRunReport[]> {\n const rows = await store.reader().query('SELECT report_json FROM g9_runs ORDER BY started_utc DESC LIMIT $c', { $c: Math.max(1, count) });\n const reports: G9SyncRunReport[] = [];\n for (const r of rows) {\n const report = reportFromJson(asString(r[0]));\n if (report) reports.push(report);\n }\n return reports;\n}\n\nexport async function getPhaseStatistics(store: StoreInternals): Promise<{ [key: string]: number }> {\n const statistics: { [key: string]: number } = {};\n for (const r of await store.reader().query('SELECT key, value FROM g9_stats')) statistics[asString(r[0])] = asNumber(r[1]);\n return statistics;\n}\n\nexport async function recordPhaseStatistics(store: StoreInternals, samples: { [key: string]: number }): Promise<void> {\n await store.writeLock.run(() =>\n store.writer().transaction('immediate', async () => {\n for (const key of Object.keys(samples)) {\n const value = samples[key] as number;\n if (!isFinite(value) || value < 0) continue;\n await store\n .writer()\n .exec(\n 'INSERT INTO g9_stats (key, value, samples) VALUES ($k, $v, 1) ON CONFLICT(key) DO UPDATE SET value = value * 0.7 + excluded.value * 0.3, samples = samples + 1',\n { $k: key, $v: value },\n );\n }\n }),\n );\n}\n\nexport async function purgeDeletedFeed(store: StoreInternals, keep: number): Promise<number> {\n return store.writeLock.run(async () => {\n const connection = store.writer();\n const threshold = await connection.scalar('SELECT local_seq FROM g9_rows WHERE deleted = 1 ORDER BY local_seq DESC LIMIT 1 OFFSET $k', { $k: Math.max(0, keep) });\n if (threshold === null) return 0;\n return connection.transaction('immediate', async () => {\n // The floor moves in the same transaction as the purge (ADR-0059).\n const purgedUpTo = await connection.scalar('SELECT max(local_seq) FROM g9_rows WHERE deleted = 1 AND dirty = 0 AND local_seq < $x', { $x: threshold });\n if (purgedUpTo === null) return 0;\n const purged = await connection.exec('DELETE FROM g9_rows WHERE deleted = 1 AND dirty = 0 AND local_seq < $x', { $x: threshold });\n await connection.exec(\n \"INSERT INTO g9_meta (key, value) VALUES ('feed_floor_seq', $f) ON CONFLICT(key) DO UPDATE SET value = excluded.value WHERE CAST(value AS INTEGER) < CAST(excluded.value AS INTEGER)\",\n { $f: asLong(purgedUpTo).toString() },\n );\n return purged;\n });\n });\n}\n\nexport async function getChangeFeedFloor(store: StoreInternals): Promise<number> {\n store.ensureInitialized();\n const text = await store.reader().scalar(\"SELECT value FROM g9_meta WHERE key = 'feed_floor_seq'\");\n if (typeof text !== 'string' || !/^[+-]?\\d+$/.test(text.trim())) return 0;\n return Number(text.trim());\n}\n\nexport { ticksToUnixMs };\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { G9ApplyReport, G9ERevocationReason, type G9QuarantinedRow, type G9RetainedRow } from '../client/storeTypes.js';
|
|
2
|
+
import { type G9Params, type G9SqliteConnection } from './connection.js';
|
|
3
|
+
import { type LocalTable, type StoreInternals } from './internals.js';
|
|
4
|
+
export declare const REVOKED_REJECTION_CODE = "G9S-REJ-REVOKED";
|
|
5
|
+
/**
|
|
6
|
+
* Applies the revocation policy to a released row (`DisposeReleasedRow`). Rows still held by another subscription are
|
|
7
|
+
* untouched. Returns true when the row left the app's table (deleted or quarantined). Reads the row first, so it sees
|
|
8
|
+
* whatever the caller ran before it; a release of many rows goes through disposeReleasedRows.
|
|
9
|
+
*/
|
|
10
|
+
export declare function disposeReleasedRow(store: StoreInternals, connection: G9SqliteConnection, table: LocalTable, key: string, reason: G9ERevocationReason, subscriptionKey: string | null, report: G9ApplyReport | null): Promise<boolean>;
|
|
11
|
+
/**
|
|
12
|
+
* `DisposeReleasedRow` for every row `releasedSql` selects (`SELECT tid, pk …`, in the order the .NET store walks them),
|
|
13
|
+
* then the clock moves once per row of a known table — the database state the row-by-row path leaves, in a handful of
|
|
14
|
+
* driver calls instead of ~6 per row (on Lynx every call crosses the bridge: 25,000 rows took 850 s row by row,
|
|
15
|
+
* LES-0051). Everything is read before anything is written, which is exact because disposing a row writes nothing
|
|
16
|
+
* another released row's decision reads: the store's connections run with foreign keys OFF, and the CALLER must take the
|
|
17
|
+
* row-by-row path when the database holds a foreign trigger. Returns how many rows left the app's tables.
|
|
18
|
+
*/
|
|
19
|
+
export declare function disposeReleasedRows(store: StoreInternals, connection: G9SqliteConnection, releasedSql: string, releasedParams: G9Params, reason: G9ERevocationReason, subscriptionKey: string | null, report: G9ApplyReport | null): Promise<number>;
|
|
20
|
+
/** `RemoveLocalRow`: the row leaves the app's table; its metadata becomes a tombstone at the clock's next value. */
|
|
21
|
+
export declare function removeLocalRow(connection: G9SqliteConnection, table: LocalTable, key: string): Promise<void>;
|
|
22
|
+
export declare function getQuarantined(store: StoreInternals): Promise<G9QuarantinedRow[]>;
|
|
23
|
+
/** Puts a quarantined row back as a pending local change (capture is ON: the restored values upload on the next push). */
|
|
24
|
+
export declare function restoreQuarantined(store: StoreInternals, localTable: string, key: string): Promise<number>;
|
|
25
|
+
export declare function discardQuarantined(store: StoreInternals, localTable: string | null, key: string | null): Promise<number>;
|
|
26
|
+
export declare function getRetained(store: StoreInternals): Promise<G9RetainedRow[]>;
|
|
27
|
+
/** Re-checks rows retained by a RetainWhile rule; deletes those no longer retained (`ReevaluateRetainedAsync`). */
|
|
28
|
+
export declare function reevaluateRetained(store: StoreInternals): Promise<number>;
|