meadow-connection-postgresql 1.0.0 → 1.0.2

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,168 @@
1
+ # Architecture
2
+
3
+ ## System Overview
4
+
5
+ The PostgreSQL connector bridges Meadow's data access abstraction with the `pg` (node-postgres) driver. It follows the Fable service provider pattern, providing connection pool management and SQL DDL generation.
6
+
7
+ ```mermaid
8
+ graph TB
9
+ subgraph Application Layer
10
+ APP[Application Code]
11
+ MEA[Meadow ORM]
12
+ FH[FoxHound Query Dialect]
13
+ end
14
+ subgraph Connection Layer
15
+ MCP["meadow-connection-postgresql<br/>(MeadowConnectionPostgreSQL)"]
16
+ POOL[pg.Pool]
17
+ end
18
+ subgraph PostgreSQL Server
19
+ PG[(PostgreSQL)]
20
+ end
21
+ APP --> MEA
22
+ MEA --> FH
23
+ FH --> MCP
24
+ MCP --> POOL
25
+ POOL --> PG
26
+ ```
27
+
28
+ ## Connection Lifecycle
29
+
30
+ ```mermaid
31
+ sequenceDiagram
32
+ participant App as Application
33
+ participant Fable as Fable
34
+ participant MCP as MeadowConnectionPostgreSQL
35
+ participant PG as pg.Pool
36
+ participant Server as PostgreSQL Server
37
+
38
+ App->>Fable: new libFable(settings)
39
+ App->>Fable: addAndInstantiateServiceType()
40
+ Fable->>MCP: constructor(fable, options)
41
+ MCP->>MCP: Read PostgreSQL config
42
+ MCP->>MCP: Normalize property names
43
+ Note over MCP: Server→host, Port→port, etc.
44
+
45
+ alt Auto-Connect Enabled
46
+ MCP->>MCP: connect()
47
+ end
48
+
49
+ App->>MCP: connectAsync(callback)
50
+
51
+ alt Already Connected
52
+ MCP-->>App: callback(null, pool)
53
+ else Not Connected
54
+ MCP->>PG: new pg.Pool(settings)
55
+ PG->>Server: Establish Connection Pool
56
+ MCP->>MCP: connected = true
57
+ MCP-->>App: callback(null, pool)
58
+ end
59
+
60
+ App->>MCP: pool (getter)
61
+ MCP-->>App: pg.Pool instance
62
+ App->>PG: pool.query(sql, params, callback)
63
+ PG->>Server: Execute query
64
+ Server-->>PG: Result rows
65
+ PG-->>App: callback(error, result)
66
+ ```
67
+
68
+ ## Service Provider Model
69
+
70
+ `MeadowConnectionPostgreSQL` extends `fable-serviceproviderbase`, providing standard lifecycle integration with the Fable ecosystem.
71
+
72
+ ```mermaid
73
+ classDiagram
74
+ class FableServiceProviderBase {
75
+ +fable
76
+ +options
77
+ +log
78
+ +serviceType
79
+ }
80
+ class MeadowConnectionPostgreSQL {
81
+ +serviceType: "MeadowConnectionPostgreSQL"
82
+ +connected: boolean
83
+ -_ConnectionPool: pg.Pool
84
+ +connect()
85
+ +connectAsync(fCallback)
86
+ +pool: pg.Pool
87
+ +createTable(schema, fCallback)
88
+ +createTables(schema, fCallback)
89
+ +generateCreateTableStatement(schema)
90
+ +generateDropTableStatement(name)
91
+ }
92
+ FableServiceProviderBase <|-- MeadowConnectionPostgreSQL
93
+ ```
94
+
95
+ ## Settings Flow
96
+
97
+ The connector normalizes Meadow-style property names to the native `pg` driver format:
98
+
99
+ ```mermaid
100
+ flowchart LR
101
+ subgraph Input Sources
102
+ OPT[Constructor Options]
103
+ SET[fable.settings.PostgreSQL]
104
+ end
105
+ subgraph Normalization
106
+ NORM["Property Mapping<br/>Server → host<br/>Port → port<br/>User → user<br/>Password → password<br/>Database → database<br/>ConnectionPoolLimit → max"]
107
+ end
108
+ subgraph Output
109
+ PGPOOL["pg.Pool Config<br/>{ host, port, user,<br/> password, database, max }"]
110
+ end
111
+ OPT --> NORM
112
+ SET --> NORM
113
+ NORM --> PGPOOL
114
+ ```
115
+
116
+ ## DDL Generation Pipeline
117
+
118
+ ```mermaid
119
+ flowchart TD
120
+ A[Meadow Table Schema] --> B[generateCreateTableStatement]
121
+ B --> C["SQL DDL String<br/>CREATE TABLE IF NOT EXISTS..."]
122
+ C --> D{createTable called?}
123
+ D -->|Yes| E[pool.query executes DDL]
124
+ E --> F{Result?}
125
+ F -->|Success| G[Callback - no error]
126
+ F -->|Error 42P07| H[Log warning - table exists]
127
+ H --> G
128
+ F -->|Other Error| I[Callback with error]
129
+ D -->|No| J[Return DDL string for inspection]
130
+ ```
131
+
132
+ ## Connection Safety
133
+
134
+ The connector includes several safety mechanisms:
135
+
136
+ ```mermaid
137
+ flowchart TD
138
+ A[connect called] --> B{Already connected?}
139
+ B -->|Yes| C[Log error with masked password]
140
+ C --> D[Return without action]
141
+ B -->|No| E[Log connection info]
142
+ E --> F[Create pg.Pool]
143
+ F --> G[Set connected = true]
144
+ ```
145
+
146
+ Key safety features:
147
+
148
+ | Feature | Implementation |
149
+ |---------|---------------|
150
+ | Double-connect guard | Logs error and returns if `_ConnectionPool` already exists |
151
+ | Password masking | Cleansed settings logged on double-connect attempt |
152
+ | Missing callback guard | `connectAsync()` provides a no-op callback if none given |
153
+ | Idempotent tables | `CREATE TABLE IF NOT EXISTS` + error code 42P07 handling |
154
+ | Quoted identifiers | Double-quoted table and column names in DDL |
155
+
156
+ ## Connector Comparison
157
+
158
+ | Feature | PostgreSQL | MySQL | MSSQL | SQLite |
159
+ |---------|-----------|-------|-------|--------|
160
+ | Driver | `pg` | `mysql2` | `mssql` | `better-sqlite3` |
161
+ | Connection | `pg.Pool` | MySQL Pool | MSSQL Pool | File path |
162
+ | Schema | SQL DDL | SQL DDL | SQL DDL | SQL DDL |
163
+ | `pool` returns | `pg.Pool` | MySQL Pool | MSSQL Pool | SQLite Database |
164
+ | Auto-increment | `SERIAL` | `AUTO_INCREMENT` | `IDENTITY` | `INTEGER PRIMARY KEY` |
165
+ | Parameterized | `$1, $2, $3` | `?, ?, ?` | `@p1, @p2, @p3` | `?, ?, ?` |
166
+ | Boolean type | `BOOLEAN` | `TINYINT(1)` | `BIT` | `INTEGER` |
167
+ | Idempotent DDL | `IF NOT EXISTS` + 42P07 | `IF NOT EXISTS` | `IF NOT EXISTS` | `IF NOT EXISTS` |
168
+ | Identifiers | Double-quoted `"col"` | Backtick-quoted `` `col` `` | Bracket-quoted `[col]` | Double-quoted `"col"` |
@@ -0,0 +1,73 @@
1
+ /* ============================================================================
2
+ Pict Docuserve - Base Styles
3
+ ============================================================================ */
4
+
5
+ /* Reset and base */
6
+ *, *::before, *::after {
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ html, body {
11
+ margin: 0;
12
+ padding: 0;
13
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
14
+ font-size: 16px;
15
+ line-height: 1.5;
16
+ color: #423D37;
17
+ background-color: #fff;
18
+ -webkit-font-smoothing: antialiased;
19
+ -moz-osx-font-smoothing: grayscale;
20
+ }
21
+
22
+ /* Typography */
23
+ h1, h2, h3, h4, h5, h6 {
24
+ margin-top: 0;
25
+ line-height: 1.3;
26
+ }
27
+
28
+ a {
29
+ color: #2E7D74;
30
+ text-decoration: none;
31
+ }
32
+
33
+ a:hover {
34
+ color: #256861;
35
+ }
36
+
37
+ /* Application container */
38
+ #Docuserve-Application-Container {
39
+ min-height: 100vh;
40
+ }
41
+
42
+ /* Utility: scrollbar styling */
43
+ ::-webkit-scrollbar {
44
+ width: 8px;
45
+ }
46
+
47
+ ::-webkit-scrollbar-track {
48
+ background: #F5F0E8;
49
+ }
50
+
51
+ ::-webkit-scrollbar-thumb {
52
+ background: #D4CCBE;
53
+ border-radius: 4px;
54
+ }
55
+
56
+ ::-webkit-scrollbar-thumb:hover {
57
+ background: #B5AA9A;
58
+ }
59
+
60
+ /* Responsive adjustments */
61
+ @media (max-width: 768px) {
62
+ html {
63
+ font-size: 14px;
64
+ }
65
+
66
+ #Docuserve-Sidebar-Container {
67
+ display: none;
68
+ }
69
+
70
+ .docuserve-body {
71
+ flex-direction: column;
72
+ }
73
+ }
@@ -0,0 +1,39 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7
+ <meta name="description" content="Documentation powered by pict-docuserve">
8
+
9
+ <title>Documentation</title>
10
+
11
+ <!-- Application Stylesheet -->
12
+ <link href="css/docuserve.css" rel="stylesheet">
13
+ <!-- KaTeX stylesheet for LaTeX equation rendering -->
14
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
15
+ <!-- PICT Dynamic View CSS Container -->
16
+ <style id="PICT-CSS"></style>
17
+
18
+ <!-- Load the PICT library from jsDelivr CDN -->
19
+ <script src="https://cdn.jsdelivr.net/npm/pict@1/dist/pict.min.js" type="text/javascript"></script>
20
+ <!-- Bootstrap the Application -->
21
+ <script type="text/javascript">
22
+ //<![CDATA[
23
+ Pict.safeOnDocumentReady(() => { Pict.safeLoadPictApplication(PictDocuserve, 2)});
24
+ //]]>
25
+ </script>
26
+ </head>
27
+ <body>
28
+ <!-- The root container for the Pict application -->
29
+ <div id="Docuserve-Application-Container"></div>
30
+
31
+ <!-- Mermaid diagram rendering -->
32
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
33
+ <script>mermaid.initialize({ startOnLoad: false, theme: 'default' });</script>
34
+ <!-- KaTeX for LaTeX equation rendering -->
35
+ <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
36
+ <!-- Load the Docuserve PICT Application Bundle from jsDelivr CDN -->
37
+ <script src="https://cdn.jsdelivr.net/npm/pict-docuserve@0/dist/pict-docuserve.min.js" type="text/javascript"></script>
38
+ </body>
39
+ </html>
@@ -0,0 +1,181 @@
1
+ # Quickstart
2
+
3
+ Get a PostgreSQL connection running in five steps.
4
+
5
+ ## Step 1: Install
6
+
7
+ ```bash
8
+ npm install meadow-connection-postgresql fable
9
+ ```
10
+
11
+ Requires a running PostgreSQL server (local or remote). The module uses the `pg` (node-postgres) driver v8.
12
+
13
+ ## Step 2: Configure and Connect
14
+
15
+ ```javascript
16
+ const libFable = require('fable');
17
+ const libMeadowConnectionPostgreSQL = require('meadow-connection-postgresql');
18
+
19
+ let _Fable = new libFable(
20
+ {
21
+ "PostgreSQL":
22
+ {
23
+ "Server": "localhost",
24
+ "Port": 5432,
25
+ "User": "postgres",
26
+ "Password": "secret",
27
+ "Database": "myapp",
28
+ "ConnectionPoolLimit": 20
29
+ }
30
+ });
31
+
32
+ _Fable.serviceManager.addAndInstantiateServiceType(
33
+ 'MeadowPostgreSQLProvider', libMeadowConnectionPostgreSQL);
34
+
35
+ _Fable.MeadowPostgreSQLProvider.connectAsync(
36
+ (pError, pPool) =>
37
+ {
38
+ if (pError)
39
+ {
40
+ console.error('Connection failed:', pError);
41
+ return;
42
+ }
43
+ console.log('Connected to PostgreSQL!');
44
+ });
45
+ ```
46
+
47
+ ## Step 3: Create a Table
48
+
49
+ Define a Meadow table schema and apply it. The `CREATE TABLE IF NOT EXISTS` clause ensures idempotent execution, and the connector also handles the PostgreSQL `42P07` (duplicate_table) error gracefully.
50
+
51
+ ```javascript
52
+ let tmpAnimalSchema =
53
+ {
54
+ TableName: 'Animal',
55
+ Columns:
56
+ [
57
+ { Column: 'IDAnimal', DataType: 'ID' },
58
+ { Column: 'GUIDAnimal', DataType: 'GUID', Size: 36 },
59
+ { Column: 'Name', DataType: 'String', Size: 128 },
60
+ { Column: 'Age', DataType: 'Numeric' },
61
+ { Column: 'Weight', DataType: 'Decimal', Size: '10,2' },
62
+ { Column: 'Description', DataType: 'Text' },
63
+ { Column: 'CreateDate', DataType: 'DateTime' },
64
+ { Column: 'Deleted', DataType: 'Boolean' }
65
+ ]
66
+ };
67
+
68
+ _Fable.MeadowPostgreSQLProvider.createTable(tmpAnimalSchema,
69
+ (pError) =>
70
+ {
71
+ if (pError)
72
+ {
73
+ console.error('Table creation failed:', pError);
74
+ return;
75
+ }
76
+ console.log('Animal table ready!');
77
+ });
78
+ ```
79
+
80
+ This generates and executes:
81
+
82
+ ```sql
83
+ CREATE TABLE IF NOT EXISTS
84
+ "Animal"
85
+ (
86
+ "IDAnimal" SERIAL PRIMARY KEY,
87
+ "GUIDAnimal" VARCHAR(36) DEFAULT '0xDe',
88
+ "Name" VARCHAR(128) NOT NULL DEFAULT '',
89
+ "Age" INTEGER NOT NULL DEFAULT 0,
90
+ "Weight" DECIMAL(10,2),
91
+ "Description" TEXT,
92
+ "CreateDate" TIMESTAMP,
93
+ "Deleted" BOOLEAN NOT NULL DEFAULT false
94
+ );
95
+ ```
96
+
97
+ ## Step 4: Use the Pool
98
+
99
+ Access the `pg.Pool` instance via the `pool` getter and run queries:
100
+
101
+ ```javascript
102
+ let tmpPool = _Fable.MeadowPostgreSQLProvider.pool;
103
+
104
+ // Simple query
105
+ tmpPool.query('SELECT * FROM "Animal" WHERE "Deleted" = false',
106
+ (pError, pResult) =>
107
+ {
108
+ if (pError) { return console.error(pError); }
109
+ console.log('Animals:', pResult.rows);
110
+ });
111
+
112
+ // Parameterized query
113
+ tmpPool.query('SELECT * FROM "Animal" WHERE "Age" > $1', [3],
114
+ (pError, pResult) =>
115
+ {
116
+ if (pError) { return console.error(pError); }
117
+ console.log('Animals older than 3:', pResult.rows);
118
+ });
119
+ ```
120
+
121
+ ## Step 5: Use with Meadow
122
+
123
+ For full ORM capabilities, pair this connection with the Meadow data access layer:
124
+
125
+ ```javascript
126
+ const libMeadow = require('meadow');
127
+
128
+ let tmpAnimalMeadow = libMeadow.new(_Fable, 'Animal')
129
+ .setProvider('PostgreSQL')
130
+ .setDefaultIdentifier('IDAnimal')
131
+ .setSchema(
132
+ [
133
+ { Column: 'IDAnimal', Type: 'AutoIdentity' },
134
+ { Column: 'GUIDAnimal', Type: 'AutoGUID' },
135
+ { Column: 'Name', Type: 'String', Size: 128 },
136
+ { Column: 'Age', Type: 'Number' },
137
+ { Column: 'Weight', Type: 'Number' },
138
+ { Column: 'Description', Type: 'String' },
139
+ { Column: 'CreateDate', Type: 'CreateDate' },
140
+ { Column: 'Deleted', Type: 'Deleted' }
141
+ ]);
142
+
143
+ // Meadow handles CRUD through FoxHound's query dialect
144
+ let tmpQuery = tmpAnimalMeadow.query.addRecord(
145
+ {
146
+ Name: 'Luna',
147
+ Age: 5,
148
+ Weight: 4.5
149
+ });
150
+
151
+ tmpAnimalMeadow.doCreate(tmpQuery,
152
+ (pQuery) =>
153
+ {
154
+ console.log('Created with ID:', pQuery.parameters.result.value);
155
+ });
156
+ ```
157
+
158
+ ## Auto-Connect Mode
159
+
160
+ Skip the explicit `connectAsync()` call by enabling auto-connect:
161
+
162
+ ```javascript
163
+ let _Fable = new libFable(
164
+ {
165
+ "PostgreSQL":
166
+ {
167
+ "Server": "localhost",
168
+ "Port": 5432,
169
+ "User": "postgres",
170
+ "Password": "secret",
171
+ "Database": "myapp"
172
+ },
173
+ "MeadowConnectionPostgreSQLAutoConnect": true
174
+ });
175
+
176
+ _Fable.serviceManager.addAndInstantiateServiceType(
177
+ 'MeadowPostgreSQLProvider', libMeadowConnectionPostgreSQL);
178
+
179
+ // Already connected -- pool is ready
180
+ let tmpPool = _Fable.MeadowPostgreSQLProvider.pool;
181
+ ```
@@ -0,0 +1,62 @@
1
+ {
2
+ "Generated": "2026-03-02T00:47:16.544Z",
3
+ "GitHubOrg": "stevenvelozo",
4
+ "DefaultBranch": "master",
5
+ "Groups": [
6
+ {
7
+ "Name": "Dist",
8
+ "Key": "dist",
9
+ "Description": "",
10
+ "Modules": [
11
+ {
12
+ "Name": "indoctrinate_content_staging",
13
+ "Repo": "indoctrinate_content_staging",
14
+ "Group": "dist",
15
+ "Branch": "master",
16
+ "HasDocs": false,
17
+ "HasCover": false,
18
+ "Sidebar": [],
19
+ "DocFiles": []
20
+ }
21
+ ]
22
+ },
23
+ {
24
+ "Name": "Docs",
25
+ "Key": "docs",
26
+ "Description": "",
27
+ "Modules": [
28
+ {
29
+ "Name": "api",
30
+ "Repo": "api",
31
+ "Group": "docs",
32
+ "Branch": "master",
33
+ "HasDocs": true,
34
+ "HasCover": false,
35
+ "Sidebar": [],
36
+ "DocFiles": [
37
+ "api/connect.md",
38
+ "api/connectAsync.md",
39
+ "api/createTable.md",
40
+ "api/createTables.md",
41
+ "api/generateCreateTableStatement.md",
42
+ "api/generateDropTableStatement.md",
43
+ "api/pool.md",
44
+ "api/reference.md"
45
+ ]
46
+ },
47
+ {
48
+ "Name": "css",
49
+ "Repo": "css",
50
+ "Group": "docs",
51
+ "Branch": "master",
52
+ "HasDocs": true,
53
+ "HasCover": false,
54
+ "Sidebar": [],
55
+ "DocFiles": [
56
+ "css/docuserve.css"
57
+ ]
58
+ }
59
+ ]
60
+ }
61
+ ]
62
+ }