retold-harness 1.1.8 → 1.1.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "retold-harness",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
4
4
  "description": "Restful API harness. Serves on 8086.",
5
5
  "main": "source/Retold-Harness.js",
6
6
  "bin": {
@@ -36,24 +36,24 @@
36
36
  "homepage": "https://github.com/stevenvelozo/retold-harness#readme",
37
37
  "dependencies": {
38
38
  "blessed": "^0.1.81",
39
- "meadow-connection-dgraph": "^1.0.2",
40
- "meadow-connection-mongodb": "^1.0.2",
41
- "meadow-connection-mssql": "^1.0.20",
42
- "meadow-connection-mysql": "^1.0.14",
43
- "meadow-connection-postgresql": "^1.0.2",
44
- "meadow-connection-solr": "^1.0.2",
45
- "meadow-connection-sqlite": "^1.0.18",
46
- "meadow-integration": "^1.0.34",
47
- "orator-authentication": "^1.0.0",
48
- "pict": "^1.0.357",
39
+ "meadow-connection-dgraph": "^1.0.3",
40
+ "meadow-connection-mongodb": "^1.0.3",
41
+ "meadow-connection-mssql": "^1.0.22",
42
+ "meadow-connection-mysql": "^1.0.18",
43
+ "meadow-connection-postgresql": "^1.0.4",
44
+ "meadow-connection-solr": "^1.0.3",
45
+ "meadow-connection-sqlite": "^1.0.19",
46
+ "meadow-integration": "^1.0.38",
47
+ "orator-authentication": "^1.0.1",
48
+ "pict": "^1.0.365",
49
49
  "pict-application": "^1.0.33",
50
50
  "pict-terminalui": "^0.0.3",
51
- "pict-view": "^1.0.67",
52
- "retold-data-service": "^2.0.42"
51
+ "pict-view": "^1.0.68",
52
+ "retold-data-service": "^2.1.0"
53
53
  },
54
54
  "devDependencies": {
55
55
  "pict-docuserve": "^0.1.5",
56
- "quackage": "^1.1.0",
56
+ "quackage": "^1.1.2",
57
57
  "stricture": "^4.0.2",
58
58
  "supertest": "^7.2.2"
59
59
  },
@@ -774,13 +774,48 @@ function parseArgvHarnessPath()
774
774
  return null;
775
775
  }
776
776
 
777
+ /**
778
+ * Walk up from pStartPath looking for a retold monorepo layout that
779
+ * contains a retold-harness source checkout at
780
+ * `<root>/modules/meadow/retold-harness`. Returns the absolute path of
781
+ * the harness if found, or null.
782
+ *
783
+ * @param {string} pStartPath
784
+ * @returns {string|null}
785
+ */
786
+ function findRetoldSourceHarness(pStartPath)
787
+ {
788
+ let tmpCurrent = libPath.resolve(pStartPath);
789
+
790
+ while (true)
791
+ {
792
+ let tmpCandidate = libPath.join(tmpCurrent, 'modules', 'meadow', 'retold-harness');
793
+ if (isValidHarnessPath(tmpCandidate))
794
+ {
795
+ return tmpCandidate;
796
+ }
797
+
798
+ let tmpParent = libPath.dirname(tmpCurrent);
799
+ if (tmpParent === tmpCurrent)
800
+ {
801
+ return null;
802
+ }
803
+ tmpCurrent = tmpParent;
804
+ }
805
+ }
806
+
777
807
  /**
778
808
  * Resolve the retold-harness directory using a priority chain:
779
809
  *
780
810
  * 1. --harness-path CLI argument
781
811
  * 2. Current working directory (if it IS the harness)
782
812
  * 3. ./retold-harness child of CWD (running from parent directory)
783
- * 4. Parent of this source directory (management tool lives inside the harness)
813
+ * 4. Local retold-monorepo source checkout walking up from CWD
814
+ * (modules/meadow/retold-harness). Preferred over the installed
815
+ * node_modules copy so the tool always runs the live source.
816
+ * 5. Parent of this source directory (management tool lives inside the
817
+ * harness — matches both the source checkout when launched from
818
+ * there and the node_modules copy when installed elsewhere)
784
819
  *
785
820
  * @returns {string|null}
786
821
  */
