kuzzle 2.17.1 → 2.17.4
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/lib/api/controllers/serverController.js +0 -1
- package/lib/api/funnel.js +4 -0
- package/lib/api/request/requestResponse.js +6 -1
- package/lib/core/backend/backendErrors.d.ts +11 -8
- package/lib/core/backend/backendErrors.js +26 -22
- package/lib/core/network/protocols/httpMessage.js +2 -1
- package/lib/core/network/protocols/httpwsProtocol.js +16 -6
- package/lib/kerror/codes/0-core.json +1 -1
- package/lib/kerror/codes/1-services.json +1 -1
- package/lib/kerror/codes/2-api.json +1 -1
- package/lib/kerror/codes/3-network.json +1 -1
- package/lib/kerror/codes/4-plugin.json +1 -1
- package/lib/kerror/codes/5-validation.json +1 -1
- package/lib/kerror/codes/6-protocol.json +1 -1
- package/lib/kerror/codes/7-security.json +1 -1
- package/lib/kerror/codes/8-cluster.json +1 -1
- package/lib/kerror/codes/index.js +7 -7
- package/lib/kerror/index.d.ts +3 -3
- package/lib/kerror/index.js +10 -6
- package/lib/types/errors/ErrorDefinition.d.ts +6 -0
- package/lib/types/errors/ErrorDomains.d.ts +2 -2
- package/package-lock.json +1 -1
- package/package.json +1 -1
|
@@ -314,7 +314,6 @@ class ServerController extends NativeController {
|
|
|
314
314
|
request.response.configure({
|
|
315
315
|
format: 'raw',
|
|
316
316
|
headers: { 'Content-Type': `application/${format}` },
|
|
317
|
-
status: 200,
|
|
318
317
|
});
|
|
319
318
|
|
|
320
319
|
return format === 'json' ? definition : jsonToYaml.stringify(definition);
|
package/lib/api/funnel.js
CHANGED
|
@@ -251,7 +251,12 @@ class RequestResponse {
|
|
|
251
251
|
if (options.headers) {
|
|
252
252
|
this.setHeaders(options.headers);
|
|
253
253
|
}
|
|
254
|
-
|
|
254
|
+
if (options.status) {
|
|
255
|
+
this.status = options.status;
|
|
256
|
+
}
|
|
257
|
+
else if (this.status === 102) {
|
|
258
|
+
this.status = 200;
|
|
259
|
+
}
|
|
255
260
|
switch (options.format) {
|
|
256
261
|
case 'raw':
|
|
257
262
|
this.raw = true;
|
|
@@ -3,54 +3,57 @@ import { ApplicationManager, Backend } from './index';
|
|
|
3
3
|
import { CustomErrorDefinition } from '../../types';
|
|
4
4
|
export declare class BackendErrors extends ApplicationManager {
|
|
5
5
|
private domains;
|
|
6
|
-
private subDomains;
|
|
7
6
|
constructor(application: Backend);
|
|
8
7
|
/**
|
|
9
8
|
* Register a new standard KuzzleError
|
|
10
9
|
*
|
|
10
|
+
* @param domain Domain name
|
|
11
11
|
* @param subDomain Subdomain name
|
|
12
12
|
* @param name Standard error name
|
|
13
13
|
* @param definition Standard error definition
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* ```
|
|
17
|
-
* app.errors.register('api', 'custom', {
|
|
17
|
+
* app.errors.register('app', 'api', 'custom', {
|
|
18
18
|
* class: 'BadRequestError',
|
|
19
19
|
* description: 'This is a custom error from API subdomain',
|
|
20
20
|
* message: 'Custom API error: %s',
|
|
21
21
|
* });
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
register(subDomain: string, name: string, definition: CustomErrorDefinition): void;
|
|
24
|
+
register(domain: string, subDomain: string, name: string, definition: CustomErrorDefinition): void;
|
|
25
25
|
/**
|
|
26
26
|
* Get a standardized KuzzleError
|
|
27
27
|
*
|
|
28
|
+
* @param domain Domain name
|
|
28
29
|
* @param subDomain Subdomain name
|
|
29
30
|
* @param name Standard error name
|
|
30
31
|
* @param placeholders Other placeholder arguments
|
|
31
32
|
*
|
|
32
|
-
* @example throw app.errors.get('api', 'custom', 'Tbilisi');
|
|
33
|
+
* @example throw app.errors.get('app', 'api', 'custom', 'Tbilisi');
|
|
33
34
|
*
|
|
34
35
|
* @returns Standardized KuzzleError
|
|
35
36
|
*/
|
|
36
|
-
get(subDomain: string, name: string, ...placeholders: any[]): KuzzleError;
|
|
37
|
+
get(domain: string, subDomain: string, name: string, ...placeholders: any[]): KuzzleError;
|
|
37
38
|
/**
|
|
38
39
|
* Get a standardized KuzzleError from an existing error to keep the stacktrace
|
|
39
40
|
*
|
|
40
41
|
* @param source Original error
|
|
42
|
+
* @param domain Domain name
|
|
41
43
|
* @param subDomain Subdomain name
|
|
42
44
|
* @param name Standard error name
|
|
43
45
|
* @param placeholders Other placeholder arguments
|
|
44
46
|
*
|
|
45
47
|
* @returns Standardized KuzzleError
|
|
46
48
|
*/
|
|
47
|
-
getFrom(source: Error, subDomain: string, name: string, ...placeholders: any[]): KuzzleError;
|
|
49
|
+
getFrom(source: Error, domain: string, subDomain: string, name: string, ...placeholders: any[]): KuzzleError;
|
|
48
50
|
/**
|
|
49
|
-
* Wrap an error manager on the subDomain
|
|
51
|
+
* Wrap an error manager on the domain and subDomain
|
|
50
52
|
*
|
|
53
|
+
* @param domain Domain name
|
|
51
54
|
* @param subDomain Subdomain to wrap to
|
|
52
55
|
*/
|
|
53
|
-
wrap(subDomain: string): {
|
|
56
|
+
wrap(domain: string, subDomain: string): {
|
|
54
57
|
get: (error: any, ...placeholders: any[]) => KuzzleError;
|
|
55
58
|
getFrom: (source: any, error: any, ...placeholders: any[]) => KuzzleError;
|
|
56
59
|
reject: (error: any, ...placeholders: any[]) => Promise<any>;
|
|
@@ -45,76 +45,80 @@ const index_1 = require("./index");
|
|
|
45
45
|
class BackendErrors extends index_1.ApplicationManager {
|
|
46
46
|
constructor(application) {
|
|
47
47
|
super(application);
|
|
48
|
-
this.domains = {
|
|
49
|
-
app: {
|
|
50
|
-
code: 9,
|
|
51
|
-
subdomains: {},
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
this.subDomains = 0;
|
|
48
|
+
this.domains = {};
|
|
55
49
|
}
|
|
56
50
|
/**
|
|
57
51
|
* Register a new standard KuzzleError
|
|
58
52
|
*
|
|
53
|
+
* @param domain Domain name
|
|
59
54
|
* @param subDomain Subdomain name
|
|
60
55
|
* @param name Standard error name
|
|
61
56
|
* @param definition Standard error definition
|
|
62
57
|
*
|
|
63
58
|
* @example
|
|
64
59
|
* ```
|
|
65
|
-
* app.errors.register('api', 'custom', {
|
|
60
|
+
* app.errors.register('app', 'api', 'custom', {
|
|
66
61
|
* class: 'BadRequestError',
|
|
67
62
|
* description: 'This is a custom error from API subdomain',
|
|
68
63
|
* message: 'Custom API error: %s',
|
|
69
64
|
* });
|
|
70
65
|
* ```
|
|
71
66
|
*/
|
|
72
|
-
register(subDomain, name, definition) {
|
|
73
|
-
if (!this.domains
|
|
74
|
-
this.domains
|
|
75
|
-
code: this.
|
|
67
|
+
register(domain, subDomain, name, definition) {
|
|
68
|
+
if (!this.domains[domain]) {
|
|
69
|
+
this.domains[domain] = {
|
|
70
|
+
code: Object.keys(this.domains).length,
|
|
71
|
+
subDomains: {},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (!this.domains[domain].subDomains[subDomain]) {
|
|
75
|
+
this.domains[domain].subDomains[subDomain] = {
|
|
76
|
+
code: Object.keys(this.domains[domain].subDomains).length,
|
|
76
77
|
errors: {},
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
|
-
this.domains.
|
|
80
|
-
code: Object.keys(this.domains.
|
|
80
|
+
this.domains[domain].subDomains[subDomain].errors[name] = {
|
|
81
|
+
code: Object.keys(this.domains[domain].subDomains[subDomain].errors).length,
|
|
81
82
|
...definition,
|
|
82
83
|
};
|
|
83
84
|
}
|
|
84
85
|
/**
|
|
85
86
|
* Get a standardized KuzzleError
|
|
86
87
|
*
|
|
88
|
+
* @param domain Domain name
|
|
87
89
|
* @param subDomain Subdomain name
|
|
88
90
|
* @param name Standard error name
|
|
89
91
|
* @param placeholders Other placeholder arguments
|
|
90
92
|
*
|
|
91
|
-
* @example throw app.errors.get('api', 'custom', 'Tbilisi');
|
|
93
|
+
* @example throw app.errors.get('app', 'api', 'custom', 'Tbilisi');
|
|
92
94
|
*
|
|
93
95
|
* @returns Standardized KuzzleError
|
|
94
96
|
*/
|
|
95
|
-
get(subDomain, name, ...placeholders) {
|
|
96
|
-
return kerror.rawGet(this.domains,
|
|
97
|
+
get(domain, subDomain, name, ...placeholders) {
|
|
98
|
+
return kerror.rawGet(this.domains, domain, subDomain, name, ...placeholders);
|
|
97
99
|
}
|
|
98
100
|
/**
|
|
99
101
|
* Get a standardized KuzzleError from an existing error to keep the stacktrace
|
|
100
102
|
*
|
|
101
103
|
* @param source Original error
|
|
104
|
+
* @param domain Domain name
|
|
102
105
|
* @param subDomain Subdomain name
|
|
103
106
|
* @param name Standard error name
|
|
104
107
|
* @param placeholders Other placeholder arguments
|
|
105
108
|
*
|
|
106
109
|
* @returns Standardized KuzzleError
|
|
107
110
|
*/
|
|
108
|
-
getFrom(source, subDomain, name, ...placeholders) {
|
|
109
|
-
return kerror.rawGetFrom(this.domains, source,
|
|
111
|
+
getFrom(source, domain, subDomain, name, ...placeholders) {
|
|
112
|
+
return kerror.rawGetFrom(this.domains, source, domain, subDomain, name, ...placeholders);
|
|
110
113
|
}
|
|
111
114
|
/**
|
|
112
|
-
* Wrap an error manager on the subDomain
|
|
115
|
+
* Wrap an error manager on the domain and subDomain
|
|
113
116
|
*
|
|
117
|
+
* @param domain Domain name
|
|
114
118
|
* @param subDomain Subdomain to wrap to
|
|
115
119
|
*/
|
|
116
|
-
wrap(subDomain) {
|
|
117
|
-
return kerror.rawWrap(this.domains,
|
|
120
|
+
wrap(domain, subDomain) {
|
|
121
|
+
return kerror.rawWrap(this.domains, domain, subDomain);
|
|
118
122
|
}
|
|
119
123
|
}
|
|
120
124
|
exports.BackendErrors = BackendErrors;
|
|
@@ -33,7 +33,6 @@ class HttpMessage {
|
|
|
33
33
|
this.connection = connection;
|
|
34
34
|
this._content = null;
|
|
35
35
|
this.ips = connection.ips;
|
|
36
|
-
this.requestId = connection.id;
|
|
37
36
|
this.query = request.getQuery();
|
|
38
37
|
this.path = request.getUrl();
|
|
39
38
|
|
|
@@ -51,6 +50,8 @@ class HttpMessage {
|
|
|
51
50
|
this.headers = {};
|
|
52
51
|
|
|
53
52
|
request.forEach((name, value) => (this.headers[name] = value));
|
|
53
|
+
|
|
54
|
+
this.requestId = this.headers['x-kuzzle-request-id'] || connection.id;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
set content (value) {
|
|
@@ -88,6 +88,7 @@ const HTTP_HEADER_TRANSFER_ENCODING = Buffer.from('Transfer-Encoding');
|
|
|
88
88
|
const CHUNKED = Buffer.from('chunked');
|
|
89
89
|
const WILDCARD = Buffer.from('*');
|
|
90
90
|
const ORIGIN = Buffer.from('Origin');
|
|
91
|
+
const X_KUZZLE_REQUEST_ID = Buffer.from('X-Kuzzle-Request-Id');
|
|
91
92
|
const CLOSE = Buffer.from('close');
|
|
92
93
|
const CHARSET_REGEX = /charset=([\w-]+)/i;
|
|
93
94
|
|
|
@@ -258,6 +259,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
258
259
|
res.upgrade(
|
|
259
260
|
{
|
|
260
261
|
cookie: req.getHeader('cookie'),
|
|
262
|
+
origin: req.getHeader('origin'),
|
|
261
263
|
},
|
|
262
264
|
req.getHeader('sec-websocket-key'),
|
|
263
265
|
req.getHeader('sec-websocket-protocol'),
|
|
@@ -268,7 +270,14 @@ class HttpWsProtocol extends Protocol {
|
|
|
268
270
|
|
|
269
271
|
wsOnOpenHandler (socket) {
|
|
270
272
|
const ip = Buffer.from(socket.getRemoteAddressAsText()).toString();
|
|
271
|
-
const connection = new ClientConnection(
|
|
273
|
+
const connection = new ClientConnection(
|
|
274
|
+
this.name,
|
|
275
|
+
[ip],
|
|
276
|
+
{
|
|
277
|
+
cookie: socket.cookie,
|
|
278
|
+
origin: socket.origin
|
|
279
|
+
}
|
|
280
|
+
);
|
|
272
281
|
|
|
273
282
|
this.entryPoint.newConnection(connection);
|
|
274
283
|
this.connectionBySocket.set(socket, connection);
|
|
@@ -634,15 +643,16 @@ class HttpWsProtocol extends Protocol {
|
|
|
634
643
|
}
|
|
635
644
|
|
|
636
645
|
/**
|
|
637
|
-
*
|
|
638
|
-
* @param {uWS.HttpRequest} request
|
|
639
|
-
* @param {uWS.HttpResponse} response
|
|
640
|
-
* @param {HttpMessage} message
|
|
646
|
+
*
|
|
647
|
+
* @param {uWS.HttpRequest} request
|
|
648
|
+
* @param {uWS.HttpResponse} response
|
|
649
|
+
* @param {HttpMessage} message
|
|
641
650
|
*/
|
|
642
651
|
httpWriteRequestHeaders (request, response, message) {
|
|
643
652
|
response.writeStatus(Buffer.from(request.response.status.toString()));
|
|
644
|
-
|
|
653
|
+
|
|
645
654
|
response.writeHeader(HTTP_HEADER_CONNECTION, CLOSE);
|
|
655
|
+
response.writeHeader(X_KUZZLE_REQUEST_ID, message.requestId);
|
|
646
656
|
|
|
647
657
|
for (const header of this.httpConfig.headers) {
|
|
648
658
|
// If header is missing, add the default one
|
|
@@ -97,8 +97,8 @@ function checkErrors (subdomain, domain, options) {
|
|
|
97
97
|
function checkSubdomains (domain, options) {
|
|
98
98
|
const subdomainCodes = new Set();
|
|
99
99
|
|
|
100
|
-
for (const subdomainName of Object.keys(domain.
|
|
101
|
-
const subdomain = domain.
|
|
100
|
+
for (const subdomainName of Object.keys(domain.subDomains)) {
|
|
101
|
+
const subdomain = domain.subDomains[subdomainName];
|
|
102
102
|
|
|
103
103
|
// Subdomain code for plugins is not required and is automatically set to 0
|
|
104
104
|
if (! options.plugin) {
|
|
@@ -158,11 +158,11 @@ function checkDomains (errorCodesFiles, options = { plugin: false }) {
|
|
|
158
158
|
domainCodes.add(domain.code);
|
|
159
159
|
|
|
160
160
|
assert(
|
|
161
|
-
has(domain, '
|
|
162
|
-
`Error configuration file : Missing required '
|
|
161
|
+
has(domain, 'subDomains'),
|
|
162
|
+
`Error configuration file : Missing required 'subDomains' field. (domain: '${domainName}').`);
|
|
163
163
|
assert(
|
|
164
|
-
isPlainObject(domain.
|
|
165
|
-
`Error configuration file : Field '
|
|
164
|
+
isPlainObject(domain.subDomains),
|
|
165
|
+
`Error configuration file : Field 'subDomains' must be an object. (domain: '${domainName}').`);
|
|
166
166
|
|
|
167
167
|
checkSubdomains(domain, options);
|
|
168
168
|
}
|
|
@@ -170,7 +170,7 @@ function checkDomains (errorCodesFiles, options = { plugin: false }) {
|
|
|
170
170
|
|
|
171
171
|
function loadPluginsErrors (pluginManifest, pluginCode) {
|
|
172
172
|
// @todo this should be in its own, independant domain
|
|
173
|
-
domains.plugin.
|
|
173
|
+
domains.plugin.subDomains[pluginManifest.name] = {
|
|
174
174
|
code: pluginCode,
|
|
175
175
|
errors: pluginManifest.errors
|
|
176
176
|
};
|
package/lib/kerror/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { ErrorDomains } from '../types';
|
|
|
4
4
|
/**
|
|
5
5
|
* Construct and return the corresponding error
|
|
6
6
|
*
|
|
7
|
-
* @param domains - Domains object with
|
|
7
|
+
* @param domains - Domains object with subDomains and error names
|
|
8
8
|
* @param domain - Domain (eg: 'external')
|
|
9
9
|
* @param subdomain - Subdomain (eg: 'elasticsearch')
|
|
10
10
|
* @param error - Error name: (eg: 'index_not_found')
|
|
@@ -15,7 +15,7 @@ export declare function rawGet(domains: ErrorDomains, domain: string, subdomain:
|
|
|
15
15
|
/**
|
|
16
16
|
* Returns a promise rejected with the corresponding error
|
|
17
17
|
*
|
|
18
|
-
* @param domains - Domains object with
|
|
18
|
+
* @param domains - Domains object with subDomains and error names
|
|
19
19
|
* @param domain - Domain (eg: 'external')
|
|
20
20
|
* @param subdomain - Subdomain (eg: 'elasticsearch')
|
|
21
21
|
* @param error - Error name: (eg: 'index_not_found')
|
|
@@ -26,7 +26,7 @@ export declare function rawReject(domains: ErrorDomains, domain: string, subdoma
|
|
|
26
26
|
* Construct and return the corresponding error, with its stack
|
|
27
27
|
* trace derivated from a provided source error
|
|
28
28
|
*
|
|
29
|
-
* @param domains - Domains object with
|
|
29
|
+
* @param domains - Domains object with subDomains and error names
|
|
30
30
|
* @param source - Original error
|
|
31
31
|
* @param domain - Domain (eg: 'external')
|
|
32
32
|
* @param subdomain - Subdomain (eg: 'elasticsearch')
|
package/lib/kerror/index.js
CHANGED
|
@@ -63,7 +63,7 @@ function _getCurrentFileName() {
|
|
|
63
63
|
/**
|
|
64
64
|
* Construct and return the corresponding error
|
|
65
65
|
*
|
|
66
|
-
* @param domains - Domains object with
|
|
66
|
+
* @param domains - Domains object with subDomains and error names
|
|
67
67
|
* @param domain - Domain (eg: 'external')
|
|
68
68
|
* @param subdomain - Subdomain (eg: 'elasticsearch')
|
|
69
69
|
* @param error - Error name: (eg: 'index_not_found')
|
|
@@ -76,7 +76,7 @@ function rawGet(domains, domain, subdomain, error, ...placeholders) {
|
|
|
76
76
|
if (lodash_1.default.isPlainObject(placeholders[placeholders.length - 1])) {
|
|
77
77
|
options = placeholders.pop();
|
|
78
78
|
}
|
|
79
|
-
const kuzzleError = lodash_1.default.get(domains, `${domain}.
|
|
79
|
+
const kuzzleError = lodash_1.default.get(domains, `${domain}.subDomains.${subdomain}.errors.${error}`);
|
|
80
80
|
if (!kuzzleError) {
|
|
81
81
|
return get('core', 'fatal', 'unexpected_error', `${domain}.${subdomain}.${error}`);
|
|
82
82
|
}
|
|
@@ -87,12 +87,16 @@ function rawGet(domains, domain, subdomain, error, ...placeholders) {
|
|
|
87
87
|
const message = options.message || (0, util_1.format)(kuzzleError.message, ...placeholders);
|
|
88
88
|
const id = `${domain}.${subdomain}.${error}`;
|
|
89
89
|
const code = domains[domain].code << 24
|
|
90
|
-
| domains[domain].
|
|
91
|
-
| domains[domain].
|
|
90
|
+
| domains[domain].subDomains[subdomain].code << 16
|
|
91
|
+
| domains[domain].subDomains[subdomain].errors[error].code;
|
|
92
92
|
let kerror;
|
|
93
93
|
if (kuzzleError.class === 'PartialError' || kuzzleError.class === 'MultipleErrorsError') {
|
|
94
94
|
kerror = new errors[kuzzleError.class](message, body, id, code);
|
|
95
95
|
}
|
|
96
|
+
else if (kuzzleError.class === 'KuzzleError') {
|
|
97
|
+
const status = kuzzleError.status || 500;
|
|
98
|
+
kerror = new errors.KuzzleError(message, status, id, code);
|
|
99
|
+
}
|
|
96
100
|
else {
|
|
97
101
|
kerror = new errors[kuzzleError.class](message, id, code);
|
|
98
102
|
}
|
|
@@ -136,7 +140,7 @@ function cleanStackTrace(error) {
|
|
|
136
140
|
/**
|
|
137
141
|
* Returns a promise rejected with the corresponding error
|
|
138
142
|
*
|
|
139
|
-
* @param domains - Domains object with
|
|
143
|
+
* @param domains - Domains object with subDomains and error names
|
|
140
144
|
* @param domain - Domain (eg: 'external')
|
|
141
145
|
* @param subdomain - Subdomain (eg: 'elasticsearch')
|
|
142
146
|
* @param error - Error name: (eg: 'index_not_found')
|
|
@@ -150,7 +154,7 @@ exports.rawReject = rawReject;
|
|
|
150
154
|
* Construct and return the corresponding error, with its stack
|
|
151
155
|
* trace derivated from a provided source error
|
|
152
156
|
*
|
|
153
|
-
* @param domains - Domains object with
|
|
157
|
+
* @param domains - Domains object with subDomains and error names
|
|
154
158
|
* @param source - Original error
|
|
155
159
|
* @param domain - Domain (eg: 'external')
|
|
156
160
|
* @param subdomain - Subdomain (eg: 'elasticsearch')
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ErrorDefinition } from './ErrorDefinition';
|
|
2
2
|
/**
|
|
3
|
-
* Represents the domains,
|
|
3
|
+
* Represents the domains, subDomains and error names with associated definitions
|
|
4
4
|
*/
|
|
5
5
|
export declare type ErrorDomains = {
|
|
6
6
|
[domain: string]: {
|
|
7
7
|
code: number;
|
|
8
|
-
|
|
8
|
+
subDomains?: {
|
|
9
9
|
[subDomain: string]: {
|
|
10
10
|
code: number;
|
|
11
11
|
errors: {
|
package/package-lock.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kuzzle",
|
|
3
3
|
"author": "The Kuzzle Team <support@kuzzle.io>",
|
|
4
|
-
"version": "2.17.
|
|
4
|
+
"version": "2.17.4",
|
|
5
5
|
"description": "Kuzzle is an open-source solution that handles all the data management through a secured API, with a large choice of protocols.",
|
|
6
6
|
"bin": {
|
|
7
7
|
"kuzzle": "bin/start-kuzzle-server"
|