@atlasauth/cli 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (530) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +4 -4
  3. package/dist/args.d.ts +0 -601
  4. package/dist/args.js +0 -289
  5. package/dist/args.js.map +0 -1
  6. package/dist/cli.d.ts +0 -2
  7. package/dist/cli.js +0 -19
  8. package/dist/cli.js.map +0 -1
  9. package/dist/commands/actions-gallery.d.ts +0 -8
  10. package/dist/commands/actions-gallery.js +0 -197
  11. package/dist/commands/actions-gallery.js.map +0 -1
  12. package/dist/commands/actions.d.ts +0 -13
  13. package/dist/commands/actions.js +0 -135
  14. package/dist/commands/actions.js.map +0 -1
  15. package/dist/commands/admin.d.ts +0 -49
  16. package/dist/commands/admin.js +0 -353
  17. package/dist/commands/admin.js.map +0 -1
  18. package/dist/commands/api-keys.d.ts +0 -4
  19. package/dist/commands/api-keys.js +0 -96
  20. package/dist/commands/api-keys.js.map +0 -1
  21. package/dist/commands/auth.d.ts +0 -25
  22. package/dist/commands/auth.js +0 -102
  23. package/dist/commands/auth.js.map +0 -1
  24. package/dist/commands/config.d.ts +0 -104
  25. package/dist/commands/config.js +0 -1506
  26. package/dist/commands/config.js.map +0 -1
  27. package/dist/commands/csp.d.ts +0 -32
  28. package/dist/commands/csp.js +0 -180
  29. package/dist/commands/csp.js.map +0 -1
  30. package/dist/commands/dev.d.ts +0 -8
  31. package/dist/commands/dev.js +0 -84
  32. package/dist/commands/dev.js.map +0 -1
  33. package/dist/commands/fga.d.ts +0 -10
  34. package/dist/commands/fga.js +0 -146
  35. package/dist/commands/fga.js.map +0 -1
  36. package/dist/commands/golive.d.ts +0 -40
  37. package/dist/commands/golive.js +0 -285
  38. package/dist/commands/golive.js.map +0 -1
  39. package/dist/commands/id-accounts.d.ts +0 -13
  40. package/dist/commands/id-accounts.js +0 -195
  41. package/dist/commands/id-accounts.js.map +0 -1
  42. package/dist/commands/import-better-auth.d.ts +0 -3
  43. package/dist/commands/import-better-auth.js +0 -164
  44. package/dist/commands/import-better-auth.js.map +0 -1
  45. package/dist/commands/import.d.ts +0 -3
  46. package/dist/commands/import.js +0 -123
  47. package/dist/commands/import.js.map +0 -1
  48. package/dist/commands/info.d.ts +0 -75
  49. package/dist/commands/info.js +0 -362
  50. package/dist/commands/info.js.map +0 -1
  51. package/dist/commands/init.d.ts +0 -3
  52. package/dist/commands/init.js +0 -438
  53. package/dist/commands/init.js.map +0 -1
  54. package/dist/commands/integrations.d.ts +0 -8
  55. package/dist/commands/integrations.js +0 -112
  56. package/dist/commands/integrations.js.map +0 -1
  57. package/dist/commands/listen.d.ts +0 -17
  58. package/dist/commands/listen.js +0 -223
  59. package/dist/commands/listen.js.map +0 -1
  60. package/dist/commands/mcp.d.ts +0 -38
  61. package/dist/commands/mcp.js +0 -111
  62. package/dist/commands/mcp.js.map +0 -1
  63. package/dist/commands/migrate.d.ts +0 -16
  64. package/dist/commands/migrate.js +0 -114
  65. package/dist/commands/migrate.js.map +0 -1
  66. package/dist/commands/move-origin.d.ts +0 -11
  67. package/dist/commands/move-origin.js +0 -95
  68. package/dist/commands/move-origin.js.map +0 -1
  69. package/dist/commands/operator.d.ts +0 -18
  70. package/dist/commands/operator.js +0 -238
  71. package/dist/commands/operator.js.map +0 -1
  72. package/dist/commands/orgs.d.ts +0 -4
  73. package/dist/commands/orgs.js +0 -72
  74. package/dist/commands/orgs.js.map +0 -1
  75. package/dist/commands/pages.d.ts +0 -14
  76. package/dist/commands/pages.js +0 -113
  77. package/dist/commands/pages.js.map +0 -1
  78. package/dist/commands/partner-platforms.d.ts +0 -10
  79. package/dist/commands/partner-platforms.js +0 -157
  80. package/dist/commands/partner-platforms.js.map +0 -1
  81. package/dist/commands/project-commands.d.ts +0 -38
  82. package/dist/commands/project-commands.js +0 -361
  83. package/dist/commands/project-commands.js.map +0 -1
  84. package/dist/commands/referrals.d.ts +0 -69
  85. package/dist/commands/referrals.js +0 -1056
  86. package/dist/commands/referrals.js.map +0 -1
  87. package/dist/commands/resources.d.ts +0 -44
  88. package/dist/commands/resources.js +0 -234
  89. package/dist/commands/resources.js.map +0 -1
  90. package/dist/commands/sessions.d.ts +0 -4
  91. package/dist/commands/sessions.js +0 -28
  92. package/dist/commands/sessions.js.map +0 -1
  93. package/dist/commands/shared.d.ts +0 -38
  94. package/dist/commands/shared.js +0 -69
  95. package/dist/commands/shared.js.map +0 -1
  96. package/dist/commands/status.d.ts +0 -3
  97. package/dist/commands/status.js +0 -77
  98. package/dist/commands/status.js.map +0 -1
  99. package/dist/commands/users.d.ts +0 -4
  100. package/dist/commands/users.js +0 -100
  101. package/dist/commands/users.js.map +0 -1
  102. package/dist/commands/webhooks.d.ts +0 -34
  103. package/dist/commands/webhooks.js +0 -301
  104. package/dist/commands/webhooks.js.map +0 -1
  105. package/dist/config-http.d.ts +0 -29
  106. package/dist/config-http.js +0 -64
  107. package/dist/config-http.js.map +0 -1
  108. package/dist/config.d.ts +0 -64
  109. package/dist/config.js +0 -69
  110. package/dist/config.js.map +0 -1
  111. package/dist/context.d.ts +0 -72
  112. package/dist/context.js +0 -27
  113. package/dist/context.js.map +0 -1
  114. package/dist/define-config.d.ts +0 -9
  115. package/dist/define-config.js +0 -8
  116. package/dist/define-config.js.map +0 -1
  117. package/dist/errors.d.ts +0 -15
  118. package/dist/errors.js +0 -25
  119. package/dist/errors.js.map +0 -1
  120. package/dist/help.d.ts +0 -8
  121. package/dist/help.js +0 -868
  122. package/dist/help.js.map +0 -1
  123. package/dist/import/csv.d.ts +0 -6
  124. package/dist/import/csv.js +0 -63
  125. package/dist/import/csv.js.map +0 -1
  126. package/dist/import/vendors.d.ts +0 -57
  127. package/dist/import/vendors.js +0 -568
  128. package/dist/import/vendors.js.map +0 -1
  129. package/dist/index.d.ts +0 -33
  130. package/dist/index.js +0 -81
  131. package/dist/index.js.map +0 -1
  132. package/dist/io.d.ts +0 -21
  133. package/dist/io.js +0 -28
  134. package/dist/io.js.map +0 -1
  135. package/dist/migrate/appwrite/plugin.d.ts +0 -2
  136. package/dist/migrate/appwrite/plugin.js +0 -103
  137. package/dist/migrate/appwrite/plugin.js.map +0 -1
  138. package/dist/migrate/auth0/plugin.d.ts +0 -2
  139. package/dist/migrate/auth0/plugin.js +0 -126
  140. package/dist/migrate/auth0/plugin.js.map +0 -1
  141. package/dist/migrate/authentik/plugin.d.ts +0 -2
  142. package/dist/migrate/authentik/plugin.js +0 -135
  143. package/dist/migrate/authentik/plugin.js.map +0 -1
  144. package/dist/migrate/azure-ad-b2c/plugin.d.ts +0 -8
  145. package/dist/migrate/azure-ad-b2c/plugin.js +0 -165
  146. package/dist/migrate/azure-ad-b2c/plugin.js.map +0 -1
  147. package/dist/migrate/better-auth/integration.d.ts +0 -59
  148. package/dist/migrate/better-auth/integration.js +0 -81
  149. package/dist/migrate/better-auth/integration.js.map +0 -1
  150. package/dist/migrate/better-auth/orchestrator.d.ts +0 -43
  151. package/dist/migrate/better-auth/orchestrator.js +0 -525
  152. package/dist/migrate/better-auth/orchestrator.js.map +0 -1
  153. package/dist/migrate/better-auth/output.d.ts +0 -20
  154. package/dist/migrate/better-auth/output.js +0 -146
  155. package/dist/migrate/better-auth/output.js.map +0 -1
  156. package/dist/migrate/better-auth/translate.d.ts +0 -77
  157. package/dist/migrate/better-auth/translate.js +0 -1989
  158. package/dist/migrate/better-auth/translate.js.map +0 -1
  159. package/dist/migrate/clerk/plugin.d.ts +0 -2
  160. package/dist/migrate/clerk/plugin.js +0 -76
  161. package/dist/migrate/clerk/plugin.js.map +0 -1
  162. package/dist/migrate/cognito/plugin.d.ts +0 -5
  163. package/dist/migrate/cognito/plugin.js +0 -170
  164. package/dist/migrate/cognito/plugin.js.map +0 -1
  165. package/dist/migrate/corbado/plugin.d.ts +0 -2
  166. package/dist/migrate/corbado/plugin.js +0 -63
  167. package/dist/migrate/corbado/plugin.js.map +0 -1
  168. package/dist/migrate/descope/plugin.d.ts +0 -2
  169. package/dist/migrate/descope/plugin.js +0 -83
  170. package/dist/migrate/descope/plugin.js.map +0 -1
  171. package/dist/migrate/entra-external-id/plugin.d.ts +0 -8
  172. package/dist/migrate/entra-external-id/plugin.js +0 -12
  173. package/dist/migrate/entra-external-id/plugin.js.map +0 -1
  174. package/dist/migrate/export/appwrite-config.d.ts +0 -28
  175. package/dist/migrate/export/appwrite-config.js +0 -170
  176. package/dist/migrate/export/appwrite-config.js.map +0 -1
  177. package/dist/migrate/export/appwrite.d.ts +0 -51
  178. package/dist/migrate/export/appwrite.js +0 -288
  179. package/dist/migrate/export/appwrite.js.map +0 -1
  180. package/dist/migrate/export/auth0-config.d.ts +0 -29
  181. package/dist/migrate/export/auth0-config.js +0 -249
  182. package/dist/migrate/export/auth0-config.js.map +0 -1
  183. package/dist/migrate/export/auth0.d.ts +0 -62
  184. package/dist/migrate/export/auth0.js +0 -575
  185. package/dist/migrate/export/auth0.js.map +0 -1
  186. package/dist/migrate/export/authentik-config.d.ts +0 -45
  187. package/dist/migrate/export/authentik-config.js +0 -357
  188. package/dist/migrate/export/authentik-config.js.map +0 -1
  189. package/dist/migrate/export/authentik.d.ts +0 -110
  190. package/dist/migrate/export/authentik.js +0 -838
  191. package/dist/migrate/export/authentik.js.map +0 -1
  192. package/dist/migrate/export/azure-config.d.ts +0 -58
  193. package/dist/migrate/export/azure-config.js +0 -344
  194. package/dist/migrate/export/azure-config.js.map +0 -1
  195. package/dist/migrate/export/azure.d.ts +0 -85
  196. package/dist/migrate/export/azure.js +0 -373
  197. package/dist/migrate/export/azure.js.map +0 -1
  198. package/dist/migrate/export/clerk-config.d.ts +0 -26
  199. package/dist/migrate/export/clerk-config.js +0 -227
  200. package/dist/migrate/export/clerk-config.js.map +0 -1
  201. package/dist/migrate/export/clerk.d.ts +0 -46
  202. package/dist/migrate/export/clerk.js +0 -486
  203. package/dist/migrate/export/clerk.js.map +0 -1
  204. package/dist/migrate/export/cognito-config.d.ts +0 -25
  205. package/dist/migrate/export/cognito-config.js +0 -273
  206. package/dist/migrate/export/cognito-config.js.map +0 -1
  207. package/dist/migrate/export/cognito.d.ts +0 -103
  208. package/dist/migrate/export/cognito.js +0 -441
  209. package/dist/migrate/export/cognito.js.map +0 -1
  210. package/dist/migrate/export/corbado-config.d.ts +0 -20
  211. package/dist/migrate/export/corbado-config.js +0 -63
  212. package/dist/migrate/export/corbado-config.js.map +0 -1
  213. package/dist/migrate/export/corbado.d.ts +0 -37
  214. package/dist/migrate/export/corbado.js +0 -132
  215. package/dist/migrate/export/corbado.js.map +0 -1
  216. package/dist/migrate/export/descope.d.ts +0 -35
  217. package/dist/migrate/export/descope.js +0 -208
  218. package/dist/migrate/export/descope.js.map +0 -1
  219. package/dist/migrate/export/fake-vendor.test-helper.d.ts +0 -27
  220. package/dist/migrate/export/fake-vendor.test-helper.js +0 -41
  221. package/dist/migrate/export/fake-vendor.test-helper.js.map +0 -1
  222. package/dist/migrate/export/firebase-config.d.ts +0 -24
  223. package/dist/migrate/export/firebase-config.js +0 -215
  224. package/dist/migrate/export/firebase-config.js.map +0 -1
  225. package/dist/migrate/export/firebase.d.ts +0 -102
  226. package/dist/migrate/export/firebase.js +0 -464
  227. package/dist/migrate/export/firebase.js.map +0 -1
  228. package/dist/migrate/export/frameworks/app-users.d.ts +0 -42
  229. package/dist/migrate/export/frameworks/app-users.js +0 -161
  230. package/dist/migrate/export/frameworks/app-users.js.map +0 -1
  231. package/dist/migrate/export/frameworks/authjs.d.ts +0 -41
  232. package/dist/migrate/export/frameworks/authjs.js +0 -146
  233. package/dist/migrate/export/frameworks/authjs.js.map +0 -1
  234. package/dist/migrate/export/frameworks/devise.d.ts +0 -39
  235. package/dist/migrate/export/frameworks/devise.js +0 -218
  236. package/dist/migrate/export/frameworks/devise.js.map +0 -1
  237. package/dist/migrate/export/frameworks/django.d.ts +0 -45
  238. package/dist/migrate/export/frameworks/django.js +0 -315
  239. package/dist/migrate/export/frameworks/django.js.map +0 -1
  240. package/dist/migrate/export/frameworks/lucia.d.ts +0 -42
  241. package/dist/migrate/export/frameworks/lucia.js +0 -263
  242. package/dist/migrate/export/frameworks/lucia.js.map +0 -1
  243. package/dist/migrate/export/frameworks/tables.d.ts +0 -90
  244. package/dist/migrate/export/frameworks/tables.js +0 -325
  245. package/dist/migrate/export/frameworks/tables.js.map +0 -1
  246. package/dist/migrate/export/frameworks/webauthn.d.ts +0 -25
  247. package/dist/migrate/export/frameworks/webauthn.js +0 -130
  248. package/dist/migrate/export/frameworks/webauthn.js.map +0 -1
  249. package/dist/migrate/export/frontegg-config.d.ts +0 -29
  250. package/dist/migrate/export/frontegg-config.js +0 -238
  251. package/dist/migrate/export/frontegg-config.js.map +0 -1
  252. package/dist/migrate/export/frontegg.d.ts +0 -67
  253. package/dist/migrate/export/frontegg.js +0 -410
  254. package/dist/migrate/export/frontegg.js.map +0 -1
  255. package/dist/migrate/export/fusionauth-config.d.ts +0 -26
  256. package/dist/migrate/export/fusionauth-config.js +0 -226
  257. package/dist/migrate/export/fusionauth-config.js.map +0 -1
  258. package/dist/migrate/export/fusionauth.d.ts +0 -99
  259. package/dist/migrate/export/fusionauth.js +0 -642
  260. package/dist/migrate/export/fusionauth.js.map +0 -1
  261. package/dist/migrate/export/hanko-config.d.ts +0 -27
  262. package/dist/migrate/export/hanko-config.js +0 -265
  263. package/dist/migrate/export/hanko-config.js.map +0 -1
  264. package/dist/migrate/export/hanko.d.ts +0 -52
  265. package/dist/migrate/export/hanko.js +0 -605
  266. package/dist/migrate/export/hanko.js.map +0 -1
  267. package/dist/migrate/export/keycloak-config.d.ts +0 -19
  268. package/dist/migrate/export/keycloak-config.js +0 -219
  269. package/dist/migrate/export/keycloak-config.js.map +0 -1
  270. package/dist/migrate/export/keycloak.d.ts +0 -155
  271. package/dist/migrate/export/keycloak.js +0 -878
  272. package/dist/migrate/export/keycloak.js.map +0 -1
  273. package/dist/migrate/export/kinde-descope-config.d.ts +0 -38
  274. package/dist/migrate/export/kinde-descope-config.js +0 -172
  275. package/dist/migrate/export/kinde-descope-config.js.map +0 -1
  276. package/dist/migrate/export/kinde.d.ts +0 -57
  277. package/dist/migrate/export/kinde.js +0 -333
  278. package/dist/migrate/export/kinde.js.map +0 -1
  279. package/dist/migrate/export/ldap.d.ts +0 -53
  280. package/dist/migrate/export/ldap.js +0 -277
  281. package/dist/migrate/export/ldap.js.map +0 -1
  282. package/dist/migrate/export/logto-config.d.ts +0 -27
  283. package/dist/migrate/export/logto-config.js +0 -264
  284. package/dist/migrate/export/logto-config.js.map +0 -1
  285. package/dist/migrate/export/logto.d.ts +0 -150
  286. package/dist/migrate/export/logto.js +0 -809
  287. package/dist/migrate/export/logto.js.map +0 -1
  288. package/dist/migrate/export/magic-config.d.ts +0 -14
  289. package/dist/migrate/export/magic-config.js +0 -28
  290. package/dist/migrate/export/magic-config.js.map +0 -1
  291. package/dist/migrate/export/magic.d.ts +0 -31
  292. package/dist/migrate/export/magic.js +0 -93
  293. package/dist/migrate/export/magic.js.map +0 -1
  294. package/dist/migrate/export/mini-yaml.d.ts +0 -19
  295. package/dist/migrate/export/mini-yaml.js +0 -581
  296. package/dist/migrate/export/mini-yaml.js.map +0 -1
  297. package/dist/migrate/export/nhost-config.d.ts +0 -26
  298. package/dist/migrate/export/nhost-config.js +0 -360
  299. package/dist/migrate/export/nhost-config.js.map +0 -1
  300. package/dist/migrate/export/nhost.d.ts +0 -64
  301. package/dist/migrate/export/nhost.js +0 -304
  302. package/dist/migrate/export/nhost.js.map +0 -1
  303. package/dist/migrate/export/okta-config.d.ts +0 -30
  304. package/dist/migrate/export/okta-config.js +0 -205
  305. package/dist/migrate/export/okta-config.js.map +0 -1
  306. package/dist/migrate/export/okta.d.ts +0 -43
  307. package/dist/migrate/export/okta.js +0 -337
  308. package/dist/migrate/export/okta.js.map +0 -1
  309. package/dist/migrate/export/onelogin.d.ts +0 -22
  310. package/dist/migrate/export/onelogin.js +0 -123
  311. package/dist/migrate/export/onelogin.js.map +0 -1
  312. package/dist/migrate/export/ory-config.d.ts +0 -58
  313. package/dist/migrate/export/ory-config.js +0 -451
  314. package/dist/migrate/export/ory-config.js.map +0 -1
  315. package/dist/migrate/export/ory.d.ts +0 -81
  316. package/dist/migrate/export/ory.js +0 -555
  317. package/dist/migrate/export/ory.js.map +0 -1
  318. package/dist/migrate/export/password-detect.d.ts +0 -29
  319. package/dist/migrate/export/password-detect.js +0 -81
  320. package/dist/migrate/export/password-detect.js.map +0 -1
  321. package/dist/migrate/export/pingone.d.ts +0 -36
  322. package/dist/migrate/export/pingone.js +0 -218
  323. package/dist/migrate/export/pingone.js.map +0 -1
  324. package/dist/migrate/export/plugin-kit.d.ts +0 -25
  325. package/dist/migrate/export/plugin-kit.js +0 -82
  326. package/dist/migrate/export/plugin-kit.js.map +0 -1
  327. package/dist/migrate/export/pocketbase-config.d.ts +0 -28
  328. package/dist/migrate/export/pocketbase-config.js +0 -180
  329. package/dist/migrate/export/pocketbase-config.js.map +0 -1
  330. package/dist/migrate/export/pocketbase.d.ts +0 -52
  331. package/dist/migrate/export/pocketbase.js +0 -243
  332. package/dist/migrate/export/pocketbase.js.map +0 -1
  333. package/dist/migrate/export/project-files.d.ts +0 -15
  334. package/dist/migrate/export/project-files.js +0 -43
  335. package/dist/migrate/export/project-files.js.map +0 -1
  336. package/dist/migrate/export/propelauth-config.d.ts +0 -18
  337. package/dist/migrate/export/propelauth-config.js +0 -58
  338. package/dist/migrate/export/propelauth-config.js.map +0 -1
  339. package/dist/migrate/export/propelauth-harness.test-helper.d.ts +0 -62
  340. package/dist/migrate/export/propelauth-harness.test-helper.js +0 -109
  341. package/dist/migrate/export/propelauth-harness.test-helper.js.map +0 -1
  342. package/dist/migrate/export/propelauth.d.ts +0 -48
  343. package/dist/migrate/export/propelauth.js +0 -255
  344. package/dist/migrate/export/propelauth.js.map +0 -1
  345. package/dist/migrate/export/record-set.d.ts +0 -44
  346. package/dist/migrate/export/record-set.js +0 -98
  347. package/dist/migrate/export/record-set.js.map +0 -1
  348. package/dist/migrate/export/source-lines.d.ts +0 -17
  349. package/dist/migrate/export/source-lines.js +0 -43
  350. package/dist/migrate/export/source-lines.js.map +0 -1
  351. package/dist/migrate/export/sso-metadata.d.ts +0 -47
  352. package/dist/migrate/export/sso-metadata.js +0 -180
  353. package/dist/migrate/export/sso-metadata.js.map +0 -1
  354. package/dist/migrate/export/stytch-config.d.ts +0 -22
  355. package/dist/migrate/export/stytch-config.js +0 -102
  356. package/dist/migrate/export/stytch-config.js.map +0 -1
  357. package/dist/migrate/export/stytch.d.ts +0 -68
  358. package/dist/migrate/export/stytch.js +0 -510
  359. package/dist/migrate/export/stytch.js.map +0 -1
  360. package/dist/migrate/export/supabase-config.d.ts +0 -48
  361. package/dist/migrate/export/supabase-config.js +0 -408
  362. package/dist/migrate/export/supabase-config.js.map +0 -1
  363. package/dist/migrate/export/supabase.d.ts +0 -86
  364. package/dist/migrate/export/supabase.js +0 -524
  365. package/dist/migrate/export/supabase.js.map +0 -1
  366. package/dist/migrate/export/supertokens-config.d.ts +0 -20
  367. package/dist/migrate/export/supertokens-config.js +0 -150
  368. package/dist/migrate/export/supertokens-config.js.map +0 -1
  369. package/dist/migrate/export/supertokens.d.ts +0 -125
  370. package/dist/migrate/export/supertokens.js +0 -464
  371. package/dist/migrate/export/supertokens.js.map +0 -1
  372. package/dist/migrate/export/table.d.ts +0 -35
  373. package/dist/migrate/export/table.js +0 -248
  374. package/dist/migrate/export/table.js.map +0 -1
  375. package/dist/migrate/export/translate-kit.d.ts +0 -46
  376. package/dist/migrate/export/translate-kit.js +0 -148
  377. package/dist/migrate/export/translate-kit.js.map +0 -1
  378. package/dist/migrate/export/userfront-config.d.ts +0 -15
  379. package/dist/migrate/export/userfront-config.js +0 -49
  380. package/dist/migrate/export/userfront-config.js.map +0 -1
  381. package/dist/migrate/export/userfront.d.ts +0 -38
  382. package/dist/migrate/export/userfront.js +0 -189
  383. package/dist/migrate/export/userfront.js.map +0 -1
  384. package/dist/migrate/export/vendor-http.d.ts +0 -46
  385. package/dist/migrate/export/vendor-http.js +0 -138
  386. package/dist/migrate/export/vendor-http.js.map +0 -1
  387. package/dist/migrate/export/workos-config.d.ts +0 -23
  388. package/dist/migrate/export/workos-config.js +0 -125
  389. package/dist/migrate/export/workos-config.js.map +0 -1
  390. package/dist/migrate/export/workos.d.ts +0 -46
  391. package/dist/migrate/export/workos.js +0 -272
  392. package/dist/migrate/export/workos.js.map +0 -1
  393. package/dist/migrate/export/zip.d.ts +0 -8
  394. package/dist/migrate/export/zip.js +0 -66
  395. package/dist/migrate/export/zip.js.map +0 -1
  396. package/dist/migrate/export/zitadel-config.d.ts +0 -37
  397. package/dist/migrate/export/zitadel-config.js +0 -279
  398. package/dist/migrate/export/zitadel-config.js.map +0 -1
  399. package/dist/migrate/export/zitadel.d.ts +0 -82
  400. package/dist/migrate/export/zitadel.js +0 -447
  401. package/dist/migrate/export/zitadel.js.map +0 -1
  402. package/dist/migrate/firebase/plugin.d.ts +0 -2
  403. package/dist/migrate/firebase/plugin.js +0 -152
  404. package/dist/migrate/firebase/plugin.js.map +0 -1
  405. package/dist/migrate/framework/builtins.d.ts +0 -2
  406. package/dist/migrate/framework/builtins.js +0 -52
  407. package/dist/migrate/framework/builtins.js.map +0 -1
  408. package/dist/migrate/framework/commands.d.ts +0 -20
  409. package/dist/migrate/framework/commands.js +0 -58
  410. package/dist/migrate/framework/commands.js.map +0 -1
  411. package/dist/migrate/framework/custom.d.ts +0 -12
  412. package/dist/migrate/framework/custom.js +0 -96
  413. package/dist/migrate/framework/custom.js.map +0 -1
  414. package/dist/migrate/framework/orchestrator.d.ts +0 -113
  415. package/dist/migrate/framework/orchestrator.js +0 -532
  416. package/dist/migrate/framework/orchestrator.js.map +0 -1
  417. package/dist/migrate/framework/plugin.d.ts +0 -167
  418. package/dist/migrate/framework/plugin.js +0 -3
  419. package/dist/migrate/framework/plugin.js.map +0 -1
  420. package/dist/migrate/framework/registry.d.ts +0 -11
  421. package/dist/migrate/framework/registry.js +0 -44
  422. package/dist/migrate/framework/registry.js.map +0 -1
  423. package/dist/migrate/framework/report.d.ts +0 -17
  424. package/dist/migrate/framework/report.js +0 -103
  425. package/dist/migrate/framework/report.js.map +0 -1
  426. package/dist/migrate/frameworks/plugins.d.ts +0 -29
  427. package/dist/migrate/frameworks/plugins.js +0 -402
  428. package/dist/migrate/frameworks/plugins.js.map +0 -1
  429. package/dist/migrate/frameworks/report.d.ts +0 -92
  430. package/dist/migrate/frameworks/report.js +0 -207
  431. package/dist/migrate/frameworks/report.js.map +0 -1
  432. package/dist/migrate/frameworks/translate-js.d.ts +0 -5
  433. package/dist/migrate/frameworks/translate-js.js +0 -503
  434. package/dist/migrate/frameworks/translate-js.js.map +0 -1
  435. package/dist/migrate/frameworks/translate-rails-django.d.ts +0 -3
  436. package/dist/migrate/frameworks/translate-rails-django.js +0 -1391
  437. package/dist/migrate/frameworks/translate-rails-django.js.map +0 -1
  438. package/dist/migrate/frontegg/plugin.d.ts +0 -2
  439. package/dist/migrate/frontegg/plugin.js +0 -78
  440. package/dist/migrate/frontegg/plugin.js.map +0 -1
  441. package/dist/migrate/fusionauth/plugin.d.ts +0 -2
  442. package/dist/migrate/fusionauth/plugin.js +0 -109
  443. package/dist/migrate/fusionauth/plugin.js.map +0 -1
  444. package/dist/migrate/hanko/plugin.d.ts +0 -2
  445. package/dist/migrate/hanko/plugin.js +0 -100
  446. package/dist/migrate/hanko/plugin.js.map +0 -1
  447. package/dist/migrate/keycloak/plugin.d.ts +0 -2
  448. package/dist/migrate/keycloak/plugin.js +0 -148
  449. package/dist/migrate/keycloak/plugin.js.map +0 -1
  450. package/dist/migrate/kinde/plugin.d.ts +0 -2
  451. package/dist/migrate/kinde/plugin.js +0 -105
  452. package/dist/migrate/kinde/plugin.js.map +0 -1
  453. package/dist/migrate/ldap/plugin.d.ts +0 -2
  454. package/dist/migrate/ldap/plugin.js +0 -73
  455. package/dist/migrate/ldap/plugin.js.map +0 -1
  456. package/dist/migrate/logto/plugin.d.ts +0 -2
  457. package/dist/migrate/logto/plugin.js +0 -131
  458. package/dist/migrate/logto/plugin.js.map +0 -1
  459. package/dist/migrate/magic/plugin.d.ts +0 -2
  460. package/dist/migrate/magic/plugin.js +0 -56
  461. package/dist/migrate/magic/plugin.js.map +0 -1
  462. package/dist/migrate/nhost/plugin.d.ts +0 -2
  463. package/dist/migrate/nhost/plugin.js +0 -115
  464. package/dist/migrate/nhost/plugin.js.map +0 -1
  465. package/dist/migrate/oidc/plugin.d.ts +0 -2
  466. package/dist/migrate/oidc/plugin.js +0 -73
  467. package/dist/migrate/oidc/plugin.js.map +0 -1
  468. package/dist/migrate/okta/plugin.d.ts +0 -2
  469. package/dist/migrate/okta/plugin.js +0 -105
  470. package/dist/migrate/okta/plugin.js.map +0 -1
  471. package/dist/migrate/onelogin/plugin.d.ts +0 -2
  472. package/dist/migrate/onelogin/plugin.js +0 -61
  473. package/dist/migrate/onelogin/plugin.js.map +0 -1
  474. package/dist/migrate/ory/plugin.d.ts +0 -2
  475. package/dist/migrate/ory/plugin.js +0 -186
  476. package/dist/migrate/ory/plugin.js.map +0 -1
  477. package/dist/migrate/ping-identity/plugin.d.ts +0 -2
  478. package/dist/migrate/ping-identity/plugin.js +0 -100
  479. package/dist/migrate/ping-identity/plugin.js.map +0 -1
  480. package/dist/migrate/pocketbase/plugin.d.ts +0 -2
  481. package/dist/migrate/pocketbase/plugin.js +0 -99
  482. package/dist/migrate/pocketbase/plugin.js.map +0 -1
  483. package/dist/migrate/propelauth/plugin.d.ts +0 -2
  484. package/dist/migrate/propelauth/plugin.js +0 -65
  485. package/dist/migrate/propelauth/plugin.js.map +0 -1
  486. package/dist/migrate/saml/plugin.d.ts +0 -2
  487. package/dist/migrate/saml/plugin.js +0 -79
  488. package/dist/migrate/saml/plugin.js.map +0 -1
  489. package/dist/migrate/stytch/plugin.d.ts +0 -2
  490. package/dist/migrate/stytch/plugin.js +0 -97
  491. package/dist/migrate/stytch/plugin.js.map +0 -1
  492. package/dist/migrate/supabase/plugin.d.ts +0 -2
  493. package/dist/migrate/supabase/plugin.js +0 -149
  494. package/dist/migrate/supabase/plugin.js.map +0 -1
  495. package/dist/migrate/supertokens/plugin.d.ts +0 -2
  496. package/dist/migrate/supertokens/plugin.js +0 -114
  497. package/dist/migrate/supertokens/plugin.js.map +0 -1
  498. package/dist/migrate/user-table/plugin.d.ts +0 -2
  499. package/dist/migrate/user-table/plugin.js +0 -62
  500. package/dist/migrate/user-table/plugin.js.map +0 -1
  501. package/dist/migrate/userfront/plugin.d.ts +0 -2
  502. package/dist/migrate/userfront/plugin.js +0 -60
  503. package/dist/migrate/userfront/plugin.js.map +0 -1
  504. package/dist/migrate/workos/plugin.d.ts +0 -2
  505. package/dist/migrate/workos/plugin.js +0 -81
  506. package/dist/migrate/workos/plugin.js.map +0 -1
  507. package/dist/migrate/zitadel/plugin.d.ts +0 -2
  508. package/dist/migrate/zitadel/plugin.js +0 -114
  509. package/dist/migrate/zitadel/plugin.js.map +0 -1
  510. package/dist/output.d.ts +0 -41
  511. package/dist/output.js +0 -72
  512. package/dist/output.js.map +0 -1
  513. package/dist/project.d.ts +0 -64
  514. package/dist/project.js +0 -86
  515. package/dist/project.js.map +0 -1
  516. package/dist/prompt.d.ts +0 -15
  517. package/dist/prompt.js +0 -46
  518. package/dist/prompt.js.map +0 -1
  519. package/dist/run.d.ts +0 -12
  520. package/dist/run.js +0 -157
  521. package/dist/run.js.map +0 -1
  522. package/dist/secrets.d.ts +0 -41
  523. package/dist/secrets.js +0 -142
  524. package/dist/secrets.js.map +0 -1
  525. package/dist/serialize.d.ts +0 -30
  526. package/dist/serialize.js +0 -131
  527. package/dist/serialize.js.map +0 -1
  528. package/dist/version.d.ts +0 -7
  529. package/dist/version.js +0 -21
  530. package/dist/version.js.map +0 -1
