@xano/cli 0.0.95-beta.9 → 1.0.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.
Files changed (240) hide show
  1. package/README.md +111 -70
  2. package/dist/base-command.d.ts +16 -1
  3. package/dist/base-command.js +57 -5
  4. package/dist/commands/auth/index.d.ts +1 -0
  5. package/dist/commands/auth/index.js +15 -10
  6. package/dist/commands/branch/create/index.d.ts +4 -1
  7. package/dist/commands/branch/create/index.js +22 -21
  8. package/dist/commands/branch/delete/index.d.ts +1 -0
  9. package/dist/commands/branch/delete/index.js +1 -4
  10. package/dist/commands/branch/edit/index.d.ts +1 -0
  11. package/dist/commands/branch/edit/index.js +1 -4
  12. package/dist/commands/branch/get/index.d.ts +1 -0
  13. package/dist/commands/branch/get/index.js +1 -4
  14. package/dist/commands/branch/list/index.d.ts +2 -6
  15. package/dist/commands/branch/list/index.js +13 -17
  16. package/dist/commands/branch/set_live/index.d.ts +1 -0
  17. package/dist/commands/branch/set_live/index.js +1 -4
  18. package/dist/commands/function/create/index.d.ts +1 -0
  19. package/dist/commands/function/create/index.js +1 -2
  20. package/dist/commands/function/edit/index.d.ts +1 -0
  21. package/dist/commands/function/edit/index.js +1 -2
  22. package/dist/commands/function/get/index.d.ts +1 -0
  23. package/dist/commands/function/get/index.js +1 -4
  24. package/dist/commands/function/list/index.d.ts +1 -0
  25. package/dist/commands/function/list/index.js +1 -4
  26. package/dist/commands/platform/get/index.d.ts +1 -0
  27. package/dist/commands/platform/get/index.js +1 -4
  28. package/dist/commands/platform/list/index.d.ts +1 -0
  29. package/dist/commands/platform/list/index.js +1 -4
  30. package/dist/commands/profile/create/index.d.ts +1 -0
  31. package/dist/commands/profile/create/index.js +10 -4
  32. package/dist/commands/profile/delete/index.d.ts +1 -0
  33. package/dist/commands/profile/delete/index.js +8 -4
  34. package/dist/commands/profile/edit/index.d.ts +1 -0
  35. package/dist/commands/profile/edit/index.js +1 -4
  36. package/dist/commands/profile/get/index.d.ts +3 -0
  37. package/dist/commands/profile/get/index.js +12 -5
  38. package/dist/commands/profile/list/index.d.ts +1 -0
  39. package/dist/commands/profile/list/index.js +8 -4
  40. package/dist/commands/profile/me/index.d.ts +1 -0
  41. package/dist/commands/profile/me/index.js +1 -4
  42. package/dist/commands/profile/set/index.d.ts +3 -0
  43. package/dist/commands/profile/set/index.js +12 -6
  44. package/dist/commands/profile/token/index.d.ts +3 -0
  45. package/dist/commands/profile/token/index.js +12 -5
  46. package/dist/commands/profile/wizard/index.d.ts +1 -0
  47. package/dist/commands/profile/wizard/index.js +13 -9
  48. package/dist/commands/profile/workspace/index.d.ts +3 -0
  49. package/dist/commands/profile/workspace/index.js +12 -5
  50. package/dist/commands/profile/workspace/set/index.d.ts +1 -0
  51. package/dist/commands/profile/workspace/set/index.js +1 -3
  52. package/dist/commands/release/create/index.d.ts +4 -1
  53. package/dist/commands/release/create/index.js +12 -14
  54. package/dist/commands/release/delete/index.d.ts +1 -0
  55. package/dist/commands/release/delete/index.js +1 -4
  56. package/dist/commands/release/deploy/index.d.ts +3 -0
  57. package/dist/commands/release/deploy/index.js +31 -1
  58. package/dist/commands/release/edit/index.d.ts +1 -0
  59. package/dist/commands/release/edit/index.js +1 -4
  60. package/dist/commands/release/export/index.d.ts +1 -0
  61. package/dist/commands/release/export/index.js +1 -3
  62. package/dist/commands/release/get/index.d.ts +1 -0
  63. package/dist/commands/release/get/index.js +1 -4
  64. package/dist/commands/release/import/index.d.ts +1 -0
  65. package/dist/commands/release/import/index.js +1 -3
  66. package/dist/commands/release/list/index.d.ts +1 -0
  67. package/dist/commands/release/list/index.js +1 -4
  68. package/dist/commands/release/pull/index.d.ts +2 -3
  69. package/dist/commands/release/pull/index.js +19 -18
  70. package/dist/commands/release/push/index.d.ts +2 -3
  71. package/dist/commands/release/push/index.js +19 -22
  72. package/dist/commands/sandbox/delete/index.d.ts +13 -0
  73. package/dist/commands/sandbox/delete/index.js +71 -0
  74. package/dist/commands/sandbox/env/delete/index.d.ts +1 -0
  75. package/dist/commands/sandbox/env/delete/index.js +4 -2
  76. package/dist/commands/sandbox/env/get/index.d.ts +1 -0
  77. package/dist/commands/sandbox/env/get/index.js +4 -2
  78. package/dist/commands/sandbox/env/get_all/index.d.ts +1 -0
  79. package/dist/commands/sandbox/env/get_all/index.js +4 -2
  80. package/dist/commands/sandbox/env/list/index.d.ts +1 -0
  81. package/dist/commands/sandbox/env/list/index.js +4 -2
  82. package/dist/commands/sandbox/env/set/index.d.ts +1 -0
  83. package/dist/commands/sandbox/env/set/index.js +4 -2
  84. package/dist/commands/sandbox/env/set_all/index.d.ts +1 -0
  85. package/dist/commands/sandbox/env/set_all/index.js +4 -2
  86. package/dist/commands/sandbox/get/index.d.ts +1 -0
  87. package/dist/commands/sandbox/get/index.js +2 -0
  88. package/dist/commands/sandbox/license/get/index.d.ts +1 -0
  89. package/dist/commands/sandbox/license/get/index.js +4 -2
  90. package/dist/commands/sandbox/license/set/index.d.ts +1 -0
  91. package/dist/commands/sandbox/license/set/index.js +4 -2
  92. package/dist/commands/sandbox/pull/index.d.ts +2 -3
  93. package/dist/commands/sandbox/pull/index.js +19 -14
  94. package/dist/commands/sandbox/push/index.d.ts +12 -4
  95. package/dist/commands/sandbox/push/index.js +150 -95
  96. package/dist/commands/sandbox/reset/index.d.ts +1 -0
  97. package/dist/commands/sandbox/reset/index.js +4 -2
  98. package/dist/commands/sandbox/review/index.d.ts +1 -0
  99. package/dist/commands/sandbox/review/index.js +4 -2
  100. package/dist/commands/sandbox/unit_test/list/index.d.ts +1 -0
  101. package/dist/commands/sandbox/unit_test/list/index.js +4 -2
  102. package/dist/commands/sandbox/unit_test/run/index.d.ts +1 -0
  103. package/dist/commands/sandbox/unit_test/run/index.js +4 -2
  104. package/dist/commands/sandbox/unit_test/run_all/index.d.ts +1 -0
  105. package/dist/commands/sandbox/unit_test/run_all/index.js +4 -0
  106. package/dist/commands/sandbox/workflow_test/list/index.d.ts +1 -0
  107. package/dist/commands/sandbox/workflow_test/list/index.js +4 -2
  108. package/dist/commands/sandbox/workflow_test/run/index.d.ts +1 -0
  109. package/dist/commands/sandbox/workflow_test/run/index.js +4 -2
  110. package/dist/commands/sandbox/workflow_test/run_all/index.d.ts +1 -0
  111. package/dist/commands/sandbox/workflow_test/run_all/index.js +4 -0
  112. package/dist/commands/static_host/build/create/index.d.ts +1 -0
  113. package/dist/commands/static_host/build/create/index.js +1 -3
  114. package/dist/commands/static_host/build/get/index.d.ts +1 -0
  115. package/dist/commands/static_host/build/get/index.js +1 -4
  116. package/dist/commands/static_host/build/list/index.d.ts +1 -0
  117. package/dist/commands/static_host/build/list/index.js +1 -4
  118. package/dist/commands/static_host/list/index.d.ts +1 -0
  119. package/dist/commands/static_host/list/index.js +1 -4
  120. package/dist/commands/tenant/backup/create/index.d.ts +1 -0
  121. package/dist/commands/tenant/backup/create/index.js +1 -4
  122. package/dist/commands/tenant/backup/delete/index.d.ts +1 -0
  123. package/dist/commands/tenant/backup/delete/index.js +1 -4
  124. package/dist/commands/tenant/backup/export/index.d.ts +1 -0
  125. package/dist/commands/tenant/backup/export/index.js +1 -3
  126. package/dist/commands/tenant/backup/import/index.d.ts +1 -0
  127. package/dist/commands/tenant/backup/import/index.js +1 -3
  128. package/dist/commands/tenant/backup/list/index.d.ts +1 -0
  129. package/dist/commands/tenant/backup/list/index.js +1 -4
  130. package/dist/commands/tenant/backup/restore/index.d.ts +1 -0
  131. package/dist/commands/tenant/backup/restore/index.js +1 -4
  132. package/dist/commands/tenant/cluster/create/index.d.ts +1 -0
  133. package/dist/commands/tenant/cluster/create/index.js +1 -3
  134. package/dist/commands/tenant/cluster/delete/index.d.ts +1 -0
  135. package/dist/commands/tenant/cluster/delete/index.js +1 -4
  136. package/dist/commands/tenant/cluster/edit/index.d.ts +1 -0
  137. package/dist/commands/tenant/cluster/edit/index.js +1 -4
  138. package/dist/commands/tenant/cluster/get/index.d.ts +1 -0
  139. package/dist/commands/tenant/cluster/get/index.js +1 -4
  140. package/dist/commands/tenant/cluster/license/get/index.d.ts +1 -0
  141. package/dist/commands/tenant/cluster/license/get/index.js +1 -3
  142. package/dist/commands/tenant/cluster/license/set/index.d.ts +1 -0
  143. package/dist/commands/tenant/cluster/license/set/index.js +1 -3
  144. package/dist/commands/tenant/cluster/list/index.d.ts +1 -0
  145. package/dist/commands/tenant/cluster/list/index.js +1 -4
  146. package/dist/commands/tenant/create/index.d.ts +1 -0
  147. package/dist/commands/tenant/create/index.js +1 -3
  148. package/dist/commands/tenant/delete/index.d.ts +1 -0
  149. package/dist/commands/tenant/delete/index.js +1 -4
  150. package/dist/commands/tenant/deploy_platform/index.d.ts +1 -0
  151. package/dist/commands/tenant/deploy_platform/index.js +1 -3
  152. package/dist/commands/tenant/deploy_release/index.d.ts +1 -0
  153. package/dist/commands/tenant/deploy_release/index.js +1 -4
  154. package/dist/commands/tenant/edit/index.d.ts +1 -0
  155. package/dist/commands/tenant/edit/index.js +1 -4
  156. package/dist/commands/tenant/env/delete/index.d.ts +1 -0
  157. package/dist/commands/tenant/env/delete/index.js +1 -4
  158. package/dist/commands/tenant/env/get/index.d.ts +1 -0
  159. package/dist/commands/tenant/env/get/index.js +1 -4
  160. package/dist/commands/tenant/env/get_all/index.d.ts +1 -0
  161. package/dist/commands/tenant/env/get_all/index.js +1 -3
  162. package/dist/commands/tenant/env/list/index.d.ts +1 -0
  163. package/dist/commands/tenant/env/list/index.js +1 -4
  164. package/dist/commands/tenant/env/set/index.d.ts +1 -0
  165. package/dist/commands/tenant/env/set/index.js +1 -4
  166. package/dist/commands/tenant/env/set_all/index.d.ts +1 -0
  167. package/dist/commands/tenant/env/set_all/index.js +1 -3
  168. package/dist/commands/tenant/get/index.d.ts +1 -0
  169. package/dist/commands/tenant/get/index.js +1 -4
  170. package/dist/commands/tenant/impersonate/index.d.ts +1 -0
  171. package/dist/commands/tenant/impersonate/index.js +1 -4
  172. package/dist/commands/tenant/license/get/index.d.ts +1 -0
  173. package/dist/commands/tenant/license/get/index.js +1 -3
  174. package/dist/commands/tenant/license/set/index.d.ts +1 -0
  175. package/dist/commands/tenant/license/set/index.js +1 -3
  176. package/dist/commands/tenant/list/index.d.ts +1 -0
  177. package/dist/commands/tenant/list/index.js +1 -4
  178. package/dist/commands/tenant/pull/index.d.ts +2 -3
  179. package/dist/commands/tenant/pull/index.js +20 -21
  180. package/dist/commands/tenant/push/index.d.ts +2 -22
  181. package/dist/commands/tenant/push/index.js +7 -225
  182. package/dist/commands/tenant/unit_test/list/index.d.ts +1 -0
  183. package/dist/commands/tenant/unit_test/list/index.js +2 -27
  184. package/dist/commands/tenant/unit_test/run/index.d.ts +1 -0
  185. package/dist/commands/tenant/unit_test/run/index.js +2 -27
  186. package/dist/commands/tenant/unit_test/run_all/index.d.ts +1 -0
  187. package/dist/commands/tenant/unit_test/run_all/index.js +2 -27
  188. package/dist/commands/tenant/workflow_test/list/index.d.ts +1 -0
  189. package/dist/commands/tenant/workflow_test/list/index.js +2 -27
  190. package/dist/commands/tenant/workflow_test/run/index.d.ts +1 -0
  191. package/dist/commands/tenant/workflow_test/run/index.js +2 -27
  192. package/dist/commands/tenant/workflow_test/run_all/index.d.ts +1 -0
  193. package/dist/commands/tenant/workflow_test/run_all/index.js +2 -27
  194. package/dist/commands/unit_test/list/index.d.ts +1 -0
  195. package/dist/commands/unit_test/list/index.js +1 -4
  196. package/dist/commands/unit_test/run/index.d.ts +1 -0
  197. package/dist/commands/unit_test/run/index.js +1 -4
  198. package/dist/commands/unit_test/run_all/index.d.ts +1 -0
  199. package/dist/commands/unit_test/run_all/index.js +1 -4
  200. package/dist/commands/update/index.d.ts +1 -0
  201. package/dist/commands/workflow_test/delete/index.d.ts +1 -0
  202. package/dist/commands/workflow_test/delete/index.js +1 -4
  203. package/dist/commands/workflow_test/get/index.d.ts +1 -0
  204. package/dist/commands/workflow_test/get/index.js +1 -4
  205. package/dist/commands/workflow_test/list/index.d.ts +1 -0
  206. package/dist/commands/workflow_test/list/index.js +1 -4
  207. package/dist/commands/workflow_test/run/index.d.ts +1 -0
  208. package/dist/commands/workflow_test/run/index.js +1 -4
  209. package/dist/commands/workflow_test/run_all/index.d.ts +1 -0
  210. package/dist/commands/workflow_test/run_all/index.js +1 -4
  211. package/dist/commands/workspace/create/index.d.ts +1 -0
  212. package/dist/commands/workspace/create/index.js +1 -4
  213. package/dist/commands/workspace/delete/index.d.ts +2 -6
  214. package/dist/commands/workspace/delete/index.js +17 -16
  215. package/dist/commands/workspace/edit/index.d.ts +2 -6
  216. package/dist/commands/workspace/edit/index.js +16 -20
  217. package/dist/commands/workspace/get/index.d.ts +2 -6
  218. package/dist/commands/workspace/get/index.js +14 -18
  219. package/dist/commands/workspace/git/pull/index.d.ts +2 -3
  220. package/dist/commands/workspace/git/pull/index.js +18 -17
  221. package/dist/commands/workspace/list/index.d.ts +1 -0
  222. package/dist/commands/workspace/list/index.js +1 -4
  223. package/dist/commands/workspace/pull/index.d.ts +2 -3
  224. package/dist/commands/workspace/pull/index.js +21 -24
  225. package/dist/commands/workspace/push/index.d.ts +7 -16
  226. package/dist/commands/workspace/push/index.js +85 -700
  227. package/dist/utils/multidoc-push.d.ts +63 -0
  228. package/dist/utils/multidoc-push.js +690 -0
  229. package/dist/utils/reference-checker.d.ts +57 -0
  230. package/dist/utils/reference-checker.js +232 -0
  231. package/oclif.manifest.json +3562 -2647
  232. package/package.json +1 -1
  233. package/dist/commands/sandbox/workflow_test/delete/index.d.ts +0 -17
  234. package/dist/commands/sandbox/workflow_test/delete/index.js +0 -59
  235. package/dist/commands/sandbox/workflow_test/get/index.d.ts +0 -17
  236. package/dist/commands/sandbox/workflow_test/get/index.js +0 -58
  237. package/dist/commands/tenant/workflow_test/delete/index.d.ts +0 -19
  238. package/dist/commands/tenant/workflow_test/delete/index.js +0 -110
  239. package/dist/commands/tenant/workflow_test/get/index.d.ts +0 -19
  240. package/dist/commands/tenant/workflow_test/get/index.js +0 -112
