primitive-admin 1.1.0-alpha.9 → 1.1.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 (591) hide show
  1. package/README.md +523 -109
  2. package/assets/skill/skills/primitive-platform/SKILL.md +289 -0
  3. package/dist/bin/primitive.d.ts +2 -0
  4. package/dist/bin/primitive.js +372 -21
  5. package/dist/bin/primitive.js.map +1 -1
  6. package/dist/src/commands/admins.d.ts +2 -0
  7. package/dist/src/commands/admins.js +136 -30
  8. package/dist/src/commands/admins.js.map +1 -1
  9. package/dist/src/commands/analytics.d.ts +2 -0
  10. package/dist/src/commands/analytics.js +713 -55
  11. package/dist/src/commands/analytics.js.map +1 -1
  12. package/dist/src/commands/apps.d.ts +2 -0
  13. package/dist/src/commands/apps.js +65 -100
  14. package/dist/src/commands/apps.js.map +1 -1
  15. package/dist/src/commands/auth-sessions.d.ts +7 -0
  16. package/dist/src/commands/auth-sessions.js +144 -0
  17. package/dist/src/commands/auth-sessions.js.map +1 -0
  18. package/dist/src/commands/auth.d.ts +2 -0
  19. package/dist/src/commands/auth.js +238 -108
  20. package/dist/src/commands/auth.js.map +1 -1
  21. package/dist/src/commands/blob-buckets.d.ts +2 -0
  22. package/dist/src/commands/blob-buckets.js +331 -0
  23. package/dist/src/commands/blob-buckets.js.map +1 -0
  24. package/dist/src/commands/catalog.d.ts +2 -0
  25. package/dist/src/commands/catalog.js +63 -48
  26. package/dist/src/commands/catalog.js.map +1 -1
  27. package/dist/src/commands/collection-type-configs.d.ts +2 -0
  28. package/dist/src/commands/collection-type-configs.js +85 -0
  29. package/dist/src/commands/collection-type-configs.js.map +1 -0
  30. package/dist/src/commands/collections.d.ts +2 -0
  31. package/dist/src/commands/collections.js +1282 -0
  32. package/dist/src/commands/collections.js.map +1 -0
  33. package/dist/src/commands/comparisons.d.ts +2 -0
  34. package/dist/src/commands/comparisons.js +6 -6
  35. package/dist/src/commands/comparisons.js.map +1 -1
  36. package/dist/src/commands/config.d.ts +46 -0
  37. package/dist/src/commands/config.js +465 -0
  38. package/dist/src/commands/config.js.map +1 -0
  39. package/dist/src/commands/connections.d.ts +2 -0
  40. package/dist/src/commands/connections.js +99 -0
  41. package/dist/src/commands/connections.js.map +1 -0
  42. package/dist/src/commands/cron-triggers.d.ts +2 -0
  43. package/dist/src/commands/cron-triggers.js +266 -0
  44. package/dist/src/commands/cron-triggers.js.map +1 -0
  45. package/dist/src/commands/database-type-configs.d.ts +2 -0
  46. package/dist/src/commands/database-type-configs.js +164 -0
  47. package/dist/src/commands/database-type-configs.js.map +1 -0
  48. package/dist/src/commands/database-types.d.ts +2 -0
  49. package/dist/src/commands/database-types.js +471 -0
  50. package/dist/src/commands/database-types.js.map +1 -0
  51. package/dist/src/commands/databases.d.ts +65 -0
  52. package/dist/src/commands/databases.js +1887 -243
  53. package/dist/src/commands/databases.js.map +1 -1
  54. package/dist/src/commands/documents.d.ts +60 -0
  55. package/dist/src/commands/documents.js +2888 -22
  56. package/dist/src/commands/documents.js.map +1 -1
  57. package/dist/src/commands/email-templates.d.ts +2 -0
  58. package/dist/src/commands/email-templates.js +175 -0
  59. package/dist/src/commands/email-templates.js.map +1 -0
  60. package/dist/src/commands/env.d.ts +23 -0
  61. package/dist/src/commands/env.js +395 -0
  62. package/dist/src/commands/env.js.map +1 -0
  63. package/dist/src/commands/feature-flags.d.ts +14 -0
  64. package/dist/src/commands/feature-flags.js +117 -0
  65. package/dist/src/commands/feature-flags.js.map +1 -0
  66. package/dist/src/commands/functions.d.ts +20 -0
  67. package/dist/src/commands/functions.js +1630 -0
  68. package/dist/src/commands/functions.js.map +1 -0
  69. package/dist/src/commands/group-type-configs.d.ts +2 -0
  70. package/dist/src/commands/group-type-configs.js +87 -0
  71. package/dist/src/commands/group-type-configs.js.map +1 -0
  72. package/dist/src/commands/groups.d.ts +2 -0
  73. package/dist/src/commands/groups.js +65 -113
  74. package/dist/src/commands/groups.js.map +1 -1
  75. package/dist/src/commands/guides.d.ts +223 -0
  76. package/dist/src/commands/guides.js +627 -69
  77. package/dist/src/commands/guides.js.map +1 -1
  78. package/dist/src/commands/init.d.ts +37 -0
  79. package/dist/src/commands/init.js +1693 -208
  80. package/dist/src/commands/init.js.map +1 -1
  81. package/dist/src/commands/integrations.d.ts +2 -0
  82. package/dist/src/commands/integrations.js +428 -187
  83. package/dist/src/commands/integrations.js.map +1 -1
  84. package/dist/src/commands/llm.d.ts +2 -0
  85. package/dist/src/commands/llm.js +4 -2
  86. package/dist/src/commands/llm.js.map +1 -1
  87. package/dist/src/commands/locks.d.ts +8 -0
  88. package/dist/src/commands/locks.js +175 -0
  89. package/dist/src/commands/locks.js.map +1 -0
  90. package/dist/src/commands/metadata-category-configs.d.ts +12 -0
  91. package/dist/src/commands/metadata-category-configs.js +113 -0
  92. package/dist/src/commands/metadata-category-configs.js.map +1 -0
  93. package/dist/src/commands/metadata.d.ts +2 -0
  94. package/dist/src/commands/metadata.js +288 -0
  95. package/dist/src/commands/metadata.js.map +1 -0
  96. package/dist/src/commands/prompts.d.ts +2 -0
  97. package/dist/src/commands/prompts.js +322 -634
  98. package/dist/src/commands/prompts.js.map +1 -1
  99. package/dist/src/commands/rule-sets.d.ts +3 -0
  100. package/dist/src/commands/rule-sets.js +139 -148
  101. package/dist/src/commands/rule-sets.js.map +1 -1
  102. package/dist/src/commands/scripts.d.ts +30 -0
  103. package/dist/src/commands/scripts.js +688 -0
  104. package/dist/src/commands/scripts.js.map +1 -0
  105. package/dist/src/commands/secrets.d.ts +2 -0
  106. package/dist/src/commands/secrets.js +109 -0
  107. package/dist/src/commands/secrets.js.map +1 -0
  108. package/dist/src/commands/sessions.d.ts +2 -0
  109. package/dist/src/commands/sessions.js +76 -0
  110. package/dist/src/commands/sessions.js.map +1 -0
  111. package/dist/src/commands/skill.d.ts +2 -0
  112. package/dist/src/commands/skill.js +29 -0
  113. package/dist/src/commands/skill.js.map +1 -0
  114. package/dist/src/commands/sync-app-settings.d.ts +158 -0
  115. package/dist/src/commands/sync-app-settings.js +328 -0
  116. package/dist/src/commands/sync-app-settings.js.map +1 -0
  117. package/dist/src/commands/sync.d.ts +2670 -0
  118. package/dist/src/commands/sync.js +17144 -834
  119. package/dist/src/commands/sync.js.map +1 -1
  120. package/dist/src/commands/tokens.d.ts +2 -0
  121. package/dist/src/commands/tokens.js +132 -22
  122. package/dist/src/commands/tokens.js.map +1 -1
  123. package/dist/src/commands/users.d.ts +2 -0
  124. package/dist/src/commands/users.js +542 -24
  125. package/dist/src/commands/users.js.map +1 -1
  126. package/dist/src/commands/vars.d.ts +8 -0
  127. package/dist/src/commands/vars.js +97 -0
  128. package/dist/src/commands/vars.js.map +1 -0
  129. package/dist/src/commands/waitlist.d.ts +2 -0
  130. package/dist/src/commands/waitlist.js +12 -11
  131. package/dist/src/commands/waitlist.js.map +1 -1
  132. package/dist/src/commands/webhooks.d.ts +31 -0
  133. package/dist/src/commands/webhooks.js +633 -0
  134. package/dist/src/commands/webhooks.js.map +1 -0
  135. package/dist/src/commands/workflows.d.ts +88 -0
  136. package/dist/src/commands/workflows.js +1568 -742
  137. package/dist/src/commands/workflows.js.map +1 -1
  138. package/dist/src/lib/access-rule-display.d.ts +21 -0
  139. package/dist/src/lib/access-rule-display.js +34 -0
  140. package/dist/src/lib/access-rule-display.js.map +1 -0
  141. package/dist/src/lib/api-client.d.ts +2550 -0
  142. package/dist/src/lib/api-client.js +2503 -160
  143. package/dist/src/lib/api-client.js.map +1 -1
  144. package/dist/src/lib/app-settings-descriptor.d.ts +263 -0
  145. package/dist/src/lib/app-settings-descriptor.js +583 -0
  146. package/dist/src/lib/app-settings-descriptor.js.map +1 -0
  147. package/dist/src/lib/auth-flow.d.ts +8 -0
  148. package/dist/src/lib/batch.d.ts +26 -0
  149. package/dist/src/lib/batch.js +32 -0
  150. package/dist/src/lib/batch.js.map +1 -0
  151. package/dist/src/lib/block-layout.d.ts +160 -0
  152. package/dist/src/lib/block-layout.js +451 -0
  153. package/dist/src/lib/block-layout.js.map +1 -0
  154. package/dist/src/lib/block-selector.d.ts +58 -0
  155. package/dist/src/lib/block-selector.js +92 -0
  156. package/dist/src/lib/block-selector.js.map +1 -0
  157. package/dist/src/lib/canonical-json.d.ts +12 -0
  158. package/dist/src/lib/canonical-json.js +35 -0
  159. package/dist/src/lib/canonical-json.js.map +1 -0
  160. package/dist/src/lib/channel.d.ts +30 -0
  161. package/dist/src/lib/channel.js +68 -0
  162. package/dist/src/lib/channel.js.map +1 -0
  163. package/dist/src/lib/cli-manifest.d.ts +68 -0
  164. package/dist/src/lib/cli-manifest.js +71 -0
  165. package/dist/src/lib/cli-manifest.js.map +1 -0
  166. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +101 -0
  167. package/dist/src/lib/codegen-shared/generatedFiles.js +191 -0
  168. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -0
  169. package/dist/src/lib/codegen-shared/prettierStable.d.ts +262 -0
  170. package/dist/src/lib/codegen-shared/prettierStable.js +610 -0
  171. package/dist/src/lib/codegen-shared/prettierStable.js.map +1 -0
  172. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +38 -0
  173. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +46 -0
  174. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -0
  175. package/dist/src/lib/collection-export.d.ts +184 -0
  176. package/dist/src/lib/collection-export.js +252 -0
  177. package/dist/src/lib/collection-export.js.map +1 -0
  178. package/dist/src/lib/config-json-field.d.ts +28 -0
  179. package/dist/src/lib/config-json-field.js +56 -0
  180. package/dist/src/lib/config-json-field.js.map +1 -0
  181. package/dist/src/lib/config-object-descriptor.d.ts +127 -0
  182. package/dist/src/lib/config-object-descriptor.js +740 -0
  183. package/dist/src/lib/config-object-descriptor.js.map +1 -0
  184. package/dist/src/lib/config-payload.d.ts +92 -0
  185. package/dist/src/lib/config-payload.js +161 -0
  186. package/dist/src/lib/config-payload.js.map +1 -0
  187. package/dist/src/lib/config-surface.d.ts +141 -0
  188. package/dist/src/lib/config-surface.js +368 -0
  189. package/dist/src/lib/config-surface.js.map +1 -0
  190. package/dist/src/lib/config-toml.d.ts +10 -0
  191. package/dist/src/lib/config-toml.js +42 -0
  192. package/dist/src/lib/config-toml.js.map +1 -0
  193. package/dist/src/lib/config.d.ts +71 -0
  194. package/dist/src/lib/config.js +71 -68
  195. package/dist/src/lib/config.js.map +1 -1
  196. package/dist/src/lib/confirm-prompt.d.ts +83 -0
  197. package/dist/src/lib/confirm-prompt.js +110 -0
  198. package/dist/src/lib/confirm-prompt.js.map +1 -0
  199. package/dist/src/lib/constants.d.ts +11 -0
  200. package/dist/src/lib/constants.js +12 -0
  201. package/dist/src/lib/constants.js.map +1 -0
  202. package/dist/src/lib/crash-handlers.d.ts +20 -0
  203. package/dist/src/lib/crash-handlers.js +49 -0
  204. package/dist/src/lib/crash-handlers.js.map +1 -0
  205. package/dist/src/lib/credentials-store.d.ts +104 -0
  206. package/dist/src/lib/credentials-store.js +336 -0
  207. package/dist/src/lib/credentials-store.js.map +1 -0
  208. package/dist/src/lib/csv.d.ts +47 -0
  209. package/dist/src/lib/csv.js +172 -0
  210. package/dist/src/lib/csv.js.map +1 -0
  211. package/dist/src/lib/data-input.d.ts +23 -0
  212. package/dist/src/lib/data-input.js +50 -0
  213. package/dist/src/lib/data-input.js.map +1 -0
  214. package/dist/src/lib/db-codegen/dbFingerprint.d.ts +10 -0
  215. package/dist/src/lib/db-codegen/dbFingerprint.js +17 -0
  216. package/dist/src/lib/db-codegen/dbFingerprint.js.map +1 -0
  217. package/dist/src/lib/db-codegen/dbGenerator.d.ts +67 -0
  218. package/dist/src/lib/db-codegen/dbGenerator.js +170 -0
  219. package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -0
  220. package/dist/src/lib/db-codegen/dbNaming.d.ts +87 -0
  221. package/dist/src/lib/db-codegen/dbNaming.js +180 -0
  222. package/dist/src/lib/db-codegen/dbNaming.js.map +1 -0
  223. package/dist/src/lib/db-codegen/dbTemplates.d.ts +272 -0
  224. package/dist/src/lib/db-codegen/dbTemplates.js +480 -0
  225. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -0
  226. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +73 -0
  227. package/dist/src/lib/db-codegen/dbTsTypes.js +139 -0
  228. package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -0
  229. package/dist/src/lib/db-codegen/dbTypeIR.d.ts +146 -0
  230. package/dist/src/lib/db-codegen/dbTypeIR.js +525 -0
  231. package/dist/src/lib/db-codegen/dbTypeIR.js.map +1 -0
  232. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.d.ts +112 -0
  233. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js +211 -0
  234. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js.map +1 -0
  235. package/dist/src/lib/deprecation.d.ts +22 -0
  236. package/dist/src/lib/deprecation.js +43 -0
  237. package/dist/src/lib/deprecation.js.map +1 -0
  238. package/dist/src/lib/document-export-permissions.d.ts +30 -0
  239. package/dist/src/lib/document-export-permissions.js +54 -0
  240. package/dist/src/lib/document-export-permissions.js.map +1 -0
  241. package/dist/src/lib/document-ingest-artifact.d.ts +120 -0
  242. package/dist/src/lib/document-ingest-artifact.js +505 -0
  243. package/dist/src/lib/document-ingest-artifact.js.map +1 -0
  244. package/dist/src/lib/document-ingest-input.d.ts +51 -0
  245. package/dist/src/lib/document-ingest-input.js +132 -0
  246. package/dist/src/lib/document-ingest-input.js.map +1 -0
  247. package/dist/src/lib/document-ingest-rows.d.ts +137 -0
  248. package/dist/src/lib/document-ingest-rows.js +181 -0
  249. package/dist/src/lib/document-ingest-rows.js.map +1 -0
  250. package/dist/src/lib/document-ingest.d.ts +101 -0
  251. package/dist/src/lib/document-ingest.js +384 -0
  252. package/dist/src/lib/document-ingest.js.map +1 -0
  253. package/dist/src/lib/env-resolver-core.d.ts +258 -0
  254. package/dist/src/lib/env-resolver-core.js +447 -0
  255. package/dist/src/lib/env-resolver-core.js.map +1 -0
  256. package/dist/src/lib/env-resolver.d.ts +99 -0
  257. package/dist/src/lib/env-resolver.js +153 -0
  258. package/dist/src/lib/env-resolver.js.map +1 -0
  259. package/dist/src/lib/fetch.d.ts +5 -0
  260. package/dist/src/lib/function-bundle.d.ts +147 -0
  261. package/dist/src/lib/function-bundle.js +341 -0
  262. package/dist/src/lib/function-bundle.js.map +1 -0
  263. package/dist/src/lib/function-collect.d.ts +123 -0
  264. package/dist/src/lib/function-collect.js +610 -0
  265. package/dist/src/lib/function-collect.js.map +1 -0
  266. package/dist/src/lib/function-db-types.d.ts +202 -0
  267. package/dist/src/lib/function-db-types.js +870 -0
  268. package/dist/src/lib/function-db-types.js.map +1 -0
  269. package/dist/src/lib/function-document-types.d.ts +144 -0
  270. package/dist/src/lib/function-document-types.js +370 -0
  271. package/dist/src/lib/function-document-types.js.map +1 -0
  272. package/dist/src/lib/function-grants-preflight.d.ts +64 -0
  273. package/dist/src/lib/function-grants-preflight.js +105 -0
  274. package/dist/src/lib/function-grants-preflight.js.map +1 -0
  275. package/dist/src/lib/function-log-lines.d.ts +76 -0
  276. package/dist/src/lib/function-log-lines.js +160 -0
  277. package/dist/src/lib/function-log-lines.js.map +1 -0
  278. package/dist/src/lib/function-log-row.d.ts +29 -0
  279. package/dist/src/lib/function-log-row.js +73 -0
  280. package/dist/src/lib/function-log-row.js.map +1 -0
  281. package/dist/src/lib/function-log-tail.d.ts +132 -0
  282. package/dist/src/lib/function-log-tail.js +262 -0
  283. package/dist/src/lib/function-log-tail.js.map +1 -0
  284. package/dist/src/lib/function-run.d.ts +289 -0
  285. package/dist/src/lib/function-run.js +389 -0
  286. package/dist/src/lib/function-run.js.map +1 -0
  287. package/dist/src/lib/function-schema-codegen.d.ts +143 -0
  288. package/dist/src/lib/function-schema-codegen.js +420 -0
  289. package/dist/src/lib/function-schema-codegen.js.map +1 -0
  290. package/dist/src/lib/function-sync.d.ts +459 -0
  291. package/dist/src/lib/function-sync.js +1258 -0
  292. package/dist/src/lib/function-sync.js.map +1 -0
  293. package/dist/src/lib/function-trigger-listing.d.ts +27 -0
  294. package/dist/src/lib/function-trigger-listing.js +70 -0
  295. package/dist/src/lib/function-trigger-listing.js.map +1 -0
  296. package/dist/src/lib/function-typecheck.d.ts +86 -0
  297. package/dist/src/lib/function-typecheck.js +370 -0
  298. package/dist/src/lib/function-typecheck.js.map +1 -0
  299. package/dist/src/lib/function-versions.d.ts +122 -0
  300. package/dist/src/lib/function-versions.js +182 -0
  301. package/dist/src/lib/function-versions.js.map +1 -0
  302. package/dist/src/lib/generated-allowlist.d.ts +28 -0
  303. package/dist/src/lib/generated-allowlist.js +281 -0
  304. package/dist/src/lib/generated-allowlist.js.map +1 -0
  305. package/dist/src/lib/generated-config-surfaces.d.ts +2936 -0
  306. package/dist/src/lib/generated-config-surfaces.js +10569 -0
  307. package/dist/src/lib/generated-config-surfaces.js.map +1 -0
  308. package/dist/src/lib/generated-sdk-types.d.ts +12 -0
  309. package/dist/src/lib/generated-sdk-types.js +13 -0
  310. package/dist/src/lib/generated-sdk-types.js.map +1 -0
  311. package/dist/src/lib/generated-template-lint.d.ts +212 -0
  312. package/dist/src/lib/generated-template-lint.js +624 -0
  313. package/dist/src/lib/generated-template-lint.js.map +1 -0
  314. package/dist/src/lib/init-adopt.d.ts +16 -0
  315. package/dist/src/lib/init-adopt.js +34 -0
  316. package/dist/src/lib/init-adopt.js.map +1 -0
  317. package/dist/src/lib/init-assets.d.ts +39 -0
  318. package/dist/src/lib/init-assets.js +97 -0
  319. package/dist/src/lib/init-assets.js.map +1 -0
  320. package/dist/src/lib/init-client-platforms.d.ts +14 -0
  321. package/dist/src/lib/init-client-platforms.js +71 -0
  322. package/dist/src/lib/init-client-platforms.js.map +1 -0
  323. package/dist/src/lib/init-config.d.ts +98 -0
  324. package/dist/src/lib/init-config.js +186 -0
  325. package/dist/src/lib/init-config.js.map +1 -0
  326. package/dist/src/lib/init-email-redirect-uris.d.ts +37 -0
  327. package/dist/src/lib/init-email-redirect-uris.js +46 -0
  328. package/dist/src/lib/init-email-redirect-uris.js.map +1 -0
  329. package/dist/src/lib/init-ios-links.d.ts +91 -0
  330. package/dist/src/lib/init-ios-links.js +219 -0
  331. package/dist/src/lib/init-ios-links.js.map +1 -0
  332. package/dist/src/lib/init-plan.d.ts +80 -0
  333. package/dist/src/lib/init-plan.js +95 -0
  334. package/dist/src/lib/init-plan.js.map +1 -0
  335. package/dist/src/lib/init-production-env.d.ts +48 -0
  336. package/dist/src/lib/init-production-env.js +59 -0
  337. package/dist/src/lib/init-production-env.js.map +1 -0
  338. package/dist/src/lib/init-schema.d.ts +74 -0
  339. package/dist/src/lib/init-schema.js +358 -0
  340. package/dist/src/lib/init-schema.js.map +1 -0
  341. package/dist/src/lib/init-xcode.d.ts +34 -0
  342. package/dist/src/lib/init-xcode.js +138 -0
  343. package/dist/src/lib/init-xcode.js.map +1 -0
  344. package/dist/src/lib/integration-request-config.d.ts +30 -0
  345. package/dist/src/lib/integration-request-config.js +145 -0
  346. package/dist/src/lib/integration-request-config.js.map +1 -0
  347. package/dist/src/lib/integration-selector.d.ts +42 -0
  348. package/dist/src/lib/integration-selector.js +46 -0
  349. package/dist/src/lib/integration-selector.js.map +1 -0
  350. package/dist/src/lib/ios-app-id.d.ts +34 -0
  351. package/dist/src/lib/ios-app-id.js +69 -0
  352. package/dist/src/lib/ios-app-id.js.map +1 -0
  353. package/dist/src/lib/list-options.d.ts +68 -0
  354. package/dist/src/lib/list-options.js +89 -0
  355. package/dist/src/lib/list-options.js.map +1 -0
  356. package/dist/src/lib/local-state.d.ts +55 -0
  357. package/dist/src/lib/local-state.js +167 -0
  358. package/dist/src/lib/local-state.js.map +1 -0
  359. package/dist/src/lib/local-test-cases.d.ts +63 -0
  360. package/dist/src/lib/local-test-cases.js +136 -0
  361. package/dist/src/lib/local-test-cases.js.map +1 -0
  362. package/dist/src/lib/log-inspection.d.ts +715 -0
  363. package/dist/src/lib/log-inspection.js +816 -0
  364. package/dist/src/lib/log-inspection.js.map +1 -0
  365. package/dist/src/lib/logout-admin-session.d.ts +33 -0
  366. package/dist/src/lib/logout-admin-session.js +70 -0
  367. package/dist/src/lib/logout-admin-session.js.map +1 -0
  368. package/dist/src/lib/migration-nag.d.ts +49 -0
  369. package/dist/src/lib/migration-nag.js +163 -0
  370. package/dist/src/lib/migration-nag.js.map +1 -0
  371. package/dist/src/lib/object-status-filter.d.ts +22 -0
  372. package/dist/src/lib/object-status-filter.js +45 -0
  373. package/dist/src/lib/object-status-filter.js.map +1 -0
  374. package/dist/src/lib/output.d.ts +124 -0
  375. package/dist/src/lib/output.js +219 -8
  376. package/dist/src/lib/output.js.map +1 -1
  377. package/dist/src/lib/package-manager.d.ts +140 -0
  378. package/dist/src/lib/package-manager.js +305 -0
  379. package/dist/src/lib/package-manager.js.map +1 -0
  380. package/dist/src/lib/paginate.d.ts +98 -0
  381. package/dist/src/lib/paginate.js +112 -0
  382. package/dist/src/lib/paginate.js.map +1 -0
  383. package/dist/src/lib/platform-owned.d.ts +63 -0
  384. package/dist/src/lib/platform-owned.js +85 -0
  385. package/dist/src/lib/platform-owned.js.map +1 -0
  386. package/dist/src/lib/project-config.d.ts +122 -0
  387. package/dist/src/lib/project-config.js +244 -0
  388. package/dist/src/lib/project-config.js.map +1 -0
  389. package/dist/src/lib/prompt-cost-format.d.ts +11 -0
  390. package/dist/src/lib/prompt-cost-format.js +41 -0
  391. package/dist/src/lib/prompt-cost-format.js.map +1 -0
  392. package/dist/src/lib/prompt-schema-codegen.d.ts +147 -0
  393. package/dist/src/lib/prompt-schema-codegen.js +462 -0
  394. package/dist/src/lib/prompt-schema-codegen.js.map +1 -0
  395. package/dist/src/lib/query-operators.d.ts +43 -0
  396. package/dist/src/lib/query-operators.js +80 -0
  397. package/dist/src/lib/query-operators.js.map +1 -0
  398. package/dist/src/lib/record-filter.d.ts +18 -0
  399. package/dist/src/lib/record-filter.js +55 -0
  400. package/dist/src/lib/record-filter.js.map +1 -0
  401. package/dist/src/lib/refresh-admin-credentials.d.ts +73 -0
  402. package/dist/src/lib/refresh-admin-credentials.js +123 -0
  403. package/dist/src/lib/refresh-admin-credentials.js.map +1 -0
  404. package/dist/src/lib/resolve-init-dev-port.d.ts +56 -0
  405. package/dist/src/lib/resolve-init-dev-port.js +55 -0
  406. package/dist/src/lib/resolve-init-dev-port.js.map +1 -0
  407. package/dist/src/lib/resolve-init-server.d.ts +64 -0
  408. package/dist/src/lib/resolve-init-server.js +77 -0
  409. package/dist/src/lib/resolve-init-server.js.map +1 -0
  410. package/dist/src/lib/resolve-owner.d.ts +19 -0
  411. package/dist/src/lib/resolve-owner.js +20 -0
  412. package/dist/src/lib/resolve-owner.js.map +1 -0
  413. package/dist/src/lib/resolve-platform.d.ts +74 -0
  414. package/dist/src/lib/resolve-platform.js +105 -0
  415. package/dist/src/lib/resolve-platform.js.map +1 -0
  416. package/dist/src/lib/run-status.d.ts +19 -0
  417. package/dist/src/lib/run-status.generated.d.ts +39 -0
  418. package/dist/src/lib/run-status.generated.js +66 -0
  419. package/dist/src/lib/run-status.generated.js.map +1 -0
  420. package/dist/src/lib/run-status.js +19 -0
  421. package/dist/src/lib/run-status.js.map +1 -0
  422. package/dist/src/lib/server-text-normalization.d.ts +51 -0
  423. package/dist/src/lib/server-text-normalization.js +90 -0
  424. package/dist/src/lib/server-text-normalization.js.map +1 -0
  425. package/dist/src/lib/server-url.d.ts +22 -0
  426. package/dist/src/lib/server-url.js +33 -0
  427. package/dist/src/lib/server-url.js.map +1 -0
  428. package/dist/src/lib/signing-secret-status.d.ts +81 -0
  429. package/dist/src/lib/signing-secret-status.js +116 -0
  430. package/dist/src/lib/signing-secret-status.js.map +1 -0
  431. package/dist/src/lib/skill-installer.d.ts +71 -0
  432. package/dist/src/lib/skill-installer.js +441 -0
  433. package/dist/src/lib/skill-installer.js.map +1 -0
  434. package/dist/src/lib/snapshot-audit-source.d.ts +45 -0
  435. package/dist/src/lib/snapshot-audit-source.js +58 -0
  436. package/dist/src/lib/snapshot-audit-source.js.map +1 -0
  437. package/dist/src/lib/snapshot-audit-store.d.ts +52 -0
  438. package/dist/src/lib/snapshot-audit-store.js +196 -0
  439. package/dist/src/lib/snapshot-audit-store.js.map +1 -0
  440. package/dist/src/lib/snapshot-audit.d.ts +207 -0
  441. package/dist/src/lib/snapshot-audit.js +431 -0
  442. package/dist/src/lib/snapshot-audit.js.map +1 -0
  443. package/dist/src/lib/snapshot-build-rows.d.ts +60 -0
  444. package/dist/src/lib/snapshot-build-rows.js +87 -0
  445. package/dist/src/lib/snapshot-build-rows.js.map +1 -0
  446. package/dist/src/lib/snapshot-build.d.ts +50 -0
  447. package/dist/src/lib/snapshot-build.js +111 -0
  448. package/dist/src/lib/snapshot-build.js.map +1 -0
  449. package/dist/src/lib/snapshot-manifest-layout.d.ts +61 -0
  450. package/dist/src/lib/snapshot-manifest-layout.js +70 -0
  451. package/dist/src/lib/snapshot-manifest-layout.js.map +1 -0
  452. package/dist/src/lib/snapshots.d.ts +98 -0
  453. package/dist/src/lib/snapshots.js +294 -0
  454. package/dist/src/lib/snapshots.js.map +1 -0
  455. package/dist/src/lib/step-run-table.d.ts +43 -0
  456. package/dist/src/lib/step-run-table.js +129 -0
  457. package/dist/src/lib/step-run-table.js.map +1 -0
  458. package/dist/src/lib/storage-pending-retry.d.ts +26 -0
  459. package/dist/src/lib/storage-pending-retry.js +42 -0
  460. package/dist/src/lib/storage-pending-retry.js.map +1 -0
  461. package/dist/src/lib/swift-codegen/agentGenerator.d.ts +42 -0
  462. package/dist/src/lib/swift-codegen/agentGenerator.js +118 -0
  463. package/dist/src/lib/swift-codegen/agentGenerator.js.map +1 -0
  464. package/dist/src/lib/swift-codegen/banners.d.ts +24 -0
  465. package/dist/src/lib/swift-codegen/banners.js +25 -0
  466. package/dist/src/lib/swift-codegen/banners.js.map +1 -0
  467. package/dist/src/lib/swift-codegen/dbGenerator.d.ts +113 -0
  468. package/dist/src/lib/swift-codegen/dbGenerator.js +926 -0
  469. package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -0
  470. package/dist/src/lib/swift-codegen/dbSwiftTypes.d.ts +42 -0
  471. package/dist/src/lib/swift-codegen/dbSwiftTypes.js +100 -0
  472. package/dist/src/lib/swift-codegen/dbSwiftTypes.js.map +1 -0
  473. package/dist/src/lib/swift-codegen/functionGenerator.d.ts +139 -0
  474. package/dist/src/lib/swift-codegen/functionGenerator.js +462 -0
  475. package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -0
  476. package/dist/src/lib/swift-codegen/generator.d.ts +100 -0
  477. package/dist/src/lib/swift-codegen/generator.js +457 -0
  478. package/dist/src/lib/swift-codegen/generator.js.map +1 -0
  479. package/dist/src/lib/swift-codegen/schemaToSwift.d.ts +87 -0
  480. package/dist/src/lib/swift-codegen/schemaToSwift.js +661 -0
  481. package/dist/src/lib/swift-codegen/schemaToSwift.js.map +1 -0
  482. package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +94 -0
  483. package/dist/src/lib/swift-codegen/siblingSymbols.js +155 -0
  484. package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -0
  485. package/dist/src/lib/swift-codegen/swiftNaming.d.ts +85 -0
  486. package/dist/src/lib/swift-codegen/swiftNaming.js +198 -0
  487. package/dist/src/lib/swift-codegen/swiftNaming.js.map +1 -0
  488. package/dist/src/lib/sync-dir-selector.d.ts +21 -0
  489. package/dist/src/lib/sync-dir-selector.js +30 -0
  490. package/dist/src/lib/sync-dir-selector.js.map +1 -0
  491. package/dist/src/lib/sync-paths.d.ts +128 -0
  492. package/dist/src/lib/sync-paths.js +195 -0
  493. package/dist/src/lib/sync-paths.js.map +1 -0
  494. package/dist/src/lib/sync-resource-types.d.ts +563 -0
  495. package/dist/src/lib/sync-resource-types.js +1073 -0
  496. package/dist/src/lib/sync-resource-types.js.map +1 -0
  497. package/dist/src/lib/sync-selectors.d.ts +138 -0
  498. package/dist/src/lib/sync-selectors.js +289 -0
  499. package/dist/src/lib/sync-selectors.js.map +1 -0
  500. package/dist/src/lib/template.d.ts +170 -0
  501. package/dist/src/lib/template.js +484 -68
  502. package/dist/src/lib/template.js.map +1 -1
  503. package/dist/src/lib/test-case-file-names.d.ts +40 -0
  504. package/dist/src/lib/test-case-file-names.js +91 -0
  505. package/dist/src/lib/test-case-file-names.js.map +1 -0
  506. package/dist/src/lib/test-case-keys.d.ts +29 -0
  507. package/dist/src/lib/test-case-keys.js +55 -0
  508. package/dist/src/lib/test-case-keys.js.map +1 -0
  509. package/dist/src/lib/test-case-variables.d.ts +29 -0
  510. package/dist/src/lib/test-case-variables.js +71 -0
  511. package/dist/src/lib/test-case-variables.js.map +1 -0
  512. package/dist/src/lib/token-inject.d.ts +56 -0
  513. package/dist/src/lib/token-inject.js +204 -0
  514. package/dist/src/lib/token-inject.js.map +1 -0
  515. package/dist/src/lib/toml-database-config.d.ts +123 -0
  516. package/dist/src/lib/toml-database-config.js +544 -0
  517. package/dist/src/lib/toml-database-config.js.map +1 -0
  518. package/dist/src/lib/toml-metadata-config.d.ts +151 -0
  519. package/dist/src/lib/toml-metadata-config.js +476 -0
  520. package/dist/src/lib/toml-metadata-config.js.map +1 -0
  521. package/dist/src/lib/toml-native-form.d.ts +46 -0
  522. package/dist/src/lib/toml-native-form.js +78 -0
  523. package/dist/src/lib/toml-native-form.js.map +1 -0
  524. package/dist/src/lib/toml-params-validator.d.ts +129 -0
  525. package/dist/src/lib/toml-params-validator.js +298 -0
  526. package/dist/src/lib/toml-params-validator.js.map +1 -0
  527. package/dist/src/lib/toml-scalar-edit.d.ts +43 -0
  528. package/dist/src/lib/toml-scalar-edit.js +283 -0
  529. package/dist/src/lib/toml-scalar-edit.js.map +1 -0
  530. package/dist/src/lib/user-selector.d.ts +24 -0
  531. package/dist/src/lib/user-selector.js +33 -0
  532. package/dist/src/lib/user-selector.js.map +1 -0
  533. package/dist/src/lib/version-check.d.ts +35 -0
  534. package/dist/src/lib/version-check.js +241 -0
  535. package/dist/src/lib/version-check.js.map +1 -0
  536. package/dist/src/lib/watch.d.ts +121 -0
  537. package/dist/src/lib/watch.js +169 -0
  538. package/dist/src/lib/watch.js.map +1 -0
  539. package/dist/src/lib/web-url.d.ts +40 -0
  540. package/dist/src/lib/web-url.js +76 -0
  541. package/dist/src/lib/web-url.js.map +1 -0
  542. package/dist/src/lib/webhook-deliver.d.ts +209 -0
  543. package/dist/src/lib/webhook-deliver.js +519 -0
  544. package/dist/src/lib/webhook-deliver.js.map +1 -0
  545. package/dist/src/lib/workflow-apply.d.ts +110 -0
  546. package/dist/src/lib/workflow-apply.js +164 -0
  547. package/dist/src/lib/workflow-apply.js.map +1 -0
  548. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.d.ts +129 -0
  549. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js +269 -0
  550. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js.map +1 -0
  551. package/dist/src/lib/workflow-codegen/generator.d.ts +96 -0
  552. package/dist/src/lib/workflow-codegen/generator.js +361 -0
  553. package/dist/src/lib/workflow-codegen/generator.js.map +1 -0
  554. package/dist/src/lib/workflow-codegen/invokerIR.d.ts +94 -0
  555. package/dist/src/lib/workflow-codegen/invokerIR.js +76 -0
  556. package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -0
  557. package/dist/src/lib/workflow-codegen/naming.d.ts +33 -0
  558. package/dist/src/lib/workflow-codegen/naming.js +81 -0
  559. package/dist/src/lib/workflow-codegen/naming.js.map +1 -0
  560. package/dist/src/lib/workflow-codegen/schemaToTs.d.ts +80 -0
  561. package/dist/src/lib/workflow-codegen/schemaToTs.js +303 -0
  562. package/dist/src/lib/workflow-codegen/schemaToTs.js.map +1 -0
  563. package/dist/src/lib/workflow-config-apply.d.ts +70 -0
  564. package/dist/src/lib/workflow-config-apply.js +137 -0
  565. package/dist/src/lib/workflow-config-apply.js.map +1 -0
  566. package/dist/src/lib/workflow-config-sidecar.d.ts +63 -0
  567. package/dist/src/lib/workflow-config-sidecar.js +96 -0
  568. package/dist/src/lib/workflow-config-sidecar.js.map +1 -0
  569. package/dist/src/lib/workflow-defaults.d.ts +29 -0
  570. package/dist/src/lib/workflow-defaults.js +41 -0
  571. package/dist/src/lib/workflow-defaults.js.map +1 -0
  572. package/dist/src/lib/workflow-fragments.d.ts +64 -0
  573. package/dist/src/lib/workflow-fragments.js +342 -0
  574. package/dist/src/lib/workflow-fragments.js.map +1 -0
  575. package/dist/src/lib/workflow-include-preserve.d.ts +76 -0
  576. package/dist/src/lib/workflow-include-preserve.js +286 -0
  577. package/dist/src/lib/workflow-include-preserve.js.map +1 -0
  578. package/dist/src/lib/workflow-payload.d.ts +98 -0
  579. package/dist/src/lib/workflow-payload.js +178 -0
  580. package/dist/src/lib/workflow-payload.js.map +1 -0
  581. package/dist/src/lib/workflow-toml-validator.d.ts +211 -0
  582. package/dist/src/lib/workflow-toml-validator.js +770 -0
  583. package/dist/src/lib/workflow-toml-validator.js.map +1 -0
  584. package/dist/src/lib/workflow-usage.d.ts +196 -0
  585. package/dist/src/lib/workflow-usage.js +310 -0
  586. package/dist/src/lib/workflow-usage.js.map +1 -0
  587. package/dist/src/types/index.d.ts +591 -0
  588. package/dist/src/validators.d.ts +65 -0
  589. package/dist/src/validators.js +64 -0
  590. package/dist/src/validators.js.map +1 -0
  591. package/package.json +34 -9
