stepzen 0.12.0 → 0.13.0-beta.2

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.
Files changed (44) hide show
  1. package/README.md +1 -1
  2. package/lib/commands/deploy.d.ts +10 -3
  3. package/lib/commands/deploy.js +5 -13
  4. package/lib/commands/import.d.ts +14 -3
  5. package/lib/commands/import.js +21 -20
  6. package/lib/commands/init.d.ts +13 -4
  7. package/lib/commands/init.js +72 -35
  8. package/lib/commands/lint.d.ts +8 -3
  9. package/lib/commands/lint.js +3 -5
  10. package/lib/commands/list.d.ts +6 -3
  11. package/lib/commands/list.js +4 -10
  12. package/lib/commands/login.d.ts +10 -3
  13. package/lib/commands/login.js +5 -19
  14. package/lib/commands/logout.d.ts +6 -3
  15. package/lib/commands/logout.js +3 -4
  16. package/lib/commands/start.d.ts +16 -3
  17. package/lib/commands/start.js +18 -24
  18. package/lib/commands/transpile.d.ts +13 -3
  19. package/lib/commands/transpile.js +3 -10
  20. package/lib/commands/upload.d.ts +10 -3
  21. package/lib/commands/upload.js +5 -14
  22. package/lib/commands/validate.d.ts +6 -3
  23. package/lib/commands/validate.js +3 -4
  24. package/lib/commands/whoami.d.ts +9 -3
  25. package/lib/commands/whoami.js +6 -10
  26. package/lib/generate/curl2sdl.d.ts +1 -1
  27. package/lib/generate/index.d.ts +1 -1
  28. package/lib/shared/configuration.d.ts +1 -1
  29. package/lib/shared/configuration.js +1 -1
  30. package/lib/shared/curl-parser.js +65 -27
  31. package/lib/shared/moniker.d.ts +2 -0
  32. package/lib/shared/moniker.js +4 -5
  33. package/lib/shared/stepzen-sdk.d.ts +8 -0
  34. package/lib/shared/stepzen-sdk.js +22 -1
  35. package/lib/shared/types.d.ts +11 -2
  36. package/lib/shared/utils.js +6 -5
  37. package/lib/shared/workspace.d.ts +2 -1
  38. package/lib/shared/workspace.js +39 -36
  39. package/lib/shared/zen-command.d.ts +14 -0
  40. package/lib/shared/zen-command.js +80 -0
  41. package/oclif.manifest.json +1 -1
  42. package/package.json +1 -2
  43. package/lib/hooks/prerun/check-account.d.ts +0 -3
  44. package/lib/hooks/prerun/check-account.js +0 -12
