@homeflare/alchemy 0.38.0 → 0.40.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 (353) hide show
  1. package/README.md +32 -8
  2. package/dist/ceph/auth-entity.d.ts +34 -0
  3. package/dist/ceph/auth-entity.d.ts.map +1 -0
  4. package/dist/ceph/ceph-argv-config.d.ts +10 -0
  5. package/dist/ceph/ceph-argv-config.d.ts.map +1 -0
  6. package/dist/ceph/ceph-argv.d.ts +19 -0
  7. package/dist/ceph/ceph-argv.d.ts.map +1 -0
  8. package/dist/ceph/ceph-auth-form.d.ts +54 -0
  9. package/dist/ceph/ceph-auth-form.d.ts.map +1 -0
  10. package/dist/ceph/ceph-auth-parse.d.ts +56 -0
  11. package/dist/ceph/ceph-auth-parse.d.ts.map +1 -0
  12. package/dist/ceph/ceph-auth-reconcile.d.ts +35 -0
  13. package/dist/ceph/ceph-auth-reconcile.d.ts.map +1 -0
  14. package/dist/ceph/ceph-config-options.d.ts +23 -0
  15. package/dist/ceph/ceph-config-options.d.ts.map +1 -0
  16. package/dist/ceph/ceph-key-leak-assert.d.ts +25 -0
  17. package/dist/ceph/ceph-key-leak-assert.d.ts.map +1 -0
  18. package/dist/ceph/ceph-transport.d.ts +37 -0
  19. package/dist/ceph/ceph-transport.d.ts.map +1 -0
  20. package/dist/ceph/fake-ceph-dial.d.ts +35 -0
  21. package/dist/ceph/fake-ceph-dial.d.ts.map +1 -0
  22. package/dist/ceph/index.d.ts +13 -0
  23. package/dist/ceph/index.d.ts.map +1 -0
  24. package/dist/ceph/index.js +1609 -0
  25. package/dist/ceph/index.js.map +38 -0
  26. package/dist/forgejo/index.js +4 -2
  27. package/dist/forgejo/index.js.map +3 -3
  28. package/dist/forgejo/team-member.d.ts +25 -0
  29. package/dist/forgejo/team-member.d.ts.map +1 -1
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/proxmox/generated/constraints/index.d.ts +1 -1
  33. package/dist/proxmox/generated/constraints/index.d.ts.map +1 -1
  34. package/dist/proxmox/generated/constraints/pve-access.d.ts +1 -1
  35. package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts +1 -1
  36. package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts +1 -1
  37. package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts +1 -1
  38. package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts +1 -1
  39. package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts +1 -1
  40. package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts +1 -1
  41. package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts +1 -1
  42. package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts +1 -1
  43. package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts +1 -1
  44. package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts +1 -1
  45. package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts +1 -1
  46. package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts +1 -1
  47. package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts +1 -1
  48. package/dist/proxmox/generated/constraints/pve-nodes-storage.d.ts +23 -0
  49. package/dist/proxmox/generated/constraints/pve-nodes-storage.d.ts.map +1 -0
  50. package/dist/proxmox/generated/constraints/pve-pools.d.ts +1 -1
  51. package/dist/proxmox/generated/constraints/pve-storage.d.ts +1 -1
  52. package/dist/proxmox/index.d.ts +14 -3
  53. package/dist/proxmox/index.d.ts.map +1 -1
  54. package/dist/proxmox/index.js +574 -133
  55. package/dist/proxmox/index.js.map +33 -23
  56. package/dist/proxmox/qemu-form.d.ts +27 -18
  57. package/dist/proxmox/qemu-form.d.ts.map +1 -1
  58. package/dist/proxmox/qemu-judge.d.ts +30 -0
  59. package/dist/proxmox/qemu-judge.d.ts.map +1 -0
  60. package/dist/proxmox/qemu-lifecycle.d.ts +16 -2
  61. package/dist/proxmox/qemu-lifecycle.d.ts.map +1 -1
  62. package/dist/proxmox/qemu-net.d.ts +9 -0
  63. package/dist/proxmox/qemu-net.d.ts.map +1 -0
  64. package/dist/proxmox/qemu-props.d.ts +112 -0
  65. package/dist/proxmox/qemu-props.d.ts.map +1 -0
  66. package/dist/proxmox/qemu-volume.d.ts +27 -0
  67. package/dist/proxmox/qemu-volume.d.ts.map +1 -0
  68. package/dist/proxmox/qemu.d.ts +14 -27
  69. package/dist/proxmox/qemu.d.ts.map +1 -1
  70. package/dist/proxmox/storage-download-errors.d.ts +5 -0
  71. package/dist/proxmox/storage-download-errors.d.ts.map +1 -0
  72. package/dist/proxmox/storage-download-identity.d.ts +27 -0
  73. package/dist/proxmox/storage-download-identity.d.ts.map +1 -0
  74. package/dist/proxmox/storage-download-lifecycle.d.ts +97 -0
  75. package/dist/proxmox/storage-download-lifecycle.d.ts.map +1 -0
  76. package/dist/proxmox/storage-download-props.d.ts +36 -0
  77. package/dist/proxmox/storage-download-props.d.ts.map +1 -0
  78. package/dist/proxmox/storage-download-task.d.ts +17 -0
  79. package/dist/proxmox/storage-download-task.d.ts.map +1 -0
  80. package/dist/proxmox/storage-download.d.ts +44 -0
  81. package/dist/proxmox/storage-download.d.ts.map +1 -0
  82. package/dist/talos/credentials-write.d.ts +46 -0
  83. package/dist/talos/credentials-write.d.ts.map +1 -0
  84. package/dist/talos/credentials.d.ts +51 -9
  85. package/dist/talos/credentials.d.ts.map +1 -1
  86. package/dist/talos/fake-process.d.ts +56 -0
  87. package/dist/talos/fake-process.d.ts.map +1 -0
  88. package/dist/talos/index.d.ts +19 -4
  89. package/dist/talos/index.d.ts.map +1 -1
  90. package/dist/talos/index.js +357 -173
  91. package/dist/talos/index.js.map +16 -10
  92. package/dist/talos/kubeconfig-attrs.d.ts +4 -0
  93. package/dist/talos/kubeconfig-attrs.d.ts.map +1 -0
  94. package/dist/talos/kubeconfig.d.ts +41 -23
  95. package/dist/talos/kubeconfig.d.ts.map +1 -1
  96. package/dist/talos/machine-config-fixtures.d.ts +60 -0
  97. package/dist/talos/machine-config-fixtures.d.ts.map +1 -0
  98. package/dist/talos/machine-config-poll.d.ts +43 -0
  99. package/dist/talos/machine-config-poll.d.ts.map +1 -0
  100. package/dist/talos/machine-config-read.d.ts +49 -0
  101. package/dist/talos/machine-config-read.d.ts.map +1 -0
  102. package/dist/talos/resource.d.ts +18 -0
  103. package/dist/talos/resource.d.ts.map +1 -1
  104. package/dist/talos/talos-bootstrap-etcd.d.ts +38 -0
  105. package/dist/talos/talos-bootstrap-etcd.d.ts.map +1 -0
  106. package/dist/talos/talos-bootstrap.d.ts +87 -0
  107. package/dist/talos/talos-bootstrap.d.ts.map +1 -1
  108. package/dist/talos/talos-cluster-health.d.ts +43 -1
  109. package/dist/talos/talos-cluster-health.d.ts.map +1 -1
  110. package/dist/talos/talos-errors.d.ts +68 -0
  111. package/dist/talos/talos-errors.d.ts.map +1 -0
  112. package/dist/talos/talos-machine-config.d.ts +51 -15
  113. package/dist/talos/talos-machine-config.d.ts.map +1 -1
  114. package/dist/talos/talosctl.d.ts +7 -0
  115. package/dist/talos/talosctl.d.ts.map +1 -1
  116. package/dist/talos/values.d.ts +23 -3
  117. package/dist/talos/values.d.ts.map +1 -1
  118. package/dist/telemetry/fake-collector.d.ts +36 -0
  119. package/dist/telemetry/fake-collector.d.ts.map +1 -0
  120. package/dist/telemetry/index.d.ts +11 -0
  121. package/dist/telemetry/index.d.ts.map +1 -0
  122. package/dist/telemetry/index.js +223 -0
  123. package/dist/telemetry/index.js.map +12 -0
  124. package/dist/telemetry/layer.d.ts +86 -0
  125. package/dist/telemetry/layer.d.ts.map +1 -0
  126. package/dist/telemetry/redact-serialization.d.ts +21 -0
  127. package/dist/telemetry/redact-serialization.d.ts.map +1 -0
  128. package/dist/telemetry/redact.d.ts +46 -0
  129. package/dist/telemetry/redact.d.ts.map +1 -0
  130. package/dist/unifi/acl-rule-drift.d.ts +22 -0
  131. package/dist/unifi/acl-rule-drift.d.ts.map +1 -0
  132. package/dist/unifi/acl-rule-form.d.ts +90 -0
  133. package/dist/unifi/acl-rule-form.d.ts.map +1 -0
  134. package/dist/unifi/acl-rule-ordering-form.d.ts +49 -0
  135. package/dist/unifi/acl-rule-ordering-form.d.ts.map +1 -0
  136. package/dist/unifi/acl-rule-ordering.d.ts +54 -0
  137. package/dist/unifi/acl-rule-ordering.d.ts.map +1 -0
  138. package/dist/unifi/acl-rule.d.ts +49 -0
  139. package/dist/unifi/acl-rule.d.ts.map +1 -0
  140. package/dist/unifi/dns-policy-drift.d.ts +21 -0
  141. package/dist/unifi/dns-policy-drift.d.ts.map +1 -0
  142. package/dist/unifi/dns-policy-form.d.ts +97 -0
  143. package/dist/unifi/dns-policy-form.d.ts.map +1 -0
  144. package/dist/unifi/dns-policy.d.ts +55 -0
  145. package/dist/unifi/dns-policy.d.ts.map +1 -0
  146. package/dist/unifi/drift.d.ts +27 -0
  147. package/dist/unifi/drift.d.ts.map +1 -0
  148. package/dist/unifi/fake-unifi.d.ts +5 -1
  149. package/dist/unifi/fake-unifi.d.ts.map +1 -1
  150. package/dist/unifi/firewall-policy-drift.d.ts +26 -0
  151. package/dist/unifi/firewall-policy-drift.d.ts.map +1 -0
  152. package/dist/unifi/firewall-policy-form.d.ts +102 -0
  153. package/dist/unifi/firewall-policy-form.d.ts.map +1 -0
  154. package/dist/unifi/firewall-policy-ordering-form.d.ts +66 -0
  155. package/dist/unifi/firewall-policy-ordering-form.d.ts.map +1 -0
  156. package/dist/unifi/firewall-policy-ordering.d.ts +57 -0
  157. package/dist/unifi/firewall-policy-ordering.d.ts.map +1 -0
  158. package/dist/unifi/firewall-policy.d.ts +57 -0
  159. package/dist/unifi/firewall-policy.d.ts.map +1 -0
  160. package/dist/unifi/firewall-zone-form.d.ts +6 -0
  161. package/dist/unifi/firewall-zone-form.d.ts.map +1 -1
  162. package/dist/unifi/firewall-zone.d.ts +1 -1
  163. package/dist/unifi/firewall-zone.d.ts.map +1 -1
  164. package/dist/unifi/index.d.ts +11 -3
  165. package/dist/unifi/index.d.ts.map +1 -1
  166. package/dist/unifi/index.js +725 -60
  167. package/dist/unifi/index.js.map +31 -9
  168. package/dist/unifi/network-drift.d.ts +26 -0
  169. package/dist/unifi/network-drift.d.ts.map +1 -0
  170. package/dist/unifi/network-form.d.ts +40 -7
  171. package/dist/unifi/network-form.d.ts.map +1 -1
  172. package/dist/unifi/network.d.ts +1 -0
  173. package/dist/unifi/network.d.ts.map +1 -1
  174. package/dist/unifi/paginate.d.ts +24 -0
  175. package/dist/unifi/paginate.d.ts.map +1 -0
  176. package/dist/unifi/providers.d.ts +1 -1
  177. package/dist/unifi/providers.d.ts.map +1 -1
  178. package/dist/unifi/resource.d.ts +36 -1
  179. package/dist/unifi/resource.d.ts.map +1 -1
  180. package/dist/unifi/scan-source.d.ts +11 -0
  181. package/dist/unifi/scan-source.d.ts.map +1 -0
  182. package/dist/unifi/traffic-matching-list-drift.d.ts +22 -0
  183. package/dist/unifi/traffic-matching-list-drift.d.ts.map +1 -0
  184. package/dist/unifi/traffic-matching-list-form.d.ts +56 -0
  185. package/dist/unifi/traffic-matching-list-form.d.ts.map +1 -0
  186. package/dist/unifi/traffic-matching-list.d.ts +51 -0
  187. package/dist/unifi/traffic-matching-list.d.ts.map +1 -0
  188. package/dist/unifi/wifi-broadcast-drift.d.ts +22 -0
  189. package/dist/unifi/wifi-broadcast-drift.d.ts.map +1 -0
  190. package/dist/unifi/wifi-broadcast-form.d.ts +127 -0
  191. package/dist/unifi/wifi-broadcast-form.d.ts.map +1 -0
  192. package/dist/unifi/wifi-broadcast.d.ts +73 -0
  193. package/dist/unifi/wifi-broadcast.d.ts.map +1 -0
  194. package/docs/telemetry-spike.md +94 -0
  195. package/docs/telemetry.md +109 -0
  196. package/docs/unifi-acl-rule.md +122 -0
  197. package/docs/unifi-dns-policy.md +75 -0
  198. package/docs/unifi-firewall-policy.md +109 -0
  199. package/docs/unifi-traffic-matching-list.md +82 -0
  200. package/docs/unifi-wifi.md +120 -0
  201. package/docs/unifi.md +64 -3
  202. package/package.json +10 -2
  203. package/src/ceph/auth-entity.ts +74 -0
  204. package/src/ceph/ceph-argv-config.ts +78 -0
  205. package/src/ceph/ceph-argv.test.ts +226 -0
  206. package/src/ceph/ceph-argv.ts +189 -0
  207. package/src/ceph/ceph-auth-form.test.ts +103 -0
  208. package/src/ceph/ceph-auth-form.ts +107 -0
  209. package/src/ceph/ceph-auth-parse.test.ts +155 -0
  210. package/src/ceph/ceph-auth-parse.ts +121 -0
  211. package/src/ceph/ceph-auth-reconcile-secrecy.test.ts +82 -0
  212. package/src/ceph/ceph-auth-reconcile.test.ts +233 -0
  213. package/src/ceph/ceph-auth-reconcile.ts +119 -0
  214. package/src/ceph/ceph-config-options.test.ts +44 -0
  215. package/src/ceph/ceph-config-options.ts +36 -0
  216. package/src/ceph/ceph-key-leak-assert.ts +35 -0
  217. package/src/ceph/ceph-no-output-logging.test.ts +52 -0
  218. package/src/ceph/ceph-transport.test.ts +125 -0
  219. package/src/ceph/ceph-transport.ts +117 -0
  220. package/src/ceph/fake-ceph-dial.ts +55 -0
  221. package/src/ceph/index.ts +12 -0
  222. package/src/forgejo/team-member.test.ts +14 -0
  223. package/src/forgejo/team-member.ts +28 -1
  224. package/src/index.ts +1 -0
  225. package/src/proxmox/constraints-required.test.ts +1 -0
  226. package/src/proxmox/generated/constraints/index.ts +3 -1
  227. package/src/proxmox/generated/constraints/pve-access.ts +1 -1
  228. package/src/proxmox/generated/constraints/pve-cluster-backup.ts +1 -1
  229. package/src/proxmox/generated/constraints/pve-cluster-ceph.ts +1 -1
  230. package/src/proxmox/generated/constraints/pve-cluster-firewall.ts +1 -1
  231. package/src/proxmox/generated/constraints/pve-cluster-ha.ts +1 -1
  232. package/src/proxmox/generated/constraints/pve-cluster-metrics.ts +1 -1
  233. package/src/proxmox/generated/constraints/pve-cluster-notifications.ts +1 -1
  234. package/src/proxmox/generated/constraints/pve-cluster-replication.ts +1 -1
  235. package/src/proxmox/generated/constraints/pve-cluster-sdn.ts +1 -1
  236. package/src/proxmox/generated/constraints/pve-nodes-ceph.ts +1 -1
  237. package/src/proxmox/generated/constraints/pve-nodes-disks.ts +1 -1
  238. package/src/proxmox/generated/constraints/pve-nodes-lxc.ts +1 -1
  239. package/src/proxmox/generated/constraints/pve-nodes-network.ts +1 -1
  240. package/src/proxmox/generated/constraints/pve-nodes-qemu.ts +1 -1
  241. package/src/proxmox/generated/constraints/pve-nodes-storage.ts +30 -0
  242. package/src/proxmox/generated/constraints/pve-pools.ts +1 -1
  243. package/src/proxmox/generated/constraints/pve-storage.ts +1 -1
  244. package/src/proxmox/index.test.ts +11 -6
  245. package/src/proxmox/index.ts +19 -3
  246. package/src/proxmox/qemu-form.ts +49 -13
  247. package/src/proxmox/qemu-identity.test.ts +30 -0
  248. package/src/proxmox/qemu-judge.test.ts +65 -0
  249. package/src/proxmox/qemu-judge.ts +84 -0
  250. package/src/proxmox/qemu-lifecycle.test.ts +78 -19
  251. package/src/proxmox/qemu-lifecycle.ts +131 -32
  252. package/src/proxmox/qemu-net.test.ts +48 -0
  253. package/src/proxmox/qemu-net.ts +78 -0
  254. package/src/proxmox/qemu-ownership.test.ts +44 -0
  255. package/src/proxmox/qemu-props.test.ts +87 -0
  256. package/src/proxmox/qemu-props.ts +157 -0
  257. package/src/proxmox/qemu-volume.test.ts +229 -0
  258. package/src/proxmox/qemu-volume.ts +178 -0
  259. package/src/proxmox/qemu.ts +15 -29
  260. package/src/proxmox/storage-download-errors.ts +7 -0
  261. package/src/proxmox/storage-download-identity.test.ts +54 -0
  262. package/src/proxmox/storage-download-identity.ts +50 -0
  263. package/src/proxmox/storage-download-lifecycle.ts +226 -0
  264. package/src/proxmox/storage-download-ownership.test.ts +50 -0
  265. package/src/proxmox/storage-download-props.ts +38 -0
  266. package/src/proxmox/storage-download-task.ts +72 -0
  267. package/src/proxmox/storage-download.test.ts +216 -0
  268. package/src/proxmox/storage-download.ts +58 -0
  269. package/src/talos/credentials-write.test.ts +81 -0
  270. package/src/talos/credentials-write.ts +95 -0
  271. package/src/talos/credentials.test.ts +157 -0
  272. package/src/talos/credentials.ts +160 -90
  273. package/src/talos/fake-process.ts +94 -0
  274. package/src/talos/index.ts +39 -4
  275. package/src/talos/kubeconfig-attrs.ts +55 -0
  276. package/src/talos/kubeconfig.test.ts +256 -0
  277. package/src/talos/kubeconfig.ts +170 -90
  278. package/src/talos/machine-config-fixtures.ts +86 -0
  279. package/src/talos/machine-config-poll.test.ts +57 -0
  280. package/src/talos/machine-config-poll.ts +82 -0
  281. package/src/talos/machine-config-read.ts +118 -0
  282. package/src/talos/resource.ts +19 -0
  283. package/src/talos/secrets-gate.test.ts +63 -0
  284. package/src/talos/talos-bootstrap-etcd.ts +71 -0
  285. package/src/talos/talos-bootstrap.test.ts +218 -0
  286. package/src/talos/talos-bootstrap.ts +151 -45
  287. package/src/talos/talos-cluster-health.test.ts +143 -0
  288. package/src/talos/talos-cluster-health.ts +91 -28
  289. package/src/talos/talos-errors.ts +85 -0
  290. package/src/talos/talos-machine-config.test.ts +189 -0
  291. package/src/talos/talos-machine-config.ts +102 -88
  292. package/src/talos/talosctl.ts +8 -0
  293. package/src/talos/values.test.ts +34 -6
  294. package/src/talos/values.ts +41 -7
  295. package/src/telemetry/fake-collector.ts +59 -0
  296. package/src/telemetry/http-client-sealing.test.ts +82 -0
  297. package/src/telemetry/index.ts +10 -0
  298. package/src/telemetry/layer.test.ts +204 -0
  299. package/src/telemetry/layer.ts +152 -0
  300. package/src/telemetry/redact-serialization.ts +178 -0
  301. package/src/telemetry/redact.test.ts +124 -0
  302. package/src/telemetry/redact.ts +147 -0
  303. package/src/unifi/acl-rule-drift.ts +57 -0
  304. package/src/unifi/acl-rule-form.ts +135 -0
  305. package/src/unifi/acl-rule-ordering-form.ts +79 -0
  306. package/src/unifi/acl-rule-ordering.test.ts +141 -0
  307. package/src/unifi/acl-rule-ordering.ts +70 -0
  308. package/src/unifi/acl-rule.test.ts +183 -0
  309. package/src/unifi/acl-rule.ts +61 -0
  310. package/src/unifi/discriminated-filter-decode.test.ts +134 -0
  311. package/src/unifi/dns-policy-drift.ts +48 -0
  312. package/src/unifi/dns-policy-form.ts +142 -0
  313. package/src/unifi/dns-policy.test.ts +154 -0
  314. package/src/unifi/dns-policy.ts +67 -0
  315. package/src/unifi/drift.test.ts +78 -0
  316. package/src/unifi/drift.ts +56 -0
  317. package/src/unifi/errors-and-secrets.test.ts +127 -0
  318. package/src/unifi/fake-unifi.ts +5 -1
  319. package/src/unifi/firewall-policy-drift.test.ts +151 -0
  320. package/src/unifi/firewall-policy-drift.ts +55 -0
  321. package/src/unifi/firewall-policy-filter-roundtrip.test.ts +143 -0
  322. package/src/unifi/firewall-policy-form.ts +146 -0
  323. package/src/unifi/firewall-policy-ordering-form.ts +111 -0
  324. package/src/unifi/firewall-policy-ordering.test.ts +185 -0
  325. package/src/unifi/firewall-policy-ordering.ts +87 -0
  326. package/src/unifi/firewall-policy.test.ts +183 -0
  327. package/src/unifi/firewall-policy.ts +73 -0
  328. package/src/unifi/firewall-zone-form.ts +31 -2
  329. package/src/unifi/firewall-zone.test.ts +21 -1
  330. package/src/unifi/firewall-zone.ts +1 -1
  331. package/src/unifi/get-only-guard.test.ts +165 -0
  332. package/src/unifi/index.ts +78 -1
  333. package/src/unifi/network-drift.test.ts +207 -0
  334. package/src/unifi/network-drift.ts +80 -0
  335. package/src/unifi/network-form.ts +11 -39
  336. package/src/unifi/network.test.ts +7 -82
  337. package/src/unifi/network.ts +6 -6
  338. package/src/unifi/paginate.test.ts +252 -0
  339. package/src/unifi/paginate.ts +141 -0
  340. package/src/unifi/providers.ts +19 -1
  341. package/src/unifi/resource.ts +73 -3
  342. package/src/unifi/scan-source.ts +64 -0
  343. package/src/unifi/traffic-matching-list-drift.ts +40 -0
  344. package/src/unifi/traffic-matching-list-form.ts +77 -0
  345. package/src/unifi/traffic-matching-list.test.ts +202 -0
  346. package/src/unifi/traffic-matching-list.ts +78 -0
  347. package/src/unifi/wifi-broadcast-details-call.test.ts +92 -0
  348. package/src/unifi/wifi-broadcast-drift.ts +69 -0
  349. package/src/unifi/wifi-broadcast-form.ts +211 -0
  350. package/src/unifi/wifi-broadcast-secrets.test.ts +145 -0
  351. package/src/unifi/wifi-broadcast.test.ts +155 -0
  352. package/src/unifi/wifi-broadcast.ts +90 -0
  353. package/src/unifi/write-op-reference.test.ts +190 -0