@@ -0,0 +1,1073 @@
1
+ /**
2
+ * Shared layout metadata for the per-entity resource types `config pull` writes
3
+ * into a sync slot (issue #1659).
4
+ *
5
+ * `config pull` writes one file per server entity and records what it wrote in
6
+ * `.sync-state.json` (`SyncState.entities[<slot>]`). Until #1659 it never
7
+ * removed a local file whose entity had disappeared server-side, so the next
8
+ * `config push` — which treats local TOML as the source of truth — silently
9
+ * re-created the just-deleted entity.
10
+ *
11
+ * This module holds the narrow descriptor the prune pass and `config diff` share:
12
+ * directory, extension, state slot, key→filename derivation, and the sidecar
13
+ * test-directory block type. The pull write loops deliberately stay as they are
14
+ * — this table is layout metadata, not a write framework.
15
+ */
16
+ import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, unlinkSync, } from "fs";
17
+ import { basename, dirname, join, resolve, sep } from "path";
18
+ import { parseConfigToml } from "./config-toml.js";
19
+ const tomlFile = (key) => `${key}.toml`;
20
+ /**
21
+ * Rule sets are keyed on disk (and in sync state) by their name with
22
+ * filesystem-hostile characters replaced. Shared by the pull write loop and the
23
+ * prune pass so the two can never derive a different key for the same rule set.
24
+ */
25
+ export function ruleSetFileKey(ruleSet) {
26
+ return String(ruleSet.name || ruleSet.ruleSetId || "").replace(/[/\\:*?"<>|]/g, "_");
27
+ }
28
+ /**
29
+ * Every per-entity resource type in a sync slot. `app.toml` and `vars.toml` are
30
+ * root singletons, not per-entity directories, so they are not listed here —
31
+ * var removal is already handled within-file by `config push` (`planVarsPush`).
32
+ */
33
+ export const SYNC_RESOURCE_TYPES = [
34
+ {
35
+ label: "integration",
36
+ dir: "integrations",
37
+ ext: ".toml",
38
+ stateSlot: "integrations",
39
+ fileFromKey: tomlFile,
40
+ // #2769 — an integration's test cases live at `integrations/<key>.tests/`,
41
+ // pulled, pushed, diffed and pruned like every other block's.
42
+ testBlockType: "integration",
43
+ // `/admin/api/apps/{id}/integrations` paginates; pull drains it with fetchAll.
44
+ listingComplete: true,
45
+ },
46
+ {
47
+ label: "webhook",
48
+ dir: "webhooks",
49
+ ext: ".toml",
50
+ stateSlot: "webhooks",
51
+ fileFromKey: tomlFile,
52
+ // `/admin/api/apps/{id}/webhooks` paginates; pull drains it with fetchAll.
53
+ listingComplete: true,
54
+ },
55
+ {
56
+ label: "cron-trigger",
57
+ dir: "cron-triggers",
58
+ ext: ".toml",
59
+ stateSlot: "cronTriggers",
60
+ fileFromKey: tomlFile,
61
+ // Server drains `triggersByApp` and filters archived across the full set (#1668).
62
+ listingComplete: true,
63
+ },
64
+ {
65
+ label: "blob-bucket",
66
+ dir: "blob-buckets",
67
+ ext: ".toml",
68
+ stateSlot: "blobBuckets",
69
+ fileFromKey: tomlFile,
70
+ // Server drains `bucketsForApp` across all pages (#1668).
71
+ listingComplete: true,
72
+ },
73
+ {
74
+ label: "prompt",
75
+ dir: "prompts",
76
+ ext: ".toml",
77
+ stateSlot: "prompts",
78
+ fileFromKey: tomlFile,
79
+ testBlockType: "prompt",
80
+ // `/admin/api/apps/{id}/prompts` paginates; pull drains it with fetchAll.
81
+ listingComplete: true,
82
+ },
83
+ {
84
+ label: "workflow",
85
+ dir: "workflows",
86
+ ext: ".toml",
87
+ stateSlot: "workflows",
88
+ fileFromKey: tomlFile,
89
+ testBlockType: "workflow",
90
+ // #2645 — named config bodies live in `workflows/<key>.configs/`.
91
+ namedConfigsDirFromKey: (key) => `${key}.configs`,
92
+ // `/admin/api/apps/{id}/workflows` paginates; pull drains it with fetchAll.
93
+ listingComplete: true,
94
+ },
95
+ {
96
+ // Rhai scripts: `.rhai` bodies, not TOML, with tests at
97
+ // `transforms/<name>.tests/`.
98
+ label: "transform",
99
+ dir: "transforms",
100
+ ext: ".rhai",
101
+ stateSlot: "scripts",
102
+ fileFromKey: (key) => `${key}.rhai`,
103
+ testBlockType: "script",
104
+ // Server drains `scriptsByApp` across all pages (#1668).
105
+ listingComplete: true,
106
+ },
107
+ {
108
+ // #3179 — server functions. `functions/<key>.toml` states the function and
109
+ // names its `entry`; the TypeScript the entry reaches lives beside it in
110
+ // the same tree and is captured into the version envelope on push, so the
111
+ // entity spans more than its one file. Prune removes the TOML; the sources
112
+ // it pulled are tracked in the state slot and removed with it.
113
+ label: "function",
114
+ dir: "functions",
115
+ ext: ".toml",
116
+ stateSlot: "functions",
117
+ fileFromKey: tomlFile,
118
+ // Server drains `functionsByApp` across all pages (#1668's rule, applied at
119
+ // birth in `server-functions-controller.ts`).
120
+ listingComplete: true,
121
+ },
122
+ {
123
+ label: "database-type-config",
124
+ dir: "database-type-configs",
125
+ ext: ".toml",
126
+ stateSlot: "databaseTypes",
127
+ fileFromKey: tomlFile,
128
+ // Server drains `databaseTypeConfigsForApp` across all pages (#1668).
129
+ listingComplete: true,
130
+ },
131
+ {
132
+ // The state key is already the sanitized filename the write loop derives
133
+ // from the rule set's name (`name.replace(/[/\\:*?"<>|]/g, "_")`), so the
134
+ // filename is the key plus the extension.
135
+ label: "rule-set",
136
+ dir: "rule-sets",
137
+ ext: ".toml",
138
+ stateSlot: "ruleSets",
139
+ fileFromKey: tomlFile,
140
+ // Server drains `ruleSetsForApp` across all pages (#1668).
141
+ listingComplete: true,
142
+ },
143
+ {
144
+ label: "group-type-config",
145
+ dir: "group-type-configs",
146
+ ext: ".toml",
147
+ stateSlot: "groupTypeConfigs",
148
+ fileFromKey: tomlFile,
149
+ // Server drains `configsForApp` across all pages (#1668).
150
+ listingComplete: true,
151
+ },
152
+ {
153
+ label: "collection-type-config",
154
+ dir: "collection-type-configs",
155
+ ext: ".toml",
156
+ stateSlot: "collectionTypeConfigs",
157
+ fileFromKey: tomlFile,
158
+ // Server drains `collectionConfigsForApp` across all pages (#1668).
159
+ listingComplete: true,
160
+ },
161
+ {
162
+ // State key is the `resourceType#category` pair; the file on disk is
163
+ // `resourceType.category.toml`.
164
+ label: "metadata-category-config",
165
+ dir: "metadata-category-configs",
166
+ ext: ".toml",
167
+ stateSlot: "metadataCategoryConfigs",
168
+ fileFromKey: (key) => `${key.replace("#", ".")}.toml`,
169
+ // Server drains `metadataCategoriesForApp` across all pages (#1668).
170
+ listingComplete: true,
171
+ },
172
+ {
173
+ label: "email-template",
174
+ dir: "email-templates",
175
+ ext: ".toml",
176
+ stateSlot: "emailTemplates",
177
+ fileFromKey: tomlFile,
178
+ // Server drains `templatesByApp` across all pages (#1668). Independently
179
+ // safe (#1684): the email-template list is the fixed, bounded `EMAIL_TYPES`
180
+ // enum (`src/admin-api.ts`), fully returned every call with no pagination —
181
+ // so there is no version-skew hazard even against a pre-#1668 server.
182
+ // `planPrune` still prunes only MANAGED keys, so hand-authored files that
183
+ // were never pulled are left alone.
184
+ listingComplete: true,
185
+ },
186
+ ];
187
+ /** Generic try/catch wrapper for a server fetch, in the ok/error shape. */
188
+ export async function attemptFetch(fn) {
189
+ try {
190
+ return { ok: true, value: await fn() };
191
+ }
192
+ catch (error) {
193
+ return { ok: false, error };
194
+ }
195
+ }
196
+ /**
197
+ * A filename must stay inside its type's directory. State is read from a file
198
+ * on disk, so a corrupt or hand-edited `.sync-state.json` must never be
199
+ * able to steer an `unlinkSync` outside the sync slot.
200
+ */
201
+ export function isSafeSyncFilename(filename) {
202
+ if (!filename || filename === "." || filename === "..")
203
+ return false;
204
+ if (filename.includes("/") || filename.includes("\\"))
205
+ return false;
206
+ if (filename.includes("\0"))
207
+ return false;
208
+ return true;
209
+ }
210
+ /**
211
+ * Whether a test-case attachment filename may be written to (or read from) the
212
+ * sidecar attachment directory (#2769).
213
+ *
214
+ * Attachment names come from the SERVER: pull writes each one into
215
+ * `<key>.tests/<slug>/`, so a stored name carrying a separator or a `..`
216
+ * segment would place a file outside the sync tree entirely. The rule is the
217
+ * same one state-derived filenames follow, plus a refusal of any leading-dot
218
+ * traversal spelling.
219
+ */
220
+ export function isSafeAttachmentFilename(filename) {
221
+ if (!isSafeSyncFilename(filename))
222
+ return false;
223
+ // `.` and `..` are caught above; `...` and friends are not path traversal but
224
+ // are not names a fixture file needs either.
225
+ if (/^\.+$/.test(filename))
226
+ return false;
227
+ return true;
228
+ }
229
+ /**
230
+ * The absolute path an attachment may be written to, or `null` when the name is
231
+ * unsafe or resolves outside `attachDir` (#2769). Both halves matter: the name
232
+ * check states the rule, and the resolved-path check enforces it against
233
+ * platform quirks the name check cannot anticipate.
234
+ */
235
+ export function attachmentWritePath(attachDir, filename) {
236
+ if (!isSafeAttachmentFilename(filename))
237
+ return null;
238
+ const destination = resolve(attachDir, filename);
239
+ const root = resolve(attachDir);
240
+ if (destination !== join(root, filename))
241
+ return null;
242
+ if (!destination.startsWith(root + sep))
243
+ return null;
244
+ return destination;
245
+ }
246
+ /**
247
+ * Decide which of a type's managed entity keys should have their local file
248
+ * removed, given the prior sync state and the outcome of this pull's fetch.
249
+ *
250
+ * A key is pruned iff it was **managed** (present in the prior
251
+ * `SyncState.entities[<slot>]`, so a previous pull wrote its file) and is
252
+ * **absent from the server keys** this pull fetched. A local file that was
253
+ * never in prior state was hand-authored — it is left alone and reported by
254
+ * `config diff` as new.
255
+ *
256
+ * Pure and side-effect-free (the caller owns the `unlinkSync`) so the safety
257
+ * rules are unit-testable without a live server.
258
+ */
259
+ export function planPrune(input) {
260
+ const { resourceType, priorKeys, presence, enabled } = input;
261
+ // Which managed keys the server did not list. Only meaningful when the fetch
262
+ // succeeded; on a failed fetch we know nothing and must not act.
263
+ const absentKeys = presence.ok
264
+ ? priorKeys.filter((key) => !new Set(presence.serverKeys).has(key))
265
+ : [];
266
+ if (!enabled) {
267
+ return {
268
+ keys: [],
269
+ skipped: true,
270
+ skipReason: "disabled",
271
+ unsafeKeys: [],
272
+ unprunedAbsentKeys: absentKeys,
273
+ };
274
+ }
275
+ if (!presence.ok) {
276
+ return {
277
+ keys: [],
278
+ skipped: true,
279
+ skipReason: "fetch-failed",
280
+ unsafeKeys: [],
281
+ unprunedAbsentKeys: [],
282
+ };
283
+ }
284
+ // A 200 is not a complete list. Deleting a file because a truncated listing
285
+ // omitted its entity is unrecoverable-looking to the operator (the entity is
286
+ // still live, so no later pull rewrites the file), which is worse than
287
+ // leaving a stale file behind. See `listingComplete`.
288
+ if (!resourceType.listingComplete) {
289
+ return {
290
+ keys: [],
291
+ skipped: true,
292
+ skipReason: "listing-incomplete",
293
+ unsafeKeys: [],
294
+ unprunedAbsentKeys: absentKeys,
295
+ };
296
+ }
297
+ const keys = [];
298
+ const unsafeKeys = [];
299
+ for (const key of absentKeys) {
300
+ if (!isSafeSyncFilename(resourceType.fileFromKey(key))) {
301
+ unsafeKeys.push(key);
302
+ continue;
303
+ }
304
+ keys.push(key);
305
+ }
306
+ return { keys, skipped: false, unsafeKeys, unprunedAbsentKeys: [] };
307
+ }
308
+ /**
309
+ * Copy prior state entries for entities the server listed but this pull did not
310
+ * write (e.g. a script with no active config, or an email template whose detail
311
+ * fetch failed). Without this the entity would drop out of the state slot and
312
+ * look hand-authored to the next pull, so its stale file could never be pruned.
313
+ */
314
+ export function preserveUnwrittenEntries(written, prior, serverKeys) {
315
+ if (!prior)
316
+ return written;
317
+ for (const key of serverKeys) {
318
+ if (!(key in written) && key in prior) {
319
+ written[key] = prior[key];
320
+ }
321
+ }
322
+ return written;
323
+ }
324
+ /**
325
+ * Best-effort check for uncommitted git changes at `path` (a file or a
326
+ * directory). Returns false when git isn't available, the path isn't in a
327
+ * repository, or anything else goes wrong — the caller treats "unknown" as
328
+ * "no local edits", so this never blocks on a non-git checkout.
329
+ *
330
+ * `path` is passed as an argument, never interpolated into a shell string:
331
+ * on the prune path its basename derives from a server entity name, and entity
332
+ * names are free-form (a rule set may legitimately be named ``a`b`` or `a$b`).
333
+ * `execFileSync` runs git directly with no shell, so no name can be read as
334
+ * a command.
335
+ *
336
+ * `includeUntracked` decides whether an untracked file counts as a change:
337
+ * - `true` (default) suits "am I about to overwrite something?" — `config revert`
338
+ * would clobber an untracked file just the same.
339
+ * - `false` suits "did the operator edit this file?" — an untracked file is the
340
+ * ordinary state of a pulled file in a sync dir that was never committed, and
341
+ * is not evidence of a local edit.
342
+ */
343
+ export async function hasUncommittedChanges(path, options = {}) {
344
+ const { includeUntracked = true } = options;
345
+ if (!existsSync(path))
346
+ return false;
347
+ try {
348
+ const { execFileSync } = await import("child_process");
349
+ // Run git from the path's own location, not the process cwd: the sync slot
350
+ // can sit in a different repository than the one the CLI was invoked from
351
+ // (or in none at all).
352
+ const cwd = statSync(path).isDirectory() ? path : dirname(path);
353
+ const args = ["status", "--porcelain"];
354
+ if (!includeUntracked)
355
+ args.push("-uno");
356
+ args.push("--", path);
357
+ const out = execFileSync("git", args, {
358
+ cwd,
359
+ stdio: ["ignore", "pipe", "ignore"],
360
+ encoding: "utf-8",
361
+ });
362
+ return out.trim().length > 0;
363
+ }
364
+ catch {
365
+ return false;
366
+ }
367
+ }
368
+ /**
369
+ * The phrase pull and diff both use for an entity that is no longer exported.
370
+ *
371
+ * The plan of record asked for "archived or deleted" on the assumption that
372
+ * archiving drops an entity from the export. It does not: the list endpoints
373
+ * filter by status only when `?status=` is passed and pull never passes it
374
+ * (`listAppPrompts`/`listAppIntegrations` in `src/admin-api.ts`; `fetchAll` in
375
+ * `cli/src/commands/sync.ts`). An archived entity stays in the export and its
376
+ * TOML is rewritten with `status = "archived"`, so it is never pruned. The only
377
+ * way to be absent from the export is to have been deleted.
378
+ */
379
+ export const ABSENT_FROM_EXPORT = "absent from sync export (deleted on server)";
380
+ /** `config diff` status for a local file whose managed entity is gone. */
381
+ export const LOCAL_ONLY_ABSENT = "local only (absent from export)";
382
+ /** `config diff` status for a local file the operator authored themselves. */
383
+ export const LOCAL_ONLY_NEW = "local only";
384
+ /**
385
+ * `config diff` status for a local file `config push` would refuse (#2880).
386
+ *
387
+ * Its own status rather than a hint on a Synced row: the whole point is that
388
+ * the file is NOT pushable, and the counts a caller reads to decide "is there
389
+ * anything to do?" have to say so.
390
+ */
391
+ export const VALIDATION_ERROR = "validation error";
392
+ /**
393
+ * Classify a local file that has no matching server entity (issue #1659).
394
+ *
395
+ * The two cases look identical on disk but mean opposite things. A key a prior
396
+ * pull managed means the entity was deleted server-side and this stale file
397
+ * will RE-create it on the next push. A key never pulled means the operator
398
+ * authored the file and push creating it is the intent.
399
+ */
400
+ export function classifyLocalOnly(priorEntities, slot, key) {
401
+ const managed = (priorEntities?.[slot] ?? {});
402
+ return key in managed ? LOCAL_ONLY_ABSENT : LOCAL_ONLY_NEW;
403
+ }
404
+ /**
405
+ * Whether `target` is still inside `root` once symlinks are resolved.
406
+ *
407
+ * A lexical prefix check answers a question about strings; this one answers the
408
+ * question the `unlinkSync` actually asks. `functions/shared` pointing at an
409
+ * external directory passes the string test and deletes someone else's file.
410
+ */
411
+ function realPathStaysInside(root, target) {
412
+ try {
413
+ if (lstatSync(target).isSymbolicLink())
414
+ return false;
415
+ const realRoot = realpathSync(root);
416
+ const real = realpathSync(target);
417
+ return real.startsWith(realRoot + sep);
418
+ }
419
+ catch {
420
+ return false;
421
+ }
422
+ }
423
+ /**
424
+ * Remove the local files for `keys` (as decided by `planPrune`), along with any
425
+ * sidecar `<key>.tests/` directory.
426
+ *
427
+ * A file with uncommitted git edits is kept and reported instead: the operator
428
+ * may be mid-way through authoring a change they meant to push — and for an
429
+ * entity that owns source files beyond its TOML (#3179), an edit to any of them
430
+ * keeps the whole entity. Everything removed here is recoverable from the
431
+ * pre-pull snapshot via `config revert`.
432
+ */
433
+ export async function applyPrune(options) {
434
+ const { configDir, resourceType, keys, testCaseEntities, extraFilesForKey, retainedFiles, logger, } = options;
435
+ const log = logger ?? (() => { });
436
+ const result = {
437
+ prunedKeys: [],
438
+ removed: [],
439
+ keptWithLocalEdits: [],
440
+ removedTestCaseKeys: [],
441
+ };
442
+ for (const key of keys) {
443
+ const filename = resourceType.fileFromKey(key);
444
+ const relativePath = `${resourceType.dir}/${filename}`;
445
+ const filePath = join(configDir, resourceType.dir, filename);
446
+ if (!existsSync(filePath))
447
+ continue;
448
+ // #3179 — the entity's own source files, recorded by the pull that wrote
449
+ // them. Each path is re-confined here: it came out of a JSON file on disk,
450
+ // so a corrupt or hand-edited sync state must never steer an `unlinkSync`
451
+ // out of the tree — lexically first, then against the real path, because a
452
+ // symlinked directory inside the tree resolves outside it.
453
+ const extras = [];
454
+ for (const extra of extraFilesForKey?.(key) ?? []) {
455
+ if (retainedFiles?.has(extra)) {
456
+ log(` Kept ${extra} — another entity still uses it`);
457
+ continue;
458
+ }
459
+ const target = resolve(configDir, extra);
460
+ const root = resolve(configDir);
461
+ if (target === root || !target.startsWith(root + sep)) {
462
+ log(` Skipped removing '${extra}' — it resolves outside the config tree`);
463
+ continue;
464
+ }
465
+ if (!existsSync(target))
466
+ continue;
467
+ if (!realPathStaysInside(root, target)) {
468
+ log(` Skipped removing '${extra}' — a symlink takes it outside the config tree`);
469
+ continue;
470
+ }
471
+ extras.push({ relative: extra, absolute: target });
472
+ }
473
+ // Untracked does not mean edited: in a sync dir that was never committed
474
+ // (or in no repository at all) every pulled file is untracked, and counting
475
+ // that as a local edit would keep every stale file and leave #1659 unfixed.
476
+ //
477
+ // The entity's source files are asked the same question BEFORE anything is
478
+ // removed (#3179): a half-pruned function — TOML gone, edited TypeScript
479
+ // kept — is neither state the operator can act on, so an edit anywhere in
480
+ // the entity keeps all of it.
481
+ const edited = [];
482
+ if (await hasUncommittedChanges(filePath, { includeUntracked: false })) {
483
+ edited.push(relativePath);
484
+ }
485
+ for (const extra of extras) {
486
+ if (await hasUncommittedChanges(extra.absolute, { includeUntracked: false })) {
487
+ edited.push(extra.relative);
488
+ }
489
+ }
490
+ if (edited.length > 0) {
491
+ result.keptWithLocalEdits.push(key);
492
+ log(` Kept ${relativePath} — ${ABSENT_FROM_EXPORT} but ${edited.join(", ")} ` +
493
+ "has uncommitted local edits; remove it yourself or re-create the entity on the server");
494
+ continue;
495
+ }
496
+ unlinkSync(filePath);
497
+ result.prunedKeys.push(key);
498
+ result.removed.push(relativePath);
499
+ log(` Removed ${relativePath} — ${ABSENT_FROM_EXPORT}`);
500
+ for (const extra of extras) {
501
+ if (!existsSync(extra.absolute))
502
+ continue;
503
+ unlinkSync(extra.absolute);
504
+ result.removed.push(extra.relative);
505
+ log(` Removed ${extra.relative} — ${ABSENT_FROM_EXPORT}`);
506
+ }
507
+ // #2645 — the entity's named config bodies go with it. They describe
508
+ // configurations of a block the server no longer has, and leaving them
509
+ // would have the next push re-create the block from its own leftovers.
510
+ const configsDirName = resourceType.namedConfigsDirFromKey?.(key);
511
+ if (configsDirName) {
512
+ const configsDir = join(configDir, resourceType.dir, configsDirName);
513
+ if (existsSync(configsDir) && isSafeSyncFilename(configsDirName)) {
514
+ rmSync(configsDir, { recursive: true, force: true });
515
+ result.removed.push(`${resourceType.dir}/${configsDirName}`);
516
+ log(` Removed ${resourceType.dir}/${configsDirName} — ${ABSENT_FROM_EXPORT}`);
517
+ }
518
+ }
519
+ if (!resourceType.testBlockType)
520
+ continue;
521
+ // Sidecar tests: `<key>.tests/` holds the block's test-case TOMLs and their
522
+ // attachment directories. With the block gone there is nothing to run them
523
+ // against, so they go too — along with their sync-state records.
524
+ const testsDirName = `${key}.tests`;
525
+ const testsDir = join(configDir, resourceType.dir, testsDirName);
526
+ if (existsSync(testsDir) && isSafeSyncFilename(testsDirName)) {
527
+ rmSync(testsDir, { recursive: true, force: true });
528
+ result.removed.push(`${resourceType.dir}/${testsDirName}`);
529
+ log(` Removed ${resourceType.dir}/${testsDirName} — ${ABSENT_FROM_EXPORT}`);
530
+ }
531
+ if (testCaseEntities) {
532
+ for (const [stateKey, entry] of Object.entries(testCaseEntities)) {
533
+ if (entry?.blockType === resourceType.testBlockType &&
534
+ entry?.blockKey === key) {
535
+ delete testCaseEntities[stateKey];
536
+ result.removedTestCaseKeys.push(stateKey);
537
+ }
538
+ }
539
+ }
540
+ }
541
+ return result;
542
+ }
543
+ /** Read `id` from an id-addressed slot's state entry. */
544
+ export const addressById = (_key, entry) => entry && typeof entry.id === "string" && entry.id ? entry.id : undefined;
545
+ /** Key-addressed slots are addressed by the state key itself. */
546
+ export const addressByKey = (key) => key || undefined;
547
+ /**
548
+ * Live modified timestamp used by the drift check. Most admin/app detail
549
+ * endpoints serialize `modifiedAt` at the top level (the same field `config pull`
550
+ * records into state), with `updatedAt` as a defensive fallback. Two detail
551
+ * endpoints nest the entity under a wrapper key and need their own extractors
552
+ * below (`workflowPruneModifiedAt`, `emailTemplatePruneModifiedAt`) so the
553
+ * drift check reads the real timestamp instead of `undefined` — a top-level
554
+ * read there always looks like drift and blocks `--prune` without `--force`.
555
+ */
556
+ export const pruneModifiedAt = (detail) => (detail && (detail.modifiedAt ?? detail.updatedAt)) || undefined;
557
+ /**
558
+ * Workflow detail-read extractor. `getWorkflow` (server `getAppWorkflow`,
559
+ * src/admin-api.ts) returns `{ workflow, draft, revisions, configs }` — the
560
+ * entity and its `modifiedAt` are nested under `workflow`, unlike every other
561
+ * detail endpoint. Read there, not the top level.
562
+ */
563
+ export const workflowPruneModifiedAt = (detail) => pruneModifiedAt(detail && detail.workflow);
564
+ /**
565
+ * Email-template detail-read extractor. `getEmailTemplate` returns
566
+ * `{ emailType, label, hasOverride, override, default, variables }`; the
567
+ * editable copy `sync` manages — and its `modifiedAt` — live under `override`
568
+ * (null when there is no override). `config pull` likewise records
569
+ * `template.override?.modifiedAt`, so the drift check must read the same path.
570
+ */
571
+ export const emailTemplatePruneModifiedAt = (detail) => pruneModifiedAt(detail && detail.override);
572
+ /**
573
+ * Read a declared identity field (`[<section>].<field>`) from a managed TOML
574
+ * file. Returns `undefined` when the field is absent, empty, or the file cannot
575
+ * be parsed — callers fall back to the filename-derived key, matching the
576
+ * `<field> || basename(file)` convention every `config push` parser uses.
577
+ *
578
+ * Parsed via `parseConfigToml` (not `parseTomlFile`): the identity fields
579
+ * pruning reads are author-declared top-level values, unaffected by the
580
+ * workflow-fragment expander, so this avoids depending on `commands/sync.ts`
581
+ * (which would be a circular import) while producing the same key push does.
582
+ */
583
+ function declaredTomlKey(filePath, section, field) {
584
+ let parsed;
585
+ try {
586
+ parsed = parseConfigToml(readFileSync(filePath, "utf-8"));
587
+ }
588
+ catch {
589
+ return undefined;
590
+ }
591
+ const value = parsed && parsed[section] && parsed[section][field];
592
+ return typeof value === "string" && value ? value : undefined;
593
+ }
594
+ /**
595
+ * Build a `keyFromFile` for a TOML type whose entity key is declared at
596
+ * `[<section>].<field>`, falling back to the file basename (the exact key the
597
+ * matching `config push` loop derives). This is what lets prune recognize an
598
+ * entity declared inside a **renamed** file and keep it out of the prune set,
599
+ * instead of deleting a remote entity that push just updated (issue #1655).
600
+ */
601
+ function tomlDeclaredKeyFromFile(section, field) {
602
+ return (filePath) => declaredTomlKey(filePath, section, field) ?? basename(filePath, ".toml");
603
+ }
604
+ /**
605
+ * `keyFromFile` for types keyed purely by filename — Rhai transforms (the
606
+ * script `name` is the `.rhai` basename) and rule-sets (the state key is the
607
+ * sanitized basename). These have no in-file identity, so a rename genuinely
608
+ * changes the entity and the basename is the correct key.
609
+ */
610
+ function basenameKeyFromFile(ext) {
611
+ return (filePath) => basename(filePath, ext);
612
+ }
613
+ /**
614
+ * Split a metadata-category key into the `resourceType`/`category` pair it
615
+ * names, or `null` when it is not exactly two non-empty components joined by a
616
+ * single `#`.
617
+ *
618
+ * Load-bearing for prune (#2762). Prune point-reads a candidate BEFORE deciding
619
+ * what to do with it, and only a missing address short-circuits that fetch — so
620
+ * a corrupt state key that still produced an address would be read with
621
+ * `undefined` in the request path, and the resulting 404 would be classified
622
+ * "already gone", silently dropping the state entry. Failing here instead
623
+ * routes such a key to the `no-id` skip: no client call, state retained.
624
+ */
625
+ export function parseMetadataCategoryKey(key) {
626
+ if (typeof key !== "string")
627
+ return null;
628
+ const parts = key.split("#");
629
+ if (parts.length !== 2)
630
+ return null;
631
+ const [resourceType, category] = parts;
632
+ if (!resourceType || !category)
633
+ return null;
634
+ return { resourceType, category };
635
+ }
636
+ /** The same parse, for the spec's read/delete — safe when called standalone. */
637
+ function requireMetadataCategoryKey(key) {
638
+ const parsed = parseMetadataCategoryKey(key);
639
+ if (!parsed) {
640
+ throw new Error(`'${key}' is not a metadata category key — expected <resourceType>#<category>`);
641
+ }
642
+ return parsed;
643
+ }
644
+ /**
645
+ * `keyFromFile` for metadata-category-configs, whose key is the
646
+ * `resourceType#category` pair. Push reads `[metadataCategoryConfig]`
647
+ * `resourceType`/`category` and falls back to the filename
648
+ * (`resourceType.category.toml`, split on the first `.`) for EACH component it
649
+ * did not find — mirror that per-component fallback exactly. Requiring both
650
+ * fields before honouring the declaration would key a half-declared file by its
651
+ * filename while push keyed it by the mix, and prune would then read the
652
+ * pushed key as undeclared and delete a definition a local file still owns.
653
+ */
654
+ function metadataCategoryKeyFromFile(filePath) {
655
+ let resourceType = declaredTomlKey(filePath, "metadataCategoryConfig", "resourceType");
656
+ let category = declaredTomlKey(filePath, "metadataCategoryConfig", "category");
657
+ const base = basename(filePath, ".toml");
658
+ const dot = base.indexOf(".");
659
+ if (dot > 0) {
660
+ resourceType = resourceType || base.slice(0, dot);
661
+ category = category || base.slice(dot + 1);
662
+ }
663
+ if (resourceType && category)
664
+ return `${resourceType}#${category}`;
665
+ return base;
666
+ }
667
+ function pruneTypeFor(label) {
668
+ const found = SYNC_RESOURCE_TYPES.find((t) => t.label === label);
669
+ if (!found)
670
+ throw new Error(`no sync resource type '${label}'`);
671
+ return found;
672
+ }
673
+ /**
674
+ * Every resource type push-prune can retire. Id-addressed types (integrations,
675
+ * webhooks, cron-triggers, blob-buckets, prompts, workflows, transforms,
676
+ * rule-sets) fetch/delete by the stored id; key-addressed types (database-type-configs,
677
+ * group-type-configs, collection-type-configs, email-templates) by the key.
678
+ *
679
+ * `--force` (the CLI's existing "override baseline drift" flag) only relaxes the
680
+ * drift check — it deliberately does NOT pass a server-side force flag on the
681
+ * delete, so each type's existing 409 dangling-ref guard stays active and a
682
+ * still-referenced entity is reported "blocked", not force-deleted.
683
+ */
684
+ export const PUSH_PRUNE_SPECS = [
685
+ {
686
+ resourceType: pruneTypeFor("integration"),
687
+ keyFromFile: tomlDeclaredKeyFromFile("integration", "key"),
688
+ addressOf: addressById,
689
+ fetchDetail: (c, a, id) => c.getIntegration(a, id),
690
+ modifiedAtOf: pruneModifiedAt,
691
+ // Hard-delete rather than archive (#2803, following #2232's webhook
692
+ // reasoning). `--prune` is the CLI's hard-delete path: the file that
693
+ // declared this integration is gone from the config tree, and an archived
694
+ // row keeps holding its `integrationKey` with no way to reclaim it from
695
+ // `config`.
696
+ deleteEntity: async (c, a, id) => {
697
+ await c.deleteIntegration(a, id, { hard: true });
698
+ },
699
+ },
700
+ {
701
+ resourceType: pruneTypeFor("webhook"),
702
+ keyFromFile: tomlDeclaredKeyFromFile("webhook", "key"),
703
+ addressOf: addressById,
704
+ fetchDetail: (c, a, id) => c.getWebhook(a, id),
705
+ modifiedAtOf: pruneModifiedAt,
706
+ // Hard-delete rather than archive (#2232). Webhooks are the one pruned type with
707
+ // a per-app cap that archived rows still consume, so an archiving prune
708
+ // would burn a slot — and keep holding the `webhookKey` — with no way to
709
+ // reclaim it from `sync`. Enough prune cycles and the app could never
710
+ // create a webhook again. `--prune` is opt-in and confirmed, and the file
711
+ // that declared this webhook is gone from the config dir, so a hard delete
712
+ // is what "retire it" means here. Delivery rows are left behind either way.
713
+ deleteEntity: async (c, a, id) => {
714
+ await c.deleteWebhook(a, id, { hard: true });
715
+ },
716
+ },
717
+ {
718
+ resourceType: pruneTypeFor("cron-trigger"),
719
+ keyFromFile: tomlDeclaredKeyFromFile("cronTrigger", "key"),
720
+ addressOf: addressById,
721
+ fetchDetail: (c, a, id) => c.getCronTrigger(a, id),
722
+ modifiedAtOf: pruneModifiedAt,
723
+ // Hard-delete rather than archive (#2803, following #2232's webhook
724
+ // reasoning). Cron triggers have a per-app cap that archived rows still
725
+ // consume, and an archived row keeps holding its `triggerKey` with no way
726
+ // to reclaim it from `config`. `--prune` is opt-in and confirmed, and the
727
+ // file that declared this trigger is gone from the config tree, so a hard
728
+ // delete is what "retire it" means here.
729
+ deleteEntity: async (c, a, id) => {
730
+ await c.deleteCronTrigger(a, id, { hard: true });
731
+ },
732
+ },
733
+ {
734
+ resourceType: pruneTypeFor("blob-bucket"),
735
+ keyFromFile: tomlDeclaredKeyFromFile("bucket", "key"),
736
+ addressOf: addressById,
737
+ fetchDetail: (c, a, id) => c.getBlobBucket(a, id),
738
+ modifiedAtOf: pruneModifiedAt,
739
+ deleteEntity: async (c, a, id) => {
740
+ await c.deleteBlobBucket(a, id);
741
+ },
742
+ },
743
+ {
744
+ resourceType: pruneTypeFor("prompt"),
745
+ keyFromFile: tomlDeclaredKeyFromFile("prompt", "key"),
746
+ addressOf: addressById,
747
+ fetchDetail: (c, a, id) => c.getPrompt(a, id),
748
+ modifiedAtOf: pruneModifiedAt,
749
+ // Hard-delete rather than archive (#2887, following #2232's webhook
750
+ // reasoning). A plain DELETE archives now, and an archived prompt keeps
751
+ // holding its `promptKey` with no way to reclaim it from `config`. This
752
+ // had to change in the SAME commit that made the plain DELETE soft, or
753
+ // `--prune` would have silently stopped destroying anything.
754
+ deleteEntity: async (c, a, id) => {
755
+ await c.deletePrompt(a, id, true);
756
+ },
757
+ },
758
+ {
759
+ // #3179 — server functions. Hard-delete rather than archive, following the
760
+ // same reasoning as every sibling: an archived function keeps holding its
761
+ // key in the app's SINGLE key namespace, which now blocks not just another
762
+ // function but a workflow, a script or a webhook wanting that key, with no
763
+ // way to reclaim it from `config`. The hard delete is also what destroys
764
+ // the versions' R2 objects, so a pruned function leaves no bundles behind.
765
+ resourceType: pruneTypeFor("function"),
766
+ keyFromFile: tomlDeclaredKeyFromFile("function", "key"),
767
+ addressOf: addressById,
768
+ fetchDetail: (c, a, id) => c.getFunction(a, id),
769
+ modifiedAtOf: pruneModifiedAt,
770
+ deleteEntity: async (c, a, id) => {
771
+ await c.deleteFunction(a, id, { hard: true });
772
+ },
773
+ },
774
+ {
775
+ resourceType: pruneTypeFor("workflow"),
776
+ keyFromFile: tomlDeclaredKeyFromFile("workflow", "key"),
777
+ addressOf: addressById,
778
+ fetchDetail: (c, a, id) => c.getWorkflow(a, id),
779
+ // `getWorkflow` nests the entity (and modifiedAt) under `workflow`.
780
+ modifiedAtOf: workflowPruneModifiedAt,
781
+ // Hard-delete rather than archive (#2887) — same reasoning as the prompt
782
+ // spec above: the file that declared this workflow is gone from the config
783
+ // tree, `--prune` is opt-in and confirmed, and an archived row would go on
784
+ // holding its `workflowKey`.
785
+ deleteEntity: async (c, a, id) => {
786
+ await c.deleteWorkflow(a, id, { hard: true });
787
+ },
788
+ },
789
+ {
790
+ resourceType: pruneTypeFor("transform"),
791
+ // Rhai scripts are keyed by the `.rhai` basename — no in-file identity.
792
+ keyFromFile: basenameKeyFromFile(".rhai"),
793
+ addressOf: addressById,
794
+ fetchDetail: (c, a, id) => c.getScript(a, id),
795
+ modifiedAtOf: pruneModifiedAt,
796
+ deleteEntity: async (c, a, id) => {
797
+ await c.deleteScript(a, id);
798
+ },
799
+ },
800
+ {
801
+ resourceType: pruneTypeFor("rule-set"),
802
+ // Rule sets are keyed by the sanitized basename — no in-file identity.
803
+ keyFromFile: basenameKeyFromFile(".toml"),
804
+ addressOf: addressById,
805
+ fetchDetail: (c, a, id) => c.getRuleSet(a, id),
806
+ modifiedAtOf: pruneModifiedAt,
807
+ deleteEntity: async (c, a, id) => {
808
+ await c.deleteRuleSet(a, id);
809
+ },
810
+ },
811
+ {
812
+ resourceType: pruneTypeFor("database-type-config"),
813
+ keyFromFile: tomlDeclaredKeyFromFile("type", "databaseType"),
814
+ addressOf: addressByKey,
815
+ fetchDetail: (c, a, key) => c.getDatabaseTypeConfig(a, key),
816
+ modifiedAtOf: pruneModifiedAt,
817
+ // No server-side force: the type's 409 dangling-ref guard must stay active.
818
+ deleteEntity: async (c, a, key) => {
819
+ await c.deleteDatabaseTypeConfig(a, key);
820
+ },
821
+ },
822
+ {
823
+ resourceType: pruneTypeFor("group-type-config"),
824
+ keyFromFile: tomlDeclaredKeyFromFile("groupTypeConfig", "groupType"),
825
+ addressOf: addressByKey,
826
+ fetchDetail: (c, a, key) => c.getGroupTypeConfig(a, key),
827
+ modifiedAtOf: pruneModifiedAt,
828
+ deleteEntity: async (c, a, key) => {
829
+ await c.deleteGroupTypeConfig(a, key);
830
+ },
831
+ },
832
+ {
833
+ resourceType: pruneTypeFor("collection-type-config"),
834
+ keyFromFile: tomlDeclaredKeyFromFile("collectionTypeConfig", "collectionType"),
835
+ addressOf: addressByKey,
836
+ fetchDetail: (c, a, key) => c.getCollectionTypeConfig(a, key),
837
+ modifiedAtOf: pruneModifiedAt,
838
+ deleteEntity: async (c, a, key) => {
839
+ await c.deleteCollectionTypeConfig(a, key);
840
+ },
841
+ },
842
+ {
843
+ resourceType: pruneTypeFor("email-template"),
844
+ keyFromFile: tomlDeclaredKeyFromFile("template", "emailType"),
845
+ addressOf: addressByKey,
846
+ fetchDetail: (c, a, key) => c.getEmailTemplate(a, key),
847
+ // `getEmailTemplate` nests the managed override (and modifiedAt) under `override`.
848
+ modifiedAtOf: emailTemplatePruneModifiedAt,
849
+ deleteEntity: async (c, a, key) => {
850
+ await c.deleteEmailTemplate(a, key);
851
+ },
852
+ },
853
+ {
854
+ // The admin-gated config-delete route (#1364) and its client wrapper are
855
+ // shipped, so this type retires exactly like its siblings: delete the file,
856
+ // `config push --prune` (#2762). The interactive `metadata categories delete`
857
+ // verb this replaced is gone.
858
+ resourceType: pruneTypeFor("metadata-category-config"),
859
+ keyFromFile: metadataCategoryKeyFromFile,
860
+ // Key-addressed like the other config types, but the key is a PAIR: one
861
+ // that does not parse has no address at all, which routes the candidate to
862
+ // the `no-id` skip BEFORE the point read (see parseMetadataCategoryKey).
863
+ addressOf: (key) => (parseMetadataCategoryKey(key) ? key : undefined),
864
+ fetchDetail: async (c, a, key) => {
865
+ // The state key is the `resourceType#category` pair; the getter is
866
+ // path-addressed by both.
867
+ const { resourceType, category } = requireMetadataCategoryKey(key);
868
+ return c.getMetadataCategoryConfig(a, resourceType, category);
869
+ },
870
+ modifiedAtOf: pruneModifiedAt,
871
+ deleteEntity: async (c, a, key) => {
872
+ const { resourceType, category } = requireMetadataCategoryKey(key);
873
+ await c.deleteMetadataCategoryConfig(a, resourceType, category);
874
+ },
875
+ // The warning the retired verb's confirm prompt used to carry.
876
+ deleteCaveat: "stored metadata values are NOT deleted and become unreachable — delete values first ('primitive metadata delete') if you need them gone",
877
+ },
878
+ ];
879
+ /**
880
+ * The set of entity keys the local managed files of a type declare — collected
881
+ * the way `config push` derives keys: parse every file in the type's directory
882
+ * and read its declared identity (`<in-file key> || basename`), NOT by assuming
883
+ * the file lives at its canonical `<key>.toml` path.
884
+ *
885
+ * This is the crux of the #1655 destructive-delete fix. `config push` accepts a
886
+ * managed file at a **noncanonical filename** (e.g. `prompts/custom.toml` with
887
+ * `prompt.key = "greeting"`) and still updates the `greeting` entity. Collecting
888
+ * declared keys — rather than checking whether `greeting.toml` exists — means a
889
+ * renamed-but-same-key file keeps its entity out of the prune set, so prune
890
+ * never deletes an entity push just updated.
891
+ */
892
+ export function collectLocalDeclaredKeys(input) {
893
+ const { spec, configDir } = input;
894
+ const dirPath = join(configDir, spec.resourceType.dir);
895
+ const keys = new Set();
896
+ if (!existsSync(dirPath))
897
+ return keys;
898
+ for (const file of readdirSync(dirPath)) {
899
+ if (!file.endsWith(spec.resourceType.ext))
900
+ continue;
901
+ const key = spec.keyFromFile(join(dirPath, file));
902
+ if (key)
903
+ keys.add(key);
904
+ }
905
+ return keys;
906
+ }
907
+ /**
908
+ * A managed entity whose local declaration is gone — a candidate for
909
+ * server-side deletion. Pure and side-effect-free: computed from the prior sync
910
+ * state and the filesystem only (no server call), so it is immune to listing
911
+ * truncation.
912
+ *
913
+ * A key is a candidate iff it was in the prior state slot (a prior pull managed
914
+ * it) AND **no** local file in the type's directory declares that key (via
915
+ * {@link collectLocalDeclaredKeys}, mirroring how `config push` derives keys). A
916
+ * key still declared by some file — even one renamed away from the canonical
917
+ * `<key>.toml` path — is left alone, since `config push` would update it, not
918
+ * delete it (issue #1655). A file whose key was never in prior state was
919
+ * hand-authored and is left for `config push` to create; it is never a candidate.
920
+ */
921
+ export function collectPushPruneCandidates(input) {
922
+ const { spec, configDir, priorEntities } = input;
923
+ if (!priorEntities)
924
+ return [];
925
+ const localKeys = collectLocalDeclaredKeys({ spec, configDir });
926
+ const candidates = [];
927
+ for (const [key, entry] of Object.entries(priorEntities)) {
928
+ if (!localKeys.has(key)) {
929
+ candidates.push({ key, entry });
930
+ }
931
+ }
932
+ return candidates;
933
+ }
934
+ /**
935
+ * Decide what to do with one prune candidate, given the point read's outcome.
936
+ * Pure so the safety rules are unit-testable without a live server.
937
+ *
938
+ * Fail-closed throughout: a candidate is deleted ONLY on a successful read whose
939
+ * live `modifiedAt` matches prior state (or under `--force`). A 404 means the
940
+ * work is already done (drop the stale state). Any other read failure, a missing
941
+ * address, or a type without a delete surface leaves everything untouched.
942
+ */
943
+ export function decidePushPruneCandidate(input) {
944
+ const { spec, address, priorModifiedAt, fetch: fetchOutcome, force } = input;
945
+ if (!address)
946
+ return { action: "skip", reason: "no-id" };
947
+ if (!spec.deleteEntity)
948
+ return { action: "skip", reason: "unsupported" };
949
+ if (fetchOutcome.ok) {
950
+ const serverModifiedAt = spec.modifiedAtOf(fetchOutcome.detail);
951
+ const unchanged = !!priorModifiedAt &&
952
+ !!serverModifiedAt &&
953
+ serverModifiedAt === priorModifiedAt;
954
+ if (force || unchanged) {
955
+ return { action: "delete", address, serverModifiedAt };
956
+ }
957
+ return { action: "skip", reason: "drift", serverModifiedAt };
958
+ }
959
+ // Fetch failed: a 404 means already gone (drop stale state); any other error
960
+ // means presence could not be confirmed — leave everything intact.
961
+ if (fetchOutcome.status === 404)
962
+ return { action: "drop-state" };
963
+ return { action: "skip", reason: "unconfirmed" };
964
+ }
965
+ /**
966
+ * Classify an error thrown by a prune delete — one arm for every type.
967
+ *
968
+ * Keyed on the status alone (`ConflictError` is an `ApiError` with 409), so no
969
+ * per-type carve-out can creep into the push command's delete loop and make the
970
+ * race recovery true for some types and not others (#2762).
971
+ */
972
+ export function classifyPruneDeleteFailure(err) {
973
+ const status = err?.statusCode;
974
+ if (status === 404)
975
+ return "raced-absent";
976
+ if (status === 409)
977
+ return "blocked";
978
+ return "failed";
979
+ }
980
+ const PRUNE_SKIP_LABELS = {
981
+ drift: "skipped — modified on server since last sync (use --force)",
982
+ unconfirmed: "skipped — could not confirm (fetch failed)",
983
+ "no-id": "skipped — no id in sync state",
984
+ unsupported: "skipped — no delete surface for this type",
985
+ };
986
+ /**
987
+ * Render the prune plan the operator reads before the batch confirmation.
988
+ *
989
+ * Pure, and here rather than in `sync.ts`, so what the plan states about a type
990
+ * is testable without a server — specifically that a type's `deleteCaveat`
991
+ * (metadata's orphaned values, which used to live in the retired verb's confirm
992
+ * prompt) appears under its delete lines and nowhere else. `config push` renders
993
+ * the plan ONCE, before the `--dry-run` branch, so both paths print the same
994
+ * lines (#2762).
995
+ */
996
+ export function formatPrunePlan(items) {
997
+ return items.map(({ spec, key, decision }) => {
998
+ const detail = `${spec.resourceType.label}: ${key}`;
999
+ if (decision.action === "delete") {
1000
+ return {
1001
+ kind: "delete",
1002
+ status: "will delete",
1003
+ detail,
1004
+ note: spec.deleteCaveat,
1005
+ };
1006
+ }
1007
+ if (decision.action === "drop-state") {
1008
+ return { kind: "absent", status: "already absent", detail };
1009
+ }
1010
+ return {
1011
+ kind: "skip",
1012
+ status: PRUNE_SKIP_LABELS[decision.reason] ?? "skipped",
1013
+ detail,
1014
+ };
1015
+ });
1016
+ }
1017
+ /**
1018
+ * Remove a pruned block's sidecar `<key>.tests/` directory and its
1019
+ * `entities.testCases` state records. The main file is already gone (that is
1020
+ * what made this a prune candidate), so `applyPrune` above — which bails when
1021
+ * the main file is absent — does not clean the sidecar; this does.
1022
+ */
1023
+ export function removePrunedSidecar(options) {
1024
+ const { configDir, resourceType, key, testCaseEntities, logger } = options;
1025
+ const log = logger ?? (() => { });
1026
+ const removed = [];
1027
+ const removedTestCaseKeys = [];
1028
+ // #2645 — the entity's named config bodies orphan the same way its tests do.
1029
+ const configsDirName = resourceType.namedConfigsDirFromKey?.(key);
1030
+ if (configsDirName) {
1031
+ const configsDir = join(configDir, resourceType.dir, configsDirName);
1032
+ if (existsSync(configsDir) && isSafeSyncFilename(configsDirName)) {
1033
+ rmSync(configsDir, { recursive: true, force: true });
1034
+ removed.push(`${resourceType.dir}/${configsDirName}`);
1035
+ log(` Removed orphaned ${resourceType.dir}/${configsDirName}`);
1036
+ }
1037
+ }
1038
+ if (!resourceType.testBlockType)
1039
+ return { removed, removedTestCaseKeys };
1040
+ const testsDirName = `${key}.tests`;
1041
+ const testsDir = join(configDir, resourceType.dir, testsDirName);
1042
+ if (existsSync(testsDir) && isSafeSyncFilename(testsDirName)) {
1043
+ rmSync(testsDir, { recursive: true, force: true });
1044
+ removed.push(`${resourceType.dir}/${testsDirName}`);
1045
+ log(` Removed orphaned ${resourceType.dir}/${testsDirName}`);
1046
+ }
1047
+ if (testCaseEntities) {
1048
+ for (const [stateKey, entry] of Object.entries(testCaseEntities)) {
1049
+ if (entry?.blockType === resourceType.testBlockType &&
1050
+ entry?.blockKey === key) {
1051
+ delete testCaseEntities[stateKey];
1052
+ removedTestCaseKeys.push(stateKey);
1053
+ }
1054
+ }
1055
+ }
1056
+ return { removed, removedTestCaseKeys };
1057
+ }
1058
+ /** `config diff` status for a managed remote entity whose local file is gone. */
1059
+ export const REMOTE_ONLY_WILL_DELETE = "remote only (will delete with push --prune)";
1060
+ /** `config diff` status for a server entity that was never synced (hand-created). */
1061
+ export const REMOTE_ONLY_UNMANAGED = "remote only (left alone)";
1062
+ /**
1063
+ * Classify a "remote only" entity (present server-side, no local file) for
1064
+ * `config diff` (issue #1655). A key the prior pull managed means the operator
1065
+ * deleted its local file and `push --prune` will delete the remote entity; a key
1066
+ * never synced was authored on the server and is left alone. Prior sync state is
1067
+ * the discriminator, exactly as it is for the local-only / pull-prune split.
1068
+ */
1069
+ export function classifyRemoteOnly(priorEntities, slot, key) {
1070
+ const managed = (priorEntities?.[slot] ?? {});
1071
+ return key in managed ? REMOTE_ONLY_WILL_DELETE : REMOTE_ONLY_UNMANAGED;
1072
+ }
1073
+ //# sourceMappingURL=sync-resource-types.js.map