sqlite-sync-core 0.2.0__tar.gz → 0.5.1__tar.gz

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. sqlite_sync_core-0.5.1/PKG-INFO +176 -0
  2. sqlite_sync_core-0.5.1/README.md +138 -0
  3. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/pyproject.toml +4 -1
  4. sqlite_sync_core-0.5.1/src/sqlite_sync/__init__.py +117 -0
  5. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/bundle/generate.py +5 -2
  6. sqlite_sync_core-0.5.1/src/sqlite_sync/cli/__init__.py +7 -0
  7. sqlite_sync_core-0.5.1/src/sqlite_sync/cli/main.py +12 -0
  8. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/db/connection.py +41 -6
  9. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/db/migrations.py +4 -1
  10. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/db/schema.py +15 -56
  11. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/db/triggers.py +15 -6
  12. sqlite_sync_core-0.5.1/src/sqlite_sync/engine.py +244 -0
  13. sqlite_sync_core-0.5.1/src/sqlite_sync/ext/__init__.py +0 -0
  14. sqlite_sync_core-0.5.1/src/sqlite_sync/ext/cli/main.py +110 -0
  15. sqlite_sync_core-0.5.1/src/sqlite_sync/ext/network_manager.py +117 -0
  16. sqlite_sync_core-0.5.1/src/sqlite_sync/ext/node.py +116 -0
  17. {sqlite_sync_core-0.2.0/src/sqlite_sync → sqlite_sync_core-0.5.1/src/sqlite_sync/ext}/server/__init__.py +1 -1
  18. {sqlite_sync_core-0.2.0/src/sqlite_sync → sqlite_sync_core-0.5.1/src/sqlite_sync/ext}/server/http_server.py +216 -109
  19. {sqlite_sync_core-0.2.0/src/sqlite_sync → sqlite_sync_core-0.5.1/src/sqlite_sync/ext}/sync_loop.py +19 -4
  20. sqlite_sync_core-0.5.1/src/sqlite_sync/hlc.py +95 -0
  21. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/import_apply/apply.py +74 -28
  22. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/import_apply/conflict.py +7 -0
  23. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/log/operations.py +35 -48
  24. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/log/vector_clock.py +25 -44
  25. sqlite_sync_core-0.5.1/src/sqlite_sync/node.py +9 -0
  26. sqlite_sync_core-0.5.1/src/sqlite_sync/resolution/lww_merge.py +87 -0
  27. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/schema_evolution.py +9 -1
  28. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/transport/http_transport.py +4 -1
  29. sqlite_sync_core-0.5.1/src/sqlite_sync_core.egg-info/PKG-INFO +176 -0
  30. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync_core.egg-info/SOURCES.txt +14 -4
  31. sqlite_sync_core-0.5.1/src/sqlite_sync_core.egg-info/entry_points.txt +2 -0
  32. sqlite_sync_core-0.2.0/PKG-INFO +0 -379
  33. sqlite_sync_core-0.2.0/README.md +0 -341
  34. sqlite_sync_core-0.2.0/src/sqlite_sync/__init__.py +0 -59
  35. sqlite_sync_core-0.2.0/src/sqlite_sync/engine.py +0 -541
  36. sqlite_sync_core-0.2.0/src/sqlite_sync_core.egg-info/PKG-INFO +0 -379
  37. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/LICENSE +0 -0
  38. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/setup.cfg +0 -0
  39. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/audit/__init__.py +0 -0
  40. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/audit/import_log.py +0 -0
  41. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/bundle/__init__.py +0 -0
  42. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/bundle/format.py +0 -0
  43. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/bundle/validate.py +0 -0
  44. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/capture/__init__.py +0 -0
  45. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/capture/change_capture.py +0 -0
  46. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/config.py +0 -0
  47. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/crash_safety.py +0 -0
  48. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/db/__init__.py +0 -0
  49. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/errors.py +0 -0
  50. {sqlite_sync_core-0.2.0/src/sqlite_sync → sqlite_sync_core-0.5.1/src/sqlite_sync/ext}/server/auth.py +0 -0
  51. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/import_apply/__init__.py +0 -0
  52. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/import_apply/dedup.py +0 -0
  53. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/import_apply/ordering.py +0 -0
  54. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/invariants.py +0 -0
  55. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/log/__init__.py +0 -0
  56. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/log_compaction.py +0 -0
  57. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/metrics.py +0 -0
  58. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/network/client.py +0 -0
  59. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/network/peer_discovery.py +0 -0
  60. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/network/protocol.py +0 -0
  61. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/network/server.py +0 -0
  62. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/resolution/__init__.py +0 -0
  63. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/security.py +0 -0
  64. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/transport/__init__.py +0 -0
  65. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/transport/base.py +0 -0
  66. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/transport/websocket_transport.py +0 -0
  67. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/utils/__init__.py +0 -0
  68. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/utils/hashing.py +0 -0
  69. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/utils/msgpack_codec.py +0 -0
  70. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync/utils/uuid7.py +0 -0
  71. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync_core.egg-info/dependency_links.txt +0 -0
  72. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync_core.egg-info/requires.txt +0 -0
  73. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/src/sqlite_sync_core.egg-info/top_level.txt +0 -0
  74. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/tests/test_conflicts.py +0 -0
  75. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/tests/test_determinism.py +0 -0
  76. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/tests/test_idempotency.py +0 -0
  77. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/tests/test_invariants.py +0 -0
  78. {sqlite_sync_core-0.2.0 → sqlite_sync_core-0.5.1}/tests/test_minimal.py +0 -0
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlite-sync-core
3
+ Version: 0.5.1
4
+ Summary: Universal SQLite Synchronization Core - A dependency-grade, local-first, offline-first SQLite synchronization primitive
5
+ License: AGPL-3.0
6
+ Project-URL: Homepage, https://github.com/shivay00001/sqlite-sync-core
7
+ Project-URL: Documentation, https://github.com/shivay00001/sqlite-sync-core#readme
8
+ Project-URL: Repository, https://github.com/shivay00001/sqlite-sync-core.git
9
+ Project-URL: Issues, https://github.com/shivay00001/sqlite-sync-core/issues
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Database
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: msgpack>=1.0.0
24
+ Requires-Dist: websockets>=12.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
27
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
28
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
29
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
30
+ Provides-Extra: server
31
+ Requires-Dist: flask>=3.0.0; extra == "server"
32
+ Provides-Extra: crypto
33
+ Requires-Dist: cryptography>=41.0.0; extra == "crypto"
34
+ Provides-Extra: all
35
+ Requires-Dist: flask>=3.0.0; extra == "all"
36
+ Requires-Dist: cryptography>=41.0.0; extra == "all"
37
+ Dynamic: license-file
38
+
39
+ # sqlite-sync-core
40
+
41
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
42
+ [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
43
+ [![PyPI](https://img.shields.io/pypi/v/sqlite-sync-core.svg)](https://pypi.org/project/sqlite-sync-core/)
44
+ [![Status: Production Grade](https://img.shields.io/badge/status-production--grade-brightgreen.svg)](https://github.com/shivay00001/sqlite-sync-core)
45
+
46
+ **A production-grade, turn-key synchronization system for SQLite.**
47
+
48
+ `sqlite-sync-core` provides a powerful, local-first synchronization engine that works seamlessly across multi-peer networks. It handles the "hard parts" of sync (vector clocks, causality, delta bundles, and conflict resolution) while providing a simple, turn-key interface for developers.
49
+
50
+ ---
51
+
52
+ ## 🚀 Turn-Key Synchronization
53
+
54
+ You can launch a full synchronization node in one command. No infrastructure required.
55
+
56
+ ### 16-Second Setup (CLI)
57
+
58
+ ```bash
59
+ # Install the package
60
+ pip install sqlite-sync-core
61
+
62
+ # Start a node and sync the 'tasks' table automatically
63
+ sqlite-sync start --db app.db --name Device-A --tables tasks
64
+ ```
65
+
66
+ ### Automatic Multi-Peer Sync
67
+
68
+ Nodes automatically discover each other on the local network (P2P) and synchronize state in the background without any manual peer configuration.
69
+
70
+ ---
71
+
72
+ ## 🏗️ Enterprise Features
73
+
74
+ - **Multi-Peer Orchestration**: Automatically scales sync across N devices.
75
+ - **P2P Discovery**: Zero-config peer-to-peer discovery on LAN.
76
+ - **Automatic Resolution**: Configurable strategies like Last-Write-Wins and Field-Level Merge.
77
+ - **Schema Evolution**: Built-in migrations that sync across the network.
78
+ - **Transport Agnostic**: Works over HTTP, WebSockets, or file transfer.
79
+
80
+ ---
81
+
82
+ ## Technical Usage (Library)
83
+
84
+ ### Initialize a Node in Code
85
+
86
+ ```python
87
+ from sqlite_sync import SyncNode
88
+
89
+ node = SyncNode(
90
+ db_path="app.db",
91
+ device_name="MobileApp",
92
+ sync_interval=10 # Sync every 10 seconds
93
+ )
94
+
95
+ await node.start()
96
+ node.enable_sync_for_table("users")
97
+ ```
98
+
99
+ ### Safe Schema Migrations
100
+
101
+ ```bash
102
+ # Safely add a column that will sync to all other peers
103
+ sqlite-sync migrate --db app.db --table tasks --add-column priority --type INTEGER
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Core Invariants
109
+
110
+ | # | Invariant | Description |
111
+ |---|-----------|-------------|
112
+ | 1 | **Causal consistency** | Vector clocks ensure the correct order of operations. |
113
+ | 2 | **Deterministic Replay** | Identical sets of operations always result in identical state. |
114
+ | 3 | **Conflict Tolerance** | Detects and resolves conflicts explicitly and safely. |
115
+ | 4 | **Offline-First** | Entirely local-first design; works without cloud or internet. |
116
+
117
+ ### 2. Generate a Delta Bundle
118
+
119
+ ```python
120
+ # To be sent to Peer B
121
+ bundle_path = engine.generate_bundle(
122
+ peer_device_id=peer_b_id,
123
+ output_path="delta.db"
124
+ )
125
+ ```
126
+
127
+ ### 3. Import and Detect Conflicts
128
+
129
+ ```python
130
+ # On Peer B
131
+ result = engine.import_bundle("delta.db")
132
+
133
+ print(f"Ops Applied: {result.applied_count}")
134
+ print(f"Conflicts Detected: {result.conflict_count}")
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Core Invariants
140
+
141
+ | # | Invariant | Description |
142
+ |---|-----------|-------------|
143
+ | 1 | **Append-only** | Operation history is immutable. |
144
+ | 2 | **Causal consistency** | Hybrid Logical Clocks (HLC) ensure correct partial ordering and wall-clock correlation. |
145
+ | 3 | **Deterministic** | Replay results are identical across all replicas. |
146
+ | 4 | **Field-Level Merge** | "Smart" LWW resolution merges concurrent non-conflicting field updates. |
147
+
148
+ ---
149
+
150
+ ## Architecture
151
+
152
+ ```
153
+ ┌─────────────────────────────────┐
154
+ │ Your Sync System / App │
155
+ └───────────────┬─────────────────┘
156
+ │ Uses
157
+ ┌───────────────▼─────────────────┐
158
+ │ sqlite-sync-core │
159
+ │ (Logging, Bundling, Clocks) │
160
+ └───────────────┬─────────────────┘
161
+ │ Persists to
162
+ ┌───────────────▼─────────────────┐
163
+ │ SQLite Database │
164
+ └─────────────────────────────────┘
165
+ ```
166
+
167
+ ---
168
+
169
+ ## License
170
+
171
+ **AGPL-3.0** for Open Source.
172
+ Contact <shivaysinghrajput@proton.me> for commercial licensing.
173
+
174
+ ---
175
+
176
+ **Built for developers who need a reliable sync foundation.**
@@ -0,0 +1,138 @@
1
+ # sqlite-sync-core
2
+
3
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
4
+ [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](https://www.gnu.org/licenses/agpl-3.0)
5
+ [![PyPI](https://img.shields.io/pypi/v/sqlite-sync-core.svg)](https://pypi.org/project/sqlite-sync-core/)
6
+ [![Status: Production Grade](https://img.shields.io/badge/status-production--grade-brightgreen.svg)](https://github.com/shivay00001/sqlite-sync-core)
7
+
8
+ **A production-grade, turn-key synchronization system for SQLite.**
9
+
10
+ `sqlite-sync-core` provides a powerful, local-first synchronization engine that works seamlessly across multi-peer networks. It handles the "hard parts" of sync (vector clocks, causality, delta bundles, and conflict resolution) while providing a simple, turn-key interface for developers.
11
+
12
+ ---
13
+
14
+ ## 🚀 Turn-Key Synchronization
15
+
16
+ You can launch a full synchronization node in one command. No infrastructure required.
17
+
18
+ ### 16-Second Setup (CLI)
19
+
20
+ ```bash
21
+ # Install the package
22
+ pip install sqlite-sync-core
23
+
24
+ # Start a node and sync the 'tasks' table automatically
25
+ sqlite-sync start --db app.db --name Device-A --tables tasks
26
+ ```
27
+
28
+ ### Automatic Multi-Peer Sync
29
+
30
+ Nodes automatically discover each other on the local network (P2P) and synchronize state in the background without any manual peer configuration.
31
+
32
+ ---
33
+
34
+ ## 🏗️ Enterprise Features
35
+
36
+ - **Multi-Peer Orchestration**: Automatically scales sync across N devices.
37
+ - **P2P Discovery**: Zero-config peer-to-peer discovery on LAN.
38
+ - **Automatic Resolution**: Configurable strategies like Last-Write-Wins and Field-Level Merge.
39
+ - **Schema Evolution**: Built-in migrations that sync across the network.
40
+ - **Transport Agnostic**: Works over HTTP, WebSockets, or file transfer.
41
+
42
+ ---
43
+
44
+ ## Technical Usage (Library)
45
+
46
+ ### Initialize a Node in Code
47
+
48
+ ```python
49
+ from sqlite_sync import SyncNode
50
+
51
+ node = SyncNode(
52
+ db_path="app.db",
53
+ device_name="MobileApp",
54
+ sync_interval=10 # Sync every 10 seconds
55
+ )
56
+
57
+ await node.start()
58
+ node.enable_sync_for_table("users")
59
+ ```
60
+
61
+ ### Safe Schema Migrations
62
+
63
+ ```bash
64
+ # Safely add a column that will sync to all other peers
65
+ sqlite-sync migrate --db app.db --table tasks --add-column priority --type INTEGER
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Core Invariants
71
+
72
+ | # | Invariant | Description |
73
+ |---|-----------|-------------|
74
+ | 1 | **Causal consistency** | Vector clocks ensure the correct order of operations. |
75
+ | 2 | **Deterministic Replay** | Identical sets of operations always result in identical state. |
76
+ | 3 | **Conflict Tolerance** | Detects and resolves conflicts explicitly and safely. |
77
+ | 4 | **Offline-First** | Entirely local-first design; works without cloud or internet. |
78
+
79
+ ### 2. Generate a Delta Bundle
80
+
81
+ ```python
82
+ # To be sent to Peer B
83
+ bundle_path = engine.generate_bundle(
84
+ peer_device_id=peer_b_id,
85
+ output_path="delta.db"
86
+ )
87
+ ```
88
+
89
+ ### 3. Import and Detect Conflicts
90
+
91
+ ```python
92
+ # On Peer B
93
+ result = engine.import_bundle("delta.db")
94
+
95
+ print(f"Ops Applied: {result.applied_count}")
96
+ print(f"Conflicts Detected: {result.conflict_count}")
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Core Invariants
102
+
103
+ | # | Invariant | Description |
104
+ |---|-----------|-------------|
105
+ | 1 | **Append-only** | Operation history is immutable. |
106
+ | 2 | **Causal consistency** | Hybrid Logical Clocks (HLC) ensure correct partial ordering and wall-clock correlation. |
107
+ | 3 | **Deterministic** | Replay results are identical across all replicas. |
108
+ | 4 | **Field-Level Merge** | "Smart" LWW resolution merges concurrent non-conflicting field updates. |
109
+
110
+ ---
111
+
112
+ ## Architecture
113
+
114
+ ```
115
+ ┌─────────────────────────────────┐
116
+ │ Your Sync System / App │
117
+ └───────────────┬─────────────────┘
118
+ │ Uses
119
+ ┌───────────────▼─────────────────┐
120
+ │ sqlite-sync-core │
121
+ │ (Logging, Bundling, Clocks) │
122
+ └───────────────┬─────────────────┘
123
+ │ Persists to
124
+ ┌───────────────▼─────────────────┐
125
+ │ SQLite Database │
126
+ └─────────────────────────────────┘
127
+ ```
128
+
129
+ ---
130
+
131
+ ## License
132
+
133
+ **AGPL-3.0** for Open Source.
134
+ Contact <shivaysinghrajput@proton.me> for commercial licensing.
135
+
136
+ ---
137
+
138
+ **Built for developers who need a reliable sync foundation.**
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sqlite-sync-core"
7
- version = "0.2.0"
7
+ version = "0.5.1"
8
8
  description = "Universal SQLite Synchronization Core - A dependency-grade, local-first, offline-first SQLite synchronization primitive"
9
9
  readme = "README.md"
10
10
  license = {text = "AGPL-3.0"}
@@ -50,6 +50,9 @@ all = [
50
50
  "cryptography>=41.0.0",
51
51
  ]
52
52
 
53
+ [project.scripts]
54
+ sqlite-sync = "sqlite_sync.cli.main:main"
55
+
53
56
  [tool.setuptools.packages.find]
54
57
  where = ["src"]
55
58
 
@@ -0,0 +1,117 @@
1
+ """
2
+ sqlite_sync - Universal SQLite Synchronization Core
3
+
4
+ A dependency-grade, local-first, offline-first SQLite synchronization primitive.
5
+ Enterprise-ready with authentication, metrics, peer discovery, and enhanced security.
6
+ """
7
+
8
+ from sqlite_sync.engine import SyncEngine
9
+ from sqlite_sync.errors import (
10
+ SyncError,
11
+ SchemaError,
12
+ BundleError,
13
+ ConflictError,
14
+ InvariantViolationError,
15
+ )
16
+
17
+ # Core
18
+ __version__ = "0.5.1"
19
+ __all__ = [
20
+ # Core
21
+ "SyncEngine",
22
+ # Errors
23
+ "SyncError",
24
+ "SchemaError",
25
+ "BundleError",
26
+ "ConflictError",
27
+ "InvariantViolationError",
28
+ ]
29
+
30
+ # Enterprise: SyncNode (full orchestration)
31
+ try:
32
+ from sqlite_sync.ext.node import SyncNode
33
+ __all__.append("SyncNode")
34
+ except ImportError:
35
+ pass
36
+
37
+ # Enterprise: P2P Discovery
38
+ try:
39
+ from sqlite_sync.network.peer_discovery import (
40
+ UDPDiscovery,
41
+ PeerManager,
42
+ Peer,
43
+ PeerStatus,
44
+ DiscoveryConfig,
45
+ create_discovery,
46
+ )
47
+ __all__.extend([
48
+ "UDPDiscovery",
49
+ "PeerManager",
50
+ "Peer",
51
+ "PeerStatus",
52
+ "DiscoveryConfig",
53
+ "create_discovery",
54
+ ])
55
+ except ImportError:
56
+ pass
57
+
58
+ # Enterprise: Conflict Resolution
59
+ try:
60
+ from sqlite_sync.resolution import (
61
+ ResolutionStrategy,
62
+ ConflictResolver,
63
+ ConflictContext,
64
+ ResolutionResult,
65
+ get_resolver,
66
+ LastWriteWinsResolver,
67
+ FieldMergeResolver,
68
+ )
69
+ __all__.extend([
70
+ "ResolutionStrategy",
71
+ "ConflictResolver",
72
+ "ConflictContext",
73
+ "ResolutionResult",
74
+ "get_resolver",
75
+ "LastWriteWinsResolver",
76
+ "FieldMergeResolver",
77
+ ])
78
+ except ImportError:
79
+ pass
80
+
81
+ # Enterprise: Schema Evolution
82
+ try:
83
+ from sqlite_sync.schema_evolution import SchemaManager, SchemaMigration
84
+ __all__.extend(["SchemaManager", "SchemaMigration"])
85
+ except ImportError:
86
+ pass
87
+
88
+ # Optional enterprise imports (fail gracefully if deps missing)
89
+ try:
90
+ from sqlite_sync.metrics import (
91
+ get_registry,
92
+ sync_operations_total,
93
+ sync_conflicts_total,
94
+ sync_latency_seconds,
95
+ configure_logging,
96
+ SyncLogger,
97
+ HealthChecker,
98
+ get_health_checker,
99
+ )
100
+ __all__.extend([
101
+ "get_registry",
102
+ "sync_operations_total",
103
+ "sync_conflicts_total",
104
+ "sync_latency_seconds",
105
+ "configure_logging",
106
+ "SyncLogger",
107
+ "HealthChecker",
108
+ "get_health_checker",
109
+ ])
110
+ except ImportError:
111
+ pass
112
+
113
+ try:
114
+ from sqlite_sync.security import SecurityManager, DeviceIdentity
115
+ __all__.extend(["SecurityManager", "DeviceIdentity"])
116
+ except ImportError:
117
+ pass
@@ -5,6 +5,7 @@ Generates a bundle containing operations that a peer hasn't seen.
5
5
  Bundles are self-contained SQLite files.
6
6
  """
7
7
 
8
+ import os
8
9
  import sqlite3
9
10
  import time
10
11
  from typing import Final
@@ -56,6 +57,8 @@ def generate_bundle(
56
57
 
57
58
  # Create bundle database
58
59
  try:
60
+ if os.path.exists(output_path):
61
+ os.remove(output_path)
59
62
  bundle_conn = sqlite3.connect(output_path)
60
63
 
61
64
  # Create bundle schema
@@ -66,10 +69,10 @@ def generate_bundle(
66
69
  bundle_conn.executemany(
67
70
  """
68
71
  INSERT INTO bundle_operations (
69
- op_id, device_id, parent_op_id, vector_clock,
72
+ op_id, device_id, parent_op_id, vector_clock, hlc,
70
73
  table_name, op_type, row_pk, old_values, new_values,
71
74
  schema_version, created_at, is_local, applied_at
72
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
75
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
73
76
  """,
74
77
  operations,
75
78
  )
@@ -0,0 +1,7 @@
1
+ """
2
+ cli - Command Line Interface for sqlite-sync-core.
3
+ """
4
+
5
+ from sqlite_sync.ext.cli.main import main, CLIManager
6
+
7
+ __all__ = ["main", "CLIManager"]
@@ -0,0 +1,12 @@
1
+ """
2
+ cli/main.py - Entry point for sqlite-sync CLI.
3
+
4
+ Re-exports the CLI from ext/cli/main.py for pyproject.toml compatibility.
5
+ """
6
+
7
+ from sqlite_sync.ext.cli.main import main, CLIManager
8
+
9
+ __all__ = ["main", "CLIManager"]
10
+
11
+ if __name__ == "__main__":
12
+ main()
@@ -7,9 +7,14 @@ registration of application-defined SQL functions.
7
7
  All connections use WAL mode for concurrent read/write.
8
8
  """
9
9
 
10
+ import logging
10
11
  import sqlite3
11
- from typing import Callable, Any
12
+ import json
13
+ import time
14
+
15
+ logger = logging.getLogger("sqlite_sync.db")
12
16
 
17
+ from typing import Callable, Any
13
18
  from sqlite_sync.config import SQLITE_PRAGMAS
14
19
  from sqlite_sync.errors import DatabaseError
15
20
 
@@ -85,32 +90,62 @@ def _register_functions(conn: sqlite3.Connection) -> None:
85
90
  from sqlite_sync.log.vector_clock import increment_vector_clock, merge_vector_clocks
86
91
  from sqlite_sync.utils.msgpack_codec import pack_primary_key, pack_dict
87
92
  import json
93
+ import sys
88
94
 
89
95
  # sync_uuid_v7() -> BLOB(16)
90
- conn.create_function("sync_uuid_v7", 0, generate_uuid_v7)
96
+ def _uuid_v7() -> bytes:
97
+ return generate_uuid_v7()
98
+
99
+ conn.create_function("sync_uuid_v7", 0, _uuid_v7)
91
100
 
92
101
  # sync_vector_clock_increment(device_id BLOB, vc_json TEXT) -> TEXT
93
- def _vc_increment(device_id: bytes, vc_json: str) -> str:
102
+ def _vc_increment(device_id: Any, vc_json: Any) -> str:
94
103
  return increment_vector_clock(device_id, vc_json)
95
104
 
96
105
  conn.create_function("sync_vector_clock_increment", 2, _vc_increment)
97
106
 
98
107
  # sync_vector_clock_merge(vc1_json TEXT, vc2_json TEXT) -> TEXT
99
- def _vc_merge(vc1_json: str, vc2_json: str) -> str:
108
+ def _vc_merge(vc1_json: Any, vc2_json: Any) -> str:
100
109
  return merge_vector_clocks(vc1_json, vc2_json)
101
110
 
102
111
  conn.create_function("sync_vector_clock_merge", 2, _vc_merge)
103
112
 
104
113
  # sync_pack_pk(value) -> BLOB
105
- conn.create_function("sync_pack_pk", 1, pack_primary_key)
114
+ def _pack_pk(value: Any) -> bytes:
115
+ return pack_primary_key(value)
116
+
117
+ conn.create_function("sync_pack_pk", 1, _pack_pk)
106
118
 
107
119
  # sync_pack_values(json_str TEXT) -> BLOB
108
- def _pack_values(json_str: str) -> bytes:
120
+ def _pack_values(json_str: Any) -> bytes:
121
+ if not json_str: return b""
109
122
  data = json.loads(json_str)
110
123
  return pack_dict(data)
111
124
 
112
125
  conn.create_function("sync_pack_values", 1, _pack_values)
113
126
 
127
+ # sync_hlc_now(node_id TEXT) -> TEXT
128
+ def _hlc_now_placeholder(node_id: Any) -> str:
129
+ return f"{int(time.time() * 1000)}:0:{node_id}"
130
+
131
+ conn.create_function("sync_hlc_now", 1, _hlc_now_placeholder)
132
+
133
+ if not hasattr(_register_functions, "_disabled_state"):
134
+ _register_functions._disabled_state = {}
135
+
136
+ def _sync_is_disabled() -> int:
137
+ return _register_functions._disabled_state.get(id(conn), 0)
138
+
139
+ conn.create_function("sync_is_disabled", 0, _sync_is_disabled)
140
+
141
+
142
+ def set_sync_disabled(conn: sqlite3.Connection, disabled: bool) -> None:
143
+ """Set the sync-disabled state for a specific connection."""
144
+ val = 1 if disabled else 0
145
+ if not hasattr(_register_functions, "_disabled_state"):
146
+ _register_functions._disabled_state = {}
147
+ _register_functions._disabled_state[id(conn)] = val
148
+
114
149
 
115
150
  def execute_in_transaction(
116
151
  conn: sqlite3.Connection,
@@ -49,10 +49,13 @@ def initialize_sync_tables(conn: sqlite3.Connection) -> bytes:
49
49
  for sql in statement.strip().split(";"):
50
50
  sql = sql.strip()
51
51
  if sql:
52
+ # print(f"DEBUG: Executing SQL: {sql}")
52
53
  conn.execute(sql)
53
54
  except sqlite3.Error as e:
55
+ # Get the failing SQL if possible
56
+ failed_sql = locals().get('sql', 'unknown')
54
57
  raise DatabaseError(
55
- f"Failed to create sync tables: {e}",
58
+ f"Failed to create sync tables: {e} (SQL: {failed_sql})",
56
59
  operation="create_tables",
57
60
  ) from e
58
61