@homeflare/alchemy 0.14.0 → 0.19.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 (567) hide show
  1. package/README.md +29 -0
  2. package/dist/linux/directory-lifecycle.d.ts +50 -0
  3. package/dist/linux/directory-lifecycle.d.ts.map +1 -0
  4. package/dist/linux/directory.d.ts +19 -0
  5. package/dist/linux/directory.d.ts.map +1 -0
  6. package/dist/linux/fake-linux-host.d.ts +35 -0
  7. package/dist/linux/fake-linux-host.d.ts.map +1 -0
  8. package/dist/linux/fake-systemd.d.ts +34 -0
  9. package/dist/linux/fake-systemd.d.ts.map +1 -0
  10. package/dist/linux/index.d.ts +27 -0
  11. package/dist/linux/index.d.ts.map +1 -0
  12. package/dist/linux/index.js +1554 -0
  13. package/dist/linux/index.js.map +37 -0
  14. package/dist/linux/providers.d.ts +17 -0
  15. package/dist/linux/providers.d.ts.map +1 -0
  16. package/dist/linux/region.d.ts +43 -0
  17. package/dist/linux/region.d.ts.map +1 -0
  18. package/dist/linux/remote-file-form.d.ts +47 -0
  19. package/dist/linux/remote-file-form.d.ts.map +1 -0
  20. package/dist/linux/remote-file-lifecycle.d.ts +36 -0
  21. package/dist/linux/remote-file-lifecycle.d.ts.map +1 -0
  22. package/dist/linux/remote-file-plan.d.ts +33 -0
  23. package/dist/linux/remote-file-plan.d.ts.map +1 -0
  24. package/dist/linux/remote-file.d.ts +28 -0
  25. package/dist/linux/remote-file.d.ts.map +1 -0
  26. package/dist/linux/ssh-command.d.ts +68 -0
  27. package/dist/linux/ssh-command.d.ts.map +1 -0
  28. package/dist/linux/ssh-runner.d.ts +22 -0
  29. package/dist/linux/ssh-runner.d.ts.map +1 -0
  30. package/dist/linux/ssh-scripts.d.ts +27 -0
  31. package/dist/linux/ssh-scripts.d.ts.map +1 -0
  32. package/dist/linux/systemctl.d.ts +67 -0
  33. package/dist/linux/systemctl.d.ts.map +1 -0
  34. package/dist/linux/unit-form.d.ts +78 -0
  35. package/dist/linux/unit-form.d.ts.map +1 -0
  36. package/dist/linux/unit-handlers.d.ts +15 -0
  37. package/dist/linux/unit-handlers.d.ts.map +1 -0
  38. package/dist/linux/unit-lifecycle.d.ts +44 -0
  39. package/dist/linux/unit-lifecycle.d.ts.map +1 -0
  40. package/dist/linux/unit-settle.d.ts +31 -0
  41. package/dist/linux/unit-settle.d.ts.map +1 -0
  42. package/dist/linux/unit.d.ts +35 -0
  43. package/dist/linux/unit.d.ts.map +1 -0
  44. package/dist/netbox/client.d.ts +52 -0
  45. package/dist/netbox/client.d.ts.map +1 -0
  46. package/dist/netbox/constraint-guard.d.ts +26 -0
  47. package/dist/netbox/constraint-guard.d.ts.map +1 -0
  48. package/dist/netbox/constraints.d.ts +76 -0
  49. package/dist/netbox/constraints.d.ts.map +1 -0
  50. package/dist/netbox/generated/constraints/index.d.ts +19 -0
  51. package/dist/netbox/generated/constraints/index.d.ts.map +1 -0
  52. package/dist/netbox/generated/constraints/netbox-ipam.d.ts +21 -0
  53. package/dist/netbox/generated/constraints/netbox-ipam.d.ts.map +1 -0
  54. package/dist/netbox/index.d.ts +17 -0
  55. package/dist/netbox/index.d.ts.map +1 -0
  56. package/dist/netbox/index.js +331 -0
  57. package/dist/netbox/index.js.map +18 -0
  58. package/dist/netbox/prefix-form.d.ts +54 -0
  59. package/dist/netbox/prefix-form.d.ts.map +1 -0
  60. package/dist/netbox/prefix.d.ts +80 -0
  61. package/dist/netbox/prefix.d.ts.map +1 -0
  62. package/dist/netbox/resource.d.ts +94 -0
  63. package/dist/netbox/resource.d.ts.map +1 -0
  64. package/dist/netbox/values.d.ts +24 -0
  65. package/dist/netbox/values.d.ts.map +1 -0
  66. package/dist/openbao/index.d.ts +10 -3
  67. package/dist/openbao/index.d.ts.map +1 -1
  68. package/dist/proxmox/acl.d.ts.map +1 -1
  69. package/dist/proxmox/api-token-form.d.ts.map +1 -1
  70. package/dist/proxmox/apply-endpoints.d.ts +30 -0
  71. package/dist/proxmox/apply-endpoints.d.ts.map +1 -0
  72. package/dist/proxmox/backup-job.d.ts.map +1 -1
  73. package/dist/proxmox/ceph-daemon-form.d.ts +29 -1
  74. package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -1
  75. package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
  76. package/dist/proxmox/ceph-flag.d.ts.map +1 -1
  77. package/dist/proxmox/ceph-fs.d.ts.map +1 -1
  78. package/dist/proxmox/ceph-osd-write.d.ts +19 -0
  79. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
  80. package/dist/proxmox/ceph-pool.d.ts.map +1 -1
  81. package/dist/proxmox/constraint-guard.d.ts +28 -0
  82. package/dist/proxmox/constraint-guard.d.ts.map +1 -0
  83. package/dist/proxmox/constraints.d.ts +94 -0
  84. package/dist/proxmox/constraints.d.ts.map +1 -0
  85. package/dist/proxmox/firewall-alias.d.ts.map +1 -1
  86. package/dist/proxmox/generated/constraints/index.d.ts +19 -0
  87. package/dist/proxmox/generated/constraints/index.d.ts.map +1 -0
  88. package/dist/proxmox/generated/constraints/pbs-config.d.ts +23 -0
  89. package/dist/proxmox/generated/constraints/pbs-config.d.ts.map +1 -0
  90. package/dist/proxmox/generated/constraints/pve-access.d.ts +23 -0
  91. package/dist/proxmox/generated/constraints/pve-access.d.ts.map +1 -0
  92. package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts +23 -0
  93. package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts.map +1 -0
  94. package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts +23 -0
  95. package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts.map +1 -0
  96. package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts +23 -0
  97. package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts.map +1 -0
  98. package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts +23 -0
  99. package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts.map +1 -0
  100. package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts +23 -0
  101. package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts.map +1 -0
  102. package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts +23 -0
  103. package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts.map +1 -0
  104. package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts +23 -0
  105. package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts.map +1 -0
  106. package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts +23 -0
  107. package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts.map +1 -0
  108. package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts +23 -0
  109. package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts.map +1 -0
  110. package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts +23 -0
  111. package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts.map +1 -0
  112. package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts +23 -0
  113. package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts.map +1 -0
  114. package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts +23 -0
  115. package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts.map +1 -0
  116. package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts +23 -0
  117. package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts.map +1 -0
  118. package/dist/proxmox/generated/constraints/pve-pools.d.ts +23 -0
  119. package/dist/proxmox/generated/constraints/pve-pools.d.ts.map +1 -0
  120. package/dist/proxmox/generated/constraints/pve-storage.d.ts +23 -0
  121. package/dist/proxmox/generated/constraints/pve-storage.d.ts.map +1 -0
  122. package/dist/proxmox/generated/pbs/access-users.d.ts +137 -0
  123. package/dist/proxmox/generated/pbs/access-users.d.ts.map +1 -0
  124. package/dist/proxmox/generated/pbs/access.d.ts +171 -0
  125. package/dist/proxmox/generated/pbs/access.d.ts.map +1 -0
  126. package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts +252 -0
  127. package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts.map +1 -0
  128. package/dist/proxmox/generated/pbs/admin-metrics.d.ts +147 -0
  129. package/dist/proxmox/generated/pbs/admin-metrics.d.ts.map +1 -0
  130. package/dist/proxmox/generated/pbs/admin.d.ts +217 -0
  131. package/dist/proxmox/generated/pbs/admin.d.ts.map +1 -0
  132. package/dist/proxmox/generated/pbs/backup.d.ts +114 -0
  133. package/dist/proxmox/generated/pbs/backup.d.ts.map +1 -0
  134. package/dist/proxmox/generated/pbs/config-access-pam.d.ts +218 -0
  135. package/dist/proxmox/generated/pbs/config-access-pam.d.ts.map +1 -0
  136. package/dist/proxmox/generated/pbs/config-access.d.ts +203 -0
  137. package/dist/proxmox/generated/pbs/config-access.d.ts.map +1 -0
  138. package/dist/proxmox/generated/pbs/config-datastore.d.ts +195 -0
  139. package/dist/proxmox/generated/pbs/config-datastore.d.ts.map +1 -0
  140. package/dist/proxmox/generated/pbs/config-media-pool.d.ts +207 -0
  141. package/dist/proxmox/generated/pbs/config-media-pool.d.ts.map +1 -0
  142. package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts +159 -0
  143. package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts.map +1 -0
  144. package/dist/proxmox/generated/pbs/config-notifications.d.ts +199 -0
  145. package/dist/proxmox/generated/pbs/config-notifications.d.ts.map +1 -0
  146. package/dist/proxmox/generated/pbs/config-remote.d.ts +175 -0
  147. package/dist/proxmox/generated/pbs/config-remote.d.ts.map +1 -0
  148. package/dist/proxmox/generated/pbs/config-sync.d.ts +145 -0
  149. package/dist/proxmox/generated/pbs/config-sync.d.ts.map +1 -0
  150. package/dist/proxmox/generated/pbs/config-tape.d.ts +200 -0
  151. package/dist/proxmox/generated/pbs/config-tape.d.ts.map +1 -0
  152. package/dist/proxmox/generated/pbs/config-verify.d.ts +77 -0
  153. package/dist/proxmox/generated/pbs/config-verify.d.ts.map +1 -0
  154. package/dist/proxmox/generated/pbs/config.d.ts +119 -0
  155. package/dist/proxmox/generated/pbs/config.d.ts.map +1 -0
  156. package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts +178 -0
  157. package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts.map +1 -0
  158. package/dist/proxmox/generated/pbs/nodes-node-services.d.ts +205 -0
  159. package/dist/proxmox/generated/pbs/nodes-node-services.d.ts.map +1 -0
  160. package/dist/proxmox/generated/pbs/nodes-node.d.ts +166 -0
  161. package/dist/proxmox/generated/pbs/nodes-node.d.ts.map +1 -0
  162. package/dist/proxmox/generated/pbs/nodes.d.ts +171 -0
  163. package/dist/proxmox/generated/pbs/nodes.d.ts.map +1 -0
  164. package/dist/proxmox/generated/pbs/ping.d.ts +18 -0
  165. package/dist/proxmox/generated/pbs/ping.d.ts.map +1 -0
  166. package/dist/proxmox/generated/pbs/pull.d.ts +37 -0
  167. package/dist/proxmox/generated/pbs/pull.d.ts.map +1 -0
  168. package/dist/proxmox/generated/pbs/push.d.ts +36 -0
  169. package/dist/proxmox/generated/pbs/push.d.ts.map +1 -0
  170. package/dist/proxmox/generated/pbs/reader.d.ts +41 -0
  171. package/dist/proxmox/generated/pbs/reader.d.ts.map +1 -0
  172. package/dist/proxmox/generated/pbs/root.d.ts +16 -0
  173. package/dist/proxmox/generated/pbs/root.d.ts.map +1 -0
  174. package/dist/proxmox/generated/pbs/status.d.ts +54 -0
  175. package/dist/proxmox/generated/pbs/status.d.ts.map +1 -0
  176. package/dist/proxmox/generated/pbs/tape-drive.d.ts +181 -0
  177. package/dist/proxmox/generated/pbs/tape-drive.d.ts.map +1 -0
  178. package/dist/proxmox/generated/pbs/tape-media.d.ts +126 -0
  179. package/dist/proxmox/generated/pbs/tape-media.d.ts.map +1 -0
  180. package/dist/proxmox/generated/pbs/tape.d.ts +90 -0
  181. package/dist/proxmox/generated/pbs/tape.d.ts.map +1 -0
  182. package/dist/proxmox/generated/pbs/version.d.ts +20 -0
  183. package/dist/proxmox/generated/pbs/version.d.ts.map +1 -0
  184. package/dist/proxmox/generated/pbs.d.ts +44 -662
  185. package/dist/proxmox/generated/pbs.d.ts.map +1 -1
  186. package/dist/proxmox/generated/pve/access-roles.d.ts +164 -0
  187. package/dist/proxmox/generated/pve/access-roles.d.ts.map +1 -0
  188. package/dist/proxmox/generated/pve/access-users.d.ts +152 -0
  189. package/dist/proxmox/generated/pve/access-users.d.ts.map +1 -0
  190. package/dist/proxmox/generated/pve/access.d.ts +221 -0
  191. package/dist/proxmox/generated/pve/access.d.ts.map +1 -0
  192. package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts +45 -0
  193. package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts.map +1 -0
  194. package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts +47 -0
  195. package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts.map +1 -0
  196. package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts +240 -0
  197. package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts.map +1 -0
  198. package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts +200 -0
  199. package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts.map +1 -0
  200. package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts +109 -0
  201. package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts.map +1 -0
  202. package/dist/proxmox/generated/pve/cluster-firewall.d.ts +225 -0
  203. package/dist/proxmox/generated/pve/cluster-firewall.d.ts.map +1 -0
  204. package/dist/proxmox/generated/pve/cluster-ha.d.ts +199 -0
  205. package/dist/proxmox/generated/pve/cluster-ha.d.ts.map +1 -0
  206. package/dist/proxmox/generated/pve/cluster-jobs.d.ts +183 -0
  207. package/dist/proxmox/generated/pve/cluster-jobs.d.ts.map +1 -0
  208. package/dist/proxmox/generated/pve/cluster-metrics.d.ts +110 -0
  209. package/dist/proxmox/generated/pve/cluster-metrics.d.ts.map +1 -0
  210. package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts +224 -0
  211. package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts.map +1 -0
  212. package/dist/proxmox/generated/pve/cluster-notifications.d.ts +91 -0
  213. package/dist/proxmox/generated/pve/cluster-notifications.d.ts.map +1 -0
  214. package/dist/proxmox/generated/pve/cluster-options.d.ts +85 -0
  215. package/dist/proxmox/generated/pve/cluster-options.d.ts.map +1 -0
  216. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts +48 -0
  217. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts.map +1 -0
  218. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts +45 -0
  219. package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts.map +1 -0
  220. package/dist/proxmox/generated/pve/cluster-replication.d.ts +130 -0
  221. package/dist/proxmox/generated/pve/cluster-replication.d.ts.map +1 -0
  222. package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts +220 -0
  223. package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts.map +1 -0
  224. package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts +229 -0
  225. package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts.map +1 -0
  226. package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts +229 -0
  227. package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts.map +1 -0
  228. package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts +51 -0
  229. package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts.map +1 -0
  230. package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts +225 -0
  231. package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts.map +1 -0
  232. package/dist/proxmox/generated/pve/cluster-sdn.d.ts +202 -0
  233. package/dist/proxmox/generated/pve/cluster-sdn.d.ts.map +1 -0
  234. package/dist/proxmox/generated/pve/cluster.d.ts +69 -0
  235. package/dist/proxmox/generated/pve/cluster.d.ts.map +1 -0
  236. package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts +167 -0
  237. package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts.map +1 -0
  238. package/dist/proxmox/generated/pve/nodes-node-config.d.ts +43 -0
  239. package/dist/proxmox/generated/pve/nodes-node-config.d.ts.map +1 -0
  240. package/dist/proxmox/generated/pve/nodes-node-disks.d.ts +225 -0
  241. package/dist/proxmox/generated/pve/nodes-node-disks.d.ts.map +1 -0
  242. package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts +145 -0
  243. package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts.map +1 -0
  244. package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts +195 -0
  245. package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts.map +1 -0
  246. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts +191 -0
  247. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts.map +1 -0
  248. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts +33 -0
  249. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts.map +1 -0
  250. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts +56 -0
  251. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts.map +1 -0
  252. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts +28 -0
  253. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts.map +1 -0
  254. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts +186 -0
  255. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts.map +1 -0
  256. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts +191 -0
  257. package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts.map +1 -0
  258. package/dist/proxmox/generated/pve/nodes-node-network.d.ts +150 -0
  259. package/dist/proxmox/generated/pve/nodes-node-network.d.ts.map +1 -0
  260. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts +301 -0
  261. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts.map +1 -0
  262. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts +170 -0
  263. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts.map +1 -0
  264. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts +144 -0
  265. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts.map +1 -0
  266. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts +34 -0
  267. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts.map +1 -0
  268. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts +137 -0
  269. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts.map +1 -0
  270. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts +179 -0
  271. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts.map +1 -0
  272. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts +143 -0
  273. package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts.map +1 -0
  274. package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts +158 -0
  275. package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts.map +1 -0
  276. package/dist/proxmox/generated/pve/nodes-node-replication.d.ts +187 -0
  277. package/dist/proxmox/generated/pve/nodes-node-replication.d.ts.map +1 -0
  278. package/dist/proxmox/generated/pve/nodes-node-services.d.ts +115 -0
  279. package/dist/proxmox/generated/pve/nodes-node-services.d.ts.map +1 -0
  280. package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts +202 -0
  281. package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts.map +1 -0
  282. package/dist/proxmox/generated/pve/nodes-node-storage.d.ts +40 -0
  283. package/dist/proxmox/generated/pve/nodes-node-storage.d.ts.map +1 -0
  284. package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts +173 -0
  285. package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts.map +1 -0
  286. package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts +97 -0
  287. package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts.map +1 -0
  288. package/dist/proxmox/generated/pve/nodes-node.d.ts +207 -0
  289. package/dist/proxmox/generated/pve/nodes-node.d.ts.map +1 -0
  290. package/dist/proxmox/generated/pve/nodes.d.ts +236 -0
  291. package/dist/proxmox/generated/pve/nodes.d.ts.map +1 -0
  292. package/dist/proxmox/generated/pve/pools.d.ts +82 -0
  293. package/dist/proxmox/generated/pve/pools.d.ts.map +1 -0
  294. package/dist/proxmox/generated/pve/storage.d.ts +159 -0
  295. package/dist/proxmox/generated/pve/storage.d.ts.map +1 -0
  296. package/dist/proxmox/generated/pve/version.d.ts +21 -0
  297. package/dist/proxmox/generated/pve/version.d.ts.map +1 -0
  298. package/dist/proxmox/generated/pve.d.ts +69 -4766
  299. package/dist/proxmox/generated/pve.d.ts.map +1 -1
  300. package/dist/proxmox/group.d.ts.map +1 -1
  301. package/dist/proxmox/ha-resource.d.ts.map +1 -1
  302. package/dist/proxmox/ha-rule.d.ts.map +1 -1
  303. package/dist/proxmox/index.d.ts +6 -1
  304. package/dist/proxmox/index.d.ts.map +1 -1
  305. package/dist/proxmox/index.js +1483 -300
  306. package/dist/proxmox/index.js.map +70 -45
  307. package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
  308. package/dist/proxmox/metric-server-form.d.ts +11 -0
  309. package/dist/proxmox/metric-server-form.d.ts.map +1 -1
  310. package/dist/proxmox/metric-server.d.ts.map +1 -1
  311. package/dist/proxmox/network-apply.d.ts.map +1 -1
  312. package/dist/proxmox/node-network-form.d.ts +15 -0
  313. package/dist/proxmox/node-network-form.d.ts.map +1 -1
  314. package/dist/proxmox/node-network.d.ts.map +1 -1
  315. package/dist/proxmox/notification-matcher.d.ts.map +1 -1
  316. package/dist/proxmox/notification-target-endpoint.d.ts +26 -0
  317. package/dist/proxmox/notification-target-endpoint.d.ts.map +1 -0
  318. package/dist/proxmox/notification-target-form.d.ts +7 -0
  319. package/dist/proxmox/notification-target-form.d.ts.map +1 -1
  320. package/dist/proxmox/notification-target.d.ts.map +1 -1
  321. package/dist/proxmox/pbs-datastore-endpoint.d.ts +22 -0
  322. package/dist/proxmox/pbs-datastore-endpoint.d.ts.map +1 -0
  323. package/dist/proxmox/pbs-datastore.d.ts.map +1 -1
  324. package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -1
  325. package/dist/proxmox/pbs-notification-target-endpoint.d.ts +29 -0
  326. package/dist/proxmox/pbs-notification-target-endpoint.d.ts.map +1 -0
  327. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
  328. package/dist/proxmox/pbs-prune-job.d.ts.map +1 -1
  329. package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -1
  330. package/dist/proxmox/pbs-verify-job.d.ts.map +1 -1
  331. package/dist/proxmox/pool.d.ts.map +1 -1
  332. package/dist/proxmox/provision-baseline.d.ts +30 -5
  333. package/dist/proxmox/provision-baseline.d.ts.map +1 -1
  334. package/dist/proxmox/qemu.d.ts +10 -0
  335. package/dist/proxmox/qemu.d.ts.map +1 -1
  336. package/dist/proxmox/replication-job.d.ts.map +1 -1
  337. package/dist/proxmox/resource-guard.d.ts +15 -0
  338. package/dist/proxmox/resource-guard.d.ts.map +1 -0
  339. package/dist/proxmox/resource-spec.d.ts +131 -0
  340. package/dist/proxmox/resource-spec.d.ts.map +1 -0
  341. package/dist/proxmox/resource.d.ts +17 -90
  342. package/dist/proxmox/resource.d.ts.map +1 -1
  343. package/dist/proxmox/role.d.ts.map +1 -1
  344. package/dist/proxmox/sdn-apply.d.ts.map +1 -1
  345. package/dist/proxmox/sdn-subnet.d.ts.map +1 -1
  346. package/dist/proxmox/sdn-vnet.d.ts.map +1 -1
  347. package/dist/proxmox/sdn-zone.d.ts.map +1 -1
  348. package/dist/proxmox/storage.d.ts.map +1 -1
  349. package/dist/proxmox/user.d.ts.map +1 -1
  350. package/dist/proxmox/zfs-pool-write.d.ts +11 -0
  351. package/dist/proxmox/zfs-pool-write.d.ts.map +1 -1
  352. package/dist/proxmox/zfs-pool.d.ts.map +1 -1
  353. package/docs/linux-host-measured.md +56 -0
  354. package/docs/linux-host.md +186 -0
  355. package/docs/netbox.md +103 -0
  356. package/docs/provision-baseline.md +34 -7
  357. package/docs/proxmox-lxc.md +40 -0
  358. package/package.json +9 -1
  359. package/src/linux/directory-lifecycle.ts +220 -0
  360. package/src/linux/directory.test.ts +132 -0
  361. package/src/linux/directory.ts +71 -0
  362. package/src/linux/fake-linux-host.ts +197 -0
  363. package/src/linux/fake-systemd.ts +103 -0
  364. package/src/linux/index.ts +32 -0
  365. package/src/linux/providers.ts +26 -0
  366. package/src/linux/region.test.ts +89 -0
  367. package/src/linux/region.ts +118 -0
  368. package/src/linux/remote-file-form.ts +96 -0
  369. package/src/linux/remote-file-lifecycle.test.ts +191 -0
  370. package/src/linux/remote-file-lifecycle.ts +161 -0
  371. package/src/linux/remote-file-plan.ts +154 -0
  372. package/src/linux/remote-file.ts +88 -0
  373. package/src/linux/ssh-command.test.ts +140 -0
  374. package/src/linux/ssh-command.ts +99 -0
  375. package/src/linux/ssh-runner.ts +188 -0
  376. package/src/linux/ssh-scripts.ts +105 -0
  377. package/src/linux/systemctl.ts +145 -0
  378. package/src/linux/unit-form.ts +183 -0
  379. package/src/linux/unit-handlers.ts +73 -0
  380. package/src/linux/unit-lifecycle.test.ts +220 -0
  381. package/src/linux/unit-lifecycle.ts +193 -0
  382. package/src/linux/unit-settle.ts +110 -0
  383. package/src/linux/unit.ts +81 -0
  384. package/src/netbox/client.test.ts +67 -0
  385. package/src/netbox/client.ts +146 -0
  386. package/src/netbox/constraint-guard.ts +57 -0
  387. package/src/netbox/constraints.test.ts +114 -0
  388. package/src/netbox/constraints.ts +152 -0
  389. package/src/netbox/generated/constraints/index.ts +23 -0
  390. package/src/netbox/generated/constraints/netbox-ipam.ts +32 -0
  391. package/src/netbox/index.ts +34 -0
  392. package/src/netbox/prefix-form.test.ts +127 -0
  393. package/src/netbox/prefix-form.ts +84 -0
  394. package/src/netbox/prefix.ts +136 -0
  395. package/src/netbox/resource.ts +208 -0
  396. package/src/netbox/values.test.ts +66 -0
  397. package/src/netbox/values.ts +40 -0
  398. package/src/openbao/index-types.test.ts +87 -0
  399. package/src/openbao/index.ts +15 -3
  400. package/src/proxmox/acl.ts +2 -0
  401. package/src/proxmox/api-token-form.ts +5 -0
  402. package/src/proxmox/apply-endpoints.ts +34 -0
  403. package/src/proxmox/backup-job.ts +2 -0
  404. package/src/proxmox/ceph-daemon-form.ts +56 -2
  405. package/src/proxmox/ceph-daemon.ts +10 -21
  406. package/src/proxmox/ceph-flag.ts +1 -0
  407. package/src/proxmox/ceph-fs.ts +1 -0
  408. package/src/proxmox/ceph-osd-write.ts +30 -4
  409. package/src/proxmox/ceph-pool.ts +10 -0
  410. package/src/proxmox/constraint-guard.ts +53 -0
  411. package/src/proxmox/constraints-dialect.test.ts +165 -0
  412. package/src/proxmox/constraints-forms.test.ts +220 -0
  413. package/src/proxmox/constraints-host-forms.test.ts +132 -0
  414. package/src/proxmox/constraints-live.test.ts +155 -0
  415. package/src/proxmox/constraints-node-forms.test.ts +188 -0
  416. package/src/proxmox/constraints-required.test.ts +172 -0
  417. package/src/proxmox/constraints.test.ts +205 -0
  418. package/src/proxmox/constraints.ts +160 -0
  419. package/src/proxmox/firewall-alias.ts +5 -0
  420. package/src/proxmox/generated/constraints/index.ts +55 -0
  421. package/src/proxmox/generated/constraints/pbs-config.ts +203 -0
  422. package/src/proxmox/generated/constraints/pve-access.ts +69 -0
  423. package/src/proxmox/generated/constraints/pve-cluster-backup.ts +74 -0
  424. package/src/proxmox/generated/constraints/pve-cluster-ceph.ts +27 -0
  425. package/src/proxmox/generated/constraints/pve-cluster-firewall.ts +35 -0
  426. package/src/proxmox/generated/constraints/pve-cluster-ha.ts +55 -0
  427. package/src/proxmox/generated/constraints/pve-cluster-metrics.ts +59 -0
  428. package/src/proxmox/generated/constraints/pve-cluster-notifications.ts +80 -0
  429. package/src/proxmox/generated/constraints/pve-cluster-replication.ts +43 -0
  430. package/src/proxmox/generated/constraints/pve-cluster-sdn.ts +90 -0
  431. package/src/proxmox/generated/constraints/pve-nodes-ceph.ts +57 -0
  432. package/src/proxmox/generated/constraints/pve-nodes-disks.ts +31 -0
  433. package/src/proxmox/generated/constraints/pve-nodes-lxc.ts +82 -0
  434. package/src/proxmox/generated/constraints/pve-nodes-network.ts +77 -0
  435. package/src/proxmox/generated/constraints/pve-nodes-qemu.ts +125 -0
  436. package/src/proxmox/generated/constraints/pve-pools.ts +31 -0
  437. package/src/proxmox/generated/constraints/pve-storage.ts +80 -0
  438. package/src/proxmox/generated/pbs/access-users.ts +137 -0
  439. package/src/proxmox/generated/pbs/access.ts +232 -0
  440. package/src/proxmox/generated/pbs/admin-datastore-store.ts +238 -0
  441. package/src/proxmox/generated/pbs/admin-metrics.ts +147 -0
  442. package/src/proxmox/generated/pbs/admin.ts +225 -0
  443. package/src/proxmox/generated/pbs/backup.ts +121 -0
  444. package/src/proxmox/generated/pbs/config-access-pam.ts +228 -0
  445. package/src/proxmox/generated/pbs/config-access.ts +228 -0
  446. package/src/proxmox/generated/pbs/config-datastore.ts +217 -0
  447. package/src/proxmox/generated/pbs/config-media-pool.ts +232 -0
  448. package/src/proxmox/generated/pbs/config-notifications-matchers.ts +187 -0
  449. package/src/proxmox/generated/pbs/config-notifications.ts +221 -0
  450. package/src/proxmox/generated/pbs/config-remote.ts +192 -0
  451. package/src/proxmox/generated/pbs/config-sync.ts +167 -0
  452. package/src/proxmox/generated/pbs/config-tape.ts +226 -0
  453. package/src/proxmox/generated/pbs/config-verify.ts +87 -0
  454. package/src/proxmox/generated/pbs/config.ts +135 -0
  455. package/src/proxmox/generated/pbs/nodes-node-dns.ts +229 -0
  456. package/src/proxmox/generated/pbs/nodes-node-services.ts +181 -0
  457. package/src/proxmox/generated/pbs/nodes-node.ts +244 -0
  458. package/src/proxmox/generated/pbs/nodes.ts +160 -0
  459. package/src/proxmox/generated/pbs/ping.ts +16 -0
  460. package/src/proxmox/generated/pbs/pull.ts +37 -0
  461. package/src/proxmox/generated/pbs/push.ts +36 -0
  462. package/src/proxmox/generated/pbs/reader.ts +41 -0
  463. package/src/proxmox/generated/pbs/root.ts +16 -0
  464. package/src/proxmox/generated/pbs/status.ts +50 -0
  465. package/src/proxmox/generated/pbs/tape-drive.ts +220 -0
  466. package/src/proxmox/generated/pbs/tape-media.ts +133 -0
  467. package/src/proxmox/generated/pbs/tape.ts +92 -0
  468. package/src/proxmox/generated/pbs/version.ts +16 -0
  469. package/src/proxmox/generated/pbs.ts +44 -787
  470. package/src/proxmox/generated/pve/access-roles.ts +168 -0
  471. package/src/proxmox/generated/pve/access-users.ts +155 -0
  472. package/src/proxmox/generated/pve/access.ts +217 -0
  473. package/src/proxmox/generated/pve/cluster-acme-plugins-id.ts +367 -0
  474. package/src/proxmox/generated/pve/cluster-acme-plugins.ts +366 -0
  475. package/src/proxmox/generated/pve/cluster-acme-tos.ts +232 -0
  476. package/src/proxmox/generated/pve/cluster-bulk-action.ts +227 -0
  477. package/src/proxmox/generated/pve/cluster-firewall-refs.ts +110 -0
  478. package/src/proxmox/generated/pve/cluster-firewall.ts +236 -0
  479. package/src/proxmox/generated/pve/cluster-ha.ts +203 -0
  480. package/src/proxmox/generated/pve/cluster-jobs.ts +190 -0
  481. package/src/proxmox/generated/pve/cluster-metrics.ts +116 -0
  482. package/src/proxmox/generated/pve/cluster-notifications-endpoints.ts +244 -0
  483. package/src/proxmox/generated/pve/cluster-notifications.ts +99 -0
  484. package/src/proxmox/generated/pve/cluster-options.ts +189 -0
  485. package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.ts +272 -0
  486. package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models.ts +268 -0
  487. package/src/proxmox/generated/pve/cluster-replication.ts +127 -0
  488. package/src/proxmox/generated/pve/cluster-sdn-fabrics.ts +218 -0
  489. package/src/proxmox/generated/pve/cluster-sdn-ipams.ts +229 -0
  490. package/src/proxmox/generated/pve/cluster-sdn-vnets-vnet.ts +245 -0
  491. package/src/proxmox/generated/pve/cluster-sdn-vnets.ts +49 -0
  492. package/src/proxmox/generated/pve/cluster-sdn-zones.ts +224 -0
  493. package/src/proxmox/generated/pve/cluster-sdn.ts +201 -0
  494. package/src/proxmox/generated/pve/cluster.ts +71 -0
  495. package/src/proxmox/generated/pve/nodes-node-ceph.ts +167 -0
  496. package/src/proxmox/generated/pve/nodes-node-config.ts +56 -0
  497. package/src/proxmox/generated/pve/nodes-node-disks.ts +237 -0
  498. package/src/proxmox/generated/pve/nodes-node-firewall.ts +239 -0
  499. package/src/proxmox/generated/pve/nodes-node-hardware.ts +215 -0
  500. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.ts +232 -0
  501. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.ts +1059 -0
  502. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.ts +49 -0
  503. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-resize.ts +285 -0
  504. package/src/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.ts +184 -0
  505. package/src/proxmox/generated/pve/nodes-node-lxc-vmid.ts +240 -0
  506. package/src/proxmox/generated/pve/nodes-node-network.ts +234 -0
  507. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-config.ts +441 -0
  508. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.ts +215 -0
  509. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.ts +145 -0
  510. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.ts +664 -0
  511. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.ts +188 -0
  512. package/src/proxmox/generated/pve/nodes-node-qemu-vmid-status.ts +233 -0
  513. package/src/proxmox/generated/pve/nodes-node-qemu-vmid.ts +187 -0
  514. package/src/proxmox/generated/pve/nodes-node-qemu.ts +204 -0
  515. package/src/proxmox/generated/pve/nodes-node-replication.ts +187 -0
  516. package/src/proxmox/generated/pve/nodes-node-services.ts +221 -0
  517. package/src/proxmox/generated/pve/nodes-node-storage-storage.ts +226 -0
  518. package/src/proxmox/generated/pve/nodes-node-storage.ts +40 -0
  519. package/src/proxmox/generated/pve/nodes-node-subscription.ts +168 -0
  520. package/src/proxmox/generated/pve/nodes-node-vzdump.ts +96 -0
  521. package/src/proxmox/generated/pve/nodes-node.ts +211 -0
  522. package/src/proxmox/generated/pve/nodes.ts +235 -0
  523. package/src/proxmox/generated/pve/pools.ts +78 -0
  524. package/src/proxmox/generated/pve/storage.ts +213 -0
  525. package/src/proxmox/generated/pve/version.ts +21 -0
  526. package/src/proxmox/generated/pve.ts +69 -7403
  527. package/src/proxmox/group.ts +2 -0
  528. package/src/proxmox/ha-resource.ts +5 -0
  529. package/src/proxmox/ha-rule.ts +2 -0
  530. package/src/proxmox/index.ts +12 -0
  531. package/src/proxmox/lxc-interior.test.ts +77 -0
  532. package/src/proxmox/lxc-lifecycle.ts +48 -9
  533. package/src/proxmox/metric-server-form.ts +10 -0
  534. package/src/proxmox/metric-server.ts +11 -2
  535. package/src/proxmox/network-apply.ts +4 -0
  536. package/src/proxmox/node-network-form.ts +19 -0
  537. package/src/proxmox/node-network.ts +9 -3
  538. package/src/proxmox/notification-matcher.ts +5 -0
  539. package/src/proxmox/notification-target-endpoint.ts +46 -0
  540. package/src/proxmox/notification-target-form.ts +11 -0
  541. package/src/proxmox/notification-target.ts +5 -2
  542. package/src/proxmox/pbs-datastore-endpoint.ts +29 -0
  543. package/src/proxmox/pbs-datastore.ts +8 -1
  544. package/src/proxmox/pbs-notification-matcher.ts +5 -0
  545. package/src/proxmox/pbs-notification-target-endpoint.ts +45 -0
  546. package/src/proxmox/pbs-notification-target-lifecycle.ts +16 -14
  547. package/src/proxmox/pbs-prune-job.ts +2 -0
  548. package/src/proxmox/pbs-sync-job-form.ts +2 -0
  549. package/src/proxmox/pbs-verify-job.ts +2 -0
  550. package/src/proxmox/pool.ts +2 -0
  551. package/src/proxmox/provision-baseline.test.ts +38 -0
  552. package/src/proxmox/provision-baseline.ts +73 -14
  553. package/src/proxmox/provision-bootstrap.test.ts +51 -0
  554. package/src/proxmox/qemu.ts +9 -1
  555. package/src/proxmox/replication-job.ts +5 -0
  556. package/src/proxmox/resource-guard.ts +61 -0
  557. package/src/proxmox/resource-spec.ts +129 -0
  558. package/src/proxmox/resource.ts +52 -93
  559. package/src/proxmox/role.ts +2 -0
  560. package/src/proxmox/sdn-apply.ts +4 -0
  561. package/src/proxmox/sdn-subnet.ts +5 -0
  562. package/src/proxmox/sdn-vnet.ts +2 -0
  563. package/src/proxmox/sdn-zone.ts +2 -0
  564. package/src/proxmox/storage.ts +2 -0
  565. package/src/proxmox/user.ts +2 -0
  566. package/src/proxmox/zfs-pool-write.ts +12 -0
  567. package/src/proxmox/zfs-pool.ts +2 -1
