dbgate-tools 7.1.8 → 7.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.
@@ -26,6 +26,7 @@ class ScriptWriterJavaScript {
26
26
  this._put();
27
27
  }
28
28
  assignCore(variableName, functionName, props) {
29
+ (0, packageTools_1.assertValidJsIdentifier)(variableName, 'variableName');
29
30
  this._put(`const ${variableName} = await ${functionName}(${JSON.stringify(props)});`);
30
31
  }
31
32
  assign(variableName, functionName, props) {
@@ -33,15 +34,20 @@ class ScriptWriterJavaScript {
33
34
  this.packageNames.push(...(0, packageTools_1.extractShellApiPlugins)(functionName, props));
34
35
  }
35
36
  assignValue(variableName, jsonValue) {
37
+ (0, packageTools_1.assertValidJsIdentifier)(variableName, 'variableName');
36
38
  this._put(`const ${variableName} = ${JSON.stringify(jsonValue)};`);
37
39
  }
38
40
  requirePackage(packageName) {
39
41
  this.packageNames.push(packageName);
40
42
  }
41
43
  copyStream(sourceVar, targetVar, colmapVar = null, progressName) {
44
+ (0, packageTools_1.assertValidJsIdentifier)(sourceVar, 'sourceVar');
45
+ (0, packageTools_1.assertValidJsIdentifier)(targetVar, 'targetVar');
42
46
  let opts = '{';
43
- if (colmapVar)
47
+ if (colmapVar) {
48
+ (0, packageTools_1.assertValidJsIdentifier)(colmapVar, 'colmapVar');
44
49
  opts += `columns: ${colmapVar}, `;
50
+ }
45
51
  if (progressName)
46
52
  opts += `progressName: ${JSON.stringify(progressName)}, `;
47
53
  opts += '}';
@@ -68,7 +74,7 @@ class ScriptWriterJavaScript {
68
74
  return prefix + this.s;
69
75
  }
70
76
  zipDirectory(inputDirectory, outputFile) {
71
- this._put(`await dbgateApi.zipDirectory('${inputDirectory}', '${outputFile}');`);
77
+ this._put(`await dbgateApi.zipDirectory(${JSON.stringify(inputDirectory)}, ${JSON.stringify(outputFile)});`);
72
78
  }
73
79
  }
74
80
  exports.ScriptWriterJavaScript = ScriptWriterJavaScript;
@@ -171,6 +177,8 @@ class ScriptWriterEval {
171
177
  endLine() { }
172
178
  requirePackage(packageName) { }
173
179
  async assign(variableName, functionName, props) {
180
+ (0, packageTools_1.assertValidJsIdentifier)(variableName, 'variableName');
181
+ (0, packageTools_1.assertValidShellApiFunctionName)(functionName);
174
182
  const func = (0, packageTools_1.evalShellApiFunctionName)(functionName, this.dbgateApi, this.requirePlugin);
175
183
  this.variables[variableName] = await func((0, cloneDeepWith_1.default)(props, node => {
176
184
  if (node === null || node === void 0 ? void 0 : node.$hostConnection) {
@@ -179,9 +187,14 @@ class ScriptWriterEval {
179
187
  }));
180
188
  }
181
189
  assignValue(variableName, jsonValue) {
190
+ (0, packageTools_1.assertValidJsIdentifier)(variableName, 'variableName');
182
191
  this.variables[variableName] = jsonValue;
183
192
  }
184
193
  async copyStream(sourceVar, targetVar, colmapVar = null, progressName) {
194
+ (0, packageTools_1.assertValidJsIdentifier)(sourceVar, 'sourceVar');
195
+ (0, packageTools_1.assertValidJsIdentifier)(targetVar, 'targetVar');
196
+ if (colmapVar != null)
197
+ (0, packageTools_1.assertValidJsIdentifier)(colmapVar, 'colmapVar');
185
198
  await this.dbgateApi.copyStream(this.variables[sourceVar], this.variables[targetVar], {
186
199
  progressName: (0, cloneDeepWith_1.default)(progressName, node => {
187
200
  if (node === null || node === void 0 ? void 0 : node.$runid) {
@@ -1,4 +1,13 @@
1
1
  import type { EngineDriver, ExtensionsDirectory } from 'dbgate-types';
2
+ export declare function isValidJsIdentifier(name: string): boolean;
3
+ export declare function assertValidJsIdentifier(name: string, label: string): void;
4
+ /**
5
+ * Validates a shell API function name.
6
+ * Allowed forms:
7
+ * - "someFunctionName" (plain identifier, resolved as dbgateApi.someFunctionName)
8
+ * - "funcName@dbgate-plugin-xxx" (namespaced, resolved as plugin.shellApi.funcName)
9
+ */
10
+ export declare function assertValidShellApiFunctionName(functionName: string): void;
2
11
  export declare function extractShellApiPlugins(functionName: any, props: any): string[];
3
12
  export declare function extractPackageName(name: any): string;
4
13
  export declare function compileShellApiFunctionName(functionName: any): string;
@@ -3,10 +3,66 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.findEngineDriver = exports.evalShellApiFunctionName = exports.compileShellApiFunctionName = exports.extractPackageName = exports.extractShellApiPlugins = void 0;
6
+ exports.findEngineDriver = exports.evalShellApiFunctionName = exports.compileShellApiFunctionName = exports.extractPackageName = exports.extractShellApiPlugins = exports.assertValidShellApiFunctionName = exports.assertValidJsIdentifier = exports.isValidJsIdentifier = void 0;
7
7
  const camelCase_1 = __importDefault(require("lodash/camelCase"));
8
8
  const isString_1 = __importDefault(require("lodash/isString"));
9
9
  const isPlainObject_1 = __importDefault(require("lodash/isPlainObject"));
10
+ const JS_IDENTIFIER_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
11
+ // ECMAScript reserved words, strict-mode keywords, and async-context keywords
12
+ // that cannot be used as variable or function names in the generated scripts.
13
+ // Sources: ECMA-262 §12.7.2 (reserved words), §12.7.3 (strict mode), §14 (contextual).
14
+ const JS_RESERVED_WORDS = new Set([
15
+ // Keywords
16
+ 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default',
17
+ 'delete', 'do', 'else', 'export', 'extends', 'false', 'finally', 'for',
18
+ 'function', 'if', 'import', 'in', 'instanceof', 'let', 'new', 'null', 'return',
19
+ 'static', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var',
20
+ 'void', 'while', 'with', 'yield',
21
+ // Strict-mode reserved words
22
+ 'implements', 'interface', 'package', 'private', 'protected', 'public',
23
+ // Async context keywords
24
+ 'async', 'await',
25
+ // Future reserved
26
+ 'enum',
27
+ 'eval', 'arguments',
28
+ ]);
29
+ function isValidJsIdentifier(name) {
30
+ return typeof name === 'string' && JS_IDENTIFIER_RE.test(name) && !JS_RESERVED_WORDS.has(name);
31
+ }
32
+ exports.isValidJsIdentifier = isValidJsIdentifier;
33
+ function assertValidJsIdentifier(name, label) {
34
+ if (!isValidJsIdentifier(name)) {
35
+ throw new Error(`DBGM-00000 Invalid ${label}: ${String(name).substring(0, 100)}`);
36
+ }
37
+ }
38
+ exports.assertValidJsIdentifier = assertValidJsIdentifier;
39
+ /**
40
+ * Validates a shell API function name.
41
+ * Allowed forms:
42
+ * - "someFunctionName" (plain identifier, resolved as dbgateApi.someFunctionName)
43
+ * - "funcName@dbgate-plugin-xxx" (namespaced, resolved as plugin.shellApi.funcName)
44
+ */
45
+ function assertValidShellApiFunctionName(functionName) {
46
+ if (typeof functionName !== 'string') {
47
+ throw new Error('DBGM-00000 functionName must be a string');
48
+ }
49
+ const nsMatch = functionName.match(/^([^@]+)@([^@]+)$/);
50
+ if (nsMatch) {
51
+ if (!isValidJsIdentifier(nsMatch[1])) {
52
+ throw new Error(`DBGM-00000 Invalid function part in functionName: ${nsMatch[1].substring(0, 100)}`);
53
+ }
54
+ if (!/^dbgate-plugin-[a-zA-Z0-9_-]+$/.test(nsMatch[2])) {
55
+ throw new Error(`DBGM-00000 Invalid plugin package in functionName: ${nsMatch[2].substring(0, 100)}`);
56
+ }
57
+ }
58
+ else {
59
+ if (!isValidJsIdentifier(functionName)) {
60
+ throw new Error(`DBGM-00000 Invalid functionName: ${functionName.substring(0, 100)}`);
61
+ }
62
+ }
63
+ }
64
+ exports.assertValidShellApiFunctionName = assertValidShellApiFunctionName;
65
+ const VALID_PLUGIN_NAME_RE = /^dbgate-plugin-[a-zA-Z0-9_-]+$/;
10
66
  function extractShellApiPlugins(functionName, props) {
11
67
  const res = [];
12
68
  const nsMatch = functionName.match(/^([^@]+)@([^@]+)/);
@@ -19,6 +75,11 @@ function extractShellApiPlugins(functionName, props) {
19
75
  res.push(nsMatchEngine[2]);
20
76
  }
21
77
  }
78
+ for (const plugin of res) {
79
+ if (!VALID_PLUGIN_NAME_RE.test(plugin)) {
80
+ throw new Error(`DBGM-00000 Invalid plugin name: ${String(plugin).substring(0, 100)}`);
81
+ }
82
+ }
22
83
  return res;
23
84
  }
24
85
  exports.extractShellApiPlugins = extractShellApiPlugins;
@@ -33,7 +94,8 @@ function extractPackageName(name) {
33
94
  }
34
95
  exports.extractPackageName = extractPackageName;
35
96
  function compileShellApiFunctionName(functionName) {
36
- const nsMatch = functionName.match(/^([^@]+)@([^@]+)/);
97
+ assertValidShellApiFunctionName(functionName);
98
+ const nsMatch = functionName.match(/^([^@]+)@([^@]+)$/);
37
99
  if (nsMatch) {
38
100
  return `${(0, camelCase_1.default)(nsMatch[2])}.shellApi.${nsMatch[1]}`;
39
101
  }
@@ -41,7 +103,8 @@ function compileShellApiFunctionName(functionName) {
41
103
  }
42
104
  exports.compileShellApiFunctionName = compileShellApiFunctionName;
43
105
  function evalShellApiFunctionName(functionName, dbgateApi, requirePlugin) {
44
- const nsMatch = functionName.match(/^([^@]+)@([^@]+)/);
106
+ assertValidShellApiFunctionName(functionName);
107
+ const nsMatch = functionName.match(/^([^@]+)@([^@]+)$/);
45
108
  if (nsMatch) {
46
109
  return requirePlugin(nsMatch[2]).shellApi[nsMatch[1]];
47
110
  }
@@ -98,22 +98,24 @@ function parseCellValue(value, editorTypes) {
98
98
  return null;
99
99
  }
100
100
  if (editorTypes === null || editorTypes === void 0 ? void 0 : editorTypes.parseHexAsBuffer) {
101
- const mUuid3 = value.match(uuid3WrapperRegex);
102
- if (mUuid3) {
103
- const base64Uuid3 = uuidToBase64(mUuid3[1]);
104
- if (base64Uuid3 != null)
105
- return { $binary: { base64: base64Uuid3, subType: '03' } };
106
- }
107
- const mUuid4 = value.match(uuid4WrapperRegex);
108
- if (mUuid4) {
109
- const base64Uuid4 = uuidToBase64(mUuid4[1]);
110
- if (base64Uuid4 != null)
111
- return { $binary: { base64: base64Uuid4, subType: '04' } };
112
- }
113
- if (uuidRegex.test(value)) {
114
- const base64UuidPlain = uuidToBase64(value);
115
- if (base64UuidPlain != null)
116
- return { $binary: { base64: base64UuidPlain, subType: '04' } };
101
+ if (editorTypes === null || editorTypes === void 0 ? void 0 : editorTypes.parseUuid) {
102
+ const mUuid3 = value.match(uuid3WrapperRegex);
103
+ if (mUuid3) {
104
+ const base64Uuid3 = uuidToBase64(mUuid3[1]);
105
+ if (base64Uuid3 != null)
106
+ return { $binary: { base64: base64Uuid3, subType: '03' } };
107
+ }
108
+ const mUuid4 = value.match(uuid4WrapperRegex);
109
+ if (mUuid4) {
110
+ const base64Uuid4 = uuidToBase64(mUuid4[1]);
111
+ if (base64Uuid4 != null)
112
+ return { $binary: { base64: base64Uuid4, subType: '04' } };
113
+ }
114
+ if (uuidRegex.test(value)) {
115
+ const base64UuidPlain = uuidToBase64(value);
116
+ if (base64UuidPlain != null)
117
+ return { $binary: { base64: base64UuidPlain, subType: '04' } };
118
+ }
117
119
  }
118
120
  const mHex = value.match(/^0x([0-9a-fA-F][0-9a-fA-F])+$/);
119
121
  if (mHex) {
@@ -301,24 +303,20 @@ function stringifyCellValue(value, intent, editorTypes, gridFormattingOptions, j
301
303
  return { value: 'false', gridStyle: 'valueCellStyle' };
302
304
  if ((_a = value === null || value === void 0 ? void 0 : value.$binary) === null || _a === void 0 ? void 0 : _a.base64) {
303
305
  const subType = value.$binary.subType;
304
- if (subType === '03' || subType === '04') {
305
- const uuidStr = base64ToUuid(value.$binary.base64);
306
- if (uuidStr != null) {
307
- if (intent === 'gridCellIntent' || intent === 'exportIntent' || intent === 'clipboardIntent' || intent === 'stringConversionIntent') {
308
- return { value: uuidStr, gridStyle: 'valueCellStyle' };
309
- }
310
- // For editing intents: tag with subType so parseCellValue can round-trip it
306
+ const isUuidType = subType === '03' || subType === '04' || (editorTypes === null || editorTypes === void 0 ? void 0 : editorTypes.parseUuid);
307
+ const uuidStr = isUuidType ? base64ToUuid(value.$binary.base64) : null;
308
+ if (uuidStr != null) {
309
+ // MongoDB editing intents: wrap with UUID()/UUID3() so parseCellValue can round-trip it
310
+ if ((editorTypes === null || editorTypes === void 0 ? void 0 : editorTypes.parseUuid) && intent !== 'gridCellIntent' && intent !== 'exportIntent' && intent !== 'clipboardIntent' && intent !== 'stringConversionIntent') {
311
311
  const tag = subType === '03' ? 'UUID3' : 'UUID';
312
312
  return { value: `${tag}("${uuidStr}")`, gridStyle: 'valueCellStyle' };
313
313
  }
314
+ return { value: uuidStr, gridStyle: 'valueCellStyle' };
314
315
  }
315
316
  if (intent === 'gridCellIntent' && value.$binary.base64.length > exports.MAX_GRID_BINARY_SIZE) {
316
317
  return { value: `(Field too large, ${formatByteSize(Math.round(value.$binary.base64.length * 3 / 4))})`, gridStyle: 'nullCellStyle' };
317
318
  }
318
- return {
319
- value: base64ToHex(value.$binary.base64),
320
- gridStyle: 'valueCellStyle',
321
- };
319
+ return { value: base64ToHex(value.$binary.base64), gridStyle: 'valueCellStyle' };
322
320
  }
323
321
  if (value === null || value === void 0 ? void 0 : value.$decimal) {
324
322
  return {
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "7.1.8",
2
+ "version": "7.1.10",
3
3
  "name": "dbgate-tools",
4
4
  "main": "lib/index.js",
5
5
  "typings": "lib/index.d.ts",
@@ -26,7 +26,7 @@
26
26
  ],
27
27
  "devDependencies": {
28
28
  "@types/node": "^13.7.0",
29
- "dbgate-types": "7.1.8",
29
+ "dbgate-types": "7.1.10",
30
30
  "jest": "^28.1.3",
31
31
  "ts-jest": "^28.0.7",
32
32
  "typescript": "^4.4.3"
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "blueimp-md5": "^2.19.0",
36
36
  "dbgate-query-splitter": "^4.12.0",
37
- "dbgate-sqltree": "7.1.8",
37
+ "dbgate-sqltree": "7.1.10",
38
38
  "debug": "^4.3.4",
39
39
  "json-stable-stringify": "^1.0.1",
40
40
  "lodash": "^4.17.21",