@xano/cli 1.0.6-beta.2 → 1.0.6-beta.20

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 (112) hide show
  1. package/README.md +217 -6
  2. package/dist/base-command.d.ts +5 -0
  3. package/dist/base-command.js +61 -4
  4. package/dist/commands/branch/list/index.js +5 -1
  5. package/dist/commands/ephemeral/create/index.js +3 -3
  6. package/dist/commands/ephemeral/list/index.js +2 -1
  7. package/dist/commands/ephemeral/pull/index.d.ts +0 -6
  8. package/dist/commands/ephemeral/pull/index.js +10 -95
  9. package/dist/commands/ephemeral/push/index.d.ts +10 -0
  10. package/dist/commands/ephemeral/push/index.js +77 -0
  11. package/dist/commands/ephemeral/static_host/build/list/index.d.ts +2 -3
  12. package/dist/commands/ephemeral/static_host/build/list/index.js +19 -40
  13. package/dist/commands/ephemeral/static_host/list/index.d.ts +2 -3
  14. package/dist/commands/ephemeral/static_host/list/index.js +20 -40
  15. package/dist/commands/flatten/index.d.ts +34 -0
  16. package/dist/commands/flatten/index.js +140 -0
  17. package/dist/commands/function/list/index.d.ts +4 -4
  18. package/dist/commands/function/list/index.js +30 -38
  19. package/dist/commands/function/run/index.d.ts +1 -0
  20. package/dist/commands/function/run/index.js +19 -5
  21. package/dist/commands/knowledge/list/index.js +2 -1
  22. package/dist/commands/platform/list/index.js +13 -14
  23. package/dist/commands/release/list/index.js +13 -14
  24. package/dist/commands/release/pull/index.d.ts +0 -6
  25. package/dist/commands/release/pull/index.js +10 -95
  26. package/dist/commands/sandbox/env/list/index.js +4 -0
  27. package/dist/commands/sandbox/pull/index.d.ts +0 -1
  28. package/dist/commands/sandbox/pull/index.js +10 -77
  29. package/dist/commands/sandbox/unit_test/list/index.d.ts +2 -0
  30. package/dist/commands/sandbox/unit_test/list/index.js +9 -14
  31. package/dist/commands/sandbox/unit_test/run/index.js +17 -11
  32. package/dist/commands/sandbox/unit_test/run_all/index.d.ts +1 -0
  33. package/dist/commands/sandbox/unit_test/run_all/index.js +76 -38
  34. package/dist/commands/sandbox/workflow_test/list/index.d.ts +2 -0
  35. package/dist/commands/sandbox/workflow_test/list/index.js +9 -14
  36. package/dist/commands/sandbox/workflow_test/run/index.js +12 -9
  37. package/dist/commands/sandbox/workflow_test/run_all/index.d.ts +1 -0
  38. package/dist/commands/sandbox/workflow_test/run_all/index.js +74 -36
  39. package/dist/commands/static_host/build/list/index.d.ts +2 -3
  40. package/dist/commands/static_host/build/list/index.js +19 -40
  41. package/dist/commands/static_host/build/pull/index.js +12 -9
  42. package/dist/commands/static_host/list/index.d.ts +2 -3
  43. package/dist/commands/static_host/list/index.js +20 -40
  44. package/dist/commands/static_host/migrate/index.js +14 -10
  45. package/dist/commands/tenant/backup/list/index.js +13 -14
  46. package/dist/commands/tenant/cluster/list/index.js +9 -10
  47. package/dist/commands/tenant/create/index.d.ts +3 -0
  48. package/dist/commands/tenant/create/index.js +21 -0
  49. package/dist/commands/tenant/deploy_release/index.d.ts +21 -0
  50. package/dist/commands/tenant/deploy_release/index.js +94 -2
  51. package/dist/commands/tenant/edit/index.d.ts +2 -0
  52. package/dist/commands/tenant/edit/index.js +39 -4
  53. package/dist/commands/tenant/env/list/index.js +4 -0
  54. package/dist/commands/tenant/list/index.js +19 -20
  55. package/dist/commands/tenant/pull/index.d.ts +0 -6
  56. package/dist/commands/tenant/pull/index.js +10 -106
  57. package/dist/commands/tenant/snapshot/list/index.js +5 -1
  58. package/dist/commands/tenant/unit_test/list/index.d.ts +4 -2
  59. package/dist/commands/tenant/unit_test/list/index.js +9 -14
  60. package/dist/commands/tenant/unit_test/run/index.js +19 -11
  61. package/dist/commands/tenant/unit_test/run_all/index.d.ts +1 -0
  62. package/dist/commands/tenant/unit_test/run_all/index.js +80 -38
  63. package/dist/commands/tenant/workflow_test/list/index.d.ts +4 -2
  64. package/dist/commands/tenant/workflow_test/list/index.js +9 -14
  65. package/dist/commands/tenant/workflow_test/run/index.js +14 -9
  66. package/dist/commands/tenant/workflow_test/run_all/index.d.ts +1 -0
  67. package/dist/commands/tenant/workflow_test/run_all/index.js +78 -36
  68. package/dist/commands/tenant_deploy_request/bypass/index.d.ts +20 -0
  69. package/dist/commands/tenant_deploy_request/bypass/index.js +79 -0
  70. package/dist/commands/tenant_deploy_request/create/index.d.ts +21 -0
  71. package/dist/commands/tenant_deploy_request/create/index.js +108 -0
  72. package/dist/commands/tenant_deploy_request/edit/index.d.ts +22 -0
  73. package/dist/commands/tenant_deploy_request/edit/index.js +103 -0
  74. package/dist/commands/tenant_deploy_request/get/index.d.ts +19 -0
  75. package/dist/commands/tenant_deploy_request/get/index.js +75 -0
  76. package/dist/commands/tenant_deploy_request/list/index.d.ts +20 -0
  77. package/dist/commands/tenant_deploy_request/list/index.js +122 -0
  78. package/dist/commands/tenant_deploy_request/revision/index.d.ts +21 -0
  79. package/dist/commands/tenant_deploy_request/revision/index.js +83 -0
  80. package/dist/commands/tenant_deploy_request/set_status/index.d.ts +21 -0
  81. package/dist/commands/tenant_deploy_request/set_status/index.js +112 -0
  82. package/dist/commands/unit_test/list/index.d.ts +3 -1
  83. package/dist/commands/unit_test/list/index.js +9 -14
  84. package/dist/commands/unit_test/run/index.js +19 -11
  85. package/dist/commands/unit_test/run_all/index.d.ts +1 -0
  86. package/dist/commands/unit_test/run_all/index.js +81 -41
  87. package/dist/commands/workflow_test/list/index.d.ts +3 -1
  88. package/dist/commands/workflow_test/list/index.js +10 -13
  89. package/dist/commands/workflow_test/run/index.js +15 -10
  90. package/dist/commands/workflow_test/run_all/index.d.ts +1 -0
  91. package/dist/commands/workflow_test/run_all/index.js +82 -39
  92. package/dist/commands/workspace/git/pull/index.js +48 -7
  93. package/dist/commands/workspace/list/index.js +7 -13
  94. package/dist/commands/workspace/pull/index.d.ts +0 -6
  95. package/dist/commands/workspace/pull/index.js +10 -115
  96. package/dist/utils/concurrency.d.ts +29 -0
  97. package/dist/utils/concurrency.js +54 -0
  98. package/dist/utils/document-parser.d.ts +90 -0
  99. package/dist/utils/document-parser.js +273 -1
  100. package/dist/utils/flatten.d.ts +36 -0
  101. package/dist/utils/flatten.js +68 -0
  102. package/dist/utils/knowledge-sync.d.ts +1 -0
  103. package/dist/utils/knowledge-sync.js +11 -5
  104. package/dist/utils/microservice-wait.d.ts +53 -0
  105. package/dist/utils/microservice-wait.js +83 -0
  106. package/dist/utils/multidoc-push.d.ts +88 -0
  107. package/dist/utils/multidoc-push.js +377 -8
  108. package/dist/utils/paging.d.ts +145 -0
  109. package/dist/utils/paging.js +199 -0
  110. package/dist/utils/reference-checker.js +13 -0
  111. package/oclif.manifest.json +4999 -3973
  112. package/package.json +1 -1
