@g9tm/syncdata 1.5.14 → 1.5.16

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/PARITY.md CHANGED
@@ -45,6 +45,7 @@ it). This file maps every .NET piece to its twin and lists every deliberate diff
45
45
  | `ExportPendingChangesAsync(path)` | `exportPendingChanges(writeLine)` | the caller writes the lines where it wants |
46
46
  | `CheckIntegrityAsync` | `checkIntegrity()` | |
47
47
  | `KeepLostConflicts` (LES-0072) | `keepLostConflicts` | same SQL, same rejection row (code, lost columns, server values as JSON); `G9ApplyReport.conflictsKept`; `test/keepLostConflicts.test.ts` |
48
+ | A value its type cannot carry refuses only its row, on the device (LES-0088) | `findUnencodable` | `FindUnencodable`: the writer's conversions run before a row enters the batch; a failure is `G9S-REJ-MALFORMED` with the column, counted in `rejectedLocally`; `MalformedLocalValueTests` / `test/malformedLocalValue.test.ts` |
48
49
  | Lost = pushed columns only; timestamps as instants (LES-0086) | `pushedMask`, `sameValue(…, type)` / `instant` | `PushedMask` / `SameValue(type, …)` / `Instant`: the dirty mask restricted to the batch's columns; DateTime/DateTimeOffset text in either form equal within one microsecond (TS: no BigInt - milliseconds plus 100 ns units); `HeldValueTests` / `test/heldValues.test.ts` |
49
50
  | `SaveHelloAsync(hello, localMidpointUtc, ct)` (LES-0075) | `saveHello(hello, signal, localMidpointMs)` | the engine measures the Hello round trip in both clients and stores `serverTime - midpoint` |
50
51
  | `G9CSqliteException` (`IG9SyncLocalStoreFailure`) | `G9SqliteStoreError` (`localStoreFailure`) | classified from the driver's code |
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.14";
2
+ export declare const VERSION = "1.5.16";
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.14';
6
+ export const VERSION = '1.5.16';
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.14';\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"]}
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.16';\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"]}
@@ -79,6 +79,7 @@ export async function buildPushBatch(store, tables, maxRows, maxBytes) {
79
79
  let budgetUsed = 0;
80
80
  let truncated = false;
81
81
  const tooLarge = [];
82
+ const malformed = [];
82
83
  for (const row of rows) {
83
84
  const entry = uploadable.get(row.tid);
84
85
  if (!entry)
@@ -120,6 +121,13 @@ export async function buildPushBatch(store, tables, maxRows, maxBytes) {
120
121
  budgetUsed += ROW_OVERHEAD + keyBytes;
121
122
  continue;
122
123
  }
124
+ // A value its column type cannot carry would throw while the frame is written and fail EVERY push of the device (LES-0088):
125
+ // that row alone is refused here. Twin of .NET FindUnencodable.
126
+ const bad = findUnencodable(values, section.columns, store.options);
127
+ if (bad) {
128
+ malformed.push({ tid: row.tid, key: row.key, column: bad.column, problem: bad.problem });
129
+ continue;
130
+ }
123
131
  let rowBytes = ROW_OVERHEAD + keyBytes;
124
132
  for (let j = 0; j < section.columns.length; j++)
125
133
  rowBytes += estimateEncodedBytes(values[j], section.columns[j].type);
@@ -157,10 +165,23 @@ export async function buildPushBatch(store, tables, maxRows, maxBytes) {
157
165
  $at: nowMs(),
158
166
  });
159
167
  }
168
+ for (const { tid, key, column, problem } of malformed) {
169
+ await connection.exec('UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk', { $t: tid, $pk: key });
170
+ await connection.exec('INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, sql_state, constraint_name, column_name, details, at_utc) ' +
171
+ 'VALUES ($t, $pk, $code, $message, NULL, NULL, $column, NULL, $at)', {
172
+ $t: tid,
173
+ $pk: key,
174
+ $code: G9SyncErrorCodes.RejectMalformed,
175
+ $message: `The device holds a value in ${column} that its type cannot carry: ${problem}`,
176
+ $column: column,
177
+ $at: nowMs(),
178
+ });
179
+ }
180
+ const refusedHere = tooLarge.length + malformed.length;
160
181
  if (included === 0 && commands.length === 0) {
161
- if (tooLarge.length === 0)
182
+ if (refusedHere === 0)
162
183
  return null;
163
- return { batchId: 0, maxSequence: 0, frames: [], tables: [], rowCount: 0, truncated: false, rejectedLocally: tooLarge.length, commands: [] };
184
+ return { batchId: 0, maxSequence: 0, frames: [], tables: [], rowCount: 0, truncated: false, rejectedLocally: refusedHere, commands: [] };
164
185
  }
165
186
  const frames = [];
166
187
  const usedTables = [];
@@ -187,7 +208,7 @@ export async function buildPushBatch(store, tables, maxRows, maxBytes) {
187
208
  for (const item of commands) {
188
209
  await connection.exec('UPDATE g9_commands SET batch_id = $b WHERE command_id = $id', { $b: batchId, $id: item.commandId });
189
210
  }
190
- return { batchId, maxSequence, frames, tables: usedTables, rowCount: included, truncated, rejectedLocally: tooLarge.length, commands };
211
+ return { batchId, maxSequence, frames, tables: usedTables, rowCount: included, truncated, rejectedLocally: refusedHere, commands };
191
212
  }));
192
213
  }
193
214
  /** `ForgetUploadedRowsAsync`: removes uploaded rows of an UPLOAD-ONLY table without uploading a delete. */
@@ -323,6 +344,27 @@ async function readLostConflicts(store, batch, result) {
323
344
  }
324
345
  return lost;
325
346
  }
347
+ /** The first column whose stored value cannot be written as its type, and why; null when every value encodes. Twin of .NET FindUnencodable. */
348
+ function findUnencodable(values, columns, options) {
349
+ // A throwaway row of the same shape: the real writer is left untouched by a value that fails half-way.
350
+ const scratch = new G9RowBatchWriter();
351
+ scratch.beginSection(G9ERowBatchOp.Push, 0, G9ESyncColumnType.Guid, columns.map((c) => c.type));
352
+ scratch.beginPushRow('00000000-0000-0000-0000-000000000000', G9EPushRowOp.Upsert, 0, 0, G9Long.ZERO, 0);
353
+ for (let j = 0; j < columns.length; j++) {
354
+ const value = values[j];
355
+ const column = columns[j];
356
+ // A null is written too: the writer checks each value against the column at its own cursor, and a skipped column
357
+ // would hold the next value up against the wrong type.
358
+ try {
359
+ writeLocalValue(scratch, value, column.type, options);
360
+ }
361
+ catch {
362
+ const text = value instanceof ArrayBuffer ? '<blob>' : String(value);
363
+ return { column: column.name, problem: `'${text.length > 40 ? text.slice(0, 40) + '…' : text}' is not a valid value of type ${column.type}` };
364
+ }
365
+ }
366
+ return null;
367
+ }
326
368
  /** The device's column bits of the columns a push sent for this table (push column order → model order). Twin of .NET PushedMask. */
