datagrok-tools 6.7.8 → 6.7.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.
- package/.devcontainer/Dockerfile.pkg_dev +1 -1
- package/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +33 -0
- package/CLAUDE.md +13 -0
- package/GROK_S.md +138 -6
- package/README.md +14 -0
- package/bin/commands/add.js +4 -4
- package/bin/commands/api.js +49 -20
- package/bin/commands/check.js +43 -1
- package/bin/commands/connect-folder.js +411 -0
- package/bin/commands/help.js +68 -2
- package/bin/commands/publish.js +6 -0
- package/bin/commands/server-domains.js +24 -7
- package/bin/commands/server-observe.js +854 -0
- package/bin/commands/server.js +72 -5
- package/bin/commands/src.js +458 -0
- package/bin/grok.js +9 -5
- package/bin/utils/connect-folder/docker.js +244 -0
- package/bin/utils/connect-folder/docker.ts +259 -0
- package/bin/utils/ent-helpers.js +2 -2
- package/bin/utils/migrate/walker.js +2 -1
- package/bin/utils/migrate/walker.ts +1 -1
- package/bin/utils/node-dapi.js +56 -17
- package/bin/utils/server-client.js +8 -1
- package/bin/utils/test-utils.js +54 -2
- package/domain-schema.schema.json +50 -1
- package/entity-template/domain-app-imports.js +2 -2
- package/package.json +5 -2
- package/plugins/func-gen-plugin.js +1 -1
- package/vitest.config.mts +4 -0
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Datagrok-tools changelog
|
|
2
2
|
|
|
3
|
+
## 6.7.10 (2026-11-06)
|
|
4
|
+
|
|
5
|
+
* Added `grok src`: git recipes across the repos of the datagrok umbrella (`clone`, `add`, `sparse`, `switch`, `status`, `pull`, `at`, `bump`, `wt add`, `doctor`)
|
|
6
|
+
* Kept positionals, `--url` and `--dir` verbatim (`1.10` no longer becomes 1.1), and an explicit boolean such as `--force` now reaches every command instead of being dropped
|
|
7
|
+
* Added `grok s o groups list|get|add|set|rm|put|reset`, `endpoints list|rm`, `logs match '<filter>'` (with `--space`) and `logs activations [stop]`: log groups and endpoints of Settings > Logger
|
|
8
|
+
* `grok publish`: A local container build that fails for the host platform on Apple Silicon is retried for linux/amd64, so a package whose base image has no arm64 variant (the Chembl ChEMBL database) builds
|
|
9
|
+
* `grok test`: A package built on u2 or ems runs with u2 strict — a u2 misuse throws a `U2UsageError` naming the fix, and one the page caught and logged fails the run when it is the package's own (a stack frame in its bundle, or none to tell; another package's prints as a warning); `U2_STRICT=report` lists them instead without failing
|
|
10
|
+
* `grok check`: A package built on u2 or ems fails on a problem of u2's consumer check whatever its version (a 0.x package only printed it), so a CSS rule over a new `.u2-*` class or `[data-u2…]` attribute fails the check
|
|
11
|
+
* Added `grok s o problems severity <id|kind:key> <info|warning|critical|none>`: pins the severity a problem's raises use (`none` unpins it)
|
|
12
|
+
* Added `grok connect-folder`: connects a local folder as a connection, served from a Docker container; the agent image tag is pulled on every connect, so a moved tag replaces a stale local agent
|
|
13
|
+
* `grok connect-folder`: Runs only the Docker Hub `datagrok/proxy_storage` agent image whatever image the server names; `--image` runs another, such as a copy in a private registry
|
|
14
|
+
* Added `grok s ... --log [<spec>]` on every `grok s` command (`grok s o` included): its requests share a `datlas-log-id`, every server instance keeps what they log at the spec (`verbose`, `standard`, levels, debug flags or `all`) in memory only, and at the end the command gathers it and prints it to stderr
|
|
15
|
+
* Added `grok s functions run ... --debug`: a debug call (`?debug=true`) whose own log, a query's stages as the query debugger shows them, is kept and printed the same way
|
|
16
|
+
* Added `grok s o logs request <logId>`: gathers what the requests of one `--log` command logged
|
|
17
|
+
* `grok s status`: server build, uptime, DB pool and HTTP connections from `/info/server`; for admins also the requests in flight (`?extended=true`)
|
|
18
|
+
* `grok api`: Fixed double plurals in table accessors (`categorieses` → `categories`)
|
|
19
|
+
* Build: Fixed `package-api.ts` of a decorator package lagging one build behind `package.g.ts`
|
|
20
|
+
* Added a `restore` arm per soft-delete table to the `<Schema>TransactionOp` that `grok api` generates, so the typed `transaction()` accepts the restore ops the server runs
|
|
21
|
+
* Made `grok api` type a required column with a `default` as optional on `<Table>Insert`: the server fills it when an insert omits it
|
|
22
|
+
* Fixed `grok api` emitting a choices type that does not compile, or holds a different value, when a choice contains `'` or `\`
|
|
23
|
+
|
|
24
|
+
## 6.7.9 (2026-10-16)
|
|
25
|
+
|
|
26
|
+
* Upgraded Puppeteer to v25, which drops the vulnerable `extract-zip` and `basic-ftp` transitive dependencies; Node.js 22.12+ is now required
|
|
27
|
+
* `grok add app --domain`, `grok api --ui`: scaffold against `@datagrok-libraries/ems`
|
|
28
|
+
* GROK-20884: Added `grok s o errors get <signature|problem>`: each occurrence of an error with its time, user, function and session, so an error incident leads to its calls
|
|
29
|
+
* GROK-20884: Added `grok s o logs container [<name|id>]`: a docker container's log lines cut by `--since`/`--from`/`--to` and `--grep`; no name lists the containers
|
|
30
|
+
* GROK-20884: `grok s o problems get` prints one field per line with every detail in full, and `errors top --limit` returns that many rows instead of the top of every source
|
|
31
|
+
* GROK-20884: Added `grok s observe` (alias `grok s o`): problems and their alerts, problem rules, logger settings with expiring user and group entries, errors, the timeline and cloud logs (CloudWatch and the log archive)
|
|
32
|
+
* GROK-20884: Added `grok s o logs capture <login|group> [--for 30m]`: saves every API request of a user or a group until the entry expires (a `logger set` of `saveHttpRequests`; an entry that expires later keeps its time); `logs capture stop` removes an expiring entry that holds only the capture, `logs capture` lists the active ones
|
|
33
|
+
* GROK-20298: `grok s domains apply` sends the manifest JSON's `version` as the now-required `ifVersion` of a user-managed schema, accepts `--if-version current` to take the registered one, refuses such an apply without either, and forwards `friendlyName`, `description` and `storage` from the JSON for a user-managed schema (a package schema keeps its key set)
|
|
34
|
+
* GROK-20298: `domain-schema.schema.json` accepts the root `storage` key (`{kind: external, connection, schema?, catalog?}`) and the remote-name keys `table`/`column`; `grok api` types an external table's rows with `id` only, an empty expand map, and emits no `<Schema>TransactionOp`/`transaction` for an external schema
|
|
35
|
+
|
|
3
36
|
## 6.7.8 (2026-10-13)
|
|
4
37
|
|
|
5
38
|
* Fixed `grok api` (and every command other than `grok s`) exiting with code 1 and its usage text in 6.7.7: the new `--send-email` flag reached every command as `send-email: false`, which `grok api` rejected as an unknown option
|
package/CLAUDE.md
CHANGED
|
@@ -33,6 +33,7 @@ The CLI uses a modular command pattern. Each command is a separate module that:
|
|
|
33
33
|
- `claude.ts` - Launch a Dockerized dev environment with Datagrok + Claude Code
|
|
34
34
|
- `migrate.ts` - Update legacy packages
|
|
35
35
|
- `init.ts` - Apply configuration to existing packages
|
|
36
|
+
- `src.ts` - Git recipes across the repos of the datagrok umbrella (`grok src`)
|
|
36
37
|
|
|
37
38
|
The commands `api`, `check`, `link`, `publish`, and `test` support the `--all` flag to run recursively across all packages in the current directory.
|
|
38
39
|
|
|
@@ -121,6 +122,18 @@ When a package has a `dockerfiles/` directory, `grok publish` automatically mana
|
|
|
121
122
|
|
|
122
123
|
**Practical implication:** editing any file inside `dockerfiles/<name>/` will cause `grok publish` to rebuild that container image on the next publish, without needing `--rebuild-docker`.
|
|
123
124
|
|
|
125
|
+
**Publish traps:**
|
|
126
|
+
- `grok publish` runs `npm run build` / `pnpm run build` itself (`buildPackage`); `--skip-check`
|
|
127
|
+
only skips checks, and `--rebuild` skips the local build rather than forcing one.
|
|
128
|
+
- With no local image and a transient error on the fallback lookup, CLI <= 6.5.10 published
|
|
129
|
+
`image: null` and the container went to `error`; 6.5.11+ builds instead.
|
|
130
|
+
- A republish renames a package's containers to `<name>_1`.
|
|
131
|
+
- Outside the pnpm workspace, a fresh `npm install` in a package may pull ts-loader 9.6.x /
|
|
132
|
+
webpack 5.109, which cannot resolve `datagrok-api/*` (tsc can); reuse a known-good
|
|
133
|
+
`node_modules` (ts-loader 9.5.7 / webpack 5.106) and junction `node_modules/datagrok-api` to a
|
|
134
|
+
built js-api (`cmd /c mklink /J`).
|
|
135
|
+
- Long publishes outlive a 10-minute agent tool limit; run them detached and poll a log.
|
|
136
|
+
|
|
124
137
|
### Testing Framework
|
|
125
138
|
|
|
126
139
|
Tests use Puppeteer for headless browser automation:
|
package/GROK_S.md
CHANGED
|
@@ -36,6 +36,8 @@ browser or a logged-in session.
|
|
|
36
36
|
| See what fields an entity type has | `grok s describe connections` |
|
|
37
37
|
| Hit any undocumented endpoint | `grok s raw GET /users/current` / `raw POST <path> --data '{...}'` |
|
|
38
38
|
| Check server + per-module health | `grok s healthcheck [--module <name>]` |
|
|
39
|
+
| Server build, uptime, DB pool, requests in flight | `grok s status` |
|
|
40
|
+
| Problems, alerts, problem rules, logger settings, errors, cloud logs | `grok s observe problems list --alert-status open` (alias `grok s o`) |
|
|
39
41
|
| Bulk operations in one round-trip | `grok s batch <entity> <verb> --json items.json` |
|
|
40
42
|
| Move entities dev to prod (bundle, or instance to instance) | `grok s pull ... --out ./bundle` / `grok s migrate ... --from dev --to prod` |
|
|
41
43
|
| Browse / query / edit domain-table rows | `grok s domains query grit.issue --filter 'status = "open"'` / `domains insert` / `domains upload` |
|
|
@@ -136,6 +138,18 @@ grok s connections test --json conn.json # test before saving
|
|
|
136
138
|
|
|
137
139
|
See `public/packages/Chembl/connections/` for connection JSON examples.
|
|
138
140
|
|
|
141
|
+
Connection tags over REST: the entity JSON carries `entityTags`
|
|
142
|
+
(`[{id, tag, owner: {id: <All users group id>}}]`, no `#type` on the owner), not `tags`.
|
|
143
|
+
`grok s connections save --json` without `entityTags` **clears** the tags (saveTags deletes rows
|
|
144
|
+
not in the list); `POST /entities/save_tags {id, entityTags}` sets them. Filter with
|
|
145
|
+
`whereTag([...])` (`?tags=x`); `whereSmart('tag = "x"')` returns nothing for connections over REST.
|
|
146
|
+
A `Web` connection's parameters must be wrapped (`{"url": {"value": "https://..."}}`); a bare
|
|
147
|
+
string makes the test route answer 400 "Invalid argument(s): value".
|
|
148
|
+
|
|
149
|
+
A connection saved over REST goes through `Project.add(uniqueName: true)` and can come back
|
|
150
|
+
renamed `<name>_1` for a package's connection — see `core/docs/PROJECTS_AND_PACKAGES.MD`
|
|
151
|
+
"Package connections on a stand".
|
|
152
|
+
|
|
139
153
|
## Group membership
|
|
140
154
|
|
|
141
155
|
`grok s groups add-members` is idempotent — it resolves each member (by login, name,
|
|
@@ -161,6 +175,12 @@ ambiguous the command prints every matching group and exits non-zero — pass a
|
|
|
161
175
|
disambiguate, or add `--user` (accepted by all four membership verbs) to restrict lookup to
|
|
162
176
|
personal groups.
|
|
163
177
|
|
|
178
|
+
> **Trap:** `grok s groups add-members <group> <users...>` without `--admin` sets `isAdmin=false`
|
|
179
|
+
> on members that are already group admins (reported as `updated`, not `noop`). Before a bulk add
|
|
180
|
+
> run `grok s groups list-members <group> --admin` and exclude those users, or re-add them with
|
|
181
|
+
> `--admin`. Pass the group UUID — `list-members Administrators` by name has returned
|
|
182
|
+
> "(no results)" while the id worked.
|
|
183
|
+
|
|
164
184
|
## User administration
|
|
165
185
|
|
|
166
186
|
```bash
|
|
@@ -198,6 +218,7 @@ grants inherited through the entity's project links (`inherited: true`).
|
|
|
198
218
|
grok s functions run 'Chem:smilesToMw("CCO")' # positional args
|
|
199
219
|
grok s functions run 'Pkg:fn({smiles:"CCO", radius:2})' # named args
|
|
200
220
|
grok s functions run Pkg:fn --json params.json # big input from a file
|
|
221
|
+
grok s functions run 'MyPkg:MyQuery()' --debug # a debug call: the query's own log on stderr
|
|
201
222
|
```
|
|
202
223
|
|
|
203
224
|
The server binds arguments by parameter name, so positional values are mapped onto the
|
|
@@ -411,12 +432,19 @@ grok s domains audit inventory --limit 50 # DDL and row e
|
|
|
411
432
|
grok s domains delete inventory --force # purge: data, audit, registry, grants
|
|
412
433
|
```
|
|
413
434
|
|
|
414
|
-
`schema.json` may be a full manifest or a partial apply body —
|
|
415
|
-
`propertySchemas` and `
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
435
|
+
`schema.json` may be a full manifest or a partial apply body — `tables`, `extend`,
|
|
436
|
+
`propertySchemas`, `dropTables` and `access` are sent, plus `friendlyName`, `description` and
|
|
437
|
+
`storage` when the schema is user-managed (`name` is dropped; `version` becomes `ifVersion` and
|
|
438
|
+
`incarnation` becomes `ifIncarnation` unless `--if-version` says otherwise — `current` takes
|
|
439
|
+
both from the schema registered right now, so a name deleted and re-created since the file was
|
|
440
|
+
written is a conflict, not a fresh `1`). Named tables
|
|
441
|
+
replace their definition wholesale; untouched tables stay as the registry has them. A plan
|
|
442
|
+
that drops or narrows anything is refused until `--confirm-destructive` is passed, and the
|
|
443
|
+
plan is printed with the refusal. `--if-version` fails the apply when the schema's apply
|
|
444
|
+
counter (or `ext_version` on a package schema) has moved; a user-managed schema REQUIRES the
|
|
445
|
+
token (the server answers `version-required` without it) — pass the version the edit was made
|
|
446
|
+
against, or `--if-version current` to take the one registered right now. Dropping a table
|
|
447
|
+
needs Delete on that table, dropping its columns Edit on it. Package-deployed
|
|
420
448
|
schemas cannot be applied to or deleted here — `apply` on one is the user-extension path
|
|
421
449
|
(needs `Extend`), and the manifest is owned by `grok publish`. `delete <schema>` requires
|
|
422
450
|
`--force` because it takes every row with it.
|
|
@@ -471,6 +499,110 @@ module the server does not report exits 1. Requires a valid dev key (standard `g
|
|
|
471
499
|
For an anonymous liveness probe — load balancer, k8s readiness — hit `/admin/health` directly;
|
|
472
500
|
it's on the server's unauthenticated allowlist.
|
|
473
501
|
|
|
502
|
+
### Server status
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
grok s status # build, uptime, DB pool, HTTP connections
|
|
506
|
+
grok s status --output json # {server, extended}
|
|
507
|
+
grok s status --output quiet # just the version
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Reads `GET /info/server` and `GET /info/server?extended=true`: `Version`, `Branch`, `Commit`,
|
|
511
|
+
build `Date`, `Uptime`, `Requests` served, `Used Connections` / `Pool Connections` of the DB pool,
|
|
512
|
+
`Isolate`, `Service network`, `webRoot`, and the `Http Server` connection counts. For an admin the
|
|
513
|
+
extended call replaces the active-connection count with the requests in flight per endpoint
|
|
514
|
+
(`{"GET /api/...": n}`), printed as a table; for anyone else the two responses are the same.
|
|
515
|
+
|
|
516
|
+
## Problems, logging and errors (`grok s observe`, alias `grok s o`)
|
|
517
|
+
|
|
518
|
+
Thin commands over the server's routes; `grok s observe` with no command prints every verb and option.
|
|
519
|
+
|
|
520
|
+
```bash
|
|
521
|
+
grok s o problems list --alert-status open # the open alerts; --status, --state, --kind, --since 7d
|
|
522
|
+
grok s o problems get 'health:Jupyter@<server>' # by id or kind:key; history <id> for its records
|
|
523
|
+
grok s o problems status connection:<connection id> --data '{"status": "muted", "reason": "maintenance", "until": "2026-10-04T06:00:00Z"}'
|
|
524
|
+
grok s o problems ack 'health:Jupyter@<server>' --reason x # resolve <id> --reason ... closes the alert
|
|
525
|
+
grok s o problems severity dashboard:<project id> critical --reason 'board meeting' # pin; none unpins
|
|
526
|
+
grok s o rules get > rules.json # Settings > Alerts problem rules; rules put --json rules.json
|
|
527
|
+
grok s o rules test # evaluates the saved rules once, now
|
|
528
|
+
grok s o logger get exportSettings # the logger settings, or one path of them
|
|
529
|
+
grok s o logger set --user alice --set debugFlags='["db"]' --for 30m --reason "ticket 123" # an expiring entry, from All users
|
|
530
|
+
grok s o logger set --user alice --remove # delete that entry
|
|
531
|
+
grok s o logger history --limit 5 # log-settings-changed audit records with their diff
|
|
532
|
+
grok s o logs capture alice --for 2h --reason "ticket 123" # logger set of saveHttpRequests on an expiring entry; 30m by default
|
|
533
|
+
grok s o logs capture # the active captures; logs capture stop alice removes one
|
|
534
|
+
grok s o errors top --since 7d --by signature --limit 20 # GET /admin/metrics errors
|
|
535
|
+
grok s o errors top --since 24h --source server # ranked by distinct users: system errors (scheduler, probes) need --source
|
|
536
|
+
grok s o errors get error-incident:<key> # each occurrence of a problem's error: time, user, function, session
|
|
537
|
+
grok s o errors get <signature> --since 1d # or of a signature from errors top --by signature
|
|
538
|
+
grok s o timeline --session <id> # its events and requests of the last 15 min
|
|
539
|
+
grok s o timeline --session <id> --from 2026-10-09T10:00 # 10 min from then; --to sets the end, at most 2 h later
|
|
540
|
+
grok s o logs cloud --since 2h --filter ERROR # CloudWatch, the instance's own group; --group, --from/--to
|
|
541
|
+
grok s o logs archive list --prefix cw/ --since 1d # keys in the log archive (WORM bucket); --since filters the returned page only
|
|
542
|
+
grok s o logs archive read <key> # one archived object decoded to events
|
|
543
|
+
grok s o logs container # docker containers and their status
|
|
544
|
+
grok s o logs container scripting-python_1 --since 1h --grep 'Kernel (started|shutdown)' # one container's log lines
|
|
545
|
+
grok s o logs match 'level in (error, warning) and not text ~ ResizeObserver' --since 2h # what a log filter matches
|
|
546
|
+
grok s o logs match --level error --package Chembl --grep 'time(d )?out' # flags add conditions joined by and; --grep is a regex
|
|
547
|
+
grok s o logs match --level audit --space Chem # audit of entities in the Chem space and its subspaces (case-sensitive)
|
|
548
|
+
grok s o logs match 'space startsWith Chem: or space endsWith :Assays' # space: the entity's space path; = ~ =~ startsWith endsWith
|
|
549
|
+
grok s o groups # log groups (Settings > Logger): name, kind, levels, filter, where to
|
|
550
|
+
grok s o groups add --name Chembl --levels error,warning --filter 'package = Chembl' --to db,grafana:otlp
|
|
551
|
+
grok s o groups set Chembl --when 'level = error and package = Chembl' --scope session --for 15m --before 60s --flags db
|
|
552
|
+
grok s o groups set Chembl --enabled false # only the flags given change; groups rm Chembl deletes it
|
|
553
|
+
grok s o groups put --json groups.json # replace every log group (the GROK_PARAMETERS settings.logger.logGroups shape)
|
|
554
|
+
grok s o groups reset # back to the default groups; the audit group can only be switched off, not removed
|
|
555
|
+
grok s o endpoints # where groups can send records; endpoints rm grafana
|
|
556
|
+
grok s o logs activations # triggered groups on now; logs activations stop <group id> <key>
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
Problems need `ManageAlerts`, errors, another session's timeline and `logs cloud`/`logs archive` `ViewTelemetry`,
|
|
560
|
+
`rules test` both, logger and rules changes, `groups`/`endpoints` changes, `logs activations stop` and `logs capture`
|
|
561
|
+
`EditPluginsSettings`; `logs match` and `logs activations` need `ViewTelemetry`, and changing the audit group or an
|
|
562
|
+
endpoint it sends to also needs `EditAuditLogging`. `logs match` takes the whole filter as one quoted argument; `--grep`
|
|
563
|
+
is a regex and `--space` matches the space and its subspaces, both case-sensitive, as `=~` is. `groups add|set|rm`
|
|
564
|
+
need a server that returns its default log groups; on an older one, replace them all with `groups put`. `capture` is an expiring
|
|
565
|
+
`userGroupSettings` entry with `saveHttpRequests` on (seeded from the All Users entry, with `usage` saved); on an entry
|
|
566
|
+
that already expires later it keeps that time. `capture stop` removes only an expiring entry that holds nothing but the
|
|
567
|
+
capture; for any other entry use `logger set --user <login> --remove`. Read what it recorded with
|
|
568
|
+
`timeline --session <id>`. `logs container` reads the container's latest `--limit` lines (20000) and only then cuts `--since`/`--from`/`--to` (UTC, matched across the container's own offset) and `--grep`, so a window older than those lines comes back empty: raise `--limit`. `logs archive list --since` filters only the page the server returned (`--limit`), so narrow
|
|
569
|
+
the listing with `--prefix` first. `logs` read with the server's AWS role, or with `--connection <id>`. In durations `m` means minutes (in `grok s pull --since` it means months).
|
|
570
|
+
|
|
571
|
+
### Logging one command (`--log`)
|
|
572
|
+
|
|
573
|
+
`logger set --user` changes what a user logs for a while; `--log` makes the server keep what the
|
|
574
|
+
requests of one command log, and prints it at the end. It works on every `grok s` command, `grok s o`
|
|
575
|
+
included:
|
|
576
|
+
|
|
577
|
+
```bash
|
|
578
|
+
grok s functions run UsageAnalysis:Groups --log # verbose: every level
|
|
579
|
+
grok s functions run UsageAnalysis:Groups --log verbose,db # and every SQL statement (the db debug flag)
|
|
580
|
+
grok s users list --log debug,storage,credentials # levels and flags; flags alone add debug
|
|
581
|
+
grok s functions run 'MyPkg:MyQuery()' --debug # the query's own debug log, plus errors and warnings
|
|
582
|
+
grok s users list --log --output json 2>log.jsonl # one JSON object per record
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
Every request of the command carries one `datlas-log-id` (a new UUID per command) and the spec in
|
|
586
|
+
`datlas-log`. A command is often several requests, and each can land on a different isolate and
|
|
587
|
+
server instance: each keeps what its request logged at the spec in memory, in a buffer for that id.
|
|
588
|
+
Those records never reach the database, the console or a sync destination, and what the server logs
|
|
589
|
+
the usual way does not change. When the command ends, `GET /log/requests/<logId>` gathers the
|
|
590
|
+
buffers from every isolate of every instance over the message queue, hands them over and forgets
|
|
591
|
+
them; the CLI prints them to stderr, so stdout keeps the result. The gather also adds what the
|
|
592
|
+
server saved the usual way for the command's session at the levels of the spec (errors, warnings, audit),
|
|
593
|
+
skipping copies of records it already has, so an instance that died before it could answer still
|
|
594
|
+
shows its saved errors. A buffer nobody gathers is dropped after an hour. No permission is needed; another user's id takes `ViewTelemetry`.
|
|
595
|
+
`grok s o logs request <logId>` gathers one id by hand.
|
|
596
|
+
|
|
597
|
+
The spec is comma-separated: `verbose`, `standard`, the levels (`error`, `warning`, `info`, `debug`,
|
|
598
|
+
`audit`, `usage`) and the debug flags of the logger settings (`db`, `socket`, `storage`, `credentials`,
|
|
599
|
+
`docker`, `auth`, ... or `all`).
|
|
600
|
+
|
|
601
|
+
`functions run --debug` sends `?debug=true`: the call runs as a debug call (not cached), the way the
|
|
602
|
+
query debugger in the UI runs it, and its own log (connection, the query's stages) is kept with the
|
|
603
|
+
rest; without `--log` the spec is `error,warning`. Only what runs in Datlas is kept: a script that
|
|
604
|
+
runs on a queue worker adds nothing here.
|
|
605
|
+
|
|
474
606
|
## Describing an entity type
|
|
475
607
|
|
|
476
608
|
```bash
|
package/README.md
CHANGED
|
@@ -151,3 +151,17 @@ For more information on configuring connections, refer to the [Connections](http
|
|
|
151
151
|
| debug | Sets a debug breakpoint before each test execution, allowing you to debug the test. Only works in `gui` mode |
|
|
152
152
|
|
|
153
153
|
- `link` command is used for public plugins development to link `datagrok-api` and libraries.
|
|
154
|
+
- `connect-folder` makes a local folder a Datagrok connection. A folder agent in Docker dials out to the server (no
|
|
155
|
+
inbound port) with a key of its own that can only serve this folder; your keypair (`grok login <server>` first)
|
|
156
|
+
never leaves the machine. Connecting the same folder again resumes the same connection. Requires Docker and the
|
|
157
|
+
"Connect local folders" permission.
|
|
158
|
+
|
|
159
|
+
```shell
|
|
160
|
+
grok connect-folder <folder> [--host <alias>] [--read-only] [--foreground] [--image <ref>] [--force]
|
|
161
|
+
grok connect-folder list [--host <alias>] [--output json]
|
|
162
|
+
grok connect-folder start|stop|logs <connection> [-f]
|
|
163
|
+
grok connect-folder rm <connection>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`<connection>` is the connection name, a connection id prefix (8+ characters), or the folder. `stop` keeps the
|
|
167
|
+
folder disconnected after a reboot; `rm` removes the container, the key and the connection.
|
package/bin/commands/add.js
CHANGED
|
@@ -16,8 +16,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
16
16
|
* answer key): u2 is not on npm, so the dependency is the workspace link, and `u2core` is the one
|
|
17
17
|
* external `@datagrok/build-config` does not know about. The css imports and the tsconfig need
|
|
18
18
|
* nothing — the shared bundler config carries the loaders and the tsconfig extends the base. */
|
|
19
|
-
const
|
|
20
|
-
const u2DgModule = '@datagrok-libraries/u2/src/dg/index.js';
|
|
19
|
+
const emsDgModule = '@datagrok-libraries/ems/src/dg';
|
|
21
20
|
const u2RspackExternal = `'datagrok-api/u2core': 'DG.U2'`;
|
|
22
21
|
const u2RspackConfig = [`const {bundler} = require('@datagrok/build-config');`, '', `module.exports = bundler({externals: {${u2RspackExternal}}});`, ''].join('\n');
|
|
23
22
|
function add(args) {
|
|
@@ -110,7 +109,7 @@ function add(args) {
|
|
|
110
109
|
* existing imports (which webpack's externals depend on). */
|
|
111
110
|
function addU2Imports() {
|
|
112
111
|
const contents = _fs.default.readFileSync(packageEntry, 'utf8');
|
|
113
|
-
if (contents.includes(
|
|
112
|
+
if (contents.includes(emsDgModule)) return;
|
|
114
113
|
const eol = contents.includes('\r\n') ? '\r\n' : '\n';
|
|
115
114
|
const imports = _fs.default.readFileSync(_path.default.join(templateDir, 'entity-template', 'domain-app-imports.js'), 'utf8').trimEnd().split(/\r?\n/);
|
|
116
115
|
const lines = contents.split(/\r?\n/);
|
|
@@ -125,7 +124,8 @@ function add(args) {
|
|
|
125
124
|
function addU2Wiring() {
|
|
126
125
|
const packageObj = JSON.parse(_fs.default.readFileSync(packagePath, 'utf8'));
|
|
127
126
|
packageObj.dependencies = packageObj.dependencies ?? {};
|
|
128
|
-
packageObj.dependencies['@datagrok-libraries/
|
|
127
|
+
packageObj.dependencies['@datagrok-libraries/ems'] ??= 'workspace:^';
|
|
128
|
+
packageObj.dependencies['@datagrok-libraries/u2'] ??= 'workspace:^';
|
|
129
129
|
_fs.default.writeFileSync(packagePath, JSON.stringify(packageObj, null, 2), 'utf8');
|
|
130
130
|
if (!_fs.default.existsSync(rspackConfigPath)) _fs.default.writeFileSync(rspackConfigPath, u2RspackConfig, 'utf8');else if (!_fs.default.readFileSync(rspackConfigPath, 'utf8').includes('datagrok-api/u2core')) color.warn(`rspack.config.js is already there — add \`externals: {${u2RspackExternal}}\` to it by hand`);
|
|
131
131
|
}
|
package/bin/commands/api.js
CHANGED
|
@@ -24,20 +24,30 @@ const dbFile = 'db.ts';
|
|
|
24
24
|
const dbUiFile = 'db-ui.ts';
|
|
25
25
|
/** Where `db-ui.ts` takes the u2 domain stack from — the same specifier Grit and Stockroom use
|
|
26
26
|
* (u2 is a relative-path dependency without a `dg` subpath export). */
|
|
27
|
-
const
|
|
27
|
+
const emsDgModule = '@datagrok-libraries/ems/src/dg';
|
|
28
28
|
|
|
29
|
-
/**
|
|
30
|
-
*
|
|
29
|
+
/** The platform's `StringUtils.toSingular` (ddt utils.dart): regular forms only; a noun already
|
|
30
|
+
* singular in `-is`/`-us`/`-ss` passes through. */
|
|
31
|
+
function singularizeTableName(name) {
|
|
32
|
+
if (name.length <= 2 || !name.endsWith('s') || /(ss|is|us)$/.test(name)) return name;
|
|
33
|
+
if (name.endsWith('ies') && name.length > 3) return name.slice(0, -3) + 'y';
|
|
34
|
+
return /(xes|zzes|ches|shes|sses|tuses)$/.test(name) ? name.slice(0, -2) : name.slice(0, -1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** The platform's `StringUtils.toPlural` (ddt utils.dart) — `issue` → `issues`, `box` →
|
|
38
|
+
* `boxes`, `category` → `categories`, `analysis` → `analyses`. */
|
|
31
39
|
function pluralizeTableName(name) {
|
|
40
|
+
if (name.endsWith('is')) return name.slice(0, -2) + 'es';
|
|
32
41
|
if (/(s|x|z|ch|sh)$/.test(name)) return name + 'es';
|
|
33
|
-
if (/[^
|
|
42
|
+
if (/[^aeiou]y$/.test(name)) return name.slice(0, -1) + 'ies';
|
|
34
43
|
return name + 's';
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
/** The camelCase plural property name a table gets on the schema client and under the
|
|
38
|
-
* schema handle's `tables` (`issue_label` → `issueLabels`)
|
|
47
|
+
* schema handle's `tables` (`issue_label` → `issueLabels`, `categories` → `categories`): the
|
|
48
|
+
* plural of the singular, as the platform derives a table's plural name. */
|
|
39
49
|
function tableProp(tableName) {
|
|
40
|
-
return utils.snakeToCamelCase(pluralizeTableName(tableName), false);
|
|
50
|
+
return utils.snakeToCamelCase(pluralizeTableName(singularizeTableName(tableName)), false);
|
|
41
51
|
}
|
|
42
52
|
const domainSchemaPath = _path.default.join(_path.default.dirname(_path.default.dirname(__dirname)), 'domain-schema.schema.json');
|
|
43
53
|
/** The platform's own schema, whose tables a qualified ref may target (`Core.users`, ...). */
|
|
@@ -229,7 +239,7 @@ function generateDomainClients(packageDir = curDir, options) {
|
|
|
229
239
|
_fs.default.writeFileSync(_path.default.join(genDir, dbFile), normEol(content).replace(/\n/g, '\r\n'), 'utf8');
|
|
230
240
|
color.log(`Successfully generated file src/generated/${dbFile}${sep}`, 'success');
|
|
231
241
|
if (ui) {
|
|
232
|
-
const uiContent = annotationForDbUiFile + `import {domains, DomainTable} from '${
|
|
242
|
+
const uiContent = annotationForDbUiFile + `import {domains, DomainTable} from '${emsDgModule}';${sep}` + wrapTokens(`import {`, dbImports, ', ', `} from './${dbFile.replace(/\.ts$/, '')}';`) + sep + sep + uiParts.join(sep);
|
|
233
243
|
_fs.default.writeFileSync(_path.default.join(genDir, dbUiFile), normEol(uiContent).replace(/\n/g, '\r\n'), 'utf8');
|
|
234
244
|
color.log(`Successfully generated file src/generated/${dbUiFile}${sep}`, 'success');
|
|
235
245
|
}
|
|
@@ -265,6 +275,10 @@ function findDependencyManifest(packageDir, schemaName) {
|
|
|
265
275
|
function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageDir) {
|
|
266
276
|
const decls = [];
|
|
267
277
|
const systemColumnNames = new Set(domainSystemColumns.map(([name]) => name));
|
|
278
|
+
// An external table's rows carry `id` (the encoded business key) and nothing else
|
|
279
|
+
// system-side; nothing is expanded and nothing is written (external-bindings §3).
|
|
280
|
+
const external = manifest.storage?.kind === 'external';
|
|
281
|
+
const systemColumns = external ? domainSystemColumns.filter(([name]) => name === 'id') : domainSystemColumns;
|
|
268
282
|
const tableNames = Object.keys(manifest.tables);
|
|
269
283
|
// Keyed by table name; a qualified ref target ('Core.queries', 'grit.issue') lands here
|
|
270
284
|
// under its qualified key so the expand map can list its columns — it gets no accessor.
|
|
@@ -273,7 +287,9 @@ function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageD
|
|
|
273
287
|
// different sets fall back to a `<alias><PascalTable>` name (deterministic).
|
|
274
288
|
const choicesAliases = {};
|
|
275
289
|
const choicesAlias = (tableName, columnName, values) => {
|
|
276
|
-
|
|
290
|
+
// JSON.stringify escapes every special character; inside single quotes only ' needs one more
|
|
291
|
+
const literals = values.map(v => `'${JSON.stringify(v).slice(1, -1).replace(/'/g, `\\'`)}'`);
|
|
292
|
+
const valueSet = literals.join(' | ');
|
|
277
293
|
let alias = utils.snakeToCamelCase(tableName) + utils.snakeToCamelCase(columnName);
|
|
278
294
|
if (choicesAliases[alias] !== undefined && choicesAliases[alias] !== valueSet) alias += utils.snakeToCamelCase(tableName);
|
|
279
295
|
if (choicesAliases[alias] !== undefined && choicesAliases[alias] !== valueSet) {
|
|
@@ -282,7 +298,7 @@ function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageD
|
|
|
282
298
|
}
|
|
283
299
|
if (choicesAliases[alias] === undefined) {
|
|
284
300
|
choicesAliases[alias] = valueSet;
|
|
285
|
-
decls.push(wrapTokens(`export type ${alias} = `,
|
|
301
|
+
decls.push(wrapTokens(`export type ${alias} = `, literals, ' | ', ';'));
|
|
286
302
|
}
|
|
287
303
|
return alias;
|
|
288
304
|
};
|
|
@@ -360,6 +376,7 @@ function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageD
|
|
|
360
376
|
insertType: column.type === 'datetime' ? 'Dayjs | string' : tsType,
|
|
361
377
|
required: column.required === true,
|
|
362
378
|
autoNumber: column.autoNumber != null,
|
|
379
|
+
hasDefault: column.default != null,
|
|
363
380
|
ref: ref
|
|
364
381
|
});
|
|
365
382
|
return true;
|
|
@@ -376,6 +393,7 @@ function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageD
|
|
|
376
393
|
}
|
|
377
394
|
return columns;
|
|
378
395
|
}
|
|
396
|
+
const accessorTables = new Map();
|
|
379
397
|
for (const tableName of tableNames) {
|
|
380
398
|
const typeName = utils.snakeToCamelCase(tableName);
|
|
381
399
|
if (emittedTypes.has(typeName)) {
|
|
@@ -383,6 +401,12 @@ function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageD
|
|
|
383
401
|
return null;
|
|
384
402
|
}
|
|
385
403
|
emittedTypes.add(typeName);
|
|
404
|
+
const accessor = tableProp(tableName);
|
|
405
|
+
if (accessorTables.has(accessor)) {
|
|
406
|
+
color.error(`${manifestPath}: tables '${accessorTables.get(accessor)}' and '${tableName}' ` + `both get the accessor '${accessor}' (rename one of them)`);
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
accessorTables.set(accessor, tableName);
|
|
386
410
|
const columns = collectColumns(tableName, manifest, false);
|
|
387
411
|
if (columns == null) return null;
|
|
388
412
|
tableColumns[tableName] = columns;
|
|
@@ -427,12 +451,12 @@ function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageD
|
|
|
427
451
|
const columns = tableColumns[tableName];
|
|
428
452
|
const relations = tableRelations[tableName];
|
|
429
453
|
const rowLines = [`/** Row of \`${manifest.name}.${tableName}\`. */`, `export interface ${typeName}Row {`];
|
|
430
|
-
for (const [name, tsType] of
|
|
454
|
+
for (const [name, tsType] of systemColumns) rowLines.push(` ${name}: ${tsType};`);
|
|
431
455
|
for (const c of columns) rowLines.push(` ${c.name}${c.required || c.autoNumber ? '' : '?'}: ${c.tsType};`);
|
|
432
456
|
rowLines.push('}');
|
|
433
457
|
decls.push(rowLines.join(sep));
|
|
434
458
|
const insertLines = [`/** Insert payload for \`${manifest.name}.${tableName}\`. */`, `export interface ${typeName}Insert {`];
|
|
435
|
-
for (const c of columns) insertLines.push(` ${c.name}${c.required ? '' : '?'}: ${c.insertType};`);
|
|
459
|
+
for (const c of columns) insertLines.push(` ${c.name}${c.required && !c.hasDefault ? '' : '?'}: ${c.insertType};`);
|
|
436
460
|
for (const r of relations) insertLines.push(` /** Link set: \`${r.target}\` row ids. */`, ` ${r.name}?: string[];`);
|
|
437
461
|
if (table.idempotency === true) insertLines.push(' idempotencyKey?: string;');
|
|
438
462
|
insertLines.push('}');
|
|
@@ -447,13 +471,13 @@ function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageD
|
|
|
447
471
|
updateLines.push('};');
|
|
448
472
|
decls.push(updateLines.join(sep));
|
|
449
473
|
}
|
|
450
|
-
decls.push(formatColumnUnion(`${typeName}Column`, [...
|
|
474
|
+
decls.push(formatColumnUnion(`${typeName}Column`, [...systemColumns.map(([name]) => name), ...columns.map(c => c.name)]));
|
|
451
475
|
|
|
452
476
|
// Expand map: MUST stay a `type` alias — object-type literals carry the implicit
|
|
453
477
|
// index signature the `TExpand extends {[key: string]: {}}` constraint needs
|
|
454
478
|
// (an interface would fail it).
|
|
455
479
|
const expandEntries = [];
|
|
456
|
-
for (const c of columns) {
|
|
480
|
+
for (const c of external ? [] : columns) {
|
|
457
481
|
if (c.ref == null) continue;
|
|
458
482
|
const fields = tableColumns[c.ref].map(tc => `'${c.name}.${tc.name}'?: ${tc.tsType}`);
|
|
459
483
|
expandEntries.push(wrapTokens(` '${c.name}': {`, fields, '; ', '};', ' '));
|
|
@@ -463,15 +487,16 @@ function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageD
|
|
|
463
487
|
// The link shape stays INLINE (structurally DG.DomainRelationLink): generated
|
|
464
488
|
// files must compile against the datagrok-api the package depends on, which may
|
|
465
489
|
// predate that interface.
|
|
466
|
-
for (const r of relations) expandEntries.push(` '${r.name}': {${r.name}?: {id: string; name: string}[]};`);
|
|
467
|
-
for (const childName of tableNames) {
|
|
490
|
+
for (const r of external ? [] : relations) expandEntries.push(` '${r.name}': {${r.name}?: {id: string; name: string}[]};`);
|
|
491
|
+
for (const childName of external ? [] : tableNames) {
|
|
468
492
|
const fks = tableColumns[childName].filter(c => c.ref === tableName);
|
|
469
493
|
if (fks.length === 0) continue;
|
|
470
494
|
const childType = utils.snakeToCamelCase(childName);
|
|
471
495
|
if (fks.length === 1) expandEntries.push(` 'details:${childName}': {${childName}?: ${childType}Row[]};`);else for (const fk of fks) expandEntries.push(` 'details:${childName}.${fk.name}': {${childName}?: ${childType}Row[]};`);
|
|
472
496
|
}
|
|
473
497
|
decls.push(expandEntries.length === 0 ? `export type ${typeName}Expand = {};` : [`/** Expand keys of \`${manifest.name}.${tableName}\` → fields each adds to the row ` + `(consumed by query()/builder). */`, `export type ${typeName}Expand = {`, ...expandEntries, '};'].join(sep));
|
|
474
|
-
txArms.push(` {op: 'insert'; table: '${tableName}'; ref?: string; values: DG.DomainTxValues<${typeName}Insert>; ` + `onDuplicate?: 'error'} |`, ` {op: 'update'; table: '${tableName}'; id: string; ` + `values: DG.DomainTxValues<${relations.length > 0 ? `${typeName}Update` : `Partial<${typeName}Row>`}>; ` + `expectedVersion?: number} |`, ` {op: 'delete'; table: '${tableName}'; id: string} |`);
|
|
498
|
+
if (!external) txArms.push(` {op: 'insert'; table: '${tableName}'; ref?: string; values: DG.DomainTxValues<${typeName}Insert>; ` + `onDuplicate?: 'error'} |`, ` {op: 'update'; table: '${tableName}'; id: string; ` + `values: DG.DomainTxValues<${relations.length > 0 ? `${typeName}Update` : `Partial<${typeName}Row>`}>; ` + `expectedVersion?: number} |`, ` {op: 'delete'; table: '${tableName}'; id: string} |`);
|
|
499
|
+
if (!external && table.softDelete !== false) txArms.push(` {op: 'restore'; table: '${tableName}'; id: string} |`);
|
|
475
500
|
|
|
476
501
|
// No datetime config: the client resolves datetime columns from the domain
|
|
477
502
|
// registry itself (the Dayjs typing in <Table>Row stays true for every client).
|
|
@@ -480,10 +505,14 @@ function generateDomainSchemaCode(manifest, manifestPath, emittedTypes, packageD
|
|
|
480
505
|
clients.push(` get ${tableProp(tableName)}() {`, ` return grok.dapi.domains.table<${typeName}Row, ${typeName}Insert, ` + `${typeName}Column, ${typeName}Expand` + `${relations.length > 0 ? `, ${typeName}Update` : ''}>('${manifest.name}.${tableName}');`, ' },');
|
|
481
506
|
}
|
|
482
507
|
const schemaType = utils.snakeToCamelCase(manifest.name);
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
508
|
+
const transaction = [];
|
|
509
|
+
if (!external) {
|
|
510
|
+
const lastArm = txArms.pop();
|
|
511
|
+
txArms.push(lastArm.replace(/ \|$/, ';'));
|
|
512
|
+
decls.push([`export type ${schemaType}TransactionOp =`, ...txArms].join(sep));
|
|
513
|
+
transaction.push(` transaction<T extends ${schemaType}TransactionOp[]>(ops: [...T]):`, ` Promise<{[K in keyof T]: DG.DomainOpResultFor<T[K]>}> {`, ` return grok.dapi.domains.transaction('${manifest.name}', ops) as`, ` Promise<{[K in keyof T]: DG.DomainOpResultFor<T[K]>}>;`, ' },');
|
|
514
|
+
}
|
|
515
|
+
decls.push([`/** Typed clients for the \`${manifest.name}\` domain schema tables ` + `(lazy — no import-time side effects). */`, `export const ${utils.snakeToCamelCase(manifest.name, false)}Db = {`, ...clients, ...transaction, '};'].join(sep));
|
|
487
516
|
return decls.join(sep.repeat(2)) + sep;
|
|
488
517
|
}
|
|
489
518
|
|
package/bin/commands/check.js
CHANGED
|
@@ -13,6 +13,7 @@ exports.checkImportStatements = checkImportStatements;
|
|
|
13
13
|
exports.checkNpmIgnore = checkNpmIgnore;
|
|
14
14
|
exports.checkPackageFile = checkPackageFile;
|
|
15
15
|
exports.checkSourceMap = checkSourceMap;
|
|
16
|
+
exports.checkU2Usage = checkU2Usage;
|
|
16
17
|
exports.extractExternals = extractExternals;
|
|
17
18
|
var _fs = _interopRequireDefault(require("fs"));
|
|
18
19
|
var _path = _interopRequireDefault(require("path"));
|
|
@@ -20,6 +21,7 @@ var _ignoreWalk = _interopRequireDefault(require("ignore-walk"));
|
|
|
20
21
|
var utils = _interopRequireWildcard(require("../utils/utils"));
|
|
21
22
|
var color = _interopRequireWildcard(require("../utils/color-utils"));
|
|
22
23
|
var _const = require("../utils/const");
|
|
24
|
+
var _child_process = require("child_process");
|
|
23
25
|
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); }
|
|
24
26
|
const warns = ['Latest package version', 'Datagrok API version should contain', 'is served by the platform'];
|
|
25
27
|
const forbiddenNames = ['function', 'class', 'export'];
|
|
@@ -91,6 +93,8 @@ function runChecks(packagePath, soft = false, noExit = false) {
|
|
|
91
93
|
if (externals) errors.push(...checkImportStatements(packagePath, jsTsFiles, externals));
|
|
92
94
|
}
|
|
93
95
|
errors.push(...checkDatagrokApiImports(packagePath, jsTsFiles));
|
|
96
|
+
const u2Errors = checkU2Usage(packagePath, json);
|
|
97
|
+
errors.push(...u2Errors);
|
|
94
98
|
warnings.push(...checkHeavyImports(packagePath, jsTsFiles, _path.default.basename(packagePath)));
|
|
95
99
|
if (!soft) errors.push(...checkSourceMap(packagePath));
|
|
96
100
|
errors.push(...checkNpmIgnore(packagePath));
|
|
@@ -111,7 +115,8 @@ function runChecks(packagePath, soft = false, noExit = false) {
|
|
|
111
115
|
if (errors.length) {
|
|
112
116
|
console.log(`Checking package ${_path.default.basename(packagePath)}...`);
|
|
113
117
|
showError(errors);
|
|
114
|
-
|
|
118
|
+
// the u2 consumer ratchet holds pre-1.0 packages too
|
|
119
|
+
if (soft || u2Errors.length === 0 && (json.version.startsWith('0') || errors.every(w => warns.some(ww => w.includes(ww))))) return true;
|
|
115
120
|
if (noExit) return false;else process.exit(1);
|
|
116
121
|
}
|
|
117
122
|
color.log(`Checking package ${_path.default.basename(packagePath)}...\t\t\t\u2713 OK`);
|
|
@@ -213,6 +218,43 @@ function checkDatagrokApiImports(packagePath, files) {
|
|
|
213
218
|
}
|
|
214
219
|
return errors;
|
|
215
220
|
}
|
|
221
|
+
|
|
222
|
+
/** u2's consumer check for a package built on u2 or ems: the u2 lint rules and the shrink-only counts of
|
|
223
|
+
* `consumers-baseline.json`. Prints the ratchet line; growth and enforced rules are errors. */
|
|
224
|
+
function checkU2Usage(packagePath, json) {
|
|
225
|
+
const deps = {
|
|
226
|
+
...json.dependencies,
|
|
227
|
+
...json.devDependencies
|
|
228
|
+
};
|
|
229
|
+
if (!('@datagrok-libraries/u2' in deps) && !('@datagrok-libraries/ems' in deps)) return [];
|
|
230
|
+
let script;
|
|
231
|
+
try {
|
|
232
|
+
script = _path.default.join(_path.default.dirname(require.resolve('@datagrok-libraries/u2/package.json', {
|
|
233
|
+
paths: [packagePath]
|
|
234
|
+
})), 'scripts', 'check-consumer.mjs');
|
|
235
|
+
} catch {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
if (!_fs.default.existsSync(script)) return [];
|
|
239
|
+
const run = (0, _child_process.spawnSync)(process.execPath, [script, packagePath, '--json'], {
|
|
240
|
+
encoding: 'utf-8',
|
|
241
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
242
|
+
timeout: 300000
|
|
243
|
+
});
|
|
244
|
+
let report;
|
|
245
|
+
try {
|
|
246
|
+
report = JSON.parse(run.stdout);
|
|
247
|
+
} catch {
|
|
248
|
+
return [`u2 consumer check could not run: ${(run.stderr || run.stdout || String(run.error)).trim()}`];
|
|
249
|
+
}
|
|
250
|
+
const errors = [];
|
|
251
|
+
for (const tree of report.trees) {
|
|
252
|
+
for (const line of tree.summary) console.log(line);
|
|
253
|
+
for (const note of tree.notes) color.log(` ${note}`, 'warn');
|
|
254
|
+
errors.push(...tree.problems);
|
|
255
|
+
}
|
|
256
|
+
return errors;
|
|
257
|
+
}
|
|
216
258
|
function checkHeavyImports(packagePath, files, packageName = '') {
|
|
217
259
|
const warnings = [];
|
|
218
260
|
const isTestPackage = /tests?$/i.test(packageName);
|