meadow 2.0.38 → 2.0.40
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/docs/_brand.json +18 -0
- package/docs/index.html +0 -2
- package/package.json +4 -5
- package/source/behaviors/Meadow-Create.js +6 -2
- package/source/providers/Meadow-Provider-ALASQL.js +61 -17
- package/source/providers/Meadow-Provider-SQLite.js +84 -6
- package/test/Meadow-Provider-ALASQL.js +29 -20
- package/test/Meadow-Provider-SQLite_tests.js +243 -1
- package/docs/css/docuserve.css +0 -327
package/docs/_brand.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Hash": "meadow",
|
|
3
|
+
"Name": "Meadow",
|
|
4
|
+
"Tagline": "Provider-agnostic data broker with CRUD operations, audit tracking, and soft deletes across 8 database engines",
|
|
5
|
+
"Palette": "mix",
|
|
6
|
+
"Icon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 96 96\" width=\"96\" height=\"96\">\n\t\t<defs>\n\t\t\t<clipPath id=\"frame-meadow-filled-light\">\n\t\t\t\t<path d=\"M 24 2\n\t\tH 72\n\t\tQ 94 2 94 24\n\t\tV 72\n\t\tQ 94 94 72 94\n\t\tH 24\n\t\tQ 2 94 2 72\n\t\tV 24\n\t\tQ 2 2 24 2 Z\"/>\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<path d=\"M 24 2\n\t\tH 72\n\t\tQ 94 2 94 24\n\t\tV 72\n\t\tQ 94 94 72 94\n\t\tH 24\n\t\tQ 2 94 2 72\n\t\tV 24\n\t\tQ 2 2 24 2 Z\" fill=\"#31cae2\"/>\n\t\t<g clip-path=\"url(#frame-meadow-filled-light)\"><path d=\"M 32.75 15.39 A 36 36 0 0 1 63.25 80.61 Z\" fill=\"rgba(255,255,255,0.18)\" opacity=\"0.95\"/>\n\t\t\t\t\t<path d=\"M 32.75 15.39 A 36 36 0 0 0 63.25 80.61 Z\" fill=\"#dd9166\" opacity=\"0.95\"/></g>\n\t\t<text x=\"48\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\"\n\t\t\tfont-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n\t\t\tfont-size=\"56\" font-weight=\"600\"\n\t\t\tfill=\"#ffffff\" letter-spacing=\"-1\">M</text>\n\t</svg>",
|
|
7
|
+
"IconType": "svg",
|
|
8
|
+
"Favicon": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 96 96\" width=\"96\" height=\"96\">\n\t\t<defs>\n\t\t\t<clipPath id=\"fav-meadow-light\">\n\t\t\t\t<path d=\"M 24 2\n\t\tH 72\n\t\tQ 94 2 94 24\n\t\tV 72\n\t\tQ 94 94 72 94\n\t\tH 24\n\t\tQ 2 94 2 72\n\t\tV 24\n\t\tQ 2 2 24 2 Z\"/>\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<path d=\"M 24 2\n\t\tH 72\n\t\tQ 94 2 94 24\n\t\tV 72\n\t\tQ 94 94 72 94\n\t\tH 24\n\t\tQ 2 94 2 72\n\t\tV 24\n\t\tQ 2 2 24 2 Z\" fill=\"#31cae2\"/>\n\t\t<g clip-path=\"url(#fav-meadow-light)\"><circle cx=\"48\" cy=\"48\" r=\"40\" fill=\"rgba(255,255,255,0.22)\"/></g>\n\t\t<text x=\"48\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\"\n\t\t\tfont-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n\t\t\tfont-size=\"60\" font-weight=\"800\"\n\t\t\tfill=\"#ffffff\" letter-spacing=\"-1\">M</text>\n\t</svg>",
|
|
9
|
+
"FaviconDark": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 96 96\" width=\"96\" height=\"96\">\n\t\t<defs>\n\t\t\t<clipPath id=\"fav-meadow-dark\">\n\t\t\t\t<path d=\"M 24 2\n\t\tH 72\n\t\tQ 94 2 94 24\n\t\tV 72\n\t\tQ 94 94 72 94\n\t\tH 24\n\t\tQ 2 94 2 72\n\t\tV 24\n\t\tQ 2 2 24 2 Z\"/>\n\t\t\t</clipPath>\n\t\t</defs>\n\t\t<path d=\"M 24 2\n\t\tH 72\n\t\tQ 94 2 94 24\n\t\tV 72\n\t\tQ 94 94 72 94\n\t\tH 24\n\t\tQ 2 94 2 72\n\t\tV 24\n\t\tQ 2 2 24 2 Z\" fill=\"#86dce9\"/>\n\t\t<g clip-path=\"url(#fav-meadow-dark)\"><circle cx=\"48\" cy=\"48\" r=\"40\" fill=\"rgba(255,255,255,0.22)\"/></g>\n\t\t<text x=\"48\" y=\"50\" text-anchor=\"middle\" dominant-baseline=\"central\"\n\t\t\tfont-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\"\n\t\t\tfont-size=\"60\" font-weight=\"800\"\n\t\t\tfill=\"#101418\" letter-spacing=\"-1\">M</text>\n\t</svg>",
|
|
10
|
+
"Colors": {
|
|
11
|
+
"Primary": "#31cae2",
|
|
12
|
+
"Secondary": "#dd9166",
|
|
13
|
+
"PrimaryLight": "#31cae2",
|
|
14
|
+
"PrimaryDark": "#86dce9",
|
|
15
|
+
"SecondaryLight": "#dd9166",
|
|
16
|
+
"SecondaryDark": "#e9c4af"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/docs/index.html
CHANGED
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
|
|
9
9
|
<title>Meadow v2.0.33 Documentation</title>
|
|
10
10
|
|
|
11
|
-
<!-- Application Stylesheet -->
|
|
12
|
-
<link href="css/docuserve.css" rel="stylesheet">
|
|
13
11
|
<!-- KaTeX stylesheet for LaTeX equation rendering -->
|
|
14
12
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
|
|
15
13
|
<!-- PICT Dynamic View CSS Container -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meadow",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.40",
|
|
4
4
|
"description": "A data access library.",
|
|
5
5
|
"main": "source/Meadow.js",
|
|
6
6
|
"scripts": {
|
|
@@ -81,9 +81,8 @@
|
|
|
81
81
|
"homepage": "https://github.com/stevenvelozo/meadow",
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"alasql": "^4.17.0",
|
|
84
|
-
"better-sqlite3": "^12.6.2",
|
|
85
84
|
"dgraph-js-http": "^21.3.0",
|
|
86
|
-
"fable": "^3.1.
|
|
85
|
+
"fable": "^3.1.72",
|
|
87
86
|
"gulp-util": "^3.0.8",
|
|
88
87
|
"meadow-connection-dgraph": "^1.0.3",
|
|
89
88
|
"meadow-connection-mongodb": "^1.0.3",
|
|
@@ -92,11 +91,11 @@
|
|
|
92
91
|
"meadow-connection-postgresql": "^1.0.5",
|
|
93
92
|
"meadow-connection-rocksdb": "^1.0.0",
|
|
94
93
|
"meadow-connection-solr": "^1.0.3",
|
|
95
|
-
"meadow-connection-sqlite": "^1.0.
|
|
94
|
+
"meadow-connection-sqlite": "^1.0.20",
|
|
96
95
|
"meadow-connection-sqlite-browser": "^1.0.2",
|
|
97
96
|
"mongodb": "^6.12.0",
|
|
98
97
|
"mysql2": "^3.18.2",
|
|
99
|
-
"pict-docuserve": "^0.1
|
|
98
|
+
"pict-docuserve": "^1.0.1",
|
|
100
99
|
"puppeteer": "^24.38.0",
|
|
101
100
|
"quackage": "^1.2.3",
|
|
102
101
|
"solr-client": "^0.9.0"
|
|
@@ -45,9 +45,13 @@ var meadowBehaviorCreate = function(pMeadow, pQuery, fCallBack)
|
|
|
45
45
|
{
|
|
46
46
|
pMeadow.fable.log.warn('Error during the unique-constraint pre-flight',
|
|
47
47
|
{ Error: pPreflightError, Message: pPreflightError && pPreflightError.message });
|
|
48
|
-
return fStageComplete
|
|
48
|
+
return setImmediate(fStageComplete, pPreflightError, pQuery, false);
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
// Defer via setImmediate so synchronous providers (node:sqlite,
|
|
51
|
+
// previously better-sqlite3) cannot stack-accumulate frames
|
|
52
|
+
// across a long synchronous chain of Creates — every record's
|
|
53
|
+
// pre-flight returns to the event loop before the INSERT runs.
|
|
54
|
+
return setImmediate(fStageComplete);
|
|
51
55
|
});
|
|
52
56
|
},
|
|
53
57
|
// Step 1: Create the record in the data source
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* @author <steven@velozo.com>
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
var libAsyncEachSeries = require('async/eachSeries');
|
|
8
|
+
|
|
7
9
|
var MeadowProvider = function()
|
|
8
10
|
{
|
|
9
11
|
function createNew(pFable)
|
|
@@ -436,7 +438,25 @@ var MeadowProvider = function()
|
|
|
436
438
|
* Data: [] (optional array of records, one object each)
|
|
437
439
|
* }
|
|
438
440
|
*/
|
|
439
|
-
|
|
441
|
+
/**
|
|
442
|
+
* Construct an ALASQL-backed meadow from a JS object array.
|
|
443
|
+
*
|
|
444
|
+
* Signatures:
|
|
445
|
+
* constructFromObject(pParameters) — bindObject path only;
|
|
446
|
+
* returns meadow synchronously
|
|
447
|
+
* constructFromObject(pParameters, fCallback) — Import path (doCreate
|
|
448
|
+
* per record); meadow + records
|
|
449
|
+
* delivered via fCallback
|
|
450
|
+
*
|
|
451
|
+
* The Import path drives `doCreate` per record, which is no longer
|
|
452
|
+
* guaranteed to complete in the same tick (Meadow-Create's pre-flight
|
|
453
|
+
* yields via setImmediate to keep bulk-create chains stack-safe on
|
|
454
|
+
* synchronous providers). Callers that need the imported records
|
|
455
|
+
* queryable on return must pass a callback. Without a callback the
|
|
456
|
+
* Import path is rejected — synchronous fire-and-forget imports were
|
|
457
|
+
* a latent footgun and are no longer supported.
|
|
458
|
+
*/
|
|
459
|
+
var constructFromObject = (pParameters, fCallback) =>
|
|
440
460
|
{
|
|
441
461
|
if ((typeof(pParameters) !== 'object') || (typeof(pParameters.Meadow) !== 'object'))
|
|
442
462
|
return false;
|
|
@@ -452,12 +472,12 @@ var MeadowProvider = function()
|
|
|
452
472
|
pParameters.Import = true;
|
|
453
473
|
if (!Array.isArray(pParameters.Data))
|
|
454
474
|
pParameters.Data = [];
|
|
455
|
-
|
|
475
|
+
|
|
456
476
|
// Construct a meadow
|
|
457
477
|
var tmpMeadow = pParameters.Meadow
|
|
458
478
|
.new(_Fable, pParameters.Scope)
|
|
459
479
|
.setProvider('ALASQL');
|
|
460
|
-
|
|
480
|
+
|
|
461
481
|
var tmpSchema = [];
|
|
462
482
|
var tmpDefaultIdentifier;
|
|
463
483
|
|
|
@@ -489,7 +509,7 @@ var MeadowProvider = function()
|
|
|
489
509
|
case "function":
|
|
490
510
|
// Do nothing with these types of properties
|
|
491
511
|
break;
|
|
492
|
-
|
|
512
|
+
|
|
493
513
|
case "boolean":
|
|
494
514
|
tmpSchema.push({ Column: tmpProperty, Type:"Boolean" });
|
|
495
515
|
break;
|
|
@@ -499,7 +519,7 @@ var MeadowProvider = function()
|
|
|
499
519
|
case "string":
|
|
500
520
|
tmpSchema.push({ Column: tmpProperty, Type:"Text" });
|
|
501
521
|
break;
|
|
502
|
-
|
|
522
|
+
|
|
503
523
|
default:
|
|
504
524
|
break;
|
|
505
525
|
}
|
|
@@ -516,23 +536,47 @@ var MeadowProvider = function()
|
|
|
516
536
|
// Now import the data
|
|
517
537
|
if(pParameters.Import)
|
|
518
538
|
{
|
|
519
|
-
|
|
539
|
+
if (typeof(fCallback) !== 'function')
|
|
520
540
|
{
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
{
|
|
524
|
-
// Maybe log the error?
|
|
525
|
-
_Fable.log.trace('Auto imported record', pRecord);
|
|
526
|
-
}
|
|
527
|
-
);
|
|
541
|
+
_Fable.log.error('Meadow-Provider-ALASQL: constructFromObject called with Import=true but no callback — doCreate is no longer synchronous, callers must await record import.');
|
|
542
|
+
return false;
|
|
528
543
|
}
|
|
544
|
+
|
|
545
|
+
libAsyncEachSeries(
|
|
546
|
+
pParameters.Data,
|
|
547
|
+
function (pRecord, fNext)
|
|
548
|
+
{
|
|
549
|
+
tmpMeadow.doCreate(tmpMeadow.query.clone().addRecord(pRecord),
|
|
550
|
+
function (pError, pQuery, pQueryRead, pCreated)
|
|
551
|
+
{
|
|
552
|
+
if (pError)
|
|
553
|
+
{
|
|
554
|
+
_Fable.log.warn('Meadow-Provider-ALASQL: auto-import doCreate failed', { Error: pError, Record: pRecord });
|
|
555
|
+
}
|
|
556
|
+
else
|
|
557
|
+
{
|
|
558
|
+
_Fable.log.trace('Auto imported record', pCreated);
|
|
559
|
+
}
|
|
560
|
+
// Errors are logged but not propagated — the historical
|
|
561
|
+
// fire-and-forget loop swallowed them, preserve that
|
|
562
|
+
// shape so partial imports still return a usable meadow.
|
|
563
|
+
return fNext();
|
|
564
|
+
});
|
|
565
|
+
},
|
|
566
|
+
function ()
|
|
567
|
+
{
|
|
568
|
+
return fCallback(null, tmpMeadow);
|
|
569
|
+
});
|
|
570
|
+
return tmpMeadow;
|
|
529
571
|
}
|
|
530
|
-
|
|
572
|
+
|
|
573
|
+
// Just assign the object
|
|
574
|
+
tmpMeadow.provider.bindObject(pParameters.Data);
|
|
575
|
+
|
|
576
|
+
if (typeof(fCallback) === 'function')
|
|
531
577
|
{
|
|
532
|
-
|
|
533
|
-
tmpMeadow.provider.bindObject(pParameters.Data);
|
|
578
|
+
fCallback(null, tmpMeadow);
|
|
534
579
|
}
|
|
535
|
-
|
|
536
580
|
return tmpMeadow;
|
|
537
581
|
};
|
|
538
582
|
|
|
@@ -105,6 +105,54 @@ var MeadowProvider = function ()
|
|
|
105
105
|
return pParams;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Filter a named-parameters object down to only those keys whose
|
|
110
|
+
* `:name` form appears in the SQL body.
|
|
111
|
+
*
|
|
112
|
+
* node:sqlite is strict about unknown named parameters — passing
|
|
113
|
+
* a key the prepared statement doesn't reference throws
|
|
114
|
+
* `Unknown named parameter '<name>'`. better-sqlite3 was lenient
|
|
115
|
+
* about extras, so this regressed silently when
|
|
116
|
+
* meadow-connection-sqlite switched to node:sqlite. It matters
|
|
117
|
+
* whenever Meadow.rawQueries.loadQuery installs an override SQL
|
|
118
|
+
* whose placeholders are a strict subset of meadow's auto-
|
|
119
|
+
* generated params (filter columns, soft-delete tracking,
|
|
120
|
+
* AutoGUID conflict-check), but the filter is cheap and applied
|
|
121
|
+
* uniformly — also protects any future case where the auto-built
|
|
122
|
+
* SQL and the params dict drift apart.
|
|
123
|
+
*
|
|
124
|
+
* Approach mirrors `convertNamedToPositional` in the PostgreSQL
|
|
125
|
+
* provider: walk the SQL with /:([A-Za-z_][A-Za-z0-9_]*)/g, keep
|
|
126
|
+
* names that exist in the params dict, leave the literal alone
|
|
127
|
+
* otherwise (handles `::typecast`-style sequences without
|
|
128
|
+
* tripping). Over-matches inside string literals are harmless
|
|
129
|
+
* because we only ever *keep* params — never invent them.
|
|
130
|
+
*/
|
|
131
|
+
var filterUsedNamedParams = function (pQueryBody, pNamedParams)
|
|
132
|
+
{
|
|
133
|
+
if (typeof (pNamedParams) !== 'object' || pNamedParams === null)
|
|
134
|
+
{
|
|
135
|
+
return pNamedParams;
|
|
136
|
+
}
|
|
137
|
+
if (typeof (pQueryBody) !== 'string')
|
|
138
|
+
{
|
|
139
|
+
return pNamedParams;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
var tmpUsed = {};
|
|
143
|
+
var tmpPattern = /:([A-Za-z_][A-Za-z0-9_]*)/g;
|
|
144
|
+
var tmpMatch;
|
|
145
|
+
while ((tmpMatch = tmpPattern.exec(pQueryBody)) !== null)
|
|
146
|
+
{
|
|
147
|
+
var tmpName = tmpMatch[1];
|
|
148
|
+
if (Object.prototype.hasOwnProperty.call(pNamedParams, tmpName))
|
|
149
|
+
{
|
|
150
|
+
tmpUsed[tmpName] = pNamedParams[tmpName];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return tmpUsed;
|
|
154
|
+
};
|
|
155
|
+
|
|
108
156
|
// The Meadow marshaller passes in the Schema as the third parameter for JSON/JSONProxy deserialization.
|
|
109
157
|
var marshalRecordFromSourceToObject = function (pObject, pRecord, pSchema)
|
|
110
158
|
{
|
|
@@ -173,6 +221,11 @@ var MeadowProvider = function ()
|
|
|
173
221
|
|
|
174
222
|
var tmpQueryBody = fixDateFunctions(pQuery.query.body);
|
|
175
223
|
coerceParameters(pQuery.query.parameters);
|
|
224
|
+
// Filter to keys actually referenced as :name in the SQL —
|
|
225
|
+
// node:sqlite is strict about unknown named parameters, and
|
|
226
|
+
// meadow auto-generates filter/Deleted/AutoGUID params that
|
|
227
|
+
// a rawQueries override may not reference.
|
|
228
|
+
var tmpBindParams = filterUsedNamedParams(tmpQueryBody, pQuery.query.parameters);
|
|
176
229
|
|
|
177
230
|
if (pQuery.logLevel > 0)
|
|
178
231
|
{
|
|
@@ -190,7 +243,7 @@ var MeadowProvider = function ()
|
|
|
190
243
|
}
|
|
191
244
|
|
|
192
245
|
var tmpStatement = tmpDB.prepare(tmpQueryBody);
|
|
193
|
-
var tmpInfo = tmpStatement.run(
|
|
246
|
+
var tmpInfo = tmpStatement.run(tmpBindParams);
|
|
194
247
|
|
|
195
248
|
tmpResult.error = null;
|
|
196
249
|
tmpResult.value = false;
|
|
@@ -222,6 +275,11 @@ var MeadowProvider = function ()
|
|
|
222
275
|
|
|
223
276
|
var tmpQueryBody = fixDateFunctions(pQuery.query.body);
|
|
224
277
|
coerceParameters(pQuery.query.parameters);
|
|
278
|
+
// Filter to keys actually referenced as :name in the SQL —
|
|
279
|
+
// node:sqlite is strict about unknown named parameters, and
|
|
280
|
+
// meadow auto-generates filter/Deleted/AutoGUID params that
|
|
281
|
+
// a rawQueries override may not reference.
|
|
282
|
+
var tmpBindParams = filterUsedNamedParams(tmpQueryBody, pQuery.query.parameters);
|
|
225
283
|
|
|
226
284
|
if (pQuery.logLevel > 0)
|
|
227
285
|
{
|
|
@@ -239,7 +297,7 @@ var MeadowProvider = function ()
|
|
|
239
297
|
}
|
|
240
298
|
|
|
241
299
|
var tmpStatement = tmpDB.prepare(tmpQueryBody);
|
|
242
|
-
var tmpRows = tmpStatement.all(
|
|
300
|
+
var tmpRows = tmpStatement.all(tmpBindParams);
|
|
243
301
|
|
|
244
302
|
tmpResult.error = null;
|
|
245
303
|
tmpResult.value = tmpRows;
|
|
@@ -262,6 +320,11 @@ var MeadowProvider = function ()
|
|
|
262
320
|
|
|
263
321
|
var tmpQueryBody = fixDateFunctions(pQuery.query.body);
|
|
264
322
|
coerceParameters(pQuery.query.parameters);
|
|
323
|
+
// Filter to keys actually referenced as :name in the SQL —
|
|
324
|
+
// node:sqlite is strict about unknown named parameters, and
|
|
325
|
+
// meadow auto-generates filter/Deleted/AutoGUID params that
|
|
326
|
+
// a rawQueries override may not reference.
|
|
327
|
+
var tmpBindParams = filterUsedNamedParams(tmpQueryBody, pQuery.query.parameters);
|
|
265
328
|
|
|
266
329
|
if (pQuery.logLevel > 0)
|
|
267
330
|
{
|
|
@@ -279,7 +342,7 @@ var MeadowProvider = function ()
|
|
|
279
342
|
}
|
|
280
343
|
|
|
281
344
|
var tmpStatement = tmpDB.prepare(tmpQueryBody);
|
|
282
|
-
var tmpInfo = tmpStatement.run(
|
|
345
|
+
var tmpInfo = tmpStatement.run(tmpBindParams);
|
|
283
346
|
|
|
284
347
|
tmpResult.error = null;
|
|
285
348
|
tmpResult.value = tmpInfo;
|
|
@@ -302,6 +365,11 @@ var MeadowProvider = function ()
|
|
|
302
365
|
|
|
303
366
|
var tmpQueryBody = fixDateFunctions(pQuery.query.body);
|
|
304
367
|
coerceParameters(pQuery.query.parameters);
|
|
368
|
+
// Filter to keys actually referenced as :name in the SQL —
|
|
369
|
+
// node:sqlite is strict about unknown named parameters, and
|
|
370
|
+
// meadow auto-generates filter/Deleted/AutoGUID params that
|
|
371
|
+
// a rawQueries override may not reference.
|
|
372
|
+
var tmpBindParams = filterUsedNamedParams(tmpQueryBody, pQuery.query.parameters);
|
|
305
373
|
|
|
306
374
|
if (pQuery.logLevel > 0)
|
|
307
375
|
{
|
|
@@ -319,7 +387,7 @@ var MeadowProvider = function ()
|
|
|
319
387
|
}
|
|
320
388
|
|
|
321
389
|
var tmpStatement = tmpDB.prepare(tmpQueryBody);
|
|
322
|
-
var tmpInfo = tmpStatement.run(
|
|
390
|
+
var tmpInfo = tmpStatement.run(tmpBindParams);
|
|
323
391
|
|
|
324
392
|
tmpResult.error = null;
|
|
325
393
|
tmpResult.value = false;
|
|
@@ -350,6 +418,11 @@ var MeadowProvider = function ()
|
|
|
350
418
|
|
|
351
419
|
var tmpQueryBody = fixDateFunctions(pQuery.query.body);
|
|
352
420
|
coerceParameters(pQuery.query.parameters);
|
|
421
|
+
// Filter to keys actually referenced as :name in the SQL —
|
|
422
|
+
// node:sqlite is strict about unknown named parameters, and
|
|
423
|
+
// meadow auto-generates filter/Deleted/AutoGUID params that
|
|
424
|
+
// a rawQueries override may not reference.
|
|
425
|
+
var tmpBindParams = filterUsedNamedParams(tmpQueryBody, pQuery.query.parameters);
|
|
353
426
|
|
|
354
427
|
if (pQuery.logLevel > 0)
|
|
355
428
|
{
|
|
@@ -367,7 +440,7 @@ var MeadowProvider = function ()
|
|
|
367
440
|
}
|
|
368
441
|
|
|
369
442
|
var tmpStatement = tmpDB.prepare(tmpQueryBody);
|
|
370
|
-
var tmpInfo = tmpStatement.run(
|
|
443
|
+
var tmpInfo = tmpStatement.run(tmpBindParams);
|
|
371
444
|
|
|
372
445
|
tmpResult.error = null;
|
|
373
446
|
tmpResult.value = false;
|
|
@@ -398,6 +471,11 @@ var MeadowProvider = function ()
|
|
|
398
471
|
|
|
399
472
|
var tmpQueryBody = fixDateFunctions(pQuery.query.body);
|
|
400
473
|
coerceParameters(pQuery.query.parameters);
|
|
474
|
+
// Filter to keys actually referenced as :name in the SQL —
|
|
475
|
+
// node:sqlite is strict about unknown named parameters, and
|
|
476
|
+
// meadow auto-generates filter/Deleted/AutoGUID params that
|
|
477
|
+
// a rawQueries override may not reference.
|
|
478
|
+
var tmpBindParams = filterUsedNamedParams(tmpQueryBody, pQuery.query.parameters);
|
|
401
479
|
|
|
402
480
|
if (pQuery.logLevel > 0)
|
|
403
481
|
{
|
|
@@ -415,7 +493,7 @@ var MeadowProvider = function ()
|
|
|
415
493
|
}
|
|
416
494
|
|
|
417
495
|
var tmpStatement = tmpDB.prepare(tmpQueryBody);
|
|
418
|
-
var tmpRows = tmpStatement.all(
|
|
496
|
+
var tmpRows = tmpStatement.all(tmpBindParams);
|
|
419
497
|
|
|
420
498
|
tmpResult.executed = true;
|
|
421
499
|
tmpResult.error = null;
|
|
@@ -968,26 +968,35 @@ suite
|
|
|
968
968
|
{Brand:'Puma', Style:'Lowtop', Size:15, SKU:'dsa33234'}
|
|
969
969
|
];
|
|
970
970
|
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
971
|
+
// constructFromObject's Import path drives doCreate per record,
|
|
972
|
+
// which is no longer synchronous (Meadow-Create defers its
|
|
973
|
+
// pre-flight callback via setImmediate to keep bulk-create
|
|
974
|
+
// chains stack-safe on sync providers). Pass a callback so
|
|
975
|
+
// records are queryable before the subsequent doReads runs.
|
|
976
|
+
require('../source/Meadow.js')
|
|
977
|
+
.new(libFable, 'Master')
|
|
978
|
+
.setProvider('ALASQL')
|
|
979
|
+
.provider.constructFromObject(
|
|
980
|
+
{
|
|
981
|
+
Meadow: require('../source/Meadow.js').new(libFable, 'Master'),
|
|
982
|
+
Scope: 'ShoeCity',
|
|
983
|
+
ObjectPrototype: myData[0],
|
|
984
|
+
Data: myData
|
|
985
|
+
},
|
|
986
|
+
function (pImportError, testMeadow)
|
|
987
|
+
{
|
|
988
|
+
Expect(pImportError, pImportError && pImportError.message).to.not.exist;
|
|
989
|
+
|
|
990
|
+
testMeadow.doReads(testMeadow.query.clone().addFilter('SKU', 'dsa33234'),
|
|
991
|
+
function(pError, pQuery, pRecords)
|
|
992
|
+
{
|
|
993
|
+
// We should have a record ....
|
|
994
|
+
Expect(pRecords[0].Style)
|
|
995
|
+
.to.equal('Lowtop');
|
|
996
|
+
fDone();
|
|
997
|
+
}
|
|
998
|
+
);
|
|
999
|
+
});
|
|
991
1000
|
}
|
|
992
1001
|
);
|
|
993
1002
|
}
|
|
@@ -744,8 +744,11 @@ suite
|
|
|
744
744
|
testMeadow.doCount(testMeadow.query.setScope('BadTable'),
|
|
745
745
|
function(pError, pQuery, pRecord)
|
|
746
746
|
{
|
|
747
|
+
// chai's type-detect classifies Error instances
|
|
748
|
+
// as 'error', not 'object' — assert against the
|
|
749
|
+
// type chai actually returns.
|
|
747
750
|
Expect(pError)
|
|
748
|
-
.to.be.an('
|
|
751
|
+
.to.be.an('error');
|
|
749
752
|
fDone();
|
|
750
753
|
}
|
|
751
754
|
)
|
|
@@ -1080,5 +1083,244 @@ suite
|
|
|
1080
1083
|
);
|
|
1081
1084
|
}
|
|
1082
1085
|
);
|
|
1086
|
+
suite
|
|
1087
|
+
(
|
|
1088
|
+
'Bulk Create — synchronous chain stack safety',
|
|
1089
|
+
function ()
|
|
1090
|
+
{
|
|
1091
|
+
// Regression for the stack-overflow seen during liveconnect_local
|
|
1092
|
+
// bulk sync: every doCreate runs the libRenameSoftDeletedConflicts
|
|
1093
|
+
// pre-flight, which on a sync provider (node:sqlite) returns its
|
|
1094
|
+
// callback in the same tick. Combined with a synchronous caller
|
|
1095
|
+
// chain (eachLimit's replenish loop, or a tail-recursive driver
|
|
1096
|
+
// like this test), per-record frames accumulate without bound.
|
|
1097
|
+
// The fix is a setImmediate barrier at the pre-flight callback
|
|
1098
|
+
// in Meadow-Create.js so each Create yields to the event loop
|
|
1099
|
+
// before the INSERT runs.
|
|
1100
|
+
var _StackTestSchema =
|
|
1101
|
+
[
|
|
1102
|
+
{ Column: 'IDStackTest', Type: 'AutoIdentity' },
|
|
1103
|
+
{ Column: 'GUIDStackTest', Type: 'AutoGUID' },
|
|
1104
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
1105
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
1106
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
1107
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
1108
|
+
{ Column: 'Deleted', Type: 'Deleted' },
|
|
1109
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
1110
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
1111
|
+
{ Column: 'Code', Type: 'String', Unique: true },
|
|
1112
|
+
{ Column: 'Name', Type: 'String' }
|
|
1113
|
+
];
|
|
1114
|
+
var _StackTestDefault =
|
|
1115
|
+
{
|
|
1116
|
+
IDStackTest: 0,
|
|
1117
|
+
GUIDStackTest: '',
|
|
1118
|
+
CreateDate: false,
|
|
1119
|
+
CreatingIDUser: 0,
|
|
1120
|
+
UpdateDate: false,
|
|
1121
|
+
UpdatingIDUser: 0,
|
|
1122
|
+
Deleted: 0,
|
|
1123
|
+
DeleteDate: false,
|
|
1124
|
+
DeletingIDUser: 0,
|
|
1125
|
+
Code: '',
|
|
1126
|
+
Name: ''
|
|
1127
|
+
};
|
|
1128
|
+
|
|
1129
|
+
var newStackTestMeadow = function ()
|
|
1130
|
+
{
|
|
1131
|
+
return require('../source/Meadow.js').new(libFable, 'StackTest')
|
|
1132
|
+
.setProvider('SQLite')
|
|
1133
|
+
.setSchema(_StackTestSchema)
|
|
1134
|
+
.setJsonSchema({ title: 'StackTest', type: 'object', properties: {}, required: [] })
|
|
1135
|
+
.setDefaultIdentifier('IDStackTest')
|
|
1136
|
+
.setDefault(_StackTestDefault);
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
suiteSetup(function (fDone)
|
|
1140
|
+
{
|
|
1141
|
+
var tmpDB = libFable.MeadowSQLiteProvider.db;
|
|
1142
|
+
tmpDB.exec(
|
|
1143
|
+
'CREATE TABLE IF NOT EXISTS StackTest (' +
|
|
1144
|
+
' IDStackTest INTEGER PRIMARY KEY AUTOINCREMENT,' +
|
|
1145
|
+
' GUIDStackTest TEXT NOT NULL DEFAULT \'\',' +
|
|
1146
|
+
' CreateDate TEXT,' +
|
|
1147
|
+
' CreatingIDUser INTEGER NOT NULL DEFAULT 0,' +
|
|
1148
|
+
' UpdateDate TEXT,' +
|
|
1149
|
+
' UpdatingIDUser INTEGER NOT NULL DEFAULT 0,' +
|
|
1150
|
+
' Deleted INTEGER NOT NULL DEFAULT 0,' +
|
|
1151
|
+
' DeleteDate TEXT,' +
|
|
1152
|
+
' DeletingIDUser INTEGER NOT NULL DEFAULT 0,' +
|
|
1153
|
+
' Code TEXT NOT NULL DEFAULT \'\',' +
|
|
1154
|
+
' Name TEXT NOT NULL DEFAULT \'\'' +
|
|
1155
|
+
');' +
|
|
1156
|
+
'CREATE UNIQUE INDEX IF NOT EXISTS stacktest_code_unique ON StackTest(Code);'
|
|
1157
|
+
);
|
|
1158
|
+
return fDone();
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
test
|
|
1162
|
+
(
|
|
1163
|
+
'1500 sync-chained doCreate calls complete without RangeError',
|
|
1164
|
+
function (fDone)
|
|
1165
|
+
{
|
|
1166
|
+
// 1500 was chosen empirically: with a single Unique column
|
|
1167
|
+
// pre-flight (one synchronous Read per Create) plus the
|
|
1168
|
+
// surrounding waterfall steps, per-record frame growth
|
|
1169
|
+
// without the fix exceeds V8's default stack budget well
|
|
1170
|
+
// before reaching this count. With the fix in place each
|
|
1171
|
+
// iteration yields to the event loop, so frame growth is
|
|
1172
|
+
// bounded and the chain completes in O(seconds).
|
|
1173
|
+
this.timeout(30000);
|
|
1174
|
+
|
|
1175
|
+
var tmpRecordCount = 1500;
|
|
1176
|
+
var tmpCreated = 0;
|
|
1177
|
+
var tmpFirstError = null;
|
|
1178
|
+
|
|
1179
|
+
var fCreateNext = function (pIndex)
|
|
1180
|
+
{
|
|
1181
|
+
if (pIndex >= tmpRecordCount)
|
|
1182
|
+
{
|
|
1183
|
+
Expect(tmpFirstError, tmpFirstError && tmpFirstError.message).to.not.exist;
|
|
1184
|
+
Expect(tmpCreated).to.equal(tmpRecordCount);
|
|
1185
|
+
return fDone();
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
var tmpMeadow = newStackTestMeadow().setIDUser(1);
|
|
1189
|
+
var tmpQuery = tmpMeadow.query.addRecord(
|
|
1190
|
+
{
|
|
1191
|
+
Code: 'stack-' + pIndex,
|
|
1192
|
+
Name: 'row-' + pIndex
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1195
|
+
// Caller invokes the next iteration synchronously
|
|
1196
|
+
// inside doCreate's terminal callback — the exact
|
|
1197
|
+
// pattern that surfaced the bug. Without the
|
|
1198
|
+
// setImmediate barrier inside Meadow-Create, this
|
|
1199
|
+
// chain stack-overflows in node:sqlite mid-loop.
|
|
1200
|
+
tmpMeadow.doCreate(tmpQuery, function (pError)
|
|
1201
|
+
{
|
|
1202
|
+
if (pError && !tmpFirstError)
|
|
1203
|
+
{
|
|
1204
|
+
tmpFirstError = pError;
|
|
1205
|
+
}
|
|
1206
|
+
else if (!pError)
|
|
1207
|
+
{
|
|
1208
|
+
tmpCreated++;
|
|
1209
|
+
}
|
|
1210
|
+
return fCreateNext(pIndex + 1);
|
|
1211
|
+
});
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
fCreateNext(0);
|
|
1215
|
+
}
|
|
1216
|
+
);
|
|
1217
|
+
}
|
|
1218
|
+
);
|
|
1219
|
+
suite
|
|
1220
|
+
(
|
|
1221
|
+
'Raw query overrides — parameter handling',
|
|
1222
|
+
function ()
|
|
1223
|
+
{
|
|
1224
|
+
// EXPECTED FAILING (until provider fix lands).
|
|
1225
|
+
//
|
|
1226
|
+
// When rawQueries.loadQuery installs a Read override that
|
|
1227
|
+
// doesn't reference some of the named parameters meadow's
|
|
1228
|
+
// query builder auto-generates (e.g. the soft-delete filter
|
|
1229
|
+
// `Deleted_w1`, or a user-added filter like `IDWidget_w0`),
|
|
1230
|
+
// the SQLite provider still binds them via
|
|
1231
|
+
// `statement.all(pQuery.query.parameters)`. node:sqlite is
|
|
1232
|
+
// strict about unknown named parameters and throws
|
|
1233
|
+
// `Unknown named parameter '<name>'` — better-sqlite3 was
|
|
1234
|
+
// lenient about extras, so this regressed silently when
|
|
1235
|
+
// meadow-connection-sqlite switched to node:sqlite.
|
|
1236
|
+
//
|
|
1237
|
+
// Isolation: dedicated table + raw-query file with no
|
|
1238
|
+
// placeholders, so the test doesn't depend on FableTest
|
|
1239
|
+
// state mutated by earlier suites.
|
|
1240
|
+
var _WidgetSchema =
|
|
1241
|
+
[
|
|
1242
|
+
{ Column: 'IDWidget', Type: 'AutoIdentity' },
|
|
1243
|
+
{ Column: 'GUIDWidget', Type: 'AutoGUID' },
|
|
1244
|
+
{ Column: 'CreateDate', Type: 'CreateDate' },
|
|
1245
|
+
{ Column: 'CreatingIDUser', Type: 'CreateIDUser' },
|
|
1246
|
+
{ Column: 'UpdateDate', Type: 'UpdateDate' },
|
|
1247
|
+
{ Column: 'UpdatingIDUser', Type: 'UpdateIDUser' },
|
|
1248
|
+
{ Column: 'Deleted', Type: 'Deleted' },
|
|
1249
|
+
{ Column: 'DeleteDate', Type: 'DeleteDate' },
|
|
1250
|
+
{ Column: 'DeletingIDUser', Type: 'DeleteIDUser' },
|
|
1251
|
+
{ Column: 'Label', Type: 'String' }
|
|
1252
|
+
];
|
|
1253
|
+
|
|
1254
|
+
var newWidgetMeadow = function ()
|
|
1255
|
+
{
|
|
1256
|
+
return require('../source/Meadow.js').new(libFable, 'Widget')
|
|
1257
|
+
.setProvider('SQLite')
|
|
1258
|
+
.setSchema(_WidgetSchema)
|
|
1259
|
+
.setJsonSchema({ title: 'Widget', type: 'object', properties: {}, required: [] })
|
|
1260
|
+
.setDefaultIdentifier('IDWidget');
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
var _RawQueryPath = __dirname + '/Meadow-Provider-SQLite-WidgetRawRead.sql';
|
|
1264
|
+
|
|
1265
|
+
suiteSetup(function (fDone)
|
|
1266
|
+
{
|
|
1267
|
+
var tmpDB = libFable.MeadowSQLiteProvider.db;
|
|
1268
|
+
tmpDB.exec(
|
|
1269
|
+
'CREATE TABLE IF NOT EXISTS Widget (' +
|
|
1270
|
+
' IDWidget INTEGER PRIMARY KEY AUTOINCREMENT,' +
|
|
1271
|
+
' GUIDWidget TEXT NOT NULL DEFAULT \'\',' +
|
|
1272
|
+
' CreateDate TEXT,' +
|
|
1273
|
+
' CreatingIDUser INTEGER NOT NULL DEFAULT 0,' +
|
|
1274
|
+
' UpdateDate TEXT,' +
|
|
1275
|
+
' UpdatingIDUser INTEGER NOT NULL DEFAULT 0,' +
|
|
1276
|
+
' Deleted INTEGER NOT NULL DEFAULT 0,' +
|
|
1277
|
+
' DeleteDate TEXT,' +
|
|
1278
|
+
' DeletingIDUser INTEGER NOT NULL DEFAULT 0,' +
|
|
1279
|
+
' Label TEXT NOT NULL DEFAULT \'\'' +
|
|
1280
|
+
');'
|
|
1281
|
+
);
|
|
1282
|
+
tmpDB.exec("INSERT INTO Widget (Label) VALUES ('alpha'), ('beta'), ('gamma');");
|
|
1283
|
+
|
|
1284
|
+
// Raw query has no placeholders — projection-only SELECT.
|
|
1285
|
+
libFS.writeFileSync(_RawQueryPath,
|
|
1286
|
+
'SELECT IDWidget, Label AS LabelAlias FROM Widget WHERE IDWidget = 1');
|
|
1287
|
+
return fDone();
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
suiteTeardown(function (fDone)
|
|
1291
|
+
{
|
|
1292
|
+
try { libFS.unlinkSync(_RawQueryPath); } catch (pError) { /* ignore */ }
|
|
1293
|
+
return fDone();
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
test
|
|
1297
|
+
(
|
|
1298
|
+
'doRead with a meadow-generated filter still resolves a parameterless raw query',
|
|
1299
|
+
function (fDone)
|
|
1300
|
+
{
|
|
1301
|
+
var testMeadow = newWidgetMeadow();
|
|
1302
|
+
|
|
1303
|
+
testMeadow.rawQueries.loadQuery('Read', _RawQueryPath, function (pLoaded)
|
|
1304
|
+
{
|
|
1305
|
+
Expect(pLoaded).to.equal(true);
|
|
1306
|
+
|
|
1307
|
+
// addFilter generates a `:IDWidget_w0` named parameter
|
|
1308
|
+
// (and soft-delete tracking adds `:Deleted_w1`).
|
|
1309
|
+
// Neither appears in the raw SQL above. Without
|
|
1310
|
+
// param-filtering in the provider, node:sqlite
|
|
1311
|
+
// throws "Unknown named parameter 'IDWidget_w0'".
|
|
1312
|
+
testMeadow.doRead(testMeadow.query.addFilter('IDWidget', 1),
|
|
1313
|
+
function (pError, pQuery, pRecord)
|
|
1314
|
+
{
|
|
1315
|
+
Expect(pError, pError && pError.message ? pError.message : pError).to.not.exist;
|
|
1316
|
+
Expect(pRecord, 'pRecord should be a marshalled object, not ' + pRecord).to.be.an('object');
|
|
1317
|
+
Expect(pRecord.LabelAlias).to.equal('alpha');
|
|
1318
|
+
fDone();
|
|
1319
|
+
});
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
);
|
|
1323
|
+
}
|
|
1324
|
+
);
|
|
1083
1325
|
}
|
|
1084
1326
|
);
|
package/docs/css/docuserve.css
DELETED
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
/* ============================================================================
|
|
2
|
-
Pict Docuserve - Base Styles & Theme Variables
|
|
3
|
-
============================================================================ */
|
|
4
|
-
|
|
5
|
-
/* ----------------------------------------------------------------------------
|
|
6
|
-
Theme variables — light defaults on :root.
|
|
7
|
-
Dark mode applies when either:
|
|
8
|
-
(a) the user explicitly selected dark via <html data-theme="dark">
|
|
9
|
-
(b) the user hasn't chosen anything AND the system prefers dark
|
|
10
|
-
An explicit <html data-theme="light"> pins the light palette regardless.
|
|
11
|
-
---------------------------------------------------------------------------- */
|
|
12
|
-
|
|
13
|
-
:root
|
|
14
|
-
{
|
|
15
|
-
/* Surfaces */
|
|
16
|
-
--docuserve-bg: #FDFBF7;
|
|
17
|
-
--docuserve-bg-elevated: #FFFFFF;
|
|
18
|
-
--docuserve-border: #DDD6CA;
|
|
19
|
-
--docuserve-border-soft: #EAE3D8;
|
|
20
|
-
|
|
21
|
-
/* Text */
|
|
22
|
-
--docuserve-text: #2A241E;
|
|
23
|
-
--docuserve-text-strong: #3D3229;
|
|
24
|
-
--docuserve-text-muted: #5E5549;
|
|
25
|
-
--docuserve-text-dim: #8A7F72;
|
|
26
|
-
|
|
27
|
-
/* Accent / links */
|
|
28
|
-
--docuserve-accent: #2E7D74;
|
|
29
|
-
--docuserve-accent-hover: #236660;
|
|
30
|
-
|
|
31
|
-
/* Top bar */
|
|
32
|
-
--docuserve-topbar-bg: #3D3229;
|
|
33
|
-
--docuserve-topbar-text: #E8E0D4;
|
|
34
|
-
--docuserve-topbar-text-muted: #B5AA9A;
|
|
35
|
-
--docuserve-topbar-text-dim: #8A7F72;
|
|
36
|
-
--docuserve-topbar-hover-bg: #524438;
|
|
37
|
-
--docuserve-topbar-version-bg: rgba(255, 255, 255, 0.06);
|
|
38
|
-
--docuserve-topbar-version-border: rgba(255, 255, 255, 0.08);
|
|
39
|
-
--docuserve-topbar-version-text: #B5AA9A;
|
|
40
|
-
|
|
41
|
-
/* Sidebar */
|
|
42
|
-
--docuserve-sidebar-bg: #FAF7F1;
|
|
43
|
-
--docuserve-sidebar-border: #DDD6CA;
|
|
44
|
-
--docuserve-sidebar-border-soft: #E5DED1;
|
|
45
|
-
--docuserve-sidebar-text: #423D37;
|
|
46
|
-
--docuserve-sidebar-group-title: #3D3229;
|
|
47
|
-
--docuserve-sidebar-module-text: #5E5549;
|
|
48
|
-
--docuserve-sidebar-hover-bg: #EAE3D8;
|
|
49
|
-
--docuserve-sidebar-hover-text: #2E7D74;
|
|
50
|
-
--docuserve-sidebar-active-bg: #E5DED1;
|
|
51
|
-
--docuserve-sidebar-active-text: #2E7D74;
|
|
52
|
-
--docuserve-sidebar-search-bg: #FFFFFF;
|
|
53
|
-
--docuserve-sidebar-search-border: #DDD6CA;
|
|
54
|
-
|
|
55
|
-
/* Inline code */
|
|
56
|
-
--docuserve-inline-code-bg: #F0ECE4;
|
|
57
|
-
--docuserve-inline-code-text: #9E3A50;
|
|
58
|
-
|
|
59
|
-
/* Code block panel */
|
|
60
|
-
--docuserve-code-bg: #F6F3EE;
|
|
61
|
-
--docuserve-code-border: #E5DED1;
|
|
62
|
-
--docuserve-code-gutter-bg: #EFEAE0;
|
|
63
|
-
--docuserve-code-gutter-border: #DDD6CA;
|
|
64
|
-
--docuserve-code-gutter-text: #A59986;
|
|
65
|
-
--docuserve-code-text: #2A241E;
|
|
66
|
-
|
|
67
|
-
/* Syntax tokens — low-chroma dark-on-light palette */
|
|
68
|
-
--docuserve-tok-keyword: #A03472;
|
|
69
|
-
--docuserve-tok-string: #1A6640;
|
|
70
|
-
--docuserve-tok-number: #B25A00;
|
|
71
|
-
--docuserve-tok-comment: #8A7F72;
|
|
72
|
-
--docuserve-tok-operator: #2E7D74;
|
|
73
|
-
--docuserve-tok-punctuation: #2A241E;
|
|
74
|
-
--docuserve-tok-function: #2A5DB0;
|
|
75
|
-
--docuserve-tok-property: #9E3A50;
|
|
76
|
-
--docuserve-tok-tag: #9E3A50;
|
|
77
|
-
--docuserve-tok-attr-name: #B25A00;
|
|
78
|
-
--docuserve-tok-attr-value: #1A6640;
|
|
79
|
-
|
|
80
|
-
/* Tables, blockquotes, mermaid */
|
|
81
|
-
--docuserve-table-header-bg: #F5F0E8;
|
|
82
|
-
--docuserve-table-row-alt-bg: #F9F6F0;
|
|
83
|
-
--docuserve-blockquote-bg: #F7F5F0;
|
|
84
|
-
--docuserve-blockquote-border: #2E7D74;
|
|
85
|
-
--docuserve-blockquote-text: #5E5549;
|
|
86
|
-
--docuserve-mermaid-bg: #FFFFFF;
|
|
87
|
-
|
|
88
|
-
/* Scrollbars */
|
|
89
|
-
--docuserve-scrollbar-track: #F5F0E8;
|
|
90
|
-
--docuserve-scrollbar-thumb: #D4CCBE;
|
|
91
|
-
--docuserve-scrollbar-thumb-hover: #B5AA9A;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
@media (prefers-color-scheme: dark)
|
|
95
|
-
{
|
|
96
|
-
:root:not([data-theme="light"])
|
|
97
|
-
{
|
|
98
|
-
--docuserve-bg: #15120F;
|
|
99
|
-
--docuserve-bg-elevated: #1B1814;
|
|
100
|
-
--docuserve-border: #2F2823;
|
|
101
|
-
--docuserve-border-soft: #26211C;
|
|
102
|
-
|
|
103
|
-
--docuserve-text: #E8E0D4;
|
|
104
|
-
--docuserve-text-strong: #F2ECE0;
|
|
105
|
-
--docuserve-text-muted: #B5AA9A;
|
|
106
|
-
--docuserve-text-dim: #7A6F62;
|
|
107
|
-
|
|
108
|
-
--docuserve-accent: #5DB8A8;
|
|
109
|
-
--docuserve-accent-hover: #7FCCB8;
|
|
110
|
-
|
|
111
|
-
--docuserve-topbar-bg: #1A1612;
|
|
112
|
-
--docuserve-topbar-text: #E8E0D4;
|
|
113
|
-
--docuserve-topbar-text-muted: #B5AA9A;
|
|
114
|
-
--docuserve-topbar-text-dim: #7A6F62;
|
|
115
|
-
--docuserve-topbar-hover-bg: #2A241E;
|
|
116
|
-
--docuserve-topbar-version-bg: rgba(255, 255, 255, 0.05);
|
|
117
|
-
--docuserve-topbar-version-border: rgba(255, 255, 255, 0.09);
|
|
118
|
-
--docuserve-topbar-version-text: #B5AA9A;
|
|
119
|
-
|
|
120
|
-
--docuserve-sidebar-bg: #1B1814;
|
|
121
|
-
--docuserve-sidebar-border: #2F2823;
|
|
122
|
-
--docuserve-sidebar-border-soft: #26211C;
|
|
123
|
-
--docuserve-sidebar-text: #C9C0B3;
|
|
124
|
-
--docuserve-sidebar-group-title: #F2ECE0;
|
|
125
|
-
--docuserve-sidebar-module-text: #B5AA9A;
|
|
126
|
-
--docuserve-sidebar-hover-bg: #2A241E;
|
|
127
|
-
--docuserve-sidebar-hover-text: #7FCCB8;
|
|
128
|
-
--docuserve-sidebar-active-bg: #2F2823;
|
|
129
|
-
--docuserve-sidebar-active-text: #7FCCB8;
|
|
130
|
-
--docuserve-sidebar-search-bg: #26211C;
|
|
131
|
-
--docuserve-sidebar-search-border: #2F2823;
|
|
132
|
-
|
|
133
|
-
--docuserve-inline-code-bg: #2A241E;
|
|
134
|
-
--docuserve-inline-code-text: #E8B07A;
|
|
135
|
-
|
|
136
|
-
--docuserve-code-bg: #1E1A17;
|
|
137
|
-
--docuserve-code-border: #2F2823;
|
|
138
|
-
--docuserve-code-gutter-bg: #17130F;
|
|
139
|
-
--docuserve-code-gutter-border: #2F2823;
|
|
140
|
-
--docuserve-code-gutter-text: #6A6052;
|
|
141
|
-
--docuserve-code-text: #E8E0D4;
|
|
142
|
-
|
|
143
|
-
--docuserve-tok-keyword: #C678DD;
|
|
144
|
-
--docuserve-tok-string: #98C379;
|
|
145
|
-
--docuserve-tok-number: #D19A66;
|
|
146
|
-
--docuserve-tok-comment: #7F848E;
|
|
147
|
-
--docuserve-tok-operator: #56B6C2;
|
|
148
|
-
--docuserve-tok-punctuation: #E8E0D4;
|
|
149
|
-
--docuserve-tok-function: #61AFEF;
|
|
150
|
-
--docuserve-tok-property: #E06C75;
|
|
151
|
-
--docuserve-tok-tag: #E06C75;
|
|
152
|
-
--docuserve-tok-attr-name: #D19A66;
|
|
153
|
-
--docuserve-tok-attr-value: #98C379;
|
|
154
|
-
|
|
155
|
-
--docuserve-table-header-bg: #26211C;
|
|
156
|
-
--docuserve-table-row-alt-bg: #1F1B17;
|
|
157
|
-
--docuserve-blockquote-bg: #1F1B17;
|
|
158
|
-
--docuserve-blockquote-border: #5DB8A8;
|
|
159
|
-
--docuserve-blockquote-text: #C9C0B3;
|
|
160
|
-
--docuserve-mermaid-bg: #E8E0D4;
|
|
161
|
-
|
|
162
|
-
--docuserve-scrollbar-track: #1B1814;
|
|
163
|
-
--docuserve-scrollbar-thumb: #3A322B;
|
|
164
|
-
--docuserve-scrollbar-thumb-hover: #524438;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
:root[data-theme="dark"]
|
|
169
|
-
{
|
|
170
|
-
--docuserve-bg: #15120F;
|
|
171
|
-
--docuserve-bg-elevated: #1B1814;
|
|
172
|
-
--docuserve-border: #2F2823;
|
|
173
|
-
--docuserve-border-soft: #26211C;
|
|
174
|
-
|
|
175
|
-
--docuserve-text: #E8E0D4;
|
|
176
|
-
--docuserve-text-strong: #F2ECE0;
|
|
177
|
-
--docuserve-text-muted: #B5AA9A;
|
|
178
|
-
--docuserve-text-dim: #7A6F62;
|
|
179
|
-
|
|
180
|
-
--docuserve-accent: #5DB8A8;
|
|
181
|
-
--docuserve-accent-hover: #7FCCB8;
|
|
182
|
-
|
|
183
|
-
--docuserve-topbar-bg: #1A1612;
|
|
184
|
-
--docuserve-topbar-text: #E8E0D4;
|
|
185
|
-
--docuserve-topbar-text-muted: #B5AA9A;
|
|
186
|
-
--docuserve-topbar-text-dim: #7A6F62;
|
|
187
|
-
--docuserve-topbar-hover-bg: #2A241E;
|
|
188
|
-
--docuserve-topbar-version-bg: rgba(255, 255, 255, 0.05);
|
|
189
|
-
--docuserve-topbar-version-border: rgba(255, 255, 255, 0.09);
|
|
190
|
-
--docuserve-topbar-version-text: #B5AA9A;
|
|
191
|
-
|
|
192
|
-
--docuserve-sidebar-bg: #1B1814;
|
|
193
|
-
--docuserve-sidebar-border: #2F2823;
|
|
194
|
-
--docuserve-sidebar-border-soft: #26211C;
|
|
195
|
-
--docuserve-sidebar-text: #C9C0B3;
|
|
196
|
-
--docuserve-sidebar-group-title: #F2ECE0;
|
|
197
|
-
--docuserve-sidebar-module-text: #B5AA9A;
|
|
198
|
-
--docuserve-sidebar-hover-bg: #2A241E;
|
|
199
|
-
--docuserve-sidebar-hover-text: #7FCCB8;
|
|
200
|
-
--docuserve-sidebar-active-bg: #2F2823;
|
|
201
|
-
--docuserve-sidebar-active-text: #7FCCB8;
|
|
202
|
-
--docuserve-sidebar-search-bg: #26211C;
|
|
203
|
-
--docuserve-sidebar-search-border: #2F2823;
|
|
204
|
-
|
|
205
|
-
--docuserve-inline-code-bg: #2A241E;
|
|
206
|
-
--docuserve-inline-code-text: #E8B07A;
|
|
207
|
-
|
|
208
|
-
--docuserve-code-bg: #1E1A17;
|
|
209
|
-
--docuserve-code-border: #2F2823;
|
|
210
|
-
--docuserve-code-gutter-bg: #17130F;
|
|
211
|
-
--docuserve-code-gutter-border: #2F2823;
|
|
212
|
-
--docuserve-code-gutter-text: #6A6052;
|
|
213
|
-
--docuserve-code-text: #E8E0D4;
|
|
214
|
-
|
|
215
|
-
--docuserve-tok-keyword: #C678DD;
|
|
216
|
-
--docuserve-tok-string: #98C379;
|
|
217
|
-
--docuserve-tok-number: #D19A66;
|
|
218
|
-
--docuserve-tok-comment: #7F848E;
|
|
219
|
-
--docuserve-tok-operator: #56B6C2;
|
|
220
|
-
--docuserve-tok-punctuation: #E8E0D4;
|
|
221
|
-
--docuserve-tok-function: #61AFEF;
|
|
222
|
-
--docuserve-tok-property: #E06C75;
|
|
223
|
-
--docuserve-tok-tag: #E06C75;
|
|
224
|
-
--docuserve-tok-attr-name: #D19A66;
|
|
225
|
-
--docuserve-tok-attr-value: #98C379;
|
|
226
|
-
|
|
227
|
-
--docuserve-table-header-bg: #26211C;
|
|
228
|
-
--docuserve-table-row-alt-bg: #1F1B17;
|
|
229
|
-
--docuserve-blockquote-bg: #1F1B17;
|
|
230
|
-
--docuserve-blockquote-border: #5DB8A8;
|
|
231
|
-
--docuserve-blockquote-text: #C9C0B3;
|
|
232
|
-
--docuserve-mermaid-bg: #E8E0D4;
|
|
233
|
-
|
|
234
|
-
--docuserve-scrollbar-track: #1B1814;
|
|
235
|
-
--docuserve-scrollbar-thumb: #3A322B;
|
|
236
|
-
--docuserve-scrollbar-thumb-hover: #524438;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/* ----------------------------------------------------------------------------
|
|
240
|
-
Reset and base
|
|
241
|
-
---------------------------------------------------------------------------- */
|
|
242
|
-
|
|
243
|
-
*, *::before, *::after
|
|
244
|
-
{
|
|
245
|
-
box-sizing: border-box;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
html, body
|
|
249
|
-
{
|
|
250
|
-
margin: 0;
|
|
251
|
-
padding: 0;
|
|
252
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
253
|
-
font-size: 16px;
|
|
254
|
-
line-height: 1.5;
|
|
255
|
-
color: var(--docuserve-text);
|
|
256
|
-
background-color: var(--docuserve-bg);
|
|
257
|
-
-webkit-font-smoothing: antialiased;
|
|
258
|
-
-moz-osx-font-smoothing: grayscale;
|
|
259
|
-
transition: background-color 0.15s ease, color 0.15s ease;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/* Typography */
|
|
263
|
-
h1, h2, h3, h4, h5, h6
|
|
264
|
-
{
|
|
265
|
-
margin-top: 0;
|
|
266
|
-
line-height: 1.3;
|
|
267
|
-
color: var(--docuserve-text-strong);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
a
|
|
271
|
-
{
|
|
272
|
-
color: var(--docuserve-accent);
|
|
273
|
-
text-decoration: none;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
a:hover
|
|
277
|
-
{
|
|
278
|
-
color: var(--docuserve-accent-hover);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/* Application container */
|
|
282
|
-
#Docuserve-Application-Container
|
|
283
|
-
{
|
|
284
|
-
min-height: 100vh;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/* Utility: scrollbar styling */
|
|
288
|
-
::-webkit-scrollbar
|
|
289
|
-
{
|
|
290
|
-
width: 8px;
|
|
291
|
-
height: 8px;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
::-webkit-scrollbar-track
|
|
295
|
-
{
|
|
296
|
-
background: var(--docuserve-scrollbar-track);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
::-webkit-scrollbar-thumb
|
|
300
|
-
{
|
|
301
|
-
background: var(--docuserve-scrollbar-thumb);
|
|
302
|
-
border-radius: 4px;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
::-webkit-scrollbar-thumb:hover
|
|
306
|
-
{
|
|
307
|
-
background: var(--docuserve-scrollbar-thumb-hover);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/* Responsive adjustments */
|
|
311
|
-
@media (max-width: 768px)
|
|
312
|
-
{
|
|
313
|
-
html
|
|
314
|
-
{
|
|
315
|
-
font-size: 14px;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
#Docuserve-Sidebar-Container
|
|
319
|
-
{
|
|
320
|
-
display: none;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.docuserve-body
|
|
324
|
-
{
|
|
325
|
-
flex-direction: column;
|
|
326
|
-
}
|
|
327
|
-
}
|