datagrok-tools 6.7.7 → 6.7.9
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/PACKAGES_DEV.md +14 -13
- package/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +16 -0
- package/CLAUDE.md +13 -1
- package/GROK_S.md +74 -7
- package/README.md +2 -1
- package/bin/commands/add.js +4 -4
- package/bin/commands/api.js +20 -12
- package/bin/commands/help.js +2 -2
- package/bin/commands/server-domains.js +24 -7
- package/bin/commands/server-observe.js +526 -0
- package/bin/commands/server.js +8 -1
- package/bin/grok.js +7 -3
- package/bin/utils/ent-helpers.js +1 -1
- package/bin/utils/migrate/walker.js +2 -1
- package/bin/utils/migrate/walker.ts +1 -1
- package/bin/utils/node-dapi.js +27 -10
- package/domain-schema.schema.json +50 -1
- package/entity-template/domain-app-imports.js +2 -2
- package/package.json +6 -3
package/bin/utils/node-dapi.js
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.NodeUsersDataSource = exports.NodeTablesDataSource = exports.NodeSharesDataSource = exports.NodePackagesDataSource = exports.NodeHttpDataSource = exports.NodeGroupsDataSource = exports.NodeFuncsDataSource = exports.NodeFilesDataSource = exports.NodeDomainsDataSource = exports.NodeDapi = exports.NodeConnectionsDataSource = exports.NodeApiClient = exports.InternalDataSource = void 0;
|
|
6
|
+
exports.UUID_RE = exports.NodeUsersDataSource = exports.NodeTablesDataSource = exports.NodeSharesDataSource = exports.NodePackagesDataSource = exports.NodeHttpDataSource = exports.NodeGroupsDataSource = exports.NodeFuncsDataSource = exports.NodeFilesDataSource = exports.NodeDomainsDataSource = exports.NodeDapi = exports.NodeConnectionsDataSource = exports.NodeApiClient = exports.InternalDataSource = void 0;
|
|
7
7
|
exports.apiPath = apiPath;
|
|
8
8
|
exports.buildQuery = buildQuery;
|
|
9
9
|
exports.ensureBodyId = ensureBodyId;
|
|
10
10
|
exports.mapPositionalParams = mapPositionalParams;
|
|
11
11
|
exports.parseDomainAddress = parseDomainAddress;
|
|
12
12
|
exports.throwIfApiError = throwIfApiError;
|
|
13
|
+
exports.tryParseJson = tryParseJson;
|
|
13
14
|
var _crypto = require("crypto");
|
|
14
15
|
var _keypair = require("./keypair");
|
|
15
16
|
/// Docs: [Grok Dapi](/docs/plans/grok-dapi/)
|
|
@@ -142,7 +143,7 @@ class NodeApiClient {
|
|
|
142
143
|
return null;
|
|
143
144
|
}
|
|
144
145
|
const ct = res.headers.get('content-type') ?? '';
|
|
145
|
-
return throwIfApiError(ct.includes('application/json') ? await res.json() : await res.text());
|
|
146
|
+
return throwIfApiError(ct.includes('application/json') ? await res.json() : await res.text(), res.headers);
|
|
146
147
|
}
|
|
147
148
|
get(path) {
|
|
148
149
|
return this.request('GET', path);
|
|
@@ -170,7 +171,7 @@ class NodeApiClient {
|
|
|
170
171
|
}, false, BYTES_TIMEOUT_MS);
|
|
171
172
|
if (!res.ok) await throwHttpError(res);
|
|
172
173
|
const ct = res.headers.get('content-type') ?? '';
|
|
173
|
-
return throwIfApiError(ct.includes('application/json') ? await res.json() : await res.text());
|
|
174
|
+
return throwIfApiError(ct.includes('application/json') ? await res.json() : await res.text(), res.headers);
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
/** POST a JSON body and read the response as raw bytes (d42 query results). */
|
|
@@ -236,13 +237,15 @@ async function throwHttpError(res) {
|
|
|
236
237
|
* a JSON string when the handler returned text. Every response passes through here so a
|
|
237
238
|
* missing entity, a rejected save or an unknown function throws instead of printing as data.
|
|
238
239
|
*/
|
|
239
|
-
|
|
240
|
+
/** A user error answers HTTP 200 with the ApiError in the body and its status in the `api-error-code` header. */
|
|
241
|
+
function throwIfApiError(payload, headers) {
|
|
240
242
|
const parsed = typeof payload === 'string' ? tryParseJson(payload) : payload;
|
|
241
243
|
if (parsed?.['#type'] === 'ApiError') {
|
|
244
|
+
const code = headers?.get('api-error-code');
|
|
242
245
|
const err = {
|
|
243
246
|
error: parsed.message ?? 'Request failed',
|
|
244
247
|
source: 'Server',
|
|
245
|
-
errorCode: parsed.errorCode,
|
|
248
|
+
errorCode: parsed.errorCode ?? (code ? Number(code) : undefined),
|
|
246
249
|
stackTrace: parsed.stackTrace,
|
|
247
250
|
body: parsed
|
|
248
251
|
};
|
|
@@ -388,7 +391,7 @@ class InternalDataSource {
|
|
|
388
391
|
}
|
|
389
392
|
}
|
|
390
393
|
exports.InternalDataSource = InternalDataSource;
|
|
391
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
394
|
+
const UUID_RE = exports.UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
392
395
|
class NodeGroupsDataSource extends NodeHttpDataSource {
|
|
393
396
|
constructor(client) {
|
|
394
397
|
super(client, 'groups', '/groups');
|
|
@@ -936,6 +939,10 @@ function parseDomainAddress(s, opts = {}) {
|
|
|
936
939
|
if (opts.table === false && address.table) throw new Error(`'${str}' names a table; this command needs a schema`);
|
|
937
940
|
return address;
|
|
938
941
|
}
|
|
942
|
+
/** Every literal a `/domains/{schema}/{table}/{id}` route registers at the id position, the dot
|
|
943
|
+
* segments a proxy folds away, and the u2 draft sentinel — the Dart `DomainRowKey.reserved` list. */
|
|
944
|
+
const RESERVED_ROW_KEYS = ['access', 'aggregate', 'audit', 'batch', 'delete', 'facets', 'filters', 'query', 'update', 'version', 'watch', 'new', '.', '..'];
|
|
945
|
+
|
|
939
946
|
/**
|
|
940
947
|
* Client for entity-mapped domain tables (`/domains/...`, DomainsRouter). Rows are plain
|
|
941
948
|
* JSON objects; the server validates, permission-checks and audits every write. Errors
|
|
@@ -950,6 +957,13 @@ class NodeDomainsDataSource {
|
|
|
950
957
|
return `/domains/${encodeURIComponent(schema)}/${encodeURIComponent(table)}`;
|
|
951
958
|
}
|
|
952
959
|
|
|
960
|
+
/** The row-id segment as the Dart `DomainRowKey` spells it (grok_shared domain_manifest_rules.dart):
|
|
961
|
+
* escaped, then percent-encoded once more so an external key's own `%2C` survives the proxy. */
|
|
962
|
+
row(schema, table, id) {
|
|
963
|
+
const escaped = RESERVED_ROW_KEYS.includes(id) || id.startsWith('~') ? `~${id}` : id;
|
|
964
|
+
return `${this.rows(schema, table)}/${encodeURIComponent(escaped)}`;
|
|
965
|
+
}
|
|
966
|
+
|
|
953
967
|
/** Registered schemas with their tables (`GET /domains/schemas`); [text] is a smart filter. */
|
|
954
968
|
schemas(text) {
|
|
955
969
|
return this.client.get(`/domains/schemas${buildQuery({
|
|
@@ -1001,7 +1015,7 @@ class NodeDomainsDataSource {
|
|
|
1001
1015
|
})}`);
|
|
1002
1016
|
}
|
|
1003
1017
|
rowAudit(schema, table, id) {
|
|
1004
|
-
return this.client.get(`${this.
|
|
1018
|
+
return this.client.get(`${this.row(schema, table, id)}/audit`);
|
|
1005
1019
|
}
|
|
1006
1020
|
grants(entityId) {
|
|
1007
1021
|
return this.client.get(`/domains/grants/${encodeURIComponent(entityId)}`);
|
|
@@ -1053,7 +1067,7 @@ class NodeDomainsDataSource {
|
|
|
1053
1067
|
/** One row, or null when it does not exist or is not visible (the server answers 404). */
|
|
1054
1068
|
async getRow(schema, table, id) {
|
|
1055
1069
|
try {
|
|
1056
|
-
return await this.client.get(
|
|
1070
|
+
return await this.client.get(this.row(schema, table, id));
|
|
1057
1071
|
} catch (err) {
|
|
1058
1072
|
if (err?.apiError?.errorCode === 404) return null;
|
|
1059
1073
|
throw err;
|
|
@@ -1073,10 +1087,10 @@ class NodeDomainsDataSource {
|
|
|
1073
1087
|
values
|
|
1074
1088
|
};
|
|
1075
1089
|
if (version !== undefined) body.version = version;
|
|
1076
|
-
return this.client.request('PATCH',
|
|
1090
|
+
return this.client.request('PATCH', this.row(schema, table, id), body);
|
|
1077
1091
|
}
|
|
1078
1092
|
deleteRow(schema, table, id) {
|
|
1079
|
-
return this.client.del(
|
|
1093
|
+
return this.client.del(this.row(schema, table, id));
|
|
1080
1094
|
}
|
|
1081
1095
|
|
|
1082
1096
|
/** Soft-deletes up to [limit] (≤1000) matching rows in one transaction; `{deleted, hasMore}`. */
|
|
@@ -1138,6 +1152,9 @@ class NodeDapi {
|
|
|
1138
1152
|
get reports() {
|
|
1139
1153
|
return this.internal('/reports');
|
|
1140
1154
|
}
|
|
1155
|
+
get problems() {
|
|
1156
|
+
return this.internal('/problems');
|
|
1157
|
+
}
|
|
1141
1158
|
get files() {
|
|
1142
1159
|
return new NodeFilesDataSource(this.client);
|
|
1143
1160
|
}
|
|
@@ -16,6 +16,36 @@
|
|
|
16
16
|
"type": "string",
|
|
17
17
|
"description": "Manifest version (semver recommended). Recorded in the registry on deploy."
|
|
18
18
|
},
|
|
19
|
+
"storage": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"description": "Where the rows live. Absent = domain: the platform creates and owns the tables. external: the tables already exist in the database of the named platform connection and are read through it (no row security, soft delete, audit, relations, constraints, property schemas or extensions; read-only unless 'writable'); every table then declares its remote primary key as businessKey, and 'table'/'column' carry the remote names when they differ from the declared ones.",
|
|
22
|
+
"required": ["kind"],
|
|
23
|
+
"additionalProperties": false,
|
|
24
|
+
"properties": {
|
|
25
|
+
"kind": {"enum": ["domain", "external"]},
|
|
26
|
+
"connection": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"minLength": 1,
|
|
29
|
+
"description": "external: the qualified name of the platform connection to read through ('Package:Connection'); required."
|
|
30
|
+
},
|
|
31
|
+
"schema": {
|
|
32
|
+
"$ref": "#/definitions/remoteIdentifier",
|
|
33
|
+
"description": "external: the schema inside the remote database (e.g. 'public', 'dbo'); required."
|
|
34
|
+
},
|
|
35
|
+
"catalog": {
|
|
36
|
+
"$ref": "#/definitions/remoteIdentifier",
|
|
37
|
+
"description": "external: the catalog/database, for targets that have one."
|
|
38
|
+
},
|
|
39
|
+
"writable": {
|
|
40
|
+
"type": "boolean",
|
|
41
|
+
"default": false,
|
|
42
|
+
"description": "external: rows may be inserted, patched and deleted through the connection (one remote transaction per request, keyed by businessKey; on ClickHouse, which commits every statement, a request is one multi-row INSERT and patch/delete/upsert are refused). The publisher must hold Query, AddRows, ChangeValues and RemoveRows on the connection; a table opts out with its own 'writable': false."
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"if": {"properties": {"kind": {"const": "external"}}},
|
|
46
|
+
"then": {"required": ["connection", "schema"]},
|
|
47
|
+
"else": {"properties": {"connection": false, "schema": false, "catalog": false, "writable": false}}
|
|
48
|
+
},
|
|
19
49
|
"extensible": {
|
|
20
50
|
"type": "object",
|
|
21
51
|
"description": "Opt-in for user extensions of this schema. Users holding the Extend permission on the schema entity may then add their own tables; per-table column extension is opted in with the table's own 'extensible' flag. User objects are retained across plugin upgrades and never appear in generated typed clients.",
|
|
@@ -64,11 +94,26 @@
|
|
|
64
94
|
{"not": {"type": "string", "pattern": "^x_"}}
|
|
65
95
|
]
|
|
66
96
|
},
|
|
97
|
+
"remoteIdentifier": {
|
|
98
|
+
"description": "A name inside the remote database, verbatim (spaces allowed, the connector quotes it); it may not contain brackets, quotes, backslashes or dots.",
|
|
99
|
+
"type": "string",
|
|
100
|
+
"minLength": 1,
|
|
101
|
+
"maxLength": 256,
|
|
102
|
+
"pattern": "^[^\\[\\]\"'`\\\\.]+$"
|
|
103
|
+
},
|
|
67
104
|
"table": {
|
|
68
105
|
"type": "object",
|
|
69
106
|
"required": ["columns"],
|
|
70
107
|
"additionalProperties": false,
|
|
71
108
|
"properties": {
|
|
109
|
+
"table": {
|
|
110
|
+
"$ref": "#/definitions/remoteIdentifier",
|
|
111
|
+
"description": "External storage only: the remote table's name when it differs from the declared one (e.g. 'Order Details')."
|
|
112
|
+
},
|
|
113
|
+
"writable": {
|
|
114
|
+
"type": "boolean",
|
|
115
|
+
"description": "Writable external storage only: false keeps this table read-only while the rest of the binding accepts writes."
|
|
116
|
+
},
|
|
72
117
|
"securityMode": {
|
|
73
118
|
"enum": ["table", "master", "row"],
|
|
74
119
|
"default": "table",
|
|
@@ -263,7 +308,11 @@
|
|
|
263
308
|
"properties": {
|
|
264
309
|
"type": {
|
|
265
310
|
"enum": ["string", "int", "float", "bool", "datetime", "string_list", "ref", "file", "json", "user", "group"],
|
|
266
|
-
"description": "Property type; ref = FK to another table in this manifest; user/group = aliases of ref Core.users / ref Core.groups (semantic type User / Group); file = a file://<connection>/<path> string into platform file storage (semantic type File); json = a jsonb-backed opaque object (filters, sorts, facets and aggregates refuse it)."
|
|
311
|
+
"description": "Property type; ref = FK to another table in this manifest; user/group = aliases of ref Core.users / ref Core.groups (semantic type User / Group); file = a file://<connection>/<path> string into platform file storage (semantic type File); json = a jsonb-backed opaque object (filters, sorts, facets and aggregates refuse it). An external storage admits string, int, float, bool, datetime and same-manifest ref only."
|
|
312
|
+
},
|
|
313
|
+
"column": {
|
|
314
|
+
"$ref": "#/definitions/remoteIdentifier",
|
|
315
|
+
"description": "External storage only: the remote column's name when it differs from the declared one (e.g. 'OrderID')."
|
|
267
316
|
},
|
|
268
317
|
"required": {"type": "boolean", "default": false},
|
|
269
318
|
"unique": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {domains} from '@datagrok-libraries/
|
|
1
|
+
import {domains} from '@datagrok-libraries/ems/src/dg';
|
|
2
2
|
|
|
3
3
|
// the skins of everything the u2 domain stack renders
|
|
4
|
-
import '@datagrok-libraries/
|
|
4
|
+
import '@datagrok-libraries/ems/src/dg/styles.js';
|
package/package.json
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
"type": "git",
|
|
5
5
|
"url": "https://github.com/datagrok-ai/public.git"
|
|
6
6
|
},
|
|
7
|
-
"version": "6.7.
|
|
7
|
+
"version": "6.7.9",
|
|
8
8
|
"description": "Utility to upload and publish packages to Datagrok",
|
|
9
9
|
"homepage": "https://github.com/datagrok-ai/public/tree/master/tools#readme",
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=22.12.0"
|
|
12
|
+
},
|
|
10
13
|
"dependencies": {
|
|
11
14
|
"@babel/parser": "^7.29.7",
|
|
12
15
|
"@babel/runtime": "^7.29.7",
|
|
@@ -24,7 +27,7 @@
|
|
|
24
27
|
"minimist": "^1.2.8",
|
|
25
28
|
"node-fetch": "^2.7.0",
|
|
26
29
|
"papaparse": "^5.5.3",
|
|
27
|
-
"puppeteer": "^
|
|
30
|
+
"puppeteer": "^25.14.0",
|
|
28
31
|
"ts-morph": "^28.0.0",
|
|
29
32
|
"@babel/generator": "^7.29.0",
|
|
30
33
|
"datagrok-api": "^1.27.12"
|
|
@@ -90,7 +93,7 @@
|
|
|
90
93
|
"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",
|
|
91
94
|
"debug-source-map": "node build.js --source-maps",
|
|
92
95
|
"test": "vitest run --project unit",
|
|
93
|
-
"test:server": "vitest run --project unit bin/__tests__/node-dapi bin/__tests__/server bin/__tests__/migrate bin/__tests__/keypair",
|
|
96
|
+
"test:server": "vitest run --project unit bin/__tests__/node-dapi bin/__tests__/server bin/__tests__/migrate bin/__tests__/keypair bin/__tests__/grok-cli",
|
|
94
97
|
"test:watch": "vitest --project unit",
|
|
95
98
|
"test:integration": "vitest run --project integration",
|
|
96
99
|
"test:all": "vitest run"
|