@@ -807,10 +842,26 @@ function resolveHarnessPath()
807
842
  return tmpCwdChild;
808
843
  }
809
844
 
810
- // 4. Parent of this source directory (management tool lives inside retold-harness/source/management-tool/)
845
+ // 4. Local retold monorepo source (prefer over node_modules so we
846
+ // always run the editable checkout rather than a stale install)
847
+ let tmpSourceHarness = findRetoldSourceHarness(tmpCwd);
848
+ if (tmpSourceHarness)
849
+ {
850
+ return tmpSourceHarness;
851
+ }
852
+
853
+ // 5. Parent of this source directory (management tool lives inside retold-harness/source/management-tool/)
811
854
  let tmpParent = libPath.resolve(__dirname, '..');
812
855
  if (isValidHarnessPath(tmpParent))
813
856
  {
857
+ // If we're running from an installed node_modules copy, try to
858
+ // walk up further to locate a sibling retold monorepo and prefer
859
+ // that over the installed snapshot.
860
+ let tmpSourceFromInstall = findRetoldSourceHarness(tmpParent);
861
+ if (tmpSourceFromInstall && tmpSourceFromInstall !== tmpParent)
862
+ {
863
+ return tmpSourceFromInstall;
864
+ }
814
865
  return tmpParent;
815
866
  }
816
867
 
@@ -49,6 +49,84 @@ class RetoldHarnessMeadowProviderConfigurator extends libFableServiceProviderBas
49
49
  return fCallback('initializeSchema not implemented');
50
50
  }
51
51
 
