@quereus/plugin-sync 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +154 -0
  2. package/dist/src/clock/hlc.d.ts +105 -0
  3. package/dist/src/clock/hlc.d.ts.map +1 -0
  4. package/dist/src/clock/hlc.js +251 -0
  5. package/dist/src/clock/hlc.js.map +1 -0
  6. package/dist/src/clock/index.d.ts +6 -0
  7. package/dist/src/clock/index.d.ts.map +1 -0
  8. package/dist/src/clock/index.js +6 -0
  9. package/dist/src/clock/index.js.map +1 -0
  10. package/dist/src/clock/site.d.ts +58 -0
  11. package/dist/src/clock/site.d.ts.map +1 -0
  12. package/dist/src/clock/site.js +137 -0
  13. package/dist/src/clock/site.js.map +1 -0
  14. package/dist/src/create-sync-module.d.ts +85 -0
  15. package/dist/src/create-sync-module.d.ts.map +1 -0
  16. package/dist/src/create-sync-module.js +54 -0
  17. package/dist/src/create-sync-module.js.map +1 -0
  18. package/dist/src/index.d.ts +31 -0
  19. package/dist/src/index.d.ts.map +1 -0
  20. package/dist/src/index.js +42 -0
  21. package/dist/src/index.js.map +1 -0
  22. package/dist/src/metadata/change-log.d.ts +67 -0
  23. package/dist/src/metadata/change-log.d.ts.map +1 -0
  24. package/dist/src/metadata/change-log.js +107 -0
  25. package/dist/src/metadata/change-log.js.map +1 -0
  26. package/dist/src/metadata/column-version.d.ts +58 -0
  27. package/dist/src/metadata/column-version.d.ts.map +1 -0
  28. package/dist/src/metadata/column-version.js +100 -0
  29. package/dist/src/metadata/column-version.js.map +1 -0
  30. package/dist/src/metadata/index.d.ts +11 -0
  31. package/dist/src/metadata/index.d.ts.map +1 -0
  32. package/dist/src/metadata/index.js +11 -0
  33. package/dist/src/metadata/index.js.map +1 -0
  34. package/dist/src/metadata/keys.d.ts +180 -0
  35. package/dist/src/metadata/keys.d.ts.map +1 -0
  36. package/dist/src/metadata/keys.js +390 -0
  37. package/dist/src/metadata/keys.js.map +1 -0
  38. package/dist/src/metadata/peer-state.d.ts +52 -0
  39. package/dist/src/metadata/peer-state.d.ts.map +1 -0
  40. package/dist/src/metadata/peer-state.js +87 -0
  41. package/dist/src/metadata/peer-state.js.map +1 -0
  42. package/dist/src/metadata/schema-migration.d.ts +60 -0
  43. package/dist/src/metadata/schema-migration.d.ts.map +1 -0
  44. package/dist/src/metadata/schema-migration.js +126 -0
  45. package/dist/src/metadata/schema-migration.js.map +1 -0
  46. package/dist/src/metadata/schema-version.d.ts +163 -0
  47. package/dist/src/metadata/schema-version.d.ts.map +1 -0
  48. package/dist/src/metadata/schema-version.js +307 -0
  49. package/dist/src/metadata/schema-version.js.map +1 -0
  50. package/dist/src/metadata/tombstones.d.ts +67 -0
  51. package/dist/src/metadata/tombstones.d.ts.map +1 -0
  52. package/dist/src/metadata/tombstones.js +125 -0
  53. package/dist/src/metadata/tombstones.js.map +1 -0
  54. package/dist/src/sync/events.d.ts +117 -0
  55. package/dist/src/sync/events.d.ts.map +1 -0
  56. package/dist/src/sync/events.js +56 -0
  57. package/dist/src/sync/events.js.map +1 -0
  58. package/dist/src/sync/index.d.ts +8 -0
  59. package/dist/src/sync/index.d.ts.map +1 -0
  60. package/dist/src/sync/index.js +8 -0
  61. package/dist/src/sync/index.js.map +1 -0
  62. package/dist/src/sync/manager.d.ts +146 -0
  63. package/dist/src/sync/manager.d.ts.map +1 -0
  64. package/dist/src/sync/manager.js +8 -0
  65. package/dist/src/sync/manager.js.map +1 -0
  66. package/dist/src/sync/protocol.d.ts +282 -0
  67. package/dist/src/sync/protocol.d.ts.map +1 -0
  68. package/dist/src/sync/protocol.js +16 -0
  69. package/dist/src/sync/protocol.js.map +1 -0
  70. package/dist/src/sync/store-adapter.d.ts +42 -0
  71. package/dist/src/sync/store-adapter.d.ts.map +1 -0
  72. package/dist/src/sync/store-adapter.js +232 -0
  73. package/dist/src/sync/store-adapter.js.map +1 -0
  74. package/dist/src/sync/sync-manager-impl.d.ts +91 -0
  75. package/dist/src/sync/sync-manager-impl.d.ts.map +1 -0
  76. package/dist/src/sync/sync-manager-impl.js +1123 -0
  77. package/dist/src/sync/sync-manager-impl.js.map +1 -0
  78. package/package.json +64 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Site ID management - unique identifier for each replica.