@@ -1,1506 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.DEFAULT_CONFIG_FILES = void 0;
37
- exports.config = config;
38
- exports.resolveFormat = resolveFormat;
39
- exports.planConfigImport = planConfigImport;
40
- exports.diffFields = diffFields;
41
- exports.readSecretsEnv = readSecretsEnv;
42
- exports.unresolvedPlaceholders = unresolvedPlaceholders;
43
- exports.configTypes = configTypes;
44
- exports.loadCodeConfig = loadCodeConfig;
45
- exports.planCommand = planCommand;
46
- exports.applyCommand = applyCommand;
47
- exports.printPlan = printPlan;
48
- exports.interpolate = interpolate;
49
- const node_fs_1 = require("node:fs");
50
- const node_os_1 = require("node:os");
51
- const node_path_1 = require("node:path");
52
- const node_url_1 = require("node:url");
53
- const args_1 = require("../args");
54
- const config_http_1 = require("../config-http");
55
- const errors_1 = require("../errors");
56
- const serialize_1 = require("../serialize");
57
- const backend_1 = require("@atlasauth/backend");
58
- const shared_1 = require("./shared");
59
- const secrets_1 = require("../secrets");
60
- // ───────────────────────────── small helpers ─────────────────────────────
61
- /** GET a `{ data: [...] }` list envelope, tolerating an absent/garbled body. */
62
- async function listData(http, path) {
63
- const res = await http.get(path);
64
- return Array.isArray(res?.data) ? res.data : [];
65
- }
66
- /** Build a config object from a row, keeping only the named keys and dropping null/undefined. */
67
- function pick(row, keys) {
68
- const out = {};
69
- for (const k of keys) {
70
- const v = row[k];
71
- if (v === null || v === undefined)
72
- continue;
73
- out[k] = v;
74
- }
75
- return out;
76
- }
77
- /**
78
- * The write body: strip null/undefined. On a PATCH (`keepNull`) an explicit file
79
- * `null` is kept so it clears a nullable field; an absent key stays unmanaged.
80
- */
81
- function compact(config, keepNull = false) {
82
- const out = {};
83
- if (config && typeof config === 'object' && !Array.isArray(config)) {
84
- for (const [k, v] of Object.entries(config)) {
85
- if (v === undefined || (v === null && !keepNull))
86
- continue;
87
- out[k] = v;
88
- }
89
- }
90
- return out;
91
- }
92
- function enc(segment) {
93
- return encodeURIComponent(segment);
94
- }
95
- // ───────────────────────────── the registry ─────────────────────────────
96
- /**
97
- * The single source of truth for what `atlas config` round-trips — the same
98
- * resource set the Terraform provider manages. Each entry maps a resource type
99
- * to its list/create/update/delete endpoints and its redacted projection.
100
- * SECRETS ARE NEVER PART OF A PROJECTION: the management API never returns them
101
- * on a read, so they never reach disk; a credential that must be (re)written is
102
- * supplied at import time via a `${ENV_VAR}` placeholder in the file.
103
- */
104
- function registry() {
105
- const specs = [
106
- // 0. Email provider — first, because email sign-in strategies need one. Secrets
107
- // (api_key, …) are write-only: the live side shows "(secret set)", never a value.
108
- {
109
- kind: 'singleton',
110
- type: 'email_provider',
111
- async fetchLive(http) {
112
- let p;
113
- try {
114
- p = await http.get('/v1/messaging_providers/email');
115
- }
116
- catch (err) {
117
- if ((0, config_http_1.isNotFound)(err))
118
- return null;
119
- throw err;
120
- }
121
- if (!p || p.configured === false)
122
- return null;
123
- const config = { ...obj((p.config ?? null)) };
124
- const set = obj((p.secrets_set ?? null));
125
- const names = Array.isArray(p.secrets_set) ? p.secrets_set : Object.keys(set).filter((k) => set[k] === true);
126
- for (const k of names)
127
- config[k] = SECRET_SET;
128
- return pick({ ...p, config }, ['transport', 'from_address', 'config']);
129
- },
130
- async apply(http, config) {
131
- await http.put('/v1/messaging_providers/email', compact(withoutMarkers(config)));
132
- },
133
- normalize: (c) => redactSecrets(c, ['config']),
134
- },
135
- // 1. Instance config (auth_config + allowed origins) — the §9.3 singleton.
136
- {
137
- kind: 'singleton',
138
- type: 'instance',
139
- async fetchLive(http) {
140
- const inst = await http.get('/v1/instance');
141
- const hints = obj(inst.allowed_origin_integrations ?? null);
142
- const origins = originStrings(inst.allowed_origins ?? []).map((o) => (hints[o] ? { origin: o, integration: hints[o] } : o));
143
- return { allowed_origins: origins, auth_config: stripOriginIntegrations(inst.auth_config ?? {}) };
144
- },
145
- async apply(http, config) {
146
- const c = compact(config);
147
- // Integration hints live in auth_config.originIntegrations server-side and a
148
- // write replaces auth_config, so carry the live hints over for any origin the
149
- // file names as a plain string (the plan ignores hints the file does not set).
150
- const live = await http.get('/v1/instance');
151
- const liveHints = obj(live.allowed_origin_integrations ?? null);
152
- const authConfig = stripOriginIntegrations(c.auth_config ?? {});
153
- const body = { auth_config: authConfig };
154
- if (c.allowed_origins !== undefined) {
155
- // A file without allowed_origins leaves the live origins alone (a partial
156
- // document, e.g. one produced by `atlas migrate better-auth config`).
157
- body.allowed_origins = (Array.isArray(c.allowed_origins) ? c.allowed_origins : []).map((e) => typeof e === 'string' && liveHints[e] ? { origin: e, integration: liveHints[e] } : e);
158
- }
159
- else {
160
- const kept = obj(live.auth_config ?? null).originIntegrations;
161
- if (kept !== undefined)
162
- authConfig.originIntegrations = kept;
163
- }
164
- await http.patch('/v1/instance', body);
165
- },
166
- normalize(c) {
167
- const d = obj(c);
168
- const hints = {};
169
- for (const e of Array.isArray(d.allowed_origins) ? d.allowed_origins : []) {
170
- const o = obj(e);
171
- if (o.integration)
172
- hints[String(o.origin)] = o.integration;
173
- }
174
- const out = {};
175
- if (d.allowed_origins !== undefined)
176
- out.allowed_origins = originStrings(d.allowed_origins);
177
- if (Object.keys(hints).length)
178
- out.allowed_origin_integrations = hints;
179
- if (d.auth_config !== undefined)
180
- out.auth_config = stripOriginIntegrations(d.auth_config);
181
- return out;
182
- },
183
- },
184
- // 2. Org settings schema (P1-5) — a singleton JSON Schema; 404 when unset.
185
- {
186
- kind: 'singleton',
187
- type: 'org_settings_schema',
188
- async fetchLive(http) {
189
- try {
190
- const s = await http.get('/v1/organization_settings_schema');
191
- return { schema: s.schema ?? {} };
192
- }
193
- catch (err) {
194
- if ((0, config_http_1.isNotFound)(err))
195
- return null;
196
- throw err;
197
- }
198
- },
199
- async apply(http, config) {
200
- const c = compact(config);
201
- await http.put('/v1/organization_settings_schema', { schema: c.schema ?? {} });
202
- },
203
- },
204
- // 3. Redirect URL allowlist — key = the (normalised) url; immutable, so no update.
205
- {
206
- kind: 'collection',
207
- type: 'redirect_urls',
208
- keyOf: (c) => String(obj(c).url ?? ''),
209
- async fetchLive(http) {
210
- const rows = await listData(http, '/v1/redirect_urls');
211
- return rows.map((r) => ({ key: r.url, id: r.id, config: pick(r, ['url']) }));
212
- },
213
- async create(http, config) {
214
- await http.post('/v1/redirect_urls', { url: obj(config).url });
215
- },
216
- async remove(http, live) {
217
- await http.delete(`/v1/redirect_urls/${enc(live.id)}`);
218
- },
219
- },
220
- // 4. OAuth clients (first-party / M2M apps) — key = name.
221
- {
222
- kind: 'collection',
223
- type: 'oauth_clients',
224
- keyOf: (c) => String(obj(c).name ?? ''),
225
- async fetchLive(http) {
226
- const rows = await listData(http, '/v1/oauth_clients');
227
- return rows.map((r) => ({
228
- key: String(r.name ?? ''),
229
- id: String(r.id ?? ''),
230
- config: pick(r, OAUTH_CLIENT_KEYS),
231
- }));
232
- },
233
- async create(http, config) {
234
- await http.post('/v1/oauth_clients', compact(config));
235
- },
236
- async update(http, live, config) {
237
- await http.patch(`/v1/oauth_clients/${enc(live.id)}`, compact(config, true));
238
- },
239
- async remove(http, live) {
240
- await http.delete(`/v1/oauth_clients/${enc(live.id)}`);
241
- },
242
- },
243
- // 4b. Hooks — after the clients. before_user_create: url, timeout_ms, fail_closed,
244
- // enabled; the signing secret is write-only ("(secret set)").
245
- {
246
- kind: 'singleton',
247
- type: 'hooks',
248
- async fetchLive(http) {
249
- let h;
250
- try {
251
- h = await http.get('/v1/instance/hooks/before_user_create');
252
- }
253
- catch (err) {
254
- if ((0, config_http_1.isNotFound)(err))
255
- return null;
256
- throw err;
257
- }
258
- if (!h || (h.url == null && h.enabled !== true))
259
- return null;
260
- const bu = pick(h, ['url', 'timeout_ms', 'fail_closed', 'enabled']);
261
- if (h.signing_secret_set === true)
262
- bu.signing_secret = SECRET_SET;
263
- return { before_user_create: bu };
264
- },
265
- async apply(http, config, opts) {
266
- const bu = compact(obj(withoutMarkers(config)).before_user_create ?? {}, true);
267
- if (Object.keys(bu).length === 0)
268
- return;
269
- try {
270
- await http.patch(`/v1/instance/hooks/before_user_create${opts.forceHook ? '?force=true' : ''}`, bu);
271
- }
272
- catch (err) {
273
- const refused = err instanceof backend_1.AtlasApiError && err.status === 409 ? err.errors.find((e) => e.code === 'HOOK_TEST_FAILED') : undefined;
274
- if (refused) {
275
- throw new errors_1.CliError(`hooks.before_user_create: the change was refused because the hook test call failed (HOOK_TEST_FAILED): ${refused.message} ` +
276
- 'The hook was left as it was. Deploy the route, or pass --force-hook to apply it anyway.');
277
- }
278
- throw err;
279
- }
280
- },
281
- normalize: (c) => redactSecrets(c, ['before_user_create']),
282
- },
283
- // 5. Social / enterprise OAuth providers — key = the catalog provider slug.
284
- {
285
- kind: 'collection',
286
- type: 'oauth_providers',
287
- keyOf: (c) => String(obj(c).provider ?? ''),
288
- async fetchLive(http) {
289
- const rows = await listData(http, '/v1/oauth_providers');
290
- return rows
291
- .filter((r) => r.configured === true)
292
- .map((r) => ({
293
- key: String(r.provider ?? ''),
294
- id: String(r.provider ?? ''),
295
- config: pick(r, OAUTH_PROVIDER_KEYS),
296
- }));
297
- },
298
- create: putProvider,
299
- update: (http, _live, config) => putProvider(http, config),
300
- async remove(http, live) {
301
- await http.delete(`/v1/oauth_providers/${enc(live.id)}`);
302
- },
303
- },
304
- // 5b. Custom (generic) OAuth 2.0 / OIDC login providers — key = provider_key.
305
- {
306
- kind: 'collection',
307
- type: 'custom_oauth_providers',
308
- keyOf: (c) => String(obj(c).provider_key ?? ''),
309
- async fetchLive(http) {
310
- const rows = await listData(http, '/v1/custom_oauth_providers');
311
- return rows.map((r) => ({
312
- key: String(r.provider_key ?? ''),
313
- id: String(r.id ?? ''),
314
- config: pick(r, CUSTOM_PROVIDER_KEYS),
315
- }));
316
- },
317
- async create(http, config) {
318
- await http.post('/v1/custom_oauth_providers', compact(config));
319
- },
320
- async update(http, live, config) {
321
- const body = compact(config, true);
322
- delete body.provider_key; // immutable
323
- await http.patch(`/v1/custom_oauth_providers/${enc(live.id)}`, body);
324
- },
325
- async remove(http, live) {
326
- await http.delete(`/v1/custom_oauth_providers/${enc(live.id)}`);
327
- },
328
- },
329
- // 6. Resource servers (API audiences) — key = identifier.
330
- {
331
- kind: 'collection',
332
- type: 'resource_servers',
333
- keyOf: (c) => String(obj(c).identifier ?? ''),
334
- async fetchLive(http) {
335
- const rows = await listData(http, '/v1/resource_servers');
336
- return rows.map((r) => ({
337
- key: String(r.identifier ?? ''),
338
- id: String(r.id ?? ''),
339
- config: pick(r, RESOURCE_SERVER_KEYS),
340
- }));
341
- },
342
- async create(http, config) {
343
- const body = compact(config);
344
- delete body.signing_alg; // read-only (reported, not settable)
345
- await http.post('/v1/resource_servers', body);
346
- },
347
- async update(http, live, config) {
348
- const body = compact(config, true);
349
- delete body.identifier; // immutable
350
- delete body.signing_alg; // read-only
351
- await http.patch(`/v1/resource_servers/${enc(live.id)}`, body);
352
- },
353
- async remove(http, live) {
354
- await http.delete(`/v1/resource_servers/${enc(live.id)}`);
355
- },
356
- },
357
- // 7. JWT templates — key = name; claims is a flat template map.
358
- {
359
- kind: 'collection',
360
- type: 'jwt_templates',
361
- keyOf: (c) => String(obj(c).name ?? ''),
362
- async fetchLive(http) {
363
- const rows = await listData(http, '/v1/jwt_templates');
364
- return rows.map((r) => ({
365
- key: r.name,
366
- id: r.name,
367
- config: { name: r.name, claims: r.claims ?? {} },
368
- }));
369
- },
370
- async create(http, config) {
371
- const c = obj(config);
372
- await http.post('/v1/jwt_templates', { name: c.name, claims: c.claims ?? {} });
373
- },
374
- async update(http, live, config) {
375
- await http.patch(`/v1/jwt_templates/${enc(live.id)}`, { claims: obj(config).claims ?? {} });
376
- },
377
- async remove(http, live) {
378
- await http.delete(`/v1/jwt_templates/${enc(live.id)}`);
379
- },
380
- },
381
- // 7b. Custom permissions — key = key. Before roles, which grant them. System ones skipped.
382
- {
383
- kind: 'collection',
384
- type: 'permissions',
385
- keyOf: (c) => String(obj(c).key ?? ''),
386
- async fetchLive(http) {
387
- const rows = await listData(http, '/v1/permissions');
388
- return rows
389
- .filter((r) => r.is_system !== true)
390
- .map((r) => ({ key: String(r.key ?? ''), id: String(r.id ?? ''), config: pick(r, PERMISSION_KEYS) }));
391
- },
392
- async create(http, config) {
393
- await http.post('/v1/permissions', compact(config));
394
- },
395
- async update(http, live, config) {
396
- const c = obj(config);
397
- await http.patch(`/v1/permissions/${enc(live.id)}`, { name: c.name, description: c.description ?? null });
398
- },
399
- async remove(http, live) {
400
- await http.delete(`/v1/permissions/${enc(live.id)}`);
401
- },
402
- },
403
- // 8. Roles — key = key; label via PATCH, permission set via PUT. System roles skipped.
404
- {
405
- kind: 'collection',
406
- type: 'roles',
407
- keyOf: (c) => String(obj(c).key ?? ''),
408
- async fetchLive(http) {
409
- const rows = await listData(http, '/v1/roles');
410
- return rows
411
- .filter((r) => r.is_system !== true)
412
- .map((r) => ({
413
- key: String(r.key ?? ''),
414
- id: String(r.id ?? ''),
415
- config: pick(r, ROLE_KEYS),
416
- }));
417
- },
418
- async create(http, config) {
419
- await http.post('/v1/roles', compact(config));
420
- },
421
- async update(http, live, config) {
422
- const c = obj(config);
423
- await http.patch(`/v1/roles/${enc(live.id)}`, {
424
- name: c.name,
425
- description: c.description ?? null,
426
- });
427
- await http.put(`/v1/roles/${enc(live.id)}/permissions`, {
428
- permissions: Array.isArray(c.permissions) ? c.permissions : [],
429
- });
430
- },
431
- async remove(http, live) {
432
- await http.delete(`/v1/roles/${enc(live.id)}`);
433
- },
434
- },
435
- // 9. Webhook endpoints — key = url.
436
- {
437
- kind: 'collection',
438
- type: 'webhook_endpoints',
439
- keyOf: (c) => String(obj(c).url ?? ''),
440
- async fetchLive(http) {
441
- const rows = await listData(http, '/v1/webhook_endpoints');
442
- return rows.map((r) => ({
443
- key: String(r.url ?? ''),
444
- id: String(r.id ?? ''),
445
- config: pick(r, WEBHOOK_KEYS),
446
- ...(typeof r.managed_by === 'string' && r.managed_by ? { managedBy: r.managed_by } : {}),
447
- }));
448
- },
449
- async create(http, config) {
450
- const c = obj(config);
451
- await http.post('/v1/webhook_endpoints', {
452
- url: c.url,
453
- enabled_events: c.enabled_events ?? [],
454
- // Sent on create so a paused endpoint is never live, even briefly.
455
- ...(typeof c.active === 'boolean' ? { active: c.active } : {}),
456
- });
457
- },
458
- async update(http, live, config) {
459
- await http.patch(`/v1/webhook_endpoints/${enc(live.id)}`, compact(config, true));
460
- },
461
- async remove(http, live) {
462
- await http.delete(`/v1/webhook_endpoints/${enc(live.id)}`);
463
- },
464
- },
465
- // 10. SSO connections — key = a stable identifier derived from the connection.
466
- {
467
- kind: 'collection',
468
- type: 'sso_connections',
469
- keyOf: ssoKey,
470
- async fetchLive(http) {
471
- const rows = await listData(http, '/v1/sso_connections');
472
- return rows.map((r) => {
473
- const config = pick(r, SSO_KEYS);
474
- return { key: ssoKey(config), id: String(r.id ?? ''), config };
475
- });
476
- },
477
- async create(http, config) {
478
- await http.post('/v1/sso_connections', compact(config));
479
- },
480
- async update(http, live, config) {
481
- const body = compact(config, true);
482
- delete body.type; // fixed at creation; PATCH does not take it
483
- await http.patch(`/v1/sso_connections/${enc(live.id)}`, body);
484
- },
485
- async remove(http, live) {
486
- await http.delete(`/v1/sso_connections/${enc(live.id)}`);
487
- },
488
- },
489
- // 10b. SSO onboarding profiles (hosted setup links) — key = name. After the SSO
490
- // connections; a profile may name an organization that already exists.
491
- {
492
- kind: 'collection',
493
- type: 'sso_onboarding_profiles',
494
- keyOf: (c) => String(obj(c).name ?? ''),
495
- async fetchLive(http) {
496
- let rows;
497
- try {
498
- rows = await listData(http, '/v1/sso_onboarding_profiles');
499
- }
500
- catch (err) {
501
- if ((0, config_http_1.isNotFound)(err))
502
- return [];
503
- throw err;
504
- }
505
- return rows.map((r) => {
506
- const config = pick(r, SSO_PROFILE_KEYS);
507
- return { key: String(r.name ?? ''), id: String(r.id ?? ''), config };
508
- });
509
- },
510
- async create(http, config) {
511
- await http.post('/v1/sso_onboarding_profiles', compact(config));
512
- },
513
- async update(http, live, config) {
514
- await http.patch(`/v1/sso_onboarding_profiles/${enc(live.id)}`, compact(config, true));
515
- },
516
- async remove(http, live) {
517
- await http.delete(`/v1/sso_onboarding_profiles/${enc(live.id)}`);
518
- },
519
- },
520
- // 11. Billing plans — key = slug.
521
- {
522
- kind: 'collection',
523
- type: 'billing_plans',
524
- keyOf: (c) => String(obj(c).slug ?? ''),
525
- async fetchLive(http) {
526
- const rows = await listData(http, '/v1/billing/plans');
527
- return rows.map((r) => ({
528
- key: String(r.slug ?? ''),
529
- id: String(r.id ?? ''),
530
- config: pick(r, BILLING_KEYS),
531
- }));
532
- },
533
- async create(http, config) {
534
- await http.post('/v1/billing/plans', compact(config));
535
- },
536
- async update(http, live, config) {
537
- const body = compact(config, true);
538
- delete body.slug; // immutable
539
- await http.patch(`/v1/billing/plans/${enc(live.id)}`, body);
540
- },
541
- async remove(http, live) {
542
- await http.delete(`/v1/billing/plans/${enc(live.id)}`);
543
- },
544
- },
545
- // 12. Notification templates (P2-12) — key = name; replace via PUT.
546
- {
547
- kind: 'collection',
548
- type: 'notification_templates',
549
- keyOf: (c) => String(obj(c).name ?? ''),
550
- async fetchLive(http) {
551
- const rows = await listData(http, '/v1/notification_templates');
552
- return rows.map((r) => ({
553
- key: String(r.name ?? ''),
554
- id: String(r.name ?? ''),
555
- config: pick(r, NOTIFICATION_KEYS),
556
- }));
557
- },
558
- async create(http, config) {
559
- await http.post('/v1/notification_templates', compact(config));
560
- },
561
- async update(http, live, config) {
562
- const body = compact(config, true);
563
- delete body.name; // the name is the URL key
564
- await http.put(`/v1/notification_templates/${enc(live.id)}`, body);
565
- },
566
- async remove(http, live) {
567
- await http.delete(`/v1/notification_templates/${enc(live.id)}`);
568
- },
569
- },
570
- // 13. Email templates (§11.1) — key = name; only the CUSTOMISED overrides are config.
571
- {
572
- kind: 'collection',
573
- type: 'email_templates',
574
- keyOf: (c) => String(obj(c).name ?? ''),
575
- async fetchLive(http) {
576
- const rows = await listData(http, '/v1/email_templates');
577
- return rows
578
- .filter((r) => r.customised && r.override)
579
- .map((r) => ({
580
- key: r.name,
581
- id: r.name,
582
- config: pick({ name: r.name, ...(r.override ?? {}) }, EMAIL_KEYS),
583
- }));
584
- },
585
- async create(http, config) {
586
- await putEmail(http, config);
587
- },
588
- async update(http, _live, config) {
589
- await putEmail(http, config);
590
- },
591
- async remove(http, live) {
592
- await http.delete(`/v1/email_templates/${enc(live.id)}`);
593
- },
594
- },
595
- // 14. Organization policies — key = organization_id; attaches to existing orgs.
596
- {
597
- kind: 'collection',
598
- type: 'organization_policies',
599
- keyOf: (c) => String(obj(c).organization_id ?? ''),
600
- async fetchLive(http) {
601
- const orgs = await listData(http, '/v1/organizations');
602
- const out = [];
603
- for (const org of orgs) {
604
- // There is no GET for org policy; an empty PATCH returns it unchanged.
605
- const res = await http.patch(`/v1/organizations/${enc(org.id)}/policy`, {});
606
- const policy = res?.policy ?? {};
607
- const config = pick({ organization_id: org.id, ...policy }, ORG_POLICY_KEYS);
608
- // Only orgs with a real (non-default) policy are config.
609
- if (Object.keys(obj(config)).length > 1) {
610
- out.push({ key: org.id, id: org.id, config });
611
- }
612
- }
613
- return out;
614
- },
615
- async create(http, config) {
616
- await patchOrgPolicy(http, config);
617
- },
618
- async update(http, _live, config) {
619
- await patchOrgPolicy(http, config);
620
- },
621
- // No delete: pruning a policy would silently loosen an org's security, so
622
- // org policies are never deleted by import (reported as skipped).
623
- },
624
- // 12. Branding — the hosted-page appearance; a partial merge on write (PATCH).
625
- {
626
- kind: 'singleton',
627
- type: 'branding',
628
- async fetchLive(http) {
629
- let b;
630
- try {
631
- b = await http.get('/v1/branding');
632
- }
633
- catch (err) {
634
- if ((0, config_http_1.isNotFound)(err))
635
- return null;
636
- throw err;
637
- }
638
- const { object: _o, resolved: _r, ...fields } = b ?? {};
639
- const out = compact(fields);
640
- return Object.keys(out).length ? out : null;
641
- },
642
- async apply(http, config) {
643
- await http.patch('/v1/branding', compact(config, true));
644
- },
645
- },
646
- // 13. Cookie-consent banner — last: it only needs the instance. PUT replaces the
647
- // whole setting and needs script_url, so apply merges the file over the live one.
648
- {
649
- kind: 'singleton',
650
- type: 'cookie_consent',
651
- async fetchLive(http) {
652
- let cc;
653
- try {
654
- cc = await http.get('/v1/instance/cookie_consent');
655
- }
656
- catch (err) {
657
- if ((0, config_http_1.isNotFound)(err))
658
- return null;
659
- throw err;
660
- }
661
- const out = pick(cc ?? {}, COOKIE_CONSENT_KEYS);
662
- return Object.keys(out).length ? out : null;
663
- },
664
- async apply(http, config) {
665
- const live = await http.get('/v1/instance/cookie_consent');
666
- const body = { ...pick(live ?? {}, COOKIE_CONSENT_KEYS), ...compact(config, true) };
667
- if (body.script_url === undefined || body.script_url === null) {
668
- throw new errors_1.CliError('cookie_consent needs a script_url (the consent manager script) when none is set on the instance.');
669
- }
670
- await http.put('/v1/instance/cookie_consent', body);
671
- },
672
- },
673
- // 14. "Sign in with Atlas" button on the hosted pages. The file says whether it
674
- // is on; `connection_id` (optional) links an SSO connection the instance
675
- // already has instead of the preset one. Only `enabled` is exported or
676
- // compared: the connection id is this instance's own.
677
- {
678
- kind: 'singleton',
679
- type: 'sign_in_with_atlas',
680
- async fetchLive(http) {
681
- let live;
682
- try {
683
- live = await http.get('/v1/instance/sign_in_with_atlas');
684
- }
685
- catch (err) {
686
- if ((0, config_http_1.isNotFound)(err))
687
- return null;
688
- throw err;
689
- }
690
- return { enabled: live?.enabled === true };
691
- },
692
- normalize: (c) => ({ enabled: obj(c).enabled === true }),
693
- async apply(http, config) {
694
- const c = obj(config);
695
- if (typeof c.enabled !== 'boolean')
696
- throw new errors_1.CliError('sign_in_with_atlas needs enabled: true or false.');
697
- await http.patch('/v1/instance/sign_in_with_atlas', {
698
- enabled: c.enabled,
699
- ...(c.connection_id !== undefined ? { connection_id: c.connection_id } : {}),
700
- });
701
- },
702
- },
703
- ];
704
- return specs;
705
- }
706
- const OAUTH_CLIENT_KEYS = [
707
- 'name',
708
- 'redirect_uris',
709
- 'allowed_scopes',
710
- 'grant_types',
711
- 'token_endpoint_auth_method',
712
- 'logo_url',
713
- 'first_party',
714
- 'post_logout_redirect_uris',
715
- 'claims_redirect_uris',
716
- 'backchannel_logout_uri',
717
- 'backchannel_logout_session_required',
718
- 'frontchannel_logout_uri',
719
- 'frontchannel_logout_session_required',
720
- 'skip_logout_confirmation',
721
- 'refresh_touches_session',
722
- 'require_verified_email',
723
- 'require_pkce',
724
- 'login_url',
725
- 'consent_url',
726
- 'subject_type',
727
- 'sector_identifier_uri',
728
- 'access_token_format',
729
- 'token_lifetimes',
730
- ];
731
- const OAUTH_PROVIDER_KEYS = [
732
- 'provider',
733
- 'enabled',
734
- 'allow_sign_in',
735
- 'allow_sign_up',
736
- 'scopes',
737
- 'client_id',
738
- ];
739
- const RESOURCE_SERVER_KEYS = [
740
- 'identifier',
741
- 'name',
742
- 'scopes',
743
- 'token_ttl_seconds',
744
- 'signing_alg',
745
- ];
746
- const ROLE_KEYS = ['key', 'name', 'description', 'permissions'];
747
- const PERMISSION_KEYS = ['key', 'name', 'description'];
748
- const CUSTOM_PROVIDER_KEYS = [
749
- 'provider_key',
750
- 'display_name',
751
- 'icon_url',
752
- 'protocol',
753
- 'issuer',
754
- 'discovery_url',
755
- 'authorization_endpoint',
756
- 'token_endpoint',
757
- 'userinfo_endpoint',
758
- 'jwks_uri',
759
- 'scopes',
760
- 'pkce',
761
- 'authorization_params',
762
- 'token_params',
763
- 'token_endpoint_auth_method',
764
- 'profile_source',
765
- 'profile_mapping',
766
- 'client_id',
767
- 'enabled',
768
- 'allow_sign_in',
769
- 'allow_sign_up',
770
- ];
771
- const WEBHOOK_KEYS = ['url', 'enabled_events', 'active'];
772
- const SSO_KEYS = [
773
- 'type',
774
- 'status',
775
- 'organization_id',
776
- 'oidc_issuer',
777
- 'oidc_client_id',
778
- 'saml_idp_entity_id',
779
- 'saml_idp_sso_url',
780
- 'saml_sp_entity_id',
781
- 'saml_allow_idp_initiated',
782
- 'saml_sign_authn_requests',
783
- 'saml_want_response_signed',
784
- 'discourse_provider_url',
785
- 'allowed_domains',
786
- 'claim_role_mappings',
787
- 'default_role_id',
788
- ];
789
- const SSO_PROFILE_KEYS = ['name', 'allowed_connection_types', 'organization_id', 'company_name', 'allow_scim', 'intents', 'return_url'];
790
- const COOKIE_CONSENT_KEYS = ['enabled', 'script_url', 'script_id', 'site_id', 'blocking_mode', 'extra_origins', 'settings_link', 'culture', 'host_aliases'];
791
- const BILLING_KEYS = [
792
- 'name',
793
- 'slug',
794
- 'stripe_price_id',
795
- 'interval',
796
- 'amount',
797
- 'currency',
798
- 'features',
799
- 'limits',
800
- 'audience',
801
- 'active',
802
- 'pricing_model',
803
- 'trial_days',
804
- 'stripe_meter_id',
805
- 'usage_unit',
806
- 'provider',
807
- 'annual_price_id',
808
- 'kind',
809
- ];
810
- const NOTIFICATION_KEYS = ['name', 'subject', 'body', 'category'];
811
- const EMAIL_KEYS = ['name', 'subject', 'text'];
812
- const ORG_POLICY_KEYS = [
813
- 'organization_id',
814
- 'requireMfa',
815
- 'ssoRequired',
816
- 'sessionIdleOverrideMs',
817
- 'allowedSignInMethods',
818
- 'ipAllowlist',
819
- 'maxSessionAgeSeconds',
820
- ];
821
- /** Narrow a Config to a plain string-keyed record for field access. */
822
- function obj(config) {
823
- return config && typeof config === 'object' && !Array.isArray(config)
824
- ? config
825
- : {};
826
- }
827
- /** What a write-only secret looks like on the live side, and in a plan. */
828
- const SECRET_SET = '(secret set)';
829
- const SECRET_KEY = /(secret|token|password|passwd|api_?key|private_?key)/i;
830
- /**
831
- * Show each secret value under `groups` as "(secret set)" so a plan never echoes it
832
- * and compares equal to the live "(secret set)". An unresolved \${VAR} stays visible
833
- * (apply refuses it), as does an empty value.
834
- */
835
- function redactSecrets(config, groups) {
836
- const out = { ...obj(config) };
837
- const mask = (g) => Object.fromEntries(Object.entries(g).map(([k, v]) => [k, SECRET_KEY.test(k) && typeof v === 'string' && v !== '' && !/^\$\{[A-Z0-9_]+\}$/.test(v) ? SECRET_SET : v]));
838
- for (const g of groups)
839
- if (out[g] && typeof out[g] === 'object' && !Array.isArray(out[g]))
840
- out[g] = mask(obj(out[g]));
841
- return out;
842
- }
843
- /** A document without any "(secret set)" marker: what an export writes and what apply sends. */
844
- function withoutMarkers(value) {
845
- if (Array.isArray(value))
846
- return value.map(withoutMarkers);
847
- if (value && typeof value === 'object') {
848
- return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== SECRET_SET).map(([k, v]) => [k, withoutMarkers(v)]));
849
- }
850
- return value;
851
- }
852
- /** An origin list in either form (strings, or { origin, integration }) as plain strings. */
853
- function originStrings(list) {
854
- return (Array.isArray(list) ? list : []).map((e) => (typeof e === 'string' ? e : String(obj(e).origin ?? '')));
855
- }
856
- /** auth_config without the integration hints, which belong to allowed_origins. */
857
- function stripOriginIntegrations(authConfig) {
858
- const { originIntegrations: _hints, ...rest } = obj(authConfig);
859
- return rest;
860
- }
861
- /** A stable natural key for an SSO connection, which has no name field. */
862
- function ssoKey(config) {
863
- const c = obj(config);
864
- const type = String(c.type ?? 'sso');
865
- const id = c.oidc_issuer ?? c.saml_idp_entity_id ?? c.saml_sp_entity_id ?? c.discourse_provider_url ?? '';
866
- return `${type}:${String(id)}`;
867
- }
868
- /** Configure an OAuth provider: credentials/scopes (PUT), then scope + enabled toggles. */
869
- async function putProvider(http, config) {
870
- const c = obj(config);
871
- // Provider settings (a tenant, a domain, Apple's team/key ids) ride along in `values`.
872
- const values = { ...obj(c.values ?? null) };
873
- if (c.client_id !== undefined && c.client_id !== null)
874
- values.client_id = c.client_id;
875
- // A secret only exists here when the file carried a ${ENV} placeholder for it.
876
- if (c.client_secret !== undefined && c.client_secret !== null) {
877
- values.client_secret = c.client_secret;
878
- }
879
- const body = {};
880
- if (Object.keys(values).length)
881
- body.values = values;
882
- if (Array.isArray(c.scopes))
883
- body.scopes = c.scopes;
884
- // Credential-less providers (siwe, siws, siwb, siwf) take verifier settings instead.
885
- if (c.settings && typeof c.settings === 'object' && !Array.isArray(c.settings))
886
- body.settings = c.settings;
887
- const provider = String(c.provider ?? '');
888
- if (Object.keys(body).length) {
889
- await http.put(`/v1/oauth_providers/${enc(provider)}`, body);
890
- }
891
- if (c.allow_sign_in !== undefined || c.allow_sign_up !== undefined) {
892
- await http.post(`/v1/oauth_providers/${enc(provider)}/scope`, {
893
- allow_sign_in: c.allow_sign_in ?? true,
894
- allow_sign_up: c.allow_sign_up ?? true,
895
- });
896
- }
897
- if (c.enabled !== undefined) {
898
- await http.post(`/v1/oauth_providers/${enc(provider)}/enabled`, { enabled: c.enabled });
899
- }
900
- }
901
- async function putEmail(http, config) {
902
- const c = obj(config);
903
- const body = {};
904
- if (c.subject !== undefined)
905
- body.subject = c.subject;
906
- if (c.text !== undefined)
907
- body.text = c.text;
908
- await http.put(`/v1/email_templates/${enc(String(c.name ?? ''))}`, body);
909
- }
910
- async function patchOrgPolicy(http, config) {
911
- const c = obj(config);
912
- const body = {};
913
- for (const k of ORG_POLICY_KEYS) {
914
- if (k === 'organization_id')
915
- continue;
916
- if (c[k] !== undefined && c[k] !== null)
917
- body[k] = c[k];
918
- }
919
- await http.patch(`/v1/organizations/${enc(String(c.organization_id ?? ''))}/policy`, body);
920
- }
921
- // ───────────────────────────── the command ─────────────────────────────
922
- /** `atlas config export|import` — Auth0 `deploy-cli`-style config-as-code. */
923
- async function config(ctx, args) {
924
- const sub = args.positionals[1];
925
- switch (sub) {
926
- case 'export':
927
- return exportConfig(ctx, args.values);
928
- case 'import':
929
- return importConfig(ctx, args.values);
930
- case 'plan':
931
- return planConfig(ctx, args.values, { dir: resolveDir(args.values), format: resolveFormat(args.values), env: readSecretsEnv(ctx, args.values) });
932
- case 'promote':
933
- return promoteConfig(ctx, args.values);
934
- default:
935
- throw new errors_1.CliError(sub
936
- ? `Unknown subcommand 'config ${sub}'. Try: export, import, plan, promote.`
937
- : 'config needs a subcommand: export, import, plan, promote.');
938
- }
939
- }
940
- function resolveFormat(values) {
941
- const raw = (0, args_1.str)(values, 'format');
942
- if (raw === undefined)
943
- return 'json';
944
- if (raw !== 'json' && raw !== 'yaml') {
945
- throw new errors_1.CliError(`--format must be 'json' or 'yaml', got '${raw}'.`);
946
- }
947
- return raw;
948
- }
949
- function resolveDir(values) {
950
- return (0, args_1.str)(values, 'dir') || './atlas-config';
951
- }
952
- /** Build the config HTTP client from the resolved profile credential. */
953
- function requireHttp(ctx, values) {
954
- const auth = (0, shared_1.resolveAuth)(ctx, values);
955
- const make = ctx.makeHttp ?? config_http_1.createConfigHttp;
956
- return make({ secretKey: auth.secretKey, apiUrl: auth.apiUrl });
957
- }
958
- /** Write every resource type's live config to `dir` (deterministic, secrets omitted); returns the file count. */
959
- async function writeExport(http, dir, format) {
960
- const ext = format === 'json' ? 'json' : 'yaml';
961
- (0, node_fs_1.mkdirSync)(dir, { recursive: true });
962
- let fileCount = 0;
963
- for (const spec of registry()) {
964
- if (spec.kind === 'singleton') {
965
- const live = await spec.fetchLive(http);
966
- if (live === null)
967
- continue; // nothing to write (e.g. no schema registered)
968
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, `${spec.type}.${ext}`), (0, serialize_1.serialize)(withoutMarkers(live), format));
969
- fileCount += 1;
970
- }
971
- else {
972
- const entries = (await spec.fetchLive(http)).filter((e) => !e.managedBy);
973
- // Deterministic: entries sorted by natural key, keys sorted inside each.
974
- const sorted = [...entries].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
975
- const doc = sorted.map((e) => (0, serialize_1.canonicalize)(e.config));
976
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, `${spec.type}.${ext}`), (0, serialize_1.serialize)(doc, format));
977
- fileCount += 1;
978
- }
979
- }
980
- return fileCount;
981
- }
982
- async function exportConfig(ctx, values) {
983
- const http = requireHttp(ctx, values);
984
- const dir = resolveDir(values);
985
- const format = resolveFormat(values);
986
- const fileCount = await writeExport(http, dir, format);
987
- ctx.io.out(`Exported ${fileCount} resource file(s) to ${dir} (${format}).`);
988
- ctx.io.out('Secrets are never exported — supply any credential at import via a ${ENV} placeholder.');
989
- return 0;
990
- }
991
- /**
992
- * Diff a config directory against the live instance. Nothing is written: the
993
- * returned changes carry their own `apply`. Used by `atlas config import` and by
994
- * `atlas migrate better-auth` (which shows the plan before applying it).
995
- */
996
- async function planConfigImport(http, opts) {
997
- const { dir, format, env, prune } = opts;
998
- const applyOpts = { forceHook: opts.forceHook === true };
999
- const ext = format === 'json' ? 'json' : 'yaml';
1000
- if (!(0, node_fs_1.existsSync)(dir)) {
1001
- throw new errors_1.CliError(`Config directory not found: ${dir}. Run 'atlas config export' first.`);
1002
- }
1003
- const present = new Set((0, node_fs_1.readdirSync)(dir));
1004
- const changes = [];
1005
- let unchanged = 0;
1006
- const skippedDeletes = [];
1007
- const managed = [];
1008
- for (const spec of registry()) {
1009
- const file = `${spec.type}.${ext}`;
1010
- // A type with no file is simply NOT MANAGED — leave live untouched.
1011
- if (!present.has(file))
1012
- continue;
1013
- const text = (0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, file), 'utf8');
1014
- const parsed = interpolate((0, serialize_1.deserialize)(text, format), env);
1015
- if (spec.kind === 'singleton') {
1016
- const desired = parsed;
1017
- const liveRaw = await spec.fetchLive(http);
1018
- const norm = spec.normalize ?? ((c) => c);
1019
- const desiredN = norm(desired);
1020
- // A section that only clears fields (all null) against nothing live is already satisfied.
1021
- if (liveRaw === null && onlyNulls(desiredN)) {
1022
- unchanged += 1;
1023
- continue;
1024
- }
1025
- const live = liveRaw === null ? null : comparable(norm(liveRaw), desiredN, prune);
1026
- if (live !== null && (0, serialize_1.deepEqual)(desiredN, live)) {
1027
- unchanged += 1;
1028
- continue;
1029
- }
1030
- changes.push({ type: spec.type, action: live === null ? 'create' : 'update', key: '', ...(live === null ? {} : { before: live }), after: desiredN, apply: () => spec.apply(http, desired, applyOpts) });
1031
- continue;
1032
- }
1033
- // Collection: diff desired-by-key against live-by-key.
1034
- const desiredList = asArray(parsed);
1035
- const desiredByKey = new Map();
1036
- for (const cfg of desiredList)
1037
- desiredByKey.set(spec.keyOf(cfg), cfg);
1038
- const all = await spec.fetchLive(http);
1039
- for (const e of all)
1040
- if (e.managedBy)
1041
- managed.push({ type: spec.type, key: e.key, managedBy: e.managedBy });
1042
- const live = all.filter((e) => !e.managedBy);
1043
- const liveByKey = new Map(live.map((e) => [e.key, e]));
1044
- for (const [key, cfg] of desiredByKey) {
1045
- const existing = liveByKey.get(key);
1046
- if (!existing) {
1047
- changes.push({ type: spec.type, action: 'create', key, after: cfg, apply: () => spec.create(http, cfg) });
1048
- }
1049
- else if (!(0, serialize_1.deepEqual)(cfg, comparable(existing.config, cfg, prune))) {
1050
- if (!spec.update) {
1051
- throw new errors_1.CliError(`${spec.type} '${key}' changed but is immutable; recreate it instead.`);
1052
- }
1053
- const upd = spec.update;
1054
- changes.push({ type: spec.type, action: 'update', key, before: comparable(existing.config, cfg, prune), after: cfg, apply: () => upd(http, existing, cfg) });
1055
- }
1056
- else {
1057
- unchanged += 1;
1058
- }
1059
- }
1060
- for (const entry of live) {
1061
- if (desiredByKey.has(entry.key))
1062
- continue;
1063
- const change = { type: spec.type, action: 'delete', key: entry.key, before: entry.config };
1064
- if (!spec.remove) {
1065
- skippedDeletes.push(change);
1066
- continue;
1067
- }
1068
- if (prune) {
1069
- const rm = spec.remove;
1070
- changes.push({ ...change, apply: () => rm(http, entry) });
1071
- }
1072
- else {
1073
- skippedDeletes.push(change);
1074
- }
1075
- }
1076
- }
1077
- return { changes, unchanged, skippedDeletes, managed };
1078
- }
1079
- /** True for a non-empty object whose every leaf is `null` (a file that only clears fields). */
1080
- function onlyNulls(x) {
1081
- if (x === null)
1082
- return true;
1083
- if (typeof x !== 'object' || Array.isArray(x) || x === undefined)
1084
- return false;
1085
- const vals = Object.values(x);
1086
- return vals.length > 0 && vals.every(onlyNulls);
1087
- }
1088
- /**
1089
- * The live document as the plan compares it. With `--prune` the whole live
1090
- * document (keys the file leaves unset show as drift). Without it only the keys
1091
- * the file sets, recursively, so a freshly applied file plans clean. A desired
1092
- * `null` means "unset", so it matches a live key that is absent.
1093
- */
1094
- function comparable(live, desired, prune) {
1095
- if (prune)
1096
- return live;
1097
- const isObj = (x) => x !== null && typeof x === 'object' && !Array.isArray(x);
1098
- if (!isObj(live) || !isObj(desired))
1099
- return live;
1100
- const out = {};
1101
- for (const k of Object.keys(desired)) {
1102
- const l = live[k];
1103
- if (l === undefined || l === null) {
1104
- if (desired[k] === null)
1105
- out[k] = null;
1106
- continue;
1107
- }
1108
- out[k] = comparable(l, desired[k], false);
1109
- }
1110
- return out;
1111
- }
1112
- /** The stored credential of a named profile (one profile per environment). */
1113
- function profileHttp(ctx, name) {
1114
- const stored = ctx.config.load().profiles[name];
1115
- if (!stored?.secret_key) {
1116
- throw new errors_1.CliError(`No profile named '${name}'. Run 'atlas login --profile ${name}' with that environment's secret key.`);
1117
- }
1118
- const secretKey = stored.secret_key;
1119
- const apiUrl = (0, shared_1.envApiUrl)(ctx.env) || stored.api_url || backend_1.DEFAULT_API_URL;
1120
- const make = ctx.makeHttp ?? config_http_1.createConfigHttp;
1121
- return { http: make({ secretKey, apiUrl }), secretKey };
1122
- }
1123
- /** True for a production (live) secret key: `atlas_live_sk_…` or `sk_live_…`. */
1124
- const isLiveKey = (key) => /(?:^|_)live_/.test(key);
1125
- /**
1126
- * `atlas config promote --from <profile> --to <profile>`: copy one environment's
1127
- * configuration to another (development → staging → production) through the same
1128
- * plan/apply engine as `config import`. Each environment is a profile holding that
1129
- * instance's secret key. Secrets never travel: `${ENV}` placeholders stay as they
1130
- * are and credentials are set per environment. `--dry-run` prints the plan only;
1131
- * applying to a live (production) key needs `--yes`.
1132
- */
1133
- async function promoteConfig(ctx, values) {
1134
- const from = (0, args_1.str)(values, 'from');
1135
- const to = (0, args_1.str)(values, 'to');
1136
- if (!from || !to)
1137
- throw new errors_1.CliError('promote needs --from <profile> and --to <profile> (one profile per environment).');
1138
- if (from === to)
1139
- throw new errors_1.CliError('--from and --to name the same profile.');
1140
- const format = resolveFormat(values);
1141
- const dryRun = values['dry-run'] === true;
1142
- const prune = values.prune === true;
1143
- const source = profileHttp(ctx, from);
1144
- const target = profileHttp(ctx, to);
1145
- if (source.secretKey === target.secretKey) {
1146
- throw new errors_1.CliError(`Profiles '${from}' and '${to}' hold the same secret key — they are the same environment.`);
1147
- }
1148
- if (!dryRun && isLiveKey(target.secretKey) && values.yes !== true) {
1149
- throw new errors_1.CliError(`'${to}' is a live (production) environment. Review with --dry-run, then re-run with --yes to apply.`);
1150
- }
1151
- const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'atlas-promote-'));
1152
- try {
1153
- await writeExport(source.http, dir, format);
1154
- const plan = await planConfigImport(target.http, { dir, format, env: ctx.env, prune });
1155
- ctx.io.out(`Promoting ${from} -> ${to}`);
1156
- printPlan(ctx, plan.changes, plan.unchanged, plan.skippedDeletes, { dryRun, prune });
1157
- if (dryRun)
1158
- return 0;
1159
- for (const change of plan.changes)
1160
- await change.apply();
1161
- ctx.io.out(plan.changes.length ? `Applied ${plan.changes.length} change(s) to ${to}.` : 'No changes to apply.');
1162
- return 0;
1163
- }
1164
- finally {
1165
- (0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
1166
- }
1167
- }
1168
- async function importConfig(ctx, values) {
1169
- const http = requireHttp(ctx, values);
1170
- const dir = resolveDir(values);
1171
- const format = resolveFormat(values);
1172
- const dryRun = values['dry-run'] === true;
1173
- const prune = values.prune === true;
1174
- const plan = await planConfigImport(http, { dir, format, env: readSecretsEnv(ctx, values), prune, forceHook: values['force-hook'] === true });
1175
- printPlan(ctx, plan.changes, plan.unchanged, plan.skippedDeletes, { dryRun, prune });
1176
- if (dryRun)
1177
- return 0;
1178
- for (const change of plan.changes)
1179
- await change.apply();
1180
- if (plan.changes.length)
1181
- ctx.io.out(`Applied ${plan.changes.length} change(s).`);
1182
- else
1183
- ctx.io.out('No changes to apply.');
1184
- return 0;
1185
- }
1186
- /** Leaf-level differences between two documents, as dotted paths (arrays compare whole). */
1187
- function diffFields(before, after, path = '') {
1188
- const isObj = (x) => x !== null && typeof x === 'object' && !Array.isArray(x);
1189
- if (isObj(before) && isObj(after)) {
1190
- const keys = [...new Set([...Object.keys(before), ...Object.keys(after)])].sort();
1191
- return keys.flatMap((k) => diffFields(before[k], after[k], path ? `${path}.${k}` : k));
1192
- }
1193
- if (before !== undefined && after !== undefined && (0, serialize_1.deepEqual)(before, after))
1194
- return [];
1195
- if (before === undefined && after === undefined)
1196
- return [];
1197
- return [{ path: path || '(document)', ...(before === undefined ? {} : { before }), ...(after === undefined ? {} : { after }) }];
1198
- }
1199
- const show = (v) => (v === undefined ? '(unset)' : typeof v === 'string' ? JSON.stringify(v) : JSON.stringify(v));
1200
- /**
1201
- * `atlas config plan`: a Terraform-style, read-only diff of a config directory
1202
- * (`atlas config export` output, committed to git) against the live instance.
1203
- * Field-level for updates, `--json` for tooling, and `--exit-code` to exit 2
1204
- * when there is drift so CI can gate on it. Never writes anything.
1205
- */
1206
- async function planConfig(ctx, values, source) {
1207
- const http = requireHttp(ctx, values);
1208
- const { dir, format, env } = source;
1209
- const where = source.label ?? dir;
1210
- const prune = values.prune === true;
1211
- const plan = await planConfigImport(http, { dir, format, env, prune });
1212
- const deletes = prune ? [] : plan.skippedDeletes;
1213
- const all = plan.changes;
1214
- const rows = all.map((c) => ({
1215
- type: c.type,
1216
- key: c.key || null,
1217
- action: c.action,
1218
- fields: c.action === 'update' ? diffFields(c.before, c.after) : [],
1219
- }));
1220
- const pending = rows.length;
1221
- if (values.json === true) {
1222
- ctx.io.out(JSON.stringify({
1223
- object: 'config_plan',
1224
- changes: rows,
1225
- unchanged: plan.unchanged,
1226
- unmanaged_live: deletes.map((d) => ({ type: d.type, key: d.key || null })),
1227
- managed_by_platform: plan.managed.map((m) => ({ type: m.type, key: m.key, managed_by: m.managedBy })),
1228
- has_changes: pending > 0,
1229
- }, null, 2));
1230
- }
1231
- else {
1232
- ctx.io.out(`Plan: ${pending} to change, ${plan.unchanged} unchanged${deletes.length ? `, ${deletes.length} live but not in config` : ''}.`);
1233
- const sign = { create: '+', update: '~', delete: '-' };
1234
- for (const c of [...all].sort(sortChange)) {
1235
- const label = c.key ? `${c.type} ${c.key}` : c.type;
1236
- ctx.io.out('');
1237
- ctx.io.out(`${sign[c.action]} ${label} (${c.action})`);
1238
- if (c.action === 'update') {
1239
- for (const f of diffFields(c.before, c.after))
1240
- ctx.io.out(` ${f.path}: ${show(f.before)} -> ${show(f.after)}`);
1241
- }
1242
- }
1243
- if (deletes.length) {
1244
- ctx.io.out('');
1245
- ctx.io.out(`Live but not in ${where} (kept; pass --prune to plan their deletion):`);
1246
- for (const d of deletes)
1247
- ctx.io.out(` ${d.type}${d.key ? ` ${d.key}` : ''}`);
1248
- }
1249
- if (plan.managed.length) {
1250
- ctx.io.out('');
1251
- ctx.io.out('Managed by a connected platform (not part of this file; never pruned):');
1252
- for (const m of plan.managed)
1253
- ctx.io.out(` ${m.type} ${m.key} (${m.managedBy})`);
1254
- }
1255
- if (pending === 0)
1256
- ctx.io.out(`No drift: the instance matches ${where}.`);
1257
- }
1258
- return wantsCheck(values) && pending > 0 ? 2 : 0;
1259
- }
1260
- /** `--check` (CI) and `--exit-code` (Terraform's name) both mean: exit 2 when there is drift. */
1261
- const wantsCheck = (values) => values['exit-code'] === true || values.check === true;
1262
- /**
1263
- * The environment `${VAR}` placeholders resolve against: the process env, then
1264
- * `--secrets-file` (a dotenv file) on top. The file holds credentials, so it
1265
- * must be readable by its owner only (mode 0600 or stricter); anything looser
1266
- * is refused rather than read. POSIX only: Windows has no such mode bits.
1267
- */
1268
- function readSecretsEnv(ctx, values) {
1269
- const file = (0, args_1.str)(values, 'secrets-file');
1270
- if (!file)
1271
- return ctx.env;
1272
- let mode;
1273
- try {
1274
- mode = (0, node_fs_1.statSync)(file).mode;
1275
- }
1276
- catch {
1277
- throw new errors_1.CliError(`--secrets-file not found: ${file}.`);
1278
- }
1279
- if (process.platform !== 'win32' && (mode & 0o077) !== 0) {
1280
- throw new errors_1.CliError(`--secrets-file ${file} is readable by others (mode ${(mode & 0o777).toString(8)}). Run 'chmod 600 ${file}' and try again.`);
1281
- }
1282
- return { ...ctx.env, ...(0, secrets_1.parseDotenv)(ctx.readFile(file)) };
1283
- }
1284
- /** `${VAR}` placeholders still in a planned change (no env value and no --secrets-file entry). */
1285
- function unresolvedPlaceholders(changes) {
1286
- const names = new Set();
1287
- for (const c of changes) {
1288
- for (const m of JSON.stringify(c.after ?? null).matchAll(/\$\{([A-Z0-9_]+)\}/g))
1289
- names.add(m[1]);
1290
- }
1291
- return [...names].sort();
1292
- }
1293
- /** The resource types a single config file may hold, in the order they are applied. */
1294
- function configTypes() {
1295
- return registry().map((s) => s.type);
1296
- }
1297
- /**
1298
- * Resolve `atlas plan|apply [<path>]`: a directory of per-type files (as
1299
- * `atlas config export` writes), or ONE file whose top-level keys are resource
1300
- * types (`instance`, `webhook_endpoints`, …), split into a temp directory.
1301
- * `cleanup` removes that temp directory.
1302
- */
1303
- /** Config files `atlas plan` / `atlas apply` pick up from the current directory when given no path. */
1304
- exports.DEFAULT_CONFIG_FILES = ['atlas.config.ts', 'atlas.config.mts', 'atlas.config.mjs', 'atlas.config.js', 'atlas.yaml', 'atlas.yml', 'atlas.json'];
1305
- const CODE_EXTS = new Set(['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs']);
1306
- // The CLI compiles to CommonJS, where tsc turns import() into require(), which cannot load ESM or .ts,
1307
- // so the real import() is reached through Function. A vm context with no import hook (a test runner)
1308
- // cannot run that one, and gets the plain import() the runner itself transforms.
1309
- const nativeImport = async (url) => {
1310
- try {
1311
- return await new Function('url', 'return import(url)')(url);
1312
- }
1313
- catch (err) {
1314
- if (err.code !== 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING')
1315
- throw err;
1316
- return Promise.resolve(`${url}`).then(s => __importStar(require(s)));
1317
- }
1318
- };
1319
- /**
1320
- * Load an `atlas.config.ts` (or .js/.mjs): its default export is the config
1321
- * object, or a function (sync or async) returning it. A .ts file runs on
1322
- * Node's own type stripping (Node 22.18+ / 23.6+), so it adds no dependency.
1323
- */
1324
- async function loadCodeConfig(path) {
1325
- let mod;
1326
- try {
1327
- mod = (await nativeImport((0, node_url_1.pathToFileURL)((0, node_path_1.resolve)(path)).href));
1328
- }
1329
- catch (err) {
1330
- const msg = err.message;
1331
- if (/\.ts|Unknown file extension|ERR_UNKNOWN_FILE_EXTENSION/.test(msg) && /\.[cm]?ts$/.test(path)) {
1332
- throw new errors_1.CliError(`${path}: this Node (${process.version}) cannot load TypeScript directly. Use Node 22.18+ (or 23.6+), or rename it to atlas.config.mjs.`);
1333
- }
1334
- throw new errors_1.CliError(`${path}: ${msg}`);
1335
- }
1336
- const exported = mod.default;
1337
- if (exported === undefined)
1338
- throw new errors_1.CliError(`${path} has no default export. Write: export default defineConfig({ ... }).`);
1339
- return typeof exported === 'function' ? await exported() : exported;
1340
- }
1341
- async function resolveSource(ctx, args) {
1342
- const path = args.positionals[1] ??
1343
- ((0, args_1.str)(args.values, 'dir') ? resolveDir(args.values) : (exports.DEFAULT_CONFIG_FILES.find((f) => (0, node_fs_1.existsSync)(f)) ?? resolveDir(args.values)));
1344
- const env = readSecretsEnv(ctx, args.values);
1345
- let isFile = false;
1346
- try {
1347
- isFile = (0, node_fs_1.statSync)(path).isFile();
1348
- }
1349
- catch {
1350
- throw new errors_1.CliError(`Config not found: ${path}. Pass a config file (atlas.config.ts / atlas.yaml / atlas.json) or a directory from 'atlas config export'.`);
1351
- }
1352
- if (!isFile)
1353
- return { dir: path, label: path, format: resolveFormat(args.values), env, cleanup: () => undefined };
1354
- const ext = (0, node_path_1.extname)(path).toLowerCase();
1355
- const format = ext === '.yaml' || ext === '.yml' ? 'yaml' : ext === '.json' ? 'json' : resolveFormat(args.values);
1356
- // A round-trip through JSON keeps only plain data (no functions, Dates become strings), like a .json file.
1357
- const doc = CODE_EXTS.has(ext) ? JSON.parse(JSON.stringify((await loadCodeConfig(path)) ?? null)) : (0, serialize_1.deserialize)(ctx.readFile(path), format);
1358
- if (doc === null || typeof doc !== 'object' || Array.isArray(doc)) {
1359
- throw new errors_1.CliError(`${path} must be an object keyed by resource type (${configTypes().join(', ')}).`);
1360
- }
1361
- const known = new Set(configTypes());
1362
- const unknown = Object.keys(doc).filter((k) => !known.has(k));
1363
- if (unknown.length) {
1364
- throw new errors_1.CliError(`${path}: unknown resource type(s) ${unknown.join(', ')}. Known: ${configTypes().join(', ')}.`);
1365
- }
1366
- const dir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'atlas-apply-'));
1367
- for (const [type, value] of Object.entries(doc))
1368
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, `${type}.json`), (0, serialize_1.serialize)(value, 'json'));
1369
- return { dir, label: path, format: 'json', env, cleanup: () => (0, node_fs_1.rmSync)(dir, { recursive: true, force: true }) };
1370
- }
1371
- /**
1372
- * `atlas plan [<file|dir>]`: the read-only diff of `atlas config plan`, taking
1373
- * a single config file as well. `--check` exits 2 on drift for CI.
1374
- */
1375
- async function planCommand(ctx, args) {
1376
- const source = await resolveSource(ctx, args);
1377
- try {
1378
- return await planConfig(ctx, args.values, source);
1379
- }
1380
- finally {
1381
- source.cleanup();
1382
- }
1383
- }
1384
- /**
1385
- * `atlas apply [<file|dir>]`: plan, show it, confirm, then apply in the fixed
1386
- * resource order (see {@link configTypes}). `--yes` skips the question (needed
1387
- * when there is no terminal); `--check` only plans and exits 2 on drift.
1388
- * Refuses to apply while any `${VAR}` placeholder is unresolved, so a missing
1389
- * secret is never sent as the literal text.
1390
- */
1391
- async function applyCommand(ctx, args) {
1392
- const values = args.values;
1393
- if (values.check === true || values['exit-code'] === true)
1394
- return planCommand(ctx, args);
1395
- const source = await resolveSource(ctx, args);
1396
- try {
1397
- const kept = keepingSecrets(requireHttp(ctx, values), (0, args_1.str)(values, 'out') || (0, secrets_1.productDir)(ctx.env, 'atlas'));
1398
- const prune = values.prune === true;
1399
- const plan = await planConfigImport(kept.http, { dir: source.dir, format: source.format, env: source.env, prune, forceHook: values['force-hook'] === true });
1400
- printPlan(ctx, plan.changes, plan.unchanged, plan.skippedDeletes, { dryRun: false, prune });
1401
- if (plan.changes.length === 0) {
1402
- ctx.io.out('No changes to apply.');
1403
- return 0;
1404
- }
1405
- const missing = unresolvedPlaceholders(plan.changes);
1406
- if (missing.length) {
1407
- throw new errors_1.CliError(`Unresolved placeholder(s): ${missing.map((n) => `\${${n}}`).join(', ')}. Set them in the environment or in --secrets-file.`);
1408
- }
1409
- if (values.yes !== true) {
1410
- const answer = (await ctx.prompt.text(`Apply ${plan.changes.length} change(s)? [y/N]`, { default: 'n' })).trim().toLowerCase();
1411
- if (answer !== 'y' && answer !== 'yes') {
1412
- ctx.io.out('Nothing applied.');
1413
- return 1;
1414
- }
1415
- }
1416
- for (const change of plan.changes)
1417
- await change.apply();
1418
- ctx.io.out(`Applied ${plan.changes.length} change(s).`);
1419
- for (const path of kept.saved)
1420
- ctx.io.out(`A new secret was written to ${path} (mode 0600). It is shown only once.`);
1421
- return 0;
1422
- }
1423
- finally {
1424
- source.cleanup();
1425
- }
1426
- }
1427
- /**
1428
- * The API, but a create that answers with a one-time secret (a new OAuth
1429
- * client's `client_secret`, a new webhook endpoint's `secret`) writes it to a
1430
- * 0600 dotenv file in `dir` (default `~/.config/atlas`, or `--out`) instead of
1431
- * dropping it. The file names are the hosted-redirect env names.
1432
- */
1433
- function keepingSecrets(http, dir) {
1434
- const saved = [];
1435
- const wrapped = {
1436
- get: (path) => http.get(path),
1437
- put: (path, body) => http.put(path, body),
1438
- patch: (path, body) => http.patch(path, body),
1439
- delete: (path, body) => http.delete(path, body),
1440
- async post(path, body) {
1441
- const res = await http.post(path, body);
1442
- const r = (res ?? {});
1443
- const id = String(r.id ?? r.client_id ?? 'new').replace(/[^A-Za-z0-9_-]/g, '_');
1444
- let entries = null;
1445
- if (path === '/v1/oauth_clients' && typeof r.client_secret === 'string') {
1446
- entries = { ATLAS_CLIENT_ID: String(r.client_id ?? ''), ATLAS_CLIENT_SECRET: r.client_secret };
1447
- }
1448
- else if (path === '/v1/webhook_endpoints' && typeof r.secret === 'string') {
1449
- entries = { ATLAS_WEBHOOK_SECRET: r.secret };
1450
- }
1451
- if (entries) {
1452
- const kind = path === '/v1/oauth_clients' ? 'oauth-client' : 'webhook';
1453
- saved.push((0, secrets_1.writeSecretFile)(dir, `${kind}-${id}.env`, entries, { comment: `Written by atlas apply. ${kind} ${id}.` }).path);
1454
- }
1455
- return res;
1456
- },
1457
- };
1458
- return { http: wrapped, saved };
1459
- }
1460
- function printPlan(ctx, changes, unchanged, skippedDeletes, opts) {
1461
- const heading = opts.dryRun ? 'Plan (dry run — nothing applied):' : 'Plan:';
1462
- ctx.io.out(heading);
1463
- if (changes.length === 0) {
1464
- ctx.io.out(' (no changes)');
1465
- }
1466
- const sign = { create: '+', update: '~', delete: '-' };
1467
- for (const c of [...changes].sort(sortChange)) {
1468
- const label = c.key ? `${c.type} ${c.key}` : c.type;
1469
- ctx.io.out(` ${sign[c.action]} ${c.action} ${label}`);
1470
- }
1471
- if (!opts.prune && skippedDeletes.length) {
1472
- ctx.io.out(` ${skippedDeletes.length} resource(s) exist live but not in config — pass --prune to delete.`);
1473
- }
1474
- ctx.io.out(` ${unchanged} unchanged.`);
1475
- }
1476
- function sortChange(a, b) {
1477
- if (a.type !== b.type)
1478
- return a.type < b.type ? -1 : 1;
1479
- if (a.action !== b.action)
1480
- return a.action < b.action ? -1 : 1;
1481
- return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
1482
- }
1483
- function asArray(value) {
1484
- return Array.isArray(value) ? value : [];
1485
- }
1486
- /**
1487
- * Replace `${ENV_VAR}` placeholders inside any string with the environment's
1488
- * value — the one sanctioned way a secret enters an import without ever living
1489
- * on disk. An unset variable is left verbatim so the plan surfaces it rather
1490
- * than silently blanking a credential.
1491
- */
1492
- function interpolate(value, env) {
1493
- if (typeof value === 'string') {
1494
- return value.replace(/\$\{([A-Z0-9_]+)\}/g, (match, name) => env[name] ?? match);
1495
- }
1496
- if (Array.isArray(value))
1497
- return value.map((v) => interpolate(v, env));
1498
- if (value !== null && typeof value === 'object') {
1499
- const out = {};
1500
- for (const [k, v] of Object.entries(value))
1501
- out[k] = interpolate(v, env);
1502
- return out;
1503
- }
1504
- return value;
1505
- }
1506
- //# sourceMappingURL=config.js.map