@@ -1,35 +1,106 @@
1
- import { Args, Flags } from '@oclif/core';
2
- import BaseCommand from '../../../base-command.js';
3
- import { findFilesWithGuid } from '../../../utils/document-parser.js';
1
+ import { Flags } from '@oclif/core';
4
2
  import * as fs from 'node:fs';
5
- import * as path from 'node:path';
3
+ import { resolve } from 'node:path';
4
+ import open from 'open';
5
+ import BaseCommand from '../../../base-command.js';
6
+ import { executePush } from '../../../utils/multidoc-push.js';
6
7
  export default class SandboxPush extends BaseCommand {
7
- static args = {
8
- directory: Args.string({
9
- description: 'Directory containing documents to push (as produced by sandbox pull or workspace pull)',
10
- required: true,
11
- }),
12
- };
13
- static description = 'Push local documents to your sandbox environment via multidoc import';
8
+ static description = 'Push local documents to your sandbox environment via multidoc import. By default, only changed files are pushed (partial mode). Use --sync to push all files. Shows a preview of changes before pushing unless --force is specified. Use --dry-run to preview only.';
14
9
  static examples = [
15
- `$ xano sandbox push ./my-workspace
16
- Pushed 42 documents to sandbox environment from ./my-workspace
10
+ `$ xano sandbox push
11
+ Push from current directory (default partial mode)
12
+ `,
13
+ `$ xano sandbox push -d ./my-workspace
14
+ Push from a specific directory
15
+ `,
16
+ `$ xano sandbox push --sync
17
+ Push all files to the sandbox
18
+ `,
19
+ `$ xano sandbox push --sync --delete
20
+ Push all files and delete remote objects not included
21
+ `,
22
+ `$ xano sandbox push --dry-run
23
+ Preview changes without pushing
24
+ `,
25
+ `$ xano sandbox push --force
26
+ Skip preview and push immediately
27
+ `,
28
+ `$ xano sandbox push --records --env`,
29
+ `$ xano sandbox push --truncate`,
30
+ `$ xano sandbox push -i "**/func*"
31
+ Push only files matching the glob pattern
32
+ `,
33
+ `$ xano sandbox push -i "function/*" -i "table/*"
34
+ Push files matching multiple patterns
35
+ `,
36
+ `$ xano sandbox push -e "table/*"
37
+ Push all files except tables
38
+ `,
39
+ `$ xano sandbox push --review
40
+ Push and open sandbox review in the browser
17
41
  `,
18
- `$ xano sandbox push ./backup --records --env`,
19
- `$ xano sandbox push ./my-workspace --truncate`,
20
42
  ];
21
43
  static flags = {
22
44
  ...BaseCommand.baseFlags,
45
+ directory: Flags.string({
46
+ char: 'd',
47
+ default: '.',
48
+ description: 'Directory containing documents to push (defaults to current directory)',
49
+ required: false,
50
+ }),
51
+ delete: Flags.boolean({
52
+ default: false,
53
+ description: 'Delete sandbox objects not included in the push (requires --sync)',
54
+ required: false,
55
+ }),
56
+ 'dry-run': Flags.boolean({
57
+ default: false,
58
+ description: 'Show preview of changes without pushing (exit after preview)',
59
+ required: false,
60
+ }),
23
61
  env: Flags.boolean({
24
62
  default: false,
25
63
  description: 'Include environment variables in import',
26
64
  required: false,
27
65
  }),
66
+ exclude: Flags.string({
67
+ char: 'e',
68
+ description: 'Glob pattern to exclude files (e.g. "table/*", "**/test*"). Matched against relative paths from the push directory.',
69
+ multiple: true,
70
+ required: false,
71
+ }),
72
+ force: Flags.boolean({
73
+ default: false,
74
+ description: 'Skip preview and confirmation prompt (for CI/CD pipelines)',
75
+ required: false,
76
+ }),
77
+ guids: Flags.boolean({
78
+ allowNo: true,
79
+ default: true,
80
+ description: 'Write server-assigned GUIDs back to local files (use --no-guids to skip)',
81
+ required: false,
82
+ }),
83
+ include: Flags.string({
84
+ char: 'i',
85
+ description: 'Glob pattern to include files (e.g. "**/func*", "table/*.xs"). Matched against relative paths from the push directory.',
86
+ multiple: true,
87
+ required: false,
88
+ }),
28
89
  records: Flags.boolean({
29
90
  default: false,
30
91
  description: 'Include records in import',
31
92
  required: false,
32
93
  }),
94
+ review: Flags.boolean({
95
+ default: false,
96
+ description: 'Open sandbox review in the browser after pushing',
97
+ required: false,
98
+ }),
99
+ sync: Flags.boolean({
100
+ default: false,
101
+ description: 'Full push — send all files, not just changed ones. Required for --delete.',
102
+ required: false,
103
+ }),
33
104
  transaction: Flags.boolean({
34
105
  allowNo: true,
35
106
  default: true,
@@ -43,99 +114,83 @@ Pushed 42 documents to sandbox environment from ./my-workspace
43
114
  }),
44
115
  };
45
116
  async run() {
46
- const { args, flags } = await this.parse(SandboxPush);
117
+ const { flags } = await this.parse(SandboxPush);
47
118
  const { profile } = this.resolveProfile(flags);
48
- const inputDir = path.resolve(args.directory);
119
+ const inputDir = resolve(flags.directory);
49
120
  if (!fs.existsSync(inputDir)) {
50
121
  this.error(`Directory not found: ${inputDir}`);
51
122
  }
52
123
  if (!fs.statSync(inputDir).isDirectory()) {
53
124
  this.error(`Not a directory: ${inputDir}`);
54
125
  }
55
- const files = this.collectFiles(inputDir);
56
- if (files.length === 0) {
57
- this.error(`No .xs files found in ${args.directory}`);
126
+ const baseUrl = `${profile.instance_origin}/api:meta/sandbox`;
127
+ const target = {
128
+ buildDryRunUrl: (params) => `${baseUrl}/multidoc/dry-run?${params.toString()}`,
129
+ buildPushUrl: (params) => `${baseUrl}/multidoc?${params.toString()}`,
130
+ cliVersion: this.config.version,
131
+ instanceOrigin: profile.instance_origin,
132
+ label: 'sandbox environment',
133
+ supportsBranches: false,
134
+ supportsPartial: true,
135
+ };
136
+ const pushFlags = {
137
+ delete: flags.delete,
138
+ 'dry-run': flags['dry-run'],
139
+ env: flags.env,
140
+ exclude: flags.exclude,
141
+ force: flags.force,
142
+ guids: flags.guids,
143
+ include: flags.include,
144
+ records: flags.records,
145
+ sync: flags.sync,
146
+ transaction: flags.transaction,
147
+ truncate: flags.truncate,
148
+ verbose: flags.verbose,
149
+ };
150
+ await executePush({
151
+ accessToken: profile.access_token,
152
+ branch: '',
153
+ command: this,
154
+ inputDir,
155
+ verboseFetch: this.verboseFetch.bind(this),
156
+ }, target, pushFlags);
157
+ if (flags.review) {
158
+ await this.openReview(profile.instance_origin, profile.access_token, flags.verbose);
58
159
  }
59
- const documentEntries = [];
60
- for (const filePath of files) {
61
- const content = fs.readFileSync(filePath, 'utf8').trim();
62
- if (content) {
63
- documentEntries.push({ content, filePath });
64
- }
160
+ }
161
+ async openReview(instanceOrigin, accessToken, verbose) {
162
+ const response = await this.verboseFetch(`${instanceOrigin}/api:meta/sandbox/impersonate`, {
163
+ headers: {
164
+ accept: 'application/json',
165
+ Authorization: `Bearer ${accessToken}`,
166
+ },
167
+ method: 'GET',
168
+ }, verbose, accessToken);
169
+ if (!response.ok) {
170
+ const message = await this.parseApiError(response, 'Failed to open sandbox review');
171
+ this.error(message);
65
172
  }
66
- if (documentEntries.length === 0) {
67
- this.error(`All .xs files in ${args.directory} are empty`);
173
+ const result = (await response.json());
174
+ if (!result._ti) {
175
+ this.error('No one-time token returned from impersonate API');
68
176
  }
69
- const multidoc = documentEntries.map((d) => d.content).join('\n---\n');
70
- const queryParams = new URLSearchParams({
71
- env: flags.env.toString(),
72
- records: flags.records.toString(),
73
- transaction: flags.transaction.toString(),
74
- truncate: flags.truncate.toString(),
75
- });
76
- const apiUrl = `${profile.instance_origin}/api:meta/sandbox/multidoc?${queryParams.toString()}`;
77
- const startTime = Date.now();
177
+ const frontendUrl = this.getFrontendUrl(instanceOrigin);
178
+ const params = new URLSearchParams({ _ti: result._ti });
179
+ const reviewUrl = `${frontendUrl}/impersonate?${params.toString()}`;
180
+ this.log('Opening sandbox review...');
181
+ await open(reviewUrl);
182
+ }
183
+ getFrontendUrl(instanceOrigin) {
78
184
  try {
79
- const response = await this.verboseFetch(apiUrl, {
80
- body: multidoc,
81
- headers: {
82
- accept: 'application/json',
83
- Authorization: `Bearer ${profile.access_token}`,
84
- 'Content-Type': 'text/x-xanoscript',
85
- },
86
- method: 'POST',
87
- }, flags.verbose, profile.access_token);
88
- if (!response.ok) {
89
- const errorText = await response.text();
90
- let errorMessage = `Push failed (${response.status})`;
91
- try {
92
- const errorJson = JSON.parse(errorText);
93
- errorMessage += `: ${errorJson.message}`;
94
- if (errorJson.payload?.param) {
95
- errorMessage += `\n Parameter: ${errorJson.payload.param}`;
96
- }
97
- }
98
- catch {
99
- errorMessage += `\n${errorText}`;
100
- }
101
- const guidMatch = errorMessage.match(/Duplicate \w+ guid: (\S+)/);
102
- if (guidMatch) {
103
- const dupeFiles = findFilesWithGuid(documentEntries, guidMatch[1]);
104
- if (dupeFiles.length > 0) {
105
- const relPaths = dupeFiles.map((f) => path.relative(inputDir, f));
106
- errorMessage += `\n Local files with this GUID:\n${relPaths.map((f) => ` ${f}`).join('\n')}`;
107
- }
108
- }
109
- this.error(errorMessage);
110
- }
111
- const responseText = await response.text();
112
- if (responseText && responseText !== 'null' && flags.verbose) {
113
- this.log(responseText);
185
+ const url = new URL(instanceOrigin);
186
+ if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
187
+ url.port = '4200';
188
+ return url.origin;
114
189
  }
115
190
  }
116
- catch (error) {
117
- if (error instanceof Error) {
118
- this.error(`Failed to push multidoc: ${error.message}`);
119
- }
120
- else {
121
- this.error(`Failed to push multidoc: ${String(error)}`);
122
- }
123
- }
124
- const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
125
- this.log(`Pushed ${documentEntries.length} documents to sandbox environment from ${args.directory} in ${elapsed}s`);
126
- }
127
- collectFiles(dir) {
128
- const files = [];
129
- const entries = fs.readdirSync(dir, { withFileTypes: true });
130
- for (const entry of entries) {
131
- const fullPath = path.join(dir, entry.name);
132
- if (entry.isDirectory()) {
133
- files.push(...this.collectFiles(fullPath));
134
- }
135
- else if (entry.isFile() && entry.name.endsWith('.xs')) {
136
- files.push(fullPath);
137
- }
191
+ catch {
192
+ // fall through
138
193
  }
139
- return files.sort();
194
+ return instanceOrigin;
140
195
  }
141
196
  }
@@ -4,6 +4,7 @@ export default class SandboxReset extends BaseCommand {
4
4
  static examples: string[];
5
5
  static flags: {
6
6
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
8
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
10
  };
@@ -39,12 +39,14 @@ Sandbox environment has been reset.
39
39
  method: 'POST',
40
40
  }, flags.verbose, profile.access_token);
41
41
  if (!response.ok) {
42
- const errorText = await response.text();
43
- this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
42
+ const message = await this.parseApiError(response, 'API request failed');
43
+ this.error(message);
44
44
  }
45
45
  this.log('Sandbox environment has been reset.');
46
46
  }
47
47
  catch (error) {
48
+ if (error instanceof Error && 'oclif' in error)
49
+ throw error;
48
50
  if (error instanceof Error) {
49
51
  this.error(`Failed to reset sandbox environment: ${error.message}`);
50
52
  }
@@ -5,6 +5,7 @@ export default class SandboxReview extends BaseCommand {
5
5
  static flags: {
6
6
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
7
7
  'url-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
@@ -40,8 +40,8 @@ Review session started!
40
40
  method: 'GET',
41
41
  }, flags.verbose, profile.access_token);
42
42
  if (!response.ok) {
43
- const errorText = await response.text();
44
- this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
43
+ const message = await this.parseApiError(response, 'API request failed');
44
+ this.error(message);
45
45
  }
46
46
  const result = (await response.json());
47
47
  if (!result._ti) {
@@ -68,6 +68,8 @@ Review session started!
68
68
  process.exit(0);
69
69
  }
70
70
  catch (error) {
71
+ if (error instanceof Error && 'oclif' in error)
72
+ throw error;
71
73
  if (error instanceof Error) {
72
74
  this.error(`Failed to open sandbox review: ${error.message}`);
73
75
  }
@@ -6,6 +6,7 @@ export default class SandboxUnitTestList extends BaseCommand {
6
6
  branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
7
  'obj-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
@@ -48,8 +48,8 @@ Unit tests:
48
48
  method: 'GET',
49
49
  }, flags.verbose, profile.access_token);
50
50
  if (!response.ok) {
51
- const errorText = await response.text();
52
- this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
51
+ const message = await this.parseApiError(response, 'API request failed');
52
+ this.error(message);
53
53
  }
54
54
  const data = (await response.json());
55
55
  let tests;
@@ -78,6 +78,8 @@ Unit tests:
78
78
  }
79
79
  }
80
80
  catch (error) {
81
+ if (error instanceof Error && 'oclif' in error)
82
+ throw error;
81
83
  if (error instanceof Error) {
82
84
  this.error(`Failed to list unit tests: ${error.message}`);
83
85
  }
@@ -7,6 +7,7 @@ export default class SandboxUnitTestRun extends BaseCommand {
7
7
  static examples: string[];
8
8
  static flags: {
9
9
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  };
@@ -42,8 +42,8 @@ Result: PASS
42
42
  method: 'POST',
43
43
  }, flags.verbose, profile.access_token);
44
44
  if (!response.ok) {
45
- const errorText = await response.text();
46
- this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
45
+ const message = await this.parseApiError(response, 'API request failed');
46
+ this.error(message);
47
47
  }
48
48
  const result = (await response.json());
49
49
  if (flags.output === 'json') {
@@ -66,6 +66,8 @@ Result: PASS
66
66
  }
67
67
  }
68
68
  catch (error) {
69
+ if (error instanceof Error && 'oclif' in error)
70
+ throw error;
69
71
  if (error instanceof Error) {
70
72
  this.error(`Failed to run unit test: ${error.message}`);
71
73
  }
@@ -6,6 +6,7 @@ export default class SandboxUnitTestRunAll extends BaseCommand {
6
6
  branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
7
  'obj-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
@@ -128,6 +128,8 @@ Results: 4 passed, 1 failed
128
128
  }
129
129
  }
130
130
  catch (error) {
131
+ if (error instanceof Error && 'oclif' in error)
132
+ throw error;
131
133
  const message = error instanceof Error ? error.message : String(error);
132
134
  results.push({
133
135
  message,
@@ -156,6 +158,8 @@ Results: 4 passed, 1 failed
156
158
  }
157
159
  }
158
160
  catch (error) {
161
+ if (error instanceof Error && 'oclif' in error)
162
+ throw error;
159
163
  if (error instanceof Error) {
160
164
  this.error(`Failed to run unit tests: ${error.message}`);
161
165
  }
@@ -5,6 +5,7 @@ export default class SandboxWorkflowTestList extends BaseCommand {
5
5
  static flags: {
6
6
  branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
7
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
@@ -41,8 +41,8 @@ Workflow tests:
41
41
  method: 'GET',
42
42
  }, flags.verbose, profile.access_token);
43
43
  if (!response.ok) {
44
- const errorText = await response.text();
45
- this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
44
+ const message = await this.parseApiError(response, 'API request failed');
45
+ this.error(message);
46
46
  }
47
47
  const data = (await response.json());
48
48
  let tests;
@@ -71,6 +71,8 @@ Workflow tests:
71
71
  }
72
72
  }
73
73
  catch (error) {
74
+ if (error instanceof Error && 'oclif' in error)
75
+ throw error;
74
76
  if (error instanceof Error) {
75
77
  this.error(`Failed to list workflow tests: ${error.message}`);
76
78
  }
@@ -10,6 +10,7 @@ export default class SandboxWorkflowTestRun extends BaseCommand {
10
10
  static examples: string[];
11
11
  static flags: {
12
12
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
15
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
16
  };
@@ -42,8 +42,8 @@ Result: PASS (0.25s)
42
42
  method: 'POST',
43
43
  }, flags.verbose, profile.access_token);
44
44
  if (!response.ok) {
45
- const errorText = await response.text();
46
- this.error(`API request failed with status ${response.status}: ${response.statusText}\n${errorText}`);
45
+ const message = await this.parseApiError(response, 'API request failed');
46
+ this.error(message);
47
47
  }
48
48
  const result = (await response.json());
49
49
  if (flags.output === 'json') {
@@ -64,6 +64,8 @@ Result: PASS (0.25s)
64
64
  }
65
65
  }
66
66
  catch (error) {
67
+ if (error instanceof Error && 'oclif' in error)
68
+ throw error;
67
69
  if (error instanceof Error) {
68
70
  this.error(`Failed to run workflow test: ${error.message}`);
69
71
  }
@@ -5,6 +5,7 @@ export default class SandboxWorkflowTestRunAll extends BaseCommand {
5
5
  static flags: {
6
6
  branch: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
7
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
9
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
10
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  };
@@ -116,6 +116,8 @@ Results: 2 passed, 1 failed
116
116
  }
117
117
  }
118
118
  catch (error) {
119
+ if (error instanceof Error && 'oclif' in error)
120
+ throw error;
119
121
  const message = error instanceof Error ? error.message : String(error);
120
122
  results.push({
121
123
  message,
@@ -142,6 +144,8 @@ Results: 2 passed, 1 failed
142
144
  }
143
145
  }
144
146
  catch (error) {
147
+ if (error instanceof Error && 'oclif' in error)
148
+ throw error;
145
149
  if (error instanceof Error) {
146
150
  this.error(`Failed to run workflow tests: ${error.message}`);
147
151
  }
@@ -11,6 +11,7 @@ export default class StaticHostBuildCreate extends BaseCommand {
11
11
  name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
12
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
13
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
15
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
16
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
17
  };
@@ -1,7 +1,6 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import * as yaml from 'js-yaml';
3
3
  import * as fs from 'node:fs';
4
- import * as os from 'node:os';
5
4
  import * as path from 'node:path';
6
5
  import BaseCommand from '../../../../base-command.js';
7
6
  export default class StaticHostBuildCreate extends BaseCommand {
@@ -171,8 +170,7 @@ Description: Production build
171
170
  }
172
171
  }
173
172
  loadCredentials() {
174
- const configDir = path.join(os.homedir(), '.xano');
175
- const credentialsPath = path.join(configDir, 'credentials.yaml');
173
+ const credentialsPath = this.getCredentialsPath();
176
174
  // Check if credentials file exists
177
175
  if (!fs.existsSync(credentialsPath)) {
178
176
  this.error(`Credentials file not found at ${credentialsPath}\n` +
@@ -9,6 +9,7 @@ export default class StaticHostBuildGet extends BaseCommand {
9
9
  static flags: {
10
10
  output: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
11
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
15
  };
@@ -1,8 +1,6 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import * as yaml from 'js-yaml';
3
3
  import * as fs from 'node:fs';
4
- import * as os from 'node:os';
5
- import * as path from 'node:path';
6
4
  import BaseCommand from '../../../../base-command.js';
7
5
  export default class StaticHostBuildGet extends BaseCommand {
8
6
  static args = {
@@ -142,8 +140,7 @@ Name: production-build
142
140
  }
143
141
  }
144
142
  loadCredentials() {
145
- const configDir = path.join(os.homedir(), '.xano');
146
- const credentialsPath = path.join(configDir, 'credentials.yaml');
143
+ const credentialsPath = this.getCredentialsPath();
147
144
  // Check if credentials file exists
148
145
  if (!fs.existsSync(credentialsPath)) {
149
146
  this.error(`Credentials file not found at ${credentialsPath}\n` +
@@ -10,6 +10,7 @@ export default class StaticHostBuildList extends BaseCommand {
10
10
  page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
11
11
  per_page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
12
12
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
14
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
15
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
16
  };
@@ -1,8 +1,6 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import * as yaml from 'js-yaml';
3
3
  import * as fs from 'node:fs';
4
- import * as os from 'node:os';
5
- import * as path from 'node:path';
6
4
  import BaseCommand from '../../../../base-command.js';
7
5
  export default class StaticHostBuildList extends BaseCommand {
8
6
  static args = {
@@ -169,8 +167,7 @@ Available builds:
169
167
  }
170
168
  }
171
169
  loadCredentials() {
172
- const configDir = path.join(os.homedir(), '.xano');
173
- const credentialsPath = path.join(configDir, 'credentials.yaml');
170
+ const credentialsPath = this.getCredentialsPath();
174
171
  // Check if credentials file exists
175
172
  if (!fs.existsSync(credentialsPath)) {
176
173
  this.error(`Credentials file not found at ${credentialsPath}\n` +
@@ -8,6 +8,7 @@ export default class StaticHostList extends BaseCommand {
8
8
  page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
9
9
  per_page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
10
10
  workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
13
  verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
14
  };
@@ -1,8 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import * as yaml from 'js-yaml';
3
3
  import * as fs from 'node:fs';
4
- import * as os from 'node:os';
5
- import * as path from 'node:path';
6
4
  import BaseCommand from '../../../base-command.js';
7
5
  export default class StaticHostList extends BaseCommand {
8
6
  static args = {};
@@ -164,8 +162,7 @@ Available static hosts:
164
162
  }
165
163
  }
166
164
  loadCredentials() {
167
- const configDir = path.join(os.homedir(), '.xano');
168
- const credentialsPath = path.join(configDir, 'credentials.yaml');
165
+ const credentialsPath = this.getCredentialsPath();
169
166
  // Check if credentials file exists
170
167
  if (!fs.existsSync(credentialsPath)) {
171
168
  this.error(`Credentials file not found at ${credentialsPath}\n` +