3
+ *
4
+ * Site IDs are 16-byte UUIDs that uniquely identify a replica in the
5
+ * distributed system. They are used for:
6
+ * - Breaking ties in HLC comparison
7
+ * - Tracking which changes came from which replica
8
+ * - Peer-to-peer sync state tracking
9
+ */
10
+ /**
11
+ * Generate a new random site ID (UUID v4).
12
+ */
13
+ export function generateSiteId() {
14
+ const id = new Uint8Array(16);
15
+ // Use crypto.getRandomValues if available (browser and Node 19+)
16
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
17
+ crypto.getRandomValues(id);
18
+ }
19
+ else {
20
+ // Fallback for older Node.js
21
+ for (let i = 0; i < 16; i++) {
22
+ id[i] = Math.floor(Math.random() * 256);
23
+ }
24
+ }
25
+ // Set version to 4 (random UUID)
26
+ id[6] = (id[6] & 0x0f) | 0x40;
27
+ // Set variant to RFC 4122
28
+ id[8] = (id[8] & 0x3f) | 0x80;
29
+ return id;
30
+ }
31
+ // Base64url alphabet (RFC 4648 Section 5)
32
+ const BASE64URL_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
33
+ /**
34
+ * Convert a Uint8Array to base64url encoding (no padding).
35
+ */
36
+ export function toBase64Url(bytes) {
37
+ let result = '';
38
+ for (let i = 0; i < bytes.length; i += 3) {
39
+ const byte1 = bytes[i];
40
+ const byte2 = i + 1 < bytes.length ? bytes[i + 1] : 0;
41
+ const byte3 = i + 2 < bytes.length ? bytes[i + 2] : 0;
42
+ const triplet = (byte1 << 16) | (byte2 << 8) | byte3;
43
+ result += BASE64URL_CHARS[(triplet >>> 18) & 0x3f];
44
+ result += BASE64URL_CHARS[(triplet >>> 12) & 0x3f];
45
+ if (i + 1 < bytes.length) {
46
+ result += BASE64URL_CHARS[(triplet >>> 6) & 0x3f];
47
+ }
48
+ if (i + 2 < bytes.length) {
49
+ result += BASE64URL_CHARS[triplet & 0x3f];
50
+ }
51
+ }
52
+ return result;
53
+ }
54
+ /**
55
+ * Convert a base64url string to Uint8Array.
56
+ */
57
+ export function fromBase64Url(str) {
58
+ // Build reverse lookup table
59
+ const lookup = {};
60
+ for (let i = 0; i < BASE64URL_CHARS.length; i++) {
61
+ lookup[BASE64URL_CHARS[i]] = i;
62
+ }
63
+ // Calculate output length (no padding in base64url)
64
+ const len = str.length;
65
+ const outputLen = Math.floor((len * 3) / 4);
66
+ const result = new Uint8Array(outputLen);
67
+ let writePos = 0;
68
+ for (let i = 0; i < len; i += 4) {
69
+ const c1 = lookup[str[i]] ?? 0;
70
+ const c2 = lookup[str[i + 1]] ?? 0;
71
+ const c3 = i + 2 < len ? lookup[str[i + 2]] ?? 0 : 0;
72
+ const c4 = i + 3 < len ? lookup[str[i + 3]] ?? 0 : 0;
73
+ const triplet = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4;
74
+ if (writePos < outputLen)
75
+ result[writePos++] = (triplet >>> 16) & 0xff;
76
+ if (writePos < outputLen)
77
+ result[writePos++] = (triplet >>> 8) & 0xff;
78
+ if (writePos < outputLen)
79
+ result[writePos++] = triplet & 0xff;
80
+ }
81
+ return result;
82
+ }
83
+ /**
84
+ * Convert site ID to base64url string for serialization.
85
+ * 16 bytes → 22 characters (no padding).
86
+ */
87
+ export function siteIdToBase64(siteId) {
88
+ return toBase64Url(siteId);
89
+ }
90
+ /**
91
+ * Parse site ID from base64url string.
92
+ */
93
+ export function siteIdFromBase64(base64) {
94
+ if (base64.length !== 22) {
95
+ throw new Error(`Invalid site ID base64 length: ${base64.length}, expected 22`);
96
+ }
97
+ return fromBase64Url(base64);
98
+ }
99
+ /**
100
+ * Compare two site IDs for equality.
101
+ */
102
+ export function siteIdEquals(a, b) {
103
+ if (a.length !== b.length)
104
+ return false;
105
+ for (let i = 0; i < a.length; i++) {
106
+ if (a[i] !== b[i])
107
+ return false;
108
+ }
109
+ return true;
110
+ }
111
+ /**
112
+ * Storage key for site identity.
113
+ */
114
+ export const SITE_ID_KEY = 'si:';
115
+ /**
116
+ * Serialize site identity for storage.
117
+ */
118
+ export function serializeSiteIdentity(identity) {
119
+ const buffer = new Uint8Array(24); // 16 bytes siteId + 8 bytes timestamp
120
+ buffer.set(identity.siteId, 0);
121
+ const view = new DataView(buffer.buffer);
122
+ view.setBigUint64(16, BigInt(identity.createdAt), false);
123
+ return buffer;
124
+ }
125
+ /**
126
+ * Deserialize site identity from storage.
127
+ */
128
+ export function deserializeSiteIdentity(buffer) {
129
+ if (buffer.length !== 24) {
130
+ throw new Error(`Invalid site identity buffer length: ${buffer.length}, expected 24`);
131
+ }
132
+ const siteId = new Uint8Array(buffer.slice(0, 16));
133
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
134
+ const createdAt = Number(view.getBigUint64(16, false));
135
+ return { siteId, createdAt };
136
+ }
137
+ //# sourceMappingURL=site.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site.js","sourceRoot":"","sources":["../../../src/clock/site.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Factory function to create a sync-enabled store module.
3
+ *
4
+ * This wraps an existing store module (LevelDB or IndexedDB) with
5
+ * CRDT sync capabilities.
6
+ */
7
+ import type { KVStore, StoreEventEmitter } from '@quereus/plugin-store';
8
+ import type { TableSchema } from '@quereus/quereus';
9
+ import { SyncEventEmitterImpl } from './sync/events.js';
10
+ import { type SyncConfig, type ApplyToStoreCallback } from './sync/protocol.js';
11
+ import type { SyncManager } from './sync/manager.js';
12
+ /**
13
+ * Function to get table schema by name.
14
+ * Used to map column indices to column names for sync.
15
+ */
16
+ export type GetTableSchemaCallback = (schemaName: string, tableName: string) => TableSchema | undefined;
17
+ /**
18
+ * Result of creating a sync module.
19
+ */
20
+ export interface CreateSyncModuleResult {
21
+ /** The sync manager for sync operations */
22
+ syncManager: SyncManager;
23
+ /** Event emitter for reactive UI integration */
24
+ syncEvents: SyncEventEmitterImpl;
25
+ }
26
+ /**
27
+ * Options for creating a sync module.
28
+ */
29
+ export interface CreateSyncModuleOptions extends Partial<SyncConfig> {
30
+ /**
31
+ * Callback for applying remote changes to the store.
32
+ *
33
+ * When provided, the SyncManager will call this to apply data and schema
34
+ * changes from remote replicas. The store should emit events with
35
+ * `remote: true` when this is called.
36
+ *
37
+ * If not provided, the SyncManager will only update CRDT metadata
38
+ * and emit sync events, but will not modify actual data. The application
39
+ * is responsible for applying changes separately.
40
+ */
41
+ applyToStore?: ApplyToStoreCallback;
42
+ /**
43
+ * Callback for getting table schema by name.
44
+ *
45
+ * When provided, the SyncManager uses this to get actual column names
46
+ * for sync. This is required for proper column-level CRDT tracking.
47
+ *
48
+ * If not provided, column names will be derived from row indices (col_0,
49
+ * col_1, etc.), which may not match across replicas if table schemas differ.
50
+ */
51
+ getTableSchema?: GetTableSchemaCallback;
52
+ }
53
+ /**
54
+ * Create a sync-enabled module.
55
+ *
56
+ * This function:
57
+ * 1. Creates a SyncManager that tracks CRDT metadata
58
+ * 2. Subscribes to store events to record changes
59
+ * 3. Returns the sync manager and event emitter for UI integration
60
+ *
61
+ * @param kv - The KV store to use for metadata storage
62
+ * @param storeEvents - The store's event emitter
63
+ * @param config - Optional sync configuration
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * import { LevelDBStore, StoreEventEmitter } from '@quereus/plugin-store';
68
+ * import { createSyncModule } from '@quereus/plugin-sync';
69
+ *
70
+ * const storeEvents = new StoreEventEmitter();
71
+ * const kv = await LevelDBStore.open({ path: './data' });
72
+ *
73
+ * const { syncManager, syncEvents } = await createSyncModule(kv, storeEvents);
74
+ *
75
+ * // Subscribe to sync events for UI
76
+ * syncEvents.onRemoteChange((event) => {
77
+ * console.log('Remote changes:', event.changes.length);
78
+ * });
79
+ *
80
+ * // Use syncManager for sync operations
81
+ * const changes = await syncManager.getChangesSince(peerSiteId, lastHLC);
82
+ * ```
83
+ */
84
+ export declare function createSyncModule(kv: KVStore, storeEvents: StoreEventEmitter, options?: CreateSyncModuleOptions): Promise<CreateSyncModuleResult>;
85
+ //# sourceMappingURL=create-sync-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-sync-module.d.ts","sourceRoot":"","sources":["../../src/create-sync-module.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAuB,KAAK,UAAU,EAAE,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACrG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,CAAC;AAExG;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,2CAA2C;IAC3C,WAAW,EAAE,WAAW,CAAC;IACzB,gDAAgD;IAChD,UAAU,EAAE,oBAAoB,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,OAAO,CAAC,UAAU,CAAC;IAClE;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,oBAAoB,CAAC;IAEpC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,OAAO,EACX,WAAW,EAAE,iBAAiB,EAC9B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAuBjC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Factory function to create a sync-enabled store module.
3
+ *
4
+ * This wraps an existing store module (LevelDB or IndexedDB) with
5
+ * CRDT sync capabilities.
6
+ */
7
+ import { SyncManagerImpl } from './sync/sync-manager-impl.js';
8
+ import { SyncEventEmitterImpl } from './sync/events.js';
9
+ import { DEFAULT_SYNC_CONFIG } from './sync/protocol.js';
10
+ /**
11
+ * Create a sync-enabled module.
12
+ *
13
+ * This function:
14
+ * 1. Creates a SyncManager that tracks CRDT metadata
15
+ * 2. Subscribes to store events to record changes
16
+ * 3. Returns the sync manager and event emitter for UI integration
17
+ *
18
+ * @param kv - The KV store to use for metadata storage
19
+ * @param storeEvents - The store's event emitter
20
+ * @param config - Optional sync configuration
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { LevelDBStore, StoreEventEmitter } from '@quereus/plugin-store';
25
+ * import { createSyncModule } from '@quereus/plugin-sync';
26
+ *
27
+ * const storeEvents = new StoreEventEmitter();
28
+ * const kv = await LevelDBStore.open({ path: './data' });
29
+ *
30
+ * const { syncManager, syncEvents } = await createSyncModule(kv, storeEvents);
31
+ *
32
+ * // Subscribe to sync events for UI
33
+ * syncEvents.onRemoteChange((event) => {
34
+ * console.log('Remote changes:', event.changes.length);
35
+ * });
36
+ *
37
+ * // Use syncManager for sync operations
38
+ * const changes = await syncManager.getChangesSince(peerSiteId, lastHLC);
39
+ * ```
40
+ */
41
+ export async function createSyncModule(kv, storeEvents, options = {}) {
42
+ const { applyToStore, getTableSchema, ...configOverrides } = options;
43
+ const fullConfig = {
44
+ ...DEFAULT_SYNC_CONFIG,
45
+ ...configOverrides,
46
+ };
47
+ const syncEvents = new SyncEventEmitterImpl();
48
+ const syncManager = await SyncManagerImpl.create(kv, storeEvents, fullConfig, syncEvents, applyToStore, getTableSchema);
49
+ return {
50
+ syncManager,
51
+ syncEvents,
52
+ };
53
+ }
54
+ //# sourceMappingURL=create-sync-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-sync-module.js","sourceRoot":"","sources":["../../src/create-sync-module.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAA8C,MAAM,oBAAoB,CAAC;AAgDrG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,EAAW,EACX,WAA8B,EAC9B,UAAmC,EAAE;IAErC,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,GAAG,eAAe,EAAE,GAAG,OAAO,CAAC;IAErE,MAAM,UAAU,GAAe;QAC7B,GAAG,mBAAmB;QACtB,GAAG,eAAe;KACnB,CAAC;IAEF,MAAM,UAAU,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAE9C,MAAM,WAAW,GAAG,MAAM,eAAe,CAAC,MAAM,CAC9C,EAAE,EACF,WAAW,EACX,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,CACf,CAAC;IAEF,OAAO;QACL,WAAW;QACX,UAAU;KACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Sync Plugin for Quereus
3
+ *
4
+ * Provides multi-master CRDT replication with automatic conflict resolution.
5
+ *
6
+ * Features:
7
+ * - Fully automatic: All tables are CRDT-enabled without opt-in
8
+ * - Column-level LWW: Fine-grained conflict resolution
9
+ * - Transport agnostic: Bring your own WebSocket/HTTP/WebRTC
10
+ * - Reactive hooks: UI integration for real-time updates
11
+ * - Offline-first: Works with local changes that sync later
12
+ *
13
+ * Usage:
14
+ * import { createSyncModule } from '@quereus/plugin-sync';
15
+ * import { LevelDBModule, StoreEventEmitter } from '@quereus/plugin-store';
16
+ *
17
+ * const storeEvents = new StoreEventEmitter();
18
+ * const store = new LevelDBModule(storeEvents);
19
+ * const { syncModule, syncManager, syncEvents } = createSyncModule(store, storeEvents);
20
+ *
21
+ * db.registerVtabModule('store', syncModule);
22
+ */
23
+ export { type HLC, HLCManager, compareHLC, hlcEquals, createHLC, serializeHLC, deserializeHLC, type SerializedHLC, hlcToJson, hlcFromJson, type SiteId, generateSiteId, siteIdToBase64, siteIdFromBase64, toBase64Url, fromBase64Url, siteIdEquals, type SiteIdentity, serializeSiteIdentity, deserializeSiteIdentity, SITE_ID_KEY, } from './clock/index.js';
24
+ export { type ColumnChange, type RowDeletion, type Change, type SchemaMigrationType, type SchemaMigration, type ChangeSet, type ApplyResult, type ColumnVersionEntry, type TableSnapshot, type Snapshot, type PeerSyncState, type SnapshotChunkType, type SnapshotHeaderChunk, type SnapshotTableStartChunk, type SnapshotColumnVersionsChunk, type SnapshotTableEndChunk, type SnapshotSchemaMigrationChunk, type SnapshotFooterChunk, type SnapshotChunk, type SnapshotProgress, type ApplyToStoreOptions, type DataChangeToApply, type SchemaChangeToApply, type ApplyToStoreResult, type ApplyToStoreCallback, type SyncConfig, DEFAULT_SYNC_CONFIG, } from './sync/protocol.js';
25
+ export { type SyncManager, type SnapshotCheckpoint } from './sync/manager.js';
26
+ export { SyncManagerImpl } from './sync/sync-manager-impl.js';
27
+ export { createStoreAdapter, type SyncStoreAdapterOptions } from './sync/store-adapter.js';
28
+ export { createSyncModule, type CreateSyncModuleResult, type CreateSyncModuleOptions, type GetTableSchemaCallback, } from './create-sync-module.js';
29
+ export { type RemoteChangeEvent, type LocalChangeEvent, type ConflictEvent, type SyncState, type Unsubscribe, type SyncEventEmitter, SyncEventEmitterImpl, } from './sync/events.js';
30
+ export { SYNC_KEY_PREFIX, buildColumnVersionKey, buildTombstoneKey, buildTransactionKey, buildPeerStateKey, buildSchemaMigrationKey, buildColumnVersionScanBounds, buildTombstoneScanBounds, buildSchemaMigrationScanBounds, encodePK, decodePK, type ColumnVersion, ColumnVersionStore, serializeColumnVersion, deserializeColumnVersion, type Tombstone, TombstoneStore, serializeTombstone, deserializeTombstone, type PeerState, PeerStateStore, serializePeerState, deserializePeerState, type SchemaVersion, type SchemaVersionType, type SchemaChangeOperation, SchemaVersionStore, buildSchemaVersionKey, buildSchemaVersionScanBounds, buildAllSchemaVersionsScanBounds, serializeSchemaVersion, deserializeSchemaVersion, parseSchemaVersionKey, getDestructiveness, getOperationDestructiveness, shouldApplySchemaChangeByOperation, } from './metadata/index.js';
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EAEL,KAAK,GAAG,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,YAAY,EACZ,cAAc,EAEd,KAAK,aAAa,EAClB,SAAS,EACT,WAAW,EAEX,KAAK,MAAM,EACX,cAAc,EAEd,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,KAAK,YAAY,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,MAAM,EAEX,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAEpB,KAAK,SAAS,EAEd,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,aAAa,EAElB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EAErB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAEzB,KAAK,UAAU,EACf,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAG9D,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAG3F,OAAO,EACL,gBAAgB,EAChB,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,GAC5B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAEL,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,4BAA4B,EAC5B,wBAAwB,EACxB,8BAA8B,EAC9B,QAAQ,EACR,QAAQ,EAER,KAAK,aAAa,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EAExB,KAAK,SAAS,EACd,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EAEpB,KAAK,SAAS,EACd,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EAEpB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,kBAAkB,EAClB,qBAAqB,EACrB,4BAA4B,EAC5B,gCAAgC,EAChC,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB,EAErB,kBAAkB,EAClB,2BAA2B,EAC3B,kCAAkC,GACnC,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Sync Plugin for Quereus
3
+ *
4
+ * Provides multi-master CRDT replication with automatic conflict resolution.
5
+ *
6
+ * Features:
7
+ * - Fully automatic: All tables are CRDT-enabled without opt-in
8
+ * - Column-level LWW: Fine-grained conflict resolution
9
+ * - Transport agnostic: Bring your own WebSocket/HTTP/WebRTC
10
+ * - Reactive hooks: UI integration for real-time updates
11
+ * - Offline-first: Works with local changes that sync later
12
+ *
13
+ * Usage:
14
+ * import { createSyncModule } from '@quereus/plugin-sync';
15
+ * import { LevelDBModule, StoreEventEmitter } from '@quereus/plugin-store';
16
+ *
17
+ * const storeEvents = new StoreEventEmitter();
18
+ * const store = new LevelDBModule(storeEvents);
19
+ * const { syncModule, syncManager, syncEvents } = createSyncModule(store, storeEvents);
20
+ *
21
+ * db.registerVtabModule('store', syncModule);
22
+ */
23
+ // Clock module
24
+ export { HLCManager, compareHLC, hlcEquals, createHLC, serializeHLC, deserializeHLC, hlcToJson, hlcFromJson, generateSiteId,
25
+ // Base64url encoding
26
+ siteIdToBase64, siteIdFromBase64, toBase64Url, fromBase64Url, siteIdEquals, serializeSiteIdentity, deserializeSiteIdentity, SITE_ID_KEY, } from './clock/index.js';
27
+ // Sync protocol types
28
+ export { DEFAULT_SYNC_CONFIG, } from './sync/protocol.js';
29
+ export { SyncManagerImpl } from './sync/sync-manager-impl.js';
30
+ // Store adapter for applying remote changes
31
+ export { createStoreAdapter } from './sync/store-adapter.js';
32
+ // Factory function
33
+ export { createSyncModule, } from './create-sync-module.js';
34
+ // Reactive events
35
+ export { SyncEventEmitterImpl, } from './sync/events.js';
36
+ // Metadata storage
37
+ export {
38
+ // Key builders
39
+ SYNC_KEY_PREFIX, buildColumnVersionKey, buildTombstoneKey, buildTransactionKey, buildPeerStateKey, buildSchemaMigrationKey, buildColumnVersionScanBounds, buildTombstoneScanBounds, buildSchemaMigrationScanBounds, encodePK, decodePK, ColumnVersionStore, serializeColumnVersion, deserializeColumnVersion, TombstoneStore, serializeTombstone, deserializeTombstone, PeerStateStore, serializePeerState, deserializePeerState, SchemaVersionStore, buildSchemaVersionKey, buildSchemaVersionScanBounds, buildAllSchemaVersionsScanBounds, serializeSchemaVersion, deserializeSchemaVersion, parseSchemaVersionKey,
40
+ // Most destructive wins
41
+ getDestructiveness, getOperationDestructiveness, shouldApplySchemaChangeByOperation, } from './metadata/index.js';
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,eAAe;AACf,OAAO,EAGL,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,YAAY,EACZ,cAAc,EAGd,SAAS,EACT,WAAW,EAGX,cAAc;AACd,qBAAqB;AACrB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,YAAY,EAEZ,qBAAqB,EACrB,uBAAuB,EACvB,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAE1B,sBAAsB;AACtB,OAAO,EAkCL,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,4CAA4C;AAC5C,OAAO,EAAE,kBAAkB,EAAgC,MAAM,yBAAyB,CAAC;AAE3F,mBAAmB;AACnB,OAAO,EACL,gBAAgB,GAIjB,MAAM,yBAAyB,CAAC;AAEjC,kBAAkB;AAClB,OAAO,EAOL,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAE1B,mBAAmB;AACnB,OAAO;AACL,eAAe;AACf,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,4BAA4B,EAC5B,wBAAwB,EACxB,8BAA8B,EAC9B,QAAQ,EACR,QAAQ,EAGR,kBAAkB,EAClB,sBAAsB,EACtB,wBAAwB,EAGxB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EAGpB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EAKpB,kBAAkB,EAClB,qBAAqB,EACrB,4BAA4B,EAC5B,gCAAgC,EAChC,sBAAsB,EACtB,wBAAwB,EACxB,qBAAqB;AACrB,wBAAwB;AACxB,kBAAkB,EAClB,2BAA2B,EAC3B,kCAAkC,GACnC,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * HLC-indexed change log for efficient delta sync.
3
+ *
4
+ * The change log stores references to changes indexed by HLC timestamp,
5
+ * enabling efficient getChangesSince() queries without scanning all data.
6
+ */
7
+ import type { KVStore, WriteBatch } from '@quereus/plugin-store';
8
+ import type { SqlValue } from '@quereus/quereus';
9
+ import type { HLC } from '../clock/hlc.js';
10
+ import { type ChangeLogEntryType } from './keys.js';
11
+ /**
12
+ * Change log entry stored in KV.
13
+ */
14
+ export interface ChangeLogEntry {
15
+ readonly hlc: HLC;
16
+ readonly entryType: ChangeLogEntryType;
17
+ readonly schema: string;
18
+ readonly table: string;
19
+ readonly pk: SqlValue[];
20
+ readonly column?: string;
21
+ }
22
+ /**
23
+ * Change log store for HLC-indexed change tracking.
24
+ *
25
+ * Each mutation (column update or row deletion) creates an entry in the
26
+ * change log, keyed by HLC. This allows efficient range scans to find
27
+ * all changes since a given timestamp.
28
+ */
29
+ export declare class ChangeLogStore {
30
+ private readonly kv;
31
+ constructor(kv: KVStore);
32
+ /**
33
+ * Record a column change in the change log.
34
+ */
35
+ recordColumnChange(hlc: HLC, schema: string, table: string, pk: SqlValue[], column: string): Promise<void>;
36
+ /**
37
+ * Record a column change in a batch.
38
+ */
39
+ recordColumnChangeBatch(batch: WriteBatch, hlc: HLC, schema: string, table: string, pk: SqlValue[], column: string): void;
40
+ /**
41
+ * Record a row deletion in the change log.
42
+ */
43
+ recordDeletion(hlc: HLC, schema: string, table: string, pk: SqlValue[]): Promise<void>;
44
+ /**
45
+ * Record a row deletion in a batch.
46
+ */
47
+ recordDeletionBatch(batch: WriteBatch, hlc: HLC, schema: string, table: string, pk: SqlValue[]): void;
48
+ /**
49
+ * Get all change log entries after a given HLC.
50
+ * Returns entries in HLC order (oldest first).
51
+ */
52
+ getChangesSince(sinceHLC: HLC): AsyncIterable<ChangeLogEntry>;
53
+ /**
54
+ * Get all change log entries.
55
+ */
56
+ getAllChanges(): AsyncIterable<ChangeLogEntry>;
57
+ /**
58
+ * Delete change log entries up to a given HLC.
59
+ * Used for pruning old entries after they've been synced to all peers.
60
+ */
61
+ pruneEntriesBefore(beforeHLC: HLC): Promise<number>;
62
+ /**
63
+ * Delete a specific change log entry (used during snapshot application).
64
+ */
65
+ deleteEntryBatch(batch: WriteBatch, hlc: HLC, entryType: ChangeLogEntryType, schema: string, table: string, pk: SqlValue[], column?: string): void;
66
+ }
67
+ //# sourceMappingURL=change-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-log.d.ts","sourceRoot":"","sources":["../../../src/metadata/change-log.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,WAAW,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,OAAO;IAExC;;OAEG;IACG,kBAAkB,CACtB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,QAAQ,EAAE,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAMhB;;OAEG;IACH,uBAAuB,CACrB,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,QAAQ,EAAE,EACd,MAAM,EAAE,MAAM,GACb,IAAI;IAKP;;OAEG;IACG,cAAc,CAClB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,QAAQ,EAAE,GACb,OAAO,CAAC,IAAI,CAAC;IAKhB;;OAEG;IACH,mBAAmB,CACjB,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,QAAQ,EAAE,GACb,IAAI;IAKP;;;OAGG;IACI,eAAe,CAAC,QAAQ,EAAE,GAAG,GAAG,aAAa,CAAC,cAAc,CAAC;IAUpE;;OAEG;IACI,aAAa,IAAI,aAAa,CAAC,cAAc,CAAC;IAUrD;;;OAGG;IACG,kBAAkB,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBzD;;OAEG;IACH,gBAAgB,CACd,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,kBAAkB,EAC7B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,QAAQ,EAAE,EACd,MAAM,CAAC,EAAE,MAAM,GACd,IAAI;CAIR"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * HLC-indexed change log for efficient delta sync.
3
+ *
4
+ * The change log stores references to changes indexed by HLC timestamp,
5
+ * enabling efficient getChangesSince() queries without scanning all data.
6
+ */
7
+ import { buildChangeLogKey, buildChangeLogScanBoundsAfter, buildAllChangeLogScanBounds, parseChangeLogKey, } from './keys.js';
8
+ /**
9
+ * Change log store for HLC-indexed change tracking.
10
+ *
11
+ * Each mutation (column update or row deletion) creates an entry in the
12
+ * change log, keyed by HLC. This allows efficient range scans to find
13
+ * all changes since a given timestamp.
14
+ */
15
+ export class ChangeLogStore {
16
+ kv;
17
+ constructor(kv) {
18
+ this.kv = kv;
19
+ }
20
+ /**
21
+ * Record a column change in the change log.
22
+ */
23
+ async recordColumnChange(hlc, schema, table, pk, column) {
24
+ const key = buildChangeLogKey(hlc, 'column', schema, table, pk, column);
25
+ // Value is empty - all info is in the key
26
+ await this.kv.put(key, new Uint8Array(0));
27
+ }
28
+ /**
29
+ * Record a column change in a batch.
30
+ */
31
+ recordColumnChangeBatch(batch, hlc, schema, table, pk, column) {
32
+ const key = buildChangeLogKey(hlc, 'column', schema, table, pk, column);
33
+ batch.put(key, new Uint8Array(0));
34
+ }
35
+ /**
36
+ * Record a row deletion in the change log.
37
+ */
38
+ async recordDeletion(hlc, schema, table, pk) {
39
+ const key = buildChangeLogKey(hlc, 'delete', schema, table, pk);
40
+ await this.kv.put(key, new Uint8Array(0));
41
+ }
42
+ /**
43
+ * Record a row deletion in a batch.
44
+ */
45
+ recordDeletionBatch(batch, hlc, schema, table, pk) {
46
+ const key = buildChangeLogKey(hlc, 'delete', schema, table, pk);
47
+ batch.put(key, new Uint8Array(0));
48
+ }
49
+ /**
50
+ * Get all change log entries after a given HLC.
51
+ * Returns entries in HLC order (oldest first).
52
+ */
53
+ async *getChangesSince(sinceHLC) {
54
+ const bounds = buildChangeLogScanBoundsAfter(sinceHLC);
55
+ for await (const entry of this.kv.iterate(bounds)) {
56
+ const parsed = parseChangeLogKey(entry.key);
57
+ if (parsed) {
58
+ yield parsed;
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * Get all change log entries.
64
+ */
65
+ async *getAllChanges() {
66
+ const bounds = buildAllChangeLogScanBounds();
67
+ for await (const entry of this.kv.iterate(bounds)) {
68
+ const parsed = parseChangeLogKey(entry.key);
69
+ if (parsed) {
70
+ yield parsed;
71
+ }
72
+ }
73
+ }
74
+ /**
75
+ * Delete change log entries up to a given HLC.
76
+ * Used for pruning old entries after they've been synced to all peers.
77
+ */
78
+ async pruneEntriesBefore(beforeHLC) {
79
+ const bounds = buildAllChangeLogScanBounds();
80
+ const batch = this.kv.batch();
81
+ let count = 0;
82
+ for await (const entry of this.kv.iterate(bounds)) {
83
+ const parsed = parseChangeLogKey(entry.key);
84
+ if (!parsed)
85
+ continue;
86
+ // Compare HLCs - stop when we reach entries >= beforeHLC
87
+ if (parsed.hlc.wallTime > beforeHLC.wallTime)
88
+ break;
89
+ if (parsed.hlc.wallTime === beforeHLC.wallTime) {
90
+ if (parsed.hlc.counter >= beforeHLC.counter)
91
+ break;
92
+ }
93
+ batch.delete(entry.key);
94
+ count++;
95
+ }
96
+ await batch.write();
97
+ return count;
98
+ }
99
+ /**
100
+ * Delete a specific change log entry (used during snapshot application).
101
+ */
102
+ deleteEntryBatch(batch, hlc, entryType, schema, table, pk, column) {
103
+ const key = buildChangeLogKey(hlc, entryType, schema, table, pk, column);
104
+ batch.delete(key);
105
+ }
106
+ }
107
+ //# sourceMappingURL=change-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-log.js","sourceRoot":"","sources":["../../../src/metadata/change-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Column version tracking for LWW conflict resolution.
3
+ *
4
+ * Each column of each row has an associated HLC timestamp.
5
+ * When merging changes, the column with the higher HLC wins.
6
+ */
7
+ import type { SqlValue } from '@quereus/quereus';
8
+ import type { KVStore, WriteBatch } from '@quereus/plugin-store';
9
+ import { type HLC } from '../clock/hlc.js';
10
+ /**
11
+ * Column version record stored in the KV store.
12
+ */
13
+ export interface ColumnVersion {
14
+ hlc: HLC;
15
+ value: SqlValue;
16
+ }
17
+ /**
18
+ * Serialize a column version for storage.
19
+ * Format: 26 bytes HLC + JSON value
20
+ */
21
+ export declare function serializeColumnVersion(cv: ColumnVersion): Uint8Array;
22
+ /**
23
+ * Deserialize a column version from storage.
24
+ */
25
+ export declare function deserializeColumnVersion(buffer: Uint8Array): ColumnVersion;
26
+ /**
27
+ * Column version store operations.
28
+ */
29
+ export declare class ColumnVersionStore {
30
+ private readonly kv;
31
+ constructor(kv: KVStore);
32
+ /**
33
+ * Get the version of a specific column.
34
+ */
35
+ getColumnVersion(schemaName: string, tableName: string, pk: SqlValue[], column: string): Promise<ColumnVersion | undefined>;
36
+ /**
37
+ * Set the version of a specific column.
38
+ */
39
+ setColumnVersion(schemaName: string, tableName: string, pk: SqlValue[], column: string, version: ColumnVersion): Promise<void>;
40
+ /**
41
+ * Set column version in a batch.
42
+ */
43
+ setColumnVersionBatch(batch: WriteBatch, schemaName: string, tableName: string, pk: SqlValue[], column: string, version: ColumnVersion): void;
44
+ /**
45
+ * Get all column versions for a row.
46
+ */
47
+ getRowVersions(schemaName: string, tableName: string, pk: SqlValue[]): Promise<Map<string, ColumnVersion>>;
48
+ /**
49
+ * Delete all column versions for a row.
50
+ */
51
+ deleteRowVersions(schemaName: string, tableName: string, pk: SqlValue[]): Promise<void>;
52
+ /**
53
+ * Check if a column write should be applied (LWW comparison).
54
+ * Returns true if the incoming HLC is newer than the current version.
55
+ */
56
+ shouldApplyWrite(schemaName: string, tableName: string, pk: SqlValue[], column: string, incomingHLC: HLC): Promise<boolean>;
57
+ }
58
+ //# sourceMappingURL=column-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-version.d.ts","sourceRoot":"","sources":["../../../src/metadata/column-version.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,KAAK,GAAG,EAA4C,MAAM,iBAAiB,CAAC;AAGrF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,GAAG,CAAC;IACT,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,aAAa,GAAG,UAAU,CASpE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,UAAU,GAAG,aAAa,CAK1E;AAED;;GAEG;AACH,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,OAAO;IAExC;;OAEG;IACG,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAOrC;;OAEG;IACG,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC;IAKhB;;OAEG;IACH,qBAAqB,CACnB,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,aAAa,GACrB,IAAI;IAKP;;OAEG;IACG,cAAc,CAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAgBtC;;OAEG;IACG,iBAAiB,CACrB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,GACb,OAAO,CAAC,IAAI,CAAC;IAWhB;;;OAGG;IACG,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,QAAQ,EAAE,EACd,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,GAAG,GACf,OAAO,CAAC,OAAO,CAAC;CAKpB"}