stepzen 0.15.0-beta.1 → 0.16.0-beta.0

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/README.md CHANGED
@@ -29,7 +29,7 @@ $ npm install -g stepzen
29
29
  $ stepzen COMMAND
30
30
  running command...
31
31
  $ stepzen (-v|--version|version)
32
- stepzen/0.15.0-beta.1 darwin-x64 node-v14.19.1
32
+ stepzen/0.16.0-beta.0 darwin-x64 node-v14.19.1
33
33
  $ stepzen --help [COMMAND]
34
34
  USAGE
35
35
  $ stepzen COMMAND
@@ -94,9 +94,34 @@ OPTIONS
94
94
  -h, --help
95
95
  show CLI help
96
96
 
97
+ --db-database=db-database
98
+ [mysql, postgresql] name of database to import
99
+
100
+ --db-host=db-host
101
+ [mysql, postgresql] database host
102
+
103
+ --db-password=db-password
104
+ [mysql, postgresql] database password
105
+
106
+ --db-schema=db-schema
107
+ [postgresql] database schema
108
+
109
+ --db-user=db-user
110
+ [mysql, postgresql] database user name
111
+
97
112
  --dir=dir
98
113
  working directory
99
114
 
115
+ --header-param=header-param
116
+ [curl] specifies a parameter in a header value. Can be formed by taking a -H, --header flag and replacing the
117
+ variable part of the header value with a $paramName placeholder. Repeat this flag once for each header with a
118
+ parameter.
119
+
120
+ Example:
121
+ stepzen import curl https://example.com/api/customers \
122
+ -H "Authorization: apikey SecretAPIKeyValue" \
123
+ --header-param 'Authorization: apikey $apikey'
124
+
100
125
  --name=name
101
126
  subfolder inside the workspace folder to save the imported schema files, defaults to the imported schema name
102
127
 
@@ -8,7 +8,6 @@ export default class Deploy extends ZenCommand {
8
8
  schema: flags.IOptionFlag<string>;
9
9
  silent: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
10
10
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
11
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
12
11
  };
13
12
  static args: {
14
13
  name: string;
@@ -2,17 +2,38 @@ import { flags } from '@oclif/command';
2
2
  import ZenCommand from '../shared/zen-command';
3
3
  export default class Import extends ZenCommand {
4
4
  static description: string;
5
+ static curlFlags: {
6
+ prefix: flags.IOptionFlag<string | undefined>;
7
+ 'query-name': flags.IOptionFlag<string | undefined>;
8
+ 'query-type': flags.IOptionFlag<string | undefined>;
9
+ 'path-params': flags.IOptionFlag<string | undefined>;
10
+ 'header-param': flags.IOptionFlag<string[]>;
11
+ };
12
+ static sqlFlags: {
13
+ 'db-host': flags.IOptionFlag<string | undefined>;
14
+ 'db-user': flags.IOptionFlag<string | undefined>;
15
+ 'db-password': flags.IOptionFlag<string | undefined>;
16
+ 'db-database': flags.IOptionFlag<string | undefined>;
17
+ };
18
+ static postgresqlFlags: {
19
+ 'db-schema': flags.IOptionFlag<string | undefined>;
20
+ };
5
21
  static flags: {
6
- dir: flags.IOptionFlag<string | undefined>;
7
- help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
8
- silent: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
9
- name: flags.IOptionFlag<string | undefined>;
22
+ 'db-schema': flags.IOptionFlag<string | undefined>;
23
+ 'db-host': flags.IOptionFlag<string | undefined>;
24
+ 'db-user': flags.IOptionFlag<string | undefined>;
25
+ 'db-password': flags.IOptionFlag<string | undefined>;
26
+ 'db-database': flags.IOptionFlag<string | undefined>;
10
27
  prefix: flags.IOptionFlag<string | undefined>;
11
28
  'query-name': flags.IOptionFlag<string | undefined>;
12
29
  'query-type': flags.IOptionFlag<string | undefined>;
13
30
  'path-params': flags.IOptionFlag<string | undefined>;
31
+ 'header-param': flags.IOptionFlag<string[]>;
32
+ dir: flags.IOptionFlag<string | undefined>;
33
+ help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
34
+ silent: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
35
+ name: flags.IOptionFlag<string | undefined>;
14
36
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
15
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
16
37
  };
17
38
  static args: {
18
39
  name: string;
@@ -20,4 +41,5 @@ export default class Import extends ZenCommand {
20
41
  }[];
21
42
  static strict: boolean;
22
43
  run(): Promise<void>;
44
+ warnAboutIgnoredFlags: (schemas: string[], maybeIgnored: string[], relevantSchemas: string[]) => void;
23
45
  }
@@ -17,7 +17,24 @@ const zen_command_1 = require("../shared/zen-command");
17
17
  const init_1 = require("./init");
18
18
  const constants_1 = require("../shared/constants");
19
19
  const path_params_parser_1 = require("../shared/path-params-parser");
20
+ const header_params_parser_1 = require("../shared/header-params-parser");
20
21
  class Import extends zen_command_1.default {
22
+ constructor() {
23
+ super(...arguments);
24
+ // notify the user about any ignored flags
25
+ this.warnAboutIgnoredFlags = (schemas, maybeIgnored, relevantSchemas) => {
26
+ if (!schemas.some(schema => relevantSchemas.includes(schema))) {
27
+ maybeIgnored.forEach(flag => {
28
+ // eslint-disable-next-line no-prototype-builtins
29
+ if (command_1.flags.hasOwnProperty(flag)) {
30
+ this.log(chalk.gray(`The ${chalk.bold(`--${flag}`)} flag only applies when importing ${relevantSchemas
31
+ .map(schema => chalk.bold(schema))
32
+ .join(', ')}. It will be ignored now.`));
33
+ }
34
+ });
35
+ }
36
+ };
37
+ }
21
38
  async run() {
22
39
  const { args, argv, flags } = this.parse(Import);
23
40
  // Get a list of schemas you're asking for
@@ -41,6 +58,14 @@ class Import extends zen_command_1.default {
41
58
  throw new errors_1.CLIError(`Could not create a StepZen workspace in the ${flags.dir ? directory : 'current'} directory.\n` + error.message);
42
59
  }
43
60
  }
61
+ this.warnAboutIgnoredFlags(schemas, Object.keys(Import.curlFlags), ['curl']);
62
+ this.warnAboutIgnoredFlags(schemas, Object.keys(Import.sqlFlags), [
63
+ 'mysql',
64
+ 'postgresql',
65
+ ]);
66
+ this.warnAboutIgnoredFlags(schemas, Object.keys(Import.postgresqlFlags), [
67
+ 'postgresql',
68
+ ]);
44
69
  // Select an import execution flow:
45
70
  // - v1 with `stepzen/engines` and cloud function
46
71
  // - v2 with the graphqlize service
@@ -74,7 +99,8 @@ class Import extends zen_command_1.default {
74
99
  'StepZen API. curl syntax is supported so that you copy and paste a ' +
75
100
  'curl command instead of the URL.');
76
101
  console.log();
77
- curl2sdlOptions = Object.assign(Object.assign({}, fixedOptions), (await curl2sdl_1.askCurlQuestions(editableOptions)));
102
+ const curlAnswers = await curl2sdl_1.askCurlQuestions(editableOptions);
103
+ curl2sdlOptions = Object.assign(Object.assign(Object.assign({}, fixedOptions), curlAnswers), { headers: curlAnswers.curlArgs.headers });
78
104
  }
79
105
  else {
80
106
  // run non-interative
@@ -86,7 +112,15 @@ class Import extends zen_command_1.default {
86
112
  if ('error' in parsedPathParamsOrError) {
87
113
  throw new errors_1.CLIError(parsedPathParamsOrError.error);
88
114
  }
89
- curl2sdlOptions = Object.assign(Object.assign(Object.assign({}, fixedOptions), editableOptions), { pathParams: parsedPathParamsOrError, curlArgs: argsOrError });
115
+ const headersOrError = header_params_parser_1.makeHeaders(argsOrError.headers, flags['header-param']);
116
+ if ('error' in headersOrError) {
117
+ throw new errors_1.CLIError(headersOrError.error);
118
+ }
119
+ const maybeDuplicateParamsMessage = curl2sdl_1.makeDuplicateParamsMessage(headersOrError, parsedPathParamsOrError, argsOrError.url);
120
+ if (maybeDuplicateParamsMessage) {
121
+ throw new errors_1.CLIError(maybeDuplicateParamsMessage);
122
+ }
123
+ curl2sdlOptions = Object.assign(Object.assign(Object.assign({}, fixedOptions), editableOptions), { pathParams: parsedPathParamsOrError, headers: headersOrError, curlArgs: argsOrError });
90
124
  }
91
125
  core_1.CliUx.ux.action.start('Starting');
92
126
  const resultOrError = await curl2sdl_1.curl2sdl(curl2sdlOptions);
@@ -100,14 +134,24 @@ class Import extends zen_command_1.default {
100
134
  result = resultOrError.outPath;
101
135
  }
102
136
  else {
103
- ;
104
- ['prefix', 'query-type', 'query-name', 'path-params'].forEach(flag => {
105
- if (flag in flags) {
106
- this.log(chalk.gray(`The ${chalk.bold(`--${flag}`)} flag only applies when importing ${chalk.bold('curl')}. It will be ignored now.`));
107
- }
108
- });
137
+ // Map flag names to the properties defined for sql engines in:
138
+ // https://github.com/steprz/generator-engines/blob/main/generator/src/shared/sql.ts
139
+ // If/when the number of property-to-answer mappings increase, create a dictionary
140
+ // { schema: { flag: field } } and build the below map based on this (a
141
+ // dictionary also enables a generic solution to the warning about ignored flags).
142
+ const preAnsweredForDBs = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (flags['db-host'] === undefined ? {} : { host: flags['db-host'] })), (flags['db-user'] === undefined ? {} : { user: flags['db-user'] })), (flags['db-password'] === undefined
143
+ ? {}
144
+ : { password: flags['db-password'] })), (flags['db-database'] === undefined
145
+ ? {}
146
+ : { database: flags['db-database'] })), (flags['db-schema'] === undefined
147
+ ? {}
148
+ : { schema: flags['db-schema'] }));
149
+ const preAnswered = {
150
+ mysql: preAnsweredForDBs,
151
+ postgresql: preAnsweredForDBs,
152
+ };
109
153
  // Let's go!