52
+ /**
53
+ * Split a multi-statement SQL string into individual statements.
54
+ * Respects single-quoted string literals so that a ';' inside a
55
+ * string (e.g. 'Frankenstein; or, The Modern Prometheus') is not
56
+ * treated as a statement terminator. Recognizes both backslash
57
+ * escapes (\') and doubled single-quote escapes ('').
58
+ *
59
+ * @param {string} pSQL - The raw SQL text
60
+ * @returns {Array<string>} - Trimmed, non-empty, non-comment statements
61
+ */
62
+ splitSQLStatements(pSQL)
63
+ {
64
+ let tmpStatements = [];
65
+ let tmpCurrent = '';
66
+ let tmpInString = false;
67
+ let tmpEscape = false;
68
+
69
+ for (let i = 0; i < pSQL.length; i++)
70
+ {
71
+ let tmpChar = pSQL[i];
72
+
73
+ if (tmpInString)
74
+ {
75
+ tmpCurrent += tmpChar;
76
+
77
+ if (tmpEscape)
78
+ {
79
+ tmpEscape = false;
80
+ continue;
81
+ }
82
+
83
+ if (tmpChar === '\\')
84
+ {
85
+ tmpEscape = true;
86
+ continue;
87
+ }
88
+
89
+ if (tmpChar === "'")
90
+ {
91
+ // Handle doubled single-quote '' as an escape for a literal quote
92
+ if (pSQL[i + 1] === "'")
93
+ {
94
+ tmpCurrent += pSQL[i + 1];
95
+ i++;
96
+ continue;
97
+ }
98
+ tmpInString = false;
99
+ }
100
+ continue;
101
+ }
102
+
103
+ if (tmpChar === "'")
104
+ {
105
+ tmpInString = true;
106
+ tmpCurrent += tmpChar;
107
+ continue;
108
+ }
109
+
110
+ if (tmpChar === ';')
111
+ {
112
+ tmpStatements.push(tmpCurrent);
113
+ tmpCurrent = '';
114
+ continue;
115
+ }
116
+
117
+ tmpCurrent += tmpChar;
118
+ }
119
+
120
+ if (tmpCurrent.trim().length > 0)
121
+ {
122
+ tmpStatements.push(tmpCurrent);
123
+ }
124
+
125
+ return tmpStatements
126
+ .map((pStatement) => pStatement.trim())
127
+ .filter((pStatement) => pStatement.length > 0 && !pStatement.startsWith('--'));
128
+ }
129
+
52
130
  /**
53
131
  * Initialize the RetoldDataService with the schema provider's options.
54
132
  * This is a base implementation that works for all providers.
@@ -97,14 +97,16 @@ class RetoldHarnessProviderMSSQL extends libRetoldHarnessMeadowProviderConfigura
97
97
  tmpAnticipate.anticipate(
98
98
  (fStepComplete) =>
99
99
  {
100
- let tmpSeedCheckQuery = tmpSchemaProvider.getSeedCheckQuery();
100
+ let tmpSeedCheckTable = tmpSchemaProvider.getSeedCheckTable();
101
101
 
102
- if (!tmpSeedCheckQuery)
102
+ if (!tmpSeedCheckTable)
103
103
  {
104
- this.log.info('No seed check query provided, skipping seed.');
104
+ this.log.info('No seed check table provided, skipping seed.');
105
105
  return fStepComplete();
106
106
  }
107
107
 
108
+ let tmpSeedCheckQuery = `SELECT COUNT(*) AS cnt FROM [${tmpSeedCheckTable}]`;
109
+
108
110
  tmpPool.request().query(tmpSeedCheckQuery)
109
111
  .then(
110
112
  (pResult) =>
@@ -97,14 +97,16 @@ class RetoldHarnessProviderMySQL extends libRetoldHarnessMeadowProviderConfigura
97
97
  tmpAnticipate.anticipate(
98
98
  (fStepComplete) =>
99
99
  {
100
- let tmpSeedCheckQuery = tmpSchemaProvider.getSeedCheckQuery();
100
+ let tmpSeedCheckTable = tmpSchemaProvider.getSeedCheckTable();
101
101
 
102
- if (!tmpSeedCheckQuery)
102
+ if (!tmpSeedCheckTable)
103
103
  {
104
- this.log.info('No seed check query provided, skipping seed.');
104
+ this.log.info('No seed check table provided, skipping seed.');
105
105
  return fStepComplete();
106
106
  }
107
107
 
108
+ let tmpSeedCheckQuery = `SELECT COUNT(*) AS cnt FROM \`${tmpSeedCheckTable}\``;
109
+
108
110
  tmpPool.query(tmpSeedCheckQuery,
109
111
  (pError, pRows) =>
110
112
  {
@@ -134,9 +136,10 @@ class RetoldHarnessProviderMySQL extends libRetoldHarnessMeadowProviderConfigura
134
136
  this.log.info('Seeding initial data into MySQL...');
135
137
  let tmpSeedSQL = libFS.readFileSync(tmpSeedSQLPath, 'utf8');
136
138
 
137
- // MySQL supports multiple statements in a single query if multipleStatements is enabled
138
- // Split by semicolons and execute each statement
139
- let tmpStatements = tmpSeedSQL.split(';').map((pStatement) => pStatement.trim()).filter((pStatement) => pStatement.length > 0);
139
+ // Split into individual statements; splitSQLStatements
140
+ // respects string literals so embedded semicolons
141
+ // (e.g. in book titles) do not terminate a statement.
142
+ let tmpStatements = this.splitSQLStatements(tmpSeedSQL);
140
143
 
141
144
  let tmpStatementIndex = 0;
142
145
  let tmpExecuteNext = () =>
@@ -97,14 +97,19 @@ class RetoldHarnessProviderPostgreSQL extends libRetoldHarnessMeadowProviderConf
97
97
  tmpAnticipate.anticipate(
98
98
  (fStepComplete) =>
99
99
  {
100
- let tmpSeedCheckQuery = tmpSchemaProvider.getSeedCheckQuery();
100
+ let tmpSeedCheckTable = tmpSchemaProvider.getSeedCheckTable();
101
101
 
102
- if (!tmpSeedCheckQuery)
102
+ if (!tmpSeedCheckTable)
103
103
  {
104
- this.log.info('No seed check query provided, skipping seed.');
104
+ this.log.info('No seed check table provided, skipping seed.');
105
105
  return fStepComplete();
106
106
  }
107
107
 
108
+ // PostgreSQL folds unquoted identifiers to lowercase; the
109
+ // Meadow PG connector creates mixed-case tables, so we must
110
+ // quote the identifier here or the lookup will miss.
111
+ let tmpSeedCheckQuery = `SELECT COUNT(*) AS cnt FROM "${tmpSeedCheckTable}"`;
112
+
108
113
  tmpPool.query(tmpSeedCheckQuery,
109
114
  (pError, pResult) =>
110
115
  {
@@ -134,8 +139,10 @@ class RetoldHarnessProviderPostgreSQL extends libRetoldHarnessMeadowProviderConf
134
139
  this.log.info('Seeding initial data into PostgreSQL...');
135
140
  let tmpSeedSQL = libFS.readFileSync(tmpSeedSQLPath, 'utf8');
136
141
 
137
- // Split by semicolons and execute each statement
138
- let tmpStatements = tmpSeedSQL.split(';').map((pStatement) => pStatement.trim()).filter((pStatement) => pStatement.length > 0 && !pStatement.startsWith('--'));
142
+ // Split into individual statements; splitSQLStatements
143
+ // respects string literals so embedded semicolons do
144
+ // not terminate a statement prematurely.
145
+ let tmpStatements = this.splitSQLStatements(tmpSeedSQL);
139
146
 
140
147
  let tmpStatementIndex = 0;
141
148
  let tmpExecuteNext = () =>
@@ -25,9 +25,9 @@ class RetoldHarnessSchemaBookstore extends libRetoldHarnessSchemaProvider
25
25
  return libPath.join(__dirname, 'bookstore');
26
26
  }
27
27
 
28
- getSeedCheckQuery()
28
+ getSeedCheckTable()
29
29
  {
30
- return 'SELECT COUNT(*) AS cnt FROM Book';
30
+ return 'Book';
31
31
  }
32
32
 
33
33
  generateTables(pDB, fCallback)
@@ -27,9 +27,9 @@ class RetoldHarnessSchemaEntertainment extends libRetoldHarnessSchemaProvider
27
27
  return libPath.join(__dirname, 'entertainment');
28
28
  }
29
29
 
30
- getSeedCheckQuery()
30
+ getSeedCheckTable()
31
31
  {
32
- return 'SELECT COUNT(*) AS cnt FROM DataSource';
32
+ return 'DataSource';
33
33
  }
34
34
 
35
35
  generateTables(pDB, fCallback)
@@ -26,9 +26,9 @@ class RetoldHarnessSchemaUSFederalData extends libRetoldHarnessSchemaProvider
26
26
  return libPath.join(__dirname, 'us-federal-data');
27
27
  }
28
28
 
29
- getSeedCheckQuery()
29
+ getSeedCheckTable()
30
30
  {
31
- return 'SELECT COUNT(*) AS cnt FROM CensusRegion';
31
+ return 'CensusRegion';
32
32
  }
33
33
 
34
34
  generateTables(pDB, fCallback)
@@ -84,13 +84,16 @@ class RetoldHarnessSchemaProvider extends libFableServiceProviderBase
84
84
  }
85
85
 
86
86
  /**
87
- * Get a SQL query that checks whether seed data has already been loaded.
88
- * Should return a query that produces a { cnt } result where cnt > 0
89
- * means data exists. Return false if seeding should always run.
87
+ * Get the unquoted name of a table whose row count indicates whether
88
+ * seed data has already been loaded. Each SQL provider quotes the
89
+ * identifier per its own convention (backticks, double-quotes, brackets)
90
+ * so case-sensitive engines like PostgreSQL find the table.
90
91
  *
91
- * @returns {string|boolean} SQL query string or false
92
+ * Return false if seeding should always run.
93
+ *
94
+ * @returns {string|boolean} Table name or false
92
95
  */
