@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.
- package/CHANGELOG.md +788 -0
- package/LICENSE +21 -0
- package/README.md +541 -0
- package/dist/.build-fingerprint +1 -0
- package/dist/agent-file-refresh-ZEMK33AX.js +38 -0
- package/dist/answer-shape-K67TAAPY.js +33 -0
- package/dist/backend-dir-2U53CIAL.js +39 -0
- package/dist/backend-slot-7QXW2QYC.js +28 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +255 -0
- package/dist/branch-NCRNWQBQ.js +61 -0
- package/dist/branch-commands-ACIRCEQJ.js +79 -0
- package/dist/bundle.d.ts +223 -0
- package/dist/bundle.js +153 -0
- package/dist/capture-WAXPD7DR.js +41 -0
- package/dist/chunk-27GWNP2I.js +23 -0
- package/dist/chunk-2CMJZDCL.js +177 -0
- package/dist/chunk-2EZ3ZJZZ.js +302 -0
- package/dist/chunk-2GNFDZA2.js +390 -0
- package/dist/chunk-2MNLRIOX.js +11 -0
- package/dist/chunk-2TFRR6JV.js +104 -0
- package/dist/chunk-2ZUEE4EK.js +408 -0
- package/dist/chunk-36JUQZVQ.js +22 -0
- package/dist/chunk-3DRXWLJ5.js +83 -0
- package/dist/chunk-3JDU4VRL.js +137 -0
- package/dist/chunk-3KZCLN4Z.js +241 -0
- package/dist/chunk-3LDULPRE.js +168 -0
- package/dist/chunk-3M4LFYNQ.js +111 -0
- package/dist/chunk-3XI5CTUE.js +22 -0
- package/dist/chunk-3YW23UFH.js +50 -0
- package/dist/chunk-45UIT5T2.js +862 -0
- package/dist/chunk-4BW2SG2N.js +46 -0
- package/dist/chunk-4KHEMC32.js +79 -0
- package/dist/chunk-4POWHT2K.js +321 -0
- package/dist/chunk-4Q2GN7NW.js +192 -0
- package/dist/chunk-5A2PLKZB.js +70 -0
- package/dist/chunk-5FBYXK5P.js +159 -0
- package/dist/chunk-5HVXPTU4.js +323 -0
- package/dist/chunk-5QU2P5CA.js +304 -0
- package/dist/chunk-5V5XU32D.js +191 -0
- package/dist/chunk-5ZSO4T2G.js +10 -0
- package/dist/chunk-622HUCZT.js +10 -0
- package/dist/chunk-65HWOMGV.js +177 -0
- package/dist/chunk-6ELFRN3P.js +365 -0
- package/dist/chunk-6HO3NCSJ.js +1055 -0
- package/dist/chunk-6IUMKYWI.js +457 -0
- package/dist/chunk-6KEB3C3U.js +65 -0
- package/dist/chunk-6MR5TI5V.js +987 -0
- package/dist/chunk-6NNTNXN7.js +157 -0
- package/dist/chunk-6PBR37LN.js +97 -0
- package/dist/chunk-6QXLMMT3.js +155 -0
- package/dist/chunk-6ZGJCBZS.js +224 -0
- package/dist/chunk-73IVBKMA.js +250 -0
- package/dist/chunk-7G3CTU7I.js +286 -0
- package/dist/chunk-A3UDOZB6.js +222 -0
- package/dist/chunk-A6E77GDW.js +85 -0
- package/dist/chunk-AHJWCYQO.js +660 -0
- package/dist/chunk-AKNCFVKZ.js +36 -0
- package/dist/chunk-AM4FKL4J.js +237 -0
- package/dist/chunk-AN6BL47T.js +26 -0
- package/dist/chunk-AOSYVMUS.js +147 -0
- package/dist/chunk-APWXY5SL.js +182 -0
- package/dist/chunk-AWEIIR2U.js +218 -0
- package/dist/chunk-AWQ7BQTQ.js +73 -0
- package/dist/chunk-BAUZOWGX.js +393 -0
- package/dist/chunk-BHACRS6R.js +940 -0
- package/dist/chunk-BWPNT55T.js +680 -0
- package/dist/chunk-C6RIMRU4.js +187 -0
- package/dist/chunk-CGVQZXYG.js +263 -0
- package/dist/chunk-CIMQZRRE.js +162 -0
- package/dist/chunk-CJHQKJU4.js +113 -0
- package/dist/chunk-CTMO46PK.js +1897 -0
- package/dist/chunk-D53J55C5.js +106 -0
- package/dist/chunk-D7VGG3WS.js +90 -0
- package/dist/chunk-DAD3ONIS.js +245 -0
- package/dist/chunk-DGWHAVRG.js +162 -0
- package/dist/chunk-DX4QR4B2.js +483 -0
- package/dist/chunk-EHP3WPEG.js +21 -0
- package/dist/chunk-ELP2HMXC.js +432 -0
- package/dist/chunk-ERVIASGL.js +21 -0
- package/dist/chunk-ET4Y4XCB.js +865 -0
- package/dist/chunk-ETO6LGLD.js +4750 -0
- package/dist/chunk-EUUUMY7J.js +24 -0
- package/dist/chunk-FQJ2TTHP.js +218 -0
- package/dist/chunk-FQOF7E2E.js +133 -0
- package/dist/chunk-FWD7AL6N.js +21 -0
- package/dist/chunk-FXFV3KU6.js +138 -0
- package/dist/chunk-FXRQOWTE.js +171 -0
- package/dist/chunk-G554NZNI.js +210 -0
- package/dist/chunk-GSGDWJB5.js +13 -0
- package/dist/chunk-H2V3N23D.js +66 -0
- package/dist/chunk-H5C7MBCR.js +159 -0
- package/dist/chunk-HHN734BU.js +371 -0
- package/dist/chunk-HRVBLCVJ.js +243 -0
- package/dist/chunk-HVRUQXGD.js +1264 -0
- package/dist/chunk-I4F5TCJH.js +130 -0
- package/dist/chunk-IDOTCMDV.js +57 -0
- package/dist/chunk-IGOLHKWF.js +427 -0
- package/dist/chunk-JGE7CAWB.js +148 -0
- package/dist/chunk-JTUKLCXS.js +92 -0
- package/dist/chunk-JZGVVJSI.js +66 -0
- package/dist/chunk-K4WX6UUU.js +923 -0
- package/dist/chunk-K5BBGTYZ.js +217 -0
- package/dist/chunk-K6RXBGUB.js +427 -0
- package/dist/chunk-KTSDSD36.js +165 -0
- package/dist/chunk-L5JYN3NM.js +1038 -0
- package/dist/chunk-LIEOKXKD.js +354 -0
- package/dist/chunk-LLNZGEFK.js +51 -0
- package/dist/chunk-LPECSJ2J.js +55 -0
- package/dist/chunk-LQCMKAJ7.js +413 -0
- package/dist/chunk-LRN4ZJCQ.js +1145 -0
- package/dist/chunk-MJ4ZVHKN.js +17 -0
- package/dist/chunk-MXHSQOC4.js +3061 -0
- package/dist/chunk-N5GKCWE5.js +367 -0
- package/dist/chunk-NA3PXMEF.js +10 -0
- package/dist/chunk-NAQDMEHV.js +76 -0
- package/dist/chunk-NWKXM5RD.js +109 -0
- package/dist/chunk-NZOWWHU5.js +196 -0
- package/dist/chunk-OHX6MIUZ.js +184 -0
- package/dist/chunk-OOXWYAA4.js +15 -0
- package/dist/chunk-OPHB5ORL.js +1990 -0
- package/dist/chunk-OXA6PSZT.js +1725 -0
- package/dist/chunk-P2HAGF6H.js +73 -0
- package/dist/chunk-PTHVMXZI.js +3036 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-Q325JKYB.js +189 -0
- package/dist/chunk-Q7DGTQ5G.js +255 -0
- package/dist/chunk-QCNYHBL7.js +601 -0
- package/dist/chunk-QO6BVGS5.js +145 -0
- package/dist/chunk-QXTKSOFI.js +76 -0
- package/dist/chunk-ROY4YAEQ.js +66 -0
- package/dist/chunk-RTJRZFFY.js +447 -0
- package/dist/chunk-SAVJUS2X.js +128 -0
- package/dist/chunk-SFLHVCKX.js +173 -0
- package/dist/chunk-STSFNCY4.js +127 -0
- package/dist/chunk-SWBF2LTK.js +170 -0
- package/dist/chunk-T5KELPAR.js +109 -0
- package/dist/chunk-TFFGOFVZ.js +1481 -0
- package/dist/chunk-TLHIFK4M.js +256 -0
- package/dist/chunk-TLQE7ZN3.js +156 -0
- package/dist/chunk-TPHWJ7UG.js +1981 -0
- package/dist/chunk-TRJCALW3.js +291 -0
- package/dist/chunk-TTEIYD5P.js +133 -0
- package/dist/chunk-U64O2MXY.js +407 -0
- package/dist/chunk-UCQ2VUTG.js +630 -0
- package/dist/chunk-UTI5G6OJ.js +44 -0
- package/dist/chunk-UVHDMMIP.js +188 -0
- package/dist/chunk-UZ6QQWJT.js +120 -0
- package/dist/chunk-VAX3UZOY.js +686 -0
- package/dist/chunk-W3HBUKFO.js +57 -0
- package/dist/chunk-WKQLGKCL.js +2823 -0
- package/dist/chunk-WQFNASK4.js +560 -0
- package/dist/chunk-WRBKLYSJ.js +224 -0
- package/dist/chunk-WTPZRR4I.js +17 -0
- package/dist/chunk-WZDJ2LLX.js +2385 -0
- package/dist/chunk-X2NL4KUO.js +14 -0
- package/dist/chunk-X4DVXBFY.js +38 -0
- package/dist/chunk-X677DMZM.js +7050 -0
- package/dist/chunk-XBKMWY7F.js +612 -0
- package/dist/chunk-XNCP4KYO.js +78 -0
- package/dist/chunk-XVBGLISP.js +35 -0
- package/dist/chunk-XW6KISBB.js +1192 -0
- package/dist/chunk-YGZHHAAB.js +8156 -0
- package/dist/chunk-YHEFQUQB.js +186 -0
- package/dist/chunk-YLDALTWZ.js +653 -0
- package/dist/chunk-YU4HSQW7.js +229 -0
- package/dist/chunk-ZDPP6RKB.js +27 -0
- package/dist/chunk-ZFDZKNGG.js +194 -0
- package/dist/chunk-ZIY7RNUT.js +49 -0
- package/dist/chunk-ZYJP6BOO.js +30 -0
- package/dist/cli.d.ts +1111 -0
- package/dist/cli.js +165 -0
- package/dist/codegen-command-VR2GWR6C.js +173 -0
- package/dist/codegen.d.ts +115 -0
- package/dist/codegen.js +19 -0
- package/dist/commands-GJMJ4XYZ.js +76 -0
- package/dist/completion-ZQLO6PB6.js +738 -0
- package/dist/config-MBYGCAFK.js +136 -0
- package/dist/deploy-command-JVZPWHMK.js +180 -0
- package/dist/deploy-source-3W45CXMP.js +81 -0
- package/dist/dev-env-sync-HN44K4D5.js +188 -0
- package/dist/diagnostics-UEAAHWVF.js +30 -0
- package/dist/display-name-hint-ABBPVQIR.js +46 -0
- package/dist/emit-CyhBDxli.d.ts +7007 -0
- package/dist/env-example-refresh-ET5HMMDW.js +126 -0
- package/dist/env-pull-command-TNMNB3CV.js +323 -0
- package/dist/env-target-O4HRKTGZ.js +40 -0
- package/dist/env-var-command-ZIRDCAZA.js +427 -0
- package/dist/ephemeral-P7F3PMAD.js +54 -0
- package/dist/ephemeral-command-CLJJKOFT.js +592 -0
- package/dist/ephemeral-state-PYCBSOGX.js +61 -0
- package/dist/file-lock-PT74GWYW.js +10 -0
- package/dist/generate-command-AYEOLA26.js +510 -0
- package/dist/gitignore-3SJ3WNRG.js +51 -0
- package/dist/guards-4EBNCZDC.js +162 -0
- package/dist/hosted-file-PSHJB3HX.js +28 -0
- package/dist/impersonate-command-5JAS3WSV.js +152 -0
- package/dist/import-EQSFU6JC.js +19 -0
- package/dist/index.d.ts +5470 -0
- package/dist/index.js +371 -0
- package/dist/init-command-X2FR4UAK.js +125 -0
- package/dist/init-modules-RY7UHAOL.js +50 -0
- package/dist/init-web-M4YZSZK6.js +42 -0
- package/dist/internal.d.ts +179 -0
- package/dist/internal.js +2634 -0
- package/dist/io-RC56QSN5.js +26 -0
- package/dist/keep-data-merge-7MPLVAVI.js +1119 -0
- package/dist/lambda-config-reconcile-OUZ4OYAB.js +116 -0
- package/dist/landed-753YLJ5H.js +40 -0
- package/dist/landing-record-SU52SKXU.js +61 -0
- package/dist/live-diff-YONW2VI3.js +65 -0
- package/dist/local-engine-choice-CH5X3W7E.js +79 -0
- package/dist/local-engine-command-Z3OL7VOD.js +766 -0
- package/dist/local-engine-config-5U3SC434.js +39 -0
- package/dist/local-engine-deploy-PCQ4SNPB.js +39 -0
- package/dist/local-engine-process-EHTE5LLU.js +63 -0
- package/dist/local-engine-state-A4FTRFAF.js +32 -0
- package/dist/local-engine-update-check-6L645VBL.js +162 -0
- package/dist/lock-MRRHW5GS.js +87 -0
- package/dist/lock-commands-2QG25HL7.js +963 -0
- package/dist/login-command-QPYRWOXZ.js +538 -0
- package/dist/logout-command-WOIQLDP6.js +56 -0
- package/dist/loop-ALIZLOSX.js +191 -0
- package/dist/marketplace-command-K5YL42NO.js +977 -0
- package/dist/marketplace-resolve-LY32JES5.js +40 -0
- package/dist/meta-client-YXGPW53C.js +244 -0
- package/dist/module-manifest-JFGHU2TC.js +38 -0
- package/dist/near-key.d.ts +13 -0
- package/dist/near-key.js +8 -0
- package/dist/node.d.ts +95 -0
- package/dist/node.js +969 -0
- package/dist/operation-outcome-TZSESNC7.js +51 -0
- package/dist/output-target-AL3I44GG.js +47 -0
- package/dist/package-manager-PRAHUSCN.js +34 -0
- package/dist/plan-disclosure-6G4MLZKD.js +114 -0
- package/dist/plugin-contributions-WRNY5ABG.js +28 -0
- package/dist/plugin-prompt-4VA3B4FF.js +20 -0
- package/dist/plugin-questions-JTSYA74Q.js +24 -0
- package/dist/plugin.d.ts +396 -0
- package/dist/plugin.js +1 -0
- package/dist/preflight-command-2T5Z36MI.js +321 -0
- package/dist/profile-command-AH3PIM4X.js +800 -0
- package/dist/profile-pointer-GRWJU3YD.js +31 -0
- package/dist/profile-select-74TRDEPD.js +23 -0
- package/dist/project-dependency-KSH63ODT.js +35 -0
- package/dist/project-sdk-REPPOIBD.js +24 -0
- package/dist/promote-command-PCWT5N6S.js +140 -0
- package/dist/prompt-II3F3LND.js +27 -0
- package/dist/publish-command-TTDUUJI5.js +414 -0
- package/dist/pull-command-VBO7C4S3.js +161 -0
- package/dist/pull-superseded-GJWLG7XO.js +20 -0
- package/dist/release-command-N4ZPKMHR.js +146 -0
- package/dist/release-ns-command-3KFDMZ6G.js +1745 -0
- package/dist/response-HlXahv4K.d.ts +925 -0
- package/dist/retry-command-AS5EVREX.js +34 -0
- package/dist/routes-manifest-SNWUVBT6.js +43 -0
- package/dist/runtime-XETHVF33.js +14 -0
- package/dist/scaffold.d.ts +1911 -0
- package/dist/scaffold.js +107 -0
- package/dist/secrets-file-LVZD6IIM.js +56 -0
- package/dist/secrets-fill-command-TTCPSVXN.js +218 -0
- package/dist/seed-VU7C3QYS.js +46 -0
- package/dist/sent-writes-N7X4OVXT.js +22 -0
- package/dist/source-resolve-S2XA3XZF.js +83 -0
- package/dist/static-host-B2Y6AG3W.js +352 -0
- package/dist/status-command-RUDXTF7I.js +554 -0
- package/dist/store-6VNCD4D7.js +20 -0
- package/dist/store-ARM6VMO5.js +98 -0
- package/dist/store-DuYfzKlW.d.ts +3144 -0
- package/dist/suggest-6FOOYKHG.js +12 -0
- package/dist/sveltekit.d.ts +154 -0
- package/dist/sveltekit.js +109 -0
- package/dist/sync-contributions-UCEROO26.js +415 -0
- package/dist/table-ENAVMITR.js +42 -0
- package/dist/tables-command-GUPXXSB5.js +156 -0
- package/dist/tenant-IIHPOHGP.js +42 -0
- package/dist/tenant-command-D5HGI2X4.js +965 -0
- package/dist/test-command-TIKZMDPZ.js +476 -0
- package/dist/tests-BGGIOK3L.js +26 -0
- package/dist/token-NICJUTMT.js +72 -0
- package/dist/toolchain-hooks-GHKLUJOH.js +96 -0
- package/dist/toolchain-modules-5EAZUONB.js +24 -0
- package/dist/tracked-backend-EIBUMDZA.js +80 -0
- package/dist/update-check-2W2NLDTC.js +136 -0
- package/dist/upgrade-command-QHNFWM7O.js +531 -0
- package/dist/verify-rollout-DRCK5TDD.js +61 -0
- package/dist/whoami-command-STGINTXH.js +51 -0
- package/dist/workspace-2CLHU2LK.js +93 -0
- package/dist/workspace-binding-56PIIPCP.js +54 -0
- package/dist/workspace-command-JEXQETJD.js +535 -0
- package/dist/workspace-env-F2RFMSC5.js +70 -0
- package/dist/workspace-export-OJSH2HIW.js +21 -0
- package/dist/writable-VA7OISSK.js +25 -0
- package/dist/xano-ICFUSPFR.js +42 -0
- package/dist/xanosdk-import-GN42TO2B.js +56 -0
- package/dist/xanosdk-project-S6DXJONQ.js +41 -0
- package/dist/xdo-CxDr2Q1C.d.ts +1371 -0
- package/guides/README.md +33 -0
- package/guides/authoring.md +921 -0
- package/guides/cli.md +401 -0
- package/guides/codegen.md +177 -0
- package/guides/codes.md +404 -0
- package/guides/coverage.md +74 -0
- package/guides/deploying.md +1525 -0
- package/guides/environment.md +400 -0
- package/guides/marketplace.md +415 -0
- package/guides/module-contract.md +558 -0
- package/guides/object-kinds.md +433 -0
- package/guides/project-structure.md +43 -0
- package/guides/scaffold.md +351 -0
- package/guides/typed-frontend.md +242 -0
- package/llms/client.md +63 -0
- package/llms/errors.md +56 -0
- package/llms/fields.md +176 -0
- package/llms/filters.md +183 -0
- package/llms/kinds-agent-mcp.md +30 -0
- package/llms/kinds-core.md +100 -0
- package/llms/kinds-knowledge.md +20 -0
- package/llms/kinds-realtime.md +76 -0
- package/llms/lambda.md +94 -0
- package/llms/legacy.md +44 -0
- package/llms/lock.md +40 -0
- package/llms/object-kinds.md +34 -0
- package/llms/statements-calls.md +62 -0
- package/llms/statements-catalog.md +320 -0
- package/llms/statements-data.md +66 -0
- package/llms/statements-runtime.md +68 -0
- package/llms/tests.md +59 -0
- package/llms/toolchain.md +16 -0
- package/llms/triggers.md +36 -0
- package/llms/values.md +29 -0
- package/llms-full.txt +2085 -0
- package/llms.txt +493 -0
- package/manifest.json +11224 -0
- package/package.json +162 -0
- package/types/lambda-globals.d.ts +129 -0
|
@@ -0,0 +1,2385 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isAwaited,
|
|
3
|
+
microserviceLine,
|
|
4
|
+
readyRatio,
|
|
5
|
+
waitForMicroservices
|
|
6
|
+
} from "./chunk-FXRQOWTE.js";
|
|
7
|
+
import {
|
|
8
|
+
passwordHashesWarning,
|
|
9
|
+
passwordOrigin
|
|
10
|
+
} from "./chunk-AWEIIR2U.js";
|
|
11
|
+
import {
|
|
12
|
+
openBrowser
|
|
13
|
+
} from "./chunk-FQOF7E2E.js";
|
|
14
|
+
import {
|
|
15
|
+
EXIT_STATIC_FAILED,
|
|
16
|
+
assertStaticHostExists,
|
|
17
|
+
noteStaticTakenDown,
|
|
18
|
+
readFrontendsAfterFailure,
|
|
19
|
+
readStaticTeardown,
|
|
20
|
+
recheckDoubtedFrontend,
|
|
21
|
+
refuseStaticHostAReplaceClears,
|
|
22
|
+
staticAfterFailedReplace,
|
|
23
|
+
staticRemovedField,
|
|
24
|
+
staticRetryCommand,
|
|
25
|
+
staticRetryHint,
|
|
26
|
+
unknownStaticOutcome,
|
|
27
|
+
warnStaticTeardown
|
|
28
|
+
} from "./chunk-FQJ2TTHP.js";
|
|
29
|
+
import {
|
|
30
|
+
HostedFilesUnresolvedError,
|
|
31
|
+
ImportNotSentError,
|
|
32
|
+
XanoSdkImportRefusal,
|
|
33
|
+
xanosdkImport
|
|
34
|
+
} from "./chunk-WQFNASK4.js";
|
|
35
|
+
import {
|
|
36
|
+
archiveHostedFiles,
|
|
37
|
+
decodeWorkspaceArchive,
|
|
38
|
+
encodeWorkspaceArchive
|
|
39
|
+
} from "./chunk-3DRXWLJ5.js";
|
|
40
|
+
import {
|
|
41
|
+
DOC_TOKEN_REMEDY,
|
|
42
|
+
assertBundleFile,
|
|
43
|
+
assertBundleInput,
|
|
44
|
+
assertEntryFile,
|
|
45
|
+
assertValueFiles,
|
|
46
|
+
compileValueFlagKinds,
|
|
47
|
+
loadBundleText,
|
|
48
|
+
missingLockDir,
|
|
49
|
+
refuseCompileValueFlags,
|
|
50
|
+
refuseValueFlagsBesideBundle
|
|
51
|
+
} from "./chunk-ETO6LGLD.js";
|
|
52
|
+
import {
|
|
53
|
+
recordDeployed
|
|
54
|
+
} from "./chunk-LPECSJ2J.js";
|
|
55
|
+
import {
|
|
56
|
+
lookupEphemeral
|
|
57
|
+
} from "./chunk-QCNYHBL7.js";
|
|
58
|
+
import {
|
|
59
|
+
getAccessToken
|
|
60
|
+
} from "./chunk-ET4Y4XCB.js";
|
|
61
|
+
import {
|
|
62
|
+
otherCredentialRefusal
|
|
63
|
+
} from "./chunk-SWBF2LTK.js";
|
|
64
|
+
import {
|
|
65
|
+
engineNameForProject,
|
|
66
|
+
getEngineRecord
|
|
67
|
+
} from "./chunk-3M4LFYNQ.js";
|
|
68
|
+
import {
|
|
69
|
+
environmentKey,
|
|
70
|
+
ephemeralLandedOn,
|
|
71
|
+
ephemeralLandingKey,
|
|
72
|
+
ephemeralStatePath,
|
|
73
|
+
getEnvironment,
|
|
74
|
+
markEnvironmentFilled,
|
|
75
|
+
readEphemeralState,
|
|
76
|
+
recordStaticUrl,
|
|
77
|
+
setEnvironment
|
|
78
|
+
} from "./chunk-5QU2P5CA.js";
|
|
79
|
+
import {
|
|
80
|
+
selectDeployArm
|
|
81
|
+
} from "./chunk-ZYJP6BOO.js";
|
|
82
|
+
import {
|
|
83
|
+
createEphemeral,
|
|
84
|
+
getEphemeral,
|
|
85
|
+
isEphemeralDisabled,
|
|
86
|
+
isExpired,
|
|
87
|
+
isUnansweredPoll,
|
|
88
|
+
listEphemeral,
|
|
89
|
+
namedEphemeral,
|
|
90
|
+
renameEphemeral,
|
|
91
|
+
waitUntilReady
|
|
92
|
+
} from "./chunk-7G3CTU7I.js";
|
|
93
|
+
import {
|
|
94
|
+
pipedYes,
|
|
95
|
+
retryCommand,
|
|
96
|
+
withheldNote,
|
|
97
|
+
yesRerun
|
|
98
|
+
} from "./chunk-T5KELPAR.js";
|
|
99
|
+
import {
|
|
100
|
+
andList
|
|
101
|
+
} from "./chunk-622HUCZT.js";
|
|
102
|
+
import {
|
|
103
|
+
contextFlags,
|
|
104
|
+
credentialFileOf
|
|
105
|
+
} from "./chunk-NAQDMEHV.js";
|
|
106
|
+
import {
|
|
107
|
+
describeWrite,
|
|
108
|
+
noteLanded
|
|
109
|
+
} from "./chunk-4KHEMC32.js";
|
|
110
|
+
import {
|
|
111
|
+
readLockFile
|
|
112
|
+
} from "./chunk-P2HAGF6H.js";
|
|
113
|
+
import {
|
|
114
|
+
storedColumnsOf,
|
|
115
|
+
withStoredColumns
|
|
116
|
+
} from "./chunk-TLQE7ZN3.js";
|
|
117
|
+
import {
|
|
118
|
+
SENT_AFTERMATH,
|
|
119
|
+
TransportError,
|
|
120
|
+
certificateFailureCode,
|
|
121
|
+
closeSentence,
|
|
122
|
+
withoutReadAftermath
|
|
123
|
+
} from "./chunk-YLDALTWZ.js";
|
|
124
|
+
import {
|
|
125
|
+
DEFAULT_PROFILE
|
|
126
|
+
} from "./chunk-QXTKSOFI.js";
|
|
127
|
+
import {
|
|
128
|
+
CliError,
|
|
129
|
+
EXIT_OUTCOME_UNKNOWN,
|
|
130
|
+
LocalFileNotFoundError,
|
|
131
|
+
UsageError,
|
|
132
|
+
activeOperation,
|
|
133
|
+
backendDestinationPayload,
|
|
134
|
+
blank,
|
|
135
|
+
credentialWriteTarget,
|
|
136
|
+
detail,
|
|
137
|
+
discloseWriteTarget,
|
|
138
|
+
displayPath,
|
|
139
|
+
elapsedSuffix,
|
|
140
|
+
formatExpiration,
|
|
141
|
+
info,
|
|
142
|
+
isMachineOutput,
|
|
143
|
+
link,
|
|
144
|
+
pastePath,
|
|
145
|
+
registerOperation,
|
|
146
|
+
spinner,
|
|
147
|
+
statesOutcomeUnknown,
|
|
148
|
+
step,
|
|
149
|
+
success,
|
|
150
|
+
warn,
|
|
151
|
+
withSpinner,
|
|
152
|
+
writeJson,
|
|
153
|
+
writeTargetPayload
|
|
154
|
+
} from "./chunk-PTHVMXZI.js";
|
|
155
|
+
import {
|
|
156
|
+
codePinnedTables
|
|
157
|
+
} from "./chunk-6MR5TI5V.js";
|
|
158
|
+
import {
|
|
159
|
+
shellQuote
|
|
160
|
+
} from "./chunk-5ZSO4T2G.js";
|
|
161
|
+
import {
|
|
162
|
+
withArticle
|
|
163
|
+
} from "./chunk-36JUQZVQ.js";
|
|
164
|
+
|
|
165
|
+
// src/emit/deploy-command.ts
|
|
166
|
+
import { existsSync, realpathSync } from "fs";
|
|
167
|
+
import { basename, isAbsolute, join, relative, resolve, sep } from "path";
|
|
168
|
+
var EXIT_MICROSERVICE_NOT_READY = 4;
|
|
169
|
+
var EXIT_TESTS_UNREACHABLE = 6;
|
|
170
|
+
function microserviceCheck(args) {
|
|
171
|
+
return { skip: args.skipLiveness, requireReady: args.requireMicroservices };
|
|
172
|
+
}
|
|
173
|
+
function buildStaticEnv(baseUrl, staticEnv) {
|
|
174
|
+
const env = {};
|
|
175
|
+
if (baseUrl) env.XANO_HOST = baseUrl;
|
|
176
|
+
Object.assign(env, staticEnv);
|
|
177
|
+
return env;
|
|
178
|
+
}
|
|
179
|
+
var PUBLISHABLE_KEY_VALUE = /^pk_(test|live)_/;
|
|
180
|
+
var SECRET_KEY_VALUE = /^(sk|rk)_(test|live)_/;
|
|
181
|
+
function warnSecretLookingStaticEnv(staticEnv) {
|
|
182
|
+
const flagged = Object.entries(staticEnv).filter(([key, value]) => {
|
|
183
|
+
if (SECRET_KEY_VALUE.test(value)) return true;
|
|
184
|
+
if (PUBLISHABLE_KEY_VALUE.test(value) || /PUBLIC|PUBLISHABLE/i.test(key)) return false;
|
|
185
|
+
return /SECRET|TOKEN|KEY|PASSWORD|PRIVATE/i.test(key);
|
|
186
|
+
}).map(([key]) => key);
|
|
187
|
+
if (flagged.length === 0) return flagged;
|
|
188
|
+
const one = flagged.length === 1;
|
|
189
|
+
const message = `--static-env ${flagged.join(", ")} ${one ? "looks" : "look"} like ${one ? "a secret" : "secrets"}. Every --static-env value is published in the static site's config, readable by anyone who loads it \u2014 keep secrets in the backend's environment variables instead.`;
|
|
190
|
+
warn(message, "static-env.secret-like");
|
|
191
|
+
return flagged;
|
|
192
|
+
}
|
|
193
|
+
function deriveDisplay(bundleText, cwd, source) {
|
|
194
|
+
if (source !== void 0 && source.startsWith("release:") && source.length > "release:".length) {
|
|
195
|
+
return source.slice("release:".length);
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
const parsed = JSON.parse(bundleText);
|
|
199
|
+
const w = parsed.payload?.workspace;
|
|
200
|
+
const name = Array.isArray(w) ? w[0]?.name : w?.name;
|
|
201
|
+
if (typeof name === "string" && name.trim() !== "") return name;
|
|
202
|
+
} catch {
|
|
203
|
+
}
|
|
204
|
+
return basename(cwd) || "xanosdk-app";
|
|
205
|
+
}
|
|
206
|
+
async function assertNoSeedLeaks(dir, values, opts = {}) {
|
|
207
|
+
const { assertStaticDir, findSeedLeaks, StaticDirError } = await import("./static-host-B2Y6AG3W.js");
|
|
208
|
+
try {
|
|
209
|
+
assertStaticDir(dir, "--static", void 0, opts);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
if (err instanceof StaticDirError) {
|
|
212
|
+
throw new (existsSync(dir) ? UsageError : LocalFileNotFoundError)(`${err.message} Nothing was deployed.`, {
|
|
213
|
+
hintFor: { command: "deploy" }
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
throw err;
|
|
217
|
+
}
|
|
218
|
+
if (values.length === 0) return;
|
|
219
|
+
const unscannable = [
|
|
220
|
+
...new Set(values.filter((v) => v.scannable === false).map((v) => `${v.table}.${v.column}`))
|
|
221
|
+
];
|
|
222
|
+
if (unscannable.length > 0) {
|
|
223
|
+
warn(
|
|
224
|
+
`${unscannable.length} non-public seed column${unscannable.length === 1 ? " holds a value" : "s hold values"} the static scan cannot search for \u2014 too short to match on, or not text at all \u2014 so they were NOT checked. Being unsearchable is not being safe: if any of these is a real credential, shorten the exposure rather than the value.`,
|
|
225
|
+
"static-env.unscannable-seed",
|
|
226
|
+
unscannable
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const leaks = findSeedLeaks(dir, values);
|
|
230
|
+
if (leaks.length === 0) return;
|
|
231
|
+
const columnsByTable = /* @__PURE__ */ new Map();
|
|
232
|
+
for (const l of leaks) columnsByTable.set(l.table, (columnsByTable.get(l.table) ?? /* @__PURE__ */ new Set()).add(l.column));
|
|
233
|
+
const sourceOf = new Map(values.map((v) => [v.table, v.source]));
|
|
234
|
+
const causes = [...columnsByTable.keys()].map((t) => seedLeakCause(t, sourceOf.get(t) ?? "inline"));
|
|
235
|
+
const declarations = [...columnsByTable].map(
|
|
236
|
+
([t, cols]) => ` table "${t}": publicSeed: [${[...cols].map((c) => JSON.stringify(c)).join(", ")}]`
|
|
237
|
+
);
|
|
238
|
+
throw new CliError(
|
|
239
|
+
"SDK_SEED_IN_STATIC",
|
|
240
|
+
`Refusing to publish: the static build in "${dir}" contains seed values from columns your schema marks non-public. These would be served at a public URL.
|
|
241
|
+
${leaks.map((l) => ` ${l.file} \u2014 ${l.table}.${l.column}`).join("\n")}
|
|
242
|
+
|
|
243
|
+
${causes.join("\n")}
|
|
244
|
+
|
|
245
|
+
If these values are deliberately public (a demo login the frontend shows), declare them on the table:
|
|
246
|
+
` + declarations.join("\n"),
|
|
247
|
+
{ details: { leaks } }
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
function seedLeakCause(table, source) {
|
|
251
|
+
switch (source) {
|
|
252
|
+
case "thunk":
|
|
253
|
+
return `"${table}" seeds with \`() => import(...)\`, and a bundler follows that import into the frontend build. Use \`seedFile("./seed.json", import.meta.url)\` instead, then rebuild the frontend.`;
|
|
254
|
+
case "inline":
|
|
255
|
+
return `"${table}" writes its seed rows inline, so a frontend that imports the table definition bundles them. Move them to \`seedFile("./seed.json", import.meta.url)\`, then rebuild the frontend.`;
|
|
256
|
+
case "file":
|
|
257
|
+
return `"${table}" reads its seed with \`seedFile\`, which no bundler follows: the value is in the frontend's own source or something it imports. Remove it there, then rebuild the frontend.`;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function ephemeralParent(auth) {
|
|
261
|
+
return credentialWriteTarget(auth);
|
|
262
|
+
}
|
|
263
|
+
async function runDeployedTests(auth, summary, args) {
|
|
264
|
+
const settled = [];
|
|
265
|
+
const notRun = [];
|
|
266
|
+
try {
|
|
267
|
+
return await runTestsOrThrow(auth, summary, args, settled, notRun);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
const { isSuiteUnreachable, suiteUnreachableCheck } = await import("./test-command-TIKZMDPZ.js");
|
|
270
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
271
|
+
const reason = isSuiteUnreachable(err) ? `${message.split("\n")[0] ?? message}
|
|
272
|
+
${suiteUnreachableCheck(
|
|
273
|
+
summary.localEngine !== void 0 ? { kind: "local-engine", env: summary.localEngine.name } : { kind: "ephemeral", env: summary.ephemeral?.name ?? null },
|
|
274
|
+
args
|
|
275
|
+
)}.` : withoutReadAftermath(message);
|
|
276
|
+
const again = testRunAllCommand(summary, args);
|
|
277
|
+
warn("The deploy stands, but its tests could not be run:", "deploy.tests-not-run", [
|
|
278
|
+
reason,
|
|
279
|
+
...again !== void 0 ? [`Run them again once it is reachable with \`${again}\` \u2014 the deploy itself does not need repeating.`] : []
|
|
280
|
+
]);
|
|
281
|
+
process.exitCode = EXIT_TESTS_UNREACHABLE;
|
|
282
|
+
return { ...await testSummaryOf(settled, notRun), error: reason, ...again !== void 0 ? { retry: again } : {} };
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
function testRunAllCommand(summary, args) {
|
|
286
|
+
const on = summary.localEngine !== void 0 ? `local-engine:${shellQuote(summary.localEngine.name)}` : summary.ephemeral?.name !== void 0 ? `ephemeral:${shellQuote(summary.ephemeral.name)}` : void 0;
|
|
287
|
+
if (on === void 0) return void 0;
|
|
288
|
+
const kind = args.kind !== void 0 ? ` --kind ${args.kind}` : "";
|
|
289
|
+
const concurrency = args.concurrency !== void 0 ? ` --concurrency ${args.concurrency}` : "";
|
|
290
|
+
const flags = summary.localEngine !== void 0 ? "" : contextFlags(args);
|
|
291
|
+
return `xanosdk test run-all --on ${on}${kind}${concurrency}${flags}`;
|
|
292
|
+
}
|
|
293
|
+
async function testSummaryOf(outcomes, notRun = []) {
|
|
294
|
+
const { suiteCounts, testRow } = await import("./test-command-TIKZMDPZ.js");
|
|
295
|
+
return { ...suiteCounts(outcomes, notRun), tests: outcomes.map(testRow) };
|
|
296
|
+
}
|
|
297
|
+
async function runTestsOrThrow(auth, summary, args, settled, notRun) {
|
|
298
|
+
const { runSuite, printTally, noTestsFound, EXIT_TESTS_FAILED } = await import("./test-command-TIKZMDPZ.js");
|
|
299
|
+
const { qualifiedName } = await import("./tests-BGGIOK3L.js");
|
|
300
|
+
if (summary.url === void 0) {
|
|
301
|
+
warn("Skipped --test: the ephemeral reported no base URL to run against.", "deploy.tests-not-run");
|
|
302
|
+
return void 0;
|
|
303
|
+
}
|
|
304
|
+
const local = summary.localEngine;
|
|
305
|
+
const name = summary.ephemeral?.name;
|
|
306
|
+
const target = local ? { base: summary.url, workspaceId: local.workspaceId, label: `local engine "${local.name}"` } : {
|
|
307
|
+
base: summary.url,
|
|
308
|
+
workspaceId: 1,
|
|
309
|
+
// Named as `test run-all` names it: `ephemeral "x" ("Display")`.
|
|
310
|
+
label: name ? namedEphemeral({ name, display: summary.ephemeral?.display }) : "the ephemeral",
|
|
311
|
+
env: name
|
|
312
|
+
};
|
|
313
|
+
let stopped;
|
|
314
|
+
const outcomes = await runSuite(auth, target, {
|
|
315
|
+
kind: args.kind,
|
|
316
|
+
concurrency: args.concurrency,
|
|
317
|
+
onOutcome: (o) => settled.push(o),
|
|
318
|
+
onUnreachable: (handle, err) => {
|
|
319
|
+
stopped ??= err;
|
|
320
|
+
notRun.push(qualifiedName(handle));
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
if (stopped !== void 0) {
|
|
324
|
+
if (outcomes.length > 0) {
|
|
325
|
+
blank();
|
|
326
|
+
const read = await testSummaryOf(outcomes, notRun);
|
|
327
|
+
printTally(read.passed, read.failed, read.notRun);
|
|
328
|
+
}
|
|
329
|
+
throw stopped;
|
|
330
|
+
}
|
|
331
|
+
const run = await testSummaryOf(outcomes);
|
|
332
|
+
blank();
|
|
333
|
+
if (outcomes.length === 0) detail(await noTestsFound(auth, target, args.kind, " in the deployed environment"));
|
|
334
|
+
else printTally(run.passed, run.failed);
|
|
335
|
+
if (run.failed > 0) process.exitCode = EXIT_TESTS_FAILED;
|
|
336
|
+
return run;
|
|
337
|
+
}
|
|
338
|
+
async function ignoreBackendSecretFilesRead(args) {
|
|
339
|
+
if (args.bundle !== void 0 || args.file === void 0) return;
|
|
340
|
+
const paths = [];
|
|
341
|
+
if (args.envFile === void 0) {
|
|
342
|
+
const { defaultWorkspaceEnvPath } = await import("./workspace-env-F2RFMSC5.js");
|
|
343
|
+
paths.push(defaultWorkspaceEnvPath(args.file));
|
|
344
|
+
}
|
|
345
|
+
if (args.secretsFile === void 0) {
|
|
346
|
+
const { defaultWorkspaceSecretsPath } = await import("./secrets-file-LVZD6IIM.js");
|
|
347
|
+
paths.push(defaultWorkspaceSecretsPath(args.file));
|
|
348
|
+
}
|
|
349
|
+
const { gitSaysIgnored } = await import("./store-ARM6VMO5.js");
|
|
350
|
+
const { ensureSecretPathGitignored, refuseIfTracked, secretPathLabel } = await import("./gitignore-3SJ3WNRG.js");
|
|
351
|
+
for (const path of paths) {
|
|
352
|
+
if (!existsSync(path)) continue;
|
|
353
|
+
if (gitSaysIgnored(path) !== false) continue;
|
|
354
|
+
const label = secretPathLabel(path);
|
|
355
|
+
let tracked = false;
|
|
356
|
+
try {
|
|
357
|
+
refuseIfTracked(path, label);
|
|
358
|
+
} catch {
|
|
359
|
+
tracked = true;
|
|
360
|
+
}
|
|
361
|
+
if (tracked) {
|
|
362
|
+
warn(
|
|
363
|
+
`${label} holds secrets and is tracked by git \u2014 an ignore rule cannot untrack it. Run \`git rm --cached ${label}\` (it stays on disk) and commit that.`,
|
|
364
|
+
"secrets.tracked-by-git"
|
|
365
|
+
);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
warn(`${label} holds secrets and git was not ignoring it.`, "secrets.not-ignored");
|
|
369
|
+
ensureSecretPathGitignored(path);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function recordedLanding(dest, isEphemeral, flags = "") {
|
|
373
|
+
if (dest.kind === "workspace") return `\`xanosdk promote <name>${flags}\``;
|
|
374
|
+
if (isEphemeral) return `\`xanosdk deploy release:<name>${flags}\``;
|
|
375
|
+
return `\`xanosdk tenant deploy ${shellQuote(dest.name)} <name>${flags}\``;
|
|
376
|
+
}
|
|
377
|
+
function storeAsReleaseHint(dest, isEphemeral, typed, flags = "", yes = false, shipsFiles = false) {
|
|
378
|
+
if (shipsFiles) {
|
|
379
|
+
return "A release cannot keep this landing: the source ships stored files (`hostedFile()`), and a release copies no file bytes, so `release create` refuses it. Re-run this deploy to land it again.";
|
|
380
|
+
}
|
|
381
|
+
const landed = "can be listed, exported and landed again";
|
|
382
|
+
const land = recordedLanding(dest, false, `${yes || process.stdin.isTTY !== true ? " --yes" : ""}${flags}`);
|
|
383
|
+
const against = typed.bundle !== void 0 ? ` --bundle ${shellQuote(typed.bundle)}` : typed.file !== void 0 ? ` --entry ${shellQuote(typed.file)}` : "";
|
|
384
|
+
if (dest.kind === "workspace") {
|
|
385
|
+
const branch = dest.branch !== void 0 && dest.branch !== "" ? ` --branch ${shellQuote(dest.branch)}` : "";
|
|
386
|
+
return `\`xanosdk release create <name> --from workspace${branch}${against}${flags}\` stores what the workspace now runs as a release that ${landed} (${land}).`;
|
|
387
|
+
}
|
|
388
|
+
if (isEphemeral) {
|
|
389
|
+
return `\`xanosdk release create <name> --from ephemeral:${shellQuote(dest.name)}${against}${flags}\` stores what it now runs as a release that ${landed}.`;
|
|
390
|
+
}
|
|
391
|
+
const source = typed.bundle !== void 0 ? ` --bundle ${shellQuote(typed.bundle)}` : typed.file !== void 0 ? ` ${shellQuote(typed.file)}` : "";
|
|
392
|
+
return `A release is cut from a backend that ran on the instance, and a standard tenant cannot be cut from. To keep this source as one that ${landed}: \`xanosdk deploy${source}${flags}\` (to this project's ephemeral), then \`xanosdk release create <name>${against}${flags}\`, then ${land} lands it here \u2014 it REPLACES what the tenant runs with that release, so anything else merged onto it (another project's objects) is dropped.`;
|
|
393
|
+
}
|
|
394
|
+
function tenantWithoutWorkspace(tenant, named, typed, args) {
|
|
395
|
+
const flags = contextFlags(args);
|
|
396
|
+
const yes = pipedYes(args);
|
|
397
|
+
const t = shellQuote(tenant);
|
|
398
|
+
const source = typed.bundle !== void 0 ? ` --bundle ${shellQuote(typed.bundle)}` : typed.file !== void 0 ? ` ${shellQuote(typed.file)}` : "";
|
|
399
|
+
const against = typed.bundle !== void 0 ? ` --bundle ${shellQuote(typed.bundle)}` : typed.file !== void 0 ? ` --entry ${shellQuote(typed.file)}` : "";
|
|
400
|
+
const land = (release) => `\`xanosdk tenant deploy ${t} ${release}${yes}${flags}\``;
|
|
401
|
+
const seed = args.seed ? " --seed" : "";
|
|
402
|
+
const rerun = retryCommand(args, {
|
|
403
|
+
add: args.dryRun || yes === "" ? [] : ["--yes"],
|
|
404
|
+
command: `deploy --to ${shellQuote(`tenant:${tenant}`)}`
|
|
405
|
+
});
|
|
406
|
+
return new CliError(
|
|
407
|
+
"SDK_USAGE",
|
|
408
|
+
`${named} has no workspace yet, so there is nothing for \`deploy --to\` to merge into or preview against \u2014 a tenant on its own domain gets its workspace when its first release lands. Nothing was written.
|
|
409
|
+
Land a release on it first: ${land("<release>")} (\`xanosdk release list${flags}\` names them). To land this source, deploy it to this project's ephemeral (\`xanosdk deploy${source}${flags}\`), cut it (\`xanosdk release create <name>${against}${seed}${flags}\`), and land that: ${land("<name>")}.
|
|
410
|
+
After that, \`${rerun.command}\` ${args.dryRun ? "previews merging" : "merges"} onto it.${withheldNote(rerun.withheld)}`,
|
|
411
|
+
{ details: { reason: "tenant-has-no-workspace", tenant } }
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
function shipsStoredFiles(bundle) {
|
|
415
|
+
const payload = bundle?.payload;
|
|
416
|
+
return Array.isArray(payload?.vault) && payload.vault.length > 0;
|
|
417
|
+
}
|
|
418
|
+
async function soleTrackedEphemeral() {
|
|
419
|
+
const { readEphemeralState: readEphemeralState2 } = await import("./ephemeral-state-PYCBSOGX.js");
|
|
420
|
+
const names = new Set(Object.values(readEphemeralState2(process.cwd()).environments).map((r) => r.name));
|
|
421
|
+
return names.size === 1 ? [...names][0] : void 0;
|
|
422
|
+
}
|
|
423
|
+
async function runDeployToDestination(args, to) {
|
|
424
|
+
const { requireBackendSlot, parseSlot } = await import("./backend-slot-7QXW2QYC.js");
|
|
425
|
+
const { resolveSource, describeBackend } = await import("./source-resolve-S2XA3XZF.js");
|
|
426
|
+
const { looksLikeSource, resolveProjectEntry } = await import("./deploy-source-3W45CXMP.js");
|
|
427
|
+
const dest = parseSlot(requireBackendSlot("deploy", void 0, "to"), to, to.trim() === "ephemeral" ? await soleTrackedEphemeral() : void 0);
|
|
428
|
+
if (dest.kind !== "workspace" && dest.kind !== "tenant") {
|
|
429
|
+
throw new Error(`Internal: \`deploy --to\` parsed ${withArticle(dest.kind)}, which its slot does not accept.`);
|
|
430
|
+
}
|
|
431
|
+
if (args.file !== void 0 && looksLikeSource(args.file)) {
|
|
432
|
+
const flags = contextFlags(args);
|
|
433
|
+
const release = /^release:(.*)$/.exec(args.file)?.[1];
|
|
434
|
+
const releaseWord = release === void 0 || release === "" ? "<name>" : shellQuote(release);
|
|
435
|
+
const { pipedYes: pipedYes2 } = await import("./retry-command-AS5EVREX.js");
|
|
436
|
+
const yes = args.dryRun === true ? "" : pipedYes2(args);
|
|
437
|
+
const tenantLanding = `\`xanosdk tenant deploy ${dest.kind === "tenant" ? shellQuote(dest.name) : "<tenant>"} ${releaseWord}${yes}${flags}\``;
|
|
438
|
+
const landing = `${args.branch === void 0 ? "" : ` --branch ${shellQuote(args.branch)}`}${args.setLive === true ? " --set-live" : ""}`;
|
|
439
|
+
const promote = `\`xanosdk promote ${releaseWord}${landing}${yes}${flags}\``;
|
|
440
|
+
const advice = /^release:/.test(args.file) ? `A release already has a home on the server, so landing it is ` + (dest.kind === "tenant" ? `${tenantLanding} (or ${promote} for your workspace)` : `${promote} (or ${tenantLanding})`) + ` \u2014 not a deploy. \`--to\` is for code that has no release yet.` + (args.dryRun === true ? ` Neither takes \`--dry-run\`: without \`--yes\`, each shows what it would land and asks first.` : ``) : `\`--to\` merges a build compiled HERE, and "${args.file}" already exists on a server. Pull it into this project first (\`xanosdk pull ${shellQuote(args.file)}${flags}\`) and deploy that, or cut a release from it and promote that.`;
|
|
441
|
+
throw new UsageError(advice, { hintFor: { command: "deploy" } });
|
|
442
|
+
}
|
|
443
|
+
refuseValueFlagsBesideBundle(args, { command: "deploy" });
|
|
444
|
+
const typed = { file: args.file, bundle: args.bundle };
|
|
445
|
+
if (args.file === void 0 && args.bundle === void 0) {
|
|
446
|
+
const entry = resolveProjectEntry(process.cwd());
|
|
447
|
+
if (entry !== void 0) args = { ...args, file: entry };
|
|
448
|
+
}
|
|
449
|
+
assertBundleFile(args, { command: "deploy" });
|
|
450
|
+
assertEntryFile(args);
|
|
451
|
+
assertValueFiles(args, { command: "deploy" });
|
|
452
|
+
refuseMissingLockDir(args);
|
|
453
|
+
refuseMissingStaticDir(args);
|
|
454
|
+
const resolved = await resolveSource(dest, async () => {
|
|
455
|
+
const auth = await getAccessToken(args);
|
|
456
|
+
await noteRunScopeFor(auth);
|
|
457
|
+
return auth;
|
|
458
|
+
}, { workspaceless: "any" });
|
|
459
|
+
const isEphemeral = dest.kind === "tenant" && resolved.target.tenantType === "ephemeral";
|
|
460
|
+
const noun = isEphemeral ? "ephemeral" : "tenant";
|
|
461
|
+
if (dest.kind === "tenant") {
|
|
462
|
+
const branchy = [
|
|
463
|
+
args.branch !== void 0 ? "--branch" : void 0,
|
|
464
|
+
args.setLive ? "--set-live" : void 0,
|
|
465
|
+
args.backupBranch !== void 0 ? "--backup-branch" : void 0
|
|
466
|
+
].filter((f) => f !== void 0);
|
|
467
|
+
if (branchy.length > 0) {
|
|
468
|
+
throw new UsageError(
|
|
469
|
+
`${andList(branchy)} ${branchy.length === 1 ? "scopes" : "scope"} a branch, and ${isEphemeral ? "an ephemeral" : "a tenant"} has none. \`--to workspace\` is the destination that has them.`,
|
|
470
|
+
{ hintFor: { command: "deploy" } }
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
const named = describeBackend(resolved);
|
|
475
|
+
if (dest.kind === "tenant" && !isEphemeral && resolved.backend.kind === "hosted") {
|
|
476
|
+
const { tenantHasWorkspace } = await import("./tenant-IIHPOHGP.js");
|
|
477
|
+
if (!await tenantHasWorkspace(resolved.backend.auth, resolved.target.base)) {
|
|
478
|
+
throw tenantWithoutWorkspace(dest.name, named, typed, args);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if (!args.dryRun) {
|
|
482
|
+
warn(`Writing to ${named}${isEphemeral ? "" : " \u2014 this is not an ephemeral"}.`, isEphemeral ? "deploy.ephemeral-write" : "deploy.tenant-write");
|
|
483
|
+
}
|
|
484
|
+
const afterLanding = (landedBundle) => {
|
|
485
|
+
const recorded = isEphemeral || !args.noLock;
|
|
486
|
+
const nothing = args.backupBranch !== void 0 && args.backupBranch !== false && dest.kind === "workspace" ? "no release to list, export, or land again \u2014 the backup branch above is what rolls its logic back" : "nothing to list, export, or roll back to";
|
|
487
|
+
detail(
|
|
488
|
+
recorded ? `No release is stored: this run records only what it landed (in ${isEphemeral ? ".xano/ephemeral.json" : "xano.lock"}, for \`--prune\`), so there is ${nothing}.` : `No release is stored, and under \`--no-lock\` nothing records what this run landed, so there is ${nothing}, and a later \`--prune\` has no record to scope it.`
|
|
489
|
+
);
|
|
490
|
+
detail(
|
|
491
|
+
storeAsReleaseHint(
|
|
492
|
+
dest.kind === "workspace" ? { kind: "workspace", ...args.branch !== void 0 ? { branch: args.branch } : {} } : dest,
|
|
493
|
+
isEphemeral,
|
|
494
|
+
typed,
|
|
495
|
+
contextFlags(args),
|
|
496
|
+
args.yes,
|
|
497
|
+
shipsStoredFiles(landedBundle)
|
|
498
|
+
)
|
|
499
|
+
);
|
|
500
|
+
};
|
|
501
|
+
const { runReleaseCommand } = await import("./release-command-N4ZPKMHR.js");
|
|
502
|
+
return runReleaseCommand(args, {
|
|
503
|
+
base: resolved.target.base,
|
|
504
|
+
workspaceId: resolved.target.workspaceId,
|
|
505
|
+
// Carried so the release knows whether this destination HAS branches: the
|
|
506
|
+
// `--replace` branch-deletion guard is a workspace-only question.
|
|
507
|
+
kind: dest.kind,
|
|
508
|
+
// Only a tenant carries a label into the step line. For `--to workspace` the
|
|
509
|
+
// resolved label is "your workspace", which the disclosure would render as
|
|
510
|
+
// "your workspace — host · workspace 42": the same word twice, and no new fact.
|
|
511
|
+
...dest.kind === "tenant" ? { label: named } : {},
|
|
512
|
+
// The machine destination's `kind` and bare `label`: the target's ACTUAL type.
|
|
513
|
+
...dest.kind === "tenant" ? { type: noun, name: resolved.target.label } : {},
|
|
514
|
+
// What people call it, for the headline and the machine destination.
|
|
515
|
+
...dest.kind === "tenant" && resolved.target.display !== void 0 && resolved.target.display !== "" ? { display: resolved.target.display } : {}
|
|
516
|
+
// The credential resolved above, so it is resolved (and its profile notice printed) once.
|
|
517
|
+
}, {
|
|
518
|
+
...resolved.backend.kind === "hosted" ? { auth: resolved.backend.auth } : {},
|
|
519
|
+
afterLanding,
|
|
520
|
+
// A merge whose outcome is unknown is previewed again with this run's own command.
|
|
521
|
+
stateCheck: toStateCheck(args, named)
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
var DESTINATION_ONLY = [
|
|
525
|
+
{ key: "dryRun", flag: "--dry-run", why: "a refresh has no plan to preview \u2014 it replaces the environment, or merges into it with `--keep-data`" },
|
|
526
|
+
{ key: "prune", flag: "--prune", why: "a refresh leaves nothing behind to prune \u2014 a replace rewrites the whole environment" },
|
|
527
|
+
{ key: "replace", flag: "--replace", why: "a refresh already replaces unless `--keep-data` says to merge" },
|
|
528
|
+
{ key: "resetData", flag: "--reset-data", why: "a refresh already replaces the rows unless `--keep-data` keeps them (`--reset` overrides that)" },
|
|
529
|
+
{ key: "seed", flag: "--seed", why: "a refresh already seeds when it replaces and never when `--keep-data` keeps the rows" },
|
|
530
|
+
{ key: "allowSharedSchemaChanges", flag: "--allow-shared-schema-changes", why: "the environment is this project's own, with no shared schema to gate" },
|
|
531
|
+
{ key: "allowBranchDeletion", flag: "--allow-branch-deletion", why: "an ephemeral or local engine has no branches to delete" },
|
|
532
|
+
{ key: "branch", flag: "--branch", why: "an ephemeral or local engine has no branch to name" },
|
|
533
|
+
{ key: "setLive", flag: "--set-live", why: "an ephemeral or local engine has no branch to make live" },
|
|
534
|
+
{ key: "backupBranch", flag: "--backup-branch", why: "an ephemeral or local engine has no branch to back up" }
|
|
535
|
+
];
|
|
536
|
+
function assertNoDestinationFlags(args) {
|
|
537
|
+
const passed = DESTINATION_ONLY.filter((f) => args[f.key] !== void 0 && args[f.key] !== false);
|
|
538
|
+
if (passed.length === 0) return;
|
|
539
|
+
const names = passed.map((f) => f.flag);
|
|
540
|
+
const one = names.length === 1;
|
|
541
|
+
const why = passed.map((f) => one ? f.why : `\`${f.flag}\`: ${f.why}`).join("; ");
|
|
542
|
+
throw new UsageError(
|
|
543
|
+
`${andList(names)} ${one ? "needs" : "need"} a destination, and this deploy names none \u2014 it refreshes this project's own ephemeral or local engine, and ${why}. Add \`--to workspace\` (or \`--to tenant:<name>\`) to reach a real destination, or drop ${one ? "the flag" : "the flags"}.` + (args.keepData ? ` \`--keep-data\` still keeps this environment's rows without them.` : "") + (args.dryRun ? ` ${previewOwnEphemeral(args)}` : ""),
|
|
544
|
+
{ hintFor: { command: "deploy" } }
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
function previewOwnEphemeral(args) {
|
|
548
|
+
let names = [];
|
|
549
|
+
try {
|
|
550
|
+
names = [...new Set(Object.values(readEphemeralState(process.cwd()).environments).map((r) => r.name))];
|
|
551
|
+
} catch {
|
|
552
|
+
}
|
|
553
|
+
const target = names.length === 1 ? `tenant:${names[0]}` : "tenant:<ephemeral>";
|
|
554
|
+
const entry = args.file === void 0 ? "" : ` ${shellQuote(pastePath(args.file))}`;
|
|
555
|
+
return `To preview a merge into this project's ephemeral, run \`xanosdk deploy${entry} --to ${target} --dry-run${contextFlags(args)}\`` + (names.length === 1 ? "." : " (`xanosdk ephemeral list` names it).");
|
|
556
|
+
}
|
|
557
|
+
var EPHEMERAL_ONLY = [
|
|
558
|
+
{ flag: "--name", passed: (a) => a.name !== void 0 },
|
|
559
|
+
{ flag: "--expires-hours", passed: (a) => a.expiresHours !== void 0 },
|
|
560
|
+
{ flag: "--reset", passed: (a) => a.reset },
|
|
561
|
+
{ flag: "--test", passed: (a) => a.test },
|
|
562
|
+
{ flag: "--kind", passed: (a) => a.kind !== void 0 },
|
|
563
|
+
{ flag: "--concurrency", passed: (a) => a.concurrency !== void 0 },
|
|
564
|
+
{ flag: "--no-dev-env", passed: (a) => a.noDevEnv },
|
|
565
|
+
{ flag: "--require-microservices", passed: (a) => a.requireMicroservices },
|
|
566
|
+
{ flag: "--open", passed: (a) => a.open }
|
|
567
|
+
];
|
|
568
|
+
var TEST_FLAGS = /* @__PURE__ */ new Set(["--test", "--kind", "--concurrency"]);
|
|
569
|
+
function assertNoEphemeralFlags(args) {
|
|
570
|
+
if (args.to === void 0) return;
|
|
571
|
+
const names = EPHEMERAL_ONLY.filter((f) => f.passed(args)).map((f) => f.flag);
|
|
572
|
+
if (names.length > 0) {
|
|
573
|
+
const one = names.length === 1;
|
|
574
|
+
const testing = names.filter((n) => TEST_FLAGS.has(n));
|
|
575
|
+
const describing = names.filter((n) => !TEST_FLAGS.has(n));
|
|
576
|
+
const what = [
|
|
577
|
+
...describing.length > 0 ? [`${andList(describing)} ${describing.length === 1 ? "describes" : "describe"} the ephemeral a deploy with no \`--to\` refreshes`] : [],
|
|
578
|
+
...testing.length > 0 ? [
|
|
579
|
+
testing.includes("--test") ? `${andList(testing)} ${testing.length === 1 ? "runs" : "run"} the ephemeral's tests after a deploy with no \`--to\`` : `${andList(testing)} ${testing.length === 1 ? "shapes" : "shape"} the test run \`--test\` makes on the ephemeral after a deploy with no \`--to\``
|
|
580
|
+
] : []
|
|
581
|
+
].join(", and ");
|
|
582
|
+
const kind = args.kind !== void 0 ? ` --kind ${args.kind}` : "";
|
|
583
|
+
const concurrency = args.concurrency !== void 0 ? ` --concurrency ${args.concurrency}` : "";
|
|
584
|
+
const testThere = testing.length > 0 ? ` To test ${args.to === "workspace" ? "the workspace" : args.to}, run \`xanosdk test run-all --on ${shellQuote(args.to)}${kind}${concurrency}${contextFlags(args)}\` once the deploy has landed.` : "";
|
|
585
|
+
throw new UsageError(
|
|
586
|
+
`${what}; \`--to\` ${args.replace ? "replaces" : "merges into"} a backend that already exists \u2014 so ${one ? "it is" : "they are"} refused rather than quietly skipped. Nothing was deployed. Drop ${one ? "it" : "them"}, or drop \`--to\` to deploy to this project's ephemeral.${testThere}`,
|
|
587
|
+
{ hintFor: { command: "deploy" } }
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
if (args.static !== void 0 && args.dryRun) {
|
|
591
|
+
throw new UsageError(
|
|
592
|
+
`\`--static\` publishes a frontend, and \`--dry-run\` writes nothing \u2014 so the preview would say nothing about it. Drop \`--static\` to preview the ${args.replace ? "replace" : "merge"}, or \`--dry-run\` to publish.`,
|
|
593
|
+
{ hintFor: { command: "deploy" } }
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
if (args.skipLiveness && args.static === void 0) {
|
|
597
|
+
throw new UsageError(
|
|
598
|
+
"`--skip-liveness` skips `--static`'s rollout check under `--to`, and this deploy publishes no frontend \u2014 so it would change nothing. Drop it, or add `--static <dir>`.",
|
|
599
|
+
{ hintFor: { command: "deploy" } }
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function resolveDeployDestination(args) {
|
|
604
|
+
return args.localEngine ? { kind: "local-engine", downloadUrl: args.localEngineUrl } : { kind: "ephemeral" };
|
|
605
|
+
}
|
|
606
|
+
async function assertLocalEngineUsable(args, dest) {
|
|
607
|
+
if (dest.kind !== "local-engine") return;
|
|
608
|
+
if (args.to !== void 0) {
|
|
609
|
+
throw new UsageError(
|
|
610
|
+
"`--local-engine` and `--to` are two destinations, and a deploy has one. Drop whichever is not meant: `--local-engine` runs this workspace on an engine on this machine, `--to` merges it into a backend that already exists. Either one alone still works exactly as it did.",
|
|
611
|
+
{ hintFor: { command: "deploy" } }
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
const ephemeralOnly = [
|
|
615
|
+
...args.name !== void 0 ? ["--name"] : [],
|
|
616
|
+
...args.expiresHours !== void 0 ? ["--expires-hours"] : []
|
|
617
|
+
];
|
|
618
|
+
if (ephemeralOnly.length > 0) {
|
|
619
|
+
const one = ephemeralOnly.length === 1;
|
|
620
|
+
throw new UsageError(
|
|
621
|
+
`${andList(ephemeralOnly)} ${one ? "describes" : "describe"} an ephemeral, and a local engine has no display name or expiry, so ${one ? "it is" : "they are"} refused rather than quietly skipped. Drop ${one ? "it" : "them"}, or drop \`--local-engine\` to deploy to an ephemeral.`,
|
|
622
|
+
{ hintFor: { command: "deploy" } }
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
const { engineSourceRefusal, MISSING_ARCHIVE, resolveEnginePlatform, SUPPORTED_PLATFORMS } = await import("./local-engine-config-5U3SC434.js");
|
|
626
|
+
const sourceRefusal = engineSourceRefusal(dest.downloadUrl);
|
|
627
|
+
if (sourceRefusal !== void 0) {
|
|
628
|
+
const Refusal = sourceRefusal.startsWith(MISSING_ARCHIVE) ? LocalFileNotFoundError : UsageError;
|
|
629
|
+
throw new Refusal(sourceRefusal, { hintFor: { command: "deploy" } });
|
|
630
|
+
}
|
|
631
|
+
if (resolveEnginePlatform() === void 0) {
|
|
632
|
+
throw new Error(
|
|
633
|
+
`There is no local engine build for ${process.platform} ${process.arch} \u2014 it runs on ${SUPPORTED_PLATFORMS.join(", ")} and nothing else, so there is nothing for \`--local-engine\` to run here. Deploy from one of those machines, or drop the flag: \`xanosdk deploy\` reaches an ephemeral from anywhere.`
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
async function deployToLocalEngine(args, dest, sourceCredential, ctx) {
|
|
638
|
+
const { startedAt } = ctx;
|
|
639
|
+
const dir = process.cwd();
|
|
640
|
+
const fetchedSource = sourceCredential !== void 0 && args.file !== void 0 ? { credential: sourceCredential, spec: args.file } : void 0;
|
|
641
|
+
const { discoverToolchainPlugins } = await import("./toolchain-modules-5EAZUONB.js");
|
|
642
|
+
const toolchain = await discoverToolchainPlugins(dir, { frozen: args.frozenLock === true });
|
|
643
|
+
const { archive, shownSource, bundleObject, bundleText, content, passwordColumns, classifiedLock, commitLock, nonPublicSeedValues } = fetchedSource ? await import("./deploy-source-3W45CXMP.js").then(async (m) => {
|
|
644
|
+
const f = await m.fetchSourceArchive(fetchedSource.credential, fetchedSource.spec, dir);
|
|
645
|
+
const decoded = decodeWorkspaceArchive(f.archive);
|
|
646
|
+
noteReleaseCarriesNoEnv(fetchedSource.spec, m.gatedApiGroups(decoded));
|
|
647
|
+
return {
|
|
648
|
+
archive: f.archive,
|
|
649
|
+
source: f.provenance,
|
|
650
|
+
// The headline's name for it: the handle, and beside it what people call it.
|
|
651
|
+
shownSource: shownSourceOf(f),
|
|
652
|
+
bundleObject: decoded,
|
|
653
|
+
// Only a merge reads the text, so it is serialized there, on demand.
|
|
654
|
+
bundleText: void 0,
|
|
655
|
+
// The seed rows a release carries, read only to name what a merge leaves unwritten.
|
|
656
|
+
content: m.archiveSeedContent(f.archive),
|
|
657
|
+
passwordColumns: m.passwordSeedColumns(f.archive),
|
|
658
|
+
classifiedLock: void 0,
|
|
659
|
+
commitLock: void 0,
|
|
660
|
+
// A fetched backend carries no seed values to look for in a frontend.
|
|
661
|
+
nonPublicSeedValues: []
|
|
662
|
+
};
|
|
663
|
+
}) : (
|
|
664
|
+
// The lock write waits for the import to land (a rename the lock was
|
|
665
|
+
// not told about, an engine that cannot apply the merge, …): a refused or
|
|
666
|
+
// failed deploy leaves `xano.lock` as it found it (E2E passes 26, 28).
|
|
667
|
+
await loadBundleText(args, { command: "deploy" }, { withSeed: true, deferLockWrite: true }).then((l) => ({
|
|
668
|
+
archive: encodeWorkspaceArchive(l.bundle, [...l.content, ...l.files]),
|
|
669
|
+
source: l.source,
|
|
670
|
+
shownSource: shownInput(l.source),
|
|
671
|
+
bundleObject: l.bundleObject ?? JSON.parse(l.bundle),
|
|
672
|
+
bundleText: l.bundle,
|
|
673
|
+
content: l.content,
|
|
674
|
+
passwordColumns: [],
|
|
675
|
+
classifiedLock: l.classifiedLock,
|
|
676
|
+
commitLock: l.commitLock,
|
|
677
|
+
nonPublicSeedValues: l.nonPublicSeedValues
|
|
678
|
+
}))
|
|
679
|
+
);
|
|
680
|
+
if (fetchedSource === void 0) await ignoreBackendSecretFilesRead(args);
|
|
681
|
+
if (args.static !== void 0) {
|
|
682
|
+
await assertNoSeedLeaks(args.static, nonPublicSeedValues, { serverRendered: true });
|
|
683
|
+
}
|
|
684
|
+
step(`Deploying ${shownSource} \u2192 local engine`);
|
|
685
|
+
{
|
|
686
|
+
const { runBundleHooks } = await import("./toolchain-hooks-GHKLUJOH.js");
|
|
687
|
+
const { readVersion } = await import("./cli.js");
|
|
688
|
+
await runBundleHooks(
|
|
689
|
+
toolchain.loaded,
|
|
690
|
+
{
|
|
691
|
+
bundle: bundleObject,
|
|
692
|
+
// Absent on the fetched-source branch, which is the documented signal
|
|
693
|
+
// for "the project did not compile this" — it lets a plugin decline
|
|
694
|
+
// rather than render a remote backend over this repo's own tree.
|
|
695
|
+
entry: fetchedSource ? void 0 : args.file,
|
|
696
|
+
cwd: dir,
|
|
697
|
+
command: "deploy",
|
|
698
|
+
frozen: args.frozenLock === true,
|
|
699
|
+
sdkVersion: readVersion()
|
|
700
|
+
},
|
|
701
|
+
"; nothing was deployed"
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
const { chooseLocalEngine, explainOverrideFailure, pinAfterAcquire } = await import("./local-engine-choice-CH5X3W7E.js");
|
|
705
|
+
const choice = await chooseLocalEngine(dest.downloadUrl, dir, isMachineOutput(args));
|
|
706
|
+
const { ensureProjectEngine } = await import("./local-engine-deploy-PCQ4SNPB.js");
|
|
707
|
+
const pin = pinAfterAcquire(choice, dir);
|
|
708
|
+
const flags = { keepData: args.keepData, reset: args.reset };
|
|
709
|
+
const { engine, reused, previousUrl, hadRecord, priorFilled, engineVersion, replaced, sweptOrphans } = await withSpinner(
|
|
710
|
+
"Preparing the local engine\u2026",
|
|
711
|
+
() => ensureProjectEngine({
|
|
712
|
+
dir,
|
|
713
|
+
source: choice.source,
|
|
714
|
+
rerun: rerunOf(args).command,
|
|
715
|
+
rerunNote: rerunOf(args).note,
|
|
716
|
+
onAcquired: pin.onAcquired,
|
|
717
|
+
carryFilled: (ctx2) => ctx2.reused && ctx2.priorFilled !== void 0 && ctx2.priorFilled.url === ctx2.url
|
|
718
|
+
}).catch((err) => explainOverrideFailure(choice, err))
|
|
719
|
+
);
|
|
720
|
+
pin.announce();
|
|
721
|
+
if (sweptOrphans !== void 0) {
|
|
722
|
+
info(
|
|
723
|
+
`Stopped ${sweptOrphans.length === 1 ? "a process" : `${sweptOrphans.length} processes`} a crashed local engine left running (pid ${sweptOrphans.join(", ")}).`
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
if (replaced !== void 0) {
|
|
727
|
+
info(
|
|
728
|
+
`Replaced local engine ${replaced.name} (${replaced.from ?? "an unrecorded version"} \u2192 ${replaced.to ?? "the resolved engine"}). It starts empty, so this deploy seeds it.`
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
const arm = selectDeployArm({
|
|
732
|
+
...flags,
|
|
733
|
+
created: !reused,
|
|
734
|
+
hadRecord,
|
|
735
|
+
priorFilled,
|
|
736
|
+
url: engine.url
|
|
737
|
+
});
|
|
738
|
+
await refuseEnvClearedByFallbackReplace(arm);
|
|
739
|
+
const target = {
|
|
740
|
+
kind: "local-engine",
|
|
741
|
+
url: engine.url,
|
|
742
|
+
workspaceId: engine.workspaceId
|
|
743
|
+
};
|
|
744
|
+
discloseWriteTarget(target);
|
|
745
|
+
announceArm(arm, "local-engine");
|
|
746
|
+
const bearer = { access_token: engine.token, instance: engine.url };
|
|
747
|
+
let merged;
|
|
748
|
+
let sent = false;
|
|
749
|
+
const sending = () => {
|
|
750
|
+
sent = true;
|
|
751
|
+
if (reused) recordDeployed(dir, "local-engine");
|
|
752
|
+
};
|
|
753
|
+
try {
|
|
754
|
+
if (arm.arm === "merge") {
|
|
755
|
+
const { mergeKeepingData } = await import("./keep-data-merge-7MPLVAVI.js");
|
|
756
|
+
merged = await mergeKeepingData({
|
|
757
|
+
auth: bearer,
|
|
758
|
+
baseUrl: engine.url,
|
|
759
|
+
workspaceId: engine.workspaceId,
|
|
760
|
+
label: `local engine ${engine.name}`,
|
|
761
|
+
bundle: bundleText ?? JSON.stringify(bundleObject),
|
|
762
|
+
content,
|
|
763
|
+
files: archiveHostedFiles(archive),
|
|
764
|
+
// No credential flags: a local engine refuses them.
|
|
765
|
+
stateCheck: `Check what local engine ${engine.name} now holds with \`xanosdk tables local-engine:${shellQuote(engine.name)}\` before retrying.`,
|
|
766
|
+
envSetTo: ` --to ${shellQuote(`local-engine:${engine.name}`)}`,
|
|
767
|
+
kind: "local-engine",
|
|
768
|
+
rerun: rerunOf(args),
|
|
769
|
+
// The lock waits for the import itself, not only its refusals: an
|
|
770
|
+
// engine that cannot apply this merge (a 501) wrote nothing, and a lock
|
|
771
|
+
// rewritten for it would be a diff to commit for a deploy that never
|
|
772
|
+
// landed (E2E pass 28). The identities it would record derive from
|
|
773
|
+
// names, so a lost write re-derives the same ones next run.
|
|
774
|
+
beforeWrite: sending,
|
|
775
|
+
...await mergeGuards(args, classifiedLock, {
|
|
776
|
+
bundle: bundleText ?? JSON.stringify(bundleObject),
|
|
777
|
+
compiled: fetchedSource === void 0 && args.bundle === void 0
|
|
778
|
+
})
|
|
779
|
+
});
|
|
780
|
+
} else {
|
|
781
|
+
sending();
|
|
782
|
+
await withSpinner(
|
|
783
|
+
IMPORTING_LABEL,
|
|
784
|
+
() => xanosdkImport(bearer, {
|
|
785
|
+
baseUrl: engine.url,
|
|
786
|
+
workspaceId: engine.workspaceId,
|
|
787
|
+
archive,
|
|
788
|
+
mode: "replace",
|
|
789
|
+
dryRun: false,
|
|
790
|
+
preserveGuids: true,
|
|
791
|
+
label: `local engine ${engine.name}`
|
|
792
|
+
})
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
} catch (err) {
|
|
796
|
+
if (err instanceof XanoSdkImportRefusal && err.retryable) {
|
|
797
|
+
const { importInProgressError } = await import("./keep-data-merge-7MPLVAVI.js");
|
|
798
|
+
throw importInProgressError(`local engine ${engine.name}`, rerunOf(args), err);
|
|
799
|
+
}
|
|
800
|
+
const { explainUnsupportedEngineImport } = await import("./local-engine-deploy-PCQ4SNPB.js");
|
|
801
|
+
const unsupported = explainUnsupportedEngineImport(err, engineVersion, {
|
|
802
|
+
latest: await latestEngineRelease(err),
|
|
803
|
+
resetRerun: arm.arm === "merge" ? `${rerunOf(args).command} --reset` : void 0
|
|
804
|
+
});
|
|
805
|
+
if (sent) {
|
|
806
|
+
const rerun = rerunOf(args);
|
|
807
|
+
warn(
|
|
808
|
+
"The import failed \u2014 the engine is still running, so nothing needs restarting:",
|
|
809
|
+
"local-engine.import-failed",
|
|
810
|
+
[
|
|
811
|
+
unsupported !== void 0 ? `It is serving at ${engine.url}, holding what it held \u2014 this engine cannot run this import; the error below names what can.` : `It is serving at ${engine.url}. Fix what the error below names, then run \`${rerun.command}\` again.${rerun.note}`,
|
|
812
|
+
`Stop it with \`xanosdk local-engine stop ${engine.name}\`.`
|
|
813
|
+
]
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
throw unsupported ?? err;
|
|
817
|
+
}
|
|
818
|
+
commitLock?.();
|
|
819
|
+
const { markEngineFilled } = await import("./local-engine-state-A4FTRFAF.js");
|
|
820
|
+
markEngineFilled(dir, engine.url);
|
|
821
|
+
const urlChanged = !reused || previousUrl !== engine.url;
|
|
822
|
+
if (urlChanged) {
|
|
823
|
+
success(`Local engine ${engine.name} deployed${elapsedSuffix(startedAt)}`);
|
|
824
|
+
info("New local engine URL:");
|
|
825
|
+
link(engine.url);
|
|
826
|
+
} else {
|
|
827
|
+
const kept = arm.arm === "merge" ? ", keeping its data" : "";
|
|
828
|
+
success(`Redeployed to ${engine.name}${kept}${urlsNote(merged)}${elapsedSuffix(startedAt)}`);
|
|
829
|
+
link(engine.url);
|
|
830
|
+
}
|
|
831
|
+
detail(`Open the builder: ${engine.signInUrl}`);
|
|
832
|
+
detail(`Meta API bearer (the local XANO_META_TOKEN): \`xanosdk local-engine token\``);
|
|
833
|
+
detail(`Stop it with \`xanosdk local-engine stop ${engine.name}\``);
|
|
834
|
+
let staticSummary;
|
|
835
|
+
if (args.static !== void 0) {
|
|
836
|
+
staticSummary = await publishStaticToLocalEngine(args, args.static, engine, engineVersion);
|
|
837
|
+
} else {
|
|
838
|
+
detail(
|
|
839
|
+
"No static site was published. Run your frontend locally against the URL above, or pass `--static <dir>` to have the engine serve a built one."
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
const summary = {
|
|
843
|
+
kind: "local-engine",
|
|
844
|
+
destination: { ...writeTargetPayload(target), label: engine.name, url: engine.url },
|
|
845
|
+
// Nothing chose a credential for this run, so there is no profile to name.
|
|
846
|
+
// Null rather than omitted: the field is non-optional, and a wrapper that
|
|
847
|
+
// reads it everywhere must not have to branch on the destination first.
|
|
848
|
+
profile: null,
|
|
849
|
+
url: engine.url,
|
|
850
|
+
localEngine: {
|
|
851
|
+
name: engine.name,
|
|
852
|
+
workspaceId: engine.workspaceId,
|
|
853
|
+
logPath: engine.logPath,
|
|
854
|
+
...engineVersion === void 0 ? {} : { engineVersion },
|
|
855
|
+
pin: pin.outcome(),
|
|
856
|
+
...choice.override === void 0 ? {} : { override: choice.override }
|
|
857
|
+
},
|
|
858
|
+
created: !reused,
|
|
859
|
+
...dataFields(arm),
|
|
860
|
+
...mergeLossFields(merged),
|
|
861
|
+
...staticSummary === void 0 ? {} : { static: staticSummary }
|
|
862
|
+
};
|
|
863
|
+
if (fetchedSource !== void 0 && summary.data === "replaced") {
|
|
864
|
+
const unverifiable = await notePasswordHashesDoNotTravel(
|
|
865
|
+
fetchedSource.credential,
|
|
866
|
+
fetchedSource.spec,
|
|
867
|
+
passwordColumns,
|
|
868
|
+
void 0
|
|
869
|
+
);
|
|
870
|
+
if (unverifiable !== void 0) summary.passwordHashesUnverifiable = unverifiable;
|
|
871
|
+
}
|
|
872
|
+
noteUnconfigured(summary, toolchain.unconfigured);
|
|
873
|
+
await syncDevEnvAfterDeploy(args, summary, dir);
|
|
874
|
+
if (args.test) summary.testRun = await runDeployedTests(bearer, summary, args);
|
|
875
|
+
if (args.open) openDeployed(summary);
|
|
876
|
+
if (isMachineOutput(args)) writeJson(summary);
|
|
877
|
+
}
|
|
878
|
+
async function publishStaticToLocalEngine(args, dir, engine, engineVersion) {
|
|
879
|
+
const retry = rerunOf(args).command;
|
|
880
|
+
try {
|
|
881
|
+
const summary = await deployStaticTo(
|
|
882
|
+
dir,
|
|
883
|
+
{ access_token: engine.token },
|
|
884
|
+
{ baseUrl: engine.url, workspaceId: engine.workspaceId, label: `local engine ${engine.name}` },
|
|
885
|
+
buildStaticEnv(engine.url, args.staticEnv),
|
|
886
|
+
Object.keys(args.staticEnv).length > 0,
|
|
887
|
+
args.staticHost,
|
|
888
|
+
// Nothing to wait for: the engine serves the build once the upload returns.
|
|
889
|
+
true,
|
|
890
|
+
args.staticRouting,
|
|
891
|
+
"--static",
|
|
892
|
+
{ serverRendered: true }
|
|
893
|
+
);
|
|
894
|
+
if (summary.serverBundle === "uploaded") {
|
|
895
|
+
detail("Server half uploaded: the engine renders the site's dynamic routes.");
|
|
896
|
+
}
|
|
897
|
+
return summary;
|
|
898
|
+
} catch (err) {
|
|
899
|
+
const unknown = err instanceof Error && statesOutcomeUnknown(err.message);
|
|
900
|
+
const tooOld = err.status === 501;
|
|
901
|
+
const which = engineVersion === void 0 ? "This local engine" : `Local engine ${engineVersion}`;
|
|
902
|
+
const message = tooOld ? `${which} cannot host a static site (HTTP 501): static hosting on a local engine came in a later release. \`xanosdk local-engine update\` moves this project to the newest one.` : unknownStaticOutcome(err instanceof Error ? err.message : String(err), unknown);
|
|
903
|
+
warn("The static-host upload failed \u2014 the backend deploy stands:", "static.upload-failed", [
|
|
904
|
+
message,
|
|
905
|
+
`Retry with \`${retry}\` (it re-imports the backend, which is quick on a local engine).`
|
|
906
|
+
]);
|
|
907
|
+
process.exitCode = unknown ? EXIT_OUTCOME_UNKNOWN : EXIT_STATIC_FAILED;
|
|
908
|
+
return { url: void 0, error: message, retry, completed: unknown ? "unknown" : "no" };
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
async function parseDeploySource(raw) {
|
|
912
|
+
const { requireBackendSlot, parseSlot } = await import("./backend-slot-7QXW2QYC.js");
|
|
913
|
+
const source = parseSlot(requireBackendSlot("deploy", void 0, "subject"), raw);
|
|
914
|
+
if (source.kind === "file") throw new Error(`Internal: "${raw}" parsed as a bundle path on the fetch arm.`);
|
|
915
|
+
return source;
|
|
916
|
+
}
|
|
917
|
+
function refuseValueFlagsForSource(args, raw, source) {
|
|
918
|
+
const kinds = compileValueFlagKinds(args);
|
|
919
|
+
const remedy = [
|
|
920
|
+
kinds.env ? `Set an env var on the deployed backend afterwards with \`xanosdk env set NAME${contextFlags(args)}\`.` : void 0,
|
|
921
|
+
kinds.docs ? DOC_TOKEN_REMEDY : void 0
|
|
922
|
+
].filter((r) => r !== void 0).join(" ");
|
|
923
|
+
refuseCompileValueFlags(
|
|
924
|
+
args,
|
|
925
|
+
raw,
|
|
926
|
+
source.kind === "release" ? `a release is already built \u2014 it carries no env vars, and its API groups' documentation tokens are the ones it was cut with \u2014 so there is nothing for them to fill. ${remedy}` : `it is fetched, not compiled, and arrives with the env values and documentation settings it already holds. ${remedy}`,
|
|
927
|
+
{ command: "deploy" }
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
function mergeLossFields(merged) {
|
|
931
|
+
if (merged === void 0) return {};
|
|
932
|
+
return {
|
|
933
|
+
...merged.droppedTables.length > 0 ? { droppedTables: merged.droppedTables } : {},
|
|
934
|
+
...merged.droppedColumns.length > 0 ? { droppedColumns: merged.droppedColumns } : {},
|
|
935
|
+
...merged.pairedColumns.length > 0 ? { pairedColumns: merged.pairedColumns } : {},
|
|
936
|
+
...merged.retypedColumns.length > 0 ? { retypedColumns: merged.retypedColumns } : {},
|
|
937
|
+
...merged.notNullKept.length > 0 ? { notNullKept: merged.notNullKept } : {},
|
|
938
|
+
...merged.narrowedEnums.length > 0 ? { narrowedEnums: merged.narrowedEnums } : {},
|
|
939
|
+
...merged.retargetedRefs.length > 0 ? { retargetedRefs: merged.retargetedRefs } : {},
|
|
940
|
+
...merged.indexChanges.length > 0 ? { indexChanges: merged.indexChanges } : {},
|
|
941
|
+
...merged.notNullTightened.length > 0 ? { notNullTightened: merged.notNullTightened } : {},
|
|
942
|
+
...merged.removed.length > 0 ? { removed: merged.removed } : {},
|
|
943
|
+
...merged.notLanded.length > 0 ? { notLanded: merged.notLanded } : {},
|
|
944
|
+
...merged.unseededTables.length > 0 ? { unseededTables: merged.unseededTables } : {},
|
|
945
|
+
...merged.renamedTables.length > 0 ? { renamedTables: merged.renamedTables } : {},
|
|
946
|
+
...merged.unappliedDocumentation.length > 0 ? { unappliedDocumentation: merged.unappliedDocumentation } : {},
|
|
947
|
+
...merged.keptEnv.length > 0 ? { unchangedEnv: merged.keptEnv } : {},
|
|
948
|
+
...merged.canonicalChanges.length > 0 ? { canonicalChanges: merged.canonicalChanges } : {}
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
function urlsNote(merged) {
|
|
952
|
+
const moved = merged?.canonicalChanges ?? [];
|
|
953
|
+
if (moved.length === 0) return " (URL unchanged)";
|
|
954
|
+
return ` (base URL unchanged; ${moved.map((m) => `${m.kind} "${m.name}" moved ${m.from} \u2192 ${m.to}`).join(", ")})`;
|
|
955
|
+
}
|
|
956
|
+
function noteReleaseCarriesNoEnv(raw, gatedGroups, landing, flags = "") {
|
|
957
|
+
if (!raw.startsWith("release:")) return;
|
|
958
|
+
const release = `Release "${raw.slice("release:".length)}" carries no env vars and no workspace documentation settings`;
|
|
959
|
+
if (landing !== void 0 && !landing.created) {
|
|
960
|
+
detail(
|
|
961
|
+
`${release}; ephemeral ${landing.ephemeral} kept the ones it already had. Change an env var with \`xanosdk env set NAME${flags}\`.`
|
|
962
|
+
);
|
|
963
|
+
} else {
|
|
964
|
+
warn(
|
|
965
|
+
`${release}.`,
|
|
966
|
+
"release.no-env",
|
|
967
|
+
[
|
|
968
|
+
(landing !== void 0 ? "The new environment has every env var unset and the workspace documentation gate off." : "A new environment stood up from it has every env var unset and the workspace documentation gate off; one it refreshes keeps the ones it had.") + ` Set env vars afterwards with \`xanosdk env set NAME${flags}\`.`
|
|
969
|
+
]
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
if (gatedGroups.length === 0) return;
|
|
973
|
+
const one = gatedGroups.length === 1;
|
|
974
|
+
detail(
|
|
975
|
+
`API group${one ? "" : "s"} ${gatedGroups.map((g) => `"${g}"`).join(", ")} keep${one ? "s" : ""} ${one ? "its" : "their"} own documentation gate: it travels with the group, token included \u2014 the token of the backend the release was cut from, which is not necessarily the one \`xanosdk secrets fill\` put in this project. ` + (landing !== void 0 ? `\`xanosdk ephemeral export ${landing.ephemeral}${flags}\` writes it out (owner-only).` : "The landed backend's export carries it.")
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
function shownSourceOf(f) {
|
|
979
|
+
return f.display === void 0 ? f.provenance : `${f.provenance} (${JSON.stringify(f.display)})`;
|
|
980
|
+
}
|
|
981
|
+
function noteLiveCopyCarriesNoRows(raw, display, flags = "") {
|
|
982
|
+
if (raw.startsWith("release:")) return;
|
|
983
|
+
info(
|
|
984
|
+
`Its tables are empty: a copy of "${raw}"${display === void 0 ? "" : ` (${JSON.stringify(display)})`} carries its schema, not its rows. \`xanosdk release create <name> --from ${shellQuote(raw)} --seed${flags}\` cuts a release from it that carries rows.`
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
async function notePasswordHashesDoNotTravel(credential, raw, columns, landedOn) {
|
|
988
|
+
if (!raw.startsWith("release:") || columns.length === 0) return void 0;
|
|
989
|
+
const release = raw.slice("release:".length);
|
|
990
|
+
const origin = await passwordOrigin(await credential(), release);
|
|
991
|
+
if (landedOn !== void 0 && origin?.name === landedOn) return void 0;
|
|
992
|
+
const where = landedOn === void 0 ? "the local engine" : `ephemeral "${landedOn}"`;
|
|
993
|
+
warn(passwordHashesWarning(release, columns, where, "the deploy", origin?.phrase), "seed.password-hashes");
|
|
994
|
+
return [...columns];
|
|
995
|
+
}
|
|
996
|
+
function carriesTables(bundle) {
|
|
997
|
+
try {
|
|
998
|
+
const dbo = JSON.parse(bundle)?.payload?.dbo;
|
|
999
|
+
return Array.isArray(dbo) && dbo.length > 0;
|
|
1000
|
+
} catch {
|
|
1001
|
+
return true;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
function noteBundleFileCarriesNoRows(path, reset) {
|
|
1005
|
+
info(
|
|
1006
|
+
`Its tables are empty: the bundle file "${path}" carries the schema, not seed rows. ` + (reset ? "Deploy the entry file to seed them." : "Deploy the entry file to seed them, or pass `--keep-data` to keep the rows an environment already has.")
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
function refuseSameEngine(source, dir) {
|
|
1010
|
+
if (source?.kind !== "local-engine") return;
|
|
1011
|
+
const own = getEngineRecord(dir)?.name ?? engineNameForProject(dir);
|
|
1012
|
+
if (source.name !== void 0 && source.name !== own) return;
|
|
1013
|
+
throw new UsageError(
|
|
1014
|
+
`"${source.name === void 0 ? "local-engine" : `local-engine:${source.name}`}" is this project's own engine (${own}), which is also where \`--local-engine\` deploys \u2014 the same engine as source and destination. Nothing was downloaded or stopped. \`xanosdk deploy local-engine\` stands its export up as an ephemeral; \`xanosdk deploy local-engine:<name> --local-engine\` copies another engine into this one.`,
|
|
1015
|
+
{ hintFor: { command: "deploy" } }
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
function refuseSameEphemeral(source, auth, dir, flags = "", keepsData = false) {
|
|
1019
|
+
if (source?.kind !== "ephemeral" && source?.kind !== "tenant") return;
|
|
1020
|
+
const record = getEnvironment(readEphemeralState(dir), auth);
|
|
1021
|
+
if (record === void 0) return;
|
|
1022
|
+
const own = record.name;
|
|
1023
|
+
const named = source.name === void 0 || source.name === own || source.name === record.display;
|
|
1024
|
+
if (!named) return;
|
|
1025
|
+
const typed = source.name === void 0 ? source.kind : `${source.kind}:${source.name}`;
|
|
1026
|
+
throw new UsageError(
|
|
1027
|
+
`"${typed}" is this project's own ephemeral (${own}), which is also where \`xanosdk deploy\` lands \u2014 the same environment as source and destination. ` + (keepsData ? `Merging it into itself would land its own export back onto it, so there is nothing to change. ` : `A redeploy of it from itself would replace it with an export that carries no rows, so every table would come back empty. `) + `Nothing was fetched or changed.
|
|
1028
|
+
` + (keepsData ? `Redeploy the project with \`xanosdk deploy --keep-data${flags}\`, or merge another environment in with \`xanosdk deploy ephemeral:<other> --keep-data${flags}\`.` : `Redeploy the project with \`xanosdk deploy${flags}\` (add \`--keep-data\` to keep the rows), or copy another environment in with \`xanosdk deploy ephemeral:<other>${flags}\`.`),
|
|
1029
|
+
{ hintFor: { command: "deploy" } }
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
var DEPENDENT_FLAGS = [
|
|
1033
|
+
{
|
|
1034
|
+
parent: "--test",
|
|
1035
|
+
present: (a) => a.test,
|
|
1036
|
+
flags: [
|
|
1037
|
+
{ flag: "--kind", passed: (a) => a.kind !== void 0 },
|
|
1038
|
+
{ flag: "--concurrency", passed: (a) => a.concurrency !== void 0 }
|
|
1039
|
+
]
|
|
1040
|
+
},
|
|
1041
|
+
{
|
|
1042
|
+
parent: "--static",
|
|
1043
|
+
present: (a) => a.static !== void 0,
|
|
1044
|
+
flags: [
|
|
1045
|
+
{ flag: "--static-host", passed: (a) => a.staticHost !== void 0 },
|
|
1046
|
+
{ flag: "--static-routing", passed: (a) => a.staticRouting !== void 0 },
|
|
1047
|
+
{ flag: "--static-env", passed: (a) => Object.keys(a.staticEnv).length > 0 }
|
|
1048
|
+
]
|
|
1049
|
+
}
|
|
1050
|
+
];
|
|
1051
|
+
function assertNoOrphanFlags(args) {
|
|
1052
|
+
for (const group of DEPENDENT_FLAGS) {
|
|
1053
|
+
if (group.present(args)) continue;
|
|
1054
|
+
const names = group.flags.filter((f) => f.passed(args)).map((f) => f.flag);
|
|
1055
|
+
if (names.length === 0) continue;
|
|
1056
|
+
const one = names.length === 1;
|
|
1057
|
+
throw new UsageError(
|
|
1058
|
+
`${andList(names)} ${one ? "applies" : "apply"} only with ${group.parent}, and this deploy has none \u2014 nothing was deployed. Add ${group.parent}, or drop ${one ? "the flag" : "the flags"}.`,
|
|
1059
|
+
{ hintFor: { command: "deploy" } }
|
|
1060
|
+
);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
function refuseMissingLockDir(args) {
|
|
1064
|
+
const noDir = missingLockDir(args);
|
|
1065
|
+
if (noDir !== void 0) {
|
|
1066
|
+
throw new LocalFileNotFoundError(`${noDir} Nothing was deployed.`, { hintFor: { command: "deploy" } });
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
function refuseMissingStaticDir(args) {
|
|
1070
|
+
if (args.static === void 0 || existsSync(args.static)) return;
|
|
1071
|
+
throw new LocalFileNotFoundError(`--static ${args.static}: directory not found. Nothing was deployed.`, {
|
|
1072
|
+
hintFor: { command: "deploy" }
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
async function runDeployCommand(args) {
|
|
1076
|
+
try {
|
|
1077
|
+
await runDeploy(args);
|
|
1078
|
+
} catch (err) {
|
|
1079
|
+
if (err instanceof Error && err.name === "KeepDataDeclined") {
|
|
1080
|
+
info(err.message);
|
|
1081
|
+
return;
|
|
1082
|
+
}
|
|
1083
|
+
throw err;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
async function runDeploy(args) {
|
|
1087
|
+
if (args.name !== void 0 && args.name.trim() === "") {
|
|
1088
|
+
throw new UsageError(
|
|
1089
|
+
"--name needs a display name with at least one visible character. Drop it to keep the current name (a new ephemeral takes the release's or workspace's name).",
|
|
1090
|
+
{ hintFor: { command: "deploy" } }
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
if (args.name !== void 0) args = { ...args, name: args.name.trim() };
|
|
1094
|
+
const destination = resolveDeployDestination(args);
|
|
1095
|
+
await assertLocalEngineUsable(args, destination);
|
|
1096
|
+
if (args.keepData && args.to !== void 0) {
|
|
1097
|
+
throw new UsageError(
|
|
1098
|
+
"`--keep-data` keeps an ephemeral's or a local engine's rows across a redeploy, and `--to` already merges \u2014 it empties tables only when `--replace` or `--reset-data` asks. Drop `--keep-data`; `--to` without those keeps the destination's data as it always has.",
|
|
1099
|
+
{ hintFor: { command: "deploy" } }
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
assertNoEphemeralFlags(args);
|
|
1103
|
+
refuseStaticHostAReplaceClears(args, args.to !== void 0 ? args.replace : !args.keepData || args.reset);
|
|
1104
|
+
assertNoOrphanFlags(args);
|
|
1105
|
+
warnSecretLookingStaticEnv(args.staticEnv);
|
|
1106
|
+
if (args.to !== void 0) return runDeployToDestination(args, args.to);
|
|
1107
|
+
assertNoDestinationFlags(args);
|
|
1108
|
+
const startedAt = Date.now();
|
|
1109
|
+
if (args.reset && !args.keepData) {
|
|
1110
|
+
info("`--reset` changes nothing here: it only overrides `--keep-data`, and without it a deploy already replaces the environment.");
|
|
1111
|
+
}
|
|
1112
|
+
const { looksLikeSource, resolveProjectEntry, fetchSourceArchive, passwordSeedColumns, archiveSeedContent, gatedApiGroups } = await import("./deploy-source-3W45CXMP.js");
|
|
1113
|
+
const fetching = args.file !== void 0 && looksLikeSource(args.file);
|
|
1114
|
+
const sourceSpec = fetching ? await parseDeploySource(args.file) : void 0;
|
|
1115
|
+
if (sourceSpec !== void 0) refuseValueFlagsForSource(args, args.file, sourceSpec);
|
|
1116
|
+
if (destination.kind === "local-engine") {
|
|
1117
|
+
refuseSameEngine(sourceSpec, process.cwd());
|
|
1118
|
+
const { refuseProfileForLocal } = await import("./tracked-backend-EIBUMDZA.js");
|
|
1119
|
+
refuseProfileForLocal(args.profile, sourceSpec === void 0 ? ["local-engine"] : ["local-engine", sourceSpec.kind], void 0, {});
|
|
1120
|
+
const { refuseCredentialFlagsForLocal } = await import("./local-engine-choice-CH5X3W7E.js");
|
|
1121
|
+
refuseCredentialFlagsForLocal(args, sourceSpec === void 0 ? ["local-engine"] : ["local-engine", sourceSpec.kind]);
|
|
1122
|
+
}
|
|
1123
|
+
if (!fetching) {
|
|
1124
|
+
if (args.file === void 0 && args.bundle === void 0) {
|
|
1125
|
+
const entry = resolveProjectEntry(process.cwd());
|
|
1126
|
+
if (entry !== void 0) args = { ...args, file: entry };
|
|
1127
|
+
}
|
|
1128
|
+
assertBundleInput(args, { command: "deploy" });
|
|
1129
|
+
refuseValueFlagsBesideBundle(args, { command: "deploy" });
|
|
1130
|
+
assertBundleFile(args, { command: "deploy" });
|
|
1131
|
+
assertValueFiles(args, { command: "deploy" });
|
|
1132
|
+
refuseMissingLockDir(args);
|
|
1133
|
+
}
|
|
1134
|
+
refuseMissingStaticDir(args);
|
|
1135
|
+
if (destination.kind === "local-engine") {
|
|
1136
|
+
const { memoCredential } = await import("./tracked-backend-EIBUMDZA.js");
|
|
1137
|
+
return deployToLocalEngine(
|
|
1138
|
+
args,
|
|
1139
|
+
destination,
|
|
1140
|
+
fetching ? memoCredential(() => getAccessToken(args)) : void 0,
|
|
1141
|
+
{ startedAt }
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
if (!fetching) assertEntryFile(args);
|
|
1145
|
+
const auth = await getAccessToken(args);
|
|
1146
|
+
refuseSameEphemeral(sourceSpec, auth, process.cwd(), contextFlags(args), args.keepData && !args.reset);
|
|
1147
|
+
await noteRunScopeFor(auth);
|
|
1148
|
+
const { discoverToolchainPlugins } = await import("./toolchain-modules-5EAZUONB.js");
|
|
1149
|
+
const toolchain = await discoverToolchainPlugins(process.cwd(), {
|
|
1150
|
+
frozen: args.frozenLock === true
|
|
1151
|
+
});
|
|
1152
|
+
const loaded = fetching ? await fetchSourceArchive(async () => auth, args.file, process.cwd()).then((f) => ({
|
|
1153
|
+
// Decoded back out of the archive rather than carried alongside it: the
|
|
1154
|
+
// downstream steps want the workspace text, and reading it from the
|
|
1155
|
+
// bytes about to be uploaded proves they are well-formed first.
|
|
1156
|
+
bundle: JSON.stringify(decodeWorkspaceArchive(f.archive)),
|
|
1157
|
+
bundleObject: void 0,
|
|
1158
|
+
source: f.provenance,
|
|
1159
|
+
// The headline's name for it: the handle, and beside it what people call it.
|
|
1160
|
+
shownSource: shownSourceOf(f),
|
|
1161
|
+
sourceDisplay: f.display,
|
|
1162
|
+
// Read only by a merge, to name the new tables whose rows it does not write.
|
|
1163
|
+
content: archiveSeedContent(f.archive),
|
|
1164
|
+
nonPublicSeedValues: [],
|
|
1165
|
+
archive: f.archive
|
|
1166
|
+
})) : (
|
|
1167
|
+
// The lock write waits for the pre-import refusals (a `--static-host`
|
|
1168
|
+
// the environment does not hold, a seed leak): a refused deploy leaves
|
|
1169
|
+
// `xano.lock` as it found it. `deployEphemeral` commits it before its
|
|
1170
|
+
// first write.
|
|
1171
|
+
await loadBundleText(args, { command: "deploy" }, { withSeed: true, deferLockWrite: true }).then((l) => ({
|
|
1172
|
+
...l,
|
|
1173
|
+
archive: void 0,
|
|
1174
|
+
shownSource: shownInput(l.source),
|
|
1175
|
+
sourceDisplay: void 0
|
|
1176
|
+
}))
|
|
1177
|
+
);
|
|
1178
|
+
const { bundle, source, content, nonPublicSeedValues } = loaded;
|
|
1179
|
+
const passwordColumns = fetching && loaded.archive !== void 0 ? passwordSeedColumns(loaded.archive) : [];
|
|
1180
|
+
if (!fetching) await ignoreBackendSecretFilesRead(args);
|
|
1181
|
+
if (args.static !== void 0) {
|
|
1182
|
+
await assertNoSeedLeaks(args.static, nonPublicSeedValues);
|
|
1183
|
+
}
|
|
1184
|
+
if (content.length > 0 && !fetching) {
|
|
1185
|
+
const hosted = "files" in loaded ? loaded.files.length : 0;
|
|
1186
|
+
const rows = (content.length === 1 ? "1 seed content file" : `${content.length} seed content files`) + (hosted === 0 ? "" : ` and ${hosted === 1 ? "1 hosted file" : `${hosted} hosted files`}`);
|
|
1187
|
+
detail(
|
|
1188
|
+
args.keepData && !args.reset ? `Bundling ${rows} (sent only if this deploy replaces the environment).` : `Bundling ${rows} (full replace re-seeds cleanly).`
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
{
|
|
1192
|
+
const { runBundleHooks } = await import("./toolchain-hooks-GHKLUJOH.js");
|
|
1193
|
+
const { readVersion } = await import("./cli.js");
|
|
1194
|
+
await runBundleHooks(
|
|
1195
|
+
toolchain.loaded,
|
|
1196
|
+
{
|
|
1197
|
+
// The compile already produced this object; only the `--bundle <path>`
|
|
1198
|
+
// branch has nothing but text, and it pays the parse alone.
|
|
1199
|
+
bundle: loaded.bundleObject ?? JSON.parse(bundle),
|
|
1200
|
+
// NOT `args.file` when fetching: on that branch it is a source
|
|
1201
|
+
// spec (`release:main`), not a path, and nothing local was compiled.
|
|
1202
|
+
// `entry` absent is the documented signal for "the project did not
|
|
1203
|
+
// compile this" — the same signal `--bundle <path>` sends — and it is
|
|
1204
|
+
// what lets a plugin decline rather than render a remote backend over
|
|
1205
|
+
// the tree describing this repo's source.
|
|
1206
|
+
entry: fetching ? void 0 : args.file,
|
|
1207
|
+
cwd: process.cwd(),
|
|
1208
|
+
command: "deploy",
|
|
1209
|
+
frozen: args.frozenLock === true,
|
|
1210
|
+
sdkVersion: readVersion()
|
|
1211
|
+
},
|
|
1212
|
+
"; nothing was deployed"
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
const archive = loaded.archive ?? encodeWorkspaceArchive(bundle, [...content, ..."files" in loaded ? loaded.files : []]);
|
|
1216
|
+
let staticSummary;
|
|
1217
|
+
const staticDir = args.static;
|
|
1218
|
+
const publishStatic = staticDir === void 0 ? void 0 : async (env) => {
|
|
1219
|
+
const staticEnv = buildStaticEnv(env.baseUrl, args.staticEnv);
|
|
1220
|
+
const explicit = Object.keys(args.staticEnv).length > 0;
|
|
1221
|
+
try {
|
|
1222
|
+
const target = { baseUrl: env.baseUrl, workspaceId: 1, label: env.label };
|
|
1223
|
+
staticSummary = await deployStaticTo(
|
|
1224
|
+
staticDir,
|
|
1225
|
+
auth,
|
|
1226
|
+
target,
|
|
1227
|
+
staticEnv,
|
|
1228
|
+
explicit,
|
|
1229
|
+
args.staticHost,
|
|
1230
|
+
args.skipLiveness,
|
|
1231
|
+
args.staticRouting,
|
|
1232
|
+
"--static",
|
|
1233
|
+
{ rerun: staticRetryCommand(staticDir, `ephemeral:${env.name}`, args, void 0) }
|
|
1234
|
+
);
|
|
1235
|
+
} catch (err) {
|
|
1236
|
+
const unknown = err instanceof Error && statesOutcomeUnknown(err.message);
|
|
1237
|
+
const message = unknownStaticOutcome(err instanceof Error ? err.message : String(err), unknown);
|
|
1238
|
+
staticSummary = {
|
|
1239
|
+
url: void 0,
|
|
1240
|
+
error: message,
|
|
1241
|
+
retry: staticRetryCommand(staticDir, `ephemeral:${env.name}`, args, err),
|
|
1242
|
+
completed: unknown ? "unknown" : "no"
|
|
1243
|
+
};
|
|
1244
|
+
if (env.republish === true) return staticSummary;
|
|
1245
|
+
warn(
|
|
1246
|
+
"The static-host upload failed \u2014 the backend deploy stands:",
|
|
1247
|
+
"static.upload-failed",
|
|
1248
|
+
[
|
|
1249
|
+
message,
|
|
1250
|
+
// `publish`, not `deploy --static`: a redeploy would re-import the
|
|
1251
|
+
// backend that just landed to retry an upload that has nothing to do with it.
|
|
1252
|
+
// `--static-routing` must be carried too — dropping it lets the retry
|
|
1253
|
+
// silently re-infer routing the caller had explicitly overridden, and
|
|
1254
|
+
// it would then 200 the wrong document instead of failing loudly.
|
|
1255
|
+
staticRetryHint(staticDir, `ephemeral:${env.name}`, args, err)
|
|
1256
|
+
]
|
|
1257
|
+
);
|
|
1258
|
+
process.exitCode = unknown ? EXIT_OUTCOME_UNKNOWN : EXIT_STATIC_FAILED;
|
|
1259
|
+
}
|
|
1260
|
+
return staticSummary;
|
|
1261
|
+
};
|
|
1262
|
+
const removedOut = {};
|
|
1263
|
+
const summary = await deployEphemeral(auth, {
|
|
1264
|
+
archive,
|
|
1265
|
+
bundle,
|
|
1266
|
+
content,
|
|
1267
|
+
source,
|
|
1268
|
+
args,
|
|
1269
|
+
startedAt,
|
|
1270
|
+
publishStatic,
|
|
1271
|
+
commitLock: "commitLock" in loaded ? loaded.commitLock : void 0,
|
|
1272
|
+
warnOrphansUncommitted: "warnOrphansUncommitted" in loaded ? loaded.warnOrphansUncommitted : void 0,
|
|
1273
|
+
lock: "classifiedLock" in loaded ? loaded.classifiedLock : void 0,
|
|
1274
|
+
shownSource: loaded.shownSource,
|
|
1275
|
+
..."commitLock" in loaded && loaded.commitLock !== void 0 && args.file !== void 0 ? { lockPath: (await import("./cli.js")).resolveLockPath(args, args.file) } : {},
|
|
1276
|
+
removedOut,
|
|
1277
|
+
...!fetching && args.bundle === void 0 && args.file !== void 0 ? recordedEntry(args.file) : {},
|
|
1278
|
+
compiled: !fetching && args.bundle === void 0
|
|
1279
|
+
}).finally(() => noteLanded(void 0));
|
|
1280
|
+
if (staticSummary !== void 0) summary.static = staticSummary;
|
|
1281
|
+
if (summary.ephemeral !== void 0) {
|
|
1282
|
+
const { landingLockPath, recordLanding, recordForeignLanding } = await import("./landing-record-SU52SKXU.js");
|
|
1283
|
+
const dest = { kind: "ephemeral", name: summary.ephemeral.name };
|
|
1284
|
+
const { storedColumnsAfter } = await import("./live-diff-YONW2VI3.js");
|
|
1285
|
+
const sent = JSON.parse(bundle);
|
|
1286
|
+
const stored = summary.data === "kept" ? removedOut.storedColumns : storedColumnsAfter(sent, void 0, /* @__PURE__ */ new Map(), "rebuild");
|
|
1287
|
+
if (fetching || args.bundle !== void 0) {
|
|
1288
|
+
summary.landingRecord = recordForeignLanding({
|
|
1289
|
+
lockPath: landingLockPath(args),
|
|
1290
|
+
instance: auth.instance,
|
|
1291
|
+
dest,
|
|
1292
|
+
bundle: sent,
|
|
1293
|
+
mode: "replace",
|
|
1294
|
+
...stored !== void 0 ? { stored } : {},
|
|
1295
|
+
// What the merge deleted leaves the lock as an entry merge's does.
|
|
1296
|
+
...removedOut.removedGuids !== void 0 ? { removedGuids: removedOut.removedGuids, removedBy: "merge" } : {}
|
|
1297
|
+
});
|
|
1298
|
+
} else {
|
|
1299
|
+
const { landedIdentities } = await import("./landed-753YLJ5H.js");
|
|
1300
|
+
summary.landingRecord = recordLanding({
|
|
1301
|
+
lockPath: landingLockPath(args, { ...args.file !== void 0 ? { entryFile: args.file } : {}, fromProject: false }),
|
|
1302
|
+
instance: auth.instance,
|
|
1303
|
+
dest,
|
|
1304
|
+
update: {
|
|
1305
|
+
mode: "replace",
|
|
1306
|
+
identities: withStoredColumns(landedIdentities(sent), stored),
|
|
1307
|
+
...removedOut.removed !== void 0 ? { removed: removedOut.removed } : {}
|
|
1308
|
+
},
|
|
1309
|
+
removedBy: "merge"
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
} else {
|
|
1313
|
+
summary.landingRecord = null;
|
|
1314
|
+
}
|
|
1315
|
+
if (fetching) {
|
|
1316
|
+
noteReleaseCarriesNoEnv(
|
|
1317
|
+
args.file,
|
|
1318
|
+
gatedApiGroups(JSON.parse(bundle)),
|
|
1319
|
+
summary.ephemeral === void 0 ? void 0 : { ephemeral: summary.ephemeral.name, created: summary.created !== false },
|
|
1320
|
+
contextFlags(args)
|
|
1321
|
+
);
|
|
1322
|
+
if (summary.data === "replaced" && carriesTables(bundle)) noteLiveCopyCarriesNoRows(args.file, loaded.sourceDisplay, contextFlags(args));
|
|
1323
|
+
if (summary.data === "replaced") {
|
|
1324
|
+
const unverifiable = await notePasswordHashesDoNotTravel(
|
|
1325
|
+
async () => auth,
|
|
1326
|
+
args.file,
|
|
1327
|
+
passwordColumns,
|
|
1328
|
+
summary.ephemeral?.name
|
|
1329
|
+
);
|
|
1330
|
+
if (unverifiable !== void 0) summary.passwordHashesUnverifiable = unverifiable;
|
|
1331
|
+
}
|
|
1332
|
+
} else if (args.bundle !== void 0 && summary.data === "replaced" && carriesTables(bundle)) {
|
|
1333
|
+
noteBundleFileCarriesNoRows(args.bundle, args.reset === true);
|
|
1334
|
+
}
|
|
1335
|
+
noteUnconfigured(summary, toolchain.unconfigured);
|
|
1336
|
+
const tracked = getEnvironment(readEphemeralState(process.cwd()), auth);
|
|
1337
|
+
const staticServing = tracked?.static_url !== void 0 && tracked.static_down !== true;
|
|
1338
|
+
await syncDevEnvAfterDeploy(args, summary, process.cwd(), { staticServing });
|
|
1339
|
+
if (args.test) summary.testRun = await runDeployedTests(auth, summary, args);
|
|
1340
|
+
if (args.open) openDeployed(summary);
|
|
1341
|
+
if (isMachineOutput(args)) writeJson(summary);
|
|
1342
|
+
}
|
|
1343
|
+
async function syncDevEnvAfterDeploy(args, summary, cwd = process.cwd(), opts = {}) {
|
|
1344
|
+
if (args.noDevEnv || summary.url === void 0) return;
|
|
1345
|
+
if (opts.staticServing === true || summary.static !== void 0 && summary.static.error === void 0) {
|
|
1346
|
+
const { staleDevEnv } = await import("./dev-env-sync-HN44K4D5.js");
|
|
1347
|
+
let stale;
|
|
1348
|
+
try {
|
|
1349
|
+
stale = staleDevEnv(cwd, summary.url);
|
|
1350
|
+
} catch {
|
|
1351
|
+
stale = void 0;
|
|
1352
|
+
}
|
|
1353
|
+
if (stale !== void 0) {
|
|
1354
|
+
detail(
|
|
1355
|
+
`${stale.label} still points your dev server at ${stale.url}; set ${stale.variable}=${summary.url} there to use this deploy.`
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
const { syncDevEnv } = await import("./dev-env-sync-HN44K4D5.js");
|
|
1361
|
+
try {
|
|
1362
|
+
const written = syncDevEnv(cwd, summary.url);
|
|
1363
|
+
if (written === void 0) return;
|
|
1364
|
+
detail(
|
|
1365
|
+
`Pointed ${written.label} at this deploy (${written.variable}) \u2014 restart your dev server to pick it up. \`--no-dev-env\` skips this.`
|
|
1366
|
+
);
|
|
1367
|
+
} catch (err) {
|
|
1368
|
+
warn(err instanceof Error ? err.message : String(err), "dev-env.sync-failed");
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
function noteUnconfigured(summary, unconfigured) {
|
|
1372
|
+
if (unconfigured.length > 0) summary.unconfigured = [...unconfigured];
|
|
1373
|
+
return summary;
|
|
1374
|
+
}
|
|
1375
|
+
function openDeployed(summary) {
|
|
1376
|
+
const url = summary.static?.url ?? summary.url;
|
|
1377
|
+
if (url === void 0) {
|
|
1378
|
+
warn("--open had no URL to open \u2014 the deploy reported none.", "deploy.open-no-url");
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
detail(`Opening ${url}`);
|
|
1382
|
+
openBrowser(url);
|
|
1383
|
+
}
|
|
1384
|
+
var WAITING_LABEL = "Waiting for microservices\u2026";
|
|
1385
|
+
var IMPORTING_LABEL = "Uploading and importing the workspace\u2026";
|
|
1386
|
+
function noteStaticUrlChange(previous, current, arm, downBefore) {
|
|
1387
|
+
if (previous === void 0) {
|
|
1388
|
+
if (downBefore !== void 0 && downBefore !== current) {
|
|
1389
|
+
detail(`This frontend URL is new \u2014 the previous one (${downBefore}) stopped serving at an earlier replace.`);
|
|
1390
|
+
}
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
if (previous === current) return;
|
|
1394
|
+
detail(
|
|
1395
|
+
arm.arm === "replace" ? `This frontend URL is new \u2014 a replacing deploy clears the environment's static hosting, so the previous one (${previous}) no longer serves, and the rows were re-seeded. \`--keep-data\` merges instead, which leaves both standing.` : `This frontend URL is new \u2014 the previous one (${previous}) no longer serves.`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
function announceArm(arm, kind, landedBefore = false, seeded = true) {
|
|
1399
|
+
if (arm.arm !== "replace" || arm.skipped === void 0) return;
|
|
1400
|
+
const why = {
|
|
1401
|
+
new: "Nothing to keep yet \u2014 this environment is new, so it is replaced and seeded. Later deploys with `--keep-data` keep its rows.",
|
|
1402
|
+
// An ephemeral expires or is deleted; a local engine's rows go when its
|
|
1403
|
+
// process restarts. Each told its own reason.
|
|
1404
|
+
recreated: kind === "ephemeral" ? "Nothing to keep \u2014 the ephemeral recorded for this project is gone (it expired, or was deleted), so a new one is created, replaced and seeded." : "Nothing to keep \u2014 the environment recorded for this project is gone (the engine restarted), so this one is replaced and seeded.",
|
|
1405
|
+
// Two different stories behind the same missing marker, each told as it
|
|
1406
|
+
// is: the landing record is written only by a completed deploy, so when it
|
|
1407
|
+
// names objects here, a deploy did land and only the marker is gone.
|
|
1408
|
+
"never-filled": landedBefore ? "Nothing to keep that can be vouched for \u2014 an earlier deploy from this project landed here, but the marker that its rows are this project's is missing from `.xano/ephemeral.json`, so it is replaced and seeded. Later deploys with `--keep-data` keep its rows." : "Nothing to keep \u2014 no deploy from this project into this environment has completed yet, so it is replaced and seeded.",
|
|
1409
|
+
reset: seeded ? "`--reset` wins over `--keep-data` \u2014 replacing the environment and re-seeding it." : "`--reset` wins over `--keep-data` \u2014 replacing the environment. This deploy carries no seed rows, so its tables come back EMPTY."
|
|
1410
|
+
};
|
|
1411
|
+
info(why[arm.skipped]);
|
|
1412
|
+
}
|
|
1413
|
+
var KEEP_DATA_REPLACES = {
|
|
1414
|
+
new: "`--keep-data` has nothing to keep \u2014 this deploy creates the environment",
|
|
1415
|
+
recreated: "`--keep-data` has nothing to keep \u2014 the environment this project recorded is gone, and this deploy creates another",
|
|
1416
|
+
"never-filled": "`--keep-data` cannot keep this environment's data \u2014 nothing records that its rows are this project's",
|
|
1417
|
+
reset: "`--reset` wins over `--keep-data`"
|
|
1418
|
+
};
|
|
1419
|
+
async function refuseEnvClearedByFallbackReplace(arm) {
|
|
1420
|
+
if (arm.arm !== "replace") return;
|
|
1421
|
+
const { envRefusalIfReplaced, docTokenRefusalIfReplaced } = await import("./cli.js");
|
|
1422
|
+
const docs = docTokenRefusalIfReplaced(KEEP_DATA_REPLACES[arm.skipped ?? "never-filled"]);
|
|
1423
|
+
if (docs !== void 0) throw new Error(docs);
|
|
1424
|
+
if (arm.skipped !== "never-filled") return;
|
|
1425
|
+
const refusal = envRefusalIfReplaced(KEEP_DATA_REPLACES["never-filled"]);
|
|
1426
|
+
if (refusal !== void 0) throw new Error(refusal);
|
|
1427
|
+
}
|
|
1428
|
+
function republishNote(republished, unknown, frontends) {
|
|
1429
|
+
if (republished.url === void 0) return failedRepublishNote(republished, unknown, frontends);
|
|
1430
|
+
const checked = republished.verified === true ? ", verified live" : republished.verified === false ? ", not yet confirmed live" : "";
|
|
1431
|
+
const published = `The frontend was republished at ${republished.url}${checked} \u2014 a replace clears it`;
|
|
1432
|
+
return unknown ? `${published}. It runs against whichever backend the environment now holds: its previous one, or this deploy's if the import landed after all.` : `${published} \u2014 and runs against this environment's PREVIOUS backend until a deploy imports cleanly.`;
|
|
1433
|
+
}
|
|
1434
|
+
function failedRepublishNote(republished, unknown, frontends) {
|
|
1435
|
+
const head = unknown ? "The import's outcome is unknown" : "The import was refused";
|
|
1436
|
+
const failure = ((republished.error ?? "no URL was returned").trim().split("\n")[0] ?? "").trim().replace(/\.+$/, "");
|
|
1437
|
+
const retry = republished.retry !== void 0 ? ` Run \`${republished.retry}\` to publish it again.` : "";
|
|
1438
|
+
const warned = frontends?.warned ?? [];
|
|
1439
|
+
const by = (s) => warned.filter((u) => (frontends?.states[u] ?? "unknown") === s);
|
|
1440
|
+
const down = by("down");
|
|
1441
|
+
const serving = by("serving");
|
|
1442
|
+
const unasked = by("unknown");
|
|
1443
|
+
const unaskedText = unasked.length === 0 ? "" : ` The frontend at ${unasked.join(", ")} could not be reached to check \u2014 the replace's clear may have taken it down.`;
|
|
1444
|
+
if (down.length > 0) {
|
|
1445
|
+
return `${head}, the replace had cleared the frontend, and republishing it failed: ${failure} \u2014 nothing serves at ${down.join(", ")}.${unaskedText}${retry}`;
|
|
1446
|
+
}
|
|
1447
|
+
if (serving.length > 0 && unasked.length === 0) {
|
|
1448
|
+
return `${head}, and republishing the frontend failed: ${failure} \u2014 the frontend at ${serving.join(", ")} still serves its previous build. Deploy again once the import goes through; that publishes this build with it.`;
|
|
1449
|
+
}
|
|
1450
|
+
return `${head}, and republishing the frontend failed: ${failure}.${unaskedText}${retry}`;
|
|
1451
|
+
}
|
|
1452
|
+
function isUnknownImportOutcome(err) {
|
|
1453
|
+
return err instanceof Error && statesOutcomeUnknown(err.message);
|
|
1454
|
+
}
|
|
1455
|
+
function rerunOf(args) {
|
|
1456
|
+
const retry = retryCommand(args);
|
|
1457
|
+
return { command: retry.command, note: withheldNote(retry.withheld) };
|
|
1458
|
+
}
|
|
1459
|
+
async function latestEngineRelease(err) {
|
|
1460
|
+
const { ImportHttpError } = await import("./import-EQSFU6JC.js");
|
|
1461
|
+
if (!(err instanceof ImportHttpError) || err.status !== 501) return void 0;
|
|
1462
|
+
const { resolveEnginePlatform } = await import("./local-engine-config-5U3SC434.js");
|
|
1463
|
+
const platform = resolveEnginePlatform();
|
|
1464
|
+
if (platform === void 0) return void 0;
|
|
1465
|
+
const { latestKnownVersion } = await import("./local-engine-update-check-6L645VBL.js");
|
|
1466
|
+
return latestKnownVersion({ platform });
|
|
1467
|
+
}
|
|
1468
|
+
async function mergeGuards(args, lock, outgoing) {
|
|
1469
|
+
const interactive = process.stdin.isTTY === true && !isMachineOutput(args);
|
|
1470
|
+
const pinned = outgoing === void 0 ? [] : (await import("./release-command-N4ZPKMHR.js")).pinnedCanonicalGuids(outgoing.bundle, lock, outgoing.compiled);
|
|
1471
|
+
const guards = {
|
|
1472
|
+
...pinned.length > 0 ? { pinned } : {},
|
|
1473
|
+
allowForeignDeletes: args.yes === true,
|
|
1474
|
+
...interactive ? {
|
|
1475
|
+
confirm: async (q) => (await import("./prompt-II3F3LND.js")).confirm(q, {
|
|
1476
|
+
flag: "--yes",
|
|
1477
|
+
refusal: { details: { deployed: false }, ...yesRerun(args, "deploy") }
|
|
1478
|
+
})
|
|
1479
|
+
} : {}
|
|
1480
|
+
};
|
|
1481
|
+
const entry = args.file;
|
|
1482
|
+
if (lock === void 0 || entry === void 0) return guards;
|
|
1483
|
+
const tables = /* @__PURE__ */ new Map();
|
|
1484
|
+
const replaced = /* @__PURE__ */ new Map();
|
|
1485
|
+
for (const [key, e] of Object.entries(lock.objects)) {
|
|
1486
|
+
if (!key.startsWith("dbo:")) continue;
|
|
1487
|
+
if (typeof e.guid === "string") tables.set(e.guid, key.slice("dbo:".length));
|
|
1488
|
+
for (const guid of e.replaced ?? []) replaced.set(guid, key.slice("dbo:".length));
|
|
1489
|
+
}
|
|
1490
|
+
const { orphanFixUps, resolveLockPath, composedChildren } = await import("./cli.js");
|
|
1491
|
+
const { clearReplacedCommand } = await import("./keep-data-merge-7MPLVAVI.js");
|
|
1492
|
+
const lockPath = resolveLockPath(args, entry);
|
|
1493
|
+
const run = (line) => (line ?? "").replace(/^(renamed|deleted)\? run: /, "");
|
|
1494
|
+
const keys = Object.keys(lock.objects);
|
|
1495
|
+
const codePinned = outgoing === void 0 ? void 0 : codePinnedAgainstDisk(outgoing.bundle, lockPath, lock);
|
|
1496
|
+
return {
|
|
1497
|
+
...guards,
|
|
1498
|
+
lockPins: {
|
|
1499
|
+
tables,
|
|
1500
|
+
...codePinned !== void 0 ? { codePinned } : {},
|
|
1501
|
+
clearReplaced: (name) => clearReplacedCommand(name, lockPath),
|
|
1502
|
+
...replaced.size > 0 ? { replaced } : {},
|
|
1503
|
+
fixUps: (name, newName) => {
|
|
1504
|
+
const [rename, prune] = orphanFixUps(`dbo:${name}`, entry, lockPath, void 0, [], newName);
|
|
1505
|
+
return { rename: run(rename), prune: run(prune) };
|
|
1506
|
+
},
|
|
1507
|
+
keyFixUps: (key, newName) => {
|
|
1508
|
+
if (!(key in lock.objects)) return void 0;
|
|
1509
|
+
const [rename, prune] = orphanFixUps(key, entry, lockPath, void 0, composedChildren(key, keys), newName);
|
|
1510
|
+
return { rename: run(rename).replace(/ \(it moves .*\)$/, ""), prune: run(prune) };
|
|
1511
|
+
},
|
|
1512
|
+
entries: new Map(
|
|
1513
|
+
Object.entries(lock.objects).flatMap(([key, e]) => typeof e.guid === "string" ? [[key, e.guid]] : [])
|
|
1514
|
+
)
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
function codePinnedAgainstDisk(bundle, lockPath, classified) {
|
|
1519
|
+
try {
|
|
1520
|
+
return codePinnedTables(JSON.parse(bundle), readLockFile(lockPath), classified);
|
|
1521
|
+
} catch {
|
|
1522
|
+
return void 0;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
function offTerminalYes() {
|
|
1526
|
+
return process.stdin.isTTY === true ? "" : " --yes";
|
|
1527
|
+
}
|
|
1528
|
+
function createdButFailedNote(name, unknown, args, landedUnresolved = false, opts = {}) {
|
|
1529
|
+
if (opts.untracked === true) {
|
|
1530
|
+
return `Created ephemeral ${name}, but could not record it in .xano/ephemeral.json, so nothing here tracks it. Delete it with \`xanosdk ephemeral delete ${shellQuote(name)}${contextFlags(args)} --yes\`, then make .xano/ writable and redeploy.`;
|
|
1531
|
+
}
|
|
1532
|
+
const remove = `\`xanosdk ephemeral delete ${shellQuote(name)}${contextFlags(args)}${offTerminalYes()}\``;
|
|
1533
|
+
if (opts.futile === true) {
|
|
1534
|
+
return `Created ephemeral ${name} (tracked in .xano/ephemeral.json); it holds nothing yet, and redeploying from this source fails the same way \u2014 delete it with ${remove}.`;
|
|
1535
|
+
}
|
|
1536
|
+
const retry = retryCommand(args);
|
|
1537
|
+
const holds = landedUnresolved ? "it holds this deploy, but its hostedFile() fields point at nothing" : unknown ? "whether it holds this deploy is unknown" : "it holds nothing yet";
|
|
1538
|
+
return `Created ephemeral ${name} (tracked in .xano/ephemeral.json); ${holds} \u2014 redeploy with \`${retry.command}\`, or ${remove}.${withheldNote(retry.withheld)}`;
|
|
1539
|
+
}
|
|
1540
|
+
function noteReplaceRerun(err, args) {
|
|
1541
|
+
if (!(err instanceof Error) || !isUnknownImportOutcome(err) && !(err instanceof ImportNotSentError)) return err;
|
|
1542
|
+
const retry = retryCommand(args);
|
|
1543
|
+
const when = certificateFailureCode(err) !== void 0 ? "Once the certificate is trusted" : "Once it is reachable";
|
|
1544
|
+
err.message = `${err.message}
|
|
1545
|
+
${when}, run \`${retry.command}\`.${withheldNote(retry.withheld)}`;
|
|
1546
|
+
return err;
|
|
1547
|
+
}
|
|
1548
|
+
function noteKeptFilledMarker(err, filled, args) {
|
|
1549
|
+
if (!filled || !(err instanceof Error) || !isUnknownImportOutcome(err)) return err;
|
|
1550
|
+
const retry = retryCommand(args, { add: ["--keep-data"], drop: ["--reset"] });
|
|
1551
|
+
const again = retryCommand(args);
|
|
1552
|
+
const replace = again.command === retry.command ? "" : `Once it is reachable, \`${again.command}\` replaces it again. `;
|
|
1553
|
+
err.message = `${err.message}
|
|
1554
|
+
${replace}Whichever way it went, the environment's rows are still recorded as this project's, so \`${retry.command}\` merges into them rather than replacing them.${withheldNote(retry.withheld)}`;
|
|
1555
|
+
return err;
|
|
1556
|
+
}
|
|
1557
|
+
var DUPLICATE_GUID = /Duplicate (\S+) guid: (\S+?)\./;
|
|
1558
|
+
function fetchedSourceOf(file) {
|
|
1559
|
+
if (file === void 0) return void 0;
|
|
1560
|
+
const colon = file.indexOf(":");
|
|
1561
|
+
const kind = colon < 0 ? file : file.slice(0, colon);
|
|
1562
|
+
const name = colon < 0 ? void 0 : file.slice(colon + 1).trim();
|
|
1563
|
+
if (kind === "release" || kind === "tenant") return name ? { kind, name } : void 0;
|
|
1564
|
+
if (kind === "ephemeral" || kind === "local-engine") return name ? { kind, name } : { kind };
|
|
1565
|
+
if (kind === "workspace" && colon < 0) return { kind };
|
|
1566
|
+
return void 0;
|
|
1567
|
+
}
|
|
1568
|
+
function isFetchedDuplicateGuid(err, file) {
|
|
1569
|
+
return err instanceof Error && fetchedSourceOf(file) !== void 0 && DUPLICATE_GUID.test(err.message);
|
|
1570
|
+
}
|
|
1571
|
+
function explainDuplicateArchiveGuid(err, file, flags = "") {
|
|
1572
|
+
if (!(err instanceof Error)) return err;
|
|
1573
|
+
const dup = DUPLICATE_GUID.exec(err.message);
|
|
1574
|
+
if (dup === null) return err;
|
|
1575
|
+
const [, section, guid] = dup;
|
|
1576
|
+
const keeps = `A deploy keeps an archive's identities, and the instance refuses one that two objects share`;
|
|
1577
|
+
const source = fetchedSourceOf(file);
|
|
1578
|
+
let why;
|
|
1579
|
+
if (source === void 0) {
|
|
1580
|
+
why = `The archive carries two ${section} objects under guid ${guid}. ${keeps} \u2014 give one of them its own guid and deploy again.`;
|
|
1581
|
+
} else if (source.kind === "release") {
|
|
1582
|
+
why = `Release "${source.name}" was stored with two ${section} objects under guid ${guid}. ${keeps}; \`xanosdk tenant deploy\` gives every object a new identity, which is why it can land the same release. Re-cut the release from a backend that holds one ${section} per guid to deploy it here.`;
|
|
1583
|
+
} else {
|
|
1584
|
+
const label = source.kind === "workspace" ? "The workspace" : source.name === void 0 ? `The ${source.kind}` : `${source.kind === "tenant" ? "Tenant" : source.kind === "ephemeral" ? "Ephemeral" : "Local engine"} ${source.name}`;
|
|
1585
|
+
const reland = source.kind === "tenant" ? `, land it there again with \`xanosdk deploy --to ${shellQuote(`tenant:${source.name}`)}${flags}\`` : source.kind === "workspace" ? `, land it there again with \`xanosdk deploy --to workspace${flags}\`` : source.kind === "ephemeral" ? `, deploy it there again from the project that tracks it` : `, land it there again`;
|
|
1586
|
+
why = `${label} holds two ${section} objects under guid ${guid}. ${keeps}, so redeploying from it fails the same way until the duplicate is fixed where it is authored: give one of them its own guid${reland}, then run this deploy again.`;
|
|
1587
|
+
}
|
|
1588
|
+
err.message = `${err.message}
|
|
1589
|
+
${why}`;
|
|
1590
|
+
return err;
|
|
1591
|
+
}
|
|
1592
|
+
function dataFields(arm) {
|
|
1593
|
+
if (arm.arm === "merge") return { data: "kept" };
|
|
1594
|
+
return { data: "replaced", ...arm.skipped === void 0 ? {} : { keepDataSkipped: arm.skipped } };
|
|
1595
|
+
}
|
|
1596
|
+
function waitingLabel(microservices) {
|
|
1597
|
+
const { ready, startable } = readyRatio(microservices);
|
|
1598
|
+
return startable === 0 ? WAITING_LABEL : `${WAITING_LABEL} (${ready}/${startable} ready)`;
|
|
1599
|
+
}
|
|
1600
|
+
async function verifyMicroservices(auth, baseUrl, opts, waitOpts = {}) {
|
|
1601
|
+
if (opts.skip) return void 0;
|
|
1602
|
+
let spin;
|
|
1603
|
+
let result;
|
|
1604
|
+
try {
|
|
1605
|
+
result = await waitForMicroservices(auth, { baseUrl, workspaceId: 1 }, {
|
|
1606
|
+
...waitOpts,
|
|
1607
|
+
onPoll: (rows) => {
|
|
1608
|
+
if (spin === void 0 && rows.some(isAwaited)) spin = spinner(WAITING_LABEL);
|
|
1609
|
+
spin?.update(waitingLabel(rows));
|
|
1610
|
+
waitOpts.onPoll?.(rows);
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
} catch (err) {
|
|
1614
|
+
const message = withoutReadAftermath(err instanceof Error ? err.message : String(err));
|
|
1615
|
+
const remedy = `xanosdk ephemeral get ${opts.name !== void 0 ? shellQuote(opts.name) : "<env>"}${opts.flags ?? ""}`;
|
|
1616
|
+
warn("Could not read microservice status (the backend deployed fine):", "microservice.status-unreadable", [
|
|
1617
|
+
message,
|
|
1618
|
+
`Check them with \`${remedy}\`.`
|
|
1619
|
+
]);
|
|
1620
|
+
opts.onReadError?.({ message, remedy });
|
|
1621
|
+
return void 0;
|
|
1622
|
+
} finally {
|
|
1623
|
+
spin?.stop();
|
|
1624
|
+
}
|
|
1625
|
+
const { microservices, timedOut, hadFailure } = result;
|
|
1626
|
+
if (microservices.length === 0) return void 0;
|
|
1627
|
+
const { ready, startable } = readyRatio(microservices);
|
|
1628
|
+
const lines = microservices.map(microserviceLine);
|
|
1629
|
+
let listed = false;
|
|
1630
|
+
if (hadFailure) {
|
|
1631
|
+
warn("Some microservices failed to start:", "microservice.failed", lines);
|
|
1632
|
+
listed = true;
|
|
1633
|
+
failDeploy();
|
|
1634
|
+
} else if (timedOut) {
|
|
1635
|
+
warn(`Microservices are still starting (${ready}/${startable} ready) \u2014 they should come up shortly:`, "microservice.starting", lines);
|
|
1636
|
+
listed = true;
|
|
1637
|
+
if (opts.requireReady) failDeploy();
|
|
1638
|
+
} else if (startable === 0) {
|
|
1639
|
+
info("No microservices to start:");
|
|
1640
|
+
} else {
|
|
1641
|
+
success(`Microservices ready (${ready}/${startable})`);
|
|
1642
|
+
}
|
|
1643
|
+
if (!listed) for (const line of lines) detail(line);
|
|
1644
|
+
if (timedOut && !hadFailure) {
|
|
1645
|
+
detail(
|
|
1646
|
+
`Re-check with \`xanosdk ephemeral get ${opts.name ?? "<env>"}${opts.flags ?? ""}\`, or skip this wait next time with --skip-liveness.`
|
|
1647
|
+
);
|
|
1648
|
+
if (!opts.requireReady) {
|
|
1649
|
+
detail("Fail the deploy on this instead of warning: --require-microservices.");
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
return microservices;
|
|
1653
|
+
}
|
|
1654
|
+
function failDeploy() {
|
|
1655
|
+
if (!process.exitCode) process.exitCode = EXIT_MICROSERVICE_NOT_READY;
|
|
1656
|
+
}
|
|
1657
|
+
function ephemeralDisabledError(source) {
|
|
1658
|
+
return new Error(
|
|
1659
|
+
`Ephemeral environments are not enabled on this instance, so there is nothing for \`xanosdk deploy ${source}\` to create \u2014 they are the only environment it writes to.
|
|
1660
|
+
Ask whoever administers the instance to enable them.
|
|
1661
|
+
Until then \`xanosdk export\` still writes the bundle, and \`xanosdk preflight\` still checks the round-trip, without needing one.`
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
function notReadyError(err, name, flags, created, retry) {
|
|
1665
|
+
const head = closeSentence((err instanceof Error ? err.message : String(err)).split("\n")[0]);
|
|
1666
|
+
const unanswered = isUnansweredPoll(err);
|
|
1667
|
+
const failure = new Error(
|
|
1668
|
+
`${head}
|
|
1669
|
+
${created ? `Ephemeral "${name}" was created and is` : `Ephemeral "${name}" is`} recorded in .xano/ephemeral.json, but ${unanswered ? "whether it is ready could not be read" : "it is not ready yet"}, so nothing was imported. \`${retry?.command ?? `xanosdk deploy${flags}`}\` retries onto it; \`xanosdk ephemeral delete ${name}${flags}${offTerminalYes()}\` removes it.${withheldNote(retry?.withheld ?? [])}${unanswered ? " The poll got no answer (a server error or a network failure), so this run exits 8." : ""}`,
|
|
1670
|
+
{ cause: err }
|
|
1671
|
+
);
|
|
1672
|
+
return unanswered ? Object.assign(failure, { exitCode: 8 }) : failure;
|
|
1673
|
+
}
|
|
1674
|
+
function ephemeralStateCheck(display, name, flags) {
|
|
1675
|
+
return `Check whether it landed in what ${display} now defines \u2014 \`xanosdk ephemeral export ${shellQuote(name)} --format multidoc --path -${flags}\` prints its backend \u2014 before retrying.`;
|
|
1676
|
+
}
|
|
1677
|
+
function toStateCheck(args, display) {
|
|
1678
|
+
const retry = retryCommand(args, { add: ["--dry-run"] });
|
|
1679
|
+
return `Preview it again with \`${retry.command}\` to see ${display}'s current state before retrying.${withheldNote(retry.withheld)}`;
|
|
1680
|
+
}
|
|
1681
|
+
async function warnSameDisplayName(auth, parentWorkspaceId, created, recorded, flags) {
|
|
1682
|
+
const display = created.display;
|
|
1683
|
+
if (display === void 0 || display === created.name) return;
|
|
1684
|
+
let same;
|
|
1685
|
+
try {
|
|
1686
|
+
same = (await listEphemeral(auth, { parentWorkspaceId })).filter(
|
|
1687
|
+
(e) => e.display === display && e.name !== created.name && e.name !== recorded && !isExpired(e.expiresAt)
|
|
1688
|
+
);
|
|
1689
|
+
} catch {
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
if (same.length === 0) return;
|
|
1693
|
+
const handles = same.map((e) => e.name);
|
|
1694
|
+
const one = handles.length === 1;
|
|
1695
|
+
warn(
|
|
1696
|
+
`${one ? "Another ephemeral is" : `${handles.length} other ephemerals are`} also named "${display}" (${handles.join(", ")}) \u2014 this deploy created ${created.name} beside ${one ? "it" : "them"}.`,
|
|
1697
|
+
"ephemeral.duplicate-name",
|
|
1698
|
+
[
|
|
1699
|
+
`If ${one ? "it is" : "they are"} left over (a create whose answer was lost), delete ${one ? "it" : "them"} with ${handles.map((h) => `\`xanosdk ephemeral delete ${shellQuote(h)}${flags}${offTerminalYes()}\``).join(", ")}.`
|
|
1700
|
+
]
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
function unknownCreateOutcome(err, display, flags, retry) {
|
|
1704
|
+
if (!(err instanceof Error) || !/may or may not have taken effect/.test(err.message)) return err;
|
|
1705
|
+
const head = closeSentence(err.message.split("\n")[0]);
|
|
1706
|
+
const again = retry?.command ?? `xanosdk deploy${flags}`;
|
|
1707
|
+
const message = `${head}
|
|
1708
|
+
The request was sent, so ephemeral "${display}" may or may not have been created \u2014 check with \`xanosdk ephemeral list${flags}\` before retrying. If it is listed, it exists and this project does not track it: delete it with \`xanosdk ephemeral delete <handle>${flags}${offTerminalYes()}\` and run \`${again}\`, or keep it and merge into it with \`xanosdk deploy --to tenant:<handle>${flags}\` (a retry as it is creates a second one beside it). If it is not listed, run \`${again}\` again.${withheldNote(retry?.withheld ?? [])}`;
|
|
1709
|
+
return err instanceof TransportError ? new TransportError(message, err.timeout, { cause: err.cause }) : new Error(message, { cause: err });
|
|
1710
|
+
}
|
|
1711
|
+
function shownInput(source) {
|
|
1712
|
+
return source === "-" ? source : displayPath(source);
|
|
1713
|
+
}
|
|
1714
|
+
async function gateEntrySwap(args, label, previous, entry, arm) {
|
|
1715
|
+
if (previous === void 0 || entry === void 0 || previous === entry) return;
|
|
1716
|
+
const what = arm === "replace" ? `this deploy of ${entry} replaces that backend with this one` : `this merge of ${entry} removes what that backend declared and this one does not`;
|
|
1717
|
+
warn(`${label} was last deployed from ${previous}; ${what}.`, "ephemeral.entry-changed");
|
|
1718
|
+
if (args.yes === true) return;
|
|
1719
|
+
const question = `Deploy ${entry} over the backend from ${previous} on ${label}?`;
|
|
1720
|
+
const { rerun, note } = yesRerun(args, "deploy");
|
|
1721
|
+
const details = { deployed: false, previousEntry: previous, entry };
|
|
1722
|
+
if (process.stdin.isTTY !== true || isMachineOutput(args)) {
|
|
1723
|
+
throw new CliError(
|
|
1724
|
+
"SDK_USAGE",
|
|
1725
|
+
`${label} was not deployed: it would have asked: ${question}
|
|
1726
|
+
Re-run as \`${rerun}\` to answer yes without being asked.${note}`,
|
|
1727
|
+
{ exitCode: 1, details: { reason: "needs-confirmation", ...details } }
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
const { confirm } = await import("./prompt-II3F3LND.js");
|
|
1731
|
+
if (await confirm(question, { flag: "--yes", refusal: { details, rerun, note } })) return;
|
|
1732
|
+
const { KeepDataDeclined } = await import("./keep-data-merge-7MPLVAVI.js");
|
|
1733
|
+
throw new KeepDataDeclined(label, "deployed");
|
|
1734
|
+
}
|
|
1735
|
+
function recordedEntry(file) {
|
|
1736
|
+
const real = (p) => {
|
|
1737
|
+
try {
|
|
1738
|
+
return realpathSync(p);
|
|
1739
|
+
} catch {
|
|
1740
|
+
return p;
|
|
1741
|
+
}
|
|
1742
|
+
};
|
|
1743
|
+
const rel = relative(real(process.cwd()), real(resolve(file))).split(sep).join("/");
|
|
1744
|
+
if (rel === "") return {};
|
|
1745
|
+
return { entry: rel.startsWith("../") || isAbsolute(rel) ? rel : `./${rel}` };
|
|
1746
|
+
}
|
|
1747
|
+
function keepDataRemovedEntries(merged, lock) {
|
|
1748
|
+
if (merged === void 0 || lock === void 0 || merged.removedGuids.length === 0) return {};
|
|
1749
|
+
const gone = new Set(merged.removedGuids);
|
|
1750
|
+
const out = {};
|
|
1751
|
+
for (const [key, entry] of Object.entries(lock.objects)) {
|
|
1752
|
+
if (entry.guid !== void 0 && gone.has(entry.guid)) out[key] = { guid: entry.guid };
|
|
1753
|
+
}
|
|
1754
|
+
return out;
|
|
1755
|
+
}
|
|
1756
|
+
async function refuseOtherProfileEphemeral(state, auth) {
|
|
1757
|
+
const current = auth.profile?.name ?? DEFAULT_PROFILE;
|
|
1758
|
+
const underOther = Object.keys(state.environments).some((k) => k.includes("/") && k.slice(0, k.indexOf("/")) !== current);
|
|
1759
|
+
if (!underOther) return;
|
|
1760
|
+
const refusal = otherCredentialRefusal(
|
|
1761
|
+
state,
|
|
1762
|
+
auth,
|
|
1763
|
+
`or pass \`--name <display>\` to create a separate ephemeral under this one. Nothing was created`,
|
|
1764
|
+
{ hintFor: { command: "deploy" } }
|
|
1765
|
+
);
|
|
1766
|
+
if (refusal === void 0) return;
|
|
1767
|
+
const { reachableFirst } = await import("./env-target-O4HRKTGZ.js");
|
|
1768
|
+
throw await reachableFirst(refusal, auth);
|
|
1769
|
+
}
|
|
1770
|
+
async function refuseKeepDataElsewhere(state, auth) {
|
|
1771
|
+
const mine = environmentKey(auth);
|
|
1772
|
+
const sameHost = mine.slice(0, mine.lastIndexOf("/") + 1);
|
|
1773
|
+
if (!Object.keys(state.environments).some((k) => k !== mine && k.startsWith(sameHost))) return;
|
|
1774
|
+
const refusal = otherCredentialRefusal(
|
|
1775
|
+
state,
|
|
1776
|
+
auth,
|
|
1777
|
+
`or run without \`--keep-data\` to create a separate, freshly seeded ephemeral in workspace ${auth.workspaceId}. Nothing was created`,
|
|
1778
|
+
{ hintFor: { command: "deploy" } }
|
|
1779
|
+
);
|
|
1780
|
+
if (refusal === void 0) return;
|
|
1781
|
+
const { reachableFirst } = await import("./env-target-O4HRKTGZ.js");
|
|
1782
|
+
throw await reachableFirst(refusal, auth);
|
|
1783
|
+
}
|
|
1784
|
+
async function reconcileRename(err, auth, name, display) {
|
|
1785
|
+
if (!(err instanceof Error) || !statesOutcomeUnknown(err.message)) return { renamed: false, unknown: false };
|
|
1786
|
+
try {
|
|
1787
|
+
const now = await getEphemeral(auth, { parentWorkspaceId: auth.workspaceId, name });
|
|
1788
|
+
if (now === null) return { renamed: false, unknown: true };
|
|
1789
|
+
return { renamed: now.display === display, unknown: false };
|
|
1790
|
+
} catch {
|
|
1791
|
+
return { renamed: false, unknown: true };
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
var CREATE_GUARD_STALE_MS = 15 * 6e4;
|
|
1795
|
+
async function takeCreateGuard(dir, args) {
|
|
1796
|
+
const { tryLock } = await import("./file-lock-PT74GWYW.js");
|
|
1797
|
+
const lockPath = join(dir, ".xano", "deploy.lock");
|
|
1798
|
+
const lock = tryLock(lockPath, { staleMs: CREATE_GUARD_STALE_MS });
|
|
1799
|
+
if ("release" in lock) return lock;
|
|
1800
|
+
const shown = displayPath(lockPath);
|
|
1801
|
+
const retry = retryCommand(args, { creates: true });
|
|
1802
|
+
const holder = lock.heldBy;
|
|
1803
|
+
const { hostname } = await import("os");
|
|
1804
|
+
const where = holder?.host !== void 0 && holder.host !== hostname() ? ` on ${holder.host}` : "";
|
|
1805
|
+
const pid = holder === void 0 ? "" : ` (pid ${holder.pid}${where})`;
|
|
1806
|
+
throw new CliError(
|
|
1807
|
+
"SDK_ERROR",
|
|
1808
|
+
`Another deploy in this project${pid} is deciding which ephemeral to deploy to, so this one did not start.
|
|
1809
|
+
Nothing was created. Once it has finished, run \`${retry.command}\` again.${withheldNote(retry.withheld)}
|
|
1810
|
+
If no deploy is running in this project, its guard was left behind: remove \`${shown}\` and run it again.`,
|
|
1811
|
+
{ exitCode: 8, details: { reason: "deploy-in-progress", rerun: retry.command, lockFile: shown } }
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
async function deployEphemeral(auth, ctx) {
|
|
1815
|
+
const dir = process.cwd();
|
|
1816
|
+
const { assertReadWritable } = await import("./writable-VA7OISSK.js");
|
|
1817
|
+
assertReadWritable(ephemeralStatePath(dir));
|
|
1818
|
+
const guard = await takeCreateGuard(dir, ctx.args);
|
|
1819
|
+
try {
|
|
1820
|
+
return await deployEphemeralGuarded(auth, ctx, guard);
|
|
1821
|
+
} finally {
|
|
1822
|
+
guard.release();
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
async function deployEphemeralGuarded(auth, ctx, createGuard) {
|
|
1826
|
+
const { archive, bundle, content, source, args, startedAt, publishStatic } = ctx;
|
|
1827
|
+
let lockCommitted = false;
|
|
1828
|
+
const commitLock = (pruned) => {
|
|
1829
|
+
if (lockCommitted) return;
|
|
1830
|
+
lockCommitted = true;
|
|
1831
|
+
ctx.commitLock?.(pruned === void 0 ? void 0 : { pruned });
|
|
1832
|
+
};
|
|
1833
|
+
const parentWorkspaceId = auth.workspaceId;
|
|
1834
|
+
const dir = process.cwd();
|
|
1835
|
+
const { assertReadWritable, assertWritable } = await import("./writable-VA7OISSK.js");
|
|
1836
|
+
assertReadWritable(ephemeralStatePath(dir));
|
|
1837
|
+
if (ctx.lockPath !== void 0) assertWritable(ctx.lockPath, "build without a lock with `--no-lock`");
|
|
1838
|
+
const state = readEphemeralState(dir);
|
|
1839
|
+
const stored = getEnvironment(state, auth);
|
|
1840
|
+
if (stored === void 0 && args.name === void 0) {
|
|
1841
|
+
await refuseOtherProfileEphemeral(state, auth);
|
|
1842
|
+
if (args.keepData && !args.reset) await refuseKeepDataElsewhere(state, auth);
|
|
1843
|
+
}
|
|
1844
|
+
let target = null;
|
|
1845
|
+
if (stored) {
|
|
1846
|
+
const existing = await lookupEphemeral(auth, stored.name);
|
|
1847
|
+
if (existing && !isExpired(existing.expiresAt)) target = existing;
|
|
1848
|
+
}
|
|
1849
|
+
const created = target === null;
|
|
1850
|
+
if (!created) createGuard.release();
|
|
1851
|
+
const flags = contextFlags(args);
|
|
1852
|
+
if (target !== null && target.state !== "ok") {
|
|
1853
|
+
const pending = target;
|
|
1854
|
+
target = await withSpinner(
|
|
1855
|
+
`Waiting for ${pending.name} to become ready\u2026`,
|
|
1856
|
+
() => waitUntilReady(auth, { parentWorkspaceId, name: pending.name })
|
|
1857
|
+
).catch((err) => {
|
|
1858
|
+
throw notReadyError(err, pending.name, flags, false, retryCommand(args));
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
const notApplied = [];
|
|
1862
|
+
if (target === null) {
|
|
1863
|
+
refuseStaticHostAReplaceClears(args, true);
|
|
1864
|
+
const { docTokenRefusalIfReplaced } = await import("./cli.js");
|
|
1865
|
+
const docs = docTokenRefusalIfReplaced(KEEP_DATA_REPLACES[stored !== void 0 ? "recreated" : "new"]);
|
|
1866
|
+
if (docs !== void 0) throw new Error(docs);
|
|
1867
|
+
const recorded = stored?.display !== void 0 && stored.display !== stored.name ? stored.display : void 0;
|
|
1868
|
+
const display = args.name ?? recorded ?? deriveDisplay(bundle, dir, source);
|
|
1869
|
+
step(`Deploying ${ctx.shownSource ?? source} \u2192 new ephemeral "${display}"`);
|
|
1870
|
+
discloseWriteTarget(ephemeralParent(auth));
|
|
1871
|
+
announceArm(
|
|
1872
|
+
selectDeployArm({ keepData: args.keepData, reset: args.reset, created: true, hadRecord: stored !== void 0, priorFilled: void 0, url: "" }),
|
|
1873
|
+
"ephemeral",
|
|
1874
|
+
false,
|
|
1875
|
+
content.length > 0
|
|
1876
|
+
);
|
|
1877
|
+
const machine = isMachineOutput(args);
|
|
1878
|
+
const listCommand = `xanosdk ephemeral list${flags}${machine ? " --json" : ""}`;
|
|
1879
|
+
const creating = activeOperation() === void 0 ? registerOperation({
|
|
1880
|
+
machine,
|
|
1881
|
+
resolveWith: listCommand,
|
|
1882
|
+
what: `the create of ephemeral "${display}"`,
|
|
1883
|
+
snapshot: () => ({ ok: false, outcome: "unknown", ephemeral: { display }, resolveWith: listCommand })
|
|
1884
|
+
}) : void 0;
|
|
1885
|
+
creating?.markWriteSent();
|
|
1886
|
+
const fresh = await createEphemeral(auth, {
|
|
1887
|
+
parentWorkspaceId,
|
|
1888
|
+
display,
|
|
1889
|
+
expiresHours: args.expiresHours
|
|
1890
|
+
}).finally(() => creating?.clear()).catch(async (err) => {
|
|
1891
|
+
if (isEphemeralDisabled(err)) throw ephemeralDisabledError(source);
|
|
1892
|
+
if (err.status === 404) {
|
|
1893
|
+
const { refuseUnreachableWorkspace } = await import("./workspace-binding-56PIIPCP.js");
|
|
1894
|
+
await refuseUnreachableWorkspace(auth);
|
|
1895
|
+
}
|
|
1896
|
+
throw unknownCreateOutcome(err, display, flags, retryCommand(args, { creates: true }));
|
|
1897
|
+
});
|
|
1898
|
+
try {
|
|
1899
|
+
setEnvironment(dir, auth, {
|
|
1900
|
+
name: fresh.name,
|
|
1901
|
+
display: fresh.display ?? display,
|
|
1902
|
+
url: fresh.url ?? "",
|
|
1903
|
+
expires_at: fresh.expiresAt,
|
|
1904
|
+
...auth.profile !== void 0 && credentialFileOf(args) !== void 0 ? { credential_file: credentialFileOf(args) } : {}
|
|
1905
|
+
});
|
|
1906
|
+
} catch (err) {
|
|
1907
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1908
|
+
throw new Error(`${reason}
|
|
1909
|
+
${createdButFailedNote(fresh.name, false, args, false, { untracked: true })}`);
|
|
1910
|
+
}
|
|
1911
|
+
noteLanded({
|
|
1912
|
+
said: `Created ephemeral ${fresh.name} (tracked in .xano/ephemeral.json).`,
|
|
1913
|
+
ifCancelled: createdButFailedNote(fresh.name, false, args),
|
|
1914
|
+
fields: { ephemeral: { name: fresh.name, display: fresh.display ?? display }, created: true }
|
|
1915
|
+
});
|
|
1916
|
+
createGuard.release();
|
|
1917
|
+
target = await withSpinner(
|
|
1918
|
+
`Waiting for ${fresh.name} to become ready\u2026`,
|
|
1919
|
+
() => waitUntilReady(auth, { parentWorkspaceId, name: fresh.name })
|
|
1920
|
+
).catch((err) => {
|
|
1921
|
+
throw notReadyError(err, fresh.name, flags, true, retryCommand(args));
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
const baseUrl = target.url;
|
|
1925
|
+
if (baseUrl === void 0) {
|
|
1926
|
+
const retry = retryCommand(args);
|
|
1927
|
+
throw new Error(`Ephemeral "${target.name}" has no base URL yet \u2014 try \`${retry.command}\` again.${withheldNote(retry.withheld)}`);
|
|
1928
|
+
}
|
|
1929
|
+
const renaming = { name: target.name, display: target.display };
|
|
1930
|
+
const renameIfAsked = async () => {
|
|
1931
|
+
const display = args.name;
|
|
1932
|
+
if (display === void 0 || display === renaming.display) return void 0;
|
|
1933
|
+
await describeWrite(
|
|
1934
|
+
{ what: `the rename of ephemeral "${renaming.name}"`, resolveWith: `xanosdk ephemeral get ${shellQuote(renaming.name)}${flags}` },
|
|
1935
|
+
() => renameEphemeral(auth, { parentWorkspaceId, name: renaming.name, display })
|
|
1936
|
+
);
|
|
1937
|
+
const rec = getEnvironment(readEphemeralState(dir), auth);
|
|
1938
|
+
if (rec !== void 0 && rec.name === renaming.name) setEnvironment(dir, auth, { ...rec, display });
|
|
1939
|
+
detail(`Renamed to "${display}".`);
|
|
1940
|
+
return display;
|
|
1941
|
+
};
|
|
1942
|
+
let renamedTo;
|
|
1943
|
+
const arm = selectDeployArm({
|
|
1944
|
+
keepData: args.keepData,
|
|
1945
|
+
reset: args.reset,
|
|
1946
|
+
created,
|
|
1947
|
+
hadRecord: stored !== void 0,
|
|
1948
|
+
priorFilled: stored?.filled,
|
|
1949
|
+
url: baseUrl
|
|
1950
|
+
});
|
|
1951
|
+
await refuseEnvClearedByFallbackReplace(arm);
|
|
1952
|
+
if (!created) {
|
|
1953
|
+
step(
|
|
1954
|
+
`Deploying ${ctx.shownSource ?? source} \u2192 ${namedEphemeral(target)} (refresh, ${arm.arm === "merge" ? "keeping data" : "full replace"})`
|
|
1955
|
+
);
|
|
1956
|
+
discloseWriteTarget(ephemeralParent(auth));
|
|
1957
|
+
await gateEntrySwap(args, namedEphemeral(target), stored?.entry, ctx.entry, arm.arm);
|
|
1958
|
+
if (arm.arm === "replace") refuseStaticHostAReplaceClears(args, true);
|
|
1959
|
+
else await assertStaticHostExists(args, auth, { baseUrl, workspaceId: 1 });
|
|
1960
|
+
if (arm.arm === "replace") {
|
|
1961
|
+
const { gateForeignReplace } = await import("./keep-data-merge-7MPLVAVI.js");
|
|
1962
|
+
const { allowForeignDeletes, confirm } = await mergeGuards(args, void 0);
|
|
1963
|
+
await gateForeignReplace({
|
|
1964
|
+
auth,
|
|
1965
|
+
baseUrl,
|
|
1966
|
+
workspaceId: 1,
|
|
1967
|
+
label: namedEphemeral(target),
|
|
1968
|
+
bundle,
|
|
1969
|
+
kind: "ephemeral",
|
|
1970
|
+
rerun: rerunOf(args),
|
|
1971
|
+
...allowForeignDeletes === void 0 ? {} : { allowForeignDeletes },
|
|
1972
|
+
...confirm === void 0 ? {} : { confirm },
|
|
1973
|
+
landedGuids: new Set(
|
|
1974
|
+
Object.values(ephemeralLandedOn(dir, ephemeralLandingKey(auth.instance, target.name)) ?? {}).map((e) => e.guid)
|
|
1975
|
+
)
|
|
1976
|
+
});
|
|
1977
|
+
}
|
|
1978
|
+
if (args.expiresHours !== void 0) {
|
|
1979
|
+
notApplied.push("--expires-hours");
|
|
1980
|
+
warn(
|
|
1981
|
+
`--expires-hours ${args.expiresHours} was not applied: ${target.name} already exists, and an ephemeral's lifetime is set only when it is created` + (target.expiresAt !== void 0 ? ` (this one expires ${String(target.expiresAt)})` : "") + `. To start a new one with that lifetime, run \`xanosdk ephemeral delete ${target.name}${contextFlags(args)}${offTerminalYes()}\` and deploy again.`,
|
|
1982
|
+
"ephemeral.expires-not-applied"
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
if (!created) announceArm(
|
|
1987
|
+
arm,
|
|
1988
|
+
"ephemeral",
|
|
1989
|
+
arm.arm === "replace" && arm.skipped === "never-filled" && Object.keys(ephemeralLandedOn(dir, ephemeralLandingKey(auth.instance, target.name)) ?? {}).length > 0,
|
|
1990
|
+
content.length > 0
|
|
1991
|
+
);
|
|
1992
|
+
const doubted = stored?.static_down === true || stored?.static_unchecked === true ? stored.static_url : void 0;
|
|
1993
|
+
const rechecked = !created && arm.arm === "replace" ? await recheckDoubtedFrontend(
|
|
1994
|
+
await readStaticTeardown(auth, { baseUrl, workspaceId: 1 }, stored?.static_down === true ? void 0 : stored?.static_url),
|
|
1995
|
+
doubted
|
|
1996
|
+
) : void 0;
|
|
1997
|
+
const teardownRead = rechecked?.read;
|
|
1998
|
+
const staticTeardown = teardownRead?.teardown;
|
|
1999
|
+
const previousStaticUrl = created ? void 0 : teardownRead?.live?.[0] ?? stored?.static_url;
|
|
2000
|
+
const servingBefore = created ? void 0 : teardownRead?.live !== void 0 ? teardownRead.live[0] : stored?.static_down === true ? void 0 : stored?.static_url;
|
|
2001
|
+
const staticDown = servingBefore === void 0;
|
|
2002
|
+
setEnvironment(dir, auth, {
|
|
2003
|
+
name: target.name,
|
|
2004
|
+
display: target.display ?? args.name ?? target.name,
|
|
2005
|
+
url: baseUrl,
|
|
2006
|
+
expires_at: target.expiresAt,
|
|
2007
|
+
...!created && stored?.filled !== void 0 && stored.filled.url === baseUrl ? { filled: stored.filled } : {},
|
|
2008
|
+
...previousStaticUrl !== void 0 ? { static_url: previousStaticUrl } : {},
|
|
2009
|
+
...previousStaticUrl !== void 0 && staticDown ? { static_down: true } : {},
|
|
2010
|
+
...previousStaticUrl !== void 0 && rechecked?.unchecked === true ? { static_unchecked: true } : {},
|
|
2011
|
+
// Which credential file reaches it, for the other-profile remedy.
|
|
2012
|
+
...auth.profile !== void 0 && credentialFileOf(args) !== void 0 ? { credential_file: credentialFileOf(args) } : {},
|
|
2013
|
+
// Which entry's backend stands here, for a remedy that redeploys it
|
|
2014
|
+
// (`env set`'s) and the entry-swap gate. Kept as it was until this import
|
|
2015
|
+
// lands — a refused one changed nothing — and rewritten once it has.
|
|
2016
|
+
...created ? ctx.entry !== void 0 ? { entry: ctx.entry } : {} : stored?.entry !== void 0 ? { entry: stored.entry } : {}
|
|
2017
|
+
});
|
|
2018
|
+
if (teardownRead !== void 0 && publishStatic === void 0) {
|
|
2019
|
+
warnStaticTeardown(teardownRead, namedEphemeral(target), {
|
|
2020
|
+
publishing: false,
|
|
2021
|
+
// The way to merge from THIS command line: `--keep-data` when it was not
|
|
2022
|
+
// passed, dropping `--reset` when that is what overrode it. When neither
|
|
2023
|
+
// can merge (nothing proves the environment holds an earlier deploy),
|
|
2024
|
+
// the only way back is a publish afterwards.
|
|
2025
|
+
keepHint: !args.keepData ? "pass `--keep-data` to merge, which leaves it serving" : arm.arm === "replace" && arm.skipped === "reset" ? "drop `--reset` to merge, which leaves it serving" : `publish it again afterwards with \`xanosdk publish <dir> --to ephemeral:${shellQuote(target.name)}${contextFlags(args)}\``
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
recordDeployed(dir, "ephemeral");
|
|
2029
|
+
let importFailure;
|
|
2030
|
+
let merged;
|
|
2031
|
+
try {
|
|
2032
|
+
if (arm.arm === "merge") {
|
|
2033
|
+
const { mergeKeepingData } = await import("./keep-data-merge-7MPLVAVI.js");
|
|
2034
|
+
merged = await mergeKeepingData({
|
|
2035
|
+
auth,
|
|
2036
|
+
baseUrl,
|
|
2037
|
+
workspaceId: 1,
|
|
2038
|
+
label: namedEphemeral(target),
|
|
2039
|
+
bundle,
|
|
2040
|
+
content,
|
|
2041
|
+
files: archiveHostedFiles(archive),
|
|
2042
|
+
// An ephemeral has no `--dry-run` to preview again: what it holds is read.
|
|
2043
|
+
stateCheck: ephemeralStateCheck(namedEphemeral(target), target.name, flags),
|
|
2044
|
+
envSetTo: ` --to ${shellQuote(`ephemeral:${target.name}`)}${flags}`,
|
|
2045
|
+
kind: "ephemeral",
|
|
2046
|
+
rerun: rerunOf(args),
|
|
2047
|
+
// A rename here changes what the merge's own failure lines call it.
|
|
2048
|
+
beforeWrite: async () => {
|
|
2049
|
+
renamedTo = await renameIfAsked();
|
|
2050
|
+
if (renamedTo === void 0) return void 0;
|
|
2051
|
+
const label = namedEphemeral({ name: renaming.name, display: renamedTo });
|
|
2052
|
+
return { label, stateCheck: ephemeralStateCheck(label, renaming.name, flags) };
|
|
2053
|
+
},
|
|
2054
|
+
...await mergeGuards(args, ctx.lock, { bundle, compiled: ctx.compiled === true }),
|
|
2055
|
+
// Provenance for the removal lines: what this project landed here.
|
|
2056
|
+
landedGuids: new Set(
|
|
2057
|
+
Object.values(ephemeralLandedOn(process.cwd(), ephemeralLandingKey(auth.instance, target.name)) ?? {}).map(
|
|
2058
|
+
(e) => e.guid
|
|
2059
|
+
)
|
|
2060
|
+
),
|
|
2061
|
+
storedColumns: storedColumnsOf(ephemeralLandedOn(process.cwd(), ephemeralLandingKey(auth.instance, target.name)))
|
|
2062
|
+
});
|
|
2063
|
+
} else {
|
|
2064
|
+
const label = namedEphemeral(target);
|
|
2065
|
+
const stateCheck = ephemeralStateCheck(label, target.name, flags);
|
|
2066
|
+
await withSpinner(
|
|
2067
|
+
IMPORTING_LABEL,
|
|
2068
|
+
() => (
|
|
2069
|
+
// An env's own internal workspace id is always 1.
|
|
2070
|
+
xanosdkImport(auth, {
|
|
2071
|
+
baseUrl,
|
|
2072
|
+
workspaceId: 1,
|
|
2073
|
+
label,
|
|
2074
|
+
archive,
|
|
2075
|
+
mode: "replace",
|
|
2076
|
+
dryRun: false,
|
|
2077
|
+
preserveGuids: true,
|
|
2078
|
+
stateCheck
|
|
2079
|
+
})
|
|
2080
|
+
)
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
} catch (err) {
|
|
2084
|
+
if (!lockCommitted) ctx.warnOrphansUncommitted?.();
|
|
2085
|
+
if (err instanceof Error && err.name === "KeepDataDeclined") throw err;
|
|
2086
|
+
if (err instanceof XanoSdkImportRefusal && err.retryable) {
|
|
2087
|
+
const { importInProgressError } = await import("./keep-data-merge-7MPLVAVI.js");
|
|
2088
|
+
const retry = retryCommand(args);
|
|
2089
|
+
throw importInProgressError(namedEphemeral(target), { command: retry.command, note: withheldNote(retry.withheld) }, err);
|
|
2090
|
+
}
|
|
2091
|
+
const { withSignatureHint } = await import("./release-command-N4ZPKMHR.js");
|
|
2092
|
+
importFailure = withSignatureHint(err, args.bundle);
|
|
2093
|
+
}
|
|
2094
|
+
if (arm.arm === "merge" && renamedTo !== void 0) target = { ...target, display: renamedTo };
|
|
2095
|
+
if (importFailure !== void 0) {
|
|
2096
|
+
const unknown = isUnknownImportOutcome(importFailure);
|
|
2097
|
+
const nothingSent = importFailure instanceof ImportNotSentError;
|
|
2098
|
+
const filledHere = stored?.filled !== void 0 && !created && stored.filled.url === baseUrl;
|
|
2099
|
+
importFailure = noteKeptFilledMarker(importFailure, filledHere, args);
|
|
2100
|
+
if (!created && !filledHere) importFailure = noteReplaceRerun(importFailure, args);
|
|
2101
|
+
const warnedDown = staticTeardown !== void 0 && staticTeardown.urls.length > 0 ? staticTeardown.urls : servingBefore !== void 0 ? [servingBefore] : [];
|
|
2102
|
+
if (arm.arm === "replace" && !nothingSent && publishStatic === void 0 && warnedDown.length > 0) {
|
|
2103
|
+
const states = await withSpinner(
|
|
2104
|
+
"Checking the frontend\u2026",
|
|
2105
|
+
() => readFrontendsAfterFailure(auth, { baseUrl, workspaceId: 1 }, warnedDown)
|
|
2106
|
+
);
|
|
2107
|
+
const note = staticAfterFailedReplace(warnedDown, states, {
|
|
2108
|
+
unknown,
|
|
2109
|
+
publish: `xanosdk publish <dir> --to ephemeral:${shellQuote(target.name)}${contextFlags(args)}`
|
|
2110
|
+
});
|
|
2111
|
+
const rec = getEnvironment(readEphemeralState(dir), auth);
|
|
2112
|
+
if (rec?.static_url !== void 0 && note.down.includes(rec.static_url)) {
|
|
2113
|
+
setEnvironment(dir, auth, { ...rec, static_down: true });
|
|
2114
|
+
} else if (rec?.static_url !== void 0 && states[rec.static_url] === "unknown") {
|
|
2115
|
+
setEnvironment(dir, auth, { ...rec, static_unchecked: true });
|
|
2116
|
+
}
|
|
2117
|
+
if (importFailure instanceof Error) importFailure.message = `${importFailure.message}
|
|
2118
|
+
${note.text}`;
|
|
2119
|
+
else warn(note.text, "static.teardown");
|
|
2120
|
+
}
|
|
2121
|
+
if (publishStatic !== void 0 && arm.arm === "replace" && !nothingSent) {
|
|
2122
|
+
warn("The import failed \u2014 republishing the frontend so the static host keeps serving.", "static.republish");
|
|
2123
|
+
const republished = await publishStatic({
|
|
2124
|
+
baseUrl,
|
|
2125
|
+
label: namedEphemeral(target),
|
|
2126
|
+
name: target.name,
|
|
2127
|
+
republish: true
|
|
2128
|
+
});
|
|
2129
|
+
if (republished.url !== void 0) recordStaticUrl(dir, auth, republished.url);
|
|
2130
|
+
let frontends;
|
|
2131
|
+
if (republished.url === void 0 && warnedDown.length > 0) {
|
|
2132
|
+
const states = await withSpinner(
|
|
2133
|
+
"Checking the frontend\u2026",
|
|
2134
|
+
() => readFrontendsAfterFailure(auth, { baseUrl, workspaceId: 1 }, warnedDown)
|
|
2135
|
+
);
|
|
2136
|
+
frontends = { warned: warnedDown, states };
|
|
2137
|
+
const rec = getEnvironment(readEphemeralState(dir), auth);
|
|
2138
|
+
if (rec?.static_url !== void 0 && states[rec.static_url] === "down") {
|
|
2139
|
+
setEnvironment(dir, auth, { ...rec, static_down: true });
|
|
2140
|
+
} else if (rec?.static_url !== void 0 && states[rec.static_url] === "unknown") {
|
|
2141
|
+
setEnvironment(dir, auth, { ...rec, static_unchecked: true });
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
const note = republishNote(republished, unknown, frontends);
|
|
2145
|
+
if (importFailure instanceof Error) importFailure.message = `${importFailure.message}
|
|
2146
|
+
${note}`;
|
|
2147
|
+
else warn(note, "static.republish");
|
|
2148
|
+
}
|
|
2149
|
+
importFailure = explainDuplicateArchiveGuid(importFailure, args.file, contextFlags(args));
|
|
2150
|
+
if (created && importFailure instanceof Error) {
|
|
2151
|
+
const landedUnresolved = importFailure instanceof HostedFilesUnresolvedError && importFailure.applied;
|
|
2152
|
+
const futile = isFetchedDuplicateGuid(importFailure, args.file);
|
|
2153
|
+
importFailure.message += `
|
|
2154
|
+
${createdButFailedNote(target.name, unknown, args, landedUnresolved, { futile })}`;
|
|
2155
|
+
}
|
|
2156
|
+
throw importFailure;
|
|
2157
|
+
}
|
|
2158
|
+
const removedEntries = keepDataRemovedEntries(merged, ctx.lock);
|
|
2159
|
+
if (ctx.removedOut !== void 0 && Object.keys(removedEntries).length > 0) ctx.removedOut.removed = removedEntries;
|
|
2160
|
+
if (ctx.removedOut !== void 0 && merged?.storedColumns !== void 0) ctx.removedOut.storedColumns = merged.storedColumns;
|
|
2161
|
+
if (ctx.removedOut !== void 0 && merged !== void 0 && merged.removedGuids.length > 0) ctx.removedOut.removedGuids = merged.removedGuids;
|
|
2162
|
+
commitLock(Object.keys(removedEntries).length > 0 ? new Set(Object.keys(removedEntries)) : void 0);
|
|
2163
|
+
const landedRelease = /^release:(.+)$/.exec(ctx.compiled === true ? "" : args.file ?? "")?.[1];
|
|
2164
|
+
markEnvironmentFilled(dir, auth, baseUrl, {
|
|
2165
|
+
...landedRelease === void 0 ? {} : { release: landedRelease },
|
|
2166
|
+
...ctx.entry === void 0 ? {} : { entry: ctx.entry }
|
|
2167
|
+
});
|
|
2168
|
+
if (arm.arm === "replace" && previousStaticUrl !== void 0) {
|
|
2169
|
+
const landed = getEnvironment(readEphemeralState(dir), auth);
|
|
2170
|
+
if (landed !== void 0) setEnvironment(dir, auth, { ...landed, static_down: true });
|
|
2171
|
+
}
|
|
2172
|
+
const noteDeployed = (landedOn) => noteLanded({
|
|
2173
|
+
said: `Deployed ${namedEphemeral(landedOn)} (${arm.arm === "merge" ? "keeping its data" : "full replace"}).`,
|
|
2174
|
+
fields: { ephemeral: { name: landedOn.name, display: landedOn.display }, url: baseUrl, created }
|
|
2175
|
+
});
|
|
2176
|
+
noteDeployed(target);
|
|
2177
|
+
if (arm.arm === "replace" && !created) {
|
|
2178
|
+
try {
|
|
2179
|
+
const renamed = await renameIfAsked();
|
|
2180
|
+
if (renamed !== void 0) target = { ...target, display: renamed };
|
|
2181
|
+
noteDeployed(target);
|
|
2182
|
+
} catch (err) {
|
|
2183
|
+
const reconciled = await reconcileRename(err, auth, target.name, args.name ?? "");
|
|
2184
|
+
if (reconciled.renamed) {
|
|
2185
|
+
const rec = getEnvironment(readEphemeralState(dir), auth);
|
|
2186
|
+
if (rec !== void 0 && rec.name === target.name) setEnvironment(dir, auth, { ...rec, display: args.name });
|
|
2187
|
+
target = { ...target, display: args.name };
|
|
2188
|
+
detail(`Renamed to "${args.name}" (its answer was lost; read back from the ephemeral).`);
|
|
2189
|
+
} else {
|
|
2190
|
+
const retry = retryCommand(args);
|
|
2191
|
+
const reason = (err instanceof Error ? err.message : String(err)).replace(SENT_AFTERMATH, "").trim().replace(/[.\s]+$/, "");
|
|
2192
|
+
const state2 = reconciled.unknown ? `Whether it took effect is unknown \u2014 \`xanosdk ephemeral get ${target.name}${contextFlags(args)}\` shows its name; if it is not "${args.name ?? ""}", \`${retry.command}\`` : `Its name is unchanged; \`${retry.command}\``;
|
|
2193
|
+
warn(
|
|
2194
|
+
`The deploy landed, but renaming ${target.name} to "${args.name ?? ""}" failed: ${reason}. ${state2} deploys again and renames it.${withheldNote(retry.withheld)}`,
|
|
2195
|
+
"ephemeral.rename-failed"
|
|
2196
|
+
);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
const urlChanged = created || stored !== void 0 && stored.url !== baseUrl;
|
|
2201
|
+
if (urlChanged) {
|
|
2202
|
+
success(`E${namedEphemeral(target).slice(1)} deployed${elapsedSuffix(startedAt)}`);
|
|
2203
|
+
info("New ephemeral URL:");
|
|
2204
|
+
link(baseUrl);
|
|
2205
|
+
} else {
|
|
2206
|
+
const kept = arm.arm === "merge" ? ", keeping its data" : "";
|
|
2207
|
+
success(`Refreshed ${namedEphemeral(target)}${kept}${urlsNote(merged)}${elapsedSuffix(startedAt)}`);
|
|
2208
|
+
link(baseUrl);
|
|
2209
|
+
}
|
|
2210
|
+
detail(`Expires ${formatExpiration(target.expiresAt)}`);
|
|
2211
|
+
detail(`Manage with \`xanosdk ephemeral get ${target.name}${contextFlags(args)}\``);
|
|
2212
|
+
detail(`Open the builder with \`xanosdk impersonate ephemeral:${target.name}${contextFlags(args)}\``);
|
|
2213
|
+
const published = await publishStatic?.({ baseUrl, label: namedEphemeral(target), name: target.name });
|
|
2214
|
+
if (published?.url !== void 0) {
|
|
2215
|
+
noteStaticUrlChange(
|
|
2216
|
+
servingBefore,
|
|
2217
|
+
published.url,
|
|
2218
|
+
arm,
|
|
2219
|
+
servingBefore === void 0 && !created && stored?.static_down === true ? stored.static_url : void 0
|
|
2220
|
+
);
|
|
2221
|
+
recordStaticUrl(dir, auth, published.url);
|
|
2222
|
+
}
|
|
2223
|
+
if (published?.error !== void 0) noteStaticTakenDown(staticTeardown, published.completed === "unknown");
|
|
2224
|
+
let microservicesError;
|
|
2225
|
+
const microservices = await verifyMicroservices(auth, baseUrl, {
|
|
2226
|
+
...microserviceCheck(args),
|
|
2227
|
+
name: target.name,
|
|
2228
|
+
flags: contextFlags(args),
|
|
2229
|
+
onReadError: (e) => microservicesError = e
|
|
2230
|
+
});
|
|
2231
|
+
if (created) await warnSameDisplayName(auth, parentWorkspaceId, target, stored?.name, contextFlags(args));
|
|
2232
|
+
return {
|
|
2233
|
+
kind: "ephemeral",
|
|
2234
|
+
destination: backendDestinationPayload(auth, {
|
|
2235
|
+
kind: "ephemeral",
|
|
2236
|
+
name: target.name,
|
|
2237
|
+
url: baseUrl,
|
|
2238
|
+
...target.display !== void 0 && target.display !== target.name ? { display: target.display } : {}
|
|
2239
|
+
}),
|
|
2240
|
+
profile: auth.profile ?? null,
|
|
2241
|
+
url: baseUrl,
|
|
2242
|
+
ephemeral: { name: target.name, display: target.display, expiresAt: target.expiresAt },
|
|
2243
|
+
created,
|
|
2244
|
+
...dataFields(arm),
|
|
2245
|
+
...mergeLossFields(merged),
|
|
2246
|
+
// Beside `--static` too: the new frontend may be at a new URL, and the one a
|
|
2247
|
+
// bookmark or a shared link holds is what this names.
|
|
2248
|
+
...staticRemovedField(staticTeardown, published?.url),
|
|
2249
|
+
...notApplied.length > 0 ? { notApplied } : {},
|
|
2250
|
+
...microservices ? { microservices } : {},
|
|
2251
|
+
...microservicesError !== void 0 ? { microservicesError } : {}
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
async function deployStaticTo(dir, auth, target, env, explicit, host, skipLiveness, routing, dirLabel, opts = {}) {
|
|
2255
|
+
const { deployStaticHost } = await import("./static-host-B2Y6AG3W.js");
|
|
2256
|
+
const where = target.label && target.label !== "" ? target.label : `workspace #${target.workspaceId}`;
|
|
2257
|
+
if (target.announced !== true) step(`Deploying static frontend ${dir} \u2192 ${where}${host ? ` (host: ${host})` : ""}`);
|
|
2258
|
+
const sh = await describeWrite(
|
|
2259
|
+
{
|
|
2260
|
+
what: `the static publish to ${where}`,
|
|
2261
|
+
check: opts.rerun === void 0 ? `Open ${where}'s frontend to see which build it serves before retrying.` : `A publish replaces the host's build, so running it again settles it either way: \`${opts.rerun}\`.`
|
|
2262
|
+
},
|
|
2263
|
+
() => deployStaticHost({
|
|
2264
|
+
host,
|
|
2265
|
+
dir,
|
|
2266
|
+
workspaceId: target.workspaceId,
|
|
2267
|
+
baseUrl: target.baseUrl,
|
|
2268
|
+
accessToken: auth.access_token,
|
|
2269
|
+
env,
|
|
2270
|
+
routing,
|
|
2271
|
+
...dirLabel === void 0 ? {} : { label: dirLabel },
|
|
2272
|
+
...opts.serverRendered === true ? { serverRendered: true } : {}
|
|
2273
|
+
})
|
|
2274
|
+
);
|
|
2275
|
+
if (sh.serverBundle === "omitted") {
|
|
2276
|
+
detail(
|
|
2277
|
+
"Server half (.xano-ssr/) not uploaded: this host serves files only. `xanosdk deploy --local-engine --static` renders it."
|
|
2278
|
+
);
|
|
2279
|
+
}
|
|
2280
|
+
if (sh.hiddenUnserved !== void 0) {
|
|
2281
|
+
const shown = sh.hiddenUnserved.slice(0, 5).join(", ") + (sh.hiddenUnserved.length > 5 ? `, and ${sh.hiddenUnserved.length - 5} more` : "");
|
|
2282
|
+
const wellKnown = sh.hiddenUnserved.some((p) => p.split("/").includes(".well-known"));
|
|
2283
|
+
warn(
|
|
2284
|
+
`Not served: ${shown} \u2014 the static host serves no path with a dot segment, so ${sh.hiddenUnserved.length === 1 ? "it answers" : "they answer"} with the fallback page${wellKnown ? " (a `.well-known/` file such as assetlinks.json or apple-app-site-association cannot be hosted here; serve it from the backend or another host)" : ""}.`,
|
|
2285
|
+
"static.hidden-unserved"
|
|
2286
|
+
);
|
|
2287
|
+
}
|
|
2288
|
+
if (sh.skippedLinks !== void 0) {
|
|
2289
|
+
const named = sh.skippedLinks.map((l) => `${l.path} (${l.reason === "outside" ? "points outside the build" : "points to nothing"})`).join(", ");
|
|
2290
|
+
warn(`Not uploaded: ${named}. A symlink is followed only to a file or directory inside the build; copy the target in instead.`, "static.link-skipped");
|
|
2291
|
+
}
|
|
2292
|
+
detail(
|
|
2293
|
+
sh.routing === "multipage" ? "Routing: multipage \u2014 routes resolve to their own documents." : "Routing: spa \u2014 every path serves index.html."
|
|
2294
|
+
);
|
|
2295
|
+
if (sh.routingSuspect === true) {
|
|
2296
|
+
const suspectPages = sh.routingSuspectPages !== void 0 && sh.routingSuspectPages.length > 0 ? sh.routingSuspectPages : ["404.html"];
|
|
2297
|
+
warn(
|
|
2298
|
+
`Routing was inferred as multipage only because the build ships ${suspectPages.join(" and ")} beside index.html \u2014 the shape of a single-page app with its own ${suspectPages.length === 1 && suspectPages[0].toLowerCase() === "200.html" ? "fallback" : "not-found"} page. If a client router serves its routes, its deep links never reach index.html.`,
|
|
2299
|
+
"static.routing-inferred-multipage",
|
|
2300
|
+
["Pass `--static-routing spa` to serve index.html for every route the build has no file for."]
|
|
2301
|
+
);
|
|
2302
|
+
}
|
|
2303
|
+
const globals = Object.keys(env).map((k) => `window.${k}`);
|
|
2304
|
+
let staticEnv;
|
|
2305
|
+
if (globals.length > 0) {
|
|
2306
|
+
staticEnv = { injected: sh.envInjected, keys: Object.keys(env), documents: sh.envDocuments };
|
|
2307
|
+
const docs = sh.envDocuments === 1 ? "1 document" : `${sh.envDocuments} documents`;
|
|
2308
|
+
if (sh.envInjected) success(`Config injected into ${docs}: ${globals.join(", ")}`);
|
|
2309
|
+
if (sh.envSkipped.length > 0) {
|
|
2310
|
+
const reason = `Config not injected into ${sh.envSkipped.join(", ")} \u2014 no <head> to anchor to; those routes run with ${globals.join(", ")} unset.`;
|
|
2311
|
+
staticEnv = { ...staticEnv, skipped: [...sh.envSkipped], reason };
|
|
2312
|
+
warn(reason, "static-env.not-injected");
|
|
2313
|
+
} else if (!sh.envInjected) {
|
|
2314
|
+
const reason = explicit ? `Config not injected \u2014 the build has no HTML document. ${globals.join(", ")} unset.` : `No HTML document to inject window.XANO_HOST into \u2014 skipped.`;
|
|
2315
|
+
staticEnv = { ...staticEnv, reason };
|
|
2316
|
+
if (explicit) warn(reason, "static-env.not-injected");
|
|
2317
|
+
else detail(reason);
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
success("Static host deployed");
|
|
2321
|
+
if (sh.url) link(sh.url);
|
|
2322
|
+
const published = {
|
|
2323
|
+
url: sh.url,
|
|
2324
|
+
routing: sh.routing,
|
|
2325
|
+
...staticEnv !== void 0 ? { staticEnv } : {},
|
|
2326
|
+
...sh.canonical !== void 0 ? { canonical: sh.canonical } : {},
|
|
2327
|
+
...sh.serverBundle !== void 0 ? { serverBundle: sh.serverBundle } : {}
|
|
2328
|
+
};
|
|
2329
|
+
if (skipLiveness || sh.url === void 0 || sh.canonical === void 0) {
|
|
2330
|
+
return published;
|
|
2331
|
+
}
|
|
2332
|
+
const { verifyRollout } = await import("./verify-rollout-DRCK5TDD.js");
|
|
2333
|
+
const { url, canonical } = sh;
|
|
2334
|
+
const { live } = await withSpinner("Verifying the frontend is live\u2026", () => verifyRollout(url, canonical));
|
|
2335
|
+
if (live) {
|
|
2336
|
+
success("Frontend is live");
|
|
2337
|
+
} else {
|
|
2338
|
+
warn(
|
|
2339
|
+
"Could not confirm the frontend is live within the wait window \u2014 the build uploaded and should come online shortly.",
|
|
2340
|
+
"static.unconfirmed-live",
|
|
2341
|
+
[`Re-check by opening ${sh.url}, or skip this wait next time with --skip-liveness.`]
|
|
2342
|
+
);
|
|
2343
|
+
}
|
|
2344
|
+
return { ...published, verified: live };
|
|
2345
|
+
}
|
|
2346
|
+
async function noteRunScopeFor(auth) {
|
|
2347
|
+
const { noteRunScope } = await import("./cli.js");
|
|
2348
|
+
noteRunScope(auth);
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
export {
|
|
2352
|
+
buildStaticEnv,
|
|
2353
|
+
warnSecretLookingStaticEnv,
|
|
2354
|
+
deriveDisplay,
|
|
2355
|
+
assertNoSeedLeaks,
|
|
2356
|
+
runDeployedTests,
|
|
2357
|
+
testRunAllCommand,
|
|
2358
|
+
ignoreBackendSecretFilesRead,
|
|
2359
|
+
recordedLanding,
|
|
2360
|
+
storeAsReleaseHint,
|
|
2361
|
+
tenantWithoutWorkspace,
|
|
2362
|
+
DESTINATION_ONLY,
|
|
2363
|
+
EPHEMERAL_ONLY,
|
|
2364
|
+
assertNoEphemeralFlags,
|
|
2365
|
+
resolveDeployDestination,
|
|
2366
|
+
refuseSameEphemeral,
|
|
2367
|
+
runDeployCommand,
|
|
2368
|
+
syncDevEnvAfterDeploy,
|
|
2369
|
+
noteUnconfigured,
|
|
2370
|
+
noteStaticUrlChange,
|
|
2371
|
+
republishNote,
|
|
2372
|
+
createdButFailedNote,
|
|
2373
|
+
noteReplaceRerun,
|
|
2374
|
+
noteKeptFilledMarker,
|
|
2375
|
+
isFetchedDuplicateGuid,
|
|
2376
|
+
explainDuplicateArchiveGuid,
|
|
2377
|
+
verifyMicroservices,
|
|
2378
|
+
ephemeralDisabledError,
|
|
2379
|
+
notReadyError,
|
|
2380
|
+
ephemeralStateCheck,
|
|
2381
|
+
toStateCheck,
|
|
2382
|
+
unknownCreateOutcome,
|
|
2383
|
+
deployStaticTo
|
|
2384
|
+
};
|
|
2385
|
+
//# sourceMappingURL=chunk-WZDJ2LLX.js.map
|