@port-labs/jq-node-bindings 0.0.11 → 0.0.13
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/index.d.ts +6 -0
- package/lib/index.js +3 -1
- package/lib/jq.js +12 -2
- package/lib/template.js +13 -0
- package/package.json +2 -2
- package/src/binding.cc +3 -3
- package/test/santiy.test.js +5 -5
- package/test/template.test.js +19 -7
package/index.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
declare module '@port-labs/jq-node-bindings' {
|
|
2
2
|
type ExecOptions = { enableEnv?: boolean, throwOnError?: boolean };
|
|
3
3
|
|
|
4
|
+
export class JqExecError extends Error {
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class JqExecCompileError extends Error {
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
export function exec(json: object, input: string, options?: ExecOptions): object | Array<any> | string | number | boolean | null;
|
|
5
11
|
|
|
6
12
|
export function renderRecursively(json: object, input: object | Array<any> | string | number | boolean | null, execOptions?: ExecOptions): object | Array<any> | string | number | boolean | null;
|
package/lib/index.js
CHANGED
package/lib/jq.js
CHANGED
|
@@ -6,6 +6,14 @@ const formatFilter = (filter, {enableEnv = false} = {}) => {
|
|
|
6
6
|
// Conditionally enable access to env
|
|
7
7
|
return enableEnv ? formattedFilter : `def env: {}; {} as $ENV | ${formattedFilter}`;
|
|
8
8
|
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class JqExecError extends Error {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class JqExecCompileError extends JqExecError {
|
|
15
|
+
}
|
|
16
|
+
|
|
9
17
|
const exec = (object, filter, {enableEnv = false, throwOnError = false} = {}) => {
|
|
10
18
|
try {
|
|
11
19
|
const data = nativeJq.exec(JSON.stringify(object), formatFilter(filter, {enableEnv}))
|
|
@@ -13,12 +21,14 @@ const exec = (object, filter, {enableEnv = false, throwOnError = false} = {}) =>
|
|
|
13
21
|
return data?.value;
|
|
14
22
|
} catch (err) {
|
|
15
23
|
if (throwOnError) {
|
|
16
|
-
throw err;
|
|
24
|
+
throw new (err?.message?.startsWith('jq: compile error') ? JqExecCompileError : JqExecError)(err.message);
|
|
17
25
|
}
|
|
18
26
|
return null
|
|
19
27
|
}
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
module.exports = {
|
|
23
|
-
exec
|
|
31
|
+
exec,
|
|
32
|
+
JqExecError,
|
|
33
|
+
JqExecCompileError
|
|
24
34
|
};
|
package/lib/template.js
CHANGED
|
@@ -93,6 +93,19 @@ const renderRecursively = (inputJson, template, execOptions = {}) => {
|
|
|
93
93
|
if (typeof template === 'object' && template !== null) {
|
|
94
94
|
return Object.fromEntries(
|
|
95
95
|
Object.entries(template).flatMap(([key, value]) => {
|
|
96
|
+
const SPREAD_KEYWORD = "spreadValue";
|
|
97
|
+
const keywordMatcher = `^\\{\\{\\s*${SPREAD_KEYWORD}\\(\\s*\\)\\s*\\}\\}$`; // matches {{ <Keyword>() }} with white spaces where you'd expect them
|
|
98
|
+
|
|
99
|
+
if (key.trim().match(keywordMatcher)) {
|
|
100
|
+
const evaluatedValue = renderRecursively(inputJson, value, execOptions);
|
|
101
|
+
if (typeof evaluatedValue !== "object") {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Evaluated value should be an object if the key is ${key}. Original value: ${value}, evaluated to: ${JSON.stringify(evaluatedValue)}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return Object.entries(evaluatedValue);
|
|
107
|
+
}
|
|
108
|
+
|
|
96
109
|
const evaluatedKey = renderRecursively(inputJson, key, execOptions);
|
|
97
110
|
if (!['undefined', 'string'].includes(typeof evaluatedKey) && evaluatedKey !== null) {
|
|
98
111
|
throw new Error(
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@port-labs/jq-node-bindings",
|
|
3
|
-
"version": "v0.0.
|
|
3
|
+
"version": "v0.0.13",
|
|
4
4
|
"description": "Node.js bindings for JQ",
|
|
5
|
-
"jq-node-bindings": "0.0.
|
|
5
|
+
"jq-node-bindings": "0.0.13",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"configure": "node-gyp configure",
|
package/src/binding.cc
CHANGED
|
@@ -63,7 +63,7 @@ void jv_object_to_v8(std::string key, jv actual, v8::Local<v8::Object> ret) {
|
|
|
63
63
|
snprintf(err, sizeof(err), "jq: error: %s", jv_string_value(msg));
|
|
64
64
|
jv_free(msg);
|
|
65
65
|
jv_free(actual);
|
|
66
|
-
Nan::
|
|
66
|
+
Nan::ThrowError(err);
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
jv_free(msg);
|
|
@@ -127,7 +127,7 @@ void throw_err_cb(void *data, jv msg) {
|
|
|
127
127
|
if (jv_get_kind(msg) != JV_KIND_STRING)
|
|
128
128
|
msg = jv_dump_string(msg, JV_PRINT_INVALID);
|
|
129
129
|
if (!strncmp(jv_string_value(msg), "jq: error", sizeof("jq: error") - 1))
|
|
130
|
-
snprintf(err_data->buf, sizeof(err_data->buf), "%s", jv_string_value(msg));
|
|
130
|
+
snprintf(err_data->buf, sizeof(err_data->buf), "jq: compile error%s", jv_string_value(msg) + strlen("jq: error"));
|
|
131
131
|
if (strchr(err_data->buf, '\n'))
|
|
132
132
|
*(strchr(err_data->buf, '\n')) = '\0';
|
|
133
133
|
jv_free(msg);
|
|
@@ -143,7 +143,7 @@ void jq_exec(std::string json, std::string filter,const Nan::FunctionCallbackInf
|
|
|
143
143
|
jq = jq_init();
|
|
144
144
|
jq_set_error_cb(jq, throw_err_cb, &err_msg);
|
|
145
145
|
if (!jq_compile(jq, filter.c_str())) {
|
|
146
|
-
Nan::
|
|
146
|
+
Nan::ThrowError(err_msg.buf);
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
cache.put(filter, jq);
|
package/test/santiy.test.js
CHANGED
|
@@ -159,11 +159,11 @@ describe('jq', () => {
|
|
|
159
159
|
})
|
|
160
160
|
|
|
161
161
|
it('test throw on error', () => {
|
|
162
|
-
expect(() => { jq.exec({}, 'foo', {throwOnError: true}) }).toThrow("jq: error: foo/0 is not defined at <top-level>, line 1:");
|
|
163
|
-
expect(() => { jq.exec({}, '1/0', {throwOnError: true}) }).toThrow("jq: error: Division by zero? at <top-level>, line 1:");
|
|
164
|
-
expect(() => { jq.exec({}, '{', {throwOnError: true}) }).toThrow("jq: error: syntax error, unexpected $end (Unix shell quoting issues?) at <top-level>, line 1:");
|
|
165
|
-
expect(() => { jq.exec({}, '{(0):1}', {throwOnError: true}) }).toThrow("jq: error: Cannot use number (0) as object key at <top-level>, line 1:");
|
|
166
|
-
expect(() => { jq.exec({}, 'if true then 1 else 0', {throwOnError: true}) }).toThrow("jq: error: Possibly unterminated 'if' statement at <top-level>, line 1:");
|
|
162
|
+
expect(() => { jq.exec({}, 'foo', {throwOnError: true}) }).toThrow("jq: compile error: foo/0 is not defined at <top-level>, line 1:");
|
|
163
|
+
expect(() => { jq.exec({}, '1/0', {throwOnError: true}) }).toThrow("jq: compile error: Division by zero? at <top-level>, line 1:");
|
|
164
|
+
expect(() => { jq.exec({}, '{', {throwOnError: true}) }).toThrow("jq: compile error: syntax error, unexpected $end (Unix shell quoting issues?) at <top-level>, line 1:");
|
|
165
|
+
expect(() => { jq.exec({}, '{(0):1}', {throwOnError: true}) }).toThrow("jq: compile error: Cannot use number (0) as object key at <top-level>, line 1:");
|
|
166
|
+
expect(() => { jq.exec({}, 'if true then 1 else 0', {throwOnError: true}) }).toThrow("jq: compile error: Possibly unterminated 'if' statement at <top-level>, line 1:");
|
|
167
167
|
expect(() => { jq.exec({}, 'null | map(.+1)', {throwOnError: true}) }).toThrow("jq: error: Cannot iterate over null (null)");
|
|
168
168
|
expect(() => { jq.exec({foo: "bar"}, '.foo + 1', {throwOnError: true}) }).toThrow("jq: error: string (\"bar\") and number (1) cannot be added");
|
|
169
169
|
})
|
package/test/template.test.js
CHANGED
|
@@ -125,6 +125,14 @@ describe('template', () => {
|
|
|
125
125
|
expect(render({'{{""}}': 'bar'})).toEqual({});
|
|
126
126
|
expect(render({'{{\'\'}}': 'bar'})).toEqual({});
|
|
127
127
|
});
|
|
128
|
+
it('testing spread key', () => {
|
|
129
|
+
const json = { foo: "bar" };
|
|
130
|
+
const render = (input) => jq.renderRecursively(json, input);
|
|
131
|
+
|
|
132
|
+
expect(render({ "{{spreadValue()}}": { foo: "bar" } })).toEqual({foo: "bar"});
|
|
133
|
+
expect(render({ " {{ spreadValue( ) }} ": { foo: "bar" } })).toEqual({foo: "bar"});
|
|
134
|
+
expect(render({ "{{spreadValue()}}": "{{ . }}" })).toEqual({ foo: "bar" });
|
|
135
|
+
});
|
|
128
136
|
it('recursive templates should work', () => {
|
|
129
137
|
const json = { foo: 'bar', bar: 'foo' };
|
|
130
138
|
const render = (input) => jq.renderRecursively(json, input);
|
|
@@ -156,15 +164,19 @@ describe('template', () => {
|
|
|
156
164
|
expect(jq.renderRecursively({}, '{{env}}')).toEqual({});
|
|
157
165
|
})
|
|
158
166
|
it('test throw on error', () => {
|
|
159
|
-
expect(() => { jq.renderRecursively({}, '{{foo}}', {throwOnError: true}) }).toThrow("jq: error: foo/0 is not defined at <top-level>, line 1:");
|
|
160
|
-
expect(() => { jq.renderRecursively({}, '{{1/0}}', {throwOnError: true}) }).toThrow("jq: error: Division by zero? at <top-level>, line 1:");
|
|
161
|
-
expect(() => { jq.renderRecursively({}, '{{{}}', {throwOnError: true}) }).toThrow("jq: error: syntax error, unexpected $end (Unix shell quoting issues?) at <top-level>, line 1:");
|
|
162
|
-
expect(() => { jq.renderRecursively({}, '{{ {(0):1} }}', {throwOnError: true}) }).toThrow("jq: error: Cannot use number (0) as object key at <top-level>, line 1:");
|
|
163
|
-
expect(() => { jq.renderRecursively({}, '{{if true then 1 else 0}}', {throwOnError: true}) }).toThrow("jq: error: Possibly unterminated 'if' statement at <top-level>, line 1:");
|
|
167
|
+
expect(() => { jq.renderRecursively({}, '{{foo}}', {throwOnError: true}) }).toThrow("jq: compile error: foo/0 is not defined at <top-level>, line 1:");
|
|
168
|
+
expect(() => { jq.renderRecursively({}, '{{1/0}}', {throwOnError: true}) }).toThrow("jq: compile error: Division by zero? at <top-level>, line 1:");
|
|
169
|
+
expect(() => { jq.renderRecursively({}, '{{{}}', {throwOnError: true}) }).toThrow("jq: compile error: syntax error, unexpected $end (Unix shell quoting issues?) at <top-level>, line 1:");
|
|
170
|
+
expect(() => { jq.renderRecursively({}, '{{ {(0):1} }}', {throwOnError: true}) }).toThrow("jq: compile error: Cannot use number (0) as object key at <top-level>, line 1:");
|
|
171
|
+
expect(() => { jq.renderRecursively({}, '{{if true then 1 else 0}}', {throwOnError: true}) }).toThrow("jq: compile error: Possibly unterminated 'if' statement at <top-level>, line 1:");
|
|
164
172
|
expect(() => { jq.renderRecursively({}, '{{null | map(.+1)}}', {throwOnError: true}) }).toThrow("jq: error: Cannot iterate over null (null)");
|
|
165
173
|
expect(() => { jq.renderRecursively({foo: "bar"}, '{{.foo + 1}}', {throwOnError: true}) }).toThrow("jq: error: string (\"bar\") and number (1) cannot be added");
|
|
166
|
-
expect(() => { jq.renderRecursively({}, '{{foo}}/{{bar}}', {throwOnError: true}) }).toThrow("jq: error: foo/0 is not defined at <top-level>, line 1:");
|
|
167
|
-
expect(() => { jq.renderRecursively({}, '/{{foo}}/', {throwOnError: true}) }).toThrow("jq: error: foo/0 is not defined at <top-level>, line 1:");
|
|
174
|
+
expect(() => { jq.renderRecursively({}, '{{foo}}/{{bar}}', {throwOnError: true}) }).toThrow("jq: compile error: foo/0 is not defined at <top-level>, line 1:");
|
|
175
|
+
expect(() => { jq.renderRecursively({}, '/{{foo}}/', {throwOnError: true}) }).toThrow("jq: compile error: foo/0 is not defined at <top-level>, line 1:");
|
|
176
|
+
expect(() => { jq.renderRecursively({}, { "{{ spreadValue() }}": "str" }, { throwOnError: true }) })
|
|
177
|
+
.toThrow('Evaluated value should be an object if the key is {{ spreadValue() }}. Original value: str, evaluated to: "str"');
|
|
178
|
+
expect(() => { jq.renderRecursively({}, { "{{ spreadValue() }}": "{{ \"str\" }}" }, { throwOnError: true }) })
|
|
179
|
+
.toThrow('Evaluated value should be an object if the key is {{ spreadValue() }}. Original value: {{ \"str\" }}, evaluated to: "str"');
|
|
168
180
|
})
|
|
169
181
|
})
|
|
170
182
|
|