@@ -1,8 +1,18 @@
1
- import { Command, flags } from '@oclif/command';
2
- export default class Transpile extends Command {
1
+ import { flags } from '@oclif/command';
2
+ import ZenCommand from '../shared/zen-command';
3
+ export default class Transpile extends ZenCommand {
3
4
  static description: string;
4
5
  static hidden: boolean;
5
- static flags: flags.Input<any>;
6
+ static flags: {
7
+ config: flags.IOptionFlag<string | undefined>;
8
+ help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
9
+ 'hide-output': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
10
+ inspect: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
11
+ 'inspect-after': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
12
+ 'output-configuration': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
13
+ silent: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
14
+ 'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
15
+ };
6
16
  static args: {
7
17
  name: string;
8
18
  required: boolean;
@@ -7,8 +7,9 @@ const errors_1 = require("@oclif/errors");
7
7
  const fs = require("fs-extra");
8
8
  const path = require("path");
9
9
  const utils_1 = require("../shared/utils");
10
+ const zen_command_1 = require("../shared/zen-command");
10
11
  const { configure, stitch, validate } = require('@stepzen/transpiler');
11
- class Transpile extends command_1.Command {
12
+ class Transpile extends zen_command_1.default {
12
13
  async run() {
13
14
  const { args, flags } = this.parse(Transpile);
14
15
  const folder = utils_1.getDirectory(args.folder);
@@ -39,15 +40,7 @@ class Transpile extends command_1.Command {
39
40
  exports.default = Transpile;
40
41
  Transpile.description = 'transpile a graphql schema';
41
42
  Transpile.hidden = true;
42
- Transpile.flags = {
43
- config: command_1.flags.string({ hidden: true }),
44
- help: command_1.flags.help({ char: 'h' }),
45
- 'hide-output': command_1.flags.boolean({ hidden: true }),
46
- inspect: command_1.flags.boolean({ char: 'i', hidden: true }),
47
- 'inspect-after': command_1.flags.boolean({ hidden: true }),
48
- 'output-configuration': command_1.flags.boolean(),
49
- silent: command_1.flags.boolean(),
50
- };
43
+ Transpile.flags = Object.assign(Object.assign({}, zen_command_1.default.flags), { config: command_1.flags.string({ hidden: true }), help: command_1.flags.help({ char: 'h' }), 'hide-output': command_1.flags.boolean({ hidden: true }), inspect: command_1.flags.boolean({ char: 'i', hidden: true }), 'inspect-after': command_1.flags.boolean({ hidden: true }), 'output-configuration': command_1.flags.boolean(), silent: command_1.flags.boolean() });
51
44
  Transpile.args = [
52
45
  {
53
46
  name: 'folder',
@@ -1,7 +1,14 @@
1
- import { Command, flags } from '@oclif/command';
2
- export default class Upload extends Command {
1
+ import { flags } from '@oclif/command';
2
+ import ZenCommand from '../shared/zen-command';
3
+ export default class Upload extends ZenCommand {
3
4
  static description: string;
4
- static flags: flags.Input<any>;
5
+ static flags: {
6
+ dir: flags.IOptionFlag<string | undefined>;
7
+ file: flags.IOptionFlag<string | undefined>;
8
+ help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
9
+ silent: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
10
+ 'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
11
+ };
5
12
  static args: ({
6
13
  name: string;
7
14
  required: boolean;
@@ -11,20 +11,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
11
11
  // provided by the user.
12
12
  const command_1 = require("@oclif/command");
13
13
  const errors_1 = require("@oclif/errors");
14
- const configuration_1 = require("../shared/configuration");
15
14
  const actions_1 = require("../shared/actions");
16
- class Upload extends command_1.Command {
15
+ const zen_command_1 = require("../shared/zen-command");
16
+ class Upload extends zen_command_1.default {
17
17
  async run() {
18
18
  const { args, flags } = this.parse(Upload);
19
19
  // Make sure that you definitely specify folder/name
20
20
  if (args.destination.includes('/') === false) {
21
21
  throw new errors_1.CLIError('You must specify the folder/name you want to use');
22
22
  }
23
- const configuration = configuration_1.readConfiguration();
24
- if (!configuration) {
25
- // Configuration not found. Most likely because someone is not logged in. Exit with error.
26
- throw new errors_1.CLIError('You are probably not logged in.');
27
- }
23
+ await this.ensureStepZenAccount();
28
24
  if (!flags.file && !flags.dir) {
29
25
  // You need to specify a file or directory. Exit with error.
30
26
  throw new errors_1.CLIError('You must specify a file or directory.');
@@ -48,15 +44,10 @@ exports.default = Upload;
48
44
  Upload.description = 'upload to stepzen';
49
45
  // The uploaded resource is either a directory or a file. In case it is the former,
50
46
  // it will be packaged into a zip archive and transferred.
51
- Upload.flags = {
52
- dir: command_1.flags.string({
47
+ Upload.flags = Object.assign(Object.assign({}, zen_command_1.default.flags), { dir: command_1.flags.string({
53
48
  description: 'A directory to upload',
54
49
  exclusive: ['file'],
55
- }),
56
- file: command_1.flags.string({ description: 'A file to upload', exclusive: ['dir'] }),
57
- help: command_1.flags.help({ char: 'h' }),
58
- silent: command_1.flags.boolean(),
59
- };
50
+ }), file: command_1.flags.string({ description: 'A file to upload', exclusive: ['dir'] }), help: command_1.flags.help({ char: 'h' }), silent: command_1.flags.boolean() });
60
51
  // the type and name of the uploaded resource.
61
52
  Upload.args = [
62
53
  {
@@ -1,8 +1,11 @@
1
- import { Command, flags } from '@oclif/command';
2
- export default class Validate extends Command {
1
+ import ZenCommand from '../shared/zen-command';
2
+ export default class Validate extends ZenCommand {
3
3
  static description: string;
4
4
  static hidden: boolean;
5
- static flags: flags.Input<any>;
5
+ static flags: {
6
+ help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
7
+ 'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
8
+ };
6
9
  static args: {
7
10
  name: string;
8
11
  required: boolean;
@@ -5,8 +5,9 @@ const chalk = require("chalk");
5
5
  const command_1 = require("@oclif/command");
6
6
  const errors_1 = require("@oclif/errors");
7
7
  const utils_1 = require("../shared/utils");
8
+ const zen_command_1 = require("../shared/zen-command");
8
9
  const { stitch, validate } = require('@stepzen/transpiler');
9
- class Validate extends command_1.Command {
10
+ class Validate extends zen_command_1.default {
10
11
  async run() {
11
12
  const { args } = this.parse(Validate);
12
13
  const folder = utils_1.getDirectory(args.folder);
@@ -24,9 +25,7 @@ class Validate extends command_1.Command {
24
25
  exports.default = Validate;
25
26
  Validate.description = 'validate a graphql schema';
26
27
  Validate.hidden = true;
27
- Validate.flags = {
28
- help: command_1.flags.help({ char: 'h' }),
29
- };
28
+ Validate.flags = Object.assign(Object.assign({}, zen_command_1.default.flags), { help: command_1.flags.help({ char: 'h' }) });
30
29
  Validate.args = [
31
30
  {
32
31
  name: 'folder',
@@ -1,8 +1,14 @@
1
- import { Command, flags } from '@oclif/command';
2
- export default class WhoAmI extends Command {
1
+ import ZenCommand from '../shared/zen-command';
2
+ export default class WhoAmI extends ZenCommand {
3
3
  static description: string;
4
4
  static hidden: boolean;
5
- static flags: flags.Input<any>;
5
+ static flags: {
6
+ help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
7
+ showkeys: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
8
+ apikey: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
9
+ adminkey: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
10
+ 'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
11
+ };
6
12
  static args: never[];
7
13
  run(): Promise<void>;
8
14
  }
@@ -5,7 +5,8 @@ const chalk = require("chalk");
5
5
  const command_1 = require("@oclif/command");
6
6
  const configuration_1 = require("../shared/configuration");
7
7
  const utils_1 = require("../shared/utils");
8
- class WhoAmI extends command_1.Command {
8
+ const zen_command_1 = require("../shared/zen-command");
9
+ class WhoAmI extends zen_command_1.default {
9
10
  async run() {
10
11
  const { flags } = this.parse(WhoAmI);
11
12
  const configuration = configuration_1.readConfiguration();
@@ -52,19 +53,14 @@ class WhoAmI extends command_1.Command {
52
53
  exports.default = WhoAmI;
53
54
  WhoAmI.description = 'stepzen whoami';
54
55
  WhoAmI.hidden = true;
55
- WhoAmI.flags = {
56
- help: command_1.flags.help({ char: 'h' }),
57
- showkeys: command_1.flags.boolean({
56
+ WhoAmI.flags = Object.assign(Object.assign({}, zen_command_1.default.flags), { help: command_1.flags.help({ char: 'h' }), showkeys: command_1.flags.boolean({
58
57
  default: false,
59
58
  exclusive: ['adminkey', 'apikey'],
60
- }),
61
- apikey: command_1.flags.boolean({
59
+ }), apikey: command_1.flags.boolean({
62
60
  default: false,
63
61
  exclusive: ['showkeys', 'adminkey'],
64
- }),
65
- adminkey: command_1.flags.boolean({
62
+ }), adminkey: command_1.flags.boolean({
66
63
  default: false,
67
64
  exclusive: ['showkeys', 'apikey'],
68
- }),
69
- };
65
+ }) });
70
66
  WhoAmI.args = [];
@@ -2,7 +2,7 @@ import { CurlArguments } from '../shared/curl-parser';
2
2
  import { PathParam } from '../shared/path-params-parser';
3
3
  export declare type Curl2SdlOptions = {
4
4
  curlArgs: CurlArguments;
5
- name: string;
5
+ name?: string;
6
6
  source: string;
7
7
  queryName?: string;
8
8
  rootType?: string;
@@ -1,2 +1,2 @@
1
- declare const _default: (schemas: any, name: string, source: string) => Promise<string>;
1
+ declare const _default: (schemas: any, name: string | undefined, source: string) => Promise<string>;
2
2
  export default _default;
@@ -1,5 +1,5 @@
1
1
  import { MachineConfiguration } from './types';
2
- export declare const readConfiguration: () => MachineConfiguration | boolean;
2
+ export declare const readConfiguration: () => MachineConfiguration | null;
3
3
  export declare const importConfiguration: (filepath: string) => MachineConfiguration;
4
4
  export declare const removeConfigurationFromLoginFile: () => boolean;
5
5
  export declare const writeConfigurationToLoginFile: (configuration: MachineConfiguration) => MachineConfiguration;
@@ -27,7 +27,7 @@ exports.readConfiguration = () => {
27
27
  return parsed;
28
28
  }
29
29
  catch (_a) {
30
- return false;
30
+ return null;
31
31
  }
32
32
  };
33
33
  exports.importConfiguration = (filepath) => {
@@ -46,13 +46,8 @@ exports.parseCurlHeaderString = (header) => {
46
46
  };
47
47
  const isAFlagArg = (arg) => curlFlagRegex.test(arg);
48
48
  const isAURL = (arg) => httpURLRegex.test(arg);
49
- const parseHeaderFlag = (argv, i, partialCurlArgs) => {
50
- if (i + 1 >= argv.length) {
51
- return {
52
- error: `The '${argv[i]}' curl flag requires a value`,
53
- };
54
- }
55
- const headerOrError = exports.parseCurlHeaderString(argv[i + 1]);
49
+ const parseHeaderFlag = (value, partialCurlArgs) => {
50
+ const headerOrError = exports.parseCurlHeaderString(value);
56
51
  if (headerOrError && 'error' in headerOrError) {
57
52
  return headerOrError; // error
58
53
  }
@@ -65,26 +60,31 @@ const parseHeaderFlag = (argv, i, partialCurlArgs) => {
65
60
  }
66
61
  return {
67
62
  result: Object.assign(Object.assign({}, partialCurlArgs), { headers }),
68
- skip: 1,
69
63
  };
70
64
  };
71
- const parseDataFlag = (argv, i, partialCurlArgs) => {
72
- if (i + 1 >= argv.length) {
73
- return {
74
- error: `The '${argv[i]}' curl flag requires a value`,
75
- };
76
- }
77
- if (argv[i] !== '--data-raw' && argv[i + 1].charAt(0) === '@') {
65
+ const parseDataFlag = (value, partialCurlArgs, match) => {
66
+ if (match !== '--data-raw' && value.charAt(0) === '@') {
78
67
  return {
79
68
  error: `Reading request data from local files in not currently supported ` +
80
- `by StepZen CLI (${argv[i]} ${argv[i + 1]}). If this is a blocker ` +
69
+ `by StepZen CLI (${match} ${value}). If this is a blocker ` +
81
70
  `for you, please let us know on Discord (${constants_1.STEPZEN_DISCORD_URL})`,
82
71
  };
83
72
  }
84
- const data = (partialCurlArgs.data ? partialCurlArgs.data + '&' : '') + argv[i + 1];
73
+ const data = (partialCurlArgs.data ? partialCurlArgs.data + '&' : '') + value;
85
74
  return {
86
75
  result: Object.assign(Object.assign({}, partialCurlArgs), { data }),
87
- skip: 1,
76
+ };
77
+ };
78
+ const parseMethodFlag = (value, partialCurlArgs) => {
79
+ const lowercaseMethod = value.toLowerCase();
80
+ if (lowercaseMethod !== 'post' && lowercaseMethod !== 'get') {
81
+ return {
82
+ error: `The method ${value} is currently not supported.`,
83
+ };
84
+ }
85
+ const method = lowercaseMethod === 'post' ? 'Post' : 'Get';
86
+ return {
87
+ result: Object.assign(Object.assign({}, partialCurlArgs), { method }),
88
88
  };
89
89
  };
90
90
  const flags = [
@@ -96,7 +96,34 @@ const flags = [
96
96
  matches: ['-d', '--data', '--data-ascii', '--data-raw', '--data-binary'],
97
97
  parse: parseDataFlag,
98
98
  },
99
+ {
100
+ matches: ['-X', '--request'],
101
+ parse: parseMethodFlag,
102
+ },
99
103
  ];
104
+ const tryMatchCurlFlag = (matches, argv, i) => {
105
+ for (const match of matches) {
106
+ const isShortFlag = match.length === 2;
107
+ if (isShortFlag && argv[i].startsWith(match) && argv[i].length > 2) {
108
+ return {
109
+ value: argv[i].substring(2),
110
+ match,
111
+ };
112
+ }
113
+ if (argv[i] === match) {
114
+ if (i + 1 >= argv.length) {
115
+ return {
116
+ error: `The '${argv[i]}' curl flag requires a value`,
117
+ };
118
+ }
119
+ return {
120
+ value: argv[i + 1],
121
+ match,
122
+ skip: 1,
123
+ };
124
+ }
125
+ }
126
+ };
100
127
  /**
101
128
  * Parse a curl command line arguments array to a JSON structure consumable by
102
129
  * the StepZen introspection service backend.
@@ -120,16 +147,27 @@ exports.parseCurlArgv = (argv) => {
120
147
  let result = {};
121
148
  for (let i = 0; i < argv.length; i++) {
122
149
  if (isAFlagArg(argv[i])) {
123
- const knownFlag = flags.find(flag => flag.matches.includes(argv[i]));
124
- if (knownFlag) {
125
- const resultOrError = knownFlag.parse(argv, i, result);
126
- if ('error' in resultOrError) {
127
- return resultOrError; // error
150
+ let isKnownFlag = false;
151
+ for (const flag of flags) {
152
+ const matcherResult = tryMatchCurlFlag(flag.matches, argv, i);
153
+ if (!matcherResult) {
154
+ // no match => try matching the next flag
155
+ continue;
156
+ }
157
+ if ('error' in matcherResult) {
158
+ // flag matched but it requies a value which is missing
159
+ return matcherResult;
160
+ }
161
+ const parserResult = flag.parse(matcherResult.value, result, matcherResult.match);
162
+ if ('error' in parserResult) {
163
+ return parserResult;
128
164
  }
129
- result = resultOrError.result; // result
130
- i += resultOrError.skip || 0;
165
+ result = parserResult.result;
166
+ i += matcherResult.skip || 0;
167
+ isKnownFlag = true;
168
+ break;
131
169
  }
132
- else {
170
+ if (!isKnownFlag) {
133
171
  return {
134
172
  error: `The '${argv[i]}' curl flag is not currently supported by StepZen CLI.` +
135
173
  ` If this is a blocker for you, please let us know on Discord (${constants_1.STEPZEN_DISCORD_URL})`,
@@ -159,7 +197,7 @@ exports.parseCurlArgv = (argv) => {
159
197
  };
160
198
  }
161
199
  result.headers = result.headers || [];
162
- result.method = result.method || result.data ? 'Post' : 'Get';
200
+ result.method = result.method || (result.data ? 'Post' : 'Get');
163
201
  // Add the default content-type header if the request has any data
164
202
  // in it, and no content-type header is explicitly provided.
165
203
  if (result.headers.findIndex(header => header.name.toLowerCase() === 'content-type') === -1 &&
@@ -1,2 +1,4 @@
1
+ export declare const getRandomAnimal: () => string;
2
+ export declare const getRandomDescriptor: () => string;
1
3
  declare const _default: () => string;
2
4
  export default _default;
@@ -1,6 +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.getRandomDescriptor = exports.getRandomAnimal = void 0;
4
5
  const lodash_1 = require("lodash");
5
6
  const animals = [
6
7
  'aardvark',
@@ -637,8 +638,6 @@ const descriptors = [
637
638
  'zinc',
638
639
  'zooming',
639
640
  ];
640
- exports.default = () => {
641
- const animal = animals[lodash_1.random(0, animals.length - 1)];
642
- const descriptor = descriptors[lodash_1.random(0, descriptors.length - 1)];
643
- return `${descriptor}-${animal}`;
644
- };
641
+ exports.getRandomAnimal = () => animals[lodash_1.random(0, animals.length - 1)];
642
+ exports.getRandomDescriptor = () => descriptors[lodash_1.random(0, descriptors.length - 1)];
643
+ exports.default = () => `${exports.getRandomDescriptor()}-${exports.getRandomAnimal()}`;
@@ -1,4 +1,12 @@
1
1
  declare const stepzen: {
2
+ login: (adminkey: string, account?: string) => Promise<{
3
+ account: string;
4
+ adminkey: string;
5
+ }>;
6
+ createAnonymousAccount: () => {
7
+ account: string;
8
+ adminkey: string;
9
+ };
2
10
  verify: (account: string, adminkey: string) => Promise<boolean>;
3
11
  client: (options: Pick<import("@stepzen/sdk").StepZenAccount, "account" | "adminkey"> & Partial<import("@stepzen/sdk").StepZenAccount>) => Promise<{
4
12
  account: () => Promise<{
@@ -1,7 +1,28 @@
1
1
  "use strict";
2
2
  // Copyright (c) 2020,2021,2022, StepZen, Inc.
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
+ const errors_1 = require("@oclif/errors");
4
5
  const sdk_1 = require("@stepzen/sdk");
5
6
  const { version } = require('../../package.json');
6
- const stepzen = sdk_1.init({ appName: `stepzen-cli/${version}` });
7
+ const stepzen = Object.assign(Object.assign({}, sdk_1.init({ appName: `stepzen-cli/${version}` })), { login: async (adminkey, account = adminkey.split(':')[0]) => {
8
+ // Check whether the account and admin key exist and are correct by calling
9
+ // the stepzen admin api.
10
+ const isValidConfiguration = await stepzen.verify(account, adminkey);
11
+ if (!isValidConfiguration) {
12
+ // Exit, with error
13
+ throw new errors_1.CLIError('We are unable to verify your account details. Could you please check them?');
14
+ }
15
+ return {
16
+ account,
17
+ adminkey,
18
+ };
19
+ }, createAnonymousAccount: () => {
20
+ const anonAdminKey = process.env.STEPZEN_ANONYMOUS_ADMINKEY ||
21
+ // LATER: call the actual API (pending https://github.com/steprz/zen/issues/5545)
22
+ 'test::steprz.io+666::test';
23
+ return {
24
+ account: anonAdminKey.split(':')[0],
25
+ adminkey: anonAdminKey,
26
+ };
27
+ } });
7
28
  exports.default = stepzen;
@@ -3,11 +3,20 @@ export interface MachineConfiguration {
3
3
  adminkey: string;
4
4
  apikey?: string;
5
5
  }
6
+ /**
7
+ * On-disk representation of a workspace config file, `stepzen.config.json`
8
+ */
6
9
  export interface WorkspaceConfiguration {
7
- directory?: string;
8
10
  endpoint: string;
9
11
  root?: string;
10
- schema?: string;
12
+ }
13
+ /**
14
+ * In-memory representation of a workspace config
15
+ */
16
+ export interface Workspace {
17
+ endpoint: string;
18
+ directory: string;
19
+ schema: string;
11
20
  }
12
21
  export interface ZenCtlResponse {
13
22
  errors?: Array<string>;
@@ -52,7 +52,7 @@ exports.clearConsole = () => {
52
52
  exports.ensureApiKey = async () => {
53
53
  const configuration = configuration_1.readConfiguration();
54
54
  const details = configuration;
55
- if (!details.apikey) {
55
+ if (details && !details.apikey) {
56
56
  debug('stepzen:account')(`Fetching API key for account ${details.account}`);
57
57
  try {
58
58
  const client = await stepzen_sdk_1.default.client(details);
@@ -68,11 +68,12 @@ exports.ensureApiKey = async () => {
68
68
  exports.getDirectory = (d = process.cwd()) => {
69
69
  let directory = d;
70
70
  // If it starts with `~`, expand this
71
- if (directory.startsWith('~'))
71
+ if (directory.startsWith('~')) {
72
72
  directory = directory.replace('~', os.homedir());
73
- // If it does not now start with `/`, then get the absolute path
74
- if (directory.startsWith('/') === false)
75
- directory = path.resolve(directory);
73
+ }
74
+ // If it does not now start with `/`, then get the absolute path.
75
+ // Also, remove any `..` and `.` segments.
76
+ directory = path.resolve(directory);
76
77
  // If the path does not exist, throw an error
77
78
  if (!fs.existsSync(directory)) {
78
79
  throw new errors_1.CLIError(`Cannot find ${directory}`);
@@ -1 +1,2 @@
1
- export declare const getWorkspace: (directory: string) => any;
1
+ import { Workspace } from './types';
2
+ export declare const getWorkspace: (directory: string) => Workspace | null;
@@ -4,50 +4,53 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.getWorkspace = void 0;
5
5
  const errors_1 = require("@oclif/errors");
6
6
  const fs = require("fs");
7
+ const os = require("os");
7
8
  const path = require("path");
8
9
  const utils_1 = require("./utils");
9
10
  exports.getWorkspace = (directory) => {
10
- let workspace = {
11
- directory: '',
12
- endpoint: '',
13
- root: '',
14
- schema: '',
15
- };
16
- // Loop through this and all parent directories to find the root
17
- let parts = directory.split(path.sep);
18
- do {
19
- const d = `${parts.join(path.sep)}`.replace(/\/*$/, '');
11
+ let workspaceRoot;
12
+ const parts = path
13
+ // Remove trailing slashes and any `..` and `.` segments
14
+ .resolve(directory)
15
+ // split into segments
16
+ .split(path.sep);
17
+ // Loop through this and all parent directories up to $HOME to find the root
18
+ for (; parts.length > 0; parts.pop()) {
19
+ const d = parts.join(path.sep);
20
20
  const f = path.join(d, 'stepzen.config.json');
21
21
  if (fs.existsSync(f)) {
22
- workspace.directory = d;
23
- parts = [];
24
- }
25
- parts.pop();
26
- } while (parts.length > 0);
27
- // If it's found a workspace, then parse the configuration file
28
- if (workspace.directory) {
29
- const filepath = path.join(workspace.directory, 'stepzen.config.json');
30
- const file = fs.readFileSync(filepath, 'utf8');
31
- try {
32
- const config = JSON.parse(file);
33
- workspace = Object.assign(Object.assign({}, workspace), config);
22
+ workspaceRoot = d;
23
+ break;
34
24
  }
35
- catch (_a) {
36
- throw new errors_1.CLIError(`Cannot parse configuration from ${filepath}`);
25
+ if (d === os.homedir()) {
26
+ break;
37
27
  }
38
28
  }
39
- // Add the 'schema' property, which is the directory + the 'root'
40
- workspace.schema = path.join(workspace.directory, workspace.root);
29
+ if (!workspaceRoot) {
30
+ return null;
31
+ }
32
+ const filepath = path.join(workspaceRoot, 'stepzen.config.json');
33
+ const file = fs.readFileSync(filepath, 'utf8');
34
+ let config;
35
+ try {
36
+ config = JSON.parse(file);
37
+ }
38
+ catch (_a) {
39
+ throw new errors_1.CLIError(`Cannot parse configuration from ${filepath}`);
40
+ }
41
41
  // Validate the workspace
42
- if (workspace.directory) {
43
- const error = utils_1.validateEndpoint(workspace.endpoint);
44
- if (typeof error === 'string')
45
- throw new errors_1.CLIError(error);
46
- const schemaExists = fs.existsSync(workspace.schema);
47
- if (!schemaExists)
48
- throw new errors_1.CLIError(`Cannot find workspace schema folder ${workspace.schema}`);
49
- return workspace;
42
+ const error = utils_1.validateEndpoint(config.endpoint);
43
+ if (typeof error === 'string') {
44
+ throw new errors_1.CLIError(error);
45
+ }
46
+ // Add the 'schema' property, which is the directory + the 'root'
47
+ const schema = path.join(workspaceRoot, config.root || '');
48
+ if (!fs.existsSync(schema)) {
49
+ throw new errors_1.CLIError(`Cannot find workspace schema folder ${schema}`);
50
50
  }
51
- // This is not a workspace folder
52
- return false;
51
+ return {
52
+ directory: workspaceRoot,
53
+ endpoint: config.endpoint,
54
+ schema: schema,
55
+ };
53
56
  };
@@ -0,0 +1,14 @@
1
+ import { Command } from '@oclif/command';
2
+ import { MachineConfiguration } from './types';
3
+ export declare abstract class ZenCommand extends Command {
4
+ static flags: {
5
+ 'non-interactive': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
6
+ };
7
+ ensureStepZenAccount(): Promise<{
8
+ configuration: MachineConfiguration;
9
+ }>;
10
+ promptUserToLogIn(): Promise<{
11
+ configuration: MachineConfiguration;
12
+ }>;
13
+ }
14
+ export default ZenCommand;