93
- getSeedCheckQuery()
96
+ getSeedCheckTable()
94
97
  {
95
98
  return false;
96
99
  }
@@ -124,7 +124,7 @@ $Country 64
124
124
  &DeleteDate
125
125
  #DeletingIDUser
126
126
  $Name 200
127
- $Description 1024
127
+ *Description
128
128
  $ContactName 200
129
129
  $ContactEmail 200
130
130
  $ContactPhone 32
@@ -3385,8 +3385,7 @@
3385
3385
  },
3386
3386
  {
3387
3387
  "Column": "Description",
3388
- "DataType": "String",
3389
- "Size": "1024"
3388
+ "DataType": "Text"
3390
3389
  },
3391
3390
  {
3392
3391
  "Column": "ContactName",
@@ -3492,7 +3491,7 @@
3492
3491
  {
3493
3492
  "Column": "Description",
3494
3493
  "Type": "String",
3495
- "Size": "1024"
3494
+ "Size": "Default"
3496
3495
  },
3497
3496
  {
3498
3497
  "Column": "ContactName",
@@ -3608,7 +3607,7 @@
3608
3607
  },
3609
3608
  "Description": {
3610
3609
  "type": "string",
3611
- "size": "1024"
3610
+ "size": "Default"
3612
3611
  },
3613
3612
  "ContactName": {
3614
3613
  "type": "string",
@@ -3701,8 +3700,7 @@
3701
3700
  },
3702
3701
  {
3703
3702
  "Column": "Description",
3704
- "DataType": "String",
3705
- "Size": "1024"
3703
+ "DataType": "Text"
3706
3704
  },
3707
3705
  {
3708
3706
  "Column": "ContactName",
@@ -3808,7 +3806,7 @@
3808
3806
  {
3809
3807
  "Column": "Description",
3810
3808
  "Type": "String",
3811
- "Size": "1024"
3809
+ "Size": "Default"
3812
3810
  },
3813
3811
  {
3814
3812
  "Column": "ContactName",
@@ -3924,7 +3922,7 @@
3924
3922
  },
3925
3923
  "Description": {
3926
3924
  "type": "string",
3927
- "size": "1024"
3925
+ "size": "Default"
3928
3926
  },
3929
3927
  "ContactName": {
3930
3928
  "type": "string",
@@ -576,8 +576,7 @@
576
576
  },
577
577
  {
578
578
  "Column": "Description",
579
- "DataType": "String",
580
- "Size": "1024"
579
+ "DataType": "Text"
581
580
  },
582
581
  {
583
582
  "Column": "ContactName",
@@ -3385,8 +3385,7 @@
3385
3385
  },
3386
3386
  {
3387
3387
  "Column": "Description",
3388
- "DataType": "String",
3389
- "Size": "1024"
3388
+ "DataType": "Text"
3390
3389
  },
3391
3390
  {
3392
3391
  "Column": "ContactName",
@@ -3492,7 +3491,7 @@
3492
3491
  {
3493
3492
  "Column": "Description",
3494
3493
  "Type": "String",
3495
- "Size": "1024"
3494
+ "Size": "Default"
3496
3495
  },
3497
3496
  {
3498
3497
  "Column": "ContactName",
@@ -3608,7 +3607,7 @@
3608
3607
  },
3609
3608
  "Description": {
3610
3609
  "type": "string",
3611
- "size": "1024"
3610
+ "size": "Default"
3612
3611
  },
3613
3612
  "ContactName": {
3614
3613
  "type": "string",
@@ -3701,8 +3700,7 @@
3701
3700
  },
3702
3701
  {
3703
3702
  "Column": "Description",
3704
- "DataType": "String",
3705
- "Size": "1024"
3703
+ "DataType": "Text"
3706
3704
  },
3707
3705
  {
3708
3706
  "Column": "ContactName",
@@ -3808,7 +3806,7 @@
3808
3806
  {
3809
3807
  "Column": "Description",
3810
3808
  "Type": "String",
3811
- "Size": "1024"
3809
+ "Size": "Default"
3812
3810
  },
3813
3811
  {
3814
3812
  "Column": "ContactName",
@@ -3924,7 +3922,7 @@
3924
3922
  },
3925
3923
  "Description": {
3926
3924
  "type": "string",
3927
- "size": "1024"
3925
+ "size": "Default"
3928
3926
  },
3929
3927
  "ContactName": {
3930
3928
  "type": "string",