datagrok-tools 6.3.2 → 6.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Datagrok-tools changelog
2
2
 
3
+ ## 6.4.0 (2026-06-18)
4
+
5
+ * Dependencies: sanitized and updated all dependencies; `npm install` is now warning-free and `npm audit` reports 0 vulnerabilities (was 24).
6
+ * Dependencies: migrated linting to ESLint 9 flat config (`eslint.config.mjs` + `typescript-eslint` + `@stylistic`), dropping the archived `eslint-config-google`.
7
+ * Dependencies: upgraded Puppeteer to v24 and migrated to its native `page.screencast()` for `--record`, removing `puppeteer-screen-recorder` and the deprecated `fluent-ffmpeg`.
8
+ * Dependencies: replaced `archiver-promise` with `archiver` directly, and replaced `@babel/cli` with a small `@babel/core` build script (`build.js`) to drop deprecated transitive packages (glob@7, inflight).
9
+
10
+ ## 6.3.3 (2026-06-16)
11
+
12
+ * Fixed Celery Docker image generation — the image wasn't built locally on publish.
13
+
3
14
  ## 6.3.2 (2026-06-15)
4
15
 
5
16
  * `func-gen` webpack plugin — generated RichFunctionView model inputs now use the script-form names (argument bounds/step `_t0`/`_t1`/`_h`, loop count `_count`) instead of the deprefixed forms, so the run, fitting, and sensitivity-analysis paths share one set of input names with diff-grok's pipeline. Fixes `Inconsistent inputs: "_t0" is missing` when starting fitting/SA from a Rich Function View.
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.processPackage = processPackage;
8
8
  exports.publish = publish;
9
- var _archiverPromise = _interopRequireDefault(require("archiver-promise"));
9
+ var _archiver = _interopRequireDefault(require("archiver"));
10
10
  var _crypto = _interopRequireDefault(require("crypto"));
11
11
  var _fs = _interopRequireDefault(require("fs"));
12
12
  var _nodeFetch = _interopRequireDefault(require("node-fetch"));
@@ -22,8 +22,6 @@ var _pythonCeleryGen = require("../utils/python-celery-gen");
22
22
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
23
23
  // @ts-ignore
24
24
 
