qtools-library-dot-d 1.0.12 → 1.1.0
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/LICENSE +21 -0
- package/README.md +89 -0
- package/package.json +9 -3
- package/qtools-library-dot-d.js +553 -2
- package/libDotD_Working_Example/README.md +0 -234
- package/libDotD_Working_Example/demoModules_DotD/demo1.js +0 -46
- package/libDotD_Working_Example/demoModules_DotD/demo2.js +0 -48
- package/libDotD_Working_Example/index.js +0 -133
- package/libDotD_Working_Example/node_modules/.package-lock.json +0 -29
- package/libDotD_Working_Example/node_modules/qtools-asynchronous-pipe-plus/README.md +0 -213
- package/libDotD_Working_Example/node_modules/qtools-asynchronous-pipe-plus/package.json +0 -20
- package/libDotD_Working_Example/node_modules/qtools-asynchronous-pipe-plus/qtools-asynchronous-pipe-plus.js +0 -278
- package/libDotD_Working_Example/node_modules/qtools-asynchronous-pipe-plus/test.js +0 -79
- package/libDotD_Working_Example/node_modules/qtools-functional-library/README.md +0 -110
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-clone/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-clone/qtools-clone.js +0 -103
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-clone/test.js +0 -78
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-get-by-property/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-get-by-property/qtools-get-by-property.js +0 -73
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-get-by-property/test.js +0 -129
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-includes-regex/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-includes-regex/qtools-includes-regex.js +0 -54
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-includes-regex/test.js +0 -76
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-log-output/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-log-output/qtools-log-output.js +0 -114
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-log-output/test.js +0 -130
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-map-properties/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-map-properties/qtools-map-properties.js +0 -99
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-map-properties/test.js +0 -118
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-merge/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-merge/qtools-merge.js +0 -147
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-merge/test.js +0 -214
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-new-method-template/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-new-method-template/qtools-new-method-template.js +0 -70
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-new-method-template/test.js +0 -66
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-number-iterator/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-number-iterator/qtools-number-iterator.js +0 -137
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-number-iterator/test.js +0 -68
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-number-keys-to-array/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-number-keys-to-array/qtools-number-keys-to-array.js +0 -131
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-number-keys-to-array/test.js +0 -181
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-object-sure-path/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-object-sure-path/qtools-object-sure-path.js +0 -204
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-object-sure-path/test.js +0 -345
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-pass-through/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-pass-through/qtools-pass-through.js +0 -58
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-pass-through/test.js +0 -66
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-pop/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-pop/qtools-pop.js +0 -175
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-pop/test.js +0 -118
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-print-debug/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-print-debug/qtools-print-debug.js +0 -352
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-print-debug/test.js +0 -77
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-select-properties/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-select-properties/qtools-select-properties.js +0 -132
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-select-properties/test.js +0 -156
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-template-replace/README.md +0 -3
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-template-replace/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-template-replace/qtools-template-replace.js +0 -106
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-template-replace/test.js +0 -168
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-to-key-value-array/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-to-key-value-array/qtools-to-key-value-array.js +0 -83
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-to-key-value-array/test.js +0 -68
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-to-string/package.json +0 -11
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-to-string/qtools-to-string.js +0 -84
- package/libDotD_Working_Example/node_modules/qtools-functional-library/lib/qtools-to-string/test.js +0 -63
- package/libDotD_Working_Example/node_modules/qtools-functional-library/package.json +0 -37
- package/libDotD_Working_Example/node_modules/qtools-functional-library/qtFunctionalDemo.js +0 -297
- package/libDotD_Working_Example/node_modules/qtools-functional-library/qtools-functional-library.js +0 -271
- package/libDotD_Working_Example/node_modules/qtools-functional-library/test.js +0 -47
- package/libDotD_Working_Example/node_modules/qtools-library-dot-d/README.md +0 -208
- package/libDotD_Working_Example/node_modules/qtools-library-dot-d/package.json +0 -15
- package/libDotD_Working_Example/node_modules/qtools-library-dot-d/qtools-library-dot-d.js +0 -239
- package/libDotD_Working_Example/package-lock.json +0 -37
- package/libDotD_Working_Example/package.json +0 -23
package/qtools-library-dot-d.js
CHANGED
|
@@ -12,6 +12,11 @@ const fs = require('fs');
|
|
|
12
12
|
const qt = require('qtools-functional-library');
|
|
13
13
|
|
|
14
14
|
//START OF moduleFunction() ============================================================
|
|
15
|
+
// Two ways to load. setLibraryPath(dirPath) is the original single directory, and behaves exactly as in 1.0.13,
|
|
16
|
+
// including swallowing a module's load error into the exception list. setLibraryPathList(pathList) loads framework
|
|
17
|
+
// roots, then instance roots, and refuses by name what single-path mode would let pass silently: name collisions,
|
|
18
|
+
// undeclared overrides, interface failures, load errors and (opt-in) forbidden requires. See README, "Loading from
|
|
19
|
+
// several directories".
|
|
15
20
|
const moduleFunction =
|
|
16
21
|
({ moduleName } = {}) =>
|
|
17
22
|
({ libraryName = 'libraryDotD' }) => {
|
|
@@ -22,14 +27,19 @@ const moduleFunction =
|
|
|
22
27
|
// SET LIBRARY PATH
|
|
23
28
|
|
|
24
29
|
let libraryPath;
|
|
30
|
+
let libraryPathMode = ''; // 'singlePath' (setLibraryPath) or 'pathList' (setLibraryPathList); never both
|
|
25
31
|
|
|
26
32
|
const setLibraryPath = (dirPath, callback) => {
|
|
33
|
+
if (libraryPathMode == 'pathList') {
|
|
34
|
+
throw `LIBRARY_PATH_MODE_CONFLICT: ${libraryName} already has a path list; setLibraryPath(${dirPath}) refused`;
|
|
35
|
+
}
|
|
27
36
|
try {
|
|
28
37
|
libraryPath = fs.realpathSync(dirPath);
|
|
29
38
|
} catch (err) {
|
|
30
39
|
throw err.toString();
|
|
31
40
|
return;
|
|
32
41
|
}
|
|
42
|
+
libraryPathMode = 'singlePath';
|
|
33
43
|
|
|
34
44
|
if (typeof callback == 'function') {
|
|
35
45
|
callback('', libraryPath);
|
|
@@ -38,6 +48,149 @@ const moduleFunction =
|
|
|
38
48
|
}
|
|
39
49
|
};
|
|
40
50
|
|
|
51
|
+
// --------------------------------------------------------------------------------
|
|
52
|
+
// SET LIBRARY PATH LIST (instance and framework roots)
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* One root in a path list. A plain string means { dirPath, provenance: 'framework', mayBeAbsent: false }.
|
|
56
|
+
* @typedef {Object} PathListEntry
|
|
57
|
+
* @property {string} dirPath
|
|
58
|
+
* @property {'instance'|'framework'} provenance - framework roots load first, then instance roots
|
|
59
|
+
* @property {boolean} [mayBeAbsent=false] - a missing root is refused (PLUGIN_ROOT_NOT_FOUND) unless true
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The contract every element of an interface-checked library must meet (setInterface).
|
|
64
|
+
* @interface InterfaceSpec
|
|
65
|
+
* @property {string} describeFunctionName - element[describeFunctionName]() returns its description object
|
|
66
|
+
* @property {string[]} requiredFunctionList - functions every element must carry
|
|
67
|
+
* @property {string} nameFieldName - description field that must equal the name passed to library.add
|
|
68
|
+
* @property {string} versionFieldName - description field holding the element's own version
|
|
69
|
+
* @property {*} contractVersion - description.contractVersion must equal this (strict ===)
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Where an element came from (getProvenance). overrides is present only for a declared instance override.
|
|
74
|
+
* @typedef {Object} ProvenanceRecord
|
|
75
|
+
* @property {'instance'|'framework'} provenance
|
|
76
|
+
* @property {string} filePath - the module (file or directory) whose require() added the element
|
|
77
|
+
* @property {{provenance: string, filePath: string, version: *, reason: string}} [overrides]
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
const provenanceNameList = ['instance', 'framework'];
|
|
81
|
+
let libraryPathList = [];
|
|
82
|
+
|
|
83
|
+
// Refusals in the setters are thrown when no callback is given, matching setLibraryPath.
|
|
84
|
+
const deliverResult = (errString, result, callback) => {
|
|
85
|
+
if (typeof callback == 'function') {
|
|
86
|
+
callback(errString, result);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (errString) {
|
|
90
|
+
throw errString;
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const normalizePathListEntry = (pathListEntry, entryIndex) => {
|
|
96
|
+
if (typeof pathListEntry == 'string') {
|
|
97
|
+
return { dirPath: pathListEntry, provenance: 'framework', mayBeAbsent: false };
|
|
98
|
+
}
|
|
99
|
+
const { dirPath, provenance, mayBeAbsent = false } = pathListEntry || {};
|
|
100
|
+
const problemList = [
|
|
101
|
+
typeof dirPath == 'string' && dirPath ? '' : 'dirPath must be a non-empty string',
|
|
102
|
+
provenanceNameList.includes(provenance) ? '' : `provenance must be one of ${provenanceNameList.join(', ')} (got ${JSON.stringify(provenance)})`,
|
|
103
|
+
typeof mayBeAbsent == 'boolean' ? '' : `mayBeAbsent must be boolean (got ${JSON.stringify(mayBeAbsent)})`,
|
|
104
|
+
].filter((problem) => problem);
|
|
105
|
+
if (problemList.length) {
|
|
106
|
+
return { entryProblem: `PATH_LIST_ENTRY_INVALID: ${libraryName} entry ${entryIndex}: ${problemList.join('; ')}` };
|
|
107
|
+
}
|
|
108
|
+
return { dirPath, provenance, mayBeAbsent };
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const setLibraryPathList = (pathList, callback) => {
|
|
112
|
+
if (libraryPathMode == 'singlePath') {
|
|
113
|
+
return deliverResult(`LIBRARY_PATH_MODE_CONFLICT: ${libraryName} already has a single library path; setLibraryPathList refused`, undefined, callback);
|
|
114
|
+
}
|
|
115
|
+
if (!Array.isArray(pathList) || !pathList.length) {
|
|
116
|
+
return deliverResult(`PATH_LIST_INVALID: ${libraryName} setLibraryPathList needs a non-empty array (got ${JSON.stringify(pathList)})`, undefined, callback);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const normalizedEntryList = pathList.map(normalizePathListEntry);
|
|
120
|
+
const problemList = normalizedEntryList
|
|
121
|
+
.filter(({ entryProblem }) => entryProblem)
|
|
122
|
+
.map(({ entryProblem }) => entryProblem);
|
|
123
|
+
|
|
124
|
+
const resolvedEntryList = normalizedEntryList
|
|
125
|
+
.filter(({ entryProblem }) => !entryProblem)
|
|
126
|
+
.map((pathListEntry) => {
|
|
127
|
+
const { dirPath, provenance, mayBeAbsent } = pathListEntry;
|
|
128
|
+
const rootExists = fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory();
|
|
129
|
+
if (rootExists) {
|
|
130
|
+
return { dirPath: fs.realpathSync(dirPath), provenance, mayBeAbsent, rootIsAbsent: false };
|
|
131
|
+
}
|
|
132
|
+
if (mayBeAbsent) {
|
|
133
|
+
logList.push(`ROOT_ABSENT_ALLOWED: ${provenance} root ${dirPath} is not an existing directory; nothing loaded from it`);
|
|
134
|
+
return { dirPath, provenance, mayBeAbsent, rootIsAbsent: true };
|
|
135
|
+
}
|
|
136
|
+
problemList.push(`PLUGIN_ROOT_NOT_FOUND: ${libraryName} ${provenance} root ${dirPath} is not an existing directory`);
|
|
137
|
+
return { dirPath, provenance, mayBeAbsent, rootIsAbsent: true };
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (problemList.length) {
|
|
141
|
+
return deliverResult(problemList.join('\n'), undefined, callback);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
libraryPathList = resolvedEntryList;
|
|
145
|
+
libraryPathMode = 'pathList';
|
|
146
|
+
return deliverResult('', libraryPathList.map((pathListEntry) => ({ ...pathListEntry })), callback);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// --------------------------------------------------------------------------------
|
|
150
|
+
// SET INTERFACE (opt-in check of every added item, path-list mode only)
|
|
151
|
+
|
|
152
|
+
let interfaceSpec;
|
|
153
|
+
|
|
154
|
+
const setInterface = (newInterfaceSpec) => {
|
|
155
|
+
const { describeFunctionName, requiredFunctionList, nameFieldName, versionFieldName, contractVersion } =
|
|
156
|
+
newInterfaceSpec || {};
|
|
157
|
+
const isNameString = (candidate) => typeof candidate == 'string' && candidate;
|
|
158
|
+
const problemList = [
|
|
159
|
+
isNameString(describeFunctionName) ? '' : 'describeFunctionName',
|
|
160
|
+
Array.isArray(requiredFunctionList) && requiredFunctionList.every(isNameString) ? '' : 'requiredFunctionList (array of function names)',
|
|
161
|
+
isNameString(nameFieldName) ? '' : 'nameFieldName',
|
|
162
|
+
isNameString(versionFieldName) ? '' : 'versionFieldName',
|
|
163
|
+
contractVersion !== undefined && contractVersion !== null ? '' : 'contractVersion',
|
|
164
|
+
].filter((problem) => problem);
|
|
165
|
+
if (problemList.length) {
|
|
166
|
+
throw `INTERFACE_SPEC_INCOMPLETE: ${libraryName} setInterface is missing or has invalid: ${problemList.join(', ')}`;
|
|
167
|
+
}
|
|
168
|
+
interfaceSpec = { describeFunctionName, requiredFunctionList: [...requiredFunctionList], nameFieldName, versionFieldName, contractVersion };
|
|
169
|
+
return { ...interfaceSpec };
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// --------------------------------------------------------------------------------
|
|
173
|
+
// SET FORBIDDEN REQUIRE CHECK (opt-in static scan, path-list mode only)
|
|
174
|
+
|
|
175
|
+
let forbiddenRequireSpec;
|
|
176
|
+
|
|
177
|
+
const setForbiddenRequireCheck = (newForbiddenRequireSpec) => {
|
|
178
|
+
const { provenanceList, allowedModuleList } = newForbiddenRequireSpec || {};
|
|
179
|
+
const problemList = [
|
|
180
|
+
Array.isArray(provenanceList) && provenanceList.length && provenanceList.every((provenance) => provenanceNameList.includes(provenance))
|
|
181
|
+
? ''
|
|
182
|
+
: `provenanceList must be a non-empty array of ${provenanceNameList.join(', ')}`,
|
|
183
|
+
Array.isArray(allowedModuleList) && allowedModuleList.every((allowedModuleName) => typeof allowedModuleName == 'string')
|
|
184
|
+
? ''
|
|
185
|
+
: 'allowedModuleList must be an array of module names',
|
|
186
|
+
].filter((problem) => problem);
|
|
187
|
+
if (problemList.length) {
|
|
188
|
+
throw `FORBIDDEN_REQUIRE_SPEC_INVALID: ${libraryName} setForbiddenRequireCheck: ${problemList.join('; ')}`;
|
|
189
|
+
}
|
|
190
|
+
forbiddenRequireSpec = { provenanceList: [...provenanceList], allowedModuleList: [...allowedModuleList] };
|
|
191
|
+
return { ...forbiddenRequireSpec };
|
|
192
|
+
};
|
|
193
|
+
|
|
41
194
|
// --------------------------------------------------------------------------------
|
|
42
195
|
// SET CUSTOM MODULE ADDING FUNCTION
|
|
43
196
|
|
|
@@ -86,12 +239,161 @@ const moduleFunction =
|
|
|
86
239
|
const exceptionList = [];
|
|
87
240
|
const library = (() => {
|
|
88
241
|
const add = (name, item) => {
|
|
242
|
+
if (libraryPathMode == 'pathList') {
|
|
243
|
+
addFromPathList(name, item);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
89
246
|
libraryObject[name] = item; //used only for producing display listings
|
|
90
247
|
externalElements[name] = item;
|
|
91
248
|
};
|
|
92
249
|
return { add };
|
|
93
250
|
})();
|
|
94
251
|
|
|
252
|
+
// ======================================================================================
|
|
253
|
+
// PATH LIST ADD (collision, declared override, interface check, provenance)
|
|
254
|
+
|
|
255
|
+
const refusalList = [];
|
|
256
|
+
// No prototype: an element named 'constructor' or 'valueOf' must not find Object.prototype's member.
|
|
257
|
+
const provenanceByName = Object.create(null); // { provenance, filePath, overrides? } per element
|
|
258
|
+
const declaredVersionByName = Object.create(null);
|
|
259
|
+
let loadContext; // { provenance, filePath, addedNameList } while one module is being required
|
|
260
|
+
let apiNameList = []; // the library's own API names; filled once externalElements exists
|
|
261
|
+
let reservedNameList = []; // apiNameList plus Object.prototype's members
|
|
262
|
+
|
|
263
|
+
const refuse = ({ refusalCode, elementName, filePath, message }) =>
|
|
264
|
+
refusalList.push({ refusalCode, elementName, filePath, message: `${refusalCode}: ${message}` });
|
|
265
|
+
|
|
266
|
+
const checkInterface = (elementName, item) => {
|
|
267
|
+
const { describeFunctionName, requiredFunctionList, nameFieldName, contractVersion } = interfaceSpec;
|
|
268
|
+
const missingFunctionList = [describeFunctionName, ...requiredFunctionList].filter(
|
|
269
|
+
(functionName) => item === null || item === undefined || typeof item[functionName] != 'function',
|
|
270
|
+
);
|
|
271
|
+
if (missingFunctionList.length) {
|
|
272
|
+
return { refusalCode: 'PLUGIN_INTERFACE_INCOMPLETE', message: `'${elementName}' lacks required function(s): ${missingFunctionList.join(', ')}` };
|
|
273
|
+
}
|
|
274
|
+
const describeOutput = item[describeFunctionName]();
|
|
275
|
+
if (describeOutput === null || typeof describeOutput != 'object') {
|
|
276
|
+
return { refusalCode: 'PLUGIN_INTERFACE_INCOMPLETE', message: `'${elementName}' ${describeFunctionName}() did not return an object` };
|
|
277
|
+
}
|
|
278
|
+
if (describeOutput[nameFieldName] !== elementName) {
|
|
279
|
+
return {
|
|
280
|
+
refusalCode: 'PLUGIN_NAME_MISMATCH',
|
|
281
|
+
message: `added as '${elementName}' but ${describeFunctionName}().${nameFieldName} is ${JSON.stringify(describeOutput[nameFieldName])}`,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (describeOutput.contractVersion !== contractVersion) {
|
|
285
|
+
return {
|
|
286
|
+
refusalCode: 'PLUGIN_CONTRACT_MISMATCH',
|
|
287
|
+
message: `'${elementName}' declares contractVersion ${JSON.stringify(describeOutput.contractVersion)}; ${libraryName} requires ${JSON.stringify(contractVersion)}`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return { describeOutput };
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
const findOverrideDeclarationProblem = (overrideDeclaration) => {
|
|
294
|
+
const { replacesVersion, reason } = overrideDeclaration;
|
|
295
|
+
const hasReplacesVersion = replacesVersion !== undefined && replacesVersion !== null && replacesVersion !== '';
|
|
296
|
+
const hasReason = typeof reason == 'string' && reason;
|
|
297
|
+
return hasReplacesVersion && hasReason ? '' : 'overridesFramework needs both replacesVersion and a reason';
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const addFromPathList = (elementName, item) => {
|
|
301
|
+
if (!loadContext) {
|
|
302
|
+
// Nothing is loading, so there is no file to attribute this to and no loadModules callback left to report it.
|
|
303
|
+
throw `PLUGIN_ADD_OUTSIDE_LOAD: ${libraryName} library.add('${elementName}') arrived while no module was loading`;
|
|
304
|
+
}
|
|
305
|
+
const { provenance, filePath } = loadContext;
|
|
306
|
+
const refuseHere = (refusalCode, message) => refuse({ refusalCode, elementName, filePath, message: `${message} (${filePath})` });
|
|
307
|
+
loadContext.addedNameList.push(elementName);
|
|
308
|
+
|
|
309
|
+
if (reservedNameList.includes(elementName)) {
|
|
310
|
+
const reservedBy = apiNameList.includes(elementName) ? `a ${libraryName} API name` : 'a member of Object.prototype';
|
|
311
|
+
refuseHere('PLUGIN_NAME_RESERVED', `'${elementName}' is ${reservedBy} and cannot be an element`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
let describeOutput = {};
|
|
316
|
+
if (interfaceSpec) {
|
|
317
|
+
const interfaceResult = checkInterface(elementName, item);
|
|
318
|
+
if (interfaceResult.refusalCode) {
|
|
319
|
+
refuseHere(interfaceResult.refusalCode, interfaceResult.message);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
describeOutput = interfaceResult.describeOutput;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// With an interface the declaration lives in the describe output; without one, on the item itself.
|
|
326
|
+
const overrideDeclaration = interfaceSpec
|
|
327
|
+
? describeOutput.overridesFramework
|
|
328
|
+
: item !== null && item !== undefined
|
|
329
|
+
? item.overridesFramework
|
|
330
|
+
: undefined;
|
|
331
|
+
if (overrideDeclaration) {
|
|
332
|
+
const declarationProblem = findOverrideDeclarationProblem(overrideDeclaration);
|
|
333
|
+
if (declarationProblem) {
|
|
334
|
+
refuseHere('PLUGIN_OVERRIDE_DECLARATION_INVALID', `'${elementName}': ${declarationProblem}`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const priorRecord = provenanceByName[elementName];
|
|
340
|
+
let overrides;
|
|
341
|
+
if (priorRecord) {
|
|
342
|
+
const isDeclaredOverride = priorRecord.provenance == 'framework' && provenance == 'instance' && overrideDeclaration;
|
|
343
|
+
if (!isDeclaredOverride) {
|
|
344
|
+
refuseHere('PLUGIN_NAME_COLLISION', `'${elementName}' is already loaded from ${priorRecord.filePath}; refused from`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const frameworkVersion = declaredVersionByName[elementName];
|
|
348
|
+
const { replacesVersion, reason } = overrideDeclaration;
|
|
349
|
+
if (frameworkVersion === undefined) {
|
|
350
|
+
logList.push(
|
|
351
|
+
`OVERRIDE_TARGET_UNVERSIONED: instance '${elementName}' (${filePath}) replaces framework ${priorRecord.filePath}, which declares no version; replacesVersion ${JSON.stringify(replacesVersion)} cannot be checked`,
|
|
352
|
+
);
|
|
353
|
+
} else if (frameworkVersion !== replacesVersion) {
|
|
354
|
+
logList.push(
|
|
355
|
+
`OVERRIDE_TARGET_CHANGED: instance '${elementName}' (${filePath}) was written against framework version ${JSON.stringify(replacesVersion)}; framework now declares ${JSON.stringify(frameworkVersion)} (${priorRecord.filePath})`,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
overrides = { provenance: priorRecord.provenance, filePath: priorRecord.filePath, version: frameworkVersion, reason };
|
|
359
|
+
} else if (overrideDeclaration) {
|
|
360
|
+
logList.push(`OVERRIDE_TARGET_ABSENT: '${elementName}' (${filePath}) declares overridesFramework but no framework element of that name is loaded`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
provenanceByName[elementName] = overrides ? { provenance, filePath, overrides } : { provenance, filePath };
|
|
364
|
+
declaredVersionByName[elementName] = interfaceSpec ? describeOutput[interfaceSpec.versionFieldName] : undefined;
|
|
365
|
+
libraryObject[elementName] = item; //used only for producing display listings
|
|
366
|
+
externalElements[elementName] = item;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const formatProvenanceSuffix = (elementName) => {
|
|
370
|
+
const provenanceRecord = provenanceByName[elementName];
|
|
371
|
+
if (libraryPathMode != 'pathList' || !provenanceRecord) {
|
|
372
|
+
return '';
|
|
373
|
+
}
|
|
374
|
+
const { provenance, filePath, overrides } = provenanceRecord;
|
|
375
|
+
const overridesString = overrides
|
|
376
|
+
? `; overrides framework v${overrides.version === undefined ? '(undeclared)' : overrides.version} (${overrides.filePath}) because ${overrides.reason}`
|
|
377
|
+
: '';
|
|
378
|
+
return ` [${provenance}: ${filePath}${overridesString}]`;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const getProvenance = (elementName) => {
|
|
382
|
+
if (libraryPathMode != 'pathList') {
|
|
383
|
+
throw `PROVENANCE_NOT_RECORDED: ${libraryName} records provenance only for libraries loaded with setLibraryPathList`;
|
|
384
|
+
}
|
|
385
|
+
const provenanceRecord = provenanceByName[elementName];
|
|
386
|
+
if (!provenanceRecord) {
|
|
387
|
+
throw `PROVENANCE_UNKNOWN_NAME: ${libraryName} has no element named '${elementName}'`;
|
|
388
|
+
}
|
|
389
|
+
return provenanceRecord.qtClone();
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const getProvenanceList = () =>
|
|
393
|
+
Object.keys(libraryObject).map((elementName) => ({ elementName, ...getProvenance(elementName) }));
|
|
394
|
+
|
|
395
|
+
const getRefusalList = () => refusalList.map((refusalRecord) => ({ ...refusalRecord }));
|
|
396
|
+
|
|
95
397
|
// ======================================================================================
|
|
96
398
|
// LIBRARY OBJECT
|
|
97
399
|
|
|
@@ -113,7 +415,7 @@ const moduleFunction =
|
|
|
113
415
|
(typeof libraryObject[name]).match(/(string|number)/i)
|
|
114
416
|
? libraryObject[name]
|
|
115
417
|
: formatProperty(libraryObject[name])
|
|
116
|
-
}\n\t`,
|
|
418
|
+
}${formatProvenanceSuffix(name)}\n\t`,
|
|
117
419
|
`\n${libraryName} Library Elements\n\t`,
|
|
118
420
|
)
|
|
119
421
|
.replace(/\n\t$/, '');
|
|
@@ -151,6 +453,11 @@ const moduleFunction =
|
|
|
151
453
|
{ excludeMode: true },
|
|
152
454
|
),
|
|
153
455
|
);
|
|
456
|
+
if (libraryPathMode == 'pathList') {
|
|
457
|
+
externalElements.qtSelectProperties(['setLibraryPathList', 'setInterface', 'setForbiddenRequireCheck'], {
|
|
458
|
+
excludeMode: true,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
154
461
|
Object.freeze(externalElements);
|
|
155
462
|
Object.seal(externalElements);
|
|
156
463
|
};
|
|
@@ -158,7 +465,240 @@ const moduleFunction =
|
|
|
158
465
|
// ======================================================================================
|
|
159
466
|
// LOAD MODULES (actual execution)
|
|
160
467
|
|
|
161
|
-
const loadModules = (
|
|
468
|
+
const loadModules = (loadOptions, callback) => {
|
|
469
|
+
if (libraryPathMode == 'pathList') {
|
|
470
|
+
return loadModulesFromPathList(loadOptions, callback);
|
|
471
|
+
}
|
|
472
|
+
if (interfaceSpec || forbiddenRequireSpec) {
|
|
473
|
+
refuse({
|
|
474
|
+
refusalCode: 'REQUIRES_PATH_LIST',
|
|
475
|
+
elementName: '',
|
|
476
|
+
filePath: libraryPath,
|
|
477
|
+
message: `${libraryName} setInterface and setForbiddenRequireCheck apply only to libraries loaded with setLibraryPathList`,
|
|
478
|
+
});
|
|
479
|
+
return deliverResult(refusalList[refusalList.length - 1].message, undefined, callback);
|
|
480
|
+
}
|
|
481
|
+
return loadModulesFromSinglePath(loadOptions, callback);
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// --------------------------------------------------------------------------------
|
|
485
|
+
// LOAD MODULES FROM A PATH LIST
|
|
486
|
+
|
|
487
|
+
const moduleFilePattern = /\.(c|m)?js$/;
|
|
488
|
+
// Static string-literal requires only. A require built from a variable or a ${} template is not seen.
|
|
489
|
+
const requireCallPattern = /\brequire\s*\(\s*(['"`])([^'"`$]+)\1\s*\)/g;
|
|
490
|
+
|
|
491
|
+
const listModuleSourceFiles = (entryPath, isDirectory) =>
|
|
492
|
+
isDirectory
|
|
493
|
+
? fs
|
|
494
|
+
.readdirSync(entryPath, { recursive: true })
|
|
495
|
+
.filter((relativePath) => moduleFilePattern.test(relativePath))
|
|
496
|
+
.map((relativePath) => path.join(entryPath, relativePath))
|
|
497
|
+
.filter((sourceFilePath) => fs.statSync(sourceFilePath).isFile())
|
|
498
|
+
: [entryPath];
|
|
499
|
+
|
|
500
|
+
const requirableExtensionList = ['.js', '.cjs', '.mjs', '.json'];
|
|
501
|
+
|
|
502
|
+
const isInsidePath = (candidatePath, containmentRootPath) =>
|
|
503
|
+
candidatePath == containmentRootPath || candidatePath.startsWith(containmentRootPath + path.sep);
|
|
504
|
+
|
|
505
|
+
const isExistingFile = (candidatePath) => fs.existsSync(candidatePath) && fs.statSync(candidatePath).isFile();
|
|
506
|
+
|
|
507
|
+
const readPackageMain = (dirPath) => {
|
|
508
|
+
const packageJsonPath = path.join(dirPath, 'package.json');
|
|
509
|
+
if (!isExistingFile(packageJsonPath)) {
|
|
510
|
+
return '';
|
|
511
|
+
}
|
|
512
|
+
try {
|
|
513
|
+
// Plugin-supplied JSON, like a plugin's require(), can only be guarded here.
|
|
514
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')).main || '';
|
|
515
|
+
} catch (err) {
|
|
516
|
+
return '';
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
// The file Node would load for a path (exact, with an extension, package.json main, or index), by its realpath.
|
|
521
|
+
// Returns '' when nothing resolves; the caller treats that as forbidden rather than guessing.
|
|
522
|
+
const resolveRequirePath = (requestedPath) => {
|
|
523
|
+
const isDirectory = fs.existsSync(requestedPath) && fs.statSync(requestedPath).isDirectory();
|
|
524
|
+
const packageMain = isDirectory ? readPackageMain(requestedPath) : '';
|
|
525
|
+
const candidatePathList = [
|
|
526
|
+
requestedPath,
|
|
527
|
+
...requirableExtensionList.map((extension) => `${requestedPath}${extension}`),
|
|
528
|
+
...(packageMain ? [path.resolve(requestedPath, packageMain), ...requirableExtensionList.map((extension) => `${path.resolve(requestedPath, packageMain)}${extension}`)] : []),
|
|
529
|
+
...requirableExtensionList.map((extension) => path.join(requestedPath, `index${extension}`)),
|
|
530
|
+
];
|
|
531
|
+
const resolvedPath = candidatePathList.find(isExistingFile);
|
|
532
|
+
return resolvedPath ? fs.realpathSync(resolvedPath) : '';
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// Why a path is not acceptable as plugin code inside containmentRootPath, or '' when it is.
|
|
536
|
+
const findPathProblem = (resolvedPath, containmentRootPath) => {
|
|
537
|
+
if (!resolvedPath) {
|
|
538
|
+
return 'does not resolve to a file';
|
|
539
|
+
}
|
|
540
|
+
if (!isInsidePath(resolvedPath, containmentRootPath)) {
|
|
541
|
+
return `resolves outside ${containmentRootPath} (${resolvedPath})`;
|
|
542
|
+
}
|
|
543
|
+
if (!requirableExtensionList.includes(path.extname(resolvedPath))) {
|
|
544
|
+
return `resolves to a non-module file (${resolvedPath})`;
|
|
545
|
+
}
|
|
546
|
+
return '';
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
const findRequireProblem = ({ requiredName, sourceFilePath, containmentRootPath }) => {
|
|
550
|
+
if (requiredName.startsWith('.') || path.isAbsolute(requiredName)) {
|
|
551
|
+
// Node resolves relative to the requiring file's realpath, so a symlinked file is judged where it really is.
|
|
552
|
+
const requestedPath = path.resolve(path.dirname(fs.realpathSync(sourceFilePath)), requiredName);
|
|
553
|
+
return findPathProblem(resolveRequirePath(requestedPath), containmentRootPath);
|
|
554
|
+
}
|
|
555
|
+
const { allowedModuleList } = forbiddenRequireSpec;
|
|
556
|
+
const isAllowed = allowedModuleList.includes(requiredName) || allowedModuleList.includes(requiredName.replace(/^node:/, ''));
|
|
557
|
+
return isAllowed ? '' : 'is not an allowed module';
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
const findForbiddenRequireList = ({ entryPath, isDirectory, rootDirPath }) => {
|
|
561
|
+
// A directory plugin may use only its own directory; a single-file plugin may use its root.
|
|
562
|
+
const containmentRootPath = fs.realpathSync(isDirectory ? entryPath : rootDirPath);
|
|
563
|
+
const entryProblem = findPathProblem(resolveRequirePath(entryPath), containmentRootPath);
|
|
564
|
+
if (entryProblem) {
|
|
565
|
+
return [`the module itself ${entryProblem}`];
|
|
566
|
+
}
|
|
567
|
+
return listModuleSourceFiles(entryPath, isDirectory).flatMap((sourceFilePath) => {
|
|
568
|
+
const sourceFileProblem = findPathProblem(fs.realpathSync(sourceFilePath), containmentRootPath);
|
|
569
|
+
if (sourceFileProblem) {
|
|
570
|
+
return [`${sourceFilePath} ${sourceFileProblem}`];
|
|
571
|
+
}
|
|
572
|
+
return [...fs.readFileSync(sourceFilePath, 'utf8').matchAll(requireCallPattern)]
|
|
573
|
+
.map((requireMatch) => ({ requiredName: requireMatch[2], requireProblem: findRequireProblem({ requiredName: requireMatch[2], sourceFilePath, containmentRootPath }) }))
|
|
574
|
+
.filter(({ requireProblem }) => requireProblem)
|
|
575
|
+
.map(({ requiredName, requireProblem }) => `require('${requiredName}') in ${sourceFilePath} ${requireProblem}`);
|
|
576
|
+
});
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
const loadOneModule = ({ entryPath, provenance, moduleArgs, tolerateLoadErrors }) => {
|
|
580
|
+
loadContext = { provenance, filePath: entryPath, addedNameList: [] };
|
|
581
|
+
let loadErrorString = '';
|
|
582
|
+
try {
|
|
583
|
+
// The one catch in path-list mode: a third-party module's top-level throw can only be caught here.
|
|
584
|
+
require(entryPath)(moduleArgs);
|
|
585
|
+
} catch (err) {
|
|
586
|
+
loadErrorString = err.toString().split('\n').qtFirst('n/a');
|
|
587
|
+
}
|
|
588
|
+
const { addedNameList } = loadContext;
|
|
589
|
+
loadContext = undefined;
|
|
590
|
+
|
|
591
|
+
if (loadErrorString && tolerateLoadErrors) {
|
|
592
|
+
const listedString = `PLUGIN_LOAD_FAILED: ${path.basename(entryPath)}: ${loadErrorString}`;
|
|
593
|
+
exceptionList.push(listedString);
|
|
594
|
+
logList.push(`${listedString} (${entryPath}; listed because tolerateLoadErrors)`);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
if (loadErrorString) {
|
|
598
|
+
refuse({ refusalCode: 'PLUGIN_LOAD_FAILED', elementName: '', filePath: entryPath, message: `${entryPath}: ${loadErrorString}` });
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
if (!addedNameList.length) {
|
|
602
|
+
logList.push(`MODULE_ADDED_NOTHING: ${provenance} ${entryPath} loaded but called library.add() for nothing`);
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
const loadOneRoot = ({ pathListEntry, moduleArgs, tolerateLoadErrors }) => {
|
|
607
|
+
const { dirPath: rootDirPath, provenance } = pathListEntry;
|
|
608
|
+
const scanForbiddenRequires = forbiddenRequireSpec && forbiddenRequireSpec.provenanceList.includes(provenance);
|
|
609
|
+
|
|
610
|
+
fs.readdirSync(rootDirPath).forEach((entryName) => {
|
|
611
|
+
const entryPath = path.join(rootDirPath, entryName);
|
|
612
|
+
const isDirectory = fs.statSync(entryPath).isDirectory();
|
|
613
|
+
if (entryName.startsWith('.') || (!isDirectory && !moduleFilePattern.test(entryName))) {
|
|
614
|
+
logList.push(`SKIPPED_NON_MODULE: ${provenance} ${entryPath}`);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (scanForbiddenRequires) {
|
|
618
|
+
const forbiddenRequireList = findForbiddenRequireList({ entryPath, isDirectory, rootDirPath });
|
|
619
|
+
if (forbiddenRequireList.length) {
|
|
620
|
+
refuse({
|
|
621
|
+
refusalCode: 'PLUGIN_FORBIDDEN_REQUIRE',
|
|
622
|
+
elementName: '',
|
|
623
|
+
filePath: entryPath,
|
|
624
|
+
message: `${provenance} ${entryPath} may require only allowed modules or its own files; found ${forbiddenRequireList.join(', ')}`,
|
|
625
|
+
});
|
|
626
|
+
return; // never executed
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
loadOneModule({ entryPath, provenance, moduleArgs, tolerateLoadErrors });
|
|
630
|
+
});
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
let pathListLoadCount = 0;
|
|
634
|
+
|
|
635
|
+
const loadModulesFromPathList = ({ passThroughParameters, tolerateLoadErrors = false } = {}, callback) => {
|
|
636
|
+
const asynchronousPipePlus = new require('qtools-asynchronous-pipe-plus')();
|
|
637
|
+
const pipeRunner = asynchronousPipePlus.pipeRunner;
|
|
638
|
+
const taskListPlus = asynchronousPipePlus.taskListPlus;
|
|
639
|
+
|
|
640
|
+
const taskList = new taskListPlus();
|
|
641
|
+
|
|
642
|
+
// --------------------------------------------------------------------------------
|
|
643
|
+
// REFUSE A SECOND LOAD (every element would collide with itself)
|
|
644
|
+
|
|
645
|
+
taskList.push((args, next) => {
|
|
646
|
+
if (pathListLoadCount > 0) {
|
|
647
|
+
next(`LOAD_MODULES_REPEATED: ${libraryName} path-list libraries load once; loadModules was already called`, args);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
pathListLoadCount++;
|
|
651
|
+
next('', args);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
// --------------------------------------------------------------------------------
|
|
655
|
+
// REFUSE A CUSTOM ADD FUNCTION (it cannot report which file failed)
|
|
656
|
+
|
|
657
|
+
taskList.push((args, next) => {
|
|
658
|
+
if (add !== defaultAdd) {
|
|
659
|
+
next(`CUSTOM_ADD_REQUIRES_SINGLE_PATH: ${libraryName} setCustomAddFunction is not supported with setLibraryPathList`, args);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
next('', args);
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
// --------------------------------------------------------------------------------
|
|
666
|
+
// LOAD ROOTS BY PROVENANCE: FRAMEWORK FIRST, THEN INSTANCE, WHATEVER THE LIST ORDER
|
|
667
|
+
|
|
668
|
+
taskList.push((args, next) => {
|
|
669
|
+
const moduleArgs = { dotD: { logList, library }, passThroughParameters };
|
|
670
|
+
const orderedEntryList = ['framework', 'instance'].flatMap((provenance) =>
|
|
671
|
+
libraryPathList.filter((pathListEntry) => pathListEntry.provenance == provenance && !pathListEntry.rootIsAbsent),
|
|
672
|
+
);
|
|
673
|
+
orderedEntryList.forEach((pathListEntry) => loadOneRoot({ pathListEntry, moduleArgs, tolerateLoadErrors }));
|
|
674
|
+
next('', args);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
// --------------------------------------------------------------------------------
|
|
678
|
+
// REPORT EVERY REFUSAL AT ONCE
|
|
679
|
+
|
|
680
|
+
taskList.push((args, next) => {
|
|
681
|
+
if (refusalList.length) {
|
|
682
|
+
next(refusalList.map(({ message }) => message).join('\n'), args);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
next('', args);
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// --------------------------------------------------------------------------------
|
|
689
|
+
// INIT AND EXECUTE THE PIPELINE
|
|
690
|
+
|
|
691
|
+
let pipeResult;
|
|
692
|
+
pipeRunner(taskList.getList(), {}, (err) => {
|
|
693
|
+
pipeResult = deliverResult(err, undefined, callback);
|
|
694
|
+
});
|
|
695
|
+
return pipeResult;
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
// --------------------------------------------------------------------------------
|
|
699
|
+
// LOAD MODULES FROM ONE PATH (1.0.13 behaviour, unchanged)
|
|
700
|
+
|
|
701
|
+
const loadModulesFromSinglePath = ({ passThroughParameters }, callback) => {
|
|
162
702
|
const asynchronousPipePlus = new require(
|
|
163
703
|
'qtools-asynchronous-pipe-plus',
|
|
164
704
|
)();
|
|
@@ -215,6 +755,12 @@ const moduleFunction =
|
|
|
215
755
|
|
|
216
756
|
const externalElements = {
|
|
217
757
|
setLibraryPath,
|
|
758
|
+
setLibraryPathList,
|
|
759
|
+
setInterface,
|
|
760
|
+
setForbiddenRequireCheck,
|
|
761
|
+
getProvenance,
|
|
762
|
+
getProvenanceList,
|
|
763
|
+
getRefusalList,
|
|
218
764
|
setCustomAddFunction,
|
|
219
765
|
logList,
|
|
220
766
|
library,
|
|
@@ -223,6 +769,11 @@ const moduleFunction =
|
|
|
223
769
|
qtdProcessLog: logList.formatList,
|
|
224
770
|
};
|
|
225
771
|
externalElements.seal = sealActual(externalElements);
|
|
772
|
+
// Elements become own properties of this object, so an element may not take an API name, nor a name
|
|
773
|
+
// Object.prototype carries: '__proto__' would replace the prototype, and a qtools-functional-library
|
|
774
|
+
// method such as qtSelectProperties (installed on Object.prototype) would be shadowed, breaking seal().
|
|
775
|
+
apiNameList = Object.keys(externalElements);
|
|
776
|
+
reservedNameList = [...apiNameList, ...Object.getOwnPropertyNames(Object.prototype)];
|
|
226
777
|
return externalElements;
|
|
227
778
|
};
|
|
228
779
|
|