datagrok-tools 6.7.0 → 6.7.2

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.
File without changes
@@ -0,0 +1,5 @@
1
+
2
+ > datagrok-tools@6.7.2 build /home/runner/work/public/public/tools
3
+ > node build.js
4
+
5
+ Successfully compiled 51 files with Babel.
package/CHANGELOG.md CHANGED
@@ -1,12 +1,17 @@
1
1
  # Datagrok-tools changelog
2
2
 
3
- ## 6.7.0 (WIP)
3
+ ## 6.7.2 (WIP)
4
+
5
+ * `grok publish` marks a bundled package for servers older than 1.28.0, which detect one only by a `webpack.config.js` in the archive; the marker is generated at publish time and never written to the package folder.
6
+
7
+ ## 6.7.1 (2026-09-15)
4
8
 
5
9
  * `grok login <server>` — keypair authentication, the replacement for the developer key. Generates an EC P-256 key, registers only its public half (in the browser, or with a one-shot `--code` from Profile > Public keys...), and keeps the private half in `~/.grok/keys/<alias>.json`. Logging in signs a server-issued nonce, so nothing reusable crosses the wire; keys can carry an expiry (`--expires`) and are revoked one at a time. `grok publish`, `grok test`, `grok stresstest` and `grok s` use it automatically whenever one is configured for the server, and fall back to the developer key otherwise. For CI, `GROK_PRIVATE_KEY` holds the private JWK (raw or base64) instead of a config file. Needs a server from 1.28 on; see https://datagrok.ai/help/govern/access-control/keypair-authentication
6
10
  * `grok s token` — prints a session token for the configured server, so shell scripts stop curling `/users/login/dev` with a long-lived key.
7
11
  * `grok config add` — `--key` is now optional: a server reached with a keypair has no developer key to record.
8
12
  * Against a server older than 1.28 — which has no keypair endpoints and refuses their paths before routing — the CLI names the version needed instead of reporting a bare 401, and falls back to the developer key when one is still configured for that server.
9
13
  * `grok login` reports its progress step by step (spinner on a terminal, one line per step in a log), including while it waits for the browser approval.
14
+ * `grok s push/migrate` — a migrated entity is stamped in its `metaParams` with `migrated_from` (the source stand URL) and `migrated_on`, so on the target it is clear the entity came from another stand. Written wherever the entity already carries `metaParams` (as `sync_id` is) and stripped before the idempotency comparison, so a re-push of an unchanged entity still reads as identical.
10
15
 
11
16
  ## 6.6.0 (2026-09-13)
12
17
 
@@ -22,6 +27,8 @@
22
27
 
23
28
  ## 6.5.8 (2026-09-10)
24
29
 
