@g9tm/syncdata 1.5.12 → 1.5.13
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/dist/client/storeTypes.d.ts +2 -0
- package/dist/client/storeTypes.js +3 -0
- package/dist/client/storeTypes.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/sqlite/push.js +20 -2
- package/dist/sqlite/push.js.map +1 -1
- package/package.json +1 -1
|
@@ -159,6 +159,8 @@ export declare class G9ApplyReport {
|
|
|
159
159
|
conflicts: number;
|
|
160
160
|
/** Conflicts the device lost that were kept on it as rejections (LES-0072). */
|
|
161
161
|
conflictsKept: number;
|
|
162
|
+
/** Conflicts the device lost on paper only: the server already held its values for every lost column (LES-0085). */
|
|
163
|
+
conflictsSameValue: number;
|
|
162
164
|
commandsCompleted: number;
|
|
163
165
|
revokedDeleted: number;
|
|
164
166
|
revokedQuarantined: number;
|
|
@@ -145,6 +145,8 @@ export class G9ApplyReport {
|
|
|
145
145
|
this.conflicts = 0;
|
|
146
146
|
/** Conflicts the device lost that were kept on it as rejections (LES-0072). */
|
|
147
147
|
this.conflictsKept = 0;
|
|
148
|
+
/** Conflicts the device lost on paper only: the server already held its values for every lost column (LES-0085). */
|
|
149
|
+
this.conflictsSameValue = 0;
|
|
148
150
|
this.commandsCompleted = 0;
|
|
149
151
|
this.revokedDeleted = 0;
|
|
150
152
|
this.revokedQuarantined = 0;
|
|
@@ -171,6 +173,7 @@ export class G9ApplyReport {
|
|
|
171
173
|
this.rejected += other.rejected;
|
|
172
174
|
this.conflicts += other.conflicts;
|
|
173
175
|
this.conflictsKept += other.conflictsKept;
|
|
176
|
+
this.conflictsSameValue += other.conflictsSameValue;
|
|
174
177
|
this.commandsCompleted += other.commandsCompleted;
|
|
175
178
|
this.revokedDeleted += other.revokedDeleted;
|
|
176
179
|
this.revokedQuarantined += other.revokedQuarantined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storeTypes.js","sourceRoot":"","sources":["../../src/client/storeTypes.ts"],"names":[],"mappings":"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","sourcesContent":["// Twins of G9SyncData.Client/Storage/*.cs: the client store contract, its records, local commands, revocation rules\r\n// and apply diagnostics. The engine is store-agnostic; `@g9tm/syncdata/sqlite` is the implementation.\r\nimport type { G9EMoveOutPolicy } from '../abstractions/enums.js';\r\nimport type { G9Long } from '../abstractions/long.js';\r\nimport type { G9SyncParameters } from '../abstractions/parameters.js';\r\nimport type {\r\n G9DtBackfillRequest,\r\n G9DtCommand,\r\n G9DtHelloResult,\r\n G9DtNegotiatedTable,\r\n G9DtPullContinuation,\r\n G9DtPullPlan,\r\n G9DtPushResult,\r\n G9DtSnapshotFileRequest,\r\n G9DtSubscriptionDefinition,\r\n G9DtSubscriptionRef,\r\n G9DtSyncChunk,\r\n G9DtTableColumns,\r\n G9DtTableDigest,\r\n} from '../protocol/dtos.js';\r\nimport type { G9SyncClientModel } from './model.js';\r\n\r\n// ---------------------------------------------------------------- server state and subscriptions\r\n\r\n/** What the client last learned from the server (`G9CClientServerState`). */\r\nexport interface G9ClientServerState {\r\n epoch: string | null;\r\n lastHelloUtcMs: number | null;\r\n /** Server clock minus device clock, ms. */\r\n serverClockOffsetMs: number;\r\n tables: G9DtNegotiatedTable[];\r\n definitions: G9DtSubscriptionDefinition[];\r\n}\r\n\r\n/** `G9ESubscriptionState`. */\r\nexport const G9ESubscriptionState = {\r\n Active: 0,\r\n NeedsReconcile: 1,\r\n Failed: 2,\r\n} as const;\r\nexport type G9ESubscriptionState = (typeof G9ESubscriptionState)[keyof typeof G9ESubscriptionState];\r\n\r\n/** A local subscription (`G9CSyncSubscriptionRecord`). Times are epoch milliseconds UTC. */\r\nexport interface G9SyncSubscriptionRecord {\r\n key: string;\r\n name: string;\r\n parameters: G9SyncParameters;\r\n /** Membership slot (bit) for persistent subscriptions; the shared ephemeral slot otherwise. */\r\n slot: number;\r\n ephemeral: boolean;\r\n ttlMs: number | null;\r\n cursor: string | null;\r\n /** The move-out policy the server last announced; kept so it still applies once access is gone. */\r\n moveOutPolicy: G9EMoveOutPolicy | null;\r\n definitionHash: string | null;\r\n authHash: string | null;\r\n state: G9ESubscriptionState;\r\n lastErrorCode: string | null;\r\n createdUtcMs: number;\r\n lastSuccessUtcMs: number | null;\r\n lastReconcileUtcMs: number | null;\r\n lastAccessUtcMs: number | null;\r\n}\r\n\r\n/** `G9CSyncSubscriptionRecord.ToReference()`. */\r\nexport function toSubscriptionRef(record: G9SyncSubscriptionRecord): G9DtSubscriptionRef {\r\n return {\r\n name: record.name,\r\n parameters: record.parameters.values,\r\n cursor: record.cursor,\r\n definitionHash: record.definitionHash,\r\n authHash: record.authHash,\r\n };\r\n}\r\n\r\n/** A partial update of a subscription record; absent members are left unchanged (`G9CSubscriptionUpdate`). */\r\nexport interface G9SubscriptionUpdate {\r\n cursor?: string | null;\r\n definitionHash?: string | null;\r\n authHash?: string | null;\r\n state?: G9ESubscriptionState | null;\r\n errorCode?: string | null;\r\n clearError?: boolean;\r\n succeeded?: boolean;\r\n reconciled?: boolean;\r\n accessed?: boolean;\r\n clearCursor?: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------- push and pull\r\n\r\n/** A push batch built from dirty rows (`G9CPushBatch`). */\r\nexport interface G9PushBatch {\r\n batchId: number;\r\n /** Highest local change sequence included; rows changed after it stay dirty after the acknowledgement. */\r\n maxSequence: number;\r\n frames: Uint8Array[];\r\n tables: G9DtTableColumns[];\r\n rowCount: number;\r\n /** True when the byte budget ended the batch early: more pending rows follow. */\r\n truncated: boolean;\r\n /** Rows rejected on the device while building the batch (G9S-REJ-TOO-LARGE). */\r\n rejectedLocally: number;\r\n /** Commands carried by this batch, in device order. */\r\n commands: G9DtCommand[];\r\n}\r\n\r\n/** A pull being staged, persisted across restarts (`G9CStagedPull`). */\r\nexport interface G9StagedPull {\r\n runId: string;\r\n plan: G9DtPullPlan;\r\n createdUtcMs: number;\r\n backfills: G9DtBackfillRequest[];\r\n /** What the world looked like when the plan was made (ADR-0046); a run only resumes a stage whose generation matches. */\r\n generation: string | null;\r\n /** The cursor each planned subscription had when the plan was made, by subscription key (ADR-0060). */\r\n plannedFromCursors: { [subscriptionKey: string]: string | null };\r\n /** The device clock when the stage began, assigned by the store. */\r\n stageSequence: number;\r\n}\r\n\r\n/** One committed slice of a staged pull (`G9CApplySlice`). */\r\nexport interface G9ApplySlice {\r\n report: G9ApplyReport;\r\n rowsApplied: number;\r\n rowsAppliedTotal: number;\r\n rowsPlanned: number;\r\n isFinal: boolean;\r\n}\r\n\r\n/** `G9CResumePoint`. */\r\nexport interface G9ResumePoint {\r\n continuation: G9DtPullContinuation | null;\r\n stagedRows: number;\r\n stagedBytes: number;\r\n complete: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------- reports\r\n\r\n/** Why a table large enough for index deferral kept its indexes live. */\r\nexport const REASON_TABLE_NOT_EMPTY = 'table-not-empty';\r\nconst MAX_FOREIGN_TRIGGER_NAMES = 10;\r\n\r\n/** How an apply ran, as opposed to what it changed (`G9CApplyDiagnostics`, ADR-0058). */\r\n/** A file table copied record for record into an empty table with identical columns (the transfer optimisation). */\r\nexport const FILE_MODE_COPY = 'copy';\r\n/** A file table copied into an empty table by column name. */\r\nexport const FILE_MODE_COLUMNS = 'columns';\r\n/** A file table merged into a table that held rows or protected rows: changed rows only. */\r\nexport const FILE_MODE_MERGE = 'merge';\r\n/** A file table inserted into a table holding only OTHER rows (a second site's first load): every row is new, no per-row diff. */\r\nexport const FILE_MODE_APPEND = 'append';\r\n\r\nexport class G9ApplyDiagnostics {\r\n transactions = 0;\r\n transactionsWithTriggersSkipped = 0;\r\n readonly foreignTriggers: string[] = [];\r\n readonly deferredIndexes: { [table: string]: number } = {};\r\n readonly liveIndexReasons: { [table: string]: string } = {};\r\n readonly rowsByTable: { [table: string]: number } = {};\r\n readonly msByTable: { [table: string]: number } = {};\r\n indexRebuildMs = 0;\r\n elapsedMs = 0;\r\n /** Snapshot files copied in (ADR-0072). */\r\n snapshotFiles = 0;\r\n /** Rows that arrived through snapshot files. */\r\n fileRows = 0;\r\n /** Milliseconds spent copying snapshot files, metadata included. */\r\n fileCopyMs = 0;\r\n /** How each table came out of a snapshot file: `copy`, `columns` or `merge` (see FILE_MODE_*). */\r\n readonly fileTables: { [table: string]: string } = {};\r\n\r\n /** True when every transaction ran with trigger execution off, false when any ran with it on, null when nothing was applied. */\r\n get triggersSkipped(): boolean | null {\r\n return this.transactions === 0 ? null : this.transactionsWithTriggersSkipped === this.transactions;\r\n }\r\n\r\n get deferredIndexCount(): number {\r\n let total = 0;\r\n for (const key of Object.keys(this.deferredIndexes)) total += this.deferredIndexes[key] as number;\r\n return total;\r\n }\r\n\r\n merge(other: G9ApplyDiagnostics): void {\r\n this.transactions += other.transactions;\r\n this.transactionsWithTriggersSkipped += other.transactionsWithTriggersSkipped;\r\n this.indexRebuildMs += other.indexRebuildMs;\r\n this.elapsedMs += other.elapsedMs;\r\n this.snapshotFiles += other.snapshotFiles;\r\n this.fileRows += other.fileRows;\r\n this.fileCopyMs += other.fileCopyMs;\r\n for (const key of Object.keys(other.fileTables)) this.fileTables[key] = other.fileTables[key] as string;\r\n for (const name of other.foreignTriggers) {\r\n if (this.foreignTriggers.length >= MAX_FOREIGN_TRIGGER_NAMES) break;\r\n if (!this.foreignTriggers.some((n) => n.toLowerCase() === name.toLowerCase())) this.foreignTriggers.push(name);\r\n }\r\n addAll(this.deferredIndexes, other.deferredIndexes);\r\n for (const key of Object.keys(other.liveIndexReasons)) this.liveIndexReasons[key] = other.liveIndexReasons[key] as string;\r\n addAll(this.rowsByTable, other.rowsByTable);\r\n addAll(this.msByTable, other.msByTable);\r\n }\r\n\r\n /** One line for a log: what engaged, what did not and why, and the tables that took the time. */\r\n toString(): string {\r\n if (this.transactions === 0) return 'nothing applied';\r\n let text = `${this.transactions} transaction(s) in ${this.elapsedMs} ms; triggers `;\r\n text +=\r\n this.triggersSkipped === true\r\n ? 'skipped'\r\n : this.transactionsWithTriggersSkipped === 0\r\n ? 'LIVE'\r\n : `skipped in ${this.transactionsWithTriggersSkipped} of ${this.transactions}`;\r\n if (this.foreignTriggers.length > 0) text += ' (foreign: ' + this.foreignTriggers.join(', ') + ')';\r\n const deferred = Object.keys(this.deferredIndexes);\r\n if (deferred.length > 0) {\r\n text += `; ${this.deferredIndexCount} index(es) rebuilt after the load in ${this.indexRebuildMs} ms (` + deferred.map((t) => t + ':' + this.deferredIndexes[t]).join(', ') + ')';\r\n }\r\n const live = Object.keys(this.liveIndexReasons);\r\n if (live.length > 0) text += '; live indexes on ' + live.map((t) => t + ' (' + this.liveIndexReasons[t] + ')').join(', ');\r\n if (this.snapshotFiles > 0) {\r\n text +=\r\n `; ${this.snapshotFiles} snapshot file(s), ${this.fileRows} rows copied in ${this.fileCopyMs} ms (` +\r\n Object.keys(this.fileTables)\r\n .map((t) => t + ':' + this.fileTables[t])\r\n .join(', ') +\r\n ')';\r\n }\r\n const slowest = Object.keys(this.msByTable)\r\n .sort((a, b) => (this.msByTable[b] as number) - (this.msByTable[a] as number))\r\n .slice(0, 4);\r\n if (slowest.length > 0) {\r\n text +=\r\n '; ' +\r\n slowest\r\n .map((t) => {\r\n const ms = this.msByTable[t] as number;\r\n const rows = this.rowsByTable[t] ?? 0;\r\n const rate = ms > 0 ? Math.floor((rows * 1000) / ms) : rows;\r\n return `${t} ${rows} rows in ${ms} ms (${rate}/s)`;\r\n })\r\n .join(', ');\r\n }\r\n return text;\r\n }\r\n}\r\n\r\nfunction addAll(target: { [key: string]: number }, source: { [key: string]: number }): void {\r\n for (const key of Object.keys(source)) target[key] = (target[key] ?? 0) + (source[key] as number);\r\n}\r\n\r\n/** What an apply changed (`G9CApplyReport`). */\r\nexport class G9ApplyReport {\r\n upserts = 0;\r\n deletes = 0;\r\n moveOuts = 0;\r\n membershipAdded = 0;\r\n /** Rows skipped because they carry a pending local change. */\r\n skippedDirty = 0;\r\n accepted = 0;\r\n rejected = 0;\r\n conflicts = 0;\r\n /** Conflicts the device lost that were kept on it as rejections (LES-0072). */\r\n conflictsKept = 0;\r\n commandsCompleted = 0;\r\n revokedDeleted = 0;\r\n revokedQuarantined = 0;\r\n revokedRetained = 0;\r\n /** Changed keys per local table (capped per table). */\r\n readonly changedKeys: { [localTable: string]: string[] } = {};\r\n /** Upserts whose row already held exactly the incoming values (ADR-0058). */\r\n unchangedRows = 0;\r\n readonly diagnostics = new G9ApplyDiagnostics();\r\n\r\n get total(): number {\r\n return this.upserts + this.deletes + this.moveOuts;\r\n }\r\n\r\n merge(other: G9ApplyReport): void {\r\n this.unchangedRows += other.unchangedRows;\r\n this.diagnostics.merge(other.diagnostics);\r\n this.upserts += other.upserts;\r\n this.deletes += other.deletes;\r\n this.moveOuts += other.moveOuts;\r\n this.membershipAdded += other.membershipAdded;\r\n this.skippedDirty += other.skippedDirty;\r\n this.accepted += other.accepted;\r\n this.rejected += other.rejected;\r\n this.conflicts += other.conflicts;\r\n this.conflictsKept += other.conflictsKept;\r\n this.commandsCompleted += other.commandsCompleted;\r\n this.revokedDeleted += other.revokedDeleted;\r\n this.revokedQuarantined += other.revokedQuarantined;\r\n this.revokedRetained += other.revokedRetained;\r\n for (const table of Object.keys(other.changedKeys)) {\r\n const list = this.changedKeys[table] ?? (this.changedKeys[table] = []);\r\n for (const key of other.changedKeys[table] as string[]) list.push(key);\r\n }\r\n }\r\n}\r\n\r\n/** A local row as seen by reconciliation (`G9CLocalRowEntry`). */\r\nexport interface G9LocalRowEntry {\r\n key: string;\r\n bucket: number;\r\n version: number;\r\n hash: G9Long;\r\n member: boolean;\r\n dirty: boolean;\r\n}\r\n\r\nexport interface G9PendingTable {\r\n localTable: string;\r\n upserts: number;\r\n deletes: number;\r\n rejected: number;\r\n}\r\n\r\nexport interface G9PendingSummary {\r\n totalRows: number;\r\n rejectedRows: number;\r\n /** Commands recorded on the device and not yet executed by the server. */\r\n pendingCommands: number;\r\n tables: G9PendingTable[];\r\n}\r\n\r\n/** One entry of the local change feed (`G9CChangeFeedEntry`). */\r\nexport interface G9ChangeFeedEntry {\r\n sequence: number;\r\n key: string;\r\n deleted: boolean;\r\n localPending: boolean;\r\n}\r\n\r\n/**\r\n * Narrows a change-feed read to the keys a query of the app's own tables selects (`G9CChangeFeedKeyFilter`): a projection of one\r\n * site no longer walks the entries of every other site. Deletions are always returned - their rows are gone, so no query can place\r\n * them. `keySql` is one SELECT of one column, embedded as `pk IN (...)`; parameters are bound by name (`$name`, `@name`, `:name`)\r\n * and `$g9t`, `$g9a`, `$g9l` belong to the read.\r\n */\r\nexport interface G9ChangeFeedKeyFilter {\r\n keySql: string;\r\n parameters?: { [name: string]: string | number | null };\r\n}\r\n\r\nconst RESERVED_FEED_PARAMETERS = ['$g9t', '$g9a', '$g9l'];\r\n\r\n/** Throws when the filter cannot be embedded safely (`G9CChangeFeedKeyFilter.Validate`). */\r\nexport function validateChangeFeedKeyFilter(filter: G9ChangeFeedKeyFilter): void {\r\n const sql = (filter.keySql ?? '').trim();\r\n if (sql.slice(0, 6).toUpperCase() !== 'SELECT' || sql.indexOf(';') >= 0) {\r\n throw new Error('A change-feed key filter is a single SELECT statement of one column.');\r\n }\r\n for (const name of Object.keys(filter.parameters ?? {})) {\r\n if (name.length < 2 || '$@:'.indexOf(name[0] as string) < 0) throw new Error(`Change-feed key filter parameter '${name}' must be named ($name, @name or :name).`);\r\n if (RESERVED_FEED_PARAMETERS.indexOf(name.toLowerCase()) >= 0) throw new Error(`Change-feed key filter parameter '${name}' is reserved by the read.`);\r\n }\r\n}\r\n\r\nexport interface G9SyncRejection {\r\n localTable: string;\r\n key: string;\r\n code: string;\r\n message: string | null;\r\n sqlState: string | null;\r\n constraint: string | null;\r\n column: string | null;\r\n details: string | null;\r\n atUtcMs: number;\r\n}\r\n\r\n/** A persisted summary of one run (`G9CSyncRunReport`); serialized with the .NET JSON names into g9_runs. */\r\nexport interface G9SyncRunReport {\r\n runId: string;\r\n startedUtcMs: number;\r\n durationMs: number;\r\n outcome: string;\r\n priority: string;\r\n errorCode: string | null;\r\n errorMessage: string | null;\r\n pushed: number;\r\n rejected: number;\r\n conflicts: number;\r\n downloaded: number;\r\n applied: number;\r\n downloadedBytes: number;\r\n subscriptions: string[];\r\n phaseMs: { [phase: string]: number };\r\n triggersSkipped: boolean | null;\r\n foreignTriggers: string[];\r\n applyTransactions: number;\r\n indexesDeferred: number;\r\n indexRebuildMs: number;\r\n liveIndexReasons: { [table: string]: string };\r\n unchangedRows: number;\r\n}\r\n\r\n// ---------------------------------------------------------------- local commands (ADR-0025)\r\n\r\n/** `G9ELocalCommandState`. */\r\nexport const G9ELocalCommandState = {\r\n Pending: 0,\r\n Ok: 1,\r\n Rejected: 2,\r\n Superseded: 3,\r\n} as const;\r\nexport type G9ELocalCommandState = (typeof G9ELocalCommandState)[keyof typeof G9ELocalCommandState];\r\n\r\n/** A command recorded on this device (`G9CLocalCommand`). */\r\nexport interface G9LocalCommand {\r\n commandId: string;\r\n sequence: number;\r\n name: string;\r\n version: number;\r\n payload: string;\r\n createdUtcMs: number;\r\n state: G9ELocalCommandState;\r\n code: string | null;\r\n message: string | null;\r\n result: string | null;\r\n completedUtcMs: number | null;\r\n}\r\n\r\n/** What to record (`G9CCommandRequest`). */\r\nexport interface G9CommandRequest {\r\n name: string;\r\n version?: number;\r\n /** JSON payload for the server's handler. */\r\n payload: string;\r\n}\r\n\r\n/** A value an app binds through {@link G9LocalCommandWriter}: the SQLite driver's value forms, plus booleans. */\r\nexport type G9LocalSqlArgument = null | number | string | boolean | ArrayBuffer | Uint8Array | { int64: string } | G9Long;\r\n\r\n/**\r\n * Writes a command's local effects in the transaction that records it (`IG9LocalCommandWriter`). Rows written here are\r\n * not uploaded as row changes; they are protected from pulls until the server's result arrives.\r\n */\r\nexport interface G9LocalCommandWriter {\r\n /** Executes SQL; parameters bind positionally as `$p0`, `$p1`, … */\r\n execute(sql: string, ...args: G9LocalSqlArgument[]): Promise<number>;\r\n scalar(sql: string, ...args: G9LocalSqlArgument[]): Promise<unknown>;\r\n}\r\n\r\n// ---------------------------------------------------------------- revocation (ADR-0026)\r\n\r\n/** `G9ERevocationReason`. */\r\nexport const G9ERevocationReason = {\r\n LeftScope: 0,\r\n Unsubscribed: 1,\r\n Expired: 2,\r\n AccessRevoked: 3,\r\n} as const;\r\nexport type G9ERevocationReason = (typeof G9ERevocationReason)[keyof typeof G9ERevocationReason];\r\n\r\n/** `G9ERevocationDisposition`. */\r\nexport const G9ERevocationDisposition = {\r\n Delete: 0,\r\n MarkRevoked: 1,\r\n Quarantine: 2,\r\n Retain: 3,\r\n} as const;\r\nexport type G9ERevocationDisposition = (typeof G9ERevocationDisposition)[keyof typeof G9ERevocationDisposition];\r\n\r\n/** A row no subscription holds any more, as a revocation rule sees it (`G9CRevokedRow`). */\r\nexport interface G9RevokedRow {\r\n localTable: string;\r\n key: string;\r\n reason: G9ERevocationReason;\r\n subscriptionKey: string | null;\r\n hasPendingChanges: boolean;\r\n isRejected: boolean;\r\n /** Column values (key included) as stored locally; empty when the row no longer exists locally. */\r\n values: { [column: string]: unknown };\r\n}\r\n\r\n/** Lets a custom rule change the row before its disposition applies. */\r\nexport interface G9RevokedRowEditor {\r\n set(column: string, value: unknown): void;\r\n}\r\n\r\nexport interface G9RevocationRule {\r\n readonly name: string;\r\n readonly condition: (row: G9RevokedRow) => boolean;\r\n readonly decide: (row: G9RevokedRow, editor: G9RevokedRowEditor) => G9ERevocationDisposition;\r\n readonly retainWhile: ((row: G9RevokedRow) => boolean) | null;\r\n}\r\n\r\n/** Ordered revocation rules (`G9CRevocationPolicy`): the first matching rule decides. */\r\nexport class G9RevocationPolicy {\r\n readonly rules: readonly G9RevocationRule[];\r\n\r\n private constructor(rules: readonly G9RevocationRule[]) {\r\n this.rules = rules;\r\n }\r\n\r\n /** No rules: delete clean rows, quarantine rows with pending changes. */\r\n static readonly default = new G9RevocationPolicy([]);\r\n\r\n static create(configure: (builder: G9RevocationPolicyBuilder) => void): G9RevocationPolicy {\r\n const builder = new G9RevocationPolicyBuilder();\r\n configure(builder);\r\n return new G9RevocationPolicy(builder.rules.slice());\r\n }\r\n\r\n decide(row: G9RevokedRow, editor: G9RevokedRowEditor): { rule: G9RevocationRule | null; disposition: G9ERevocationDisposition } {\r\n for (const rule of this.rules) {\r\n if (rule.condition(row)) return { rule, disposition: rule.decide(row, editor) };\r\n }\r\n return { rule: null, disposition: row.hasPendingChanges ? G9ERevocationDisposition.Quarantine : G9ERevocationDisposition.Delete };\r\n }\r\n\r\n find(name: string): G9RevocationRule | null {\r\n for (const rule of this.rules) if (rule.name === name) return rule;\r\n return null;\r\n }\r\n}\r\n\r\nexport class G9RevocationPolicyBuilder {\r\n readonly rules: G9RevocationRule[] = [];\r\n\r\n when(name: string, condition: (row: G9RevokedRow) => boolean): G9RevocationActionBuilder {\r\n if (typeof name !== 'string' || name.trim() === '') throw new TypeError('A rule needs a name.');\r\n if (this.rules.some((r) => r.name === name)) throw new RangeError(`Revocation rule '${name}' is declared twice.`);\r\n return new G9RevocationActionBuilder(this, name, condition);\r\n }\r\n\r\n /** Rows of one local table. */\r\n forTable(name: string, localTable: string, condition?: (row: G9RevokedRow) => boolean): G9RevocationActionBuilder {\r\n const table = localTable.toLowerCase();\r\n return this.when(name, (row) => row.localTable.toLowerCase() === table && (condition ? condition(row) : true));\r\n }\r\n}\r\n\r\nexport class G9RevocationActionBuilder {\r\n private readonly _owner: G9RevocationPolicyBuilder;\r\n private readonly _name: string;\r\n private readonly _condition: (row: G9RevokedRow) => boolean;\r\n\r\n constructor(owner: G9RevocationPolicyBuilder, name: string, condition: (row: G9RevokedRow) => boolean) {\r\n this._owner = owner;\r\n this._name = name;\r\n this._condition = condition;\r\n }\r\n\r\n delete(): G9RevocationPolicyBuilder {\r\n return this._add(() => G9ERevocationDisposition.Delete, null);\r\n }\r\n\r\n markRevoked(): G9RevocationPolicyBuilder {\r\n return this._add(() => G9ERevocationDisposition.MarkRevoked, null);\r\n }\r\n\r\n quarantine(): G9RevocationPolicyBuilder {\r\n return this._add(() => G9ERevocationDisposition.Quarantine, null);\r\n }\r\n\r\n retainWhile(retainWhile: (row: G9RevokedRow) => boolean): G9RevocationPolicyBuilder {\r\n return this._add(() => G9ERevocationDisposition.Retain, retainWhile);\r\n }\r\n\r\n custom(decide: (row: G9RevokedRow, editor: G9RevokedRowEditor) => G9ERevocationDisposition): G9RevocationPolicyBuilder {\r\n return this._add(decide, null);\r\n }\r\n\r\n private _add(\r\n decide: (row: G9RevokedRow, editor: G9RevokedRowEditor) => G9ERevocationDisposition,\r\n retainWhile: ((row: G9RevokedRow) => boolean) | null,\r\n ): G9RevocationPolicyBuilder {\r\n this._owner.rules.push({ name: this._name, condition: this._condition, decide, retainWhile });\r\n return this._owner;\r\n }\r\n}\r\n\r\n/** A row in the holding area (`G9CQuarantinedRow`). */\r\nexport interface G9QuarantinedRow {\r\n localTable: string;\r\n key: string;\r\n reason: G9ERevocationReason;\r\n rule: string | null;\r\n hadPendingChanges: boolean;\r\n values: { [column: string]: unknown };\r\n atUtcMs: number;\r\n}\r\n\r\n/** A row kept in the app's table after revocation (`G9CRetainedRow`). */\r\nexport interface G9RetainedRow {\r\n localTable: string;\r\n key: string;\r\n disposition: G9ERevocationDisposition;\r\n reason: G9ERevocationReason;\r\n rule: string | null;\r\n atUtcMs: number;\r\n}\r\n\r\n// ---------------------------------------------------------------- failures\r\n\r\n/** What went wrong inside a client store, in terms an application can act on (`G9ELocalStoreFailure`). */\r\nexport const G9ELocalStoreFailure = {\r\n Other: 0,\r\n Corrupt: 1,\r\n DiskFull: 2,\r\n Busy: 3,\r\n} as const;\r\nexport type G9ELocalStoreFailure = (typeof G9ELocalStoreFailure)[keyof typeof G9ELocalStoreFailure];\r\n\r\n/** Implemented by a store's errors so the engine can give a run's failure a stable code (`IG9SyncLocalStoreFailure`). */\r\nexport interface G9SyncLocalStoreFailure {\r\n readonly localStoreFailure: G9ELocalStoreFailure;\r\n}\r\n\r\nexport function isLocalStoreFailure(error: unknown): error is G9SyncLocalStoreFailure {\r\n return typeof error === 'object' && error !== null && typeof (error as { localStoreFailure?: unknown }).localStoreFailure === 'number';\r\n}\r\n\r\n// ---------------------------------------------------------------- the store contract\r\n\r\n/**\r\n * The client storage provider (`IG9SyncClientStore`): local tables, change capture, subscription membership,\r\n * staging, apply and metadata. Every method takes an optional AbortSignal (the CancellationToken twin).\r\n */\r\nexport interface G9SyncClientStore {\r\n initialize(model: G9SyncClientModel, signal?: AbortSignal): Promise<void>;\r\n getClientId(signal?: AbortSignal): Promise<string>;\r\n getServerState(signal?: AbortSignal): Promise<G9ClientServerState>;\r\n /**\r\n * Stores the Hello answer. `localMidpointMs`: the local time the server's clock reading corresponds to - the midpoint of the round\r\n * trip (LES-0075); the offset is then exact up to the asymmetry of the two legs. Omitted: the time of this call.\r\n */\r\n saveHello(hello: G9DtHelloResult, signal?: AbortSignal, localMidpointMs?: number): Promise<void>;\r\n getPendingBackfills(signal?: AbortSignal): Promise<G9DtBackfillRequest[]>;\r\n\r\n getSubscriptions(signal?: AbortSignal): Promise<G9SyncSubscriptionRecord[]>;\r\n ensureSubscription(\r\n name: string,\r\n parameters: G9SyncParameters,\r\n ephemeral: boolean,\r\n ttlMs: number | null,\r\n adoptExistingRows: boolean,\r\n signal?: AbortSignal,\r\n ): Promise<G9SyncSubscriptionRecord>;\r\n removeSubscription(key: string, policy: G9EMoveOutPolicy, signal?: AbortSignal): Promise<void>;\r\n releaseSubscriptionRows(key: string, signal?: AbortSignal): Promise<G9ApplyReport>;\r\n updateSubscription(key: string, update: G9SubscriptionUpdate, signal?: AbortSignal): Promise<void>;\r\n collectExpiredEphemeral(utcNowMs: number, signal?: AbortSignal): Promise<number>;\r\n\r\n buildPushBatch(tables: readonly G9DtNegotiatedTable[], maxRows: number, maxBytes: number, signal?: AbortSignal): Promise<G9PushBatch | null>;\r\n forgetUploadedRows(localTable: string, keys: readonly string[] | null, signal?: AbortSignal): Promise<number>;\r\n applyPushResult(batch: G9PushBatch, result: G9DtPushResult, signal?: AbortSignal): Promise<G9ApplyReport>;\r\n\r\n getStagedPull(signal?: AbortSignal): Promise<G9StagedPull | null>;\r\n /** The newest staged pull planned under exactly this generation, or null (ADR-0060). */\r\n getStagedPullForGeneration(generation: string, signal?: AbortSignal): Promise<G9StagedPull | null>;\r\n beginStagedPull(pull: G9StagedPull, signal?: AbortSignal): Promise<void>;\r\n pruneStagedPulls(olderThanUtcMs: number, maxBytes: number, keepGeneration: string | null, signal?: AbortSignal): Promise<number>;\r\n discardAllStagedPulls(signal?: AbortSignal): Promise<number>;\r\n stageChunk(runId: string, chunk: G9DtSyncChunk, signal?: AbortSignal): Promise<void>;\r\n /**\r\n * The store's offer to receive large first downloads as SQLite snapshot files (ADR-0072), or null for rows only. A store that\r\n * makes an offer stages file segments (`chunk.file`) in `stageChunk` and copies the files in `applyStagedPull`.\r\n */\r\n describeSnapshotFiles?(negotiated: readonly G9DtNegotiatedTable[]): G9DtSnapshotFileRequest | null;\r\n getResumePoint(runId: string, signal?: AbortSignal): Promise<G9ResumePoint>;\r\n applyStagedPull(\r\n runId: string,\r\n rowsApplied: ((rows: number) => void) | null,\r\n slices: ((slice: G9ApplySlice) => void) | null,\r\n signal?: AbortSignal,\r\n ): Promise<G9ApplyReport>;\r\n discardStagedPull(runId: string, signal?: AbortSignal): Promise<void>;\r\n\r\n computeDigest(subscriptionKey: string, table: G9DtNegotiatedTable, signal?: AbortSignal): Promise<G9DtTableDigest>;\r\n readLocalEntries(\r\n subscriptionKey: string,\r\n table: G9DtNegotiatedTable,\r\n buckets: ReadonlySet<number>,\r\n serverKeys: ReadonlySet<string>,\r\n signal?: AbortSignal,\r\n ): Promise<G9LocalRowEntry[]>;\r\n applyReconcile(\r\n subscriptionKey: string,\r\n table: G9DtNegotiatedTable,\r\n fetchedFrames: readonly Uint8Array[],\r\n addMembership: readonly string[],\r\n moveOut: readonly string[],\r\n policy: G9EMoveOutPolicy,\r\n signal?: AbortSignal,\r\n ): Promise<G9ApplyReport>;\r\n\r\n getPendingSummary(signal?: AbortSignal): Promise<G9PendingSummary>;\r\n enqueueCommand(\r\n request: G9CommandRequest,\r\n applyLocally: ((writer: G9LocalCommandWriter, signal?: AbortSignal) => Promise<void>) | null,\r\n signal?: AbortSignal,\r\n ): Promise<G9LocalCommand>;\r\n getQuarantined(signal?: AbortSignal): Promise<G9QuarantinedRow[]>;\r\n restoreQuarantined(localTable: string, key: string, signal?: AbortSignal): Promise<number>;\r\n discardQuarantined(localTable: string | null, key: string | null, signal?: AbortSignal): Promise<number>;\r\n getRetained(signal?: AbortSignal): Promise<G9RetainedRow[]>;\r\n reevaluateRetained(signal?: AbortSignal): Promise<number>;\r\n getCommands(state: G9ELocalCommandState | null, limit: number, signal?: AbortSignal): Promise<G9LocalCommand[]>;\r\n readChangeFeed(localTable: string, afterSequence: number, limit: number, signal?: AbortSignal): Promise<G9ChangeFeedEntry[]>;\r\n /** The feed narrowed to the keys `keyFilter` selects, plus every deletion; fewer than `limit` entries means the read is complete. */\r\n readChangeFeedFiltered(localTable: string, afterSequence: number, limit: number, keyFilter: G9ChangeFeedKeyFilter, signal?: AbortSignal): Promise<G9ChangeFeedEntry[]>;\r\n getChangeSequence(signal?: AbortSignal): Promise<number>;\r\n getChangeFeedFloor(signal?: AbortSignal): Promise<number>;\r\n getRejections(signal?: AbortSignal): Promise<G9SyncRejection[]>;\r\n retryRejected(localTable: string | null, key: string | null, signal?: AbortSignal): Promise<number>;\r\n /** Gives up the local change of rejected rows and takes the server's copy (LES-0070); run a forced reconcile after it. */\r\n revertRejected(localTable: string | null, key: string | null, signal?: AbortSignal): Promise<number>;\r\n markDirty(localTable: string, keys: readonly string[], signal?: AbortSignal): Promise<number>;\r\n adoptExistingRows(signal?: AbortSignal): Promise<number>;\r\n saveRunReport(report: G9SyncRunReport, keep: number, signal?: AbortSignal): Promise<void>;\r\n getRecentRunReports(count: number, signal?: AbortSignal): Promise<G9SyncRunReport[]>;\r\n getPhaseStatistics(signal?: AbortSignal): Promise<{ [key: string]: number }>;\r\n recordPhaseStatistics(samples: { [key: string]: number }, signal?: AbortSignal): Promise<void>;\r\n purgeDeletedFeed(keep: number, signal?: AbortSignal): Promise<number>;\r\n dispose(): Promise<void>;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"storeTypes.js","sourceRoot":"","sources":["../../src/client/storeTypes.ts"],"names":[],"mappings":"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","sourcesContent":["// Twins of G9SyncData.Client/Storage/*.cs: the client store contract, its records, local commands, revocation rules\r\n// and apply diagnostics. The engine is store-agnostic; `@g9tm/syncdata/sqlite` is the implementation.\r\nimport type { G9EMoveOutPolicy } from '../abstractions/enums.js';\r\nimport type { G9Long } from '../abstractions/long.js';\r\nimport type { G9SyncParameters } from '../abstractions/parameters.js';\r\nimport type {\r\n G9DtBackfillRequest,\r\n G9DtCommand,\r\n G9DtHelloResult,\r\n G9DtNegotiatedTable,\r\n G9DtPullContinuation,\r\n G9DtPullPlan,\r\n G9DtPushResult,\r\n G9DtSnapshotFileRequest,\r\n G9DtSubscriptionDefinition,\r\n G9DtSubscriptionRef,\r\n G9DtSyncChunk,\r\n G9DtTableColumns,\r\n G9DtTableDigest,\r\n} from '../protocol/dtos.js';\r\nimport type { G9SyncClientModel } from './model.js';\r\n\r\n// ---------------------------------------------------------------- server state and subscriptions\r\n\r\n/** What the client last learned from the server (`G9CClientServerState`). */\r\nexport interface G9ClientServerState {\r\n epoch: string | null;\r\n lastHelloUtcMs: number | null;\r\n /** Server clock minus device clock, ms. */\r\n serverClockOffsetMs: number;\r\n tables: G9DtNegotiatedTable[];\r\n definitions: G9DtSubscriptionDefinition[];\r\n}\r\n\r\n/** `G9ESubscriptionState`. */\r\nexport const G9ESubscriptionState = {\r\n Active: 0,\r\n NeedsReconcile: 1,\r\n Failed: 2,\r\n} as const;\r\nexport type G9ESubscriptionState = (typeof G9ESubscriptionState)[keyof typeof G9ESubscriptionState];\r\n\r\n/** A local subscription (`G9CSyncSubscriptionRecord`). Times are epoch milliseconds UTC. */\r\nexport interface G9SyncSubscriptionRecord {\r\n key: string;\r\n name: string;\r\n parameters: G9SyncParameters;\r\n /** Membership slot (bit) for persistent subscriptions; the shared ephemeral slot otherwise. */\r\n slot: number;\r\n ephemeral: boolean;\r\n ttlMs: number | null;\r\n cursor: string | null;\r\n /** The move-out policy the server last announced; kept so it still applies once access is gone. */\r\n moveOutPolicy: G9EMoveOutPolicy | null;\r\n definitionHash: string | null;\r\n authHash: string | null;\r\n state: G9ESubscriptionState;\r\n lastErrorCode: string | null;\r\n createdUtcMs: number;\r\n lastSuccessUtcMs: number | null;\r\n lastReconcileUtcMs: number | null;\r\n lastAccessUtcMs: number | null;\r\n}\r\n\r\n/** `G9CSyncSubscriptionRecord.ToReference()`. */\r\nexport function toSubscriptionRef(record: G9SyncSubscriptionRecord): G9DtSubscriptionRef {\r\n return {\r\n name: record.name,\r\n parameters: record.parameters.values,\r\n cursor: record.cursor,\r\n definitionHash: record.definitionHash,\r\n authHash: record.authHash,\r\n };\r\n}\r\n\r\n/** A partial update of a subscription record; absent members are left unchanged (`G9CSubscriptionUpdate`). */\r\nexport interface G9SubscriptionUpdate {\r\n cursor?: string | null;\r\n definitionHash?: string | null;\r\n authHash?: string | null;\r\n state?: G9ESubscriptionState | null;\r\n errorCode?: string | null;\r\n clearError?: boolean;\r\n succeeded?: boolean;\r\n reconciled?: boolean;\r\n accessed?: boolean;\r\n clearCursor?: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------- push and pull\r\n\r\n/** A push batch built from dirty rows (`G9CPushBatch`). */\r\nexport interface G9PushBatch {\r\n batchId: number;\r\n /** Highest local change sequence included; rows changed after it stay dirty after the acknowledgement. */\r\n maxSequence: number;\r\n frames: Uint8Array[];\r\n tables: G9DtTableColumns[];\r\n rowCount: number;\r\n /** True when the byte budget ended the batch early: more pending rows follow. */\r\n truncated: boolean;\r\n /** Rows rejected on the device while building the batch (G9S-REJ-TOO-LARGE). */\r\n rejectedLocally: number;\r\n /** Commands carried by this batch, in device order. */\r\n commands: G9DtCommand[];\r\n}\r\n\r\n/** A pull being staged, persisted across restarts (`G9CStagedPull`). */\r\nexport interface G9StagedPull {\r\n runId: string;\r\n plan: G9DtPullPlan;\r\n createdUtcMs: number;\r\n backfills: G9DtBackfillRequest[];\r\n /** What the world looked like when the plan was made (ADR-0046); a run only resumes a stage whose generation matches. */\r\n generation: string | null;\r\n /** The cursor each planned subscription had when the plan was made, by subscription key (ADR-0060). */\r\n plannedFromCursors: { [subscriptionKey: string]: string | null };\r\n /** The device clock when the stage began, assigned by the store. */\r\n stageSequence: number;\r\n}\r\n\r\n/** One committed slice of a staged pull (`G9CApplySlice`). */\r\nexport interface G9ApplySlice {\r\n report: G9ApplyReport;\r\n rowsApplied: number;\r\n rowsAppliedTotal: number;\r\n rowsPlanned: number;\r\n isFinal: boolean;\r\n}\r\n\r\n/** `G9CResumePoint`. */\r\nexport interface G9ResumePoint {\r\n continuation: G9DtPullContinuation | null;\r\n stagedRows: number;\r\n stagedBytes: number;\r\n complete: boolean;\r\n}\r\n\r\n// ---------------------------------------------------------------- reports\r\n\r\n/** Why a table large enough for index deferral kept its indexes live. */\r\nexport const REASON_TABLE_NOT_EMPTY = 'table-not-empty';\r\nconst MAX_FOREIGN_TRIGGER_NAMES = 10;\r\n\r\n/** How an apply ran, as opposed to what it changed (`G9CApplyDiagnostics`, ADR-0058). */\r\n/** A file table copied record for record into an empty table with identical columns (the transfer optimisation). */\r\nexport const FILE_MODE_COPY = 'copy';\r\n/** A file table copied into an empty table by column name. */\r\nexport const FILE_MODE_COLUMNS = 'columns';\r\n/** A file table merged into a table that held rows or protected rows: changed rows only. */\r\nexport const FILE_MODE_MERGE = 'merge';\r\n/** A file table inserted into a table holding only OTHER rows (a second site's first load): every row is new, no per-row diff. */\r\nexport const FILE_MODE_APPEND = 'append';\r\n\r\nexport class G9ApplyDiagnostics {\r\n transactions = 0;\r\n transactionsWithTriggersSkipped = 0;\r\n readonly foreignTriggers: string[] = [];\r\n readonly deferredIndexes: { [table: string]: number } = {};\r\n readonly liveIndexReasons: { [table: string]: string } = {};\r\n readonly rowsByTable: { [table: string]: number } = {};\r\n readonly msByTable: { [table: string]: number } = {};\r\n indexRebuildMs = 0;\r\n elapsedMs = 0;\r\n /** Snapshot files copied in (ADR-0072). */\r\n snapshotFiles = 0;\r\n /** Rows that arrived through snapshot files. */\r\n fileRows = 0;\r\n /** Milliseconds spent copying snapshot files, metadata included. */\r\n fileCopyMs = 0;\r\n /** How each table came out of a snapshot file: `copy`, `columns` or `merge` (see FILE_MODE_*). */\r\n readonly fileTables: { [table: string]: string } = {};\r\n\r\n /** True when every transaction ran with trigger execution off, false when any ran with it on, null when nothing was applied. */\r\n get triggersSkipped(): boolean | null {\r\n return this.transactions === 0 ? null : this.transactionsWithTriggersSkipped === this.transactions;\r\n }\r\n\r\n get deferredIndexCount(): number {\r\n let total = 0;\r\n for (const key of Object.keys(this.deferredIndexes)) total += this.deferredIndexes[key] as number;\r\n return total;\r\n }\r\n\r\n merge(other: G9ApplyDiagnostics): void {\r\n this.transactions += other.transactions;\r\n this.transactionsWithTriggersSkipped += other.transactionsWithTriggersSkipped;\r\n this.indexRebuildMs += other.indexRebuildMs;\r\n this.elapsedMs += other.elapsedMs;\r\n this.snapshotFiles += other.snapshotFiles;\r\n this.fileRows += other.fileRows;\r\n this.fileCopyMs += other.fileCopyMs;\r\n for (const key of Object.keys(other.fileTables)) this.fileTables[key] = other.fileTables[key] as string;\r\n for (const name of other.foreignTriggers) {\r\n if (this.foreignTriggers.length >= MAX_FOREIGN_TRIGGER_NAMES) break;\r\n if (!this.foreignTriggers.some((n) => n.toLowerCase() === name.toLowerCase())) this.foreignTriggers.push(name);\r\n }\r\n addAll(this.deferredIndexes, other.deferredIndexes);\r\n for (const key of Object.keys(other.liveIndexReasons)) this.liveIndexReasons[key] = other.liveIndexReasons[key] as string;\r\n addAll(this.rowsByTable, other.rowsByTable);\r\n addAll(this.msByTable, other.msByTable);\r\n }\r\n\r\n /** One line for a log: what engaged, what did not and why, and the tables that took the time. */\r\n toString(): string {\r\n if (this.transactions === 0) return 'nothing applied';\r\n let text = `${this.transactions} transaction(s) in ${this.elapsedMs} ms; triggers `;\r\n text +=\r\n this.triggersSkipped === true\r\n ? 'skipped'\r\n : this.transactionsWithTriggersSkipped === 0\r\n ? 'LIVE'\r\n : `skipped in ${this.transactionsWithTriggersSkipped} of ${this.transactions}`;\r\n if (this.foreignTriggers.length > 0) text += ' (foreign: ' + this.foreignTriggers.join(', ') + ')';\r\n const deferred = Object.keys(this.deferredIndexes);\r\n if (deferred.length > 0) {\r\n text += `; ${this.deferredIndexCount} index(es) rebuilt after the load in ${this.indexRebuildMs} ms (` + deferred.map((t) => t + ':' + this.deferredIndexes[t]).join(', ') + ')';\r\n }\r\n const live = Object.keys(this.liveIndexReasons);\r\n if (live.length > 0) text += '; live indexes on ' + live.map((t) => t + ' (' + this.liveIndexReasons[t] + ')').join(', ');\r\n if (this.snapshotFiles > 0) {\r\n text +=\r\n `; ${this.snapshotFiles} snapshot file(s), ${this.fileRows} rows copied in ${this.fileCopyMs} ms (` +\r\n Object.keys(this.fileTables)\r\n .map((t) => t + ':' + this.fileTables[t])\r\n .join(', ') +\r\n ')';\r\n }\r\n const slowest = Object.keys(this.msByTable)\r\n .sort((a, b) => (this.msByTable[b] as number) - (this.msByTable[a] as number))\r\n .slice(0, 4);\r\n if (slowest.length > 0) {\r\n text +=\r\n '; ' +\r\n slowest\r\n .map((t) => {\r\n const ms = this.msByTable[t] as number;\r\n const rows = this.rowsByTable[t] ?? 0;\r\n const rate = ms > 0 ? Math.floor((rows * 1000) / ms) : rows;\r\n return `${t} ${rows} rows in ${ms} ms (${rate}/s)`;\r\n })\r\n .join(', ');\r\n }\r\n return text;\r\n }\r\n}\r\n\r\nfunction addAll(target: { [key: string]: number }, source: { [key: string]: number }): void {\r\n for (const key of Object.keys(source)) target[key] = (target[key] ?? 0) + (source[key] as number);\r\n}\r\n\r\n/** What an apply changed (`G9CApplyReport`). */\r\nexport class G9ApplyReport {\r\n upserts = 0;\r\n deletes = 0;\r\n moveOuts = 0;\r\n membershipAdded = 0;\r\n /** Rows skipped because they carry a pending local change. */\r\n skippedDirty = 0;\r\n accepted = 0;\r\n rejected = 0;\r\n conflicts = 0;\r\n /** Conflicts the device lost that were kept on it as rejections (LES-0072). */\r\n conflictsKept = 0;\r\n /** Conflicts the device lost on paper only: the server already held its values for every lost column (LES-0085). */\r\n conflictsSameValue = 0;\r\n commandsCompleted = 0;\r\n revokedDeleted = 0;\r\n revokedQuarantined = 0;\r\n revokedRetained = 0;\r\n /** Changed keys per local table (capped per table). */\r\n readonly changedKeys: { [localTable: string]: string[] } = {};\r\n /** Upserts whose row already held exactly the incoming values (ADR-0058). */\r\n unchangedRows = 0;\r\n readonly diagnostics = new G9ApplyDiagnostics();\r\n\r\n get total(): number {\r\n return this.upserts + this.deletes + this.moveOuts;\r\n }\r\n\r\n merge(other: G9ApplyReport): void {\r\n this.unchangedRows += other.unchangedRows;\r\n this.diagnostics.merge(other.diagnostics);\r\n this.upserts += other.upserts;\r\n this.deletes += other.deletes;\r\n this.moveOuts += other.moveOuts;\r\n this.membershipAdded += other.membershipAdded;\r\n this.skippedDirty += other.skippedDirty;\r\n this.accepted += other.accepted;\r\n this.rejected += other.rejected;\r\n this.conflicts += other.conflicts;\r\n this.conflictsKept += other.conflictsKept;\r\n this.conflictsSameValue += other.conflictsSameValue;\r\n this.commandsCompleted += other.commandsCompleted;\r\n this.revokedDeleted += other.revokedDeleted;\r\n this.revokedQuarantined += other.revokedQuarantined;\r\n this.revokedRetained += other.revokedRetained;\r\n for (const table of Object.keys(other.changedKeys)) {\r\n const list = this.changedKeys[table] ?? (this.changedKeys[table] = []);\r\n for (const key of other.changedKeys[table] as string[]) list.push(key);\r\n }\r\n }\r\n}\r\n\r\n/** A local row as seen by reconciliation (`G9CLocalRowEntry`). */\r\nexport interface G9LocalRowEntry {\r\n key: string;\r\n bucket: number;\r\n version: number;\r\n hash: G9Long;\r\n member: boolean;\r\n dirty: boolean;\r\n}\r\n\r\nexport interface G9PendingTable {\r\n localTable: string;\r\n upserts: number;\r\n deletes: number;\r\n rejected: number;\r\n}\r\n\r\nexport interface G9PendingSummary {\r\n totalRows: number;\r\n rejectedRows: number;\r\n /** Commands recorded on the device and not yet executed by the server. */\r\n pendingCommands: number;\r\n tables: G9PendingTable[];\r\n}\r\n\r\n/** One entry of the local change feed (`G9CChangeFeedEntry`). */\r\nexport interface G9ChangeFeedEntry {\r\n sequence: number;\r\n key: string;\r\n deleted: boolean;\r\n localPending: boolean;\r\n}\r\n\r\n/**\r\n * Narrows a change-feed read to the keys a query of the app's own tables selects (`G9CChangeFeedKeyFilter`): a projection of one\r\n * site no longer walks the entries of every other site. Deletions are always returned - their rows are gone, so no query can place\r\n * them. `keySql` is one SELECT of one column, embedded as `pk IN (...)`; parameters are bound by name (`$name`, `@name`, `:name`)\r\n * and `$g9t`, `$g9a`, `$g9l` belong to the read.\r\n */\r\nexport interface G9ChangeFeedKeyFilter {\r\n keySql: string;\r\n parameters?: { [name: string]: string | number | null };\r\n}\r\n\r\nconst RESERVED_FEED_PARAMETERS = ['$g9t', '$g9a', '$g9l'];\r\n\r\n/** Throws when the filter cannot be embedded safely (`G9CChangeFeedKeyFilter.Validate`). */\r\nexport function validateChangeFeedKeyFilter(filter: G9ChangeFeedKeyFilter): void {\r\n const sql = (filter.keySql ?? '').trim();\r\n if (sql.slice(0, 6).toUpperCase() !== 'SELECT' || sql.indexOf(';') >= 0) {\r\n throw new Error('A change-feed key filter is a single SELECT statement of one column.');\r\n }\r\n for (const name of Object.keys(filter.parameters ?? {})) {\r\n if (name.length < 2 || '$@:'.indexOf(name[0] as string) < 0) throw new Error(`Change-feed key filter parameter '${name}' must be named ($name, @name or :name).`);\r\n if (RESERVED_FEED_PARAMETERS.indexOf(name.toLowerCase()) >= 0) throw new Error(`Change-feed key filter parameter '${name}' is reserved by the read.`);\r\n }\r\n}\r\n\r\nexport interface G9SyncRejection {\r\n localTable: string;\r\n key: string;\r\n code: string;\r\n message: string | null;\r\n sqlState: string | null;\r\n constraint: string | null;\r\n column: string | null;\r\n details: string | null;\r\n atUtcMs: number;\r\n}\r\n\r\n/** A persisted summary of one run (`G9CSyncRunReport`); serialized with the .NET JSON names into g9_runs. */\r\nexport interface G9SyncRunReport {\r\n runId: string;\r\n startedUtcMs: number;\r\n durationMs: number;\r\n outcome: string;\r\n priority: string;\r\n errorCode: string | null;\r\n errorMessage: string | null;\r\n pushed: number;\r\n rejected: number;\r\n conflicts: number;\r\n downloaded: number;\r\n applied: number;\r\n downloadedBytes: number;\r\n subscriptions: string[];\r\n phaseMs: { [phase: string]: number };\r\n triggersSkipped: boolean | null;\r\n foreignTriggers: string[];\r\n applyTransactions: number;\r\n indexesDeferred: number;\r\n indexRebuildMs: number;\r\n liveIndexReasons: { [table: string]: string };\r\n unchangedRows: number;\r\n}\r\n\r\n// ---------------------------------------------------------------- local commands (ADR-0025)\r\n\r\n/** `G9ELocalCommandState`. */\r\nexport const G9ELocalCommandState = {\r\n Pending: 0,\r\n Ok: 1,\r\n Rejected: 2,\r\n Superseded: 3,\r\n} as const;\r\nexport type G9ELocalCommandState = (typeof G9ELocalCommandState)[keyof typeof G9ELocalCommandState];\r\n\r\n/** A command recorded on this device (`G9CLocalCommand`). */\r\nexport interface G9LocalCommand {\r\n commandId: string;\r\n sequence: number;\r\n name: string;\r\n version: number;\r\n payload: string;\r\n createdUtcMs: number;\r\n state: G9ELocalCommandState;\r\n code: string | null;\r\n message: string | null;\r\n result: string | null;\r\n completedUtcMs: number | null;\r\n}\r\n\r\n/** What to record (`G9CCommandRequest`). */\r\nexport interface G9CommandRequest {\r\n name: string;\r\n version?: number;\r\n /** JSON payload for the server's handler. */\r\n payload: string;\r\n}\r\n\r\n/** A value an app binds through {@link G9LocalCommandWriter}: the SQLite driver's value forms, plus booleans. */\r\nexport type G9LocalSqlArgument = null | number | string | boolean | ArrayBuffer | Uint8Array | { int64: string } | G9Long;\r\n\r\n/**\r\n * Writes a command's local effects in the transaction that records it (`IG9LocalCommandWriter`). Rows written here are\r\n * not uploaded as row changes; they are protected from pulls until the server's result arrives.\r\n */\r\nexport interface G9LocalCommandWriter {\r\n /** Executes SQL; parameters bind positionally as `$p0`, `$p1`, … */\r\n execute(sql: string, ...args: G9LocalSqlArgument[]): Promise<number>;\r\n scalar(sql: string, ...args: G9LocalSqlArgument[]): Promise<unknown>;\r\n}\r\n\r\n// ---------------------------------------------------------------- revocation (ADR-0026)\r\n\r\n/** `G9ERevocationReason`. */\r\nexport const G9ERevocationReason = {\r\n LeftScope: 0,\r\n Unsubscribed: 1,\r\n Expired: 2,\r\n AccessRevoked: 3,\r\n} as const;\r\nexport type G9ERevocationReason = (typeof G9ERevocationReason)[keyof typeof G9ERevocationReason];\r\n\r\n/** `G9ERevocationDisposition`. */\r\nexport const G9ERevocationDisposition = {\r\n Delete: 0,\r\n MarkRevoked: 1,\r\n Quarantine: 2,\r\n Retain: 3,\r\n} as const;\r\nexport type G9ERevocationDisposition = (typeof G9ERevocationDisposition)[keyof typeof G9ERevocationDisposition];\r\n\r\n/** A row no subscription holds any more, as a revocation rule sees it (`G9CRevokedRow`). */\r\nexport interface G9RevokedRow {\r\n localTable: string;\r\n key: string;\r\n reason: G9ERevocationReason;\r\n subscriptionKey: string | null;\r\n hasPendingChanges: boolean;\r\n isRejected: boolean;\r\n /** Column values (key included) as stored locally; empty when the row no longer exists locally. */\r\n values: { [column: string]: unknown };\r\n}\r\n\r\n/** Lets a custom rule change the row before its disposition applies. */\r\nexport interface G9RevokedRowEditor {\r\n set(column: string, value: unknown): void;\r\n}\r\n\r\nexport interface G9RevocationRule {\r\n readonly name: string;\r\n readonly condition: (row: G9RevokedRow) => boolean;\r\n readonly decide: (row: G9RevokedRow, editor: G9RevokedRowEditor) => G9ERevocationDisposition;\r\n readonly retainWhile: ((row: G9RevokedRow) => boolean) | null;\r\n}\r\n\r\n/** Ordered revocation rules (`G9CRevocationPolicy`): the first matching rule decides. */\r\nexport class G9RevocationPolicy {\r\n readonly rules: readonly G9RevocationRule[];\r\n\r\n private constructor(rules: readonly G9RevocationRule[]) {\r\n this.rules = rules;\r\n }\r\n\r\n /** No rules: delete clean rows, quarantine rows with pending changes. */\r\n static readonly default = new G9RevocationPolicy([]);\r\n\r\n static create(configure: (builder: G9RevocationPolicyBuilder) => void): G9RevocationPolicy {\r\n const builder = new G9RevocationPolicyBuilder();\r\n configure(builder);\r\n return new G9RevocationPolicy(builder.rules.slice());\r\n }\r\n\r\n decide(row: G9RevokedRow, editor: G9RevokedRowEditor): { rule: G9RevocationRule | null; disposition: G9ERevocationDisposition } {\r\n for (const rule of this.rules) {\r\n if (rule.condition(row)) return { rule, disposition: rule.decide(row, editor) };\r\n }\r\n return { rule: null, disposition: row.hasPendingChanges ? G9ERevocationDisposition.Quarantine : G9ERevocationDisposition.Delete };\r\n }\r\n\r\n find(name: string): G9RevocationRule | null {\r\n for (const rule of this.rules) if (rule.name === name) return rule;\r\n return null;\r\n }\r\n}\r\n\r\nexport class G9RevocationPolicyBuilder {\r\n readonly rules: G9RevocationRule[] = [];\r\n\r\n when(name: string, condition: (row: G9RevokedRow) => boolean): G9RevocationActionBuilder {\r\n if (typeof name !== 'string' || name.trim() === '') throw new TypeError('A rule needs a name.');\r\n if (this.rules.some((r) => r.name === name)) throw new RangeError(`Revocation rule '${name}' is declared twice.`);\r\n return new G9RevocationActionBuilder(this, name, condition);\r\n }\r\n\r\n /** Rows of one local table. */\r\n forTable(name: string, localTable: string, condition?: (row: G9RevokedRow) => boolean): G9RevocationActionBuilder {\r\n const table = localTable.toLowerCase();\r\n return this.when(name, (row) => row.localTable.toLowerCase() === table && (condition ? condition(row) : true));\r\n }\r\n}\r\n\r\nexport class G9RevocationActionBuilder {\r\n private readonly _owner: G9RevocationPolicyBuilder;\r\n private readonly _name: string;\r\n private readonly _condition: (row: G9RevokedRow) => boolean;\r\n\r\n constructor(owner: G9RevocationPolicyBuilder, name: string, condition: (row: G9RevokedRow) => boolean) {\r\n this._owner = owner;\r\n this._name = name;\r\n this._condition = condition;\r\n }\r\n\r\n delete(): G9RevocationPolicyBuilder {\r\n return this._add(() => G9ERevocationDisposition.Delete, null);\r\n }\r\n\r\n markRevoked(): G9RevocationPolicyBuilder {\r\n return this._add(() => G9ERevocationDisposition.MarkRevoked, null);\r\n }\r\n\r\n quarantine(): G9RevocationPolicyBuilder {\r\n return this._add(() => G9ERevocationDisposition.Quarantine, null);\r\n }\r\n\r\n retainWhile(retainWhile: (row: G9RevokedRow) => boolean): G9RevocationPolicyBuilder {\r\n return this._add(() => G9ERevocationDisposition.Retain, retainWhile);\r\n }\r\n\r\n custom(decide: (row: G9RevokedRow, editor: G9RevokedRowEditor) => G9ERevocationDisposition): G9RevocationPolicyBuilder {\r\n return this._add(decide, null);\r\n }\r\n\r\n private _add(\r\n decide: (row: G9RevokedRow, editor: G9RevokedRowEditor) => G9ERevocationDisposition,\r\n retainWhile: ((row: G9RevokedRow) => boolean) | null,\r\n ): G9RevocationPolicyBuilder {\r\n this._owner.rules.push({ name: this._name, condition: this._condition, decide, retainWhile });\r\n return this._owner;\r\n }\r\n}\r\n\r\n/** A row in the holding area (`G9CQuarantinedRow`). */\r\nexport interface G9QuarantinedRow {\r\n localTable: string;\r\n key: string;\r\n reason: G9ERevocationReason;\r\n rule: string | null;\r\n hadPendingChanges: boolean;\r\n values: { [column: string]: unknown };\r\n atUtcMs: number;\r\n}\r\n\r\n/** A row kept in the app's table after revocation (`G9CRetainedRow`). */\r\nexport interface G9RetainedRow {\r\n localTable: string;\r\n key: string;\r\n disposition: G9ERevocationDisposition;\r\n reason: G9ERevocationReason;\r\n rule: string | null;\r\n atUtcMs: number;\r\n}\r\n\r\n// ---------------------------------------------------------------- failures\r\n\r\n/** What went wrong inside a client store, in terms an application can act on (`G9ELocalStoreFailure`). */\r\nexport const G9ELocalStoreFailure = {\r\n Other: 0,\r\n Corrupt: 1,\r\n DiskFull: 2,\r\n Busy: 3,\r\n} as const;\r\nexport type G9ELocalStoreFailure = (typeof G9ELocalStoreFailure)[keyof typeof G9ELocalStoreFailure];\r\n\r\n/** Implemented by a store's errors so the engine can give a run's failure a stable code (`IG9SyncLocalStoreFailure`). */\r\nexport interface G9SyncLocalStoreFailure {\r\n readonly localStoreFailure: G9ELocalStoreFailure;\r\n}\r\n\r\nexport function isLocalStoreFailure(error: unknown): error is G9SyncLocalStoreFailure {\r\n return typeof error === 'object' && error !== null && typeof (error as { localStoreFailure?: unknown }).localStoreFailure === 'number';\r\n}\r\n\r\n// ---------------------------------------------------------------- the store contract\r\n\r\n/**\r\n * The client storage provider (`IG9SyncClientStore`): local tables, change capture, subscription membership,\r\n * staging, apply and metadata. Every method takes an optional AbortSignal (the CancellationToken twin).\r\n */\r\nexport interface G9SyncClientStore {\r\n initialize(model: G9SyncClientModel, signal?: AbortSignal): Promise<void>;\r\n getClientId(signal?: AbortSignal): Promise<string>;\r\n getServerState(signal?: AbortSignal): Promise<G9ClientServerState>;\r\n /**\r\n * Stores the Hello answer. `localMidpointMs`: the local time the server's clock reading corresponds to - the midpoint of the round\r\n * trip (LES-0075); the offset is then exact up to the asymmetry of the two legs. Omitted: the time of this call.\r\n */\r\n saveHello(hello: G9DtHelloResult, signal?: AbortSignal, localMidpointMs?: number): Promise<void>;\r\n getPendingBackfills(signal?: AbortSignal): Promise<G9DtBackfillRequest[]>;\r\n\r\n getSubscriptions(signal?: AbortSignal): Promise<G9SyncSubscriptionRecord[]>;\r\n ensureSubscription(\r\n name: string,\r\n parameters: G9SyncParameters,\r\n ephemeral: boolean,\r\n ttlMs: number | null,\r\n adoptExistingRows: boolean,\r\n signal?: AbortSignal,\r\n ): Promise<G9SyncSubscriptionRecord>;\r\n removeSubscription(key: string, policy: G9EMoveOutPolicy, signal?: AbortSignal): Promise<void>;\r\n releaseSubscriptionRows(key: string, signal?: AbortSignal): Promise<G9ApplyReport>;\r\n updateSubscription(key: string, update: G9SubscriptionUpdate, signal?: AbortSignal): Promise<void>;\r\n collectExpiredEphemeral(utcNowMs: number, signal?: AbortSignal): Promise<number>;\r\n\r\n buildPushBatch(tables: readonly G9DtNegotiatedTable[], maxRows: number, maxBytes: number, signal?: AbortSignal): Promise<G9PushBatch | null>;\r\n forgetUploadedRows(localTable: string, keys: readonly string[] | null, signal?: AbortSignal): Promise<number>;\r\n applyPushResult(batch: G9PushBatch, result: G9DtPushResult, signal?: AbortSignal): Promise<G9ApplyReport>;\r\n\r\n getStagedPull(signal?: AbortSignal): Promise<G9StagedPull | null>;\r\n /** The newest staged pull planned under exactly this generation, or null (ADR-0060). */\r\n getStagedPullForGeneration(generation: string, signal?: AbortSignal): Promise<G9StagedPull | null>;\r\n beginStagedPull(pull: G9StagedPull, signal?: AbortSignal): Promise<void>;\r\n pruneStagedPulls(olderThanUtcMs: number, maxBytes: number, keepGeneration: string | null, signal?: AbortSignal): Promise<number>;\r\n discardAllStagedPulls(signal?: AbortSignal): Promise<number>;\r\n stageChunk(runId: string, chunk: G9DtSyncChunk, signal?: AbortSignal): Promise<void>;\r\n /**\r\n * The store's offer to receive large first downloads as SQLite snapshot files (ADR-0072), or null for rows only. A store that\r\n * makes an offer stages file segments (`chunk.file`) in `stageChunk` and copies the files in `applyStagedPull`.\r\n */\r\n describeSnapshotFiles?(negotiated: readonly G9DtNegotiatedTable[]): G9DtSnapshotFileRequest | null;\r\n getResumePoint(runId: string, signal?: AbortSignal): Promise<G9ResumePoint>;\r\n applyStagedPull(\r\n runId: string,\r\n rowsApplied: ((rows: number) => void) | null,\r\n slices: ((slice: G9ApplySlice) => void) | null,\r\n signal?: AbortSignal,\r\n ): Promise<G9ApplyReport>;\r\n discardStagedPull(runId: string, signal?: AbortSignal): Promise<void>;\r\n\r\n computeDigest(subscriptionKey: string, table: G9DtNegotiatedTable, signal?: AbortSignal): Promise<G9DtTableDigest>;\r\n readLocalEntries(\r\n subscriptionKey: string,\r\n table: G9DtNegotiatedTable,\r\n buckets: ReadonlySet<number>,\r\n serverKeys: ReadonlySet<string>,\r\n signal?: AbortSignal,\r\n ): Promise<G9LocalRowEntry[]>;\r\n applyReconcile(\r\n subscriptionKey: string,\r\n table: G9DtNegotiatedTable,\r\n fetchedFrames: readonly Uint8Array[],\r\n addMembership: readonly string[],\r\n moveOut: readonly string[],\r\n policy: G9EMoveOutPolicy,\r\n signal?: AbortSignal,\r\n ): Promise<G9ApplyReport>;\r\n\r\n getPendingSummary(signal?: AbortSignal): Promise<G9PendingSummary>;\r\n enqueueCommand(\r\n request: G9CommandRequest,\r\n applyLocally: ((writer: G9LocalCommandWriter, signal?: AbortSignal) => Promise<void>) | null,\r\n signal?: AbortSignal,\r\n ): Promise<G9LocalCommand>;\r\n getQuarantined(signal?: AbortSignal): Promise<G9QuarantinedRow[]>;\r\n restoreQuarantined(localTable: string, key: string, signal?: AbortSignal): Promise<number>;\r\n discardQuarantined(localTable: string | null, key: string | null, signal?: AbortSignal): Promise<number>;\r\n getRetained(signal?: AbortSignal): Promise<G9RetainedRow[]>;\r\n reevaluateRetained(signal?: AbortSignal): Promise<number>;\r\n getCommands(state: G9ELocalCommandState | null, limit: number, signal?: AbortSignal): Promise<G9LocalCommand[]>;\r\n readChangeFeed(localTable: string, afterSequence: number, limit: number, signal?: AbortSignal): Promise<G9ChangeFeedEntry[]>;\r\n /** The feed narrowed to the keys `keyFilter` selects, plus every deletion; fewer than `limit` entries means the read is complete. */\r\n readChangeFeedFiltered(localTable: string, afterSequence: number, limit: number, keyFilter: G9ChangeFeedKeyFilter, signal?: AbortSignal): Promise<G9ChangeFeedEntry[]>;\r\n getChangeSequence(signal?: AbortSignal): Promise<number>;\r\n getChangeFeedFloor(signal?: AbortSignal): Promise<number>;\r\n getRejections(signal?: AbortSignal): Promise<G9SyncRejection[]>;\r\n retryRejected(localTable: string | null, key: string | null, signal?: AbortSignal): Promise<number>;\r\n /** Gives up the local change of rejected rows and takes the server's copy (LES-0070); run a forced reconcile after it. */\r\n revertRejected(localTable: string | null, key: string | null, signal?: AbortSignal): Promise<number>;\r\n markDirty(localTable: string, keys: readonly string[], signal?: AbortSignal): Promise<number>;\r\n adoptExistingRows(signal?: AbortSignal): Promise<number>;\r\n saveRunReport(report: G9SyncRunReport, keep: number, signal?: AbortSignal): Promise<void>;\r\n getRecentRunReports(count: number, signal?: AbortSignal): Promise<G9SyncRunReport[]>;\r\n getPhaseStatistics(signal?: AbortSignal): Promise<{ [key: string]: number }>;\r\n recordPhaseStatistics(samples: { [key: string]: number }, signal?: AbortSignal): Promise<void>;\r\n purgeDeletedFeed(keep: number, signal?: AbortSignal): Promise<number>;\r\n dispose(): Promise<void>;\r\n}\r\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The package version; equal to the .NET packages' (G9SyncDataVersion) — one release, both sides. */
|
|
2
|
-
export declare const VERSION = "1.5.
|
|
2
|
+
export declare const VERSION = "1.5.13";
|
|
3
3
|
export { G9EConflictPolicy, G9EMoveOutPolicy, G9ESyncColumnType, G9ESyncDirection, columnTypeName, isColumnType } from './abstractions/enums.js';
|
|
4
4
|
export { G9SyncErrorCodes, G9SyncException, isRetryableCode, isSyncException, type G9SyncExceptionOptions } from './abstractions/errors.js';
|
|
5
5
|
export { G9Long, parseJson, stringifyJson } from './abstractions/long.js';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// doubles `@g9tm/syncdata/testing`, the pure-JavaScript Brotli decoder `@g9tm/syncdata/brotli-js`; Lynx wiring is the
|
|
4
4
|
// separate package `@g9tm/syncdata-lynx`. Every public type keeps its .NET name without the G9C prefix (PARITY.md).
|
|
5
5
|
/** The package version; equal to the .NET packages' (G9SyncDataVersion) — one release, both sides. */
|
|
6
|
-
export const VERSION = '1.5.
|
|
6
|
+
export const VERSION = '1.5.13';
|
|
7
7
|
// abstractions
|
|
8
8
|
export { G9EConflictPolicy, G9EMoveOutPolicy, G9ESyncColumnType, G9ESyncDirection, columnTypeName, isColumnType } from './abstractions/enums.js';
|
|
9
9
|
export { G9SyncErrorCodes, G9SyncException, isRetryableCode, isSyncException } from './abstractions/errors.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wHAAwH;AACxH,uHAAuH;AACvH,sHAAsH;AACtH,oHAAoH;AAEpH,sGAAsG;AACtG,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC;AAEhC,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACjJ,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAA+B,MAAM,0BAA0B,CAAC;AAC5I,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACvG,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAElF,WAAW;AACX,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC5G,OAAO,EACL,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,kBAAkB,GAEnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACrI,OAAO,EAAE,gBAAgB,EAA4E,MAAM,+BAA+B,CAAC;AAC3I,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,2BAA2B,GAI5B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAqB,MAAM,4BAA4B,CAAC;AAE3H,SAAS;AACT,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,wBAAwB,EAAmD,MAAM,mBAAmB,CAAC;AAC9K,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,SAAS,EAAyB,MAAM,sBAAsB,CAAC;AAC7G,OAAO,EACL,cAAc,EACd,eAAe,EACf,cAAc,EACd,SAAS,EACT,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,YAAY,EACZ,oBAAoB,GAYrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEnE,2CAA2C;AAC3C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["// @g9tm/syncdata — the TypeScript twin of the G9SyncData .NET client (abstractions, protocol, engine). The SQLite store\r\n// is `@g9tm/syncdata/sqlite`, the SignalR transport `@g9tm/syncdata/signalr`, Node helpers `@g9tm/syncdata/node`, test\r\n// doubles `@g9tm/syncdata/testing`, the pure-JavaScript Brotli decoder `@g9tm/syncdata/brotli-js`; Lynx wiring is the\r\n// separate package `@g9tm/syncdata-lynx`. Every public type keeps its .NET name without the G9C prefix (PARITY.md).\r\n\r\n/** The package version; equal to the .NET packages' (G9SyncDataVersion) — one release, both sides. */\r\nexport const VERSION = '1.5.
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wHAAwH;AACxH,uHAAuH;AACvH,sHAAsH;AACtH,oHAAoH;AAEpH,sGAAsG;AACtG,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC;AAEhC,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACjJ,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAA+B,MAAM,0BAA0B,CAAC;AAC5I,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACvG,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAElF,WAAW;AACX,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC5G,OAAO,EACL,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,kBAAkB,GAEnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACrI,OAAO,EAAE,gBAAgB,EAA4E,MAAM,+BAA+B,CAAC;AAC3I,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,2BAA2B,GAI5B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAqB,MAAM,4BAA4B,CAAC;AAE3H,SAAS;AACT,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,wBAAwB,EAAmD,MAAM,mBAAmB,CAAC;AAC9K,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,SAAS,EAAyB,MAAM,sBAAsB,CAAC;AAC7G,OAAO,EACL,cAAc,EACd,eAAe,EACf,cAAc,EACd,SAAS,EACT,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,WAAW,EACX,YAAY,EACZ,oBAAoB,GAYrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEnE,2CAA2C;AAC3C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC","sourcesContent":["// @g9tm/syncdata — the TypeScript twin of the G9SyncData .NET client (abstractions, protocol, engine). The SQLite store\r\n// is `@g9tm/syncdata/sqlite`, the SignalR transport `@g9tm/syncdata/signalr`, Node helpers `@g9tm/syncdata/node`, test\r\n// doubles `@g9tm/syncdata/testing`, the pure-JavaScript Brotli decoder `@g9tm/syncdata/brotli-js`; Lynx wiring is the\r\n// separate package `@g9tm/syncdata-lynx`. Every public type keeps its .NET name without the G9C prefix (PARITY.md).\r\n\r\n/** The package version; equal to the .NET packages' (G9SyncDataVersion) — one release, both sides. */\r\nexport const VERSION = '1.5.13';\r\n\r\n// abstractions\r\nexport { G9EConflictPolicy, G9EMoveOutPolicy, G9ESyncColumnType, G9ESyncDirection, columnTypeName, isColumnType } from './abstractions/enums.js';\r\nexport { G9SyncErrorCodes, G9SyncException, isRetryableCode, isSyncException, type G9SyncExceptionOptions } from './abstractions/errors.js';\r\nexport { G9Long, parseJson, stringifyJson } from './abstractions/long.js';\r\nexport { EMPTY_GUID, guidToN, newGuid, normalizeGuid, tryNormalizeGuid } from './abstractions/guid.js';\r\nexport { G9SyncKey } from './abstractions/key.js';\r\nexport { G9SyncDigest } from './abstractions/digest.js';\r\nexport { G9SyncParameters, escapeDataString } from './abstractions/parameters.js';\r\n\r\n// protocol\r\nexport * from './protocol/dtos.js';\r\nexport { G9SyncHubErrors, G9SyncProtocol, negotiateFrameVersion, tableGroup } from './protocol/protocol.js';\r\nexport {\r\n bytesField,\r\n dateTimeMs,\r\n fromMessagePackNames,\r\n G9WireGuid,\r\n GUID_EXTENSION_TYPE,\r\n readChangesAvailable,\r\n readContinuation,\r\n readHelloResult,\r\n readPullPlan,\r\n readPushResult,\r\n readReconcileSummary,\r\n readSyncChunk,\r\n toMessagePackNames,\r\n type G9WireFormat,\r\n} from './protocol/wire.js';\r\nexport { G9ECompression, G9EPushRowOp, G9ERowBatchOp, G9RowBatchFormat, G9RowBatchFormatError } from './protocol/rowBatch/format.js';\r\nexport { G9RowBatchWriter, type G9DateTimeOffsetValue, type G9DateTimeValue, type G9FrameCompressor } from './protocol/rowBatch/writer.js';\r\nexport {\r\n G9RowBatchReader,\r\n getDefaultFrameDecompressor,\r\n openRowBatch,\r\n openRowBatchSync,\r\n readFrameHeader,\r\n setDefaultFrameDecompressor,\r\n type G9FrameDecompressor,\r\n type G9FrameHeader,\r\n type G9OpenRowBatchOptions,\r\n} from './protocol/rowBatch/reader.js';\r\nexport { decimalToDouble, formatDecimal, formatDouble, parseDecimal, type DecimalParts } from './internal/dotnetFormat.js';\r\n\r\n// client\r\nexport { G9SyncClientModel, G9SyncClientModelBuilder, G9SyncClientTable, G9SyncClientTableBuilder, type G9SyncClientColumn, type G9SyncClientIndex } from './client/model.js';\r\nexport * from './client/storeTypes.js';\r\nexport type { G9SyncTransport } from './client/transport.js';\r\nexport { G9ESyncPhase, G9SyncProgressTracker, phaseName, type G9DtSyncProgress } from './client/progress.js';\r\nexport {\r\n G9ESyncOutcome,\r\n G9ESyncPriority,\r\n G9SyncPostStep,\r\n G9SyncRun,\r\n describeModelProblem,\r\n isTransientError,\r\n normalizeError,\r\n outcomeName,\r\n priorityName,\r\n resolveEngineOptions,\r\n type G9RowsAppliedEvent,\r\n type G9SubscriptionOptions,\r\n type G9SyncEngineOptions,\r\n type G9SyncHealth,\r\n type G9SyncHostHooks,\r\n type G9SyncLogger,\r\n type G9SyncLogLevel,\r\n type G9SyncModelProblem,\r\n type G9SyncPostStepContext,\r\n type G9SyncRequest,\r\n type G9SyncRunResult,\r\n} from './client/runs.js';\r\nexport { G9SyncEngine, stageGeneration } from './client/engine.js';\r\n\r\n// helpers the Lynx package and tests share\r\nexport { base64ToBytes, bytesToBase64 } from './internal/base64.js';\r\nexport { utf8Decode, utf8Encode } from './internal/utf8.js';\r\nexport { md5 } from './internal/md5.js';\r\nexport { sha256, toHex } from './internal/sha256.js';\r\nexport { cancellationError, createAbortController, delay, linkSignals, onAbort, yieldToEventLoop } from './internal/abort.js';\r\n"]}
|
package/dist/sqlite/push.js
CHANGED
|
@@ -319,6 +319,14 @@ async function readLostConflicts(store, batch, result) {
|
|
|
319
319
|
}
|
|
320
320
|
return lost;
|
|
321
321
|
}
|
|
322
|
+
/** Two SQLite values are the same value (a blob by its bytes); a different type counts as different. Twin of .NET SameValue. */
|
|
323
|
+
function sameValue(a, b) {
|
|
324
|
+
if (a instanceof Uint8Array && b instanceof Uint8Array)
|
|
325
|
+
return a.length === b.length && a.every((x, i) => x === b[i]);
|
|
326
|
+
if (typeof a === 'bigint' || typeof b === 'bigint')
|
|
327
|
+
return typeof a === typeof b && a === b;
|
|
328
|
+
return a === b || (a == null && b == null);
|
|
329
|
+
}
|
|
322
330
|
/** Twin of .NET KeepLostConflicts: the device's lost values back over the canonical rows, parked as rejections (LES-0072). */
|
|
323
331
|
async function keepLostConflicts(store, context, lost) {
|
|
324
332
|
var _a;
|
|
@@ -329,6 +337,7 @@ async function keepLostConflicts(store, context, lost) {
|
|
|
329
337
|
const now = await readLocalValues(connection, table, item.key);
|
|
330
338
|
let columnNames = null;
|
|
331
339
|
let serverValues = null;
|
|
340
|
+
let lostMask = item.lostMask;
|
|
332
341
|
if (item.dirty === 2) {
|
|
333
342
|
if (Object.keys(now).length > 0)
|
|
334
343
|
await connection.exec(`DELETE FROM ${table.quoted} WHERE ${table.keyQuoted} = $k`, { $k: key });
|
|
@@ -343,7 +352,16 @@ async function keepLostConflicts(store, context, lost) {
|
|
|
343
352
|
await connection.exec(`INSERT INTO ${table.quoted} (${all.map(quote).join(', ')}) VALUES (${all.map((_, i) => '$v' + i).join(', ')})`, params);
|
|
344
353
|
}
|
|
345
354
|
else {
|
|
346
|
-
const
|
|
355
|
+
const lostColumns = table.model.columns.map((c, i) => ({ name: c.name, i })).filter((c) => item.lostMask.testBit(c.i) && c.name in item.values);
|
|
356
|
+
// LES-0085: a column the server already holds with the device's own value lost nothing; when none differs, the row is in sync.
|
|
357
|
+
const differing = lostColumns.filter((c) => !sameValue(item.values[c.name], now[c.name]));
|
|
358
|
+
if (lostColumns.length > 0 && differing.length === 0) {
|
|
359
|
+
context.report.conflictsSameValue++;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (differing.length > 0)
|
|
363
|
+
lostMask = differing.reduce((m, c) => m.or(G9Long.ONE.shiftLeft(c.i)), G9Long.ZERO);
|
|
364
|
+
const columns = differing.map((c) => c.name);
|
|
347
365
|
if (columns.length > 0) {
|
|
348
366
|
columnNames = columns.join(',');
|
|
349
367
|
const server = {};
|
|
@@ -355,7 +373,7 @@ async function keepLostConflicts(store, context, lost) {
|
|
|
355
373
|
await connection.exec(`UPDATE ${table.quoted} SET ${columns.map((c, i) => quote(c) + ' = $v' + i).join(', ')} WHERE ${table.keyQuoted} = $k`, params);
|
|
356
374
|
}
|
|
357
375
|
}
|
|
358
|
-
const mask = item.dirty === 2 ? G9Long.ZERO :
|
|
376
|
+
const mask = item.dirty === 2 ? G9Long.ZERO : lostMask;
|
|
359
377
|
await connection.exec('INSERT INTO g9_rows (tid, pk, server_ver, local_seq, subs, dirty, dirty_mask, rejected, deleted) ' +
|
|
360
378
|
'VALUES ($t, $pk, $v, (SELECT seq FROM g9_clock WHERE id = 1) + 1, $subs, $d, $m, 1, 0) ' +
|
|
361
379
|
'ON CONFLICT(tid, pk) DO UPDATE SET dirty = excluded.dirty, dirty_mask = excluded.dirty_mask, rejected = 1, deleted = 0, ' +
|
package/dist/sqlite/push.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/sqlite/push.ts"],"names":[],"mappings":"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","sourcesContent":["// Twin of G9SyncData.Client.Sqlite/G9CSqliteSyncStore.Push.cs: dirty rows in local change order become push frames; the\r\n// result clears exactly the rows that were sent (rows edited meanwhile stay dirty), flags rejections and applies canonical\r\n// server rows.\r\nimport { G9ESyncDirection } from '../abstractions/enums.js';\r\nimport { G9SyncErrorCodes } from '../abstractions/errors.js';\r\nimport { G9Long } from '../abstractions/long.js';\r\nimport { utf8Encode } from '../internal/utf8.js';\r\nimport { G9ApplyReport, type G9PushBatch } from '../client/storeTypes.js';\r\nimport type { G9DtNegotiatedTable, G9DtPushResult, G9DtTableColumns } from '../protocol/dtos.js';\r\nimport { G9EPushRowOp, G9ERowBatchOp } from '../protocol/rowBatch/format.js';\r\nimport { G9RowBatchWriter } from '../protocol/rowBatch/writer.js';\r\nimport { applyFrame, beginApply, endApply } from './apply.js';\r\nimport { completeCommands, readPendingCommands } from './commands.js';\r\nimport { asLong, asNumber, asString } from './connection.js';\r\nimport type { G9SqlValue } from './driver.js';\r\nimport { findByServerName, getLocal, keyValue, meta, nowMs, readLocalValues, serializeValues, setMeta, type LocalTable, type StoreInternals } from './internals.js';\r\nimport { quote } from './sql.js';\r\nimport { estimateEncodedBytes, pushSelectExpression, writeLocalValue } from './values.js';\r\n\r\ninterface PushColumn {\r\n name: string;\r\n type: number;\r\n local: number;\r\n}\r\n\r\ninterface PushSection {\r\n table: LocalTable;\r\n negotiated: G9DtNegotiatedTable;\r\n columns: PushColumn[];\r\n writer: G9RowBatchWriter;\r\n select: string;\r\n rows: number;\r\n}\r\n\r\nconst ROW_OVERHEAD = 48;\r\n\r\n/** `BuildPushBatchAsync`: the next batch of pending changes, oldest first, within the row and byte budgets. */\r\nexport async function buildPushBatch(store: StoreInternals, tables: readonly G9DtNegotiatedTable[], maxRows: number, maxBytes: number): Promise<G9PushBatch | null> {\r\n store.ensureInitialized();\r\n const uploadable = new Map<number, { table: LocalTable; negotiated: G9DtNegotiatedTable; columns: PushColumn[] }>();\r\n for (const negotiated of tables) {\r\n if (negotiated.direction === G9ESyncDirection.DownloadOnly) continue;\r\n const local = findByServerName(store, negotiated.name);\r\n if (!local) continue;\r\n const readOnly = new Set<string>();\r\n for (const c of negotiated.readOnlyColumns) readOnly.add(c.toLowerCase());\r\n for (const c of negotiated.computedColumns) readOnly.add(c.toLowerCase());\r\n const columns = negotiated.columns\r\n .filter((c) => !readOnly.has(c.name.toLowerCase()) && local.model.indexOf(c.name) >= 0)\r\n .map((c) => ({ name: c.name, type: c.type, local: local.model.indexOf(c.name) }));\r\n uploadable.set(local.tid, { table: local, negotiated, columns });\r\n }\r\n\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n const inflightBatch = await meta(connection, 'inflight_batch_id');\r\n const inflightMax = await meta(connection, 'inflight_max_seq');\r\n const offsetText = await meta(connection, 'server_offset_ms');\r\n const offsetMs = offsetText !== null && /^[+-]?\\d+$/.test(offsetText) ? Number(offsetText) : 0;\r\n\r\n const tids = Array.from(uploadable.keys());\r\n // `+tid`: the unary plus keeps the tid list from driving the plan. Without it SQLite searches the primary key by\r\n // tid — every row of every uploadable table (486k on a farm device, 16 s on the bridge) — and sorts them, instead\r\n // of walking the partial g9_rows_dirty index, which holds only the pending rows and is already in local_seq order.\r\n const sql =\r\n 'SELECT tid, pk, dirty, dirty_mask, server_ver, local_seq, coalesce(hlc_ms, 0) FROM g9_rows WHERE dirty <> 0 AND rejected = 0 AND +tid IN (' +\r\n tids.join(',') +\r\n ')' +\r\n (inflightMax === null ? '' : ' AND local_seq <= $imax') +\r\n ' ORDER BY local_seq LIMIT $max';\r\n const commands = await readPendingCommands(store, connection, inflightBatch, Math.max(1, maxRows));\r\n const pending =\r\n uploadable.size === 0\r\n ? []\r\n : await connection.query(sql, inflightMax === null ? { $max: Math.max(1, maxRows) } : { $max: Math.max(1, maxRows), $imax: Number(inflightMax) });\r\n const rows = pending.map((r) => ({\r\n tid: asNumber(r[0]),\r\n key: asString(r[1]),\r\n dirty: asNumber(r[2]),\r\n mask: asLong(r[3]),\r\n base: asNumber(r[4]),\r\n seq: asNumber(r[5]),\r\n hlc: asNumber(r[6]),\r\n }));\r\n\r\n if (rows.length === 0 && commands.length === 0) {\r\n if (inflightBatch !== null) {\r\n // Everything the in-flight batch carried has been resolved another way; forget it.\r\n await connection.exec(\"DELETE FROM g9_meta WHERE key IN ('inflight_batch_id', 'inflight_max_seq')\");\r\n }\r\n return null;\r\n }\r\n\r\n // Rows are encoded in local_seq order and the batch ends at the first row past the byte budget, so a batch is always\r\n // a PREFIX of the pending changes. One section (frame) per table.\r\n const sections = new Map<number, PushSection>();\r\n let included = 0;\r\n let includedMaxSequence = 0;\r\n let budgetUsed = 0;\r\n let truncated = false;\r\n const tooLarge: { tid: number; key: string; bytes: number }[] = [];\r\n for (const row of rows) {\r\n const entry = uploadable.get(row.tid);\r\n if (!entry) continue;\r\n let section = sections.get(row.tid);\r\n if (!section) {\r\n const select =\r\n entry.columns.length === 0\r\n ? `SELECT 1 FROM ${entry.table.quoted} WHERE ${entry.table.keyQuoted} = $k`\r\n : `SELECT ${entry.columns.map((c) => pushSelectExpression(quote(c.name), c.type)).join(', ')} FROM ${entry.table.quoted} WHERE ${entry.table.keyQuoted} = $k`;\r\n const writer = new G9RowBatchWriter();\r\n writer.beginSection(\r\n G9ERowBatchOp.Push,\r\n entry.negotiated.tableId,\r\n entry.table.model.keyType,\r\n entry.columns.map((c) => c.type),\r\n );\r\n section = { table: entry.table, negotiated: entry.negotiated, columns: entry.columns, writer, select, rows: 0 };\r\n sections.set(row.tid, section);\r\n }\r\n\r\n const hlc = row.hlc === 0 ? 0 : row.hlc + offsetMs;\r\n const keyBytes = utf8Encode(row.key).length;\r\n if (row.dirty === 2) {\r\n const deleteBytes = ROW_OVERHEAD + keyBytes;\r\n if (included > 0 && budgetUsed + deleteBytes > maxBytes) {\r\n truncated = true;\r\n break;\r\n }\r\n section.writer.beginPushRow(row.key, G9EPushRowOp.Delete, row.seq, row.base, G9Long.ZERO, hlc);\r\n section.writer.endRow();\r\n section.rows++;\r\n included++;\r\n includedMaxSequence = row.seq;\r\n budgetUsed += deleteBytes;\r\n continue;\r\n }\r\n\r\n const found = await connection.query(section.select, { $k: keyValue(section.table, row.key) });\r\n const values = found[0];\r\n if (!values) {\r\n section.writer.beginPushRow(row.key, G9EPushRowOp.Delete, row.seq, row.base, G9Long.ZERO, hlc);\r\n section.writer.endRow();\r\n section.rows++;\r\n included++;\r\n includedMaxSequence = row.seq;\r\n budgetUsed += ROW_OVERHEAD + keyBytes;\r\n continue;\r\n }\r\n\r\n let rowBytes = ROW_OVERHEAD + keyBytes;\r\n for (let j = 0; j < section.columns.length; j++) rowBytes += estimateEncodedBytes(values[j] as G9SqlValue, (section.columns[j] as PushColumn).type);\r\n if (rowBytes > maxBytes) {\r\n // Can never fit, whatever else the batch carries: reject it here so it does not block every later push.\r\n tooLarge.push({ tid: row.tid, key: row.key, bytes: rowBytes });\r\n continue;\r\n }\r\n if (included > 0 && budgetUsed + rowBytes > maxBytes) {\r\n truncated = true;\r\n break;\r\n }\r\n\r\n let pushMask = G9Long.ZERO;\r\n for (let j = 0; j < section.columns.length; j++) {\r\n if (row.mask.testBit((section.columns[j] as PushColumn).local)) pushMask = pushMask.or(G9Long.ONE.shiftLeft(j));\r\n }\r\n section.writer.beginPushRow(row.key, G9EPushRowOp.Upsert, row.seq, row.base, pushMask, hlc);\r\n for (let j = 0; j < section.columns.length; j++) writeLocalValue(section.writer, values[j] as G9SqlValue, (section.columns[j] as PushColumn).type, store.options);\r\n section.writer.endRow();\r\n section.rows++;\r\n included++;\r\n includedMaxSequence = row.seq;\r\n budgetUsed += rowBytes;\r\n }\r\n\r\n for (const { tid, key, bytes } of tooLarge) {\r\n await connection.exec('UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk', { $t: tid, $pk: key });\r\n await connection.exec(\r\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +\r\n 'VALUES ($t, $pk, $code, $message, NULL, NULL, NULL, NULL, $at)',\r\n {\r\n $t: tid,\r\n $pk: key,\r\n $code: G9SyncErrorCodes.RejectTooLarge,\r\n $message: `The row needs about ${bytes} bytes; the server accepts at most ${maxBytes} bytes per push.`,\r\n $at: nowMs(),\r\n },\r\n );\r\n }\r\n\r\n if (included === 0 && commands.length === 0) {\r\n if (tooLarge.length === 0) return null;\r\n return { batchId: 0, maxSequence: 0, frames: [], tables: [], rowCount: 0, truncated: false, rejectedLocally: tooLarge.length, commands: [] };\r\n }\r\n\r\n const frames: Uint8Array[] = [];\r\n const usedTables: G9DtTableColumns[] = [];\r\n sections.forEach((section) => {\r\n if (section.rows === 0) return;\r\n section.writer.endSection();\r\n frames.push(section.writer.toFrame({ compressor: store.options.frameCompressor }));\r\n usedTables.push({ tableId: section.negotiated.tableId, columns: section.columns.map((c) => c.name) });\r\n });\r\n\r\n let batchId: number;\r\n let maxSequence: number;\r\n if (inflightBatch !== null) {\r\n batchId = Number(inflightBatch);\r\n maxSequence = Number(inflightMax);\r\n } else {\r\n batchId = Number((await meta(connection, 'next_batch_id')) ?? '1');\r\n maxSequence = includedMaxSequence;\r\n await setMeta(connection, 'next_batch_id', String(batchId + 1));\r\n await setMeta(connection, 'inflight_batch_id', String(batchId));\r\n await setMeta(connection, 'inflight_max_seq', String(maxSequence));\r\n }\r\n for (const item of commands) {\r\n await connection.exec('UPDATE g9_commands SET batch_id = $b WHERE command_id = $id', { $b: batchId, $id: item.commandId });\r\n }\r\n return { batchId, maxSequence, frames, tables: usedTables, rowCount: included, truncated, rejectedLocally: tooLarge.length, commands };\r\n }),\r\n );\r\n}\r\n\r\n/** `ForgetUploadedRowsAsync`: removes uploaded rows of an UPLOAD-ONLY table without uploading a delete. */\r\nexport async function forgetUploadedRows(store: StoreInternals, localTable: string, keys: readonly string[] | null): Promise<number> {\r\n store.ensureInitialized();\r\n const table = getLocal(store, localTable);\r\n if (!table.negotiated || table.negotiated.direction !== G9ESyncDirection.UploadOnly) {\r\n throw new Error(`'${localTable}' is not upload-only: deleting its rows locally is a change the server must receive.`);\r\n }\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n await connection.exec('UPDATE g9_ctx SET applying = 1 WHERE id = 1'); // a library write: not captured\r\n await connection.exec('CREATE TEMP TABLE IF NOT EXISTS g9_forget (pk TEXT PRIMARY KEY); DELETE FROM g9_forget');\r\n if (keys === null) {\r\n await connection.exec('INSERT INTO g9_forget (pk) SELECT pk FROM g9_rows WHERE tid = $t AND dirty = 0', { $t: table.tid });\r\n } else {\r\n for (const key of keys) {\r\n await connection.exec('INSERT OR IGNORE INTO g9_forget (pk) SELECT pk FROM g9_rows WHERE tid = $t AND pk = $pk AND dirty = 0', { $t: table.tid, $pk: key });\r\n }\r\n }\r\n const forgotten = asNumber(await connection.scalar('SELECT count(*) FROM g9_forget'));\r\n await connection.exec(`DELETE FROM ${table.quoted} WHERE CAST(${table.keyQuoted} AS TEXT) IN (SELECT pk FROM g9_forget)`);\r\n await connection.exec('DELETE FROM g9_rows WHERE tid = $t AND pk IN (SELECT pk FROM g9_forget); DELETE FROM g9_forget', { $t: table.tid });\r\n await connection.exec('UPDATE g9_ctx SET applying = 0 WHERE id = 1');\r\n return forgotten;\r\n }),\r\n );\r\n}\r\n\r\n/** `ApplyPushResultAsync`. */\r\nexport async function applyPushResult(store: StoreInternals, batch: G9PushBatch, result: G9DtPushResult, signal?: AbortSignal): Promise<G9ApplyReport> {\r\n store.ensureInitialized();\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n // Before BeginApply: finished commands release their rows, so the canonical rows below replace the optimistic ones.\r\n const completed = await completeCommands(connection, result.commandResults);\r\n // Read BEFORE anything below touches the rows: what the device sent for each conflict it may have lost (LES-0072).\r\n const lost = await readLostConflicts(store, batch, result);\r\n const context = await beginApply(store, connection, { dirtyAfterSequence: batch.maxSequence, backfills: [], signal });\r\n context.report.commandsCompleted = completed;\r\n\r\n const acceptSql =\r\n 'UPDATE g9_rows SET server_ver = $v, ' +\r\n 'dirty = CASE WHEN local_seq <= $m THEN 0 ELSE dirty END, ' +\r\n 'dirty_mask = CASE WHEN local_seq <= $m THEN 0 ELSE dirty_mask END, ' +\r\n 'deleted = CASE WHEN local_seq <= $m AND dirty = 2 THEN 1 ELSE deleted END, ' +\r\n 'rejected = 0 WHERE tid = $t AND pk = $pk; ' +\r\n 'DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk';\r\n for (const accepted of result.accepted) {\r\n const table = store.byServerId.get(accepted.tableId);\r\n if (!table) continue;\r\n for (let i = 0; i < accepted.keys.length; i++) {\r\n await context.queue(acceptSql, { $v: i < accepted.versions.length ? (accepted.versions[i] as number) : 0, $m: batch.maxSequence, $t: table.tid, $pk: accepted.keys[i] as string });\r\n context.report.accepted++;\r\n }\r\n }\r\n\r\n const rejectSql =\r\n 'UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk AND local_seq <= $m; ' +\r\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +\r\n 'VALUES ($t, $pk, $code, $message, $state, $constraint, $column, $details, $now)';\r\n const now = nowMs();\r\n for (const rejection of result.rejections) {\r\n const table = store.byServerId.get(rejection.tableId);\r\n if (!table) continue;\r\n await context.queue(rejectSql, {\r\n $t: table.tid,\r\n $pk: rejection.key,\r\n $m: batch.maxSequence,\r\n $code: rejection.code,\r\n $message: rejection.message ?? null,\r\n $state: rejection.sqlState ?? null,\r\n $constraint: rejection.constraint ?? null,\r\n $column: rejection.column ?? null,\r\n $details: rejection.details ?? null,\r\n $now: now,\r\n });\r\n context.report.rejected++;\r\n }\r\n\r\n context.report.conflicts = result.conflicts.length;\r\n await context.flush();\r\n for (const frame of result.canonicalFrames) await applyFrame(store, context, frame, null);\r\n await context.flush();\r\n await keepLostConflicts(store, context, lost);\r\n\r\n await endApply(context);\r\n await connection.exec(\"DELETE FROM g9_meta WHERE key IN ('inflight_batch_id', 'inflight_max_seq')\");\r\n return context.report;\r\n }),\r\n );\r\n}\r\n\r\n/** A conflict the device lost, with its row as it was sent. */\r\ninterface LostConflict {\r\n table: LocalTable;\r\n key: string;\r\n code: string;\r\n dirty: number;\r\n lostMask: G9Long;\r\n subs: G9Long;\r\n version: G9Long;\r\n values: { [column: string]: G9SqlValue };\r\n}\r\n\r\n/**\r\n * Twin of .NET ReadLostConflicts (LES-0072): the conflicts of this push in which the server kept its own values for something the\r\n * device changed - the whole change, or the overlapping columns of a column merge - each with the device's row as it was sent.\r\n */\r\nasync function readLostConflicts(store: StoreInternals, batch: G9PushBatch, result: G9DtPushResult): Promise<LostConflict[]> {\r\n const lost: LostConflict[] = [];\r\n if (!store.options.keepLostConflicts || result.conflicts.length === 0) return lost;\r\n const connection = store.writer();\r\n const pushOrder = new Map(batch.tables.map((t) => [t.tableId, t.columns]));\r\n for (const conflict of result.conflicts) {\r\n const table = store.byServerId.get(conflict.tableId);\r\n if (!table) continue;\r\n const row = (await connection.query('SELECT dirty, dirty_mask, subs, server_ver, local_seq FROM g9_rows WHERE tid = $t AND pk = $pk', { $t: table.tid, $pk: conflict.key }))[0];\r\n if (!row) continue;\r\n const dirty = asNumber(row[0]);\r\n const mask = asLong(row[1]);\r\n if (dirty === 0 || asNumber(row[4]) > batch.maxSequence) continue;\r\n const serverDeleted = conflict.code === G9SyncErrorCodes.ConflictDeleted;\r\n if (dirty === 2 && serverDeleted) continue; // both sides deleted it: nothing was lost\r\n\r\n let lostMask = mask;\r\n const names = pushOrder.get(conflict.tableId);\r\n if (dirty === 1 && conflict.partiallyApplied && !conflict.serverChangedMask.isNegative() && conflict.code === G9SyncErrorCodes.ConflictColumns && names) {\r\n // The server's bits are in push column order; the device's are model columns.\r\n lostMask = G9Long.ZERO;\r\n for (let j = 0; j < names.length && j < 63; j++) {\r\n if (!conflict.serverChangedMask.testBit(j)) continue;\r\n const local = table.model.indexOf(names[j] as string);\r\n if (local >= 0 && mask.testBit(local)) lostMask = lostMask.or(G9Long.ONE.shiftLeft(local));\r\n }\r\n if (lostMask.isZero()) continue;\r\n }\r\n\r\n lost.push({ table, key: conflict.key, code: conflict.code, dirty, lostMask, subs: asLong(row[2]), version: asLong(row[3]), values: await readLocalValues(connection, table, conflict.key) });\r\n }\r\n return lost;\r\n}\r\n\r\n/** Twin of .NET KeepLostConflicts: the device's lost values back over the canonical rows, parked as rejections (LES-0072). */\r\nasync function keepLostConflicts(store: StoreInternals, context: Awaited<ReturnType<typeof beginApply>>, lost: LostConflict[]): Promise<void> {\r\n const connection = context.connection;\r\n for (const item of lost) {\r\n const table = item.table;\r\n const key = keyValue(table, item.key);\r\n const now = await readLocalValues(connection, table, item.key);\r\n let columnNames: string | null = null;\r\n let serverValues: string | null = null;\r\n if (item.dirty === 2) {\r\n if (Object.keys(now).length > 0) await connection.exec(`DELETE FROM ${table.quoted} WHERE ${table.keyQuoted} = $k`, { $k: key });\r\n } else if (Object.keys(now).length === 0) {\r\n // The server deleted it: the device's row comes back as it was sent.\r\n const all = Object.keys(item.values);\r\n if (all.length === 0) continue;\r\n const params: { [name: string]: G9SqlValue } = {};\r\n all.forEach((column, i) => (params['$v' + i] = item.values[column] as G9SqlValue));\r\n await connection.exec(`INSERT INTO ${table.quoted} (${all.map(quote).join(', ')}) VALUES (${all.map((_, i) => '$v' + i).join(', ')})`, params);\r\n } else {\r\n const columns = table.model.columns.map((c, i) => ({ name: c.name, i })).filter((c) => item.lostMask.testBit(c.i) && c.name in item.values).map((c) => c.name);\r\n if (columns.length > 0) {\r\n columnNames = columns.join(',');\r\n const server: { [column: string]: unknown } = {};\r\n for (const column of columns) server[column] = now[column] ?? null;\r\n serverValues = serializeValues(server);\r\n const params: { [name: string]: G9SqlValue } = { $k: key };\r\n columns.forEach((column, i) => (params['$v' + i] = item.values[column] as G9SqlValue));\r\n await connection.exec(`UPDATE ${table.quoted} SET ${columns.map((c, i) => quote(c) + ' = $v' + i).join(', ')} WHERE ${table.keyQuoted} = $k`, params);\r\n }\r\n }\r\n\r\n const mask = item.dirty === 2 ? G9Long.ZERO : item.lostMask;\r\n await connection.exec(\r\n 'INSERT INTO g9_rows (tid, pk, server_ver, local_seq, subs, dirty, dirty_mask, rejected, deleted) ' +\r\n 'VALUES ($t, $pk, $v, (SELECT seq FROM g9_clock WHERE id = 1) + 1, $subs, $d, $m, 1, 0) ' +\r\n 'ON CONFLICT(tid, pk) DO UPDATE SET dirty = excluded.dirty, dirty_mask = excluded.dirty_mask, rejected = 1, deleted = 0, ' +\r\n 'subs = CASE WHEN g9_rows.subs = 0 THEN excluded.subs ELSE g9_rows.subs END, local_seq = excluded.local_seq; ' +\r\n 'UPDATE g9_clock SET seq = seq + 1 WHERE id = 1',\r\n { $t: table.tid, $pk: item.key, $v: item.version.toSqlValue(), $subs: item.subs.toSqlValue(), $d: item.dirty, $m: mask.toSqlValue() },\r\n );\r\n await connection.exec(\r\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +\r\n 'VALUES ($t, $pk, $code, $message, NULL, NULL, $column, $details, $now)',\r\n {\r\n $t: table.tid,\r\n $pk: item.key,\r\n $code: item.code,\r\n $message:\r\n item.dirty === 2\r\n ? \"The server changed this row after the device last saw it; the device's delete was kept here, not applied.\"\r\n : `The server changed ${columnNames ?? 'this row'} after the device last saw it; the device's values were kept here, not applied.`,\r\n $column: columnNames,\r\n $details: serverValues,\r\n $now: nowMs(),\r\n },\r\n );\r\n context.report.conflictsKept++;\r\n if (context.wantsChangedKey(table.model.localName)) context.addChanged(table.model.localName, item.key);\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/sqlite/push.ts"],"names":[],"mappings":"AAAA,wHAAwH;AACxH,2HAA2H;AAC3H,eAAe;AACf,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAoB,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,OAAO,EAAwC,MAAM,gBAAgB,CAAC;AACpK,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAiB1F,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB,+GAA+G;AAC/G,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAqB,EAAE,MAAsC,EAAE,OAAe,EAAE,QAAgB;IACnI,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAyF,CAAC;IACpH,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE,CAAC;QAChC,IAAI,UAAU,CAAC,SAAS,KAAK,gBAAgB,CAAC,YAAY;YAAE,SAAS;QACrE,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,eAAe;YAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1E,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,eAAe;YAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1E,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO;aAC/B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACtF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACpF,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,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,aAAa,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAC;QAClE,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;QAC/D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,UAAU,KAAK,IAAI,IAAI,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE/F,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3C,iHAAiH;QACjH,kHAAkH;QAClH,mHAAmH;QACnH,MAAM,GAAG,GACP,4IAA4I;YAC5I,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;YACd,GAAG;YACH,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,yBAAyB,CAAC;YACvD,gCAAgC,CAAC;QACnC,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACnG,MAAM,OAAO,GACX,UAAU,CAAC,IAAI,KAAK,CAAC;YACnB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACtJ,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/B,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnB,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnB,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAClB,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpB,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnB,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACpB,CAAC,CAAC,CAAC;QAEJ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;gBAC3B,mFAAmF;gBACnF,MAAM,UAAU,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;YACtG,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,qHAAqH;QACrH,kEAAkE;QAClE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;QAChD,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,mBAAmB,GAAG,CAAC,CAAC;QAC5B,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,MAAM,QAAQ,GAAkD,EAAE,CAAC;QACnE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,IAAI,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,MAAM,GACV,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;oBACxB,CAAC,CAAC,iBAAiB,KAAK,CAAC,KAAK,CAAC,MAAM,UAAU,KAAK,CAAC,KAAK,CAAC,SAAS,OAAO;oBAC3E,CAAC,CAAC,UAAU,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,KAAK,CAAC,MAAM,UAAU,KAAK,CAAC,KAAK,CAAC,SAAS,OAAO,CAAC;gBAClK,MAAM,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;gBACtC,MAAM,CAAC,YAAY,CACjB,aAAa,CAAC,IAAI,EAClB,KAAK,CAAC,UAAU,CAAC,OAAO,EACxB,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EACzB,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CACjC,CAAC;gBACF,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;gBAChH,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACjC,CAAC;YAED,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,QAAQ,CAAC;YACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;YAC5C,IAAI,GAAG,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;gBACpB,MAAM,WAAW,GAAG,YAAY,GAAG,QAAQ,CAAC;gBAC5C,IAAI,QAAQ,GAAG,CAAC,IAAI,UAAU,GAAG,WAAW,GAAG,QAAQ,EAAE,CAAC;oBACxD,SAAS,GAAG,IAAI,CAAC;oBACjB,MAAM;gBACR,CAAC;gBACD,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC/F,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACxB,OAAO,CAAC,IAAI,EAAE,CAAC;gBACf,QAAQ,EAAE,CAAC;gBACX,mBAAmB,GAAG,GAAG,CAAC,GAAG,CAAC;gBAC9B,UAAU,IAAI,WAAW,CAAC;gBAC1B,SAAS;YACX,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/F,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;gBAC/F,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACxB,OAAO,CAAC,IAAI,EAAE,CAAC;gBACf,QAAQ,EAAE,CAAC;gBACX,mBAAmB,GAAG,GAAG,CAAC,GAAG,CAAC;gBAC9B,UAAU,IAAI,YAAY,GAAG,QAAQ,CAAC;gBACtC,SAAS;YACX,CAAC;YAED,IAAI,QAAQ,GAAG,YAAY,GAAG,QAAQ,CAAC;YACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE;gBAAE,QAAQ,IAAI,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAe,EAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAgB,CAAC,IAAI,CAAC,CAAC;YACpJ,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;gBACxB,wGAAwG;gBACxG,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAC/D,SAAS;YACX,CAAC;YACD,IAAI,QAAQ,GAAG,CAAC,IAAI,UAAU,GAAG,QAAQ,GAAG,QAAQ,EAAE,CAAC;gBACrD,SAAS,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR,CAAC;YAED,IAAI,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC;YAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAChD,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAgB,CAAC,KAAK,CAAC;oBAAE,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YAClH,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;YAC5F,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE;gBAAE,eAAe,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAe,EAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAClK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACxB,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,QAAQ,EAAE,CAAC;YACX,mBAAmB,GAAG,GAAG,CAAC,GAAG,CAAC;YAC9B,UAAU,IAAI,QAAQ,CAAC;QACzB,CAAC;QAED,KAAK,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC3C,MAAM,UAAU,CAAC,IAAI,CAAC,6DAA6D,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;YAC5G,MAAM,UAAU,CAAC,IAAI,CACnB,0HAA0H;gBACxH,gEAAgE,EAClE;gBACE,EAAE,EAAE,GAAG;gBACP,GAAG,EAAE,GAAG;gBACR,KAAK,EAAE,gBAAgB,CAAC,cAAc;gBACtC,QAAQ,EAAE,uBAAuB,KAAK,sCAAsC,QAAQ,kBAAkB;gBACtG,GAAG,EAAE,KAAK,EAAE;aACb,CACF,CAAC;QACJ,CAAC;QAED,IAAI,QAAQ,KAAK,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACvC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QAC/I,CAAC;QAED,MAAM,MAAM,GAAiB,EAAE,CAAC;QAChC,MAAM,UAAU,GAAuB,EAAE,CAAC;QAC1C,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3B,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO;YAC/B,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;YACnF,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxG,CAAC,CAAC,CAAC;QAEH,IAAI,OAAe,CAAC;QACpB,IAAI,WAAmB,CAAC;QACxB,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;YAChC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,MAAM,CAAC,MAAA,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,mCAAI,GAAG,CAAC,CAAC;YACnE,WAAW,GAAG,mBAAmB,CAAC;YAClC,MAAM,OAAO,CAAC,UAAU,EAAE,eAAe,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;YAChE,MAAM,OAAO,CAAC,UAAU,EAAE,mBAAmB,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAChE,MAAM,OAAO,CAAC,UAAU,EAAE,kBAAkB,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QACrE,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,UAAU,CAAC,IAAI,CAAC,6DAA6D,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAC7H,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;IACzI,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,2GAA2G;AAC3G,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAqB,EAAE,UAAkB,EAAE,IAA8B;IAChH,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC1C,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,KAAK,gBAAgB,CAAC,UAAU,EAAE,CAAC;QACpF,MAAM,IAAI,KAAK,CAAC,IAAI,UAAU,sFAAsF,CAAC,CAAC;IACxH,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,6CAA6C,CAAC,CAAC,CAAC,gCAAgC;QACtG,MAAM,UAAU,CAAC,IAAI,CAAC,wFAAwF,CAAC,CAAC;QAChH,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,MAAM,UAAU,CAAC,IAAI,CAAC,gFAAgF,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7H,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,UAAU,CAAC,IAAI,CAAC,uGAAuG,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;YAC9J,CAAC;QACH,CAAC;QACD,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,gCAAgC,CAAC,CAAC,CAAC;QACtF,MAAM,UAAU,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,MAAM,eAAe,KAAK,CAAC,SAAS,yCAAyC,CAAC,CAAC;QAC1H,MAAM,UAAU,CAAC,IAAI,CAAC,gGAAgG,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAC3I,MAAM,UAAU,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QACrE,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,8BAA8B;AAC9B,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAqB,EAAE,KAAkB,EAAE,MAAsB,EAAE,MAAoB;IAC3H,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,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,oHAAoH;QACpH,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;QAC5E,mHAAmH;QACnH,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,kBAAkB,EAAE,KAAK,CAAC,WAAW,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QACtH,OAAO,CAAC,MAAM,CAAC,iBAAiB,GAAG,SAAS,CAAC;QAE7C,MAAM,SAAS,GACb,sCAAsC;YACtC,2DAA2D;YAC3D,qEAAqE;YACrE,6EAA6E;YAC7E,4CAA4C;YAC5C,uDAAuD,CAAC;QAC1D,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACrD,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC9C,MAAM,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAY,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAW,EAAE,CAAC,CAAC;gBACnL,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GACb,mFAAmF;YACnF,0HAA0H;YAC1H,iFAAiF,CAAC;QACpF,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;QACpB,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACtD,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE;gBAC7B,EAAE,EAAE,KAAK,CAAC,GAAG;gBACb,GAAG,EAAE,SAAS,CAAC,GAAG;gBAClB,EAAE,EAAE,KAAK,CAAC,WAAW;gBACrB,KAAK,EAAE,SAAS,CAAC,IAAI;gBACrB,QAAQ,EAAE,MAAA,SAAS,CAAC,OAAO,mCAAI,IAAI;gBACnC,MAAM,EAAE,MAAA,SAAS,CAAC,QAAQ,mCAAI,IAAI;gBAClC,WAAW,EAAE,MAAA,SAAS,CAAC,UAAU,mCAAI,IAAI;gBACzC,OAAO,EAAE,MAAA,SAAS,CAAC,MAAM,mCAAI,IAAI;gBACjC,QAAQ,EAAE,MAAA,SAAS,CAAC,OAAO,mCAAI,IAAI;gBACnC,IAAI,EAAE,GAAG;aACV,CAAC,CAAC;YACH,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QACnD,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,eAAe;YAAE,MAAM,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC1F,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAE9C,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC;QACxB,MAAM,UAAU,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;QACpG,OAAO,OAAO,CAAC,MAAM,CAAC;IACxB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAcD;;;GAGG;AACH,KAAK,UAAU,iBAAiB,CAAC,KAAqB,EAAE,KAAkB,EAAE,MAAsB;IAChG,MAAM,IAAI,GAAmB,EAAE,CAAC;IAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,iBAAiB,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnF,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3E,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrD,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,GAAG,GAAG,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,gGAAgG,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChL,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,KAAK,KAAK,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,WAAW;YAAE,SAAS;QAClE,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,KAAK,gBAAgB,CAAC,eAAe,CAAC;QACzE,IAAI,KAAK,KAAK,CAAC,IAAI,aAAa;YAAE,SAAS,CAAC,0CAA0C;QAEtF,IAAI,QAAQ,GAAG,IAAI,CAAC;QACpB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,KAAK,KAAK,CAAC,IAAI,QAAQ,CAAC,gBAAgB,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,UAAU,EAAE,IAAI,QAAQ,CAAC,IAAI,KAAK,gBAAgB,CAAC,eAAe,IAAI,KAAK,EAAE,CAAC;YACxJ,8EAA8E;YAC9E,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC;YACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;gBAChD,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;oBAAE,SAAS;gBACrD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAC;gBACtD,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;oBAAE,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YAC7F,CAAC;YACD,IAAI,QAAQ,CAAC,MAAM,EAAE;gBAAE,SAAS;QAClC,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/L,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gIAAgI;AAChI,SAAS,SAAS,CAAC,CAAU,EAAE,CAAU;IACvC,IAAI,CAAC,YAAY,UAAU,IAAI,CAAC,YAAY,UAAU;QAAE,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtH,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5F,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,8HAA8H;AAC9H,KAAK,UAAU,iBAAiB,CAAC,KAAqB,EAAE,OAA+C,EAAE,IAAoB;;IAC3H,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACtC,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/D,IAAI,WAAW,GAAkB,IAAI,CAAC;QACtC,IAAI,YAAY,GAAkB,IAAI,CAAC;QACvC,IAAI,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC7B,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACrB,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,UAAU,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,MAAM,UAAU,KAAK,CAAC,SAAS,OAAO,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACnI,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzC,qEAAqE;YACrE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAC/B,MAAM,MAAM,GAAmC,EAAE,CAAC;YAClD,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAe,CAAC,CAAC,CAAC;YACnF,MAAM,UAAU,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACjJ,CAAC;aAAM,CAAC;YACN,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;YAChJ,+HAA+H;YAC/H,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1F,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACrD,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBACpC,SAAS;YACX,CAAC;YACD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;gBAAE,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9G,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAChC,MAAM,MAAM,GAAkC,EAAE,CAAC;gBACjD,KAAK,MAAM,MAAM,IAAI,OAAO;oBAAE,MAAM,CAAC,MAAM,CAAC,GAAG,MAAA,GAAG,CAAC,MAAM,CAAC,mCAAI,IAAI,CAAC;gBACnE,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBACvC,MAAM,MAAM,GAAmC,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;gBAC3D,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAe,CAAC,CAAC,CAAC;gBACvF,MAAM,UAAU,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,MAAM,QAAQ,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,SAAS,OAAO,EAAE,MAAM,CAAC,CAAC;YACxJ,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;QACvD,MAAM,UAAU,CAAC,IAAI,CACnB,mGAAmG;YACjG,yFAAyF;YACzF,0HAA0H;YAC1H,8GAA8G;YAC9G,gDAAgD,EAClD,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,EAAE,CACtI,CAAC;QACF,MAAM,UAAU,CAAC,IAAI,CACnB,0HAA0H;YACxH,wEAAwE,EAC1E;YACE,EAAE,EAAE,KAAK,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAK,EAAE,IAAI,CAAC,IAAI;YAChB,QAAQ,EACN,IAAI,CAAC,KAAK,KAAK,CAAC;gBACd,CAAC,CAAC,2GAA2G;gBAC7G,CAAC,CAAC,sBAAsB,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,UAAU,iFAAiF;YACtI,OAAO,EAAE,WAAW;YACpB,QAAQ,EAAE,YAAY;YACtB,IAAI,EAAE,KAAK,EAAE;SACd,CACF,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;QAC/B,IAAI,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC;YAAE,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1G,CAAC;AACH,CAAC","sourcesContent":["// Twin of G9SyncData.Client.Sqlite/G9CSqliteSyncStore.Push.cs: dirty rows in local change order become push frames; the\r\n// result clears exactly the rows that were sent (rows edited meanwhile stay dirty), flags rejections and applies canonical\r\n// server rows.\r\nimport { G9ESyncDirection } from '../abstractions/enums.js';\r\nimport { G9SyncErrorCodes } from '../abstractions/errors.js';\r\nimport { G9Long } from '../abstractions/long.js';\r\nimport { utf8Encode } from '../internal/utf8.js';\r\nimport { G9ApplyReport, type G9PushBatch } from '../client/storeTypes.js';\r\nimport type { G9DtNegotiatedTable, G9DtPushResult, G9DtTableColumns } from '../protocol/dtos.js';\r\nimport { G9EPushRowOp, G9ERowBatchOp } from '../protocol/rowBatch/format.js';\r\nimport { G9RowBatchWriter } from '../protocol/rowBatch/writer.js';\r\nimport { applyFrame, beginApply, endApply } from './apply.js';\r\nimport { completeCommands, readPendingCommands } from './commands.js';\r\nimport { asLong, asNumber, asString } from './connection.js';\r\nimport type { G9SqlValue } from './driver.js';\r\nimport { findByServerName, getLocal, keyValue, meta, nowMs, readLocalValues, serializeValues, setMeta, type LocalTable, type StoreInternals } from './internals.js';\r\nimport { quote } from './sql.js';\r\nimport { estimateEncodedBytes, pushSelectExpression, writeLocalValue } from './values.js';\r\n\r\ninterface PushColumn {\r\n name: string;\r\n type: number;\r\n local: number;\r\n}\r\n\r\ninterface PushSection {\r\n table: LocalTable;\r\n negotiated: G9DtNegotiatedTable;\r\n columns: PushColumn[];\r\n writer: G9RowBatchWriter;\r\n select: string;\r\n rows: number;\r\n}\r\n\r\nconst ROW_OVERHEAD = 48;\r\n\r\n/** `BuildPushBatchAsync`: the next batch of pending changes, oldest first, within the row and byte budgets. */\r\nexport async function buildPushBatch(store: StoreInternals, tables: readonly G9DtNegotiatedTable[], maxRows: number, maxBytes: number): Promise<G9PushBatch | null> {\r\n store.ensureInitialized();\r\n const uploadable = new Map<number, { table: LocalTable; negotiated: G9DtNegotiatedTable; columns: PushColumn[] }>();\r\n for (const negotiated of tables) {\r\n if (negotiated.direction === G9ESyncDirection.DownloadOnly) continue;\r\n const local = findByServerName(store, negotiated.name);\r\n if (!local) continue;\r\n const readOnly = new Set<string>();\r\n for (const c of negotiated.readOnlyColumns) readOnly.add(c.toLowerCase());\r\n for (const c of negotiated.computedColumns) readOnly.add(c.toLowerCase());\r\n const columns = negotiated.columns\r\n .filter((c) => !readOnly.has(c.name.toLowerCase()) && local.model.indexOf(c.name) >= 0)\r\n .map((c) => ({ name: c.name, type: c.type, local: local.model.indexOf(c.name) }));\r\n uploadable.set(local.tid, { table: local, negotiated, columns });\r\n }\r\n\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n const inflightBatch = await meta(connection, 'inflight_batch_id');\r\n const inflightMax = await meta(connection, 'inflight_max_seq');\r\n const offsetText = await meta(connection, 'server_offset_ms');\r\n const offsetMs = offsetText !== null && /^[+-]?\\d+$/.test(offsetText) ? Number(offsetText) : 0;\r\n\r\n const tids = Array.from(uploadable.keys());\r\n // `+tid`: the unary plus keeps the tid list from driving the plan. Without it SQLite searches the primary key by\r\n // tid — every row of every uploadable table (486k on a farm device, 16 s on the bridge) — and sorts them, instead\r\n // of walking the partial g9_rows_dirty index, which holds only the pending rows and is already in local_seq order.\r\n const sql =\r\n 'SELECT tid, pk, dirty, dirty_mask, server_ver, local_seq, coalesce(hlc_ms, 0) FROM g9_rows WHERE dirty <> 0 AND rejected = 0 AND +tid IN (' +\r\n tids.join(',') +\r\n ')' +\r\n (inflightMax === null ? '' : ' AND local_seq <= $imax') +\r\n ' ORDER BY local_seq LIMIT $max';\r\n const commands = await readPendingCommands(store, connection, inflightBatch, Math.max(1, maxRows));\r\n const pending =\r\n uploadable.size === 0\r\n ? []\r\n : await connection.query(sql, inflightMax === null ? { $max: Math.max(1, maxRows) } : { $max: Math.max(1, maxRows), $imax: Number(inflightMax) });\r\n const rows = pending.map((r) => ({\r\n tid: asNumber(r[0]),\r\n key: asString(r[1]),\r\n dirty: asNumber(r[2]),\r\n mask: asLong(r[3]),\r\n base: asNumber(r[4]),\r\n seq: asNumber(r[5]),\r\n hlc: asNumber(r[6]),\r\n }));\r\n\r\n if (rows.length === 0 && commands.length === 0) {\r\n if (inflightBatch !== null) {\r\n // Everything the in-flight batch carried has been resolved another way; forget it.\r\n await connection.exec(\"DELETE FROM g9_meta WHERE key IN ('inflight_batch_id', 'inflight_max_seq')\");\r\n }\r\n return null;\r\n }\r\n\r\n // Rows are encoded in local_seq order and the batch ends at the first row past the byte budget, so a batch is always\r\n // a PREFIX of the pending changes. One section (frame) per table.\r\n const sections = new Map<number, PushSection>();\r\n let included = 0;\r\n let includedMaxSequence = 0;\r\n let budgetUsed = 0;\r\n let truncated = false;\r\n const tooLarge: { tid: number; key: string; bytes: number }[] = [];\r\n for (const row of rows) {\r\n const entry = uploadable.get(row.tid);\r\n if (!entry) continue;\r\n let section = sections.get(row.tid);\r\n if (!section) {\r\n const select =\r\n entry.columns.length === 0\r\n ? `SELECT 1 FROM ${entry.table.quoted} WHERE ${entry.table.keyQuoted} = $k`\r\n : `SELECT ${entry.columns.map((c) => pushSelectExpression(quote(c.name), c.type)).join(', ')} FROM ${entry.table.quoted} WHERE ${entry.table.keyQuoted} = $k`;\r\n const writer = new G9RowBatchWriter();\r\n writer.beginSection(\r\n G9ERowBatchOp.Push,\r\n entry.negotiated.tableId,\r\n entry.table.model.keyType,\r\n entry.columns.map((c) => c.type),\r\n );\r\n section = { table: entry.table, negotiated: entry.negotiated, columns: entry.columns, writer, select, rows: 0 };\r\n sections.set(row.tid, section);\r\n }\r\n\r\n const hlc = row.hlc === 0 ? 0 : row.hlc + offsetMs;\r\n const keyBytes = utf8Encode(row.key).length;\r\n if (row.dirty === 2) {\r\n const deleteBytes = ROW_OVERHEAD + keyBytes;\r\n if (included > 0 && budgetUsed + deleteBytes > maxBytes) {\r\n truncated = true;\r\n break;\r\n }\r\n section.writer.beginPushRow(row.key, G9EPushRowOp.Delete, row.seq, row.base, G9Long.ZERO, hlc);\r\n section.writer.endRow();\r\n section.rows++;\r\n included++;\r\n includedMaxSequence = row.seq;\r\n budgetUsed += deleteBytes;\r\n continue;\r\n }\r\n\r\n const found = await connection.query(section.select, { $k: keyValue(section.table, row.key) });\r\n const values = found[0];\r\n if (!values) {\r\n section.writer.beginPushRow(row.key, G9EPushRowOp.Delete, row.seq, row.base, G9Long.ZERO, hlc);\r\n section.writer.endRow();\r\n section.rows++;\r\n included++;\r\n includedMaxSequence = row.seq;\r\n budgetUsed += ROW_OVERHEAD + keyBytes;\r\n continue;\r\n }\r\n\r\n let rowBytes = ROW_OVERHEAD + keyBytes;\r\n for (let j = 0; j < section.columns.length; j++) rowBytes += estimateEncodedBytes(values[j] as G9SqlValue, (section.columns[j] as PushColumn).type);\r\n if (rowBytes > maxBytes) {\r\n // Can never fit, whatever else the batch carries: reject it here so it does not block every later push.\r\n tooLarge.push({ tid: row.tid, key: row.key, bytes: rowBytes });\r\n continue;\r\n }\r\n if (included > 0 && budgetUsed + rowBytes > maxBytes) {\r\n truncated = true;\r\n break;\r\n }\r\n\r\n let pushMask = G9Long.ZERO;\r\n for (let j = 0; j < section.columns.length; j++) {\r\n if (row.mask.testBit((section.columns[j] as PushColumn).local)) pushMask = pushMask.or(G9Long.ONE.shiftLeft(j));\r\n }\r\n section.writer.beginPushRow(row.key, G9EPushRowOp.Upsert, row.seq, row.base, pushMask, hlc);\r\n for (let j = 0; j < section.columns.length; j++) writeLocalValue(section.writer, values[j] as G9SqlValue, (section.columns[j] as PushColumn).type, store.options);\r\n section.writer.endRow();\r\n section.rows++;\r\n included++;\r\n includedMaxSequence = row.seq;\r\n budgetUsed += rowBytes;\r\n }\r\n\r\n for (const { tid, key, bytes } of tooLarge) {\r\n await connection.exec('UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk', { $t: tid, $pk: key });\r\n await connection.exec(\r\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +\r\n 'VALUES ($t, $pk, $code, $message, NULL, NULL, NULL, NULL, $at)',\r\n {\r\n $t: tid,\r\n $pk: key,\r\n $code: G9SyncErrorCodes.RejectTooLarge,\r\n $message: `The row needs about ${bytes} bytes; the server accepts at most ${maxBytes} bytes per push.`,\r\n $at: nowMs(),\r\n },\r\n );\r\n }\r\n\r\n if (included === 0 && commands.length === 0) {\r\n if (tooLarge.length === 0) return null;\r\n return { batchId: 0, maxSequence: 0, frames: [], tables: [], rowCount: 0, truncated: false, rejectedLocally: tooLarge.length, commands: [] };\r\n }\r\n\r\n const frames: Uint8Array[] = [];\r\n const usedTables: G9DtTableColumns[] = [];\r\n sections.forEach((section) => {\r\n if (section.rows === 0) return;\r\n section.writer.endSection();\r\n frames.push(section.writer.toFrame({ compressor: store.options.frameCompressor }));\r\n usedTables.push({ tableId: section.negotiated.tableId, columns: section.columns.map((c) => c.name) });\r\n });\r\n\r\n let batchId: number;\r\n let maxSequence: number;\r\n if (inflightBatch !== null) {\r\n batchId = Number(inflightBatch);\r\n maxSequence = Number(inflightMax);\r\n } else {\r\n batchId = Number((await meta(connection, 'next_batch_id')) ?? '1');\r\n maxSequence = includedMaxSequence;\r\n await setMeta(connection, 'next_batch_id', String(batchId + 1));\r\n await setMeta(connection, 'inflight_batch_id', String(batchId));\r\n await setMeta(connection, 'inflight_max_seq', String(maxSequence));\r\n }\r\n for (const item of commands) {\r\n await connection.exec('UPDATE g9_commands SET batch_id = $b WHERE command_id = $id', { $b: batchId, $id: item.commandId });\r\n }\r\n return { batchId, maxSequence, frames, tables: usedTables, rowCount: included, truncated, rejectedLocally: tooLarge.length, commands };\r\n }),\r\n );\r\n}\r\n\r\n/** `ForgetUploadedRowsAsync`: removes uploaded rows of an UPLOAD-ONLY table without uploading a delete. */\r\nexport async function forgetUploadedRows(store: StoreInternals, localTable: string, keys: readonly string[] | null): Promise<number> {\r\n store.ensureInitialized();\r\n const table = getLocal(store, localTable);\r\n if (!table.negotiated || table.negotiated.direction !== G9ESyncDirection.UploadOnly) {\r\n throw new Error(`'${localTable}' is not upload-only: deleting its rows locally is a change the server must receive.`);\r\n }\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n await connection.exec('UPDATE g9_ctx SET applying = 1 WHERE id = 1'); // a library write: not captured\r\n await connection.exec('CREATE TEMP TABLE IF NOT EXISTS g9_forget (pk TEXT PRIMARY KEY); DELETE FROM g9_forget');\r\n if (keys === null) {\r\n await connection.exec('INSERT INTO g9_forget (pk) SELECT pk FROM g9_rows WHERE tid = $t AND dirty = 0', { $t: table.tid });\r\n } else {\r\n for (const key of keys) {\r\n await connection.exec('INSERT OR IGNORE INTO g9_forget (pk) SELECT pk FROM g9_rows WHERE tid = $t AND pk = $pk AND dirty = 0', { $t: table.tid, $pk: key });\r\n }\r\n }\r\n const forgotten = asNumber(await connection.scalar('SELECT count(*) FROM g9_forget'));\r\n await connection.exec(`DELETE FROM ${table.quoted} WHERE CAST(${table.keyQuoted} AS TEXT) IN (SELECT pk FROM g9_forget)`);\r\n await connection.exec('DELETE FROM g9_rows WHERE tid = $t AND pk IN (SELECT pk FROM g9_forget); DELETE FROM g9_forget', { $t: table.tid });\r\n await connection.exec('UPDATE g9_ctx SET applying = 0 WHERE id = 1');\r\n return forgotten;\r\n }),\r\n );\r\n}\r\n\r\n/** `ApplyPushResultAsync`. */\r\nexport async function applyPushResult(store: StoreInternals, batch: G9PushBatch, result: G9DtPushResult, signal?: AbortSignal): Promise<G9ApplyReport> {\r\n store.ensureInitialized();\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n // Before BeginApply: finished commands release their rows, so the canonical rows below replace the optimistic ones.\r\n const completed = await completeCommands(connection, result.commandResults);\r\n // Read BEFORE anything below touches the rows: what the device sent for each conflict it may have lost (LES-0072).\r\n const lost = await readLostConflicts(store, batch, result);\r\n const context = await beginApply(store, connection, { dirtyAfterSequence: batch.maxSequence, backfills: [], signal });\r\n context.report.commandsCompleted = completed;\r\n\r\n const acceptSql =\r\n 'UPDATE g9_rows SET server_ver = $v, ' +\r\n 'dirty = CASE WHEN local_seq <= $m THEN 0 ELSE dirty END, ' +\r\n 'dirty_mask = CASE WHEN local_seq <= $m THEN 0 ELSE dirty_mask END, ' +\r\n 'deleted = CASE WHEN local_seq <= $m AND dirty = 2 THEN 1 ELSE deleted END, ' +\r\n 'rejected = 0 WHERE tid = $t AND pk = $pk; ' +\r\n 'DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk';\r\n for (const accepted of result.accepted) {\r\n const table = store.byServerId.get(accepted.tableId);\r\n if (!table) continue;\r\n for (let i = 0; i < accepted.keys.length; i++) {\r\n await context.queue(acceptSql, { $v: i < accepted.versions.length ? (accepted.versions[i] as number) : 0, $m: batch.maxSequence, $t: table.tid, $pk: accepted.keys[i] as string });\r\n context.report.accepted++;\r\n }\r\n }\r\n\r\n const rejectSql =\r\n 'UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk AND local_seq <= $m; ' +\r\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +\r\n 'VALUES ($t, $pk, $code, $message, $state, $constraint, $column, $details, $now)';\r\n const now = nowMs();\r\n for (const rejection of result.rejections) {\r\n const table = store.byServerId.get(rejection.tableId);\r\n if (!table) continue;\r\n await context.queue(rejectSql, {\r\n $t: table.tid,\r\n $pk: rejection.key,\r\n $m: batch.maxSequence,\r\n $code: rejection.code,\r\n $message: rejection.message ?? null,\r\n $state: rejection.sqlState ?? null,\r\n $constraint: rejection.constraint ?? null,\r\n $column: rejection.column ?? null,\r\n $details: rejection.details ?? null,\r\n $now: now,\r\n });\r\n context.report.rejected++;\r\n }\r\n\r\n context.report.conflicts = result.conflicts.length;\r\n await context.flush();\r\n for (const frame of result.canonicalFrames) await applyFrame(store, context, frame, null);\r\n await context.flush();\r\n await keepLostConflicts(store, context, lost);\r\n\r\n await endApply(context);\r\n await connection.exec(\"DELETE FROM g9_meta WHERE key IN ('inflight_batch_id', 'inflight_max_seq')\");\r\n return context.report;\r\n }),\r\n );\r\n}\r\n\r\n/** A conflict the device lost, with its row as it was sent. */\r\ninterface LostConflict {\r\n table: LocalTable;\r\n key: string;\r\n code: string;\r\n dirty: number;\r\n lostMask: G9Long;\r\n subs: G9Long;\r\n version: G9Long;\r\n values: { [column: string]: G9SqlValue };\r\n}\r\n\r\n/**\r\n * Twin of .NET ReadLostConflicts (LES-0072): the conflicts of this push in which the server kept its own values for something the\r\n * device changed - the whole change, or the overlapping columns of a column merge - each with the device's row as it was sent.\r\n */\r\nasync function readLostConflicts(store: StoreInternals, batch: G9PushBatch, result: G9DtPushResult): Promise<LostConflict[]> {\r\n const lost: LostConflict[] = [];\r\n if (!store.options.keepLostConflicts || result.conflicts.length === 0) return lost;\r\n const connection = store.writer();\r\n const pushOrder = new Map(batch.tables.map((t) => [t.tableId, t.columns]));\r\n for (const conflict of result.conflicts) {\r\n const table = store.byServerId.get(conflict.tableId);\r\n if (!table) continue;\r\n const row = (await connection.query('SELECT dirty, dirty_mask, subs, server_ver, local_seq FROM g9_rows WHERE tid = $t AND pk = $pk', { $t: table.tid, $pk: conflict.key }))[0];\r\n if (!row) continue;\r\n const dirty = asNumber(row[0]);\r\n const mask = asLong(row[1]);\r\n if (dirty === 0 || asNumber(row[4]) > batch.maxSequence) continue;\r\n const serverDeleted = conflict.code === G9SyncErrorCodes.ConflictDeleted;\r\n if (dirty === 2 && serverDeleted) continue; // both sides deleted it: nothing was lost\r\n\r\n let lostMask = mask;\r\n const names = pushOrder.get(conflict.tableId);\r\n if (dirty === 1 && conflict.partiallyApplied && !conflict.serverChangedMask.isNegative() && conflict.code === G9SyncErrorCodes.ConflictColumns && names) {\r\n // The server's bits are in push column order; the device's are model columns.\r\n lostMask = G9Long.ZERO;\r\n for (let j = 0; j < names.length && j < 63; j++) {\r\n if (!conflict.serverChangedMask.testBit(j)) continue;\r\n const local = table.model.indexOf(names[j] as string);\r\n if (local >= 0 && mask.testBit(local)) lostMask = lostMask.or(G9Long.ONE.shiftLeft(local));\r\n }\r\n if (lostMask.isZero()) continue;\r\n }\r\n\r\n lost.push({ table, key: conflict.key, code: conflict.code, dirty, lostMask, subs: asLong(row[2]), version: asLong(row[3]), values: await readLocalValues(connection, table, conflict.key) });\r\n }\r\n return lost;\r\n}\r\n\r\n/** Two SQLite values are the same value (a blob by its bytes); a different type counts as different. Twin of .NET SameValue. */\r\nfunction sameValue(a: unknown, b: unknown): boolean {\r\n if (a instanceof Uint8Array && b instanceof Uint8Array) return a.length === b.length && a.every((x, i) => x === b[i]);\r\n if (typeof a === 'bigint' || typeof b === 'bigint') return typeof a === typeof b && a === b;\r\n return a === b || (a == null && b == null);\r\n}\r\n\r\n/** Twin of .NET KeepLostConflicts: the device's lost values back over the canonical rows, parked as rejections (LES-0072). */\r\nasync function keepLostConflicts(store: StoreInternals, context: Awaited<ReturnType<typeof beginApply>>, lost: LostConflict[]): Promise<void> {\r\n const connection = context.connection;\r\n for (const item of lost) {\r\n const table = item.table;\r\n const key = keyValue(table, item.key);\r\n const now = await readLocalValues(connection, table, item.key);\r\n let columnNames: string | null = null;\r\n let serverValues: string | null = null;\r\n let lostMask = item.lostMask;\r\n if (item.dirty === 2) {\r\n if (Object.keys(now).length > 0) await connection.exec(`DELETE FROM ${table.quoted} WHERE ${table.keyQuoted} = $k`, { $k: key });\r\n } else if (Object.keys(now).length === 0) {\r\n // The server deleted it: the device's row comes back as it was sent.\r\n const all = Object.keys(item.values);\r\n if (all.length === 0) continue;\r\n const params: { [name: string]: G9SqlValue } = {};\r\n all.forEach((column, i) => (params['$v' + i] = item.values[column] as G9SqlValue));\r\n await connection.exec(`INSERT INTO ${table.quoted} (${all.map(quote).join(', ')}) VALUES (${all.map((_, i) => '$v' + i).join(', ')})`, params);\r\n } else {\r\n const lostColumns = table.model.columns.map((c, i) => ({ name: c.name, i })).filter((c) => item.lostMask.testBit(c.i) && c.name in item.values);\r\n // LES-0085: a column the server already holds with the device's own value lost nothing; when none differs, the row is in sync.\r\n const differing = lostColumns.filter((c) => !sameValue(item.values[c.name], now[c.name]));\r\n if (lostColumns.length > 0 && differing.length === 0) {\r\n context.report.conflictsSameValue++;\r\n continue;\r\n }\r\n if (differing.length > 0) lostMask = differing.reduce((m, c) => m.or(G9Long.ONE.shiftLeft(c.i)), G9Long.ZERO);\r\n const columns = differing.map((c) => c.name);\r\n if (columns.length > 0) {\r\n columnNames = columns.join(',');\r\n const server: { [column: string]: unknown } = {};\r\n for (const column of columns) server[column] = now[column] ?? null;\r\n serverValues = serializeValues(server);\r\n const params: { [name: string]: G9SqlValue } = { $k: key };\r\n columns.forEach((column, i) => (params['$v' + i] = item.values[column] as G9SqlValue));\r\n await connection.exec(`UPDATE ${table.quoted} SET ${columns.map((c, i) => quote(c) + ' = $v' + i).join(', ')} WHERE ${table.keyQuoted} = $k`, params);\r\n }\r\n }\r\n\r\n const mask = item.dirty === 2 ? G9Long.ZERO : lostMask;\r\n await connection.exec(\r\n 'INSERT INTO g9_rows (tid, pk, server_ver, local_seq, subs, dirty, dirty_mask, rejected, deleted) ' +\r\n 'VALUES ($t, $pk, $v, (SELECT seq FROM g9_clock WHERE id = 1) + 1, $subs, $d, $m, 1, 0) ' +\r\n 'ON CONFLICT(tid, pk) DO UPDATE SET dirty = excluded.dirty, dirty_mask = excluded.dirty_mask, rejected = 1, deleted = 0, ' +\r\n 'subs = CASE WHEN g9_rows.subs = 0 THEN excluded.subs ELSE g9_rows.subs END, local_seq = excluded.local_seq; ' +\r\n 'UPDATE g9_clock SET seq = seq + 1 WHERE id = 1',\r\n { $t: table.tid, $pk: item.key, $v: item.version.toSqlValue(), $subs: item.subs.toSqlValue(), $d: item.dirty, $m: mask.toSqlValue() },\r\n );\r\n await connection.exec(\r\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +\r\n 'VALUES ($t, $pk, $code, $message, NULL, NULL, $column, $details, $now)',\r\n {\r\n $t: table.tid,\r\n $pk: item.key,\r\n $code: item.code,\r\n $message:\r\n item.dirty === 2\r\n ? \"The server changed this row after the device last saw it; the device's delete was kept here, not applied.\"\r\n : `The server changed ${columnNames ?? 'this row'} after the device last saw it; the device's values were kept here, not applied.`,\r\n $column: columnNames,\r\n $details: serverValues,\r\n $now: nowMs(),\r\n },\r\n );\r\n context.report.conflictsKept++;\r\n if (context.wantsChangedKey(table.model.localName)) context.addChanged(table.model.localName, item.key);\r\n }\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@g9tm/syncdata",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.13",
|
|
4
4
|
"description": "TypeScript twin of the G9SyncData .NET client for Lynx, Node and browsers: the sync engine, the G9RowBatch protocol, the SQLite client store (same schema as the .NET store) and the SignalR transport.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Iman Kari (G9TM)",
|