stepzen 0.17.0 → 0.18.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.17.0 darwin-x64 node-v14.19.3
32
+ stepzen/0.18.0-beta.0 darwin-x64 node-v14.19.3
33
33
  $ stepzen --help [COMMAND]
34
34
  USAGE
35
35
  $ stepzen COMMAND
@@ -91,6 +91,13 @@ USAGE
91
91
  $ stepzen import SCHEMA
92
92
 
93
93
  OPTIONS
94
+ -H, --header=header
95
+ [curl, graphql] specifies a request header to pass
96
+
97
+ Example:
98
+ stepzen import curl https://example.com/api/customers \
99
+ -H "Authorization: apikey SecretAPIKeyValue"
100
+
94
101
  -h, --help
95
102
  show CLI help
96
103
 
@@ -113,8 +120,8 @@ OPTIONS
113
120
  working directory
114
121
 
115
122
  --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
123
+ [curl, graphql] specifies a parameter in a header value. Can be formed by taking a -H, --header flag and replacing
124
+ the variable part of the header value with a $paramName placeholder. Repeat this flag once for each header with a
118
125
  parameter.
119
126
 
120
127
  Example:
@@ -1,16 +1,18 @@
1
1
  import { flags } from '@oclif/command';
2
2
  import ZenCommand from '../shared/zen-command';
3
+ import { HeaderInput } from '../shared/header';
3
4
  import { Workspace } from '../shared/types';
