@superlayer/cli 1.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/LICENSE.md +202 -0
  2. package/NOTICE +4 -0
  3. package/README.md +67 -0
  4. package/bin/index.js +3 -0
  5. package/dist/auth/index.d.ts +4 -0
  6. package/dist/auth/index.d.ts.map +1 -0
  7. package/dist/auth/index.js +103 -0
  8. package/dist/auth/index.js.map +1 -0
  9. package/dist/commands/app/delete.d.ts +7 -0
  10. package/dist/commands/app/delete.d.ts.map +1 -0
  11. package/dist/commands/app/delete.js +57 -0
  12. package/dist/commands/app/delete.js.map +1 -0
  13. package/dist/commands/app/list.d.ts +6 -0
  14. package/dist/commands/app/list.d.ts.map +1 -0
  15. package/dist/commands/app/list.js +29 -0
  16. package/dist/commands/app/list.js.map +1 -0
  17. package/dist/commands/auth/client/add.d.ts +9 -0
  18. package/dist/commands/auth/client/add.d.ts.map +1 -0
  19. package/dist/commands/auth/client/add.js +409 -0
  20. package/dist/commands/auth/client/add.js.map +1 -0
  21. package/dist/commands/auth/client/delete.d.ts +11 -0
  22. package/dist/commands/auth/client/delete.d.ts.map +1 -0
  23. package/dist/commands/auth/client/delete.js +48 -0
  24. package/dist/commands/auth/client/delete.js.map +1 -0
  25. package/dist/commands/auth/client/list.d.ts +6 -0
  26. package/dist/commands/auth/client/list.d.ts.map +1 -0
  27. package/dist/commands/auth/client/list.js +38 -0
  28. package/dist/commands/auth/client/list.js.map +1 -0
  29. package/dist/commands/auth/client/shared.d.ts +70 -0
  30. package/dist/commands/auth/client/shared.d.ts.map +1 -0
  31. package/dist/commands/auth/client/shared.js +140 -0
  32. package/dist/commands/auth/client/shared.js.map +1 -0
  33. package/dist/commands/auth/client/update.d.ts +8 -0
  34. package/dist/commands/auth/client/update.d.ts.map +1 -0
  35. package/dist/commands/auth/client/update.js +445 -0
  36. package/dist/commands/auth/client/update.js.map +1 -0
  37. package/dist/commands/auth/email/pull.d.ts +24 -0
  38. package/dist/commands/auth/email/pull.d.ts.map +1 -0
  39. package/dist/commands/auth/email/pull.js +100 -0
  40. package/dist/commands/auth/email/pull.js.map +1 -0
  41. package/dist/commands/auth/email/push.d.ts +8 -0
  42. package/dist/commands/auth/email/push.d.ts.map +1 -0
  43. package/dist/commands/auth/email/push.js +58 -0
  44. package/dist/commands/auth/email/push.js.map +1 -0
  45. package/dist/commands/auth/email/resend.d.ts +3 -0
  46. package/dist/commands/auth/email/resend.d.ts.map +1 -0
  47. package/dist/commands/auth/email/resend.js +7 -0
  48. package/dist/commands/auth/email/resend.js.map +1 -0
  49. package/dist/commands/auth/email/reset.d.ts +5 -0
  50. package/dist/commands/auth/email/reset.d.ts.map +1 -0
  51. package/dist/commands/auth/email/reset.js +25 -0
  52. package/dist/commands/auth/email/reset.js.map +1 -0
  53. package/dist/commands/auth/email/status.d.ts +57 -0
  54. package/dist/commands/auth/email/status.d.ts.map +1 -0
  55. package/dist/commands/auth/email/status.js +96 -0
  56. package/dist/commands/auth/email/status.js.map +1 -0
  57. package/dist/commands/auth/email/verify.d.ts +5 -0
  58. package/dist/commands/auth/email/verify.d.ts.map +1 -0
  59. package/dist/commands/auth/email/verify.js +12 -0
  60. package/dist/commands/auth/email/verify.js.map +1 -0
  61. package/dist/commands/auth/origin/add.d.ts +12 -0
  62. package/dist/commands/auth/origin/add.d.ts.map +1 -0
  63. package/dist/commands/auth/origin/add.js +153 -0
  64. package/dist/commands/auth/origin/add.js.map +1 -0
  65. package/dist/commands/auth/origin/delete.d.ts +8 -0
  66. package/dist/commands/auth/origin/delete.d.ts.map +1 -0
  67. package/dist/commands/auth/origin/delete.js +38 -0
  68. package/dist/commands/auth/origin/delete.js.map +1 -0
  69. package/dist/commands/auth/origin/list.d.ts +9 -0
  70. package/dist/commands/auth/origin/list.d.ts.map +1 -0
  71. package/dist/commands/auth/origin/list.js +52 -0
  72. package/dist/commands/auth/origin/list.js.map +1 -0
  73. package/dist/commands/backup/download.d.ts +10 -0
  74. package/dist/commands/backup/download.d.ts.map +1 -0
  75. package/dist/commands/backup/download.js +174 -0
  76. package/dist/commands/backup/download.js.map +1 -0
  77. package/dist/commands/backup/list.d.ts +11 -0
  78. package/dist/commands/backup/list.d.ts.map +1 -0
  79. package/dist/commands/backup/list.js +74 -0
  80. package/dist/commands/backup/list.js.map +1 -0
  81. package/dist/commands/claim.d.ts +6 -0
  82. package/dist/commands/claim.d.ts.map +1 -0
  83. package/dist/commands/claim.js +22 -0
  84. package/dist/commands/claim.js.map +1 -0
  85. package/dist/commands/explorer.d.ts +6 -0
  86. package/dist/commands/explorer.d.ts.map +1 -0
  87. package/dist/commands/explorer.js +13 -0
  88. package/dist/commands/explorer.js.map +1 -0
  89. package/dist/commands/info.d.ts +9 -0
  90. package/dist/commands/info.d.ts.map +1 -0
  91. package/dist/commands/info.js +41 -0
  92. package/dist/commands/info.js.map +1 -0
  93. package/dist/commands/init.d.ts +5 -0
  94. package/dist/commands/init.d.ts.map +1 -0
  95. package/dist/commands/init.js +41 -0
  96. package/dist/commands/init.js.map +1 -0
  97. package/dist/commands/initWithoutFiles.d.ts +6 -0
  98. package/dist/commands/initWithoutFiles.d.ts.map +1 -0
  99. package/dist/commands/initWithoutFiles.js +64 -0
  100. package/dist/commands/initWithoutFiles.js.map +1 -0
  101. package/dist/commands/login.d.ts +9 -0
  102. package/dist/commands/login.d.ts.map +1 -0
  103. package/dist/commands/login.js +44 -0
  104. package/dist/commands/login.js.map +1 -0
  105. package/dist/commands/logout.d.ts +3 -0
  106. package/dist/commands/logout.d.ts.map +1 -0
  107. package/dist/commands/logout.js +14 -0
  108. package/dist/commands/logout.js.map +1 -0
  109. package/dist/commands/pull.d.ts +5 -0
  110. package/dist/commands/pull.d.ts.map +1 -0
  111. package/dist/commands/pull.js +15 -0
  112. package/dist/commands/pull.js.map +1 -0
  113. package/dist/commands/push.d.ts +6 -0
  114. package/dist/commands/push.d.ts.map +1 -0
  115. package/dist/commands/push.js +20 -0
  116. package/dist/commands/push.js.map +1 -0
  117. package/dist/commands/query.d.ts +7 -0
  118. package/dist/commands/query.d.ts.map +1 -0
  119. package/dist/commands/query.js +52 -0
  120. package/dist/commands/query.js.map +1 -0
  121. package/dist/commands/webhooks/add.d.ts +9 -0
  122. package/dist/commands/webhooks/add.d.ts.map +1 -0
  123. package/dist/commands/webhooks/add.js +75 -0
  124. package/dist/commands/webhooks/add.js.map +1 -0
  125. package/dist/commands/webhooks/delete.d.ts +6 -0
  126. package/dist/commands/webhooks/delete.d.ts.map +1 -0
  127. package/dist/commands/webhooks/delete.js +17 -0
  128. package/dist/commands/webhooks/delete.js.map +1 -0
  129. package/dist/commands/webhooks/disable.d.ts +7 -0
  130. package/dist/commands/webhooks/disable.d.ts.map +1 -0
  131. package/dist/commands/webhooks/disable.js +18 -0
  132. package/dist/commands/webhooks/disable.js.map +1 -0
  133. package/dist/commands/webhooks/enable.d.ts +6 -0
  134. package/dist/commands/webhooks/enable.d.ts.map +1 -0
  135. package/dist/commands/webhooks/enable.js +18 -0
  136. package/dist/commands/webhooks/enable.js.map +1 -0
  137. package/dist/commands/webhooks/events/list.d.ts +7 -0
  138. package/dist/commands/webhooks/events/list.d.ts.map +1 -0
  139. package/dist/commands/webhooks/events/list.js +31 -0
  140. package/dist/commands/webhooks/events/list.js.map +1 -0
  141. package/dist/commands/webhooks/events/payload.d.ts +8 -0
  142. package/dist/commands/webhooks/events/payload.d.ts.map +1 -0
  143. package/dist/commands/webhooks/events/payload.js +39 -0
  144. package/dist/commands/webhooks/events/payload.js.map +1 -0
  145. package/dist/commands/webhooks/events/resend.d.ts +8 -0
  146. package/dist/commands/webhooks/events/resend.d.ts.map +1 -0
  147. package/dist/commands/webhooks/events/resend.js +43 -0
  148. package/dist/commands/webhooks/events/resend.js.map +1 -0
  149. package/dist/commands/webhooks/list.d.ts +8 -0
  150. package/dist/commands/webhooks/list.d.ts.map +1 -0
  151. package/dist/commands/webhooks/list.js +29 -0
  152. package/dist/commands/webhooks/list.js.map +1 -0
  153. package/dist/commands/webhooks/shared.d.ts +40 -0
  154. package/dist/commands/webhooks/shared.d.ts.map +1 -0
  155. package/dist/commands/webhooks/shared.js +248 -0
  156. package/dist/commands/webhooks/shared.js.map +1 -0
  157. package/dist/commands/webhooks/update.d.ts +10 -0
  158. package/dist/commands/webhooks/update.d.ts.map +1 -0
  159. package/dist/commands/webhooks/update.js +189 -0
  160. package/dist/commands/webhooks/update.js.map +1 -0
  161. package/dist/context/authToken.d.ts +29 -0
  162. package/dist/context/authToken.d.ts.map +1 -0
  163. package/dist/context/authToken.js +73 -0
  164. package/dist/context/authToken.js.map +1 -0
  165. package/dist/context/currentApp.d.ts +38 -0
  166. package/dist/context/currentApp.d.ts.map +1 -0
  167. package/dist/context/currentApp.js +222 -0
  168. package/dist/context/currentApp.js.map +1 -0
  169. package/dist/context/globalOpts.d.ts +11 -0
  170. package/dist/context/globalOpts.d.ts.map +1 -0
  171. package/dist/context/globalOpts.js +13 -0
  172. package/dist/context/globalOpts.js.map +1 -0
  173. package/dist/context/platformApi.d.ts +19 -0
  174. package/dist/context/platformApi.d.ts.map +1 -0
  175. package/dist/context/platformApi.js +24 -0
  176. package/dist/context/platformApi.js.map +1 -0
  177. package/dist/context/projectInfo.d.ts +30 -0
  178. package/dist/context/projectInfo.d.ts.map +1 -0
  179. package/dist/context/projectInfo.js +152 -0
  180. package/dist/context/projectInfo.js.map +1 -0
  181. package/dist/errors.d.ts +10 -0
  182. package/dist/errors.d.ts.map +1 -0
  183. package/dist/errors.js +6 -0
  184. package/dist/errors.js.map +1 -0
  185. package/dist/index.d.ts +159 -0
  186. package/dist/index.d.ts.map +1 -0
  187. package/dist/index.js +714 -0
  188. package/dist/index.js.map +1 -0
  189. package/dist/layer.d.ts +25 -0
  190. package/dist/layer.d.ts.map +1 -0
  191. package/dist/layer.js +119 -0
  192. package/dist/layer.js.map +1 -0
  193. package/dist/lib/args.d.ts +212 -0
  194. package/dist/lib/args.d.ts.map +1 -0
  195. package/dist/lib/args.js +313 -0
  196. package/dist/lib/args.js.map +1 -0
  197. package/dist/lib/backupDownload.d.ts +21 -0
  198. package/dist/lib/backupDownload.d.ts.map +1 -0
  199. package/dist/lib/backupDownload.js +151 -0
  200. package/dist/lib/backupDownload.js.map +1 -0
  201. package/dist/lib/backups.d.ts +12 -0
  202. package/dist/lib/backups.d.ts.map +1 -0
  203. package/dist/lib/backups.js +26 -0
  204. package/dist/lib/backups.js.map +1 -0
  205. package/dist/lib/config.d.ts +6 -0
  206. package/dist/lib/config.d.ts.map +1 -0
  207. package/dist/lib/config.js +39 -0
  208. package/dist/lib/config.js.map +1 -0
  209. package/dist/lib/createApp.d.ts +12 -0
  210. package/dist/lib/createApp.d.ts.map +1 -0
  211. package/dist/lib/createApp.js +15 -0
  212. package/dist/lib/createApp.js.map +1 -0
  213. package/dist/lib/email.d.ts +47 -0
  214. package/dist/lib/email.d.ts.map +1 -0
  215. package/dist/lib/email.js +71 -0
  216. package/dist/lib/email.js.map +1 -0
  217. package/dist/lib/handleEnv.d.ts +7 -0
  218. package/dist/lib/handleEnv.d.ts.map +1 -0
  219. package/dist/lib/handleEnv.js +99 -0
  220. package/dist/lib/handleEnv.js.map +1 -0
  221. package/dist/lib/http.d.ts +30 -0
  222. package/dist/lib/http.d.ts.map +1 -0
  223. package/dist/lib/http.js +55 -0
  224. package/dist/lib/http.js.map +1 -0
  225. package/dist/lib/login.d.ts +12 -0
  226. package/dist/lib/login.d.ts.map +1 -0
  227. package/dist/lib/login.js +38 -0
  228. package/dist/lib/login.js.map +1 -0
  229. package/dist/lib/oauth.d.ts +306 -0
  230. package/dist/lib/oauth.d.ts.map +1 -0
  231. package/dist/lib/oauth.js +195 -0
  232. package/dist/lib/oauth.js.map +1 -0
  233. package/dist/lib/platformApi.d.ts +5 -0
  234. package/dist/lib/platformApi.d.ts.map +1 -0
  235. package/dist/lib/platformApi.js +11 -0
  236. package/dist/lib/platformApi.js.map +1 -0
  237. package/dist/lib/pullPerms.d.ts +7 -0
  238. package/dist/lib/pullPerms.d.ts.map +1 -0
  239. package/dist/lib/pullPerms.js +41 -0
  240. package/dist/lib/pullPerms.js.map +1 -0
  241. package/dist/lib/pullSchema.d.ts +12 -0
  242. package/dist/lib/pullSchema.d.ts.map +1 -0
  243. package/dist/lib/pullSchema.js +62 -0
  244. package/dist/lib/pullSchema.js.map +1 -0
  245. package/dist/lib/pushPerms.d.ts +13 -0
  246. package/dist/lib/pushPerms.d.ts.map +1 -0
  247. package/dist/lib/pushPerms.js +54 -0
  248. package/dist/lib/pushPerms.js.map +1 -0
  249. package/dist/lib/pushSchema.d.ts +53 -0
  250. package/dist/lib/pushSchema.d.ts.map +1 -0
  251. package/dist/lib/pushSchema.js +165 -0
  252. package/dist/lib/pushSchema.js.map +1 -0
  253. package/dist/lib/ui.d.ts +18 -0
  254. package/dist/lib/ui.d.ts.map +1 -0
  255. package/dist/lib/ui.js +32 -0
  256. package/dist/lib/ui.js.map +1 -0
  257. package/dist/lib/webhooks.d.ts +27 -0
  258. package/dist/lib/webhooks.d.ts.map +1 -0
  259. package/dist/lib/webhooks.js +95 -0
  260. package/dist/lib/webhooks.js.map +1 -0
  261. package/dist/logging.d.ts +5 -0
  262. package/dist/logging.d.ts.map +1 -0
  263. package/dist/logging.js +25 -0
  264. package/dist/logging.js.map +1 -0
  265. package/dist/old.d.ts +17 -0
  266. package/dist/old.d.ts.map +1 -0
  267. package/dist/old.js +410 -0
  268. package/dist/old.js.map +1 -0
  269. package/dist/program.d.ts +3 -0
  270. package/dist/program.d.ts.map +1 -0
  271. package/dist/program.js +3 -0
  272. package/dist/program.js.map +1 -0
  273. package/dist/renderSchemaPlan.d.ts +19 -0
  274. package/dist/renderSchemaPlan.d.ts.map +1 -0
  275. package/dist/renderSchemaPlan.js +220 -0
  276. package/dist/renderSchemaPlan.js.map +1 -0
  277. package/dist/ui/index.d.ts +292 -0
  278. package/dist/ui/index.d.ts.map +1 -0
  279. package/dist/ui/index.js +1296 -0
  280. package/dist/ui/index.js.map +1 -0
  281. package/dist/ui/lib.d.ts +124 -0
  282. package/dist/ui/lib.d.ts.map +1 -0
  283. package/dist/ui/lib.js +333 -0
  284. package/dist/ui/lib.js.map +1 -0
  285. package/dist/util/findConfigCandidates.d.ts +13 -0
  286. package/dist/util/findConfigCandidates.d.ts.map +1 -0
  287. package/dist/util/findConfigCandidates.js +211 -0
  288. package/dist/util/findConfigCandidates.js.map +1 -0
  289. package/dist/util/fs.d.ts +4 -0
  290. package/dist/util/fs.d.ts.map +1 -0
  291. package/dist/util/fs.js +22 -0
  292. package/dist/util/fs.js.map +1 -0
  293. package/dist/util/getAppName.d.ts +5 -0
  294. package/dist/util/getAppName.d.ts.map +1 -0
  295. package/dist/util/getAppName.js +14 -0
  296. package/dist/util/getAppName.js.map +1 -0
  297. package/dist/util/instantConfig.d.ts +9 -0
  298. package/dist/util/instantConfig.d.ts.map +1 -0
  299. package/dist/util/instantConfig.js +13 -0
  300. package/dist/util/instantConfig.js.map +1 -0
  301. package/dist/util/isHeadlessEnvironment.d.ts +4 -0
  302. package/dist/util/isHeadlessEnvironment.d.ts.map +1 -0
  303. package/dist/util/isHeadlessEnvironment.js +11 -0
  304. package/dist/util/isHeadlessEnvironment.js.map +1 -0
  305. package/dist/util/loadConfig.d.ts +3 -0
  306. package/dist/util/loadConfig.d.ts.map +1 -0
  307. package/dist/util/loadConfig.js +72 -0
  308. package/dist/util/loadConfig.js.map +1 -0
  309. package/dist/util/loadEnv.d.ts +2 -0
  310. package/dist/util/loadEnv.d.ts.map +1 -0
  311. package/dist/util/loadEnv.js +10 -0
  312. package/dist/util/loadEnv.js.map +1 -0
  313. package/dist/util/mergeSchema.d.ts +10 -0
  314. package/dist/util/mergeSchema.d.ts.map +1 -0
  315. package/dist/util/mergeSchema.js +338 -0
  316. package/dist/util/mergeSchema.js.map +1 -0
  317. package/dist/util/projectDir.d.ts +7 -0
  318. package/dist/util/projectDir.d.ts.map +1 -0
  319. package/dist/util/projectDir.js +43 -0
  320. package/dist/util/projectDir.js.map +1 -0
  321. package/dist/util/renamePrompt.d.ts +15 -0
  322. package/dist/util/renamePrompt.d.ts.map +1 -0
  323. package/dist/util/renamePrompt.js +57 -0
  324. package/dist/util/renamePrompt.js.map +1 -0
  325. package/dist/version.d.ts +3 -0
  326. package/dist/version.d.ts.map +1 -0
  327. package/dist/version.js +3 -0
  328. package/dist/version.js.map +1 -0
  329. package/package.json +96 -0
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Args lets commands read CLI flags as a small pipeline:
3
+ *
4
+ * 1. Parse a value from opts
5
+ * 2. Gate it: when is this flag available?
6
+ * 3. Prompt for a missing value, when interactive
7
+ * 4. Validate and finish as required or optional
8
+ *
9
+ * This keeps the "can this flag be used here?" logic next to the value it
10
+ * controls. For example, a Google client secret is only meaningful when using
11
+ * custom web credentials:
12
+ *
13
+ * const clientSecret = yield* Args.text(opts, 'client-secret').pipe(
14
+ * Args.availableWhen(usesCustomWebCredentials),
15
+ * Args.prompt(clientSecretPrompt({ providerUrl })),
16
+ * Args.required(),
17
+ * );
18
+ *
19
+ * The key argument to text/bool/has is the exact opts lookup key. If the
20
+ * user-facing flag name differs, pass simpleName for errors:
21
+ *
22
+ * Args.text(opts, 'customRedirectUri', { simpleName: '--custom-redirect-uri' })
23
+ */
24
+ import { Effect } from 'effect';
25
+ import { pipeArguments } from 'effect/Pipeable';
26
+ import { BadArgsError } from "../errors.js";
27
+ import { GlobalOpts } from "../context/globalOpts.js";
28
+ import { UI } from "../ui/index.js";
29
+ import { runUIEffect } from "./ui.js";
30
+ const makeArg = (flag, state) => {
31
+ const arg = {
32
+ flag,
33
+ state,
34
+ pipe() {
35
+ return pipeArguments(this, arguments);
36
+ },
37
+ };
38
+ return arg;
39
+ };
40
+ const active = (value, provided) => ({ _tag: 'Active', provided, value });
41
+ const inactive = { _tag: 'Inactive' };
42
+ const missingMessage = (flag) => `Missing required value for ${flag}`;
43
+ /**
44
+ * @example
45
+ * if (Args.has(opts, 'custom-redirect-uri')) {
46
+ * // The user explicitly passed --custom-redirect-uri.
47
+ * }
48
+ */
49
+ function has(opts, key) {
50
+ return Object.prototype.hasOwnProperty.call(opts, key);
51
+ }
52
+ /**
53
+ * @example
54
+ * const configureWeb = Args.hasAny(opts, [
55
+ * 'team-id',
56
+ * 'key-id',
57
+ * 'private-key-file',
58
+ * ]);
59
+ */
60
+ function hasAny(opts, keys) {
61
+ return keys.some((key) => has(opts, key));
62
+ }
63
+ /**
64
+ * Returns true only for boolean true or the string "true".
65
+ *
66
+ * @example
67
+ * const useDevCredentials = Args.isTrue(opts, 'dev-credentials');
68
+ */
69
+ function isTrue(opts, key) {
70
+ const value = opts[key];
71
+ return value === true || value === 'true';
72
+ }
73
+ /**
74
+ * Starts a string arg pipeline. Strings are trimmed, numbers are stringified,
75
+ * empty values become missing values, and other types produce BadArgsError.
76
+ *
77
+ * @example
78
+ * const clientId = yield* Args.text(opts, 'client-id').pipe(
79
+ * Args.prompt(clientIdPrompt({ providerUrl })),
80
+ * Args.required(),
81
+ * );
82
+ */
83
+ function text(opts, key, options) {
84
+ const flag = options?.simpleName ?? `--${key}`;
85
+ const provided = Object.prototype.hasOwnProperty.call(opts, key);
86
+ return makeArg(flag, Effect.succeed(active(readTextValue(opts[key], flag), provided)));
87
+ }
88
+ function readTextValue(value, flag) {
89
+ return Effect.gen(function* readTextValue() {
90
+ if (value === undefined || value === null)
91
+ return undefined;
92
+ if (typeof value === 'string' || typeof value === 'number') {
93
+ const trimmed = String(value).trim();
94
+ return trimmed.length > 0 ? trimmed : undefined;
95
+ }
96
+ return yield* BadArgsError.make({
97
+ message: `Invalid value for ${flag}`,
98
+ });
99
+ });
100
+ }
101
+ /**
102
+ * Starts a boolean arg pipeline. Accepts booleans and the strings "true" or
103
+ * "false"; other supplied values produce BadArgsError.
104
+ *
105
+ * @example
106
+ * const configureWeb = yield* Args.bool(opts, 'configure-web').pipe(
107
+ * Args.confirm({ promptText: 'Configure web redirect flow?' }),
108
+ * Args.required(),
109
+ * );
110
+ */
111
+ function bool(opts, key, options) {
112
+ const flag = options?.simpleName ?? `--${key}`;
113
+ const provided = Object.prototype.hasOwnProperty.call(opts, key);
114
+ return makeArg(flag, Effect.succeed(active(readBooleanValue(opts[key], flag), provided)));
115
+ }
116
+ function readBooleanValue(value, flag) {
117
+ return Effect.gen(function* readBooleanValue() {
118
+ if (value === undefined || value === null)
119
+ return undefined;
120
+ if (value === true || value === 'true')
121
+ return true;
122
+ if (value === false || value === 'false')
123
+ return false;
124
+ return yield* BadArgsError.make({
125
+ message: `Invalid value for ${flag}`,
126
+ });
127
+ });
128
+ }
129
+ function uiErrorToBadArgs(e) {
130
+ return BadArgsError.make({ message: `UI error: ${e.message}` });
131
+ }
132
+ function mapActive(wrapper, mapValue) {
133
+ return makeArg(wrapper.flag, Effect.gen(function* mapArgState() {
134
+ const state = yield* wrapper.state;
135
+ if (state._tag === 'Inactive')
136
+ return inactive;
137
+ return active(mapValue(state.value), state.provided);
138
+ }));
139
+ }
140
+ /**
141
+ * Gates an arg behind a mode or earlier choice.
142
+ *
143
+ * If the condition is false and the user supplied the flag, this returns an
144
+ * error.
145
+ *
146
+ * @example
147
+ * const clientSecret = yield* Args.text(opts, 'client-secret').pipe(
148
+ * Args.availableWhen(appType === 'web'),
149
+ * Args.prompt(clientSecretPrompt({ providerUrl })),
150
+ * Args.required(),
151
+ * );
152
+ */
153
+ function availableWhen(condition, options) {
154
+ return function availableWhenArg(arg) {
155
+ return makeArg(arg.flag, Effect.gen(function* gateArgAvailability() {
156
+ const state = yield* arg.state;
157
+ if (condition || state._tag === 'Inactive')
158
+ return state;
159
+ if (state.provided) {
160
+ return yield* BadArgsError.make({
161
+ message: options?.message ??
162
+ `${arg.flag} is not compatible with other options`,
163
+ });
164
+ }
165
+ return inactive;
166
+ }));
167
+ };
168
+ }
169
+ /**
170
+ * Prompts for a missing text value.
171
+ *
172
+ * With --yes, the prompt is skipped. If inputProps.defaultValue is set, that
173
+ * value is used. Otherwise the value stays missing.
174
+ *
175
+ * @example
176
+ * const clientName = yield* Args.text(opts, 'name').pipe(
177
+ * Args.prompt({
178
+ * prompt: 'Client Name:',
179
+ * defaultValue: suggestedClientName,
180
+ * }),
181
+ * Args.required(),
182
+ * );
183
+ */
184
+ function prompt(inputProps) {
185
+ return function promptArg(arg) {
186
+ return mapActive(arg, (valueEffect) => Effect.gen(function* promptWhenMissing() {
187
+ const value = yield* valueEffect;
188
+ if (value !== undefined)
189
+ return value;
190
+ const { yes } = yield* GlobalOpts;
191
+ const result = yes
192
+ ? inputProps.defaultValue
193
+ : yield* runUIEffect(new UI.TextInput(inputProps)).pipe(Effect.catchTag('UIError', uiErrorToBadArgs));
194
+ if (result === undefined)
195
+ return undefined;
196
+ const trimmed = result.trim();
197
+ return trimmed.length > 0 ? trimmed : undefined;
198
+ }));
199
+ };
200
+ }
201
+ /**
202
+ * Prompts for a missing boolean value.
203
+ *
204
+ * With --yes, the prompt is skipped and confirmationProps.defaultValue is used.
205
+ *
206
+ * @example
207
+ * const configureWeb = yield* Args.bool(opts, 'configure-web').pipe(
208
+ * Args.confirm({
209
+ * promptText: 'Configure web redirect flow?',
210
+ * defaultValue: false,
211
+ * }),
212
+ * Args.required(),
213
+ * );
214
+ */
215
+ function confirm(confirmationProps) {
216
+ return function confirmArg(arg) {
217
+ return mapActive(arg, (valueEffect) => Effect.gen(function* confirmWhenMissing() {
218
+ const value = yield* valueEffect;
219
+ if (value !== undefined)
220
+ return value;
221
+ const { yes } = yield* GlobalOpts;
222
+ if (yes)
223
+ return confirmationProps.defaultValue;
224
+ return yield* runUIEffect(new UI.Confirmation(confirmationProps)).pipe(Effect.catchTag('UIError', uiErrorToBadArgs));
225
+ }));
226
+ };
227
+ }
228
+ /**
229
+ * Validates a present value. Return an error message to fail, or undefined to
230
+ * accept the value. Missing and inactive args pass through unchanged.
231
+ *
232
+ * @example
233
+ * const projectId = yield* Args.text(opts, 'project-id').pipe(
234
+ * Args.prompt(firebaseProjectIdPrompt({})),
235
+ * Args.validate(validateFirebaseProjectId),
236
+ * Args.required(),
237
+ * );
238
+ */
239
+ function validate(validator) {
240
+ return function validateArg(arg) {
241
+ return mapActive(arg, (valueEffect) => Effect.gen(function* validateValue() {
242
+ const value = yield* valueEffect;
243
+ if (value === undefined)
244
+ return undefined;
245
+ const message = validator(value);
246
+ if (!message)
247
+ return value;
248
+ return yield* BadArgsError.make({ message });
249
+ }));
250
+ };
251
+ }
252
+ /**
253
+ * Finishes a pipeline with a required value.
254
+ *
255
+ * Missing active args produce BadArgsError. Inactive args return undefined, so
256
+ * callers can skip downstream work for unavailable flags.
257
+ *
258
+ * @example
259
+ * const clientId = yield* Args.text(opts, 'client-id').pipe(
260
+ * Args.prompt(clientIdPrompt({ providerUrl })),
261
+ * Args.required(),
262
+ * );
263
+ */
264
+ function required(options) {
265
+ return function requiredArg(arg) {
266
+ return Effect.gen(function* requireValue() {
267
+ const state = yield* arg.state;
268
+ if (state._tag === 'Inactive')
269
+ return undefined;
270
+ const value = yield* state.value;
271
+ if (value !== undefined)
272
+ return value;
273
+ return yield* BadArgsError.make({
274
+ message: options?.message ?? missingMessage(arg.flag),
275
+ });
276
+ });
277
+ };
278
+ }
279
+ /**
280
+ * Finishes a pipeline with an optional value.
281
+ *
282
+ * Missing and inactive args return undefined.
283
+ *
284
+ * @example
285
+ * const customRedirectUri = yield* Args.text(opts, 'custom-redirect-uri').pipe(
286
+ * Args.prompt(optionalRedirectPrompt),
287
+ * Args.optional(),
288
+ * );
289
+ */
290
+ function optional() {
291
+ return function optionalArg(arg) {
292
+ return Effect.gen(function* optionalValue() {
293
+ const state = yield* arg.state;
294
+ if (state._tag === 'Inactive')
295
+ return undefined;
296
+ return yield* state.value;
297
+ });
298
+ };
299
+ }
300
+ export const Args = {
301
+ text,
302
+ bool,
303
+ has,
304
+ hasAny,
305
+ isTrue,
306
+ availableWhen,
307
+ prompt,
308
+ confirm,
309
+ validate,
310
+ required,
311
+ optional,
312
+ };
313
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/lib/args.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Args lets commands read CLI flags as a small pipeline:\n *\n * 1. Parse a value from opts\n * 2. Gate it: when is this flag available?\n * 3. Prompt for a missing value, when interactive\n * 4. Validate and finish as required or optional\n *\n * This keeps the \"can this flag be used here?\" logic next to the value it\n * controls. For example, a Google client secret is only meaningful when using\n * custom web credentials:\n *\n * const clientSecret = yield* Args.text(opts, 'client-secret').pipe(\n * Args.availableWhen(usesCustomWebCredentials),\n * Args.prompt(clientSecretPrompt({ providerUrl })),\n * Args.required(),\n * );\n *\n * The key argument to text/bool/has is the exact opts lookup key. If the\n * user-facing flag name differs, pass simpleName for errors:\n *\n * Args.text(opts, 'customRedirectUri', { simpleName: '--custom-redirect-uri' })\n */\nimport { Effect } from 'effect';\nimport { pipeArguments, type Pipeable } from 'effect/Pipeable';\nimport { BadArgsError } from '../errors.ts';\nimport { GlobalOpts } from '../context/globalOpts.ts';\nimport { UI } from '../ui/index.ts';\nimport { runUIEffect } from './ui.ts';\n\ntype ActiveArg<A, E, R> = {\n readonly _tag: 'Active';\n readonly provided: boolean;\n readonly value: Effect.Effect<A | undefined, E, R>;\n};\n\ntype InactiveArg = {\n readonly _tag: 'Inactive';\n};\n\ntype ArgState<A, E, R, CanBeInactive extends boolean> =\n | ActiveArg<A, E, R>\n | (CanBeInactive extends true ? InactiveArg : never);\n\n/**\n * A parsed CLI arg that can be composed with Args helpers.\n *\n * Commands usually create one with text() or bool(), then finish with\n * required() or optional().\n *\n * @example\n * const name = yield* Args.text(opts, 'name').pipe(\n * Args.prompt({ prompt: 'Client Name:' }),\n * Args.required(),\n * );\n */\nexport interface Arg<\n A,\n E = never,\n R = never,\n CanBeInactive extends boolean = false,\n> extends Pipeable {\n readonly flag: string;\n readonly state: Effect.Effect<ArgState<A, E, R, CanBeInactive>, E, R>;\n}\n\nexport type ArgOptions = {\n /**\n * Use when the opts key is not the user-facing flag name.\n *\n * @example\n * Args.text(opts, 'customRedirectUri', {\n * simpleName: '--custom-redirect-uri',\n * })\n */\n simpleName?: string;\n};\n\nconst makeArg = <A, E, R, CanBeInactive extends boolean>(\n flag: string,\n state: Effect.Effect<ArgState<A, E, R, CanBeInactive>, E, R>,\n): Arg<A, E, R, CanBeInactive> => {\n const arg: Arg<A, E, R, CanBeInactive> = {\n flag,\n state,\n pipe() {\n return pipeArguments(this, arguments);\n },\n };\n return arg;\n};\n\nconst active = <A, E, R>(\n value: Effect.Effect<A | undefined, E, R>,\n provided: boolean,\n): ActiveArg<A, E, R> => ({ _tag: 'Active', provided, value });\n\nconst inactive: InactiveArg = { _tag: 'Inactive' };\n\nconst missingMessage = (flag: string) => `Missing required value for ${flag}`;\n\ntype MissingOptions = {\n message?: string;\n};\n\ntype UnavailableOptions = {\n message?: string;\n};\n\n/**\n * @example\n * if (Args.has(opts, 'custom-redirect-uri')) {\n * // The user explicitly passed --custom-redirect-uri.\n * }\n */\nfunction has(opts: Record<string, unknown>, key: string) {\n return Object.prototype.hasOwnProperty.call(opts, key);\n}\n\n/**\n * @example\n * const configureWeb = Args.hasAny(opts, [\n * 'team-id',\n * 'key-id',\n * 'private-key-file',\n * ]);\n */\nfunction hasAny(opts: Record<string, unknown>, keys: string[]) {\n return keys.some((key) => has(opts, key));\n}\n\n/**\n * Returns true only for boolean true or the string \"true\".\n *\n * @example\n * const useDevCredentials = Args.isTrue(opts, 'dev-credentials');\n */\nfunction isTrue(opts: Record<string, unknown>, key: string) {\n const value = opts[key];\n return value === true || value === 'true';\n}\n\n/**\n * Starts a string arg pipeline. Strings are trimmed, numbers are stringified,\n * empty values become missing values, and other types produce BadArgsError.\n *\n * @example\n * const clientId = yield* Args.text(opts, 'client-id').pipe(\n * Args.prompt(clientIdPrompt({ providerUrl })),\n * Args.required(),\n * );\n */\nfunction text(\n opts: Record<string, unknown>,\n key: string,\n options?: ArgOptions,\n): Arg<string, BadArgsError> {\n const flag = options?.simpleName ?? `--${key}`;\n const provided = Object.prototype.hasOwnProperty.call(opts, key);\n\n return makeArg(\n flag,\n Effect.succeed(active(readTextValue(opts[key], flag), provided)),\n );\n}\n\nfunction readTextValue(value: unknown, flag: string) {\n return Effect.gen(function* readTextValue() {\n if (value === undefined || value === null) return undefined;\n\n if (typeof value === 'string' || typeof value === 'number') {\n const trimmed = String(value).trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n\n return yield* BadArgsError.make({\n message: `Invalid value for ${flag}`,\n });\n });\n}\n\n/**\n * Starts a boolean arg pipeline. Accepts booleans and the strings \"true\" or\n * \"false\"; other supplied values produce BadArgsError.\n *\n * @example\n * const configureWeb = yield* Args.bool(opts, 'configure-web').pipe(\n * Args.confirm({ promptText: 'Configure web redirect flow?' }),\n * Args.required(),\n * );\n */\nfunction bool(\n opts: Record<string, unknown>,\n key: string,\n options?: ArgOptions,\n): Arg<boolean, BadArgsError> {\n const flag = options?.simpleName ?? `--${key}`;\n const provided = Object.prototype.hasOwnProperty.call(opts, key);\n\n return makeArg(\n flag,\n Effect.succeed(active(readBooleanValue(opts[key], flag), provided)),\n );\n}\n\nfunction readBooleanValue(value: unknown, flag: string) {\n return Effect.gen(function* readBooleanValue() {\n if (value === undefined || value === null) return undefined;\n if (value === true || value === 'true') return true;\n if (value === false || value === 'false') return false;\n\n return yield* BadArgsError.make({\n message: `Invalid value for ${flag}`,\n });\n });\n}\n\nfunction uiErrorToBadArgs(e: { message: string }) {\n return BadArgsError.make({ message: `UI error: ${e.message}` });\n}\n\nfunction mapActive<A, B, E, R, E2, R2, CanBeInactive extends boolean>(\n wrapper: Arg<A, E, R, CanBeInactive>,\n mapValue: (\n value: Effect.Effect<A | undefined, E, R>,\n ) => Effect.Effect<B | undefined, E | E2, R | R2>,\n): Arg<B, E | E2, R | R2, CanBeInactive> {\n return makeArg(\n wrapper.flag,\n Effect.gen(function* mapArgState() {\n const state = yield* wrapper.state;\n if (state._tag === 'Inactive') return inactive;\n return active(mapValue(state.value), state.provided);\n }) as Effect.Effect<\n ArgState<B, E | E2, R | R2, CanBeInactive>,\n E | E2,\n R | R2\n >,\n );\n}\n\n/**\n * Gates an arg behind a mode or earlier choice.\n *\n * If the condition is false and the user supplied the flag, this returns an\n * error.\n *\n * @example\n * const clientSecret = yield* Args.text(opts, 'client-secret').pipe(\n * Args.availableWhen(appType === 'web'),\n * Args.prompt(clientSecretPrompt({ providerUrl })),\n * Args.required(),\n * );\n */\nfunction availableWhen(condition: boolean, options?: UnavailableOptions) {\n return function availableWhenArg<A, E, R, CanBeInactive extends boolean>(\n arg: Arg<A, E, R, CanBeInactive>,\n ) {\n return makeArg<A, E | BadArgsError, R, true>(\n arg.flag,\n Effect.gen(function* gateArgAvailability() {\n const state = yield* arg.state;\n if (condition || state._tag === 'Inactive') return state;\n if (state.provided) {\n return yield* BadArgsError.make({\n message:\n options?.message ??\n `${arg.flag} is not compatible with other options`,\n });\n }\n return inactive;\n }),\n );\n };\n}\n\n/**\n * Prompts for a missing text value.\n *\n * With --yes, the prompt is skipped. If inputProps.defaultValue is set, that\n * value is used. Otherwise the value stays missing.\n *\n * @example\n * const clientName = yield* Args.text(opts, 'name').pipe(\n * Args.prompt({\n * prompt: 'Client Name:',\n * defaultValue: suggestedClientName,\n * }),\n * Args.required(),\n * );\n */\nfunction prompt(inputProps: UI.TextInputProps) {\n return function promptArg<E, R, CanBeInactive extends boolean>(\n arg: Arg<string, E, R, CanBeInactive>,\n ): Arg<string, E | BadArgsError, R | GlobalOpts, CanBeInactive> {\n return mapActive(arg, (valueEffect) =>\n Effect.gen(function* promptWhenMissing() {\n const value = yield* valueEffect;\n if (value !== undefined) return value;\n\n const { yes } = yield* GlobalOpts;\n const result = yes\n ? inputProps.defaultValue\n : yield* runUIEffect(new UI.TextInput(inputProps)).pipe(\n Effect.catchTag('UIError', uiErrorToBadArgs),\n );\n if (result === undefined) return undefined;\n\n const trimmed = result.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }),\n );\n };\n}\n\n/**\n * Prompts for a missing boolean value.\n *\n * With --yes, the prompt is skipped and confirmationProps.defaultValue is used.\n *\n * @example\n * const configureWeb = yield* Args.bool(opts, 'configure-web').pipe(\n * Args.confirm({\n * promptText: 'Configure web redirect flow?',\n * defaultValue: false,\n * }),\n * Args.required(),\n * );\n */\nfunction confirm(confirmationProps: UI.ConfirmationProps) {\n return function confirmArg<E, R, CanBeInactive extends boolean>(\n arg: Arg<boolean, E, R, CanBeInactive>,\n ): Arg<boolean, E | BadArgsError, R | GlobalOpts, CanBeInactive> {\n return mapActive(arg, (valueEffect) =>\n Effect.gen(function* confirmWhenMissing() {\n const value = yield* valueEffect;\n if (value !== undefined) return value;\n\n const { yes } = yield* GlobalOpts;\n if (yes) return confirmationProps.defaultValue;\n\n return yield* runUIEffect(new UI.Confirmation(confirmationProps)).pipe(\n Effect.catchTag('UIError', uiErrorToBadArgs),\n );\n }),\n );\n };\n}\n\n/**\n * Validates a present value. Return an error message to fail, or undefined to\n * accept the value. Missing and inactive args pass through unchanged.\n *\n * @example\n * const projectId = yield* Args.text(opts, 'project-id').pipe(\n * Args.prompt(firebaseProjectIdPrompt({})),\n * Args.validate(validateFirebaseProjectId),\n * Args.required(),\n * );\n */\nfunction validate<A>(validator: (value: A) => string | undefined) {\n return function validateArg<E, R, CanBeInactive extends boolean>(\n arg: Arg<A, E, R, CanBeInactive>,\n ): Arg<A, E | BadArgsError, R, CanBeInactive> {\n return mapActive(arg, (valueEffect) =>\n Effect.gen(function* validateValue() {\n const value = yield* valueEffect;\n if (value === undefined) return undefined;\n\n const message = validator(value);\n if (!message) return value;\n\n return yield* BadArgsError.make({ message });\n }),\n );\n };\n}\n\n/**\n * Finishes a pipeline with a required value.\n *\n * Missing active args produce BadArgsError. Inactive args return undefined, so\n * callers can skip downstream work for unavailable flags.\n *\n * @example\n * const clientId = yield* Args.text(opts, 'client-id').pipe(\n * Args.prompt(clientIdPrompt({ providerUrl })),\n * Args.required(),\n * );\n */\nfunction required(options?: MissingOptions) {\n return function requiredArg<A, E, R, CanBeInactive extends boolean>(\n arg: Arg<A, E, R, CanBeInactive>,\n ): Effect.Effect<\n CanBeInactive extends true ? A | undefined : A,\n E | BadArgsError,\n R\n > {\n return Effect.gen(function* requireValue() {\n const state = yield* arg.state;\n if (state._tag === 'Inactive') return undefined;\n\n const value = yield* state.value;\n if (value !== undefined) return value;\n\n return yield* BadArgsError.make({\n message: options?.message ?? missingMessage(arg.flag),\n });\n }) as Effect.Effect<\n CanBeInactive extends true ? A | undefined : A,\n E | BadArgsError,\n R\n >;\n };\n}\n\n/**\n * Finishes a pipeline with an optional value.\n *\n * Missing and inactive args return undefined.\n *\n * @example\n * const customRedirectUri = yield* Args.text(opts, 'custom-redirect-uri').pipe(\n * Args.prompt(optionalRedirectPrompt),\n * Args.optional(),\n * );\n */\nfunction optional() {\n return function optionalArg<A, E, R, CanBeInactive extends boolean>(\n arg: Arg<A, E, R, CanBeInactive>,\n ) {\n return Effect.gen(function* optionalValue() {\n const state = yield* arg.state;\n if (state._tag === 'Inactive') return undefined;\n\n return yield* state.value;\n });\n };\n}\n\nexport const Args = {\n text,\n bool,\n has,\n hasAny,\n isTrue,\n availableWhen,\n prompt,\n confirm,\n validate,\n required,\n optional,\n};\n"]}
@@ -0,0 +1,21 @@
1
+ import type { AppBackup, BackupDownloadProgress, BackupDownloadResult, BackupsManager } from '@superlayer/platform';
2
+ export type { BackupDownloadProgress, BackupDownloadResult, } from '@superlayer/platform';
3
+ /**
4
+ * Downloads a backup into a zip file at `outPath` via the shared
5
+ * `BackupsManager.downloadArchive` pipeline, supplying the Node-specific
6
+ * pieces:
7
+ * presigned URLs are fetched with node:http(s) and decompressed explicitly,
8
+ * and the archive streams to disk with backpressure so memory stays flat
9
+ * regardless of backup size.
10
+ *
11
+ * Writes to `<outPath>.partial` and renames on success; a failed or aborted
12
+ * download removes the partial file.
13
+ */
14
+ export declare function downloadBackupToFile(opts: {
15
+ manager: BackupsManager;
16
+ backup: AppBackup;
17
+ outPath: string;
18
+ signal: AbortSignal;
19
+ onProgress: (progress: BackupDownloadProgress) => void;
20
+ }): Promise<BackupDownloadResult>;
21
+ //# sourceMappingURL=backupDownload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backupDownload.d.ts","sourceRoot":"","sources":["../../src/lib/backupDownload.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,SAAS,EAET,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAM9B,YAAY,EACV,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAuG9B;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,CAAC,QAAQ,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACxD,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA4ChC"}
@@ -0,0 +1,151 @@
1
+ import { createWriteStream } from 'node:fs';
2
+ import { open, rename, unlink } from 'node:fs/promises';
3
+ import { randomBytes } from 'node:crypto';
4
+ import { once } from 'node:events';
5
+ import { get as httpGet } from 'node:http';
6
+ import { get as httpsGet } from 'node:https';
7
+ import { Readable, Writable } from 'node:stream';
8
+ import zlib from 'node:zlib';
9
+ import version from '../version.js';
10
+ // Set a user-agent or else cloudfront might block the request
11
+ const userAgent = `instant-cli/${version}`;
12
+ // zstd landed in node:zlib in 22.15 / 23.8; on older Nodes the property is
13
+ // absent, so feature-detect instead of assuming the type declarations match
14
+ // the runtime.
15
+ const createZstdDecompress = zlib.createZstdDecompress;
16
+ function fetchStream(url, signal) {
17
+ return new Promise((resolve, reject) => {
18
+ const get = url.startsWith('https:') ? httpsGet : httpGet;
19
+ const req = get(url, { signal, headers: { 'user-agent': userAgent } }, resolve);
20
+ req.on('error', reject);
21
+ });
22
+ }
23
+ // .pipe doesn't forward errors, so a source failure would otherwise leave the
24
+ // destination (and the zip writer reading from it) hanging forever.
25
+ function pipe(src, dst) {
26
+ src.on('error', (e) => dst.destroy(e));
27
+ return src.pipe(dst);
28
+ }
29
+ // S3/CloudFront answer a rejected presigned URL with a short XML body naming
30
+ // the actual cause (SignatureDoesNotMatch, AccessDenied, expired, …). Read a
31
+ // bounded prefix so the error surfaces the reason instead of a bare status.
32
+ async function readErrorBody(res, signal) {
33
+ // A stalled or dribbling error body must not hang the download; destroying
34
+ // res ends the read below. The request's own signal already tears res down
35
+ // on a caller abort, so this only guards the no-abort stall.
36
+ const timer = setTimeout(() => res.destroy(), 5000);
37
+ try {
38
+ const chunks = [];
39
+ let total = 0;
40
+ for await (const chunk of res) {
41
+ chunks.push(chunk);
42
+ total += chunk.length;
43
+ if (total >= 2048)
44
+ break;
45
+ }
46
+ return Buffer.concat(chunks)
47
+ .toString('utf8')
48
+ .replace(/\s+/g, ' ')
49
+ .trim()
50
+ .slice(0, 500);
51
+ }
52
+ catch (e) {
53
+ // A caller-initiated abort must propagate, not be masked as an empty body;
54
+ // only other read failures fall back to no reason.
55
+ if (signal.aborted)
56
+ throw e;
57
+ return '';
58
+ }
59
+ finally {
60
+ clearTimeout(timer);
61
+ res.destroy();
62
+ }
63
+ }
64
+ // Fetches a presigned URL with node:http(s), decompressing explicitly: the
65
+ // entity shards are served with `Content-Encoding: zstd` and Node doesn't
66
+ // auto-decompress that. downloadBackupToFile refuses to run without zstd
67
+ // support, so the assertion below can't fire.
68
+ async function fetchBody(url, signal) {
69
+ const res = await fetchStream(url, signal);
70
+ if (res.statusCode !== 200) {
71
+ const body = await readErrorBody(res, signal);
72
+ throw new Error(`HTTP ${res.statusCode}${body ? ` — ${body}` : ''}`);
73
+ }
74
+ const encoding = res.headers['content-encoding'];
75
+ let stream = res;
76
+ if (encoding === 'zstd') {
77
+ stream = pipe(stream, createZstdDecompress());
78
+ }
79
+ else if (encoding === 'gzip') {
80
+ stream = pipe(stream, zlib.createGunzip());
81
+ }
82
+ else if (encoding) {
83
+ res.destroy();
84
+ throw new Error(`Unsupported content encoding: ${encoding}`);
85
+ }
86
+ return Readable.toWeb(stream);
87
+ }
88
+ async function createZipWriter(sink, signal) {
89
+ // Loaded on demand so every other CLI command skips parsing it.
90
+ const { ZipWriter } = await import('@zip.js/zip.js');
91
+ // zip64: without it any archive whose central-directory offset passes 4GB
92
+ // writes a wrapped 32-bit offset and the zip is unreadable.
93
+ return new ZipWriter(sink, { zip64: true, signal });
94
+ }
95
+ /**
96
+ * Downloads a backup into a zip file at `outPath` via the shared
97
+ * `BackupsManager.downloadArchive` pipeline, supplying the Node-specific
98
+ * pieces:
99
+ * presigned URLs are fetched with node:http(s) and decompressed explicitly,
100
+ * and the archive streams to disk with backpressure so memory stays flat
101
+ * regardless of backup size.
102
+ *
103
+ * Writes to `<outPath>.partial` and renames on success; a failed or aborted
104
+ * download removes the partial file.
105
+ */
106
+ export async function downloadBackupToFile(opts) {
107
+ // The entity shards are served with `Content-Encoding: zstd`; fail before
108
+ // writing anything if this Node can't decompress them.
109
+ if (!createZstdDecompress) {
110
+ throw new Error('Downloading backups requires Node 22.15 or newer (for zstd support).');
111
+ }
112
+ // Randomized so a stale partial or a concurrent download of the same
113
+ // backup can't collide; 'wx' turns any remaining collision into an error
114
+ // instead of silently truncating another run's file.
115
+ const partialPath = `${opts.outPath}.partial-${randomBytes(4).toString('hex')}`;
116
+ const fileStream = createWriteStream(partialPath, { flags: 'wx' });
117
+ const awaitFileClosed = async () => {
118
+ if (!fileStream.closed)
119
+ await once(fileStream, 'close');
120
+ };
121
+ try {
122
+ const result = await opts.manager.downloadArchive({
123
+ backup: opts.backup,
124
+ fetchBody,
125
+ sink: Writable.toWeb(fileStream),
126
+ createWriter: createZipWriter,
127
+ signal: opts.signal,
128
+ onProgress: opts.onProgress,
129
+ });
130
+ await awaitFileClosed();
131
+ // Flush to disk before the rename so a crash right after can't leave a
132
+ // complete-looking zip with unwritten tails.
133
+ const fh = await open(partialPath, 'r+');
134
+ try {
135
+ await fh.sync();
136
+ }
137
+ finally {
138
+ await fh.close();
139
+ }
140
+ await rename(partialPath, opts.outPath);
141
+ return result;
142
+ }
143
+ catch (e) {
144
+ // The pipeline already aborted the sink; wait for the fd to close, then
145
+ // discard the partial file on disk.
146
+ await awaitFileClosed().catch(() => { });
147
+ await unlink(partialPath).catch(() => { });
148
+ throw e;
149
+ }
150
+ }
151
+ //# sourceMappingURL=backupDownload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backupDownload.js","sourceRoot":"","sources":["../../src/lib/backupDownload.ts"],"names":[],"mappings":"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","sourcesContent":["import { createWriteStream } from 'node:fs';\nimport { open, rename, unlink } from 'node:fs/promises';\nimport { randomBytes } from 'node:crypto';\nimport { once } from 'node:events';\nimport { get as httpGet, type IncomingMessage } from 'node:http';\nimport { get as httpsGet } from 'node:https';\nimport { Readable, Writable, type Duplex } from 'node:stream';\nimport zlib from 'node:zlib';\nimport type {\n AppBackup,\n BackupArchiveWriter,\n BackupDownloadProgress,\n BackupDownloadResult,\n BackupsManager,\n} from '@superlayer/platform';\nimport version from '../version.js';\n\n// Set a user-agent or else cloudfront might block the request\nconst userAgent = `instant-cli/${version}`;\n\nexport type {\n BackupDownloadProgress,\n BackupDownloadResult,\n} from '@superlayer/platform';\n\n// zstd landed in node:zlib in 22.15 / 23.8; on older Nodes the property is\n// absent, so feature-detect instead of assuming the type declarations match\n// the runtime.\nconst createZstdDecompress: (() => Duplex) | undefined = (\n zlib as { createZstdDecompress?: () => Duplex }\n).createZstdDecompress;\n\nfunction fetchStream(\n url: string,\n signal: AbortSignal,\n): Promise<IncomingMessage> {\n return new Promise((resolve, reject) => {\n const get = url.startsWith('https:') ? httpsGet : httpGet;\n const req = get(\n url,\n { signal, headers: { 'user-agent': userAgent } },\n resolve,\n );\n req.on('error', reject);\n });\n}\n\n// .pipe doesn't forward errors, so a source failure would otherwise leave the\n// destination (and the zip writer reading from it) hanging forever.\nfunction pipe(src: Readable, dst: Duplex): Duplex {\n src.on('error', (e) => dst.destroy(e));\n return src.pipe(dst);\n}\n\n// S3/CloudFront answer a rejected presigned URL with a short XML body naming\n// the actual cause (SignatureDoesNotMatch, AccessDenied, expired, …). Read a\n// bounded prefix so the error surfaces the reason instead of a bare status.\nasync function readErrorBody(\n res: IncomingMessage,\n signal: AbortSignal,\n): Promise<string> {\n // A stalled or dribbling error body must not hang the download; destroying\n // res ends the read below. The request's own signal already tears res down\n // on a caller abort, so this only guards the no-abort stall.\n const timer = setTimeout(() => res.destroy(), 5000);\n try {\n const chunks: Buffer[] = [];\n let total = 0;\n for await (const chunk of res) {\n chunks.push(chunk as Buffer);\n total += (chunk as Buffer).length;\n if (total >= 2048) break;\n }\n return Buffer.concat(chunks)\n .toString('utf8')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, 500);\n } catch (e) {\n // A caller-initiated abort must propagate, not be masked as an empty body;\n // only other read failures fall back to no reason.\n if (signal.aborted) throw e;\n return '';\n } finally {\n clearTimeout(timer);\n res.destroy();\n }\n}\n\n// Fetches a presigned URL with node:http(s), decompressing explicitly: the\n// entity shards are served with `Content-Encoding: zstd` and Node doesn't\n// auto-decompress that. downloadBackupToFile refuses to run without zstd\n// support, so the assertion below can't fire.\nasync function fetchBody(\n url: string,\n signal: AbortSignal,\n): Promise<ReadableStream<Uint8Array>> {\n const res = await fetchStream(url, signal);\n if (res.statusCode !== 200) {\n const body = await readErrorBody(res, signal);\n throw new Error(`HTTP ${res.statusCode}${body ? ` — ${body}` : ''}`);\n }\n const encoding = res.headers['content-encoding'];\n let stream: Readable = res;\n if (encoding === 'zstd') {\n stream = pipe(stream, createZstdDecompress!());\n } else if (encoding === 'gzip') {\n stream = pipe(stream, zlib.createGunzip());\n } else if (encoding) {\n res.destroy();\n throw new Error(`Unsupported content encoding: ${encoding}`);\n }\n return Readable.toWeb(stream) as ReadableStream<Uint8Array>;\n}\n\nasync function createZipWriter(\n sink: WritableStream<Uint8Array>,\n signal: AbortSignal,\n): Promise<BackupArchiveWriter> {\n // Loaded on demand so every other CLI command skips parsing it.\n const { ZipWriter } = await import('@zip.js/zip.js');\n // zip64: without it any archive whose central-directory offset passes 4GB\n // writes a wrapped 32-bit offset and the zip is unreadable.\n return new ZipWriter(sink, { zip64: true, signal });\n}\n\n/**\n * Downloads a backup into a zip file at `outPath` via the shared\n * `BackupsManager.downloadArchive` pipeline, supplying the Node-specific\n * pieces:\n * presigned URLs are fetched with node:http(s) and decompressed explicitly,\n * and the archive streams to disk with backpressure so memory stays flat\n * regardless of backup size.\n *\n * Writes to `<outPath>.partial` and renames on success; a failed or aborted\n * download removes the partial file.\n */\nexport async function downloadBackupToFile(opts: {\n manager: BackupsManager;\n backup: AppBackup;\n outPath: string;\n signal: AbortSignal;\n onProgress: (progress: BackupDownloadProgress) => void;\n}): Promise<BackupDownloadResult> {\n // The entity shards are served with `Content-Encoding: zstd`; fail before\n // writing anything if this Node can't decompress them.\n if (!createZstdDecompress) {\n throw new Error(\n 'Downloading backups requires Node 22.15 or newer (for zstd support).',\n );\n }\n\n // Randomized so a stale partial or a concurrent download of the same\n // backup can't collide; 'wx' turns any remaining collision into an error\n // instead of silently truncating another run's file.\n const partialPath = `${opts.outPath}.partial-${randomBytes(4).toString('hex')}`;\n const fileStream = createWriteStream(partialPath, { flags: 'wx' });\n const awaitFileClosed = async () => {\n if (!fileStream.closed) await once(fileStream, 'close');\n };\n try {\n const result = await opts.manager.downloadArchive({\n backup: opts.backup,\n fetchBody,\n sink: Writable.toWeb(fileStream) as WritableStream<Uint8Array>,\n createWriter: createZipWriter,\n signal: opts.signal,\n onProgress: opts.onProgress,\n });\n await awaitFileClosed();\n // Flush to disk before the rename so a crash right after can't leave a\n // complete-looking zip with unwritten tails.\n const fh = await open(partialPath, 'r+');\n try {\n await fh.sync();\n } finally {\n await fh.close();\n }\n await rename(partialPath, opts.outPath);\n return result;\n } catch (e) {\n // The pipeline already aborted the sink; wait for the fd to close, then\n // discard the partial file on disk.\n await awaitFileClosed().catch(() => {});\n await unlink(partialPath).catch(() => {});\n throw e;\n }\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import { Effect } from 'effect';
2
+ import type { BackupsManager } from '@superlayer/platform';
3
+ import { CurrentApp } from '../context/currentApp.ts';
4
+ import { PlatformApiError } from '../context/platformApi.ts';
5
+ export declare const useBackupsManager: <R>(fun: (manager: BackupsManager) => Promise<R>, errorMessage?: string) => Effect.Effect<R, import("../errors.ts").BadArgsError | import("effect/Cause").UnknownException | import("effect/ConfigError").ConfigError | PlatformApiError, import("../context/authToken.ts").AuthToken | CurrentApp>;
6
+ /**
7
+ * Yields a `BackupsManager` instance scoped to the current app. Use when you
8
+ * need to hold on to the manager outside an Effect (e.g. to drive the
9
+ * long-running download pipeline).
10
+ */
11
+ export declare const buildBackupsManager: Effect.Effect<BackupsManager, import("../errors.ts").BadArgsError | import("effect/Cause").UnknownException | import("effect/ConfigError").ConfigError, import("../context/authToken.ts").AuthToken | CurrentApp>;
12
+ //# sourceMappingURL=backups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backups.d.ts","sourceRoot":"","sources":["../../src/lib/backups.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAG7D,eAAO,MAAM,iBAAiB,GAAI,CAAC,EACjC,KAAK,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,EAC5C,eAAe,MAAM,4NAanB,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,mNAI9B,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { Effect } from 'effect';
2
+ import { CurrentApp } from "../context/currentApp.js";
3
+ import { PlatformApiError } from "../context/platformApi.js";
4
+ import { getAuthedPlatformApi } from "./platformApi.js";
5
+ export const useBackupsManager = (fun, errorMessage) => Effect.gen(function* () {
6
+ const api = yield* getAuthedPlatformApi;
7
+ const { appId } = yield* CurrentApp;
8
+ return yield* Effect.tryPromise({
9
+ try: () => fun(api.backups(appId)),
10
+ catch: (e) => new PlatformApiError({
11
+ message: errorMessage ?? 'Error using backups api',
12
+ cause: e,
13
+ }),
14
+ });
15
+ });
16
+ /**
17
+ * Yields a `BackupsManager` instance scoped to the current app. Use when you
18
+ * need to hold on to the manager outside an Effect (e.g. to drive the
19
+ * long-running download pipeline).
20
+ */
21
+ export const buildBackupsManager = Effect.gen(function* () {
22
+ const api = yield* getAuthedPlatformApi;
23
+ const { appId } = yield* CurrentApp;
24
+ return api.backups(appId);
25
+ });
26
+ //# sourceMappingURL=backups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backups.js","sourceRoot":"","sources":["../../src/lib/backups.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,GAA4C,EAC5C,YAAqB,EACrB,EAAE,CACF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAC;IACxC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC;IACpC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QAC9B,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAClC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CACX,IAAI,gBAAgB,CAAC;YACnB,OAAO,EAAE,YAAY,IAAI,yBAAyB;YAClD,KAAK,EAAE,CAAC;SACT,CAAC;KACL,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACrD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAC;IACxC,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC;IACpC,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC,CAAC,CAAC","sourcesContent":["import { Effect } from 'effect';\nimport type { BackupsManager } from '@superlayer/platform';\nimport { CurrentApp } from '../context/currentApp.ts';\nimport { PlatformApiError } from '../context/platformApi.ts';\nimport { getAuthedPlatformApi } from './platformApi.ts';\n\nexport const useBackupsManager = <R>(\n fun: (manager: BackupsManager) => Promise<R>,\n errorMessage?: string,\n) =>\n Effect.gen(function* () {\n const api = yield* getAuthedPlatformApi;\n const { appId } = yield* CurrentApp;\n return yield* Effect.tryPromise({\n try: () => fun(api.backups(appId)),\n catch: (e) =>\n new PlatformApiError({\n message: errorMessage ?? 'Error using backups api',\n cause: e,\n }),\n });\n });\n\n/**\n * Yields a `BackupsManager` instance scoped to the current app. Use when you\n * need to hold on to the manager outside an Effect (e.g. to drive the\n * long-running download pipeline).\n */\nexport const buildBackupsManager = Effect.gen(function* () {\n const api = yield* getAuthedPlatformApi;\n const { appId } = yield* CurrentApp;\n return api.backups(appId);\n});\n"]}
@@ -0,0 +1,6 @@
1
+ import { Effect } from 'effect';
2
+ import { BadArgsError } from '../errors.ts';
3
+ export declare const getBaseUrl: Effect.Effect<string, BadArgsError | import("effect/Cause").UnknownException | import("effect/ConfigError").ConfigError, never>;
4
+ export declare const getOAuthCallbackUrl: Effect.Effect<string, BadArgsError | import("effect/Cause").UnknownException | import("effect/ConfigError").ConfigError, never>;
5
+ export declare const getDashUrl: Effect.Effect<string, BadArgsError | import("effect/Cause").UnknownException | import("effect/ConfigError").ConfigError, never>;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,MAAM,EAAkB,MAAM,QAAQ,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAY5C,eAAO,MAAM,UAAU,iIA0BrB,CAAC;AAEH,eAAO,MAAM,mBAAmB,iIAE/B,CAAC;AAEF,eAAO,MAAM,UAAU,iIA0BrB,CAAC"}