30
+ * `grok setup [--check] [--global]`: gets a public/ checkout ready (Node check, pnpm via corepack at the pinned version, removal of npm-era per-package node_modules and lockfiles, `pnpm install`, global grok version check). `grok tsc ...` runs the workspace TypeScript compiler; `grok build` inside a package or a Turborepo task builds just that package (bundle + metadata + check, or a library emit). `grok --version`.
31
+ * Workspace toolchain: `public/` is a pnpm workspace with a Turborepo task graph and the shared `@datagrok/build-config` package (rspack + swc bundling, TypeScript 7 type-checking). `grok build` is now a front for Turborepo: the package in cwd and its dependencies in order, cached, never prompting; `--all`, `--affected`, `--typecheck`, `--filter`. `grok link` is a no-op inside a workspace (`pnpm install` links everything). `grok check` accepts `workspace:` and `catalog:` for the `datagrok-api` range. `grok publish` recognises packages built by `grok build --skip-check` (no `webpack.config.js`). `grok create` emits the new package shape (runtime deps only, `extends` tsconfig, four scripts); outside a workspace it pins published ranges and adds `@datagrok/build-config` as a devDependency. Command modules load lazily: `grok` starts in ~0.6 s instead of ~6 s. The webpack `FuncGeneratorPlugin` is also used by rspack builds; `@babel/generator` is a declared dependency now.
25
32
  * `grok s` — correctness pass from the CLI audit: every server failure now exits 1 with one line on stderr (a 200 carrying an `ApiError` — missing entity, duplicate login or group name, unknown function — used to print as data with exit 0); `--limit`/`--offset` are honoured for users, groups, connections and functions (their public routes ignore paging, so `list` and the new `count` verb go through the internal routers); `files list` works (the public files route only downloads; listing goes through the connection's file route) and prints `path, kind, size`; `tables list|get|delete` no longer throw, and `tables download|get|delete` accept the bare table name, the full `Project:Table` name or the id; `describe <entity|type>` works (registry record plus the fields of a live sample; it called a nonexistent endpoint before); positional `functions run 'F(1, 2)'` maps the values onto the function's inputs instead of silently sending `{"0": 1}` (which ran the function with nulls); `raw` takes API-relative paths (a leading `/api` is accepted), sends a body from `--json <file>` or `--data '<json>'`, and fails on non-2xx — so it, and `healthcheck`, now work against a bare-Datlas `--host` too; `shares list` accepts a name and shows the grants inherited through project links; `groups` name resolution prefers an exact match (`admin` no longer collides with `Administrators`) and `--user` works on `list-members`/`list-memberships`; `connections delete` of an unknown id fails instead of reporting success; a bad `--host` URL or alias reports the reason without the help dump. `users delete` refuses with a pointer at `users block`: the platform has no user deletion, and removing the entity record leaves the login unusable. `functions delete` covers scripts and queries.
26
33
 
27
34
  * GROK-20298: `grok s domains` — entity-mapped domain schemas and their rows from the CLI, addressed as `<schema>` or `<schema>.<table>`: `list`, `get` (manifest, a table's columns, or one row), `query` (`--filter`, `--columns`, `--sort`, `--expand`, `--limit`, `--offset`), `count`, `aggregate` (`--measures 'count,sum(x) as t' --group-by a`), `insert` / `update` (from `--json` or inline `col=value`, `--version` for optimistic concurrency), `delete` (one row, `--filter` bulk, or a whole user schema with `--force`), `upload` (csv, d42, json; `--upsert`, `--no-all-or-nothing`), `download` (csv to stdout or `-O out.csv|out.d42`), `transaction`, `audit`, `capabilities`, `grants` / `grant` / `revoke` (groups by name), `create`, and `apply` (`--dry-run`, `--confirm-destructive`, `--if-version`). Validation errors print per row; a destructive plan awaiting confirmation is printed with the error.
package/CLAUDE.md CHANGED
@@ -141,12 +141,11 @@ Tests use Puppeteer for headless browser automation:
141
141
 
142
142
  ### `grok build` Command
143
143
 
144
- Builds packages with `npm install` + `npm run build`. Supports:
145
- - Single package: `grok build` (from package directory)
146
- - Recursive: `grok build --recursive` (discovers and builds all packages in subdirectories)
147
- - `--filter "name:Chem"` - Filter packages by package.json fields (supports regex, `&&` for multiple conditions)
148
- - `--parallel N` - Max parallel build jobs (default 4)
149
- - `--no-incremental` - Force full rebuild (default uses `--env incremental`)
144
+ A front for Turborepo inside the `public/` pnpm workspace (`pnpm exec turbo run build ...`), never prompting:
145
+ - `grok build` (from a package directory): the package and everything it depends on, in dependency order, cached
146
+ - `grok build --all`: the whole workspace; `grok build --affected`: everything the diff against `origin/master` touches
147
+ - `--typecheck` adds the type-check task; `--filter <turbo filter>`; `--parallel N` (default 3); `--force` ignores the cache
148
+ - Outside a workspace it explains that `pnpm run build` / `npm run build` in the package is the way
150
149
 
151
150
  ### `grok claude` Command
152
151
 
package/LICENSE.md ADDED
@@ -0,0 +1,16 @@
1
+ Copyright 2020 Datagrok, INC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
4
+ associated documentation files (the "Software"), to deal in the Software without restriction,
5
+ including without limitation the rights to use, copy, modify, merge, publish, distribute,
6
+ sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
7
+ furnished to do so, subject to the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be included in all copies or substantial
10
+ portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
13
+ NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
15
+ OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -26,7 +26,9 @@ npm install datagrok-tools -g
26
26
 
27
27
  A new folder `<package-name>` will be created automatically as well as its contents.
28
28
  3. Run `npm install` in your package directory to get the required dependencies (the command is called
29
- automatically after package creation, if it ran successfully, skip this step).
29
+ automatically after package creation, if it ran successfully, skip this step). Inside the
30
+ [public repository](https://github.com/datagrok-ai/public) the packages form one pnpm workspace: run
31
+ `pnpm install` once at the repository root instead, and build with `grok build`.
30
32
  4. Start working on the functionality of your package. Use `grok add` to create function templates.
31
33
  5. Once you have completed the work on your package, upload it by running:
32
34
 
@@ -92,8 +94,8 @@ For more information on configuring connections, refer to the [Connections](http
92
94
  ```
93
95
 
94
96
  Options:
95
- - `--build` or `--rebuild`: boolean flags that indicate whether a local webpack bundle should be used or it should
96
- be generated on the server side
97
+ - the package is built locally before upload (`npm run build`, or `pnpm run build` inside the workspace);
98
+ `--skip-build` uploads the existing `dist/` as is, `--rebuild` asks the server to build instead
97
99
  - `--debug` or `--release`: boolean flags that determine whether to publish a debug version of the package visible
98
100
  only to the developer or a release version accessible by all eligible users and user groups
99
101
  - `--key`: a string containing a developer key that is not listed in the config file, e.g., the key for a new server
@@ -8,74 +8,136 @@ exports.applyFilter = applyFilter;
8
8
  exports.build = build;
9
9
  exports.confirm = confirm;
10
10
  exports.discoverPackages = discoverPackages;
11
+ exports.findWorkspaceRoot = findWorkspaceRoot;
11
12
  exports.getNestedValue = getNestedValue;
12
13
  var _fs = _interopRequireDefault(require("fs"));
13
14
  var _path = _interopRequireDefault(require("path"));
14
- var _util = require("util");
15
- var _child_process = require("child_process");
16
15
  var readline = _interopRequireWildcard(require("readline"));
17
- var utils = _interopRequireWildcard(require("../utils/utils"));
16
+ var _child_process = require("child_process");
18
17
  var color = _interopRequireWildcard(require("../utils/color-utils"));
18
+ var _toolchain = require("../utils/toolchain");
19
19
  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); }
20
- const execAsync = (0, _util.promisify)(_child_process.exec);
20
+ /** Walks up from `dir` to the pnpm workspace root (the directory holding pnpm-workspace.yaml). */
21
+ function findWorkspaceRoot(dir) {
22
+ let d = _path.default.resolve(dir);
23
+ for (;;) {
24
+ if (_fs.default.existsSync(_path.default.join(d, 'pnpm-workspace.yaml'))) return d;
25
+ const parent = _path.default.dirname(d);
26
+ if (parent === d) return null;
27
+ d = parent;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * `grok build` in a workspace drives Turborepo: the package in cwd and everything it depends on,
33
+ * in dependency order, cached (`--all`, `--affected`, `--typecheck`, `--filter`). Inside a Turborepo
34
+ * task (TURBO_HASH is set), in a standalone package, or with `--local`, it builds just the package in
35
+ * cwd: an rspack bundle plus `grok check` for a plugin (src/package.ts or rspack.config.js), a
36
+ * TypeScript emit into dist/ for a library.
37
+ */
21
38
  async function build(args) {
22
- if (args.verbose) color.setVerbose(true);
23
- const buildCmd = args['no-incremental'] ? 'npm run build' : 'npm run build -- --env incremental';
24
- if (args.recursive) return await buildRecursive(process.cwd(), args, buildCmd);else return await buildSingle(process.cwd(), buildCmd);
39
+ const cwd = process.cwd();
40
+ const root = findWorkspaceRoot(cwd);
41
+ if (args.local || process.env.TURBO_HASH || !root) return buildLocal(cwd, args);
42
+ return buildWithTurbo(cwd, root, args);
25
43
  }
26
- async function buildSingle(dir, buildCmd) {
27
- if (!utils.isPackageDir(dir)) {
28
- color.error('Not a package directory (no package.json found)');
29
- return false;
30
- }
31
- const packageJson = JSON.parse(_fs.default.readFileSync(_path.default.join(dir, 'package.json'), 'utf-8'));
32
- const name = packageJson.friendlyName || packageJson.name;
33
- console.log(`Building ${name}...`);
34
- try {
35
- await utils.runScript('npm install', dir, color.isVerbose());
36
- await utils.runScript(buildCmd, dir, color.isVerbose());
37
- color.success(`Successfully built ${name}`);
38
- return true;
39
- } catch (error) {
40
- color.error(`Failed to build ${name}`);
41
- if (error.message) color.error(error.message);
42
- return false;
44
+ async function buildWithTurbo(cwd, root, args) {
45
+ const filters = [];
46
+ if (args.affected) filters.push('--filter=...[origin/master]');else if (!(args.all || args.recursive)) {
47
+ const pkgPath = _path.default.join(cwd, 'package.json');
48
+ if (!_fs.default.existsSync(pkgPath)) {
49
+ color.error('Not a package directory (no package.json). Use --all or --affected from anywhere in the workspace.');
50
+ return false;
51
+ }
52
+ filters.push(`--filter=${JSON.parse(_fs.default.readFileSync(pkgPath, 'utf8')).name}...`);
43
53
  }
54
+ if (args.filter) filters.push(`--filter=${args.filter}`);
55
+ const tasks = ['build'];
56
+ if (args.typecheck) tasks.push('typecheck');
57
+ const cmd = ['exec', 'turbo', 'run', ...tasks, ...filters, `--concurrency=${args.parallel ?? 3}`, '--continue'];
58
+ if (!args.verbose) cmd.push('--output-logs=errors-only');
59
+ if (args.force) cmd.push('--force');
60
+ const r = (0, _child_process.spawnSync)('pnpm', cmd, {
61
+ stdio: 'inherit',
62
+ cwd: root,
63
+ shell: true,
64
+ env: {
65
+ ...process.env,
66
+ TURBO_TELEMETRY_DISABLED: '1'
67
+ }
68
+ });
69
+ return r.status === 0;
44
70
  }
45
- async function buildRecursive(baseDir, args, buildCmd) {
46
- const packages = discoverPackages(baseDir);
47
- if (packages.length === 0) {
48
- color.warn('No packages found in the current directory');
71
+ async function buildLocal(dir, args) {
72
+ if (!_fs.default.existsSync(_path.default.join(dir, 'package.json'))) {
73
+ color.error('Not a package directory (no package.json).');
49
74
  return false;
50
75
  }
51
- const filtered = args.filter ? applyFilter(packages, args.filter) : packages;
52
- if (filtered.length === 0) {
53
- color.warn('No packages match the filter');
54
- return false;
55
- }
56
- console.log(`Found ${filtered.length} package(s): ${filtered.map(p => p.friendlyName).join(', ')}`);
57
- if (!args.silent && !args.s) {
58
- const confirmed = await confirm(`\nBuild ${filtered.length} package(s)?`);
59
- if (!confirmed) {
60
- console.log('Aborted.');
76
+ const isPlugin = _fs.default.existsSync(_path.default.join(dir, 'rspack.config.js')) || ['ts', 'js'].some(e => _fs.default.existsSync(_path.default.join(dir, 'src', `package.${e}`)));
77
+ return isPlugin ? bundle(dir, args) : emitLibrary(dir);
78
+ }
79
+ async function bundle(dir, args) {
80
+ const {
81
+ bundler,
82
+ rspack
83
+ } = (0, _toolchain.toolchain)(dir).buildConfig;
84
+ // --key=value arguments reach a config exported as a function, e.g. `grok build --only=browser`
85
+ const env = {};
86
+ for (const [k, v] of Object.entries(args)) if (!['_', 'local', 'verbose', 'skip-check', 'typecheck', 'all', 'affected', 'filter', 'parallel', 'force'].includes(k)) env[k] = v;
87
+ const cfgPath = _path.default.join(dir, 'rspack.config.js');
88
+ let config = _fs.default.existsSync(cfgPath) ? require(cfgPath) : bundler({
89
+ dir
90
+ });
91
+ if (typeof config === 'function') config = config(env);
92
+ if (config && typeof config.then === 'function') config = await config;
93
+ if (!args['skip-check'] && _fs.default.existsSync(_path.default.join(dir, 'src', 'package.ts'))) {
94
+ const {
95
+ check
96
+ } = require('./check');
97
+ if (check({
98
+ _: ['check'],
99
+ soft: true,
100
+ 'no-exit': true
101
+ }) === false) {
102
+ color.error('grok check failed');
61
103
  return false;
62
104
  }
63
105
  }
64
- const maxParallel = args.parallel || 4;
65
- const results = await buildParallel(filtered, buildCmd, maxParallel);
66
- return results.every(r => r.success);
106
+ const t0 = Date.now();
107
+ const ok = await new Promise((resolve, reject) => {
108
+ rspack(config, (err, stats) => {
109
+ if (err) return reject(err);
110
+ const text = stats.toString({
111
+ colors: process.stdout.isTTY,
112
+ preset: 'errors-warnings'
113
+ });
114
+ if (text.trim()) console.log(text);
115
+ resolve(!stats.hasErrors());
116
+ });
117
+ });
118
+ const dist = _path.default.join(dir, 'dist', 'package.js');
119
+ const size = _fs.default.existsSync(dist) ? `${(_fs.default.statSync(dist).size / 1024).toFixed(0)} KB` : '';
120
+ console.log(`${ok ? 'bundled' : 'FAILED'} ${_path.default.basename(dir)} in ${((Date.now() - t0) / 1000).toFixed(1)}s ${size}`);
121
+ return ok;
67
122
  }
123
+
124
+ /** A library: `tsc -p tsconfig.json` into dist/, then the css/wasm/json assets its sources import. */
125
+ function emitLibrary(dir) {
126
+ (0, _toolchain.ensureLibraryExports)(dir);
127
+ const r = (0, _child_process.spawnSync)(process.execPath, [(0, _toolchain.toolchain)(dir).tsc, '-p', 'tsconfig.json'], {
128
+ stdio: 'inherit',
129
+ cwd: dir
130
+ });
131
+ if (r.status !== 0) return false;
132
+ (0, _toolchain.copyLibraryAssets)(dir);
133
+ return true;
134
+ }
135
+ /** Packages directly under `baseDir` (used by `grok testall`). */
68
136
  function discoverPackages(baseDir) {
69
- const entries = _fs.default.readdirSync(baseDir);
70
137
  const packages = [];
71
- for (const entry of entries) {
138
+ for (const entry of _fs.default.readdirSync(baseDir)) {
72
139
  if (entry.startsWith('.') || entry === 'node_modules') continue;
73
140
  const dir = _path.default.join(baseDir, entry);
74
- try {
75
- if (!_fs.default.statSync(dir).isDirectory()) continue;
76
- } catch (_) {
77
- continue;
78
- }
79
141
  const packageJsonPath = _path.default.join(dir, 'package.json');
80
142
  if (!_fs.default.existsSync(packageJsonPath)) continue;
81
143
  try {
@@ -87,114 +149,36 @@ function discoverPackages(baseDir) {
87
149
  version: packageJson.version || '0.0.0',
88
150
  packageJson
89
151
  });
90
- } catch (_) {
91
- continue;
92
- }
152
+ } catch (_) {/* not a package */}
93
153
  }
94
154
  return packages.sort((a, b) => a.friendlyName.localeCompare(b.friendlyName));
95
155
  }
156
+
157
+ /** `field:regex && field:regex` filter over package.json fields. */
96
158
  function applyFilter(packages, filterStr) {
97
- const conditions = filterStr.split('&&').map(s => s.trim());
98
- const parsedConditions = conditions.map(cond => {
159
+ const conditions = filterStr.split('&&').map(s => s.trim()).map(cond => {
99
160
  const colonIdx = cond.indexOf(':');
100
- if (colonIdx === -1) return {
161
+ return colonIdx === -1 ? {
101
162
  field: cond,
102
163
  pattern: new RegExp('.')
164
+ } : {
165
+ field: cond.substring(0, colonIdx).trim(),
166
+ pattern: new RegExp(cond.substring(colonIdx + 1).trim())
103
167
  };
104
- const field = cond.substring(0, colonIdx).trim();
105
- const pattern = new RegExp(cond.substring(colonIdx + 1).trim());
106
- return {
107
- field,
108
- pattern
109
- };
110
- });
111
- return packages.filter(pkg => {
112
- for (const cond of parsedConditions) {
113
- const value = getNestedValue(pkg.packageJson, cond.field);
114
- if (value === undefined || !cond.pattern.test(String(value))) return false;
115
- }
116
- return true;
117
168
  });
169
+ return packages.filter(pkg => conditions.every(cond => {
170
+ const value = getNestedValue(pkg.packageJson, cond.field);
171
+ return value !== undefined && cond.pattern.test(String(value));
172
+ }));
118
173
  }
119
174
  function getNestedValue(obj, path) {
120
- const parts = path.split('.');
121
175
  let current = obj;
122
- for (const part of parts) {
176
+ for (const part of path.split('.')) {
123
177
  if (current == null || typeof current !== 'object') return undefined;
124
178
  current = current[part];
125
179
  }
126
180
  return current;
127
181
  }
128
- async function buildParallel(packages, buildCmd, maxParallel) {
129
- const results = [];
130
- const headers = ['Plugin', 'Version', 'Build time', 'Bundle size'];
131
- const widths = [Math.max(headers[0].length, ...packages.map(p => p.friendlyName.length)), Math.max(headers[1].length, ...packages.map(p => p.version.length)), Math.max(headers[2].length, 10), Math.max(headers[3].length, 40)];
132
- const pad = (s, w) => s + ' '.repeat(Math.max(0, w - s.length));
133
- console.log(`\nBuilding with ${maxParallel} parallel job(s)...`);
134
- console.log(headers.map((h, i) => pad(h, widths[i])).join(' | '));
135
- console.log(widths.map(w => '-'.repeat(w)).join('-+-'));
136
- const buildOne = async pkg => {
137
- const start = Date.now();
138
- let success = true;
139
- let buildTime = '';
140
- let bundleSize = '';
141
- try {
142
- await execAsync('npm install', {
143
- cwd: pkg.dir,
144
- maxBuffer: 10 * 1024 * 1024
145
- });
146
- await execAsync(buildCmd, {
147
- cwd: pkg.dir,
148
- maxBuffer: 10 * 1024 * 1024
149
- });
150
- const elapsed = (Date.now() - start) / 1000;
151
- buildTime = `${elapsed.toFixed(1)}s`;
152
- bundleSize = getBundleSize(pkg.dir);
153
- } catch (error) {
154
- success = false;
155
- buildTime = 'Error';
156
- const raw = (error.stderr || error.stdout || error.message || 'Unknown error').trim();
157
- bundleSize = raw.replace(/\r?\n/g, ' ').replace(/\s+/g, ' ').substring(0, 40);
158
- }
159
- const result = {
160
- name: pkg.friendlyName,
161
- version: pkg.version,
162
- buildTime,
163
- bundleSize,
164
- success
165
- };
166
- results.push(result);
167
- const cells = [result.name, result.version, result.buildTime, result.bundleSize];
168
- const line = cells.map((cell, j) => pad(cell, widths[j])).join(' | ');
169
- if (success) color.info(line);else color.error(line);
170
- return result;
171
- };
172
- let idx = 0;
173
- const next = async () => {
174
- while (idx < packages.length) {
175
- const pkg = packages[idx++];
176
- await buildOne(pkg);
177
- }
178
- };
179
- const workers = Array.from({
180
- length: Math.min(maxParallel, packages.length)
181
- }, () => next());
182
- await Promise.all(workers);
183
- const succeeded = results.filter(r => r.success).length;
184
- const failed = results.length - succeeded;
185
- console.log('');
186
- if (failed === 0) color.success(`All ${results.length} package(s) built successfully`);else color.warn(`${succeeded} succeeded, ${failed} failed`);
187
- return results;
188
- }
189
- function getBundleSize(dir) {
190
- const bundlePath = _path.default.join(dir, 'dist', 'package.js');
191
- try {
192
- const stats = _fs.default.statSync(bundlePath);
193
- return `${(stats.size / 1024).toFixed(1)} KB`;
194
- } catch (_) {
195
- return 'N/A';
196
- }
197
- }
198
182
  function confirm(message) {
199
183
  const rl = readline.createInterface({
200
184
  input: process.stdin,
@@ -19,7 +19,6 @@ var _path = _interopRequireDefault(require("path"));
19
19
  var _ignoreWalk = _interopRequireDefault(require("ignore-walk"));
20
20
  var utils = _interopRequireWildcard(require("../utils/utils"));
21
21
  var color = _interopRequireWildcard(require("../utils/color-utils"));
22
- var testUtils = _interopRequireWildcard(require("../utils/test-utils"));
23
22
  var _const = require("../utils/const");
24
23
  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); }
25
24
  const warns = ['Latest package version', 'Datagrok API version should contain'];
@@ -57,7 +56,7 @@ function check(args) {
57
56
  color.error('File `package.json` not found. Run the command from the package directory');
58
57
  return false;
59
58
  }
60
- return runChecks(curDir, args.soft ?? false, false);
59
+ return runChecks(curDir, args.soft ?? false, args['no-exit'] ?? false);
61
60
  }
62
61
  }
63
62
  function runChecks(packagePath, soft = false, noExit = false) {
@@ -113,7 +112,7 @@ function runChecks(packagePath, soft = false, noExit = false) {
113
112
  console.log(`Checking package ${_path.default.basename(packagePath)}...`);
114
113
  showError(errors);
115
114
  if (soft || json.version.startsWith('0') || errors.every(w => warns.some(ww => w.includes(ww)))) return true;
116
- if (noExit) return false;else testUtils.exitWithCode(1);
115
+ if (noExit) return false;else process.exit(1);
117
116
  }
118
117
  color.log(`Checking package ${_path.default.basename(packagePath)}...\t\t\t\u2713 OK`);
119
118
  return true;
@@ -410,7 +409,7 @@ function checkPackageFile(packagePath, json, options) {
410
409
  if (json.version.includes('beta') && isPublicPackage) warnings.push('File "package.json": public package cannot be beta version.');
411
410
  const api = json.dependencies?.['datagrok-api'];
412
411
  if (api) {
413
- if (api === '../../js-api') {} else if (api === 'latest') warnings.push('File "package.json": you should specify Datagrok API version constraint (for example ^1.16.0, >=1.16.0).');else if (options?.isReleaseCandidateVersion === false && !/^(\^|>|<|~).+/.test(api)) warnings.push('File "package.json": Datagrok API version should starts with > | >= | ~ | ^ | < | <=');
412
+ if (api === '../../js-api') {} else if (api === 'latest') warnings.push('File "package.json": you should specify Datagrok API version constraint (for example ^1.16.0, >=1.16.0).');else if (options?.isReleaseCandidateVersion === false && !/^(\^|>|<|~|workspace:|catalog:).+/.test(api)) warnings.push('File "package.json": Datagrok API version should starts with > | >= | ~ | ^ | < | <=');
414
413
  }
415
414
 
416
415
  // const dt = json.devDependencies?.['datagrok-tools'] ?? json.dependencies?.['datagrok-tools'];
@@ -21,6 +21,12 @@ const curFolder = _path.default.basename(curDir);
21
21
  const grokDir = _path.default.join(_os.default.homedir(), '.grok');
22
22
  const confPath = _path.default.join(grokDir, 'config.yaml');
23
23
  const templateDir = _path.default.join(_path.default.dirname(_path.default.dirname(__dirname)), 'package-template');
24
+ function isPnpmWorkspace(dir) {
25
+ for (let d = _path.default.resolve(dir);; d = _path.default.dirname(d)) {
26
+ if (_fs.default.existsSync(_path.default.join(d, 'pnpm-workspace.yaml'))) return true;
27
+ if (_path.default.dirname(d) === d) return false;
28
+ }
29
+ }
24
30
  const confTemplateDir = _path.default.join(_path.default.dirname(_path.default.dirname(__dirname)), 'config-template.yaml');
25
31
  const confTemplate = _jsYaml.default.load(_fs.default.readFileSync(confTemplateDir, {
26
32
  encoding: 'utf-8'
@@ -37,7 +43,7 @@ function createDirectoryContents(name, friendlyName, config, templateDir, packag
37
43
  _fs.default.writeFileSync(copyFilePath, _fs.default.readFileSync(origFilePath, 'base64'), 'base64');
38
44
  return false;
39
45
  }
40
- let contents = _fs.default.readFileSync(file === 'webpack.config.js' && ts ? _path.default.join(templateDir, 'ts.webpack.config.js') : origFilePath, 'utf8');
46
+ let contents = _fs.default.readFileSync(origFilePath, 'utf8');
41
47
  contents = contents.replace(/#{PACKAGE_FRIENDLY_NAME}/g, friendlyName);
42
48
  contents = contents.replace(/#{PACKAGE_NAME}/g, name);
43
49
  contents = contents.replace(/#{PACKAGE_DETECTORS_NAME}/g, utils.kebabToCamelCase(name));
@@ -49,20 +55,40 @@ function createDirectoryContents(name, friendlyName, config, templateDir, packag
49
55
  contents = contents.replace(/#{GROK_HOST_ALIAS}/g, config.default);
50
56
  contents = contents.replace(/#{GROK_HOST}/g, /localhost|127\.0\.0\.1/.test(config['servers'][config.default]['url']) ? 'http://localhost:63343/login.html' : new URL(config['servers'][config.default]['url']).origin);
51
57
  if (file === 'package.json') {
52
- // Generate scripts for non-default servers from `config.yaml`
58
+ // The toolchain (bundler, TypeScript, eslint) comes from the workspace root; a package
59
+ // declares only what it imports. Deploy with `grok publish <alias>`, no per-server scripts.
53
60
  const _package = JSON.parse(contents);
54
61
  _package['scripts'] = _package['scripts'] ?? {};
55
- for (const server in config.servers) {
56
- if (server === config.default) continue;
57
- _package['scripts'][`debug-${name.toLowerCase()}-${server}`] = `webpack && grok publish ${server}`;
58
- _package['scripts'][`release-${name.toLowerCase()}-${server}`] = `webpack && grok publish ${server} --release`;
59
- }
60
- if (ts) {
61
- _package.devDependencies = _package.devDependencies ?? {};
62
+ _package.devDependencies = _package.devDependencies ?? {};
63
+ // Outside the public/ pnpm workspace the template's workspace:/catalog: specifiers mean
64
+ // nothing to npm: pin published ranges and bring the toolchain in as a devDependency.
65
+ if (!isPnpmWorkspace(packageDir)) {
66
+ const published = {
67
+ 'datagrok-api': `^${require('datagrok-api/package.json').version}`,
68
+ '@datagrok-libraries/test': '^1.4.0',
69
+ 'rxjs': '^6.5.5',
70
+ 'cash-dom': '^8.1.5',
71
+ 'dayjs': '^1.11.13',
72
+ 'wu': '^2.1.0',
73
+ 'typescript': '^7.0.2'
74
+ };
75
+ for (const sect of ['dependencies', 'devDependencies']) {
76
+ for (const [dep, spec] of Object.entries(_package[sect] ?? {})) {
77
+ if (spec === 'workspace:^' || spec === 'catalog:') _package[sect][dep] = published[dep] ?? 'latest';
78
+ }
79
+ }
62
80
  Object.assign(_package.devDependencies, {
63
- 'ts-loader': 'latest',
64
- 'typescript': 'latest'
81
+ '@datagrok/build-config': '^0.1.0',
82
+ 'eslint': '^8.57.1',
83
+ '@typescript-eslint/parser': '^8.39.0',
84
+ '@typescript-eslint/eslint-plugin': '^8.39.0',
85
+ 'eslint-config-google': '^0.14.0',
86
+ 'typescript': '^5.9.3'
65
87
  });
88
+ _fs.default.writeFileSync(_path.default.join(packageDir, '.eslintrc.json'), JSON.stringify({
89
+ root: true,
90
+ extends: './node_modules/@datagrok/build-config/eslintrc.json'
91
+ }, null, 2) + '\n');
66
92
  }
67
93
  if (eslint) {
68
94
  Object.assign(_package.devDependencies, {