@xano/sdk 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 (336) hide show
  1. package/CHANGELOG.md +788 -0
  2. package/LICENSE +21 -0
  3. package/README.md +541 -0
  4. package/dist/.build-fingerprint +1 -0
  5. package/dist/agent-file-refresh-ZEMK33AX.js +38 -0
  6. package/dist/answer-shape-K67TAAPY.js +33 -0
  7. package/dist/backend-dir-2U53CIAL.js +39 -0
  8. package/dist/backend-slot-7QXW2QYC.js +28 -0
  9. package/dist/bin.d.ts +1 -0
  10. package/dist/bin.js +255 -0
  11. package/dist/branch-NCRNWQBQ.js +61 -0
  12. package/dist/branch-commands-ACIRCEQJ.js +79 -0
  13. package/dist/bundle.d.ts +223 -0
  14. package/dist/bundle.js +153 -0
  15. package/dist/capture-WAXPD7DR.js +41 -0
  16. package/dist/chunk-27GWNP2I.js +23 -0
  17. package/dist/chunk-2CMJZDCL.js +177 -0
  18. package/dist/chunk-2EZ3ZJZZ.js +302 -0
  19. package/dist/chunk-2GNFDZA2.js +390 -0
  20. package/dist/chunk-2MNLRIOX.js +11 -0
  21. package/dist/chunk-2TFRR6JV.js +104 -0
  22. package/dist/chunk-2ZUEE4EK.js +408 -0
  23. package/dist/chunk-36JUQZVQ.js +22 -0
  24. package/dist/chunk-3DRXWLJ5.js +83 -0
  25. package/dist/chunk-3JDU4VRL.js +137 -0
  26. package/dist/chunk-3KZCLN4Z.js +241 -0
  27. package/dist/chunk-3LDULPRE.js +168 -0
  28. package/dist/chunk-3M4LFYNQ.js +111 -0
  29. package/dist/chunk-3XI5CTUE.js +22 -0
  30. package/dist/chunk-3YW23UFH.js +50 -0
  31. package/dist/chunk-45UIT5T2.js +862 -0
  32. package/dist/chunk-4BW2SG2N.js +46 -0
  33. package/dist/chunk-4KHEMC32.js +79 -0
  34. package/dist/chunk-4POWHT2K.js +321 -0
  35. package/dist/chunk-4Q2GN7NW.js +192 -0
  36. package/dist/chunk-5A2PLKZB.js +70 -0
  37. package/dist/chunk-5FBYXK5P.js +159 -0
  38. package/dist/chunk-5HVXPTU4.js +323 -0
  39. package/dist/chunk-5QU2P5CA.js +304 -0
  40. package/dist/chunk-5V5XU32D.js +191 -0
  41. package/dist/chunk-5ZSO4T2G.js +10 -0
  42. package/dist/chunk-622HUCZT.js +10 -0
  43. package/dist/chunk-65HWOMGV.js +177 -0
  44. package/dist/chunk-6ELFRN3P.js +365 -0
  45. package/dist/chunk-6HO3NCSJ.js +1055 -0
  46. package/dist/chunk-6IUMKYWI.js +457 -0
  47. package/dist/chunk-6KEB3C3U.js +65 -0
  48. package/dist/chunk-6MR5TI5V.js +987 -0
  49. package/dist/chunk-6NNTNXN7.js +157 -0
  50. package/dist/chunk-6PBR37LN.js +97 -0
  51. package/dist/chunk-6QXLMMT3.js +155 -0
  52. package/dist/chunk-6ZGJCBZS.js +224 -0
  53. package/dist/chunk-73IVBKMA.js +250 -0
  54. package/dist/chunk-7G3CTU7I.js +286 -0
  55. package/dist/chunk-A3UDOZB6.js +222 -0
  56. package/dist/chunk-A6E77GDW.js +85 -0
  57. package/dist/chunk-AHJWCYQO.js +660 -0
  58. package/dist/chunk-AKNCFVKZ.js +36 -0
  59. package/dist/chunk-AM4FKL4J.js +237 -0
  60. package/dist/chunk-AN6BL47T.js +26 -0
  61. package/dist/chunk-AOSYVMUS.js +147 -0
  62. package/dist/chunk-APWXY5SL.js +182 -0
  63. package/dist/chunk-AWEIIR2U.js +218 -0
  64. package/dist/chunk-AWQ7BQTQ.js +73 -0
  65. package/dist/chunk-BAUZOWGX.js +393 -0
  66. package/dist/chunk-BHACRS6R.js +940 -0
  67. package/dist/chunk-BWPNT55T.js +680 -0
  68. package/dist/chunk-C6RIMRU4.js +187 -0
  69. package/dist/chunk-CGVQZXYG.js +263 -0
  70. package/dist/chunk-CIMQZRRE.js +162 -0
  71. package/dist/chunk-CJHQKJU4.js +113 -0
  72. package/dist/chunk-CTMO46PK.js +1897 -0
  73. package/dist/chunk-D53J55C5.js +106 -0
  74. package/dist/chunk-D7VGG3WS.js +90 -0
  75. package/dist/chunk-DAD3ONIS.js +245 -0
  76. package/dist/chunk-DGWHAVRG.js +162 -0
  77. package/dist/chunk-DX4QR4B2.js +483 -0
  78. package/dist/chunk-EHP3WPEG.js +21 -0
  79. package/dist/chunk-ELP2HMXC.js +432 -0
  80. package/dist/chunk-ERVIASGL.js +21 -0
  81. package/dist/chunk-ET4Y4XCB.js +865 -0
  82. package/dist/chunk-ETO6LGLD.js +4750 -0
  83. package/dist/chunk-EUUUMY7J.js +24 -0
  84. package/dist/chunk-FQJ2TTHP.js +218 -0
  85. package/dist/chunk-FQOF7E2E.js +133 -0
  86. package/dist/chunk-FWD7AL6N.js +21 -0
  87. package/dist/chunk-FXFV3KU6.js +138 -0
  88. package/dist/chunk-FXRQOWTE.js +171 -0
  89. package/dist/chunk-G554NZNI.js +210 -0
  90. package/dist/chunk-GSGDWJB5.js +13 -0
  91. package/dist/chunk-H2V3N23D.js +66 -0
  92. package/dist/chunk-H5C7MBCR.js +159 -0
  93. package/dist/chunk-HHN734BU.js +371 -0
  94. package/dist/chunk-HRVBLCVJ.js +243 -0
  95. package/dist/chunk-HVRUQXGD.js +1264 -0
  96. package/dist/chunk-I4F5TCJH.js +130 -0
  97. package/dist/chunk-IDOTCMDV.js +57 -0
  98. package/dist/chunk-IGOLHKWF.js +427 -0
  99. package/dist/chunk-JGE7CAWB.js +148 -0
  100. package/dist/chunk-JTUKLCXS.js +92 -0
  101. package/dist/chunk-JZGVVJSI.js +66 -0
  102. package/dist/chunk-K4WX6UUU.js +923 -0
  103. package/dist/chunk-K5BBGTYZ.js +217 -0
  104. package/dist/chunk-K6RXBGUB.js +427 -0
  105. package/dist/chunk-KTSDSD36.js +165 -0
  106. package/dist/chunk-L5JYN3NM.js +1038 -0
  107. package/dist/chunk-LIEOKXKD.js +354 -0
  108. package/dist/chunk-LLNZGEFK.js +51 -0
  109. package/dist/chunk-LPECSJ2J.js +55 -0
  110. package/dist/chunk-LQCMKAJ7.js +413 -0
  111. package/dist/chunk-LRN4ZJCQ.js +1145 -0
  112. package/dist/chunk-MJ4ZVHKN.js +17 -0
  113. package/dist/chunk-MXHSQOC4.js +3061 -0
  114. package/dist/chunk-N5GKCWE5.js +367 -0
  115. package/dist/chunk-NA3PXMEF.js +10 -0
  116. package/dist/chunk-NAQDMEHV.js +76 -0
  117. package/dist/chunk-NWKXM5RD.js +109 -0
  118. package/dist/chunk-NZOWWHU5.js +196 -0
  119. package/dist/chunk-OHX6MIUZ.js +184 -0
  120. package/dist/chunk-OOXWYAA4.js +15 -0
  121. package/dist/chunk-OPHB5ORL.js +1990 -0
  122. package/dist/chunk-OXA6PSZT.js +1725 -0
  123. package/dist/chunk-P2HAGF6H.js +73 -0
  124. package/dist/chunk-PTHVMXZI.js +3036 -0
  125. package/dist/chunk-PZ5AY32C.js +10 -0
  126. package/dist/chunk-Q325JKYB.js +189 -0
  127. package/dist/chunk-Q7DGTQ5G.js +255 -0
  128. package/dist/chunk-QCNYHBL7.js +601 -0
  129. package/dist/chunk-QO6BVGS5.js +145 -0
  130. package/dist/chunk-QXTKSOFI.js +76 -0
  131. package/dist/chunk-ROY4YAEQ.js +66 -0
  132. package/dist/chunk-RTJRZFFY.js +447 -0
  133. package/dist/chunk-SAVJUS2X.js +128 -0
  134. package/dist/chunk-SFLHVCKX.js +173 -0
  135. package/dist/chunk-STSFNCY4.js +127 -0
  136. package/dist/chunk-SWBF2LTK.js +170 -0
  137. package/dist/chunk-T5KELPAR.js +109 -0
  138. package/dist/chunk-TFFGOFVZ.js +1481 -0
  139. package/dist/chunk-TLHIFK4M.js +256 -0
  140. package/dist/chunk-TLQE7ZN3.js +156 -0
  141. package/dist/chunk-TPHWJ7UG.js +1981 -0
  142. package/dist/chunk-TRJCALW3.js +291 -0
  143. package/dist/chunk-TTEIYD5P.js +133 -0
  144. package/dist/chunk-U64O2MXY.js +407 -0
  145. package/dist/chunk-UCQ2VUTG.js +630 -0
  146. package/dist/chunk-UTI5G6OJ.js +44 -0
  147. package/dist/chunk-UVHDMMIP.js +188 -0
  148. package/dist/chunk-UZ6QQWJT.js +120 -0
  149. package/dist/chunk-VAX3UZOY.js +686 -0
  150. package/dist/chunk-W3HBUKFO.js +57 -0
  151. package/dist/chunk-WKQLGKCL.js +2823 -0
  152. package/dist/chunk-WQFNASK4.js +560 -0
  153. package/dist/chunk-WRBKLYSJ.js +224 -0
  154. package/dist/chunk-WTPZRR4I.js +17 -0
  155. package/dist/chunk-WZDJ2LLX.js +2385 -0
  156. package/dist/chunk-X2NL4KUO.js +14 -0
  157. package/dist/chunk-X4DVXBFY.js +38 -0
  158. package/dist/chunk-X677DMZM.js +7050 -0
  159. package/dist/chunk-XBKMWY7F.js +612 -0
  160. package/dist/chunk-XNCP4KYO.js +78 -0
  161. package/dist/chunk-XVBGLISP.js +35 -0
  162. package/dist/chunk-XW6KISBB.js +1192 -0
  163. package/dist/chunk-YGZHHAAB.js +8156 -0
  164. package/dist/chunk-YHEFQUQB.js +186 -0
  165. package/dist/chunk-YLDALTWZ.js +653 -0
  166. package/dist/chunk-YU4HSQW7.js +229 -0
  167. package/dist/chunk-ZDPP6RKB.js +27 -0
  168. package/dist/chunk-ZFDZKNGG.js +194 -0
  169. package/dist/chunk-ZIY7RNUT.js +49 -0
  170. package/dist/chunk-ZYJP6BOO.js +30 -0
  171. package/dist/cli.d.ts +1111 -0
  172. package/dist/cli.js +165 -0
  173. package/dist/codegen-command-VR2GWR6C.js +173 -0
  174. package/dist/codegen.d.ts +115 -0
  175. package/dist/codegen.js +19 -0
  176. package/dist/commands-GJMJ4XYZ.js +76 -0
  177. package/dist/completion-ZQLO6PB6.js +738 -0
  178. package/dist/config-MBYGCAFK.js +136 -0
  179. package/dist/deploy-command-JVZPWHMK.js +180 -0
  180. package/dist/deploy-source-3W45CXMP.js +81 -0
  181. package/dist/dev-env-sync-HN44K4D5.js +188 -0
  182. package/dist/diagnostics-UEAAHWVF.js +30 -0
  183. package/dist/display-name-hint-ABBPVQIR.js +46 -0
  184. package/dist/emit-CyhBDxli.d.ts +7007 -0
  185. package/dist/env-example-refresh-ET5HMMDW.js +126 -0
  186. package/dist/env-pull-command-TNMNB3CV.js +323 -0
  187. package/dist/env-target-O4HRKTGZ.js +40 -0
  188. package/dist/env-var-command-ZIRDCAZA.js +427 -0
  189. package/dist/ephemeral-P7F3PMAD.js +54 -0
  190. package/dist/ephemeral-command-CLJJKOFT.js +592 -0
  191. package/dist/ephemeral-state-PYCBSOGX.js +61 -0
  192. package/dist/file-lock-PT74GWYW.js +10 -0
  193. package/dist/generate-command-AYEOLA26.js +510 -0
  194. package/dist/gitignore-3SJ3WNRG.js +51 -0
  195. package/dist/guards-4EBNCZDC.js +162 -0
  196. package/dist/hosted-file-PSHJB3HX.js +28 -0
  197. package/dist/impersonate-command-5JAS3WSV.js +152 -0
  198. package/dist/import-EQSFU6JC.js +19 -0
  199. package/dist/index.d.ts +5470 -0
  200. package/dist/index.js +371 -0
  201. package/dist/init-command-X2FR4UAK.js +125 -0
  202. package/dist/init-modules-RY7UHAOL.js +50 -0
  203. package/dist/init-web-M4YZSZK6.js +42 -0
  204. package/dist/internal.d.ts +179 -0
  205. package/dist/internal.js +2634 -0
  206. package/dist/io-RC56QSN5.js +26 -0
  207. package/dist/keep-data-merge-7MPLVAVI.js +1119 -0
  208. package/dist/lambda-config-reconcile-OUZ4OYAB.js +116 -0
  209. package/dist/landed-753YLJ5H.js +40 -0
  210. package/dist/landing-record-SU52SKXU.js +61 -0
  211. package/dist/live-diff-YONW2VI3.js +65 -0
  212. package/dist/local-engine-choice-CH5X3W7E.js +79 -0
  213. package/dist/local-engine-command-Z3OL7VOD.js +766 -0
  214. package/dist/local-engine-config-5U3SC434.js +39 -0
  215. package/dist/local-engine-deploy-PCQ4SNPB.js +39 -0
  216. package/dist/local-engine-process-EHTE5LLU.js +63 -0
  217. package/dist/local-engine-state-A4FTRFAF.js +32 -0
  218. package/dist/local-engine-update-check-6L645VBL.js +162 -0
  219. package/dist/lock-MRRHW5GS.js +87 -0
  220. package/dist/lock-commands-2QG25HL7.js +963 -0
  221. package/dist/login-command-QPYRWOXZ.js +538 -0
  222. package/dist/logout-command-WOIQLDP6.js +56 -0
  223. package/dist/loop-ALIZLOSX.js +191 -0
  224. package/dist/marketplace-command-K5YL42NO.js +977 -0
  225. package/dist/marketplace-resolve-LY32JES5.js +40 -0
  226. package/dist/meta-client-YXGPW53C.js +244 -0
  227. package/dist/module-manifest-JFGHU2TC.js +38 -0
  228. package/dist/near-key.d.ts +13 -0
  229. package/dist/near-key.js +8 -0
  230. package/dist/node.d.ts +95 -0
  231. package/dist/node.js +969 -0
  232. package/dist/operation-outcome-TZSESNC7.js +51 -0
  233. package/dist/output-target-AL3I44GG.js +47 -0
  234. package/dist/package-manager-PRAHUSCN.js +34 -0
  235. package/dist/plan-disclosure-6G4MLZKD.js +114 -0
  236. package/dist/plugin-contributions-WRNY5ABG.js +28 -0
  237. package/dist/plugin-prompt-4VA3B4FF.js +20 -0
  238. package/dist/plugin-questions-JTSYA74Q.js +24 -0
  239. package/dist/plugin.d.ts +396 -0
  240. package/dist/plugin.js +1 -0
  241. package/dist/preflight-command-2T5Z36MI.js +321 -0
  242. package/dist/profile-command-AH3PIM4X.js +800 -0
  243. package/dist/profile-pointer-GRWJU3YD.js +31 -0
  244. package/dist/profile-select-74TRDEPD.js +23 -0
  245. package/dist/project-dependency-KSH63ODT.js +35 -0
  246. package/dist/project-sdk-REPPOIBD.js +24 -0
  247. package/dist/promote-command-PCWT5N6S.js +140 -0
  248. package/dist/prompt-II3F3LND.js +27 -0
  249. package/dist/publish-command-TTDUUJI5.js +414 -0
  250. package/dist/pull-command-VBO7C4S3.js +161 -0
  251. package/dist/pull-superseded-GJWLG7XO.js +20 -0
  252. package/dist/release-command-N4ZPKMHR.js +146 -0
  253. package/dist/release-ns-command-3KFDMZ6G.js +1745 -0
  254. package/dist/response-HlXahv4K.d.ts +925 -0
  255. package/dist/retry-command-AS5EVREX.js +34 -0
  256. package/dist/routes-manifest-SNWUVBT6.js +43 -0
  257. package/dist/runtime-XETHVF33.js +14 -0
  258. package/dist/scaffold.d.ts +1911 -0
  259. package/dist/scaffold.js +107 -0
  260. package/dist/secrets-file-LVZD6IIM.js +56 -0
  261. package/dist/secrets-fill-command-TTCPSVXN.js +218 -0
  262. package/dist/seed-VU7C3QYS.js +46 -0
  263. package/dist/sent-writes-N7X4OVXT.js +22 -0
  264. package/dist/source-resolve-S2XA3XZF.js +83 -0
  265. package/dist/static-host-B2Y6AG3W.js +352 -0
  266. package/dist/status-command-RUDXTF7I.js +554 -0
  267. package/dist/store-6VNCD4D7.js +20 -0
  268. package/dist/store-ARM6VMO5.js +98 -0
  269. package/dist/store-DuYfzKlW.d.ts +3144 -0
  270. package/dist/suggest-6FOOYKHG.js +12 -0
  271. package/dist/sveltekit.d.ts +154 -0
  272. package/dist/sveltekit.js +109 -0
  273. package/dist/sync-contributions-UCEROO26.js +415 -0
  274. package/dist/table-ENAVMITR.js +42 -0
  275. package/dist/tables-command-GUPXXSB5.js +156 -0
  276. package/dist/tenant-IIHPOHGP.js +42 -0
  277. package/dist/tenant-command-D5HGI2X4.js +965 -0
  278. package/dist/test-command-TIKZMDPZ.js +476 -0
  279. package/dist/tests-BGGIOK3L.js +26 -0
  280. package/dist/token-NICJUTMT.js +72 -0
  281. package/dist/toolchain-hooks-GHKLUJOH.js +96 -0
  282. package/dist/toolchain-modules-5EAZUONB.js +24 -0
  283. package/dist/tracked-backend-EIBUMDZA.js +80 -0
  284. package/dist/update-check-2W2NLDTC.js +136 -0
  285. package/dist/upgrade-command-QHNFWM7O.js +531 -0
  286. package/dist/verify-rollout-DRCK5TDD.js +61 -0
  287. package/dist/whoami-command-STGINTXH.js +51 -0
  288. package/dist/workspace-2CLHU2LK.js +93 -0
  289. package/dist/workspace-binding-56PIIPCP.js +54 -0
  290. package/dist/workspace-command-JEXQETJD.js +535 -0
  291. package/dist/workspace-env-F2RFMSC5.js +70 -0
  292. package/dist/workspace-export-OJSH2HIW.js +21 -0
  293. package/dist/writable-VA7OISSK.js +25 -0
  294. package/dist/xano-ICFUSPFR.js +42 -0
  295. package/dist/xanosdk-import-GN42TO2B.js +56 -0
  296. package/dist/xanosdk-project-S6DXJONQ.js +41 -0
  297. package/dist/xdo-CxDr2Q1C.d.ts +1371 -0
  298. package/guides/README.md +33 -0
  299. package/guides/authoring.md +921 -0
  300. package/guides/cli.md +401 -0
  301. package/guides/codegen.md +177 -0
  302. package/guides/codes.md +404 -0
  303. package/guides/coverage.md +74 -0
  304. package/guides/deploying.md +1525 -0
  305. package/guides/environment.md +400 -0
  306. package/guides/marketplace.md +415 -0
  307. package/guides/module-contract.md +558 -0
  308. package/guides/object-kinds.md +433 -0
  309. package/guides/project-structure.md +43 -0
  310. package/guides/scaffold.md +351 -0
  311. package/guides/typed-frontend.md +242 -0
  312. package/llms/client.md +63 -0
  313. package/llms/errors.md +56 -0
  314. package/llms/fields.md +176 -0
  315. package/llms/filters.md +183 -0
  316. package/llms/kinds-agent-mcp.md +30 -0
  317. package/llms/kinds-core.md +100 -0
  318. package/llms/kinds-knowledge.md +20 -0
  319. package/llms/kinds-realtime.md +76 -0
  320. package/llms/lambda.md +94 -0
  321. package/llms/legacy.md +44 -0
  322. package/llms/lock.md +40 -0
  323. package/llms/object-kinds.md +34 -0
  324. package/llms/statements-calls.md +62 -0
  325. package/llms/statements-catalog.md +320 -0
  326. package/llms/statements-data.md +66 -0
  327. package/llms/statements-runtime.md +68 -0
  328. package/llms/tests.md +59 -0
  329. package/llms/toolchain.md +16 -0
  330. package/llms/triggers.md +36 -0
  331. package/llms/values.md +29 -0
  332. package/llms-full.txt +2085 -0
  333. package/llms.txt +493 -0
  334. package/manifest.json +11224 -0
  335. package/package.json +162 -0
  336. package/types/lambda-globals.d.ts +129 -0