@@ -0,0 +1,114 @@
1
+ /**
2
+ * The constraint reader over a JSON body — and the four ways a naive one gets it wrong.
3
+ *
4
+ * ★ EVERY RULE EXERCISED HERE COMES FROM THE GENERATED TABLE, not a literal invented in this
5
+ * file, wherever a real endpoint states it. A test that made up its own `maxLength` would pass
6
+ * forever while the committed table said something else.
7
+ */
8
+ import { describe, expect, test } from 'bun:test';
9
+ import { bodyViolations, constraintsFor } from './constraint-guard.ts';
10
+ import { violations } from './constraints.ts';
11
+
12
+ const CREATE = 'netbox:POST /api/ipam/prefixes/';
13
+ const UPDATE = 'netbox:PATCH /api/ipam/prefixes/{id}/';
14
+
15
+ describe('the generated table is the one being read', () => {
16
+ test('the prefix create table carries NetBox 4.7.0 own rules', () => {
17
+ const table = constraintsFor(CREATE);
18
+ expect(table['prefix']?.required).toBe(true);
19
+ expect(table['description']?.maxLength).toBe(200);
20
+ expect(table['status']?.enum).toEqual(['container', 'active', 'reserved', 'deprecated']);
21
+ });
22
+
23
+ /** ⛔ An unknown key is a defect: the tables were not regenerated, and silence would hide it. */
24
+ test('an endpoint nobody generated throws rather than passing everything', () => {
25
+ expect(() => constraintsFor('netbox:POST /api/dcim/devices/')).toThrow(
26
+ /no vendor constraint table/,
27
+ );
28
+ });
29
+ });
30
+
31
+ describe('what it refuses', () => {
32
+ test('a description one character over the vendor limit', () => {
33
+ const found = bodyViolations(
34
+ CREATE,
35
+ { description: 'x'.repeat(201), prefix: '10.0.0.0/24' },
36
+ true,
37
+ );
38
+ expect(found).toEqual(['description: at most 200 characters']);
39
+ });
40
+
41
+ test('exactly the limit is accepted', () => {
42
+ expect(
43
+ bodyViolations(CREATE, { description: 'x'.repeat(200), prefix: '10.0.0.0/24' }, true),
44
+ ).toEqual([]);
45
+ });
46
+
47
+ /**
48
+ * ⚠️ `[...value].length`, NOT `value.length`. An emoji is two UTF-16 code units and ONE
49
+ * character; Django counts characters. Counting code units would refuse a description NetBox
50
+ * accepts — rejecting a legal value at plan time being the failure worse than the 400.
51
+ */
52
+ test('a 200-character description made of astral characters is accepted', () => {
53
+ const found = bodyViolations(
54
+ CREATE,
55
+ { description: '🌐'.repeat(200), prefix: '10.0.0.0/24' },
56
+ true,
57
+ );
58
+ expect(found).toEqual([]);
59
+ });
60
+
61
+ test('a status outside the vendor enum', () => {
62
+ const found = bodyViolations(CREATE, { prefix: '10.0.0.0/24', status: 'retired' }, true);
63
+ expect(found).toEqual(['status: must be one of container, active, reserved, deprecated']);
64
+ });
65
+ });
66
+
67
+ describe('presence is a create-only rule', () => {
68
+ /** ⛔ A PATCH is deliberately partial; requiring presence would refuse every ordinary edit. */
69
+ test('a missing required property is refused on create and allowed on update', () => {
70
+ expect(bodyViolations(CREATE, { status: 'active' }, true)).toEqual(['prefix: required']);
71
+ expect(bodyViolations(UPDATE, { status: 'active' }, false)).toEqual([]);
72
+ });
73
+
74
+ /**
75
+ * ⚠️ `null` IS A VALUE, NOT AN ABSENCE. NetBox clears a nullable foreign key with an explicit
76
+ * null, so a presence check that used `value === undefined` would call it missing.
77
+ */
78
+ test('an explicit null satisfies presence and trips no string rule', () => {
79
+ expect(bodyViolations(CREATE, { prefix: null }, true)).toEqual([]);
80
+ });
81
+ });
82
+
83
+ describe('a JSON body is not a form, and that changes the checks', () => {
84
+ /**
85
+ * ⛔ THE REASON THIS READER IS NOT THE PROXMOX ONE. `Number(false)` is 0, so a body that
86
+ * coerced its values would pass a boolean straight through a `minimum: 0` and report nothing.
87
+ */
88
+ test('a boolean is never coerced into a numeric bound', () => {
89
+ const table = { count: { minimum: 1, type: 'integer' } } as const;
90
+ expect(violations(table, { count: false })).toEqual([]);
91
+ expect(violations(table, { count: 0 })).toEqual(['count: at least 1']);
92
+ });
93
+
94
+ test('an empty string is not treated as zero', () => {
95
+ const table = { count: { minimum: 0, type: 'integer' } } as const;
96
+ expect(violations(table, { count: '' })).toEqual([]);
97
+ });
98
+
99
+ /** ⚠️ An array is a repeated value — tags and every many-to-many — so each element is checked. */
100
+ test('every element of an array faces the same rule', () => {
101
+ const table = { names: { maxLength: 3, type: 'string' } } as const;
102
+ expect(violations(table, { names: ['ok', 'toolong', 'alsotoolong'] })).toEqual([
103
+ 'names: at most 3 characters',
104
+ 'names: at most 3 characters',
105
+ ]);
106
+ });
107
+
108
+ /** ⚠️ The table carries the CONSTRAINED properties, not every property. */
109
+ test('a key the table does not mention is not an error', () => {
110
+ expect(bodyViolations(CREATE, { prefix: '10.0.0.0/24', vrf: 4, whatever: 'x' }, true)).toEqual(
111
+ [],
112
+ );
113
+ });
114
+ });
@@ -0,0 +1,152 @@
1
+ /**
2
+ * NetBox's own rules, checked here, before the request is built.
3
+ *
4
+ * 🔴 THE INCIDENT THIS PATTERN EXISTS FOR, 2026-09-22 (Proxmox, not NetBox). `homeflare-proxmox`'s
5
+ * first `deploy:pbs` adopted ten objects and then failed its one create:
6
+ * `comment: value may only be 128 characters long`. Half a deploy in, on the server, from a
7
+ * limit the vendor publishes in its own schema and the generated type did not carry. Plan was
8
+ * green, `bun run check` was green, the adopt verifier was green. ★ NetBox gets the same
9
+ * treatment BEFORE its first write rather than after it.
10
+ *
11
+ * ★ SO THE TABLE IS GENERATED AND THIS FILE IS THE READER. `generated/constraints/*` is emitted
12
+ * from NetBox's own OpenAPI document by `bun codegen/netbox.ts`; nothing here is hand-typed,
13
+ * because a hand-typed limit is tribal knowledge that does not run.
14
+ *
15
+ * ⛔ A SEPARATE READER FROM `../proxmox/constraints.ts`, AND IT IS NOT DUPLICATION. That one
16
+ * checks a `PveForm` — every value already a string, because PVE takes form encoding. A NetBox
17
+ * body is JSON: a number stays a number, a boolean stays a boolean, a foreign key is an
18
+ * integer, and `String(value)` on any of them would make `minimum: 0` pass for `false` and
19
+ * `maxLength` meaningless. Sharing one reader would mean stringifying at the door, which is
20
+ * exactly the mangling the Proxmox version's own ⛔ warns about.
21
+ *
22
+ * ⚠️ WHAT IT DELIBERATELY DOES NOT ENFORCE, so nobody reads a green plan as more than it is:
23
+ * - a `format` NAME (`uri`, `email`, `date-time`): a validator DRF implements server-side.
24
+ * Recorded, never checked.
25
+ * - a pattern the generator could not carry faithfully (codegen/py-pattern.ts). Recorded
26
+ * verbatim as `patternSource`, never checked.
27
+ * - UNIQUENESS, VRF containment, custom-field validators, and every other rule that lives in
28
+ * Django rather than in the schema. Those still arrive as a server-side 400.
29
+ * Each is a fact in the table rather than a guess, which is the rule this follows: if the
30
+ * vendor did not state it, it is not enforced and the table says so.
31
+ */
32
+
33
+ export interface ParamConstraint {
34
+ readonly type?: string;
35
+ /** From the schema's `required` array. ⚠️ Checked on CREATE only — see `violations`. */
36
+ readonly required?: boolean;
37
+ readonly maxLength?: number;
38
+ readonly minLength?: number;
39
+ readonly minimum?: number;
40
+ readonly maximum?: number;
41
+ readonly enum?: readonly string[];
42
+ /** A JavaScript-safe translation. Absent when Python's dialect could not be carried over. */
43
+ readonly pattern?: string;
44
+ /** The vendor's own spelling, always — what a violation message quotes, and the audit trail. */
45
+ readonly patternSource?: string;
46
+ /** DRF's server-side validator name. Recorded so a reader knows what is NOT checked here. */
47
+ readonly format?: string;
48
+ readonly default?: string;
49
+ }
50
+
51
+ export type EndpointConstraints = Readonly<Record<string, ParamConstraint>>;
52
+
53
+ /** `netbox:POST /api/ipam/prefixes/` — exactly how the generated tables key an endpoint. */
54
+ export type EndpointKey = string;
55
+
56
+ /** A NetBox request body: JSON, with the types the schema declares. */
57
+ export type NetboxBody = Record<string, unknown>;
58
+
59
+ export interface ViolationOptions {
60
+ /**
61
+ * Check that every schema-required property is present.
62
+ *
63
+ * ⛔ CREATE ONLY. A NetBox update is a PATCH and is deliberately PARTIAL — it carries the fields
64
+ * that changed and nothing else — so requiring presence there would refuse every ordinary
65
+ * edit.
66
+ */
67
+ readonly presence?: boolean;
68
+ }
69
+
70
+ /**
71
+ * ⚠️ `[...value].length`, NOT `value.length`. Django counts characters (Python `len` on a `str`
72
+ * counts code points); JavaScript counts UTF-16 code units, so an emoji in a description counts
73
+ * twice and a 200-character limit would refuse a description NetBox accepts. Rejecting a legal
74
+ * value at plan time is the one failure mode worse than the server 400 this replaces.
75
+ */
76
+ const lengthOf = (value: string): number => [...value].length;
77
+
78
+ const checkString = (name: string, value: string, rule: ParamConstraint): string[] => {
79
+ const out: string[] = [];
80
+ if (rule.maxLength !== undefined && lengthOf(value) > rule.maxLength) {
81
+ out.push(`${name}: at most ${String(rule.maxLength)} characters`);
82
+ }
83
+ if (rule.minLength !== undefined && lengthOf(value) < rule.minLength) {
84
+ out.push(`${name}: at least ${String(rule.minLength)} characters`);
85
+ }
86
+ if (rule.enum !== undefined && !rule.enum.includes(value)) {
87
+ out.push(`${name}: must be one of ${rule.enum.join(', ')}`);
88
+ }
89
+ if (rule.pattern !== undefined && !new RegExp(rule.pattern).test(value)) {
90
+ out.push(`${name}: must match ${rule.patternSource ?? rule.pattern}`);
91
+ }
92
+ return out;
93
+ };
94
+
95
+ /**
96
+ * ⛔ BOUNDS ARE CHECKED ONLY ON A REAL NUMBER, NEVER ON A COERCED ONE. `Number(false)` is 0 and
97
+ * `Number('')` is 0, so coercing would silently pass a boolean and an empty string through a
98
+ * `minimum: 0`. A NetBox body carries real numbers; anything else is a type error upstream.
99
+ */
100
+ const checkNumber = (name: string, value: number, rule: ParamConstraint): string[] => {
101
+ const out: string[] = [];
102
+ if (!Number.isFinite(value)) return out;
103
+ if (rule.minimum !== undefined && value < rule.minimum) {
104
+ out.push(`${name}: at least ${String(rule.minimum)}`);
105
+ }
106
+ if (rule.maximum !== undefined && value > rule.maximum) {
107
+ out.push(`${name}: at most ${String(rule.maximum)}`);
108
+ }
109
+ return out;
110
+ };
111
+
112
+ const checkValue = (name: string, value: unknown, rule: ParamConstraint): readonly string[] => {
113
+ if (typeof value === 'string') return checkString(name, value, rule);
114
+ if (typeof value === 'number') return checkNumber(name, value, rule);
115
+ return [];
116
+ };
117
+
118
+ /**
119
+ * Every vendor rule this body breaks, in the order the properties are declared. Pure: no network,
120
+ * no clock, no environment — so `diff` can call it and a test can call it with a literal.
121
+ *
122
+ * ⚠️ A KEY THE TABLE DOES NOT MENTION IS NOT AN ERROR. The table carries the constrained
123
+ * properties, not every property, and a body may legitimately send an unconstrained one.
124
+ * ⚠️ `null` IS A VALUE, NOT AN ABSENCE. NetBox uses explicit `null` to CLEAR a nullable foreign
125
+ * key, so it must not trip the presence check — and no string or number rule applies to it.
126
+ */
127
+ export const violations = (
128
+ table: EndpointConstraints,
129
+ body: NetboxBody,
130
+ options: ViolationOptions = {},
131
+ ): readonly string[] => {
132
+ const out: string[] = [];
133
+ for (const [name, rule] of Object.entries(table)) {
134
+ if (!Object.hasOwn(body, name)) {
135
+ if (options.presence === true && rule.required === true) out.push(`${name}: required`);
136
+ continue;
137
+ }
138
+ const value = body[name];
139
+ // ⚠️ AN ARRAY IS A REPEATED VALUE (tags, and every many-to-many), so every element faces the
140
+ // same rule rather than the array being skipped as "not a scalar".
141
+ for (const item of Array.isArray(value) ? value : [value]) {
142
+ out.push(...checkValue(name, item, rule));
143
+ }
144
+ }
145
+ return out;
146
+ };
147
+
148
+ /** The message a plan dies with. ⛔ Names the endpoint, because the rule belongs to the vendor. */
149
+ export const refusal = (endpoint: EndpointKey, found: readonly string[]): string =>
150
+ `${endpoint} would be rejected by the vendor:\n - ${found.join('\n - ')}\n` +
151
+ "These are NetBox's own limits, read from its published OpenAPI document " +
152
+ '(packages/alchemy/src/netbox/generated/constraints). Fix the declaration.';
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Every generated NetBox constraint table, merged — DO NOT EDIT BY HAND.
3
+ *
4
+ * Run: bun codegen/netbox.ts
5
+ *
6
+ * ⚠️ EVERY KEY IS PREFIXED `netbox:` FOR THE SAME REASON THE PROXMOX TABLES ARE PREFIXED. One
7
+ * estate runs several vendors and the reader is shared; an unprefixed `POST /api/…` would be
8
+ * one vendor away from colliding.
9
+ */
10
+ import type { EndpointConstraints } from '../../constraints.ts';
11
+ import { NETBOX_IPAM_CONSTRAINTS } from './netbox-ipam.ts';
12
+
13
+ export const NETBOX_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {
14
+ ...NETBOX_IPAM_CONSTRAINTS,
15
+ };
16
+
17
+ /**
18
+ * sha256 of the merged table, truncated.
19
+ *
20
+ * ★ A DIGEST OF THE DATA, NOT OF THE FILE TEXT, so reformatting is not a stale generation while
21
+ * changing a 200 to a 201 by hand is. `tests/netbox-manifest.test.ts` recomputes it.
22
+ */
23
+ export const NETBOX_CONSTRAINTS_DIGEST = '1de1cebe7491d94f';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Generated NetBox REST API (OpenAPI 3.0.3) request-body constraints for `/api/ipam` — DO NOT EDIT BY HAND.
3
+ *
4
+ * Run: bun codegen/netbox.ts
5
+ * Manifest entry: `netbox-openapi` — NetBox REST API (OpenAPI 3.0.3) 4.7.0
6
+ * sha256 be7f971179b1d6ba, vendor repository, pinned to the release tag
7
+ * contrib/openapi.json in netbox-community/netbox, at tag v4.7.0
8
+ *
9
+ * 2 of this product's 949 POST/PUT/PATCH/DELETE endpoints are tabled across all
10
+ * areas: the ones this package writes to, named in its own source. Every other vendor write
11
+ * endpoint is UNTABLED and therefore unchecked at plan time.
12
+ *
13
+ * ⚠️ A `patternSource` with no `pattern` beside it could NOT be carried into a JavaScript
14
+ * RegExp faithfully — Python's `\w` is Unicode and JavaScript's is ASCII
15
+ * (codegen/py-pattern.ts). It is recorded and NOT enforced.
16
+ * ⚠️ A FOREIGN KEY CARRIES ONLY ITS TYPE. `tenant`, `vlan`, `role` and friends are
17
+ * `integer|object` in the schema, and the related object's own rules do not govern them.
18
+ */
19
+ import type { EndpointConstraints } from '../../constraints.ts';
20
+
21
+ export const NETBOX_IPAM_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {
22
+ "netbox:PATCH /api/ipam/prefixes/{id}/": {
23
+ "description": {"maxLength":200,"type":"string"},
24
+ "prefix": {"minLength":1,"type":"string"},
25
+ "status": {"enum":["container","active","reserved","deprecated"],"type":"string"},
26
+ },
27
+ "netbox:POST /api/ipam/prefixes/": {
28
+ "description": {"maxLength":200,"type":"string"},
29
+ "prefix": {"minLength":1,"required":true,"type":"string"},
30
+ "status": {"enum":["container","active","reserved","deprecated"],"type":"string"},
31
+ },
32
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * NetBox providers for Alchemy.
3
+ *
4
+ * ⛔ THIS BARREL IS THE PUBLIC API, AND IT IS DELIBERATELY SMALLER THAN THE DIRECTORY. These are
5
+ * the symbols a real stack consumes; the rest of the files are internals a provider needs but a
6
+ * consumer should not depend on. An `export *` here would publish every helper as API and make
7
+ * the next refactor a breaking change.
8
+ * ★ The constraint reader IS exported, because a consumer writing its own family needs the same
9
+ * guard — and because a table nobody can reach is a table nobody uses.
10
+ */
11
+ export { NetboxError, apiBase } from './client.ts';
12
+ export {
13
+ NETBOX_CONSTRAINTS,
14
+ bodyViolations,
15
+ constraintsFor,
16
+ guardBody,
17
+ } from './constraint-guard.ts';
18
+ export {
19
+ type EndpointConstraints,
20
+ type EndpointKey,
21
+ type NetboxBody,
22
+ type ParamConstraint,
23
+ refusal,
24
+ violations,
25
+ } from './constraints.ts';
26
+ export { NETBOX_CONSTRAINTS_DIGEST } from './generated/constraints/index.ts';
27
+ export {
28
+ NetboxPrefix,
29
+ NetboxPrefixProvider,
30
+ type PrefixAttributes,
31
+ type PrefixProps,
32
+ type PrefixStatus,
33
+ } from './prefix.ts';
34
+ export { type NetboxRequirements, type NetboxSpec, netboxHandlers } from './resource.ts';
@@ -0,0 +1,127 @@
1
+ /**
2
+ * What an undeclared property means — and the day it meant "delete that".
3
+ *
4
+ * 🔴 THE BUG THIS PINS, FOUND BY REVIEW RATHER THAN BY AN INCIDENT. `Netbox.Prefix` shipped
5
+ * sending `description: ''` whenever the prop was absent. On a create that is invisible. ⛔ On
6
+ * an ADOPT it is data loss: NetBox is the estate's record of DECISIONS, and a prefix's
7
+ * description is usually the only written trace of why that range exists. The first deploy that
8
+ * adopted one would have PATCHed it to empty, and the plan would have read `update` — a diff
9
+ * that looks like converging a declaration and is really deleting a sentence.
10
+ *
11
+ * ★ THE TELL WAS THE INCONSISTENCY, not a failure. Foreign keys were already omitted when
12
+ * undeclared, with a comment saying why; free text was not. Two fields, the same hazard, two
13
+ * answers — that gap is the defect.
14
+ */
15
+ import { describe, expect, test } from 'bun:test';
16
+ import { prefixBody, prefixMatches } from './prefix-form.ts';
17
+ import type { PrefixAttributes } from './prefix.ts';
18
+
19
+ const live = (over: Partial<PrefixAttributes> = {}): PrefixAttributes => ({
20
+ comments: '',
21
+ description: '',
22
+ isPool: false,
23
+ markUtilized: false,
24
+ prefix: '10.0.0.0/24',
25
+ prefixId: 1,
26
+ status: 'active',
27
+ tenant: undefined,
28
+ vlan: undefined,
29
+ vrf: undefined,
30
+ ...over,
31
+ });
32
+
33
+ describe('an undeclared field is not an instruction to clear it', () => {
34
+ test('the body omits description and comments entirely when they are absent', () => {
35
+ const body = prefixBody({ prefix: '10.0.0.0/24' });
36
+ expect(Object.hasOwn(body, 'description')).toBe(false);
37
+ expect(Object.hasOwn(body, 'comments')).toBe(false);
38
+ });
39
+
40
+ /** ⛔ The regression itself: a prefix carrying a human's note must read as converged. */
41
+ test('a live description nobody declared does NOT count as drift', () => {
42
+ expect(
43
+ prefixMatches(live({ description: 'retired 2026-09-14, do not reuse' }), {
44
+ prefix: '10.0.0.0/24',
45
+ }),
46
+ ).toBe(true);
47
+ });
48
+
49
+ test('the same is true of comments and of an optional foreign key', () => {
50
+ const attributes = live({ comments: 'see the retirement doc', tenant: 4, vlan: 9 });
51
+ expect(prefixMatches(attributes, { prefix: '10.0.0.0/24' })).toBe(true);
52
+ });
53
+
54
+ /**
55
+ * ⚠️ THE COST, STATED: prose is cleared by declaring it empty, on purpose. That is the readable
56
+ * way to say a destructive thing, and it still reaches the wire.
57
+ */
58
+ test('an explicitly empty description IS sent and IS compared', () => {
59
+ expect(prefixBody({ description: '', prefix: '10.0.0.0/24' })['description']).toBe('');
60
+ expect(
61
+ prefixMatches(live({ description: 'a note' }), { description: '', prefix: '10.0.0.0/24' }),
62
+ ).toBe(false);
63
+ });
64
+ });
65
+
66
+ describe('a field NetBox itself defaults is still settled', () => {
67
+ /**
68
+ * ★ THE LINE BETWEEN THE TWO GROUPS. `status`, `is_pool` and `mark_utilized` have vendor
69
+ * defaults in the schema, so omitting one genuinely means "the default" and settling it says
70
+ * what NetBox would have done anyway. Free text has no such default.
71
+ */
72
+ test('status, is_pool and mark_utilized are always on the wire', () => {
73
+ const body = prefixBody({ prefix: '10.0.0.0/24' });
74
+ expect(body['status']).toBe('active');
75
+ expect(body['is_pool']).toBe(false);
76
+ expect(body['mark_utilized']).toBe(false);
77
+ });
78
+
79
+ test('a live status that differs from the settled default IS drift', () => {
80
+ expect(prefixMatches(live({ status: 'reserved' }), { prefix: '10.0.0.0/24' })).toBe(false);
81
+ expect(
82
+ prefixMatches(live({ status: 'reserved' }), {
83
+ prefix: '10.0.0.0/24',
84
+ status: 'reserved',
85
+ }),
86
+ ).toBe(true);
87
+ });
88
+
89
+ /** ★ The move this resource exists for: recording a retirement as a declaration. */
90
+ test('declaring deprecated is drift against an active prefix, and converges', () => {
91
+ const props = { prefix: '10.1.1.0/24', status: 'deprecated' } as const;
92
+ expect(prefixMatches(live({ prefix: '10.1.1.0/24' }), props)).toBe(false);
93
+ expect(prefixBody(props)['status']).toBe('deprecated');
94
+ expect(prefixMatches(live({ prefix: '10.1.1.0/24', status: 'deprecated' }), props)).toBe(true);
95
+ });
96
+ });
97
+
98
+ /**
99
+ * ⛔ THE INVARIANT THAT KEEPS THE TWO IN STEP. A field compared but never sent produces a plan
100
+ * that says `update` forever: the PATCH omits it, so the next read is unchanged.
101
+ */
102
+ describe('body and matches agree about which fields are ours', () => {
103
+ test('every field matches compares as optional is one the body sends when declared', () => {
104
+ const declared = prefixBody({
105
+ comments: 'c',
106
+ description: 'd',
107
+ prefix: '10.0.0.0/24',
108
+ tenant: 4,
109
+ vlan: 9,
110
+ });
111
+ for (const key of ['description', 'comments', 'tenant', 'vlan']) {
112
+ expect(Object.hasOwn(declared, key)).toBe(true);
113
+ }
114
+ });
115
+
116
+ test('an undeclared optional is neither sent nor compared', () => {
117
+ const bare = prefixBody({ prefix: '10.0.0.0/24' });
118
+ for (const key of ['description', 'comments', 'tenant', 'vlan', 'vrf']) {
119
+ expect(Object.hasOwn(bare, key)).toBe(false);
120
+ }
121
+ expect(
122
+ prefixMatches(live({ comments: 'x', description: 'y', tenant: 1, vlan: 2 }), {
123
+ prefix: '10.0.0.0/24',
124
+ }),
125
+ ).toBe(true);
126
+ });
127
+ });
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The wire body for a `Netbox.Prefix`, and the comparison that decides whether it needs writing.
3
+ *
4
+ * ★ EXTRACTED SO IT CAN BE TESTED WITHOUT A SERVER. `body` and `matches` are where a NetBox
5
+ * resource either converges or quietly destroys something, and both are pure functions of the
6
+ * props — so they belong somewhere a test can call them with a literal, the way the Proxmox
7
+ * families keep their `*-form.ts` beside the resource.
8
+ */
9
+ import type { PrefixAttributes, PrefixProps, PrefixStatus } from './prefix.ts';
10
+
11
+ /** ⚠️ NetBox's own default when the property is omitted, copied from the schema, not assumed. */
12
+ const DEFAULT_STATUS: PrefixStatus = 'active';
13
+
14
+ /**
15
+ * The fields an undeclared prop still settles, because NetBox itself would.
16
+ *
17
+ * ⛔ FREE TEXT IS NOT IN HERE, AND THE LINE IS NOT COSMETIC. `status`, `is_pool` and
18
+ * `mark_utilized` have vendor defaults in the schema — omitting one genuinely means "the
19
+ * default", so settling it says what NetBox would have done anyway. `description` and
20
+ * `comments` have no such default: the schema's `''` is the absence of a value, not a decision.
21
+ * Treating "I did not mention it" as "set it to empty" is how a declaration that says nothing
22
+ * about prose ERASES prose somebody wrote.
23
+ */
24
+ export const settled = (props: PrefixProps) => ({
25
+ isPool: props.isPool ?? false,
26
+ markUtilized: props.markUtilized ?? false,
27
+ status: props.status ?? DEFAULT_STATUS,
28
+ });
29
+
30
+ /**
31
+ * ⛔ THE BODY IS BUILT ONCE AND USED FOR BOTH CREATE AND UPDATE, so a field cannot be settable on
32
+ * one path and forgotten on the other — the failure mode that makes a resource converge on
33
+ * create and drift forever after.
34
+ *
35
+ * 🔴 AN UNDECLARED FIELD THAT NETBOX DOES NOT DEFAULT IS OMITTED, NEVER SENT EMPTY OR NULL. This
36
+ * resource shipped sending `description: ''` whenever the prop was absent. On a fresh create
37
+ * that is invisible — the field was empty anyway. ⛔ ON AN ADOPT IT IS DATA LOSS: NetBox is the
38
+ * estate's record of DECISIONS, so the description on an existing prefix is usually the only
39
+ * written trace of why that range exists, and the first deploy that adopted it would have
40
+ * PATCHed it to empty. `matches` would have reported drift, the plan would have said `update`,
41
+ * and the diff would have read as converging a declaration rather than deleting a sentence.
42
+ * ★ THE TELL WAS ALREADY IN THIS FILE: foreign keys were omitted for exactly this reason while
43
+ * free text was not. An inconsistency between two fields with the same hazard is the bug.
44
+ * ⚠️ THE COST IS THAT PROSE CANNOT BE CLEARED BY OMISSION — clearing it is `description: ''`,
45
+ * written on purpose, which is the readable way to say a destructive thing.
46
+ */
47
+ export const prefixBody = (props: PrefixProps): Record<string, unknown> => {
48
+ const fixed = settled(props);
49
+ const out: Record<string, unknown> = {
50
+ is_pool: fixed.isPool,
51
+ mark_utilized: fixed.markUtilized,
52
+ prefix: props.prefix,
53
+ status: fixed.status,
54
+ };
55
+ if (props.description !== undefined) out['description'] = props.description;
56
+ if (props.comments !== undefined) out['comments'] = props.comments;
57
+ if (props.vrf !== undefined) out['vrf'] = props.vrf;
58
+ if (props.tenant !== undefined) out['tenant'] = props.tenant;
59
+ if (props.vlan !== undefined) out['vlan'] = props.vlan;
60
+ return out;
61
+ };
62
+
63
+ /**
64
+ * Whether the live object already says what the declaration says.
65
+ *
66
+ * ⛔ EVERY FIELD `prefixBody` OMITS IS ALSO UNCOMPARED HERE, AND THE TWO MUST MOVE TOGETHER. A
67
+ * comparison against a field the body does not send reports drift no write can fix: the plan
68
+ * says `update`, the PATCH omits the field, and the next plan says `update` again — forever.
69
+ * An undeclared prop means "not mine", in both directions.
70
+ * ⚠️ `vrf` IS ABSENT ON PURPOSE. It is identity, not settings: `identifies` has already proved
71
+ * this row is in the declared VRF, and a prefix's VRF cannot be edited into another one here.
72
+ */
73
+ export const prefixMatches = (attributes: PrefixAttributes, props: PrefixProps): boolean => {
74
+ const fixed = settled(props);
75
+ return (
76
+ attributes.status === fixed.status &&
77
+ attributes.isPool === fixed.isPool &&
78
+ attributes.markUtilized === fixed.markUtilized &&
79
+ (props.description === undefined || attributes.description === props.description) &&
80
+ (props.comments === undefined || attributes.comments === props.comments) &&
81
+ (props.tenant === undefined || attributes.tenant === props.tenant) &&
82
+ (props.vlan === undefined || attributes.vlan === props.vlan)
83
+ );
84
+ };