4
5
  export default class Import extends ZenCommand {
5
6
  static description: string;
6
7
  static commonIntrospectionFlags: {
7
8
  prefix: flags.IOptionFlag<string | undefined>;
9
+ header: flags.IOptionFlag<string[]>;
10
+ 'header-param': flags.IOptionFlag<string[]>;
8
11
  };
9
12
  static curlFlags: {
10
13
  'query-name': flags.IOptionFlag<string | undefined>;
11
14
  'query-type': flags.IOptionFlag<string | undefined>;
12
15
  'path-params': flags.IOptionFlag<string | undefined>;
13
- 'header-param': flags.IOptionFlag<string[]>;
14
16
  };
15
17
  static sqlFlags: {
16
18
  'db-host': flags.IOptionFlag<string | undefined>;
@@ -24,6 +26,8 @@ export default class Import extends ZenCommand {
24
26
  static flagsForSchemas: ({
25
27
  flags: {
26
28
  prefix: flags.IOptionFlag<string | undefined>;
29
+ header: flags.IOptionFlag<string[]>;
30
+ 'header-param': flags.IOptionFlag<string[]>;
27
31
  };
28
32
  schemas: string[];
29
33
  } | {
@@ -31,7 +35,6 @@ export default class Import extends ZenCommand {
31
35
  'query-name': flags.IOptionFlag<string | undefined>;
32
36
  'query-type': flags.IOptionFlag<string | undefined>;
33
37
  'path-params': flags.IOptionFlag<string | undefined>;
34
- 'header-param': flags.IOptionFlag<string[]>;
35
38
  };
36
39
  schemas: string[];
37
40
  } | {
@@ -57,8 +60,9 @@ export default class Import extends ZenCommand {
57
60
  'query-name': flags.IOptionFlag<string | undefined>;
58
61
  'query-type': flags.IOptionFlag<string | undefined>;
59
62
  'path-params': flags.IOptionFlag<string | undefined>;
60
- 'header-param': flags.IOptionFlag<string[]>;
61
63
  prefix: flags.IOptionFlag<string | undefined>;
64
+ header: flags.IOptionFlag<string[]>;
65
+ 'header-param': flags.IOptionFlag<string[]>;
62
66
  dir: flags.IOptionFlag<string | undefined>;
63
67
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
64
68
  silent: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
@@ -85,8 +89,9 @@ export default class Import extends ZenCommand {
85
89
  'query-name': string | undefined;
86
90
  'query-type': string | undefined;
87
91
  'path-params': string | undefined;
88
- 'header-param': string[];
89
92
  prefix: string | undefined;
93
+ header: string[];
94
+ 'header-param': string[];
90
95
  dir: string | undefined;
91
96
  help: void;
92
97
  silent: boolean;
@@ -96,4 +101,5 @@ export default class Import extends ZenCommand {
96
101
  }, {
97
102
  [name: string]: any;
98
103
  }>;
104
+ parseHeaderFlags(headerFlagValues?: string[], headerParamFlagValues?: string[]): HeaderInput[];
99
105
  }
@@ -19,6 +19,7 @@ const graphql2sdl_1 = require("../generate/graphql2sdl");
19
19
  const constants_1 = require("../shared/constants");
20
20
  const path_params_parser_1 = require("../shared/path-params-parser");
21
21
  const header_params_parser_1 = require("../shared/header-params-parser");
22
+ const header_1 = require("../shared/header");
22
23
  class Import extends zen_command_1.default {
23
24
  async run() {
24
25
  const { args, argv, flags } = this.parseWorkaround();
@@ -29,6 +30,7 @@ class Import extends zen_command_1.default {
29
30
  // Define a sane onConflict behaviour (possibly interactive)
30
31
  const onConflict = await this.ensureOnConflictBehavior(workspace, schema, flags);
31
32
  this.warnAboutIgnoredFlags(schema, flags);
33
+ const headers = this.parseHeaderFlags(flags.header, flags['header-param']);
32
34
  // Select an import execution flow:
33
35
  // - v1 with `stepzen/engines` and cloud function
34
36
  // - v2 with the graphqlize service
@@ -61,7 +63,9 @@ class Import extends zen_command_1.default {
61
63
  'curl command instead of the URL.');
62
64
  console.log();
63
65
  const curlAnswers = await curl2sdl_1.askCurlQuestions(editableOptions);
64
- curl2sdlOptions = Object.assign(Object.assign(Object.assign({}, fixedOptions), curlAnswers), { headers: curlAnswers.curlArgs.headers });
66
+ curl2sdlOptions = Object.assign(Object.assign(Object.assign({}, fixedOptions), curlAnswers), {
67
+ // include boths headers passed via flags and headers entered interactively
68
+ headers: headers.concat(curlAnswers.curlArgs.headers) });
65
69
  }
66
70
  else {
67
71
  // run non-interative
@@ -73,15 +77,11 @@ class Import extends zen_command_1.default {
73
77
  if ('error' in parsedPathParamsOrError) {
74
78
  throw new errors_1.CLIError(parsedPathParamsOrError.error);
75
79
  }
76
- const headersOrError = header_params_parser_1.makeHeaders(argsOrError.headers, flags['header-param']);
77
- if ('error' in headersOrError) {
78
- throw new errors_1.CLIError(headersOrError.error);
79
- }
80
- const maybeDuplicateParamsMessage = curl2sdl_1.makeDuplicateParamsMessage(headersOrError, parsedPathParamsOrError, argsOrError.url);
80
+ const maybeDuplicateParamsMessage = curl2sdl_1.makeDuplicateParamsMessage(headers, parsedPathParamsOrError, argsOrError.url);
81
81
  if (maybeDuplicateParamsMessage) {
82
82
  throw new errors_1.CLIError(maybeDuplicateParamsMessage);
83
83
  }
84
- curl2sdlOptions = Object.assign(Object.assign(Object.assign({}, fixedOptions), editableOptions), { pathParams: parsedPathParamsOrError, headers: headersOrError, curlArgs: argsOrError });
84
+ curl2sdlOptions = Object.assign(Object.assign(Object.assign({}, fixedOptions), editableOptions), { pathParams: parsedPathParamsOrError, headers: headers, curlArgs: argsOrError });
85
85
  }
86
86
  core_1.CliUx.ux.action.start('Starting');
87
87
  const resultOrError = await curl2sdl_1.curl2sdl(curl2sdlOptions);
@@ -97,6 +97,7 @@ class Import extends zen_command_1.default {
97
97
  else if (schema === 'graphql') {
98
98
  const editableOptions = {
99
99
  typePrefix: flags.prefix,
100
+ headers: headers,
100
101
  };
101
102
  let answers;
102
103
  if (argv.length === 1) {
@@ -217,37 +218,64 @@ class Import extends zen_command_1.default {
217
218
  parseWorkaround() {
218
219
  const argv = this.argv;
219
220
  const parsed = this.parse(Import, argv);
220
- if (!parsed.flags['header-param']) {
221
- return parsed;
222
- }
223
- // Every ['--header-param', value] pair in the argv array should match
224
- // only one value in the 'header-param' flag's value list.
225
- const matchedIndices = new Set();
226
- // For each value in the 'header-param' flag's value list, find the original
227
- // ['--header-param', value] pair in the argv array.
228
- for (let i = 0; i < parsed.flags['header-param'].length; i++) {
229
- const value = parsed.flags['header-param'][i];
230
- const flagIdx = argv.findIndex((arg, idx) => {
231
- const spaceDelimMatch = arg === '--header-param' &&
232
- idx < argv.length &&
233
- argv[idx + 1] === value;
234
- const eqlDelimMatch = arg === `--header-param=${value}`;
235
- return (spaceDelimMatch || eqlDelimMatch) && !matchedIndices.has(idx);
236
- });
237
- // If not found, that means the given value was associated with the
238
- // `--header-param` flag incorrectly. In that case, it's moved from
239
- // `flags` to `argv` in the parsed output.
240
- if (flagIdx === -1) {
241
- parsed.flags['header-param'].splice(i, 1);
242
- i -= 1; // compensate for the .splice() call above
243
- parsed.argv.push(value);
244
- }
245
- else {
246
- matchedIndices.add(flagIdx);
221
+ const fixFlag = (flag) => {
222
+ if (parsed.flags[flag]) {
223
+ // Every ['--flag', value] pair in the argv array should match
224
+ // only one value in the flag's value list.
225
+ const matchedIndices = new Set();
226
+ // For each value in the flag's value list, find the original
227
+ // ['--flag', value] pair in the argv array.
228
+ for (let i = 0; i < parsed.flags[flag].length; i++) {
229
+ const value = parsed.flags[flag][i];
230
+ const flagIdx = argv.findIndex((arg, idx) => {
231
+ const spaceDelimMatch = (arg === `--${flag}` ||
232
+ (Import.flags[flag].char &&
233
+ arg === `-${Import.flags[flag].char}`)) &&
234
+ idx < argv.length &&
235
+ argv[idx + 1] === value;
236
+ const eqlDelimMatch = arg === `--${flag}=${value}`;
237
+ return ((spaceDelimMatch || eqlDelimMatch) && !matchedIndices.has(idx));
238
+ });
239
+ // If not found, that means the given value was associated with the
240
+ // `flag` incorrectly. In that case, it's moved from `flags` to `argv` in
241
+ // the parsed output.
242
+ if (flagIdx === -1) {
243
+ parsed.flags[flag].splice(i, 1);
244
+ i -= 1; // compensate for the .splice() call above
245
+ parsed.argv.push(value);
246
+ }
247
+ else {
248
+ matchedIndices.add(flagIdx);
249
+ }
250
+ }
247
251
  }
248
- }
252
+ };
253
+ fixFlag('header');
254
+ fixFlag('header-param');
249
255
  return parsed;
250
256
  }
257
+ parseHeaderFlags(headerFlagValues, headerParamFlagValues) {
258
+ const headers = [];
259
+ if (headerFlagValues) {
260
+ headerFlagValues.forEach(value => {
261
+ const headerOrError = header_1.parseHeader(value);
262
+ if (headerOrError && 'error' in headerOrError) {
263
+ throw new errors_1.CLIError(headerOrError.error);
264
+ }
265
+ // A `null` from parseCurlHeaderString() means a header should NOT be
266
+ // sent. This is not supported by zenserv / the introspection service
267
+ // so the CLI simply omits such headers
268
+ if (headerOrError) {
269
+ headers.push(headerOrError); // header
270
+ }
271
+ });
272
+ }
273
+ const headersOrError = header_params_parser_1.makeHeaders(headers, headerParamFlagValues);
274
+ if ('error' in headersOrError) {
275
+ throw new errors_1.CLIError(headersOrError.error);
276
+ }
277
+ return headersOrError;
278
+ }
251
279
  }
252
280
  exports.default = Import;
253
281
  Import.description = 'import a schema for an external data source or a API endpoint to your GraphQL API';
@@ -255,6 +283,26 @@ Import.commonIntrospectionFlags = {
255
283
  prefix: command_1.flags.string({
256
284
  description: '[curl, graphql] prefix to add every type in the generated schema.',
257
285
  }),
286
+ header: command_1.flags.string({
287
+ char: 'H',
288
+ description: `[curl, graphql] specifies a request header to pass` +
289
+ `\n` +
290
+ `\nExample:` +
291
+ `\nstepzen import curl https://example.com/api/customers \\` +
292
+ `\n\t-H ${chalk.bold('"Authorization: apikey SecretAPIKeyValue"')}`,
293
+ multiple: true,
294
+ }),
295
+ 'header-param': command_1.flags.string({
296
+ description: `[curl, graphql] specifies a parameter in a header value.` +
297
+ ` Can be formed by taking a ${chalk.bold('-H, --header')} flag and replacing the` +
298
+ ` variable part of the header value with a ${chalk.bold('$paramName')} placeholder. Repeat this flag once for each header with a parameter.` +
299
+ `\n` +
300
+ `\nExample:` +
301
+ `\nstepzen import curl https://example.com/api/customers \\` +
302
+ `\n\t-H "Authorization: apikey SecretAPIKeyValue" \\` +
303
+ `\n\t--header-param 'Authorization: apikey ${chalk.bold('$apikey')}'`,
304
+ multiple: true,
305
+ }),
258
306
  };
259
307
  Import.curlFlags = {
260
308
  'query-name': command_1.flags.string({
@@ -275,17 +323,6 @@ Import.curlFlags = {
275
323
  ` --path-params` +
276
324
  ` '/users/${chalk.bold('$userId')}/posts/${chalk.bold('$postId')}'`,
277
325
  }),
278
- 'header-param': command_1.flags.string({
279
- description: `[curl] specifies a parameter in a header value.` +
280
- ` Can be formed by taking a ${chalk.bold('-H, --header')} flag and replacing the` +
281
- ` variable part of the header value with a ${chalk.bold('$paramName')} placeholder. Repeat this flag once for each header with a parameter.` +
282
- `\n` +
283
- `\nExample:` +
284
- `\nstepzen import curl https://example.com/api/customers \\` +
285
- `\n\t-H "Authorization: apikey SecretAPIKeyValue" \\` +
286
- `\n\t--header-param 'Authorization: apikey ${chalk.bold('$apikey')}'`,
287
- multiple: true,
288
- }),
289
326
  };
290
327
  Import.sqlFlags = {
291
328
  'db-host': command_1.flags.string({
@@ -12,7 +12,11 @@ class WhoAmI extends zen_command_1.default {
12
12
  const configuration = await configuration_1.readConfiguration();
13
13
  if (!('account' in configuration)) {
14
14
  this.log('You are not logged in.');
15
- this.exit();
15
+ return;
16
+ }
17
+ if (configuration.account.startsWith('public')) {
18
+ this.log(`Account: ${chalk.bold(configuration.account)}`);
19
+ return;
16
20
  }
17
21
  const details = {
18
22
  account: `${configuration.account} `,
@@ -5,10 +5,10 @@ export declare type GraphQL2SdlOptions = {
5
5
  endpoint: string;
6
6
  typePrefix?: string;
7
7
  includeRootOperations?: boolean;
8
- header?: string;
8
+ headers: readonly HeaderInput[];
9
9
  onConflict: 'overwrite' | 'append';
10
10
  };
11
- export declare type EditableGraphQL2SdlOptions = Pick<GraphQL2SdlOptions, 'endpoint' | 'typePrefix' | 'includeRootOperations' | 'header'>;
11
+ export declare type EditableGraphQL2SdlOptions = Pick<GraphQL2SdlOptions, 'endpoint' | 'typePrefix' | 'includeRootOperations' | 'headers'>;
12
12
  export declare type GraphQLAnswers = {
13
13
  endpoint: string;
14
14
  typePrefix: string;
@@ -16,7 +16,7 @@ export declare type GraphQLAnswers = {
16
16
  headers: readonly HeaderInput[];
17
17
  };
18
18
  export declare const askGraphQLQuestions: (defaultAnswers?: Partial<GraphQLAnswers>) => Promise<EditableGraphQL2SdlOptions>;
19
- export declare const graphql2sdl: ({ name, source, endpoint, typePrefix, includeRootOperations, header, onConflict, }: GraphQL2SdlOptions) => Promise<{
19
+ export declare const graphql2sdl: ({ name, source, endpoint, typePrefix, includeRootOperations, headers, onConflict, }: GraphQL2SdlOptions) => Promise<{
20
20
  error: string;
21
21
  } | {
22
22
  outPath: string;
@@ -33,7 +33,7 @@ exports.askGraphQLQuestions = async (defaultAnswers = {}) => {
33
33
  questions = questions.map(question => (Object.assign(Object.assign({}, question), { default: lodash_1.get(defaultAnswers, question.name) })));
34
34
  return inquirer.prompt(questions);
35
35
  };
36
- exports.graphql2sdl = async ({ name, source, endpoint, typePrefix, includeRootOperations, header, onConflict, }) => {
36
+ exports.graphql2sdl = async ({ name, source, endpoint, typePrefix, includeRootOperations, headers, onConflict, }) => {
37
37
  const response = await helpers_1.queryIntrospectionService({
38
38
  operation: 'getSDLFromGraphQL',
39
39
  variables: {
@@ -51,7 +51,7 @@ exports.graphql2sdl = async ({ name, source, endpoint, typePrefix, includeRootOp
51
51
  },
52
52
  headers: {
53
53
  type: '[HeaderInput!]',
54
- value: header ? [header_1.parseHeaderNameValue(header)] : null,
54
+ value: headers && headers.length > 0 ? headers : null,
55
55
  },
56
56
  },
57
57
  });
@@ -9,7 +9,7 @@ const path = require("path");
9
9
  const transpiler = require("@stepzen/transpiler");
10
10
  const helpers_1 = require("./helpers");
11
11
  exports.default = async ({ schema, name, onConflict, source, preAnswered = {}, }) => {
12
- var _a, _b;
12
+ var _a, _b, _c;
13
13
  // Store the generator
14
14
  let generator;
15
15
  // Initial answers from flags, keyed as <schema>_config
@@ -28,6 +28,9 @@ exports.default = async ({ schema, name, onConflict, source, preAnswered = {}, }
28
28
  answers: undefined,
29
29
  };
30
30
  }
31
+ if ((_b = configure === null || configure === void 0 ? void 0 : configure.errors) === null || _b === void 0 ? void 0 : _b.error) {
32
+ throw new errors_1.CLIError(configure.errors.error);
33
+ }
31
34
  // For housekeeping
32
35
  const tempFiles = [];
33
36
  const cleanUp = async () => {
@@ -47,7 +50,7 @@ exports.default = async ({ schema, name, onConflict, source, preAnswered = {}, }
47
50
  const config = fs.readFileSync(file, 'utf8');
48
51
  const json = JSON.parse(config);
49
52
  generator = {
50
- questions: ((_b = json === null || json === void 0 ? void 0 : json.config) === null || _b === void 0 ? void 0 : _b.questions) || [],
53
+ questions: ((_c = json === null || json === void 0 ? void 0 : json.config) === null || _c === void 0 ? void 0 : _c.questions) || [],
51
54
  type: 'template',
52
55
  answers: undefined,
53
56
  };
@@ -1,26 +1,11 @@
1
- export interface CurlHeader {
2
- name: string;
3
- value: string;
4
- }
1
+ import { Header } from './header';
2
+ import { ParseError } from './types';
5
3
  export interface CurlArguments {
6
4
  url: string;
7
5
  method: 'Get' | 'Post';
8
- headers: CurlHeader[];
6
+ headers: Header[];
9
7
  data?: string;
10
8
  }
11
- export interface ParseError {
12
- error: string;
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;
24
9
  /**
25
10
  * Parse a curl command line arguments array to a JSON structure consumable by
26
11
  * the StepZen introspection service backend.
@@ -1,54 +1,16 @@
1
1
  "use strict";
2
2
  // Copyright (c) 2020,2021,2022, StepZen, Inc.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.parseCurlArgv = exports.parseCurlHeaderString = void 0;
4
+ exports.parseCurlArgv = void 0;
5
5
  const constants_1 = require("./constants");
6
+ const header_1 = require("./header");
6
7
  // Based on the existing curl flags at https://curl.se/docs/manpage.html
7
8
  const curlFlagRegex = /^(-[a-zA-Z0-9.:#])|(--[a-zA-Z0-9.-]+)$/;
8
- // Based on the definition of a `token` at
9
- // https://datatracker.ietf.org/doc/html/rfc2616#page-17
10
- const httpHeaderRegex = /^(?<name>[a-zA-Z0-9!#$%&'*+,-.^_`|~]+)\s*:(?<value>.*)$/;
11
9
  const httpURLRegex = /^https?:\/\//i;
12
- /**
13
- * Parse a header string according to https://curl.se/docs/manpage.html#-H
14
- * and https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
15
- *
16
- * @param {*} header a curl header string, e.g. `"api-key: asfdasdfad"`
17
- * @returns {*} a name/value record or
18
- * `null` for the `Header:` notation that means "remove this header" in
19
- * the cURL spec or a error object if cannot parse the string.
20
- */
21
- exports.parseCurlHeaderString = (header) => {
22
- const trimmed = header.trim();
23
- // Check if it's a `Header:` case
24
- if (trimmed.indexOf(':') === trimmed.length - 1) {
25
- // the user intent was to _remove_ this header
26
- return null;
27
- }
28
- // Check if it's a `Header;` case
29
- if (trimmed.indexOf(':') === -1 &&
30
- trimmed.indexOf(';') === trimmed.length - 1) {
31
- return {
32
- name: trimmed.substring(0, trimmed.length - 1).trim(),
33
- value: '',
34
- };
35
- }
36
- const match = trimmed.match(httpHeaderRegex);
37
- if (!match || !match.groups || !match.groups.name || !match.groups.value) {
38
- return {
39
- error: `Unexpected header syntax in "${header}". ` +
40
- `Expected "[name]: [value]", "[name];" or "[name]:"`,
41
- };
42
- }
43
- return {
44
- name: match.groups.name,
45
- value: match.groups.value.trim(),
46
- };
47
- };
48
10
  const isAFlagArg = (arg) => curlFlagRegex.test(arg);
49
11
  const isAURL = (arg) => httpURLRegex.test(arg);
50
12
  const parseHeaderFlag = (value, partialCurlArgs) => {
51
- const headerOrError = exports.parseCurlHeaderString(value);
13
+ const headerOrError = header_1.parseHeader(value);
52
14
  if (headerOrError && 'error' in headerOrError) {
53
15
  return headerOrError; // error
54
16
  }
@@ -1,7 +1,7 @@
1
- import { HeaderInput } from './header';
2
- import { ParseError, CurlHeader } from './curl-parser';
3
- export declare const parseHeaderParam: (headers: readonly CurlHeader[], headerParam: string) => {
1
+ import { Header, HeaderInput } from './header';
2
+ import { ParseError } from './types';
3
+ export declare const parseHeaderParam: (headers: readonly Header[], headerParam: string) => {
4
4
  header: HeaderInput;
5
- remainingHeaders: CurlHeader[];
5
+ remainingHeaders: Header[];
6
6
  } | ParseError;
7
- export declare const makeHeaders: (curlHeaders: readonly CurlHeader[], headerParams?: readonly string[]) => ParseError | HeaderInput[];
7
+ export declare const makeHeaders: (curlHeaders: readonly Header[], headerParams?: readonly string[]) => ParseError | HeaderInput[];
@@ -4,10 +4,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.makeHeaders = exports.parseHeaderParam = void 0;
5
5
  const chalk = require("chalk");
6
6
  const debug = require("debug");
7
- const curl_parser_1 = require("./curl-parser");
7
+ const header_1 = require("./header");
8
8
  const headerParamRegex = /^(?<prefix>([^$]|\$\$)*)(?<param>\$[_A-Za-z]\w*);?(?<suffix>.*)$/;
9
9
  exports.parseHeaderParam = (headers, headerParam) => {
10
- const headerOrError = curl_parser_1.parseCurlHeaderString(headerParam);
10
+ const headerOrError = header_1.parseHeader(headerParam);
11
11
  if (!headerOrError || 'error' in headerOrError) {
12
12
  if (headerOrError) {
13
13
  debug('stepzen:curl2sdl')(`Failed to parse a header param ${headerParam}.` +
@@ -1,7 +1,9 @@
1
- export declare type NameValueHeaderInput = {
1
+ import { ParseError } from './types';
2
+ export interface Header {
2
3
  name: string;
3
4
  value: string;
4
- };
5
+ }
6
+ export declare type NameValueHeaderInput = Header;
5
7
  export declare type NamePartsHeaderInput = {
6
8
  name: string;
7
9
  parts: HeaderInputValuePart[];
@@ -18,4 +20,13 @@ export declare type HeaderInputVariableValuePart = {
18
20
  };
19
21
  export declare type HeaderInputValuePart = HeaderInputConstantValuePart | HeaderInputVariableValuePart;
20
22
  export declare const HEADER_REGEX: RegExp;
21
- export declare const parseHeaderNameValue: (input: string) => NameValueHeaderInput | null;
23
+ /**
24
+ * Parse a header string according to https://curl.se/docs/manpage.html#-H
25
+ * and https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
26
+ *
27
+ * @param {*} header a curl header string, e.g. `"api-key: asfdasdfad"`
28
+ * @returns {*} a name/value record or
29
+ * `null` for the `Header:` notation that means "remove this header" in
30
+ * the cURL spec or a error object if cannot parse the string.
31
+ */
32
+ export declare const parseHeader: (header: string) => Header | null | ParseError;
@@ -1,13 +1,43 @@
1
1
  "use strict";
2
2
  // Copyright (c) 2020,2021,2022, StepZen, Inc.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.parseHeaderNameValue = exports.HEADER_REGEX = void 0;
5
- exports.HEADER_REGEX = /([\w-]+): (.*)/;
6
- exports.parseHeaderNameValue = (input) => {
7
- const match = input.match(exports.HEADER_REGEX);
8
- if (match) {
9
- const [name, value] = match.splice(1, 2);
10
- return { name, value };
4
+ exports.parseHeader = exports.HEADER_REGEX = void 0;
5
+ // Based on the definition of a `token` at
6
+ // https://datatracker.ietf.org/doc/html/rfc2616#page-17
7
+ exports.HEADER_REGEX = /^(?<name>[a-zA-Z0-9!#$%&'*+,-.^_`|~]+)\s*:(?<value>.*)$/;
8
+ /**
9
+ * Parse a header string according to https://curl.se/docs/manpage.html#-H
10
+ * and https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
11
+ *
12
+ * @param {*} header a curl header string, e.g. `"api-key: asfdasdfad"`
13
+ * @returns {*} a name/value record or
14
+ * `null` for the `Header:` notation that means "remove this header" in
15
+ * the cURL spec or a error object if cannot parse the string.
16
+ */
17
+ exports.parseHeader = (header) => {
18
+ const trimmed = header.trim();
19
+ // Check if it's a `Header:` case
20
+ if (trimmed.indexOf(':') === trimmed.length - 1) {
21
+ // the user intent was to _remove_ this header
22
+ return null;
11
23
  }
12
- return null;
24
+ // Check if it's a `Header;` case
25
+ if (trimmed.indexOf(':') === -1 &&
26
+ trimmed.indexOf(';') === trimmed.length - 1) {
27
+ return {
28
+ name: trimmed.substring(0, trimmed.length - 1).trim(),
29
+ value: '',
30
+ };
31
+ }
32
+ const match = trimmed.match(exports.HEADER_REGEX);
33
+ if (!match || !match.groups || !match.groups.name || !match.groups.value) {
34
+ return {
35
+ error: `Unexpected header syntax in "${header}". ` +
36
+ `Expected "[name]: [value]", "[name];" or "[name]:"`,
37
+ };
38
+ }
39
+ return {
40
+ name: match.groups.name,
41
+ value: match.groups.value.trim(),
42
+ };
13
43
  };
@@ -1,4 +1,4 @@
1
- import { ParseError } from './curl-parser';
1
+ import { ParseError } from './types';
2
2
  export declare type PathParam = {
3
3
  index: number;
4
4
  name: string;
@@ -36,3 +36,9 @@ export interface ZenCtlResponse {
36
36
  results?: Array<string>;
37
37
  success: boolean;
38
38
  }
39
+ /**
40
+ * Represents an error during parsing of a string.
41
+ */
42
+ export interface ParseError {
43
+ error: string;
44
+ }
@@ -1 +1 @@
1
- {"version":"0.17.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 to. Defaults to the name of the imported schema."},"overwrite":{"name":"overwrite","type":"boolean","description":"Overwrite any existing schema with the same name. Cannot be used without also providing a --name flag.","hidden":true,"allowNo":false},"prefix":{"name":"prefix","type":"option","description":"[curl, graphql] 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 \u001b[1m--query-type\u001b[22m is not prefixed by \u001b[1m--prefix\u001b[22m 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 \u001b[1m$paramName\u001b[22m placeholders.\n\nExample:\nstepzen import curl https://example.com/users/jane/posts/12 --path-params '/users/\u001b[1m$userId\u001b[22m/posts/\u001b[1m$postId\u001b[22m'"},"header-param":{"name":"header-param","type":"option","description":"[curl] specifies a parameter in a header value. Can be formed by taking a \u001b[1m-H, --header\u001b[22m flag and replacing the variable part of the header value with a \u001b[1m$paramName\u001b[22m 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 \u001b[1m$apikey\u001b[22m'"},"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":"schema","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-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":"display your credentials with 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":[]}}}
1
+ {"version":"0.18.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 to. Defaults to the name of the imported schema."},"overwrite":{"name":"overwrite","type":"boolean","description":"Overwrite any existing schema with the same name. Cannot be used without also providing a --name flag.","hidden":true,"allowNo":false},"prefix":{"name":"prefix","type":"option","description":"[curl, graphql] prefix to add every type in the generated schema."},"header":{"name":"header","type":"option","char":"H","description":"[curl, graphql] specifies a request header to pass\n\nExample:\nstepzen import curl https://example.com/api/customers \\\n\t-H \"Authorization: apikey SecretAPIKeyValue\""},"header-param":{"name":"header-param","type":"option","description":"[curl, graphql] 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'"},"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'"},"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":"schema","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-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":"display your credentials with 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.17.0",
4
+ "version": "0.18.0-beta.0",
5
5
  "license": "MIT",
6
6
  "author": "Darren Waddell <darren@stepzen.com>",
7
7
  "contributors": [