meadow 2.0.17 → 2.0.18

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.
@@ -0,0 +1,408 @@
1
+ # Schema
2
+
3
+ > Define your data model once — get validation, defaults, audit tracking, and authorization for free
4
+
5
+ Meadow's schema system is the foundation of every data entity. It defines column structure, validation rules, default object templates, and authorization policies in a single declarative configuration. The schema drives everything from query generation to automatic audit stamping.
6
+
7
+ ## Overview
8
+
9
+ A Meadow schema consists of four complementary parts:
10
+
11
+ ```
12
+ Schema Definition
13
+ ├── Column Schema (array) → Defines columns, types, and sizes
14
+ ├── JSON Schema (object) → Validates objects against JSON Schema v4
15
+ ├── Default Object (object) → Template merged with new records on create
16
+ └── Authorizer (object) → Role-based access control per operation
17
+ ```
18
+
19
+ Each part is optional and can be set independently, but together they provide a complete data model definition.
20
+
21
+ ## Column Schema
22
+
23
+ The column schema is an array of objects that defines the columns in your entity. Each column has a `Column` name, a `Type`, and an optional `Size`.
24
+
25
+ ```javascript
26
+ const tmpSchema =
27
+ [
28
+ { Column: 'IDBook', Type: 'AutoIdentity' },
29
+ { Column: 'GUIDBook', Type: 'AutoGUID' },
30
+ { Column: 'Title', Type: 'String', Size: '255' },
31
+ { Column: 'Author', Type: 'String', Size: '128' },
32
+ { Column: 'PageCount', Type: 'Numeric' },
33
+ { Column: 'Price', Type: 'Decimal', Size: '12,2' },
34
+ { Column: 'InPrint', Type: 'Boolean' },
35
+ { Column: 'PublishDate', Type: 'DateTime' },
36
+ { Column: 'Synopsis', Type: 'Text' },
37
+ { Column: 'CreateDate', Type: 'CreateDate' },
38
+ { Column: 'CreatingIDUser', Type: 'CreateIDUser' },
39
+ { Column: 'UpdateDate', Type: 'UpdateDate' },
40
+ { Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
41
+ { Column: 'DeleteDate', Type: 'DeleteDate' },
42
+ { Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
43
+ { Column: 'Deleted', Type: 'Deleted' }
44
+ ];
45
+
46
+ meadow.setSchema(tmpSchema);
47
+ ```
48
+
49
+ ### Column Types
50
+
51
+ | Type | Description | SQL Mapping | Default Value |
52
+ |------|-------------|-------------|---------------|
53
+ | `AutoIdentity` | Auto-increment primary key | `INT UNSIGNED AUTO_INCREMENT` | N/A (generated) |
54
+ | `AutoGUID` | Automatically generated GUID | `CHAR(36)` | `'00000000-0000-0000-0000-000000000000'` |
55
+ | `String` | Variable-length string | `VARCHAR(Size)` | `''` |
56
+ | `Text` | Long text field | `TEXT` | `null` |
57
+ | `Numeric` | Integer field | `INT` | `0` |
58
+ | `Integer` | Integer field (alias) | `INT` | `0` |
59
+ | `Decimal` | Decimal number | `DECIMAL(precision,scale)` | `0` |
60
+ | `Boolean` | Boolean flag | `INT` / `BOOLEAN` | `0` |
61
+ | `DateTime` | Date/time field | `DATETIME` | `null` |
62
+
63
+ ### Audit Column Types
64
+
65
+ These special types trigger automatic behavior during CRUD operations:
66
+
67
+ | Type | Description | Auto-Populated |
68
+ |------|-------------|----------------|
69
+ | `CreateDate` | Timestamp when record was created | Set to current UTC time on `doCreate` |
70
+ | `CreateIDUser` | User who created the record | Set from `query.IDUser` on `doCreate` |
71
+ | `UpdateDate` | Timestamp of last update | Set to current UTC time on `doUpdate` |
72
+ | `UpdateIDUser` | User who last updated | Set from `query.IDUser` on `doUpdate` |
73
+ | `DeleteDate` | Timestamp of soft deletion | Set to current UTC time on `doDelete` |
74
+ | `DeleteIDUser` | User who soft deleted | Set from `query.IDUser` on `doDelete` |
75
+ | `Deleted` | Soft delete flag | Set to `1` on `doDelete`, `0` on `doUndelete` |
76
+
77
+ ### Column Properties
78
+
79
+ | Property | Required | Description |
80
+ |----------|----------|-------------|
81
+ | `Column` | Yes | The column name in the database |
82
+ | `Type` | Yes | One of the column types listed above |
83
+ | `Size` | No | For `String`: max length (e.g., `'255'`). For `Decimal`: precision and scale (e.g., `'12,2'`) |
84
+
85
+ ## JSON Schema Validation
86
+
87
+ Meadow supports JSON Schema v4 for validating objects before they reach the database. Validation uses the [is-my-json-valid](https://github.com/mafintosh/is-my-json-valid) library with greedy mode enabled.
88
+
89
+ ```javascript
90
+ meadow.setJsonSchema({
91
+ title: 'Book',
92
+ description: 'A book in the catalog',
93
+ type: 'object',
94
+ properties:
95
+ {
96
+ IDBook:
97
+ {
98
+ description: 'The unique identifier for a book',
99
+ type: 'integer'
100
+ },
101
+ Title:
102
+ {
103
+ description: 'The book title',
104
+ type: 'string'
105
+ },
106
+ Author:
107
+ {
108
+ description: 'The book author',
109
+ type: 'string'
110
+ },
111
+ PageCount:
112
+ {
113
+ description: 'Number of pages',
114
+ type: 'integer'
115
+ }
116
+ },
117
+ required: ['Title']
118
+ });
119
+ ```
120
+
121
+ ### Validating Objects
122
+
123
+ ```javascript
124
+ // Valid object
125
+ const tmpResult = meadow.schemaFull.validateObject(
126
+ { IDBook: 1, Title: 'Dune', Author: 'Frank Herbert', PageCount: 412 });
127
+ // { Valid: true, Errors: [] }
128
+
129
+ // Invalid object (missing required field)
130
+ const tmpResult = meadow.schemaFull.validateObject(
131
+ { IDBook: 2, Author: 'Isaac Asimov' });
132
+ // { Valid: false, Errors: [...] }
133
+ ```
134
+
135
+ The `validateObject` method returns an object with:
136
+
137
+ | Property | Type | Description |
138
+ |----------|------|-------------|
139
+ | `Valid` | boolean | Whether the object passed validation |
140
+ | `Errors` | array | Validation errors (empty when valid) |
141
+
142
+ ## Default Object
143
+
144
+ The default object is a template that gets merged with submitted record data during `doCreate` operations. This ensures every column has a value even if the caller doesn't provide one.
145
+
146
+ ```javascript
147
+ meadow.setDefault({
148
+ IDBook: null,
149
+ GUIDBook: '',
150
+ Title: 'Untitled',
151
+ Author: 'Unknown',
152
+ PageCount: 0,
153
+ Price: 0,
154
+ InPrint: false,
155
+ Synopsis: null,
156
+ CreateDate: false,
157
+ CreatingIDUser: 0,
158
+ UpdateDate: false,
159
+ UpdatingIDUser: 0,
160
+ Deleted: 0,
161
+ DeletingIDUser: 0,
162
+ DeleteDate: false
163
+ });
164
+ ```
165
+
166
+ ### How Default Merging Works
167
+
168
+ During `doCreate`, Meadow merges the default object with the submitted record:
169
+
170
+ ```javascript
171
+ // Internally, Meadow does:
172
+ // finalRecord = extend({}, defaultObject, submittedRecord)
173
+
174
+ // If you create with:
175
+ meadow.doCreate(
176
+ meadow.query.addRecord({ Title: 'Neuromancer', Author: 'William Gibson' }),
177
+ (pError, pCreateQuery, pReadQuery, pRecord) =>
178
+ {
179
+ // pRecord will contain:
180
+ // {
181
+ // IDBook: <auto-generated>,
182
+ // GUIDBook: <auto-generated>,
183
+ // Title: 'Neuromancer', ← from submitted record
184
+ // Author: 'William Gibson', ← from submitted record
185
+ // PageCount: 0, ← from default object
186
+ // Price: 0, ← from default object
187
+ // InPrint: false, ← from default object
188
+ // ...
189
+ // }
190
+ });
191
+ ```
192
+
193
+ ## Authorizer
194
+
195
+ The authorizer defines role-based access control for each CRUD operation. Roles map to permission rules that Meadow-Endpoints uses to gate API access.
196
+
197
+ ```javascript
198
+ meadow.setAuthorizer({
199
+ Unauthenticated:
200
+ {
201
+ Create: 'Deny',
202
+ Read: 'Deny',
203
+ Reads: 'Deny',
204
+ Update: 'Deny',
205
+ Delete: 'Deny',
206
+ Count: 'Deny',
207
+ Schema: 'Deny'
208
+ },
209
+ User:
210
+ {
211
+ Create: 'Allow',
212
+ Read: 'Allow',
213
+ Reads: 'Allow',
214
+ Update: 'Mine',
215
+ Delete: 'Mine',
216
+ Count: 'Allow',
217
+ Schema: 'Allow'
218
+ },
219
+ Administrator:
220
+ {
221
+ Create: 'Allow',
222
+ Read: 'Allow',
223
+ Reads: 'Allow',
224
+ Update: 'Allow',
225
+ Delete: 'Allow',
226
+ Count: 'Allow',
227
+ Schema: 'Allow'
228
+ }
229
+ });
230
+ ```
231
+
232
+ ### Permission Values
233
+
234
+ | Value | Description |
235
+ |-------|-------------|
236
+ | `'Allow'` | Full access to the operation |
237
+ | `'Deny'` | No access to the operation |
238
+ | Custom string | Contextual filter (e.g., `'Mine'`, `'MyCustomer'`) applied by Meadow-Endpoints behaviors |
239
+
240
+ ### Operations
241
+
242
+ | Operation | CRUD Method | Description |
243
+ |-----------|-------------|-------------|
244
+ | `Create` | `doCreate` | Insert new records |
245
+ | `Read` | `doRead` | Retrieve a single record |
246
+ | `Reads` | `doReads` | Retrieve multiple records |
247
+ | `ReadsBy` | `doReads` | Retrieve by alternate key |
248
+ | `ReadMax` | `doRead` | Retrieve max value |
249
+ | `ReadSelectList` | `doReads` | Retrieve select lists |
250
+ | `Update` | `doUpdate` | Modify existing records |
251
+ | `Delete` | `doDelete` | Soft delete records |
252
+ | `Count` | `doCount` | Count records |
253
+ | `CountBy` | `doCount` | Count by alternate key |
254
+ | `Schema` | — | Access schema metadata |
255
+ | `Validate` | — | Validate objects |
256
+ | `New` | — | Generate default objects |
257
+
258
+ ### Default Role Names
259
+
260
+ Meadow uses a default set of role names (configurable via `MeadowRoleNames` in Fable settings):
261
+
262
+ ```javascript
263
+ ['Unauthenticated', 'User', 'Manager', 'Director', 'Executive', 'Administrator']
264
+ ```
265
+
266
+ ## Loading from a Package File
267
+
268
+ The most common way to define a complete schema is through a package JSON file that bundles all four parts together.
269
+
270
+ ### Package File Format
271
+
272
+ ```json
273
+ {
274
+ "Scope": "Book",
275
+ "Domain": "Library",
276
+ "DefaultIdentifier": "IDBook",
277
+ "Schema": [
278
+ { "Column": "IDBook", "Type": "AutoIdentity" },
279
+ { "Column": "GUIDBook", "Type": "AutoGUID" },
280
+ { "Column": "Title", "Type": "String", "Size": "255" },
281
+ { "Column": "Author", "Type": "String", "Size": "128" },
282
+ { "Column": "CreateDate", "Type": "CreateDate" },
283
+ { "Column": "CreatingIDUser", "Type": "CreateIDUser" },
284
+ { "Column": "UpdateDate", "Type": "UpdateDate" },
285
+ { "Column": "UpdatingIDUser", "Type": "UpdateIDUser" },
286
+ { "Column": "DeleteDate", "Type": "DeleteDate" },
287
+ { "Column": "DeletingIDUser", "Type": "DeleteIDUser" },
288
+ { "Column": "Deleted", "Type": "Deleted" }
289
+ ],
290
+ "JsonSchema": {
291
+ "title": "Book",
292
+ "type": "object",
293
+ "properties": {
294
+ "IDBook": { "type": "integer" },
295
+ "Title": { "type": "string" },
296
+ "Author": { "type": "string" }
297
+ },
298
+ "required": ["Title"]
299
+ },
300
+ "DefaultObject": {
301
+ "IDBook": null,
302
+ "GUIDBook": "",
303
+ "Title": "Untitled",
304
+ "Author": "Unknown"
305
+ },
306
+ "Authorization": {
307
+ "Unauthenticated": { "Read": "Deny", "Create": "Deny" },
308
+ "User": { "Read": "Allow", "Create": "Allow" },
309
+ "Administrator": { "Read": "Allow", "Create": "Allow" }
310
+ }
311
+ }
312
+ ```
313
+
314
+ ### Loading from File
315
+
316
+ ```javascript
317
+ const meadow = require('meadow')
318
+ .new(libFable)
319
+ .loadFromPackage(__dirname + '/Book.json');
320
+ ```
321
+
322
+ The `loadFromPackage` method reads the JSON file and calls:
323
+ - `setScope(package.Scope)`
324
+ - `setDefaultIdentifier(package.DefaultIdentifier)`
325
+ - `setSchema(package.Schema)`
326
+ - `setJsonSchema(package.JsonSchema)`
327
+ - `setDefault(package.DefaultObject)`
328
+ - `setAuthorizer(package.Authorization)`
329
+
330
+ ### Loading from Object
331
+
332
+ If you already have the package definition as a JavaScript object:
333
+
334
+ ```javascript
335
+ const meadow = require('meadow')
336
+ .new(libFable)
337
+ .loadFromPackageObject({
338
+ Scope: 'Book',
339
+ DefaultIdentifier: 'IDBook',
340
+ Schema:
341
+ [
342
+ { Column: 'IDBook', Type: 'AutoIdentity' },
343
+ { Column: 'Title', Type: 'String', Size: '255' }
344
+ ],
345
+ DefaultObject:
346
+ {
347
+ IDBook: null,
348
+ Title: 'Untitled'
349
+ }
350
+ });
351
+ ```
352
+
353
+ ### Package Properties
354
+
355
+ | Property | Required | Description |
356
+ |----------|----------|-------------|
357
+ | `Scope` | Yes | Entity name (e.g., `'Book'`) |
358
+ | `Domain` | No | Domain grouping for organization |
359
+ | `DefaultIdentifier` | Yes | Primary key column name (e.g., `'IDBook'`) |
360
+ | `Schema` | Yes | Column schema array |
361
+ | `JsonSchema` | No | JSON Schema v4 for validation |
362
+ | `DefaultObject` | No | Default values template |
363
+ | `Authorization` | No | Role-based access control rules |
364
+
365
+ ## API Reference
366
+
367
+ ### Meadow Schema Methods
368
+
369
+ All schema methods are chainable and return the Meadow instance.
370
+
371
+ | Method | Parameters | Description |
372
+ |--------|------------|-------------|
373
+ | `setSchema(pSchema)` | Array of column definitions | Set the column schema |
374
+ | `setJsonSchema(pJsonSchema)` | JSON Schema v4 object | Set validation schema |
375
+ | `setDefault(pDefault)` | Default values object | Set default object template |
376
+ | `setAuthorizer(pAuthorizer)` | Authorization rules object | Set role-based access rules |
377
+ | `loadFromPackage(pPath)` | File path to JSON | Load all schema parts from a file |
378
+ | `loadFromPackageObject(pObject)` | Package object | Load all schema parts from an object |
379
+ | `validateObject(pObject)` | Object to validate | Validate against JSON Schema |
380
+
381
+ ### Meadow Schema Properties
382
+
383
+ | Property | Type | Description |
384
+ |----------|------|-------------|
385
+ | `schema` | array | The column definition array |
386
+ | `schemaFull` | MeadowSchema | The full schema object with all methods |
387
+ | `jsonSchema` | object | The JSON Schema v4 definition |
388
+ | `defaultIdentifier` | string | The primary key column name |
389
+ | `defaultGUIdentifier` | string | The GUID column name (derived from identifier) |
390
+
391
+ ## Provider Synchronization
392
+
393
+ When you call `setSchema()`, Meadow automatically synchronizes the schema with the active provider. This is important for providers like ALASQL that create tables dynamically based on the schema definition.
394
+
395
+ ```javascript
396
+ // Internally, Meadow calls:
397
+ // provider.setSchema(scope, schema, defaultIdentifier, defaultGUIdentifier)
398
+
399
+ // This means you can change schemas at runtime:
400
+ meadow.setSchema(updatedSchema);
401
+ // The provider is automatically updated
402
+ ```
403
+
404
+ ## Related Documentation
405
+
406
+ - [Query Overview](query/README.md) — How queries interact with schema-defined columns
407
+ - [Providers Overview](providers/README.md) — How providers use schema for SQL generation
408
+ - [Create Operation](query/create.md) — How default objects and auto-stamps work during create
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meadow",
3
- "version": "2.0.17",
3
+ "version": "2.0.18",
4
4
  "description": "A data access library.",
5
5
  "main": "source/Meadow.js",
6
6
  "scripts": {
@@ -11,7 +11,18 @@
11
11
  "build": "./node_modules/.bin/gulp build --full-paths",
12
12
  "docker-dev-build": "docker build ./ -f Dockerfile_LUXURYCode -t retold/meadow:local",
13
13
  "docker-dev-run": "docker run -it -d --name meadow-dev -p 12342:8080 -p 12106:3306 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/meadow\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" retold/meadow:local",
14
- "docker-dev-shell": "docker exec -it meadow-dev /bin/bash"
14
+ "docker-dev-shell": "docker exec -it meadow-dev /bin/bash",
15
+ "docker-mysql-start": "./scripts/mysql-test-db.sh start",
16
+ "docker-mysql-stop": "./scripts/mysql-test-db.sh stop",
17
+ "docker-mysql-status": "./scripts/mysql-test-db.sh status",
18
+ "test-mysql": "./scripts/mysql-test-db.sh start && ./node_modules/mocha/bin/_mocha -u tdd --exit -R spec --grep Meadow-Provider-MySQL",
19
+ "test-mysql-all": "./scripts/mysql-test-db.sh start && ./node_modules/mocha/bin/_mocha -u tdd --exit -R spec",
20
+ "docker-mssql-start": "./scripts/mssql-test-db.sh start",
21
+ "docker-mssql-stop": "./scripts/mssql-test-db.sh stop",
22
+ "docker-mssql-status": "./scripts/mssql-test-db.sh status",
23
+ "test-mssql": "./scripts/mssql-test-db.sh start && ./node_modules/mocha/bin/_mocha -u tdd --exit -R spec --grep Meadow-Provider-MSSQL",
24
+ "test-mssql-all": "./scripts/mssql-test-db.sh start && ./node_modules/mocha/bin/_mocha -u tdd --exit -R spec",
25
+ "test-all-providers": "./scripts/mysql-test-db.sh start && ./scripts/mssql-test-db.sh start && ./node_modules/mocha/bin/_mocha -u tdd --exit -R spec"
15
26
  },
16
27
  "mocha": {
17
28
  "diff": true,
@@ -47,9 +58,10 @@
47
58
  "homepage": "https://github.com/stevenvelozo/meadow",
48
59
  "devDependencies": {
49
60
  "alasql": "^4.1.10",
61
+ "better-sqlite3": "^12.6.2",
50
62
  "browserify": "^17.0.0",
51
63
  "chai": "4.3.10",
52
- "fable": "^3.1.18",
64
+ "fable": "^3.1.51",
53
65
  "gulp": "^4.0.2",
54
66
  "gulp-babel": "^8.0.0",
55
67
  "gulp-sourcemaps": "^3.0.0",
@@ -57,6 +69,7 @@
57
69
  "gulp-util": "^3.0.8",
58
70
  "meadow-connection-mssql": "^1.0.9",
59
71
  "meadow-connection-mysql": "^1.0.4",
72
+ "meadow-connection-sqlite": "file:../meadow-connection-sqlite",
60
73
  "mocha": "10.2.0",
61
74
  "mysql2": "^3.6.3",
62
75
  "nyc": "^15.1.0",
@@ -0,0 +1,111 @@
1
+ #!/bin/bash
2
+ # MSSQL Test Database Management Script
3
+ #
4
+ # Usage:
5
+ # ./scripts/mssql-test-db.sh start - Start MSSQL container and wait for readiness
6
+ # ./scripts/mssql-test-db.sh stop - Stop and remove the container
7
+ # ./scripts/mssql-test-db.sh status - Check if the container is running
8
+ #
9
+ # The container settings match the test configuration in
10
+ # test/Meadow-Provider-MSSQL_tests.js:
11
+ # Host: 127.0.0.1, Port: 14333, User: sa
12
+ # Password: 1234567890abc., Database: bookstore
13
+
14
+ CONTAINER_NAME="meadow-mssql-test"
15
+ SA_PASSWORD="1234567890abc."
16
+ MSSQL_DATABASE="bookstore"
17
+ MSSQL_PORT="14333"
18
+ MSSQL_IMAGE="mcr.microsoft.com/mssql/server:2022-latest"
19
+
20
+ start_mssql() {
21
+ # Check if container already exists
22
+ if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
23
+ if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
24
+ echo "MSSQL test container is already running."
25
+ return 0
26
+ else
27
+ echo "Removing stopped container..."
28
+ docker rm "${CONTAINER_NAME}" > /dev/null 2>&1
29
+ fi
30
+ fi
31
+
32
+ echo "Starting MSSQL test container..."
33
+ docker run -d \
34
+ --name "${CONTAINER_NAME}" \
35
+ -e ACCEPT_EULA=Y \
36
+ -e "MSSQL_SA_PASSWORD=${SA_PASSWORD}" \
37
+ -p "${MSSQL_PORT}:1433" \
38
+ "${MSSQL_IMAGE}"
39
+
40
+ if [ $? -ne 0 ]; then
41
+ echo "ERROR: Failed to start MSSQL container."
42
+ exit 1
43
+ fi
44
+
45
+ echo "Waiting for MSSQL to be ready..."
46
+ RETRIES=30
47
+ until docker exec "${CONTAINER_NAME}" /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "${SA_PASSWORD}" -C -Q "SELECT 1" > /dev/null 2>&1; do
48
+ RETRIES=$((RETRIES - 1))
49
+ if [ $RETRIES -le 0 ]; then
50
+ echo "ERROR: MSSQL failed to become ready in time."
51
+ docker logs "${CONTAINER_NAME}" 2>&1 | tail -20
52
+ exit 1
53
+ fi
54
+ echo " ...waiting (${RETRIES} retries left)"
55
+ sleep 2
56
+ done
57
+
58
+ # Create the bookstore database
59
+ echo "Creating database '${MSSQL_DATABASE}'..."
60
+ docker exec "${CONTAINER_NAME}" /opt/mssql-tools18/bin/sqlcmd \
61
+ -S localhost -U sa -P "${SA_PASSWORD}" -C \
62
+ -Q "IF NOT EXISTS(SELECT * FROM sys.databases WHERE name = '${MSSQL_DATABASE}') BEGIN CREATE DATABASE [${MSSQL_DATABASE}] END"
63
+
64
+ echo ""
65
+ echo "MSSQL test database is ready!"
66
+ echo " Container: ${CONTAINER_NAME}"
67
+ echo " Host: 127.0.0.1:${MSSQL_PORT}"
68
+ echo " User: sa"
69
+ echo " Password: ${SA_PASSWORD}"
70
+ echo " Database: ${MSSQL_DATABASE}"
71
+ echo ""
72
+ echo "Run tests with: npm test"
73
+ }
74
+
75
+ stop_mssql() {
76
+ if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
77
+ echo "Stopping and removing MSSQL test container..."
78
+ docker stop "${CONTAINER_NAME}" > /dev/null 2>&1
79
+ docker rm "${CONTAINER_NAME}" > /dev/null 2>&1
80
+ echo "MSSQL test container removed."
81
+ else
82
+ echo "No MSSQL test container found."
83
+ fi
84
+ }
85
+
86
+ status_mssql() {
87
+ if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
88
+ echo "MSSQL test container is running."
89
+ docker ps --filter "name=${CONTAINER_NAME}" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
90
+ elif docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
91
+ echo "MSSQL test container exists but is stopped."
92
+ else
93
+ echo "MSSQL test container is not running."
94
+ fi
95
+ }
96
+
97
+ case "${1}" in
98
+ start)
99
+ start_mssql
100
+ ;;
101
+ stop)
102
+ stop_mssql
103
+ ;;
104
+ status)
105
+ status_mssql
106
+ ;;
107
+ *)
108
+ echo "Usage: $0 {start|stop|status}"
109
+ exit 1
110
+ ;;
111
+ esac
@@ -0,0 +1,108 @@
1
+ #!/bin/bash
2
+ # MySQL Test Database Management Script
3
+ #
4
+ # Usage:
5
+ # ./scripts/mysql-test-db.sh start - Start MySQL container and wait for readiness
6
+ # ./scripts/mysql-test-db.sh stop - Stop and remove the container
7
+ # ./scripts/mysql-test-db.sh status - Check if the container is running
8
+ #
9
+ # The container settings match the test configuration in
10
+ # test/Meadow-Provider-MySQL_tests.js:
11
+ # Host: localhost, Port: 3306, User: root
12
+ # Password: 123456789, Database: bookstore
13
+
14
+ CONTAINER_NAME="meadow-mysql-test"
15
+ MYSQL_ROOT_PASSWORD="123456789"
16
+ MYSQL_DATABASE="bookstore"
17
+ MYSQL_PORT="3306"
18
+ MYSQL_IMAGE="mysql:8.0"
19
+
20
+ start_mysql() {
21
+ # Check if container already exists
22
+ if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
23
+ if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
24
+ echo "MySQL test container is already running."
25
+ return 0
26
+ else
27
+ echo "Removing stopped container..."
28
+ docker rm "${CONTAINER_NAME}" > /dev/null 2>&1
29
+ fi
30
+ fi
31
+
32
+ echo "Starting MySQL test container..."
33
+ docker run -d \
34
+ --name "${CONTAINER_NAME}" \
35
+ -e MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD}" \
36
+ -e MYSQL_DATABASE="${MYSQL_DATABASE}" \
37
+ -p "${MYSQL_PORT}:3306" \
38
+ "${MYSQL_IMAGE}" \
39
+ --default-authentication-plugin=mysql_native_password \
40
+ --character-set-server=utf8mb4 \
41
+ --collation-server=utf8mb4_unicode_ci
42
+
43
+ if [ $? -ne 0 ]; then
44
+ echo "ERROR: Failed to start MySQL container."
45
+ exit 1
46
+ fi
47
+
48
+ echo "Waiting for MySQL to be ready..."
49
+ RETRIES=30
50
+ until docker exec "${CONTAINER_NAME}" mysqladmin ping -h localhost -u root -p"${MYSQL_ROOT_PASSWORD}" --silent 2>/dev/null; do
51
+ RETRIES=$((RETRIES - 1))
52
+ if [ $RETRIES -le 0 ]; then
53
+ echo "ERROR: MySQL failed to become ready in time."
54
+ docker logs "${CONTAINER_NAME}" 2>&1 | tail -20
55
+ exit 1
56
+ fi
57
+ echo " ...waiting (${RETRIES} retries left)"
58
+ sleep 2
59
+ done
60
+
61
+ echo ""
62
+ echo "MySQL test database is ready!"
63
+ echo " Container: ${CONTAINER_NAME}"
64
+ echo " Host: localhost:${MYSQL_PORT}"
65
+ echo " User: root"
66
+ echo " Password: ${MYSQL_ROOT_PASSWORD}"
67
+ echo " Database: ${MYSQL_DATABASE}"
68
+ echo ""
69
+ echo "Run tests with: npm test"
70
+ }
71
+
72
+ stop_mysql() {
73
+ if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
74
+ echo "Stopping and removing MySQL test container..."
75
+ docker stop "${CONTAINER_NAME}" > /dev/null 2>&1
76
+ docker rm "${CONTAINER_NAME}" > /dev/null 2>&1
77
+ echo "MySQL test container removed."
78
+ else
79
+ echo "No MySQL test container found."
80
+ fi
81
+ }
82
+
83
+ status_mysql() {
84
+ if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
85
+ echo "MySQL test container is running."
86
+ docker ps --filter "name=${CONTAINER_NAME}" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
87
+ elif docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
88
+ echo "MySQL test container exists but is stopped."
89
+ else
90
+ echo "MySQL test container is not running."
91
+ fi
92
+ }
93
+
94
+ case "${1}" in
95
+ start)
96
+ start_mysql
97
+ ;;
98
+ stop)
99
+ stop_mysql
100
+ ;;
101
+ status)
102
+ status_mysql
103
+ ;;
104
+ *)
105
+ echo "Usage: $0 {start|stop|status}"
106
+ exit 1
107
+ ;;
108
+ esac
package/source/Meadow.js CHANGED
@@ -136,6 +136,7 @@ var Meadow = function()
136
136
  'MeadowEndpoints': require(`./providers/Meadow-Provider-MeadowEndpoints.js`),
137
137
  'MySQL': require(`./providers/Meadow-Provider-MySQL.js`),
138
138
  'MSSQL': require(`./providers/Meadow-Provider-MSSQL.js`),
139
+ 'SQLite': require(`./providers/Meadow-Provider-SQLite.js`),
139
140
  'None': require(`./providers/Meadow-Provider-None.js`),
140
141
  });
141
142
  var setProvider = function(pProviderName)