@@ -0,0 +1,1745 @@
1
+ import {
2
+ OperationError,
3
+ createOperation,
4
+ runOperation
5
+ } from "./chunk-TLHIFK4M.js";
6
+ import {
7
+ EXIT_RELEASE_REFUSED,
8
+ describeOrigin,
9
+ findReleaseSource,
10
+ lookupRelease,
11
+ readUnanswered,
12
+ releaseOrigin,
13
+ requireName
14
+ } from "./chunk-AWEIIR2U.js";
15
+ import {
16
+ confirm
17
+ } from "./chunk-65HWOMGV.js";
18
+ import {
19
+ guardedIn,
20
+ listTables,
21
+ someTableRow
22
+ } from "./chunk-C6RIMRU4.js";
23
+ import {
24
+ resolveProjectEntry
25
+ } from "./chunk-AOSYVMUS.js";
26
+ import {
27
+ assertBranchReadSupported,
28
+ exportWorkspaceBundle
29
+ } from "./chunk-CJHQKJU4.js";
30
+ import {
31
+ ArchiveTooLargeError,
32
+ decodeWorkspaceArchive,
33
+ unwrapGzip
34
+ } from "./chunk-3DRXWLJ5.js";
35
+ import "./chunk-X4DVXBFY.js";
36
+ import {
37
+ classifyFailure
38
+ } from "./chunk-A6E77GDW.js";
39
+ import {
40
+ listBranches,
41
+ liveBranchLabel,
42
+ requireBranch
43
+ } from "./chunk-TRJCALW3.js";
44
+ import {
45
+ sha256Hex
46
+ } from "./chunk-NA3PXMEF.js";
47
+ import {
48
+ resolveOutputTarget,
49
+ secretsPhrase,
50
+ writeExportFile
51
+ } from "./chunk-SAVJUS2X.js";
52
+ import {
53
+ SETTINGS_LABEL,
54
+ compareToLive,
55
+ secretsInMultidoc
56
+ } from "./chunk-45UIT5T2.js";
57
+ import "./chunk-HRVBLCVJ.js";
58
+ import "./chunk-YHEFQUQB.js";
59
+ import {
60
+ assertBundleFile,
61
+ loadBundleText
62
+ } from "./chunk-ETO6LGLD.js";
63
+ import {
64
+ assertOneName
65
+ } from "./chunk-ZIY7RNUT.js";
66
+ import "./chunk-DAD3ONIS.js";
67
+ import {
68
+ memoCredential,
69
+ selectBackend
70
+ } from "./chunk-HHN734BU.js";
71
+ import "./chunk-LPECSJ2J.js";
72
+ import "./chunk-6NNTNXN7.js";
73
+ import {
74
+ SourceError,
75
+ describeBackend,
76
+ differOnlyInNormalisation,
77
+ differingCodePoints,
78
+ isUnansweredLookup,
79
+ normalisationNote,
80
+ orNamesNoted,
81
+ refuseWorkspaceless,
82
+ resolveSource
83
+ } from "./chunk-QCNYHBL7.js";
84
+ import {
85
+ RESERVED_DESCRIPTION_PREFIX,
86
+ assertUsableReleaseName,
87
+ createRelease,
88
+ deleteRelease,
89
+ describeAsCopy,
90
+ describeWithOrigin,
91
+ downloadRelease,
92
+ exportRelease,
93
+ getRelease,
94
+ importRelease,
95
+ listReleases,
96
+ reservedDescriptionLine,
97
+ updateReleaseDescription
98
+ } from "./chunk-2ZUEE4EK.js";
99
+ import "./chunk-AN6BL47T.js";
100
+ import {
101
+ ProfileNotFoundError,
102
+ getAccessToken,
103
+ resolveNamedProfile
104
+ } from "./chunk-ET4Y4XCB.js";
105
+ import "./chunk-SWBF2LTK.js";
106
+ import "./chunk-UZ6QQWJT.js";
107
+ import "./chunk-A3UDOZB6.js";
108
+ import "./chunk-2MNLRIOX.js";
109
+ import "./chunk-3M4LFYNQ.js";
110
+ import "./chunk-AM4FKL4J.js";
111
+ import "./chunk-Q325JKYB.js";
112
+ import {
113
+ requireBackendSlot
114
+ } from "./chunk-STSFNCY4.js";
115
+ import "./chunk-I4F5TCJH.js";
116
+ import "./chunk-5QU2P5CA.js";
117
+ import "./chunk-ZYJP6BOO.js";
118
+ import "./chunk-QO6BVGS5.js";
119
+ import "./chunk-JZGVVJSI.js";
120
+ import "./chunk-7G3CTU7I.js";
121
+ import "./chunk-6PBR37LN.js";
122
+ import {
123
+ pipedYes,
124
+ retryCommand,
125
+ withheldNote,
126
+ yesRerun
127
+ } from "./chunk-T5KELPAR.js";
128
+ import "./chunk-622HUCZT.js";
129
+ import {
130
+ contextFlags
131
+ } from "./chunk-NAQDMEHV.js";
132
+ import {
133
+ backendDirIn
134
+ } from "./chunk-5A2PLKZB.js";
135
+ import "./chunk-AHJWCYQO.js";
136
+ import "./chunk-DX4QR4B2.js";
137
+ import "./chunk-6QXLMMT3.js";
138
+ import "./chunk-ZFDZKNGG.js";
139
+ import {
140
+ credentialFlags,
141
+ shellWord
142
+ } from "./chunk-FWD7AL6N.js";
143
+ import "./chunk-2GNFDZA2.js";
144
+ import "./chunk-JTUKLCXS.js";
145
+ import {
146
+ describeWrite
147
+ } from "./chunk-4KHEMC32.js";
148
+ import "./chunk-U64O2MXY.js";
149
+ import "./chunk-ROY4YAEQ.js";
150
+ import "./chunk-P2HAGF6H.js";
151
+ import "./chunk-TLQE7ZN3.js";
152
+ import "./chunk-WRBKLYSJ.js";
153
+ import "./chunk-3LDULPRE.js";
154
+ import "./chunk-XW6KISBB.js";
155
+ import {
156
+ writeData
157
+ } from "./chunk-AWQ7BQTQ.js";
158
+ import "./chunk-EUUUMY7J.js";
159
+ import {
160
+ bindingFor
161
+ } from "./chunk-YLDALTWZ.js";
162
+ import "./chunk-5FBYXK5P.js";
163
+ import "./chunk-LLNZGEFK.js";
164
+ import "./chunk-QXTKSOFI.js";
165
+ import "./chunk-X2NL4KUO.js";
166
+ import "./chunk-OPHB5ORL.js";
167
+ import "./chunk-UCQ2VUTG.js";
168
+ import "./chunk-DGWHAVRG.js";
169
+ import "./chunk-JGE7CAWB.js";
170
+ import {
171
+ CUT_KINDS,
172
+ CliError,
173
+ LocalFileNotFoundError,
174
+ UsageError,
175
+ credentialWriteTarget,
176
+ describeWriteTarget,
177
+ detail,
178
+ discloseWriteTarget,
179
+ displayPath,
180
+ formatTableListing,
181
+ info,
182
+ isMachineOutput,
183
+ pastePath,
184
+ printHuman,
185
+ relForwardSlash,
186
+ safeText,
187
+ stdoutStyle,
188
+ step,
189
+ success,
190
+ tablesCommand,
191
+ unknownSubcommand,
192
+ warn,
193
+ writeJson,
194
+ writeTargetPayload
195
+ } from "./chunk-PTHVMXZI.js";
196
+ import "./chunk-MXHSQOC4.js";
197
+ import "./chunk-6IUMKYWI.js";
198
+ import "./chunk-2CMJZDCL.js";
199
+ import "./chunk-CTMO46PK.js";
200
+ import "./chunk-ZDPP6RKB.js";
201
+ import "./chunk-K5BBGTYZ.js";
202
+ import "./chunk-LQCMKAJ7.js";
203
+ import "./chunk-YGZHHAAB.js";
204
+ import "./chunk-XVBGLISP.js";
205
+ import "./chunk-6HO3NCSJ.js";
206
+ import "./chunk-5V5XU32D.js";
207
+ import "./chunk-GSGDWJB5.js";
208
+ import "./chunk-HVRUQXGD.js";
209
+ import "./chunk-ERVIASGL.js";
210
+ import "./chunk-4POWHT2K.js";
211
+ import "./chunk-H2V3N23D.js";
212
+ import "./chunk-3JDU4VRL.js";
213
+ import "./chunk-WTPZRR4I.js";
214
+ import {
215
+ suggest
216
+ } from "./chunk-UTI5G6OJ.js";
217
+ import "./chunk-2EZ3ZJZZ.js";
218
+ import "./chunk-6MR5TI5V.js";
219
+ import "./chunk-5ZSO4T2G.js";
220
+ import {
221
+ HOSTED_ICON_KINDS
222
+ } from "./chunk-XNCP4KYO.js";
223
+ import "./chunk-AKNCFVKZ.js";
224
+ import "./chunk-YU4HSQW7.js";
225
+ import "./chunk-TFFGOFVZ.js";
226
+ import "./chunk-L5JYN3NM.js";
227
+ import "./chunk-G554NZNI.js";
228
+ import "./chunk-N5GKCWE5.js";
229
+ import "./chunk-OXA6PSZT.js";
230
+ import "./chunk-VAX3UZOY.js";
231
+ import "./chunk-NZOWWHU5.js";
232
+ import "./chunk-4BW2SG2N.js";
233
+ import "./chunk-TTEIYD5P.js";
234
+ import "./chunk-CIMQZRRE.js";
235
+ import "./chunk-OHX6MIUZ.js";
236
+ import {
237
+ withArticle
238
+ } from "./chunk-36JUQZVQ.js";
239
+ import "./chunk-PZ5AY32C.js";
240
+
241
+ // src/emit/release-create.ts
242
+ import { join, resolve } from "path";
243
+ import { existsSync, statSync } from "fs";
244
+
245
+ // src/deploy/release-hosted-files.ts
246
+ function isFileColumnType(type) {
247
+ return typeof type === "string" && type.startsWith("blob");
248
+ }
249
+ function fileColumnsIn(schema, prefix = "") {
250
+ if (!Array.isArray(schema)) return [];
251
+ return schema.flatMap((col) => {
252
+ if (col === null || typeof col !== "object") return [];
253
+ const c = col;
254
+ if (typeof c.name !== "string" || c.name === "") return [];
255
+ const path = `${prefix}${c.name}`;
256
+ return isFileColumnType(c.type) ? [path] : fileColumnsIn(c.children, `${path}.`);
257
+ });
258
+ }
259
+ function isLibraryIconSrc(src) {
260
+ return typeof src === "string" && src.startsWith("/vault/");
261
+ }
262
+ function rowHoldsFile(row, column) {
263
+ const holds = (value, path) => {
264
+ if (Array.isArray(value)) return value.some((v2) => holds(v2, path));
265
+ if (value === null || typeof value !== "object") return false;
266
+ const v = value;
267
+ if (path.length === 0) return typeof v.path === "string" && v.path !== "";
268
+ return holds(v[path[0]], path.slice(1));
269
+ };
270
+ return holds(row, column.split("."));
271
+ }
272
+ async function uncarriedFiles(payload, seeded, rowsHoldFiles) {
273
+ const out = [];
274
+ for (const kind of HOSTED_ICON_KINDS) {
275
+ const rows = payload[kind];
276
+ if (!Array.isArray(rows)) continue;
277
+ for (const row of rows) {
278
+ if (row === null || typeof row !== "object" || !Array.isArray(row.icons)) continue;
279
+ row.icons.forEach((icon, i) => {
280
+ if (icon !== null && typeof icon === "object" && isLibraryIconSrc(icon.src)) {
281
+ out.push({ kind, name: String(row.name ?? ""), field: `icons[${i}]` });
282
+ }
283
+ });
284
+ }
285
+ }
286
+ const library = Array.isArray(payload.vault) ? payload.vault.length : 0;
287
+ if (seeded !== void 0 && seeded.length > 0 && library > 0) {
288
+ const tables = Array.isArray(payload.dbo) ? payload.dbo : [];
289
+ for (const want of seeded) {
290
+ const table = tables.find((t) => t !== null && typeof t === "object" && t.guid === want.guid) ?? tables.find((t) => t !== null && typeof t === "object" && t.name === want.name);
291
+ const columns = table === void 0 ? [] : fileColumnsIn(table.schema);
292
+ if (columns.length > 0 && await rowsHoldFiles(want, columns)) {
293
+ out.push({ kind: "table", name: want.name, field: columns.join(", ") });
294
+ }
295
+ }
296
+ }
297
+ return out;
298
+ }
299
+ function describeUncarried(file) {
300
+ return file.kind === "table" ? `table "${file.name}" rows holding files \u2014 file column${file.field.includes(",") ? "s" : ""} ${file.field}` : `${file.kind} "${file.name}" \`${file.field}\``;
301
+ }
302
+
303
+ // src/emit/release-create.ts
304
+ async function resolveFrom(args, credential, cwd) {
305
+ const slot = requireBackendSlot("release", "create", "from");
306
+ const source = await selectBackend(slot, args.from, { credential, deps: { cwd } });
307
+ if (!CUT_KINDS.includes(source.kind)) {
308
+ throw new Error(`Internal: \`release create --from\` selected ${withArticle(source.kind)}, which its slot does not accept.`);
309
+ }
310
+ return source;
311
+ }
312
+ var THROWAWAY_TENANT_TYPES = ["ephemeral", "sandbox"];
313
+ async function refuseLiveDeployment(resolved) {
314
+ if (resolved.kind !== "tenant") return;
315
+ const type = resolved.target.tenantType;
316
+ if (type !== void 0 && THROWAWAY_TENANT_TYPES.includes(type)) return;
317
+ const t = resolved.target.label;
318
+ const { tenantHasWorkspace } = await import("./tenant-IIHPOHGP.js");
319
+ const pullable = resolved.backend?.kind !== "hosted" || await tenantHasWorkspace(resolved.backend.auth, resolved.target.base);
320
+ throw new UsageError(
321
+ `A release cannot be cut from ${t}${type === void 0 ? "" : ` \u2014 it is a \`${type}\` tenant`}.
322
+ Only a throwaway tenant (${THROWAWAY_TENANT_TYPES.join(" or ")}) can be a source: a standard or run tenant is a live deployment. \`xanosdk tenant get ${t}${contextFlags()}\` names the last release landed there, and that release lands anywhere as it is \u2014 but any \`deploy --to\` merge since may have changed what the tenant runs.
323
+ ` + // Never with `--yes`: the pull's own confirmation and its uncommitted-changes
324
+ // check are what keep it from replacing edits, and off a terminal it
325
+ // refuses with its own rerun.
326
+ (pullable ? `To start from what it runs now, pull it into this project (\`xanosdk pull tenant:${t}${contextFlags()}\`), \`xanosdk deploy\` it to an ephemeral, and cut from there.` : `It has no workspace yet \u2014 a tenant on its own domain gets one when its first release lands \u2014 so there is nothing on it to pull or cut from until then.`),
327
+ { hintFor: { command: "release", subcommand: "create" } }
328
+ );
329
+ }
330
+ function sourceLabel(resolved, branch) {
331
+ if (resolved.kind === "ephemeral" || resolved.kind === "tenant") return describeBackend(resolved);
332
+ return resolved.kind === "workspace" && branch !== void 0 ? `${resolved.target.label} (branch ${JSON.stringify(branch)})` : resolved.target.label;
333
+ }
334
+ function environmentType(resolved) {
335
+ if (resolved.kind === "ephemeral") return "ephemeral";
336
+ return resolved.target.tenantType ?? "environment";
337
+ }
338
+ function comparisonInput(args, cwd) {
339
+ const lockDefaults = {
340
+ lock: false,
341
+ lockPath: void 0,
342
+ noLock: false,
343
+ frozenLock: false,
344
+ allowLockOrphans: false,
345
+ // Read, never written: this compile answers a question, and a cut that
346
+ // created or rewrote `xano.lock` (with its "commit it" notice) changed the
347
+ // project behind a command that only reads it.
348
+ lockReadOnly: true
349
+ };
350
+ if (args.bundle !== void 0) return { ...args, ...lockDefaults, file: void 0 };
351
+ const entry = args.entryPath ?? resolveProjectEntry(cwd);
352
+ if (entry === void 0) return void 0;
353
+ return { ...args, ...lockDefaults, file: resolve(cwd, entry), bundle: void 0 };
354
+ }
355
+ function remedyFor(resolved, bundle, branch, name, entry, settings) {
356
+ const flags = contextFlags();
357
+ const input = `${entry === void 0 ? "" : ` ${shellWord(entry)}`}${bundle === void 0 ? "" : ` --bundle ${shellWord(bundle)}`}${flags}`;
358
+ const what = bundle === void 0 ? "the current code" : "that bundle";
359
+ if (resolved.kind === "workspace") {
360
+ if (branch === void 0) {
361
+ return `Run \`xanosdk deploy --to workspace${input}\` first if you meant to cut ${what}.`;
362
+ }
363
+ const cutName = name === void 0 ? "<name>" : shellWord(name);
364
+ const settingsNote = `Workspace settings are shared by every branch, so no branch deploy changes them \u2014 and a promote keeps the destination's own, so they do not change what this release lands. To clear the settings line, declare the workspace's values in this project, or apply yours with \`xanosdk deploy --to workspace${input}\` (onto live).`;
365
+ if (settings === "only") return settingsNote;
366
+ return `To cut ${what}, deploy it to a new branch and cut from that: \`xanosdk deploy --to workspace --branch <new-label>${input}\`, then \`xanosdk release create ${cutName} --from workspace --branch <new-label>${flags}\`. (\`deploy --to workspace --branch\` refuses a label that already exists, so it cannot update ${shellWord(branch)}.)` + (settings === "also" ? ` ${settingsNote}` : "");
367
+ }
368
+ if (resolved.kind === "tenant") {
369
+ return `Run \`xanosdk deploy --to tenant:${resolved.target.label}${input}\` first if you meant to cut ${what}.`;
370
+ }
371
+ return `Run \`xanosdk deploy${input}\` first if you meant to cut ${what}.`;
372
+ }
373
+ async function warnOnDivergence(args, resolved, cwd, live) {
374
+ const input = comparisonInput(args, cwd);
375
+ if (input === void 0) {
376
+ const looked = relForwardSlash(cwd, join(backendDirIn(cwd), "index.ts"));
377
+ detail(
378
+ `No project entry found (looked for ${looked}) - skipped the comparison against a local compile. \`--entry=<path>\` names the entry to compare against.`
379
+ );
380
+ return { checked: false, reason: `no project entry found (looked for ${looked})` };
381
+ }
382
+ const bundleGiven = input.bundle !== void 0;
383
+ try {
384
+ const branch = resolved.kind === "workspace" ? args.branch : void 0;
385
+ const local = await loadBundleText(input, { command: "release", subcommand: "create" });
386
+ const { codePinnedCanonicals, releaseCanonicalLock } = await import("./release-command-N4ZPKMHR.js");
387
+ const pinned = codePinnedCanonicals(
388
+ local.bundle,
389
+ releaseCanonicalLock(input, local.classifiedLock),
390
+ input.file !== void 0
391
+ );
392
+ const convergence = compareToLive(
393
+ JSON.parse(local.bundle),
394
+ live,
395
+ new Set(pinned.map((p) => `${p.payloadKey}:${p.name}`)),
396
+ { documentation: "compare", sample: "all" }
397
+ );
398
+ const extra = convergence.liveOnly.length;
399
+ const behind = convergence.differingCount + convergence.missingCount + extra;
400
+ const objects = [
401
+ ...convergence.differing.map(
402
+ (name) => name === SETTINGS_LABEL && convergence.settingsFields.length > 0 ? `${name} \u2014 ${convergence.settingsFields.join(", ")}` : name
403
+ ),
404
+ ...convergence.missing.map((name) => `${name} (not on the source)`),
405
+ ...convergence.liveOnly.map((name) => `${name} (not in ${bundleGiven ? "the bundle" : "this project"})`)
406
+ ];
407
+ const against = displayPath(resolve(cwd, local.source));
408
+ if (behind > 0) {
409
+ const label = sentenceStart(sourceLabel(resolved, branch));
410
+ warn(
411
+ `${label} does not match ${bundleGiven ? against : "a fresh compile of this project"} (${behind} object${behind === 1 ? "" : "s"} differing, missing or extra).`,
412
+ "release.drift",
413
+ [
414
+ ...objects.slice(0, DRIFT_SHOWN),
415
+ ...behind > DRIFT_SHOWN ? [`\u2026 and ${behind - DRIFT_SHOWN} more`] : [],
416
+ `The release will carry what is RUNNING there, not what ${bundleGiven ? "that bundle holds" : "is on your disk"}.`,
417
+ remedyFor(
418
+ resolved,
419
+ bundleGiven ? against : void 0,
420
+ branch,
421
+ args.positionals[0]?.trim(),
422
+ !bundleGiven && args.entryPath !== void 0 ? pastePath(args.entryPath) : void 0,
423
+ convergence.differing.includes(SETTINGS_LABEL) ? behind === 1 ? "only" : "also" : void 0
424
+ )
425
+ ]
426
+ );
427
+ }
428
+ return { checked: true, against, count: behind, objects };
429
+ } catch (err) {
430
+ if (err instanceof Error && err.message.startsWith("Invalid lock file ")) {
431
+ throw new Error(`${err.message}
432
+ Nothing was cut.`, { cause: err });
433
+ }
434
+ detail(`Could not compare against a local compile: ${err.message}`);
435
+ return { checked: false, reason: err.message };
436
+ }
437
+ }
438
+ var FILES_NOT_CARRIED = "SDK_RELEASE_FILES_NOT_CARRIED";
439
+ function sentenceStart(label) {
440
+ return label.replace(/^(your|ephemeral|tenant|workspace)\b/, (w) => `${w.charAt(0).toUpperCase()}${w.slice(1)}`);
441
+ }
442
+ async function readSourceExport(args, auth, resolved) {
443
+ const branch = resolved.kind === "workspace" ? args.branch : void 0;
444
+ try {
445
+ if (branch !== void 0) {
446
+ await assertBranchReadSupported(auth, {
447
+ base: resolved.target.base,
448
+ workspaceId: resolved.target.workspaceId,
449
+ branch
450
+ });
451
+ }
452
+ return await exportWorkspaceBundle(auth, { ...resolved.target, ...branch !== void 0 ? { branch } : {} });
453
+ } catch (err) {
454
+ throw cannotVouch(args, resolved, `could not read its export: ${err.message}`);
455
+ }
456
+ }
457
+ async function readBeforeCut(args, what, read) {
458
+ try {
459
+ return await read();
460
+ } catch (err) {
461
+ const { command, withheld } = retryCommand(args, { command: "release create", creates: true });
462
+ throw new CliError(
463
+ "SDK_ERROR",
464
+ `Could not ${what}: ${err.message}
465
+ Nothing was cut. Re-run \`${command}\` once it answers.${withheldNote(withheld)}`,
466
+ { exitCode: 8 }
467
+ );
468
+ }
469
+ }
470
+ function cannotVouch(args, resolved, why) {
471
+ const { command, withheld } = retryCommand(args, { command: "release create", creates: true });
472
+ return new CliError(
473
+ "SDK_ERROR",
474
+ `Could not check ${resolved.target.label} for stored files a release cannot carry \u2014 ${why}
475
+ A release copies no file bytes, and a cut that was not checked could drop them silently. Nothing was cut. Re-run \`${command}\` once it answers.${withheldNote(withheld)}`,
476
+ { exitCode: 8 }
477
+ );
478
+ }
479
+ async function refuseUncarriedFiles(payload, selection, resolved, args, auth, cwd) {
480
+ const byGuid = new Map(selection?.tables.map((t) => [t.guid, t]));
481
+ const files = await uncarriedFiles(payload, selection?.tables, async (want, columns) => {
482
+ const table = byGuid.get(want.guid);
483
+ if (table === void 0) return true;
484
+ try {
485
+ return await someTableRow(
486
+ auth,
487
+ {
488
+ workspaceId: resolved.target.workspaceId,
489
+ tableId: table.id,
490
+ base: resolved.target.base,
491
+ binding: bindingFor(auth, resolved.target.workspaceId, resolved.kind !== "workspace")
492
+ },
493
+ (row) => columns.some((c) => rowHoldsFile(row, c))
494
+ );
495
+ } catch (err) {
496
+ throw cannotVouch(args, resolved, `could not read table "${table.name}"'s rows: ${err.message}`);
497
+ }
498
+ });
499
+ if (files.length === 0) return;
500
+ const rows = files.some((f) => f.kind === "table");
501
+ const effects = [
502
+ ...rows ? ["those rows' files would be empty"] : [],
503
+ ...files.some((f) => f.kind !== "table") ? ["those icons would answer 404"] : []
504
+ ];
505
+ const entry = remedyEntry(args, cwd);
506
+ throw new CliError(
507
+ FILES_NOT_CARRIED,
508
+ `${sentenceStart(resolved.target.label)} serves stored files this release would point at but cannot carry:
509
+ ` + files.map((f) => ` ${describeUncarried(f)}`).join("\n") + `
510
+ A release copies no file bytes, so wherever it lands ${effects.join(" and ")}. Land the project with its files by deploying it directly: \`xanosdk deploy${entry.word}--to tenant:<name>${rows ? " --seed" : ""}${contextFlags(args)}\`${entry.note}. Nothing was cut.`,
511
+ { exitCode: EXIT_RELEASE_REFUSED, details: { files } }
512
+ );
513
+ }
514
+ function remedyEntry(args, cwd) {
515
+ if (args.entryPath !== void 0) return { word: ` ${shellWord(args.entryPath)} `, note: "" };
516
+ if (args.bundle === void 0 && resolveProjectEntry(cwd) !== void 0) return { word: " ", note: "" };
517
+ return { word: " <entry> ", note: ` (\`<entry>\`: the backend's \`index.ts\` the source was deployed from)` };
518
+ }
519
+ var DRIFT_SHOWN = 5;
520
+ async function cutFromEnvironment(auth, name, resolved, description, op, lookFor, flags, rerun) {
521
+ const tenant = resolved.target.env;
522
+ if (tenant === void 0) {
523
+ throw new Error(
524
+ `cut release: ${resolved.target.label} did not resolve to a tenant name, so it cannot be named as a source. Nothing was added to your workspace.`
525
+ );
526
+ }
527
+ const cut2 = await sendCut(
528
+ auth,
529
+ op,
530
+ lookFor,
531
+ () => createRelease(auth, {
532
+ workspaceId: auth.workspaceId,
533
+ name,
534
+ branch: "",
535
+ sourceTenant: tenant,
536
+ // Carries `tableIds` when rows were asked for — the TENANT's ids, resolved
537
+ // against the tenant's own listing, because a tenant's workspace is always
538
+ // id 1 and ids from anywhere else name other tables.
539
+ ...description,
540
+ // The origin, on the description's last line: the record keeps no source
541
+ // field and the audit entry that names one is pruned with the log, so
542
+ // `release show` and a landing's password-hash warning read it here.
543
+ description: describeWithOrigin(description.description, { type: environmentType(resolved), name: tenant })
544
+ })
545
+ );
546
+ detail(`Confirming it was cut from ${resolved.target.label}`);
547
+ await confirmCutFrom(auth, op, {
548
+ name: cut2.name !== "" ? cut2.name : name,
549
+ tenant,
550
+ cut: cut2,
551
+ label: resolved.target.label,
552
+ flags,
553
+ rerun
554
+ });
555
+ if (cut2.id === void 0) return cut2;
556
+ try {
557
+ return await getRelease(auth, { workspaceId: auth.workspaceId, id: cut2.id }) ?? cut2;
558
+ } catch (err) {
559
+ const cause = causeOnly(err);
560
+ const shown = cut2.name !== "" ? cut2.name : name;
561
+ detail(
562
+ `The release was cut, but reading it back failed (${cause}); reporting it as the cut returned it. \`xanosdk release show ${shellWord(shown)}${contextFlags()}\` reads it again.`
563
+ );
564
+ return cut2;
565
+ }
566
+ }
567
+ async function confirmCutFrom(auth, op, opts) {
568
+ let recorded;
569
+ try {
570
+ recorded = await findReleaseSource(auth, { workspaceId: auth.workspaceId, releaseName: opts.name });
571
+ } catch (err) {
572
+ const unanswered = isUnansweredLookup(err);
573
+ const again = `re-run \`${opts.rerun.command}\` once it answers.${withheldNote(opts.rerun.withheld)}`;
574
+ return refuseCut(
575
+ auth,
576
+ op,
577
+ opts.cut,
578
+ opts.name,
579
+ opts.flags,
580
+ (removal) => `Cut "${opts.name}" from ${opts.label}, but could not confirm where it was cut from: ${causeOnly(err)}.
581
+ ` + (removal.removed ? `The release has been removed rather than left with contents that cannot be vouched for` + (unanswered ? ` \u2014 ${again.charAt(0).toUpperCase()}${again.slice(1)}` : ".") : `It was left behind and could NOT be removed \u2014 delete it with \`${removal.removeWith}\`` + (unanswered ? `, then ${again}` : ` before re-cutting, or the name will collide.`)),
582
+ unanswered ? 8 : void 0
583
+ );
584
+ }
585
+ if (recorded !== null && recorded.tenantName === opts.tenant) return;
586
+ const source = recorded?.tenantName;
587
+ return refuseCut(
588
+ auth,
589
+ op,
590
+ opts.cut,
591
+ opts.name,
592
+ opts.flags,
593
+ (removal) => source === void 0 ? `Refusing the release "${opts.name}": this instance ignored the source and cut from your workspace instead.
594
+ instance: ${auth.instance}
595
+ It accepted \`${opts.tenant}\` as a source and recorded no source at all, which is what an instance predating this capability does. The release would have carried YOUR workspace's current state under ${opts.label}'s name \u2014 ` + (removal.removed ? `so it has been removed.` : `and it could NOT be removed: ${removal.remedy}`) + `
596
+ Cut \`--from workspace\` if that is what you want, or wait for this instance to take the update.` : `Refusing the release "${opts.name}": it was cut from "${source}", not from ${opts.label} (\`${opts.tenant}\`). ` + (removal.removed ? `It has been removed.` : `It could NOT be removed \u2014 ${removal.remedy}`)
597
+ );
598
+ }
599
+ async function discard(auth, cut2) {
600
+ if (cut2.id === void 0) return false;
601
+ return deleteRelease(auth, { workspaceId: auth.workspaceId, id: cut2.id }).then(
602
+ () => true,
603
+ () => false
604
+ );
605
+ }
606
+ async function refuseCut(auth, op, cut2, storedName, deleteFlags, message, exitCode) {
607
+ const removed = await discard(auth, cut2);
608
+ const removeWith = `xanosdk release delete ${shellWord(storedName)}${deleteFlags}`;
609
+ if (!removed) {
610
+ op.setResidue({ ...cut2.id === void 0 ? {} : { id: cut2.id }, name: storedName, removeWith });
611
+ }
612
+ op.finish("cut", "no");
613
+ const text = message({
614
+ removed,
615
+ removeWith,
616
+ remedy: `delete it with \`${removeWith}\` before re-cutting, or the name will collide.`
617
+ });
618
+ throw exitCode === void 0 ? new Error(text) : new CliError("SDK_ERROR", text, { exitCode });
619
+ }
620
+ function causeOnly(err) {
621
+ const message = err instanceof Error ? err.message : String(err);
622
+ return (message.split("\n")[0] ?? "").replace(/\s*Nothing was (?:changed|created)[^.]*\.?/g, "").trim().replace(/[.:;]$/, "");
623
+ }
624
+ var SEED_HELP = { hintFor: { command: "release", subcommand: "create" } };
625
+ var CREATE_HINT = { hintFor: { command: "release", subcommand: "create" } };
626
+ function listingVerb(resolved) {
627
+ return tablesCommand(resolved.provenance);
628
+ }
629
+ function parseSeedGuids(raw) {
630
+ const tokens = raw.split(",").map((t) => t.trim());
631
+ if (tokens.every((t) => t === "")) {
632
+ throw new UsageError(
633
+ `\`--seed=\` names no tables. Use bare \`--seed\` for every table's rows, or \`--seed=<guids>\` with guids from \`${tablesCommand()}\`.`,
634
+ SEED_HELP
635
+ );
636
+ }
637
+ const guids = [];
638
+ for (const token of tokens) {
639
+ if (token === "") {
640
+ throw new UsageError(
641
+ `\`--seed\` has an empty entry \u2014 a stray comma in ${JSON.stringify(raw)}. List one guid per comma.`,
642
+ SEED_HELP
643
+ );
644
+ }
645
+ if (/\s/.test(token)) {
646
+ throw new UsageError(
647
+ `\`--seed\` takes table GUIDS, and ${JSON.stringify(token)} is not one \u2014 a guid holds no whitespace. A table NAME cannot be used here: a name may contain a comma or a space, and renaming a table would silently re-target a saved selection. Run \`${tablesCommand()}\` for the guids.`,
648
+ SEED_HELP
649
+ );
650
+ }
651
+ if (!guids.includes(token)) guids.push(token);
652
+ }
653
+ return guids;
654
+ }
655
+ var ENGINE_GUID_SHAPE = /^[A-Za-z0-9_-]{20,}$/;
656
+ function isHexGuid(guid) {
657
+ return /^[0-9a-f]{32}$/i.test(guid);
658
+ }
659
+ function unmatchedGuidCauses(unmatched, tables = []) {
660
+ const lines = [];
661
+ const byName = new Map(tables.map((t) => [t.name, t]));
662
+ const named = unmatched.filter((g) => byName.has(g));
663
+ for (const name of named) {
664
+ lines.push(`"${name}" is a table NAME, and \`--seed\` expects a table guid \u2014 its guid is ${byName.get(name).guid}.`);
665
+ }
666
+ const byId = tableById(tables);
667
+ const ids = unmatched.filter((g) => !byName.has(g) && byId.has(g));
668
+ for (const id of ids) {
669
+ const t = byId.get(id);
670
+ lines.push(`${id} is table "${t.name}"'s ID, and \`--seed\` expects its guid \u2014 ${t.guid}. IDs are per host; the guid travels.`);
671
+ }
672
+ const guids = tables.flatMap((t) => t.guid === void 0 ? [] : [t.guid]);
673
+ const byGuid = new Map(tables.map((t) => [t.guid, t]));
674
+ const typos = /* @__PURE__ */ new Map();
675
+ for (const g of unmatched) {
676
+ if (byName.has(g) || byId.has(g) || !isHexGuid(g) && !ENGINE_GUID_SHAPE.test(g)) continue;
677
+ const near = suggest(g, guids);
678
+ if (near !== void 0) typos.set(g, byGuid.get(near));
679
+ }
680
+ for (const [g, t] of typos) {
681
+ lines.push(
682
+ `${g} is close to table "${t.name}"'s guid ${t.guid} but not it \u2014 a mistyped copy? Use ${t.guid}` + (isHexGuid(g) ? "." : ` (an engine guid is matched exactly, case included).`)
683
+ );
684
+ }
685
+ const rest = unmatched.filter((g) => !byName.has(g) && !byId.has(g) && !typos.has(g));
686
+ const notGuids = rest.filter((g) => !isHexGuid(g) && !ENGINE_GUID_SHAPE.test(g));
687
+ if (notGuids.length > 0) {
688
+ lines.push(
689
+ `${notGuids.map((g) => JSON.stringify(g)).join(", ")} ${notGuids.length === 1 ? "is" : "are"} not a table guid \u2014 \`--seed\` expects table guids.`
690
+ );
691
+ }
692
+ const derived = rest.some(isHexGuid);
693
+ const engineMinted = rest.some((g) => !isHexGuid(g) && ENGINE_GUID_SHAPE.test(g));
694
+ if (derived) {
695
+ lines.push(
696
+ `A 32-character hex guid is the value THIS PROJECT derives and pins in \`xano.lock\`, and a deploy stores it as-is \u2014 so one that does not match usually means the table was RENAMED or removed in code since the guid was noted (the name is what the guid derives from). Re-read it from the listing below, or run \`xanosdk lock rename\` if the rename was intended.`
697
+ );
698
+ }
699
+ if (engineMinted) {
700
+ lines.push(
701
+ `A guid in the engine's own format belongs to a backend this project did not deploy, or to one deployed before guids were preserved. Those are per-environment and do not travel \u2014 the listing below is the source of truth for the source being cut from.`
702
+ );
703
+ }
704
+ return lines.join("\n");
705
+ }
706
+ function tableById(tables) {
707
+ return new Map(tables.filter((t) => t.guid !== void 0).map((t) => [String(t.id), t]));
708
+ }
709
+ var SeedTableNotFoundError = class extends UsageError {
710
+ code = "SDK_ERROR";
711
+ exitCode = 8;
712
+ /** Every guid meant, when several unmatched guids each had one (`suggestion` is the first). */
713
+ suggestions;
714
+ constructor(message, near) {
715
+ super(message, near.length === 0 ? {} : { suggestion: near[0] });
716
+ this.suggestions = near.length > 1 ? [...near] : void 0;
717
+ }
718
+ };
719
+ function nearSeedGuids(unmatched, tables) {
720
+ const byName = new Map(tables.map((t) => [t.name, t.guid]));
721
+ const guids = tables.flatMap((t) => t.guid === void 0 ? [] : [t.guid]);
722
+ const byId = tableById(tables);
723
+ const near = unmatched.flatMap((g) => {
724
+ const named = byName.get(g) ?? (byName.has(g) ? void 0 : byId.get(g)?.guid);
725
+ if (named !== void 0) return [named];
726
+ if (!isHexGuid(g) && !ENGINE_GUID_SHAPE.test(g)) return [];
727
+ const close = suggest(g, guids);
728
+ return close === void 0 ? [] : [close];
729
+ });
730
+ return [...new Set(near)];
731
+ }
732
+ async function resolveSeedSelection(args, auth, resolved) {
733
+ if (!args.seed) return void 0;
734
+ const requested = args.seedTableGuids === void 0 ? void 0 : parseSeedGuids(args.seedTableGuids);
735
+ const tables = await readBeforeCut(
736
+ args,
737
+ `list ${resolved.target.label}'s tables for \`--seed\``,
738
+ () => listTables(auth, {
739
+ workspaceId: resolved.target.workspaceId,
740
+ base: resolved.target.base,
741
+ binding: bindingFor(auth, resolved.target.workspaceId, resolved.kind !== "workspace")
742
+ })
743
+ );
744
+ if (tables.length === 0 && resolved.kind === "workspace") {
745
+ const { refuseUnreachableWorkspace } = await import("./workspace-binding-56PIIPCP.js");
746
+ await refuseUnreachableWorkspace(auth);
747
+ }
748
+ if (tables.length === 0) {
749
+ throw new UsageError(
750
+ `${resolved.target.label} has no tables, so there are no rows to carry. Drop \`--seed\`.`
751
+ );
752
+ }
753
+ if (requested === void 0) return { tables };
754
+ const known = new Map(tables.map((t) => [t.guid, t]));
755
+ const lookup = (guid) => known.get(guid) ?? (isHexGuid(guid) ? known.get(guid.toLowerCase()) : void 0);
756
+ const unmatched = requested.filter((guid) => lookup(guid) === void 0);
757
+ if (unmatched.length > 0) {
758
+ const listing = `${listingVerb(resolved)}${contextFlags(args)}`;
759
+ throw new SeedTableNotFoundError(
760
+ `${resolved.target.label} has no table with ${unmatched.length === 1 ? `guid ${unmatched[0]}` : `guids ${unmatched.join(", ")}`}.
761
+ ${unmatchedGuidCauses(unmatched, tables)}
762
+ Run \`${listing}\` for the current ones.
763
+ Tables in ${resolved.target.label}:
764
+ ${formatTableListing(tables, " ")}`,
765
+ nearSeedGuids(unmatched, tables)
766
+ );
767
+ }
768
+ return { tables: [...new Set(requested.flatMap((guid) => lookup(guid) ?? []))] };
769
+ }
770
+ function withExportedGuards(selection, payload) {
771
+ const rows = payload?.dbo;
772
+ const byGuid = /* @__PURE__ */ new Map();
773
+ for (const r of Array.isArray(rows) ? rows : []) {
774
+ const row = r;
775
+ if (typeof row?.guid === "string") byGuid.set(row.guid, row.schema);
776
+ }
777
+ const unchecked = [];
778
+ const tables = selection.tables.map((t) => {
779
+ if (!byGuid.has(t.guid)) {
780
+ unchecked.push(t.name);
781
+ return t;
782
+ }
783
+ return { ...t, guardedColumns: [.../* @__PURE__ */ new Set([...t.guardedColumns, ...guardedIn(byGuid.get(t.guid))])] };
784
+ });
785
+ return { tables, unchecked };
786
+ }
787
+ async function confirmNonPublicRows(args, selection) {
788
+ const flagged = selection.tables.filter((t) => t.guardedColumns.length > 0);
789
+ const unchecked = selection.unchecked ?? [];
790
+ if (unchecked.length > 0) {
791
+ warn(
792
+ `Could not read which columns are sensitive in ${unchecked.length === 1 ? "this table" : "these tables"} \u2014 the source's export does not carry ${unchecked.length === 1 ? "it" : "them"}, so only \`internal\` and password columns were checked: ${unchecked.join(", ")}`,
793
+ "seed.guarded-columns"
794
+ );
795
+ }
796
+ if (flagged.length === 0) return true;
797
+ warn(`These tables have non-public columns whose values would ride along:`, "seed.guarded-columns", [
798
+ ...flagged.map((t) => `${t.name}: ${t.guardedColumns.join(", ")}`),
799
+ `A release is durable and can be landed on another environment.`
800
+ ]);
801
+ if (args.yes) return true;
802
+ const { yesRerun: yesRerun2 } = await import("./retry-command-AS5EVREX.js");
803
+ const { rerun, note } = yesRerun2(args, "release create");
804
+ return confirm(`Carry these rows anyway?`, {
805
+ flag: "--yes",
806
+ refusal: {
807
+ details: {
808
+ verb: "create",
809
+ created: false,
810
+ guardedColumns: Object.fromEntries(flagged.map((t) => [t.name, [...t.guardedColumns]]))
811
+ },
812
+ rerun,
813
+ note
814
+ }
815
+ });
816
+ }
817
+ async function assertRowsLanded(auth, op, created, requested, selection, flags) {
818
+ if (created.seededTables.length >= selection.tables.length) return;
819
+ const landedGuids = new Set(created.seededTables.flatMap((t) => t.guid === void 0 ? [] : [t.guid]));
820
+ const landedNames = new Set(created.seededTables.map((t) => t.name));
821
+ const missing = selection.tables.filter((t) => landedGuids.size > 0 ? !landedGuids.has(t.guid) : !landedNames.has(t.name)).map((t) => t.name);
822
+ const stored = created.name !== "" ? created.name : requested;
823
+ return refuseCut(
824
+ auth,
825
+ op,
826
+ created,
827
+ stored,
828
+ flags,
829
+ (removal) => `Release "${stored}" carries rows for ${created.seededTables.length} of the ${selection.tables.length} tables asked for${missing.length === 0 ? "" : ` (missing ${missing.map((m) => JSON.stringify(m)).join(", ")})`}. The server accepted the cut and dropped those tables silently.
830
+ ` + (removal.removed ? `The release was removed.` : `It could NOT be removed \u2014 ${removal.remedy}`) + ` Check the ids against the source with \`${tablesCommand()}\`.`
831
+ );
832
+ }
833
+ function storedIdentity(auth, release, requested) {
834
+ return {
835
+ instance: auth.instance,
836
+ workspaceId: auth.workspaceId,
837
+ id: release.id,
838
+ name: release.name !== "" ? release.name : requested
839
+ };
840
+ }
841
+ function resolveCut(args) {
842
+ return `xanosdk release list --json${contextFlags(args)}`;
843
+ }
844
+ var CREATED_AFTER_SKEW_MS = 5e3;
845
+ function createdAfterBound(startedAt) {
846
+ return new Date(Math.floor(startedAt / 1e3) * 1e3 - CREATED_AFTER_SKEW_MS).toISOString();
847
+ }
848
+ function lookForCut(name, startedAt) {
849
+ return {
850
+ name,
851
+ suffixPattern: `^${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}-[0-9a-f]{4}$`,
852
+ createdAfter: createdAfterBound(startedAt)
853
+ };
854
+ }
855
+ async function sendCut(auth, op, lookFor, send) {
856
+ const name = lookFor.name;
857
+ op.begin("cut");
858
+ op.set("lookFor", lookFor);
859
+ op.sending();
860
+ try {
861
+ const cut2 = await send();
862
+ op.set("lookFor", void 0);
863
+ op.setRelease(storedIdentity(auth, cut2, name));
864
+ return cut2;
865
+ } catch (err) {
866
+ if (classifyFailure(err, { writeSent: true }) === "no") op.set("lookFor", void 0);
867
+ throw err;
868
+ }
869
+ }
870
+ async function runReleaseCreate(args) {
871
+ const typed = args.positionals[0];
872
+ if (typed === void 0) {
873
+ throw new UsageError(
874
+ `\`xanosdk release create\` needs a name for the new release \u2014 one no release in this workspace holds yet, e.g. \`xanosdk release create v2${contextFlags()}\`.`,
875
+ CREATE_HINT
876
+ );
877
+ }
878
+ const name = typed.trim();
879
+ assertOneName(typed, "the release name", { args, helpFor: CREATE_HINT.hintFor });
880
+ try {
881
+ assertUsableReleaseName(name);
882
+ } catch (err) {
883
+ throw new UsageError(err.message, CREATE_HINT);
884
+ }
885
+ const reserved = args.description === void 0 ? void 0 : reservedDescriptionLine(args.description);
886
+ if (reserved !== void 0) {
887
+ throw new UsageError(
888
+ `\`--description\` cannot hold a line starting with \`${RESERVED_DESCRIPTION_PREFIX}\`, and this one does: ${JSON.stringify(reserved.trim())}.
889
+ The CLI writes those lines itself to record where a release came from, and \`release show\` reads them back as its origin. Reword that line. Nothing was cut.`,
890
+ CREATE_HINT
891
+ );
892
+ }
893
+ assertBundleFile(args, { command: "release", subcommand: "create" }, "Nothing was cut.");
894
+ if (args.entryPath !== void 0) {
895
+ if (args.bundle !== void 0) {
896
+ throw new UsageError(
897
+ "`--entry` and `--bundle` both name what the cut is compared against \u2014 pass one. Nothing was cut.",
898
+ CREATE_HINT
899
+ );
900
+ }
901
+ if (!existsSync(args.entryPath) || !statSync(args.entryPath).isFile()) {
902
+ const Refusal = existsSync(args.entryPath) ? UsageError : LocalFileNotFoundError;
903
+ throw new Refusal(
904
+ `\`--entry ${args.entryPath}\` ${existsSync(args.entryPath) ? "is not a file" : "does not exist"}. Nothing was cut. Pass the backend's entry file (its \`index.ts\`).`,
905
+ CREATE_HINT
906
+ );
907
+ }
908
+ }
909
+ const cwd = process.cwd();
910
+ const credential = memoCredential(() => getAccessToken(args));
911
+ const source = await resolveFrom(args, credential, cwd);
912
+ const auth = await credential();
913
+ const op = createOperation({
914
+ operation: "release create",
915
+ destination: { ...credentialWriteTarget(auth), kind: "workspace" },
916
+ release: { instance: auth.instance, workspaceId: auth.workspaceId, id: void 0, name },
917
+ branch: args.branch ?? null,
918
+ steps: ["cut"],
919
+ extras: { declined: false }
920
+ });
921
+ const lookFor = lookForCut(name, op.startedAt);
922
+ let done;
923
+ let declined = false;
924
+ try {
925
+ await runOperation(
926
+ op,
927
+ { machine: isMachineOutput(args), resolveWith: resolveCut(args), what: "the release cut" },
928
+ async () => {
929
+ const result = await cut(args, auth, source, name, cwd, op, lookFor);
930
+ if (result === "declined") declined = true;
931
+ else done = result;
932
+ }
933
+ );
934
+ } catch (err) {
935
+ if (declined) return;
936
+ if (err instanceof OperationError && err.result.resolveWith !== void 0) {
937
+ detail(
938
+ `Look for "${name}", or "${name}-" plus four hex characters, created after ${lookFor.createdAfter}.`
939
+ );
940
+ }
941
+ throw err;
942
+ }
943
+ if (done === void 0) return;
944
+ const { created, provenance } = done;
945
+ success(`Cut release ${created.name}`);
946
+ detail(`from ${provenance}`);
947
+ if (created.seededTables.length === 0) {
948
+ detail(`Carries no table rows \u2014 every table's schema and logic is included.`);
949
+ }
950
+ }
951
+ async function cut(args, auth, source, name, cwd, op, lookFor) {
952
+ const resolved = await resolveSource(source, async () => auth, { cwd, workspaceless: "any" });
953
+ await refuseLiveDeployment(resolved);
954
+ await refuseWorkspaceless(resolved, "read");
955
+ const existing = await lookupRelease(auth, name);
956
+ if (existing !== null) {
957
+ const holder = { id: existing.id, name: existing.name !== "" ? existing.name : name };
958
+ if (resolved.kind === "workspace" && args.branch === void 0) {
959
+ try {
960
+ const target = { baseUrl: resolved.target.base, workspaceId: resolved.target.workspaceId };
961
+ op.setBranch(liveBranchLabel(await listBranches(auth, target)) ?? null);
962
+ } catch {
963
+ }
964
+ }
965
+ const origin = await releaseOrigin(auth, existing);
966
+ throw new CliError(
967
+ "SDK_RELEASE_NAME_TAKEN",
968
+ `A release named "${name}" already exists (cut ${existing.createdAt ?? "earlier"}${origin === void 0 ? "" : ` from ${describeOrigin(origin)}`}). Releases are not replaced \u2014 cut a new name.`,
969
+ { exitCode: EXIT_RELEASE_REFUSED, details: { conflictsWith: holder } }
970
+ );
971
+ }
972
+ if (/[\u0080-\uffff]/.test(name)) {
973
+ const { releaseNames } = await import("./source-resolve-S2XA3XZF.js");
974
+ const twin = (await releaseNames(auth).catch(() => [])).find((n) => differOnlyInNormalisation(n, name));
975
+ if (twin !== void 0) {
976
+ const [there, typed] = differingCodePoints(twin, name);
977
+ throw new CliError(
978
+ "SDK_RELEASE_NAME_TAKEN",
979
+ `A release named "${twin}" already exists, and "${name}" differs from it only in Unicode normalisation (${there} there, ${typed} typed), so the two would read as the same name everywhere. Nothing was cut \u2014 cut a different name.`,
980
+ { exitCode: EXIT_RELEASE_REFUSED, details: { conflictsWith: { name: twin } } }
981
+ );
982
+ }
983
+ }
984
+ if (resolved.kind !== "workspace" && args.branch !== void 0) {
985
+ throw new UsageError(
986
+ `\`--branch\` names a branch of your workspace, and ${resolved.target.label} has one branch. Drop it, or cut \`--from workspace --branch ${args.branch}\` instead.`,
987
+ { hintFor: { command: "release", subcommand: "create" } }
988
+ );
989
+ }
990
+ const branchTarget = { baseUrl: resolved.target.base, workspaceId: resolved.target.workspaceId };
991
+ let branch = "";
992
+ if (resolved.kind === "workspace" && args.branch !== void 0) {
993
+ branch = requireBranch(await readBeforeCut(args, "list the workspace's branches", () => listBranches(auth, branchTarget)), args.branch).label;
994
+ op.setBranch(branch);
995
+ }
996
+ const selection = await resolveSeedSelection(args, auth, resolved);
997
+ const readArgs = args.branch !== void 0 ? { ...args, branch } : args;
998
+ const live = await readSourceExport(readArgs, auth, resolved);
999
+ await refuseUncarriedFiles(live.payload, selection, resolved, args, auth, cwd);
1000
+ if (selection !== void 0 && !await confirmNonPublicRows(args, withExportedGuards(selection, live.payload))) {
1001
+ info("Release create cancelled \u2014 nothing was cut.");
1002
+ op.set("declined", true);
1003
+ op.finish("cut", "no");
1004
+ return "declined";
1005
+ }
1006
+ step(`Cutting "${name}" from ${sourceLabel(resolved, args.branch !== void 0 ? branch : void 0)}`);
1007
+ discloseWriteTarget(credentialWriteTarget(auth));
1008
+ if (selection !== void 0) {
1009
+ detail(
1010
+ `Carrying rows from ${selection.tables.length} table${selection.tables.length === 1 ? "" : "s"}: ${selection.tables.map((t) => t.name).join(", ")}`
1011
+ );
1012
+ }
1013
+ op.set("drift", await warnOnDivergence(readArgs, resolved, cwd, live));
1014
+ if (resolved.kind === "workspace" && args.branch === void 0) {
1015
+ branch = liveBranchLabel(await readBeforeCut(args, "list the workspace's branches", () => listBranches(auth, branchTarget))) ?? "";
1016
+ op.setBranch(branch === "" ? null : branch);
1017
+ }
1018
+ const description = args.description !== void 0 ? { description: args.description } : {};
1019
+ const seeding = selection !== void 0 ? { tableIds: selection.tables.map((t) => t.id) } : {};
1020
+ const deleteFlags = `${pipedYes(args)}${contextFlags(args)}`;
1021
+ const created = resolved.kind === "workspace" ? (
1022
+ // The label resolved above: the one named, or the live branch's.
1023
+ await sendCut(
1024
+ auth,
1025
+ op,
1026
+ lookFor,
1027
+ () => createRelease(auth, {
1028
+ workspaceId: auth.workspaceId,
1029
+ name,
1030
+ branch,
1031
+ ...description,
1032
+ ...seeding
1033
+ })
1034
+ )
1035
+ ) : await cutFromEnvironment(
1036
+ auth,
1037
+ name,
1038
+ resolved,
1039
+ { ...description, ...seeding },
1040
+ op,
1041
+ lookFor,
1042
+ deleteFlags,
1043
+ retryCommand(args, { command: "release create", creates: true })
1044
+ );
1045
+ const identity = storedIdentity(auth, created, name);
1046
+ op.setRelease(identity);
1047
+ const stored = identity.name;
1048
+ if (selection !== void 0) await assertRowsLanded(auth, op, created, name, selection, deleteFlags);
1049
+ if (stored !== name) {
1050
+ op.set("requestedName", name);
1051
+ const removeWith = `xanosdk release delete ${shellWord(stored)}${deleteFlags}`;
1052
+ op.setResidue({ ...identity.id === void 0 ? {} : { id: identity.id }, name: stored, removeWith });
1053
+ op.finish("cut", "no");
1054
+ throw new CliError(
1055
+ "SDK_RELEASE_NAME_TAKEN",
1056
+ `The server stored this cut as "${stored}", not the "${name}" asked for \u2014 the name was taken while it was being cut. Anything that addresses "${name}" (a \`promote ${shellWord(name)}\`) does not reach this release.
1057
+ Delete this one with \`${removeWith}\`, then cut again under a free name.`,
1058
+ { exitCode: EXIT_RELEASE_REFUSED, details: { conflictsWith: { name }, storedAs: stored } }
1059
+ );
1060
+ }
1061
+ op.set("origin", await releaseOrigin(auth, created).catch(() => void 0) ?? null);
1062
+ op.finish("cut");
1063
+ return {
1064
+ created,
1065
+ provenance: resolved.kind === "workspace" && args.branch !== void 0 ? `${resolved.provenance} (branch ${JSON.stringify(branch)})` : resolved.provenance
1066
+ };
1067
+ }
1068
+
1069
+ // src/emit/release-transfer.ts
1070
+ var HELP = { helpFor: { command: "release", subcommand: "transfer" } };
1071
+ var CANDIDATE_LAYER_SLACK = 1024 * 1024;
1072
+ var TransferDisagreedError = class extends Error {
1073
+ name = "TransferDisagreedError";
1074
+ exitCode = EXIT_RELEASE_REFUSED;
1075
+ };
1076
+ async function unreachableDestination(dst, toProfile, args, rerun) {
1077
+ const { readReachableWorkspaces, reachableLines } = await import("./workspace-binding-56PIIPCP.js");
1078
+ const all = await readReachableWorkspaces(dst, 15e3);
1079
+ if (all === void 0 || all.length === 0 || all.some((w) => w.id === dst.workspaceId)) return void 0;
1080
+ const { resolveAuthFilePath, readCredentialFile, readProfile, loginCommand, profileAddCommand } = await import("./store-ARM6VMO5.js");
1081
+ const path = resolveAuthFilePath(args);
1082
+ const only = all.length === 1 ? all[0].id : void 0;
1083
+ let repin;
1084
+ if (dst.credentialType === "token") {
1085
+ repin = `Re-add it pinned to ${only === void 0 ? "one of the workspaces below" : `workspace ${only}`}: \`${profileAddCommand(toProfile, { path, instance: dst.instance, workspaceId: only })} --force\``;
1086
+ } else {
1087
+ let origin;
1088
+ try {
1089
+ const file = readCredentialFile(path);
1090
+ const saved = file === null ? null : readProfile(file, toProfile, path);
1091
+ origin = saved !== null && saved.type !== "token" ? saved.auth_host : void 0;
1092
+ } catch {
1093
+ origin = void 0;
1094
+ }
1095
+ repin = `Sign it in again and choose ${only === void 0 ? "one of the workspaces below" : `workspace ${only}`}: \`${loginCommand(toProfile, { path, origin }, ["--force"])}\``;
1096
+ }
1097
+ return new UsageError(
1098
+ `Workspace ${dst.workspaceId} does not exist on ${new URL(dst.instance).host} (or the \`${toProfile}\` credential cannot see it), and \`--to-profile ${toProfile}\` is pinned to it. Nothing was sent.
1099
+ ${repin}, then run \`${rerun}\`.
1100
+
1101
+ Workspaces the \`${toProfile}\` credential can reach:
1102
+ ${reachableLines(all)}`
1103
+ );
1104
+ }
1105
+ function sameBackend(a, b) {
1106
+ const origin = (u) => {
1107
+ try {
1108
+ return new URL(u).origin;
1109
+ } catch {
1110
+ return u.replace(/\/+$/, "");
1111
+ }
1112
+ };
1113
+ return origin(a.instance) === origin(b.instance) && a.workspaceId === b.workspaceId;
1114
+ }
1115
+ function decodeSourceArchive(bytes) {
1116
+ try {
1117
+ return decodeWorkspaceArchive(bytes);
1118
+ } catch (err) {
1119
+ throw new Error(
1120
+ `The source archive could not be read as a release archive (${err.message}). Nothing was sent.`
1121
+ );
1122
+ }
1123
+ }
1124
+ function embeddedReleaseName(decoded) {
1125
+ const release = decoded?.payload?.metadata?.release;
1126
+ const name = release?.name;
1127
+ if (typeof name !== "string" || name === "") {
1128
+ throw new Error(
1129
+ `The source archive carries no release name in its metadata, so the destination would not accept it as a release. Nothing was sent.`
1130
+ );
1131
+ }
1132
+ return name;
1133
+ }
1134
+ function isCandidateName(name, embedded) {
1135
+ if (name === embedded) return true;
1136
+ if (!name.startsWith(`${embedded}-`)) return false;
1137
+ return /^[0-9a-f]{4}$/.test(name.slice(embedded.length + 1));
1138
+ }
1139
+ function candidateOrder(embedded) {
1140
+ return (a, b) => {
1141
+ const exact = Number(b.name === embedded) - Number(a.name === embedded);
1142
+ if (exact !== 0) return exact;
1143
+ return (a.id ?? Number.MAX_SAFE_INTEGER) - (b.id ?? Number.MAX_SAFE_INTEGER);
1144
+ };
1145
+ }
1146
+ function contentSha256(bytes) {
1147
+ return sha256Hex(unwrapGzip(bytes));
1148
+ }
1149
+ async function findIdentical(dst, candidates, sha, tarLength) {
1150
+ const bound = tarLength + CANDIDATE_LAYER_SLACK;
1151
+ for (const c of candidates) {
1152
+ if (c.id === void 0) continue;
1153
+ let theirs;
1154
+ try {
1155
+ theirs = await downloadRelease(dst, { workspaceId: dst.workspaceId, id: c.id });
1156
+ } catch (err) {
1157
+ throw new Error(
1158
+ `Could not download the destination's release "${c.name}" (id ${c.id}) to compare it: ${err.message}
1159
+ Nothing was imported \u2014 without its bytes it cannot be told apart from this one. Retry once it downloads.`
1160
+ );
1161
+ }
1162
+ let theirSha;
1163
+ try {
1164
+ theirSha = sha256Hex(unwrapGzip(theirs, { maxBytes: bound }));
1165
+ } catch (err) {
1166
+ if (err instanceof ArchiveTooLargeError) continue;
1167
+ throw err;
1168
+ }
1169
+ if (theirSha === sha) return c;
1170
+ }
1171
+ return void 0;
1172
+ }
1173
+ function seededRows(found, decoded) {
1174
+ if (found.seededTables.length === 0) return [];
1175
+ const payload = decoded?.payload;
1176
+ const tables = Array.isArray(payload?.dbo) ? payload.dbo : [];
1177
+ const byGuid = /* @__PURE__ */ new Map();
1178
+ const byName = /* @__PURE__ */ new Map();
1179
+ for (const t of tables) {
1180
+ if (t === null || typeof t !== "object" || !Array.isArray(t.schema)) continue;
1181
+ const guarded = guardedIn(t.schema);
1182
+ if (typeof t.guid === "string") byGuid.set(t.guid, guarded);
1183
+ if (typeof t.name === "string") byName.set(t.name, guarded);
1184
+ }
1185
+ return found.seededTables.map((t) => {
1186
+ const columns = (t.guid !== void 0 ? byGuid.get(t.guid) : void 0) ?? byName.get(t.name);
1187
+ return {
1188
+ name: t.name,
1189
+ guid: t.guid,
1190
+ count: t.count,
1191
+ ...columns === void 0 ? {} : { nonPublicColumns: [...columns] }
1192
+ };
1193
+ });
1194
+ }
1195
+ function reportSeeded(rows) {
1196
+ if (rows.length === 0) return;
1197
+ warn(
1198
+ `This release carries table rows, and they go to the destination with it:`,
1199
+ "release.rows-transferred",
1200
+ rows.map((r) => {
1201
+ const count = r.count === void 0 ? "" : ` (${r.count} row${r.count === 1 ? "" : "s"})`;
1202
+ const guarded = r.nonPublicColumns === void 0 ? " \u2014 non-public columns could not be read" : r.nonPublicColumns.length === 0 ? "" : ` \u2014 non-public columns: ${r.nonPublicColumns.join(", ")}`;
1203
+ return `${r.name}${count}${guarded}`;
1204
+ })
1205
+ );
1206
+ }
1207
+ async function destinationCommand(words, toProfile, args) {
1208
+ const { environmentCredentialVars } = await import("./token-NICJUTMT.js");
1209
+ const env = environmentCredentialVars();
1210
+ const unset = env.complete && env.vars.length > 0;
1211
+ const prefix = unset ? `env ${env.vars.map((v) => `-u ${v}`).join(" ")} ` : "";
1212
+ const yes = unset ? " --yes" : pipedYes(args);
1213
+ return `${prefix}xanosdk ${words}${yes}${credentialFlags({ profile: toProfile, authFile: args.authFile })}`;
1214
+ }
1215
+ function identityOf(auth, r) {
1216
+ return { instance: auth.instance, workspaceId: auth.workspaceId, id: r.id, name: r.name };
1217
+ }
1218
+ async function runReleaseTransfer(args) {
1219
+ const name = args.positionals[0];
1220
+ if (name === void 0 || name === "") {
1221
+ throw new UsageError(
1222
+ `\`xanosdk release transfer\` needs a release name. Run \`xanosdk release list${contextFlags()}\` to see them.`,
1223
+ { hintFor: HELP.helpFor }
1224
+ );
1225
+ }
1226
+ assertOneName(name, "the release name", { args, helpFor: HELP.helpFor });
1227
+ const toProfile = args.toProfile;
1228
+ if (toProfile === void 0) {
1229
+ throw new UsageError(
1230
+ `\`xanosdk release transfer\` needs a destination: \`--to-profile <name>\`, a stored credential profile bound to the workspace that should receive the release. \`xanosdk profile list\` shows them.`,
1231
+ { hintFor: HELP.helpFor }
1232
+ );
1233
+ }
1234
+ const transferCommand = (release, profile) => `xanosdk release transfer ${shellWord(release)} --to-profile ${shellWord(profile)}${args.dryRun === true ? " --dry-run" : ""}${args.json === true ? " --json" : ""}${credentialFlags(args)}`;
1235
+ const again = () => `${transferCommand(name, toProfile)}${args.dryRun === true ? "" : pipedYes(args)}`;
1236
+ const src = await getAccessToken(args);
1237
+ const dst = await resolveNamedProfile(toProfile, args, "--to-profile").catch(async (err) => {
1238
+ if (!(err instanceof ProfileNotFoundError)) throw err;
1239
+ const { readCredentialFile, profileNames, resolveAuthFilePath } = await import("./store-ARM6VMO5.js");
1240
+ const { suggestAll } = await import("./suggest-6FOOYKHG.js");
1241
+ const file = readCredentialFile(resolveAuthFilePath(args));
1242
+ const near = suggestAll(toProfile, file === null ? [] : profileNames(file));
1243
+ if (near.length === 0) throw err;
1244
+ throw new ProfileNotFoundError(
1245
+ `${err.message}${near.map((n) => `
1246
+ Did you mean "${safeText(n)}"? \`${transferCommand(name, n)}\``).join("")}`
1247
+ );
1248
+ });
1249
+ if (sameBackend(src, dst)) {
1250
+ throw new UsageError(
1251
+ `\`--to-profile ${toProfile}\` is bound to the same instance and workspace the release is read from (${describeWriteTarget({ base: dst.instance, workspaceId: dst.workspaceId })}). A transfer onto itself can only store a duplicate. Name a profile bound to another workspace.`,
1252
+ // A runtime refusal: the command's shape was fine, so a pointer, not the block.
1253
+ { hintFor: HELP.helpFor }
1254
+ );
1255
+ }
1256
+ const dryRun = args.dryRun === true;
1257
+ const destination = { base: dst.instance, workspaceId: dst.workspaceId, label: `profile "${toProfile}"` };
1258
+ const source = { base: src.instance, workspaceId: src.workspaceId };
1259
+ step(`${dryRun ? "Checking" : "Transferring"} release "${name}"`);
1260
+ detail(`from ${describeWriteTarget(source)}`);
1261
+ discloseWriteTarget(destination);
1262
+ const op = createOperation({
1263
+ operation: "release transfer",
1264
+ // The one destination shape: a workspace is `{ instance, workspaceId, kind }`.
1265
+ // The profile's name is the progress line's label, not a machine field.
1266
+ destination: { base: dst.instance, workspaceId: dst.workspaceId, kind: "workspace" },
1267
+ // The embedded name replaces this once the archive has been read.
1268
+ release: identityOf(dst, { id: void 0, name }),
1269
+ branch: null,
1270
+ steps: ["read", "search", "import", "verify"]
1271
+ });
1272
+ const resolveWith = `xanosdk release transfer ${shellWord(name)} --to-profile ${shellWord(toProfile)} --dry-run${credentialFlags(args)} --json`;
1273
+ const machine = isMachineOutput(args);
1274
+ let answeredNo = false;
1275
+ try {
1276
+ await runOperation(op, { machine, resolveWith, what: `the import of "${name}"` }, async () => {
1277
+ op.begin("read");
1278
+ const found = await lookupRelease(src, name);
1279
+ if (found === null) {
1280
+ const { releaseNames } = await import("./source-resolve-S2XA3XZF.js");
1281
+ const { suggestAll } = await import("./suggest-6FOOYKHG.js");
1282
+ const near = suggestAll(name, (await releaseNames(src).catch(() => [])).filter((n) => n !== name));
1283
+ const meant = near.map(
1284
+ (n) => `
1285
+ Did you mean "${safeText(n)}"${normalisationNote(name, n)}? \`${transferCommand(n, toProfile)}\``
1286
+ );
1287
+ throw new SourceError(
1288
+ `No release named "${name}". \`xanosdk release list${contextFlags()}\` shows the ones that exist.${meant.join("")}`,
1289
+ "gone",
1290
+ "release",
1291
+ near[0],
1292
+ near.length > 1 ? near : void 0
1293
+ );
1294
+ }
1295
+ if (found.id === void 0) {
1296
+ throw new Error(`Release "${name}" carries no id, so it cannot be downloaded. Nothing was sent.`);
1297
+ }
1298
+ op.set("source", identityOf(src, { id: found.id, name: found.name }));
1299
+ const bytes = await downloadRelease(src, { workspaceId: src.workspaceId, id: found.id });
1300
+ const tar = unwrapGzip(bytes);
1301
+ const decoded = decodeSourceArchive(tar);
1302
+ const seeded = seededRows(found, decoded);
1303
+ op.set("seededTables", seeded);
1304
+ reportSeeded(seeded);
1305
+ const sha = sha256Hex(tar);
1306
+ op.set("sha256", sha);
1307
+ const embedded = embeddedReleaseName(decoded);
1308
+ op.setRelease(identityOf(dst, { id: void 0, name: embedded }));
1309
+ if (embedded !== found.name) {
1310
+ detail(`Stored at the source as "${found.name}"; the archive names itself "${embedded}".`);
1311
+ }
1312
+ op.finish("read");
1313
+ op.begin("search");
1314
+ let rows;
1315
+ try {
1316
+ rows = await listReleases(dst, { workspaceId: dst.workspaceId });
1317
+ } catch (err) {
1318
+ const unanswered = readUnanswered(
1319
+ "release",
1320
+ `search the destination (${describeWriteTarget(destination)}) for release "${embedded}"`,
1321
+ "a refusal",
1322
+ err
1323
+ );
1324
+ if (unanswered !== err) throw unanswered;
1325
+ const unreachable = await unreachableDestination(dst, toProfile, args, again());
1326
+ if (unreachable !== void 0) throw unreachable;
1327
+ const [head = "", ...rest] = (err instanceof Error ? err.message : String(err)).split("\n");
1328
+ throw new Error(
1329
+ [
1330
+ `The \`${toProfile}\` credential cannot list releases on ${describeWriteTarget(destination)}: ${head.trim().replace(/[.:]$/, "")}. A transfer never imports without searching first. Nothing was sent.`,
1331
+ ...rest
1332
+ ].join("\n")
1333
+ );
1334
+ }
1335
+ const candidates = rows.filter((r) => isCandidateName(r.name, embedded)).sort(candidateOrder(embedded));
1336
+ const match = await findIdentical(dst, candidates, sha, tar.length);
1337
+ const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
1338
+ op.set("checkedAt", checkedAt);
1339
+ if (match !== void 0) {
1340
+ op.setRelease(identityOf(dst, match));
1341
+ op.set("reused", true);
1342
+ if (dryRun) op.set("present", "identical");
1343
+ op.finish("search");
1344
+ op.finish("import", "skipped");
1345
+ op.finish("verify");
1346
+ success(
1347
+ `${dryRun ? "Already at" : "Reused"} the destination: "${match.name}" (id ${match.id}) holds the same bytes`
1348
+ );
1349
+ detail(`sha256 ${sha}`);
1350
+ if (match.id !== void 0 && unrecordedCopy(match)) {
1351
+ if (dryRun) detail("It does not record which release it was copied from; a transfer would record it.");
1352
+ else await recordCopy(op, dst, { id: match.id, name: match.name }, found, src, again());
1353
+ }
1354
+ return;
1355
+ }
1356
+ const holder = candidates.find((c) => c.name === embedded);
1357
+ if (holder !== void 0) {
1358
+ if (dryRun) op.set("present", "conflict");
1359
+ op.finish("search", "no");
1360
+ throw new CliError(
1361
+ "SDK_RELEASE_NAME_TAKEN",
1362
+ `The destination already holds a different release named "${embedded}"${holder.id === void 0 ? "" : ` (id ${holder.id})`}, and no release there holds these bytes. Releases are not replaced \u2014 nothing was imported.
1363
+ To replace it, delete it there with \`${await destinationCommand(`release delete ${shellWord(holder.name)}`, toProfile, args)}\`, then run \`${again()}\` again.`,
1364
+ { exitCode: EXIT_RELEASE_REFUSED, details: { conflictsWith: { id: holder.id, name: holder.name } } }
1365
+ );
1366
+ }
1367
+ op.finish("search");
1368
+ if (dryRun) {
1369
+ op.set("present", "absent");
1370
+ op.finish("import", "no");
1371
+ answeredNo = true;
1372
+ info(`Not at the destination as of ${checkedAt}. A transfer would import it as "${embedded}".`);
1373
+ return;
1374
+ }
1375
+ if (args.yes !== true) {
1376
+ const { rerun, note } = yesRerun(args, `release transfer ${shellWord(found.name)}`);
1377
+ const ok = await confirm(`Import release "${embedded}" into ${describeWriteTarget(destination)}?`, {
1378
+ flag: "--yes",
1379
+ refusal: { details: { verb: "transfer", imported: false, declined: false }, rerun, note }
1380
+ });
1381
+ if (!ok) {
1382
+ warn("Transfer cancelled \u2014 nothing was imported.", "release.transfer-cancelled");
1383
+ answeredNo = true;
1384
+ return;
1385
+ }
1386
+ }
1387
+ op.set("reused", false);
1388
+ op.begin("import");
1389
+ op.sending();
1390
+ const { id } = await importRelease(dst, { workspaceId: dst.workspaceId, archive: bytes }).catch(async (err) => {
1391
+ throw await unreachableDestination(dst, toProfile, args, again()) ?? err;
1392
+ });
1393
+ op.finish("import");
1394
+ op.setRelease(identityOf(dst, { id, name: embedded }));
1395
+ const stored = await verify(op, { dst, id, embedded, sha, tarLength: tar.length, toProfile, config: args.authFile, yes: pipedYes(args) });
1396
+ await recordCopy(op, dst, { id, name: stored }, found, src, again());
1397
+ });
1398
+ } catch (err) {
1399
+ if (answeredNo) return;
1400
+ throw err;
1401
+ }
1402
+ }
1403
+ async function verify(op, ctx) {
1404
+ const { dst, id, embedded, sha } = ctx;
1405
+ op.begin("verify");
1406
+ let landed;
1407
+ let landedBytes;
1408
+ try {
1409
+ const got = await getRelease(dst, { workspaceId: dst.workspaceId, id });
1410
+ if (got === null) {
1411
+ throw new Error(`The import answered with id ${id}, and the destination holds no release by that id.`);
1412
+ }
1413
+ landed = got;
1414
+ landedBytes = await downloadRelease(dst, { workspaceId: dst.workspaceId, id });
1415
+ } catch (err) {
1416
+ op.finish("verify", "unknown");
1417
+ throw err;
1418
+ }
1419
+ const stored = landed.name !== "" ? landed.name : embedded;
1420
+ op.setRelease(identityOf(dst, { id, name: stored }));
1421
+ const landedSha = contentSha256(landedBytes);
1422
+ if (landedSha !== sha) {
1423
+ const removeWith = `xanosdk release delete ${shellWord(stored)}${ctx.yes} --profile ${shellWord(ctx.toProfile)}${ctx.config === void 0 ? "" : ` --config ${shellWord(ctx.config)}`}`;
1424
+ op.set("destinationSha256", landedSha);
1425
+ op.setResidue({ id, name: stored, removeWith });
1426
+ op.finish("verify", "no");
1427
+ throw new TransferDisagreedError(
1428
+ `Imported "${stored}" (id ${id}), but what the destination holds does not hash to what was sent (sent ${sha}, holds ${landedSha}).
1429
+ It has been kept, not removed \u2014 it is the evidence. Remove it with \`${removeWith}\`.`
1430
+ );
1431
+ }
1432
+ const duplicate = stored === embedded ? void 0 : await concurrentDuplicate(dst, { id, embedded, sha, tarLength: ctx.tarLength });
1433
+ if (duplicate !== void 0) op.set("duplicateOf", duplicate);
1434
+ op.finish("verify");
1435
+ success(`Transferred "${stored}" (id ${id})`);
1436
+ detail(`sha256 ${sha} \u2014 verified on the destination`);
1437
+ if (stored !== embedded) {
1438
+ warn(`The destination stored it as "${stored}": "${embedded}" was taken by the time it landed.`, "release.name-stored");
1439
+ }
1440
+ if (duplicate !== void 0) {
1441
+ detail(
1442
+ `"${duplicate.name}" (id ${duplicate.id}) holds the same release \u2014 a concurrent transfer's. Neither was removed.`
1443
+ );
1444
+ }
1445
+ return stored;
1446
+ }
1447
+ async function recordCopy(op, dst, copy, source, src, rerun) {
1448
+ const cut2 = source.copiedFrom?.cut ?? source.recordedOrigin ?? (typeof source.branch === "string" && source.branch !== "" ? { type: "branch", name: source.branch } : void 0);
1449
+ const copied = {
1450
+ source: { host: new URL(src.instance).host, workspaceId: src.workspaceId, release: source.name },
1451
+ ...cut2 === void 0 ? {} : { cut: cut2 }
1452
+ };
1453
+ const description = describeAsCopy(source.description, copied);
1454
+ try {
1455
+ const updated = await updateReleaseDescription(dst, { workspaceId: dst.workspaceId, ...copy, description });
1456
+ if (updated.copiedFrom === void 0) {
1457
+ throw new Error("the update answered without the description it was sent");
1458
+ }
1459
+ op.set("copyRecorded", true);
1460
+ } catch (err) {
1461
+ op.set("copyRecorded", false);
1462
+ const cause = (err.message.split("\n")[0] ?? "").trim().replace(/[.:;]$/, "");
1463
+ warn(
1464
+ `Transferred, but recording on "${copy.name}" which release it was copied from failed (${cause}). The copy is intact; until it is recorded, \`release show\` at the destination reports its origin as the branch the import tagged it with.`,
1465
+ "release.copy-unrecorded",
1466
+ [`Run \`${rerun}\` again to record it.`]
1467
+ );
1468
+ }
1469
+ }
1470
+ function unrecordedCopy(r) {
1471
+ return r.copiedFrom === void 0 && r.recordedOrigin === void 0 && (r.description ?? "") === "";
1472
+ }
1473
+ async function concurrentDuplicate(dst, ctx) {
1474
+ try {
1475
+ const rows = await listReleases(dst, { workspaceId: dst.workspaceId });
1476
+ const others = rows.filter((r) => r.id !== ctx.id && isCandidateName(r.name, ctx.embedded)).sort(candidateOrder(ctx.embedded));
1477
+ const twin = await findIdentical(dst, others, ctx.sha, ctx.tarLength);
1478
+ return twin?.id === void 0 ? void 0 : { id: twin.id, name: twin.name };
1479
+ } catch {
1480
+ detail(`Could not check the destination for a concurrent copy of "${ctx.embedded}".`);
1481
+ return void 0;
1482
+ }
1483
+ }
1484
+
1485
+ // src/emit/release-ns-command.ts
1486
+ async function runReleaseCommand(args) {
1487
+ switch (args.subcommand) {
1488
+ case "create":
1489
+ return runReleaseCreate(args);
1490
+ case "list":
1491
+ return runList(args);
1492
+ case "show":
1493
+ return runShow(args);
1494
+ case "export":
1495
+ return runExport(args);
1496
+ case "delete":
1497
+ return runDelete(args);
1498
+ case "transfer":
1499
+ return runReleaseTransfer(args);
1500
+ default:
1501
+ throw unknownSubcommand("release", args.subcommand);
1502
+ }
1503
+ }
1504
+ async function runList(args) {
1505
+ const auth = await getAccessToken(args);
1506
+ const rows = await listReleases(auth, { workspaceId: auth.workspaceId }).catch((err) => {
1507
+ throw readUnanswered("release", "list releases", "an empty list", err);
1508
+ });
1509
+ if (isMachineOutput(args)) {
1510
+ const origins2 = await Promise.all(rows.map((r) => releaseOrigin(auth, r)));
1511
+ writeJson({ releases: rows.map((r, i) => ({ ...publicRelease(r), origin: origins2[i] ?? null })) });
1512
+ return;
1513
+ }
1514
+ if (rows.length === 0) {
1515
+ info(`No releases yet. \`xanosdk release create <name>${contextFlags()}\` cuts one from a running environment.`);
1516
+ return;
1517
+ }
1518
+ const s = stdoutStyle();
1519
+ const origins = await Promise.all(rows.map((r) => releaseOrigin(auth, r)));
1520
+ for (const [i, r] of rows.entries()) {
1521
+ const origin = origins[i];
1522
+ const from = origin === void 0 ? "from: not recorded" : `from ${describeOrigin(origin)}`;
1523
+ printHuman(`${s.bold(safeText(r.name))} ${from} ${safeText(r.createdAt ?? "")}
1524
+ `);
1525
+ if (r.description !== void 0 && r.description !== "") detail(safeText(oneLine(r.description)));
1526
+ }
1527
+ }
1528
+ var LIST_DESCRIPTION_WIDTH = 100;
1529
+ function oneLine(description) {
1530
+ const lines = description.split("\n").map((l) => l.trim());
1531
+ const first = lines.find((l) => l !== "") ?? "";
1532
+ const more = lines.filter((l) => l !== "").length > 1;
1533
+ if (first.length > LIST_DESCRIPTION_WIDTH) return `${first.slice(0, LIST_DESCRIPTION_WIDTH).trimEnd()}\u2026`;
1534
+ return more ? `${first} \u2026` : first;
1535
+ }
1536
+ async function runShow(args) {
1537
+ const name = requireName(args, "show");
1538
+ const auth = await getAccessToken(args);
1539
+ const found = await requireRelease(auth, name, contextFlags(args));
1540
+ const origin = await releaseOrigin(auth, found);
1541
+ if (isMachineOutput(args)) {
1542
+ writeJson({ ...publicRelease(found), origin: origin ?? null });
1543
+ return;
1544
+ }
1545
+ const s = stdoutStyle();
1546
+ printHuman(`${s.bold(safeText(found.name))}
1547
+ `);
1548
+ detail(`from: ${origin === void 0 ? "not recorded" : describeOrigin(origin)}`);
1549
+ detail(`created: ${safeText(found.createdAt ?? "-")}`);
1550
+ if (found.description !== void 0 && found.description !== "") {
1551
+ for (const line of found.description.split("\n")) detail(safeText(line));
1552
+ }
1553
+ detail(
1554
+ found.seededTables.length === 0 ? "rows: none (every table's schema is included)" : `rows: ${found.seededTables.map((t) => `${safeText(t.name)}${t.count === void 0 ? "" : ` (${t.count})`}`).join(", ")}`
1555
+ );
1556
+ if (!found.hasResource) {
1557
+ warn("Its stored contents are missing \u2014 cut it again from a live environment.", "release.contents-missing");
1558
+ }
1559
+ }
1560
+ function publicRelease(r) {
1561
+ const { recordedOrigin: _origin, copiedFrom: _copied, ...rest } = r;
1562
+ return rest;
1563
+ }
1564
+ async function requireRelease(auth, name, flags = "") {
1565
+ const found = await lookupRelease(auth, name);
1566
+ if (found === null) {
1567
+ const { releaseNames } = await import("./source-resolve-S2XA3XZF.js");
1568
+ const { suggestAll } = await import("./suggest-6FOOYKHG.js");
1569
+ const near = suggestAll(name, (await releaseNames(auth).catch(() => [])).filter((n) => n !== name));
1570
+ throw new SourceError(
1571
+ `No release named "${name}". \`xanosdk release list${flags}\` shows the ones that exist.` + (near.length === 0 ? "" : `
1572
+ Did you mean ${orNamesNoted(name, near)}?`),
1573
+ "gone",
1574
+ "release",
1575
+ near[0],
1576
+ near
1577
+ );
1578
+ }
1579
+ return found;
1580
+ }
1581
+ async function runExport(args) {
1582
+ const name = requireName(args, "export");
1583
+ const target = resolveOutputTarget({ path: args.path, name, ext: "xs" });
1584
+ const auth = await getAccessToken(args);
1585
+ const found = await requireRelease(auth, name, contextFlags(args));
1586
+ if (found.id === void 0) {
1587
+ throw new Error(`Release "${name}" carries no id, so it cannot be exported.`);
1588
+ }
1589
+ const seeded = found.seededTables.filter((t) => t.count !== 0);
1590
+ const content = await exportRelease(auth, {
1591
+ workspaceId: auth.workspaceId,
1592
+ id: found.id,
1593
+ records: seeded.length > 0,
1594
+ name: found.name
1595
+ });
1596
+ const rowsLine = seeded.length === 0 ? void 0 : `Includes the rows of ${seeded.length} seeded table${seeded.length === 1 ? "" : "s"}: ${seeded.map((t) => t.name).join(", ")}`;
1597
+ if (target.kind === "stdout") {
1598
+ writeData(process.stdout, content + "\n");
1599
+ if (rowsLine !== void 0) detail(rowsLine);
1600
+ const secrets = secretsInMultidoc(content);
1601
+ if (secrets.length > 0) {
1602
+ warn(
1603
+ `The export carries ${secretsPhrase(secrets)} in cleartext \u2014 written to stdout, where no file permission protects it; do not commit or log it.`,
1604
+ "secrets.cleartext-stdout"
1605
+ );
1606
+ }
1607
+ return;
1608
+ }
1609
+ writeExportFile(target.path, content + "\n", secretsInMultidoc(content));
1610
+ if (isMachineOutput(args)) {
1611
+ writeJson({ path: target.path, name, seededTables: seeded.map((t) => t.name) });
1612
+ }
1613
+ success(`Wrote ${target.path}`);
1614
+ if (rowsLine !== void 0) detail(rowsLine);
1615
+ }
1616
+ async function runDelete(args) {
1617
+ const name = requireName(args, "delete");
1618
+ const auth = await getAccessToken(args);
1619
+ const destination = credentialWriteTarget(auth);
1620
+ const { asDeleteLookupFailure } = await import("./ephemeral-command-CLJJKOFT.js");
1621
+ const found = await lookupRelease(auth, name).catch((err) => {
1622
+ throw asDeleteLookupFailure(err);
1623
+ });
1624
+ if (found === null || found.id === void 0) {
1625
+ const { releaseNames, SourceError: SourceError2 } = await import("./source-resolve-S2XA3XZF.js");
1626
+ const { suggestAll } = await import("./suggest-6FOOYKHG.js");
1627
+ const near = suggestAll(name, (await releaseNames(auth).catch(() => [])).filter((n) => n !== name));
1628
+ const again = `${args.json === true ? " --json" : ""}${contextFlags(args)}`;
1629
+ const missed = `No release named "${name}" on ${describeWriteTarget(destination)} \u2014 nothing was deleted. \`xanosdk release list${contextFlags(args)}\` shows the ones that exist.`;
1630
+ const meant = near.map((n) => `Did you mean "${safeText(n)}"${normalisationNote(name, n)}? \`xanosdk release delete ${shellWord(n)}${again}\` deletes it, after asking to confirm.`);
1631
+ if (near.length > 0) {
1632
+ throw new SourceError2([missed, ...meant].join("\n"), "gone", "release", near[0], near.length > 1 ? near : void 0);
1633
+ }
1634
+ warn(missed, "release.not-found");
1635
+ if (isMachineOutput(args)) {
1636
+ writeJson({
1637
+ verb: "delete",
1638
+ destination: writeTargetPayload(destination),
1639
+ name,
1640
+ deleted: false,
1641
+ alreadyGone: true,
1642
+ declined: false
1643
+ });
1644
+ }
1645
+ return;
1646
+ }
1647
+ const refusing = args.yes !== true && process.stdin.isTTY !== true;
1648
+ step(`${refusing ? "Would delete" : "Deleting"} release "${name}"`);
1649
+ discloseWriteTarget(destination);
1650
+ const runners = await releaseRunners(auth, { id: found.id, name: found.name });
1651
+ const runBy = runners.tenants.length + runners.branches.length === 0 ? void 0 : runners;
1652
+ const runningIt = runBy === void 0 ? void 0 : describeRunners(runBy);
1653
+ if (runningIt !== void 0) {
1654
+ warn(
1655
+ `Release "${name}" is in use: ${runningIt}. Deleting it leaves what runs it running, but it can no longer be landed again \u2014 nor put back after a deploy there stops partway.`,
1656
+ "release.delete-in-use"
1657
+ );
1658
+ }
1659
+ if (!args.yes) {
1660
+ const ok = await confirm(
1661
+ runningIt === void 0 ? `Delete release "${name}"? Anything running it keeps running; the record goes.` : `Delete release "${name}"? ${runningIt.charAt(0).toUpperCase()}${runningIt.slice(1)} \u2014 it keeps running there; the record goes.`,
1662
+ {
1663
+ flag: "--yes",
1664
+ // Off a terminal, the details `tenant delete` carries, and this delete with `--yes`.
1665
+ refusal: {
1666
+ details: { verb: "delete", name, deleted: false, alreadyGone: false, declined: false, ...runBy === void 0 ? {} : { runBy } },
1667
+ rerun: `xanosdk release delete ${shellWord(name)} --yes${args.json === true ? " --json" : ""}${contextFlags(args)}`
1668
+ }
1669
+ }
1670
+ );
1671
+ if (!ok) {
1672
+ info("Deletion cancelled \u2014 nothing was deleted.");
1673
+ if (isMachineOutput(args)) {
1674
+ writeJson({
1675
+ verb: "delete",
1676
+ destination: writeTargetPayload(destination),
1677
+ name,
1678
+ deleted: false,
1679
+ alreadyGone: false,
1680
+ declined: true,
1681
+ ...runBy === void 0 ? {} : { runBy }
1682
+ });
1683
+ }
1684
+ return;
1685
+ }
1686
+ }
1687
+ const id = found.id;
1688
+ const { alreadyGone } = await describeWrite(
1689
+ { what: `the delete of release "${name}"`, resolveWith: `xanosdk release show ${shellWord(name)}${contextFlags(args)}` },
1690
+ () => deleteRelease(auth, { workspaceId: auth.workspaceId, id })
1691
+ ).catch(async (err) => {
1692
+ const { writeTransportFailure } = await import("./answer-shape-K67TAAPY.js");
1693
+ const { unknownDeleteOutcome } = await import("./ephemeral-command-CLJJKOFT.js");
1694
+ throw unknownDeleteOutcome(writeTransportFailure(err), `release "${name}"`, name, contextFlags(args), "release");
1695
+ });
1696
+ if (alreadyGone) warn(`Release "${name}" was already gone.`, "release.already-gone");
1697
+ else success(`Deleted release ${name}`);
1698
+ if (isMachineOutput(args)) {
1699
+ writeJson({
1700
+ verb: "delete",
1701
+ destination: writeTargetPayload(destination),
1702
+ name,
1703
+ deleted: !alreadyGone,
1704
+ alreadyGone,
1705
+ declined: false,
1706
+ ...runBy === void 0 ? {} : { runBy }
1707
+ });
1708
+ }
1709
+ }
1710
+ async function releaseRunners(auth, release) {
1711
+ const { listTenants } = await import("./tenant-IIHPOHGP.js");
1712
+ const { listEphemeral } = await import("./ephemeral-P7F3PMAD.js");
1713
+ const { listBranchListing } = await import("./branch-NCRNWQBQ.js");
1714
+ const { isDerivedPromoteLabel } = await import("./promote-command-PCWT5N6S.js");
1715
+ const [tenants, ephemerals, listing] = await Promise.all([
1716
+ listTenants(auth, { workspaceId: auth.workspaceId }).catch(() => []),
1717
+ listEphemeral(auth, { parentWorkspaceId: auth.workspaceId }).catch(() => []),
1718
+ listBranchListing(auth, { baseUrl: auth.instance, workspaceId: auth.workspaceId }).catch(() => void 0)
1719
+ ]);
1720
+ const runners = /* @__PURE__ */ new Map();
1721
+ for (const t of [...tenants, ...ephemerals]) {
1722
+ if (t.deployedReleaseId === release.id && !runners.has(t.name)) runners.set(t.name, t.type === "ephemeral" ? "ephemeral" : "tenant");
1723
+ }
1724
+ const { getEnvironment, readEphemeralState } = await import("./ephemeral-state-PYCBSOGX.js");
1725
+ const { projectDirFrom } = await import("./xanosdk-project-S6DXJONQ.js");
1726
+ const dir = projectDirFrom(process.cwd());
1727
+ const tracked = dir === void 0 ? void 0 : getEnvironment(readEphemeralState(dir), auth);
1728
+ if (tracked?.release === release.name && ephemerals.some((e) => e.name === tracked.name)) runners.set(tracked.name, "ephemeral");
1729
+ return {
1730
+ tenants: [...runners].map(([name, kind]) => ({ name, kind })),
1731
+ branches: (listing?.branches ?? []).filter((b) => isDerivedPromoteLabel(b.label, release.name)).map((b) => b.label)
1732
+ };
1733
+ }
1734
+ function describeRunners(r) {
1735
+ const list = (named) => named.length === 1 ? named[0] : `${named.slice(0, -1).join(", ")} and ${named.at(-1)}`;
1736
+ return [
1737
+ ...r.tenants.length === 0 ? [] : [`it is the last release landed on ${list(r.tenants.map((t) => `${t.kind} "${t.name}"`))}`],
1738
+ ...r.branches.length === 0 ? [] : [`a promote of it landed ${list(r.branches.map((b) => `branch "${b}"`))}`]
1739
+ ].join("; ");
1740
+ }
1741
+ export {
1742
+ oneLine,
1743
+ runReleaseCommand
1744
+ };
1745
+ //# sourceMappingURL=release-ns-command-3KFDMZ6G.js.map