25
- // @ts-ignore
26
-
27
25
  const {
28
26
  exec,
29
27
  execSync
@@ -426,7 +424,7 @@ async function processPackage(debug, rebuild, host, devKey, packageName, dropDb,
426
424
  if (color.isVerbose()) console.error(error);
427
425
  return 1;
428
426
  }
429
- const zip = (0, _archiverPromise.default)('zip', {
427
+ const zip = (0, _archiver.default)('zip', {
430
428
  store: false
431
429
  });
432
430
  const chunks = [];
@@ -8,6 +8,7 @@ exports.generateCeleryArtifacts = generateCeleryArtifacts;
8
8
  var _fs = _interopRequireDefault(require("fs"));
9
9
  var _path = _interopRequireDefault(require("path"));
10
10
  var color = _interopRequireWildcard(require("./color-utils"));
11
+ var _utils = require("./utils");
11
12
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
12
13
  // Header tags recognized in Python function metadata comments (ported from ScriptParser.headerTags)
13
14
  const headerTags = ['name', 'description', 'help-url', 'input', 'output', 'tags', 'sample', 'language', 'endpoint', 'requiresServer', 'param-csrfmiddlewaretoken', 'returns', 'test', 'sidebar', 'condition', 'top-menu', 'environment', 'require', 'editor-for', 'schedule', 'schedule.runAs', 'reference', 'editor'];
@@ -191,13 +192,16 @@ function copyDirContents(src, dest) {
191
192
  function useConda(dir) {
192
193
  return _fs.default.existsSync(_path.default.join(dir, 'environment.yaml')) || _fs.default.existsSync(_path.default.join(dir, 'environment.yml'));
193
194
  }
194
- function deployFolder(packageDir, folderPath, dirName) {
195
+
196
+ // `dockerSubfolder` is the dockerfiles/ subdirectory name; the published image is
197
+ // `<base>-<dockerSubfolder>` (see publish.discoverDockerfiles).
198
+ function deployFolder(packageDir, folderPath, dockerSubfolder, base) {
195
199
  const tasks = scanPythonFunctions(folderPath, folderPath);
196
200
  if (tasks.length === 0) {
197
- color.log(`No annotated Python functions found in ${dirName}`);
201
+ color.log(`No annotated Python functions found in ${dockerSubfolder}`);
198
202
  return false;
199
203
  }
200
- const dockerfilesDir = _path.default.join(packageDir, 'dockerfiles', dirName);
204
+ const dockerfilesDir = _path.default.join(packageDir, 'dockerfiles', dockerSubfolder);
201
205
  _fs.default.mkdirSync(dockerfilesDir, {
202
206
  recursive: true
203
207
  });
@@ -211,8 +215,9 @@ function deployFolder(packageDir, folderPath, dirName) {
211
215
  tasks
212
216
  }, null, 2));
213
217
 
214
- // Generate Celery entry point
215
- const celeryName = dirName.replace(/-/g, '_');
218
+ // Entry point file name must equal $DATAGROK_CELERY_NAME (the image name, '-' -> '_'),
219
+ // so `celery -A $DATAGROK_CELERY_NAME` resolves it.
220
+ const celeryName = `${base}-${dockerSubfolder}`.replace(/-/g, '_');
216
221
  _fs.default.writeFileSync(_path.default.join(dockerfilesDir, celeryName + '.py'), TEMPLATE_PYTHON_ENTRY);
217
222
 
218
223
  // Copy Python source files
@@ -222,26 +227,27 @@ function deployFolder(packageDir, folderPath, dirName) {
222
227
  const containerJsonSrc = _path.default.join(folderPath, 'container.json');
223
228
  const containerJsonDest = _path.default.join(dockerfilesDir, 'container.json');
224
229
  if (_fs.default.existsSync(containerJsonSrc) && !_fs.default.existsSync(containerJsonDest)) _fs.default.copyFileSync(containerJsonSrc, containerJsonDest);
225
- color.log(`Generated Celery Docker artifacts in dockerfiles/${dirName}/`);
230
+ color.log(`Generated Celery Docker artifacts in dockerfiles/${dockerSubfolder}/`);
226
231
  return true;
227
232
  }
228
233
  function generateCeleryArtifacts(packageDir) {
229
234
  const pythonDir = _path.default.join(packageDir, 'python');
230
235
  if (!_fs.default.existsSync(pythonDir)) return false;
236
+ const packageJson = JSON.parse(_fs.default.readFileSync(_path.default.join(packageDir, 'package.json'), 'utf-8'));
237
+ const base = (0, _utils.removeScope)(packageJson.name).toLowerCase();
231
238
  const entries = _fs.default.readdirSync(pythonDir, {
232
239
  withFileTypes: true
233
240
  });
234
- const isNested = entries.length > 0 && entries.every(e => e.isDirectory);
241
+ const isNested = entries.length > 0 && entries.every(e => e.isDirectory());
235
242
  let generated = false;
236
243
  if (isNested) {
237
244
  for (const entry of entries) {
238
245
  if (!entry.isDirectory()) continue;
239
246
  const folderPath = _path.default.join(pythonDir, entry.name);
240
- if (deployFolder(packageDir, folderPath, entry.name)) generated = true;
247
+ if (deployFolder(packageDir, folderPath, `${entry.name.toLowerCase()}-celery`, base)) generated = true;
241
248
  }
242
249
  } else {
243
- const dirName = _path.default.basename(packageDir).toLowerCase();
244
- if (deployFolder(packageDir, pythonDir, dirName)) generated = true;
250
+ if (deployFolder(packageDir, pythonDir, 'celery', base)) generated = true;
245
251
  }
246
252
  return generated;
247
253
  }
@@ -21,7 +21,6 @@ exports.loadPackages = loadPackages;
21
21
  exports.loadTestsList = loadTestsList;
22
22
  exports.mergeBrowsersResults = mergeBrowsersResults;
23
23
  exports.printBrowsersResult = printBrowsersResult;
24
- exports.recorderConfig = void 0;
25
24
  exports.runBrowser = runBrowser;
26
25
  exports.runWithTimeout = runWithTimeout;
27
26
  exports.saveCsvResults = saveCsvResults;
@@ -32,7 +31,6 @@ var _path = _interopRequireDefault(require("path"));
32
31
  var _jsYaml = _interopRequireDefault(require("js-yaml"));
33
32
  var _utils = _interopRequireWildcard(require("../utils/utils"));
34
33
  var utils = _utils;
35
- var _puppeteerScreenRecorder = require("puppeteer-screen-recorder");
36
34
  var _puppeteer = _interopRequireDefault(require("puppeteer"));
37
35
  var color = _interopRequireWildcard(require("../utils/color-utils"));
38
36
  var _papaparse = _interopRequireDefault(require("papaparse"));
@@ -43,8 +41,8 @@ const confPath = _path.default.join(grokDir, 'config.yaml');
43
41
  const testCollectionTimeout = 600000;
44
42
  const defaultLaunchParameters = exports.defaultLaunchParameters = {
45
43
  args: ['--disable-dev-shm-usage', '--disable-features=site-per-process', '--window-size=1920,1080', '--js-flags=--expose-gc'],
46
- ignoreHTTPSErrors: true,
47
- headless: 'new',
44
+ acceptInsecureCerts: true,
45
+ headless: true,
48
46
  protocolTimeout: 0
49
47
  };
50
48
  async function getToken(url, key) {
@@ -150,9 +148,10 @@ async function getBrowserPage(puppeteer, params = defaultLaunchParameters, urlPa
150
148
  });
151
149
  page.setDefaultNavigationTimeout(0);
152
150
  await page.goto(`${url}/oauth/`);
153
- await page.setCookie({
151
+ await page.browser().setCookie({
154
152
  name: 'auth',
155
- value: token
153
+ value: token,
154
+ domain: new URL(url).hostname
156
155
  });
157
156
  await page.evaluate(token => {
158
157
  window.localStorage.setItem('auth', token);
@@ -201,23 +200,6 @@ function exitWithCode(code) {
201
200
  console.log(`Exiting with code ${code}`);
202
201
  process.exit(code);
203
202
  }
204
- const recorderConfig = exports.recorderConfig = {
205
- followNewTab: true,
206
- fps: 25,
207
- ffmpeg_Path: null,
208
- videoFrame: {
209
- width: 1280,
210
- height: 630
211
- },
212
- videoCrf: 18,
213
- videoCodec: 'libx264',
214
- videoPreset: 'ultrafast',
215
- videoBitrate: 1000,
216
- autopad: {
217
- color: 'black'
218
- }
219
- // aspectRatio: '16:9',
220
- };
221
203
  async function loadPackage(packageDir, dirName, hostString, skipPublish, skipBuild, linkPackage, release) {
222
204
  if (skipPublish != true) {
223
205
  process.stdout.write(`Building and publishing ${dirName}...`);
@@ -274,8 +256,9 @@ async function loadTestsList(packages, core = false, record = false) {
274
256
  const suffix = process.env.BACKUP_SIZE && process.env.WORKER_ID && process.env.TOTAL_WORKERS ? `_${process.env.BACKUP_SIZE}_${process.env.WORKER_ID}_${process.env.TOTAL_WORKERS}` : '';
275
257
  const logsDir = `./load-test-console-output${suffix}.log`;
276
258
  const recordDir = `./load-test-record${suffix}.mp4`;
277
- recorder = new _puppeteerScreenRecorder.PuppeteerScreenRecorder(page, recorderConfig);
278
- await recorder.start(recordDir);
259
+ recorder = await page.screencast({
260
+ path: recordDir
261
+ });
279
262
  await page.exposeFunction('addLogsToFile', addLogsToFile);
280
263
  _fs.default.writeFileSync(logsDir, ``);
281
264
  page.on('console', msg => {
@@ -648,13 +631,15 @@ async function runBrowser(testExecutionData, browserOptions, browsersId, testInv
648
631
  page = out.page;
649
632
  webUrl = await getWebUrlFromPage(page);
650
633
  }
651
- const recorder = new _puppeteerScreenRecorder.PuppeteerScreenRecorder(page, recorderConfig);
634
+ let recorder = null;
652
635
  const currentBrowserNum = browsersId;
653
636
  const logsDir = `./test-console-output-${currentBrowserNum}.log`;
654
637
  const recordDir = `./test-record-${currentBrowserNum}.mp4`;
655
638
  if (browserOptions.record && !existingBrowserSession) {
656
639
  // Only set up recording on initial browser creation, not on retry
657
- await recorder.start(recordDir);
640
+ recorder = await page.screencast({
641
+ path: recordDir
642
+ });
658
643
  await page.exposeFunction('addLogsToFile', addLogsToFile);
659
644
  _fs.default.writeFileSync(logsDir, ``);
660
645
  page.on('console', msg => {
@@ -866,7 +851,7 @@ async function runBrowser(testExecutionData, browserOptions, browsersId, testInv
866
851
 
867
852
  // Print the final category summary
868
853
  printFinalCategorySummary();
869
- if (browserOptions.record && !existingBrowserSession) await recorder.stop();
854
+ if (browserOptions.record && !existingBrowserSession) await recorder?.stop();
870
855
  if (modernOutput) {
871
856
  testingResults.verbosePassed = '';
872
857
  testingResults.verboseSkipped = '';
@@ -0,0 +1,42 @@
1
+ import js from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+ import stylistic from '@stylistic/eslint-plugin';
4
+ import globals from 'globals';
5
+
6
+ // Flat config (ESLint 9). Replaces the legacy .eslintrc.json + eslint-config-google.
7
+ // Stylistic rules that used to live in ESLint core now come from @stylistic.
8
+ export default tseslint.config(
9
+ {
10
+ ignores: [
11
+ 'bin/**/*.js',
12
+ 'bin/**/*.js.map',
13
+ 'node_modules/**',
14
+ 'package-template/**',
15
+ 'entity-template/**',
16
+ 'script-template/**',
17
+ ],
18
+ },
19
+ js.configs.recommended,
20
+ ...tseslint.configs.recommended,
21
+ {
22
+ files: ['bin/**/*.ts'],
23
+ plugins: {'@stylistic': stylistic},
24
+ languageOptions: {
25
+ ecmaVersion: 2022,
26
+ sourceType: 'module',
27
+ globals: {...globals.browser, ...globals.node},
28
+ },
29
+ rules: {
30
+ '@stylistic/no-trailing-spaces': 'off',
31
+ '@stylistic/indent': ['error', 2],
32
+ '@stylistic/max-len': ['error', 140],
33
+ '@stylistic/padded-blocks': 'off',
34
+ '@stylistic/spaced-comment': 'off',
35
+ '@stylistic/linebreak-style': 'off',
36
+ 'guard-for-in': 'off',
37
+ 'curly': ['error', 'multi-or-nest'],
38
+ '@stylistic/brace-style': ['error', '1tbs', {allowSingleLine: true}],
39
+ '@stylistic/block-spacing': ['error', 'always'],
40
+ },
41
+ },
42
+ );
package/package.json CHANGED
@@ -1,39 +1,35 @@
1
1
  {
2
2
  "name": "datagrok-tools",
3
- "version": "6.3.2",
3
+ "version": "6.4.0",
4
4
  "description": "Utility to upload and publish packages to Datagrok",
5
5
  "homepage": "https://github.com/datagrok-ai/public/tree/master/tools#readme",
6
6
  "dependencies": {
7
- "@babel/parser": "^7.26.10",
8
- "@babel/runtime": "^7.23.8",
9
- "@babel/traverse": "^7.23.7",
10
- "@typescript-eslint/typescript-estree": "^8.31.1",
11
- "@typescript-eslint/visitor-keys": "^8.31.1",
7
+ "@babel/parser": "^7.29.7",
8
+ "@babel/runtime": "^7.29.7",
9
+ "@babel/traverse": "^7.29.7",
10
+ "@typescript-eslint/typescript-estree": "^8.61.1",
11
+ "@typescript-eslint/visitor-keys": "^8.61.1",
12
12
  "adm-zip": "^0.5.17",
13
- "archiver": "^4.0.2",
14
- "archiver-promise": "^1.0.0",
15
- "datagrok-api": "^1.26.0",
13
+ "archiver": "^7.0.1",
14
+ "datagrok-api": "^1.27.6",
16
15
  "estraverse": "^5.3.0",
17
16
  "glob": "^13.0.6",
18
- "ignore-walk": "^3.0.4",
19
- "inquirer": "^7.3.3",
20
- "js-yaml": "^4.1.0",
17
+ "ignore-walk": "^9.0.0",
18
+ "inquirer": "^8.2.7",
19
+ "js-yaml": "^4.2.0",
21
20
  "minimist": "^1.2.8",
22
21
  "node-fetch": "^2.7.0",
23
- "os": "^0.1.2",
24
- "papaparse": "^5.4.1",
25
- "path": "^0.12.7",
26
- "puppeteer": "22.10.0",
27
- "puppeteer-screen-recorder": "3.0.3",
28
- "ts-morph": "^27.0.2"
22
+ "papaparse": "^5.5.3",
23
+ "puppeteer": "^24.15.0",
24
+ "ts-morph": "^28.0.0"
29
25
  },
30
26
  "scripts": {
31
27
  "link": "npm link",
32
- "prepublishOnly": "babel bin --extensions .ts -d bin",
33
- "babel": "babel bin --extensions .ts -d bin",
34
- "build": "babel bin --extensions .ts -d bin",
28
+ "prepublishOnly": "node build.js",
29
+ "babel": "node build.js",
30
+ "build": "node build.js",
35
31
  "update:ivp-parser": "esbuild plugins/ivp-parser.entry.mjs --bundle --format=cjs --platform=node --alias:diff-grok=../libraries/compute-utils/node_modules/diff-grok --outfile=plugins/ivp-parser.bundle.cjs",
36
- "debug-source-map": "babel bin --extensions .ts -d bin --source-maps true",
32
+ "debug-source-map": "node build.js --source-maps",
37
33
  "test": "vitest run --project unit",
38
34
  "test:watch": "vitest --project unit",
39
35
  "test:integration": "vitest run --project integration",
@@ -64,27 +60,33 @@
64
60
  ]
65
61
  },
66
62
  "devDependencies": {
67
- "@babel/cli": "^7.23.4",
68
- "@babel/core": "^7.23.7",
69
- "@babel/plugin-proposal-decorators": "^7.23.7",
70
- "esbuild": "^0.27.7",
71
- "@babel/plugin-transform-runtime": "^7.23.7",
72
- "@babel/preset-env": "^7.23.8",
73
- "@babel/preset-typescript": "7.15.0",
74
- "@datagrok-misc/eslint-plugin-config": "^1.0.0",
63
+ "@babel/core": "^7.29.7",
64
+ "@babel/plugin-proposal-decorators": "^7.29.0",
65
+ "@babel/plugin-transform-runtime": "^7.29.0",
66
+ "@babel/preset-env": "^7.29.7",
67
+ "@babel/preset-typescript": "^7.29.0",
68
+ "@eslint/js": "^9.39.0",
69
+ "@stylistic/eslint-plugin": "^5.10.0",
75
70
  "@types/adm-zip": "^0.5.8",
71
+ "@types/archiver": "^6.0.3",
76
72
  "@types/ignore-walk": "^4.0.3",
77
73
  "@types/inquirer": "^8.2.10",
78
74
  "@types/js-yaml": "^4.0.9",
79
- "@types/node": "^18.0.0",
75
+ "@types/node": "^22.0.0",
80
76
  "@types/papaparse": "^5.3.15",
81
- "@typescript-eslint/eslint-plugin": "^5.62.0",
82
- "@typescript-eslint/parser": "^5.62.0",
83
- "eslint": "^8.56.0",
84
- "eslint-config-google": "^0.14.0",
77
+ "esbuild": "^0.28.1",
78
+ "eslint": "^9.39.0",
79
+ "globals": "^17.6.0",
85
80
  "typescript": "^5.3.3",
86
- "vitest": "^3.2.4",
81
+ "typescript-eslint": "^8.61.1",
82
+ "vitest": "^3.2.6",
87
83
  "webpack": "^5.89.0",
88
84
  "webpack-cli": "^5.1.4"
85
+ },
86
+ "overrides": {
87
+ "esbuild": "^0.28.1",
88
+ "archiver-utils": {
89
+ "glob": "$glob"
90
+ }
89
91
  }
90
92
  }
package/.eslintrc.json DELETED
@@ -1,43 +0,0 @@
1
- {
2
- "env": {
3
- "browser": true,
4
- "es2022": true
5
- },
6
- "extends": [
7
- "google"
8
- ],
9
- "parser": "@typescript-eslint/parser",
10
- "parserOptions": {
11
- "ecmaVersion": 12,
12
- "sourceType": "module"
13
- },
14
- "plugins": ["@typescript-eslint"],
15
- "rules": {
16
- "no-trailing-spaces": "off",
17
- "indent": [
18
- "error",
19
- 2
20
- ],
21
- "max-len": [
22
- "error",
23
- 140
24
- ],
25
- "padded-blocks": "off",
26
- "require-jsdoc": "off",
27
- "spaced-comment": "off",
28
- "linebreak-style": "off",
29
- "guard-for-in": "off",
30
- "curly": [
31
- "error",
32
- "multi-or-nest"
33
- ],
34
- "brace-style": [
35
- "error",
36
- "1tbs",
37
- {
38
- "allowSingleLine": true
39
- }
40
- ],
41
- "block-spacing": 2
42
- }
43
- }
@@ -1,116 +0,0 @@
1
- "use strict";
2
-
3
- var _vitest = require("vitest");
4
- var _build = require("../commands/build");
5
- (0, _vitest.describe)('getNestedValue', () => {
6
- (0, _vitest.it)('returns value for a simple key', () => {
7
- (0, _vitest.expect)((0, _build.getNestedValue)({
8
- name: 'Chem'
9
- }, 'name')).toBe('Chem');
10
- });
11
- (0, _vitest.it)('returns value for a nested path', () => {
12
- (0, _vitest.expect)((0, _build.getNestedValue)({
13
- a: {
14
- b: {
15
- c: 42
16
- }
17
- }
18
- }, 'a.b.c')).toBe(42);
19
- });
20
- (0, _vitest.it)('returns undefined for a missing key', () => {
21
- (0, _vitest.expect)((0, _build.getNestedValue)({
22
- name: 'Chem'
23
- }, 'version')).toBeUndefined();
24
- });
25
- (0, _vitest.it)('returns undefined when a mid-path segment is null', () => {
26
- (0, _vitest.expect)((0, _build.getNestedValue)({
27
- a: null
28
- }, 'a.b')).toBeUndefined();
29
- });
30
- (0, _vitest.it)('returns undefined when a mid-path segment is missing', () => {
31
- (0, _vitest.expect)((0, _build.getNestedValue)({
32
- a: {}
33
- }, 'a.b.c')).toBeUndefined();
34
- });
35
- (0, _vitest.it)('returns undefined for an empty path (splits to empty string key)', () => {
36
- (0, _vitest.expect)((0, _build.getNestedValue)({
37
- x: 1
38
- }, '')).toBeUndefined();
39
- });
40
- });
41
- const pkg = overrides => ({
42
- dir: '/tmp/pkg',
43
- name: overrides.name ?? 'test-pkg',
44
- friendlyName: overrides.friendlyName ?? overrides.name ?? 'Test Pkg',
45
- version: overrides.version ?? '1.0.0',
46
- packageJson: overrides
47
- });
48
- (0, _vitest.describe)('applyFilter', () => {
49
- const packages = [pkg({
50
- name: 'Chem',
51
- version: '1.5.0',
52
- category: 'Cheminformatics'
53
- }), pkg({
54
- name: 'Bio',
55
- version: '2.0.0',
56
- category: 'Bioinformatics'
57
- }), pkg({
58
- name: 'PowerGrid',
59
- version: '1.5.0',
60
- category: 'Viewers'
61
- })];
62
- (0, _vitest.it)('returns all packages when filter matches all', () => {
63
- (0, _vitest.expect)((0, _build.applyFilter)(packages, 'name:.')).toHaveLength(3);
64
- });
65
- (0, _vitest.it)('filters by exact name match', () => {
66
- const result = (0, _build.applyFilter)(packages, 'name:^Chem$');
67
- (0, _vitest.expect)(result).toHaveLength(1);
68
- (0, _vitest.expect)(result[0].name).toBe('Chem');
69
- });
70
- (0, _vitest.it)('filters by partial name (regex substring)', () => {
71
- const result = (0, _build.applyFilter)(packages, 'name:Bio');
72
- (0, _vitest.expect)(result).toHaveLength(1);
73
- (0, _vitest.expect)(result[0].name).toBe('Bio');
74
- });
75
- (0, _vitest.it)('returns empty array when nothing matches', () => {
76
- (0, _vitest.expect)((0, _build.applyFilter)(packages, 'name:NOMATCH')).toHaveLength(0);
77
- });
78
- (0, _vitest.it)('filters by version', () => {
79
- const result = (0, _build.applyFilter)(packages, 'version:^1\\.5');
80
- (0, _vitest.expect)(result).toHaveLength(2);
81
- (0, _vitest.expect)(result.map(p => p.name)).toEqual(_vitest.expect.arrayContaining(['Chem', 'PowerGrid']));
82
- });
83
- (0, _vitest.it)('applies && conjunction (both conditions must match)', () => {
84
- const result = (0, _build.applyFilter)(packages, 'name:Chem && version:1\\.5');
85
- (0, _vitest.expect)(result).toHaveLength(1);
86
- (0, _vitest.expect)(result[0].name).toBe('Chem');
87
- });
88
- (0, _vitest.it)('returns empty when one part of && conjunction fails', () => {
89
- (0, _vitest.expect)((0, _build.applyFilter)(packages, 'name:Chem && version:^2')).toHaveLength(0);
90
- });
91
- (0, _vitest.it)('filters by nested field', () => {
92
- const withNested = [pkg({
93
- name: 'A',
94
- datagrok: {
95
- apiVersion: '1.0'
96
- }
97
- }), pkg({
98
- name: 'B',
99
- datagrok: {
100
- apiVersion: '2.0'
101
- }
102
- })];
103
- const result = (0, _build.applyFilter)(withNested, 'datagrok.apiVersion:^1');
104
- (0, _vitest.expect)(result).toHaveLength(1);
105
- (0, _vitest.expect)(result[0].name).toBe('A');
106
- });
107
- (0, _vitest.it)('returns empty when field does not exist', () => {
108
- (0, _vitest.expect)((0, _build.applyFilter)(packages, 'nonexistent:anything')).toHaveLength(0);
109
- });
110
- (0, _vitest.it)('treats filter with no colon as field name with match-all pattern', () => {
111
- // No colon → field = whole string, pattern = /./ (matches any value)
112
- // The function returns packages where the field exists and is non-empty
113
- const result = (0, _build.applyFilter)(packages, 'name');
114
- (0, _vitest.expect)(result).toHaveLength(3);
115
- });
116
- });
@@ -1,101 +0,0 @@
1
- "use strict";
2
-
3
- var _vitest = require("vitest");
4
- var _report = require("../commands/report");
5
- (0, _vitest.describe)('markdownToJiraWiki — basic rules', () => {
6
- (0, _vitest.it)('converts H1', () => {
7
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('# Title')).toBe('h1. Title');
8
- });
9
- (0, _vitest.it)('converts H2 / H3 / H6', () => {
10
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('## Sub')).toBe('h2. Sub');
11
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('### Sub-sub')).toBe('h3. Sub-sub');
12
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('###### Deep')).toBe('h6. Deep');
13
- });
14
- (0, _vitest.it)('converts bold', () => {
15
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('hello **world** foo')).toBe('hello *world* foo');
16
- });
17
- (0, _vitest.it)('converts italic with single asterisks', () => {
18
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('hello *world* foo')).toBe('hello _world_ foo');
19
- });
20
- (0, _vitest.it)('converts strikethrough', () => {
21
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('~~gone~~')).toBe('-gone-');
22
- });
23
- (0, _vitest.it)('converts links', () => {
24
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('see [docs](https://x.example/y)')).toBe('see [docs|https://x.example/y]');
25
- });
26
- (0, _vitest.it)('converts blockquote', () => {
27
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('> quoted line')).toBe('bq. quoted line');
28
- });
29
- (0, _vitest.it)('converts unordered list', () => {
30
- const md = '- one\n- two\n- three';
31
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('* one\n* two\n* three');
32
- });
33
- (0, _vitest.it)('converts one level of nested unordered list', () => {
34
- const md = '- top\n - sub\n- back';
35
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('* top\n** sub\n* back');
36
- });
37
- (0, _vitest.it)('converts ordered list', () => {
38
- const md = '1. one\n2. two\n3. three';
39
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('# one\n# two\n# three');
40
- });
41
- (0, _vitest.it)('converts inline code', () => {
42
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('use `foo()` here')).toBe('use {{foo()}} here');
43
- });
44
- (0, _vitest.it)('converts plain code fence to noformat', () => {
45
- const md = '```\nraw code\nmore\n```';
46
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('{noformat}\nraw code\nmore\n{noformat}');
47
- });
48
- (0, _vitest.it)('converts code fence with language tag to {code:lang}', () => {
49
- const md = '```js\nconst x = 1;\n```';
50
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('{code:js}\nconst x = 1;\n{code}');
51
- });
52
- (0, _vitest.it)('converts HTML entities &nbsp; / &amp; / &lt; / &gt;', () => {
53
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('a&nbsp;b')).toBe('a b');
54
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('a&amp;b')).toBe('a&b');
55
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('a&lt;b&gt;c')).toBe('a<b>c');
56
- });
57
- });
58
- (0, _vitest.describe)('markdownToJiraWiki — content-protection inside code fences', () => {
59
- (0, _vitest.it)('does not transform `{{plates}}` inside a fenced block (run-#4 false-positive case)', () => {
60
- const md = ['Before', '```', 'context: {{plates}} should stay literal', '# not a heading', '- not a list', '```', 'After'].join('\n');
61
- const out = (0, _report.markdownToJiraWiki)(md);
62
- (0, _vitest.expect)(out).toContain('{noformat}\ncontext: {{plates}} should stay literal');
63
- (0, _vitest.expect)(out).toContain('# not a heading');
64
- (0, _vitest.expect)(out).toContain('- not a list');
65
- (0, _vitest.expect)(out).not.toContain('h1.');
66
- });
67
- (0, _vitest.it)('does not transform headings/links/lists inside an inline code span', () => {
68
- const md = 'use `# not a heading` and `[ref](u)` here';
69
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('use {{# not a heading}} and {{[ref](u)}} here');
70
- });
71
- });
72
- (0, _vitest.describe)('markdownToJiraWiki — edge cases', () => {
73
- (0, _vitest.it)('handles bold containing italic: **bold *inside* bold**', () => {
74
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('**bold *inside* bold**')).toBe('*bold _inside_ bold*');
75
- });
76
- (0, _vitest.it)('preserves bold and italic on the same line', () => {
77
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('**a** and *b*')).toBe('*a* and _b_');
78
- });
79
- (0, _vitest.it)('handles a heading whose text contains backticks', () => {
80
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('## use `foo()` for x')).toBe('h2. use {{foo()}} for x');
81
- });
82
- (0, _vitest.it)('passes plain text through unchanged', () => {
83
- const plain = 'Just a normal sentence with no markdown.';
84
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)(plain)).toBe(plain);
85
- });
86
- (0, _vitest.it)('returns empty string unchanged', () => {
87
- (0, _vitest.expect)((0, _report.markdownToJiraWiki)('')).toBe('');
88
- });
89
- (0, _vitest.it)('handles a multi-block document end-to-end', () => {
90
- const md = ['# Handoff', '', 'Some **bold** intro and a [link](https://x.example).', '', '## Findings', '', '- first', '- second', '', '> note: this matters', '', '```js', 'const x = 1;', '```', '', 'Done&nbsp;here.'].join('\n');
91
- const out = (0, _report.markdownToJiraWiki)(md);
92
- (0, _vitest.expect)(out).toContain('h1. Handoff');
93
- (0, _vitest.expect)(out).toContain('h2. Findings');
94
- (0, _vitest.expect)(out).toContain('Some *bold* intro and a [link|https://x.example].');
95
- (0, _vitest.expect)(out).toContain('* first\n* second');
96
- (0, _vitest.expect)(out).toContain('bq. note: this matters');
97
- (0, _vitest.expect)(out).toContain('{code:js}\nconst x = 1;\n{code}');
98
- (0, _vitest.expect)(out).toContain('Done here.');
99
- (0, _vitest.expect)(out).not.toContain('&nbsp;');
100
- });
101
- });