datagrok-tools 6.5.4 → 6.5.6

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.
@@ -1,171 +0,0 @@
1
- "use strict";
2
-
3
- var _vitest = require("vitest");
4
- var _serverOutput = require("../utils/server-output");
5
- (0, _vitest.describe)('cellStr', () => {
6
- (0, _vitest.it)('returns empty string for null', () => {
7
- (0, _vitest.expect)((0, _serverOutput.cellStr)(null)).toBe('');
8
- });
9
- (0, _vitest.it)('returns empty string for undefined', () => {
10
- (0, _vitest.expect)((0, _serverOutput.cellStr)(undefined)).toBe('');
11
- });
12
- (0, _vitest.it)('returns the name property for objects with a name', () => {
13
- (0, _vitest.expect)((0, _serverOutput.cellStr)({
14
- name: 'Alice',
15
- id: '1'
16
- })).toBe('Alice');
17
- });
18
- (0, _vitest.it)('falls back to id when name is absent', () => {
19
- (0, _vitest.expect)((0, _serverOutput.cellStr)({
20
- id: 'abc-123'
21
- })).toBe('abc-123');
22
- });
23
- (0, _vitest.it)('returns truncated JSON for objects with neither name nor id', () => {
24
- const result = (0, _serverOutput.cellStr)({
25
- foo: 'bar',
26
- baz: 42
27
- });
28
- (0, _vitest.expect)(result).toBe('{"foo":"bar","baz":42}');
29
- (0, _vitest.expect)(result.length).toBeLessThanOrEqual(40);
30
- });
31
- (0, _vitest.it)('truncates long JSON to 40 characters', () => {
32
- const obj = {
33
- key: 'a'.repeat(50)
34
- };
35
- (0, _vitest.expect)((0, _serverOutput.cellStr)(obj).length).toBe(40);
36
- });
37
- (0, _vitest.it)('converts a number to string', () => {
38
- (0, _vitest.expect)((0, _serverOutput.cellStr)(123)).toBe('123');
39
- });
40
- (0, _vitest.it)('converts a boolean to string', () => {
41
- (0, _vitest.expect)((0, _serverOutput.cellStr)(true)).toBe('true');
42
- (0, _vitest.expect)((0, _serverOutput.cellStr)(false)).toBe('false');
43
- });
44
- (0, _vitest.it)('returns a plain string as-is', () => {
45
- (0, _vitest.expect)((0, _serverOutput.cellStr)('hello')).toBe('hello');
46
- });
47
- });
48
- (0, _vitest.describe)('csvCell', () => {
49
- (0, _vitest.it)('returns the string unchanged when no special chars', () => {
50
- (0, _vitest.expect)((0, _serverOutput.csvCell)('hello')).toBe('hello');
51
- });
52
- (0, _vitest.it)('wraps in quotes when the value contains a comma', () => {
53
- (0, _vitest.expect)((0, _serverOutput.csvCell)('a,b')).toBe('"a,b"');
54
- });
55
- (0, _vitest.it)('wraps and escapes double quotes', () => {
56
- (0, _vitest.expect)((0, _serverOutput.csvCell)('say "hi"')).toBe('"say ""hi"""');
57
- });
58
- (0, _vitest.it)('wraps in quotes when the value contains a newline', () => {
59
- (0, _vitest.expect)((0, _serverOutput.csvCell)('line1\nline2')).toBe('"line1\nline2"');
60
- });
61
- (0, _vitest.it)('handles an empty string', () => {
62
- (0, _vitest.expect)((0, _serverOutput.csvCell)('')).toBe('');
63
- });
64
- });
65
- (0, _vitest.describe)('getKeys', () => {
66
- (0, _vitest.it)('returns an empty array for an empty input', () => {
67
- (0, _vitest.expect)((0, _serverOutput.getKeys)([])).toEqual([]);
68
- });
69
- (0, _vitest.it)('returns keys from a single object', () => {
70
- (0, _vitest.expect)((0, _serverOutput.getKeys)([{
71
- a: 1,
72
- b: 2
73
- }])).toEqual(['a', 'b']);
74
- });
75
- (0, _vitest.it)('unions keys across multiple objects', () => {
76
- (0, _vitest.expect)((0, _serverOutput.getKeys)([{
77
- a: 1
78
- }, {
79
- b: 2
80
- }, {
81
- a: 3,
82
- c: 4
83
- }])).toEqual(['a', 'b', 'c']);
84
- });
85
- (0, _vitest.it)('deduplicates keys', () => {
86
- (0, _vitest.expect)((0, _serverOutput.getKeys)([{
87
- a: 1,
88
- b: 2
89
- }, {
90
- a: 3,
91
- b: 4
92
- }])).toEqual(['a', 'b']);
93
- });
94
- (0, _vitest.it)('preserves insertion order (first appearance wins)', () => {
95
- (0, _vitest.expect)((0, _serverOutput.getKeys)([{
96
- b: 1,
97
- a: 2
98
- }, {
99
- a: 3,
100
- c: 4
101
- }])).toEqual(['b', 'a', 'c']);
102
- });
103
- (0, _vitest.it)('caps the result at 12 keys', () => {
104
- const row = Object.fromEntries(Array.from({
105
- length: 15
106
- }, (_, i) => [`k${i}`, i]));
107
- const keys = (0, _serverOutput.getKeys)([row]);
108
- (0, _vitest.expect)(keys).toHaveLength(12);
109
- (0, _vitest.expect)(keys).toEqual(Array.from({
110
- length: 12
111
- }, (_, i) => `k${i}`));
112
- });
113
- });
114
- (0, _vitest.describe)('printBatchOutput', () => {
115
- let logLines;
116
- let errLines;
117
- (0, _vitest.beforeEach)(() => {
118
- logLines = [];
119
- errLines = [];
120
- _vitest.vi.spyOn(console, 'log').mockImplementation((...args) => logLines.push(args.join(' ')));
121
- _vitest.vi.spyOn(process.stderr, 'write').mockImplementation(chunk => {
122
- errLines.push(String(chunk));
123
- return true;
124
- });
125
- });
126
- (0, _vitest.afterEach)(() => {
127
- _vitest.vi.restoreAllMocks();
128
- });
129
- (0, _vitest.it)('json: output is valid JSON equal to the full response', () => {
130
- const resp = {
131
- summary: {
132
- total: 1,
133
- succeeded: 1,
134
- failed: 0,
135
- partial: 0,
136
- skipped: 0
137
- },
138
- results: [{
139
- id: 'op0',
140
- action: 'users.delete',
141
- status: 'success'
142
- }]
143
- };
144
- (0, _serverOutput.printBatchOutput)(resp, 'json');
145
- (0, _vitest.expect)(JSON.parse(logLines[0])).toEqual(resp);
146
- });
147
- (0, _vitest.it)('table: failed operations are written to stderr as structured JSON', () => {
148
- const resp = {
149
- summary: {
150
- total: 1,
151
- succeeded: 0,
152
- failed: 1,
153
- partial: 0,
154
- skipped: 0
155
- },
156
- results: [{
157
- id: 'op0',
158
- action: 'users.delete',
159
- status: 'error',
160
- error: {
161
- error: 'Not found'
162
- }
163
- }]
164
- };
165
- (0, _serverOutput.printBatchOutput)(resp, 'table');
166
- (0, _vitest.expect)(errLines.length).toBeGreaterThan(0);
167
- const parsed = JSON.parse(errLines[0]);
168
- (0, _vitest.expect)(parsed[0].id).toBe('op0');
169
- (0, _vitest.expect)(parsed[0].error.error).toBe('Not found');
170
- });
171
- });
@@ -1,133 +0,0 @@
1
- import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest';
2
- import {cellStr, csvCell, getKeys, printBatchOutput} from '../utils/server-output';
3
- import type {BatchResponse} from '../utils/node-dapi';
4
-
5
- describe('cellStr', () => {
6
- it('returns empty string for null', () => {
7
- expect(cellStr(null)).toBe('');
8
- });
9
-
10
- it('returns empty string for undefined', () => {
11
- expect(cellStr(undefined)).toBe('');
12
- });
13
-
14
- it('returns the name property for objects with a name', () => {
15
- expect(cellStr({name: 'Alice', id: '1'})).toBe('Alice');
16
- });
17
-
18
- it('falls back to id when name is absent', () => {
19
- expect(cellStr({id: 'abc-123'})).toBe('abc-123');
20
- });
21
-
22
- it('returns truncated JSON for objects with neither name nor id', () => {
23
- const result = cellStr({foo: 'bar', baz: 42});
24
- expect(result).toBe('{"foo":"bar","baz":42}');
25
- expect(result.length).toBeLessThanOrEqual(40);
26
- });
27
-
28
- it('truncates long JSON to 40 characters', () => {
29
- const obj = {key: 'a'.repeat(50)};
30
- expect(cellStr(obj).length).toBe(40);
31
- });
32
-
33
- it('converts a number to string', () => {
34
- expect(cellStr(123)).toBe('123');
35
- });
36
-
37
- it('converts a boolean to string', () => {
38
- expect(cellStr(true)).toBe('true');
39
- expect(cellStr(false)).toBe('false');
40
- });
41
-
42
- it('returns a plain string as-is', () => {
43
- expect(cellStr('hello')).toBe('hello');
44
- });
45
- });
46
-
47
- describe('csvCell', () => {
48
- it('returns the string unchanged when no special chars', () => {
49
- expect(csvCell('hello')).toBe('hello');
50
- });
51
-
52
- it('wraps in quotes when the value contains a comma', () => {
53
- expect(csvCell('a,b')).toBe('"a,b"');
54
- });
55
-
56
- it('wraps and escapes double quotes', () => {
57
- expect(csvCell('say "hi"')).toBe('"say ""hi"""');
58
- });
59
-
60
- it('wraps in quotes when the value contains a newline', () => {
61
- expect(csvCell('line1\nline2')).toBe('"line1\nline2"');
62
- });
63
-
64
- it('handles an empty string', () => {
65
- expect(csvCell('')).toBe('');
66
- });
67
- });
68
-
69
- describe('getKeys', () => {
70
- it('returns an empty array for an empty input', () => {
71
- expect(getKeys([])).toEqual([]);
72
- });
73
-
74
- it('returns keys from a single object', () => {
75
- expect(getKeys([{a: 1, b: 2}])).toEqual(['a', 'b']);
76
- });
77
-
78
- it('unions keys across multiple objects', () => {
79
- expect(getKeys([{a: 1}, {b: 2}, {a: 3, c: 4}])).toEqual(['a', 'b', 'c']);
80
- });
81
-
82
- it('deduplicates keys', () => {
83
- expect(getKeys([{a: 1, b: 2}, {a: 3, b: 4}])).toEqual(['a', 'b']);
84
- });
85
-
86
- it('preserves insertion order (first appearance wins)', () => {
87
- expect(getKeys([{b: 1, a: 2}, {a: 3, c: 4}])).toEqual(['b', 'a', 'c']);
88
- });
89
-
90
- it('caps the result at 12 keys', () => {
91
- const row = Object.fromEntries(Array.from({length: 15}, (_, i) => [`k${i}`, i]));
92
- const keys = getKeys([row]);
93
- expect(keys).toHaveLength(12);
94
- expect(keys).toEqual(Array.from({length: 12}, (_, i) => `k${i}`));
95
- });
96
- });
97
-
98
- describe('printBatchOutput', () => {
99
- let logLines: string[];
100
- let errLines: string[];
101
-
102
- beforeEach(() => {
103
- logLines = [];
104
- errLines = [];
105
- vi.spyOn(console, 'log').mockImplementation((...args) => logLines.push(args.join(' ')));
106
- vi.spyOn(process.stderr, 'write').mockImplementation((chunk) => { errLines.push(String(chunk)); return true; });
107
- });
108
-
109
- afterEach(() => {
110
- vi.restoreAllMocks();
111
- });
112
-
113
- it('json: output is valid JSON equal to the full response', () => {
114
- const resp: BatchResponse = {
115
- summary: {total: 1, succeeded: 1, failed: 0, partial: 0, skipped: 0},
116
- results: [{id: 'op0', action: 'users.delete', status: 'success'}],
117
- };
118
- printBatchOutput(resp, 'json');
119
- expect(JSON.parse(logLines[0])).toEqual(resp);
120
- });
121
-
122
- it('table: failed operations are written to stderr as structured JSON', () => {
123
- const resp: BatchResponse = {
124
- summary: {total: 1, succeeded: 0, failed: 1, partial: 0, skipped: 0},
125
- results: [{id: 'op0', action: 'users.delete', status: 'error', error: {error: 'Not found'}}],
126
- };
127
- printBatchOutput(resp, 'table');
128
- expect(errLines.length).toBeGreaterThan(0);
129
- const parsed = JSON.parse(errLines[0]);
130
- expect(parsed[0].id).toBe('op0');
131
- expect(parsed[0].error.error).toBe('Not found');
132
- });
133
- });
@@ -1,277 +0,0 @@
1
- "use strict";
2
-
3
- var _vitest = require("vitest");
4
- var os = _interopRequireWildcard(require("os"));
5
- var fs = _interopRequireWildcard(require("fs"));
6
- var path = _interopRequireWildcard(require("path"));
7
- var _server = require("../commands/server");
8
- 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); }
9
- (0, _vitest.describe)('parseFuncCall', () => {
10
- (0, _vitest.it)('parses a single string argument', () => {
11
- (0, _vitest.expect)((0, _server.parseFuncCall)('Chem:smilesToMw("ccc")')).toEqual({
12
- name: 'Chem:smilesToMw',
13
- params: {
14
- '0': 'ccc'
15
- }
16
- });
17
- });
18
- (0, _vitest.it)('parses a single-quoted string argument', () => {
19
- (0, _vitest.expect)((0, _server.parseFuncCall)("Pkg:fn('hello')")).toEqual({
20
- name: 'Pkg:fn',
21
- params: {
22
- '0': 'hello'
23
- }
24
- });
25
- });
26
- (0, _vitest.it)('parses a numeric argument', () => {
27
- (0, _vitest.expect)((0, _server.parseFuncCall)('Pkg:fn(42)')).toEqual({
28
- name: 'Pkg:fn',
29
- params: {
30
- '0': 42
31
- }
32
- });
33
- });
34
- (0, _vitest.it)('parses multiple positional arguments', () => {
35
- (0, _vitest.expect)((0, _server.parseFuncCall)('Pkg:fn(1, "hello", 3.14)')).toEqual({
36
- name: 'Pkg:fn',
37
- params: {
38
- '0': 1,
39
- '1': 'hello',
40
- '2': 3.14
41
- }
42
- });
43
- });
44
- (0, _vitest.it)('parses an object argument with quoted keys', () => {
45
- (0, _vitest.expect)((0, _server.parseFuncCall)('Pkg:fn({"a":5,"b":22})')).toEqual({
46
- name: 'Pkg:fn',
47
- params: {
48
- a: 5,
49
- b: 22
50
- }
51
- });
52
- });
53
- (0, _vitest.it)('parses an object argument with unquoted keys', () => {
54
- (0, _vitest.expect)((0, _server.parseFuncCall)('Pkg:fn({a:5,b:22})')).toEqual({
55
- name: 'Pkg:fn',
56
- params: {
57
- a: 5,
58
- b: 22
59
- }
60
- });
61
- });
62
- (0, _vitest.it)('parses an object argument with a boolean value', () => {
63
- (0, _vitest.expect)((0, _server.parseFuncCall)('Pkg:fn({unquoted:true})')).toEqual({
64
- name: 'Pkg:fn',
65
- params: {
66
- unquoted: true
67
- }
68
- });
69
- });
70
- (0, _vitest.it)('returns empty params for a no-argument call', () => {
71
- (0, _vitest.expect)((0, _server.parseFuncCall)('Pkg:fn()')).toEqual({
72
- name: 'Pkg:fn',
73
- params: {}
74
- });
75
- });
76
- (0, _vitest.it)('returns empty params when there are no parentheses', () => {
77
- (0, _vitest.expect)((0, _server.parseFuncCall)('Pkg:fn')).toEqual({
78
- name: 'Pkg:fn',
79
- params: {}
80
- });
81
- });
82
- (0, _vitest.it)('handles a package-less function name', () => {
83
- (0, _vitest.expect)((0, _server.parseFuncCall)('myFunc("arg")')).toEqual({
84
- name: 'myFunc',
85
- params: {
86
- '0': 'arg'
87
- }
88
- });
89
- });
90
- });
91
- (0, _vitest.describe)('buildInlineManifest', () => {
92
- (0, _vitest.it)('sets action to entity.verb', () => {
93
- const result = (0, _server.buildInlineManifest)('users', 'delete', ['abc']);
94
- (0, _vitest.expect)(result.operations[0].action).toBe('users.delete');
95
- });
96
- (0, _vitest.it)('maps string args to {id} param for non-file entities', () => {
97
- const result = (0, _server.buildInlineManifest)('users', 'delete', ['id1', 'id2']);
98
- (0, _vitest.expect)(result.operations).toEqual([{
99
- id: 'op0',
100
- action: 'users.delete',
101
- params: {
102
- id: 'id1'
103
- }
104
- }, {
105
- id: 'op1',
106
- action: 'users.delete',
107
- params: {
108
- id: 'id2'
109
- }
110
- }]);
111
- });
112
- (0, _vitest.it)('maps string args to {path} param for the files entity', () => {
113
- const result = (0, _server.buildInlineManifest)('files', 'delete', ['System:AppData/a.txt', 'System:AppData/b.txt']);
114
- (0, _vitest.expect)(result.operations).toEqual([{
115
- id: 'op0',
116
- action: 'files.delete',
117
- params: {
118
- path: 'System:AppData/a.txt'
119
- }
120
- }, {
121
- id: 'op1',
122
- action: 'files.delete',
123
- params: {
124
- path: 'System:AppData/b.txt'
125
- }
126
- }]);
127
- });
128
- (0, _vitest.it)('passes object array args through as params directly', () => {
129
- const objs = [{
130
- name: 'Alice',
131
- email: 'a@x.com'
132
- }, {
133
- name: 'Bob',
134
- email: 'b@x.com'
135
- }];
136
- const result = (0, _server.buildInlineManifest)('users', 'create', objs);
137
- (0, _vitest.expect)(result.operations).toEqual([{
138
- id: 'op0',
139
- action: 'users.create',
140
- params: {
141
- name: 'Alice',
142
- email: 'a@x.com'
143
- }
144
- }, {
145
- id: 'op1',
146
- action: 'users.create',
147
- params: {
148
- name: 'Bob',
149
- email: 'b@x.com'
150
- }
151
- }]);
152
- });
153
- (0, _vitest.it)('assigns sequential op ids starting at op0', () => {
154
- const result = (0, _server.buildInlineManifest)('groups', 'delete', ['x', 'y', 'z']);
155
- (0, _vitest.expect)(result.operations.map(o => o.id)).toEqual(['op0', 'op1', 'op2']);
156
- });
157
- (0, _vitest.it)('returns a single operation for a single arg', () => {
158
- const result = (0, _server.buildInlineManifest)('connections', 'delete', ['conn-id']);
159
- (0, _vitest.expect)(result.operations).toHaveLength(1);
160
- });
161
- });
162
- (0, _vitest.describe)('resolveManifestSources', () => {
163
- let tmpFile;
164
- (0, _vitest.beforeEach)(() => {
165
- tmpFile = path.join(os.tmpdir(), `grok-batch-test-${Date.now()}.bin`);
166
- });
167
- (0, _vitest.afterEach)(() => {
168
- if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile);
169
- });
170
- (0, _vitest.it)('passes through a manifest with no files.put operations unchanged', () => {
171
- const manifest = {
172
- operations: [{
173
- id: 'op0',
174
- action: 'users.delete',
175
- params: {
176
- id: 'abc'
177
- }
178
- }]
179
- };
180
- (0, _vitest.expect)((0, _server.resolveManifestSources)(manifest)).toEqual(manifest);
181
- });
182
- (0, _vitest.it)('passes through a files.put operation that has no source field', () => {
183
- const manifest = {
184
- operations: [{
185
- id: 'op0',
186
- action: 'files.put',
187
- params: {
188
- path: 'System:AppData/f.txt',
189
- content: 'aGk='
190
- }
191
- }]
192
- };
193
- (0, _vitest.expect)((0, _server.resolveManifestSources)(manifest)).toEqual(manifest);
194
- });
195
- (0, _vitest.it)('reads the source file, base64-encodes its contents, and removes the source key', () => {
196
- const data = 'hello world';
197
- fs.writeFileSync(tmpFile, data);
198
- const manifest = {
199
- operations: [{
200
- id: 'op0',
201
- action: 'files.put',
202
- params: {
203
- path: 'System:AppData/f.txt',
204
- source: tmpFile
205
- }
206
- }]
207
- };
208
- const result = (0, _server.resolveManifestSources)(manifest);
209
- const params = result.operations[0].params;
210
- (0, _vitest.expect)(params.content).toBe(Buffer.from(data).toString('base64'));
211
- (0, _vitest.expect)(params.source).toBeUndefined();
212
- });
213
- (0, _vitest.it)('preserves other params alongside the injected content', () => {
214
- fs.writeFileSync(tmpFile, 'data');
215
- const manifest = {
216
- operations: [{
217
- id: 'op0',
218
- action: 'files.put',
219
- params: {
220
- path: 'System:AppData/f.txt',
221
- source: tmpFile,
222
- extra: 'val'
223
- }
224
- }]
225
- };
226
- const result = (0, _server.resolveManifestSources)(manifest);
227
- const params = result.operations[0].params;
228
- (0, _vitest.expect)(params.path).toBe('System:AppData/f.txt');
229
- (0, _vitest.expect)(params.extra).toBe('val');
230
- });
231
- (0, _vitest.it)('leaves non-files.put operations untouched in a mixed manifest', () => {
232
- fs.writeFileSync(tmpFile, 'x');
233
- const manifest = {
234
- operations: [{
235
- id: 'op0',
236
- action: 'users.delete',
237
- params: {
238
- id: 'u1'
239
- }
240
- }, {
241
- id: 'op1',
242
- action: 'files.put',
243
- params: {
244
- path: 'System:AppData/f.txt',
245
- source: tmpFile
246
- }
247
- }]
248
- };
249
- const result = (0, _server.resolveManifestSources)(manifest);
250
- (0, _vitest.expect)(result.operations[0]).toEqual({
251
- id: 'op0',
252
- action: 'users.delete',
253
- params: {
254
- id: 'u1'
255
- }
256
- });
257
- (0, _vitest.expect)(result.operations[1].params.source).toBeUndefined();
258
- (0, _vitest.expect)(result.operations[1].params.content).toBeDefined();
259
- });
260
- (0, _vitest.it)('correctly encodes binary file contents', () => {
261
- const bytes = Buffer.from([0x00, 0xff, 0x10, 0xab]);
262
- fs.writeFileSync(tmpFile, bytes);
263
- const manifest = {
264
- operations: [{
265
- id: 'op0',
266
- action: 'files.put',
267
- params: {
268
- path: 'System:AppData/f.bin',
269
- source: tmpFile
270
- }
271
- }]
272
- };
273
- const result = (0, _server.resolveManifestSources)(manifest);
274
- const decoded = Buffer.from(result.operations[0].params.content, 'base64');
275
- (0, _vitest.expect)(decoded).toEqual(bytes);
276
- });
277
- });