@@ -0,0 +1,122 @@
1
+ import { Flags } from '@oclif/core';
2
+ import BaseCommand from '../../../base-command.js';
3
+ export default class TenantDeployRequestList extends BaseCommand {
4
+ static description = 'List tenant deploy requests (deployment approval requests) in a workspace';
5
+ static examples = [
6
+ `$ xano tenant_deploy_request list
7
+ Deploy requests in workspace 5:
8
+ - #12 "Deploy v1.2 to prod" [pending] -> prod / v1.2
9
+ `,
10
+ `$ xano tenant_deploy_request list --tenant prod --status pending`,
11
+ `$ xano tenant_deploy_request list --tenant prod --release v1.2`,
12
+ `$ xano tenant_deploy_request list --to-review -o json`,
13
+ ];
14
+ static flags = {
15
+ ...BaseCommand.baseFlags,
16
+ mine: Flags.boolean({
17
+ default: false,
18
+ description: 'Only requests you authored',
19
+ required: false,
20
+ }),
21
+ output: Flags.string({
22
+ char: 'o',
23
+ default: 'summary',
24
+ description: 'Output format',
25
+ options: ['summary', 'json'],
26
+ required: false,
27
+ }),
28
+ page: Flags.integer({
29
+ default: 1,
30
+ description: 'Page number',
31
+ required: false,
32
+ }),
33
+ 'per-page': Flags.integer({
34
+ default: 25,
35
+ description: 'Items per page',
36
+ required: false,
37
+ }),
38
+ release: Flags.string({
39
+ char: 'r',
40
+ description: 'Filter to requests targeting this release. Applied client-side (the API has no release filter), after --page/--per-page, so a narrow page size can hide matches on other pages.',
41
+ required: false,
42
+ }),
43
+ status: Flags.string({
44
+ description: 'Filter by status',
45
+ options: ['draft', 'pending', 'changes_requested', 'approved', 'closed'],
46
+ required: false,
47
+ }),
48
+ tenant: Flags.string({
49
+ description: 'Filter to requests targeting this tenant',
50
+ required: false,
51
+ }),
52
+ 'to-review': Flags.boolean({
53
+ default: false,
54
+ description: 'Only requests you are able to review',
55
+ required: false,
56
+ }),
57
+ workspace: Flags.string({
58
+ char: 'w',
59
+ description: 'Workspace ID (uses profile workspace if not provided)',
60
+ required: false,
61
+ }),
62
+ };
63
+ async run() {
64
+ const { flags } = await this.parse(TenantDeployRequestList);
65
+ const { profile } = this.resolveProfile(flags);
66
+ const workspaceId = flags.workspace || profile.workspace;
67
+ if (!workspaceId) {
68
+ this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
69
+ }
70
+ const params = new URLSearchParams();
71
+ if (flags.status)
72
+ params.set('status', flags.status);
73
+ if (flags.tenant)
74
+ params.set('tenant_name', flags.tenant);
75
+ if (flags.mine)
76
+ params.set('mine', 'true');
77
+ if (flags['to-review'])
78
+ params.set('to_review', 'true');
79
+ params.set('page', String(flags.page));
80
+ params.set('per_page', String(flags['per-page']));
81
+ const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/approval_request?${params}`;
82
+ try {
83
+ const response = await this.verboseFetch(apiUrl, {
84
+ headers: {
85
+ accept: 'application/json',
86
+ Authorization: `Bearer ${profile.access_token}`,
87
+ },
88
+ method: 'GET',
89
+ }, flags.verbose, profile.access_token);
90
+ if (!response.ok) {
91
+ const message = await this.parseApiError(response, 'Failed to list tenant deploy requests');
92
+ this.error(message);
93
+ }
94
+ const data = (await response.json());
95
+ const allItems = Array.isArray(data) ? data : (data.items ?? []);
96
+ const items = flags.release ? allItems.filter((item) => item._release?.name === flags.release) : allItems;
97
+ if (flags.output === 'json') {
98
+ this.log(JSON.stringify(items, null, 2));
99
+ }
100
+ else if (items.length === 0) {
101
+ this.log('No tenant deploy requests found');
102
+ }
103
+ else {
104
+ this.log(`Deploy requests in workspace ${workspaceId}:`);
105
+ for (const item of items) {
106
+ const review = item.can_review ? ' (reviewable)' : '';
107
+ const tenantName = item._tenant?.name ?? '?';
108
+ const releaseName = item._release?.name ?? '?';
109
+ this.log(` - #${item.id} "${item.title}" [${item.status}]${review} -> ${tenantName} / ${releaseName}`);
110
+ }
111
+ }
112
+ }
113
+ catch (error) {
114
+ if (error instanceof Error) {
115
+ this.error(`Failed to list tenant deploy requests: ${error.message}`);
116
+ }
117
+ else {
118
+ this.error(`Failed to list tenant deploy requests: ${String(error)}`);
119
+ }
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,21 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class TenantDeployRequestRevision extends BaseCommand {
3
+ static args: {
4
+ id: import("@oclif/core/interfaces").Arg<number, {
5
+ max?: number;
6
+ min?: number;
7
+ }>;
8
+ };
9
+ static description: string;
10
+ static examples: string[];
11
+ static flags: {
12
+ note: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ release: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
15
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
+ };
20
+ run(): Promise<void>;
21
+ }
@@ -0,0 +1,83 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import BaseCommand from '../../../base-command.js';
3
+ export default class TenantDeployRequestRevision extends BaseCommand {
4
+ static args = {
5
+ id: Args.integer({
6
+ description: 'Tenant deploy request ID',
7
+ required: true,
8
+ }),
9
+ };
10
+ static description = 'Link a newer release candidate to an existing tenant deploy request after changes were requested. The previous candidate is kept in the revision history and the request returns to pending. Author only.';
11
+ static examples = [
12
+ `$ xano tenant_deploy_request revision 12 --release v1.2.1 --note "Added the missing migration"
13
+ Deploy request #12 is now: pending
14
+ `,
15
+ ];
16
+ static flags = {
17
+ ...BaseCommand.baseFlags,
18
+ note: Flags.string({
19
+ description: 'Note describing what changed in this revision',
20
+ required: false,
21
+ }),
22
+ output: Flags.string({
23
+ char: 'o',
24
+ default: 'summary',
25
+ description: 'Output format',
26
+ options: ['summary', 'json'],
27
+ required: false,
28
+ }),
29
+ release: Flags.string({
30
+ char: 'r',
31
+ description: 'Name of the new release to review',
32
+ required: true,
33
+ }),
34
+ workspace: Flags.string({
35
+ char: 'w',
36
+ description: 'Workspace ID (uses profile workspace if not provided)',
37
+ required: false,
38
+ }),
39
+ };
40
+ async run() {
41
+ const { args, flags } = await this.parse(TenantDeployRequestRevision);
42
+ const { profile } = this.resolveProfile(flags);
43
+ const workspaceId = flags.workspace || profile.workspace;
44
+ if (!workspaceId) {
45
+ this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
46
+ }
47
+ const body = {
48
+ note: flags.note ?? '',
49
+ release_name: flags.release,
50
+ };
51
+ const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/approval_request/${args.id}/revision`;
52
+ try {
53
+ const response = await this.verboseFetch(apiUrl, {
54
+ body: JSON.stringify(body),
55
+ headers: {
56
+ accept: 'application/json',
57
+ Authorization: `Bearer ${profile.access_token}`,
58
+ 'Content-Type': 'application/json',
59
+ },
60
+ method: 'POST',
61
+ }, flags.verbose, profile.access_token);
62
+ if (!response.ok) {
63
+ const message = await this.parseApiError(response, 'Failed to add revision to tenant deploy request');
64
+ this.error(message);
65
+ }
66
+ const item = (await response.json());
67
+ if (flags.output === 'json') {
68
+ this.log(JSON.stringify(item, null, 2));
69
+ }
70
+ else {
71
+ this.log(`Deploy request #${item.id} is now: ${item.status}`);
72
+ }
73
+ }
74
+ catch (error) {
75
+ if (error instanceof Error) {
76
+ this.error(`Failed to add revision to tenant deploy request: ${error.message}`);
77
+ }
78
+ else {
79
+ this.error(`Failed to add revision to tenant deploy request: ${String(error)}`);
80
+ }
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,21 @@
1
+ import BaseCommand from '../../../base-command.js';
2
+ export default class TenantDeployRequestSetStatus extends BaseCommand {
3
+ static args: {
4
+ id: import("@oclif/core/interfaces").Arg<number, {
5
+ max?: number;
6
+ min?: number;
7
+ }>;
8
+ };
9
+ static description: string;
10
+ static examples: string[];
11
+ static flags: {
12
+ output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ reason: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ status: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
15
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
19
+ };
20
+ run(): Promise<void>;
21
+ }
@@ -0,0 +1,112 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import BaseCommand from '../../../base-command.js';
3
+ export default class TenantDeployRequestSetStatus extends BaseCommand {
4
+ static args = {
5
+ id: Args.integer({
6
+ description: 'Tenant deploy request ID',
7
+ required: true,
8
+ }),
9
+ };
10
+ static description = "[CRITICAL] STOP and confirm with the user before running --status approve; if this is the vote that completes the tenant's required reviewer count, it deploys the release to the tenant immediately as part of approving. Change a tenant deploy request's status (draft, submit, approve, request_changes, close, or reopen).";
11
+ static examples = [
12
+ `$ xano tenant_deploy_request set_status 12 --status submit
13
+ Deploy request #12 is now: pending
14
+ `,
15
+ `$ xano tenant_deploy_request set_status 12 --status approve
16
+ Vote recorded on deploy request #12: 1/2 required reviewers.
17
+ `,
18
+ `$ xano tenant_deploy_request set_status 12 --status approve
19
+ Deploy request #12 is now: approved (2/2 required reviewers)
20
+ Deployed to tenant as part of this approval.
21
+ `,
22
+ `$ xano tenant_deploy_request set_status 12 --status request_changes --reason "Needs a migration note"`,
23
+ `$ xano tenant_deploy_request set_status 12 --status close --reason "Superseded by #14"`,
24
+ `$ xano tenant_deploy_request set_status 12 --status reopen`,
25
+ ];
26
+ static flags = {
27
+ ...BaseCommand.baseFlags,
28
+ output: Flags.string({
29
+ char: 'o',
30
+ default: 'summary',
31
+ description: 'Output format',
32
+ options: ['summary', 'json'],
33
+ required: false,
34
+ }),
35
+ reason: Flags.string({
36
+ description: 'Reason for this status change. Required for request_changes and close.',
37
+ required: false,
38
+ }),
39
+ status: Flags.string({
40
+ description: 'The status transition to apply',
41
+ options: ['draft', 'submit', 'approve', 'request_changes', 'close', 'reopen'],
42
+ required: true,
43
+ }),
44
+ workspace: Flags.string({
45
+ char: 'w',
46
+ description: 'Workspace ID (uses profile workspace if not provided)',
47
+ required: false,
48
+ }),
49
+ };
50
+ async run() {
51
+ const { args, flags } = await this.parse(TenantDeployRequestSetStatus);
52
+ const { profile } = this.resolveProfile(flags);
53
+ const workspaceId = flags.workspace || profile.workspace;
54
+ if (!workspaceId) {
55
+ this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
56
+ }
57
+ if ((flags.status === 'request_changes' || flags.status === 'close') && !flags.reason) {
58
+ this.error(`--reason is required when --status is "${flags.status}"`);
59
+ }
60
+ const body = {
61
+ action: flags.status,
62
+ reason: flags.reason ?? '',
63
+ };
64
+ const apiUrl = `${profile.instance_origin}/api:meta/workspace/${workspaceId}/approval_request/${args.id}/status`;
65
+ try {
66
+ const response = await this.verboseFetch(apiUrl, {
67
+ body: JSON.stringify(body),
68
+ headers: {
69
+ accept: 'application/json',
70
+ Authorization: `Bearer ${profile.access_token}`,
71
+ 'Content-Type': 'application/json',
72
+ },
73
+ method: 'POST',
74
+ }, flags.verbose, profile.access_token);
75
+ if (!response.ok) {
76
+ const message = await this.parseApiError(response, 'Failed to change tenant deploy request status');
77
+ this.error(message);
78
+ }
79
+ const item = (await response.json());
80
+ if (flags.output === 'json') {
81
+ this.log(JSON.stringify(item, null, 2));
82
+ }
83
+ else if (flags.status === 'approve' && item.fully_approved === false) {
84
+ // required_reviewers > 1: this vote didn't complete the quorum, so status
85
+ // stays "pending" and nothing was deployed yet.
86
+ this.log(`Vote recorded on deploy request #${item.id}: ${item.approvals_count}/${item.required_reviewers} required reviewers.`);
87
+ }
88
+ else {
89
+ const quorum = item.approvals_count !== undefined && item.required_reviewers !== undefined
90
+ ? ` (${item.approvals_count}/${item.required_reviewers} required reviewers)`
91
+ : '';
92
+ this.log(`Deploy request #${item.id} is now: ${item.status}${quorum}`);
93
+ if (flags.status === 'approve') {
94
+ if (item.resolution?.deploy_status === 'deployed') {
95
+ this.log('Deployed to tenant as part of this approval.');
96
+ }
97
+ else if (item.resolution?.deploy_status === 'failed') {
98
+ this.log(`Approved, but the deploy failed: ${item.resolution.deploy_error ?? 'unknown error'}`);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ catch (error) {
104
+ if (error instanceof Error) {
105
+ this.error(`Failed to change tenant deploy request status: ${error.message}`);
106
+ }
107
+ else {
108
+ this.error(`Failed to change tenant deploy request status: ${String(error)}`);
109
+ }
110
+ }
111
+ }
112
+ }
@@ -3,10 +3,12 @@ export default class UnitTestList extends BaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
5
  static flags: {
6
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
8
+ per_page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
6
9
  branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
10
  'obj-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
11
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
- workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
12
  config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
13
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
14
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
@@ -1,5 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import BaseCommand from '../../../base-command.js';
3
+ import { buildPagingJson, buildPagingParams, formatPagingFooter, normalizeListResponse, pagingFlags } from '../../../utils/paging.js';
3
4
  export default class UnitTestList extends BaseCommand {
4
5
  static description = 'List all unit tests in a workspace';
5
6
  static examples = [
@@ -30,6 +31,7 @@ Unit tests in workspace 5:
30
31
  options: ['summary', 'json'],
31
32
  required: false,
32
33
  }),
34
+ ...pagingFlags('envelope', { maxPerPage: 10_000 }),
33
35
  workspace: Flags.string({
34
36
  char: 'w',
35
37
  description: 'Workspace ID (uses profile workspace if not provided)',
@@ -43,8 +45,7 @@ Unit tests in workspace 5:
43
45
  if (!workspaceId) {
44
46
  this.error('No workspace ID provided. Use --workspace flag or set one in your profile.');
45
47
  }
46
- const params = new URLSearchParams();
47
- params.set('per_page', '10000');
48
+ const params = new URLSearchParams(buildPagingParams(flags, 'envelope'));
48
49
  if (flags.branch) {
49
50
  params.set('branch', flags.branch);
50
51
  }
@@ -64,19 +65,10 @@ Unit tests in workspace 5:
64
65
  const errorText = await response.text();
65
66
  this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
66
67
  }
67
- const data = await response.json();
68
- let tests;
69
- if (Array.isArray(data)) {
70
- tests = data;
71
- }
72
- else if (data && typeof data === 'object' && 'items' in data && Array.isArray(data.items)) {
73
- tests = data.items;
74
- }
75
- else {
76
- this.error('Unexpected API response format');
77
- }
68
+ const list = normalizeListResponse(await response.json());
69
+ const tests = list.items;
78
70
  if (flags.output === 'json') {
79
- this.log(JSON.stringify(tests, null, 2));
71
+ this.log(JSON.stringify(buildPagingJson(list, { page: flags.page, perPage: flags.per_page, tier: 'envelope' }), null, 2));
80
72
  }
81
73
  else {
82
74
  if (tests.length === 0) {
@@ -88,6 +80,9 @@ Unit tests in workspace 5:
88
80
  this.log(` - ${test.name} (ID: ${test.id}) [${test.obj_type}: ${test.obj_name}]`);
89
81
  }
90
82
  }
83
+ const footer = formatPagingFooter(list, { noun: 'unit test', page: flags.page, tier: 'envelope' });
84
+ if (footer)
85
+ this.log(footer);
91
86
  }
92
87
  }
93
88
  catch (error) {
@@ -63,26 +63,34 @@ Result: PASS
63
63
  this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
64
64
  }
65
65
  const result = await response.json();
66
+ // Anything other than 'ok' is a failure -- the API has been observed
67
+ // returning several distinct non-ok statuses, so never enumerate them.
68
+ const passed = result.status === 'ok';
66
69
  if (flags.output === 'json') {
67
70
  this.log(JSON.stringify(result, null, 2));
68
71
  }
72
+ else if (passed) {
73
+ this.log('Result: PASS');
74
+ }
69
75
  else {
70
- if (result.status === 'ok') {
71
- this.log('Result: PASS');
72
- }
73
- else {
74
- this.log('Result: FAIL');
75
- const failedExpects = result.results?.filter(r => r.status === 'fail') ?? [];
76
- for (const expect of failedExpects) {
77
- if (expect.message) {
78
- this.log(` Error: ${expect.message}`);
79
- }
76
+ this.log('Result: FAIL');
77
+ const failedExpects = result.results?.filter(r => r.status === 'fail') ?? [];
78
+ for (const expect of failedExpects) {
79
+ if (expect.message) {
80
+ this.log(` Error: ${expect.message}`);
80
81
  }
81
- this.exit(1);
82
82
  }
83
83
  }
84
+ // Set the code rather than throwing via this.exit(): the exit must apply
85
+ // in both output modes, and process.exitCode cannot be swallowed by the
86
+ // surrounding catch. Mirrors what run_all already does.
87
+ if (!passed) {
88
+ process.exitCode = 1;
89
+ }
84
90
  }
85
91
  catch (error) {
92
+ if (error instanceof Error && 'oclif' in error)
93
+ throw error;
86
94
  if (error instanceof Error) {
87
95
  this.error(`Failed to run unit test: ${error.message}`);
88
96
  }
@@ -4,6 +4,7 @@ export default class UnitTestRunAll extends BaseCommand {
4
4
  static examples: string[];
5
5
  static flags: {
6
6
  branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ concurrency: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
7
8
  'obj-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
10
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;