327
369
  function pushedMask(table, pushed) {
328
370
  let mask = G9Long.ZERO;
@@ -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 { G9ESyncColumnType, 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 // Only what this push CARRIED can have been lost (LES-0086): a column the device changed but never sends - read-only on the\r\n // server, or not negotiated - was not part of the conflict, and the canonical row simply replaces it.\r\n const names = pushOrder.get(conflict.tableId);\r\n let lostMask = names ? mask.and(pushedMask(table, names)) : mask;\r\n if (dirty === 1 && lostMask.isZero()) continue;\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/** The device's column bits of the columns a push sent for this table (push column order → model order). Twin of .NET PushedMask. */\r\nfunction pushedMask(table: { model: { indexOf(name: string): number } }, pushed: readonly string[]): G9Long {\r\n let mask = G9Long.ZERO;\r\n for (const name of pushed) {\r\n const local = table.model.indexOf(name);\r\n if (local >= 0 && local < 63) mask = mask.or(G9Long.ONE.shiftLeft(local));\r\n }\r\n return mask;\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, type?: G9ESyncColumnType): 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 if (a === b || (a == null && b == null)) return true;\r\n // A timestamp is the same INSTANT to the microsecond (LES-0086): the device keeps 100 ns, the server microseconds.\r\n if ((type === G9ESyncColumnType.DateTime || type === G9ESyncColumnType.DateTimeOffset) && typeof a === 'string' && typeof b === 'string') {\r\n const first = instant(a);\r\n const second = instant(b);\r\n if (first && second) return Math.abs((first[0] - second[0]) * 10000 + (first[1] - second[1])) < 10;\r\n }\r\n return false;\r\n}\r\n\r\nconst instantPattern = /^(\\d{4})-(\\d{2})-(\\d{2})[T ](\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,7}))?(Z|[+-]\\d{2}:?\\d{2})?$/;\r\n\r\n/** A timestamp text as [Unix milliseconds, 100 ns units within that millisecond]; no BigInt (Lynx). Twin of .NET Instant. */\r\nfunction instant(text: string): [number, number] | null {\r\n const m = instantPattern.exec(text.trim());\r\n if (!m) return null;\r\n const fraction = ((m[7] ?? '') + '0000000').slice(0, 7);\r\n let ms = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5]), Number(m[6]), Number(fraction.slice(0, 3)));\r\n const zone = m[8];\r\n if (zone && zone !== 'Z') {\r\n const sign = zone[0] === '-' ? -1 : 1;\r\n const digits = zone.slice(1).replace(':', '');\r\n ms -= sign * (Number(digits.slice(0, 2)) * 60 + Number(digits.slice(2, 4))) * 60000;\r\n }\r\n return [ms, Number(fraction.slice(3))];\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], table.model.columns[c.i]?.type));\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"]}
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 { G9ESyncColumnType, 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 const malformed: { tid: number; key: string; column: string; problem: string }[] = [];\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 // A value its column type cannot carry would throw while the frame is written and fail EVERY push of the device (LES-0088):\r\n // that row alone is refused here. Twin of .NET FindUnencodable.\r\n const bad = findUnencodable(values as readonly G9SqlValue[], section.columns, store.options);\r\n if (bad) {\r\n malformed.push({ tid: row.tid, key: row.key, column: bad.column, problem: bad.problem });\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 for (const { tid, key, column, problem } of malformed) {\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, $column, NULL, $at)',\r\n {\r\n $t: tid,\r\n $pk: key,\r\n $code: G9SyncErrorCodes.RejectMalformed,\r\n $message: `The device holds a value in ${column} that its type cannot carry: ${problem}`,\r\n $column: column,\r\n $at: nowMs(),\r\n },\r\n );\r\n }\r\n\r\n const refusedHere = tooLarge.length + malformed.length;\r\n if (included === 0 && commands.length === 0) {\r\n if (refusedHere === 0) return null;\r\n return { batchId: 0, maxSequence: 0, frames: [], tables: [], rowCount: 0, truncated: false, rejectedLocally: refusedHere, 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: refusedHere, 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 // Only what this push CARRIED can have been lost (LES-0086): a column the device changed but never sends - read-only on the\r\n // server, or not negotiated - was not part of the conflict, and the canonical row simply replaces it.\r\n const names = pushOrder.get(conflict.tableId);\r\n let lostMask = names ? mask.and(pushedMask(table, names)) : mask;\r\n if (dirty === 1 && lostMask.isZero()) continue;\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/** The first column whose stored value cannot be written as its type, and why; null when every value encodes. Twin of .NET FindUnencodable. */\r\nfunction findUnencodable(\r\n values: readonly G9SqlValue[],\r\n columns: readonly PushColumn[],\r\n options: Parameters<typeof writeLocalValue>[3],\r\n): { column: string; problem: string } | null {\r\n // A throwaway row of the same shape: the real writer is left untouched by a value that fails half-way.\r\n const scratch = new G9RowBatchWriter();\r\n scratch.beginSection(G9ERowBatchOp.Push, 0, G9ESyncColumnType.Guid, columns.map((c) => c.type));\r\n scratch.beginPushRow('00000000-0000-0000-0000-000000000000', G9EPushRowOp.Upsert, 0, 0, G9Long.ZERO, 0);\r\n for (let j = 0; j < columns.length; j++) {\r\n const value = values[j] as G9SqlValue;\r\n const column = columns[j] as PushColumn;\r\n // A null is written too: the writer checks each value against the column at its own cursor, and a skipped column\r\n // would hold the next value up against the wrong type.\r\n try {\r\n writeLocalValue(scratch, value, column.type, options);\r\n } catch {\r\n const text = value instanceof ArrayBuffer ? '<blob>' : String(value);\r\n return { column: column.name, problem: `'${text.length > 40 ? text.slice(0, 40) + '…' : text}' is not a valid value of type ${column.type}` };\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/** The device's column bits of the columns a push sent for this table (push column order → model order). Twin of .NET PushedMask. */\r\nfunction pushedMask(table: { model: { indexOf(name: string): number } }, pushed: readonly string[]): G9Long {\r\n let mask = G9Long.ZERO;\r\n for (const name of pushed) {\r\n const local = table.model.indexOf(name);\r\n if (local >= 0 && local < 63) mask = mask.or(G9Long.ONE.shiftLeft(local));\r\n }\r\n return mask;\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, type?: G9ESyncColumnType): 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 if (a === b || (a == null && b == null)) return true;\r\n // A timestamp is the same INSTANT to the microsecond (LES-0086): the device keeps 100 ns, the server microseconds.\r\n if ((type === G9ESyncColumnType.DateTime || type === G9ESyncColumnType.DateTimeOffset) && typeof a === 'string' && typeof b === 'string') {\r\n const first = instant(a);\r\n const second = instant(b);\r\n if (first && second) return Math.abs((first[0] - second[0]) * 10000 + (first[1] - second[1])) < 10;\r\n }\r\n return false;\r\n}\r\n\r\nconst instantPattern = /^(\\d{4})-(\\d{2})-(\\d{2})[T ](\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,7}))?(Z|[+-]\\d{2}:?\\d{2})?$/;\r\n\r\n/** A timestamp text as [Unix milliseconds, 100 ns units within that millisecond]; no BigInt (Lynx). Twin of .NET Instant. */\r\nfunction instant(text: string): [number, number] | null {\r\n const m = instantPattern.exec(text.trim());\r\n if (!m) return null;\r\n const fraction = ((m[7] ?? '') + '0000000').slice(0, 7);\r\n let ms = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5]), Number(m[6]), Number(fraction.slice(0, 3)));\r\n const zone = m[8];\r\n if (zone && zone !== 'Z') {\r\n const sign = zone[0] === '-' ? -1 : 1;\r\n const digits = zone.slice(1).replace(':', '');\r\n ms -= sign * (Number(digits.slice(0, 2)) * 60 + Number(digits.slice(2, 4))) * 60000;\r\n }\r\n return [ms, Number(fraction.slice(3))];\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], table.model.columns[c.i]?.type));\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"]}
@@ -142,6 +142,12 @@ export async function disposeReleasedRows(store, connection, releasedSql, releas
142
142
  if (released.length === 0)
143
143
  return 0;
144
144
  const id = (tid, key) => tid + '\u001f' + key;
145
+ // The default policy (no rules) decides a clean row without its values: it is deleted. Such rows - most of a released
146
+ // site - are written by a few set statements per table instead of four per row (P2-03: 38,632 rows took 15.2 s, of
147
+ // which ~9.5 s the per-row writes and 2.7 s reading values nobody looked at). A duplicate key keeps the walk.
148
+ if (store.options.revocation.rules.length === 0 && new Set(released.map((r) => id(r.tid, r.key))).size === released.length) {
149
+ return disposeReleasedRowsBySet(store, connection, released, releasedSql, releasedParams, reason, subscriptionKey, report, onRemoved);
150
+ }
145
151
  const states = new Map();
146
152
  const stateRows = await connection.query('SELECT r.tid, r.pk, r.subs, r.dirty, r.dirty_mask, r.rejected, ' +
147
153
  'EXISTS (SELECT 1 FROM g9_command_rows cr JOIN g9_commands c ON c.command_id = cr.command_id WHERE c.state = 0 AND cr.tid = r.tid AND cr.pk = r.pk) ' +
@@ -198,6 +204,107 @@ export async function disposeReleasedRows(store, connection, releasedSql, releas
198
204
  await connection.exec('UPDATE g9_clock SET seq = seq + $n WHERE id = 1', { $n: advanced });
199
205
  return removed;
200
206
  }
207
+ /**
208
+ * disposeReleasedRows under the default policy: the database state of the walk, with the clean rows written as sets.
209
+ * `temp.g9_dispose` holds every released row of a known table with its place in the walk (`ord`, so its tombstone gets
210
+ * `base + ord` - the value the walk gives it) and its kind: 1 clean with its app row gone (a tombstone only), 2 clean
211
+ * with an app row (removed), 0 any other (held, pending change or command: decided from its values, as before).
212
+ */
213
+ async function disposeReleasedRowsBySet(store, connection, released, releasedSql, releasedParams, reason, subscriptionKey, report, onRemoved) {
214
+ var _a, _b, _c;
215
+ const id = (tid, key) => tid + '\u001f' + key;
216
+ const states = new Map();
217
+ const stateRows = await connection.query('SELECT r.tid, r.pk, r.subs, r.dirty, r.dirty_mask, r.rejected, ' +
218
+ 'EXISTS (SELECT 1 FROM g9_command_rows cr JOIN g9_commands c ON c.command_id = cr.command_id WHERE c.state = 0 AND cr.tid = r.tid AND cr.pk = r.pk) ' +
219
+ `FROM (${releasedSql}) g JOIN g9_rows r ON r.tid = g.tid AND r.pk = g.pk WHERE r.deleted = 0`, releasedParams);
220
+ for (const r of stateRows) {
221
+ states.set(id(asNumber(r[0]), asString(r[1])), {
222
+ held: !asLong(r[2]).isZero(),
223
+ dirty: asNumber(r[3]),
224
+ dirtyMask: r[4],
225
+ rejected: asNumber(r[5]),
226
+ pendingCommand: asNumber(r[6]) !== 0,
227
+ values: {},
228
+ });
229
+ }
230
+ // The walk's order and each row's kind; the clock moves once per released row of a known table.
231
+ const walk = [];
232
+ let advanced = 0;
233
+ for (const row of released) {
234
+ const table = byTid(store, row.tid);
235
+ if (!table)
236
+ continue;
237
+ const state = (_a = states.get(id(row.tid, row.key))) !== null && _a !== void 0 ? _a : emptyState();
238
+ walk.push({ table, tid: row.tid, key: row.key, ord: ++advanced, clean: !state.held && state.dirty === 0 && !state.pendingCommand });
239
+ }
240
+ if (walk.length === 0)
241
+ return 0;
242
+ await connection.exec('DROP TABLE IF EXISTS temp.g9_dispose; ' +
243
+ 'CREATE TEMP TABLE g9_dispose (tid INTEGER NOT NULL, pk TEXT NOT NULL, ord INTEGER NOT NULL, kind INTEGER NOT NULL, PRIMARY KEY (tid, pk)) WITHOUT ROWID');
244
+ const CHUNK = 20000;
245
+ for (let i = 0; i < walk.length; i += CHUNK) {
246
+ const part = walk.slice(i, i + CHUNK).map((w) => [w.tid, w.key, w.ord, w.clean ? 1 : 0]);
247
+ await connection.exec("INSERT INTO temp.g9_dispose (tid, pk, ord, kind) SELECT json_extract(j.value, '$[0]'), json_extract(j.value, '$[1]'), json_extract(j.value, '$[2]'), json_extract(j.value, '$[3]') FROM json_each($rows) AS j", { $rows: JSON.stringify(part) });
248
+ }
249
+ const base = asLong(await connection.scalar('SELECT seq FROM g9_clock WHERE id = 1'));
250
+ const removedKeys = new Set();
251
+ const tables = new Map();
252
+ for (const w of walk)
253
+ tables.set(w.tid, w.table);
254
+ for (const [tid, table] of tables) {
255
+ const integerKey = table.model.keyType === G9ESyncColumnType.Int32 || table.model.keyType === G9ESyncColumnType.Int64;
256
+ const keyOf = integerKey ? 'CAST(d.pk AS INTEGER)' : 'd.pk';
257
+ // A clean row whose app row is there is removed (kind 2); one whose app row is gone gets its tombstone only (kind 1).
258
+ await connection.exec(`UPDATE temp.g9_dispose SET kind = 2 WHERE tid = ${tid} AND kind = 1 AND EXISTS (SELECT 1 FROM ${table.quoted} t WHERE t.${table.keyQuoted} = ${keyOf.replace('d.pk', 'g9_dispose.pk')})`);
259
+ for (const r of await connection.query(`SELECT pk FROM temp.g9_dispose WHERE tid = ${tid} AND kind = 2`))
260
+ removedKeys.add(id(tid, asString(r[0])));
261
+ await connection.exec(`DELETE FROM ${table.quoted} WHERE ${table.keyQuoted} IN (SELECT ${keyOf} FROM temp.g9_dispose AS d WHERE d.tid = ${tid} AND d.kind = 2)`);
262
+ // Every other released row of the table: decided from its values, as the walk decides it.
263
+ const sql = `SELECT d.pk, t.* FROM temp.g9_dispose AS d JOIN ${table.quoted} t ON t.${table.keyQuoted} = ${keyOf} WHERE d.tid = ${tid} AND d.kind = 0`;
264
+ const result = (await connection.runStatements(statementsOf(sql, {})))[0];
265
+ const values = new Map();
266
+ if (result) {
267
+ const columns = result.columns.slice(1);
268
+ for (const row of result.rows) {
269
+ const v = {};
270
+ columns.forEach((name, i) => (v[name] = row[i + 1]));
271
+ values.set(asString(row[0]), v);
272
+ }
273
+ }
274
+ const batch = new G9StatementBatch(connection, store.options.statementsPerBatch);
275
+ for (const w of walk) {
276
+ if (w.tid !== tid || w.clean)
277
+ continue;
278
+ const state = (_b = states.get(id(w.tid, w.key))) !== null && _b !== void 0 ? _b : emptyState();
279
+ state.values = (_c = values.get(w.key)) !== null && _c !== void 0 ? _c : {};
280
+ const plan = planDisposal(store, table, w.key, state, reason, subscriptionKey, report, '$seq', { $seq: base.add(G9Long.fromNumber(w.ord)) });
281
+ if (plan.removed)
282
+ removedKeys.add(id(w.tid, w.key));
283
+ for (const statement of plan.statements)
284
+ if (batch.add(statement.script, statement.params))
285
+ await batch.flush();
286
+ }
287
+ await batch.flush();
288
+ }
289
+ // The clean rows' metadata, as removeLocalRowStatements and the tombstone-only branch of planDisposal write it.
290
+ await connection.exec('UPDATE g9_rows SET deleted = 1, dirty = 0, dirty_mask = 0, rejected = 0, ' +
291
+ 'local_seq = $base + (SELECT d.ord FROM temp.g9_dispose AS d WHERE d.tid = g9_rows.tid AND d.pk = g9_rows.pk) ' +
292
+ 'WHERE (tid, pk) IN (SELECT tid, pk FROM temp.g9_dispose WHERE kind IN (1, 2)); ' +
293
+ 'DELETE FROM g9_rejections WHERE (tid, pk) IN (SELECT tid, pk FROM temp.g9_dispose WHERE kind = 2); ' +
294
+ 'DELETE FROM g9_revoked WHERE (tid, pk) IN (SELECT tid, pk FROM temp.g9_dispose WHERE kind = 2); ' +
295
+ 'DROP TABLE temp.g9_dispose', { $base: base });
296
+ await connection.exec('UPDATE g9_clock SET seq = seq + $n WHERE id = 1', { $n: advanced });
297
+ let removed = 0;
298
+ for (const w of walk) {
299
+ if (!removedKeys.has(id(w.tid, w.key)))
300
+ continue;
301
+ removed++;
302
+ if (w.clean && report)
303
+ report.revokedDeleted++;
304
+ onRemoved === null || onRemoved === void 0 ? void 0 : onRemoved(w.tid, w.key);
305
+ }
306
+ return removed;
307
+ }
201
308
  function removeLocalRowStatements(table, key, seq, seqParams) {
202
309
  return [
203
310
  { script: `DELETE FROM ${table.quoted} WHERE ${table.keyQuoted} = $k`, params: { $k: keyValue(table, key) } },
@@ -1 +1 @@
1
- {"version":3,"file":"revocation.js","sourceRoot":"","sources":["../../src/sqlite/revocation.ts"],"names":[],"mappings":"AAAA,yHAAyH;AACzH,yHAAyH;AACzH,6GAA6G;AAC7G,EAAE;AACF,qHAAqH;AACrH,0EAA0E;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,GAKrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAA0C,MAAM,iBAAiB,CAAC;AAEvJ,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EAAE,eAAe,EAAwC,MAAM,gBAAgB,CAAC;AAC7J,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEjC,MAAM,CAAC,MAAM,sBAAsB,GAAG,iBAAiB,CAAC;AACxD,MAAM,QAAQ,GAAG,6CAA6C,CAAC;AAE/D,MAAM,SAAS;IAIb,YAAY,MAAwC;QAH3C,YAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;QAI3C,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IAED,GAAG,CAAC,MAAc,EAAE,KAAc;QAChC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,MAAM,IAAI,SAAS,CAAC,uBAAuB,CAAC,CAAC;QACrG,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/F,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM,IAAI,UAAU,CAAC,IAAI,MAAM,+BAA+B,CAAC,CAAC;QAC1F,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,KAAmB,CAAC;QAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;CACF;AAeD,mGAAmG;AACnG,SAAS,UAAU;IACjB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACjG,CAAC;AAOD;;;GAGG;AACH,SAAS,YAAY,CACnB,KAAqB,EACrB,KAAiB,EACjB,GAAW,EACX,KAAuB,EACvB,MAA2B,EAC3B,eAA8B,EAC9B,MAA4B,EAC5B,GAAW,EACX,SAAmB;IAEnB,IAAI,KAAK,CAAC,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAC1D,wGAAwG;IACxG,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,cAAc;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAEzF,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,uFAAuF,GAAG,GAAG,GAAG,8BAA8B,CAAC;QAC9I,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,GAAG,SAAS,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC;IACzG,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,GAAG,GAAiB;QACxB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS;QACjC,GAAG;QACH,MAAM;QACN,eAAe;QACf,iBAAiB,EAAE,KAAK,CAAC,KAAK,KAAK,CAAC;QACpC,UAAU,EAAE,KAAK,CAAC,QAAQ,KAAK,CAAC;QAChC,MAAM;KACP,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3E,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;IACpB,MAAM,UAAU,GAAgB,EAAE,CAAC;IAEnC,QAAQ,WAAW,EAAE,CAAC;QACpB,KAAK,wBAAwB,CAAC,WAAW,CAAC;QAC1C,KAAK,wBAAwB,CAAC,MAAM,CAAC,CAAC,CAAC;YACrC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;gBACpD,MAAM,MAAM,GAAa,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;gBACtD,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAe,CAAC,CAAC,CAAC;gBACxE,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,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,EAAE,CAAC,CAAC;YAC9J,CAAC;YACD,UAAU,CAAC,IAAI,CAAC;gBACd,MAAM,EAAE,sHAAsH;gBAC9H,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;aAC5G,CAAC,CAAC;YACH,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;gBACtB,2GAA2G;gBAC3G,UAAU,CAAC,IAAI,CAAC;oBACd,MAAM,EACJ,+DAA+D;wBAC/D,+GAA+G;oBACjH,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,sBAAsB,EAAE,QAAQ,EAAE,mFAAmF,EAAE,IAAI,EAAE,GAAG,EAAE;iBAC7K,CAAC,CAAC;YACL,CAAC;YACD,IAAI,MAAM;gBAAE,MAAM,CAAC,eAAe,EAAE,CAAC;YACrC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QACxC,CAAC;QACD,KAAK,wBAAwB,CAAC,UAAU;YACtC,UAAU,CAAC,IAAI,CAAC;gBACd,MAAM,EAAE,0JAA0J;gBAClK,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE;aAC/K,CAAC,CAAC;YACH,UAAU,CAAC,IAAI,CAAC,GAAG,wBAAwB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;YACzE,IAAI,MAAM;gBAAE,MAAM,CAAC,kBAAkB,EAAE,CAAC;YACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QACvC;YACE,UAAU,CAAC,IAAI,CAAC,GAAG,wBAAwB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;YACzE,IAAI,MAAM;gBAAE,MAAM,CAAC,cAAc,EAAE,CAAC;YACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IACzC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAqB,EACrB,UAA8B,EAC9B,KAAiB,EACjB,GAAW,EACX,MAA2B,EAC3B,eAA8B,EAC9B,MAA4B;IAE5B,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,mGAAmG,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5K,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACxC,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAe,CAAC;QACzC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,KAAK,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IAC7B,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,MAAM,CACtC,0IAA0I,EAC1I,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAC5B,CAAC;QACF,KAAK,CAAC,cAAc,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,KAAK,CAAC,cAAc;YAAE,OAAO,KAAK,CAAC;IACzC,CAAC;IACD,KAAK,CAAC,MAAM,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IACnG,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU;QAAE,MAAM,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IACnG,OAAO,IAAI,CAAC,OAAO,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAAqB,EACrB,UAA8B,EAC9B,WAAmB,EACnB,cAAwB,EACxB,MAA2B,EAC3B,eAA8B,EAC9B,MAA4B,EAC5B,SAA8C;;IAE9C,MAAM,QAAQ,GAAG,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAClI,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,CAAC,GAAW,EAAE,GAAW,EAAU,EAAE,CAAC,GAAG,GAAG,QAAQ,GAAG,GAAG,CAAC;IAEtE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAC;IACnD,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,KAAK,CACtC,iEAAiE;QAC/D,qJAAqJ;QACrJ,SAAS,WAAW,yEAAyE,EAC/F,cAAc,CACf,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;YAC7C,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;YAC5B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrB,SAAS,EAAE,CAAC,CAAC,CAAC,CAAe;YAC7B,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACxB,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YACpC,MAAM,EAAE,EAAE;SACX,CAAC,CAAC;IACL,CAAC;IAED,mGAAmG;IACnG,KAAK,MAAM,GAAG,IAAI,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,iBAAiB,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,iBAAiB,CAAC,KAAK,CAAC;QACtH,MAAM,GAAG,GAAG,0BAA0B,WAAW,YAAY,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,SAAS,MAAM,UAAU,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;QAC9K,MAAM,MAAM,GAAG,CAAC,MAAM,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtF,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACxC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,KAAK,GAAG,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAe,CAAC,CAAC,CAAC;YACtD,MAAM,KAAK,GAAG,MAAA,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,mCAAI,UAAU,EAAE,CAAC;YAChD,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACzB,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAe,CAAC,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACjF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,uCAAuC,CAAC,CAAC,CAAC;IACtF,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QACpC,yGAAyG;QACzG,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,KAAK,GAAG,MAAA,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,mCAAI,UAAU,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC;QACpJ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,EAAE,CAAC;YACV,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAG,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,CAAC;QACD,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;gBAAE,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;IAClH,CAAC;IACD,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;IACpB,IAAI,QAAQ,GAAG,CAAC;QAAE,MAAM,UAAU,CAAC,IAAI,CAAC,iDAAiD,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7G,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAiB,EAAE,GAAW,EAAE,GAAW,EAAE,SAAmB;IAChG,OAAO;QACL,EAAE,MAAM,EAAE,eAAe,KAAK,CAAC,MAAM,UAAU,KAAK,CAAC,SAAS,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE;QAC7G;YACE,MAAM,EACJ,uFAAuF;gBACvF,GAAG;gBACH,gCAAgC;gBAChC,2GAA2G;YAC7G,MAAM,EAAE,EAAE,GAAG,SAAS,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;SAClD;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAqB,EAAE,UAAyB,EAAE,GAAkB;IACvG,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,IAAI,KAAK,GAAG,cAAc,CAAC;IAC3B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,KAAK,IAAI,eAAe,CAAC;QACzB,MAAM,CAAC,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC;IAC9C,CAAC;IACD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,KAAK,IAAI,eAAe,CAAC;QACzB,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC9B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,mEAAmE,GAAG,KAAK,EAAE,MAAM,CAAC,CAAC;QACzH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAChC,MAAM,UAAU,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QACrE,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,KAAK,MAAM,SAAS,IAAI,wBAAwB,CAAC,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC;oBAAE,MAAM,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;gBACrI,MAAM,UAAU,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;gBACxE,SAAS;YACX,CAAC;YACD,MAAM,UAAU,CAAC,IAAI,CACnB,yDAAyD;gBACvD,+HAA+H;gBAC/H,yGAAyG,EAC3G,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,oBAAoB,CAAC,cAAc,EAAE,CAClE,CAAC;QACJ,CAAC;QACD,MAAM,UAAU,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QACrE,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,oHAAoH;AACpH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAA8B,EAAE,KAAiB,EAAE,GAAW;IACjG,KAAK,MAAM,SAAS,IAAI,wBAAwB,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC;QAAE,MAAM,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;AACxI,CAAC;AAED,kGAAkG;AAElG,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAqB;IACxD,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,MAAM,KAAK;SACrB,MAAM,EAAE;SACR,KAAK,CAAC,2JAA2J,CAAC,CAAC;IACtK,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAwB;QAC7C,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5B,iBAAiB,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACvC,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACxB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,0HAA0H;AAC1H,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAqB,EAAE,UAAkB,EAAE,GAAW;IAC7F,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC9B,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,yFAAyF,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5J,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,CAAC;QACrB,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QACrF,MAAM,MAAM,GAAG,CAAC,IAAY,EAAsB,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7H,IAAI,MAAM,EAAE,CAAC;YACX,wGAAwG;YACxG,MAAM,OAAO,GAAa,EAAE,CAAC;YAC7B,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACnC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBAC9B,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACxG,CAAC,CAAC,CAAC;YACH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAmC,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC;gBAC5E,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAe,CAAC,CAAC,CAAC;gBACxE,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;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpC,MAAM,MAAM,GAAmC,EAAE,CAAC;YAClD,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAe,CAAC,CAAC,CAAC;YACxE,MAAM,UAAU,CAAC,IAAI,CAAC,eAAe,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACzJ,CAAC;QACD,MAAM,UAAU,CAAC,IAAI,CAAC,uDAAuD,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QAC5G,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAqB,EAAE,UAAyB,EAAE,GAAkB;IAC3G,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAG,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC;IACzE,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAC9B,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,wFAAwF,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CACrI,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAqB;IACrD,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAC1B,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,4IAA4I,CAAC,CAAC;IACtL,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAA6B;QACvD,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAwB;QAC7C,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5B,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACxB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,mHAAmH;AACnH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAqB;IAC5D,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,MAAM,QAAQ,GAAG,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjH,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnB,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACnB,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5B,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAC5B,CAAC,CAAC,CAAC;QACJ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QACpC,MAAM,UAAU,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QACrE,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,IAAI,GAAG,MAAM,CACjB,MAAM,UAAU,CAAC,MAAM,CAAC,wFAAwF,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CACrJ,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;gBACnB,oGAAoG;gBACpG,MAAM,UAAU,CAAC,IAAI,CACnB,8HAA8H;oBAC5H,sIAAsI,EACxI,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,sBAAsB,EAAE,CACjE,CAAC;gBACF,SAAS;YACX,CAAC;YACD,IAAI,KAAK,CAAC,WAAW,KAAK,wBAAwB,CAAC,MAAM;gBAAE,SAAS;YACpE,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YACnE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACpF,MAAM,GAAG,GAAiB;gBACxB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS;gBACjC,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,MAAM,EAAE,KAAK,CAAC,MAA6B;gBAC3C,eAAe,EAAE,IAAI;gBACrB,iBAAiB,EAAE,KAAK;gBACxB,UAAU,EAAE,KAAK;gBACjB,MAAM;aACP,CAAC;YACF,IAAI,IAAI,IAAI,IAAI,CAAC,WAAW,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;gBAAE,SAAS;YAClG,MAAM,cAAc,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;YACnD,MAAM,UAAU,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;YACxE,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,UAAU,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QACrE,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,CACH,CAAC;AACJ,CAAC","sourcesContent":["// Twin of G9SyncData.Client.Sqlite/G9CSqliteSyncStore.Revocation.cs: revocation dispositions (ADR-0026). Every path that\r\n// releases a row — a move-out in a pull or a reconciliation, an unsubscribe, an expired ephemeral subscription — ends in\r\n// disposeReleasedRow, which asks the revocation policy what to do with a row NO subscription holds any more.\r\n//\r\n// The local sequence such a row gets is the clock's next value, (SELECT seq FROM g9_clock) + 1; the CALLER moves the\r\n// clock afterwards (unconditionally, like the .NET store's `++sequence`).\r\nimport { G9ESyncColumnType } from '../abstractions/enums.js';\r\nimport { G9Long } from '../abstractions/long.js';\r\nimport {\r\n G9ApplyReport,\r\n G9ERevocationDisposition,\r\n G9ERevocationReason,\r\n G9ESubscriptionState,\r\n type G9QuarantinedRow,\r\n type G9RetainedRow,\r\n type G9RevokedRow,\r\n type G9RevokedRowEditor,\r\n} from '../client/storeTypes.js';\r\nimport { asLong, asNumber, asOptionalString, asString, G9StatementBatch, statementsOf, type G9Params, type G9SqliteConnection } from './connection.js';\r\nimport type { G9SqlValue } from './driver.js';\r\nimport { byTid, deserializeValues, getLocal, keyValue, nowMs, readLocalValues, serializeValues, type LocalTable, type StoreInternals } from './internals.js';\r\nimport { quote } from './sql.js';\r\n\r\nexport const REVOKED_REJECTION_CODE = 'G9S-REJ-REVOKED';\r\nconst NEXT_SEQ = '(SELECT seq FROM g9_clock WHERE id = 1) + 1';\r\n\r\nclass RowEditor implements G9RevokedRowEditor {\r\n readonly changed = new Map<string, string>();\r\n private readonly _values: { [column: string]: G9SqlValue };\r\n\r\n constructor(values: { [column: string]: G9SqlValue }) {\r\n this._values = values;\r\n }\r\n\r\n set(column: string, value: unknown): void {\r\n if (typeof column !== 'string' || column.trim() === '') throw new TypeError('A column is required.');\r\n const actual = Object.keys(this._values).find((c) => c.toLowerCase() === column.toLowerCase());\r\n if (actual === undefined) throw new RangeError(`'${column}' is not a column of the row.`);\r\n this._values[actual] = value as G9SqlValue;\r\n this.changed.set(actual.toLowerCase(), actual);\r\n }\r\n}\r\n\r\n/** What disposing a released row reads about it before anything is written. */\r\ninterface ReleasedRowState {\r\n /** Another subscription still holds the row. */\r\n held: boolean;\r\n dirty: number;\r\n dirtyMask: G9SqlValue;\r\n rejected: number;\r\n /** A pending command touches the row (only looked at when the row has no pending change). */\r\n pendingCommand: boolean;\r\n /** The app row; empty when it is gone. */\r\n values: { [column: string]: G9SqlValue };\r\n}\r\n\r\n/** A row with no metadata row: what the per-row path assumes when its first read finds nothing. */\r\nfunction emptyState(): ReleasedRowState {\r\n return { held: false, dirty: 0, dirtyMask: 0, rejected: 0, pendingCommand: false, values: {} };\r\n}\r\n\r\ninterface Statement {\r\n script: string;\r\n params: G9Params;\r\n}\r\n\r\n/**\r\n * The revocation decision for one released row, as the statements that carry it out, in the order the .NET store runs\r\n * them. `seq` is the SQL of the local sequence the row gets when it moves: NEXT_SEQ, or `$seq` with its value in `seqParams`.\r\n */\r\nfunction planDisposal(\r\n store: StoreInternals,\r\n table: LocalTable,\r\n key: string,\r\n state: ReleasedRowState,\r\n reason: G9ERevocationReason,\r\n subscriptionKey: string | null,\r\n report: G9ApplyReport | null,\r\n seq: string,\r\n seqParams: G9Params,\r\n): { removed: boolean; statements: Statement[] } {\r\n if (state.held) return { removed: false, statements: [] };\r\n // A row touched by a pending command is settled by that command's result, never disposed underneath it.\r\n if (state.dirty === 0 && state.pendingCommand) return { removed: false, statements: [] };\r\n\r\n const values = state.values;\r\n if (Object.keys(values).length === 0) {\r\n const script = 'UPDATE g9_rows SET deleted = 1, dirty = 0, dirty_mask = 0, rejected = 0, local_seq = ' + seq + ' WHERE tid = $t AND pk = $pk';\r\n return { removed: false, statements: [{ script, params: { ...seqParams, $t: table.tid, $pk: key } }] };\r\n }\r\n\r\n const editor = new RowEditor(values);\r\n const row: G9RevokedRow = {\r\n localTable: table.model.localName,\r\n key,\r\n reason,\r\n subscriptionKey,\r\n hasPendingChanges: state.dirty !== 0,\r\n isRejected: state.rejected !== 0,\r\n values,\r\n };\r\n const { rule, disposition } = store.options.revocation.decide(row, editor);\r\n const now = nowMs();\r\n const statements: Statement[] = [];\r\n\r\n switch (disposition) {\r\n case G9ERevocationDisposition.MarkRevoked:\r\n case G9ERevocationDisposition.Retain: {\r\n if (editor.changed.size > 0) {\r\n const columns = Array.from(editor.changed.values());\r\n const params: G9Params = { $k: keyValue(table, key) };\r\n columns.forEach((c, i) => (params['$v' + i] = values[c] as G9SqlValue));\r\n statements.push({ script: `UPDATE ${table.quoted} SET ${columns.map((c, i) => quote(c) + ' = $v' + i).join(', ')} WHERE ${table.keyQuoted} = $k`, params });\r\n }\r\n statements.push({\r\n script: 'INSERT OR REPLACE INTO g9_revoked (tid, pk, disposition, reason, rule, at_utc) VALUES ($t, $pk, $d, $r, $rule, $now)',\r\n params: { $t: table.tid, $pk: key, $d: disposition, $r: reason, $rule: rule ? rule.name : null, $now: now },\r\n });\r\n if (state.dirty !== 0) {\r\n // Its pending change can no longer be accepted: park it as rejected instead of uploading it on every push.\r\n statements.push({\r\n script:\r\n 'UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk; ' +\r\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, at_utc) VALUES ($t, $pk, $code, $message, $now)',\r\n params: { $t: table.tid, $pk: key, $code: REVOKED_REJECTION_CODE, $message: 'The row is no longer visible to this device; its pending change was not uploaded.', $now: now },\r\n });\r\n }\r\n if (report) report.revokedRetained++;\r\n return { removed: false, statements };\r\n }\r\n case G9ERevocationDisposition.Quarantine:\r\n statements.push({\r\n script: 'INSERT OR REPLACE INTO g9_quarantine (tid, pk, reason, rule, row_json, had_pending, dirty_mask, at_utc) VALUES ($t, $pk, $r, $rule, $json, $p, $m, $now)',\r\n params: { $t: table.tid, $pk: key, $r: reason, $rule: rule ? rule.name : null, $json: serializeValues(values), $p: state.dirty !== 0 ? 1 : 0, $m: state.dirtyMask, $now: now },\r\n });\r\n statements.push(...removeLocalRowStatements(table, key, seq, seqParams));\r\n if (report) report.revokedQuarantined++;\r\n return { removed: true, statements };\r\n default:\r\n statements.push(...removeLocalRowStatements(table, key, seq, seqParams));\r\n if (report) report.revokedDeleted++;\r\n return { removed: true, statements };\r\n }\r\n}\r\n\r\n/**\r\n * Applies the revocation policy to a released row (`DisposeReleasedRow`). Rows still held by another subscription are\r\n * untouched. Returns true when the row left the app's table (deleted or quarantined). Reads the row first, so it sees\r\n * whatever the caller ran before it; a release of many rows goes through disposeReleasedRows.\r\n */\r\nexport async function disposeReleasedRow(\r\n store: StoreInternals,\r\n connection: G9SqliteConnection,\r\n table: LocalTable,\r\n key: string,\r\n reason: G9ERevocationReason,\r\n subscriptionKey: string | null,\r\n report: G9ApplyReport | null,\r\n): Promise<boolean> {\r\n const state = emptyState();\r\n const first = (await connection.query('SELECT subs, dirty, dirty_mask, rejected FROM g9_rows WHERE tid = $t AND pk = $pk AND deleted = 0', { $t: table.tid, $pk: key }))[0];\r\n if (first) {\r\n state.held = !asLong(first[0]).isZero();\r\n state.dirty = asNumber(first[1]);\r\n state.dirtyMask = first[2] as G9SqlValue;\r\n state.rejected = asNumber(first[3]);\r\n }\r\n if (state.held) return false;\r\n if (state.dirty === 0) {\r\n const commands = await connection.scalar(\r\n 'SELECT count(*) FROM g9_command_rows r JOIN g9_commands c ON c.command_id = r.command_id WHERE c.state = 0 AND r.tid = $t AND r.pk = $pk',\r\n { $t: table.tid, $pk: key },\r\n );\r\n state.pendingCommand = asNumber(commands) > 0;\r\n if (state.pendingCommand) return false;\r\n }\r\n state.values = await readLocalValues(connection, table, key);\r\n const plan = planDisposal(store, table, key, state, reason, subscriptionKey, report, NEXT_SEQ, {});\r\n for (const statement of plan.statements) await connection.exec(statement.script, statement.params);\r\n return plan.removed;\r\n}\r\n\r\n/**\r\n * `DisposeReleasedRow` for every row `releasedSql` selects (`SELECT tid, pk …`, in the order the .NET store walks them),\r\n * then the clock moves once per row of a known table — the database state the row-by-row path leaves, in a handful of\r\n * driver calls instead of ~6 per row (on Lynx every call crosses the bridge: 25,000 rows took 850 s row by row,\r\n * LES-0051). Everything is read before anything is written, which is exact because disposing a row writes nothing\r\n * another released row's decision reads: the store's connections run with foreign keys OFF, and the CALLER must take the\r\n * row-by-row path when the database holds a foreign trigger. Returns how many rows left the app's tables.\r\n */\r\nexport async function disposeReleasedRows(\r\n store: StoreInternals,\r\n connection: G9SqliteConnection,\r\n releasedSql: string,\r\n releasedParams: G9Params,\r\n reason: G9ERevocationReason,\r\n subscriptionKey: string | null,\r\n report: G9ApplyReport | null,\r\n onRemoved?: (tid: number, key: string) => void,\r\n): Promise<number> {\r\n const released = (await connection.query(releasedSql, releasedParams)).map((r) => ({ tid: asNumber(r[0]), key: asString(r[1]) }));\r\n if (released.length === 0) return 0;\r\n const id = (tid: number, key: string): string => tid + '\\u001f' + key;\r\n\r\n const states = new Map<string, ReleasedRowState>();\r\n const stateRows = await connection.query(\r\n 'SELECT r.tid, r.pk, r.subs, r.dirty, r.dirty_mask, r.rejected, ' +\r\n 'EXISTS (SELECT 1 FROM g9_command_rows cr JOIN g9_commands c ON c.command_id = cr.command_id WHERE c.state = 0 AND cr.tid = r.tid AND cr.pk = r.pk) ' +\r\n `FROM (${releasedSql}) g JOIN g9_rows r ON r.tid = g.tid AND r.pk = g.pk WHERE r.deleted = 0`,\r\n releasedParams,\r\n );\r\n for (const r of stateRows) {\r\n states.set(id(asNumber(r[0]), asString(r[1])), {\r\n held: !asLong(r[2]).isZero(),\r\n dirty: asNumber(r[3]),\r\n dirtyMask: r[4] as G9SqlValue,\r\n rejected: asNumber(r[5]),\r\n pendingCommand: asNumber(r[6]) !== 0,\r\n values: {},\r\n });\r\n }\r\n\r\n // The app rows, one query per table: `SELECT *` of exactly the rows the per-row path reads by key.\r\n for (const tid of new Set(released.map((r) => r.tid))) {\r\n const table = byTid(store, tid);\r\n if (!table) continue;\r\n const integerKey = table.model.keyType === G9ESyncColumnType.Int32 || table.model.keyType === G9ESyncColumnType.Int64;\r\n const sql = `SELECT g.pk, t.* FROM (${releasedSql}) g JOIN ${table.quoted} t ON t.${table.keyQuoted} = ${integerKey ? 'CAST(g.pk AS INTEGER)' : 'g.pk'} WHERE g.tid = ${tid}`;\r\n const result = (await connection.runStatements(statementsOf(sql, releasedParams)))[0];\r\n if (!result) continue;\r\n const columns = result.columns.slice(1);\r\n for (const row of result.rows) {\r\n const rowId = id(tid, asString(row[0] as G9SqlValue));\r\n const state = states.get(rowId) ?? emptyState();\r\n states.set(rowId, state);\r\n columns.forEach((name, i) => (state.values[name] = row[i + 1] as G9SqlValue));\r\n }\r\n }\r\n\r\n const batch = new G9StatementBatch(connection, store.options.statementsPerBatch);\r\n const base = asLong(await connection.scalar('SELECT seq FROM g9_clock WHERE id = 1'));\r\n let advanced = 0;\r\n let removed = 0;\r\n for (const row of released) {\r\n const table = byTid(store, row.tid);\r\n // The .NET store advances its sequence for every released row of a known table, whether or not it moved.\r\n if (!table) continue;\r\n const state = states.get(id(row.tid, row.key)) ?? emptyState();\r\n const plan = planDisposal(store, table, row.key, state, reason, subscriptionKey, report, '$seq', { $seq: base.add(G9Long.fromNumber(++advanced)) });\r\n if (plan.removed) {\r\n removed++;\r\n onRemoved?.(row.tid, row.key);\r\n }\r\n for (const statement of plan.statements) if (batch.add(statement.script, statement.params)) await batch.flush();\r\n }\r\n await batch.flush();\r\n if (advanced > 0) await connection.exec('UPDATE g9_clock SET seq = seq + $n WHERE id = 1', { $n: advanced });\r\n return removed;\r\n}\r\n\r\nfunction removeLocalRowStatements(table: LocalTable, key: string, seq: string, seqParams: G9Params): Statement[] {\r\n return [\r\n { script: `DELETE FROM ${table.quoted} WHERE ${table.keyQuoted} = $k`, params: { $k: keyValue(table, key) } },\r\n {\r\n script:\r\n 'UPDATE g9_rows SET deleted = 1, dirty = 0, dirty_mask = 0, rejected = 0, local_seq = ' +\r\n seq +\r\n ' WHERE tid = $t AND pk = $pk; ' +\r\n 'DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk; DELETE FROM g9_revoked WHERE tid = $t AND pk = $pk',\r\n params: { ...seqParams, $t: table.tid, $pk: key },\r\n },\r\n ];\r\n}\r\n\r\n/**\r\n * `RevertRejectedAsync` (LES-0070): gives up the local change of rejected rows and takes the server's copy. A row the server never had\r\n * (`server_ver = 0` and held by no subscription) is removed locally. Any other drops its pending change and gets `server_ver = -1`: the\r\n * digest hashes key + server_ver, not the values, and a server reports version 0 for every row unchanged since its tracking began, so\r\n * \"forget the version\" (0) would leave the hash equal and the local values in place for good. -1 is a version no server issues: the\r\n * reconcile fetches the row and the apply writes the server's values and real version. The subscriptions holding the row become\r\n * NeedsReconcile, so their next sync of any kind does it (offline now is fine). A library write: capture is silent, nothing uploads.\r\n */\r\nexport async function revertRejected(store: StoreInternals, localTable: string | null, key: string | null): Promise<number> {\r\n store.ensureInitialized();\r\n let where = 'rejected = 1';\r\n const params: G9Params = {};\r\n if (localTable !== null) {\r\n where += ' AND tid = $t';\r\n params.$t = getLocal(store, localTable).tid;\r\n }\r\n if (key !== null) {\r\n where += ' AND pk = $pk';\r\n params.$pk = key;\r\n }\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n const rows = await connection.query('SELECT tid, pk, (server_ver = 0 AND subs = 0) FROM g9_rows WHERE ' + where, params);\r\n if (rows.length === 0) return 0;\r\n await connection.exec('UPDATE g9_ctx SET applying = 1 WHERE id = 1');\r\n for (const r of rows) {\r\n const tid = asNumber(r[0]);\r\n const pk = asString(r[1]);\r\n const table = byTid(store, tid);\r\n if (!table) continue;\r\n if (asNumber(r[2]) === 1) {\r\n for (const statement of removeLocalRowStatements(table, pk, NEXT_SEQ, {})) await connection.exec(statement.script, statement.params);\r\n await connection.exec('UPDATE g9_clock SET seq = seq + 1 WHERE id = 1');\r\n continue;\r\n }\r\n await connection.exec(\r\n 'DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk; ' +\r\n 'UPDATE g9_subscriptions SET state = $needs WHERE (((SELECT subs FROM g9_rows WHERE tid = $t AND pk = $pk) >> slot) & 1) = 1; ' +\r\n 'UPDATE g9_rows SET dirty = 0, dirty_mask = 0, rejected = 0, server_ver = -1 WHERE tid = $t AND pk = $pk',\r\n { $t: tid, $pk: pk, $needs: G9ESubscriptionState.NeedsReconcile },\r\n );\r\n }\r\n await connection.exec('UPDATE g9_ctx SET applying = 0 WHERE id = 1');\r\n return rows.length;\r\n }),\r\n );\r\n}\r\n\r\n/** `RemoveLocalRow`: the row leaves the app's table; its metadata becomes a tombstone at the clock's next value. */\r\nexport async function removeLocalRow(connection: G9SqliteConnection, table: LocalTable, key: string): Promise<void> {\r\n for (const statement of removeLocalRowStatements(table, key, NEXT_SEQ, {})) await connection.exec(statement.script, statement.params);\r\n}\r\n\r\n// ================================================================ holding area and retained rows\r\n\r\nexport async function getQuarantined(store: StoreInternals): Promise<G9QuarantinedRow[]> {\r\n store.ensureInitialized();\r\n const rows = await store\r\n .reader()\r\n .query('SELECT t.local_name, q.pk, q.reason, q.rule, q.had_pending, q.row_json, q.at_utc FROM g9_quarantine q JOIN g9_tables t ON t.tid = q.tid ORDER BY q.at_utc');\r\n return rows.map((r) => ({\r\n localTable: asString(r[0]),\r\n key: asString(r[1]),\r\n reason: asNumber(r[2]) as G9ERevocationReason,\r\n rule: asOptionalString(r[3]),\r\n hadPendingChanges: asNumber(r[4]) !== 0,\r\n values: deserializeValues(asString(r[5])),\r\n atUtcMs: asNumber(r[6]),\r\n }));\r\n}\r\n\r\n/** Puts a quarantined row back as a pending local change (capture is ON: the restored values upload on the next push). */\r\nexport async function restoreQuarantined(store: StoreInternals, localTable: string, key: string): Promise<number> {\r\n store.ensureInitialized();\r\n const table = getLocal(store, localTable);\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n const rows = await connection.query('SELECT row_json, dirty_mask, had_pending FROM g9_quarantine WHERE tid = $t AND pk = $pk', { $t: table.tid, $pk: key });\r\n const first = rows[0];\r\n if (!first) return 0;\r\n const values = deserializeValues(asString(first[0]));\r\n const mask = asNumber(first[2]) !== 0 ? asLong(first[1]) : null;\r\n const exists = Object.keys(await readLocalValues(connection, table, key)).length > 0;\r\n const lookup = (name: string): string | undefined => Object.keys(values).find((c) => c.toLowerCase() === name.toLowerCase());\r\n if (exists) {\r\n // Only the columns the user had changed are written back, so newer server values of the others survive.\r\n const columns: string[] = [];\r\n table.model.columns.forEach((c, i) => {\r\n const actual = lookup(c.name);\r\n if (actual !== undefined && (mask === null || mask.isZero() || mask.testBit(i))) columns.push(actual);\r\n });\r\n if (columns.length > 0) {\r\n const params: { [name: string]: G9SqlValue } = { $k: keyValue(table, key) };\r\n columns.forEach((c, i) => (params['$v' + i] = values[c] 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 } else {\r\n const columns = Object.keys(values);\r\n const params: { [name: string]: G9SqlValue } = {};\r\n columns.forEach((c, i) => (params['$v' + i] = values[c] as G9SqlValue));\r\n await connection.exec(`INSERT INTO ${table.quoted} (${columns.map(quote).join(', ')}) VALUES (${columns.map((_, i) => '$v' + i).join(', ')})`, params);\r\n }\r\n await connection.exec('DELETE FROM g9_quarantine WHERE tid = $t AND pk = $pk', { $t: table.tid, $pk: key });\r\n return 1;\r\n }),\r\n );\r\n}\r\n\r\nexport async function discardQuarantined(store: StoreInternals, localTable: string | null, key: string | null): Promise<number> {\r\n store.ensureInitialized();\r\n const tid = localTable === null ? null : getLocal(store, localTable).tid;\r\n return store.writeLock.run(() =>\r\n store.writer().exec('DELETE FROM g9_quarantine WHERE ($t IS NULL OR tid = $t) AND ($pk IS NULL OR pk = $pk)', { $t: tid, $pk: key }),\r\n );\r\n}\r\n\r\nexport async function getRetained(store: StoreInternals): Promise<G9RetainedRow[]> {\r\n store.ensureInitialized();\r\n const rows = await store.reader().query('SELECT t.local_name, r.pk, r.disposition, r.reason, r.rule, r.at_utc FROM g9_revoked r JOIN g9_tables t ON t.tid = r.tid ORDER BY r.at_utc');\r\n return rows.map((r) => ({\r\n localTable: asString(r[0]),\r\n key: asString(r[1]),\r\n disposition: asNumber(r[2]) as G9ERevocationDisposition,\r\n reason: asNumber(r[3]) as G9ERevocationReason,\r\n rule: asOptionalString(r[4]),\r\n atUtcMs: asNumber(r[5]),\r\n }));\r\n}\r\n\r\n/** Re-checks rows retained by a RetainWhile rule; deletes those no longer retained (`ReevaluateRetainedAsync`). */\r\nexport async function reevaluateRetained(store: StoreInternals): Promise<number> {\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 const retained = (await connection.query('SELECT tid, pk, reason, rule, disposition FROM g9_revoked')).map((r) => ({\r\n tid: asNumber(r[0]),\r\n key: asString(r[1]),\r\n reason: asNumber(r[2]),\r\n rule: asOptionalString(r[3]),\r\n disposition: asNumber(r[4]),\r\n }));\r\n if (retained.length === 0) return 0;\r\n await connection.exec('UPDATE g9_ctx SET applying = 1 WHERE id = 1');\r\n let deleted = 0;\r\n for (const entry of retained) {\r\n const table = byTid(store, entry.tid);\r\n if (!table) continue;\r\n const subs = asLong(\r\n await connection.scalar('SELECT coalesce(max(subs), 0) FROM g9_rows WHERE tid = $t AND pk = $pk AND deleted = 0', { $t: entry.tid, $pk: entry.key }),\r\n );\r\n if (!subs.isZero()) {\r\n // Visible again: no longer revoked, and a pending change parked by the revocation may upload again.\r\n await connection.exec(\r\n 'DELETE FROM g9_revoked WHERE tid = $t AND pk = $pk; DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk AND code = $code; ' +\r\n 'UPDATE g9_rows SET rejected = 0 WHERE tid = $t AND pk = $pk AND NOT EXISTS (SELECT 1 FROM g9_rejections WHERE tid = $t AND pk = $pk)',\r\n { $t: entry.tid, $pk: entry.key, $code: REVOKED_REJECTION_CODE },\r\n );\r\n continue;\r\n }\r\n if (entry.disposition !== G9ERevocationDisposition.Retain) continue;\r\n const values = await readLocalValues(connection, table, entry.key);\r\n const rule = entry.rule === null ? null : store.options.revocation.find(entry.rule);\r\n const row: G9RevokedRow = {\r\n localTable: table.model.localName,\r\n key: entry.key,\r\n reason: entry.reason as G9ERevocationReason,\r\n subscriptionKey: null,\r\n hasPendingChanges: false,\r\n isRejected: false,\r\n values,\r\n };\r\n if (rule && rule.retainWhile && Object.keys(values).length > 0 && rule.retainWhile(row)) continue;\r\n await removeLocalRow(connection, table, entry.key);\r\n await connection.exec('UPDATE g9_clock SET seq = seq + 1 WHERE id = 1');\r\n deleted++;\r\n }\r\n await connection.exec('UPDATE g9_ctx SET applying = 0 WHERE id = 1');\r\n return deleted;\r\n }),\r\n );\r\n}\r\n"]}
1
+ {"version":3,"file":"revocation.js","sourceRoot":"","sources":["../../src/sqlite/revocation.ts"],"names":[],"mappings":"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","sourcesContent":["// Twin of G9SyncData.Client.Sqlite/G9CSqliteSyncStore.Revocation.cs: revocation dispositions (ADR-0026). Every path that\r\n// releases a row — a move-out in a pull or a reconciliation, an unsubscribe, an expired ephemeral subscription — ends in\r\n// disposeReleasedRow, which asks the revocation policy what to do with a row NO subscription holds any more.\r\n//\r\n// The local sequence such a row gets is the clock's next value, (SELECT seq FROM g9_clock) + 1; the CALLER moves the\r\n// clock afterwards (unconditionally, like the .NET store's `++sequence`).\r\nimport { G9ESyncColumnType } from '../abstractions/enums.js';\r\nimport { G9Long } from '../abstractions/long.js';\r\nimport {\r\n G9ApplyReport,\r\n G9ERevocationDisposition,\r\n G9ERevocationReason,\r\n G9ESubscriptionState,\r\n type G9QuarantinedRow,\r\n type G9RetainedRow,\r\n type G9RevokedRow,\r\n type G9RevokedRowEditor,\r\n} from '../client/storeTypes.js';\r\nimport { asLong, asNumber, asOptionalString, asString, G9StatementBatch, statementsOf, type G9Params, type G9SqliteConnection } from './connection.js';\r\nimport type { G9SqlValue } from './driver.js';\r\nimport { byTid, deserializeValues, getLocal, keyValue, nowMs, readLocalValues, serializeValues, type LocalTable, type StoreInternals } from './internals.js';\r\nimport { quote } from './sql.js';\r\n\r\nexport const REVOKED_REJECTION_CODE = 'G9S-REJ-REVOKED';\r\nconst NEXT_SEQ = '(SELECT seq FROM g9_clock WHERE id = 1) + 1';\r\n\r\nclass RowEditor implements G9RevokedRowEditor {\r\n readonly changed = new Map<string, string>();\r\n private readonly _values: { [column: string]: G9SqlValue };\r\n\r\n constructor(values: { [column: string]: G9SqlValue }) {\r\n this._values = values;\r\n }\r\n\r\n set(column: string, value: unknown): void {\r\n if (typeof column !== 'string' || column.trim() === '') throw new TypeError('A column is required.');\r\n const actual = Object.keys(this._values).find((c) => c.toLowerCase() === column.toLowerCase());\r\n if (actual === undefined) throw new RangeError(`'${column}' is not a column of the row.`);\r\n this._values[actual] = value as G9SqlValue;\r\n this.changed.set(actual.toLowerCase(), actual);\r\n }\r\n}\r\n\r\n/** What disposing a released row reads about it before anything is written. */\r\ninterface ReleasedRowState {\r\n /** Another subscription still holds the row. */\r\n held: boolean;\r\n dirty: number;\r\n dirtyMask: G9SqlValue;\r\n rejected: number;\r\n /** A pending command touches the row (only looked at when the row has no pending change). */\r\n pendingCommand: boolean;\r\n /** The app row; empty when it is gone. */\r\n values: { [column: string]: G9SqlValue };\r\n}\r\n\r\n/** A row with no metadata row: what the per-row path assumes when its first read finds nothing. */\r\nfunction emptyState(): ReleasedRowState {\r\n return { held: false, dirty: 0, dirtyMask: 0, rejected: 0, pendingCommand: false, values: {} };\r\n}\r\n\r\ninterface Statement {\r\n script: string;\r\n params: G9Params;\r\n}\r\n\r\n/**\r\n * The revocation decision for one released row, as the statements that carry it out, in the order the .NET store runs\r\n * them. `seq` is the SQL of the local sequence the row gets when it moves: NEXT_SEQ, or `$seq` with its value in `seqParams`.\r\n */\r\nfunction planDisposal(\r\n store: StoreInternals,\r\n table: LocalTable,\r\n key: string,\r\n state: ReleasedRowState,\r\n reason: G9ERevocationReason,\r\n subscriptionKey: string | null,\r\n report: G9ApplyReport | null,\r\n seq: string,\r\n seqParams: G9Params,\r\n): { removed: boolean; statements: Statement[] } {\r\n if (state.held) return { removed: false, statements: [] };\r\n // A row touched by a pending command is settled by that command's result, never disposed underneath it.\r\n if (state.dirty === 0 && state.pendingCommand) return { removed: false, statements: [] };\r\n\r\n const values = state.values;\r\n if (Object.keys(values).length === 0) {\r\n const script = 'UPDATE g9_rows SET deleted = 1, dirty = 0, dirty_mask = 0, rejected = 0, local_seq = ' + seq + ' WHERE tid = $t AND pk = $pk';\r\n return { removed: false, statements: [{ script, params: { ...seqParams, $t: table.tid, $pk: key } }] };\r\n }\r\n\r\n const editor = new RowEditor(values);\r\n const row: G9RevokedRow = {\r\n localTable: table.model.localName,\r\n key,\r\n reason,\r\n subscriptionKey,\r\n hasPendingChanges: state.dirty !== 0,\r\n isRejected: state.rejected !== 0,\r\n values,\r\n };\r\n const { rule, disposition } = store.options.revocation.decide(row, editor);\r\n const now = nowMs();\r\n const statements: Statement[] = [];\r\n\r\n switch (disposition) {\r\n case G9ERevocationDisposition.MarkRevoked:\r\n case G9ERevocationDisposition.Retain: {\r\n if (editor.changed.size > 0) {\r\n const columns = Array.from(editor.changed.values());\r\n const params: G9Params = { $k: keyValue(table, key) };\r\n columns.forEach((c, i) => (params['$v' + i] = values[c] as G9SqlValue));\r\n statements.push({ script: `UPDATE ${table.quoted} SET ${columns.map((c, i) => quote(c) + ' = $v' + i).join(', ')} WHERE ${table.keyQuoted} = $k`, params });\r\n }\r\n statements.push({\r\n script: 'INSERT OR REPLACE INTO g9_revoked (tid, pk, disposition, reason, rule, at_utc) VALUES ($t, $pk, $d, $r, $rule, $now)',\r\n params: { $t: table.tid, $pk: key, $d: disposition, $r: reason, $rule: rule ? rule.name : null, $now: now },\r\n });\r\n if (state.dirty !== 0) {\r\n // Its pending change can no longer be accepted: park it as rejected instead of uploading it on every push.\r\n statements.push({\r\n script:\r\n 'UPDATE g9_rows SET rejected = 1 WHERE tid = $t AND pk = $pk; ' +\r\n 'INSERT OR REPLACE INTO g9_rejections (tid, pk, code, message, at_utc) VALUES ($t, $pk, $code, $message, $now)',\r\n params: { $t: table.tid, $pk: key, $code: REVOKED_REJECTION_CODE, $message: 'The row is no longer visible to this device; its pending change was not uploaded.', $now: now },\r\n });\r\n }\r\n if (report) report.revokedRetained++;\r\n return { removed: false, statements };\r\n }\r\n case G9ERevocationDisposition.Quarantine:\r\n statements.push({\r\n script: 'INSERT OR REPLACE INTO g9_quarantine (tid, pk, reason, rule, row_json, had_pending, dirty_mask, at_utc) VALUES ($t, $pk, $r, $rule, $json, $p, $m, $now)',\r\n params: { $t: table.tid, $pk: key, $r: reason, $rule: rule ? rule.name : null, $json: serializeValues(values), $p: state.dirty !== 0 ? 1 : 0, $m: state.dirtyMask, $now: now },\r\n });\r\n statements.push(...removeLocalRowStatements(table, key, seq, seqParams));\r\n if (report) report.revokedQuarantined++;\r\n return { removed: true, statements };\r\n default:\r\n statements.push(...removeLocalRowStatements(table, key, seq, seqParams));\r\n if (report) report.revokedDeleted++;\r\n return { removed: true, statements };\r\n }\r\n}\r\n\r\n/**\r\n * Applies the revocation policy to a released row (`DisposeReleasedRow`). Rows still held by another subscription are\r\n * untouched. Returns true when the row left the app's table (deleted or quarantined). Reads the row first, so it sees\r\n * whatever the caller ran before it; a release of many rows goes through disposeReleasedRows.\r\n */\r\nexport async function disposeReleasedRow(\r\n store: StoreInternals,\r\n connection: G9SqliteConnection,\r\n table: LocalTable,\r\n key: string,\r\n reason: G9ERevocationReason,\r\n subscriptionKey: string | null,\r\n report: G9ApplyReport | null,\r\n): Promise<boolean> {\r\n const state = emptyState();\r\n const first = (await connection.query('SELECT subs, dirty, dirty_mask, rejected FROM g9_rows WHERE tid = $t AND pk = $pk AND deleted = 0', { $t: table.tid, $pk: key }))[0];\r\n if (first) {\r\n state.held = !asLong(first[0]).isZero();\r\n state.dirty = asNumber(first[1]);\r\n state.dirtyMask = first[2] as G9SqlValue;\r\n state.rejected = asNumber(first[3]);\r\n }\r\n if (state.held) return false;\r\n if (state.dirty === 0) {\r\n const commands = await connection.scalar(\r\n 'SELECT count(*) FROM g9_command_rows r JOIN g9_commands c ON c.command_id = r.command_id WHERE c.state = 0 AND r.tid = $t AND r.pk = $pk',\r\n { $t: table.tid, $pk: key },\r\n );\r\n state.pendingCommand = asNumber(commands) > 0;\r\n if (state.pendingCommand) return false;\r\n }\r\n state.values = await readLocalValues(connection, table, key);\r\n const plan = planDisposal(store, table, key, state, reason, subscriptionKey, report, NEXT_SEQ, {});\r\n for (const statement of plan.statements) await connection.exec(statement.script, statement.params);\r\n return plan.removed;\r\n}\r\n\r\n/**\r\n * `DisposeReleasedRow` for every row `releasedSql` selects (`SELECT tid, pk …`, in the order the .NET store walks them),\r\n * then the clock moves once per row of a known table — the database state the row-by-row path leaves, in a handful of\r\n * driver calls instead of ~6 per row (on Lynx every call crosses the bridge: 25,000 rows took 850 s row by row,\r\n * LES-0051). Everything is read before anything is written, which is exact because disposing a row writes nothing\r\n * another released row's decision reads: the store's connections run with foreign keys OFF, and the CALLER must take the\r\n * row-by-row path when the database holds a foreign trigger. Returns how many rows left the app's tables.\r\n */\r\nexport async function disposeReleasedRows(\r\n store: StoreInternals,\r\n connection: G9SqliteConnection,\r\n releasedSql: string,\r\n releasedParams: G9Params,\r\n reason: G9ERevocationReason,\r\n subscriptionKey: string | null,\r\n report: G9ApplyReport | null,\r\n onRemoved?: (tid: number, key: string) => void,\r\n): Promise<number> {\r\n const released = (await connection.query(releasedSql, releasedParams)).map((r) => ({ tid: asNumber(r[0]), key: asString(r[1]) }));\r\n if (released.length === 0) return 0;\r\n const id = (tid: number, key: string): string => tid + '\\u001f' + key;\r\n // The default policy (no rules) decides a clean row without its values: it is deleted. Such rows - most of a released\r\n // site - are written by a few set statements per table instead of four per row (P2-03: 38,632 rows took 15.2 s, of\r\n // which ~9.5 s the per-row writes and 2.7 s reading values nobody looked at). A duplicate key keeps the walk.\r\n if (store.options.revocation.rules.length === 0 && new Set(released.map((r) => id(r.tid, r.key))).size === released.length) {\r\n return disposeReleasedRowsBySet(store, connection, released, releasedSql, releasedParams, reason, subscriptionKey, report, onRemoved);\r\n }\r\n\r\n const states = new Map<string, ReleasedRowState>();\r\n const stateRows = await connection.query(\r\n 'SELECT r.tid, r.pk, r.subs, r.dirty, r.dirty_mask, r.rejected, ' +\r\n 'EXISTS (SELECT 1 FROM g9_command_rows cr JOIN g9_commands c ON c.command_id = cr.command_id WHERE c.state = 0 AND cr.tid = r.tid AND cr.pk = r.pk) ' +\r\n `FROM (${releasedSql}) g JOIN g9_rows r ON r.tid = g.tid AND r.pk = g.pk WHERE r.deleted = 0`,\r\n releasedParams,\r\n );\r\n for (const r of stateRows) {\r\n states.set(id(asNumber(r[0]), asString(r[1])), {\r\n held: !asLong(r[2]).isZero(),\r\n dirty: asNumber(r[3]),\r\n dirtyMask: r[4] as G9SqlValue,\r\n rejected: asNumber(r[5]),\r\n pendingCommand: asNumber(r[6]) !== 0,\r\n values: {},\r\n });\r\n }\r\n\r\n // The app rows, one query per table: `SELECT *` of exactly the rows the per-row path reads by key.\r\n for (const tid of new Set(released.map((r) => r.tid))) {\r\n const table = byTid(store, tid);\r\n if (!table) continue;\r\n const integerKey = table.model.keyType === G9ESyncColumnType.Int32 || table.model.keyType === G9ESyncColumnType.Int64;\r\n const sql = `SELECT g.pk, t.* FROM (${releasedSql}) g JOIN ${table.quoted} t ON t.${table.keyQuoted} = ${integerKey ? 'CAST(g.pk AS INTEGER)' : 'g.pk'} WHERE g.tid = ${tid}`;\r\n const result = (await connection.runStatements(statementsOf(sql, releasedParams)))[0];\r\n if (!result) continue;\r\n const columns = result.columns.slice(1);\r\n for (const row of result.rows) {\r\n const rowId = id(tid, asString(row[0] as G9SqlValue));\r\n const state = states.get(rowId) ?? emptyState();\r\n states.set(rowId, state);\r\n columns.forEach((name, i) => (state.values[name] = row[i + 1] as G9SqlValue));\r\n }\r\n }\r\n\r\n const batch = new G9StatementBatch(connection, store.options.statementsPerBatch);\r\n const base = asLong(await connection.scalar('SELECT seq FROM g9_clock WHERE id = 1'));\r\n let advanced = 0;\r\n let removed = 0;\r\n for (const row of released) {\r\n const table = byTid(store, row.tid);\r\n // The .NET store advances its sequence for every released row of a known table, whether or not it moved.\r\n if (!table) continue;\r\n const state = states.get(id(row.tid, row.key)) ?? emptyState();\r\n const plan = planDisposal(store, table, row.key, state, reason, subscriptionKey, report, '$seq', { $seq: base.add(G9Long.fromNumber(++advanced)) });\r\n if (plan.removed) {\r\n removed++;\r\n onRemoved?.(row.tid, row.key);\r\n }\r\n for (const statement of plan.statements) if (batch.add(statement.script, statement.params)) await batch.flush();\r\n }\r\n await batch.flush();\r\n if (advanced > 0) await connection.exec('UPDATE g9_clock SET seq = seq + $n WHERE id = 1', { $n: advanced });\r\n return removed;\r\n}\r\n\r\n/**\r\n * disposeReleasedRows under the default policy: the database state of the walk, with the clean rows written as sets.\r\n * `temp.g9_dispose` holds every released row of a known table with its place in the walk (`ord`, so its tombstone gets\r\n * `base + ord` - the value the walk gives it) and its kind: 1 clean with its app row gone (a tombstone only), 2 clean\r\n * with an app row (removed), 0 any other (held, pending change or command: decided from its values, as before).\r\n */\r\nasync function disposeReleasedRowsBySet(\r\n store: StoreInternals,\r\n connection: G9SqliteConnection,\r\n released: readonly { tid: number; key: string }[],\r\n releasedSql: string,\r\n releasedParams: G9Params,\r\n reason: G9ERevocationReason,\r\n subscriptionKey: string | null,\r\n report: G9ApplyReport | null,\r\n onRemoved?: (tid: number, key: string) => void,\r\n): Promise<number> {\r\n const id = (tid: number, key: string): string => tid + '\\u001f' + key;\r\n const states = new Map<string, ReleasedRowState>();\r\n const stateRows = await connection.query(\r\n 'SELECT r.tid, r.pk, r.subs, r.dirty, r.dirty_mask, r.rejected, ' +\r\n 'EXISTS (SELECT 1 FROM g9_command_rows cr JOIN g9_commands c ON c.command_id = cr.command_id WHERE c.state = 0 AND cr.tid = r.tid AND cr.pk = r.pk) ' +\r\n `FROM (${releasedSql}) g JOIN g9_rows r ON r.tid = g.tid AND r.pk = g.pk WHERE r.deleted = 0`,\r\n releasedParams,\r\n );\r\n for (const r of stateRows) {\r\n states.set(id(asNumber(r[0]), asString(r[1])), {\r\n held: !asLong(r[2]).isZero(),\r\n dirty: asNumber(r[3]),\r\n dirtyMask: r[4] as G9SqlValue,\r\n rejected: asNumber(r[5]),\r\n pendingCommand: asNumber(r[6]) !== 0,\r\n values: {},\r\n });\r\n }\r\n\r\n // The walk's order and each row's kind; the clock moves once per released row of a known table.\r\n const walk: { table: LocalTable; tid: number; key: string; ord: number; clean: boolean }[] = [];\r\n let advanced = 0;\r\n for (const row of released) {\r\n const table = byTid(store, row.tid);\r\n if (!table) continue;\r\n const state = states.get(id(row.tid, row.key)) ?? emptyState();\r\n walk.push({ table, tid: row.tid, key: row.key, ord: ++advanced, clean: !state.held && state.dirty === 0 && !state.pendingCommand });\r\n }\r\n if (walk.length === 0) return 0;\r\n\r\n await connection.exec(\r\n 'DROP TABLE IF EXISTS temp.g9_dispose; ' +\r\n 'CREATE TEMP TABLE g9_dispose (tid INTEGER NOT NULL, pk TEXT NOT NULL, ord INTEGER NOT NULL, kind INTEGER NOT NULL, PRIMARY KEY (tid, pk)) WITHOUT ROWID',\r\n );\r\n const CHUNK = 20_000;\r\n for (let i = 0; i < walk.length; i += CHUNK) {\r\n const part = walk.slice(i, i + CHUNK).map((w) => [w.tid, w.key, w.ord, w.clean ? 1 : 0]);\r\n await connection.exec(\r\n \"INSERT INTO temp.g9_dispose (tid, pk, ord, kind) SELECT json_extract(j.value, '$[0]'), json_extract(j.value, '$[1]'), json_extract(j.value, '$[2]'), json_extract(j.value, '$[3]') FROM json_each($rows) AS j\",\r\n { $rows: JSON.stringify(part) },\r\n );\r\n }\r\n\r\n const base = asLong(await connection.scalar('SELECT seq FROM g9_clock WHERE id = 1'));\r\n const removedKeys = new Set<string>();\r\n const tables = new Map<number, LocalTable>();\r\n for (const w of walk) tables.set(w.tid, w.table);\r\n for (const [tid, table] of tables) {\r\n const integerKey = table.model.keyType === G9ESyncColumnType.Int32 || table.model.keyType === G9ESyncColumnType.Int64;\r\n const keyOf = integerKey ? 'CAST(d.pk AS INTEGER)' : 'd.pk';\r\n // A clean row whose app row is there is removed (kind 2); one whose app row is gone gets its tombstone only (kind 1).\r\n await connection.exec(\r\n `UPDATE temp.g9_dispose SET kind = 2 WHERE tid = ${tid} AND kind = 1 AND EXISTS (SELECT 1 FROM ${table.quoted} t WHERE t.${table.keyQuoted} = ${keyOf.replace('d.pk', 'g9_dispose.pk')})`,\r\n );\r\n for (const r of await connection.query(`SELECT pk FROM temp.g9_dispose WHERE tid = ${tid} AND kind = 2`)) removedKeys.add(id(tid, asString(r[0])));\r\n await connection.exec(`DELETE FROM ${table.quoted} WHERE ${table.keyQuoted} IN (SELECT ${keyOf} FROM temp.g9_dispose AS d WHERE d.tid = ${tid} AND d.kind = 2)`);\r\n\r\n // Every other released row of the table: decided from its values, as the walk decides it.\r\n const sql = `SELECT d.pk, t.* FROM temp.g9_dispose AS d JOIN ${table.quoted} t ON t.${table.keyQuoted} = ${keyOf} WHERE d.tid = ${tid} AND d.kind = 0`;\r\n const result = (await connection.runStatements(statementsOf(sql, {})))[0];\r\n const values = new Map<string, { [column: string]: G9SqlValue }>();\r\n if (result) {\r\n const columns = result.columns.slice(1);\r\n for (const row of result.rows) {\r\n const v: { [column: string]: G9SqlValue } = {};\r\n columns.forEach((name, i) => (v[name] = row[i + 1] as G9SqlValue));\r\n values.set(asString(row[0] as G9SqlValue), v);\r\n }\r\n }\r\n const batch = new G9StatementBatch(connection, store.options.statementsPerBatch);\r\n for (const w of walk) {\r\n if (w.tid !== tid || w.clean) continue;\r\n const state = states.get(id(w.tid, w.key)) ?? emptyState();\r\n state.values = values.get(w.key) ?? {};\r\n const plan = planDisposal(store, table, w.key, state, reason, subscriptionKey, report, '$seq', { $seq: base.add(G9Long.fromNumber(w.ord)) });\r\n if (plan.removed) removedKeys.add(id(w.tid, w.key));\r\n for (const statement of plan.statements) if (batch.add(statement.script, statement.params)) await batch.flush();\r\n }\r\n await batch.flush();\r\n }\r\n\r\n // The clean rows' metadata, as removeLocalRowStatements and the tombstone-only branch of planDisposal write it.\r\n await connection.exec(\r\n 'UPDATE g9_rows SET deleted = 1, dirty = 0, dirty_mask = 0, rejected = 0, ' +\r\n 'local_seq = $base + (SELECT d.ord FROM temp.g9_dispose AS d WHERE d.tid = g9_rows.tid AND d.pk = g9_rows.pk) ' +\r\n 'WHERE (tid, pk) IN (SELECT tid, pk FROM temp.g9_dispose WHERE kind IN (1, 2)); ' +\r\n 'DELETE FROM g9_rejections WHERE (tid, pk) IN (SELECT tid, pk FROM temp.g9_dispose WHERE kind = 2); ' +\r\n 'DELETE FROM g9_revoked WHERE (tid, pk) IN (SELECT tid, pk FROM temp.g9_dispose WHERE kind = 2); ' +\r\n 'DROP TABLE temp.g9_dispose',\r\n { $base: base },\r\n );\r\n await connection.exec('UPDATE g9_clock SET seq = seq + $n WHERE id = 1', { $n: advanced });\r\n\r\n let removed = 0;\r\n for (const w of walk) {\r\n if (!removedKeys.has(id(w.tid, w.key))) continue;\r\n removed++;\r\n if (w.clean && report) report.revokedDeleted++;\r\n onRemoved?.(w.tid, w.key);\r\n }\r\n return removed;\r\n}\r\n\r\nfunction removeLocalRowStatements(table: LocalTable, key: string, seq: string, seqParams: G9Params): Statement[] {\r\n return [\r\n { script: `DELETE FROM ${table.quoted} WHERE ${table.keyQuoted} = $k`, params: { $k: keyValue(table, key) } },\r\n {\r\n script:\r\n 'UPDATE g9_rows SET deleted = 1, dirty = 0, dirty_mask = 0, rejected = 0, local_seq = ' +\r\n seq +\r\n ' WHERE tid = $t AND pk = $pk; ' +\r\n 'DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk; DELETE FROM g9_revoked WHERE tid = $t AND pk = $pk',\r\n params: { ...seqParams, $t: table.tid, $pk: key },\r\n },\r\n ];\r\n}\r\n\r\n/**\r\n * `RevertRejectedAsync` (LES-0070): gives up the local change of rejected rows and takes the server's copy. A row the server never had\r\n * (`server_ver = 0` and held by no subscription) is removed locally. Any other drops its pending change and gets `server_ver = -1`: the\r\n * digest hashes key + server_ver, not the values, and a server reports version 0 for every row unchanged since its tracking began, so\r\n * \"forget the version\" (0) would leave the hash equal and the local values in place for good. -1 is a version no server issues: the\r\n * reconcile fetches the row and the apply writes the server's values and real version. The subscriptions holding the row become\r\n * NeedsReconcile, so their next sync of any kind does it (offline now is fine). A library write: capture is silent, nothing uploads.\r\n */\r\nexport async function revertRejected(store: StoreInternals, localTable: string | null, key: string | null): Promise<number> {\r\n store.ensureInitialized();\r\n let where = 'rejected = 1';\r\n const params: G9Params = {};\r\n if (localTable !== null) {\r\n where += ' AND tid = $t';\r\n params.$t = getLocal(store, localTable).tid;\r\n }\r\n if (key !== null) {\r\n where += ' AND pk = $pk';\r\n params.$pk = key;\r\n }\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n const rows = await connection.query('SELECT tid, pk, (server_ver = 0 AND subs = 0) FROM g9_rows WHERE ' + where, params);\r\n if (rows.length === 0) return 0;\r\n await connection.exec('UPDATE g9_ctx SET applying = 1 WHERE id = 1');\r\n for (const r of rows) {\r\n const tid = asNumber(r[0]);\r\n const pk = asString(r[1]);\r\n const table = byTid(store, tid);\r\n if (!table) continue;\r\n if (asNumber(r[2]) === 1) {\r\n for (const statement of removeLocalRowStatements(table, pk, NEXT_SEQ, {})) await connection.exec(statement.script, statement.params);\r\n await connection.exec('UPDATE g9_clock SET seq = seq + 1 WHERE id = 1');\r\n continue;\r\n }\r\n await connection.exec(\r\n 'DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk; ' +\r\n 'UPDATE g9_subscriptions SET state = $needs WHERE (((SELECT subs FROM g9_rows WHERE tid = $t AND pk = $pk) >> slot) & 1) = 1; ' +\r\n 'UPDATE g9_rows SET dirty = 0, dirty_mask = 0, rejected = 0, server_ver = -1 WHERE tid = $t AND pk = $pk',\r\n { $t: tid, $pk: pk, $needs: G9ESubscriptionState.NeedsReconcile },\r\n );\r\n }\r\n await connection.exec('UPDATE g9_ctx SET applying = 0 WHERE id = 1');\r\n return rows.length;\r\n }),\r\n );\r\n}\r\n\r\n/** `RemoveLocalRow`: the row leaves the app's table; its metadata becomes a tombstone at the clock's next value. */\r\nexport async function removeLocalRow(connection: G9SqliteConnection, table: LocalTable, key: string): Promise<void> {\r\n for (const statement of removeLocalRowStatements(table, key, NEXT_SEQ, {})) await connection.exec(statement.script, statement.params);\r\n}\r\n\r\n// ================================================================ holding area and retained rows\r\n\r\nexport async function getQuarantined(store: StoreInternals): Promise<G9QuarantinedRow[]> {\r\n store.ensureInitialized();\r\n const rows = await store\r\n .reader()\r\n .query('SELECT t.local_name, q.pk, q.reason, q.rule, q.had_pending, q.row_json, q.at_utc FROM g9_quarantine q JOIN g9_tables t ON t.tid = q.tid ORDER BY q.at_utc');\r\n return rows.map((r) => ({\r\n localTable: asString(r[0]),\r\n key: asString(r[1]),\r\n reason: asNumber(r[2]) as G9ERevocationReason,\r\n rule: asOptionalString(r[3]),\r\n hadPendingChanges: asNumber(r[4]) !== 0,\r\n values: deserializeValues(asString(r[5])),\r\n atUtcMs: asNumber(r[6]),\r\n }));\r\n}\r\n\r\n/** Puts a quarantined row back as a pending local change (capture is ON: the restored values upload on the next push). */\r\nexport async function restoreQuarantined(store: StoreInternals, localTable: string, key: string): Promise<number> {\r\n store.ensureInitialized();\r\n const table = getLocal(store, localTable);\r\n return store.writeLock.run(() =>\r\n store.writer().transaction('immediate', async () => {\r\n const connection = store.writer();\r\n const rows = await connection.query('SELECT row_json, dirty_mask, had_pending FROM g9_quarantine WHERE tid = $t AND pk = $pk', { $t: table.tid, $pk: key });\r\n const first = rows[0];\r\n if (!first) return 0;\r\n const values = deserializeValues(asString(first[0]));\r\n const mask = asNumber(first[2]) !== 0 ? asLong(first[1]) : null;\r\n const exists = Object.keys(await readLocalValues(connection, table, key)).length > 0;\r\n const lookup = (name: string): string | undefined => Object.keys(values).find((c) => c.toLowerCase() === name.toLowerCase());\r\n if (exists) {\r\n // Only the columns the user had changed are written back, so newer server values of the others survive.\r\n const columns: string[] = [];\r\n table.model.columns.forEach((c, i) => {\r\n const actual = lookup(c.name);\r\n if (actual !== undefined && (mask === null || mask.isZero() || mask.testBit(i))) columns.push(actual);\r\n });\r\n if (columns.length > 0) {\r\n const params: { [name: string]: G9SqlValue } = { $k: keyValue(table, key) };\r\n columns.forEach((c, i) => (params['$v' + i] = values[c] 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 } else {\r\n const columns = Object.keys(values);\r\n const params: { [name: string]: G9SqlValue } = {};\r\n columns.forEach((c, i) => (params['$v' + i] = values[c] as G9SqlValue));\r\n await connection.exec(`INSERT INTO ${table.quoted} (${columns.map(quote).join(', ')}) VALUES (${columns.map((_, i) => '$v' + i).join(', ')})`, params);\r\n }\r\n await connection.exec('DELETE FROM g9_quarantine WHERE tid = $t AND pk = $pk', { $t: table.tid, $pk: key });\r\n return 1;\r\n }),\r\n );\r\n}\r\n\r\nexport async function discardQuarantined(store: StoreInternals, localTable: string | null, key: string | null): Promise<number> {\r\n store.ensureInitialized();\r\n const tid = localTable === null ? null : getLocal(store, localTable).tid;\r\n return store.writeLock.run(() =>\r\n store.writer().exec('DELETE FROM g9_quarantine WHERE ($t IS NULL OR tid = $t) AND ($pk IS NULL OR pk = $pk)', { $t: tid, $pk: key }),\r\n );\r\n}\r\n\r\nexport async function getRetained(store: StoreInternals): Promise<G9RetainedRow[]> {\r\n store.ensureInitialized();\r\n const rows = await store.reader().query('SELECT t.local_name, r.pk, r.disposition, r.reason, r.rule, r.at_utc FROM g9_revoked r JOIN g9_tables t ON t.tid = r.tid ORDER BY r.at_utc');\r\n return rows.map((r) => ({\r\n localTable: asString(r[0]),\r\n key: asString(r[1]),\r\n disposition: asNumber(r[2]) as G9ERevocationDisposition,\r\n reason: asNumber(r[3]) as G9ERevocationReason,\r\n rule: asOptionalString(r[4]),\r\n atUtcMs: asNumber(r[5]),\r\n }));\r\n}\r\n\r\n/** Re-checks rows retained by a RetainWhile rule; deletes those no longer retained (`ReevaluateRetainedAsync`). */\r\nexport async function reevaluateRetained(store: StoreInternals): Promise<number> {\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 const retained = (await connection.query('SELECT tid, pk, reason, rule, disposition FROM g9_revoked')).map((r) => ({\r\n tid: asNumber(r[0]),\r\n key: asString(r[1]),\r\n reason: asNumber(r[2]),\r\n rule: asOptionalString(r[3]),\r\n disposition: asNumber(r[4]),\r\n }));\r\n if (retained.length === 0) return 0;\r\n await connection.exec('UPDATE g9_ctx SET applying = 1 WHERE id = 1');\r\n let deleted = 0;\r\n for (const entry of retained) {\r\n const table = byTid(store, entry.tid);\r\n if (!table) continue;\r\n const subs = asLong(\r\n await connection.scalar('SELECT coalesce(max(subs), 0) FROM g9_rows WHERE tid = $t AND pk = $pk AND deleted = 0', { $t: entry.tid, $pk: entry.key }),\r\n );\r\n if (!subs.isZero()) {\r\n // Visible again: no longer revoked, and a pending change parked by the revocation may upload again.\r\n await connection.exec(\r\n 'DELETE FROM g9_revoked WHERE tid = $t AND pk = $pk; DELETE FROM g9_rejections WHERE tid = $t AND pk = $pk AND code = $code; ' +\r\n 'UPDATE g9_rows SET rejected = 0 WHERE tid = $t AND pk = $pk AND NOT EXISTS (SELECT 1 FROM g9_rejections WHERE tid = $t AND pk = $pk)',\r\n { $t: entry.tid, $pk: entry.key, $code: REVOKED_REJECTION_CODE },\r\n );\r\n continue;\r\n }\r\n if (entry.disposition !== G9ERevocationDisposition.Retain) continue;\r\n const values = await readLocalValues(connection, table, entry.key);\r\n const rule = entry.rule === null ? null : store.options.revocation.find(entry.rule);\r\n const row: G9RevokedRow = {\r\n localTable: table.model.localName,\r\n key: entry.key,\r\n reason: entry.reason as G9ERevocationReason,\r\n subscriptionKey: null,\r\n hasPendingChanges: false,\r\n isRejected: false,\r\n values,\r\n };\r\n if (rule && rule.retainWhile && Object.keys(values).length > 0 && rule.retainWhile(row)) continue;\r\n await removeLocalRow(connection, table, entry.key);\r\n await connection.exec('UPDATE g9_clock SET seq = seq + 1 WHERE id = 1');\r\n deleted++;\r\n }\r\n await connection.exec('UPDATE g9_ctx SET applying = 0 WHERE id = 1');\r\n return deleted;\r\n }),\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.14",
3
+ "version": "1.5.16",
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)",