meadow-integration 1.0.5 → 1.0.7

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 (67) hide show
  1. package/.dockerignore +11 -0
  2. package/Docker-Build.sh +2 -0
  3. package/Docker-Compose.sh +2 -0
  4. package/Docker-Push.sh +2 -0
  5. package/Docker-Tag.sh +2 -0
  6. package/Dockerfile +28 -0
  7. package/Dockerfile_LUXURYCode +23 -0
  8. package/README.md +139 -25
  9. package/docker-compose.yml +16 -0
  10. package/docs/README.md +65 -18
  11. package/docs/_cover.md +3 -2
  12. package/docs/_sidebar.md +52 -7
  13. package/docs/_topbar.md +2 -0
  14. package/docs/api/clone-rest-client.md +278 -0
  15. package/docs/api/connection-manager.md +179 -0
  16. package/docs/api/guid-map.md +234 -0
  17. package/docs/api/integration-adapter.md +283 -0
  18. package/docs/api/operation.md +241 -0
  19. package/docs/api/sync-entity-initial.md +227 -0
  20. package/docs/api/sync-entity-ongoing.md +244 -0
  21. package/docs/api/sync.md +213 -0
  22. package/docs/api/tabular-check.md +213 -0
  23. package/docs/api/tabular-transform.md +316 -0
  24. package/docs/architecture.md +423 -0
  25. package/docs/cli/comprehensionarray.md +111 -0
  26. package/docs/cli/comprehensionintersect.md +132 -0
  27. package/docs/cli/csvcheck.md +111 -0
  28. package/docs/cli/csvtransform.md +170 -0
  29. package/docs/cli/data-clone.md +277 -0
  30. package/docs/cli/jsonarraytransform.md +166 -0
  31. package/docs/cli/load-comprehension.md +129 -0
  32. package/docs/cli/objectarraytocsv.md +159 -0
  33. package/docs/cli/overview.md +96 -0
  34. package/docs/cli/serve.md +102 -0
  35. package/docs/cli/tsvtransform.md +144 -0
  36. package/docs/data-clone/configuration.md +357 -0
  37. package/docs/data-clone/connection-manager.md +206 -0
  38. package/docs/data-clone/docker.md +290 -0
  39. package/docs/data-clone/overview.md +173 -0
  40. package/docs/data-clone/sync-modes.md +186 -0
  41. package/docs/implementation-reference.md +311 -0
  42. package/docs/overview.md +156 -0
  43. package/docs/quickstart.md +233 -0
  44. package/docs/rest/comprehension-push.md +209 -0
  45. package/docs/rest/comprehension.md +506 -0
  46. package/docs/rest/csv.md +255 -0
  47. package/docs/rest/entity-generation.md +158 -0
  48. package/docs/rest/json-array.md +243 -0
  49. package/docs/rest/overview.md +120 -0
  50. package/docs/rest/status.md +63 -0
  51. package/docs/rest/tsv.md +241 -0
  52. package/docs/retold-catalog.json +93 -3
  53. package/docs/retold-keyword-index.json +23683 -1901
  54. package/package.json +6 -3
  55. package/scripts/run.sh +18 -0
  56. package/source/Meadow-Integration.js +15 -1
  57. package/source/cli/Default-Meadow-Integration-Configuration.json +37 -2
  58. package/source/cli/Meadow-Integration-CLI-Program.js +4 -1
  59. package/source/cli/commands/Meadow-Integration-Command-DataClone.js +284 -0
  60. package/source/services/clone/Meadow-Service-ConnectionManager.js +251 -0
  61. package/source/services/clone/Meadow-Service-Operation.js +196 -0
  62. package/source/services/clone/Meadow-Service-RestClient.js +364 -0
  63. package/source/services/clone/Meadow-Service-Sync-Entity-Initial.js +502 -0
  64. package/source/services/clone/Meadow-Service-Sync-Entity-Ongoing.js +592 -0
  65. package/source/services/clone/Meadow-Service-Sync.js +154 -0
  66. /package/docs/examples/bookstore/{mapping_books_Author.json → mapping_books_author.json} +0 -0
  67. /package/docs/examples/bookstore/{mapping_books_Book.json → mapping_books_book.json} +0 -0
