meadow 2.0.16 → 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.
- package/.config/configstore/update-notifier-npm.json +1 -1
- package/Dockerfile_LUXURYCode +1 -42
- package/README.md +174 -112
- package/docs/README.md +276 -0
- package/docs/_sidebar.md +38 -0
- package/docs/providers/README.md +253 -0
- package/docs/providers/alasql.md +271 -0
- package/docs/providers/mssql.md +296 -0
- package/docs/providers/mysql.md +260 -0
- package/docs/providers/sqlite.md +173 -0
- package/docs/query/README.md +175 -0
- package/docs/query/count.md +228 -0
- package/docs/query/create.md +226 -0
- package/docs/query/delete.md +264 -0
- package/docs/query/read.md +350 -0
- package/docs/query/update.md +250 -0
- package/docs/schema/README.md +408 -0
- package/package.json +16 -3
- package/scripts/mssql-test-db.sh +111 -0
- package/scripts/mysql-test-db.sh +108 -0
- package/source/Meadow.js +1 -0
- package/source/providers/Meadow-Provider-MSSQL.js +21 -0
- package/source/providers/Meadow-Provider-SQLite.js +235 -155
- package/test/Meadow-Provider-SQLite-AnimalReadQuery.sql +5 -0
- package/test/Meadow-Provider-SQLite_tests.js +931 -0
package/Dockerfile_LUXURYCode
CHANGED
|
@@ -26,47 +26,6 @@ RUN ( sudo mysqld_safe --skip-grant-tables --skip-networking & ) && sleep 5 &&
|
|
|
26
26
|
# Import the initial database
|
|
27
27
|
RUN sudo service mariadb restart && sleep 5 && mysql -u root -p"123456789" -e "CREATE DATABASE FableTest;"
|
|
28
28
|
|
|
29
|
-
RUN echo "...installing vscode extensions..."
|
|
30
|
-
# Mocha unit testing in the sidebar
|
|
31
|
-
RUN code-server --install-extension hbenl.vscode-mocha-test-adapter
|
|
32
|
-
RUN code-server --install-extension hbenl.test-adapter-converter
|
|
33
|
-
RUN code-server --install-extension hbenl.vscode-test-explorer
|
|
34
|
-
|
|
35
|
-
# Magic indentation rainbow
|
|
36
|
-
RUN code-server --install-extension oderwat.indent-rainbow
|
|
37
|
-
RUN code-server --install-extension dbaeumer.vscode-eslint
|
|
38
|
-
|
|
39
|
-
# Contextual git
|
|
40
|
-
RUN code-server --install-extension eamodio.gitlens
|
|
41
|
-
|
|
42
|
-
# Other extensions (uncomment them to have them automagic, or run this from a terminal to install in the container):
|
|
43
|
-
|
|
44
|
-
# SQL Tools
|
|
45
|
-
RUN code-server --install-extension mtxr.sqltools
|
|
46
|
-
RUN code-server --install-extension mtxr.sqltools-driver-mysql
|
|
47
|
-
|
|
48
|
-
# Microsoft's AI code completion
|
|
49
|
-
# RUN code-server --install-extension VisualStudioExptTeam.vscodeintellicode
|
|
50
|
-
|
|
51
|
-
# Live server -- make sure to open up the port on the docker image
|
|
52
|
-
# RUN code-server --install-extension ritwickdey.LiveServer
|
|
53
|
-
|
|
54
|
-
# Quick link to required modules' documentation
|
|
55
|
-
# RUN code-server --install-extension bengreenier.vscode-node-readme
|
|
56
|
-
|
|
57
|
-
# Switch up fonts
|
|
58
|
-
# RUN code-server --install-extension evan-buss.font-switcher
|
|
59
|
-
|
|
60
|
-
# Icons
|
|
61
|
-
# RUN code-server --install-extension vscode-icons-team.vscode-icons
|
|
62
|
-
# RUN code-server --install-extension PKief.material-icon-theme
|
|
63
|
-
|
|
64
|
-
# Hover over CSS colors to see them previewed
|
|
65
|
-
# RUN code-server --install-extension bierner.color-info
|
|
66
|
-
|
|
67
|
-
# An easy on the eyes color theme
|
|
68
|
-
# RUN code-server --install-extension daylerees.rainglow
|
|
69
|
-
|
|
70
29
|
SHELL ["/bin/bash", "-c"]
|
|
71
30
|
USER coder
|
|
72
31
|
|
|
@@ -88,4 +47,4 @@ RUN . ~/.nvm/nvm.sh && source ~/.bashrc && nvm alias default 14
|
|
|
88
47
|
|
|
89
48
|
WORKDIR /home/coder/meadow
|
|
90
49
|
|
|
91
|
-
ENTRYPOINT ["/usr/bin/MySQL-Laden-Entry.sh"]
|
|
50
|
+
ENTRYPOINT ["/usr/bin/MySQL-Laden-Entry.sh"]
|
package/README.md
CHANGED
|
@@ -1,153 +1,215 @@
|
|
|
1
|
-
Meadow
|
|
2
|
-
======
|
|
1
|
+
# Meadow
|
|
3
2
|
|
|
4
|
-
A
|
|
3
|
+
> A data access layer providing magic where you want it, programmability where you don't
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
Meadow is a JavaScript data broker that handles repetitive CRUD operations through a consistent, provider-agnostic interface. It doesn't care whether your data lives in MySQL, MSSQL, SQLite, or an in-browser IndexedDB store -- Meadow provides the schema management, query building, and data marshalling while you provide the business logic.
|
|
7
6
|
|
|
8
|
-
##
|
|
7
|
+
## Features
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
- **Provider-Agnostic Design** - Pluggable database backends through a consistent interface
|
|
10
|
+
- **Schema-Driven** - Define your data model once, get validation, default objects, and audit tracking for free
|
|
11
|
+
- **FoxHound Query DSL** - Fluent query builder that generates dialect-specific SQL
|
|
12
|
+
- **Automatic Audit Tracking** - Auto-populated create/update/delete timestamps and user stamps
|
|
13
|
+
- **Soft Deletes** - Built-in logical deletion with automatic query filtering
|
|
14
|
+
- **GUID Uniqueness** - Automatic GUID generation and uniqueness enforcement on create
|
|
15
|
+
- **Raw Query Overrides** - Escape hatch for custom SQL when the DSL isn't enough
|
|
16
|
+
- **Fable Integration** - First-class service in the Fable ecosystem with logging and configuration
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
const libFable = require('fable').new();
|
|
22
|
+
const libMeadow = require('meadow');
|
|
23
|
+
|
|
24
|
+
// Create a Meadow DAL for the "Book" entity
|
|
25
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
26
|
+
.setProvider('MySQL')
|
|
27
|
+
.setDefaultIdentifier('IDBook')
|
|
28
|
+
.setSchema([
|
|
29
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
30
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
31
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
32
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
33
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
34
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
35
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
36
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
37
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
38
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
39
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
// Read a single record
|
|
43
|
+
meadow.doRead(meadow.query.addFilter('IDBook', 42),
|
|
44
|
+
(pError, pQuery, pBook) =>
|
|
45
|
+
{
|
|
46
|
+
if (pError)
|
|
47
|
+
{
|
|
48
|
+
return console.log('Error reading book:', pError);
|
|
49
|
+
}
|
|
50
|
+
console.log('Found:', pBook.Title, 'by', pBook.Author);
|
|
51
|
+
});
|
|
12
52
|
```
|
|
13
53
|
|
|
14
|
-
|
|
54
|
+
## Installation
|
|
15
55
|
|
|
16
|
-
```
|
|
17
|
-
|
|
56
|
+
```bash
|
|
57
|
+
npm install meadow
|
|
18
58
|
```
|
|
19
59
|
|
|
20
|
-
##
|
|
60
|
+
## How It Works
|
|
21
61
|
|
|
22
|
-
|
|
62
|
+
Meadow follows the Fable service provider pattern. You create a DAL instance for each entity in your data model, configure its schema and provider, then use the CRUD methods to interact with your database. Meadow handles query generation, data marshalling, schema validation, and audit stamping automatically.
|
|
23
63
|
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
Database: "sales_data",
|
|
34
|
-
ConnectionPoolLimit: 20
|
|
35
|
-
}
|
|
36
|
-
};
|
|
64
|
+
```
|
|
65
|
+
Fable (Core)
|
|
66
|
+
└── Meadow (Data Access Layer)
|
|
67
|
+
├── Schema (Column definitions, validation, defaults)
|
|
68
|
+
├── FoxHound Query DSL (Dialect-specific SQL generation)
|
|
69
|
+
├── Behaviors (Create, Read, Reads, Update, Delete, Count, Undelete)
|
|
70
|
+
└── Provider (MySQL, MSSQL, SQLite, ALASQL, MeadowEndpoints, None)
|
|
71
|
+
└── Database Connection
|
|
72
|
+
```
|
|
37
73
|
|
|
38
|
-
|
|
74
|
+
## CRUD Operations
|
|
39
75
|
|
|
40
|
-
|
|
41
|
-
var libMySQL = require('mysql2');
|
|
42
|
-
fable.MeadowMySQLConnectionPool = libMySQL.createPool
|
|
43
|
-
(
|
|
44
|
-
{
|
|
45
|
-
connectionLimit: _Fable.settings.MySQL.ConnectionPoolLimit,
|
|
46
|
-
host: _Fable.settings.MySQL.Server,
|
|
47
|
-
port: _Fable.settings.MySQL.Port,
|
|
48
|
-
user: _Fable.settings.MySQL.User,
|
|
49
|
-
password: _Fable.settings.MySQL.Password,
|
|
50
|
-
database: _Fable.settings.MySQL.Database,
|
|
51
|
-
namedPlaceholders: true
|
|
52
|
-
}
|
|
53
|
-
);
|
|
76
|
+
All operations follow an async waterfall pattern with a consistent callback signature.
|
|
54
77
|
|
|
78
|
+
### Create
|
|
55
79
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
80
|
+
```javascript
|
|
81
|
+
const tmpQuery = meadow.query.addRecord({ Title: 'Dune', Author: 'Frank Herbert' });
|
|
82
|
+
meadow.doCreate(tmpQuery,
|
|
83
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
84
|
+
{
|
|
85
|
+
console.log('Created book with ID:', pRecord.IDBook);
|
|
86
|
+
});
|
|
87
|
+
```
|
|
60
88
|
|
|
61
|
-
|
|
62
|
-
|
|
89
|
+
### Read
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
// Single record
|
|
93
|
+
meadow.doRead(meadow.query.addFilter('IDBook', 1),
|
|
94
|
+
(pError, pQuery, pRecord) =>
|
|
95
|
+
{
|
|
96
|
+
console.log('Book:', pRecord.Title);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Multiple records with pagination
|
|
100
|
+
meadow.doReads(meadow.query.setCap(25).setBegin(0),
|
|
101
|
+
(pError, pQuery, pRecords) =>
|
|
102
|
+
{
|
|
103
|
+
console.log('Found', pRecords.length, 'books');
|
|
104
|
+
});
|
|
105
|
+
```
|
|
63
106
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
console.log('Found customer ID '+customer.customerID+' who is named '+customer.name);
|
|
77
|
-
}
|
|
78
|
-
);
|
|
107
|
+
### Update
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
const tmpQuery = meadow.query
|
|
111
|
+
.addFilter('IDBook', 1)
|
|
112
|
+
.addRecord({ Title: 'Updated Title' });
|
|
113
|
+
meadow.doUpdate(tmpQuery,
|
|
114
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
115
|
+
{
|
|
116
|
+
console.log('Updated:', pRecord.Title);
|
|
117
|
+
});
|
|
79
118
|
```
|
|
80
119
|
|
|
81
|
-
|
|
120
|
+
### Delete and Count
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
// Delete (soft delete if schema supports it)
|
|
124
|
+
meadow.doDelete(meadow.query.addFilter('IDBook', 1),
|
|
125
|
+
(pError, pQuery, pResult) =>
|
|
126
|
+
{
|
|
127
|
+
console.log('Deleted:', pResult, 'record(s)');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Count records
|
|
131
|
+
meadow.doCount(meadow.query,
|
|
132
|
+
(pError, pQuery, pCount) =>
|
|
133
|
+
{
|
|
134
|
+
console.log('Total books:', pCount);
|
|
135
|
+
});
|
|
136
|
+
```
|
|
82
137
|
|
|
83
|
-
|
|
84
|
-
// These settings are read automatically from the fable.settings object by meadow
|
|
85
|
-
var fable = require('fable').new();
|
|
138
|
+
## Schema
|
|
86
139
|
|
|
87
|
-
|
|
88
|
-
var libALASQL = require('alasql');
|
|
89
|
-
libALASQL('CREATE INDEXEDDB DATABASE IF NOT EXISTS example;');
|
|
90
|
-
libALASQL('ATTACH INDEXEDDB DATABASE example;');
|
|
91
|
-
libALASQL('USE example;');
|
|
92
|
-
fable.ALASQL = libALASQL;
|
|
140
|
+
Meadow schemas define your data model with special column types that enable automatic behavior:
|
|
93
141
|
|
|
142
|
+
| Type | Description |
|
|
143
|
+
|------|-------------|
|
|
144
|
+
| `AutoIdentity` | Auto-increment primary key |
|
|
145
|
+
| `AutoGUID` | Automatically generated GUID |
|
|
146
|
+
| `CreateDate` / `CreateIDUser` | Auto-populated on create |
|
|
147
|
+
| `UpdateDate` / `UpdateIDUser` | Auto-populated on update |
|
|
148
|
+
| `DeleteDate` / `DeleteIDUser` | Auto-populated on delete |
|
|
149
|
+
| `Deleted` | Soft delete flag |
|
|
150
|
+
| `String`, `Text`, `Numeric`, `Decimal`, `Boolean`, `DateTime` | Standard data types |
|
|
94
151
|
|
|
152
|
+
## Providers
|
|
95
153
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
154
|
+
| Provider | Description |
|
|
155
|
+
|----------|-------------|
|
|
156
|
+
| `MySQL` | MySQL/MariaDB via mysql2 |
|
|
157
|
+
| `MSSQL` | Microsoft SQL Server with prepared statements |
|
|
158
|
+
| `SQLite` | SQLite via meadow-connection-sqlite |
|
|
159
|
+
| `ALASQL` | In-browser IndexedDB via ALASQL |
|
|
160
|
+
| `MeadowEndpoints` | REST proxy to remote Meadow API |
|
|
161
|
+
| `None` | Null provider for testing |
|
|
100
162
|
|
|
101
|
-
|
|
102
|
-
var queryDescription = meadow.query.addFilter('customerID', 17);
|
|
163
|
+
## ALASQL Provider Example
|
|
103
164
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (error)
|
|
113
|
-
{
|
|
114
|
-
return console.log('Error querying customer data: '+error);
|
|
115
|
-
}
|
|
116
|
-
console.log('Found customer ID '+customer.customerID+' who is named '+customer.name);
|
|
117
|
-
}
|
|
118
|
-
);
|
|
119
|
-
```
|
|
165
|
+
```javascript
|
|
166
|
+
const libFable = require('fable').new();
|
|
167
|
+
const libALASQL = require('alasql');
|
|
168
|
+
|
|
169
|
+
libALASQL('CREATE INDEXEDDB DATABASE IF NOT EXISTS example;');
|
|
170
|
+
libALASQL('ATTACH INDEXEDDB DATABASE example;');
|
|
171
|
+
libALASQL('USE example;');
|
|
172
|
+
libFable.ALASQL = libALASQL;
|
|
120
173
|
|
|
174
|
+
const meadow = require('meadow').new(libFable, 'Customers')
|
|
175
|
+
.setProvider('ALASQL')
|
|
176
|
+
.setDefaultIdentifier('customerID');
|
|
177
|
+
```
|
|
121
178
|
|
|
122
179
|
## MSSQL Docker Image
|
|
123
180
|
|
|
124
|
-
To run
|
|
181
|
+
To run Microsoft SQL Server tests locally:
|
|
125
182
|
|
|
126
|
-
```
|
|
127
|
-
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=1234567890abc."
|
|
183
|
+
```bash
|
|
184
|
+
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=1234567890abc." \
|
|
185
|
+
-p 14333:1433 --name meadow-mssql-test --hostname meadowsqltest \
|
|
186
|
+
-d mcr.microsoft.com/mssql/server:2022-latest
|
|
187
|
+
|
|
188
|
+
docker exec meadow-mssql-test sh -c \
|
|
189
|
+
"/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P '1234567890abc.' -Q 'CREATE DATABASE bookstore;'"
|
|
128
190
|
```
|
|
129
191
|
|
|
130
|
-
|
|
192
|
+
## Testing
|
|
131
193
|
|
|
132
|
-
```
|
|
133
|
-
|
|
194
|
+
```bash
|
|
195
|
+
npm test
|
|
134
196
|
```
|
|
135
197
|
|
|
136
|
-
|
|
198
|
+
## Documentation
|
|
137
199
|
|
|
200
|
+
Detailed documentation is available in the `docs/` folder and can be served locally:
|
|
138
201
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
docker build ./ -t retold/meadow:local
|
|
202
|
+
```bash
|
|
203
|
+
npx docsify-cli serve docs
|
|
142
204
|
```
|
|
143
205
|
|
|
144
|
-
|
|
145
|
-
```
|
|
146
|
-
docker run -it --name meadow-dev -p 127.0.0.1:12342:8080 -v "$PWD/.config:/home/coder/.config" -v "$PWD:/home/coder/meadow" -u "$(id -u):$(id -g)" -e "DOCKER_USER=$USER" retold/meadow:local
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
3. Go to http://localhost:12342/ in a web browser
|
|
150
|
-
|
|
151
|
-
4. The password is "retold"
|
|
206
|
+
## Related Packages
|
|
152
207
|
|
|
153
|
-
|
|
208
|
+
- [foxhound](https://github.com/stevenvelozo/foxhound) - Query DSL for SQL generation
|
|
209
|
+
- [stricture](https://github.com/stevenvelozo/stricture) - Schema definition language
|
|
210
|
+
- [meadow-endpoints](https://github.com/stevenvelozo/meadow-endpoints) - Automatic REST endpoint generation
|
|
211
|
+
- [meadow-connection-mysql](https://github.com/stevenvelozo/meadow-connection-mysql) - MySQL connection provider
|
|
212
|
+
- [meadow-connection-mssql](https://github.com/stevenvelozo/meadow-connection-mssql) - MSSQL connection provider
|
|
213
|
+
- [meadow-connection-sqlite](https://github.com/stevenvelozo/meadow-connection-sqlite) - SQLite connection provider
|
|
214
|
+
- [fable](https://github.com/stevenvelozo/fable) - Service provider framework
|
|
215
|
+
- [orator](https://github.com/stevenvelozo/orator) - API server abstraction
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Meadow
|
|
2
|
+
|
|
3
|
+
> A data access layer providing magic where you want it, programmability where you don't
|
|
4
|
+
|
|
5
|
+
Meadow is a JavaScript data broker that handles repetitive CRUD operations through a consistent, provider-agnostic interface. It doesn't care whether your data lives in MySQL, MSSQL, SQLite, or an in-browser IndexedDB store -- Meadow provides the schema management, query building, and data marshalling while you provide the business logic.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Provider-Agnostic Design** - Pluggable database backends through a consistent interface
|
|
10
|
+
- **Schema-Driven** - Define your data model once, get validation, default objects, and audit tracking for free
|
|
11
|
+
- **FoxHound Query DSL** - Fluent query builder that generates dialect-specific SQL
|
|
12
|
+
- **Automatic Audit Tracking** - Auto-populated create/update/delete timestamps and user stamps
|
|
13
|
+
- **Soft Deletes** - Built-in logical deletion with automatic query filtering
|
|
14
|
+
- **GUID Uniqueness** - Automatic GUID generation and uniqueness enforcement on create
|
|
15
|
+
- **Raw Query Overrides** - Escape hatch for custom SQL when the DSL isn't enough
|
|
16
|
+
- **Fable Integration** - First-class service in the Fable ecosystem with logging and configuration
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
const libFable = require('fable').new();
|
|
22
|
+
const libMeadow = require('meadow');
|
|
23
|
+
|
|
24
|
+
// Create a Meadow DAL for the "Book" entity
|
|
25
|
+
const meadow = libMeadow.new(libFable, 'Book')
|
|
26
|
+
.setProvider('MySQL')
|
|
27
|
+
.setDefaultIdentifier('IDBook')
|
|
28
|
+
.setSchema([
|
|
29
|
+
{ Column: 'IDBook', Type: 'AutoIdentity' },
|
|
30
|
+
{ Column: 'GUIDBook', Type: 'AutoGUID' },
|
|
31
|
+
{ Column: 'Title', Type: 'String', Size: '255' },
|
|
32
|
+
{ Column: 'Author', Type: 'String', Size: '128' },
|
|
33
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
34
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
35
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
36
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
37
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
38
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
39
|
+
{ Column: 'Deleted', Type: 'Deleted' }
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
// Read a single record
|
|
43
|
+
meadow.doRead(meadow.query.addFilter('IDBook', 42),
|
|
44
|
+
(pError, pQuery, pBook) =>
|
|
45
|
+
{
|
|
46
|
+
if (pError)
|
|
47
|
+
{
|
|
48
|
+
return console.log('Error reading book:', pError);
|
|
49
|
+
}
|
|
50
|
+
console.log('Found:', pBook.Title, 'by', pBook.Author);
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm install meadow
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## How It Works
|
|
61
|
+
|
|
62
|
+
Meadow follows the Fable service provider pattern. You create a DAL instance for each entity in your data model, configure its schema and provider, then use the CRUD methods to interact with your database. Meadow handles query generation, data marshalling, schema validation, and audit stamping automatically.
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Fable (Core)
|
|
66
|
+
└── Meadow (Data Access Layer)
|
|
67
|
+
├── Schema (Column definitions, validation, defaults)
|
|
68
|
+
├── FoxHound Query DSL (Dialect-specific SQL generation)
|
|
69
|
+
├── Behaviors (Create, Read, Reads, Update, Delete, Count, Undelete)
|
|
70
|
+
└── Provider (MySQL, MSSQL, SQLite, ALASQL, MeadowEndpoints, None)
|
|
71
|
+
└── Database Connection
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## CRUD Operations
|
|
75
|
+
|
|
76
|
+
All operations follow an async waterfall pattern with a consistent callback signature.
|
|
77
|
+
|
|
78
|
+
### Create
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
const tmpQuery = meadow.query.addRecord({ Title: 'Dune', Author: 'Frank Herbert' });
|
|
82
|
+
meadow.doCreate(tmpQuery,
|
|
83
|
+
(pError, pCreateQuery, pReadQuery, pRecord) =>
|
|
84
|
+
{
|
|
85
|
+
console.log('Created book with ID:', pRecord.IDBook);
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Read
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
// Single record
|
|
93
|
+
meadow.doRead(meadow.query.addFilter('IDBook', 1),
|
|
94
|
+
(pError, pQuery, pRecord) =>
|
|
95
|
+
{
|
|
96
|
+
console.log('Book:', pRecord.Title);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Multiple records
|
|
100
|
+
meadow.doReads(meadow.query.setCap(25).setBegin(0),
|
|
101
|
+
(pError, pQuery, pRecords) =>
|
|
102
|
+
{
|
|
103
|
+
console.log('Found', pRecords.length, 'books');
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Update
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
const tmpQuery = meadow.query
|
|
111
|
+
.addFilter('IDBook', 1)
|
|
112
|
+
.addRecord({ Title: 'Updated Title' });
|
|
113
|
+
meadow.doUpdate(tmpQuery,
|
|
114
|
+
(pError, pUpdateQuery, pReadQuery, pRecord) =>
|
|
115
|
+
{
|
|
116
|
+
console.log('Updated:', pRecord.Title);
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Delete
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
meadow.doDelete(meadow.query.addFilter('IDBook', 1),
|
|
124
|
+
(pError, pQuery, pResult) =>
|
|
125
|
+
{
|
|
126
|
+
console.log('Deleted:', pResult, 'record(s)');
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Count
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
meadow.doCount(meadow.query,
|
|
134
|
+
(pError, pQuery, pCount) =>
|
|
135
|
+
{
|
|
136
|
+
console.log('Total books:', pCount);
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Schema
|
|
141
|
+
|
|
142
|
+
Meadow schemas define your data model with special column types that enable automatic behavior.
|
|
143
|
+
|
|
144
|
+
### Column Types
|
|
145
|
+
|
|
146
|
+
| Type | Description |
|
|
147
|
+
|------|-------------|
|
|
148
|
+
| `AutoIdentity` | Auto-increment primary key |
|
|
149
|
+
| `AutoGUID` | Automatically generated GUID |
|
|
150
|
+
| `CreateDate` | Auto-populated timestamp on create |
|
|
151
|
+
| `CreateIDUser` | Auto-populated user ID on create |
|
|
152
|
+
| `UpdateDate` | Auto-populated timestamp on update |
|
|
153
|
+
| `UpdateIDUser` | Auto-populated user ID on update |
|
|
154
|
+
| `DeleteDate` | Auto-populated timestamp on delete |
|
|
155
|
+
| `DeleteIDUser` | Auto-populated user ID on delete |
|
|
156
|
+
| `Deleted` | Soft delete flag (enables logical deletion) |
|
|
157
|
+
| `String` | String field with optional `Size` |
|
|
158
|
+
| `Text` | Long text field |
|
|
159
|
+
| `Numeric` | Integer field |
|
|
160
|
+
| `Decimal` | Decimal field with `Size` as `"precision,scale"` |
|
|
161
|
+
| `Boolean` | Boolean field |
|
|
162
|
+
| `DateTime` | Date/time field |
|
|
163
|
+
|
|
164
|
+
### JSON Schema Validation
|
|
165
|
+
|
|
166
|
+
Meadow also supports JSON Schema (v4) for object validation:
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
meadow.setJsonSchema({
|
|
170
|
+
title: 'Book',
|
|
171
|
+
type: 'object',
|
|
172
|
+
properties:
|
|
173
|
+
{
|
|
174
|
+
IDBook: { type: 'integer' },
|
|
175
|
+
Title: { type: 'string' },
|
|
176
|
+
Author: { type: 'string' }
|
|
177
|
+
},
|
|
178
|
+
required: ['Title']
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const result = meadow.schemaFull.validateObject({ Title: 'Dune' });
|
|
182
|
+
// { Valid: true, Errors: [] }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Configuration
|
|
186
|
+
|
|
187
|
+
Meadow reads database configuration from the Fable settings object:
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"Product": "MyApp",
|
|
192
|
+
"ProductVersion": "1.0.0",
|
|
193
|
+
"MySQL": {
|
|
194
|
+
"Server": "localhost",
|
|
195
|
+
"Port": 3306,
|
|
196
|
+
"User": "root",
|
|
197
|
+
"Password": "",
|
|
198
|
+
"Database": "myapp",
|
|
199
|
+
"ConnectionPoolLimit": 20
|
|
200
|
+
},
|
|
201
|
+
"QueryThresholdWarnTime": 200
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
| Setting | Type | Default | Description |
|
|
206
|
+
|---------|------|---------|-------------|
|
|
207
|
+
| `MySQL` | object | - | MySQL connection settings |
|
|
208
|
+
| `QueryThresholdWarnTime` | number | `200` | Slow query warning threshold (ms) |
|
|
209
|
+
| `MeadowProvider` | string | - | Default provider name |
|
|
210
|
+
| `MeadowRoleNames` | array | `['Unauthenticated', 'User', 'Manager', 'Director', 'Executive', 'Administrator']` | Role name mapping |
|
|
211
|
+
|
|
212
|
+
## Providers
|
|
213
|
+
|
|
214
|
+
Meadow supports multiple database backends through its provider system.
|
|
215
|
+
|
|
216
|
+
| Provider | Description | Connection |
|
|
217
|
+
|----------|-------------|------------|
|
|
218
|
+
| `MySQL` | MySQL/MariaDB via mysql2 | `fable.MeadowMySQLConnectionPool` or `fable.MeadowMySQLProvider` |
|
|
219
|
+
| `MSSQL` | Microsoft SQL Server | `fable.MeadowMSSQLProvider` |
|
|
220
|
+
| `SQLite` | SQLite database | Via meadow-connection-sqlite |
|
|
221
|
+
| `ALASQL` | In-browser IndexedDB | `fable.ALASQL` |
|
|
222
|
+
| `MeadowEndpoints` | REST proxy to remote Meadow API | Via `MeadowEndpoints` settings |
|
|
223
|
+
| `None` | Null provider for testing | No connection required |
|
|
224
|
+
|
|
225
|
+
## Raw Query Overrides
|
|
226
|
+
|
|
227
|
+
For complex queries that exceed the DSL capabilities:
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
// Set a custom query for Read operations
|
|
231
|
+
meadow.rawQueries.setQuery('Read',
|
|
232
|
+
'SELECT b.*, a.Name AS AuthorName FROM Book b JOIN Author a ON b.IDAuthor = a.IDAuthor WHERE b.IDBook = :IDBook');
|
|
233
|
+
|
|
234
|
+
// Load from file
|
|
235
|
+
meadow.rawQueries.loadQuery('CustomReport', './queries/book-report.sql');
|
|
236
|
+
|
|
237
|
+
// Check and use
|
|
238
|
+
if (meadow.rawQueries.checkQuery('CustomReport'))
|
|
239
|
+
{
|
|
240
|
+
const sql = meadow.rawQueries.getQuery('CustomReport');
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Testing
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
npm test
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Documentation
|
|
251
|
+
|
|
252
|
+
- [Architecture](architecture.md) - System architecture and design patterns
|
|
253
|
+
- [Schema](schema/README.md) - Column definitions, JSON Schema validation, default objects, authorization
|
|
254
|
+
- [Query DSL](query-dsl.md) - FoxHound query building
|
|
255
|
+
- [Query Operations](query/README.md) - Query object overview and CRUD operations
|
|
256
|
+
- [Create](query/create.md) - Insert new records
|
|
257
|
+
- [Read](query/read.md) - Retrieve single and multiple records
|
|
258
|
+
- [Update](query/update.md) - Modify existing records
|
|
259
|
+
- [Delete](query/delete.md) - Soft delete and undelete records
|
|
260
|
+
- [Count](query/count.md) - Count records matching criteria
|
|
261
|
+
- [Providers](providers/README.md) - Database provider system
|
|
262
|
+
- [MySQL](providers/mysql.md) - Connection pooling, named placeholders
|
|
263
|
+
- [MSSQL](providers/mssql.md) - Prepared statements, type mapping
|
|
264
|
+
- [SQLite](providers/sqlite.md) - Embedded database
|
|
265
|
+
- [ALASQL](providers/alasql.md) - In-memory SQL, browser support
|
|
266
|
+
|
|
267
|
+
## Related Packages
|
|
268
|
+
|
|
269
|
+
- [foxhound](https://github.com/stevenvelozo/foxhound) - Query DSL for SQL generation
|
|
270
|
+
- [stricture](https://github.com/stevenvelozo/stricture) - Schema definition language
|
|
271
|
+
- [meadow-endpoints](https://github.com/stevenvelozo/meadow-endpoints) - Automatic REST endpoint generation
|
|
272
|
+
- [meadow-connection-mysql](https://github.com/stevenvelozo/meadow-connection-mysql) - MySQL connection provider
|
|
273
|
+
- [meadow-connection-mssql](https://github.com/stevenvelozo/meadow-connection-mssql) - MSSQL connection provider
|
|
274
|
+
- [meadow-connection-sqlite](https://github.com/stevenvelozo/meadow-connection-sqlite) - SQLite connection provider
|
|
275
|
+
- [fable](https://github.com/stevenvelozo/fable) - Service provider framework
|
|
276
|
+
- [orator](https://github.com/stevenvelozo/orator) - API server abstraction
|