110
- result = await generate_1.default(schemas, flags.name, workspace.schema);
154
+ result = await generate_1.default(schemas, flags.name, workspace.schema, preAnswered);
111
155
  // Validate
112
156
  await transpiler_1.validate(result, {
113
157
  extensions: await utils_1.getStepZenExtensions(),
@@ -123,18 +167,19 @@ class Import extends zen_command_1.default {
123
167
  }
124
168
  exports.default = Import;
125
169
  Import.description = 'Import a schema for an external data source or a API endpoint to your GraphQL API.';
126
- Import.flags = Object.assign(Object.assign({}, zen_command_1.default.flags), { dir: command_1.flags.string({ description: 'working directory' }), help: command_1.flags.help({ char: 'h' }), silent: command_1.flags.boolean({ hidden: true }), name: command_1.flags.string({
127
- description: 'subfolder inside the workspace folder to save the imported' +
128
- ' schema files, defaults to the imported schema name',
129
- }), prefix: command_1.flags.string({
170
+ Import.curlFlags = {
171
+ prefix: command_1.flags.string({
130
172
  description: '[curl] prefix to add every type in the generated schema.',
131
- }), 'query-name': command_1.flags.string({
173
+ }),
174
+ 'query-name': command_1.flags.string({
132
175
  description: '[curl] property name to add to the Query type as a way to' +
133
176
  ' access the imported cURL endpoint.',
134
- }), 'query-type': command_1.flags.string({
177
+ }),
178
+ 'query-type': command_1.flags.string({
135
179
  description: '[curl] name for the type returned by the cURL endpoint in the ' +
136
180
  `generated schema. The name specified by ${chalk.bold('--query-type')} is not prefixed by ${chalk.bold('--prefix')} if both flags are present.`,
137
- }), 'path-params': command_1.flags.string({
181
+ }),
182
+ 'path-params': command_1.flags.string({
138
183
  description: `[curl] specifies path parameters in the URL path.` +
139
184
  ` Can be formed by taking the original path and replacing the` +
140
185
  ` variable segments with ${chalk.bold('$paramName')} placeholders.` +
@@ -143,7 +188,42 @@ Import.flags = Object.assign(Object.assign({}, zen_command_1.default.flags), { d
143
188
  `\nstepzen import curl https://example.com/users/jane/posts/12` +
144
189
  ` --path-params` +
145
190
  ` '/users/${chalk.bold('$userId')}/posts/${chalk.bold('$postId')}'`,
146
- }) });
191
+ }),
192
+ 'header-param': command_1.flags.string({
193
+ description: `[curl] specifies a parameter in a header value.` +
194
+ ` Can be formed by taking a ${chalk.bold('-H, --header')} flag and replacing the` +
195
+ ` variable part of the header value with a ${chalk.bold('$paramName')} placeholder. Repeat this flag once for each header with a parameter.` +
196
+ `\n` +
197
+ `\nExample:` +
198
+ `\nstepzen import curl https://example.com/api/customers \\` +
199
+ `\n\t-H "Authorization: apikey SecretAPIKeyValue" \\` +
200
+ `\n\t--header-param 'Authorization: apikey ${chalk.bold('$apikey')}'`,
201
+ multiple: true,
202
+ }),
203
+ };
204
+ Import.sqlFlags = {
205
+ 'db-host': command_1.flags.string({
206
+ description: '[mysql, postgresql] database host',
207
+ }),
208
+ 'db-user': command_1.flags.string({
209
+ description: '[mysql, postgresql] database user name',
210
+ }),
211
+ 'db-password': command_1.flags.string({
212
+ description: '[mysql, postgresql] database password',
213
+ }),
214
+ 'db-database': command_1.flags.string({
215
+ description: '[mysql, postgresql] name of database to import',
216
+ }),
217
+ };
218
+ Import.postgresqlFlags = {
219
+ 'db-schema': command_1.flags.string({
220
+ description: '[postgresql] database schema',
221
+ }),
222
+ };
223
+ Import.flags = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, zen_command_1.default.flags), { dir: command_1.flags.string({ description: 'working directory' }), help: command_1.flags.help({ char: 'h' }), silent: command_1.flags.boolean({ hidden: true }), name: command_1.flags.string({
224
+ description: 'subfolder inside the workspace folder to save the imported' +
225
+ ' schema files, defaults to the imported schema name',
226
+ }) }), Import.curlFlags), Import.sqlFlags), Import.postgresqlFlags);
147
227
  Import.args = [
148
228
  {
149
229
  name: 'schemas',
@@ -11,7 +11,6 @@ export default class Init extends ZenCommand {
11
11
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
12
12
  yes: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
13
13
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
14
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
15
14
  };
16
15
  static args: {
17
16
  hidden: boolean;
@@ -7,7 +7,6 @@ export default class Init extends ZenCommand {
7
7
  dir: flags.IOptionFlag<string | undefined>;
8
8
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
9
9
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
10
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
11
10
  };
12
11
  run(): Promise<void>;
13
12
  }
@@ -1,11 +1,9 @@
1
- import { flags } from '@oclif/command';
2
1
  import ZenCommand from '../shared/zen-command';
3
2
  export default class List extends ZenCommand {
4
3
  static description: string;
5
4
  static flags: {
6
5
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
7
6
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
8
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
9
7
  };
10
8
  static args: {
11
9
  name: string;
@@ -9,7 +9,6 @@ export default class Login extends ZenCommand {
9
9
  config: flags.IOptionFlag<string | undefined>;
10
10
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
11
11
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
12
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
13
12
  };
14
13
  run(): Promise<void>;
15
14
  }
@@ -1,11 +1,9 @@
1
- import { flags } from '@oclif/command';
2
1
  import ZenCommand from '../shared/zen-command';
3
2
  export default class Logout extends ZenCommand {
4
3
  static description: string;
5
4
  static flags: {
6
5
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
7
6
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
8
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
9
7
  };
10
8
  run(): Promise<void>;
11
9
  }
@@ -14,7 +14,6 @@ export default class Start extends ZenCommand {
14
14
  'no-watcher': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
15
15
  port: import("@oclif/parser/lib/flags").IOptionFlag<number>;
16
16
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
17
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
18
17
  };
19
18
  static args: never[];
20
19
  run(): Promise<void>;
@@ -12,7 +12,6 @@ export default class Transpile extends ZenCommand {
12
12
  'output-configuration': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
13
13
  silent: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
14
14
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
15
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
16
15
  };
17
16
  static args: {
18
17
  name: string;
@@ -8,7 +8,6 @@ export default class Upload extends ZenCommand {
8
8
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
9
9
  silent: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
10
10
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
11
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
12
11
  };
13
12
  static args: ({
14
13
  name: string;
@@ -1,4 +1,3 @@
1
- import { flags } from '@oclif/command';
2
1
  import ZenCommand from '../shared/zen-command';
3
2
  export default class Validate extends ZenCommand {
4
3
  static description: string;
@@ -6,7 +5,6 @@ export default class Validate extends ZenCommand {
6
5
  static flags: {
7
6
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
8
7
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
9
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
10
8
  };
11
9
  static args: {
12
10
  name: string;
@@ -1,4 +1,3 @@
1
- import { flags } from '@oclif/command';
2
1
  import ZenCommand from '../shared/zen-command';
3
2
  export default class WhoAmI extends ZenCommand {
4
3
  static description: string;
@@ -9,7 +8,6 @@ export default class WhoAmI extends ZenCommand {
9
8
  apikey: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
10
9
  adminkey: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
11
10
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
12
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
13
11
  };
14
12
  static args: never[];
15
13
  run(): Promise<void>;
@@ -1,5 +1,24 @@
1
1
  import { CurlArguments } from '../shared/curl-parser';
2
2
  import { PathParam } from '../shared/path-params-parser';
3
+ export declare type NameValueHeaderInput = {
4
+ name: string;
5
+ value: string;
6
+ };
7
+ export declare type NamePartsHeaderInput = {
8
+ name: string;
9
+ parts: HeaderInputValuePart[];
10
+ };
11
+ export declare type HeaderInput = NameValueHeaderInput | NamePartsHeaderInput;
12
+ export declare type HeaderInputConstantValuePart = {
13
+ kind: 'Constant';
14
+ value: string;
15
+ };
16
+ export declare type HeaderInputVariableValuePart = {
17
+ kind: 'Variable';
18
+ value: string;
19
+ name: string;
20
+ };
21
+ export declare type HeaderInputValuePart = HeaderInputConstantValuePart | HeaderInputVariableValuePart;
3
22
  export declare type Curl2SdlOptions = {
4
23
  curlArgs: CurlArguments;
5
24
  name?: string;
@@ -8,6 +27,7 @@ export declare type Curl2SdlOptions = {
8
27
  rootType?: string;
9
28
  typePrefix?: string;
10
29
  pathParams: readonly PathParam[];
30
+ headers: readonly HeaderInput[];
11
31
  };
12
32
  export declare type EditableCurl2SdlOptions = Pick<Curl2SdlOptions, 'curlArgs' | 'queryName' | 'rootType' | 'typePrefix' | 'pathParams'>;
13
33
  export declare type CurlAnswers = {
@@ -17,8 +37,35 @@ export declare type CurlAnswers = {
17
37
  typePrefix: string;
18
38
  pathParams: string;
19
39
  };
40
+ export declare type CurlQueryParameter = {
41
+ name: string;
42
+ value: string;
43
+ context: string;
44
+ };
45
+ /**
46
+ * Collect together all query parameters from HTTP headers and from the
47
+ * pathname, and return them as a uniform list.
48
+ *
49
+ * @param {*} headers list of curl headers, possibly with parameters
50
+ * @param {*} pathParams list of path parameters
51
+ * @param {*} pathname full pathname of the REST endpoint
52
+ * @returns {*} combined list of query parameters in a uniform format
53
+ */
54
+ export declare const compileParameterList: (headers: HeaderInput[], pathParams: PathParam[], pathname: string) => CurlQueryParameter[];
55
+ /**
56
+ * Check the header and path parameters for duplicates, and construct a
57
+ * user-friendly error message describing identified problems (if any).
58
+ * If no problems are found, return `null`.
59
+ *
60
+ * @param {*} headers list of curl headers, possibly with parameters
61
+ * @param {*} pathParams list of path parameters
62
+ * @param {*} url full URL of the REST endpoint
63
+ * @returns {*} a message describing duplicate query parameters, or null if
64
+ * there are no duplicates
65
+ */
66
+ export declare const makeDuplicateParamsMessage: (headers: HeaderInput[], pathParams: PathParam[], url: string) => string | null;
20
67
  export declare const askCurlQuestions: (defaultAnswers?: Partial<CurlAnswers>) => Promise<EditableCurl2SdlOptions>;
21
- export declare const curl2sdl: ({ curlArgs, name, source, queryName, rootType, typePrefix, pathParams, }: Curl2SdlOptions) => Promise<{
68
+ export declare const curl2sdl: ({ curlArgs, name, source, queryName, rootType, typePrefix, pathParams, headers, }: Curl2SdlOptions) => Promise<{
22
69
  error: string;
23
70
  } | {
24
71
  outPath: string;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  // Copyright (c) 2020,2021,2022, StepZen, Inc.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.curl2sdl = exports.askCurlQuestions = void 0;
4
+ exports.curl2sdl = exports.askCurlQuestions = exports.makeDuplicateParamsMessage = exports.compileParameterList = void 0;
5
5
  const errors_1 = require("@oclif/errors");
6
6
  const chalk = require("chalk");
7
7
  const fs = require("fs-extra");
@@ -18,6 +18,84 @@ const curl_parser_1 = require("../shared/curl-parser");
18
18
  const path_params_parser_1 = require("../shared/path-params-parser");
19
19
  const constants_1 = require("../shared/constants");
20
20
  const errors_2 = require("../shared/errors");
21
+ /**
22
+ * Collect together all query parameters from HTTP headers and from the
23
+ * pathname, and return them as a uniform list.
24
+ *
25
+ * @param {*} headers list of curl headers, possibly with parameters
26
+ * @param {*} pathParams list of path parameters
27
+ * @param {*} pathname full pathname of the REST endpoint
28
+ * @returns {*} combined list of query parameters in a uniform format
29
+ */
30
+ exports.compileParameterList = (headers, pathParams, pathname) => {
31
+ // Drop empty segments, including empty string before the leading slash
32
+ const pathSegments = pathname.split('/').filter(Boolean);
33
+ // For each header, make a list of the parameters it contains.
34
+ // `flatMap()` concatenates each header's param lists into a single list.
35
+ return lodash_1.flatMap(headers, header => {
36
+ // skip headers that don't contain parameters
37
+ if (!('parts' in header)) {
38
+ return [];
39
+ }
40
+ // Reconstruct the full header string (for the context).
41
+ const fullHeader = `${header.name}: ${header.parts
42
+ .map(part => part.value)
43
+ .join('')}`;
44
+ // Filter out the 'Variable' parts of the header value, and convert them
45
+ // to the `CurlyQueryParameter` structure.
46
+ return header.parts
47
+ .map(part => part.kind === 'Variable'
48
+ ? {
49
+ name: part.name,
50
+ value: part.value,
51
+ context: `'${header.name}' header ${chalk.dim(`(${fullHeader})`)}`,
52
+ }
53
+ : null)
54
+ .filter(Boolean); // remove nulls
55
+ }).concat(
56
+ // Append the path parameters to the list, converting them to
57
+ // the `CurlyQueryParameter` structure as well.
58
+ pathParams.map(param => ({
59
+ name: param.name,
60
+ value: pathSegments[param.index],
61
+ context: `pathname ${chalk.dim(`(${pathname})`)}`,
62
+ })));
63
+ };
64
+ /**
65
+ * Check the header and path parameters for duplicates, and construct a
66
+ * user-friendly error message describing identified problems (if any).
67
+ * If no problems are found, return `null`.
68
+ *
69
+ * @param {*} headers list of curl headers, possibly with parameters
70
+ * @param {*} pathParams list of path parameters
71
+ * @param {*} url full URL of the REST endpoint
72
+ * @returns {*} a message describing duplicate query parameters, or null if
73
+ * there are no duplicates
74
+ */
75
+ exports.makeDuplicateParamsMessage = (headers, pathParams, url) => {
76
+ // Create a uniform list of query parameters from the headers and path
77
+ const parameters = exports.compileParameterList(headers, pathParams, new URL(url).pathname);
78
+ const errors = Object.entries(lodash_1.groupBy(parameters, 'name'))
79
+ // Create groups for each unique parameter name,
80
+ // and then sub-groups for each unique [name, value] tuple.
81
+ .map(([name, group]) => ({
82
+ name,
83
+ values: Object.entries(lodash_1.groupBy(group, 'value')),
84
+ }))
85
+ // ignore the [name, value] tuples that occur only once
86
+ .filter(({ values }) => values.length > 1)
87
+ // for the rest, create a message describing the duplicates
88
+ .map(({ name, values }) => `The parameter ${chalk.bold(`$${name}`)} is used for ${values.length} different values:` +
89
+ `\n${values
90
+ .map(([value, params], i) => `\t${i + 1}: '${value}' in the ${params[0].context}`)
91
+ .join('\n')}`);
92
+ if (errors.length > 0) {
93
+ return ('Could not use the same name for different parameters.\n' +
94
+ errors.join('\n') +
95
+ '\nPlease give a different parameter name for each variable value.');
96
+ }
97
+ return null;
98
+ };
21
99
  exports.askCurlQuestions = async (defaultAnswers = {}) => {
22
100
  let questions = [
23
101
  {
@@ -73,7 +151,7 @@ exports.askCurlQuestions = async (defaultAnswers = {}) => {
73
151
  pathParams: parsedPathParamsOrError,
74
152
  };
75
153
  };
76
- exports.curl2sdl = async ({ curlArgs, name, source, queryName, rootType, typePrefix, pathParams, }) => {
154
+ exports.curl2sdl = async ({ curlArgs, name, source, queryName, rootType, typePrefix, pathParams, headers, }) => {
77
155
  let json;
78
156
  try {
79
157
  const url = `${constants_1.STEPZEN_JSON2SDL_SERVER_URL}/api/graphql`;
@@ -106,7 +184,7 @@ exports.curl2sdl = async ({ curlArgs, name, source, queryName, rootType, typePre
106
184
  queryName: queryName || null,
107
185
  rootType: rootType || null,
108
186
  typePrefix: typePrefix || null,
109
- headers: curlArgs.headers.length > 0 ? curlArgs.headers : null,
187
+ headers: headers.length > 0 ? headers : null,
110
188
  data: curlArgs.data || null,
111
189
  method: curlArgs.method,
112
190
  pathParams: pathParams.length > 0 ? pathParams : null,
@@ -121,15 +121,13 @@ exports.askGeneratorQuestions = async (id, settings, state) => {
121
121
  var _a, _b;
122
122
  let status = -1;
123
123
  do {
124
- if (settings.questions.length === 0) {
125
- // would cause a hot loop, so it's an error
126
- throw new errors_1.CLIError(errors_2.PERMANENT_STEPZEN_ERROR);
127
- }
128
- const questions = settings.questions.map((question) => (Object.assign(Object.assign({ type: 'password' }, question), {
129
- // set the previous iteration's answer as the default value, except for password fields
130
- default: question.type && question.type !== 'password'
131
- ? lodash_1.get(state, question.name)
132
- : undefined })));
124
+ const questions = settings.questions.map((question) => {
125
+ return Object.assign(Object.assign({ type: 'password' }, question), {
126
+ // set the previous iteration's answer as the default value, except for password fields
127
+ default: question.type && question.type !== 'password'
128
+ ? lodash_1.get(state, question.name)
129
+ : undefined });
130
+ });
133
131
  // eslint-disable-next-line no-await-in-loop
134
132
  const answers = await inquirer.prompt(questions);
135
133
  state = lodash_1.merge(state, answers);
@@ -142,7 +140,12 @@ exports.askGeneratorQuestions = async (id, settings, state) => {
142
140
  if (result.status === -1 && result.questions.length === 0) {
143
141
  console.log();
144
142
  console.log(chalk.red(`A problem occurred when running ${id} import${((_a = result.errors) === null || _a === void 0 ? void 0 : _a.error) ? `: "${(_b = result.errors) === null || _b === void 0 ? void 0 : _b.error}"` : '.'}`));
145
- console.log('Please try again');
143
+ console.log('Please try again and check command-line parameters');
144
+ if (questions.length === 0) {
145
+ // all questions are pre-answered, so there is no chance to save the
146
+ // situation interactively => don't loop
147
+ throw new errors_1.CLIError('Unable to import');
148
+ }
146
149
  }
147
150
  else {
148
151
  settings = {
@@ -1,2 +1,6 @@
1
- declare const _default: (schemas: any, name: string | undefined, source: string) => Promise<string>;
1
+ declare const _default: (schemas: any, name: string | undefined, source: string, preAnswered?: {
2
+ [schema: string]: {
3
+ [question: string]: string;
4
+ };
5
+ }) => Promise<string>;
2
6
  export default _default;
@@ -10,7 +10,7 @@ const os = require("os");
10
10
  const path = require("path");
11
11
  const transpiler_1 = require("@stepzen/transpiler");
12
12
  const helpers_1 = require("./helpers");
13
- exports.default = async (schemas, name, source) => {
13
+ exports.default = async (schemas, name, source, preAnswered = {}) => {
14
14
  var e_1, _a, e_2, _b, e_3, _c, e_4, _d;
15
15
  var _e;
16
16
  // Store the generators
@@ -18,15 +18,19 @@ exports.default = async (schemas, name, source) => {
18
18
  for (const schema of schemas) {
19
19
  generators[schema] = null;
20
20
  }
21
- // Store the answers
22
- let answers = {};
21
+ // Initial answers from flags. Assumption: <schema> -> <schema>_config
22
+ const initialAnswers = {};
23
+ Object.keys(generators).forEach(id => {
24
+ initialAnswers[`${id}_config`] = preAnswered[id] || {};
25
+ });
26
+ let answers = initialAnswers;
23
27
  // Start downloading
24
28
  core_1.CliUx.ux.action.start('Downloading from StepZen...');
25
29
  try {
26
30
  // Get all generators from...Generators
27
31
  for (var _f = tslib_1.__asyncValues(Object.keys(generators)), _g; _g = await _f.next(), !_g.done;) {
28
32
  const id = _g.value;
29
- const configure = await helpers_1.getConfiguration(id, {});
33
+ const configure = await helpers_1.getConfiguration(id, answers);
30
34
  if (configure) {
31
35
  generators[id] = {
32
36
  questions: configure.questions,
@@ -6,10 +6,10 @@ const chalk = require("chalk");
6
6
  const compareVersions = require("compare-versions");
7
7
  const fs = require("fs-extra");
8
8
  const debug_1 = require("debug");
9
+ const parser_1 = require("@oclif/parser");
9
10
  const node_fetch_1 = require("node-fetch");
10
11
  const constants_1 = require("../../shared/constants");
11
12
  const errors_1 = require("../../shared/errors");
12
- const { version } = require('../../../package.json');
13
13
  const majorRegexp = /\d+\./;
14
14
  const timeoutBeta = 1000 * 60 * 60 * 24; // one day
15
15
  const timeoutStable = 7 * timeoutBeta; // one week
@@ -89,15 +89,24 @@ ${chalk.green('npm install -g stepzen')}
89
89
  }
90
90
  return result;
91
91
  };
92
- const hook = async function () {
93
- const { message, isBlocking } = await exports.checkUpgrade(version);
94
- if (message) {
92
+ const hook = async function (options) {
93
+ const { message, isBlocking } = await exports.checkUpgrade(this.config.version);
94
+ // parse the command line to get `flags['non-interactive']`
95
+ const TheCommand = options.Command;
96
+ const { flags } = parser_1.parse(options.argv, Object.assign({ context: this }, TheCommand));
97
+ // In a non-interactive shell only print the upgrade nudge if the upgrade
98
+ // is blocking.
99
+ // For non-blocking upgrades we don't want to print the upgrade nudge when
100
+ // the CLI is executed as a part of a non-interactive shell script. This
101
+ // additional output might by unexpected by the rest of script and could
102
+ // break the pipeline.
103
+ if (message && (isBlocking || !flags['non-interactive'])) {
95
104
  this.log('');
96
105
  this.log(message.trim());
97
106
  this.log('');
98
- if (isBlocking) {
99
- this.exit(1);
100
- }
107
+ }
108
+ if (isBlocking) {
109
+ this.exit(1);
101
110
  }
102
111
  };
103
112
  exports.default = hook;
@@ -6,7 +6,7 @@ exports.removeCredentialsFromConfigFile = exports.writeCredentialsToConfigFile =
6
6
  const errors_1 = require("@oclif/errors");
7
7
  const debug = require("debug");
8
8
  const fs = require("fs");
9
- const crypto = require("crypto");
9
+ const uuid_1 = require("uuid");
10
10
  const path = require("path");
11
11
  const yaml = require("yaml");
12
12
  const stepzen_sdk_1 = require("./stepzen-sdk");
@@ -35,7 +35,7 @@ exports.ensureValidConfiguration = async (maybeConfiguration) => {
35
35
  }
36
36
  // ensure configuration has a UUID
37
37
  if (!configuration.uuid) {
38
- configuration.uuid = crypto.randomUUID();
38
+ configuration.uuid = uuid_1.v4();
39
39
  debug('stepzen:configuration')(`Generated a new machine UUID: ${configuration.uuid}`);
40
40
  modified = true;
41
41
  }
@@ -1,30 +1,26 @@
1
- /**
2
- * Parse a header string according to https://curl.se/docs/manpage.html#-H
3
- * and https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
4
- *
5
- * @param {*} header a curl header string, e.g. `"api-key: asfdasdfad"`
6
- * @returns {*} a name/value record or
7
- * `null` for the `Header:` notation that means "remove this header" in
8
- * the cURL spec or a error object if cannot parse the string.
9
- */
10
- export declare const parseCurlHeaderString: (header: string) => {
1
+ export interface CurlHeader {
11
2
  name: string;
12
3
  value: string;
13
- } | null | {
14
- error: string;
15
- };
4
+ }
16
5
  export interface CurlArguments {
17
6
  url: string;
18
7
  method: 'Get' | 'Post';
19
- headers: Array<{
20
- name: string;
21
- value: string;
22
- }>;
8
+ headers: CurlHeader[];
23
9
  data?: string;
24
10
  }
25
11
  export interface ParseError {
26
12
  error: string;
27
13
  }
14
+ /**
15
+ * Parse a header string according to https://curl.se/docs/manpage.html#-H
16
+ * and https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
17
+ *
18
+ * @param {*} header a curl header string, e.g. `"api-key: asfdasdfad"`
19
+ * @returns {*} a name/value record or
20
+ * `null` for the `Header:` notation that means "remove this header" in
21
+ * the cURL spec or a error object if cannot parse the string.
22
+ */
23
+ export declare const parseCurlHeaderString: (header: string) => CurlHeader | null | ParseError;
28
24
  /**
29
25
  * Parse a curl command line arguments array to a JSON structure consumable by
30
26
  * the StepZen introspection service backend.
@@ -26,7 +26,8 @@ exports.parseCurlHeaderString = (header) => {
26
26
  return null;
27
27
  }
28
28
  // Check if it's a `Header;` case
29
- if (trimmed.indexOf(';') === trimmed.length - 1) {
29
+ if (trimmed.indexOf(':') === -1 &&
30
+ trimmed.indexOf(';') === trimmed.length - 1) {
30
31
  return {
31
32
  name: trimmed.substring(0, trimmed.length - 1).trim(),
32
33
  value: '',
@@ -0,0 +1,7 @@
1
+ import { HeaderInput } from '../generate/curl2sdl';
2
+ import { ParseError, CurlHeader } from './curl-parser';
3
+ export declare const parseHeaderParam: (headers: readonly CurlHeader[], headerParam: string) => {
4
+ header: HeaderInput;
5
+ remainingHeaders: CurlHeader[];
6
+ } | ParseError;
7
+ export declare const makeHeaders: (curlHeaders: readonly CurlHeader[], headerParams?: readonly string[]) => ParseError | HeaderInput[];
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ // Copyright (c) 2020,2021,2022, StepZen, Inc.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.makeHeaders = exports.parseHeaderParam = void 0;
5
+ const chalk = require("chalk");
6
+ const debug = require("debug");
7
+ const curl_parser_1 = require("./curl-parser");
8
+ const headerParamRegex = /^(?<prefix>([^$]|\$\$)*)(?<param>\$[_A-Za-z]\w*);?(?<suffix>.*)$/;
9
+ exports.parseHeaderParam = (headers, headerParam) => {
10
+ const headerOrError = curl_parser_1.parseCurlHeaderString(headerParam);
11
+ if (!headerOrError || 'error' in headerOrError) {
12
+ if (headerOrError) {
13
+ debug('stepzen:curl2sdl')(`Failed to parse a header param ${headerParam}.` +
14
+ ` Error: ${headerOrError.error}`);
15
+ }
16
+ return {
17
+ error: `Could not find a header name in` +
18
+ ` ${chalk.bold(`--header-param '${headerParam}'`)}.`,
19
+ };
20
+ }
21
+ const { name, value } = headerOrError;
22
+ const paramValueMatches = value.match(headerParamRegex);
23
+ if (!paramValueMatches || !paramValueMatches.groups) {
24
+ return {
25
+ error: `Could not find a $-prefixed parameter name in` +
26
+ ` ${chalk.bold(`--header-param '${headerParam}'`)}.` +
27
+ ` Did you use single quotes to prevent shell variable expansion?`,
28
+ };
29
+ }
30
+ const prefix = paramValueMatches.groups.prefix.replace(/\$\$/g, '$');
31
+ const paramName = paramValueMatches.groups.param.substring(1);
32
+ const suffix = paramValueMatches.groups.suffix.replace(/\$\$/g, '$');
33
+ if (suffix.match(headerParamRegex)) {
34
+ return {
35
+ error: `StepZen CLI currently supports at max 1 parameter per header` +
36
+ ` ${chalk.bold(`--header-param '${headerParam}'`)} has several.`,
37
+ };
38
+ }
39
+ const candidates = headers.filter(h => h.name.toLowerCase() === name.toLowerCase());
40
+ if (candidates.length === 0) {
41
+ return {
42
+ error: `Could not find a matching '-H, --header' curl flag for` +
43
+ ` ${chalk.bold(`--header-param '${headerParam}'`)}.` +
44
+ ` No header matches the name ${name}.`,
45
+ };
46
+ }
47
+ const matches = candidates.filter(({ value }) => value.startsWith(prefix) &&
48
+ value.substring(prefix.length).endsWith(suffix));
49
+ if (matches.length === 0) {
50
+ return {
51
+ error: `Could not find a matching '-H, --header' curl flag for` +
52
+ ` ${chalk.bold(`--header-param '${headerParam}'`)}.` +
53
+ (candidates.length > 1
54
+ ? ` None of the ${name} headers matches the '${value}' pattern.`
55
+ : ` The ${name} header does not match the '${value}' pattern.`),
56
+ };
57
+ }
58
+ if (matches.length > 1) {
59
+ debug('stepzen:curl2sdl')(`Ambiguous --header-param '${headerParam}'. Matched headers:` +
60
+ `\n${matches
61
+ .map(header => `\t-H '${header.name}: ${header.value}'`)
62
+ .join('\n')}` +
63
+ `\nUsing the first match.`);
64
+ }
65
+ const asConstantHeaderValuePart = (value) => ({
66
+ kind: 'Constant',
67
+ value,
68
+ });
69
+ const asVariableHeaderValuePart = (value, name) => ({
70
+ kind: 'Variable',
71
+ value,
72
+ name,
73
+ });
74
+ return {
75
+ header: {
76
+ name: matches[0].name,
77
+ parts: [
78
+ ...(prefix
79
+ ? [asConstantHeaderValuePart(prefix)]
80
+ : [
81
+ /* spreading an empty array is a no-op */
82
+ ]),
83
+ asVariableHeaderValuePart(matches[0].value.substring(prefix.length, matches[0].value.length - suffix.length), paramName),
84
+ ...(suffix
85
+ ? [asConstantHeaderValuePart(suffix)]
86
+ : [
87
+ /* spreading an empty array is a no-op */
88
+ ]),
89
+ ],
90
+ },
91
+ // assume each --header is referenced by at most one --header-param
92
+ remainingHeaders: headers.filter(h => h !== matches[0]),
93
+ };
94
+ };
95
+ exports.makeHeaders = (curlHeaders, headerParams = []) => {
96
+ const headers = [];
97
+ let remainingCurlHeaders = curlHeaders;
98
+ for (const headerParam of headerParams) {
99
+ const resultOrError = exports.parseHeaderParam(remainingCurlHeaders, headerParam);
100
+ if ('error' in resultOrError) {
101
+ return resultOrError;
102
+ }
103
+ headers.push(resultOrError.header);
104
+ remainingCurlHeaders = resultOrError.remainingHeaders;
105
+ }
106
+ headers.push(...remainingCurlHeaders);
107
+ return headers;
108
+ };
@@ -1,9 +1,8 @@
1
- import { Command, flags } from '@oclif/command';
1
+ import { Command } from '@oclif/command';
2
2
  import { MachineConfiguration, StepZenCredentials } from './types';
3
3
  export declare abstract class ZenCommand extends Command {
4
4
  static flags: {
5
5
  'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
6
- 'enable-login-prompt': flags.IOptionFlag<boolean | undefined>;
7
6
  };
8
7
  ensureStepZenAccount(): Promise<{
9
8
  configuration: import("./types").LoggedInMachineConfiguration;
@@ -3,7 +3,6 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.ZenCommand = void 0;
5
5
  const command_1 = require("@oclif/command");
6
- const errors_1 = require("@oclif/errors");
7
6
  const inquirer = require("inquirer");
8
7
  const chalk = require("chalk");
9
8
  const configuration_1 = require("./configuration");
@@ -16,19 +15,15 @@ class ZenCommand extends command_1.Command {
16
15
  configuration,
17
16
  };
18
17
  }
19
- const TheCommand = this.constructor;
20
- const { flags } = this.parse(TheCommand);
21
- if (flags['enable-login-prompt']) {
22
- return this.promptUserToLogIn(configuration.uuid);
23
- }
24
- throw new errors_1.CLIError('Could not find StepZen account credentials.' +
25
- ' Please run `stepzen login` first.');
18
+ return this.promptUserToLogIn(configuration.uuid);
26
19
  }
27
20
  async promptUserToLogIn(uuid) {
28
- this.log(chalk.bold(chalk.cyan('Welcome to StepZen CLI!')));
21
+ this.log(chalk.bold(chalk.cyan('Welcome to the StepZen CLI!')));
29
22
  this.log('');
30
- this.log(`Please log in with your StepZen account to make your GraphQL API private.` +
31
- ` All APIs created without logging in will be public.`);
23
+ this.log(`You are about to deploy your GraphQL API to the cloud on a public` +
24
+ ` endpoint. Log in or create a StepZen account to get a` +
25
+ ` production-ready GraphQL API deployed on a secure and private` +
26
+ ` endpoint.`);
32
27
  this.log('');
33
28
  const answers = await inquirer.prompt([
34
29
  {
@@ -82,19 +77,12 @@ ZenCommand.flags = {
82
77
  'non-interactive': command_1.flags.boolean({
83
78
  description: 'disable all interactive prompts',
84
79
  hidden: true,
85
- }),
86
- // The flag type has to be `option` (not `boolean`) for `env` to take effect
87
- 'enable-login-prompt': command_1.flags.option({
88
- description: 'An (internal) feature flag to enable the "prompt user to log in"' +
89
- ' feature for the users who run the CLI without being logged in. It' +
90
- ' is intentially hidden and not documented because in the future' +
91
- ' this feature will be enabled for all users and the flag will be' +
92
- ' removed.',
93
- hidden: true,
94
- options: ['true', 'false'],
95
- parse: value => Boolean(value),
96
- default: false,
97
- env: 'STEPZEN_ENABLE_LOGIN_PROMPT',
80
+ default: () => {
81
+ if (process.env.STEPZEN_INTERACTIVE !== undefined) {
82
+ return process.env.STEPZEN_INTERACTIVE.toLowerCase() === 'false';
83
+ }
84
+ return !process.stdin.isTTY || !process.stdout.isTTY;
85
+ },
98
86
  }),
99
87
  };
100
88
  exports.default = ZenCommand;
@@ -1 +1 @@
1
- {"version":"0.15.0-beta.1","commands":{"deploy":{"id":"deploy","description":"deploy to stepzen","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"configurationsets":{"name":"configurationsets","type":"option","description":"Configurationsets to use","default":""},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"schema":{"name":"schema","type":"option","description":"Schema to use","required":true},"silent":{"name":"silent","type":"boolean","allowNo":false}},"args":[{"name":"destination","description":"destination","required":true}]},"import":{"id":"import","description":"Import a schema for an external data source or a API endpoint to your GraphQL API.","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"dir":{"name":"dir","type":"option","description":"working directory"},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"silent":{"name":"silent","type":"boolean","hidden":true,"allowNo":false},"name":{"name":"name","type":"option","description":"subfolder inside the workspace folder to save the imported schema files, defaults to the imported schema name"},"prefix":{"name":"prefix","type":"option","description":"[curl] prefix to add every type in the generated schema."},"query-name":{"name":"query-name","type":"option","description":"[curl] property name to add to the Query type as a way to access the imported cURL endpoint."},"query-type":{"name":"query-type","type":"option","description":"[curl] name for the type returned by the cURL endpoint in the generated schema. The name specified by --query-type is not prefixed by --prefix if both flags are present."},"path-params":{"name":"path-params","type":"option","description":"[curl] specifies path parameters in the URL path. Can be formed by taking the original path and replacing the variable segments with $paramName placeholders.\n\nExample:\nstepzen import curl https://example.com/users/jane/posts/12 --path-params '/users/$userId/posts/$postId'"}},"args":[{"name":"schemas","required":true}]},"init":{"id":"init","description":"stepzen init","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"endpoint":{"name":"endpoint","type":"option","hidden":true},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"yes":{"name":"yes","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"directory","hidden":true}]},"lint":{"id":"lint","description":"stepzen lint","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"dir":{"name":"dir","type":"option","hidden":true},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"list":{"id":"list","description":"list your items","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[{"name":"type","description":"type","required":true,"options":["configurationsets","schemas"]}]},"login":{"id":"login","description":"Log in to StepZen","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"account":{"name":"account","type":"option","char":"a","hidden":true},"adminkey":{"name":"adminkey","type":"option","char":"k","hidden":true},"public":{"name":"public","type":"boolean","description":"Create a public anonymous StepZen account and use it. This is handy for trying StepZen out, but it not suitable for handling private data as all endpoints created with a public account will be public.","allowNo":false},"config":{"name":"config","type":"option","hidden":true},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"logout":{"id":"logout","description":"log out of stepzen","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"start":{"id":"start","description":"upload and deploy your schema","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"dir":{"name":"dir","type":"option","description":"working directory"},"endpoint":{"name":"endpoint","type":"option","description":"Override workspace endpoint"},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"no-console":{"name":"no-console","type":"boolean","hidden":true,"allowNo":false},"no-dashboard":{"name":"no-dashboard","type":"boolean","hidden":true,"allowNo":false},"no-init":{"name":"no-init","type":"boolean","hidden":true,"allowNo":false},"no-server":{"name":"no-server","type":"boolean","hidden":true,"allowNo":false},"no-validate":{"name":"no-validate","type":"boolean","hidden":true,"allowNo":false},"no-watcher":{"name":"no-watcher","type":"boolean","hidden":true,"allowNo":false},"port":{"name":"port","type":"option","default":5001}},"args":[]},"transpile":{"id":"transpile","description":"transpile a graphql schema","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"config":{"name":"config","type":"option","hidden":true},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"hide-output":{"name":"hide-output","type":"boolean","hidden":true,"allowNo":false},"inspect":{"name":"inspect","type":"boolean","char":"i","hidden":true,"allowNo":false},"inspect-after":{"name":"inspect-after","type":"boolean","hidden":true,"allowNo":false},"output-configuration":{"name":"output-configuration","type":"boolean","allowNo":false},"silent":{"name":"silent","type":"boolean","allowNo":false}},"args":[{"name":"folder","required":true}]},"upload":{"id":"upload","description":"upload to stepzen","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"dir":{"name":"dir","type":"option","description":"A directory to upload"},"file":{"name":"file","type":"option","description":"A file to upload"},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"silent":{"name":"silent","type":"boolean","allowNo":false}},"args":[{"name":"type","description":"type","required":true,"options":["configurationset","schema"]},{"name":"destination","description":"destination","required":true}]},"validate":{"id":"validate","description":"validate a graphql schema","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[{"name":"folder","required":true}]},"whoami":{"id":"whoami","description":"stepzen whoami","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"enable-login-prompt":{"name":"enable-login-prompt","type":"option","description":"An (internal) feature flag to enable the \"prompt user to log in\" feature for the users who run the CLI without being logged in. It is intentially hidden and not documented because in the future this feature will be enabled for all users and the flag will be removed.","hidden":true,"options":["true","false"],"default":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"showkeys":{"name":"showkeys","type":"boolean","allowNo":false},"apikey":{"name":"apikey","type":"boolean","allowNo":false},"adminkey":{"name":"adminkey","type":"boolean","allowNo":false}},"args":[]}}}
1
+ {"version":"0.16.0-beta.0","commands":{"deploy":{"id":"deploy","description":"deploy to stepzen","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"configurationsets":{"name":"configurationsets","type":"option","description":"Configurationsets to use","default":""},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"schema":{"name":"schema","type":"option","description":"Schema to use","required":true},"silent":{"name":"silent","type":"boolean","allowNo":false}},"args":[{"name":"destination","description":"destination","required":true}]},"import":{"id":"import","description":"Import a schema for an external data source or a API endpoint to your GraphQL API.","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"dir":{"name":"dir","type":"option","description":"working directory"},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"silent":{"name":"silent","type":"boolean","hidden":true,"allowNo":false},"name":{"name":"name","type":"option","description":"subfolder inside the workspace folder to save the imported schema files, defaults to the imported schema name"},"prefix":{"name":"prefix","type":"option","description":"[curl] prefix to add every type in the generated schema."},"query-name":{"name":"query-name","type":"option","description":"[curl] property name to add to the Query type as a way to access the imported cURL endpoint."},"query-type":{"name":"query-type","type":"option","description":"[curl] name for the type returned by the cURL endpoint in the generated schema. The name specified by --query-type is not prefixed by --prefix if both flags are present."},"path-params":{"name":"path-params","type":"option","description":"[curl] specifies path parameters in the URL path. Can be formed by taking the original path and replacing the variable segments with $paramName placeholders.\n\nExample:\nstepzen import curl https://example.com/users/jane/posts/12 --path-params '/users/$userId/posts/$postId'"},"header-param":{"name":"header-param","type":"option","description":"[curl] specifies a parameter in a header value. Can be formed by taking a -H, --header flag and replacing the variable part of the header value with a $paramName placeholder. Repeat this flag once for each header with a parameter.\n\nExample:\nstepzen import curl https://example.com/api/customers \\\n\t-H \"Authorization: apikey SecretAPIKeyValue\" \\\n\t--header-param 'Authorization: apikey $apikey'"},"db-host":{"name":"db-host","type":"option","description":"[mysql, postgresql] database host"},"db-user":{"name":"db-user","type":"option","description":"[mysql, postgresql] database user name"},"db-password":{"name":"db-password","type":"option","description":"[mysql, postgresql] database password"},"db-database":{"name":"db-database","type":"option","description":"[mysql, postgresql] name of database to import"},"db-schema":{"name":"db-schema","type":"option","description":"[postgresql] database schema"}},"args":[{"name":"schemas","required":true}]},"init":{"id":"init","description":"stepzen init","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"endpoint":{"name":"endpoint","type":"option","hidden":true},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"yes":{"name":"yes","type":"boolean","hidden":true,"allowNo":false}},"args":[{"name":"directory","hidden":true}]},"lint":{"id":"lint","description":"stepzen lint","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"dir":{"name":"dir","type":"option","hidden":true},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"list":{"id":"list","description":"list your items","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[{"name":"type","description":"type","required":true,"options":["configurationsets","schemas"]}]},"login":{"id":"login","description":"Log in to StepZen","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"account":{"name":"account","type":"option","char":"a","hidden":true},"adminkey":{"name":"adminkey","type":"option","char":"k","hidden":true},"public":{"name":"public","type":"boolean","description":"Create a public anonymous StepZen account and use it. This is handy for trying StepZen out, but it not suitable for handling private data as all endpoints created with a public account will be public.","allowNo":false},"config":{"name":"config","type":"option","hidden":true},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"logout":{"id":"logout","description":"log out of stepzen","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"start":{"id":"start","description":"upload and deploy your schema","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"dir":{"name":"dir","type":"option","description":"working directory"},"endpoint":{"name":"endpoint","type":"option","description":"Override workspace endpoint"},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"no-console":{"name":"no-console","type":"boolean","hidden":true,"allowNo":false},"no-dashboard":{"name":"no-dashboard","type":"boolean","hidden":true,"allowNo":false},"no-init":{"name":"no-init","type":"boolean","hidden":true,"allowNo":false},"no-server":{"name":"no-server","type":"boolean","hidden":true,"allowNo":false},"no-validate":{"name":"no-validate","type":"boolean","hidden":true,"allowNo":false},"no-watcher":{"name":"no-watcher","type":"boolean","hidden":true,"allowNo":false},"port":{"name":"port","type":"option","default":5001}},"args":[]},"transpile":{"id":"transpile","description":"transpile a graphql schema","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"config":{"name":"config","type":"option","hidden":true},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"hide-output":{"name":"hide-output","type":"boolean","hidden":true,"allowNo":false},"inspect":{"name":"inspect","type":"boolean","char":"i","hidden":true,"allowNo":false},"inspect-after":{"name":"inspect-after","type":"boolean","hidden":true,"allowNo":false},"output-configuration":{"name":"output-configuration","type":"boolean","allowNo":false},"silent":{"name":"silent","type":"boolean","allowNo":false}},"args":[{"name":"folder","required":true}]},"upload":{"id":"upload","description":"upload to stepzen","pluginName":"stepzen","pluginType":"core","aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"dir":{"name":"dir","type":"option","description":"A directory to upload"},"file":{"name":"file","type":"option","description":"A file to upload"},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"silent":{"name":"silent","type":"boolean","allowNo":false}},"args":[{"name":"type","description":"type","required":true,"options":["configurationset","schema"]},{"name":"destination","description":"destination","required":true}]},"validate":{"id":"validate","description":"validate a graphql schema","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[{"name":"folder","required":true}]},"whoami":{"id":"whoami","description":"stepzen whoami","pluginName":"stepzen","pluginType":"core","hidden":true,"aliases":[],"flags":{"non-interactive":{"name":"non-interactive","type":"boolean","description":"disable all interactive prompts","hidden":true,"allowNo":false},"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"showkeys":{"name":"showkeys","type":"boolean","allowNo":false},"apikey":{"name":"apikey","type":"boolean","allowNo":false},"adminkey":{"name":"adminkey","type":"boolean","allowNo":false}},"args":[]}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stepzen",
3
3
  "description": "The StepZen CLI",
4
- "version": "0.15.0-beta.1",
4
+ "version": "0.16.0-beta.0",
5
5
  "license": "MIT",
6
6
  "author": "Darren Waddell <darren@stepzen.com>",
7
7
  "contributors": [
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "main": "lib/index.js",
15
15
  "engines": {
16
- "node": ">=14.17.0",
16
+ "node": ">=14.0",
17
17
  "npm": ">=6.14"
18
18
  },
19
19
  "files": [
@@ -28,7 +28,7 @@
28
28
  "dependencies": {
29
29
  "@oclif/command": "^1.8.0",
30
30
  "@oclif/config": "^1.18.3",
31
- "@oclif/core": "1.6.3",
31
+ "@oclif/core": "1.7.0",
32
32
  "@oclif/errors": "1.3.5",
33
33
  "@oclif/plugin-help": "^5.1.12",
34
34
  "@stepzen/dashboard": "0.1.37",
@@ -38,7 +38,7 @@
38
38
  "chokidar": "^3.5.2",
39
39
  "compare-versions": "^3.6.0",
40
40
  "date-fns": "^2.26.0",
41
- "debug": "^4.3.0",
41
+ "debug": "^4.3.4",
42
42
  "detect-port": "^1.3.0",
43
43
  "dotenv": "^8.6.0",
44
44
  "fs-extra": "^9.1.0",
@@ -46,10 +46,11 @@
46
46
  "inquirer": "^7.3.3",
47
47
  "lodash": "^4.17.21",
48
48
  "node-fetch": "^2.6.6",
49
- "prettier": "^2.4.1",
49
+ "prettier": "^2.6.2",
50
50
  "pretty-ms": "^7.0.1",
51
51
  "shelljs": "^0.8.5",
52
52
  "throttle-debounce": "^3.0.1",
53
+ "uuid": "^8.3.2",
53
54
  "yaml": "^1.10.0"
54
55
  },
55
56
  "devDependencies": {
@@ -69,14 +70,16 @@
69
70
  "@types/minimist": "^1.2.1",
70
71
  "@types/mocha": "^5.2.7",
71
72
  "@types/mock-fs": "^4.13.0",
72
- "@types/node": "^14.18.13",
73
+ "@types/node": "^14.0.27",
73
74
  "@types/node-fetch": "^2.5.7",
74
75
  "@types/prettier": "^2.1.6",
75
76
  "@types/rimraf": "^3.0.0",
77
+ "@types/semver": "^7.3.9",
76
78
  "@types/shelljs": "^0.8.8",
77
79
  "@types/sinon-chai": "3.2.8",
78
80
  "@types/throttle-debounce": "^2.1.0",
79
81
  "@types/unzipper": "^0.10.4",
82
+ "@types/uuid": "^8.3.4",
80
83
  "chai": "^4.2.0",
81
84
  "copyfiles": "^2.4.1",
82
85
  "cross-env": "^7.0.3",
@@ -93,8 +96,10 @@
93
96
  "nock": "^13.0.4",
94
97
  "nodemon": "^2.0.7",
95
98
  "nyc": "^14.1.1",
99
+ "semver": "^7.3.7",
96
100
  "sinon": "13.0.1",
97
101
  "sinon-chai": "3.7.0",
102
+ "strip-ansi": "^6.0.1",
98
103
  "ts-node": "^8.10.2",
99
104
  "typescript": "^3.9.7"
100
105
  },
@@ -118,9 +123,9 @@
118
123
  "postpack": "rm -f oclif.manifest.json",
119
124
  "posttest": "eslint . --ignore-path .prettierignore",
120
125
  "prepack": "rm -rf lib && tsc -b && oclif-dev manifest && oclif-dev readme",
121
- "test": "tsc -b && cross-env STEPZEN_DOMAIN=steprz.io STEPZEN_SERVER_URL=http://localhost nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
126
+ "test": "tsc -b && cross-env STEPZEN_INTERACTIVE=true STEPZEN_DOMAIN=steprz.io STEPZEN_SERVER_URL=http://localhost nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
122
127
  "test:single:example": "npm run test:single -- test/src/shared/curl-parser.test.ts",
123
- "test:single": "tsc -b && cross-env STEPZEN_DOMAIN=steprz.io STEPZEN_SERVER_URL=http://localhost mocha --forbid-only test/_global.test.ts",
128
+ "test:single": "tsc -b && cross-env STEPZEN_INTERACTIVE=true STEPZEN_DOMAIN=steprz.io STEPZEN_SERVER_URL=http://localhost mocha --forbid-only test/_global.test.ts",
124
129
  "version": "oclif-dev readme && git add README.md",
125
130
  "prepare": "husky install"
126
131
  },