@@ -1,16 +1,38 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/unifi/policy.ts", "../src/unifi/network.ts", "../src/unifi/network-form.ts", "../src/unifi/resource.ts", "../src/unifi/firewall-zone.ts", "../src/unifi/firewall-zone-form.ts", "../src/unifi/providers.ts"],
3
+ "sources": ["../src/unifi/policy.ts", "../src/unifi/resource.ts", "../src/unifi/network.ts", "../src/unifi/network-form.ts", "../src/unifi/drift.ts", "../src/unifi/network-drift.ts", "../src/unifi/firewall-zone.ts", "../src/unifi/firewall-zone-form.ts", "../src/unifi/dns-policy.ts", "../src/unifi/dns-policy-form.ts", "../src/unifi/dns-policy-drift.ts", "../src/unifi/acl-rule.ts", "../src/unifi/acl-rule-form.ts", "../src/unifi/acl-rule-drift.ts", "../src/unifi/acl-rule-ordering.ts", "../src/unifi/acl-rule-ordering-form.ts", "../src/unifi/wifi-broadcast.ts", "../src/unifi/wifi-broadcast-form.ts", "../src/unifi/wifi-broadcast-drift.ts", "../src/unifi/paginate.ts", "../src/unifi/firewall-policy.ts", "../src/unifi/firewall-policy-form.ts", "../src/unifi/firewall-policy-drift.ts", "../src/unifi/firewall-policy-ordering.ts", "../src/unifi/firewall-policy-ordering-form.ts", "../src/unifi/traffic-matching-list.ts", "../src/unifi/traffic-matching-list-form.ts", "../src/unifi/traffic-matching-list-drift.ts", "../src/unifi/providers.ts"],
4
4
  "sourcesContent": [
5
5
  "/**\n * The one refusal every `Unifi.*` write handler returns — never an SDK write call.\n *\n * ⛔ TIM'S RULE, 2026-09-24: UniFi (and OPNsense) are READ-ONLY FOR NOW. This family has no\n * `create`, no `update` body and no delete call anywhere in its source — `reconcile` and\n * `delete` both fail with this typed error instead, and `resource.test.ts` proves the point\n * with a fake `HttpClient` that fails the test on any request that is not a `GET`. Lifting the\n * policy is a kit change (a new PR that adds a write path, reviewed as one), not a flag a stack\n * can pass.\n *\n * ★ A TYPED TAG, NOT A THROWN STRING (S21). `Effect.catchTag('UnifiWriteRefused', …)` lets a\n * caller (a future import script, a test) branch on the refusal without parsing a message.\n */\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\n\nexport const UNIFI_READ_ONLY_POLICY =\n \"read-only by Tim's rule, 2026-09-24; lifting it is a kit change\";\n\n/** What kind of write was asked for and refused — for the message only, never acted on. */\nexport type UnifiWriteAction = 'create' | 'update' | 'delete';\n\nexport class UnifiWriteRefused extends Data.TaggedError('UnifiWriteRefused')<{\n /** The vendor type string, e.g. `Unifi.Network`. */\n readonly type: string;\n /** `spec.describe(props)` — the object the write would have touched. */\n readonly identity: string;\n readonly action: UnifiWriteAction;\n}> {\n override get message(): string {\n return `${this.type} ${this.identity}: refusing to ${this.action} -- ${UNIFI_READ_ONLY_POLICY}.`;\n }\n}\n\n/** The only thing `reconcile`/`delete` ever return for a write this family cannot make. */\nexport const refuseWrite = (type: string, identity: string, action: UnifiWriteAction) =>\n Effect.fail(new UnifiWriteRefused({ type, identity, action }));\n",
6
- "/**\n * `Unifi.Network` — one network on a UniFi Network site, READ-ONLY (`policy.ts`).\n *\n * ★ WHY NETWORK IS IN THE FIRST IMPORT SET. `getNetworkDetails`/`getNetworksOverviewPage`\n * (`../../distilled-unifi-network/src/services/networks.ts`) is a list+get pair over\n * genuine configuration — VLAN, DHCP guarding, IPv4/IPv6 addressing, isolation — not runtime\n * state like a connected client, a device statistic or a hotspot voucher. It is also the SDK\n * package's own README recommendation for a first read/adopt-only resource, alongside `Site`\n * (skipped here — no `getSite`, only the list operation, so it fails this task's own \"both a\n * list and a get\" bar) and `FirewallZone`/`FirewallPolicy` (`firewall-zone.ts` ships\n * `FirewallZone`; `FirewallPolicy`'s several discriminated filter variants and its\n * list-replacing ordering endpoint are a bigger, separate PR).\n *\n * ⛔ THE WHOLE-OBJECT-PUT TRAP (`docs/unifi-api-notes.md`) NEVER FIRES HERE, BY CONSTRUCTION.\n * `updateNetwork` is a `PUT` that disables `dhcpGuarding`/`ipv4Configuration`/`ipv6Configuration`\n * outright when the caller omits them — the exact failure mode a read-only family cannot cause,\n * because `resource.ts`'s `reconcile` never calls `updateNetwork` at all. Recorded here for\n * whoever adds the write path this docs note anticipates: read-merge-write the WHOLE object,\n * never a props-only body.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as networks from '@distilled.cloud/unifi-network/networks';\nimport * as Effect from 'effect/Effect';\nimport {\n type NetworkAttributes,\n type NetworkProps,\n attributesOf,\n matches,\n} from './network-form.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { NetworkAttributes, NetworkProps } from './network-form.ts';\nexport { declareNetwork } from './network-form.ts';\n\nexport interface UnifiNetwork extends Resource<\n 'Unifi.Network',\n NetworkProps,\n NetworkAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiNetwork = Resource<UnifiNetwork>('Unifi.Network', {\n defaultRemovalPolicy: 'retain',\n});\n\n/**\n * `network('lan', props)` — `adopt(true)` piped on by default (H5). This family never writes, so\n * the only thing a first deploy against an existing network could ever do is bind to it or refuse\n * `OwnedBySomeoneElse`; piping `adopt(true)` here picks the former without a stack having to\n * remember `--adopt`, mirroring `discord/application-command.ts`'s `applicationCommand`.\n */\nexport const network = (id: string, props: NetworkProps) =>\n UnifiNetwork(id, props).pipe(adopt(true));\n\n/**\n * ★ EXPORTED for direct testing against a fake `Credentials` layer (`fake-unifi.ts`), the same\n * seam `netbox/prefix.ts`'s `spec` and `discord/application-command.ts`'s `spec` use — `handlers`\n * bakes in `CredentialsFromEnv`, which resolves environment variables a test cannot repoint.\n */\nexport const spec: UnifiSpec<\n NetworkProps,\n networks.NetworkDetails,\n NetworkAttributes,\n networks.GetNetworkDetailsError\n> = {\n type: 'Unifi.Network',\n describe: (props) => `sites/${props.siteId}/networks/${props.networkId}`,\n fetchLive: (props) =>\n networks\n .getNetworkDetails({ siteId: props.siteId, networkId: props.networkId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiNetworkProvider = () =>\n Provider.effect(UnifiNetwork, Effect.succeed(UnifiNetwork.Provider.of(handlers)));\n",
7
- "/**\n * `Unifi.Network`'s wire shape: the declarable subset of `NetworkDetails`, and how a live read\n * becomes both plan attributes and a rendered declaration.\n *\n * ⚠️ PROPS MIRROR `UpdateNetworkRequest`, NOT `NetworkDetails`. `default` and `metadata` are\n * server-derived — no writable endpoint in `networks.ts` accepts either — so they are\n * attributes-only, reported but never compared or declared. Everything else in `NetworkProps`\n * is exactly what `updateNetwork`/`createNetwork` would accept, even though this family never\n * calls either: the shape is what a future write path would need, and what today's read-only\n * `matches` compares against.\n *\n * ⛔ `ipv4Configuration` STAYS `unknown`, ON THE SDK'S OWN AUTHORITY. `networks.ts`'s own comment\n * (\"Shape varies by variant — widened by scripts/convert.ts; see README\") says the OpenAPI\n * discriminator this field varies over was flattened at generation time, not decoded per\n * variant. Comparing it as an opaque JSON value (below) is honest about that; guessing a\n * narrower shape here would silently stop matching the day a new variant's key order differs.\n * ⚠️ It may ALSO hide an unordered array the way `dhcpGuarding` and `ipv6Configuration` do\n * below — this family just can't know, because the shape is never decoded. If a future variant\n * plans spurious `update`s, suspect this field first and widen it into a typed, order-normalized\n * shape rather than adding another opaque comparison.\n *\n * ⛔ THREE FIELDS ARE SETS, NOT SEQUENCES: `dhcpGuarding.trustedDhcpServerIpAddresses`,\n * `ipv6Configuration.additionalHostIpSubnets`, and `ipv6Configuration.dnsServerIpAddressesOverride`.\n * The vendor document gives no indication any of the three is ordered — same reasoning\n * `firewall-zone-form.ts` gives for `networkIds`, and `proxmox/user-wire.ts`'s `groupSet` gives\n * for PVE's `groups` — but `matches` compares via alchemy's `deepEqual`, which sorts object KEYS\n * but NOT ARRAY ELEMENTS (`node_modules/alchemy/lib/Diff.js`). Left alone, a console that\n * returns the same set in a different order between calls plans a spurious `update`, and the\n * read-only reconcile then refuses every deploy. `sortedSet` (dedupe + sort) neutralizes that\n * before any of the three is attributed, declared, or compared. Every OTHER array field in this\n * family (there are none today besides these three) would stay declaration-order as written.\n */\nimport { deepEqual } from 'alchemy/Diff';\nimport type * as networks from '@distilled.cloud/unifi-network/networks';\n\nconst sortedSet = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\n// ⚠️ `value == null` (NOT `=== undefined`) ON PURPOSE, IN BOTH NORMALIZERS BELOW. The SDK types\n// `dhcpGuarding`/`ipv6Configuration` `T | undefined`, but `matches`'s own header says UniFi's\n// JSON answers `null` for an unset optional field — a reality `deepEqual`'s `stripNullish`\n// already treats as equivalent to `undefined` (`value == null` in alchemy's `Diff.js`), which\n// this family's props/attributes types don't model at the TS level. Destructuring a `null` with\n// `=== undefined`'s false branch (`{ ...value, field: value.field }`) would throw\n// `TypeError: Cannot read properties of null` the first time a console omits either field —\n// `== null` catches both and returns the value through untouched, matching what the rest of\n// this file already does for every other optional field.\nconst normalizeDhcpGuarding = (\n value: networks.NetworkDHCPGuarding | undefined,\n): networks.NetworkDHCPGuarding | undefined =>\n value == null\n ? value\n : { ...value, trustedDhcpServerIpAddresses: sortedSet(value.trustedDhcpServerIpAddresses) };\n\n// ⚠️ `exactOptionalPropertyTypes` (see the props header) means these two subfields must be\n// OMITTED, not set to an explicit `undefined`, when the live/declared value has no override —\n// `NetworkIPv6Configuration` types them `field?: T`, not `field?: T | undefined`. Conditional\n// spread (rather than `field: value.field === undefined ? undefined : sortedSet(...)`) is what\n// keeps an absent key absent.\nconst normalizeIpv6Configuration = (\n value: networks.NetworkIPv6Configuration | undefined,\n): networks.NetworkIPv6Configuration | undefined =>\n value == null\n ? value\n : {\n ...value,\n ...(value.additionalHostIpSubnets !== undefined\n ? { additionalHostIpSubnets: sortedSet(value.additionalHostIpSubnets) }\n : {}),\n ...(value.dnsServerIpAddressesOverride !== undefined\n ? { dnsServerIpAddressesOverride: sortedSet(value.dnsServerIpAddressesOverride) }\n : {}),\n };\n\n/**\n * ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` ON PURPOSE. `tsconfig.json` sets\n * `exactOptionalPropertyTypes`, which treats `field?: T` (absent-or-T) and\n * `field?: T | undefined` (absent-or-explicitly-undefined) as different types. `declareNetwork`\n * below assigns `live.dhcpGuarding` (typed `T | undefined`) straight through — the whole point\n * of a renderer that echoes a live optional field structurally rather than defaulting it — so\n * the target has to admit an explicit `undefined`, not just omission.\n */\nexport interface NetworkProps {\n siteId: string;\n networkId: string;\n name: string;\n enabled: boolean;\n management: string;\n /** VLAN ID. Must be 1 for the default network and >= 2 for additional networks. */\n vlanId: number;\n dhcpGuarding?: networks.NetworkDHCPGuarding | undefined;\n cellularBackupEnabled?: boolean | undefined;\n internetAccessEnabled?: boolean | undefined;\n /** Opaque — see the header. */\n ipv4Configuration?: unknown;\n ipv6Configuration?: networks.NetworkIPv6Configuration | undefined;\n isolationEnabled?: boolean | undefined;\n mdnsForwardingEnabled?: boolean | undefined;\n zoneId?: string | undefined;\n deviceId?: string | undefined;\n}\n\nexport interface NetworkAttributes {\n siteId: string;\n networkId: string;\n /** Whether this is the site's undeletable default network — server-derived, never declared. */\n default: boolean;\n name: string;\n enabled: boolean;\n management: string;\n vlanId: number;\n dhcpGuarding: networks.NetworkDHCPGuarding | undefined;\n cellularBackupEnabled: boolean | undefined;\n internetAccessEnabled: boolean | undefined;\n ipv4Configuration: unknown;\n ipv6Configuration: networks.NetworkIPv6Configuration | undefined;\n isolationEnabled: boolean | undefined;\n mdnsForwardingEnabled: boolean | undefined;\n zoneId: string | undefined;\n deviceId: string | undefined;\n}\n\nexport const attributesOf = (\n live: networks.NetworkDetails,\n props: NetworkProps,\n): NetworkAttributes => ({\n siteId: props.siteId,\n networkId: live.id,\n default: live.default,\n name: live.name,\n enabled: live.enabled,\n management: live.management,\n vlanId: live.vlanId,\n dhcpGuarding: normalizeDhcpGuarding(live.dhcpGuarding),\n cellularBackupEnabled: live.cellularBackupEnabled,\n internetAccessEnabled: live.internetAccessEnabled,\n ipv4Configuration: live.ipv4Configuration,\n ipv6Configuration: normalizeIpv6Configuration(live.ipv6Configuration),\n isolationEnabled: live.isolationEnabled,\n mdnsForwardingEnabled: live.mdnsForwardingEnabled,\n zoneId: live.zoneId,\n deviceId: live.deviceId,\n});\n\n/**\n * ⚠️ `stripNullish: true` ON EVERY COMPARISON. UniFi's JSON answers `null` for an unset optional\n * field; a declaration that omits the same prop carries `undefined`. Without this option every\n * plan against an object with one unset optional field would report `update` forever — the\n * exact \"read and write are different shapes\" trap `netbox/values.ts` and `proxmox/user-wire.ts`\n * both work around, here solved once with the engine's own `deepEqual` instead of a per-field\n * coercion, because nothing here needs PVE's or NetBox's extra wire-format translation.\n */\nexport const matches = (attributes: NetworkAttributes, props: NetworkProps): boolean =>\n attributes.name === props.name &&\n attributes.enabled === props.enabled &&\n attributes.management === props.management &&\n attributes.vlanId === props.vlanId &&\n deepEqual(\n normalizeDhcpGuarding(attributes.dhcpGuarding),\n normalizeDhcpGuarding(props.dhcpGuarding),\n {\n stripNullish: true,\n },\n ) &&\n deepEqual(attributes.cellularBackupEnabled, props.cellularBackupEnabled, {\n stripNullish: true,\n }) &&\n deepEqual(attributes.internetAccessEnabled, props.internetAccessEnabled, {\n stripNullish: true,\n }) &&\n deepEqual(attributes.ipv4Configuration, props.ipv4Configuration, { stripNullish: true }) &&\n deepEqual(\n normalizeIpv6Configuration(attributes.ipv6Configuration),\n normalizeIpv6Configuration(props.ipv6Configuration),\n { stripNullish: true },\n ) &&\n deepEqual(attributes.isolationEnabled, props.isolationEnabled, { stripNullish: true }) &&\n deepEqual(attributes.mdnsForwardingEnabled, props.mdnsForwardingEnabled, {\n stripNullish: true,\n }) &&\n deepEqual(attributes.zoneId, props.zoneId, { stripNullish: true }) &&\n deepEqual(attributes.deviceId, props.deviceId, { stripNullish: true });\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getNetworkDetails`'s own response\n * straight in and writes the result into `alchemy.run.ts` as `network('<id>', declareNetwork(live,\n * siteId))` — no field is invented or defaulted here beyond what `attributesOf` already reports,\n * so `matches(attributesOf(live, props), declareNetwork(live, siteId))` is `true` by construction\n * (`network.test.ts` proves it) for any live object this SDK can decode.\n */\nexport const declareNetwork = (live: networks.NetworkDetails, siteId: string): NetworkProps => ({\n siteId,\n networkId: live.id,\n name: live.name,\n enabled: live.enabled,\n management: live.management,\n vlanId: live.vlanId,\n dhcpGuarding: normalizeDhcpGuarding(live.dhcpGuarding),\n cellularBackupEnabled: live.cellularBackupEnabled,\n internetAccessEnabled: live.internetAccessEnabled,\n ipv4Configuration: live.ipv4Configuration,\n ipv6Configuration: normalizeIpv6Configuration(live.ipv6Configuration),\n isolationEnabled: live.isolationEnabled,\n mdnsForwardingEnabled: live.mdnsForwardingEnabled,\n zoneId: live.zoneId,\n deviceId: live.deviceId,\n});\n",
8
- "/**\n * One shape for every UniFi Network read-only object — `Unifi.Network`, `Unifi.FirewallZone`,\n * and whatever import adds next.\n *\n * ★ MIRRORS `../discord/resource.ts` (S7-S10, H1, H5), NOT `../netbox/resource.ts`. Both are\n * \"marker-less API\" families (S8): UniFi Network objects carry no tag or metadata field this\n * stack could stamp as ownership, so a cold read of a match is `Unowned(attrs)` exactly like\n * `Cloudflare/Snippets/Snippet.ts@v2.0.0-beta.79#read`, the reference implementation S8 and H1\n * both cite. `adopt(true)`, piped on by each resource's convenience constructor (H5), turns\n * that into a silent one-time bind instead of an `OwnedBySomeoneElse` refusal.\n *\n * ⛔ THE DIFFERENCE FROM EVERY OTHER FAMILY HERE: THERE IS NO `create`, `update` OR `destroy` IN\n * THIS SPEC AT ALL. Tim's rule, 2026-09-24 (`policy.ts`): UniFi and OPNsense are read-only. So\n * `reconcile` below can only ever refuse or report an exact match — it has no write path to\n * fall into by accident, and no resource file wired through this engine can reach one either.\n *\n * ⚠️ RECONCILE STILL HAS TO SUCCEED ON AN EXACT-MATCH ADOPTION, NOT JUST REFUSE UNCONDITIONALLY.\n * Measured against `Plan.ts@v2.0.0-beta.79` (`forceUpdateAfterAdoption`, H6): beta.79 forces\n * ONE reconcile call after every cold adoption, whether or not the diff said noop — so a\n * resource whose `reconcile` always failed would break the ordinary \"adopt an unchanged\n * object\" deploy, not just a real write. The fix, exactly like `discordOperations.reconcile`:\n * read the live object again, and refuse ONLY when it is missing (would need a create) or\n * drifted (would need a write to converge). An exact match returns the live attributes and\n * calls nothing — S10's \"zero write calls when there is no drift\" holds even under the forced\n * post-adoption call.\n *\n * ⚠️ `NotFound` IS FOLDED TO \"ABSENT\" INSIDE EACH RESOURCE FILE'S OWN `fetchLive`, NOT HERE —\n * same split netbox's `resource.ts` uses and for the same reason: `Effect.catchTag`'s\n * tag-literal inference needs a concrete error union to resolve `\"NotFound\"` against, and a\n * spec's `E` is only concrete at each resource file's own call site. Every OTHER failure (a\n * real 5xx, a network error, `Forbidden`) is NOT folded anywhere — it fails the plan loudly,\n * per the task's own rule: only a genuine not-found may mean absent.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { CredentialsFromEnv } from '@distilled.cloud/unifi-network/Credentials';\nimport type { UnifiNetworkOpContext } from '@distilled.cloud/unifi-network/Protocol';\nimport * as Effect from 'effect/Effect';\nimport type * as HttpClient from 'effect/unstable/http/HttpClient';\nimport { refuseWrite } from './policy.ts';\n\n/** What every handler needs from the caller's runtime once `CredentialsFromEnv` is baked in. */\nexport type UnifiRequirements = HttpClient.HttpClient;\n\n/**\n * One UniFi Network object's read. `Live` is whatever the SDK decodes (a `NetworkDetails`, a\n * `FirewallZone`, …); `E` is left to each resource file to declare (its own `fetchLive`'s error\n * union, with `NotFound` already folded away) so the precise per-operation error union flows\n * through instead of being widened by hand here.\n */\nexport type UnifiSpec<Props extends object, Live, Attributes extends object, E> = {\n /** The vendor type string, e.g. `Unifi.Network` — for the refusal message only. */\n readonly type: string;\n /** Already folds a genuine not-found to `undefined`; nothing else is folded (see header). */\n readonly fetchLive: (props: Props) => Effect.Effect<Live | undefined, E, UnifiNetworkOpContext>;\n readonly attributes: (live: Live, props: Props) => Attributes;\n readonly matches: (attributes: Attributes, props: Props) => boolean;\n /** For the refusal message and read-back failures. */\n readonly describe: (props: Props) => string;\n};\n\nexport const unifiOperations = <Props extends object, Live, Attributes extends object, E>(\n spec: UnifiSpec<Props, Live, Attributes, E>,\n) => {\n const read = (props: Props) =>\n spec\n .fetchLive(props)\n .pipe(Effect.map((live) => (live === undefined ? undefined : spec.attributes(live, props))));\n\n return {\n read,\n\n readHandler: ({ olds, output }: { olds: Props; output: Attributes | undefined }) =>\n Effect.gen(function* () {\n const attrs = yield* read(olds);\n if (attrs === undefined) return undefined;\n // Warm read (persisted `output` says a prior apply already adopted this exact object):\n // plain attributes. Cold read (fresh store, or state lost): no ownership marker on this\n // vendor exists to tell \"ours\" from \"someone else's\" apart, so Unowned until adopt(true).\n return output !== undefined ? attrs : Unowned(attrs);\n }),\n\n diff: (news: Input<Props>, output: Attributes | undefined) =>\n Effect.gen(function* () {\n if (output === undefined || !isResolved(news)) return undefined;\n const live = yield* read(news);\n if (live === undefined) return { action: 'update' } as const;\n return spec.matches(live, news)\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: (news: Props) =>\n Effect.gen(function* () {\n const live = yield* read(news);\n if (live === undefined) return yield* refuseWrite(spec.type, spec.describe(news), 'create');\n if (!spec.matches(live, news)) {\n return yield* refuseWrite(spec.type, spec.describe(news), 'update');\n }\n // Exact match: the object already is what is declared. Zero write calls (S10), correct\n // whether this is a routine deploy or beta.79's forced post-adoption reconcile (H6).\n return live;\n }),\n\n destroy: (olds: Props) => refuseWrite(spec.type, spec.describe(olds), 'delete'),\n };\n};\n\n/**\n * The four provider handlers for a spec'd read-only UniFi object, wired once.\n *\n * ⛔ `list` ANSWERS EMPTY. `GET /v1/sites/{siteId}/networks` answers every network on the site;\n * adoption stays explicit, the same reasoning `Proxmox.User`'s and NetBox's `list` give.\n */\nexport const unifiHandlers = <Props extends object, Live, Attributes extends object, E>(\n spec: UnifiSpec<Props, Live, Attributes, E>,\n) => {\n const ops = unifiOperations(spec);\n const withCredentials = <A, Err>(effect: Effect.Effect<A, Err, UnifiNetworkOpContext>) =>\n Effect.provide(effect, CredentialsFromEnv);\n return {\n list: () => Effect.succeed([]),\n read: (args: { olds: Props; output: Attributes | undefined }) =>\n withCredentials(ops.readHandler(args)),\n diff: (args: { news: Input<Props>; output: Attributes | undefined }) =>\n withCredentials(ops.diff(args.news, args.output)),\n reconcile: (args: { news: Props }) => withCredentials(ops.reconcile(args.news)),\n delete: (args: { olds: Props }) => withCredentials(ops.destroy(args.olds)),\n };\n};\n",
9
- "/**\n * `Unifi.FirewallZone` — one firewall zone on a UniFi Network site, READ-ONLY (`policy.ts`).\n *\n * ★ WHY FIREWALL ZONE IS IN THE FIRST IMPORT SET AND `Unifi.FirewallPolicy` IS NOT.\n * `getFirewallZone`/`getFirewallZones` (`firewall.ts`) is a list+get pair over genuine,\n * simply-shaped configuration: `{id, metadata, name, networkIds}`, four fields, no nested\n * discriminated filter. `FirewallPolicy` (same file) is also list+get and also configuration,\n * but its `source`/`destination`/`ipProtocolScope` fields carry several of `docs/unifi-api-notes.md`'s\n * \"converter-flattened discriminator variants\" each, and `firewall/policies/ordering` replaces\n * the whole ordered list — modelling those correctly is a bigger, separate PR (`network.ts`'s\n * header note names it explicitly) rather than something to rush alongside this one.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport * as Effect from 'effect/Effect';\nimport {\n type FirewallZoneAttributes,\n type FirewallZoneProps,\n attributesOf,\n matches,\n} from './firewall-zone-form.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { FirewallZoneAttributes, FirewallZoneProps } from './firewall-zone-form.ts';\nexport { declareFirewallZone } from './firewall-zone-form.ts';\n\nexport interface UnifiFirewallZone extends Resource<\n 'Unifi.FirewallZone',\n FirewallZoneProps,\n FirewallZoneAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiFirewallZone = Resource<UnifiFirewallZone>('Unifi.FirewallZone', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `firewallZone('lan', props)` — `adopt(true)` piped on by default (H5); see `network.ts`. */\nexport const firewallZone = (id: string, props: FirewallZoneProps) =>\n UnifiFirewallZone(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n FirewallZoneProps,\n firewall.FirewallZone,\n FirewallZoneAttributes,\n firewall.GetFirewallZoneError\n> = {\n type: 'Unifi.FirewallZone',\n describe: (props) => `sites/${props.siteId}/firewall/zones/${props.firewallZoneId}`,\n fetchLive: (props) =>\n firewall\n .getFirewallZone({ siteId: props.siteId, firewallZoneId: props.firewallZoneId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiFirewallZoneProvider = () =>\n Provider.effect(UnifiFirewallZone, Effect.succeed(UnifiFirewallZone.Provider.of(handlers)));\n",
10
- "/**\n * `Unifi.FirewallZone`'s wire shape — the declarable subset of `FirewallZone`, and how a live\n * read becomes both plan attributes and a rendered declaration.\n *\n * ⚠️ ONLY `name` AND `networkIds` ARE DECLARABLE. `CreateFirewallZoneRequest`\n * (`firewall.ts`) accepts exactly those two beyond `siteId`; `metadata` (origin/source) and\n * `id` are server-derived, so — same split as `network-form.ts`'s `default`/`metadata` —\n * they are attributes-only, reported but never compared or declared.\n *\n * ⛔ `networkIds` ORDER IS NOT PART OF IDENTITY. The vendor document gives no indication the\n * list is ordered (it is a set of attached networks, not a sequence like the ordering\n * endpoints' rule lists), so `matches` compares it as a set — sorted before comparing —\n * the same reasoning `proxmox/user-wire.ts`'s `groupSet` gives for PVE's `groups`.\n */\nimport type * as firewall from '@distilled.cloud/unifi-network/firewall';\n\nexport interface FirewallZoneProps {\n siteId: string;\n firewallZoneId: string;\n /** Name of a firewall zone. */\n name: string;\n /** Network IDs attached to this zone — compared as a set, see the header. */\n networkIds: string[];\n}\n\nexport interface FirewallZoneAttributes {\n siteId: string;\n firewallZoneId: string;\n name: string;\n networkIds: string[];\n /** `origin`/`source` (e.g. `SDWAN`) — server-derived, never declared. */\n metadataOrigin: string;\n}\n\nconst sortedSet = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\nexport const attributesOf = (\n live: firewall.FirewallZone,\n props: FirewallZoneProps,\n): FirewallZoneAttributes => ({\n siteId: props.siteId,\n firewallZoneId: live.id,\n name: live.name,\n networkIds: sortedSet(live.networkIds),\n metadataOrigin: live.metadata.origin,\n});\n\nexport const matches = (attributes: FirewallZoneAttributes, props: FirewallZoneProps): boolean =>\n attributes.name === props.name &&\n attributes.networkIds.join(',') === sortedSet(props.networkIds).join(',');\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). `matches(attributesOf(live, props), declareFirewallZone(live,\n * siteId))` is `true` by construction (`firewall-zone.test.ts` proves it).\n */\nexport const declareFirewallZone = (\n live: firewall.FirewallZone,\n siteId: string,\n): FirewallZoneProps => ({\n siteId,\n firewallZoneId: live.id,\n name: live.name,\n networkIds: sortedSet(live.networkIds),\n});\n",
11
- "/**\n * Every `Unifi.*` provider as one layer for a stack.\n *\n * Layer.mergeAll(unifiProviders(), …the stack's other providers)\n * .pipe(Layer.provide(FetchHttpClient.layer))\n *\n * S16: a subpath's `providers()` plays the role upstream's own `Providers.ts` does — one minimal\n * insertion per family. `FetchHttpClient.layer` is the stack's own responsibility to provide,\n * mirroring `../discord/providers.ts`.\n */\nimport * as Layer from 'effect/Layer';\nimport { UnifiFirewallZoneProvider } from './firewall-zone.ts';\nimport { UnifiNetworkProvider } from './network.ts';\n\nexport const providers = () => Layer.mergeAll(UnifiNetworkProvider(), UnifiFirewallZoneProvider());\n"
6
+ "/**\n * One shape for every UniFi Network read-only object — `Unifi.Network`, `Unifi.FirewallZone`,\n * and whatever import adds next.\n *\n * ★ MIRRORS `../discord/resource.ts` (S7-S10, H1, H5), NOT `../netbox/resource.ts`. Both are\n * \"marker-less API\" families (S8): UniFi Network objects carry no tag or metadata field this\n * stack could stamp as ownership, so a cold read of a match is `Unowned(attrs)` exactly like\n * `Cloudflare/Snippets/Snippet.ts@v2.0.0-beta.79#read`, the reference implementation S8 and H1\n * both cite. `adopt(true)`, piped on by each resource's convenience constructor (H5), turns\n * that into a silent one-time bind instead of an `OwnedBySomeoneElse` refusal.\n *\n * ⛔ THE DIFFERENCE FROM EVERY OTHER FAMILY HERE: THERE IS NO `create`, `update` OR `destroy` IN\n * THIS SPEC AT ALL. Tim's rule, 2026-09-24 (`policy.ts`): UniFi and OPNsense are read-only. So\n * `reconcile` below can only ever refuse or report an exact match — it has no write path to\n * fall into by accident, and no resource file wired through this engine can reach one either.\n *\n * ⚠️ RECONCILE STILL HAS TO SUCCEED ON AN EXACT-MATCH ADOPTION, NOT JUST REFUSE UNCONDITIONALLY.\n * Measured against `Plan.ts@v2.0.0-beta.79` (`forceUpdateAfterAdoption`, H6): beta.79 forces\n * ONE reconcile call after every cold adoption, whether or not the diff said noop — so a\n * resource whose `reconcile` always failed would break the ordinary \"adopt an unchanged\n * object\" deploy, not just a real write. The fix, exactly like `discordOperations.reconcile`:\n * read the live object again, and refuse ONLY when it is missing (would need a create) or\n * drifted (would need a write to converge). An exact match returns the live attributes and\n * calls nothing — S10's \"zero write calls when there is no drift\" holds even under the forced\n * post-adoption call.\n *\n * ⚠️ `NotFound` IS FOLDED TO \"ABSENT\" INSIDE EACH RESOURCE FILE'S OWN `fetchLive`, NOT HERE —\n * same split netbox's `resource.ts` uses and for the same reason: `Effect.catchTag`'s\n * tag-literal inference needs a concrete error union to resolve `\"NotFound\"` against, and a\n * spec's `E` is only concrete at each resource file's own call site. Every OTHER failure (a\n * real 5xx, a network error, `Forbidden`) is NOT folded anywhere — it fails the plan loudly,\n * per the task's own rule: only a genuine not-found may mean absent.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { CredentialsFromEnv } from '@distilled.cloud/unifi-network/Credentials';\nimport type { UnifiNetworkOpContext } from '@distilled.cloud/unifi-network/Protocol';\nimport * as Effect from 'effect/Effect';\nimport * as HttpClient from 'effect/unstable/http/HttpClient';\nimport { UNIFI_READ_ONLY_POLICY, refuseWrite } from './policy.ts';\n\n/** What every handler needs from the caller's runtime once `CredentialsFromEnv` is baked in. */\nexport type UnifiRequirements = HttpClient.HttpClient;\n\n/**\n * One UniFi Network object's read. `Live` is whatever the SDK decodes (a `NetworkDetails`, a\n * `FirewallZone`, …); `E` is left to each resource file to declare (its own `fetchLive`'s error\n * union, with `NotFound` already folded away) so the precise per-operation error union flows\n * through instead of being widened by hand here.\n */\nexport type UnifiSpec<Props extends object, Live, Attributes extends object, E> = {\n /** The vendor type string, e.g. `Unifi.Network` — for the refusal message only. */\n readonly type: string;\n /** Already folds a genuine not-found to `undefined`; nothing else is folded (see header). */\n readonly fetchLive: (props: Props) => Effect.Effect<Live | undefined, E, UnifiNetworkOpContext>;\n readonly attributes: (live: Live, props: Props) => Attributes;\n readonly matches: (attributes: Attributes, props: Props) => boolean;\n /** For the refusal message and read-back failures. */\n readonly describe: (props: Props) => string;\n};\n\nexport const unifiOperations = <Props extends object, Live, Attributes extends object, E>(\n spec: UnifiSpec<Props, Live, Attributes, E>,\n) => {\n const read = (props: Props) =>\n spec\n .fetchLive(props)\n .pipe(Effect.map((live) => (live === undefined ? undefined : spec.attributes(live, props))));\n\n return {\n read,\n\n readHandler: ({ olds, output }: { olds: Props; output: Attributes | undefined }) =>\n Effect.gen(function* () {\n const attrs = yield* read(olds);\n if (attrs === undefined) return undefined;\n // Warm read (persisted `output` says a prior apply already adopted this exact object):\n // plain attributes. Cold read (fresh store, or state lost): no ownership marker on this\n // vendor exists to tell \"ours\" from \"someone else's\" apart, so Unowned until adopt(true).\n return output !== undefined ? attrs : Unowned(attrs);\n }),\n\n diff: (news: Input<Props>, output: Attributes | undefined) =>\n Effect.gen(function* () {\n if (output === undefined || !isResolved(news)) return undefined;\n const live = yield* read(news);\n if (live === undefined) return { action: 'update' } as const;\n return spec.matches(live, news)\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: (news: Props) =>\n Effect.gen(function* () {\n const live = yield* read(news);\n if (live === undefined) return yield* refuseWrite(spec.type, spec.describe(news), 'create');\n if (!spec.matches(live, news)) {\n return yield* refuseWrite(spec.type, spec.describe(news), 'update');\n }\n // Exact match: the object already is what is declared. Zero write calls (S10), correct\n // whether this is a routine deploy or beta.79's forced post-adoption reconcile (H6).\n return live;\n }),\n\n destroy: (olds: Props) => refuseWrite(spec.type, spec.describe(olds), 'delete'),\n };\n};\n\n/**\n * B0a / T12 DEFENSE IN DEPTH. `policy.ts`'s `UnifiWriteRefused` stops write INTENT at\n * `reconcile`/`destroy` — every path this engine exposes already returns that refusal instead of\n * calling an SDK write op. This guard stops the same thing one layer lower, AT THE WIRE: it wraps\n * whatever `HttpClient` the caller already provides (the stack's `FetchHttpClient.layer` in\n * production, `fakeUnifiLayer` in tests — `openbao/bao-http.ts`'s header names this same\n * \"wrap, don't replace\" seam) so a future resource file that, by mistake, called an SDK write\n * operation directly — bypassing `unifiOperations` entirely — would still never reach the vendor\n * API. `write-op-reference.test.ts` proves no such call exists today; this is what stops one from\n * ever taking effect if one is ever added.\n *\n * ⚠️ `Effect.die`, NOT A TYPED FAILURE — classified per distilled-doctrine's \"classify every\n * die/orDie\". Every generated SDK operation (`networks.getNetworkDetails`, …) declares its OWN\n * closed error union from the pinned OpenAPI spec, and generated files are never hand-edited to\n * add a member to it. A typed `Effect.fail` here would be a failure mode absent from every op's\n * declared type the moment it occurred — a type-system lie. A defect is the sound way to add a\n * NEW \"this must be structurally impossible\" failure underneath types this package does not\n * own, exactly like `openbao`'s `refuse()` helpers' `Effect.die(new Error(...))` for a\n * configuration move that should never be reachable.\n */\nexport class UnifiNonGetRequest extends Error {\n constructor(\n readonly method: string,\n /** PATH ONLY, never the full URL — see `requestPath` below. */\n readonly path: string,\n ) {\n super(\n `UniFi HttpClient guard: refused ${method} ${path} -- ${UNIFI_READ_ONLY_POLICY}. This is a ` +\n 'defect, not a recoverable condition: something under src/unifi tried to send a non-GET ' +\n \"request, which policy.ts's per-operation refusal should already have made impossible. \" +\n 'Fix the kit code that produced this request.',\n );\n this.name = 'UnifiNonGetRequest';\n }\n}\n\n/**\n * ⛔ PATH ONLY, NEVER THE HOST. A cloud connector's base URL embeds the account's Console ID\n * (`docs/unifi.md`'s Credentials section, T3) — a defect message is still a message that can\n * reach a log, a CI failure body or a pasted error report, so it gets the same treatment as\n * `redactUrls` gives the import layer's own error text. Falls back to a fixed placeholder rather\n * than the raw string on a parse failure, so a malformed URL can never leak through unredacted.\n */\nconst requestPath = (url: string): string => {\n try {\n return new URL(url).pathname;\n } catch {\n return '<unparseable request url>';\n }\n};\n\nconst guardGetOnly = (client: HttpClient.HttpClient): HttpClient.HttpClient =>\n client.pipe(\n HttpClient.mapRequestEffect((request) =>\n request.method === 'GET'\n ? Effect.succeed(request)\n : Effect.die(new UnifiNonGetRequest(request.method, requestPath(request.url))),\n ),\n );\n\n/**\n * A `Layer` that reads whatever `HttpClient` is already in the calling context and replaces it,\n * for everything downstream, with `guardGetOnly`'s wrapped version (`HttpClient.layerMergedContext`\n * — see the guard above). Exported so `get-only-guard.test.ts` can prove the mechanism directly,\n * against a bare `HttpClient` service, without needing `CredentialsFromEnv` or any env var at all.\n */\nexport const GetOnlyHttpClient = HttpClient.layerMergedContext(\n Effect.map(HttpClient.HttpClient, guardGetOnly),\n);\n\n/**\n * The four provider handlers for a spec'd read-only UniFi object, wired once.\n *\n * ⛔ `list` ANSWERS EMPTY. `GET /v1/sites/{siteId}/networks` answers every network on the site;\n * adoption stays explicit, the same reasoning `Proxmox.User`'s and NetBox's `list` give.\n */\nexport const unifiHandlers = <Props extends object, Live, Attributes extends object, E>(\n spec: UnifiSpec<Props, Live, Attributes, E>,\n) => {\n const ops = unifiOperations(spec);\n const withCredentials = <A, Err>(effect: Effect.Effect<A, Err, UnifiNetworkOpContext>) =>\n effect.pipe(Effect.provide(GetOnlyHttpClient), Effect.provide(CredentialsFromEnv));\n return {\n list: () => Effect.succeed([]),\n read: (args: { olds: Props; output: Attributes | undefined }) =>\n withCredentials(ops.readHandler(args)),\n diff: (args: { news: Input<Props>; output: Attributes | undefined }) =>\n withCredentials(ops.diff(args.news, args.output)),\n reconcile: (args: { news: Props }) => withCredentials(ops.reconcile(args.news)),\n delete: (args: { olds: Props }) => withCredentials(ops.destroy(args.olds)),\n };\n};\n",
7
+ "/**\n * `Unifi.Network` — one network on a UniFi Network site, READ-ONLY (`policy.ts`).\n *\n * ★ WHY NETWORK IS IN THE FIRST IMPORT SET. `getNetworkDetails`/`getNetworksOverviewPage`\n * (`../../distilled-unifi-network/src/services/networks.ts`) is a list+get pair over\n * genuine configuration — VLAN, DHCP guarding, IPv4/IPv6 addressing, isolation — not runtime\n * state like a connected client, a device statistic or a hotspot voucher. It is also the SDK\n * package's own README recommendation for a first read/adopt-only resource, alongside `Site`\n * (skipped here — no `getSite`, only the list operation, so it fails this task's own \"both a\n * list and a get\" bar) and `FirewallZone`/`FirewallPolicy` (`firewall-zone.ts` ships\n * `FirewallZone`; `FirewallPolicy`'s several discriminated filter variants and its\n * list-replacing ordering endpoint are a bigger, separate PR).\n *\n * ⛔ THE WHOLE-OBJECT-PUT TRAP (`docs/unifi-api-notes.md`) NEVER FIRES HERE, BY CONSTRUCTION.\n * `updateNetwork` is a `PUT` that disables `dhcpGuarding`/`ipv4Configuration`/`ipv6Configuration`\n * outright when the caller omits them — the exact failure mode a read-only family cannot cause,\n * because `resource.ts`'s `reconcile` never calls `updateNetwork` at all. Recorded here for\n * whoever adds the write path this docs note anticipates: read-merge-write the WHOLE object,\n * never a props-only body.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as networks from '@distilled.cloud/unifi-network/networks';\nimport * as Effect from 'effect/Effect';\nimport { type NetworkAttributes, type NetworkProps, attributesOf } from './network-form.ts';\nimport { matches } from './network-drift.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { NetworkAttributes, NetworkProps } from './network-form.ts';\nexport { declareNetwork } from './network-form.ts';\n// MEDIUM-4: `matches` moved here from `network-form.ts` — see network-drift.ts's own header. Not\n// re-exported from this barrel file either, same as before the move: `spec.matches` below is the\n// only consumer a provider needs.\nexport { driftOf } from './network-drift.ts';\n\nexport interface UnifiNetwork extends Resource<\n 'Unifi.Network',\n NetworkProps,\n NetworkAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiNetwork = Resource<UnifiNetwork>('Unifi.Network', {\n defaultRemovalPolicy: 'retain',\n});\n\n/**\n * `network('lan', props)` — `adopt(true)` piped on by default (H5). This family never writes, so\n * the only thing a first deploy against an existing network could ever do is bind to it or refuse\n * `OwnedBySomeoneElse`; piping `adopt(true)` here picks the former without a stack having to\n * remember `--adopt`, mirroring `discord/application-command.ts`'s `applicationCommand`.\n */\nexport const network = (id: string, props: NetworkProps) =>\n UnifiNetwork(id, props).pipe(adopt(true));\n\n/**\n * ★ EXPORTED for direct testing against a fake `Credentials` layer (`fake-unifi.ts`), the same\n * seam `netbox/prefix.ts`'s `spec` and `discord/application-command.ts`'s `spec` use — `handlers`\n * bakes in `CredentialsFromEnv`, which resolves environment variables a test cannot repoint.\n */\nexport const spec: UnifiSpec<\n NetworkProps,\n networks.NetworkDetails,\n NetworkAttributes,\n networks.GetNetworkDetailsError\n> = {\n type: 'Unifi.Network',\n describe: (props) => `sites/${props.siteId}/networks/${props.networkId}`,\n fetchLive: (props) =>\n networks\n .getNetworkDetails({ siteId: props.siteId, networkId: props.networkId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiNetworkProvider = () =>\n Provider.effect(UnifiNetwork, Effect.succeed(UnifiNetwork.Provider.of(handlers)));\n",
8
+ "/**\n * `Unifi.Network`'s wire shape: the declarable subset of `NetworkDetails`, and how a live read\n * becomes both plan attributes and a rendered declaration.\n *\n * ⚠️ PROPS MIRROR `UpdateNetworkRequest`, NOT `NetworkDetails`. `default` and `metadata` are\n * server-derived — no writable endpoint in `networks.ts` accepts either — so they are\n * attributes-only, reported but never compared or declared. Everything else in `NetworkProps`\n * is exactly what `updateNetwork`/`createNetwork` would accept, even though this family never\n * calls either: the shape is what a future write path would need, and what today's read-only\n * `matches` compares against.\n *\n * ⛔ `ipv4Configuration` STAYS `unknown`, ON THE SDK'S OWN AUTHORITY. `networks.ts`'s own comment\n * (\"Shape varies by variant — widened by scripts/convert.ts; see README\") says the OpenAPI\n * discriminator this field varies over was flattened at generation time, not decoded per\n * variant. Comparing it as an opaque JSON value (below) is honest about that; guessing a\n * narrower shape here would silently stop matching the day a new variant's key order differs.\n * ⚠️ It may ALSO hide an unordered array the way `dhcpGuarding` and `ipv6Configuration` do\n * below — this family just can't know, because the shape is never decoded. If a future variant\n * plans spurious `update`s, suspect this field first and widen it into a typed, order-normalized\n * shape rather than adding another opaque comparison.\n *\n * ⛔ THREE FIELDS ARE SETS, NOT SEQUENCES: `dhcpGuarding.trustedDhcpServerIpAddresses`,\n * `ipv6Configuration.additionalHostIpSubnets`, and `ipv6Configuration.dnsServerIpAddressesOverride`.\n * The vendor document gives no indication any of the three is ordered — same reasoning\n * `firewall-zone-form.ts` gives for `networkIds`, and `proxmox/user-wire.ts`'s `groupSet` gives\n * for PVE's `groups` — but `matches` compares via alchemy's `deepEqual`, which sorts object KEYS\n * but NOT ARRAY ELEMENTS (`node_modules/alchemy/lib/Diff.js`). Left alone, a console that\n * returns the same set in a different order between calls plans a spurious `update`, and the\n * read-only reconcile then refuses every deploy. `sortedSet` (dedupe + sort) neutralizes that\n * before any of the three is attributed, declared, or compared. Every OTHER array field in this\n * family (there are none today besides these three) would stay declaration-order as written.\n */\nimport type * as networks from '@distilled.cloud/unifi-network/networks';\n\nconst sortedSet = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\n// ⚠️ `value == null` (NOT `=== undefined`) ON PURPOSE, IN BOTH NORMALIZERS BELOW. The SDK types\n// `dhcpGuarding`/`ipv6Configuration` `T | undefined`, but `matches`'s own header says UniFi's\n// JSON answers `null` for an unset optional field — a reality `deepEqual`'s `stripNullish`\n// already treats as equivalent to `undefined` (`value == null` in alchemy's `Diff.js`), which\n// this family's props/attributes types don't model at the TS level. Destructuring a `null` with\n// `=== undefined`'s false branch (`{ ...value, field: value.field }`) would throw\n// `TypeError: Cannot read properties of null` the first time a console omits either field —\n// `== null` catches both and returns the value through untouched, matching what the rest of\n// this file already does for every other optional field.\n// ★ EXPORTED (not just used below): `network-drift.ts`'s `driftOf` reuses these exact two\n// normalizers so it can never quietly disagree with `matches` about what counts as a change —\n// see that file's own header.\nexport const normalizeDhcpGuarding = (\n value: networks.NetworkDHCPGuarding | undefined,\n): networks.NetworkDHCPGuarding | undefined =>\n value == null\n ? value\n : { ...value, trustedDhcpServerIpAddresses: sortedSet(value.trustedDhcpServerIpAddresses) };\n\n// ⚠️ `exactOptionalPropertyTypes` (see the props header) means these two subfields must be\n// OMITTED, not set to an explicit `undefined`, when the live/declared value has no override —\n// `NetworkIPv6Configuration` types them `field?: T`, not `field?: T | undefined`. Conditional\n// spread (rather than `field: value.field === undefined ? undefined : sortedSet(...)`) is what\n// keeps an absent key absent.\nexport const normalizeIpv6Configuration = (\n value: networks.NetworkIPv6Configuration | undefined,\n): networks.NetworkIPv6Configuration | undefined =>\n value == null\n ? value\n : {\n ...value,\n ...(value.additionalHostIpSubnets !== undefined\n ? { additionalHostIpSubnets: sortedSet(value.additionalHostIpSubnets) }\n : {}),\n ...(value.dnsServerIpAddressesOverride !== undefined\n ? { dnsServerIpAddressesOverride: sortedSet(value.dnsServerIpAddressesOverride) }\n : {}),\n };\n\n/**\n * ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` ON PURPOSE. `tsconfig.json` sets\n * `exactOptionalPropertyTypes`, which treats `field?: T` (absent-or-T) and\n * `field?: T | undefined` (absent-or-explicitly-undefined) as different types. `declareNetwork`\n * below assigns `live.dhcpGuarding` (typed `T | undefined`) straight through — the whole point\n * of a renderer that echoes a live optional field structurally rather than defaulting it — so\n * the target has to admit an explicit `undefined`, not just omission.\n */\nexport interface NetworkProps {\n siteId: string;\n networkId: string;\n name: string;\n enabled: boolean;\n management: string;\n /** VLAN ID. Must be 1 for the default network and >= 2 for additional networks. */\n vlanId: number;\n dhcpGuarding?: networks.NetworkDHCPGuarding | undefined;\n cellularBackupEnabled?: boolean | undefined;\n internetAccessEnabled?: boolean | undefined;\n /** Opaque — see the header. */\n ipv4Configuration?: unknown;\n ipv6Configuration?: networks.NetworkIPv6Configuration | undefined;\n isolationEnabled?: boolean | undefined;\n mdnsForwardingEnabled?: boolean | undefined;\n zoneId?: string | undefined;\n deviceId?: string | undefined;\n}\n\nexport interface NetworkAttributes {\n siteId: string;\n networkId: string;\n /** Whether this is the site's undeletable default network — server-derived, never declared. */\n default: boolean;\n name: string;\n enabled: boolean;\n management: string;\n vlanId: number;\n dhcpGuarding: networks.NetworkDHCPGuarding | undefined;\n cellularBackupEnabled: boolean | undefined;\n internetAccessEnabled: boolean | undefined;\n ipv4Configuration: unknown;\n ipv6Configuration: networks.NetworkIPv6Configuration | undefined;\n isolationEnabled: boolean | undefined;\n mdnsForwardingEnabled: boolean | undefined;\n zoneId: string | undefined;\n deviceId: string | undefined;\n}\n\nexport const attributesOf = (\n live: networks.NetworkDetails,\n props: NetworkProps,\n): NetworkAttributes => ({\n siteId: props.siteId,\n networkId: live.id,\n default: live.default,\n name: live.name,\n enabled: live.enabled,\n management: live.management,\n vlanId: live.vlanId,\n dhcpGuarding: normalizeDhcpGuarding(live.dhcpGuarding),\n cellularBackupEnabled: live.cellularBackupEnabled,\n internetAccessEnabled: live.internetAccessEnabled,\n ipv4Configuration: live.ipv4Configuration,\n ipv6Configuration: normalizeIpv6Configuration(live.ipv6Configuration),\n isolationEnabled: live.isolationEnabled,\n mdnsForwardingEnabled: live.mdnsForwardingEnabled,\n zoneId: live.zoneId,\n deviceId: live.deviceId,\n});\n\n/**\n * MEDIUM-4 (red team, 2026-09-26): `matches` used to be a hand-written boolean expression here,\n * kept in sync BY HAND with `network-drift.ts`'s separately-maintained field list — two comparisons\n * that could (and, per a red-team mutant, silently did) drift apart. `matches` now lives in\n * `network-drift.ts`, defined as `fieldDrift(...).length === 0` — the SAME comparison `driftOf`\n * runs, not a second one. See that file's own header for the `stripNullish`/normalizer reasoning\n * this expression used to carry.\n */\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getNetworkDetails`'s own response\n * straight in and writes the result into `alchemy.run.ts` as `network('<id>', declareNetwork(live,\n * siteId))` — no field is invented or defaulted here beyond what `attributesOf` already reports,\n * so `matches(attributesOf(live, props), declareNetwork(live, siteId))` is `true` by construction\n * (`network.test.ts` proves it) for any live object this SDK can decode.\n */\nexport const declareNetwork = (live: networks.NetworkDetails, siteId: string): NetworkProps => ({\n siteId,\n networkId: live.id,\n name: live.name,\n enabled: live.enabled,\n management: live.management,\n vlanId: live.vlanId,\n dhcpGuarding: normalizeDhcpGuarding(live.dhcpGuarding),\n cellularBackupEnabled: live.cellularBackupEnabled,\n internetAccessEnabled: live.internetAccessEnabled,\n ipv4Configuration: live.ipv4Configuration,\n ipv6Configuration: normalizeIpv6Configuration(live.ipv6Configuration),\n isolationEnabled: live.isolationEnabled,\n mdnsForwardingEnabled: live.mdnsForwardingEnabled,\n zoneId: live.zoneId,\n deviceId: live.deviceId,\n});\n",
9
+ "/**\n * B6: the field-level drift framework every family's own `driftOf(live, props)` is built on\n * (`network-form.ts`, `firewall-zone-form.ts`, and whatever family follows). Consumed by\n * `homeflare-network`'s pre-import drift check (its own C4, not built here) to show WHICH fields\n * a live object and a committed declaration disagree on, before that script overwrites the\n * committed module — never by a `Unifi.*` provider itself, which only ever needs `matches`'s\n * boolean.\n *\n * ★ BUILT ON `matches`, NOT A SEPARATE COMPARISON. Each family's `driftOf` reuses the SAME\n * normalizers (`sortedSet`, the `dhcpGuarding`/`ipv6Configuration` null-vs-undefined handling)\n * its own `matches` already applies — so this reports NOTHING `matches` would call a noop, and\n * everything `matches` would call `update`, broken down by field instead of collapsed to one\n * boolean. Two comparisons drifting apart would be its own bug class; there is exactly one.\n */\nimport { deepEqual } from 'alchemy/Diff';\n\n/** One field whose live and declared values differ, as `makeDriftOf` reports it. */\nexport interface FieldDrift {\n readonly field: string;\n readonly live: unknown;\n readonly declared: unknown;\n}\n\n/** One field's comparison recipe for `makeDriftOf`. */\nexport interface DriftField<Attributes, Props> {\n readonly field: string;\n readonly live: (attributes: Attributes) => unknown;\n readonly declared: (props: Props) => unknown;\n /**\n * Defaults to `alchemy/Diff`'s `deepEqual` with `stripNullish: true` — the same tolerance\n * every `matches` in this directory already gives UniFi's `null`-for-unset optionals (see\n * `network-form.ts`'s header). Pass one explicitly for a field `matches` compares as a\n * normalized SET (`firewall-zone-form.ts`'s `networkIds`), where plain `deepEqual` would\n * report order as drift `matches` never would.\n */\n readonly equal?: (live: unknown, declared: unknown) => boolean;\n}\n\nconst defaultEqual = (live: unknown, declared: unknown) =>\n deepEqual(live, declared, { stripNullish: true });\n\n/**\n * Builds a `(attributes, props) => FieldDrift[]` from a field list — the framework a family's own\n * `*-form.ts` calls once, then wraps as `driftOf(live, props)` by feeding it `attributesOf(live,\n * props)` (the task's own API: callers have a live SDK object, not `Attributes`, on hand).\n */\nexport const makeDriftOf =\n <Attributes, Props>(fields: readonly DriftField<Attributes, Props>[]) =>\n (attributes: Attributes, props: Props): FieldDrift[] =>\n fields.flatMap(({ field, live, declared, equal = defaultEqual }) => {\n const liveValue = live(attributes);\n const declaredValue = declared(props);\n return equal(liveValue, declaredValue)\n ? []\n : [{ field, live: liveValue, declared: declaredValue }];\n });\n",
10
+ "/**\n * `Unifi.Network`'s `matches` AND `driftOf` (B6) — both built on the ONE field list below, not two\n * hand-kept-in-sync comparisons. `matches` used to be a separate boolean expression in\n * `network-form.ts`; MEDIUM-4 (red team, 2026-09-26) found that a `fieldDrift` entry could be\n * deleted entirely and every test still passed, because `matches` never consulted `fieldDrift` at\n * all. `matches(attrs, props) === (driftOf(live, props).length === 0)` is now true BY\n * CONSTRUCTION — there is exactly one comparison — and every `fieldDrift` entry names ONE field\n * plus its own live/declared value instead of collapsing straight to a boolean.\n *\n * ⚠️ `stripNullish: true` ON EVERY FIELD BY DEFAULT (`drift.ts`'s `defaultEqual`). UniFi's JSON\n * answers `null` for an unset optional field; a declaration that omits the same prop carries\n * `undefined`. Without this, every plan against an object with one unset optional field would\n * report `update` forever — the exact \"read and write are different shapes\" trap\n * `netbox/values.ts` and `proxmox/user-wire.ts` both work around, here solved once by\n * `makeDriftOf`'s own default instead of a per-field coercion.\n */\nimport type * as networks from '@distilled.cloud/unifi-network/networks';\nimport { makeDriftOf } from './drift.ts';\nimport {\n type NetworkAttributes,\n type NetworkProps,\n attributesOf,\n normalizeDhcpGuarding,\n normalizeIpv6Configuration,\n} from './network-form.ts';\n\nconst fieldDrift = makeDriftOf<NetworkAttributes, NetworkProps>([\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n { field: 'enabled', live: (a) => a.enabled, declared: (p) => p.enabled },\n { field: 'management', live: (a) => a.management, declared: (p) => p.management },\n { field: 'vlanId', live: (a) => a.vlanId, declared: (p) => p.vlanId },\n {\n field: 'dhcpGuarding',\n live: (a) => normalizeDhcpGuarding(a.dhcpGuarding),\n declared: (p) => normalizeDhcpGuarding(p.dhcpGuarding),\n },\n {\n field: 'cellularBackupEnabled',\n live: (a) => a.cellularBackupEnabled,\n declared: (p) => p.cellularBackupEnabled,\n },\n {\n field: 'internetAccessEnabled',\n live: (a) => a.internetAccessEnabled,\n declared: (p) => p.internetAccessEnabled,\n },\n {\n field: 'ipv4Configuration',\n live: (a) => a.ipv4Configuration,\n declared: (p) => p.ipv4Configuration,\n },\n {\n field: 'ipv6Configuration',\n live: (a) => normalizeIpv6Configuration(a.ipv6Configuration),\n declared: (p) => normalizeIpv6Configuration(p.ipv6Configuration),\n },\n {\n field: 'isolationEnabled',\n live: (a) => a.isolationEnabled,\n declared: (p) => p.isolationEnabled,\n },\n {\n field: 'mdnsForwardingEnabled',\n live: (a) => a.mdnsForwardingEnabled,\n declared: (p) => p.mdnsForwardingEnabled,\n },\n { field: 'zoneId', live: (a) => a.zoneId, declared: (p) => p.zoneId },\n { field: 'deviceId', live: (a) => a.deviceId, declared: (p) => p.deviceId },\n]);\n\nexport const matches = (attributes: NetworkAttributes, props: NetworkProps): boolean =>\n fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: networks.NetworkDetails, props: NetworkProps) =>\n fieldDrift(attributesOf(live, props), props);\n",
11
+ "/**\n * `Unifi.FirewallZone` — one firewall zone on a UniFi Network site, READ-ONLY (`policy.ts`).\n *\n * ★ WHY FIREWALL ZONE IS IN THE FIRST IMPORT SET AND `Unifi.FirewallPolicy` IS NOT.\n * `getFirewallZone`/`getFirewallZones` (`firewall.ts`) is a list+get pair over genuine,\n * simply-shaped configuration: `{id, metadata, name, networkIds}`, four fields, no nested\n * discriminated filter. `FirewallPolicy` (same file) is also list+get and also configuration,\n * but its `source`/`destination`/`ipProtocolScope` fields carry several of `docs/unifi-api-notes.md`'s\n * \"converter-flattened discriminator variants\" each, and `firewall/policies/ordering` replaces\n * the whole ordered list — modelling those correctly is a bigger, separate PR (`network.ts`'s\n * header note names it explicitly) rather than something to rush alongside this one.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport * as Effect from 'effect/Effect';\nimport {\n type FirewallZoneAttributes,\n type FirewallZoneProps,\n attributesOf,\n matches,\n} from './firewall-zone-form.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { FirewallZoneAttributes, FirewallZoneProps } from './firewall-zone-form.ts';\nexport { declareFirewallZone, driftOf } from './firewall-zone-form.ts';\n\nexport interface UnifiFirewallZone extends Resource<\n 'Unifi.FirewallZone',\n FirewallZoneProps,\n FirewallZoneAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiFirewallZone = Resource<UnifiFirewallZone>('Unifi.FirewallZone', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `firewallZone('lan', props)` — `adopt(true)` piped on by default (H5); see `network.ts`. */\nexport const firewallZone = (id: string, props: FirewallZoneProps) =>\n UnifiFirewallZone(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n FirewallZoneProps,\n firewall.FirewallZone,\n FirewallZoneAttributes,\n firewall.GetFirewallZoneError\n> = {\n type: 'Unifi.FirewallZone',\n describe: (props) => `sites/${props.siteId}/firewall/zones/${props.firewallZoneId}`,\n fetchLive: (props) =>\n firewall\n .getFirewallZone({ siteId: props.siteId, firewallZoneId: props.firewallZoneId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiFirewallZoneProvider = () =>\n Provider.effect(UnifiFirewallZone, Effect.succeed(UnifiFirewallZone.Provider.of(handlers)));\n",
12
+ "/**\n * `Unifi.FirewallZone`'s wire shape — the declarable subset of `FirewallZone`, and how a live\n * read becomes both plan attributes and a rendered declaration.\n *\n * ⚠️ ONLY `name` AND `networkIds` ARE DECLARABLE. `CreateFirewallZoneRequest`\n * (`firewall.ts`) accepts exactly those two beyond `siteId`; `metadata` (origin/source) and\n * `id` are server-derived, so — same split as `network-form.ts`'s `default`/`metadata` —\n * they are attributes-only, reported but never compared or declared.\n *\n * ⛔ `networkIds` ORDER IS NOT PART OF IDENTITY. The vendor document gives no indication the\n * list is ordered (it is a set of attached networks, not a sequence like the ordering\n * endpoints' rule lists), so `matches` compares it as a set — sorted before comparing —\n * the same reasoning `proxmox/user-wire.ts`'s `groupSet` gives for PVE's `groups`.\n */\nimport type * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport { makeDriftOf } from './drift.ts';\n\nexport interface FirewallZoneProps {\n siteId: string;\n firewallZoneId: string;\n /** Name of a firewall zone. */\n name: string;\n /** Network IDs attached to this zone — compared as a set, see the header. */\n networkIds: string[];\n}\n\nexport interface FirewallZoneAttributes {\n siteId: string;\n firewallZoneId: string;\n name: string;\n networkIds: string[];\n /** `origin`/`source` (e.g. `SDWAN`) — server-derived, never declared. */\n metadataOrigin: string;\n}\n\nconst sortedSet = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\nexport const attributesOf = (\n live: firewall.FirewallZone,\n props: FirewallZoneProps,\n): FirewallZoneAttributes => ({\n siteId: props.siteId,\n firewallZoneId: live.id,\n name: live.name,\n networkIds: sortedSet(live.networkIds),\n metadataOrigin: live.metadata.origin,\n});\n\n/**\n * B6: `fieldDrift` (`makeDriftOf`, `drift.ts`) is now the ONLY comparison this family has — MEDIUM-4\n * (red team, 2026-09-26): a hand-written `matches` kept beside `fieldDrift` is two field lists an\n * edit to one can silently leave out of sync with the other (a mutant deleting a `fieldDrift` entry\n * left every test green, because `matches` never consulted it). `matches(attrs, props) ===\n * (driftOf(live, props).length === 0)` is now true BY CONSTRUCTION, not just by a test that happens\n * to check both. `networkIds`' custom `equal` is the set comparison the family has always used —\n * see the header — and its reported `live`/`declared` values in a `driftOf` result are the SORTED\n * arrays, not the joined strings this `equal` compares internally, so a real diff reads as a set\n * difference, not two opaque strings.\n */\nconst fieldDrift = makeDriftOf<FirewallZoneAttributes, FirewallZoneProps>([\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n {\n field: 'networkIds',\n live: (a) => a.networkIds,\n declared: (p) => sortedSet(p.networkIds),\n equal: (live, declared) => (live as string[]).join(',') === (declared as string[]).join(','),\n },\n]);\n\nexport const matches = (attributes: FirewallZoneAttributes, props: FirewallZoneProps): boolean =>\n fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: firewall.FirewallZone, props: FirewallZoneProps) =>\n fieldDrift(attributesOf(live, props), props);\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). `matches(attributesOf(live, props), declareFirewallZone(live,\n * siteId))` is `true` by construction (`firewall-zone.test.ts` proves it).\n */\nexport const declareFirewallZone = (\n live: firewall.FirewallZone,\n siteId: string,\n): FirewallZoneProps => ({\n siteId,\n firewallZoneId: live.id,\n name: live.name,\n networkIds: sortedSet(live.networkIds),\n});\n",
13
+ "/**\n * `Unifi.DnsPolicy` — one DNS policy on a UniFi Network site, READ-ONLY (`policy.ts`).\n *\n * ★ T11 GATE, CHECKED 2026-09-26, CORRECTED SAME DAY (red team, Important finding 1): `DNS\n * Policies` is 5 operations, `Access Control (ACL Rules)` (`acl-rule.ts`) is 7 — 12 total.\n * Diffed the pinned 10.4.57 spec against a 10.6.97 mirror (`beezly/unifi-apis`, diffing aid\n * only, `docs/unifi-acl-rule.md` records the sha256 + mirror commit): all 12 full operation\n * objects (parameters, request body, responses, unresolved `$ref`s included) are byte-identical\n * across versions. ⚠️ The schema closure is 46, not 17 — a `$ref`-only walk misses every schema\n * reachable only via a `discriminator.mapping` (both `DNS policy` and `ACL rule` discriminate on\n * `type`); the 46-schema closure, `$ref` + `mapping` together, shares zero names with the 14\n * schemas that DID change between the two versions (all in `Switching`, `Filtering`, or one\n * `UniFi Devices` enum — none of it DNS- or ACL-shaped). Same reachability method\n * `spec-version-provenance.md` (distilled package) used for Networks/FirewallZones, extended to\n * follow `mapping` too; full breakdown in `docs/unifi-acl-rule.md`, not repeated per family.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as dnsPolicies from '@distilled.cloud/unifi-network/dns_policies';\nimport * as Effect from 'effect/Effect';\nimport { type DnsPolicyAttributes, type DnsPolicyProps, attributesOf } from './dns-policy-form.ts';\nimport { matches } from './dns-policy-drift.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { DnsPolicyAttributes, DnsPolicyProps } from './dns-policy-form.ts';\nexport { declareDnsPolicy } from './dns-policy-form.ts';\nexport { driftOf } from './dns-policy-drift.ts';\n\nexport interface UnifiDnsPolicy extends Resource<\n 'Unifi.DnsPolicy',\n DnsPolicyProps,\n DnsPolicyAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiDnsPolicy = Resource<UnifiDnsPolicy>('Unifi.DnsPolicy', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `dnsPolicy('lan-forwarding', props)` — `adopt(true)` piped on by default (H5); see `network.ts`. */\nexport const dnsPolicy = (id: string, props: DnsPolicyProps) =>\n UnifiDnsPolicy(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n DnsPolicyProps,\n dnsPolicies.DNSPolicy,\n DnsPolicyAttributes,\n dnsPolicies.GetDnsPolicyError\n> = {\n type: 'Unifi.DnsPolicy',\n describe: (props) => `sites/${props.siteId}/dns/policies/${props.dnsPolicyId}`,\n fetchLive: (props) =>\n dnsPolicies\n .getDnsPolicy({ siteId: props.siteId, dnsPolicyId: props.dnsPolicyId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiDnsPolicyProvider = () =>\n Provider.effect(UnifiDnsPolicy, Effect.succeed(UnifiDnsPolicy.Provider.of(handlers)));\n",
14
+ "/**\n * `Unifi.DnsPolicy`'s wire shape — the declarable subset of `DNSPolicy`, and how a live read\n * becomes both plan attributes and a rendered declaration.\n *\n * ★ ONE FLAT DTO FOR EVERY DNS RECORD `type` (`A_RECORD`/`AAAA_RECORD`/`CNAME_RECORD`/\n * `FORWARD_DOMAIN`/`MX_RECORD`/`SRV_RECORD`/`TXT_RECORD` — the pinned spec's `DNS policy` schema\n * DOES discriminate on `type`, via `discriminator.mapping` to 7 variant DTOs; see\n * `docs/unifi-dns-policy.md`'s \"One flat DTO\" section, corrected 2026-09-26). Unlike\n * `access_control_acl_rules.ts`'s `sourceFilter`/`destinationFilter` or `firewall.ts`'s policy\n * filters, NONE of `dns_policies.ts`'s fields were widened to `unknown` by the converter (T10) —\n * `scripts/convert.ts` flattens the 7 variants into one struct instead, because no two variants\n * collide on a field name, so every optional field still decodes as a plain scalar and there is\n * no discriminated-variant trap here to flag or defer past this PR.\n *\n * ⚠️ PROPS MIRROR `UpdateDnsPolicyRequest`, NOT `DNSPolicy` — same split `network-form.ts` and\n * `firewall-zone-form.ts` both use. `id` and `metadata` are server-derived (no writable field\n * accepts either), so they are attributes-only, reported but never compared or declared.\n * Everything else in `DnsPolicyProps` is exactly what `updateDnsPolicy`/`createDnsPolicy` would\n * accept, even though this family never calls either.\n *\n * ⛔ NO ARRAY FIELD ANYWHERE IN THIS SHAPE. Every field below is a scalar (`string`/`number`/\n * `boolean`), so none of `network-form.ts`'s/`firewall-zone-form.ts`'s `sortedSet` normalizing\n * is needed — `deepEqual`'s default `stripNullish` handling (`drift.ts`) is the whole story.\n */\nimport type * as dnsPolicies from '@distilled.cloud/unifi-network/dns_policies';\n\n/**\n * ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` ON PURPOSE (`exactOptionalPropertyTypes` —\n * see `network-form.ts`'s own header for why `declareDnsPolicy` below needs it: it assigns a\n * live optional field straight through, so the target type must admit an explicit `undefined`,\n * not just omission).\n */\nexport interface DnsPolicyProps {\n siteId: string;\n dnsPolicyId: string;\n enabled: boolean;\n /** DNS record type: `A_RECORD`, `AAAA_RECORD`, `CNAME_RECORD`, `FORWARD_DOMAIN`, `MX_RECORD`,\n * `SRV_RECORD` or `TXT_RECORD` (the discriminator's own mapping keys, see the header). */\n type: string;\n domain?: string | undefined;\n ipv6Address?: string | undefined;\n /** Time to live in seconds. */\n ttlSeconds?: number | undefined;\n ipv4Address?: string | undefined;\n targetDomain?: string | undefined;\n /** IP address of the DNS Server that the DNS query is forwarded to. */\n ipAddress?: string | undefined;\n mailServerDomain?: string | undefined;\n /** Priority. A lower number is preferred. */\n priority?: number | undefined;\n port?: number | undefined;\n /** Protocol used by the service. */\n protocol?: string | undefined;\n /** Domain of the server that is running the service. */\n serverDomain?: string | undefined;\n /** Service associated with this SRV record. */\n service?: string | undefined;\n /** Weight. A relative value applicable for records with the same priority. A lower number is\n * preferred. */\n weight?: number | undefined;\n /** The text value associated with this TXT DNS record. Text can contain up to four\n * 255-character strings. Lines containing commas must be enclosed in double quotes (\"). */\n text?: string | undefined;\n}\n\nexport interface DnsPolicyAttributes {\n siteId: string;\n dnsPolicyId: string;\n enabled: boolean;\n type: string;\n domain: string | undefined;\n ipv6Address: string | undefined;\n ttlSeconds: number | undefined;\n ipv4Address: string | undefined;\n targetDomain: string | undefined;\n ipAddress: string | undefined;\n mailServerDomain: string | undefined;\n priority: number | undefined;\n port: number | undefined;\n protocol: string | undefined;\n serverDomain: string | undefined;\n service: string | undefined;\n weight: number | undefined;\n text: string | undefined;\n /** `metadata.origin` (e.g. `USER`) — server-derived, never declared; see\n * `firewall-zone-form.ts`'s own `metadataOrigin` for the same split. */\n metadataOrigin: string;\n}\n\nexport const attributesOf = (\n live: dnsPolicies.DNSPolicy,\n props: DnsPolicyProps,\n): DnsPolicyAttributes => ({\n siteId: props.siteId,\n dnsPolicyId: live.id,\n enabled: live.enabled,\n type: live.type,\n domain: live.domain,\n ipv6Address: live.ipv6Address,\n ttlSeconds: live.ttlSeconds,\n ipv4Address: live.ipv4Address,\n targetDomain: live.targetDomain,\n ipAddress: live.ipAddress,\n mailServerDomain: live.mailServerDomain,\n priority: live.priority,\n port: live.port,\n protocol: live.protocol,\n serverDomain: live.serverDomain,\n service: live.service,\n weight: live.weight,\n text: live.text,\n metadataOrigin: live.metadata.origin,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getDnsPolicy`'s own response straight\n * in and writes the result into `alchemy.run.ts` as `dnsPolicy('<id>', declareDnsPolicy(live,\n * siteId))` — no field is invented or defaulted here beyond what `attributesOf` already reports,\n * so `matches(attributesOf(live, props), declareDnsPolicy(live, siteId))` is `true` by\n * construction (`dns-policy.test.ts` proves it) for any live object this SDK can decode.\n */\nexport const declareDnsPolicy = (live: dnsPolicies.DNSPolicy, siteId: string): DnsPolicyProps => ({\n siteId,\n dnsPolicyId: live.id,\n enabled: live.enabled,\n type: live.type,\n domain: live.domain,\n ipv6Address: live.ipv6Address,\n ttlSeconds: live.ttlSeconds,\n ipv4Address: live.ipv4Address,\n targetDomain: live.targetDomain,\n ipAddress: live.ipAddress,\n mailServerDomain: live.mailServerDomain,\n priority: live.priority,\n port: live.port,\n protocol: live.protocol,\n serverDomain: live.serverDomain,\n service: live.service,\n weight: live.weight,\n text: live.text,\n});\n",
15
+ "/**\n * `Unifi.DnsPolicy`'s `matches` AND `driftOf` (B6) — both built on the ONE field list below, the\n * same `fieldDrift`-is-the-only-comparison shape `network-drift.ts`'s own header explains (MEDIUM-4,\n * red team, 2026-09-26): `matches(attrs, props) === (driftOf(live, props).length === 0)` is true\n * BY CONSTRUCTION, never two hand-kept-in-sync comparisons.\n *\n * ★ NO CUSTOM `equal` ANYWHERE. Every field is a scalar and `drift.ts`'s `defaultEqual`\n * (`deepEqual` with `stripNullish: true`) already gives UniFi's `null`-for-unset-optional\n * reality the same tolerance `network-drift.ts`/`firewall-zone-form.ts` rely on — see\n * `dns-policy-form.ts`'s own header for why no field here needs a set/order normalizer.\n */\nimport type * as dnsPolicies from '@distilled.cloud/unifi-network/dns_policies';\nimport { makeDriftOf } from './drift.ts';\nimport { type DnsPolicyAttributes, type DnsPolicyProps, attributesOf } from './dns-policy-form.ts';\n\nconst fieldDrift = makeDriftOf<DnsPolicyAttributes, DnsPolicyProps>([\n { field: 'enabled', live: (a) => a.enabled, declared: (p) => p.enabled },\n { field: 'type', live: (a) => a.type, declared: (p) => p.type },\n { field: 'domain', live: (a) => a.domain, declared: (p) => p.domain },\n { field: 'ipv6Address', live: (a) => a.ipv6Address, declared: (p) => p.ipv6Address },\n { field: 'ttlSeconds', live: (a) => a.ttlSeconds, declared: (p) => p.ttlSeconds },\n { field: 'ipv4Address', live: (a) => a.ipv4Address, declared: (p) => p.ipv4Address },\n { field: 'targetDomain', live: (a) => a.targetDomain, declared: (p) => p.targetDomain },\n { field: 'ipAddress', live: (a) => a.ipAddress, declared: (p) => p.ipAddress },\n {\n field: 'mailServerDomain',\n live: (a) => a.mailServerDomain,\n declared: (p) => p.mailServerDomain,\n },\n { field: 'priority', live: (a) => a.priority, declared: (p) => p.priority },\n { field: 'port', live: (a) => a.port, declared: (p) => p.port },\n { field: 'protocol', live: (a) => a.protocol, declared: (p) => p.protocol },\n { field: 'serverDomain', live: (a) => a.serverDomain, declared: (p) => p.serverDomain },\n { field: 'service', live: (a) => a.service, declared: (p) => p.service },\n { field: 'weight', live: (a) => a.weight, declared: (p) => p.weight },\n { field: 'text', live: (a) => a.text, declared: (p) => p.text },\n]);\n\nexport const matches = (attributes: DnsPolicyAttributes, props: DnsPolicyProps): boolean =>\n fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: dnsPolicies.DNSPolicy, props: DnsPolicyProps) =>\n fieldDrift(attributesOf(live, props), props);\n",
16
+ "/**\n * `Unifi.AclRule` — one user-defined ACL rule on a UniFi Network site, READ-ONLY (`policy.ts`).\n * `Unifi.AclRuleOrdering` (`acl-rule-ordering.ts`) is the sibling resource for rule ORDER (T5) —\n * one per site, never this file's concern.\n *\n * ★ T11 GATE — see `dns-policy.ts`'s own header for the full closure-diff record: `Access Control\n * (ACL Rules)` (7 ops, including `getAclRuleOrdering`/`updateAclRuleOrdering`) is\n * byte-identical between 10.4.57 and 10.6.97, and unreachable from any of the 14 schemas that DID\n * change. Full breakdown: `docs/unifi-acl-rule.md`.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as aclRules from '@distilled.cloud/unifi-network/access_control_acl_rules';\nimport * as Effect from 'effect/Effect';\nimport { type AclRuleAttributes, type AclRuleProps, attributesOf } from './acl-rule-form.ts';\nimport { matches } from './acl-rule-drift.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { AclRuleAttributes, AclRuleProps } from './acl-rule-form.ts';\nexport { declareAclRule } from './acl-rule-form.ts';\nexport { driftOf } from './acl-rule-drift.ts';\n\nexport interface UnifiAclRule extends Resource<\n 'Unifi.AclRule',\n AclRuleProps,\n AclRuleAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiAclRule = Resource<UnifiAclRule>('Unifi.AclRule', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `aclRule('block-iot-wan', props)` — `adopt(true)` piped on by default (H5); see `network.ts`. */\nexport const aclRule = (id: string, props: AclRuleProps) =>\n UnifiAclRule(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n AclRuleProps,\n aclRules.ACLRule,\n AclRuleAttributes,\n aclRules.GetAclRuleError\n> = {\n type: 'Unifi.AclRule',\n describe: (props) => `sites/${props.siteId}/acl-rules/${props.aclRuleId}`,\n fetchLive: (props) =>\n aclRules\n .getAclRule({ siteId: props.siteId, aclRuleId: props.aclRuleId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiAclRuleProvider = () =>\n Provider.effect(UnifiAclRule, Effect.succeed(UnifiAclRule.Provider.of(handlers)));\n",
17
+ "/**\n * `Unifi.AclRule`'s wire shape — the declarable subset of `ACLRule` (`getAclRule`'s own decode\n * target — `ACLRuleObject` is the structurally-identical page-list item type, unused here), and\n * how a live read\n * becomes both plan attributes and a rendered declaration. Rule ORDER is a separate resource\n * (`acl-rule-ordering.ts`, T5) — this file is one rule's own fields only.\n *\n * ⚠️ `sourceFilter`/`destinationFilter` STAY `unknown`, ON THE SDK'S OWN AUTHORITY.\n * `access_control_acl_rules.ts`'s own comment on both (\"Shape varies by variant — widened by\n * scripts/convert.ts; see README\") says the OpenAPI discriminator these vary over was flattened\n * at generation time (T10), same as `network-form.ts`'s `ipv4Configuration`. A3 (typing these\n * per-discriminator) is scoped OUT of this PR — see the plan's family inventory — so both stay\n * opaque JSON, compared with plain `deepEqual`, honest about not being decoded.\n *\n * ⚠️ KNOWN GAP UNTIL A3 (T15, red team, Minor finding 4): the vendor's own variant DTOs for these\n * filters carry SET-LIKE arrays too (`networkIds`, `ipAddressesOrSubnets`, `portFilter`,\n * `macAddresses` — `docs/unifi-acl-rule.md`'s own field notes). Because the filter stays opaque,\n * `deepEqual` compares those nested arrays order-sensitively, the same false-`update` risk this\n * file already removes for `deviceIds`/`protocolFilter` by normalizing them — but here there is\n * no decoded field to normalize. Accepted as a known gap, not fixed, until A3 types the filter\n * per-discriminator and can apply `sortedSet` to its own array fields.\n *\n * ⛔ `index` IS ATTRIBUTES-ONLY, NEVER DECLARED. `CreateAclRuleRequest`'s own field comment:\n * \"ACL rule index. This property is deprecated and has no effect. Use the dedicated ACL rule\n * reordering endpoint.\" The live object's current position is `acl-rule-ordering.ts`'s concern\n * (`ACLRuleOrdering.orderedAclRuleIds`), not this resource's declared identity — reporting it\n * here without comparing it avoids a field that can never converge under this family's own\n * `matches` (nothing here could ever change it) from silently pretending to be declarable.\n *\n * ⛔ `enforcingDeviceFilter.deviceIds` AND `protocolFilter` ARE SETS, NOT SEQUENCES — same\n * reasoning `firewall-zone-form.ts` gives for `networkIds`: \"IDs of the Switch-capable devices\n * used to enforce the ACL rule\" and \"Protocols this ACL rule will be applied to\" both describe\n * membership, with no indication either list is ordered like `acl-rule-ordering.ts`'s rule-id\n * sequence. Left alone, a console that returns either in a different order between calls would\n * plan a spurious `update` that this read-only family's `reconcile` then refuses forever.\n */\nimport type * as aclRules from '@distilled.cloud/unifi-network/access_control_acl_rules';\n\nconst sortedSet = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\n/** ★ EXPORTED so `acl-rule-drift.ts` normalizes both live and declared sides identically — see\n * `network-form.ts`'s own header for why a normalizer applied to only one side is a bug class. */\nexport const normalizeDeviceFilter = (\n value: aclRules.ACLRuleDeviceFilter | undefined,\n): aclRules.ACLRuleDeviceFilter | undefined =>\n value == null\n ? value\n : {\n ...value,\n ...(value.deviceIds !== undefined ? { deviceIds: sortedSet(value.deviceIds) } : {}),\n };\n\nexport const normalizeProtocolFilter = (\n value: readonly string[] | undefined,\n): string[] | undefined => (value == null ? undefined : sortedSet(value));\n\n/** ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` — `exactOptionalPropertyTypes`; see\n * `network-form.ts`'s own header. */\nexport interface AclRuleProps {\n siteId: string;\n aclRuleId: string;\n /** `ALLOW` or `BLOCK` (also accepts a future vendor value the spec did not enumerate). */\n action: string;\n description?: string | undefined;\n /** Traffic destination filter — opaque, see the header. */\n destinationFilter?: unknown;\n enabled: boolean;\n /** Devices enforcing this rule — compared as a SET, see the header. */\n enforcingDeviceFilter?: aclRules.ACLRuleDeviceFilter | undefined;\n name: string;\n /** Traffic source filter — opaque, see the header. */\n sourceFilter?: unknown;\n type: string;\n /** Protocols this rule applies to — compared as a SET, see the header. */\n protocolFilter?: string[] | undefined;\n networkIdFilter?: string | undefined;\n}\n\nexport interface AclRuleAttributes {\n siteId: string;\n aclRuleId: string;\n action: string;\n description: string | undefined;\n destinationFilter: unknown;\n enabled: boolean;\n enforcingDeviceFilter: aclRules.ACLRuleDeviceFilter | undefined;\n /** ACL rule index (current priority position) — server-derived, never declared; see header. */\n index: number;\n name: string;\n sourceFilter: unknown;\n type: string;\n protocolFilter: string[] | undefined;\n networkIdFilter: string | undefined;\n /** `metadata.origin` — server-derived, never declared; see `firewall-zone-form.ts`'s own. */\n metadataOrigin: string;\n}\n\nexport const attributesOf = (live: aclRules.ACLRule, props: AclRuleProps): AclRuleAttributes => ({\n siteId: props.siteId,\n aclRuleId: live.id,\n action: live.action,\n description: live.description,\n destinationFilter: live.destinationFilter,\n enabled: live.enabled,\n enforcingDeviceFilter: normalizeDeviceFilter(live.enforcingDeviceFilter),\n index: live.index,\n name: live.name,\n sourceFilter: live.sourceFilter,\n type: live.type,\n protocolFilter: normalizeProtocolFilter(live.protocolFilter),\n networkIdFilter: live.networkIdFilter,\n metadataOrigin: live.metadata.origin,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getAclRule`'s own response straight in\n * and writes the result as `aclRule('<id>', declareAclRule(live, siteId))` — no field is invented\n * or defaulted beyond what `attributesOf` already reports, so `matches(attributesOf(live, props),\n * declareAclRule(live, siteId))` is `true` by construction (`acl-rule.test.ts` proves it).\n */\nexport const declareAclRule = (live: aclRules.ACLRule, siteId: string): AclRuleProps => ({\n siteId,\n aclRuleId: live.id,\n action: live.action,\n description: live.description,\n destinationFilter: live.destinationFilter,\n enabled: live.enabled,\n enforcingDeviceFilter: normalizeDeviceFilter(live.enforcingDeviceFilter),\n name: live.name,\n sourceFilter: live.sourceFilter,\n type: live.type,\n protocolFilter: normalizeProtocolFilter(live.protocolFilter),\n networkIdFilter: live.networkIdFilter,\n});\n",
18
+ "/**\n * `Unifi.AclRule`'s `matches` AND `driftOf` (B6) — both built on the ONE field list below, the\n * same `fieldDrift`-is-the-only-comparison shape `network-drift.ts`'s header explains (MEDIUM-4,\n * red team, 2026-09-26): `matches(attrs, props) === (driftOf(live, props).length === 0)` is true\n * BY CONSTRUCTION.\n *\n * ⚠️ `stripNullish: true` ON EVERY FIELD BY DEFAULT (`drift.ts`'s `defaultEqual`) — same \"UniFi's\n * JSON answers `null`, a declaration omits\" tolerance `network-drift.ts` documents.\n * ⛔ `enforcingDeviceFilter`/`protocolFilter` RUN THROUGH `acl-rule-form.ts`'s normalizers on BOTH\n * sides before comparing — never applied to only `live` or only `declared` (that specific gap is\n * what MEDIUM-4's own mutant found in `network-drift.ts`; see its header).\n */\nimport type * as aclRules from '@distilled.cloud/unifi-network/access_control_acl_rules';\nimport { makeDriftOf } from './drift.ts';\nimport {\n type AclRuleAttributes,\n type AclRuleProps,\n attributesOf,\n normalizeDeviceFilter,\n normalizeProtocolFilter,\n} from './acl-rule-form.ts';\n\nconst fieldDrift = makeDriftOf<AclRuleAttributes, AclRuleProps>([\n { field: 'action', live: (a) => a.action, declared: (p) => p.action },\n { field: 'description', live: (a) => a.description, declared: (p) => p.description },\n {\n field: 'destinationFilter',\n live: (a) => a.destinationFilter,\n declared: (p) => p.destinationFilter,\n },\n { field: 'enabled', live: (a) => a.enabled, declared: (p) => p.enabled },\n {\n field: 'enforcingDeviceFilter',\n live: (a) => normalizeDeviceFilter(a.enforcingDeviceFilter),\n declared: (p) => normalizeDeviceFilter(p.enforcingDeviceFilter),\n },\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n { field: 'sourceFilter', live: (a) => a.sourceFilter, declared: (p) => p.sourceFilter },\n { field: 'type', live: (a) => a.type, declared: (p) => p.type },\n {\n field: 'protocolFilter',\n live: (a) => normalizeProtocolFilter(a.protocolFilter),\n declared: (p) => normalizeProtocolFilter(p.protocolFilter),\n },\n { field: 'networkIdFilter', live: (a) => a.networkIdFilter, declared: (p) => p.networkIdFilter },\n]);\n\nexport const matches = (attributes: AclRuleAttributes, props: AclRuleProps): boolean =>\n fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: aclRules.ACLRule, props: AclRuleProps) =>\n fieldDrift(attributesOf(live, props), props);\n",
19
+ "/**\n * `Unifi.AclRuleOrdering` — the ONE ordered list of user-defined ACL rule IDs for a site\n * (`getAclRuleOrdering`/`updateAclRuleOrdering`, `GET`/`PUT /v1/sites/{siteId}/acl-rules/ordering`),\n * READ-ONLY (`policy.ts`). T5's per-family variant of the ordering trap `docs/unifi-api-notes.md`\n * warns about for `Unifi.FirewallPolicyOrdering` (`firewall-policy-ordering.ts`, keyed per zone\n * pair rather than site-wide — see its own header for why): the endpoint replaces the WHOLE list\n * on a write, so a partial or resorted body would drop or reorder every OTHER rule too.\n * This family never writes at all, but its `matches`/`driftOf` (`acl-rule-ordering-form.ts`) are\n * still order-preserving, never `sortedSet`, so a future write path inherits a correct comparison\n * rather than one that has to be fixed alongside the first `update`.\n *\n * ★ ONE OBJECT PER SITE, NOT PER RULE — unlike `acl-rule.ts`, there is no per-object id in the\n * wire response at all (`ACLRuleOrdering` is just `{ orderedAclRuleIds }`); the resource's own\n * `id` (the stack-chosen string passed to `aclRuleOrdering(id, props)`) is a label the DECLARER\n * picks, same as `firewallZone`/`network`, not something read back from the vendor.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as aclRules from '@distilled.cloud/unifi-network/access_control_acl_rules';\nimport * as Effect from 'effect/Effect';\nimport {\n type AclRuleOrderingAttributes,\n type AclRuleOrderingProps,\n attributesOf,\n matches,\n} from './acl-rule-ordering-form.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { AclRuleOrderingAttributes, AclRuleOrderingProps } from './acl-rule-ordering-form.ts';\nexport { declareAclRuleOrdering, driftOf } from './acl-rule-ordering-form.ts';\n\nexport interface UnifiAclRuleOrdering extends Resource<\n 'Unifi.AclRuleOrdering',\n AclRuleOrderingProps,\n AclRuleOrderingAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiAclRuleOrdering = Resource<UnifiAclRuleOrdering>('Unifi.AclRuleOrdering', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `aclRuleOrdering('site-1', props)` — `adopt(true)` piped on by default (H5); see `network.ts`. */\nexport const aclRuleOrdering = (id: string, props: AclRuleOrderingProps) =>\n UnifiAclRuleOrdering(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n AclRuleOrderingProps,\n aclRules.ACLRuleOrdering,\n AclRuleOrderingAttributes,\n aclRules.GetAclRuleOrderingError\n> = {\n type: 'Unifi.AclRuleOrdering',\n describe: (props) => `sites/${props.siteId}/acl-rules/ordering`,\n fetchLive: (props) =>\n aclRules\n .getAclRuleOrdering({ siteId: props.siteId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiAclRuleOrderingProvider = () =>\n Provider.effect(UnifiAclRuleOrdering, Effect.succeed(UnifiAclRuleOrdering.Provider.of(handlers)));\n",
20
+ "/**\n * `Unifi.AclRuleOrdering`'s wire shape — ONE resource per site (`getAclRuleOrdering`'s own scope,\n * `GET /v1/sites/{siteId}/acl-rules/ordering`), not per rule. Compare with `firewall-zone-form.ts`'s\n * `networkIds`, which is a genuine SET: this file's `orderedAclRuleIds` is the opposite case.\n *\n * ⛔ T5 — `orderedAclRuleIds` IS A SEQUENCE, NEVER RUN THROUGH `sortedSet`. The endpoint's own name\n * (`updateAclRuleOrdering`) and its field's own vendor comment on `ACLRule.index` (\"Lower index\n * has higher priority\") both say this list's POSITION is the entire value it carries — rule\n * priority. `firewall-zone-form.ts`'s `sortedSet`/`network-form.ts`'s three set normalizers all\n * exist to stop the OPPOSITE mistake (treating a set as if order mattered); applying the same\n * fix here would silently discard the one thing this resource represents. `drift.ts`'s\n * `defaultEqual` (`deepEqual`) already compares arrays element-by-element without sorting them\n * (its header: \"sorts object KEYS but NOT ARRAY ELEMENTS\") — so leaving this field un-normalized\n * is what makes the comparison order-sensitive, not an oversight to \"fix\" later.\n *\n * ★ `makeDriftOf` (`drift.ts`), NO CUSTOM `equal` — same framework every other family's\n * `matches`/`driftOf` share (MEDIUM-4). Its default `deepEqual` is ALREADY order-sensitive for\n * arrays (see the paragraph above), so the absence of a custom `equal` here, unlike\n * `firewall-zone-form.ts`'s `networkIds` entry, is itself the \"never sortedSet\" rule in force —\n * not an oversight.\n */\nimport type * as aclRules from '@distilled.cloud/unifi-network/access_control_acl_rules';\nimport { makeDriftOf } from './drift.ts';\n\nexport interface AclRuleOrderingProps {\n siteId: string;\n /** Rule IDs in priority order, highest priority first — never compared as a set (see header). */\n orderedAclRuleIds: string[];\n}\n\nexport interface AclRuleOrderingAttributes {\n siteId: string;\n orderedAclRuleIds: string[];\n}\n\nexport const attributesOf = (\n live: aclRules.ACLRuleOrdering,\n props: AclRuleOrderingProps,\n): AclRuleOrderingAttributes => ({\n siteId: props.siteId,\n orderedAclRuleIds: live.orderedAclRuleIds,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getAclRuleOrdering`'s own response\n * straight in and writes the result as `aclRuleOrdering('site-1', declareAclRuleOrdering(live,\n * siteId))` — `matches(attributesOf(live, props), declareAclRuleOrdering(live, siteId))` is `true`\n * by construction (`acl-rule-ordering.test.ts` proves it).\n */\nexport const declareAclRuleOrdering = (\n live: aclRules.ACLRuleOrdering,\n siteId: string,\n): AclRuleOrderingProps => ({\n siteId,\n orderedAclRuleIds: live.orderedAclRuleIds,\n});\n\nconst fieldDrift = makeDriftOf<AclRuleOrderingAttributes, AclRuleOrderingProps>([\n {\n field: 'orderedAclRuleIds',\n live: (a) => a.orderedAclRuleIds,\n declared: (p) => p.orderedAclRuleIds,\n },\n]);\n\nexport const matches = (\n attributes: AclRuleOrderingAttributes,\n props: AclRuleOrderingProps,\n): boolean => fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`). Only one field exists, so this either reports nothing or the whole\n * ordered pair, never a per-position breakdown -- a future consumer wanting \"which rule moved\"\n * would need to diff the two arrays itself.\n */\nexport const driftOf = (live: aclRules.ACLRuleOrdering, props: AclRuleOrderingProps) =>\n fieldDrift(attributesOf(live, props), props);\n",
21
+ "/**\n * `Unifi.WifiBroadcast` — one WiFi network (SSID) broadcast on a UniFi Network site, READ-ONLY\n * (`policy.ts`).\n *\n * ⛔ T23, THE REASON THIS FAMILY NEVER CALLS `getWifiBroadcastDetails`. That single-object GET\n * returns `WifiBroadcastDetails`, whose `securityConfiguration`\n * (`WifiSecurityConfigurationDetailObject`) carries the WPA passphrase AND every PPSK entry's\n * passphrase — the pinned spec has NO `writeOnly` flag on either (verified: 4 `passphrase`\n * props, 0 `writeOnly`; A2 already redacts them to `Redacted.Redacted<string>` at the SDK layer,\n * `errors-and-secrets.test.ts`). This family goes further and never fetches that shape AT ALL:\n * the only call below is `getWifiBroadcastPage`, which answers `WifiBroadcastOverview` instead —\n * whose own `securityConfiguration` (`WifiSecurityConfigurationOverview`) is `{type,\n * presharedKeyNetworkIds}`. No passphrase field exists in that type, so `WifiBroadcastProps`/\n * `Attributes` (`wifi-broadcast-form.ts`) cannot carry one even by a future mistake.\n *\n * ⚠️ NO GET-BY-ID CALL EXISTS FOR THE OVERVIEW SHAPE — unlike every other family in this\n * directory (`getNetworkDetails`, `getFirewallZone`, `getDnsPolicy`, `getAclRule`),\n * `wifi_broadcasts.ts` only has `getWifiBroadcastPage` (list) and `getWifiBroadcastDetails`\n * (single, passphrase-bearing — forbidden above). `fetchLive` below walks every page with B0b's\n * `pageAll` and finds the one row matching `wifiBroadcastId` — the first real resource-level\n * consumer of that pager (previously exercised only by `paginate.test.ts` in isolation).\n *\n * ⚠️ THE `NotFound` CATCH BELOW IS FOR THE WHOLE WALK, NOT JUST THE FIRST PAGE CALL (red team,\n * MINOR-4, 2026-09-26 — corrected from an earlier claim that it only applied to the first call).\n * `pageAll` makes its sequential page calls inside one Effect pipeline, and `catchTag` here wraps\n * that entire pipeline; a 404 on ANY page of the walk — not only the first — folds to `undefined`\n * and discards whatever rows the walk had already collected. In practice this means \"the site\n * vanished partway through listing its broadcasts\" is indistinguishable from \"the site was never\n * there\" and from \"the target id just isn't among the collected rows\" — none of these three cases\n * has a separate vendor signal, and no other family's `fetchLive` distinguishes its own analogous\n * cases either. Read-only reporting treats all three the same conservative way (absent, not an\n * error); a write path would need to tell them apart.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as wifiBroadcasts from '@distilled.cloud/unifi-network/wifi_broadcasts';\nimport * as Effect from 'effect/Effect';\nimport {\n type WifiBroadcastAttributes,\n type WifiBroadcastProps,\n attributesOf,\n} from './wifi-broadcast-form.ts';\nimport { matches } from './wifi-broadcast-drift.ts';\nimport { type UnifiPaginationInconsistent, pageAll } from './paginate.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { WifiBroadcastAttributes, WifiBroadcastProps } from './wifi-broadcast-form.ts';\nexport { declareWifiBroadcast } from './wifi-broadcast-form.ts';\nexport { driftOf } from './wifi-broadcast-drift.ts';\n\nexport interface UnifiWifiBroadcast extends Resource<\n 'Unifi.WifiBroadcast',\n WifiBroadcastProps,\n WifiBroadcastAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiWifiBroadcast = Resource<UnifiWifiBroadcast>('Unifi.WifiBroadcast', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `wifiBroadcast('guest', props)` — `adopt(true)` piped on by default (H5); see `network.ts`. */\nexport const wifiBroadcast = (id: string, props: WifiBroadcastProps) =>\n UnifiWifiBroadcast(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n WifiBroadcastProps,\n wifiBroadcasts.WifiBroadcastOverview,\n WifiBroadcastAttributes,\n wifiBroadcasts.GetWifiBroadcastPageError | UnifiPaginationInconsistent\n> = {\n type: 'Unifi.WifiBroadcast',\n describe: (props) => `sites/${props.siteId}/wifi/broadcasts/${props.wifiBroadcastId}`,\n fetchLive: (props) =>\n pageAll(wifiBroadcasts.getWifiBroadcastPage, { siteId: props.siteId }).pipe(\n Effect.catchTag('NotFound', () => Effect.succeed(undefined)),\n Effect.map((rows) => rows?.find((row) => row.id === props.wifiBroadcastId)),\n ),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiWifiBroadcastProvider = () =>\n Provider.effect(UnifiWifiBroadcast, Effect.succeed(UnifiWifiBroadcast.Provider.of(handlers)));\n",
22
+ "/**\n * `Unifi.WifiBroadcast`'s wire shape — the declarable subset of `WifiBroadcastOverview`, and how\n * a live read becomes both plan attributes and a rendered declaration.\n *\n * ⛔ T23, THE WHOLE REASON THIS SHAPE DIFFERS FROM EVERY OTHER FAMILY HERE. Every other family's\n * `*-form.ts` (`network-form.ts`, `dns-policy-form.ts`, …) types its props against the vendor's\n * CREATE/UPDATE request — the fullest declarable shape, even though this stack never calls it.\n * `WifiBroadcastProps`/`WifiBroadcastAttributes` do NOT: `securityConfiguration` is typed\n * `wifiBroadcasts.WifiSecurityConfigurationOverview` — the PAGE OVERVIEW's own type, `{type,\n * presharedKeyNetworkIds}` — never `WifiSecurityConfigurationDetailObject` (the\n * `getWifiBroadcastDetails`/create/update shape, which carries `passphrase` and\n * `radiusConfiguration`). There is no DECLARED field on `WifiSecurityConfigurationOverview` for\n * `attributesOf`/`declareWifiBroadcast` below to name a passphrase into. See `wifi-broadcast.ts`'s\n * header for why `fetchLive` can only ever produce a `WifiBroadcastOverview` in the first place\n * (no get-by-id call exists for it).\n *\n * ⛔ THE TYPE SYSTEM ALONE DOES NOT ENFORCE THIS (red team, IMPORTANT-1, 2026-09-26). `@distilled\n * .cloud/core`'s wire decode does not strip a key a schema doesn't declare (this file's next\n * header block); the TS types above describe what a CORRECTLY-shaped response looks like, not\n * what the decoder actually returns at runtime, so a stray key nested inside `network`,\n * `hotspotConfiguration`, `broadcastingDeviceFilter`, or an element of `presharedKeyNetworkIds`\n * would ride along through a naive `{...live}`/pass-through copy exactly like the top-level\n * `securityConfiguration` object does. `normalizeRef`/`normalizeHotspot`/`normalizeDeviceFilter`\n * below rebuild EVERY nested object field-by-field for the same reason\n * `normalizeSecurityConfiguration` does — enumeration, not the type annotation, is what actually\n * stops it. `wifi-broadcast-secrets.test.ts` plants a sentinel inside each nested shape\n * (including one `presharedKeyNetworkIds` element) to prove it, not just the top level.\n *\n * ⚠️ EVERY OTHER FIELD REUSES THE SDK'S OWN OVERVIEW TYPES DIRECTLY (`WifiNetworkReference`,\n * `BroadcastingDeviceFilter`, `IntegrationWifiHotspotConfigurationOverviewDto`) rather than\n * redeclaring them — same reasoning `network-form.ts` gives for reusing `NetworkDHCPGuarding`/\n * `NetworkIPv6Configuration` verbatim: one shape to keep in sync with the SDK's own regen.\n *\n * ⛔ THREE FIELDS ARE SETS, NOT SEQUENCES, UNCONFIRMED AGAINST A LIVE CONSOLE (same caveat\n * `network-form.ts`'s header gives for its own three): `broadcastingDeviceFilter.deviceIds`/\n * `.deviceTagIds` and `securityConfiguration.presharedKeyNetworkIds` are membership sets by the\n * vendor description's own wording (\"Defines the custom scope of devices…\"), not ordered\n * sequences. `normalizeDeviceFilter`/`normalizeSecurityConfiguration`/`normalizeFrequencies`\n * neutralize the same \"spurious update, refused by a read-only reconcile\" failure mode\n * `network-form.ts`'s header describes, exactly like `firewall-zone-form.ts`'s `networkIds`.\n */\nimport type * as wifiBroadcasts from '@distilled.cloud/unifi-network/wifi_broadcasts';\n\nconst sortedStrings = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\nconst sortedFrequencies = (values: readonly number[]): number[] =>\n [...new Set(values)].sort((a, b) => a - b);\n\n/** Canonical sort key for a `{type, networkId?}` reference — stable regardless of vendor order. */\nconst refKey = (ref: wifiBroadcasts.WifiNetworkReference): string =>\n `${ref.type}:${ref.networkId ?? ''}`;\n\n/**\n * Rebuilds a reference field-by-field — never returns the object as received. IMPORTANT-1: this\n * runs on EVERY reference this file handles (`network`, and each `presharedKeyNetworkIds`\n * element) precisely because a reference is exactly the shape (`{network: WifiNetworkReference,\n * passphrase}`) the details call uses to carry a PPSK passphrase (`IntegrationWifiPresharedKeyDto`,\n * SDK `wifi_broadcasts.ts`); it is the most plausible place for a stray passphrase-shaped key to\n * appear on the wire if the vendor ever leaked one onto the overview response.\n */\nexport const normalizeRef = (\n ref: wifiBroadcasts.WifiNetworkReference | undefined,\n): wifiBroadcasts.WifiNetworkReference | undefined =>\n ref === undefined\n ? undefined\n : { type: ref.type, ...(ref.networkId !== undefined ? { networkId: ref.networkId } : {}) };\n\nconst sortedRefs = (\n values: readonly wifiBroadcasts.WifiNetworkReference[],\n): wifiBroadcasts.WifiNetworkReference[] => {\n const byKey = new Map(\n values.map(\n (ref) => [refKey(ref), normalizeRef(ref) as wifiBroadcasts.WifiNetworkReference] as const,\n ),\n );\n return [...byKey.values()].sort((a, b) => refKey(a).localeCompare(refKey(b)));\n};\n\n/**\n * Rebuilds the hotspot configuration field-by-field. `IntegrationWifiHotspotConfigurationOverviewDto`\n * declares only `type` (SDK `wifi_broadcasts.ts:743-753`) — a PASSPOINT/RADIUS secret would live on\n * a wire key this type doesn't name, exactly the IMPORTANT-1 risk `normalizeRef` guards against.\n */\nexport const normalizeHotspot = (\n value: wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined,\n): wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined =>\n value === undefined ? undefined : { type: value.type };\n\n/**\n * ★ EXPORTED: `wifi-broadcast-drift.ts` reuses these exact normalizers so `matches`/`driftOf` can\n * never quietly disagree about what counts as a change (MEDIUM-4, `network-drift.ts`'s header).\n *\n * ⛔ NO `{...value}` SPREAD (red team, IMPORTANT-1) — `BroadcastingDeviceFilter` declares exactly\n * `type`/`deviceIds`/`deviceTagIds` (SDK `wifi_broadcasts.ts:125-129`); enumerating exactly those\n * three is what stops a stray wire key here, the same reason `normalizeSecurityConfiguration`\n * (below) never spreads its input.\n */\nexport const normalizeDeviceFilter = (\n value: wifiBroadcasts.BroadcastingDeviceFilter | undefined,\n): wifiBroadcasts.BroadcastingDeviceFilter | undefined =>\n value == null\n ? value\n : {\n type: value.type,\n ...(value.deviceIds !== undefined ? { deviceIds: sortedStrings(value.deviceIds) } : {}),\n ...(value.deviceTagIds !== undefined\n ? { deviceTagIds: sortedStrings(value.deviceTagIds) }\n : {}),\n };\n\nexport const normalizeFrequencies = (value: readonly number[] | undefined): number[] | undefined =>\n value === undefined ? value : sortedFrequencies(value);\n\n/**\n * ⛔ NO `{...value}` SPREAD, UNLIKE EVERY OTHER NORMALIZER IN THIS DIRECTORY (`network-form.ts`'s\n * `normalizeDhcpGuarding`/`normalizeIpv6Configuration` both spread). This is the one field in\n * the whole `Unifi.*` tree with a spec-confirmed passphrase risk (T23): nothing in\n * `@distilled.cloud/core`'s wire key-mapping strips an object key the schema doesn't declare —\n * it only renames declared ones — so a stray extra key on the WIRE `securityConfiguration`\n * object (a vendor bug, or a future console version) survives `getWifiBroadcastPage`'s own\n * decode untyped, and `{...value}` would carry it straight through into `Attributes`/`Props`/the\n * rendered declaration. Enumerating exactly the two fields\n * `WifiSecurityConfigurationOverview` declares is what actually stops that — proved by\n * `wifi-broadcast-secrets.test.ts`'s sentinel test, which injects exactly this kind of stray key.\n */\nexport const normalizeSecurityConfiguration = (\n value: wifiBroadcasts.WifiSecurityConfigurationOverview,\n): wifiBroadcasts.WifiSecurityConfigurationOverview => ({\n type: value.type,\n ...(value.presharedKeyNetworkIds !== undefined\n ? { presharedKeyNetworkIds: sortedRefs(value.presharedKeyNetworkIds) }\n : {}),\n});\n\n/**\n * ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` ON PURPOSE — `exactOptionalPropertyTypes`; see\n * `network-form.ts`'s own header. `attributesOf`/`declareWifiBroadcast` echo a live optional\n * field structurally rather than defaulting it, so the target type must admit an explicit\n * `undefined`.\n */\nexport interface WifiBroadcastProps {\n siteId: string;\n wifiBroadcastId: string;\n name: string;\n enabled: boolean;\n type: string;\n network?: wifiBroadcasts.WifiNetworkReference | undefined;\n broadcastingDeviceFilter?: wifiBroadcasts.BroadcastingDeviceFilter | undefined;\n broadcastingFrequenciesGHz?: number[] | undefined;\n hotspotConfiguration?: wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined;\n /** ⛔ T23: the PAGE OVERVIEW's security shape ONLY — see this file's header. */\n securityConfiguration: wifiBroadcasts.WifiSecurityConfigurationOverview;\n}\n\nexport interface WifiBroadcastAttributes {\n siteId: string;\n wifiBroadcastId: string;\n name: string;\n enabled: boolean;\n type: string;\n network: wifiBroadcasts.WifiNetworkReference | undefined;\n broadcastingDeviceFilter: wifiBroadcasts.BroadcastingDeviceFilter | undefined;\n broadcastingFrequenciesGHz: number[] | undefined;\n hotspotConfiguration: wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined;\n securityConfiguration: wifiBroadcasts.WifiSecurityConfigurationOverview;\n /** `metadata.origin` — server-derived, never declared; same split as every other family. */\n metadataOrigin: string;\n}\n\nexport const attributesOf = (\n live: wifiBroadcasts.WifiBroadcastOverview,\n props: WifiBroadcastProps,\n): WifiBroadcastAttributes => ({\n siteId: props.siteId,\n wifiBroadcastId: live.id,\n name: live.name,\n enabled: live.enabled,\n type: live.type,\n network: normalizeRef(live.network),\n broadcastingDeviceFilter: normalizeDeviceFilter(live.broadcastingDeviceFilter),\n broadcastingFrequenciesGHz: normalizeFrequencies(live.broadcastingFrequenciesGHz),\n hotspotConfiguration: normalizeHotspot(live.hotspotConfiguration),\n securityConfiguration: normalizeSecurityConfiguration(live.securityConfiguration),\n metadataOrigin: live.metadata.origin,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getWifiBroadcastPage`'s own per-row\n * response straight in and writes the result into `alchemy.run.ts` as `wifiBroadcast('<id>',\n * declareWifiBroadcast(live, siteId))` — no field is invented or defaulted here beyond what\n * `attributesOf` already reports, so `matches(attributesOf(live, props), declareWifiBroadcast(live,\n * siteId))` is `true` by construction (`wifi-broadcast.test.ts` proves it) for any live object\n * this SDK can decode. Builds ONLY from fields `WifiBroadcastOverview` has — never a passphrase,\n * which that type cannot express (see this file's header).\n */\nexport const declareWifiBroadcast = (\n live: wifiBroadcasts.WifiBroadcastOverview,\n siteId: string,\n): WifiBroadcastProps => ({\n siteId,\n wifiBroadcastId: live.id,\n name: live.name,\n enabled: live.enabled,\n type: live.type,\n network: normalizeRef(live.network),\n broadcastingDeviceFilter: normalizeDeviceFilter(live.broadcastingDeviceFilter),\n broadcastingFrequenciesGHz: normalizeFrequencies(live.broadcastingFrequenciesGHz),\n hotspotConfiguration: normalizeHotspot(live.hotspotConfiguration),\n securityConfiguration: normalizeSecurityConfiguration(live.securityConfiguration),\n});\n",
23
+ "/**\n * `Unifi.WifiBroadcast`'s `matches` AND `driftOf` (B6) — built on the ONE field list below, the\n * same `fieldDrift`-is-the-only-comparison shape `dns-policy-drift.ts`'s header explains\n * (MEDIUM-4): `matches(attrs, props) === (driftOf(live, props).length === 0)` is true BY\n * CONSTRUCTION, never two hand-kept-in-sync comparisons.\n *\n * ★ THREE FIELDS REUSE `wifi-broadcast-form.ts`'s OWN NORMALIZERS ON THE `declared` SIDE TOO, not\n * just the `live` side — `firewall-zone-form.ts`'s `networkIds` gives the same reason: comparing\n * a freshly-normalized live value against an un-normalized declared value would report spurious\n * drift the moment a committed declaration's array happens to be in a different (still valid)\n * order than what `declareWifiBroadcast` would itself produce today.\n */\nimport type * as wifiBroadcasts from '@distilled.cloud/unifi-network/wifi_broadcasts';\nimport { makeDriftOf } from './drift.ts';\nimport {\n type WifiBroadcastAttributes,\n type WifiBroadcastProps,\n attributesOf,\n normalizeDeviceFilter,\n normalizeFrequencies,\n normalizeHotspot,\n normalizeRef,\n normalizeSecurityConfiguration,\n} from './wifi-broadcast-form.ts';\n\nconst fieldDrift = makeDriftOf<WifiBroadcastAttributes, WifiBroadcastProps>([\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n { field: 'enabled', live: (a) => a.enabled, declared: (p) => p.enabled },\n { field: 'type', live: (a) => a.type, declared: (p) => p.type },\n {\n // IMPORTANT-1: `a.network` is already rebuilt field-by-field inside `attributesOf`; the\n // declared side gets the same treatment so a hand-written declaration carrying a stray key\n // cannot report phantom drift against a live side that never could.\n field: 'network',\n live: (a) => a.network,\n declared: (p) => normalizeRef(p.network),\n },\n {\n field: 'broadcastingDeviceFilter',\n live: (a) => a.broadcastingDeviceFilter,\n declared: (p) => normalizeDeviceFilter(p.broadcastingDeviceFilter),\n },\n {\n field: 'broadcastingFrequenciesGHz',\n live: (a) => a.broadcastingFrequenciesGHz,\n declared: (p) => normalizeFrequencies(p.broadcastingFrequenciesGHz),\n },\n {\n field: 'hotspotConfiguration',\n live: (a) => a.hotspotConfiguration,\n declared: (p) => normalizeHotspot(p.hotspotConfiguration),\n },\n {\n field: 'securityConfiguration',\n live: (a) => a.securityConfiguration,\n declared: (p) => normalizeSecurityConfiguration(p.securityConfiguration),\n },\n]);\n\nexport const matches = (attributes: WifiBroadcastAttributes, props: WifiBroadcastProps): boolean =>\n fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: wifiBroadcasts.WifiBroadcastOverview, props: WifiBroadcastProps) =>\n fieldDrift(attributesOf(live, props), props);\n",
24
+ "/**\n * B0b / T9: a CONSUMER-SIDE sequential offset-pager for the SDK's list operations. None of them\n * paginate on their own — `getNetworksOverviewPage`, `getFirewallZones`, `getAclRules`, … all take\n * `{ ..., offset?, limit? }` and answer `{ count, data, limit, offset, totalCount }` (the shape\n * `protocol.ts`'s own header names); there is no `smithy.api#paginated` trait in the pinned spec\n * to generate a helper from.\n *\n * ⛔ NO SDK OR `@distilled.cloud/core` CHANGE (M1 — upstream's own convention: pagination stays\n * out of the 59 packages walked down for `distilled-doctrine`). This is a plain function over\n * whatever `Effect.Effect<OffsetPage<T>, E, R>` a resource file's own `fetchPage` already is.\n * ⛔ STOPS ON `totalCount`, NEVER ON `data.length < limit` (T9's own wording). A page can come back\n * short of the requested `limit` for reasons unrelated to \"this was the last page\" (a vendor-side\n * per-call cap, a narrow filter) — `totalCount` is the one number the response commits to, so it\n * is the only thing this loop trusts to stop early. An empty page (`data.length === 0`) is the\n * other legitimate stop, and the only one checked before `totalCount` is read.\n * ⚠️ NOT SEQUENCED AGAINST A LIVE CONSOLE THAT MUTATES BETWEEN CALLS. `totalCount` is whatever the\n * MOST RECENT page reported; a page walk racing a live edit could see a row twice, miss one, or\n * just see a moving target. `homeflare-network`'s own import already treats one family's fetch\n * as one sequenced run for this reason (T27's neighboring trap) — nothing here can fix a\n * concurrent mutation, only DETECT the common ways one shows up (below) instead of returning a\n * silently wrong row set.\n *\n * ⚠️ RED TEAM (2026-09-26, IMPORTANT-3): the first version of this file trusted the vendor's\n * paged response completely — a lying or drifting `totalCount`, an ignored `offset`, or a row\n * added/removed mid-walk all produced a WRONG row set with no signal at all (silent truncation,\n * an infinite loop, or a subtly incomplete list an importer would commit as if it were correct).\n * Every list op's response already carries `offset` alongside `totalCount` (`networks.ts`'s\n * `NetworkOverviewPage`, and `protocol.ts`'s header for every OTHER list op too); the three\n * checks below are consistency proofs built entirely from fields the vendor already sends, plus\n * a hard ceiling as a backstop against a walk that can never converge. These are typed failures,\n * not `Effect.die`: an inconsistent paged response is a VENDOR-DATA condition a caller may\n * legitimately want to retry or report on (distilled-doctrine: a defect is for \"structurally\n * impossible\" code paths, not for data the wire actually sent).\n */\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\n\n/** The one response shape every UniFi Network list operation already has (`protocol.ts`). */\nexport interface OffsetPage<T> {\n readonly data: readonly T[];\n readonly offset: number;\n readonly totalCount: number;\n}\n\n/** The one request shape every UniFi Network list operation already accepts, plus its own fields. */\nexport type OffsetPageRequest = { readonly offset?: number; readonly limit?: number };\n\n/** Well past any family's real row count, even at a one-row page size (`docs/unifi.md`: a real\n * console's largest family is in the hundreds, not thousands) — a walk that reaches this many\n * pages is not looking at a real UniFi site, it is looking at a response that never lets `offset`\n * converge on `totalCount`. */\nconst MAX_PAGES = 1000;\n\nexport class UnifiPaginationInconsistent extends Data.TaggedError('UnifiPaginationInconsistent')<{\n readonly reason:\n | 'offset-mismatch'\n | 'total-count-changed'\n | 'row-count-mismatch'\n | 'page-ceiling';\n readonly detail: string;\n}> {\n override get message(): string {\n return (\n `UniFi pager: ${this.reason} -- ${this.detail}. The paged response contradicted itself (or ` +\n 'the walk ran implausibly long); the rows collected so far cannot be trusted and are not returned.'\n );\n }\n}\n\n/**\n * Walks every page of `fetchPage`, starting at `request.offset ?? 0`, and returns every row in\n * order. `request.limit`, if given, is kept as the page size for every call; the vendor's own\n * default applies otherwise — this loop never invents one (T18: \"pass ops only their own fields\").\n *\n * Three self-consistency checks run against fields the vendor already sends with every page —\n * detection, not a fix, for the \"live console mutates mid-walk\" case the header above accepts as\n * unfixable from the client alone: a row added AND removed in the same window can still net out to\n * a `totalCount` and final row count that agree by coincidence. What this closes is the SILENT\n * version of every case actually observed in review: an ignored `offset`, a `totalCount` that\n * drifts between calls, and an undercounted `totalCount` that truncates the walk early.\n *\n * ⛔ `numOrPlaceholder` (red team, MINOR-3, 2026-09-26): nothing in `@distilled.cloud/core` decodes\n * or validates a page response's shape (this file's own `wifi-broadcast.ts` neighbor documents\n * why), so `page.offset`/`page.totalCount` can be ANY JSON value, not just a number, if the wire\n * ever sends something malformed. Every `detail` string below prints these through\n * `numOrPlaceholder` rather than raw template interpolation, so a non-numeric value — however it\n * got there — can never inject arbitrary response text into an error message a caller might log.\n */\n/** A finite number renders as itself; anything else (a malformed wire value) renders as a fixed\n * placeholder, never as the value itself — see this file's header (MINOR-3). */\nconst numOrPlaceholder = (value: unknown): string =>\n typeof value === 'number' && Number.isFinite(value) ? String(value) : '<non-numeric>';\n\nexport const pageAll = <Req extends OffsetPageRequest, T, E, R>(\n fetchPage: (request: Req) => Effect.Effect<OffsetPage<T>, E, R>,\n request: Req,\n): Effect.Effect<T[], E | UnifiPaginationInconsistent, R> =>\n Effect.gen(function* () {\n const rows: T[] = [];\n const startOffset = request.offset ?? 0;\n let offset = startOffset;\n let totalCount: number | undefined;\n for (let pageCount = 1; ; pageCount++) {\n if (pageCount > MAX_PAGES) {\n return yield* new UnifiPaginationInconsistent({\n reason: 'page-ceiling',\n detail: `stopped after ${MAX_PAGES} pages at offset ${offset} without reaching totalCount`,\n });\n }\n // ⚠️ THE CAST IS SOUND, NOT A SHORTCUT: `Req` is a generic parameter, so TS cannot see that\n // spreading a value already typed `Req` and overriding one of `OffsetPageRequest`'s own\n // fields reconstructs a `Req` — but every field except `offset` is untouched from the\n // caller's own `request`, so it does.\n const page = yield* fetchPage({ ...request, offset } as Req);\n if (page.offset !== offset) {\n return yield* new UnifiPaginationInconsistent({\n reason: 'offset-mismatch',\n detail: `requested offset ${offset}, page echoed ${numOrPlaceholder(page.offset)} -- rows may be duplicated or skipped`,\n });\n }\n if (totalCount !== undefined && page.totalCount !== totalCount) {\n return yield* new UnifiPaginationInconsistent({\n reason: 'total-count-changed',\n detail: `totalCount changed from ${numOrPlaceholder(totalCount)} to ${numOrPlaceholder(page.totalCount)} between page calls`,\n });\n }\n totalCount = page.totalCount;\n rows.push(...page.data);\n if (page.data.length === 0) break;\n offset += page.data.length;\n if (offset >= totalCount) break;\n }\n const expected = totalCount - startOffset;\n if (rows.length !== expected) {\n return yield* new UnifiPaginationInconsistent({\n reason: 'row-count-mismatch',\n detail: `collected ${rows.length} rows but totalCount(${numOrPlaceholder(totalCount)}) - startOffset(${startOffset}) = ${numOrPlaceholder(expected)}`,\n });\n }\n return rows;\n });\n",
25
+ "/**\n * `Unifi.FirewallPolicy` — one firewall policy on a UniFi Network site, READ-ONLY (`policy.ts`).\n * `Unifi.FirewallPolicyOrdering` (`firewall-policy-ordering.ts`) is the sibling resource for policy\n * ORDER within a zone pair (T5) — one per `(sourceFirewallZoneId, destinationFirewallZoneId)`, never\n * this file's concern. `docs/unifi.md`'s own header names this pair as \"a bigger, separate PR\";\n * this is that PR — see `docs/unifi-firewall-policy.md` for the full breakdown.\n *\n * ★ T11 GATE, CHECKED 2026-09-26: the `Firewall` tag's 13 operations (5 already covered for\n * `Unifi.FirewallZone` in `docs/unifi.md`, 8 new here for `FirewallPolicy` — `getFirewallPolicies`,\n * `createFirewallPolicy`, `getFirewallPolicyOrdering`, `updateFirewallPolicyOrdering`,\n * `deleteFirewallPolicy`, `getFirewallPolicy`, `patchFirewallPolicy`, `updateFirewallPolicy`) are\n * byte-identical between 10.4.57 and 10.6.97, full raw operation objects; the 106-schema closure\n * reachable from all 13 (`$ref` + `discriminator.mapping`) shares zero names with the 14 schemas\n * that DID change elsewhere in the document. Corroborated independently by the A3 changeset's own\n * broader 25-operation/139-schema re-check (`.changeset/unifi-network-a3-typed-discriminator-filters.md`).\n * Full breakdown: `docs/unifi-firewall-policy.md`.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport * as Effect from 'effect/Effect';\nimport {\n type FirewallPolicyAttributes,\n type FirewallPolicyProps,\n attributesOf,\n} from './firewall-policy-form.ts';\nimport { matches } from './firewall-policy-drift.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { FirewallPolicyAttributes, FirewallPolicyProps } from './firewall-policy-form.ts';\nexport { declareFirewallPolicy } from './firewall-policy-form.ts';\nexport { driftOf } from './firewall-policy-drift.ts';\n\nexport interface UnifiFirewallPolicy extends Resource<\n 'Unifi.FirewallPolicy',\n FirewallPolicyProps,\n FirewallPolicyAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiFirewallPolicy = Resource<UnifiFirewallPolicy>('Unifi.FirewallPolicy', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `firewallPolicy('block-iot-wan', props)` — `adopt(true)` piped on by default (H5); see\n * `network.ts`. */\nexport const firewallPolicy = (id: string, props: FirewallPolicyProps) =>\n UnifiFirewallPolicy(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n FirewallPolicyProps,\n firewall.FirewallPolicy,\n FirewallPolicyAttributes,\n firewall.GetFirewallPolicyError\n> = {\n type: 'Unifi.FirewallPolicy',\n describe: (props) => `sites/${props.siteId}/firewall/policies/${props.firewallPolicyId}`,\n fetchLive: (props) =>\n firewall\n .getFirewallPolicy({ siteId: props.siteId, firewallPolicyId: props.firewallPolicyId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiFirewallPolicyProvider = () =>\n Provider.effect(UnifiFirewallPolicy, Effect.succeed(UnifiFirewallPolicy.Provider.of(handlers)));\n",
26
+ "/**\n * `Unifi.FirewallPolicy`'s wire shape — the declarable subset of `FirewallPolicy` (`getFirewallPolicy`'s\n * own decode target), and how a live read becomes both plan attributes and a rendered declaration.\n * Rule ORDER is a separate resource (`firewall-policy-ordering.ts`, T5) — this file is one policy's\n * own fields only.\n *\n * ★ `source`/`destination`/`ipProtocolScope`/`action`/`schedule` STAY COMPOUND, COMPARED WHOLESALE —\n * post-A3 (`8ee11f1`, \"type FirewallPolicy/ACL/TrafficMatchingList filters per discriminator\"),\n * these decode as real per-discriminator TS unions instead of `unknown`\n * (`discriminated-filter-decode.test.ts` proves the wire path keeps every key, including one this\n * pinned spec doesn't yet declare), so this file is honest about their real shape rather than\n * casting to `unknown` the way `acl-rule-form.ts` still does for its own opaque filters. It does\n * NOT follow that up with a leaf-by-leaf declarable schema for all 12 filter variants, though:\n * that is new modelling work this PR does not attempt, so each field is compared with plain\n * `deepEqual` (`stripNullish`) as ONE value, same proportion `acl-rule-form.ts`'s\n * `enforcingDeviceFilter` gets (one level of structure, not exploded into scalars).\n *\n * ⚠️ KNOWN GAP (T15, same shape as `acl-rule-form.ts`'s own — accepted, not fixed, here either):\n * several of the vendor's filter variants nested inside `source`/`destination`/`ipProtocolScope`\n * are themselves SET-LIKE arrays with no documented order —\n * `FirewallPolicyMACAddressFilter.macAddresses`, `FirewallPolicyNetworkFilter.networkIds`,\n * `FirewallPolicyApplicationFilter.applicationIds`, `FirewallPolicyIPAddressFilter.items`,\n * `FirewallPolicyRegionFilter.regions`, `FirewallPolicyVPNServerFilter.vpnServerIds`,\n * `FirewallSchedule.repeatOnDays` — `deepEqual` compares every one of them order-sensitively, so a\n * console-side reorder with no membership change would report a spurious drift this read-only\n * family's `reconcile` then refuses forever. Not fixed here for the same reason ACL's own gap\n * isn't: normalizing 12 filter variants' own nested arrays is a bigger, separate change, not a\n * proportional extension of typing per-discriminator (A3) into set-aware comparison too.\n *\n * ⛔ `connectionStateFilter` IS THE ONE TOP-LEVEL PROPS ARRAY, AND IS A SET — \"Match on firewall\n * connection state. If null, matches all connection states\" describes membership, not a sequence\n * (unlike `firewall-policy-ordering-form.ts`'s own `beforeSystemDefined`/`afterSystemDefined`,\n * where position IS the value). Normalized with the same `sortedSet` every other family in this\n * directory uses for a set-shaped array (`firewall-zone-form.ts`'s `networkIds`,\n * `acl-rule-form.ts`'s `protocolFilter`).\n *\n * ⛔ `index` IS ATTRIBUTES-ONLY, NEVER DECLARED. `CreateFirewallPolicyRequest` has no `index`\n * field at all — the live object's current position among ALL policies on the site is reported,\n * but a policy's PRIORITY relative to its own zone pair is `firewall-policy-ordering.ts`'s concern\n * (`OrderedFirewallPolicyIDs`), same split `acl-rule-form.ts`'s own `index` documents.\n *\n * ⛔ `metadata.origin` IS ATTRIBUTES-ONLY, same split every other family here uses\n * (`firewall-zone-form.ts`'s `metadataOrigin`) — `UserOrSystemDefinedOrDerivedEntityMetadata` is\n * server-derived, no writable field accepts it.\n */\nimport type * as firewall from '@distilled.cloud/unifi-network/firewall';\n\nconst sortedSet = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\n/** ★ EXPORTED so `firewall-policy-drift.ts` normalizes both live and declared sides identically —\n * see `acl-rule-form.ts`'s own header for why a normalizer applied to only one side is a bug\n * class (MEDIUM-4). */\nexport const normalizeConnectionStateFilter = (\n value: readonly string[] | undefined,\n): string[] | undefined => (value == null ? undefined : sortedSet(value));\n\n/** ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` — `exactOptionalPropertyTypes`; see\n * `network-form.ts`'s own header. */\nexport interface FirewallPolicyProps {\n siteId: string;\n firewallPolicyId: string;\n action: firewall.FirewallPolicyAction;\n /** Connection states this policy matches — compared as a SET, see the header. */\n connectionStateFilter?: string[] | undefined;\n description?: string | undefined;\n /** Traffic destination — compound, compared wholesale; see the header's known gap. */\n destination: firewall.FirewallPolicyDestination;\n enabled: boolean;\n /** IP version + protocol scope — compound, compared wholesale; see the header's known gap. */\n ipProtocolScope: firewall.FirewallPolicyIPProtocolScope;\n ipsecFilter?: string | undefined;\n loggingEnabled: boolean;\n name: string;\n /** Schedule this policy is active on — compound, compared wholesale; see the header's known gap. */\n schedule?: firewall.FirewallSchedule | undefined;\n /** Traffic source — compound, compared wholesale; see the header's known gap. */\n source: firewall.FirewallPolicySource;\n}\n\nexport interface FirewallPolicyAttributes {\n siteId: string;\n firewallPolicyId: string;\n action: firewall.FirewallPolicyAction;\n connectionStateFilter: string[] | undefined;\n description: string | undefined;\n destination: firewall.FirewallPolicyDestination;\n enabled: boolean;\n /** Current position among ALL policies on the site — server-derived, never declared; see header. */\n index: number;\n ipProtocolScope: firewall.FirewallPolicyIPProtocolScope;\n ipsecFilter: string | undefined;\n loggingEnabled: boolean;\n name: string;\n schedule: firewall.FirewallSchedule | undefined;\n source: firewall.FirewallPolicySource;\n /** `metadata.origin` — server-derived, never declared; see `firewall-zone-form.ts`'s own. */\n metadataOrigin: string;\n}\n\nexport const attributesOf = (\n live: firewall.FirewallPolicy,\n props: FirewallPolicyProps,\n): FirewallPolicyAttributes => ({\n siteId: props.siteId,\n firewallPolicyId: live.id,\n action: live.action,\n connectionStateFilter: normalizeConnectionStateFilter(live.connectionStateFilter),\n description: live.description,\n destination: live.destination,\n enabled: live.enabled,\n index: live.index,\n ipProtocolScope: live.ipProtocolScope,\n ipsecFilter: live.ipsecFilter,\n loggingEnabled: live.loggingEnabled,\n name: live.name,\n schedule: live.schedule,\n source: live.source,\n metadataOrigin: live.metadata.origin,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getFirewallPolicy`'s own response\n * straight in and writes the result as `firewallPolicy('<id>', declareFirewallPolicy(live, siteId))`\n * — no field is invented or defaulted beyond what `attributesOf` already reports, so\n * `matches(attributesOf(live, props), declareFirewallPolicy(live, siteId))` is `true` by\n * construction (`firewall-policy.test.ts` proves it).\n */\nexport const declareFirewallPolicy = (\n live: firewall.FirewallPolicy,\n siteId: string,\n): FirewallPolicyProps => ({\n siteId,\n firewallPolicyId: live.id,\n action: live.action,\n connectionStateFilter: normalizeConnectionStateFilter(live.connectionStateFilter),\n description: live.description,\n destination: live.destination,\n enabled: live.enabled,\n ipProtocolScope: live.ipProtocolScope,\n ipsecFilter: live.ipsecFilter,\n loggingEnabled: live.loggingEnabled,\n name: live.name,\n schedule: live.schedule,\n source: live.source,\n});\n",
27
+ "/**\n * `Unifi.FirewallPolicy`'s `matches` AND `driftOf` (B6) — both built on the ONE field list below,\n * the same `fieldDrift`-is-the-only-comparison shape `network-drift.ts`'s header explains (MEDIUM-4,\n * red team, 2026-09-26): `matches(attrs, props) === (driftOf(live, props).length === 0)` is true\n * BY CONSTRUCTION, never two hand-kept-in-sync comparisons.\n *\n * ⚠️ `stripNullish: true` ON EVERY FIELD BY DEFAULT (`drift.ts`'s `defaultEqual`) — same \"UniFi's\n * JSON answers `null`, a declaration omits\" tolerance every other family here relies on.\n * ⛔ `connectionStateFilter` RUNS THROUGH `firewall-policy-form.ts`'s normalizer on BOTH sides\n * before comparing — never applied to only `live` or only `declared` (the specific gap MEDIUM-4's\n * own mutant found in `network-drift.ts`; see its header, and `acl-rule-drift.ts`'s own repeat of\n * this same rule for `protocolFilter`/`enforcingDeviceFilter`).\n * ★ `action`/`destination`/`ipProtocolScope`/`schedule`/`source` compare with the plain default\n * `equal` (whole-value `deepEqual`) — see `firewall-policy-form.ts`'s header for the accepted\n * known gap this leaves in their own nested set-like arrays.\n */\nimport type * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport { makeDriftOf } from './drift.ts';\nimport {\n type FirewallPolicyAttributes,\n type FirewallPolicyProps,\n attributesOf,\n normalizeConnectionStateFilter,\n} from './firewall-policy-form.ts';\n\nconst fieldDrift = makeDriftOf<FirewallPolicyAttributes, FirewallPolicyProps>([\n { field: 'action', live: (a) => a.action, declared: (p) => p.action },\n {\n field: 'connectionStateFilter',\n live: (a) => normalizeConnectionStateFilter(a.connectionStateFilter),\n declared: (p) => normalizeConnectionStateFilter(p.connectionStateFilter),\n },\n { field: 'description', live: (a) => a.description, declared: (p) => p.description },\n { field: 'destination', live: (a) => a.destination, declared: (p) => p.destination },\n { field: 'enabled', live: (a) => a.enabled, declared: (p) => p.enabled },\n { field: 'ipProtocolScope', live: (a) => a.ipProtocolScope, declared: (p) => p.ipProtocolScope },\n { field: 'ipsecFilter', live: (a) => a.ipsecFilter, declared: (p) => p.ipsecFilter },\n { field: 'loggingEnabled', live: (a) => a.loggingEnabled, declared: (p) => p.loggingEnabled },\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n { field: 'schedule', live: (a) => a.schedule, declared: (p) => p.schedule },\n { field: 'source', live: (a) => a.source, declared: (p) => p.source },\n]);\n\nexport const matches = (\n attributes: FirewallPolicyAttributes,\n props: FirewallPolicyProps,\n): boolean => fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: firewall.FirewallPolicy, props: FirewallPolicyProps) =>\n fieldDrift(attributesOf(live, props), props);\n",
28
+ "/**\n * `Unifi.FirewallPolicyOrdering` — the ordered `before`/`afterSystemDefined` policy-id lists for\n * ONE `(sourceFirewallZoneId, destinationFirewallZoneId)` zone pair (`getFirewallPolicyOrdering`,\n * `GET /v1/sites/{siteId}/firewall/policies/ordering`), READ-ONLY (`policy.ts`). T5/I4's per-family\n * variant: `docs/unifi.md`'s own header named this \"a future `Unifi.FirewallPolicyOrdering`\" as a\n * separate, bigger PR (see `acl-rule-ordering.ts`'s header for the same forward reference, now\n * resolved) — this is that resource.\n *\n * ⚠️ NOT ONE OBJECT PER SITE, UNLIKE `acl-rule-ordering.ts` — the identity is the TRIPLE\n * `(siteId, sourceFirewallZoneId, destinationFirewallZoneId)`, both zone ids passed as QUERY\n * parameters on the same path every time (`firewall.ts`'s `GetFirewallPolicyOrderingRequest`).\n * There is no \"list every zone pair with an ordering\" endpoint — a caller (a future Lane C import)\n * has to already know which pairs exist, typically by walking `firewall.getFirewallPolicies`\n * (T9's own 424-live-policy scale — B0b's `pageAll`, see `firewall-policy.test.ts`'s own decode\n * proof) and grouping by each policy's `source.zoneId`/`destination.zoneId`. Nothing here performs\n * that discovery — this resource is one already-known pair's ordering, same as\n * `firewall-policy.ts`/`acl-rule.ts` are one already-known object's own fields.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport * as Effect from 'effect/Effect';\nimport {\n type FirewallPolicyOrderingAttributes,\n type FirewallPolicyOrderingProps,\n attributesOf,\n matches,\n} from './firewall-policy-ordering-form.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type {\n FirewallPolicyOrderingAttributes,\n FirewallPolicyOrderingProps,\n} from './firewall-policy-ordering-form.ts';\nexport { declareFirewallPolicyOrdering, driftOf } from './firewall-policy-ordering-form.ts';\n\nexport interface UnifiFirewallPolicyOrdering extends Resource<\n 'Unifi.FirewallPolicyOrdering',\n FirewallPolicyOrderingProps,\n FirewallPolicyOrderingAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiFirewallPolicyOrdering = Resource<UnifiFirewallPolicyOrdering>(\n 'Unifi.FirewallPolicyOrdering',\n { defaultRemovalPolicy: 'retain' },\n);\n\n/** `firewallPolicyOrdering('iot-to-external', props)` — `adopt(true)` piped on by default (H5); see\n * `network.ts`. */\nexport const firewallPolicyOrdering = (id: string, props: FirewallPolicyOrderingProps) =>\n UnifiFirewallPolicyOrdering(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n FirewallPolicyOrderingProps,\n firewall.IntegrationFirewallPolicyOrderingDto,\n FirewallPolicyOrderingAttributes,\n firewall.GetFirewallPolicyOrderingError\n> = {\n type: 'Unifi.FirewallPolicyOrdering',\n describe: (props) =>\n `sites/${props.siteId}/firewall/policies/ordering` +\n `?sourceFirewallZoneId=${props.sourceFirewallZoneId}` +\n `&destinationFirewallZoneId=${props.destinationFirewallZoneId}`,\n fetchLive: (props) =>\n firewall\n .getFirewallPolicyOrdering({\n siteId: props.siteId,\n sourceFirewallZoneId: props.sourceFirewallZoneId,\n destinationFirewallZoneId: props.destinationFirewallZoneId,\n })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiFirewallPolicyOrderingProvider = () =>\n Provider.effect(\n UnifiFirewallPolicyOrdering,\n Effect.succeed(UnifiFirewallPolicyOrdering.Provider.of(handlers)),\n );\n",
29
+ "/**\n * `Unifi.FirewallPolicyOrdering`'s wire shape — ONE resource per `(sourceFirewallZoneId,\n * destinationFirewallZoneId)` ZONE PAIR (`getFirewallPolicyOrdering`'s own scope, `GET\n * /v1/sites/{siteId}/firewall/policies/ordering?sourceFirewallZoneId=…&destinationFirewallZoneId=…`),\n * not per policy and not site-wide. Compare with `acl-rule-ordering-form.ts`'s own header: ACL rule\n * order is ONE list for the whole site because ACL rules have no zone concept; firewall policies are\n * scoped to a zone pair (`firewall-zone.ts`'s own `Unifi.FirewallZone`), so the vendor keys ordering\n * the same way — a console can have as many `Unifi.FirewallPolicyOrdering` resources as it has\n * zone pairs with at least one user-defined policy between them.\n *\n * ⛔ T5 — `beforeSystemDefined`/`afterSystemDefined` ARE EACH A SEQUENCE, NEVER RUN THROUGH\n * `sortedSet`. `OrderedFirewallPolicyIDs`' own shape splits a zone pair's user-defined policies\n * into the ones ordered BEFORE the system-defined policies and the ones ordered AFTER them —\n * within each half, position is priority, the same \"lower index/earlier position wins\" reasoning\n * `acl-rule-ordering-form.ts` gives for `orderedAclRuleIds`. `drift.ts`'s `defaultEqual`\n * (`deepEqual`) already compares arrays element-by-element without sorting them, so leaving both\n * fields un-normalized is what makes the comparison order-sensitive, not an oversight.\n *\n * ★ TWO SEQUENCES, NOT ONE — the one real difference from `acl-rule-ordering-form.ts`'s single\n * `orderedAclRuleIds`. A policy ordered \"before system-defined\" and one ordered \"after\" can never\n * swap sides through this endpoint (there is no single combined list position that would let one\n * policy move from one half to the other) — `matches`/`driftOf` below compare each half\n * independently, so a policy moving from `beforeSystemDefined` to `afterSystemDefined` reports as\n * drift on BOTH fields, not a single reordering of one array.\n *\n * ★ `makeDriftOf`, NO CUSTOM `equal` — same framework every other family's `matches`/`driftOf`\n * share (MEDIUM-4). Its default `deepEqual` is already order-sensitive for arrays, so the absence\n * of a custom `equal` here is itself the \"never sortedSet\" rule in force, exactly as\n * `acl-rule-ordering-form.ts`'s own header explains for its one field.\n */\nimport type * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport { makeDriftOf } from './drift.ts';\n\nexport interface FirewallPolicyOrderingProps {\n siteId: string;\n sourceFirewallZoneId: string;\n destinationFirewallZoneId: string;\n /** Policy IDs ordered before the zone pair's system-defined policies, highest priority first —\n * never compared as a set (see header). */\n beforeSystemDefined: string[];\n /** Policy IDs ordered after the zone pair's system-defined policies, highest priority first —\n * never compared as a set (see header). */\n afterSystemDefined: string[];\n}\n\nexport interface FirewallPolicyOrderingAttributes {\n siteId: string;\n sourceFirewallZoneId: string;\n destinationFirewallZoneId: string;\n beforeSystemDefined: string[];\n afterSystemDefined: string[];\n}\n\nexport const attributesOf = (\n live: firewall.IntegrationFirewallPolicyOrderingDto,\n props: FirewallPolicyOrderingProps,\n): FirewallPolicyOrderingAttributes => ({\n siteId: props.siteId,\n sourceFirewallZoneId: props.sourceFirewallZoneId,\n destinationFirewallZoneId: props.destinationFirewallZoneId,\n beforeSystemDefined: live.orderedFirewallPolicyIds.beforeSystemDefined,\n afterSystemDefined: live.orderedFirewallPolicyIds.afterSystemDefined,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getFirewallPolicyOrdering`'s own\n * response straight in and writes the result as `firewallPolicyOrdering('<id>',\n * declareFirewallPolicyOrdering(live, siteId, sourceZoneId, destZoneId))` —\n * `matches(attributesOf(live, props), declareFirewallPolicyOrdering(live, …))` is `true` by\n * construction (`firewall-policy-ordering.test.ts` proves it).\n */\nexport const declareFirewallPolicyOrdering = (\n live: firewall.IntegrationFirewallPolicyOrderingDto,\n siteId: string,\n sourceFirewallZoneId: string,\n destinationFirewallZoneId: string,\n): FirewallPolicyOrderingProps => ({\n siteId,\n sourceFirewallZoneId,\n destinationFirewallZoneId,\n beforeSystemDefined: live.orderedFirewallPolicyIds.beforeSystemDefined,\n afterSystemDefined: live.orderedFirewallPolicyIds.afterSystemDefined,\n});\n\nconst fieldDrift = makeDriftOf<FirewallPolicyOrderingAttributes, FirewallPolicyOrderingProps>([\n {\n field: 'beforeSystemDefined',\n live: (a) => a.beforeSystemDefined,\n declared: (p) => p.beforeSystemDefined,\n },\n {\n field: 'afterSystemDefined',\n live: (a) => a.afterSystemDefined,\n declared: (p) => p.afterSystemDefined,\n },\n]);\n\nexport const matches = (\n attributes: FirewallPolicyOrderingAttributes,\n props: FirewallPolicyOrderingProps,\n): boolean => fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`).\n */\nexport const driftOf = (\n live: firewall.IntegrationFirewallPolicyOrderingDto,\n props: FirewallPolicyOrderingProps,\n) => fieldDrift(attributesOf(live, props), props);\n",
30
+ "/**\n * `Unifi.TrafficMatchingList` — one traffic matching list (IPv4/IPv6/port match entries) on a UniFi\n * Network site, READ-ONLY (`policy.ts`).\n *\n * ★ T11 GATE, CHECKED 2026-09-26: `Traffic Matching Lists`' 5 operations are byte-identical between\n * 10.4.57 and 10.6.97, full raw operation objects; the 19-schema closure reachable from all 5\n * (`$ref` + `discriminator.mapping`) shares zero names with the 14 schemas that DID change\n * elsewhere in the document. Corroborated independently by the A3 changeset's own broader\n * 25-operation/139-schema re-check (`.changeset/unifi-network-a3-typed-discriminator-filters.md`).\n * Full breakdown: `docs/unifi-traffic-matching-list.md`.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as trafficMatchingLists from '@distilled.cloud/unifi-network/traffic_matching_lists';\nimport * as Effect from 'effect/Effect';\nimport {\n type TrafficMatchingListAttributes,\n type TrafficMatchingListProps,\n attributesOf,\n} from './traffic-matching-list-form.ts';\nimport { matches } from './traffic-matching-list-drift.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type {\n TrafficMatchingListAttributes,\n TrafficMatchingListProps,\n} from './traffic-matching-list-form.ts';\nexport { declareTrafficMatchingList } from './traffic-matching-list-form.ts';\nexport { driftOf } from './traffic-matching-list-drift.ts';\n\nexport interface UnifiTrafficMatchingList extends Resource<\n 'Unifi.TrafficMatchingList',\n TrafficMatchingListProps,\n TrafficMatchingListAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiTrafficMatchingList = Resource<UnifiTrafficMatchingList>(\n 'Unifi.TrafficMatchingList',\n { defaultRemovalPolicy: 'retain' },\n);\n\n/** `trafficMatchingList('office-subnets', props)` — `adopt(true)` piped on by default (H5); see\n * `network.ts`. */\nexport const trafficMatchingList = (id: string, props: TrafficMatchingListProps) =>\n UnifiTrafficMatchingList(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n TrafficMatchingListProps,\n trafficMatchingLists.TrafficMatchingList,\n TrafficMatchingListAttributes,\n trafficMatchingLists.GetTrafficMatchingListError\n> = {\n type: 'Unifi.TrafficMatchingList',\n describe: (props) =>\n `sites/${props.siteId}/traffic-matching-lists/${props.trafficMatchingListId}`,\n fetchLive: (props) =>\n trafficMatchingLists\n .getTrafficMatchingList({\n siteId: props.siteId,\n trafficMatchingListId: props.trafficMatchingListId,\n })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiTrafficMatchingListProvider = () =>\n Provider.effect(\n UnifiTrafficMatchingList,\n Effect.succeed(UnifiTrafficMatchingList.Provider.of(handlers)),\n );\n",
31
+ "/**\n * `Unifi.TrafficMatchingList`'s wire shape — the declarable subset of `TrafficMatchingList`\n * (`getTrafficMatchingList`'s own decode target), and how a live read becomes both plan attributes\n * and a rendered declaration.\n *\n * ★ NO `metadata` FIELD AT ALL — the simplest object shape in this directory. Unlike every other\n * family here (`FirewallZone`/`DNSPolicy`/`ACLRule`/`FirewallPolicy` all carry a server-derived\n * `metadata.origin`), `TrafficMatchingList` is just `{id, name, type, items?}` — nothing here is\n * attributes-only the way `metadataOrigin`/`index` are elsewhere.\n *\n * ★ `items` IS A REAL TS UNION POST-A3, COMPARED WHOLESALE — `Array<IPv4Matching> |\n * Array<IPv6Matching> | Array<PortMatching>`, keyed by the sibling `type` field\n * (`traffic_matching_lists.ts`'s own comment: \"Shape depends on the sibling discriminator value\").\n * The runtime schema is still a passthrough `S.Unknown` (A3's own changeset: narrower for a reader\n * at the type level only, so decode keeps every key including one this pinned spec doesn't yet\n * declare — same proof `discriminated-filter-decode.test.ts` runs for `FirewallPolicy`/`AclRule`).\n *\n * ⚠️ KNOWN GAP (T15, same shape `acl-rule-form.ts`/`firewall-policy-form.ts` both carry): a traffic\n * matching list's `items` is semantically a SET of match entries (matching is \"is this traffic\n * covered by ANY entry,\" not order-dependent, unlike `firewall-policy-ordering-form.ts`'s own\n * sequences), but each entry is an OBJECT (`{type, value?, start?, stop?}`), not a bare string --\n * there is no cheap canonical sort key the way `sortedSet` gives a `string[]`. `deepEqual` compares\n * `items` order-sensitively, so a console-side reorder with no membership change would report a\n * spurious drift this read-only family's `reconcile` then refuses forever. Accepted, not fixed,\n * same proportion as ACL rule's own nested-filter gap: a generic object-set normalizer is bigger,\n * separate modelling work, not a mechanical extension of typing `items` per-discriminator (A3).\n */\nimport type * as trafficMatchingLists from '@distilled.cloud/unifi-network/traffic_matching_lists';\n\nexport interface TrafficMatchingListProps {\n siteId: string;\n trafficMatchingListId: string;\n name: string;\n /** Discriminator for `items`' own shape (e.g. an IPv4/IPv6/port list) — the spec enumerates no\n * fixed value set for it (also accepts a future vendor value it did not enumerate). */\n type: string;\n /** Match entries — a SET semantically, compared wholesale; see the header's known gap. */\n items?: trafficMatchingLists.TrafficMatchingListItems | undefined;\n}\n\nexport interface TrafficMatchingListAttributes {\n siteId: string;\n trafficMatchingListId: string;\n name: string;\n type: string;\n items: trafficMatchingLists.TrafficMatchingListItems | undefined;\n}\n\nexport const attributesOf = (\n live: trafficMatchingLists.TrafficMatchingList,\n props: TrafficMatchingListProps,\n): TrafficMatchingListAttributes => ({\n siteId: props.siteId,\n trafficMatchingListId: live.id,\n name: live.name,\n type: live.type,\n items: live.items,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getTrafficMatchingList`'s own response\n * straight in and writes the result as `trafficMatchingList('<id>',\n * declareTrafficMatchingList(live, siteId))` — no field is invented or defaulted beyond what\n * `attributesOf` already reports, so `matches(attributesOf(live, props), declareTrafficMatchingList(\n * live, siteId))` is `true` by construction (`traffic-matching-list.test.ts` proves it).\n */\nexport const declareTrafficMatchingList = (\n live: trafficMatchingLists.TrafficMatchingList,\n siteId: string,\n): TrafficMatchingListProps => ({\n siteId,\n trafficMatchingListId: live.id,\n name: live.name,\n type: live.type,\n items: live.items,\n});\n",
32
+ "/**\n * `Unifi.TrafficMatchingList`'s `matches` AND `driftOf` (B6) — both built on the ONE field list\n * below, the same `fieldDrift`-is-the-only-comparison shape `network-drift.ts`'s header explains\n * (MEDIUM-4, red team, 2026-09-26): `matches(attrs, props) === (driftOf(live, props).length === 0)`\n * is true BY CONSTRUCTION, never two hand-kept-in-sync comparisons.\n *\n * ⚠️ `stripNullish: true` ON EVERY FIELD BY DEFAULT (`drift.ts`'s `defaultEqual`) — same \"UniFi's\n * JSON answers `null`, a declaration omits\" tolerance every other family here relies on.\n * ★ `items` compares with the plain default `equal` (whole-value `deepEqual`) — see\n * `traffic-matching-list-form.ts`'s header for the accepted known gap this leaves for a\n * membership-preserving reorder.\n */\nimport type * as trafficMatchingLists from '@distilled.cloud/unifi-network/traffic_matching_lists';\nimport { makeDriftOf } from './drift.ts';\nimport {\n type TrafficMatchingListAttributes,\n type TrafficMatchingListProps,\n attributesOf,\n} from './traffic-matching-list-form.ts';\n\nconst fieldDrift = makeDriftOf<TrafficMatchingListAttributes, TrafficMatchingListProps>([\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n { field: 'type', live: (a) => a.type, declared: (p) => p.type },\n { field: 'items', live: (a) => a.items, declared: (p) => p.items },\n]);\n\nexport const matches = (\n attributes: TrafficMatchingListAttributes,\n props: TrafficMatchingListProps,\n): boolean => fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (\n live: trafficMatchingLists.TrafficMatchingList,\n props: TrafficMatchingListProps,\n) => fieldDrift(attributesOf(live, props), props);\n",
33
+ "/**\n * Every `Unifi.*` provider as one layer for a stack.\n *\n * Layer.mergeAll(unifiProviders(), …the stack's other providers)\n * .pipe(Layer.provide(FetchHttpClient.layer))\n *\n * S16: a subpath's `providers()` plays the role upstream's own `Providers.ts` does — one minimal\n * insertion per family. `FetchHttpClient.layer` is the stack's own responsibility to provide,\n * mirroring `../discord/providers.ts`.\n */\nimport * as Layer from 'effect/Layer';\nimport { UnifiAclRuleProvider } from './acl-rule.ts';\nimport { UnifiAclRuleOrderingProvider } from './acl-rule-ordering.ts';\nimport { UnifiDnsPolicyProvider } from './dns-policy.ts';\nimport { UnifiFirewallPolicyProvider } from './firewall-policy.ts';\nimport { UnifiFirewallPolicyOrderingProvider } from './firewall-policy-ordering.ts';\nimport { UnifiFirewallZoneProvider } from './firewall-zone.ts';\nimport { UnifiNetworkProvider } from './network.ts';\nimport { UnifiTrafficMatchingListProvider } from './traffic-matching-list.ts';\nimport { UnifiWifiBroadcastProvider } from './wifi-broadcast.ts';\n\nexport const providers = () =>\n Layer.mergeAll(\n UnifiNetworkProvider(),\n UnifiFirewallZoneProvider(),\n UnifiDnsPolicyProvider(),\n UnifiAclRuleProvider(),\n UnifiAclRuleOrderingProvider(),\n UnifiWifiBroadcastProvider(),\n UnifiFirewallPolicyProvider(),\n UnifiFirewallPolicyOrderingProvider(),\n UnifiTrafficMatchingListProvider(),\n );\n"
12
34
  ],
13
- "mappings": "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",
14
- "debugId": "033CFC419E398B0664756E2164756E21",
35
+ "mappings": "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",
36
+ "debugId": "8F60CB2F43A8D0E164756E2164756E21",
15
37
  "names": []
16
38
  }