@@ -0,0 +1,290 @@
1
+ # Docker Deployment
2
+
3
+ The meadow-integration module includes Docker support for running data clone operations in containerized environments.
4
+
5
+ ## Dockerfiles
6
+
7
+ ### Production Dockerfile
8
+
9
+ The primary `Dockerfile` builds a production-ready image based on `node:20-bookworm`.
10
+
11
+ #### Build Stages
12
+
13
+ The Dockerfile uses a multi-stage build:
14
+
15
+ **Base stage**:
16
+ 1. Starts from `node:20-bookworm`.
17
+ 2. Installs system utilities: `curl`, `vim`, `nano`, `less`, `tmux`, `uuid-runtime`.
18
+ 3. Installs `nodemon` globally.
19
+ 4. Copies `package.json` and runs `npm install --omit=dev` (production dependencies only).
20
+ 5. Copies `source/` and `scripts/` directories.
21
+ 6. Cleans up development artifacts (`package-lock.json`, `.git`, `test`).
22
+ 7. If a `Meadow-Config-Docker.json` file exists, it is moved to `source/cli/Default-Meadow-Integration-Configuration.json` to serve as the default configuration inside the container.
23
+
24
+ **Production stage**:
25
+ 1. Extends the base stage.
26
+ 2. Records the build timestamp in `build.date`.
27
+ 3. Runs `scripts/run.sh` as the default command.
28
+
29
+ #### Building the Image
30
+
31
+ ```bash
32
+ docker build -t retold/meadow-integration:latest .
33
+ ```
34
+
35
+ Or use the provided helper script:
36
+
37
+ ```bash
38
+ ./Docker-Build.sh
39
+ ```
40
+
41
+ #### Custom Configuration at Build Time
42
+
43
+ To bake a default configuration into the image, create a `Meadow-Config-Docker.json` file in the project root before building:
44
+
45
+ ```json
46
+ {
47
+ "Source": {
48
+ "ServerURL": "https://api.production.example.com/1.0/",
49
+ "UserID": "sync_service",
50
+ "Password": "service_password"
51
+ },
52
+ "Destination": {
53
+ "Provider": "MySQL",
54
+ "MySQL": {
55
+ "server": "mysql-host",
56
+ "port": 3306,
57
+ "user": "clone_user",
58
+ "password": "clone_password",
59
+ "database": "production_clone",
60
+ "connectionLimit": 20
61
+ }
62
+ },
63
+ "SchemaPath": "/service_root/schema/Model-Extended.json",
64
+ "Sync": {
65
+ "DefaultSyncMode": "Initial",
66
+ "PageSize": 100,
67
+ "SyncEntityList": []
68
+ }
69
+ }
70
+ ```
71
+
72
+ This file is automatically picked up during the build and used as the default configuration.
73
+
74
+ ### Development Dockerfile (Dockerfile_LUXURYCode)
75
+
76
+ The `Dockerfile_LUXURYCode` builds a development image based on `codercom/code-server:latest`. It provides a browser-based VS Code environment for developing and debugging the meadow-integration module.
77
+
78
+ #### What It Includes
79
+
80
+ - **Base**: code-server (VS Code in the browser)
81
+ - **Runtime**: Node.js 20 via NVM
82
+ - **System tools**: vim, curl, tmux
83
+ - **VS Code extensions**:
84
+ - Mocha Test Adapter
85
+ - Test Explorer
86
+ - Indent Rainbow
87
+ - ESLint
88
+ - GitLens
89
+
90
+ #### Volumes
91
+
92
+ | Volume | Purpose |
93
+ |--------|---------|
94
+ | `/home/coder/.config` | code-server configuration |
95
+ | `/home/coder/.vscode` | VS Code settings |
96
+ | `/home/coder/meadow-integration` | Project source (mount your local checkout here) |
97
+
98
+ #### Building the Development Image
99
+
100
+ ```bash
101
+ docker build -f Dockerfile_LUXURYCode -t retold/meadow-integration-dev:latest .
102
+ ```
103
+
104
+ #### Running the Development Image
105
+
106
+ ```bash
107
+ docker run -d \
108
+ -p 8443:8080 \
109
+ -v "$(pwd):/home/coder/meadow-integration" \
110
+ retold/meadow-integration-dev:latest
111
+ ```
112
+
113
+ Access the development environment at `http://localhost:8443`.
114
+
115
+ ## Docker Compose
116
+
117
+ The `docker-compose.yml` file defines a service for running the data clone:
118
+
119
+ ```yaml
120
+ version: '2'
121
+
122
+ services:
123
+ meadow-integration-clone:
124
+ image: retold/meadow-integration:latest
125
+ volumes:
126
+ - "${RETOLD_DIR:-./}:/service_root"
127
+ environment:
128
+ - RUN_LOCAL_DEV=true
129
+ networks:
130
+ - back
131
+
132
+ networks:
133
+ back:
134
+ external:
135
+ name: meadow_backend
136
+ ```
137
+
138
+ ### Running with Docker Compose
139
+
140
+ ```bash
141
+ docker-compose up -d meadow-integration-clone
142
+ ```
143
+
144
+ Or use the provided helper script:
145
+
146
+ ```bash
147
+ ./Docker-Compose.sh
148
+ ```
149
+
150
+ ### Environment Variables
151
+
152
+ | Variable | Default | Description |
153
+ |----------|---------|-------------|
154
+ | `RETOLD_DIR` | `./` | Host directory to mount as `/service_root` |
155
+ | `RUN_LOCAL_DEV` | - | Set to `true` for local development mode |
156
+ | `MEADOW_INTEGRATION_PORT` | `8086` | Port override for the REST server (when running the `serve` command) |
157
+
158
+ ### Network
159
+
160
+ The compose file expects an external Docker network named `meadow_backend`. Create it before running:
161
+
162
+ ```bash
163
+ docker network create meadow_backend
164
+ ```
165
+
166
+ ## Volume Mounts for Configuration
167
+
168
+ ### Mounting a Config File
169
+
170
+ To provide a `.meadow.config.json` at runtime without baking it into the image:
171
+
172
+ ```bash
173
+ docker run \
174
+ -v "/path/to/.meadow.config.json:/service_root/.meadow.config.json" \
175
+ -v "/path/to/schema:/service_root/schema" \
176
+ retold/meadow-integration:latest
177
+ ```
178
+
179
+ ### Mounting a Schema Directory
180
+
181
+ The schema file referenced by `SchemaPath` must be accessible inside the container. Mount it as a volume:
182
+
183
+ ```bash
184
+ docker run \
185
+ -v "/host/path/schema:/service_root/schema" \
186
+ retold/meadow-integration:latest
187
+ ```
188
+
189
+ ## Production Deployment Tips
190
+
191
+ ### Running an Initial Clone
192
+
193
+ ```bash
194
+ docker run --rm \
195
+ --network meadow_backend \
196
+ -v "/config/.meadow.config.json:/service_root/.meadow.config.json" \
197
+ -v "/data/schema:/service_root/schema" \
198
+ retold/meadow-integration:latest \
199
+ node source/cli/Meadow-Integration-CLI-Run.js data-clone --sync_mode Initial
200
+ ```
201
+
202
+ ### Running an Ongoing Sync on a Schedule
203
+
204
+ Use cron or a container orchestrator to run ongoing syncs periodically:
205
+
206
+ ```bash
207
+ docker run --rm \
208
+ --network meadow_backend \
209
+ -v "/config/.meadow.config.json:/service_root/.meadow.config.json" \
210
+ -v "/data/schema:/service_root/schema" \
211
+ retold/meadow-integration:latest \
212
+ node source/cli/Meadow-Integration-CLI-Run.js data-clone --sync_mode Ongoing
213
+ ```
214
+
215
+ ### Using the Post-Run Delay
216
+
217
+ The `--post_run_delay` flag keeps the container alive for a specified number of minutes after the sync completes. This is useful in orchestrated environments where you want to inspect logs before the container exits:
218
+
219
+ ```bash
220
+ docker run --rm \
221
+ --network meadow_backend \
222
+ -v "/config/.meadow.config.json:/service_root/.meadow.config.json" \
223
+ -v "/data/schema:/service_root/schema" \
224
+ retold/meadow-integration:latest \
225
+ node source/cli/Meadow-Integration-CLI-Run.js data-clone --post_run_delay 5
226
+ ```
227
+
228
+ ### Running the REST Server in Docker
229
+
230
+ To run the integration REST API server inside Docker:
231
+
232
+ ```bash
233
+ docker run -d \
234
+ -p 8086:8086 \
235
+ --name meadow-integration-api \
236
+ retold/meadow-integration:latest \
237
+ node source/cli/Meadow-Integration-CLI-Run.js serve --port 8086
238
+ ```
239
+
240
+ ### Tagging and Pushing
241
+
242
+ Helper scripts are provided for image management:
243
+
244
+ ```bash
245
+ # Tag the image
246
+ ./Docker-Tag.sh
247
+
248
+ # Push to a registry
249
+ ./Docker-Push.sh
250
+ ```
251
+
252
+ ### Health Checks
253
+
254
+ When running the REST server, use the Status endpoint as a health check:
255
+
256
+ ```yaml
257
+ services:
258
+ meadow-integration-api:
259
+ image: retold/meadow-integration:latest
260
+ command: node source/cli/Meadow-Integration-CLI-Run.js serve
261
+ ports:
262
+ - "8086:8086"
263
+ healthcheck:
264
+ test: ["CMD", "curl", "-f", "http://localhost:8086/1.0/Status"]
265
+ interval: 30s
266
+ timeout: 10s
267
+ retries: 3
268
+ ```
269
+
270
+ ### Database Connectivity
271
+
272
+ When running inside Docker, make sure the container can reach the database server. Common patterns:
273
+
274
+ - **Same Docker network**: Use the container name as the hostname (e.g., `mysql-server`).
275
+ - **Host machine**: Use `host.docker.internal` (Docker Desktop) or `172.17.0.1` (Linux).
276
+ - **External database**: Use the external hostname or IP.
277
+
278
+ Update the `Destination` configuration accordingly:
279
+
280
+ ```json
281
+ {
282
+ "Destination": {
283
+ "Provider": "MySQL",
284
+ "MySQL": {
285
+ "server": "mysql-server",
286
+ "database": "production_clone"
287
+ }
288
+ }
289
+ }
290
+ ```
@@ -0,0 +1,173 @@
1
+ # Data Clone System Overview
2
+
3
+ The data clone system synchronizes data from a remote Meadow API server to a local relational database. It is designed for offline access, reporting, data warehousing, and building local replicas of Meadow-managed datasets.
4
+
5
+ ## What It Does
6
+
7
+ The data clone system connects to a source Meadow REST API, reads entity records using paginated queries, and writes them into a local MySQL or MSSQL database. Tables are auto-created from the Meadow schema, and indexes are built on GUID and Deleted columns for query performance.
8
+
9
+ ## Why Use It
10
+
11
+ - **Offline access**: Work with your data without a persistent connection to the source API.
12
+ - **Reporting**: Run complex SQL queries against a local database without impacting the production API.
13
+ - **Data warehousing**: Aggregate data from Meadow APIs into a central database for analytics.
14
+ - **Development**: Maintain a local copy of production data for development and testing.
15
+
16
+ ## Key Components
17
+
18
+ ### ConnectionManager
19
+
20
+ Manages database connections for MySQL and MSSQL providers. Handles connection pooling, table creation, and index management.
21
+
22
+ See [connection-manager.md](connection-manager.md) for details.
23
+
24
+ ### RestClient
25
+
26
+ A REST client service (`MeadowCloneRestClient`) that communicates with the source Meadow API. Handles authentication, session management, caching, and paginated entity retrieval via keep-alive HTTP connections.
27
+
28
+ Key capabilities:
29
+ - Session-based authentication (`authenticate` / `deauthenticate`)
30
+ - CRUD operations (`getEntity`, `createEntity`, `updateEntity`, `upsertEntity`, `deleteEntity`)
31
+ - Bulk retrieval with automatic pagination (`getEntitySet`)
32
+ - Built-in LRU object cache per entity type (30-second TTL, 10,000 entries)
33
+
34
+ ### Sync
35
+
36
+ The `MeadowSync` service orchestrates the overall synchronization. It loads a Meadow schema, creates `SyncEntity` instances for each entity, and runs them in sequence.
37
+
38
+ ### SyncEntity (Initial and Ongoing)
39
+
40
+ Two sync strategies are available:
41
+
42
+ - **MeadowSyncEntityInitial**: Full clone using ID-based pagination. Only creates new records.
43
+ - **MeadowSyncEntityOngoing**: Incremental sync using UpdateDate comparison. Creates new records and updates changed ones.
44
+
45
+ See [sync-modes.md](sync-modes.md) for a detailed comparison.
46
+
47
+ ## Supported Providers
48
+
49
+ | Provider | Module | Default Port |
50
+ |----------|--------|-------------|
51
+ | MySQL | `meadow-connection-mysql` | 3306 |
52
+ | MSSQL | `meadow-connection-mssql` | 1433 |
53
+
54
+ ## Sync Modes
55
+
56
+ | Mode | Strategy | Creates | Updates | Use Case |
57
+ |------|----------|---------|---------|----------|
58
+ | Initial | ID-based pagination from max local ID | Yes | No | First-time full clone |
59
+ | Ongoing | UpdateDate comparison across all records | Yes | Yes | Incremental sync after initial clone |
60
+
61
+ ## Running a Data Clone
62
+
63
+ ### Via CLI
64
+
65
+ ```bash
66
+ mdwint data-clone \
67
+ --api_server "https://api.example.com/1.0/" \
68
+ --api_username "admin" \
69
+ --api_password "secret" \
70
+ --db_host "127.0.0.1" \
71
+ --db_name "local_clone" \
72
+ --schema_path "./schema/Model-Extended.json" \
73
+ --sync_mode "Initial"
74
+ ```
75
+
76
+ ### Via Configuration File
77
+
78
+ Create a `.meadow.config.json` file (see [configuration.md](configuration.md)) and run:
79
+
80
+ ```bash
81
+ mdwint data-clone
82
+ ```
83
+
84
+ ### Programmatically
85
+
86
+ ```javascript
87
+ const meadowIntegration = require('meadow-integration');
88
+ const libFable = require('fable');
89
+
90
+ const fable = new libFable({});
91
+
92
+ // Register services
93
+ fable.serviceManager.addServiceType('MeadowCloneRestClient', meadowIntegration.CloneRestClient);
94
+ fable.serviceManager.instantiateServiceProvider('MeadowCloneRestClient', {
95
+ ServerURL: 'https://api.example.com/1.0/',
96
+ UserID: 'admin',
97
+ Password: 'secret'
98
+ });
99
+
100
+ fable.serviceManager.addServiceType('MeadowConnectionManager', meadowIntegration.ConnectionManager);
101
+ fable.serviceManager.instantiateServiceProvider('MeadowConnectionManager', {
102
+ Provider: 'MySQL',
103
+ MySQL: {
104
+ server: '127.0.0.1',
105
+ port: 3306,
106
+ user: 'root',
107
+ password: '',
108
+ database: 'local_clone',
109
+ connectionLimit: 20
110
+ }
111
+ });
112
+
113
+ fable.serviceManager.addServiceType('MeadowSync', meadowIntegration.Sync);
114
+
115
+ // Authenticate, connect, load schema, sync
116
+ fable.MeadowCloneRestClient.authenticate((pAuthError) => {
117
+ fable.MeadowConnectionManager.connect((pConnectError, pPool) => {
118
+ fable.serviceManager.instantiateServiceProvider('MeadowSync', {
119
+ ConnectionPool: pPool,
120
+ PageSize: 100
121
+ });
122
+ fable.MeadowSync.SyncMode = 'Initial';
123
+ const schema = require('./schema/Model-Extended.json');
124
+ fable.MeadowSync.loadMeadowSchema(schema, (pLoadError) => {
125
+ fable.MeadowSync.syncAll((pSyncError) => {
126
+ console.log('Sync complete!');
127
+ });
128
+ });
129
+ });
130
+ });
131
+ ```
132
+
133
+ ## Architecture Diagram
134
+
135
+ ```
136
+ +-------------------+ HTTPS/HTTP +-------------------+
137
+ | Source Meadow | <-------------------> | RestClient |
138
+ | API Server | GET /Entity/... | (MeadowClone |
139
+ +-------------------+ Session Auth | RestClient) |
140
+ +--------+----------+
141
+ |
142
+ v
143
+ +-------------------+
144
+ | MeadowSync |
145
+ | (Orchestrator) |
146
+ +--------+----------+
147
+ |
148
+ +------------+------------+
149
+ | |
150
+ +------+------+ +-------+-------+
151
+ | SyncEntity | | SyncEntity |
152
+ | Initial | | Ongoing |
153
+ +------+------+ +-------+-------+
154
+ | |
155
+ v v
156
+ +------+-------------------------+------+
157
+ | ConnectionManager |
158
+ | (MySQL / MSSQL) |
159
+ +---------------------------------------+
160
+ |
161
+ v
162
+ +-------------------+
163
+ | Local Database |
164
+ | (MySQL / MSSQL) |
165
+ +-------------------+
166
+ ```
167
+
168
+ ## Related Documentation
169
+
170
+ - [Connection Manager](connection-manager.md) -- Database connection setup and index creation
171
+ - [Sync Modes](sync-modes.md) -- Detailed comparison of Initial vs Ongoing sync
172
+ - [Configuration](configuration.md) -- Full `.meadow.config.json` reference
173
+ - [Docker Deployment](docker.md) -- Running data-clone in Docker
@@ -0,0 +1,186 @@
1
+ # Sync Modes
2
+
3
+ The data clone system supports two sync modes: **Initial** and **Ongoing**. Each mode uses a different strategy for determining which records to fetch and how to write them to the local database.
4
+
5
+ ## Initial Sync
6
+
7
+ **Purpose**: Perform a full clone of data from the source API to the local database.
8
+
9
+ ### Strategy
10
+
11
+ 1. **Read local max ID**: Query the local database for the maximum value of the entity's `DefaultIdentifier` (primary key).
12
+ 2. **Read local count**: Count the total records in the local database.
13
+ 3. **Read server max ID**: Query the source API for the maximum ID via `GET /{Entity}/Max/{DefaultIdentifier}`.
14
+ 4. **Read server count**: Query the source API for the total count via `GET /{Entity}s/Count`.
15
+ 5. **Estimate work**: Calculate the estimated number of new records as `Server.RecordCount - Local.RecordCount`.
16
+ 6. **Generate paginated URLs**: Build a list of URL partials using the filter `FBV~{ID}~GT~{LocalMaxID}~FSF~{ID}~ASC~ASC` with pagination offsets.
17
+ 7. **Fetch and insert**: For each page of records:
18
+ - Check if the record exists locally by its ID.
19
+ - If the record does not exist, marshal and create it.
20
+ - If the record already exists, skip it.
21
+
22
+ ### Key Characteristics
23
+
24
+ | Aspect | Behavior |
25
+ |--------|----------|
26
+ | Record creation | Yes |
27
+ | Record updates | No |
28
+ | Pagination | All pages generated upfront based on server count |
29
+ | Filter | Records with ID greater than local max |
30
+ | Sort | Ascending by DefaultIdentifier |
31
+ | Concurrency | 1 page at a time, 5 records in parallel per page |
32
+ | Progress tracking | Tracks estimated vs completed record count |
33
+
34
+ ### When to Use
35
+
36
+ - First-time data cloning from a source API.
37
+ - Rebuilding a local database from scratch.
38
+ - When you only need new records and do not need to detect changes to existing records.
39
+
40
+ ### Create Behavior
41
+
42
+ When creating records during Initial sync, the following flags are set on the Meadow query:
43
+
44
+ - `setDisableAutoIdentity(true)` -- Preserves the original ID from the source.
45
+ - `setDisableAutoDateStamp(true)` -- Preserves original CreateDate/UpdateDate values.
46
+ - `setDisableAutoUserStamp(true)` -- Preserves original CreatingIDUser/UpdatingIDUser values.
47
+ - `setDisableDeleteTracking(true)` -- Preserves the original Deleted flag.
48
+ - `AllowIdentityInsert = true` -- Allows inserting records with explicit ID values.
49
+
50
+ ---
51
+
52
+ ## Ongoing Sync
53
+
54
+ **Purpose**: Incrementally sync new and changed records from the source API.
55
+
56
+ ### Strategy
57
+
58
+ 1. **Check for UpdateDate column**: Verify that the entity schema contains an `UpdateDate` column.
59
+ 2. **Read local max ID**: Same as Initial.
60
+ 3. **Read local max UpdateDate**: Query for the most recent `UpdateDate` in the local database.
61
+ 4. **Read local count**: Same as Initial.
62
+ 5. **Read server max ID**: Same as Initial.
63
+ 6. **Read server max UpdateDate**: Query the source API via `GET /{Entity}/Max/UpdateDate`.
64
+ 7. **Read server count**: Same as Initial.
65
+ 8. **Estimate request count**: Calculate `Math.ceil(Server.RecordCount / PageSize)`.
66
+ 9. **Recursive fetch**: Using a recursive `anticipate` pattern:
67
+ - Fetch a page of records sorted by ID ascending, starting from `LastRequestedID`.
68
+ - For each record:
69
+ - If it does not exist locally, create it.
70
+ - If it exists, compare `UpdateDate` values. If the difference exceeds 5 milliseconds, update the local record.
71
+ - If the dates are within 5ms, skip the record.
72
+ - After processing the page, recursively add the next page fetch to the anticipate chain.
73
+ - Continue until all estimated pages have been processed.
74
+
75
+ ### Key Characteristics
76
+
77
+ | Aspect | Behavior |
78
+ |--------|----------|
79
+ | Record creation | Yes |
80
+ | Record updates | Yes |
81
+ | Pagination | Recursive, one page at a time |
82
+ | Filter | Records with ID greater than `LastRequestedID` (starts at 0) |
83
+ | Sort | Ascending by DefaultIdentifier |
84
+ | UpdateDate threshold | 5 milliseconds (changes smaller than 5ms are ignored) |
85
+ | Concurrency | Sequential pages, sequential records within each page |
86
+ | Progress tracking | Tracks request count against estimated total |
87
+
88
+ ### Recursive Anticipate Pattern
89
+
90
+ The Ongoing sync uses a recursive pattern where each page fetch, upon completion, adds another page fetch to the `anticipate` queue. This ensures that the `LastRequestedID` advances as records are processed:
91
+
92
+ ```
93
+ Page 1 (IDs 1-100) -> process -> update LastRequestedID to 100
94
+ -> add Page 2 (IDs > 100) to anticipate queue
95
+ Page 2 (IDs 101-200) -> process -> update LastRequestedID to 200
96
+ -> add Page 3 (IDs > 200) to anticipate queue
97
+ ...
98
+ ```
99
+
100
+ This approach handles cases where the server record count changes during the sync process.
101
+
102
+ ### When to Use
103
+
104
+ - After an Initial sync has been completed.
105
+ - When running periodic sync jobs to keep the local database up to date.
106
+ - When you need to detect and apply changes to existing records.
107
+
108
+ ### Create and Update Behavior
109
+
110
+ **Creating** new records uses the same flags as Initial sync (disabled auto-identity, auto-date, auto-user, and delete tracking with `AllowIdentityInsert`).
111
+
112
+ **Updating** existing records also uses the same disabled flags to preserve the original metadata from the source.
113
+
114
+ ---
115
+
116
+ ## Comparison Table
117
+
118
+ | Feature | Initial | Ongoing |
119
+ |---------|---------|---------|
120
+ | Creates new records | Yes | Yes |
121
+ | Updates existing records | No | Yes |
122
+ | Starting point | Local max ID | ID 0 (scans all records) |
123
+ | Pagination strategy | Pre-computed URL list | Recursive anticipate |
124
+ | UpdateDate comparison | No | Yes (5ms threshold) |
125
+ | Schema requirement | DefaultIdentifier | DefaultIdentifier, UpdateDate |
126
+ | Typical use | First-time clone | Incremental updates |
127
+ | Performance | Fast (skips existing) | Thorough (checks every record) |
128
+
129
+ ## Setting the Sync Mode
130
+
131
+ ### Via CLI
132
+
133
+ ```bash
134
+ mdwint data-clone --sync_mode Initial
135
+ mdwint data-clone --sync_mode Ongoing
136
+ ```
137
+
138
+ ### Via Configuration
139
+
140
+ In `.meadow.config.json`:
141
+
142
+ ```json
143
+ {
144
+ "Sync": {
145
+ "DefaultSyncMode": "Initial"
146
+ }
147
+ }
148
+ ```
149
+
150
+ ### Programmatically
151
+
152
+ ```javascript
153
+ fable.MeadowSync.SyncMode = 'Ongoing';
154
+ ```
155
+
156
+ The sync mode must be set **before** calling `loadMeadowSchema()`, because the mode determines which `SyncEntity` class (Initial or Ongoing) is instantiated for each entity.
157
+
158
+ ## Record Marshaling
159
+
160
+ Both sync modes use the same `marshalRecord` method to prepare source records for local database insertion:
161
+
162
+ - Columns defined in the entity schema are mapped from the source record.
163
+ - `null` and `undefined` values are skipped.
164
+ - Object values are JSON-stringified.
165
+ - `DateTime` values are reformatted to `YYYY-MM-DD HH:mm:ss.SSS` in UTC.
166
+ - Empty string values are skipped for non-DateTime fields.
167
+ - Columns ending in `JSON` are auto-populated from matching object properties (e.g., `ConfigJSON` is populated from `Config` if `Config` is an object).
168
+
169
+ ## Table and Index Creation
170
+
171
+ Both sync modes automatically:
172
+
173
+ 1. Create the database table if it does not exist (via Meadow provider's `createTable`).
174
+ 2. Create a unique index on the GUID column (if present).
175
+ 3. Create a non-unique index on the Deleted column (if present).
176
+
177
+ This happens during the `initialize()` phase before any records are synced.
178
+
179
+ ## Progress Tracking
180
+
181
+ Both modes use the `MeadowOperation` utility class for progress tracking:
182
+
183
+ - **Initial**: Tracks `FullSync-{TableName}` with estimated record count.
184
+ - **Ongoing**: Tracks `UpdateSync-{TableName}` with estimated request count.
185
+
186
+ Progress includes: percent complete, average operation time, and estimated completion time.