@homeflare/alchemy 0.15.0 → 0.19.1
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.
- package/README.md +29 -0
- package/dist/linux/directory-lifecycle.d.ts +50 -0
- package/dist/linux/directory-lifecycle.d.ts.map +1 -0
- package/dist/linux/directory.d.ts +19 -0
- package/dist/linux/directory.d.ts.map +1 -0
- package/dist/linux/fake-linux-host.d.ts +35 -0
- package/dist/linux/fake-linux-host.d.ts.map +1 -0
- package/dist/linux/fake-systemd.d.ts +34 -0
- package/dist/linux/fake-systemd.d.ts.map +1 -0
- package/dist/linux/index.d.ts +27 -0
- package/dist/linux/index.d.ts.map +1 -0
- package/dist/linux/index.js +1575 -0
- package/dist/linux/index.js.map +38 -0
- package/dist/linux/providers.d.ts +17 -0
- package/dist/linux/providers.d.ts.map +1 -0
- package/dist/linux/region.d.ts +43 -0
- package/dist/linux/region.d.ts.map +1 -0
- package/dist/linux/remote-file-form.d.ts +47 -0
- package/dist/linux/remote-file-form.d.ts.map +1 -0
- package/dist/linux/remote-file-lifecycle.d.ts +36 -0
- package/dist/linux/remote-file-lifecycle.d.ts.map +1 -0
- package/dist/linux/remote-file-plan.d.ts +33 -0
- package/dist/linux/remote-file-plan.d.ts.map +1 -0
- package/dist/linux/remote-file.d.ts +28 -0
- package/dist/linux/remote-file.d.ts.map +1 -0
- package/dist/linux/ssh-command.d.ts +68 -0
- package/dist/linux/ssh-command.d.ts.map +1 -0
- package/dist/linux/ssh-runner.d.ts +22 -0
- package/dist/linux/ssh-runner.d.ts.map +1 -0
- package/dist/linux/ssh-scripts.d.ts +27 -0
- package/dist/linux/ssh-scripts.d.ts.map +1 -0
- package/dist/linux/systemctl.d.ts +67 -0
- package/dist/linux/systemctl.d.ts.map +1 -0
- package/dist/linux/unit-form.d.ts +78 -0
- package/dist/linux/unit-form.d.ts.map +1 -0
- package/dist/linux/unit-handlers.d.ts +15 -0
- package/dist/linux/unit-handlers.d.ts.map +1 -0
- package/dist/linux/unit-lifecycle.d.ts +40 -0
- package/dist/linux/unit-lifecycle.d.ts.map +1 -0
- package/dist/linux/unit-preflight.d.ts +41 -0
- package/dist/linux/unit-preflight.d.ts.map +1 -0
- package/dist/linux/unit-settle.d.ts +31 -0
- package/dist/linux/unit-settle.d.ts.map +1 -0
- package/dist/linux/unit.d.ts +35 -0
- package/dist/linux/unit.d.ts.map +1 -0
- package/dist/netbox/client.d.ts +52 -0
- package/dist/netbox/client.d.ts.map +1 -0
- package/dist/netbox/constraint-guard.d.ts +26 -0
- package/dist/netbox/constraint-guard.d.ts.map +1 -0
- package/dist/netbox/constraints.d.ts +76 -0
- package/dist/netbox/constraints.d.ts.map +1 -0
- package/dist/netbox/generated/constraints/index.d.ts +19 -0
- package/dist/netbox/generated/constraints/index.d.ts.map +1 -0
- package/dist/netbox/generated/constraints/netbox-ipam.d.ts +21 -0
- package/dist/netbox/generated/constraints/netbox-ipam.d.ts.map +1 -0
- package/dist/netbox/index.d.ts +17 -0
- package/dist/netbox/index.d.ts.map +1 -0
- package/dist/netbox/index.js +331 -0
- package/dist/netbox/index.js.map +18 -0
- package/dist/netbox/prefix-form.d.ts +54 -0
- package/dist/netbox/prefix-form.d.ts.map +1 -0
- package/dist/netbox/prefix.d.ts +80 -0
- package/dist/netbox/prefix.d.ts.map +1 -0
- package/dist/netbox/resource.d.ts +94 -0
- package/dist/netbox/resource.d.ts.map +1 -0
- package/dist/netbox/values.d.ts +24 -0
- package/dist/netbox/values.d.ts.map +1 -0
- package/dist/proxmox/api-token-form.d.ts.map +1 -1
- package/dist/proxmox/apply-endpoints.d.ts +30 -0
- package/dist/proxmox/apply-endpoints.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon-form.d.ts +29 -1
- package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -1
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
- package/dist/proxmox/ceph-flag.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs.d.ts.map +1 -1
- package/dist/proxmox/ceph-osd-write.d.ts +19 -0
- package/dist/proxmox/ceph-osd-write.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool.d.ts.map +1 -1
- package/dist/proxmox/constraints.d.ts +28 -5
- package/dist/proxmox/constraints.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/index.d.ts +1 -1
- package/dist/proxmox/generated/constraints/index.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pbs-config.d.ts +6 -1
- package/dist/proxmox/generated/constraints/pbs-config.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pve-access.d.ts +6 -1
- package/dist/proxmox/generated/constraints/pve-access.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-backup.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-ceph.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-firewall.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-ha.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-metrics.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-notifications.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-replication.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-cluster-sdn.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-ceph.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-disks.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-lxc.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-network.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts +23 -0
- package/dist/proxmox/generated/constraints/pve-nodes-qemu.d.ts.map +1 -0
- package/dist/proxmox/generated/constraints/pve-pools.d.ts +6 -1
- package/dist/proxmox/generated/constraints/pve-pools.d.ts.map +1 -1
- package/dist/proxmox/generated/constraints/pve-storage.d.ts +6 -1
- package/dist/proxmox/generated/constraints/pve-storage.d.ts.map +1 -1
- package/dist/proxmox/generated/pbs/access-users.d.ts +137 -0
- package/dist/proxmox/generated/pbs/access-users.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/access.d.ts +171 -0
- package/dist/proxmox/generated/pbs/access.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts +252 -0
- package/dist/proxmox/generated/pbs/admin-datastore-store.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/admin-metrics.d.ts +147 -0
- package/dist/proxmox/generated/pbs/admin-metrics.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/admin.d.ts +217 -0
- package/dist/proxmox/generated/pbs/admin.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/backup.d.ts +114 -0
- package/dist/proxmox/generated/pbs/backup.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-access-pam.d.ts +218 -0
- package/dist/proxmox/generated/pbs/config-access-pam.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-access.d.ts +203 -0
- package/dist/proxmox/generated/pbs/config-access.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-datastore.d.ts +195 -0
- package/dist/proxmox/generated/pbs/config-datastore.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-media-pool.d.ts +207 -0
- package/dist/proxmox/generated/pbs/config-media-pool.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts +159 -0
- package/dist/proxmox/generated/pbs/config-notifications-matchers.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-notifications.d.ts +199 -0
- package/dist/proxmox/generated/pbs/config-notifications.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-remote.d.ts +175 -0
- package/dist/proxmox/generated/pbs/config-remote.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-sync.d.ts +145 -0
- package/dist/proxmox/generated/pbs/config-sync.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-tape.d.ts +200 -0
- package/dist/proxmox/generated/pbs/config-tape.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config-verify.d.ts +77 -0
- package/dist/proxmox/generated/pbs/config-verify.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/config.d.ts +119 -0
- package/dist/proxmox/generated/pbs/config.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts +178 -0
- package/dist/proxmox/generated/pbs/nodes-node-dns.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/nodes-node-services.d.ts +205 -0
- package/dist/proxmox/generated/pbs/nodes-node-services.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/nodes-node.d.ts +166 -0
- package/dist/proxmox/generated/pbs/nodes-node.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/nodes.d.ts +171 -0
- package/dist/proxmox/generated/pbs/nodes.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/ping.d.ts +18 -0
- package/dist/proxmox/generated/pbs/ping.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/pull.d.ts +37 -0
- package/dist/proxmox/generated/pbs/pull.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/push.d.ts +36 -0
- package/dist/proxmox/generated/pbs/push.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/reader.d.ts +41 -0
- package/dist/proxmox/generated/pbs/reader.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/root.d.ts +16 -0
- package/dist/proxmox/generated/pbs/root.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/status.d.ts +54 -0
- package/dist/proxmox/generated/pbs/status.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/tape-drive.d.ts +181 -0
- package/dist/proxmox/generated/pbs/tape-drive.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/tape-media.d.ts +126 -0
- package/dist/proxmox/generated/pbs/tape-media.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/tape.d.ts +90 -0
- package/dist/proxmox/generated/pbs/tape.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs/version.d.ts +20 -0
- package/dist/proxmox/generated/pbs/version.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs.d.ts +44 -662
- package/dist/proxmox/generated/pbs.d.ts.map +1 -1
- package/dist/proxmox/generated/pve/access-roles.d.ts +164 -0
- package/dist/proxmox/generated/pve/access-roles.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/access-users.d.ts +152 -0
- package/dist/proxmox/generated/pve/access-users.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/access.d.ts +221 -0
- package/dist/proxmox/generated/pve/access.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts +45 -0
- package/dist/proxmox/generated/pve/cluster-acme-plugins-id.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts +47 -0
- package/dist/proxmox/generated/pve/cluster-acme-plugins.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts +240 -0
- package/dist/proxmox/generated/pve/cluster-acme-tos.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts +200 -0
- package/dist/proxmox/generated/pve/cluster-bulk-action.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts +109 -0
- package/dist/proxmox/generated/pve/cluster-firewall-refs.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-firewall.d.ts +225 -0
- package/dist/proxmox/generated/pve/cluster-firewall.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-ha.d.ts +199 -0
- package/dist/proxmox/generated/pve/cluster-ha.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-jobs.d.ts +183 -0
- package/dist/proxmox/generated/pve/cluster-jobs.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-metrics.d.ts +110 -0
- package/dist/proxmox/generated/pve/cluster-metrics.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts +224 -0
- package/dist/proxmox/generated/pve/cluster-notifications-endpoints.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-notifications.d.ts +91 -0
- package/dist/proxmox/generated/pve/cluster-notifications.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-options.d.ts +85 -0
- package/dist/proxmox/generated/pve/cluster-options.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts +48 -0
- package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts +45 -0
- package/dist/proxmox/generated/pve/cluster-qemu-custom-cpu-models.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-replication.d.ts +130 -0
- package/dist/proxmox/generated/pve/cluster-replication.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts +220 -0
- package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts +229 -0
- package/dist/proxmox/generated/pve/cluster-sdn-ipams.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts +229 -0
- package/dist/proxmox/generated/pve/cluster-sdn-vnets-vnet.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts +51 -0
- package/dist/proxmox/generated/pve/cluster-sdn-vnets.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts +225 -0
- package/dist/proxmox/generated/pve/cluster-sdn-zones.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster-sdn.d.ts +202 -0
- package/dist/proxmox/generated/pve/cluster-sdn.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/cluster.d.ts +69 -0
- package/dist/proxmox/generated/pve/cluster.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts +167 -0
- package/dist/proxmox/generated/pve/nodes-node-ceph.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-config.d.ts +43 -0
- package/dist/proxmox/generated/pve/nodes-node-config.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-disks.d.ts +225 -0
- package/dist/proxmox/generated/pve/nodes-node-disks.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts +145 -0
- package/dist/proxmox/generated/pve/nodes-node-firewall.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts +195 -0
- package/dist/proxmox/generated/pve/nodes-node-hardware.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts +191 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts +33 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts +56 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts +28 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-resize.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts +186 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts +191 -0
- package/dist/proxmox/generated/pve/nodes-node-lxc-vmid.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-network.d.ts +150 -0
- package/dist/proxmox/generated/pve/nodes-node-network.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts +301 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-config.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts +170 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts +144 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts +34 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts +137 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts +179 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid-status.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts +143 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu-vmid.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts +158 -0
- package/dist/proxmox/generated/pve/nodes-node-qemu.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-replication.d.ts +187 -0
- package/dist/proxmox/generated/pve/nodes-node-replication.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-services.d.ts +115 -0
- package/dist/proxmox/generated/pve/nodes-node-services.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts +202 -0
- package/dist/proxmox/generated/pve/nodes-node-storage-storage.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-storage.d.ts +40 -0
- package/dist/proxmox/generated/pve/nodes-node-storage.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts +173 -0
- package/dist/proxmox/generated/pve/nodes-node-subscription.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts +97 -0
- package/dist/proxmox/generated/pve/nodes-node-vzdump.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes-node.d.ts +207 -0
- package/dist/proxmox/generated/pve/nodes-node.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/nodes.d.ts +236 -0
- package/dist/proxmox/generated/pve/nodes.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/pools.d.ts +82 -0
- package/dist/proxmox/generated/pve/pools.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/storage.d.ts +159 -0
- package/dist/proxmox/generated/pve/storage.d.ts.map +1 -0
- package/dist/proxmox/generated/pve/version.d.ts +21 -0
- package/dist/proxmox/generated/pve/version.d.ts.map +1 -0
- package/dist/proxmox/generated/pve.d.ts +69 -4766
- package/dist/proxmox/generated/pve.d.ts.map +1 -1
- package/dist/proxmox/index.js +996 -372
- package/dist/proxmox/index.js.map +48 -32
- package/dist/proxmox/lxc-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/metric-server-form.d.ts +11 -0
- package/dist/proxmox/metric-server-form.d.ts.map +1 -1
- package/dist/proxmox/metric-server.d.ts.map +1 -1
- package/dist/proxmox/network-apply.d.ts.map +1 -1
- package/dist/proxmox/node-network-form.d.ts +15 -0
- package/dist/proxmox/node-network-form.d.ts.map +1 -1
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/dist/proxmox/notification-target-endpoint.d.ts +26 -0
- package/dist/proxmox/notification-target-endpoint.d.ts.map +1 -0
- package/dist/proxmox/notification-target-form.d.ts +7 -0
- package/dist/proxmox/notification-target-form.d.ts.map +1 -1
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target-endpoint.d.ts +29 -0
- package/dist/proxmox/pbs-notification-target-endpoint.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/qemu.d.ts +10 -0
- package/dist/proxmox/qemu.d.ts.map +1 -1
- package/dist/proxmox/resource-guard.d.ts +15 -0
- package/dist/proxmox/resource-guard.d.ts.map +1 -0
- package/dist/proxmox/resource-spec.d.ts +15 -4
- package/dist/proxmox/resource-spec.d.ts.map +1 -1
- package/dist/proxmox/resource.d.ts +8 -0
- package/dist/proxmox/resource.d.ts.map +1 -1
- package/dist/proxmox/sdn-apply.d.ts.map +1 -1
- package/dist/proxmox/sdn-subnet.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool-write.d.ts +11 -0
- package/dist/proxmox/zfs-pool-write.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool.d.ts.map +1 -1
- package/docs/linux-host-measured.md +56 -0
- package/docs/linux-host.md +192 -0
- package/docs/netbox.md +103 -0
- package/package.json +9 -1
- package/src/linux/directory-lifecycle.ts +220 -0
- package/src/linux/directory.test.ts +132 -0
- package/src/linux/directory.ts +71 -0
- package/src/linux/fake-linux-host.ts +197 -0
- package/src/linux/fake-systemd.ts +103 -0
- package/src/linux/index.ts +32 -0
- package/src/linux/providers.ts +26 -0
- package/src/linux/region.test.ts +89 -0
- package/src/linux/region.ts +118 -0
- package/src/linux/remote-file-form.ts +96 -0
- package/src/linux/remote-file-lifecycle.test.ts +191 -0
- package/src/linux/remote-file-lifecycle.ts +161 -0
- package/src/linux/remote-file-plan.ts +154 -0
- package/src/linux/remote-file.ts +88 -0
- package/src/linux/ssh-command.test.ts +140 -0
- package/src/linux/ssh-command.ts +99 -0
- package/src/linux/ssh-runner.ts +188 -0
- package/src/linux/ssh-scripts.ts +105 -0
- package/src/linux/systemctl.ts +145 -0
- package/src/linux/unit-form.ts +183 -0
- package/src/linux/unit-handlers.ts +73 -0
- package/src/linux/unit-lifecycle.test.ts +220 -0
- package/src/linux/unit-lifecycle.ts +175 -0
- package/src/linux/unit-preflight.test.ts +128 -0
- package/src/linux/unit-preflight.ts +100 -0
- package/src/linux/unit-settle.ts +110 -0
- package/src/linux/unit.ts +81 -0
- package/src/netbox/client.test.ts +67 -0
- package/src/netbox/client.ts +146 -0
- package/src/netbox/constraint-guard.ts +57 -0
- package/src/netbox/constraints.test.ts +114 -0
- package/src/netbox/constraints.ts +152 -0
- package/src/netbox/generated/constraints/index.ts +23 -0
- package/src/netbox/generated/constraints/netbox-ipam.ts +32 -0
- package/src/netbox/index.ts +34 -0
- package/src/netbox/prefix-form.test.ts +127 -0
- package/src/netbox/prefix-form.ts +84 -0
- package/src/netbox/prefix.ts +136 -0
- package/src/netbox/resource.ts +208 -0
- package/src/netbox/values.test.ts +66 -0
- package/src/netbox/values.ts +40 -0
- package/src/proxmox/api-token-form.ts +5 -0
- package/src/proxmox/apply-endpoints.ts +34 -0
- package/src/proxmox/ceph-daemon-form.ts +56 -2
- package/src/proxmox/ceph-daemon.ts +10 -21
- package/src/proxmox/ceph-flag.ts +1 -0
- package/src/proxmox/ceph-fs.ts +1 -0
- package/src/proxmox/ceph-osd-write.ts +30 -4
- package/src/proxmox/ceph-pool.ts +10 -0
- package/src/proxmox/constraints-dialect.test.ts +165 -0
- package/src/proxmox/constraints-forms.test.ts +228 -0
- package/src/proxmox/constraints-host-forms.test.ts +132 -0
- package/src/proxmox/constraints-live.test.ts +155 -0
- package/src/proxmox/constraints-node-forms.test.ts +197 -0
- package/src/proxmox/constraints-required.test.ts +172 -0
- package/src/proxmox/constraints.test.ts +8 -63
- package/src/proxmox/constraints.ts +31 -6
- package/src/proxmox/generated/constraints/index.ts +27 -3
- package/src/proxmox/generated/constraints/pbs-config.ts +63 -1
- package/src/proxmox/generated/constraints/pve-access.ts +15 -3
- package/src/proxmox/generated/constraints/pve-cluster-backup.ts +74 -0
- package/src/proxmox/generated/constraints/pve-cluster-ceph.ts +27 -0
- package/src/proxmox/generated/constraints/pve-cluster-firewall.ts +35 -0
- package/src/proxmox/generated/constraints/pve-cluster-ha.ts +55 -0
- package/src/proxmox/generated/constraints/pve-cluster-metrics.ts +59 -0
- package/src/proxmox/generated/constraints/pve-cluster-notifications.ts +80 -0
- package/src/proxmox/generated/constraints/pve-cluster-replication.ts +43 -0
- package/src/proxmox/generated/constraints/pve-cluster-sdn.ts +90 -0
- package/src/proxmox/generated/constraints/pve-nodes-ceph.ts +57 -0
- package/src/proxmox/generated/constraints/pve-nodes-disks.ts +31 -0
- package/src/proxmox/generated/constraints/pve-nodes-lxc.ts +82 -0
- package/src/proxmox/generated/constraints/pve-nodes-network.ts +77 -0
- package/src/proxmox/generated/constraints/pve-nodes-qemu.ts +125 -0
- package/src/proxmox/generated/constraints/pve-pools.ts +6 -1
- package/src/proxmox/generated/constraints/pve-storage.ts +8 -3
- package/src/proxmox/generated/pbs/access-users.ts +137 -0
- package/src/proxmox/generated/pbs/access.ts +232 -0
- package/src/proxmox/generated/pbs/admin-datastore-store.ts +238 -0
- package/src/proxmox/generated/pbs/admin-metrics.ts +147 -0
- package/src/proxmox/generated/pbs/admin.ts +225 -0
- package/src/proxmox/generated/pbs/backup.ts +121 -0
- package/src/proxmox/generated/pbs/config-access-pam.ts +228 -0
- package/src/proxmox/generated/pbs/config-access.ts +228 -0
- package/src/proxmox/generated/pbs/config-datastore.ts +217 -0
- package/src/proxmox/generated/pbs/config-media-pool.ts +232 -0
- package/src/proxmox/generated/pbs/config-notifications-matchers.ts +187 -0
- package/src/proxmox/generated/pbs/config-notifications.ts +221 -0
- package/src/proxmox/generated/pbs/config-remote.ts +192 -0
- package/src/proxmox/generated/pbs/config-sync.ts +167 -0
- package/src/proxmox/generated/pbs/config-tape.ts +226 -0
- package/src/proxmox/generated/pbs/config-verify.ts +87 -0
- package/src/proxmox/generated/pbs/config.ts +135 -0
- package/src/proxmox/generated/pbs/nodes-node-dns.ts +229 -0
- package/src/proxmox/generated/pbs/nodes-node-services.ts +181 -0
- package/src/proxmox/generated/pbs/nodes-node.ts +244 -0
- package/src/proxmox/generated/pbs/nodes.ts +160 -0
- package/src/proxmox/generated/pbs/ping.ts +16 -0
- package/src/proxmox/generated/pbs/pull.ts +37 -0
- package/src/proxmox/generated/pbs/push.ts +36 -0
- package/src/proxmox/generated/pbs/reader.ts +41 -0
- package/src/proxmox/generated/pbs/root.ts +16 -0
- package/src/proxmox/generated/pbs/status.ts +50 -0
- package/src/proxmox/generated/pbs/tape-drive.ts +220 -0
- package/src/proxmox/generated/pbs/tape-media.ts +133 -0
- package/src/proxmox/generated/pbs/tape.ts +92 -0
- package/src/proxmox/generated/pbs/version.ts +16 -0
- package/src/proxmox/generated/pbs.ts +44 -787
- package/src/proxmox/generated/pve/access-roles.ts +168 -0
- package/src/proxmox/generated/pve/access-users.ts +155 -0
- package/src/proxmox/generated/pve/access.ts +217 -0
- package/src/proxmox/generated/pve/cluster-acme-plugins-id.ts +367 -0
- package/src/proxmox/generated/pve/cluster-acme-plugins.ts +366 -0
- package/src/proxmox/generated/pve/cluster-acme-tos.ts +232 -0
- package/src/proxmox/generated/pve/cluster-bulk-action.ts +227 -0
- package/src/proxmox/generated/pve/cluster-firewall-refs.ts +110 -0
- package/src/proxmox/generated/pve/cluster-firewall.ts +236 -0
- package/src/proxmox/generated/pve/cluster-ha.ts +203 -0
- package/src/proxmox/generated/pve/cluster-jobs.ts +190 -0
- package/src/proxmox/generated/pve/cluster-metrics.ts +116 -0
- package/src/proxmox/generated/pve/cluster-notifications-endpoints.ts +244 -0
- package/src/proxmox/generated/pve/cluster-notifications.ts +99 -0
- package/src/proxmox/generated/pve/cluster-options.ts +189 -0
- package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models-cputype.ts +272 -0
- package/src/proxmox/generated/pve/cluster-qemu-custom-cpu-models.ts +268 -0
- package/src/proxmox/generated/pve/cluster-replication.ts +127 -0
- package/src/proxmox/generated/pve/cluster-sdn-fabrics.ts +218 -0
- package/src/proxmox/generated/pve/cluster-sdn-ipams.ts +229 -0
- package/src/proxmox/generated/pve/cluster-sdn-vnets-vnet.ts +245 -0
- package/src/proxmox/generated/pve/cluster-sdn-vnets.ts +49 -0
- package/src/proxmox/generated/pve/cluster-sdn-zones.ts +224 -0
- package/src/proxmox/generated/pve/cluster-sdn.ts +201 -0
- package/src/proxmox/generated/pve/cluster.ts +71 -0
- package/src/proxmox/generated/pve/nodes-node-ceph.ts +167 -0
- package/src/proxmox/generated/pve/nodes-node-config.ts +56 -0
- package/src/proxmox/generated/pve/nodes-node-disks.ts +237 -0
- package/src/proxmox/generated/pve/nodes-node-firewall.ts +239 -0
- package/src/proxmox/generated/pve/nodes-node-hardware.ts +215 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-firewall.ts +232 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-move-volume.ts +1059 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-mtunnel.ts +49 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-resize.ts +285 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid-rrd.ts +184 -0
- package/src/proxmox/generated/pve/nodes-node-lxc-vmid.ts +240 -0
- package/src/proxmox/generated/pve/nodes-node-network.ts +234 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-config.ts +441 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-dbus-vmstate.ts +215 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-firewall-rules.ts +145 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-move-disk.ts +664 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-mtunnel.ts +188 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid-status.ts +233 -0
- package/src/proxmox/generated/pve/nodes-node-qemu-vmid.ts +187 -0
- package/src/proxmox/generated/pve/nodes-node-qemu.ts +204 -0
- package/src/proxmox/generated/pve/nodes-node-replication.ts +187 -0
- package/src/proxmox/generated/pve/nodes-node-services.ts +221 -0
- package/src/proxmox/generated/pve/nodes-node-storage-storage.ts +226 -0
- package/src/proxmox/generated/pve/nodes-node-storage.ts +40 -0
- package/src/proxmox/generated/pve/nodes-node-subscription.ts +168 -0
- package/src/proxmox/generated/pve/nodes-node-vzdump.ts +96 -0
- package/src/proxmox/generated/pve/nodes-node.ts +211 -0
- package/src/proxmox/generated/pve/nodes.ts +235 -0
- package/src/proxmox/generated/pve/pools.ts +78 -0
- package/src/proxmox/generated/pve/storage.ts +213 -0
- package/src/proxmox/generated/pve/version.ts +21 -0
- package/src/proxmox/generated/pve.ts +69 -7403
- package/src/proxmox/lxc-interior.test.ts +17 -1
- package/src/proxmox/lxc-lifecycle.ts +48 -9
- package/src/proxmox/metric-server-form.ts +10 -0
- package/src/proxmox/metric-server.ts +11 -2
- package/src/proxmox/network-apply.ts +4 -0
- package/src/proxmox/node-network-form.ts +19 -0
- package/src/proxmox/node-network.ts +9 -3
- package/src/proxmox/notification-target-endpoint.ts +46 -0
- package/src/proxmox/notification-target-form.ts +11 -0
- package/src/proxmox/notification-target.ts +5 -2
- package/src/proxmox/pbs-notification-target-endpoint.ts +45 -0
- package/src/proxmox/pbs-notification-target-lifecycle.ts +16 -14
- package/src/proxmox/qemu.ts +9 -1
- package/src/proxmox/resource-guard.ts +61 -0
- package/src/proxmox/resource-spec.ts +16 -4
- package/src/proxmox/resource.ts +27 -27
- package/src/proxmox/sdn-apply.ts +4 -0
- package/src/proxmox/sdn-subnet.ts +5 -0
- package/src/proxmox/zfs-pool-write.ts +12 -0
- package/src/proxmox/zfs-pool.ts +2 -1
- package/dist/proxmox/generated/constraints/pve-cluster.d.ts +0 -18
- package/dist/proxmox/generated/constraints/pve-cluster.d.ts.map +0 -1
- package/src/proxmox/generated/constraints/pve-cluster.ts +0 -174
|
@@ -1,36 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/proxmox/acl.ts", "../src/proxmox/client.ts", "../src/proxmox/credentials.ts", "../src/proxmox/lease-cache.ts", "../src/proxmox/members.ts", "../src/proxmox/resource.ts", "../src/proxmox/constraint-guard.ts", "../src/proxmox/constraints.ts", "../src/proxmox/generated/constraints/pve-access.ts", "../src/proxmox/generated/constraints/pve-cluster.ts", "../src/proxmox/generated/constraints/pve-pools.ts", "../src/proxmox/generated/constraints/pve-storage.ts", "../src/proxmox/generated/constraints/pbs-config.ts", "../src/proxmox/generated/constraints/index.ts", "../src/proxmox/update-guard.ts", "../src/proxmox/alertmanager-body.ts", "../src/proxmox/api-token.ts", "../src/proxmox/values.ts", "../src/proxmox/api-token-form.ts", "../src/proxmox/backup-job.ts", "../src/proxmox/ceph-daemon.ts", "../src/proxmox/ceph-daemon-form.ts", "../src/proxmox/ceph-flag.ts", "../src/proxmox/ceph-fs.ts", "../src/proxmox/ceph-fs-wire.ts", "../src/proxmox/ceph-osd.ts", "../src/proxmox/ceph-osd-tree.ts", "../src/proxmox/ceph-osd-write.ts", "../src/proxmox/ceph-pool.ts", "../src/proxmox/ceph-pool-form.ts", "../src/proxmox/ceph-pool-settle.ts", "../src/proxmox/firewall-alias.ts", "../src/proxmox/firewall-alias-form.ts", "../src/proxmox/group.ts", "../src/proxmox/ha-resource.ts", "../src/proxmox/ha-rule.ts", "../src/proxmox/ha-rule-form.ts", "../src/proxmox/lxc.ts", "../src/ownership/adopt.ts", "../src/ownership/resume.ts", "../src/ownership/rows.ts", "../src/ownership/whole.ts", "../src/ownership/probe.ts", "../src/proxmox/lxc-wire.ts", "../src/proxmox/lxc-props.ts", "../src/proxmox/lxc-volume.ts", "../src/proxmox/lxc-judge.ts", "../src/proxmox/lxc-create-form.ts", "../src/proxmox/lxc-identity.ts", "../src/proxmox/lxc-adoption.ts", "../src/ownership/adopting.ts", "../src/proxmox/lxc-lifecycle.ts", "../src/proxmox/network-apply-read.ts", "../src/proxmox/metric-server.ts", "../src/proxmox/metric-server-form.ts", "../src/proxmox/metric-server-otel.ts", "../src/proxmox/network-apply.ts", "../src/proxmox/node-network.ts", "../src/proxmox/node-network-form.ts", "../src/proxmox/notification-matcher.ts", "../src/proxmox/notification-matcher-form.ts", "../src/proxmox/notification-target.ts", "../src/proxmox/notification-target-form.ts", "../src/proxmox/pbs-datastore.ts", "../src/proxmox/pbs-datastore-form.ts", "../src/proxmox/pbs-datastore-guard.ts", "../src/proxmox/pbs-datastore-endpoint.ts", "../src/proxmox/pbs-notification-matcher.ts", "../src/proxmox/pbs-notification-target.ts", "../src/proxmox/pbs-notification-target-lifecycle.ts", "../src/proxmox/credential-literals.ts", "../src/proxmox/pbs-notification-target-wire.ts", "../src/proxmox/write-only.ts", "../src/proxmox/pbs-notification-target-form.ts", "../src/proxmox/pbs-prune-job.ts", "../src/proxmox/pbs-prune-job-form.ts", "../src/proxmox/pbs-sync-job.ts", "../src/proxmox/pbs-sync-job-form.ts", "../src/proxmox/pbs-verify-job.ts", "../src/proxmox/pbs-verify-job-form.ts", "../src/proxmox/pool.ts", "../src/proxmox/provision-baseline.ts", "../src/proxmox/provision-bootstrap.ts", "../src/proxmox/provision-declare.ts", "../src/proxmox/role.ts", "../src/proxmox/user.ts", "../src/proxmox/qemu.ts", "../src/proxmox/replication-job.ts", "../src/proxmox/replication-job-form.ts", "../src/proxmox/sdn-apply.ts", "../src/proxmox/sdn-apply-read.ts", "../src/proxmox/sdn-subnet.ts", "../src/proxmox/sdn-subnet-form.ts", "../src/proxmox/sdn-vnet.ts", "../src/proxmox/sdn-zone.ts", "../src/proxmox/storage.ts", "../src/proxmox/zfs-pool.ts", "../src/proxmox/zfs-pool-write.ts"],
|
|
3
|
+
"sources": ["../src/proxmox/acl.ts", "../src/proxmox/client.ts", "../src/proxmox/credentials.ts", "../src/proxmox/lease-cache.ts", "../src/proxmox/members.ts", "../src/proxmox/resource.ts", "../src/proxmox/resource-guard.ts", "../src/proxmox/constraint-guard.ts", "../src/proxmox/constraints.ts", "../src/proxmox/generated/constraints/pve-access.ts", "../src/proxmox/generated/constraints/pve-cluster-backup.ts", "../src/proxmox/generated/constraints/pve-cluster-ceph.ts", "../src/proxmox/generated/constraints/pve-cluster-firewall.ts", "../src/proxmox/generated/constraints/pve-cluster-ha.ts", "../src/proxmox/generated/constraints/pve-cluster-metrics.ts", "../src/proxmox/generated/constraints/pve-cluster-notifications.ts", "../src/proxmox/generated/constraints/pve-cluster-replication.ts", "../src/proxmox/generated/constraints/pve-cluster-sdn.ts", "../src/proxmox/generated/constraints/pve-nodes-ceph.ts", "../src/proxmox/generated/constraints/pve-nodes-disks.ts", "../src/proxmox/generated/constraints/pve-nodes-lxc.ts", "../src/proxmox/generated/constraints/pve-nodes-network.ts", "../src/proxmox/generated/constraints/pve-nodes-qemu.ts", "../src/proxmox/generated/constraints/pve-pools.ts", "../src/proxmox/generated/constraints/pve-storage.ts", "../src/proxmox/generated/constraints/pbs-config.ts", "../src/proxmox/generated/constraints/index.ts", "../src/proxmox/update-guard.ts", "../src/proxmox/alertmanager-body.ts", "../src/proxmox/api-token.ts", "../src/proxmox/values.ts", "../src/proxmox/api-token-form.ts", "../src/proxmox/backup-job.ts", "../src/proxmox/ceph-daemon.ts", "../src/proxmox/ceph-daemon-form.ts", "../src/proxmox/ceph-flag.ts", "../src/proxmox/ceph-fs.ts", "../src/proxmox/ceph-fs-wire.ts", "../src/proxmox/ceph-osd.ts", "../src/proxmox/ceph-osd-tree.ts", "../src/proxmox/ceph-osd-write.ts", "../src/proxmox/ceph-pool.ts", "../src/proxmox/ceph-pool-form.ts", "../src/proxmox/ceph-pool-settle.ts", "../src/proxmox/firewall-alias.ts", "../src/proxmox/firewall-alias-form.ts", "../src/proxmox/group.ts", "../src/proxmox/ha-resource.ts", "../src/proxmox/ha-rule.ts", "../src/proxmox/ha-rule-form.ts", "../src/proxmox/lxc.ts", "../src/ownership/adopt.ts", "../src/ownership/resume.ts", "../src/ownership/rows.ts", "../src/ownership/whole.ts", "../src/ownership/probe.ts", "../src/proxmox/lxc-wire.ts", "../src/proxmox/lxc-props.ts", "../src/proxmox/lxc-volume.ts", "../src/proxmox/lxc-judge.ts", "../src/proxmox/lxc-create-form.ts", "../src/proxmox/lxc-identity.ts", "../src/proxmox/lxc-adoption.ts", "../src/ownership/adopting.ts", "../src/proxmox/lxc-lifecycle.ts", "../src/proxmox/network-apply-read.ts", "../src/proxmox/metric-server.ts", "../src/proxmox/metric-server-form.ts", "../src/proxmox/metric-server-otel.ts", "../src/proxmox/network-apply.ts", "../src/proxmox/apply-endpoints.ts", "../src/proxmox/node-network.ts", "../src/proxmox/node-network-form.ts", "../src/proxmox/notification-matcher.ts", "../src/proxmox/notification-matcher-form.ts", "../src/proxmox/notification-target.ts", "../src/proxmox/notification-target-endpoint.ts", "../src/proxmox/notification-target-form.ts", "../src/proxmox/pbs-datastore.ts", "../src/proxmox/pbs-datastore-form.ts", "../src/proxmox/pbs-datastore-guard.ts", "../src/proxmox/pbs-datastore-endpoint.ts", "../src/proxmox/pbs-notification-matcher.ts", "../src/proxmox/pbs-notification-target.ts", "../src/proxmox/pbs-notification-target-lifecycle.ts", "../src/proxmox/pbs-notification-target-endpoint.ts", "../src/proxmox/credential-literals.ts", "../src/proxmox/pbs-notification-target-wire.ts", "../src/proxmox/write-only.ts", "../src/proxmox/pbs-notification-target-form.ts", "../src/proxmox/pbs-prune-job.ts", "../src/proxmox/pbs-prune-job-form.ts", "../src/proxmox/pbs-sync-job.ts", "../src/proxmox/pbs-sync-job-form.ts", "../src/proxmox/pbs-verify-job.ts", "../src/proxmox/pbs-verify-job-form.ts", "../src/proxmox/pool.ts", "../src/proxmox/provision-baseline.ts", "../src/proxmox/provision-bootstrap.ts", "../src/proxmox/provision-declare.ts", "../src/proxmox/role.ts", "../src/proxmox/user.ts", "../src/proxmox/qemu.ts", "../src/proxmox/replication-job.ts", "../src/proxmox/replication-job-form.ts", "../src/proxmox/sdn-apply.ts", "../src/proxmox/sdn-apply-read.ts", "../src/proxmox/sdn-subnet.ts", "../src/proxmox/sdn-subnet-form.ts", "../src/proxmox/sdn-vnet.ts", "../src/proxmox/sdn-zone.ts", "../src/proxmox/storage.ts", "../src/proxmox/zfs-pool.ts", "../src/proxmox/zfs-pool-write.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * `Proxmox.Acl` — one grant: a role bound to a subject at a path. The line that makes a role real.\n *\n * ★ USERS, GROUPS, ROLES AND TOKENS ARE ALL INERT UNTIL AN ACL BINDS THEM. A role is a named list\n * of privileges that grants nothing by itself; a user can do nothing until\n * {path, user|group|token, role} exists. That is what makes those four families worth declaring,\n * and why the grants that let OpenBao mint a PVE token today are only clicks somebody made once.\n *\n * ⛔ THE ONE FAMILY THAT DOES NOT FIT `pveOperations` AS WRITTEN, AND THE MISFIT IS MEASURED. The\n * cluster's own schema — `/pve-docs/api-viewer/apidoc.js`, read unauthenticated 2026-09-13 —\n * lists exactly TWO methods on `/access/acl`: GET and PUT. There is no DELETE (it answers\n * \"Method 'DELETE /access/acl' not implemented\") and, less famously, NO POST either. So create\n * and update are the SAME call, `PUT /access/acl`, and removal is that PUT with `delete=1` —\n * which is why `delete` below does not use `ops.destroy`.\n * ⚠️ THE REAL FIX BELONGS IN `resource.ts`: give `PveSpec` a `deleteForm` and the method to go\n * with it, and this override disappears. Until then it is local and loud — do not \"restore\n * symmetry\" by pointing `delete` at `ops.destroy`, which reports every removal as a 501 while\n * the grant stays exactly where it was.\n *\n * ⛔ A SINGLE GRANT HAS NO URL OF ITS OWN. `GET /access/acl` answers ONE FLAT LIST for the whole\n * cluster, so identity is the tuple (path, type, ugid, roleid), matched client-side in\n * `attributes` below. Two resources declaring the SAME tuple are the SAME grant — Alchemy sees\n * two resource ids, not one collision — and deleting either takes the access away from both,\n * the same hazard as two guests declaring one vmid.\n *\n * ⛔ AND THAT LIST IS FILTERED BY WHO IS ASKING. The schema's own words for GET: \"The returned list\n * is restricted to objects where you have rights to modify permissions.\" A credential that can\n * read the cluster but not modify permissions is answered `[]` — not a 403, not an error — so\n * the mount's `read` role needs permission-modify rights on the declared path exactly as\n * `provision` does. Work reported on a grant that is plainly there means that role is too narrow.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { pve } from './client.ts';\nimport { type PveRequirements, type WithTarget, pveOperations } from './resource.ts';\n\n/** PVE's three kinds of subject. The read answers this word; the write wants its plural. */\nexport type AclSubjectType = 'user' | 'group' | 'token';\n\nexport interface AclProps extends WithTarget {\n /**\n * The PVE object path the grant is ON — `/`, `/pool/lab`, `/vms/101`, `/storage/local-zfs`.\n * ⚠️ NOT THE API PATH: `spec.path` below is the endpoint (`access/acl`). PVE's API overloads the\n * word, so this file does too rather than renaming a field the cluster calls `path`.\n */\n path: string;\n /** Which kind of subject `ugid` names. Identity: changing it is a different grant. */\n type: AclSubjectType;\n /** `alice@pve`, `admins`, `hf-provision@pve!hf-provision-…`. Identity. */\n ugid: string;\n /** The role bound here, e.g. `PVEAuditor`. ⚠️ It must exist — PVE refuses an unknown roleid. */\n roleid: string;\n /** Inherit down the path. PVE's default is ON, and this is the grant's ONLY mutable field. */\n propagate?: boolean;\n}\n\nexport interface AclAttributes {\n path: string;\n type: AclSubjectType;\n ugid: string;\n roleid: string;\n propagate: boolean;\n /**\n * ⛔ PRESENCE AS AN ATTRIBUTE, AND IT IS WHAT MAKES THE FACTORY WORK HERE. `attributes` never\n * returns undefined: if it did, `reconcile` would take the create branch and POST to\n * `access/acl`, which PVE does not implement. Present-but-unbound sends every write down the\n * PUT branch instead, the only branch PVE has. The cost: the factory's read-back guard cannot\n * fire, so `reconcile` below re-checks this field itself and dies with the same honesty.\n */\n bound: boolean;\n}\n\nexport interface ProxmoxAcl extends Resource<\n 'Proxmox.Acl',\n AclProps,\n AclAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxAcl = Resource<ProxmoxAcl>('Proxmox.Acl');\n\n/**\n * ⚠️ PVE NORMALISES ACL PATHS AND RETURNS THE NORMALISED FORM, a forever-update trap when only one\n * side of the comparison is normalised. `PVE::AccessControl::normalize_path` collapses repeated\n * slashes and strips the trailing one, so a declared `/pool/lab/` reads back as `/pool/lab`\n * and a naive match never fires again. It also refuses a path with no leading slash, so one is\n * added here rather than letting `pool/lab` 400.\n */\nconst normalize = (raw: string) => `/${raw.split('/').filter(Boolean).join('/')}`;\n\n/**\n * ⚠️ THE WRITE NAMES THE SUBJECT WITH A PLURAL KEY THAT DIFFERS PER KIND — `users`, `groups`,\n * `tokens` — while the READ answers a singular `type`/`ugid` pair. The PUT declares\n * `additionalProperties: 0`, so `user=` fails with a 400 rather than being ignored. The bad case\n * is the WRONG plural: a valid parameter naming a subject kind you did not mean.\n */\nconst SUBJECT_FIELD = { group: 'groups', token: 'tokens', user: 'users' } as const;\n\n/** The tuple PVE keys a grant by, as the form it takes. Every write to this family starts here. */\nconst tuple = (props: AclProps): Record<string, string> => ({\n path: normalize(props.path),\n roles: props.roleid,\n [SUBJECT_FIELD[props.type]]: props.ugid,\n});\n\n/** Create and update are one call: the tuple plus the single mutable field. */\nconst bind = (props: AclProps): Record<string, string> => ({\n propagate: props.propagate === false ? '0' : '1',\n ...tuple(props),\n});\n\n/** What a change of this string means: not an edit, a different grant. Used by `diff` below. */\nconst identity = (grant: Pick<AclAttributes, 'path' | 'roleid' | 'type' | 'ugid'>) =>\n [normalize(grant.path), grant.type, grant.ugid, grant.roleid].join(' ');\n\n/**\n * ⚠️ `GET /access/acl` ANSWERS AN ARRAY, while the factory hands `attributes` the\n * `Record<string, unknown>` every other PVE read is shaped like. Rows are narrowed, not trusted,\n * and an empty result is not evidence of an empty cluster — see the last ⛔ in the header.\n */\nconst find = (live: unknown, props: AclProps) =>\n (Array.isArray(live) ? live : [])\n .filter((row): row is Record<string, unknown> => typeof row === 'object' && row !== null)\n .find(\n (row) =>\n row['path'] === normalize(props.path) &&\n row['type'] === props.type &&\n row['ugid'] === props.ugid &&\n row['roleid'] === props.roleid,\n );\n\n/**\n * ⚠️ PVE ANSWERS `propagate` AS 1/0 RATHER THAN true/false, AND OMITS IT WHEN IT CARRIES THE API\n * DEFAULT, WHICH IS ON. Reading an absent field as `false` would report drift on every plan,\n * forever, for every grant this provider did not create itself.\n */\nconst propagates = (row: Record<string, unknown>) => {\n const value = row['propagate'];\n return value === undefined || value === 1 || value === true || value === '1';\n};\n\nconst ops = pveOperations<AclProps, AclAttributes>({\n attributes: (live, props) => {\n const row = find(live, props);\n return {\n // ⚠️ `propagate` reads false when nothing is bound, because there is no grant to inherit.\n // `bound` is the field that carries presence; this one means something only once it is set.\n bound: row !== undefined,\n path: normalize(props.path),\n propagate: row !== undefined && propagates(row),\n roleid: props.roleid,\n type: props.type,\n ugid: props.ugid,\n };\n },\n /**\n * ⛔ THE CREATE PATH IS UNREACHABLE ON A HEALTHY CLUSTER, AND ITS ERROR WILL MISLEAD YOU. The\n * factory POSTs `collection` only when the read came back undefined, and `attributes` never\n * does — so a POST means the GET itself failed (an expired 300s lease, a node down), not that\n * the grant is missing. PVE answers \"Method 'POST /access/acl' not implemented\": read that 501\n * as \"the read failed\" and go and look at the credential, not at the ACL.\n */\n /**\n * ⛔ `provision`, AND WITHOUT IT EVERY GRANT READS BACK AS ABSENT. `GET /access/acl` is\n * FILTERED, NOT GATED — the schema's own words are \"The returned list is restricted to objects\n * where you have rights to modify permissions\". A credential without Permissions.Modify gets\n * HTTP 200 and an EMPTY ARRAY, never a 403, so there is no error for `read` to fold; it simply\n * sees nothing.\n * ⛔ THE READ LEASE IS EXACTLY SUCH A CREDENTIAL. `hf-read@pve` is PVEAuditor on `/`, whose\n * privilege set is Datastore.Audit, Mapping.Audit, Pool.Audit, SDN.Audit, Sys.Audit,\n * VM.Audit, VM.GuestAgent.Audit — MEASURED in the 2026-09-13 cluster read. No\n * Permissions.Modify. So on the default lease all fourteen live grants read back unbound,\n * `matches` is false for every one, the plan reports fourteen updates for grants that\n * plainly exist, and the deploy PUTs each one before acl.ts's own read-back guard dies.\n * ★ THIS IS THE SAME CLASS AS storage.ts, sdn-zone.ts, sdn-vnet.ts AND api-token.ts, and it is\n * the worst instance of it: those three are gated and answer 403, which is at least an\n * error. A filtered endpoint answers success with less data, which is indistinguishable from\n * the object not being there.\n */\n readRole: 'provision',\n collection: () => 'access/acl',\n createForm: bind,\n /**\n * ⚠️ ONLY `propagate` IS COMPARED, AND ONLY ONCE THE GRANT IS THERE. path/type/ugid/roleid are\n * the FILTER that produced these attributes, not a reading of the cluster: comparing them with\n * the props they came from is true by construction. An identity change is a DIFFERENT grant,\n * which `diff` in the provider handles.\n */\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: { create: 'pve:PUT /access/acl', update: 'pve:PUT /access/acl' },\n matches: (attributes, props) =>\n attributes.bound && attributes.propagate === (props.propagate !== false),\n path: () => 'access/acl',\n updateForm: bind,\n});\n\nexport const ProxmoxAclProvider = () =>\n Provider.effect(\n ProxmoxAcl,\n Effect.succeed(\n ProxmoxAcl.Provider.of({\n /**\n * ⛔ EMPTY LIKE EVERY OTHER RESOURCE HERE, AND MOST OF ALL THIS ONE. `GET /access/acl`\n * hands back every grant a human ever clicked; adopting them would let a later plan\n * DELETE somebody's access as tidy-up. Adoption is an explicit act.\n */\n list: () => Effect.succeed([]),\n read: Effect.fn(function* ({ olds }) {\n return yield* ops.read(olds);\n }),\n /**\n * ⛔ AN IDENTITY CHANGE IS A REPLACE, AND THE FACTORY CANNOT SAY SO ALONE — it answers\n * `replace` only for objects with no update path, and this one has PUT. Left to delegate,\n * editing `roleid` (or the subject, or the path) would PUT the NEW grant and LEAVE THE\n * OLD ONE BOUND: access nobody declared, held indefinitely, invisible in the plan because\n * the provider believes it converged. Replace makes Alchemy call `delete` with the OLD\n * props, which is the only thing that removes it.\n * ⚠️ CREATE-FIRST, DELIBERATELY (Alchemy's default; `deleteFirst: true` would invert it).\n * Delete-first could remove the very grant the provision credential mints against and\n * leave nothing able to put it back. The cost is that a NARROWING change leaves the\n * wider grant bound until Phase 2 collects the old generation.\n */\n diff: Effect.fn(function* ({ news, output }) {\n if (output !== undefined && isResolved(news) && identity(news) !== identity(output)) {\n return { action: 'replace' } as const;\n }\n return yield* ops.diff(news, output);\n }),\n /**\n * ⚠️ THE READ-BACK GUARD, RESTORED FOR A FAMILY WHOSE \"ABSENT\" IS NOT `undefined`.\n * `ops.reconcile` refuses when the object is still missing after a write, but missing\n * here is `bound: false`, which that check cannot see. PVE answers 200 with\n * `{\"data\":null}` on calls that did nothing: without this, a PUT that silently no-oped\n * is recorded as a landed grant and the next plan reads noop over the gap.\n */\n reconcile: Effect.fn(function* ({ news }) {\n const after = yield* ops.reconcile(news);\n if (!after.bound) {\n return yield* Effect.die(\n new Error(\n `access/acl: the write returned no error but ${identity(news)} is still not ` +\n 'bound. Either the PUT did nothing, or the read credential cannot see the ' +\n 'grant -- GET /access/acl is filtered to paths you may MODIFY permissions on, ' +\n 'so a read-only role is answered [] rather than 403.',\n ),\n );\n }\n return after;\n }),\n /**\n * ⛔ NOT `ops.destroy`, AND NOT AN OVERSIGHT. `DELETE /access/acl` is not implemented, so a\n * factory delete would fail every destroy — while looking like a permissions problem —\n * and leave the grant bound. Removal is the create call with `delete=1` and the identity\n * tuple, no `propagate`, because removal is keyed on the tuple and nothing else.\n *\n * ⚠️ IDEMPOTENT, AND SHARED: removing a grant that is already gone is a no-op, but the\n * tuple is cluster-global — re-read the second ⛔ in the header before assuming this\n * only takes away what this stack declared.\n */\n delete: Effect.fn(function* ({ olds }) {\n const removal = { delete: '1', ...tuple(olds) };\n yield* pve(olds.target, 'provision', 'PUT', 'access/acl', removal);\n }),\n }),\n ),\n );\n",
|
|
6
6
|
"/**\n * One authenticated call to the Proxmox VE API.\n *\n * ★ PLAIN `fetch`, NO TLS ESCAPE HATCH, AND THAT IS A MEASURED CHOICE RATHER THAN AN OMISSION.\n * Proxmox is usually met with `curl -k` because a fresh install serves its own self-signed\n * certificate — I reached for `-k` myself before checking. This cluster serves a real Let's\n * Encrypt certificate (`CN=cluster-c1.example.com`), and strict TLS answers 401, i.e. the\n * handshake succeeds and only the credential is missing. So there is no `rejectUnauthorized`\n * option here to be left switched on by accident in somebody else's estate.\n *\n * ⚠️ EVERY PVE ANSWER IS WRAPPED IN `{\"data\": ...}`, errors included — a failed call can still be\n * HTTP 200 with `{\"data\": null}`. Unwrapping is not cosmetic; a caller that reads the envelope\n * as the payload sees `undefined` and calls it \"absent\".\n */\nimport * as Effect from 'effect/Effect';\nimport * as HttpClient from 'effect/unstable/http/HttpClient';\nimport * as HttpClientRequest from 'effect/unstable/http/HttpClientRequest';\nimport {\n type ApiTarget,\n type PveCredential,\n type PveRole,\n type PveTarget,\n authorization,\n} from './credentials.ts';\nimport { leased } from './lease-cache.ts';\nimport { executeOnCluster } from './members.ts';\n\nexport class PveError extends Error {\n constructor(\n readonly status: number,\n readonly method: string,\n readonly path: string,\n detail: string,\n ) {\n super(`PVE ${method} ${path} -> ${String(status)}: ${detail}`);\n this.name = 'PveError';\n }\n}\n\nconst clusterExhausted = (\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n attempts: ReadonlyArray<{ member: string; why: string }>,\n) =>\n new PveError(\n 0,\n method,\n path,\n `all members failed: ${attempts.map((a) => `${a.member} (${a.why})`).join('; ')}`,\n );\n\n/**\n * A form body, where a value may be a LIST.\n *\n * ⛔ PVE AND PBS SPELL A MULTI-VALUED FIELD DIFFERENTLY, AND THE LIST FORM IS FOR PBS. PBS is Rust:\n * its schemas use `type: array` — `delete` on every update endpoint is one — and its urlencoded\n * decoder builds that array from REPEATED KEYS (`delete=a&delete=b`). PVE's Perl takes a comma\n * string for the same idea, which is what `withClears` in values.ts produces, and for the one\n * PVE field that genuinely needs repetition it wants a NUL-joined value instead (see\n * notification-target.ts). So: PVE callers pass strings and are unaffected; a PBS caller that\n * needs an array passes one and gets repeated keys rather than reaching for PVE's encoding.\n */\nexport type PveForm = Record<string, readonly string[] | string>;\n\n/** ⚠️ `append`, NOT `set`: a repeated key is the point, and `set` would keep only the last. */\nconst encode = (form: PveForm): string => {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(form)) {\n if (typeof value === 'string') params.append(key, value);\n else for (const item of value) params.append(key, item);\n }\n return params.toString();\n};\n\nconst buildRequest = (\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n apiBase: string,\n path: string,\n credential: PveCredential,\n target: ApiTarget,\n form?: PveForm,\n) => {\n const url = `${apiBase}/${path}`;\n /**\n * ⛔ THE CONTENT TYPE IS AN ARGUMENT TO `bodyText`, NOT AN ENTRY IN THE HEADER MAP, AND GETTING\n * THAT WRONG MADE EVERY WRITE IN THIS PACKAGE FAIL.\n *\n * 🔴 THE BUG, AND HOW IT HID FOR A WHOLE DAY. The header map DID set\n * `application/x-www-form-urlencoded` — and `bodyText(s)` with no second argument sets\n * `text/plain` and OVERWRITES it, because it is piped AFTER `setHeaders`. PVE and PBS accept\n * ONLY form encoding or JSON and reject everything else outright (measured in\n * proxmox-rest-server's `get_request_parameters`), so every create, update and delete this\n * package can perform was broken from the day the client was written.\n * ⛔ NOTHING CAUGHT IT BECAUSE NOTHING HAD EVER WRITTEN. Ninety-eight resources were adopted\n * against the live cluster and every one matched, so `reconcile` never reached a PUT. The\n * identical bug in <estate>/forgejo/src/client.ts surfaced the moment a write token existed\n * and answered `422 Unsupported Content-Type`; this one is the same line, found by looking.\n * ★ A PLAN THAT SAYS `noop` PROVES THE READ PATH AND NOTHING ELSE. That is the lesson worth\n * more than the fix.\n *\n * ⚠️ PVE TAKES FORM ENCODING, NOT JSON. Sending application/json is accepted and then SILENTLY\n * IGNORED on several endpoints, which reads as \"the API did nothing\" rather than as a wrong\n * content type — so the type below is load bearing in both directions.\n */\n return HttpClientRequest.make(method)(url).pipe(\n HttpClientRequest.setHeaders({ Authorization: authorization(credential, target) }),\n form === undefined\n ? (self) => self\n : HttpClientRequest.bodyText(encode(form), 'application/x-www-form-urlencoded'),\n );\n};\n\nconst executeOne = (\n target: ApiTarget,\n credential: PveCredential,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n form: PveForm | undefined,\n apiBase: string,\n) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient;\n const request = buildRequest(method, apiBase, path, credential, target, form);\n return yield* client\n .execute(request)\n .pipe(Effect.mapError((cause) => new PveError(0, method, path, String(cause))));\n });\n\nconst readResponse = <T>(\n response: {\n status: number;\n text: Effect.Effect<string, unknown>;\n json: Effect.Effect<unknown, unknown>;\n },\n method: string,\n path: string,\n) =>\n Effect.gen(function* () {\n /**\n * ⛔ STATUS FIRST, THEN THE BODY, AND THE BODY IS READ EXACTLY ONCE. `text` and `json` both\n * consume the response stream, so asking for both is a bug waiting to happen. A failure is\n * read as TEXT because an error body is not reliably JSON — PVE answers HTML for some 5xx —\n * and a success is read as JSON by the client rather than by a hand-rolled `JSON.parse`.\n */\n if (response.status < 200 || response.status >= 300) {\n // ⚠️ 401 HERE USUALLY MEANS THE LEASE EXPIRED, NOT THAT THE ROLE IS WRONG. `provision` is a\n // 300s non-renewable lease; a long reconcile should mint again rather than widen the role.\n const detail = yield* response.text.pipe(Effect.orElseSucceed(() => ''));\n return yield* Effect.fail(new PveError(response.status, method, path, detail.slice(0, 300)));\n }\n const body = (yield* response.json.pipe(\n Effect.mapError((cause) => new PveError(0, method, path, String(cause))),\n )) as { data?: T };\n return body.data;\n });\n\n/** A call that already holds a credential. Used when one mint serves several calls. */\nexport const pveWith = <T>(\n target: ApiTarget,\n credential: PveCredential,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n form?: PveForm,\n) =>\n Effect.gen(function* () {\n const response =\n target.scheme === 'pve'\n ? yield* executeOnCluster(target, method, path, (apiBase) =>\n buildRequest(method, apiBase, path, credential, target, form),\n ).pipe(\n Effect.mapError((failure) =>\n failure.tag === 'cluster'\n ? clusterExhausted(failure.method, failure.path, failure.attempts)\n : new PveError(0, method, path, String(failure)),\n ),\n )\n : yield* executeOne(target, credential, method, path, form, target.api);\n return yield* readResponse<T>(response, method, path);\n });\n\n/**\n * Like `pveWith`, but returns the full JSON body — for endpoints whose answer lives beside `data`.\n *\n * ⚠️ `network-apply-read.ts` is the caller today; the local fetch there should fold into this once\n * the envelope shape is stable across both paths.\n */\nexport const pveEnvelopeWith = (\n target: PveTarget,\n credential: PveCredential,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n) =>\n Effect.gen(function* () {\n const response = yield* executeOnCluster(target, method, path, (apiBase) =>\n buildRequest(method, apiBase, path, credential, target),\n ).pipe(\n Effect.mapError((failure) =>\n failure.tag === 'cluster'\n ? clusterExhausted(failure.method, failure.path, failure.attempts)\n : new PveError(0, method, path, String(failure)),\n ),\n );\n if (response.status < 200 || response.status >= 300) {\n const detail = yield* response.text.pipe(Effect.orElseSucceed(() => ''));\n return yield* Effect.fail(new PveError(response.status, method, path, detail.slice(0, 300)));\n }\n return (yield* response.json.pipe(\n Effect.mapError((cause) => new PveError(0, method, path, String(cause))),\n )) as Record<string, unknown>;\n });\n\n/**\n * Make one call on a credential for `role`, REUSING a still-valid lease rather than minting one.\n *\n * 🔴 IT USED TO MINT PER CALL, AND AT 98 RESOURCES THAT STOPPED BEING FREE. One plan plus deploy\n * of the C1 stack left 760 token entries in `/etc/pve/user.cfg` — read, diff, reconcile and the\n * read-back each minted their own, and `/etc/pve` is a replicated cluster filesystem every node\n * has to agree on. src/lease-cache.ts carries the measurement and the safety argument.\n *\n * ⚠️ `pveWith` IS STILL THERE AND STILL TAKES AN EXPLICIT CREDENTIAL. network-apply.ts needs ONE\n * identity across its apply and its task poll — PVE only skips the task-status privilege check\n * for the task's OWNER — and it threads a lease through by hand for that. Caching makes that\n * property easier to hold, never weaker, but the explicit path stays because that caller is\n * reasoning about identity rather than about cost.\n *\n * ★ TASK POLL ON A DIFFERENT MEMBER THAN THE APPLY STILL HOLDS: the owner is the token id, not the\n * node that received the PUT. Any member serves any task path; the privilege skip is keyed on\n * whether the caller IS the task's owner.\n */\nexport const pve = <T>(\n target: ApiTarget,\n role: PveRole,\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n form?: PveForm,\n) =>\n Effect.gen(function* () {\n const credential = yield* leased(target, role);\n return yield* pveWith<T>(target, credential, method, path, form);\n });\n",
|
|
7
7
|
"/**\n * The PVE credential a reconcile runs with: minted from OpenBao, alive for five minutes.\n *\n * ⛔ THERE IS NO STATIC WRITE TOKEN IN THIS ESTATE, AND THAT IS DELIBERATE. Every PVE token stored\n * on the estate's static KV shelf is read-only — PVEAuditor on each cluster, Audit on PBS, all\n * as one metrics user. The MCP proxmox client says why in its own header: the auditor token bounds\n * what a BUG can do, independently of what the code bounds. Adding a long-lived write token to\n * that shelf would delete the outer lock for every reader of the shelf, not just for this\n * provider.\n *\n * ★ SO WRITES COME FROM A DYNAMIC MOUNT INSTEAD, which the estate already built: `proxmox-c1/`\n * exposes two roles — `read` (ttl 3600s) and `provision` (ttl 300s, max 1800s, NOT renewable).\n * A mint returns a PVE API token of its own, `hf-provision@pve!hf-provision-…-<timestamp>-<id>`,\n * which expires on its own whether or not anything cleans up. Same shape as\n * `cloudflare-<account>-<surface>/creds/<role>`, same reason.\n * ⚠️ THAT PATH IS WRITTEN WITH `<surface>` RATHER THAN A GLOB ON PURPOSE: a `*` followed by\n * a slash CLOSES THIS BLOCK COMMENT, and everything below it then parses as code. It cost\n * six TS1005/TS1443 errors pointing at innocent lines twenty rows further down.\n *\n * ⚠️ FIVE MINUTES IS THE DESIGN, NOT AN OBSTACLE. A reconcile that cannot finish inside the lease\n * should mint again rather than ask for a longer one: the short lease is what makes a leaked\n * token uninteresting. `provision` is explicitly non-renewable, so there is no renew path to\n * reach for.\n *\n * ⛔ THE SECRET NEVER TOUCHES DISK, A LOG, OR ALCHEMY STATE. Alchemy persists resource attributes\n * WITHOUT encryption — StateEncoding.ts writes `Redacted` as `{\"@redacted\": <plaintext>}` — and\n * this estate's state store is the `alchemy` Postgres, which a nightly job dumps. A PVE\n * secret that reached an attribute would outlive its 300s lease by months, in four places.\n * Nothing here returns it to a resource; it is used to build a header and then dropped.\n */\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport * as FetchHttpClient from 'effect/unstable/http/FetchHttpClient';\nimport * as Headers from 'effect/unstable/http/Headers';\nimport * as HttpClient from 'effect/unstable/http/HttpClient';\nimport * as HttpClientRequest from 'effect/unstable/http/HttpClientRequest';\n\n/**\n * ★ NO SESSION, ON PURPOSE, AND THIS IS WHAT UNBLOCKED `wip/bao-policy`.\n * `CommandExecutor.run` requires a `ScopedPlanStatusSession`, and Alchemy gives one to\n * `reconcile` and `delete` but NOT to `read` or `diff` — so a provider built on `run` can only\n * ever write. Alchemy's OWN Docker provider does not use `CommandExecutor` for this at all: it\n * takes `ChildProcessSpawner` and pipes `ChildProcess.make(...)` through `spawner.spawn`, which\n * needs a `Scope` and nothing else (Docker.ts:476-553). That was the pattern here until\n * 2026-09-14, when `mint` moved to `HttpClient` — which needs no session either, so `mint` is\n * still callable from all four operations.\n *\n * ⛔ DO NOT SATISFY THE OLD SIGNATURE BY FAKING A SESSION. A session carries plan status Alchemy\n * uses to report progress; inventing one would make read and diff report work they are not doing.\n */\n/** Which role a call needs. `read` for read/diff, `provision` for reconcile/delete. */\nexport type PveRole = 'read' | 'provision';\n\n/**\n * Where credentials come from, and which cluster they are for.\n *\n * ⚠️ A PARAMETER, NOT A CONSTANT, SO THIS PACKAGE CAN LEAVE THE ESTATE. Everything\n * HomeFlare-specific — the mount name, the API host — belongs in the stack that declares\n * resources, not in the provider. Anyone with an OpenBao mount that vends Proxmox API tokens can\n * use this file unchanged; that is the whole difference between a provider and a script.\n */\nexport type PveTarget = {\n /** OpenBao mount that vends API tokens for this host, e.g. `proxmox-c1`. */\n readonly mount: string;\n /**\n * mgmt hostnames for cluster members, e.g. `node-b.mgmt.example.com`. Any member's :8006 API\n * manages the whole cluster; `client.ts` failovers across them.\n */\n readonly members: readonly string[];\n /**\n * Which product answers at `api`. Defaults to `pve`.\n *\n * ★ PBS IS THE SAME CLIENT WITH A DIFFERENT AUTHORIZATION HEADER, AND THAT IS WHY THERE IS NO\n * SECOND CLIENT HERE. Proxmox Backup Server speaks the same `/api2/json` paths, wraps every\n * answer in the same `{\"data\": …}` envelope, and its OpenBao mount vends the same\n * `{token_id, secret}` shape — so `mint`, `pve()` and `pveOperations` all work against it\n * unchanged. The ONE difference is the header scheme: PVE spells it\n * `PVEAPIToken=<id>=<secret>` and PBS spells it `PBSAPIToken=<id>:<secret>` — a different\n * prefix AND a different separator.\n *\n * ⛔ IT IS REQUIRED, NOT OPTIONAL, AND THAT IS THE WHOLE TYPE-SAFETY ARGUMENT. A PBS target and\n * a PVE target are the same two strings, so with an OPTIONAL discriminant TypeScript's\n * structural typing would accept a PBS host wherever a PVE one belongs — and `pve()` would\n * then build `PVEAPIToken=<id>=<secret>` against a server wanting\n * `PBSAPIToken=<id>:<secret>`. Every call 401s, `pveOperations.read` folds that into \"absent\",\n * the plan says CREATE for an object that is plainly there, the POST is refused, and not one\n * symptom points at the header. Required, the mistake is a compile error instead.\n *\n * ⚠️ REASONED FROM PBS'S DOCUMENTED SCHEME, NOT MEASURED. The estate has no PBS credential yet\n * (there is no PBS mount in OpenBao), and an unauthenticated probe cannot tell the schemes\n * apart — `https://pbs.example.com:8007/api2/json/nodes` answers 401 to a missing header, a\n * PVE-shaped one and a PBS-shaped one alike. The first real token will confirm or correct it,\n * and a wrong guess fails closed with a 401 rather than doing something odd.\n */\n readonly scheme: 'pve';\n};\n\n/** The same client against a Proxmox Backup Server. See the ⛔ on `scheme` above. */\nexport type PbsTarget = {\n readonly mount: string;\n readonly api: string;\n readonly scheme: 'pbs';\n};\n\n/** Either product. What `pve()` and `pveOperations` accept; what a RESOURCE accepts is narrower. */\nexport type ApiTarget = PbsTarget | PveTarget;\n\n/**\n * What a mint returns.\n *\n * ⚠️ `secret` IS PRESENT AND MUST NOT BE PERSISTED. It is typed as a plain string rather than\n * `Redacted` on purpose: Alchemy's Redacted is a STATE-ENCODING marker, not encryption, so\n * wrapping it would imply a protection that does not exist. The protection here is that this\n * value never leaves the function that builds the Authorization header.\n */\nexport type PveCredential = {\n /** `hf-provision@pve!hf-provision-…` — an identifier, safe to log. */\n readonly tokenId: string;\n /** ⛔ NEVER LOG, NEVER PERSIST, NEVER RETURN FROM A RESOURCE. */\n readonly secret: string;\n /** Seconds the lease was granted for, as reported by OpenBao. */\n readonly leaseSeconds: number;\n};\n\n/** The environment `mint` resolves OpenBao from — `process.env` unless a test passes its own. */\nexport type BaoEnvironment = Readonly<Record<string, string | undefined>>;\n\n/** openbao v2.6.2 api/env.go:22-34 — a present BAO_* wins, even empty; else its VAULT_* twin. */\nconst baoVariable = (env: BaoEnvironment, name: string) =>\n env[`BAO_${name}`] ?? env[`VAULT_${name}`] ?? '';\n\n/**\n * Mint one credential for `role`: `GET /v1/<mount>/creds/<role>` on OpenBao's HTTP API.\n *\n * 🔴 IT SHELLED OUT TO `bao read -format=json` UNTIL 2026-09-14. The case for the CLI was that one\n * client could not disagree with `bao kv get` about what this machine may do. <estate>/openbao then\n * learned twice what deciding by stderr costs — every failure read as absent, and two commands\n * said \"absent\" in different words — so this reads STATUS CODES: 2xx is a credential, and 403\n * (denied), 404 (no such role or mount — a mint has no \"absent\"), 503 (sealed) all fail.\n * ★ THE ENVIRONMENT CONTRACT IS STILL THE CLI's, READ THE CLI's WAY, which keeps the case above\n * true (openbao v2.6.2 api/client.go:34-66, 370, 503-507, 761-762, 776-785): BAO_AGENT_ADDR over\n * BAO_ADDR over https://127.0.0.1:8200; BAO_NAMESPACE as X-Vault-Namespace; BAO_TOKEN as\n * X-Vault-Token; X-Vault-Request always. <estate>/openbao/src/bao-address.ts has the long form.\n * ★ AGENT MODE IS NO BAO_TOKEN AND NO TOKEN HEADER. An Agent or Proxy listener with\n * `api_proxy { use_auto_auth_token = true }` (or the older `cache { … }`) substitutes its\n * auto-auth token exactly when a request carries none — command/agentproxyshared/cache/handler.go\n * :29-38, command/agent.go:517-541, command/agent/config/config.go:288-292, and\n * website/content/docs/agent-and-proxy/agent/apiproxy.mdx:57-62. The CLI's token-file fallback\n * is deliberately not read. ⚠️ So an unset BAO_TOKEN aimed at a SERVER rather than an agent is a\n * 403, which reads like a missing grant rather than a missing variable.\n * ★ UNIX SOCKETS: `unix:///path` is plain HTTP to `localhost` over that socket, as the CLI does it\n * (api/client.go:654-667). Bun's fetch takes a `unix` option; `FetchHttpClient.RequestInit` is\n * Effect's documented way to pass fetch options, read from the calling fiber (effect\n * FetchHttpClient.ts:59-72). Off Bun it REFUSES, rather than reach http://localhost:80.\n */\nexport const mint = (target: ApiTarget, role: PveRole, env: BaoEnvironment = process.env) =>\n Effect.gen(function* () {\n const path = `${target.mount}/creds/${role}`;\n // ⛔ METHOD, PATH AND OpenBao's `errors` ONLY — never a header (the token), never a success\n // body (the credential is IN it, so echoing it would print the secret into a log).\n const refuse = (status: number, detail: string) =>\n new Error(`OpenBao GET /v1/${path} -> ${status === 0 ? 'no response' : status}: ${detail}`);\n const address =\n baoVariable(env, 'AGENT_ADDR') || baoVariable(env, 'ADDR') || 'https://127.0.0.1:8200';\n const socket = address.startsWith('unix://') ? address.slice('unix://'.length) : undefined;\n const base = socket === undefined ? address.replace(/\\/+$/, '') : 'http://localhost';\n const headers: Record<string, string> = { 'X-Vault-Request': 'true' };\n const namespace = baoVariable(env, 'NAMESPACE');\n if (namespace !== '') headers['X-Vault-Namespace'] = namespace;\n const token = baoVariable(env, 'TOKEN');\n if (token !== '') headers['X-Vault-Token'] = token;\n if (socket !== undefined && !('Bun' in globalThis)) {\n return yield* Effect.fail(refuse(0, 'a unix:// address needs Bun fetch; this is not Bun'));\n }\n\n const client = yield* HttpClient.HttpClient;\n // ⛔ Effect records request headers on the client span, and its default redaction list does\n // not name x-vault-token (effect Headers.ts:753-762). It is added for this call.\n const redacted = yield* Headers.CurrentRedactedNames;\n const defaults = Option.getOrElse(\n yield* Effect.serviceOption(FetchHttpClient.RequestInit),\n () => ({}),\n );\n const request = HttpClientRequest.get(`${base}/v1/${path}`).pipe(\n HttpClientRequest.setHeaders(headers),\n );\n const exchange = client.execute(request).pipe(\n Effect.flatMap((response) =>\n Effect.map(response.text, (text) => ({ status: response.status, text })),\n ),\n // ⚠️ The CLI's default client timeout (api/client.go:324) — a hung agent must not hang a plan.\n Effect.timeout('60 seconds'),\n Effect.provideService(Headers.CurrentRedactedNames, [...redacted, 'x-vault-token']),\n );\n // ⚠️ `| undefined` under exactOptionalPropertyTypes: `socket` is narrowed to a string\n // only on the branch below, so the object literal genuinely carries\n // `unix: string | undefined` and the type must say so. The alternative — building\n // the key conditionally — hides that this request is the unix-socket path.\n const init: RequestInit & { unix?: string | undefined } = { ...defaults, unix: socket };\n const { status, text } = yield* (\n socket === undefined\n ? exchange\n : Effect.provideService(exchange, FetchHttpClient.RequestInit, init)\n ).pipe(\n // ⛔ `.message` only — the error object holds the request, and the request holds the token.\n Effect.mapError((cause) => refuse(0, cause.message)),\n );\n\n let parsed:\n | {\n lease_duration?: unknown;\n data?: { token_id?: unknown; secret?: unknown };\n errors?: unknown;\n }\n | undefined;\n try {\n parsed = JSON.parse(text) as typeof parsed;\n } catch {\n parsed = undefined;\n }\n if (status < 200 || status >= 300) {\n const errors = parsed?.errors;\n const detail = Array.isArray(errors)\n ? errors.map(String).join('; ')\n : 'not an OpenBao error body';\n return yield* Effect.fail(refuse(status, detail || '(no errors given)'));\n }\n const tokenId = parsed?.data?.token_id;\n const secret = parsed?.data?.secret;\n if (typeof tokenId !== 'string' || typeof secret !== 'string') {\n // ⛔ REFUSE RATHER THAN RETURN A HALF CREDENTIAL, and again WITHOUT quoting the body.\n return yield* Effect.fail(\n new Error(\n `${target.mount}/creds/${role} returned no token_id/secret. Either this approle lacks ` +\n `the grant, or the mount is not ${target.mount}. Say which mount and role you needed ` +\n `than falling back to a stored credential -- every stored PVE token is read-only.`,\n ),\n );\n }\n const lease = parsed?.lease_duration;\n const leaseSeconds = typeof lease === 'number' ? lease : 0;\n return { leaseSeconds, secret, tokenId } satisfies PveCredential;\n });\n\n/**\n * The header PVE wants. `PVEAPIToken=<tokenid>=<secret>`.\n *\n * ⚠️ THE SEPARATOR IS `=`, NOT `:`. Proxmox rejects the colon form with a 401 that says only\n * \"authentication failure\", which reads as a bad credential rather than as a malformed header.\n */\nexport const authorization = (credential: PveCredential, target: ApiTarget): string =>\n target.scheme === 'pbs'\n ? `PBSAPIToken=${credential.tokenId}:${credential.secret}`\n : `PVEAPIToken=${credential.tokenId}=${credential.secret}`;\n",
|
|
8
8
|
"/**\n * One credential per (target, role) for the life of a run, instead of one per API call.\n *\n * 🔴 THE MEASUREMENT THAT MADE THIS NECESSARY. A single `alchemy plan` plus `deploy` of the C1\n * stack — 98 resources — left **760 API token entries in `/etc/pve/user.cfg`**, counted on node-b\n * immediately afterwards. `mint()` asked OpenBao for a new credential on EVERY call, and each\n * mint creates a real Proxmox token: read, diff, reconcile and read-back each got their own. They\n * drain as the leases expire (760 down to 712 over five minutes), so nothing was broken — but\n * `/etc/pve` is pmxcfs, a REPLICATED cluster filesystem, and every one of those mints is a write\n * to it that all three nodes must agree on. Two plans in quick succession stack thousands.\n *\n * ⛔ AND ON PBS THE COST IS SECONDS, NOT BYTES. `pbs_grant.go` waits for PBS to honour a freshly\n * granted ACL before handing the credential over — measured at about 4.5s, and unavoidable,\n * because PBS caches its parsed ACL config. Per mint. The ten PBS resources in pbs.ts would pay\n * that ten times over for credentials identical to each other.\n *\n * ★ SO THE FIX IS TO STOP THROWING AWAY A CREDENTIAL THAT IS STILL VALID. This is strictly FEWER\n * credentials in existence at once, not more: the lease TTL is untouched, and a token that would\n * have been minted and abandoned is simply never minted. It is the same reasoning\n * `network-apply.ts` already applies by hand when it threads one lease through `pveWith` for its\n * apply and its task poll.\n *\n * ⚠️ THE CACHE IS PROCESS-SCOPED AND THAT IS THE INTENDED LIFETIME. A plan or a deploy is one\n * process; the next one starts cold. Nothing is written to disk, and a credential never outlives\n * the run that minted it.\n *\n * ⛔ IT IS NOT A PERFORMANCE CACHE, SO IT MUST NEVER SERVE A CREDENTIAL THAT COULD DIE MID-CALL.\n * The provision lease is 300s and a deploy can run longer than that. A credential handed out at\n * t+299 would fail the call it was handed to, and the failure surfaces as a 401 — which `read`\n * folds into \"absent\", which is how a plan comes to say CREATE for an object that exists. That is\n * the exact class of bug this package found three times in one day, so the margin below is\n * deliberately generous rather than tuned.\n *\n * ★ THE MECHANISM IS EFFECT'S OWN `Cache`, AND THE HAND-WRITTEN ONE IT REPLACED WAS WRONG IN\n * EXACTLY THE PLACE A LIBRARY IS NOT. A TTL map alone still minted 89 credentials for 98\n * resources, because Alchemy plans concurrently and every resource missed in the same instant;\n * the in-flight map added to fix that got the first miss right and the failure path wrong. A\n * waiter woken by a FAILED or uncacheable mint claimed the slot without looking again, the\n * waiters overwrote each other's claims, and each one's cleanup deleted somebody else's — the\n * thundering herd back through the error door (found in review 2026-09-14, confirmed by\n * reading). `Cache.get` shares one pending lookup among concurrent callers by design. MEASURED\n * with a probe before this was written: ten concurrent gets on equal-but-distinct key objects ran\n * the lookup once; a zero time-to-live ran it again for the next caller; a failure reached all\n * ten waiters and was not kept.\n */\nimport * as Cache from 'effect/Cache';\nimport * as Duration from 'effect/Duration';\nimport * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport { type ApiTarget, type PveCredential, type PveRole, mint } from './credentials.ts';\n\n/**\n * Re-mint once a lease is inside this much of its end, in seconds.\n *\n * ⚠️ 60s AGAINST A 300s PROVISION LEASE MEANS A CACHED CREDENTIAL IS ONLY EVER HANDED OUT WITH AT\n * LEAST A MINUTE LEFT. The slowest single call in this package is `network-apply`'s reload poll,\n * and that mints its own lease deliberately — see the ⚠️ on `pveWith` there — so nothing using\n * this cache comes near a minute.\n * ⚠️ A MOUNT REPORTING NO `lease_duration` IS NEVER CACHED: `leaseSeconds` is `0` and\n * `timeToLive` answers zero. Not caching is always safe; caching a credential whose lifetime is\n * unknown is not.\n */\nconst REMINT_MARGIN_SECONDS = 60;\n\n/**\n * What a credential is cached under.\n *\n * ⛔ KEYED ON THE OPENBAO MOUNT AND ROLE, NOT ON A MEMBER HOSTNAME. A PVE API token is\n * cluster-wide — `user.cfg` is replicated — so node-b, node-c and node-d share one credential. Keying on\n * `api` would mint three identical tokens when failover rotates members.\n * ★ THE MOUNT CANNOT COLLIDE ACROSS CLUSTERS: `proxmox-c1`, `proxmox-c2` and `pbs-c1` are\n * distinct OpenBao mounts with distinct policies; two estates never share a mount name.\n * ★ A PLAIN OBJECT IS A SAFE KEY BECAUSE EFFECT COMPARES KEYS STRUCTURALLY — two separately built\n * literals with the same fields are one entry, which the probe above measured.\n */\nexport type LeaseKey = {\n readonly mount: string;\n readonly role: PveRole;\n readonly scheme: ApiTarget['scheme'];\n};\n\nconst keyOf = (target: ApiTarget, role: PveRole): LeaseKey => ({\n mount: target.mount,\n role,\n scheme: target.scheme,\n});\n\n/**\n * How long a freshly minted credential may be handed out again.\n *\n * ⛔ ZERO FOR A FAILURE, FOR AN UNKNOWN LIFETIME AND FOR A LEASE INSIDE THE MARGIN. Zero means \"do\n * not keep\": the callers already waiting on THIS mint still receive its result, and the next\n * caller mints again. Keeping a failure would turn one refused mint into a refused run.\n * ⚠️ EXPORTED FOR THE TEST. The margin is the whole safety property, so it is pinned from both\n * sides of its edge without waiting four minutes of wall clock.\n */\nexport const timeToLive = (exit: Exit.Exit<PveCredential, unknown>): Duration.Duration =>\n Exit.isSuccess(exit) && exit.value.leaseSeconds > REMINT_MARGIN_SECONDS\n ? Duration.seconds(exit.value.leaseSeconds - REMINT_MARGIN_SECONDS)\n : Duration.zero;\n\n/**\n * A lease cache over any mint.\n *\n * ⚠️ ONE BEHAVIOUR CHANGED WITH THE MOVE TO `Cache`, DELIBERATELY. Callers waiting on a mint that\n * FAILS now receive that failure, where the hand-written version had each of them mint again.\n * They asked in the same instant for the same credential, and a plan with one failed resource\n * fails either way — so the change is in how many errors the output lists, not in the outcome,\n * and a sealed vault is asked once rather than ninety-eight times.\n * ★ `requireServicesAt: 'lookup'` IS WHAT LETS IT BE BUILT ONCE, AT IMPORT. The mint needs an\n * `HttpClient`; that requirement moves to each `get`, where every caller already has one.\n * ⚠️ EXPORTED FOR THE TEST, which hands it a counting mint instead of calling OpenBao.\n */\nexport const makeLeases = <E, R>(mintFor: (key: LeaseKey) => Effect.Effect<PveCredential, E, R>) =>\n Cache.makeWith(mintFor, { capacity: 64, requireServicesAt: 'lookup', timeToLive });\n\nconst targetForKey = (key: LeaseKey): ApiTarget =>\n key.scheme === 'pbs'\n ? { api: 'https://pbs.invalid/api2/json', mount: key.mount, scheme: 'pbs' }\n : { members: [], mount: key.mount, scheme: 'pve' };\n\nconst leases = Effect.runSync(makeLeases((key) => mint(targetForKey(key), key.role)));\n\n/**\n * A credential for `role` on `target`: a still-valid lease when one is kept, one shared mint when\n * not.\n *\n * ★ client.ts is the one place that decides to use the cache. `mint` stays exported and unchanged\n * for the caller that reasons about identity rather than cost — network-apply.ts.\n */\nexport const leased = (target: ApiTarget, role: PveRole) => Cache.get(leases, keyOf(target, role));\n",
|
|
9
9
|
"/**\n * C1 cluster members: ordered failover, last-good preference, transport classification.\n *\n * ★ ANY MEMBER'S :8006 API MANAGES THE WHOLE CLUSTER — PVE proxies node paths itself — so the\n * provider reaches the cluster through whichever member is up, not through one pinned node.\n *\n * ⛔ NEVER cluster-c1.example.com: it resolves to 192.0.2.250, which nothing serves.\n */\nimport * as Effect from 'effect/Effect';\nimport * as Result from 'effect/Result';\nimport * as HttpClient from 'effect/unstable/http/HttpClient';\nimport * as HttpClientError from 'effect/unstable/http/HttpClientError';\nimport type * as HttpClientRequest from 'effect/unstable/http/HttpClientRequest';\nimport type * as HttpClientResponse from 'effect/unstable/http/HttpClientResponse';\nimport type { PveTarget } from './credentials.ts';\n\nexport const PVE_API_PORT = 8006;\n\n/** Strict TLS, always :8006 — each node's certificate carries its mgmt name as a SAN. */\nexport const pveApiBase = (member: string) => `https://${member}:${String(PVE_API_PORT)}/api2/json`;\n\n/** Process-scoped last member that answered, keyed by OpenBao mount (= one PVE cluster). */\nconst lastGoodByMount = new Map<string, string>();\n\n/** ⚠️ TESTS ONLY — resets sticky member choice between cases. */\nexport const resetLastGoodForTest = () => lastGoodByMount.clear();\n\nexport const noteGoodMember = (target: PveTarget, member: string) => {\n lastGoodByMount.set(target.mount, member);\n};\n\n/** Last-good first, then the rest in configuration order. */\nexport const orderedMembers = (target: PveTarget): readonly string[] => {\n const preferred = lastGoodByMount.get(target.mount);\n if (preferred === undefined || !target.members.includes(preferred)) return target.members;\n return [preferred, ...target.members.filter((member) => member !== preferred)];\n};\n\nconst errorCode = (cause: unknown): string | undefined => {\n if (cause !== null && typeof cause === 'object') {\n if ('code' in cause && typeof cause.code === 'string') return cause.code;\n if ('cause' in cause) return errorCode((cause as { cause: unknown }).cause);\n }\n return undefined;\n};\n\nconst isTimeoutCause = (cause: unknown): boolean => {\n const code = errorCode(cause);\n if (code === 'AbortError' || code === 'ETIMEDOUT' || code === 'UND_ERR_CONNECT_TIMEOUT') {\n return true;\n }\n return cause instanceof Error && /timed out|timeout/i.test(cause.message);\n};\n\n/**\n * Error codes that PROVE the request never left this machine.\n *\n * ★ MEASURED 2026-09-14 through Effect's FetchHttpClient on Bun 1.4. The codes are Bun's, not\n * Node's, which made the first draft's Node-only list dead code for the commonest case:\n * connection refused cause.code = \"ConnectionRefused\" (Node says ECONNREFUSED)\n * name does not resolve cause.code = \"ENOTFOUND\"\n * wrong certificate name cause.code = \"ERR_TLS_CERT_ALTNAME_INVALID\"\n * no answer after connect Effect's TimeoutError — not an HttpClientError at all\n * ⛔ CODES ONLY, NEVER MESSAGE TEXT. The first draft also matched /TLS|SSL|certificate/ in the\n * message, and a connection that drops mid-response can mention TLS too — which would have sent a\n * write that had already landed to a second node.\n * ⚠️ A CERTIFICATE failure is pre-send: the handshake has to finish before a byte of the request is\n * written. A generic ERR_SSL_* is deliberately absent, because it can happen mid-stream.\n */\nconst PRE_SEND_CODES = new Set(['ConnectionRefused', 'ECONNREFUSED', 'ENOTFOUND', 'EAI_AGAIN']);\n\nconst isCertificateCode = (code: string) =>\n /^(ERR_TLS_CERT_|CERT_|UNABLE_TO_VERIFY_|DEPTH_ZERO_SELF_SIGNED|SELF_SIGNED_CERT)/.test(code);\n\nconst isPreSendCause = (cause: unknown): boolean => {\n const code = errorCode(cause);\n return code !== undefined && (PRE_SEND_CODES.has(code) || isCertificateCode(code));\n};\n\n/**\n * Effect HttpClientError.reason._tag — see effect HttpClientError.ts RequestError vs ResponseError.\n * ★ TransportError | InvalidUrlError | EncodeError occur before a response exists; StatusCodeError\n * and kin include a response. client.execute does NOT filter status, so HTTP answers are never\n * TransportError — only the transport layer is.\n */\nexport const isTransportFailure = (cause: unknown): boolean => {\n if (!HttpClientError.isHttpClientError(cause)) return false;\n const tag = cause.reason._tag;\n return tag === 'TransportError' || tag === 'InvalidUrlError' || tag === 'EncodeError';\n};\n\n/** Pre-send only: connection refused, DNS, a certificate refused — never a timeout after connect. */\nexport const isPreSendTransport = (cause: unknown): boolean => {\n if (!HttpClientError.isHttpClientError(cause)) return false;\n const { reason } = cause;\n if (reason._tag === 'InvalidUrlError' || reason._tag === 'EncodeError') return true;\n if (reason._tag === 'TransportError') {\n if (isTimeoutCause(reason.cause)) return false;\n return isPreSendCause(reason.cause);\n }\n return false;\n};\n\nexport const describeTransport = (cause: unknown): string => {\n if (HttpClientError.isHttpClientError(cause)) return cause.message;\n return String(cause);\n};\n\nexport type MemberAttempt = { readonly member: string; readonly why: string };\n\nexport type PveMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\n/**\n * Run one request against cluster members with failover.\n *\n * ⛔ A POST/PUT/DELETE WHOSE REQUEST MAY HAVE REACHED A MEMBER MUST NOT BE SENT AGAIN TO ANOTHER.\n * Only fail over a write on a provably pre-send transport failure — connection refused, DNS or a\n * refused certificate. A timeout after sending is NOT pre-send: fail it, never repeat it. An HTTP\n * status of any kind, including 5xx and PVE's 596, is the answer from a live member — retrying\n * elsewhere can repeat a write.\n *\n * ★ Reads fail over on any transport failure, including timeout — no side effect to duplicate.\n * MEASURED 2026-09-14: full C1 plans stayed all-noop with the first member refused (127.0.0.1)\n * and with it unresolvable (n9.invalid).\n */\nexport const executeOnCluster = (\n target: PveTarget,\n method: PveMethod,\n path: string,\n build: (apiBase: string) => HttpClientRequest.HttpClientRequest,\n): Effect.Effect<\n HttpClientResponse.HttpClientResponse,\n {\n readonly tag: 'cluster';\n readonly method: PveMethod;\n readonly path: string;\n readonly attempts: readonly MemberAttempt[];\n },\n HttpClient.HttpClient\n> =>\n Effect.gen(function* () {\n /**\n * ★ `HttpClient`, NOT RAW `fetch`, BECAUSE THAT IS WHAT ALCHEMY'S OWN PROVIDERS USE. Its\n * Hetzner provider builds on `FetchHttpClient.layer` and the `HttpClient` service\n * (src/Hetzner/Providers.ts), and there is no bare `fetch` anywhere in its Docker,\n * Kubernetes or GitHub providers. Going through the service rather than the global buys\n * the things a provider actually needs: a typed error channel instead of a thrown promise,\n * interruption when a plan is cancelled, whatever tracing the runtime has installed, and a\n * client that can be swapped for a stub in a test without monkey-patching a global.\n *\n * ⚠️ THE REQUIREMENT IS REAL AND IT PROPAGATES. Every operation carries `HttpClient`, so every\n * resource's Requirements type names it (PveRequirements in resource.ts) and the stack\n * provides `FetchHttpClient.layer`. That is the cost of the above, paid once.\n */\n const client = yield* HttpClient.HttpClient;\n const isWrite = method !== 'GET';\n const attempts: MemberAttempt[] = [];\n for (const member of orderedMembers(target)) {\n const outcome = yield* client.execute(build(pveApiBase(member))).pipe(Effect.result);\n if (Result.isSuccess(outcome)) {\n noteGoodMember(target, member);\n return outcome.success;\n }\n const why = describeTransport(outcome.failure);\n attempts.push({ member, why });\n const mayRetry = isWrite\n ? isPreSendTransport(outcome.failure)\n : isTransportFailure(outcome.failure);\n if (!mayRetry) {\n return yield* Effect.fail({ attempts, method, path, tag: 'cluster' as const });\n }\n }\n return yield* Effect.fail({ attempts, method, path, tag: 'cluster' as const });\n });\n",
|
|
10
|
-
"/**\n * The four operations every PVE object shares, and the five handlers that wire them to Alchemy.\n *\n * ★ THE SHAPE THEY READ IS `PveSpec`, IN resource-spec.ts, WITH THE ARGUMENT FOR IT. This file was\n * one file until 2026-09-22, when the vendor-constraint guard took it past the 250-line cap; the\n * split is by what a reader is doing — declaring an object, or changing how every object is\n * written — and the types are re-exported below so no importer moved.\n *\n * ⚠️ EVERY PVE ANSWER IS WRAPPED IN `{\"data\": ...}` AND A FAILED CALL CAN STILL BE HTTP 200 with\n * `{\"data\": null}`. That is why `reconcile` below READS BACK and refuses when the object is still\n * absent, rather than trusting a status code — see the ⛔ in that function.\n */\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Effect from 'effect/Effect';\nimport { pve } from './client.ts';\nimport { guardForm } from './constraint-guard.ts';\nimport type { PveSpec, WithApiTarget } from './resource-spec.ts';\nimport { formToSend } from './update-guard.ts';\n\nexport type {\n PveRequirements,\n PveSpec,\n WithApiTarget,\n WithPbsTarget,\n WithTarget,\n} from './resource-spec.ts';\n\nexport const pveOperations = <Props extends WithApiTarget, Attributes>(\n spec: PveSpec<Props, Attributes>,\n) => {\n /**\n * Both forms against their own endpoint's table.\n *\n * ⚠️ PRESENCE IS CHECKED ON CREATE ONLY. An update form is deliberately partial — `formToSend`\n * sends what changed — so requiring the vendor's required parameters there would refuse every\n * ordinary edit. Value rules (length, range, enum, pattern) apply to both.\n */\n const guardForms = (props: Props) =>\n Effect.gen(function* () {\n /**\n * ⛔ THE FORM IS BUILT ONLY WHEN THERE IS A TABLE TO CHECK IT AGAINST. Calling\n * `spec.createForm` unconditionally would run every family's form builder on every diff,\n * including the fifteen that declare no `endpoint` — work nobody asked for, and a new way\n * for a builder that throws on an update-only path to break a plan it never touched.\n */\n const create = spec.endpoint?.create;\n if (create !== undefined) yield* guardForm(create, spec.createForm(props), true);\n const update = spec.endpoint?.update;\n if (update === undefined) return;\n const form = spec.updateForm?.(props);\n if (form !== undefined) yield* guardForm(update, form, false);\n });\n\n /** The live object, or undefined. ⚠️ A 404 and `{\"data\": null}` are ANSWERS, not failures. */\n const read = (props: Props) =>\n pve<Record<string, unknown>>(\n props.target,\n spec.readRole ?? 'read',\n 'GET',\n spec.path(props),\n ).pipe(\n Effect.map((data) => (data == null ? undefined : spec.attributes(data, props))),\n Effect.orElseSucceed(() => undefined),\n );\n\n return {\n read,\n\n // ⚠️ `Input<Props>`, NOT `Props`. At plan time a prop can still be an unresolved Output or\n // Config; `isResolved` is the narrowing, so the parameter has to admit the wider type or\n // every caller casts — and a cast here would be a lie about what plan actually has.\n diff: (news: Input<Props>, output: Attributes | undefined) =>\n Effect.gen(function* () {\n // ⚠️ A prop can still be an unresolved Output at plan time. Alchemy's own providers guard\n // with isResolved and skip rather than guess; comparing a placeholder to a live value\n // reports an update nobody asked for.\n if (!isResolved(news)) return undefined;\n /**\n * ⛔ THE VENDOR CHECK RUNS BEFORE THE `output === undefined` RETURN, AND THE ORDER IS THE\n * WHOLE FIX. `output === undefined` IS the resource Alchemy has no state for — the\n * create. Checking after it would leave exactly the case that failed on 2026-09-22\n * unchecked, and the refusal would arrive from the server, half a deploy in.\n */\n yield* guardForms(news);\n if (output === undefined) return undefined;\n const live = yield* read(news);\n // ⚠️ `update`, NOT `create` — Alchemy's Diff admits only noop/update/replace. An object\n // Alchemy has state for but the cluster does not is drift, and reconcile repairs it.\n if (live === undefined) return { action: 'update' } as const;\n if (spec.matches(live, news)) return { action: 'noop' } as const;\n // An object with no update path cannot be edited in place; say replace and mean it.\n return spec.updateForm === undefined\n ? ({ action: 'replace' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: (news: Props) =>\n Effect.gen(function* () {\n // ⚠️ CHECKED AGAIN HERE, AND NOT BECAUSE `diff` IS UNTRUSTED. `reconcile` also runs for an\n // ADOPTED row, whose diff answer Alchemy discards (verify/fake-engine.ts records it), and\n // a family that writes its own reconcile bypasses the one above entirely.\n yield* guardForms(news);\n const live = yield* read(news);\n if (live === undefined) {\n yield* pve(\n news.target,\n 'provision',\n 'POST',\n spec.collection(news),\n spec.createForm(news),\n );\n } else if (spec.updateForm !== undefined) {\n /**\n * ⛔ ADOPTING AN OBJECT THAT ALREADY MATCHES MUST NOT WRITE TO IT, AND WITHOUT THE\n * `matches` GUARD IT DID. Alchemy's `adopted` action is NOT a read: Apply.ts routes it\n * down the same branch as `update` and calls `reconcile` (Apply.ts:980 and :1045). So\n * the first time a declaration names an object Alchemy has no state for — which is\n * exactly what adopting the live estate means — reconcile runs, finds it present, and\n * fell through to this PUT.\n *\n * For most families that was a pointless write-back of identical values. For\n * `Proxmox.NodeNetwork` it is worse than pointless: a PUT under `/nodes/{node}/network`\n * STAGES a change into `/etc/network/interfaces.new`, so merely adopting the interface\n * a node already has would leave that node with a pending network change waiting for an\n * apply — on a cluster whose Ceph traffic rides vmbr1.42. Adoption has to be free.\n *\n * ★ SO THE CONDITION IS THE SAME PREDICATE `diff` USES. If `matches` is true the object\n * already says what the declaration says, and there is nothing to write — by\n * definition, since every field this provider manages is a field `matches` compares.\n * A field deliberately left OUT of `matches` is one this resource does not manage, so\n * its drift is not this provider's to repair.\n *\n * ★ THE PREDICATE LIVES IN update-guard.ts NOW, with the ⚠️ on why an empty form is not a\n * write either, because CephPool and PbsDatastore write their own reconcile and need\n * the same one — CephPool did not have it (see that file).\n */\n const form = formToSend(spec.matches, live, news, spec.updateForm(news));\n if (form !== undefined) {\n yield* pve(news.target, 'provision', 'PUT', spec.path(news), form);\n }\n }\n const after = yield* read(news);\n if (after === undefined) {\n /**\n * ⛔ REFUSE RATHER THAN RETURN THE PROPS AS THOUGH THEY LANDED. PVE answers 200 with\n * `{\"data\": null}` on several endpoints, so \"no error\" is not evidence of a write. A\n * provider that returned its own inputs here would record state for an object that does\n * not exist, and the next plan would read `noop` over the gap.\n */\n return yield* Effect.die(\n new Error(\n `${spec.path(news)}: the write returned no error but the object is still absent. ` +\n 'PVE wraps every answer in {\"data\":...} and can report success on a call that did ' +\n 'nothing -- read back rather than trusting the status code.',\n ),\n );\n }\n return after;\n }),\n\n /**\n * ⚠️ PVE REFUSES TO DELETE THINGS THAT ARE STILL IN USE — a pool holding guests, a storage with\n * volumes. That refusal is the cluster declining to orphan something because a line left a\n * file, so it is surfaced as-is rather than retried with a force flag.\n */\n destroy: (olds: Props) => pve(olds.target, 'provision', 'DELETE', spec.path(olds)),\n };\n};\n\n/**\n * The five provider handlers for a spec'd PVE object, wired once.\n *\n * ⛔ THIS BLOCK WAS WRITTEN TEN TIMES BEFORE IT WAS EXTRACTED, and every copy was character for\n * character identical apart from its comments: `list` answering empty, and four handlers whose\n * entire body was `yield* ops.<same name>(<same argument>)`. Ten copies of a delegation is ten\n * chances to delegate to the wrong one — `ops.read(olds)` inside `reconcile` typechecks, returns\n * a plausible value, and turns every deploy into a no-op that reports success.\n *\n * ★ IT TAKES THE SPEC, NOT THE OPERATIONS, so a resource declares its four spec functions and\n * stops. `pveOperations` stays exported for an object that genuinely needs a handler of its own;\n * nothing in this package does yet, and the day one does it should be visibly different from the\n * ten that are not.\n *\n * ⛔ IT STOPS AT THE HANDLERS AND DOES NOT RETURN THE LAYER, AND THAT IS NOT AN OVERSIGHT — it was\n * written the other way first and reverted. Wrapping `Provider.effect(cls, …)` here means naming\n * the resource class generically, and Alchemy's `Props<R>` and `R[\"Attributes\"]` are not the\n * plain `Props` and `Attributes` this spec is written against: `Attributes` comes back as\n * `Attributes & AttrOutput<Attributes[\"Attributes\"]>`. Against a free type variable TypeScript\n * cannot prove those line up — \"could be instantiated with a different subtype\" — so the only\n * way to compile a Layer-returning version is a cast, and a cast is a LIE ABOUT WHETHER THE\n * HANDLERS MATCH THE RESOURCE, which is the one thing the call site exists to check. Leaving\n * `Cls.Provider.of(...)` in each file keeps that check where the types are concrete and real.\n * One line per resource is the price of it, and it is worth paying.\n *\n * ⚠️ `list` IS OPTIONAL IN `ProviderServiceInput` AND IS PASSED ANYWAY. An empty list is a claim —\n * this provider adopts nothing — and it is made here for every PVE object at once, because the\n * reasoning is identical for all of them: every PVE index endpoint answers with the whole\n * cluster's objects, PVE's own built-ins included. `GET /pools` returns pools a human made years\n * ago; `GET /access/roles` returns `Administrator`. Returning any of them would invite Alchemy to\n * adopt an object it never created, and therefore one day to narrow or delete it. Adoption stays\n * an explicit act. A resource with a reason to differ passes its own `list` and says why.\n */\nexport const pveHandlers = <Props extends WithApiTarget, Attributes extends object>(\n spec: PveSpec<Props, Attributes>,\n) => {\n const ops = pveOperations(spec);\n return {\n list: () => Effect.succeed([]),\n read: ({ olds }: { olds: Props }) => ops.read(olds),\n diff: ({ news, output }: { news: Input<Props>; output: Attributes | undefined }) =>\n ops.diff(news, output),\n reconcile: ({ news }: { news: Props }) => ops.reconcile(news),\n delete: ({ olds }: { olds: Props }) => ops.destroy(olds),\n };\n};\n",
|
|
10
|
+
"/**\n * The four operations every PVE object shares, and the five handlers that wire them to Alchemy.\n *\n * ★ THE SHAPE THEY READ IS `PveSpec`, IN resource-spec.ts, WITH THE ARGUMENT FOR IT. This file was\n * one file until 2026-09-22, when the vendor-constraint guard took it past the 250-line cap; the\n * split is by what a reader is doing — declaring an object, or changing how every object is\n * written — and the types are re-exported below so no importer moved.\n *\n * ⚠️ EVERY PVE ANSWER IS WRAPPED IN `{\"data\": ...}` AND A FAILED CALL CAN STILL BE HTTP 200 with\n * `{\"data\": null}`. That is why `reconcile` below READS BACK and refuses when the object is still\n * absent, rather than trusting a status code — see the ⛔ in that function.\n */\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Effect from 'effect/Effect';\nimport { pve } from './client.ts';\nimport { specGuards } from './resource-guard.ts';\nimport type { PveSpec, WithApiTarget } from './resource-spec.ts';\nimport { formToSend } from './update-guard.ts';\n\nexport type {\n PveRequirements,\n PveSpec,\n WithApiTarget,\n WithPbsTarget,\n WithTarget,\n} from './resource-spec.ts';\n\nexport const pveOperations = <Props extends WithApiTarget, Attributes>(\n spec: PveSpec<Props, Attributes>,\n) => {\n const { guardCreate, guardUpdate } = specGuards(spec);\n\n /** The live object, or undefined. ⚠️ A 404 and `{\"data\": null}` are ANSWERS, not failures. */\n const read = (props: Props) =>\n pve<Record<string, unknown>>(\n props.target,\n spec.readRole ?? 'read',\n 'GET',\n spec.path(props),\n ).pipe(\n Effect.map((data) => (data == null ? undefined : spec.attributes(data, props))),\n Effect.orElseSucceed(() => undefined),\n );\n\n return {\n /**\n * ★ EXPORTED SO A FAMILY THAT WRITES ITS OWN `reconcile` REACHES THE SAME CHECK BY THE SAME\n * NAME. CephPool, CephFs and ZfsPool all bypass the reconcile below — forked workers, a\n * settle loop, a destructive create — and without these two they would be checked at plan\n * time and not at apply time, which is exactly the gap an ADOPTED row falls through.\n */\n guardCreate,\n guardUpdate,\n\n read,\n\n // ⚠️ `Input<Props>`, NOT `Props`. At plan time a prop can still be an unresolved Output or\n // Config; `isResolved` is the narrowing, so the parameter has to admit the wider type or\n // every caller casts — and a cast here would be a lie about what plan actually has.\n diff: (news: Input<Props>, output: Attributes | undefined) =>\n Effect.gen(function* () {\n // ⚠️ A prop can still be an unresolved Output at plan time. Alchemy's own providers guard\n // with isResolved and skip rather than guess; comparing a placeholder to a live value\n // reports an update nobody asked for.\n if (!isResolved(news)) return undefined;\n /**\n * ⛔ THE VENDOR CHECK RUNS BEFORE THE `output === undefined` RETURN, AND THE ORDER IS THE\n * WHOLE FIX. `output === undefined` IS the resource Alchemy has no state for — the\n * create. Checking after it would leave exactly the case that failed on 2026-09-22\n * unchecked, and the refusal would arrive from the server, half a deploy in.\n */\n yield* guardCreate(news, output === undefined);\n yield* guardUpdate(news);\n if (output === undefined) return undefined;\n const live = yield* read(news);\n // ⚠️ `update`, NOT `create` — Alchemy's Diff admits only noop/update/replace. An object\n // Alchemy has state for but the cluster does not is drift, and reconcile repairs it.\n if (live === undefined) {\n /**\n * ⛔ DRIFT REACHES `reconcile`'s POST, so the create form's required parameters ARE\n * about to go on the wire after all. Saying so here rather than leaving it to\n * reconcile keeps the refusal in `plan`, where an operator is reading.\n */\n yield* guardCreate(news, true);\n return { action: 'update' } as const;\n }\n if (spec.matches(live, news)) return { action: 'noop' } as const;\n // An object with no update path cannot be edited in place; say replace and mean it.\n return spec.updateForm === undefined\n ? ({ action: 'replace' } as const)\n : ({ action: 'update' } as const);\n }),\n\n reconcile: (news: Props) =>\n Effect.gen(function* () {\n const live = yield* read(news);\n // ⚠️ CHECKED AGAIN HERE, AND NOT BECAUSE `diff` IS UNTRUSTED. `reconcile` also runs for an\n // ADOPTED row, whose diff answer Alchemy discards (verify/fake-engine.ts records it), and\n // a family that writes its own reconcile bypasses the one above entirely.\n // ⛔ AFTER THE READ, BECAUSE THE READ IS WHAT SAYS WHICH REQUEST IS ABOUT TO BE MADE. A GET\n // changes nothing, so nothing has been written by the time a violation refuses the plan.\n yield* guardCreate(news, live === undefined);\n yield* guardUpdate(news);\n if (live === undefined) {\n yield* pve(\n news.target,\n 'provision',\n 'POST',\n spec.collection(news),\n spec.createForm(news),\n );\n } else if (spec.updateForm !== undefined) {\n /**\n * ⛔ ADOPTING AN OBJECT THAT ALREADY MATCHES MUST NOT WRITE TO IT, AND WITHOUT THE\n * `matches` GUARD IT DID. Alchemy's `adopted` action is NOT a read: Apply.ts routes it\n * down the same branch as `update` and calls `reconcile` (Apply.ts:980 and :1045). So\n * the first time a declaration names an object Alchemy has no state for — which is\n * exactly what adopting the live estate means — reconcile runs, finds it present, and\n * fell through to this PUT.\n *\n * For most families that was a pointless write-back of identical values. For\n * `Proxmox.NodeNetwork` it is worse than pointless: a PUT under `/nodes/{node}/network`\n * STAGES a change into `/etc/network/interfaces.new`, so merely adopting the interface\n * a node already has would leave that node with a pending network change waiting for an\n * apply — on a cluster whose Ceph traffic rides vmbr1.42. Adoption has to be free.\n *\n * ★ SO THE CONDITION IS THE SAME PREDICATE `diff` USES. If `matches` is true the object\n * already says what the declaration says, and there is nothing to write — by\n * definition, since every field this provider manages is a field `matches` compares.\n * A field deliberately left OUT of `matches` is one this resource does not manage, so\n * its drift is not this provider's to repair.\n *\n * ★ THE PREDICATE LIVES IN update-guard.ts NOW, with the ⚠️ on why an empty form is not a\n * write either, because CephPool and PbsDatastore write their own reconcile and need\n * the same one — CephPool did not have it (see that file).\n */\n const form = formToSend(spec.matches, live, news, spec.updateForm(news));\n if (form !== undefined) {\n yield* pve(news.target, 'provision', 'PUT', spec.path(news), form);\n }\n }\n const after = yield* read(news);\n if (after === undefined) {\n /**\n * ⛔ REFUSE RATHER THAN RETURN THE PROPS AS THOUGH THEY LANDED. PVE answers 200 with\n * `{\"data\": null}` on several endpoints, so \"no error\" is not evidence of a write. A\n * provider that returned its own inputs here would record state for an object that does\n * not exist, and the next plan would read `noop` over the gap.\n */\n return yield* Effect.die(\n new Error(\n `${spec.path(news)}: the write returned no error but the object is still absent. ` +\n 'PVE wraps every answer in {\"data\":...} and can report success on a call that did ' +\n 'nothing -- read back rather than trusting the status code.',\n ),\n );\n }\n return after;\n }),\n\n /**\n * ⚠️ PVE REFUSES TO DELETE THINGS THAT ARE STILL IN USE — a pool holding guests, a storage with\n * volumes. That refusal is the cluster declining to orphan something because a line left a\n * file, so it is surfaced as-is rather than retried with a force flag.\n */\n destroy: (olds: Props) => pve(olds.target, 'provision', 'DELETE', spec.path(olds)),\n };\n};\n\n/**\n * The five provider handlers for a spec'd PVE object, wired once.\n *\n * ⛔ THIS BLOCK WAS WRITTEN TEN TIMES BEFORE IT WAS EXTRACTED, and every copy was character for\n * character identical apart from its comments: `list` answering empty, and four handlers whose\n * entire body was `yield* ops.<same name>(<same argument>)`. Ten copies of a delegation is ten\n * chances to delegate to the wrong one — `ops.read(olds)` inside `reconcile` typechecks, returns\n * a plausible value, and turns every deploy into a no-op that reports success.\n *\n * ★ IT TAKES THE SPEC, NOT THE OPERATIONS, so a resource declares its four spec functions and\n * stops. `pveOperations` stays exported for an object that genuinely needs a handler of its own;\n * nothing in this package does yet, and the day one does it should be visibly different from the\n * ten that are not.\n *\n * ⛔ IT STOPS AT THE HANDLERS AND DOES NOT RETURN THE LAYER, AND THAT IS NOT AN OVERSIGHT — it was\n * written the other way first and reverted. Wrapping `Provider.effect(cls, …)` here means naming\n * the resource class generically, and Alchemy's `Props<R>` and `R[\"Attributes\"]` are not the\n * plain `Props` and `Attributes` this spec is written against: `Attributes` comes back as\n * `Attributes & AttrOutput<Attributes[\"Attributes\"]>`. Against a free type variable TypeScript\n * cannot prove those line up — \"could be instantiated with a different subtype\" — so the only\n * way to compile a Layer-returning version is a cast, and a cast is a LIE ABOUT WHETHER THE\n * HANDLERS MATCH THE RESOURCE, which is the one thing the call site exists to check. Leaving\n * `Cls.Provider.of(...)` in each file keeps that check where the types are concrete and real.\n * One line per resource is the price of it, and it is worth paying.\n *\n * ⚠️ `list` IS OPTIONAL IN `ProviderServiceInput` AND IS PASSED ANYWAY. An empty list is a claim —\n * this provider adopts nothing — and it is made here for every PVE object at once, because the\n * reasoning is identical for all of them: every PVE index endpoint answers with the whole\n * cluster's objects, PVE's own built-ins included. `GET /pools` returns pools a human made years\n * ago; `GET /access/roles` returns `Administrator`. Returning any of them would invite Alchemy to\n * adopt an object it never created, and therefore one day to narrow or delete it. Adoption stays\n * an explicit act. A resource with a reason to differ passes its own `list` and says why.\n */\nexport const pveHandlers = <Props extends WithApiTarget, Attributes extends object>(\n spec: PveSpec<Props, Attributes>,\n) => {\n const ops = pveOperations(spec);\n return {\n list: () => Effect.succeed([]),\n read: ({ olds }: { olds: Props }) => ops.read(olds),\n diff: ({ news, output }: { news: Input<Props>; output: Attributes | undefined }) =>\n ops.diff(news, output),\n reconcile: ({ news }: { news: Props }) => ops.reconcile(news),\n delete: ({ olds }: { olds: Props }) => ops.destroy(olds),\n };\n};\n",
|
|
11
|
+
"/**\n * A spec's two forms, checked against the vendor's own tables before either goes on the wire.\n *\n * ★ A FILE OF ITS OWN BECAUSE resource.ts REACHED THE 250-LINE CAP, and the seam is the same one\n * resource-spec.ts cuts on: that file says what a PVE object DECLARES, resource.ts says how one\n * is read and written, and this one says what makes a declaration legal. `constraint-guard.ts`\n * is the layer below — it owns the lookup and the refusal; this owns which form to check when.\n */\nimport * as Effect from 'effect/Effect';\nimport { guardForm } from './constraint-guard.ts';\nimport type { PveSpec, WithApiTarget } from './resource-spec.ts';\n\nexport const specGuards = <Props extends WithApiTarget, Attributes>(\n spec: PveSpec<Props, Attributes>,\n) => {\n /** ⚠️ A function for the two families whose endpoint is a prop — see `PveSpec['endpoint']`. */\n const endpointsOf = (props: Props) =>\n typeof spec.endpoint === 'function' ? spec.endpoint(props) : spec.endpoint;\n\n /**\n * The create form against its own endpoint's table.\n *\n * ⛔ `presence` MEANS \"A CREATE IS REALLY ABOUT TO HAPPEN\", NOT \"THIS IS THE CREATE FORM\", AND\n * THE DIFFERENCE IS A WHOLE FAMILY. `Proxmox.NotificationTarget` cannot send gotify's `token`\n * or smtp's `password` — they are write-only secrets and props are persisted to the state\n * store unencrypted (notification-target.ts's third ⛔) — so PVE's `token: required` is\n * permanently unsatisfiable here. The documented way to run one is to create it out of band\n * with its secret and then DECLARE it, which is an adopt and then updates forever after.\n * Requiring the create form's parameters on that path would refuse a deploy that has worked\n * all along, which is the one failure worse than the 400 this feature removes.\n * ⚠️ VALUE RULES STILL RUN ON EVERY PLAN, presence or not: a 129-character comment is wrong\n * whether it is being created or edited, and that is the case this whole feature exists for.\n *\n * ⛔ THE FORM IS BUILT ONLY WHEN THERE IS A TABLE TO CHECK IT AGAINST. Calling `spec.createForm`\n * unconditionally would run every family's form builder on every diff — work nobody asked\n * for, and a new way for a builder that throws on an update-only path to break a plan it\n * never touched.\n */\n const guardCreate = (props: Props, presence: boolean): Effect.Effect<void> =>\n Effect.suspend(() => {\n const create = endpointsOf(props)?.create;\n return create === undefined\n ? Effect.void\n : guardForm(create, spec.createForm(props), presence);\n });\n\n /**\n * ⛔ NEVER WITH PRESENCE. An update form is deliberately PARTIAL — `formToSend` sends the fields\n * that changed and nothing else — so requiring the vendor's required parameters here would\n * refuse every ordinary edit. Value rules (length, range, enum, pattern) apply to both forms.\n */\n const guardUpdate = (props: Props): Effect.Effect<void> =>\n Effect.suspend(() => {\n const update = endpointsOf(props)?.update;\n if (update === undefined) return Effect.void;\n const form = spec.updateForm?.(props);\n return form === undefined ? Effect.void : guardForm(update, form, false);\n });\n\n return { guardCreate, guardUpdate };\n};\n",
|
|
11
12
|
"/**\n * The generated tables, looked up by endpoint, and the refusal that stops a plan.\n *\n * ★ ONE FILE BETWEEN THE PROVIDERS AND THE GENERATED DATA, so `resource.ts` gains three lines\n * rather than an import of two generated modules and a merge, and so a family that writes its\n * own handlers (CephPool, PbsDatastore) reaches the same check by the same name.\n *\n * ⛔ AN UNKNOWN KEY IS A DEFECT, NOT A REASON TO SKIP THE CHECK. Silently passing would put us back\n * where this started: a declaration nothing checked, and a plan that says so in no way at all.\n * The generator resolves every key in its source scan against the vendor schema, so a key that\n * is absent here means the tables were not regenerated — which the message says.\n */\nimport * as Effect from 'effect/Effect';\nimport type { PveForm } from './client.ts';\nimport { type EndpointConstraints, type EndpointKey, refusal, violations } from './constraints.ts';\nimport { PROXMOX_CONSTRAINTS } from './generated/constraints/index.ts';\n\nexport { PROXMOX_CONSTRAINTS };\n\nexport const constraintsFor = (key: EndpointKey): EndpointConstraints => {\n const table = PROXMOX_CONSTRAINTS[key];\n if (table === undefined) {\n throw new Error(\n `no vendor constraint table for '${key}'. It is named in this package but not generated: ` +\n 'run `bun codegen/constraints.ts` (see codegen/README.md).',\n );\n }\n return table;\n};\n\n/** Every vendor rule this form breaks. Pure — a test calls it with a literal form. */\nexport const formViolations = (\n key: EndpointKey,\n form: PveForm,\n presence: boolean,\n): readonly string[] => violations(constraintsFor(key), form, { presence });\n\n/**\n * ⛔ `Effect.die`, NOT `Effect.fail`. A declaration the vendor would reject is a DEFECT in the\n * stack file, not a recoverable condition: there is no retry and no fallback that makes a\n * 129-character comment legal. The same call that refuses a write-back with no read-back\n * (resource.ts) dies for the same reason.\n */\nexport const guardForm = (\n key: EndpointKey | undefined,\n form: PveForm,\n presence: boolean,\n): Effect.Effect<void> =>\n Effect.suspend(() => {\n if (key === undefined) return Effect.void;\n const found = formViolations(key, form, presence);\n return found.length === 0 ? Effect.void : Effect.die(new Error(refusal(key, found)));\n });\n",
|
|
12
|
-
"/**\n * The vendor's own rules, checked here, before the request is built.\n *\n * 🔴 THE INCIDENT THIS EXISTS FOR, 2026-09-22. `homeflare-proxmox`'s first `deploy:pbs` adopted ten\n * objects and then failed its one create: `PVE POST config/verify -> 400: parameter verification\n * failed - comment: value may only be 128 characters long`. Half a deploy in, on the server, from\n * a limit PBS publishes in its own schema and the generated `comment?: string` does not carry.\n * Plan was green. `bun run check` was green. `hf-adopt-verify` was green — its offline fake serves\n * a fixture and refuses anything but GET, so a newly declared object's VALUES are never exercised.\n *\n * ★ SO THE TABLE IS GENERATED AND THIS FILE IS THE READER. `generated/constraints/*` is emitted\n * from the cluster's own `apidoc.js` by `bun codegen/constraints.ts`; nothing here is hand-typed,\n * because a hand-typed limit is the tribal knowledge that was already in a doc comment and\n * already did not run.\n *\n * ⛔ IT CHECKS THE FORM, NOT THE PROPS, AND THAT IS THE WHOLE POINT. The form is what goes on the\n * wire: `String(props['max-depth'])`, `'0' | '1'` for a boolean, an omitted key for an undeclared\n * field. A props-level check would pass values the form then mangles, and would have to be\n * written once per family — which is how the copy that forgets a field gets written.\n *\n * ⚠️ WHAT IT DELIBERATELY DOES NOT ENFORCE, so nobody reads a green plan as more than it is:\n * - a `format` NAME (PVE's `pve-calendar-event`, `pve-node
|
|
13
|
-
"/**\n * Generated pve-manager parameter constraints for `/access` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n *
|
|
14
|
-
"/**\n * Generated pve-manager parameter constraints for `/cluster` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 27 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_CLUSTER_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /cluster/backup\": {\n \"bwlimit\": {\"default\":\"0\",\"minimum\":0,\"type\":\"integer\"},\n \"comment\": {\"maxLength\":512,\"type\":\"string\"},\n \"compress\": {\"default\":\"0\",\"enum\":[\"0\",\"1\",\"gzip\",\"lzo\",\"zstd\"],\"type\":\"string\"},\n \"dow\": {\"default\":\"mon,tue,wed,thu,fri,sat,sun\",\"format\":\"pve-day-of-week-list\",\"type\":\"string\"},\n \"exclude\": {\"format\":\"pve-vmid-list\",\"type\":\"string\"},\n \"fleecing\": {\"format\":\"backup-fleecing\",\"type\":\"string\"},\n \"id\": {\"format\":\"pve-configid\",\"type\":\"string\"},\n \"ionice\": {\"default\":\"7\",\"maximum\":8,\"minimum\":0,\"type\":\"integer\"},\n \"lockwait\": {\"default\":\"180\",\"minimum\":0,\"type\":\"integer\"},\n \"mailnotification\": {\"default\":\"always\",\"enum\":[\"always\",\"failure\"],\"type\":\"string\"},\n \"mailto\": {\"format\":\"email-or-username-list\",\"type\":\"string\"},\n \"mode\": {\"default\":\"snapshot\",\"enum\":[\"snapshot\",\"suspend\",\"stop\"],\"type\":\"string\"},\n \"node\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"notes-template\": {\"maxLength\":1024,\"type\":\"string\"},\n \"notification-mode\": {\"default\":\"auto\",\"enum\":[\"auto\",\"legacy-sendmail\",\"notification-system\"],\"type\":\"string\"},\n \"pbs-change-detection-mode\": {\"enum\":[\"legacy\",\"data\",\"metadata\"],\"type\":\"string\"},\n \"performance\": {\"format\":\"backup-performance\",\"type\":\"string\"},\n \"prune-backups\": {\"default\":\"keep-all=1\",\"format\":\"prune-backups\",\"type\":\"string\"},\n \"schedule\": {\"format\":\"pve-calendar-event\",\"maxLength\":128,\"type\":\"string\"},\n \"starttime\": {\"pattern\":\"\\\\d{1,2}:\\\\d{1,2}\",\"patternSource\":\"\\\\d{1,2}:\\\\d{1,2}\",\"type\":\"string\"},\n \"stopwait\": {\"default\":\"10\",\"minimum\":0,\"type\":\"integer\"},\n \"storage\": {\"format\":\"pve-storage-id\",\"type\":\"string\"},\n \"vmid\": {\"format\":\"pve-vmid-list\",\"type\":\"string\"},\n },\n \"pve:POST /cluster/firewall/aliases\": {\n \"cidr\": {\"format\":\"IPorCIDR\",\"required\":true,\"type\":\"string\"},\n \"comment\": {\"format\":\"pve-fw-comment-spec\",\"type\":\"string\"},\n \"name\": {\"maxLength\":64,\"minLength\":2,\"pattern\":\"[A-Za-z][A-Za-z0-9\\\\-\\\\_]+\",\"patternSource\":\"[A-Za-z][A-Za-z0-9\\\\-\\\\_]+\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/ha/resources\": {\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"group\": {\"format\":\"pve-configid\",\"type\":\"string\"},\n \"max_relocate\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n \"max_restart\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n \"sid\": {\"format\":\"pve-ha-resource-or-vm-id\",\"required\":true,\"type\":\"string\"},\n \"state\": {\"default\":\"started\",\"enum\":[\"started\",\"stopped\",\"enabled\",\"disabled\",\"ignored\"],\"type\":\"string\"},\n \"type\": {\"enum\":[\"ct\",\"vm\"],\"type\":\"string\"},\n },\n \"pve:POST /cluster/ha/rules\": {},\n \"pve:POST /cluster/notifications/matchers\": {\n \"mode\": {\"default\":\"all\",\"enum\":[\"all\",\"any\"],\"type\":\"string\"},\n \"name\": {\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/replication\": {\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"id\": {\"format\":\"pve-replication-job-id\",\"pattern\":\"[1-9][0-9]{2,8}-\\\\d{1,9}\",\"patternSource\":\"[1-9][0-9]{2,8}-\\\\d{1,9}\",\"required\":true,\"type\":\"string\"},\n \"rate\": {\"minimum\":1,\"type\":\"number\"},\n \"remove_job\": {\"enum\":[\"local\",\"full\"],\"type\":\"string\"},\n \"schedule\": {\"default\":\"*/15\",\"format\":\"pve-calendar-event\",\"maxLength\":128,\"type\":\"string\"},\n \"source\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"target\": {\"format\":\"pve-node\",\"required\":true,\"type\":\"string\"},\n \"type\": {\"enum\":[\"local\"],\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/sdn/vnets\": {\n \"alias\": {\"maxLength\":256,\"patternSource\":\"(?^i:[\\\\(\\\\)-_.\\\\w\\\\d\\\\s]{0,256})\",\"type\":\"string\"},\n \"tag\": {\"maximum\":16777215,\"minimum\":1,\"type\":\"integer\"},\n \"type\": {\"enum\":[\"vnet\"],\"type\":\"string\"},\n \"vnet\": {\"maxLength\":8,\"minLength\":2,\"pattern\":\"[a-zA-Z][a-zA-Z0-9]*[a-zA-Z0-9]\",\"patternSource\":\"[a-zA-Z][a-zA-Z0-9]*[a-zA-Z0-9]\",\"required\":true,\"type\":\"string\"},\n \"zone\": {\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/sdn/zones\": {\n \"dhcp\": {\"enum\":[\"dnsmasq\"],\"type\":\"string\"},\n \"dnszone\": {\"format\":\"dns-name\",\"type\":\"string\"},\n \"exitnodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"exitnodes-primary\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"fabric\": {\"format\":\"pve-sdn-fabric-id\",\"type\":\"string\"},\n \"mac\": {\"format\":\"mac-addr\",\"type\":\"string\"},\n \"nodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"peers\": {\"format\":\"ip-list\",\"type\":\"string\"},\n \"rt-import\": {\"format\":\"pve-sdn-bgp-rt-list\",\"type\":\"string\"},\n \"tag\": {\"minimum\":0,\"type\":\"integer\"},\n \"type\": {\"enum\":[\"evpn\",\"faucet\",\"qinq\",\"simple\",\"vlan\",\"vxlan\"],\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n \"vlan-protocol\": {\"default\":\"802.1q\",\"enum\":[\"802.1q\",\"802.1ad\"],\"type\":\"string\"},\n \"vrf-vxlan\": {\"maximum\":16777215,\"minimum\":1,\"type\":\"integer\"},\n \"vxlan-port\": {\"default\":\"4789\",\"maximum\":65536,\"minimum\":1,\"type\":\"integer\"},\n \"zone\": {\"maxLength\":8,\"minLength\":2,\"pattern\":\"[a-zA-Z][a-zA-Z0-9]*[a-zA-Z0-9]\",\"patternSource\":\"[a-zA-Z][a-zA-Z0-9]*[a-zA-Z0-9]\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:PUT /cluster/backup/{id}\": {\n \"bwlimit\": {\"default\":\"0\",\"minimum\":0,\"type\":\"integer\"},\n \"comment\": {\"maxLength\":512,\"type\":\"string\"},\n \"compress\": {\"default\":\"0\",\"enum\":[\"0\",\"1\",\"gzip\",\"lzo\",\"zstd\"],\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"type\":\"string\"},\n \"dow\": {\"format\":\"pve-day-of-week-list\",\"type\":\"string\"},\n \"exclude\": {\"format\":\"pve-vmid-list\",\"type\":\"string\"},\n \"fleecing\": {\"format\":\"backup-fleecing\",\"type\":\"string\"},\n \"ionice\": {\"default\":\"7\",\"maximum\":8,\"minimum\":0,\"type\":\"integer\"},\n \"lockwait\": {\"default\":\"180\",\"minimum\":0,\"type\":\"integer\"},\n \"mailnotification\": {\"default\":\"always\",\"enum\":[\"always\",\"failure\"],\"type\":\"string\"},\n \"mailto\": {\"format\":\"email-or-username-list\",\"type\":\"string\"},\n \"mode\": {\"default\":\"snapshot\",\"enum\":[\"snapshot\",\"suspend\",\"stop\"],\"type\":\"string\"},\n \"node\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"notes-template\": {\"maxLength\":1024,\"type\":\"string\"},\n \"notification-mode\": {\"default\":\"auto\",\"enum\":[\"auto\",\"legacy-sendmail\",\"notification-system\"],\"type\":\"string\"},\n \"pbs-change-detection-mode\": {\"enum\":[\"legacy\",\"data\",\"metadata\"],\"type\":\"string\"},\n \"performance\": {\"format\":\"backup-performance\",\"type\":\"string\"},\n \"prune-backups\": {\"default\":\"keep-all=1\",\"format\":\"prune-backups\",\"type\":\"string\"},\n \"schedule\": {\"format\":\"pve-calendar-event\",\"maxLength\":128,\"type\":\"string\"},\n \"starttime\": {\"pattern\":\"\\\\d{1,2}:\\\\d{1,2}\",\"patternSource\":\"\\\\d{1,2}:\\\\d{1,2}\",\"type\":\"string\"},\n \"stopwait\": {\"default\":\"10\",\"minimum\":0,\"type\":\"integer\"},\n \"storage\": {\"format\":\"pve-storage-id\",\"type\":\"string\"},\n \"vmid\": {\"format\":\"pve-vmid-list\",\"type\":\"string\"},\n },\n \"pve:PUT /cluster/firewall/aliases/{name}\": {\n \"cidr\": {\"format\":\"IPorCIDR\",\"required\":true,\"type\":\"string\"},\n \"comment\": {\"format\":\"pve-fw-comment-spec\",\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"rename\": {\"maxLength\":64,\"minLength\":2,\"pattern\":\"[A-Za-z][A-Za-z0-9\\\\-\\\\_]+\",\"patternSource\":\"[A-Za-z][A-Za-z0-9\\\\-\\\\_]+\",\"type\":\"string\"},\n },\n \"pve:PUT /cluster/ha/resources/{sid}\": {\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"group\": {\"format\":\"pve-configid\",\"type\":\"string\"},\n \"max_relocate\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n \"max_restart\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n \"state\": {\"default\":\"started\",\"enum\":[\"started\",\"stopped\",\"enabled\",\"disabled\",\"ignored\"],\"type\":\"string\"},\n },\n \"pve:PUT /cluster/ha/rules/{rule}\": {},\n \"pve:PUT /cluster/notifications/matchers/{name}\": {\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"mode\": {\"default\":\"all\",\"enum\":[\"all\",\"any\"],\"type\":\"string\"},\n },\n \"pve:PUT /cluster/replication/{id}\": {\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"rate\": {\"minimum\":1,\"type\":\"number\"},\n \"remove_job\": {\"enum\":[\"local\",\"full\"],\"type\":\"string\"},\n \"schedule\": {\"default\":\"*/15\",\"format\":\"pve-calendar-event\",\"maxLength\":128,\"type\":\"string\"},\n \"source\": {\"format\":\"pve-node\",\"type\":\"string\"},\n },\n \"pve:PUT /cluster/sdn/vnets/{vnet}\": {\n \"alias\": {\"maxLength\":256,\"patternSource\":\"(?^i:[\\\\(\\\\)-_.\\\\w\\\\d\\\\s]{0,256})\",\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"tag\": {\"maximum\":16777215,\"minimum\":1,\"type\":\"integer\"},\n },\n \"pve:PUT /cluster/sdn/zones/{zone}\": {\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"dhcp\": {\"enum\":[\"dnsmasq\"],\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"dnszone\": {\"format\":\"dns-name\",\"type\":\"string\"},\n \"exitnodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"exitnodes-primary\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"fabric\": {\"format\":\"pve-sdn-fabric-id\",\"type\":\"string\"},\n \"mac\": {\"format\":\"mac-addr\",\"type\":\"string\"},\n \"nodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"peers\": {\"format\":\"ip-list\",\"type\":\"string\"},\n \"rt-import\": {\"format\":\"pve-sdn-bgp-rt-list\",\"type\":\"string\"},\n \"tag\": {\"minimum\":0,\"type\":\"integer\"},\n \"vlan-protocol\": {\"default\":\"802.1q\",\"enum\":[\"802.1q\",\"802.1ad\"],\"type\":\"string\"},\n \"vrf-vxlan\": {\"maximum\":16777215,\"minimum\":1,\"type\":\"integer\"},\n \"vxlan-port\": {\"default\":\"4789\",\"maximum\":65536,\"minimum\":1,\"type\":\"integer\"},\n },\n};\n",
|
|
15
|
-
"/**\n * Generated pve-manager parameter constraints for `/
|
|
16
|
-
"/**\n * Generated pve-manager parameter constraints for `/
|
|
17
|
-
"/**\n * Generated proxmox-backup-server parameter constraints for `/config` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pbs-apidoc` — proxmox-backup-server 4.2.6-1 (running 4.2.3)\n * sha256 274ab9f6fc075aea, read on a PBS host from\n * /usr/share/doc/proxmox-backup/html/api-viewer/apidoc.js\n *\n * 10 of this product's 144 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PBS_CONFIG_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pbs:POST /config/datastore\": {\n \"backing-device\": {\"pattern\":\"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$\",\"patternSource\":\"/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/\",\"type\":\"string\"},\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"keep-daily\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-hourly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-monthly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-weekly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-yearly\": {\"minimum\":1,\"type\":\"integer\"},\n \"name\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"notification-mode\": {\"default\":\"notification-system\",\"enum\":[\"legacy-sendmail\",\"notification-system\"],\"type\":\"string\"},\n \"notify-user\": {\"maxLength\":64,\"minLength\":3,\"pattern\":\"^(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"path\": {\"maxLength\":4096,\"minLength\":1,\"required\":true,\"type\":\"string\"},\n },\n \"pbs:POST /config/notifications/matchers\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"mode\": {\"default\":\"all\",\"enum\":[\"all\",\"any\"],\"type\":\"string\"},\n \"name\": {\"maxLength\":32,\"minLength\":2,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"origin\": {\"enum\":[\"user-created\",\"builtin\",\"modified-builtin\"],\"type\":\"string\"},\n },\n \"pbs:POST /config/prune\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"id\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"keep-daily\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-hourly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-monthly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-weekly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-yearly\": {\"minimum\":1,\"type\":\"integer\"},\n \"max-depth\": {\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"schedule\": {\"required\":true,\"type\":\"string\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n },\n \"pbs:POST /config/sync\": {\n \"active-encryption-key\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"burst-in\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"burst-out\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"id\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"max-depth\": {\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"owner\": {\"maxLength\":64,\"minLength\":3,\"pattern\":\"^(?:(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)|(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)!(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))$\",\"patternSource\":\"/^(?:(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)|(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)!(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))$/\",\"type\":\"string\"},\n \"rate-in\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"rate-out\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"remote\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"remote-ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"remote-store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"sync-direction\": {\"default\":\"pull\",\"enum\":[\"pull\",\"push\"],\"type\":\"string\"},\n \"transfer-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"worker-threads\": {\"default\":\"1\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n },\n \"pbs:POST /config/verify\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"id\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"max-depth\": {\"default\":\"7\",\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"outdated-after\": {\"minimum\":0,\"type\":\"integer\"},\n \"read-threads\": {\"default\":\"1\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"verify-threads\": {\"default\":\"4\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n },\n \"pbs:PUT /config/datastore/{name}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"keep-daily\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-hourly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-monthly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-weekly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-yearly\": {\"minimum\":1,\"type\":\"integer\"},\n \"notification-mode\": {\"default\":\"notification-system\",\"enum\":[\"legacy-sendmail\",\"notification-system\"],\"type\":\"string\"},\n \"notify-user\": {\"maxLength\":64,\"minLength\":3,\"pattern\":\"^(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n },\n \"pbs:PUT /config/notifications/matchers/{name}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"mode\": {\"default\":\"all\",\"enum\":[\"all\",\"any\"],\"type\":\"string\"},\n },\n \"pbs:PUT /config/prune/{id}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"keep-daily\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-hourly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-monthly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-weekly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-yearly\": {\"minimum\":1,\"type\":\"integer\"},\n \"max-depth\": {\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n },\n \"pbs:PUT /config/sync/{id}\": {\n \"active-encryption-key\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"burst-in\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"burst-out\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"max-depth\": {\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"owner\": {\"maxLength\":64,\"minLength\":3,\"pattern\":\"^(?:(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)|(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)!(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))$\",\"patternSource\":\"/^(?:(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)|(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)!(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))$/\",\"type\":\"string\"},\n \"rate-in\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"rate-out\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"remote\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"remote-ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"remote-store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"sync-direction\": {\"default\":\"pull\",\"enum\":[\"pull\",\"push\"],\"type\":\"string\"},\n \"transfer-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"worker-threads\": {\"default\":\"1\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n },\n \"pbs:PUT /config/verify/{id}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"max-depth\": {\"default\":\"7\",\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"outdated-after\": {\"minimum\":0,\"type\":\"integer\"},\n \"read-threads\": {\"default\":\"1\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"verify-threads\": {\"default\":\"4\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n },\n};\n",
|
|
18
|
-
"/**\n *
|
|
13
|
+
"/**\n * The vendor's own rules, checked here, before the request is built.\n *\n * 🔴 THE INCIDENT THIS EXISTS FOR, 2026-09-22. `homeflare-proxmox`'s first `deploy:pbs` adopted ten\n * objects and then failed its one create: `PVE POST config/verify -> 400: parameter verification\n * failed - comment: value may only be 128 characters long`. Half a deploy in, on the server, from\n * a limit PBS publishes in its own schema and the generated `comment?: string` does not carry.\n * Plan was green. `bun run check` was green. `hf-adopt-verify` was green — its offline fake serves\n * a fixture and refuses anything but GET, so a newly declared object's VALUES are never exercised.\n *\n * ★ SO THE TABLE IS GENERATED AND THIS FILE IS THE READER. `generated/constraints/*` is emitted\n * from the cluster's own `apidoc.js` by `bun codegen/constraints.ts`; nothing here is hand-typed,\n * because a hand-typed limit is the tribal knowledge that was already in a doc comment and\n * already did not run.\n *\n * ⛔ IT CHECKS THE FORM, NOT THE PROPS, AND THAT IS THE WHOLE POINT. The form is what goes on the\n * wire: `String(props['max-depth'])`, `'0' | '1'` for a boolean, an omitted key for an undeclared\n * field. A props-level check would pass values the form then mangles, and would have to be\n * written once per family — which is how the copy that forgets a field gets written.\n *\n * ⚠️ WHAT IT DELIBERATELY DOES NOT ENFORCE, so nobody reads a green plan as more than it is. The\n * full kind-by-kind table, with the counts measured over the 37 tabled endpoints, is in\n * codegen/README.md. The short version:\n * - a `format` NAME (PVE's `pve-calendar-event`, `pve-node`; 105 parameters here): the name is a\n * validator PVE implements server-side and publishes nothing about. Recorded, never checked.\n * - a `format` OBJECT — PBS's `notify`, `tuning`, `maintenance-mode`, PVE's `bwlimit`; 10\n * parameters. It is a sub-schema for the inside of a PROPERTY STRING, and the packed keys\n * carry their own limits. Not flattened, because a rule about `notify` is not a rule about\n * `notify.gc`. ⛔ This is the largest remaining gap and it is a gap on purpose.\n * - `requires` (8 parameters): \"this one needs that one\". A dependency, not a value rule.\n * - `typetext` with nothing else, such as PBS `schedule`'s `<calendar-event>` (21 parameters).\n * The schema states a syntax it does not describe; a malformed schedule still reaches PBS.\n * - a pattern the generator could not translate faithfully (codegen/pattern.ts). Recorded\n * verbatim as `patternSource`, never checked.\n * Each of those is a fact in the table rather than a guess, which is the rule this follows:\n * if the vendor did not state it, it is not enforced and the table says so.\n */\nimport type { PveForm } from './client.ts';\n\nexport interface ParamConstraint {\n readonly type?: string;\n /** From the vendor's `optional`. ⚠️ Checked on CREATE only — see `violations`. */\n readonly required?: boolean;\n readonly maxLength?: number;\n readonly minLength?: number;\n readonly minimum?: number;\n readonly maximum?: number;\n readonly enum?: readonly string[];\n /**\n * A JavaScript-safe EQUIVALENT. Absent when the vendor's dialect could not be carried over.\n *\n * ⛔ NOT THE VENDOR'S SPELLING, AND FOR PVE NOT EVEN THE SAME SHAPE. PVE publishes the inside of\n * an anchored match and applies `m/^$pattern$/` itself (MEASURED in JSONSchema.pm), so the\n * generator anchors it here. Quote `patternSource` at a human, never this.\n */\n readonly pattern?: string;\n /** `m` when the vendor asked for multi-line. ⚠️ Dropping it enforces the wrong semantics. */\n readonly patternFlags?: string;\n /** The vendor's own spelling, always — what a violation message quotes, and the audit trail. */\n readonly patternSource?: string;\n /** PVE's server-side validator name. Recorded so a reader knows what is NOT checked here. */\n readonly format?: string;\n readonly default?: string;\n /**\n * The value rules describe each ELEMENT of a repeated key, not one joined string.\n *\n * ⚠️ RECORDED, NOT BRANCHED ON. `violations` already checks every element of an array value, so\n * this changes no behaviour — it tells a reader of the generated table why a `maxLength` of 32\n * sits on a parameter that carries a list.\n */\n readonly each?: true;\n}\n\nexport type EndpointConstraints = Readonly<Record<string, ParamConstraint>>;\n\n/** `POST /config/verify` — exactly how the generated tables key an endpoint. */\nexport type EndpointKey = string;\n\nexport interface ViolationOptions {\n /**\n * Check that every vendor-required parameter is present.\n *\n * ⛔ CREATE ONLY. An update form is deliberately PARTIAL — `formToSend` sends the fields that\n * changed and nothing else — so requiring presence there would refuse every ordinary edit.\n */\n readonly presence?: boolean;\n}\n\nconst lengthOf = (value: string): number => [...value].length;\n\n/**\n * ⚠️ `[...value].length`, NOT `value.length`. Proxmox counts characters; JavaScript counts UTF-16\n * code units, so an emoji in a comment counts twice and a 128-character limit would refuse a\n * comment PBS accepts. Rejecting a legal value at plan time is the one failure mode worse than\n * the server 400 this replaces.\n */\nconst checkValue = (name: string, value: string, rule: ParamConstraint): string[] => {\n const out: string[] = [];\n if (rule.maxLength !== undefined && lengthOf(value) > rule.maxLength) {\n out.push(`${name}: at most ${rule.maxLength} characters`);\n }\n if (rule.minLength !== undefined && lengthOf(value) < rule.minLength) {\n out.push(`${name}: at least ${rule.minLength} characters`);\n }\n if (rule.enum !== undefined && !rule.enum.includes(value)) {\n out.push(`${name}: must be one of ${rule.enum.join(', ')}`);\n }\n // ⚠️ `patternFlags` IS PASSED, NOT IGNORED. It carries the vendor's own `(?m)`; enforcing a\n // multi-line rule with single-line semantics refuses values the vendor accepts.\n if (rule.pattern !== undefined && !new RegExp(rule.pattern, rule.patternFlags).test(value)) {\n out.push(`${name}: must match ${rule.patternSource ?? rule.pattern}`);\n }\n // ⚠️ NUMBER-NESS IS NOT CHECKED, ONLY THE BOUND. The form is strings by construction and a\n // non-numeric string is a type error upstream; `Number('')` is 0, so a blank would read as in\n // range. Guarding on Number.isFinite keeps a blank from silently passing a minimum of 0.\n const numeric = Number(value);\n const bounded = rule.minimum !== undefined || rule.maximum !== undefined;\n if (bounded && value !== '' && Number.isFinite(numeric)) {\n if (rule.minimum !== undefined && numeric < rule.minimum) {\n out.push(`${name}: at least ${rule.minimum}`);\n }\n if (rule.maximum !== undefined && numeric > rule.maximum) {\n out.push(`${name}: at most ${rule.maximum}`);\n }\n }\n return out;\n};\n\n/**\n * Every vendor rule this form breaks, in the order the parameters are declared. Pure: no network,\n * no clock, no environment — so `diff` can call it and a test can call it with a literal.\n *\n * ⚠️ A KEY THE TABLE DOES NOT MENTION IS NOT AN ERROR. The table carries the constrained\n * parameters, not every parameter, and a form may legitimately send an unconstrained one.\n */\nexport const violations = (\n table: EndpointConstraints,\n form: PveForm,\n options: ViolationOptions = {},\n): readonly string[] => {\n const out: string[] = [];\n for (const [name, rule] of Object.entries(table)) {\n const value = form[name];\n if (value === undefined) {\n if (options.presence === true && rule.required === true) out.push(`${name}: required`);\n continue;\n }\n // ⚠️ AN ARRAY IS REPEATED KEYS ON THE WIRE (client.ts), so every element faces the same rule.\n for (const item of typeof value === 'string' ? [value] : value) {\n out.push(...checkValue(name, item, rule));\n }\n }\n return out;\n};\n\n/** The message a plan dies with. ⛔ Names the endpoint, because the rule belongs to the vendor. */\nexport const refusal = (endpoint: EndpointKey, found: readonly string[]): string =>\n `${endpoint} would be rejected by the vendor:\\n - ${found.join('\\n - ')}\\n` +\n \"These are the vendor's own limits, read from its published schema \" +\n '(packages/alchemy/src/proxmox/generated/constraints). Fix the declaration.';\n",
|
|
14
|
+
"/**\n * Generated pve-manager parameter constraints for `/access` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_ACCESS_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /access/groups\": {\n \"groupid\": {\"format\":\"pve-groupid\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /access/roles\": {\n \"privs\": {\"format\":\"pve-priv-list\",\"type\":\"string\"},\n \"roleid\": {\"format\":\"pve-roleid\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /access/users\": {\n \"comment\": {\"maxLength\":2048,\"type\":\"string\"},\n \"email\": {\"format\":\"email-opt\",\"maxLength\":254,\"type\":\"string\"},\n \"expire\": {\"minimum\":0,\"type\":\"integer\"},\n \"firstname\": {\"maxLength\":1024,\"type\":\"string\"},\n \"groups\": {\"format\":\"pve-groupid-list\",\"type\":\"string\"},\n \"keys\": {\"pattern\":\"^[0-9a-zA-Z!=]{0,4096}\\\\n?$\",\"patternSource\":\"[0-9a-zA-Z!=]{0,4096}\",\"type\":\"string\"},\n \"lastname\": {\"maxLength\":1024,\"type\":\"string\"},\n \"password\": {\"maxLength\":64,\"minLength\":8,\"type\":\"string\"},\n \"userid\": {\"format\":\"pve-userid\",\"maxLength\":64,\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /access/users/{userid}/token/{tokenid}\": {\n \"expire\": {\"default\":\"same as user\",\"minimum\":0,\"type\":\"integer\"},\n },\n \"pve:PUT /access/acl\": {\n \"groups\": {\"format\":\"pve-groupid-list\",\"type\":\"string\"},\n \"path\": {\"required\":true,\"type\":\"string\"},\n \"roles\": {\"format\":\"pve-roleid-list\",\"required\":true,\"type\":\"string\"},\n \"tokens\": {\"format\":\"pve-tokenid-list\",\"type\":\"string\"},\n \"users\": {\"format\":\"pve-userid-list\",\"type\":\"string\"},\n },\n \"pve:PUT /access/groups/{groupid}\": {},\n \"pve:PUT /access/roles/{roleid}\": {\n \"privs\": {\"format\":\"pve-priv-list\",\"type\":\"string\"},\n },\n \"pve:PUT /access/users/{userid}\": {\n \"comment\": {\"maxLength\":2048,\"type\":\"string\"},\n \"email\": {\"format\":\"email-opt\",\"maxLength\":254,\"type\":\"string\"},\n \"expire\": {\"minimum\":0,\"type\":\"integer\"},\n \"firstname\": {\"maxLength\":1024,\"type\":\"string\"},\n \"groups\": {\"format\":\"pve-groupid-list\",\"type\":\"string\"},\n \"keys\": {\"pattern\":\"^[0-9a-zA-Z!=]{0,4096}\\\\n?$\",\"patternSource\":\"[0-9a-zA-Z!=]{0,4096}\",\"type\":\"string\"},\n \"lastname\": {\"maxLength\":1024,\"type\":\"string\"},\n },\n \"pve:PUT /access/users/{userid}/token/{tokenid}\": {\n \"delete\": {\"format\":\"pve-configid-list\",\"type\":\"string\"},\n \"expire\": {\"default\":\"same as user\",\"minimum\":0,\"type\":\"integer\"},\n },\n};\n",
|
|
15
|
+
"/**\n * Generated pve-manager parameter constraints for `/cluster/backup` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_CLUSTER_BACKUP_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /cluster/backup\": {\n \"bwlimit\": {\"default\":\"0\",\"minimum\":0,\"type\":\"integer\"},\n \"comment\": {\"maxLength\":512,\"type\":\"string\"},\n \"compress\": {\"default\":\"0\",\"enum\":[\"0\",\"1\",\"gzip\",\"lzo\",\"zstd\"],\"type\":\"string\"},\n \"dow\": {\"default\":\"mon,tue,wed,thu,fri,sat,sun\",\"format\":\"pve-day-of-week-list\",\"type\":\"string\"},\n \"exclude\": {\"format\":\"pve-vmid-list\",\"type\":\"string\"},\n \"fleecing\": {\"format\":\"backup-fleecing\",\"type\":\"string\"},\n \"id\": {\"format\":\"pve-configid\",\"type\":\"string\"},\n \"ionice\": {\"default\":\"7\",\"maximum\":8,\"minimum\":0,\"type\":\"integer\"},\n \"lockwait\": {\"default\":\"180\",\"minimum\":0,\"type\":\"integer\"},\n \"mailnotification\": {\"default\":\"always\",\"enum\":[\"always\",\"failure\"],\"type\":\"string\"},\n \"mailto\": {\"format\":\"email-or-username-list\",\"type\":\"string\"},\n \"mode\": {\"default\":\"snapshot\",\"enum\":[\"snapshot\",\"suspend\",\"stop\"],\"type\":\"string\"},\n \"node\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"notes-template\": {\"maxLength\":1024,\"type\":\"string\"},\n \"notification-mode\": {\"default\":\"auto\",\"enum\":[\"auto\",\"legacy-sendmail\",\"notification-system\"],\"type\":\"string\"},\n \"pbs-change-detection-mode\": {\"enum\":[\"legacy\",\"data\",\"metadata\"],\"type\":\"string\"},\n \"performance\": {\"format\":\"backup-performance\",\"type\":\"string\"},\n \"prune-backups\": {\"default\":\"keep-all=1\",\"format\":\"prune-backups\",\"type\":\"string\"},\n \"schedule\": {\"format\":\"pve-calendar-event\",\"maxLength\":128,\"type\":\"string\"},\n \"starttime\": {\"pattern\":\"^\\\\d{1,2}:\\\\d{1,2}\\\\n?$\",\"patternSource\":\"\\\\d{1,2}:\\\\d{1,2}\",\"type\":\"string\"},\n \"stopwait\": {\"default\":\"10\",\"minimum\":0,\"type\":\"integer\"},\n \"storage\": {\"format\":\"pve-storage-id\",\"type\":\"string\"},\n \"vmid\": {\"format\":\"pve-vmid-list\",\"type\":\"string\"},\n },\n \"pve:PUT /cluster/backup/{id}\": {\n \"bwlimit\": {\"default\":\"0\",\"minimum\":0,\"type\":\"integer\"},\n \"comment\": {\"maxLength\":512,\"type\":\"string\"},\n \"compress\": {\"default\":\"0\",\"enum\":[\"0\",\"1\",\"gzip\",\"lzo\",\"zstd\"],\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"type\":\"string\"},\n \"dow\": {\"format\":\"pve-day-of-week-list\",\"type\":\"string\"},\n \"exclude\": {\"format\":\"pve-vmid-list\",\"type\":\"string\"},\n \"fleecing\": {\"format\":\"backup-fleecing\",\"type\":\"string\"},\n \"ionice\": {\"default\":\"7\",\"maximum\":8,\"minimum\":0,\"type\":\"integer\"},\n \"lockwait\": {\"default\":\"180\",\"minimum\":0,\"type\":\"integer\"},\n \"mailnotification\": {\"default\":\"always\",\"enum\":[\"always\",\"failure\"],\"type\":\"string\"},\n \"mailto\": {\"format\":\"email-or-username-list\",\"type\":\"string\"},\n \"mode\": {\"default\":\"snapshot\",\"enum\":[\"snapshot\",\"suspend\",\"stop\"],\"type\":\"string\"},\n \"node\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"notes-template\": {\"maxLength\":1024,\"type\":\"string\"},\n \"notification-mode\": {\"default\":\"auto\",\"enum\":[\"auto\",\"legacy-sendmail\",\"notification-system\"],\"type\":\"string\"},\n \"pbs-change-detection-mode\": {\"enum\":[\"legacy\",\"data\",\"metadata\"],\"type\":\"string\"},\n \"performance\": {\"format\":\"backup-performance\",\"type\":\"string\"},\n \"prune-backups\": {\"default\":\"keep-all=1\",\"format\":\"prune-backups\",\"type\":\"string\"},\n \"schedule\": {\"format\":\"pve-calendar-event\",\"maxLength\":128,\"type\":\"string\"},\n \"starttime\": {\"pattern\":\"^\\\\d{1,2}:\\\\d{1,2}\\\\n?$\",\"patternSource\":\"\\\\d{1,2}:\\\\d{1,2}\",\"type\":\"string\"},\n \"stopwait\": {\"default\":\"10\",\"minimum\":0,\"type\":\"integer\"},\n \"storage\": {\"format\":\"pve-storage-id\",\"type\":\"string\"},\n \"vmid\": {\"format\":\"pve-vmid-list\",\"type\":\"string\"},\n },\n};\n",
|
|
16
|
+
"/**\n * Generated pve-manager parameter constraints for `/cluster/ceph` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_CLUSTER_CEPH_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:PUT /cluster/ceph/flags/{flag}\": {\n \"value\": {\"required\":true,\"type\":\"boolean\"},\n },\n};\n",
|
|
17
|
+
"/**\n * Generated pve-manager parameter constraints for `/cluster/firewall` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_CLUSTER_FIREWALL_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /cluster/firewall/aliases\": {\n \"cidr\": {\"format\":\"IPorCIDR\",\"required\":true,\"type\":\"string\"},\n \"comment\": {\"format\":\"pve-fw-comment-spec\",\"type\":\"string\"},\n \"name\": {\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[A-Za-z][A-Za-z0-9\\\\-\\\\_]+\\\\n?$\",\"patternSource\":\"[A-Za-z][A-Za-z0-9\\\\-\\\\_]+\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:PUT /cluster/firewall/aliases/{name}\": {\n \"cidr\": {\"format\":\"IPorCIDR\",\"required\":true,\"type\":\"string\"},\n \"comment\": {\"format\":\"pve-fw-comment-spec\",\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"rename\": {\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[A-Za-z][A-Za-z0-9\\\\-\\\\_]+\\\\n?$\",\"patternSource\":\"[A-Za-z][A-Za-z0-9\\\\-\\\\_]+\",\"type\":\"string\"},\n },\n};\n",
|
|
18
|
+
"/**\n * Generated pve-manager parameter constraints for `/cluster/ha` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_CLUSTER_HA_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /cluster/ha/resources\": {\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"group\": {\"format\":\"pve-configid\",\"type\":\"string\"},\n \"max_relocate\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n \"max_restart\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n \"sid\": {\"format\":\"pve-ha-resource-or-vm-id\",\"required\":true,\"type\":\"string\"},\n \"state\": {\"default\":\"started\",\"enum\":[\"started\",\"stopped\",\"enabled\",\"disabled\",\"ignored\"],\"type\":\"string\"},\n \"type\": {\"enum\":[\"ct\",\"vm\"],\"type\":\"string\"},\n },\n \"pve:POST /cluster/ha/rules\": {\n \"affinity\": {\"enum\":[\"positive\",\"negative\"],\"type\":\"string\"},\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"resources\": {\"format\":\"pve-ha-resource-id-list\",\"required\":true,\"type\":\"string\"},\n \"rule\": {\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:PUT /cluster/ha/resources/{sid}\": {\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"group\": {\"format\":\"pve-configid\",\"type\":\"string\"},\n \"max_relocate\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n \"max_restart\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n \"state\": {\"default\":\"started\",\"enum\":[\"started\",\"stopped\",\"enabled\",\"disabled\",\"ignored\"],\"type\":\"string\"},\n },\n \"pve:PUT /cluster/ha/rules/{rule}\": {\n \"affinity\": {\"enum\":[\"positive\",\"negative\"],\"type\":\"string\"},\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"resources\": {\"format\":\"pve-ha-resource-id-list\",\"type\":\"string\"},\n },\n};\n",
|
|
19
|
+
"/**\n * Generated pve-manager parameter constraints for `/cluster/metrics` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_CLUSTER_METRICS_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /cluster/metrics/server/{id}\": {\n \"influxdbproto\": {\"default\":\"udp\",\"enum\":[\"udp\",\"http\",\"https\"],\"type\":\"string\"},\n \"max-body-size\": {\"default\":\"25000000\",\"minimum\":1,\"type\":\"integer\"},\n \"mtu\": {\"default\":\"1500\",\"maximum\":65536,\"minimum\":512,\"type\":\"integer\"},\n \"otel-compression\": {\"default\":\"gzip\",\"enum\":[\"none\",\"gzip\"],\"type\":\"string\"},\n \"otel-headers\": {\"maxLength\":1024,\"type\":\"string\"},\n \"otel-max-body-size\": {\"default\":\"10000000\",\"minimum\":1024,\"type\":\"integer\"},\n \"otel-protocol\": {\"default\":\"https\",\"enum\":[\"http\",\"https\"],\"type\":\"string\"},\n \"otel-resource-attributes\": {\"maxLength\":1024,\"type\":\"string\"},\n \"otel-timeout\": {\"default\":\"5\",\"maximum\":10,\"minimum\":1,\"type\":\"integer\"},\n \"path\": {\"format\":\"graphite-path\",\"type\":\"string\"},\n \"port\": {\"maximum\":65536,\"minimum\":1,\"required\":true,\"type\":\"integer\"},\n \"proto\": {\"enum\":[\"udp\",\"tcp\"],\"type\":\"string\"},\n \"server\": {\"format\":\"address\",\"required\":true,\"type\":\"string\"},\n \"timeout\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n \"type\": {\"enum\":[\"graphite\",\"influxdb\",\"opentelemetry\"],\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:PUT /cluster/metrics/server/{id}\": {\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"influxdbproto\": {\"default\":\"udp\",\"enum\":[\"udp\",\"http\",\"https\"],\"type\":\"string\"},\n \"max-body-size\": {\"default\":\"25000000\",\"minimum\":1,\"type\":\"integer\"},\n \"mtu\": {\"default\":\"1500\",\"maximum\":65536,\"minimum\":512,\"type\":\"integer\"},\n \"otel-compression\": {\"default\":\"gzip\",\"enum\":[\"none\",\"gzip\"],\"type\":\"string\"},\n \"otel-headers\": {\"maxLength\":1024,\"type\":\"string\"},\n \"otel-max-body-size\": {\"default\":\"10000000\",\"minimum\":1024,\"type\":\"integer\"},\n \"otel-protocol\": {\"default\":\"https\",\"enum\":[\"http\",\"https\"],\"type\":\"string\"},\n \"otel-resource-attributes\": {\"maxLength\":1024,\"type\":\"string\"},\n \"otel-timeout\": {\"default\":\"5\",\"maximum\":10,\"minimum\":1,\"type\":\"integer\"},\n \"path\": {\"format\":\"graphite-path\",\"type\":\"string\"},\n \"port\": {\"maximum\":65536,\"minimum\":1,\"required\":true,\"type\":\"integer\"},\n \"proto\": {\"enum\":[\"udp\",\"tcp\"],\"type\":\"string\"},\n \"server\": {\"format\":\"address\",\"required\":true,\"type\":\"string\"},\n \"timeout\": {\"default\":\"1\",\"minimum\":0,\"type\":\"integer\"},\n },\n};\n",
|
|
20
|
+
"/**\n * Generated pve-manager parameter constraints for `/cluster/notifications` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_CLUSTER_NOTIFICATIONS_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /cluster/notifications/endpoints/gotify\": {\n \"name\": {\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n \"server\": {\"required\":true,\"type\":\"string\"},\n \"token\": {\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/notifications/endpoints/sendmail\": {\n \"mailto\": {\"each\":true,\"format\":\"email-or-username\",\"type\":\"array\"},\n \"mailto-user\": {\"each\":true,\"format\":\"pve-userid\",\"type\":\"array\"},\n \"name\": {\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/notifications/endpoints/smtp\": {\n \"from-address\": {\"required\":true,\"type\":\"string\"},\n \"mailto\": {\"each\":true,\"format\":\"email-or-username\",\"type\":\"array\"},\n \"mailto-user\": {\"each\":true,\"format\":\"pve-userid\",\"type\":\"array\"},\n \"mode\": {\"default\":\"tls\",\"enum\":[\"insecure\",\"starttls\",\"tls\"],\"type\":\"string\"},\n \"name\": {\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n \"server\": {\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/notifications/endpoints/webhook\": {\n \"method\": {\"enum\":[\"post\",\"put\",\"get\"],\"required\":true,\"type\":\"string\"},\n \"name\": {\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n \"url\": {\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/notifications/matchers\": {\n \"mode\": {\"default\":\"all\",\"enum\":[\"all\",\"any\"],\"type\":\"string\"},\n \"name\": {\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n \"target\": {\"each\":true,\"format\":\"pve-configid\",\"type\":\"array\"},\n },\n \"pve:PUT /cluster/notifications/endpoints/gotify/{name}\": {\n \"delete\": {\"each\":true,\"format\":\"pve-configid\",\"type\":\"array\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n },\n \"pve:PUT /cluster/notifications/endpoints/sendmail/{name}\": {\n \"delete\": {\"each\":true,\"format\":\"pve-configid\",\"type\":\"array\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"mailto\": {\"each\":true,\"format\":\"email-or-username\",\"type\":\"array\"},\n \"mailto-user\": {\"each\":true,\"format\":\"pve-userid\",\"type\":\"array\"},\n },\n \"pve:PUT /cluster/notifications/endpoints/smtp/{name}\": {\n \"delete\": {\"each\":true,\"format\":\"pve-configid\",\"type\":\"array\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"mailto\": {\"each\":true,\"format\":\"email-or-username\",\"type\":\"array\"},\n \"mailto-user\": {\"each\":true,\"format\":\"pve-userid\",\"type\":\"array\"},\n \"mode\": {\"default\":\"tls\",\"enum\":[\"insecure\",\"starttls\",\"tls\"],\"type\":\"string\"},\n },\n \"pve:PUT /cluster/notifications/endpoints/webhook/{name}\": {\n \"delete\": {\"each\":true,\"format\":\"pve-configid\",\"type\":\"array\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"method\": {\"enum\":[\"post\",\"put\",\"get\"],\"type\":\"string\"},\n },\n \"pve:PUT /cluster/notifications/matchers/{name}\": {\n \"delete\": {\"each\":true,\"format\":\"pve-configid\",\"type\":\"array\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"mode\": {\"default\":\"all\",\"enum\":[\"all\",\"any\"],\"type\":\"string\"},\n \"target\": {\"each\":true,\"format\":\"pve-configid\",\"type\":\"array\"},\n },\n};\n",
|
|
21
|
+
"/**\n * Generated pve-manager parameter constraints for `/cluster/replication` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_CLUSTER_REPLICATION_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /cluster/replication\": {\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"id\": {\"format\":\"pve-replication-job-id\",\"pattern\":\"^[1-9][0-9]{2,8}-\\\\d{1,9}\\\\n?$\",\"patternSource\":\"[1-9][0-9]{2,8}-\\\\d{1,9}\",\"required\":true,\"type\":\"string\"},\n \"rate\": {\"minimum\":1,\"type\":\"number\"},\n \"remove_job\": {\"enum\":[\"local\",\"full\"],\"type\":\"string\"},\n \"schedule\": {\"default\":\"*/15\",\"format\":\"pve-calendar-event\",\"maxLength\":128,\"type\":\"string\"},\n \"source\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"target\": {\"format\":\"pve-node\",\"required\":true,\"type\":\"string\"},\n \"type\": {\"enum\":[\"local\"],\"required\":true,\"type\":\"string\"},\n },\n \"pve:PUT /cluster/replication/{id}\": {\n \"comment\": {\"maxLength\":4096,\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"rate\": {\"minimum\":1,\"type\":\"number\"},\n \"remove_job\": {\"enum\":[\"local\",\"full\"],\"type\":\"string\"},\n \"schedule\": {\"default\":\"*/15\",\"format\":\"pve-calendar-event\",\"maxLength\":128,\"type\":\"string\"},\n \"source\": {\"format\":\"pve-node\",\"type\":\"string\"},\n },\n};\n",
|
|
22
|
+
"/**\n * Generated pve-manager parameter constraints for `/cluster/sdn` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_CLUSTER_SDN_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /cluster/sdn/vnets\": {\n \"alias\": {\"maxLength\":256,\"patternSource\":\"(?^i:[\\\\(\\\\)-_.\\\\w\\\\d\\\\s]{0,256})\",\"type\":\"string\"},\n \"tag\": {\"maximum\":16777215,\"minimum\":1,\"type\":\"integer\"},\n \"type\": {\"enum\":[\"vnet\"],\"type\":\"string\"},\n \"vnet\": {\"maxLength\":8,\"minLength\":2,\"pattern\":\"^[a-zA-Z][a-zA-Z0-9]*[a-zA-Z0-9]\\\\n?$\",\"patternSource\":\"[a-zA-Z][a-zA-Z0-9]*[a-zA-Z0-9]\",\"required\":true,\"type\":\"string\"},\n \"zone\": {\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/sdn/vnets/{vnet}/subnets\": {\n \"dhcp-dns-server\": {\"format\":\"ip\",\"type\":\"string\"},\n \"dhcp-range\": {\"each\":true,\"format\":\"pve-sdn-dhcp-range\",\"type\":\"array\"},\n \"dnszoneprefix\": {\"format\":\"dns-name\",\"type\":\"string\"},\n \"gateway\": {\"format\":\"ip\",\"type\":\"string\"},\n \"subnet\": {\"format\":\"pve-sdn-subnet-id\",\"required\":true,\"type\":\"string\"},\n \"type\": {\"enum\":[\"subnet\"],\"required\":true,\"type\":\"string\"},\n },\n \"pve:POST /cluster/sdn/zones\": {\n \"dhcp\": {\"enum\":[\"dnsmasq\"],\"type\":\"string\"},\n \"dnszone\": {\"format\":\"dns-name\",\"type\":\"string\"},\n \"exitnodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"exitnodes-primary\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"fabric\": {\"format\":\"pve-sdn-fabric-id\",\"type\":\"string\"},\n \"mac\": {\"format\":\"mac-addr\",\"type\":\"string\"},\n \"nodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"peers\": {\"format\":\"ip-list\",\"type\":\"string\"},\n \"rt-import\": {\"format\":\"pve-sdn-bgp-rt-list\",\"type\":\"string\"},\n \"secondary-controllers\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[a-zA-Z][a-zA-Z0-9_-]*[a-zA-Z0-9]\\\\n?$\",\"patternSource\":\"[a-zA-Z][a-zA-Z0-9_-]*[a-zA-Z0-9]\",\"type\":\"array\"},\n \"tag\": {\"minimum\":0,\"type\":\"integer\"},\n \"type\": {\"enum\":[\"evpn\",\"faucet\",\"qinq\",\"simple\",\"vlan\",\"vxlan\"],\"format\":\"pve-configid\",\"required\":true,\"type\":\"string\"},\n \"vlan-protocol\": {\"default\":\"802.1q\",\"enum\":[\"802.1q\",\"802.1ad\"],\"type\":\"string\"},\n \"vrf-vxlan\": {\"maximum\":16777215,\"minimum\":1,\"type\":\"integer\"},\n \"vxlan-port\": {\"default\":\"4789\",\"maximum\":65536,\"minimum\":1,\"type\":\"integer\"},\n \"zone\": {\"maxLength\":8,\"minLength\":2,\"pattern\":\"^[a-zA-Z][a-zA-Z0-9]*[a-zA-Z0-9]\\\\n?$\",\"patternSource\":\"[a-zA-Z][a-zA-Z0-9]*[a-zA-Z0-9]\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:PUT /cluster/sdn\": {},\n \"pve:PUT /cluster/sdn/vnets/{vnet}\": {\n \"alias\": {\"maxLength\":256,\"patternSource\":\"(?^i:[\\\\(\\\\)-_.\\\\w\\\\d\\\\s]{0,256})\",\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"tag\": {\"maximum\":16777215,\"minimum\":1,\"type\":\"integer\"},\n },\n \"pve:PUT /cluster/sdn/vnets/{vnet}/subnets/{subnet}\": {\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"dhcp-dns-server\": {\"format\":\"ip\",\"type\":\"string\"},\n \"dhcp-range\": {\"each\":true,\"format\":\"pve-sdn-dhcp-range\",\"type\":\"array\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"dnszoneprefix\": {\"format\":\"dns-name\",\"type\":\"string\"},\n \"gateway\": {\"format\":\"ip\",\"type\":\"string\"},\n },\n \"pve:PUT /cluster/sdn/zones/{zone}\": {\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"dhcp\": {\"enum\":[\"dnsmasq\"],\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"dnszone\": {\"format\":\"dns-name\",\"type\":\"string\"},\n \"exitnodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"exitnodes-primary\": {\"format\":\"pve-node\",\"type\":\"string\"},\n \"fabric\": {\"format\":\"pve-sdn-fabric-id\",\"type\":\"string\"},\n \"mac\": {\"format\":\"mac-addr\",\"type\":\"string\"},\n \"nodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"peers\": {\"format\":\"ip-list\",\"type\":\"string\"},\n \"rt-import\": {\"format\":\"pve-sdn-bgp-rt-list\",\"type\":\"string\"},\n \"secondary-controllers\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[a-zA-Z][a-zA-Z0-9_-]*[a-zA-Z0-9]\\\\n?$\",\"patternSource\":\"[a-zA-Z][a-zA-Z0-9_-]*[a-zA-Z0-9]\",\"type\":\"array\"},\n \"tag\": {\"minimum\":0,\"type\":\"integer\"},\n \"vlan-protocol\": {\"default\":\"802.1q\",\"enum\":[\"802.1q\",\"802.1ad\"],\"type\":\"string\"},\n \"vrf-vxlan\": {\"maximum\":16777215,\"minimum\":1,\"type\":\"integer\"},\n \"vxlan-port\": {\"default\":\"4789\",\"maximum\":65536,\"minimum\":1,\"type\":\"integer\"},\n },\n};\n",
|
|
23
|
+
"/**\n * Generated pve-manager parameter constraints for `/nodes/{node}/ceph` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_NODES_CEPH_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /nodes/{node}/ceph/fs/{name}\": {\n \"pg_num\": {\"default\":\"128\",\"maximum\":32768,\"minimum\":8,\"type\":\"integer\"},\n },\n \"pve:POST /nodes/{node}/ceph/mds/{name}\": {},\n \"pve:POST /nodes/{node}/ceph/mgr/{id}\": {},\n \"pve:POST /nodes/{node}/ceph/mon/{monid}\": {\n \"mon-address\": {\"format\":\"ip-list\",\"type\":\"string\"},\n },\n \"pve:POST /nodes/{node}/ceph/osd\": {\n \"db_dev_size\": {\"minimum\":1,\"type\":\"number\"},\n \"dev\": {\"required\":true,\"type\":\"string\"},\n \"osds-per-device\": {\"minimum\":1,\"type\":\"integer\"},\n \"wal_dev_size\": {\"minimum\":0.5,\"type\":\"number\"},\n },\n \"pve:POST /nodes/{node}/ceph/pool\": {\n \"application\": {\"default\":\"rbd\",\"enum\":[\"rbd\",\"cephfs\",\"rgw\"],\"type\":\"string\"},\n \"min_size\": {\"default\":\"2\",\"maximum\":7,\"minimum\":1,\"type\":\"integer\"},\n \"name\": {\"patternSource\":\"(?^:^[^:/\\\\s]+$)\",\"required\":true,\"type\":\"string\"},\n \"pg_autoscale_mode\": {\"default\":\"warn\",\"enum\":[\"on\",\"off\",\"warn\"],\"type\":\"string\"},\n \"pg_num\": {\"default\":\"128\",\"maximum\":32768,\"minimum\":1,\"type\":\"integer\"},\n \"pg_num_min\": {\"maximum\":32768,\"type\":\"integer\"},\n \"size\": {\"default\":\"3\",\"maximum\":7,\"minimum\":1,\"type\":\"integer\"},\n \"target_size\": {\"pattern\":\"^^(\\\\d+(\\\\.\\\\d+)?)([KMGT])?$\\\\n?$\",\"patternSource\":\"^(\\\\d+(\\\\.\\\\d+)?)([KMGT])?$\",\"type\":\"string\"},\n },\n \"pve:PUT /nodes/{node}/ceph/pool/{name}\": {\n \"application\": {\"enum\":[\"rbd\",\"cephfs\",\"rgw\"],\"type\":\"string\"},\n \"min_size\": {\"maximum\":7,\"minimum\":1,\"type\":\"integer\"},\n \"pg_autoscale_mode\": {\"enum\":[\"on\",\"off\",\"warn\"],\"type\":\"string\"},\n \"pg_num\": {\"maximum\":32768,\"minimum\":1,\"type\":\"integer\"},\n \"pg_num_min\": {\"maximum\":32768,\"type\":\"integer\"},\n \"size\": {\"maximum\":7,\"minimum\":1,\"type\":\"integer\"},\n \"target_size\": {\"pattern\":\"^^(\\\\d+(\\\\.\\\\d+)?)([KMGT])?$\\\\n?$\",\"patternSource\":\"^(\\\\d+(\\\\.\\\\d+)?)([KMGT])?$\",\"type\":\"string\"},\n },\n};\n",
|
|
24
|
+
"/**\n * Generated pve-manager parameter constraints for `/nodes/{node}/disks` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_NODES_DISKS_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /nodes/{node}/disks/zfs\": {\n \"ashift\": {\"default\":\"12\",\"maximum\":16,\"minimum\":9,\"type\":\"integer\"},\n \"compression\": {\"default\":\"on\",\"enum\":[\"on\",\"off\",\"gzip\",\"lz4\",\"lzjb\",\"zle\",\"zstd\"],\"type\":\"string\"},\n \"devices\": {\"format\":\"string-list\",\"required\":true,\"type\":\"string\"},\n \"name\": {\"format\":\"pve-storage-id\",\"required\":true,\"type\":\"string\"},\n \"raidlevel\": {\"enum\":[\"single\",\"mirror\",\"raid10\",\"raidz\",\"raidz2\",\"raidz3\",\"draid\",\"draid2\",\"draid3\"],\"required\":true,\"type\":\"string\"},\n },\n};\n",
|
|
25
|
+
"/**\n * Generated pve-manager parameter constraints for `/nodes/{node}/lxc` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_NODES_LXC_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /nodes/{node}/lxc\": {\n \"arch\": {\"default\":\"amd64\",\"enum\":[\"amd64\",\"i386\",\"arm64\",\"armhf\",\"riscv32\",\"riscv64\"],\"type\":\"string\"},\n \"bwlimit\": {\"default\":\"restore limit from datacenter or storage config\",\"minimum\":0,\"type\":\"number\"},\n \"cmode\": {\"default\":\"tty\",\"enum\":[\"shell\",\"console\",\"tty\"],\"type\":\"string\"},\n \"cores\": {\"maximum\":8192,\"minimum\":1,\"type\":\"integer\"},\n \"cpulimit\": {\"default\":\"0\",\"maximum\":8192,\"minimum\":0,\"type\":\"number\"},\n \"cpuunits\": {\"default\":\"cgroup v1: 1024, cgroup v2: 100\",\"maximum\":500000,\"minimum\":0,\"type\":\"integer\"},\n \"description\": {\"maxLength\":8192,\"type\":\"string\"},\n \"entrypoint\": {\"default\":\"/sbin/init\",\"patternSource\":\"(?^:[^\\\\x00-\\\\x08\\\\x0a-\\\\x1F\\\\x7F]+)\",\"type\":\"string\"},\n \"env\": {\"patternSource\":\"(?^:(?:\\\\w+=[^\\\\x00-\\\\x08\\\\x0a-\\\\x1F\\\\x7F]*)(?:\\\\0\\\\w+=[^\\\\x00-\\\\x08\\\\x0a-\\\\x1F\\\\x7F]*)*)\",\"type\":\"string\"},\n \"hookscript\": {\"format\":\"pve-volume-id\",\"type\":\"string\"},\n \"hostname\": {\"format\":\"dns-name\",\"maxLength\":255,\"type\":\"string\"},\n \"lock\": {\"enum\":[\"backup\",\"create\",\"destroyed\",\"disk\",\"fstrim\",\"migrate\",\"mounted\",\"rollback\",\"snapshot\",\"snapshot-delete\"],\"type\":\"string\"},\n \"memory\": {\"default\":\"512\",\"minimum\":16,\"type\":\"integer\"},\n \"nameserver\": {\"format\":\"lxc-ip-with-ll-iface-list\",\"type\":\"string\"},\n \"ostemplate\": {\"maxLength\":255,\"required\":true,\"type\":\"string\"},\n \"ostype\": {\"enum\":[\"debian\",\"devuan\",\"ubuntu\",\"centos\",\"fedora\",\"opensuse\",\"archlinux\",\"alpine\",\"gentoo\",\"nixos\",\"unmanaged\"],\"type\":\"string\"},\n \"password\": {\"minLength\":5,\"type\":\"string\"},\n \"pool\": {\"format\":\"pve-poolid\",\"type\":\"string\"},\n \"searchdomain\": {\"format\":\"dns-name-list\",\"type\":\"string\"},\n \"startup\": {\"format\":\"pve-startup-order\",\"type\":\"string\"},\n \"storage\": {\"default\":\"local\",\"format\":\"pve-storage-id\",\"type\":\"string\"},\n \"swap\": {\"default\":\"512\",\"minimum\":0,\"type\":\"integer\"},\n \"tags\": {\"format\":\"pve-tag-list\",\"type\":\"string\"},\n \"timezone\": {\"format\":\"pve-ct-timezone\",\"type\":\"string\"},\n \"tty\": {\"default\":\"2\",\"maximum\":6,\"minimum\":0,\"type\":\"integer\"},\n \"vmid\": {\"format\":\"pve-vmid\",\"maximum\":999999999,\"minimum\":100,\"required\":true,\"type\":\"integer\"},\n },\n \"pve:PUT /nodes/{node}/lxc/{vmid}/config\": {\n \"arch\": {\"default\":\"amd64\",\"enum\":[\"amd64\",\"i386\",\"arm64\",\"armhf\",\"riscv32\",\"riscv64\"],\"type\":\"string\"},\n \"cmode\": {\"default\":\"tty\",\"enum\":[\"shell\",\"console\",\"tty\"],\"type\":\"string\"},\n \"cores\": {\"maximum\":8192,\"minimum\":1,\"type\":\"integer\"},\n \"cpulimit\": {\"default\":\"0\",\"maximum\":8192,\"minimum\":0,\"type\":\"number\"},\n \"cpuunits\": {\"default\":\"cgroup v1: 1024, cgroup v2: 100\",\"maximum\":500000,\"minimum\":0,\"type\":\"integer\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"type\":\"string\"},\n \"description\": {\"maxLength\":8192,\"type\":\"string\"},\n \"digest\": {\"maxLength\":40,\"type\":\"string\"},\n \"entrypoint\": {\"default\":\"/sbin/init\",\"patternSource\":\"(?^:[^\\\\x00-\\\\x08\\\\x0a-\\\\x1F\\\\x7F]+)\",\"type\":\"string\"},\n \"env\": {\"patternSource\":\"(?^:(?:\\\\w+=[^\\\\x00-\\\\x08\\\\x0a-\\\\x1F\\\\x7F]*)(?:\\\\0\\\\w+=[^\\\\x00-\\\\x08\\\\x0a-\\\\x1F\\\\x7F]*)*)\",\"type\":\"string\"},\n \"hookscript\": {\"format\":\"pve-volume-id\",\"type\":\"string\"},\n \"hostname\": {\"format\":\"dns-name\",\"maxLength\":255,\"type\":\"string\"},\n \"lock\": {\"enum\":[\"backup\",\"create\",\"destroyed\",\"disk\",\"fstrim\",\"migrate\",\"mounted\",\"rollback\",\"snapshot\",\"snapshot-delete\"],\"type\":\"string\"},\n \"memory\": {\"default\":\"512\",\"minimum\":16,\"type\":\"integer\"},\n \"nameserver\": {\"format\":\"lxc-ip-with-ll-iface-list\",\"type\":\"string\"},\n \"ostype\": {\"enum\":[\"debian\",\"devuan\",\"ubuntu\",\"centos\",\"fedora\",\"opensuse\",\"archlinux\",\"alpine\",\"gentoo\",\"nixos\",\"unmanaged\"],\"type\":\"string\"},\n \"revert\": {\"format\":\"pve-configid-list\",\"type\":\"string\"},\n \"searchdomain\": {\"format\":\"dns-name-list\",\"type\":\"string\"},\n \"startup\": {\"format\":\"pve-startup-order\",\"type\":\"string\"},\n \"swap\": {\"default\":\"512\",\"minimum\":0,\"type\":\"integer\"},\n \"tags\": {\"format\":\"pve-tag-list\",\"type\":\"string\"},\n \"timezone\": {\"format\":\"pve-ct-timezone\",\"type\":\"string\"},\n \"tty\": {\"default\":\"2\",\"maximum\":6,\"minimum\":0,\"type\":\"integer\"},\n },\n \"pve:PUT /nodes/{node}/lxc/{vmid}/resize\": {\n \"digest\": {\"maxLength\":40,\"type\":\"string\"},\n \"disk\": {\"enum\":[\"rootfs\",\"mp0\",\"mp1\",\"mp2\",\"mp3\",\"mp4\",\"mp5\",\"mp6\",\"mp7\",\"mp8\",\"mp9\",\"mp10\",\"mp11\",\"mp12\",\"mp13\",\"mp14\",\"mp15\",\"mp16\",\"mp17\",\"mp18\",\"mp19\",\"mp20\",\"mp21\",\"mp22\",\"mp23\",\"mp24\",\"mp25\",\"mp26\",\"mp27\",\"mp28\",\"mp29\",\"mp30\",\"mp31\",\"mp32\",\"mp33\",\"mp34\",\"mp35\",\"mp36\",\"mp37\",\"mp38\",\"mp39\",\"mp40\",\"mp41\",\"mp42\",\"mp43\",\"mp44\",\"mp45\",\"mp46\",\"mp47\",\"mp48\",\"mp49\",\"mp50\",\"mp51\",\"mp52\",\"mp53\",\"mp54\",\"mp55\",\"mp56\",\"mp57\",\"mp58\",\"mp59\",\"mp60\",\"mp61\",\"mp62\",\"mp63\",\"mp64\",\"mp65\",\"mp66\",\"mp67\",\"mp68\",\"mp69\",\"mp70\",\"mp71\",\"mp72\",\"mp73\",\"mp74\",\"mp75\",\"mp76\",\"mp77\",\"mp78\",\"mp79\",\"mp80\",\"mp81\",\"mp82\",\"mp83\",\"mp84\",\"mp85\",\"mp86\",\"mp87\",\"mp88\",\"mp89\",\"mp90\",\"mp91\",\"mp92\",\"mp93\",\"mp94\",\"mp95\",\"mp96\",\"mp97\",\"mp98\",\"mp99\",\"mp100\",\"mp101\",\"mp102\",\"mp103\",\"mp104\",\"mp105\",\"mp106\",\"mp107\",\"mp108\",\"mp109\",\"mp110\",\"mp111\",\"mp112\",\"mp113\",\"mp114\",\"mp115\",\"mp116\",\"mp117\",\"mp118\",\"mp119\",\"mp120\",\"mp121\",\"mp122\",\"mp123\",\"mp124\",\"mp125\",\"mp126\",\"mp127\",\"mp128\",\"mp129\",\"mp130\",\"mp131\",\"mp132\",\"mp133\",\"mp134\",\"mp135\",\"mp136\",\"mp137\",\"mp138\",\"mp139\",\"mp140\",\"mp141\",\"mp142\",\"mp143\",\"mp144\",\"mp145\",\"mp146\",\"mp147\",\"mp148\",\"mp149\",\"mp150\",\"mp151\",\"mp152\",\"mp153\",\"mp154\",\"mp155\",\"mp156\",\"mp157\",\"mp158\",\"mp159\",\"mp160\",\"mp161\",\"mp162\",\"mp163\",\"mp164\",\"mp165\",\"mp166\",\"mp167\",\"mp168\",\"mp169\",\"mp170\",\"mp171\",\"mp172\",\"mp173\",\"mp174\",\"mp175\",\"mp176\",\"mp177\",\"mp178\",\"mp179\",\"mp180\",\"mp181\",\"mp182\",\"mp183\",\"mp184\",\"mp185\",\"mp186\",\"mp187\",\"mp188\",\"mp189\",\"mp190\",\"mp191\",\"mp192\",\"mp193\",\"mp194\",\"mp195\",\"mp196\",\"mp197\",\"mp198\",\"mp199\",\"mp200\",\"mp201\",\"mp202\",\"mp203\",\"mp204\",\"mp205\",\"mp206\",\"mp207\",\"mp208\",\"mp209\",\"mp210\",\"mp211\",\"mp212\",\"mp213\",\"mp214\",\"mp215\",\"mp216\",\"mp217\",\"mp218\",\"mp219\",\"mp220\",\"mp221\",\"mp222\",\"mp223\",\"mp224\",\"mp225\",\"mp226\",\"mp227\",\"mp228\",\"mp229\",\"mp230\",\"mp231\",\"mp232\",\"mp233\",\"mp234\",\"mp235\",\"mp236\",\"mp237\",\"mp238\",\"mp239\",\"mp240\",\"mp241\",\"mp242\",\"mp243\",\"mp244\",\"mp245\",\"mp246\",\"mp247\",\"mp248\",\"mp249\",\"mp250\",\"mp251\",\"mp252\",\"mp253\",\"mp254\",\"mp255\"],\"required\":true,\"type\":\"string\"},\n \"size\": {\"pattern\":\"^\\\\+?\\\\d+(\\\\.\\\\d+)?[KMGT]?\\\\n?$\",\"patternSource\":\"\\\\+?\\\\d+(\\\\.\\\\d+)?[KMGT]?\",\"required\":true,\"type\":\"string\"},\n },\n};\n",
|
|
26
|
+
"/**\n * Generated pve-manager parameter constraints for `/nodes/{node}/network` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_NODES_NETWORK_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /nodes/{node}/network\": {\n \"address\": {\"format\":\"ipv4\",\"type\":\"string\"},\n \"address6\": {\"format\":\"ipv6\",\"type\":\"string\"},\n \"bond-primary\": {\"format\":\"pve-iface\",\"type\":\"string\"},\n \"bond_mode\": {\"enum\":[\"balance-rr\",\"active-backup\",\"balance-xor\",\"broadcast\",\"802.3ad\",\"balance-tlb\",\"balance-alb\",\"balance-slb\",\"lacp-balance-slb\",\"lacp-balance-tcp\"],\"type\":\"string\"},\n \"bond_xmit_hash_policy\": {\"enum\":[\"layer2\",\"layer2+3\",\"layer3+4\"],\"type\":\"string\"},\n \"bridge_ports\": {\"format\":\"pve-iface-list\",\"type\":\"string\"},\n \"bridge_vids\": {\"format\":\"pve-vlan-id-or-range-list\",\"type\":\"string\"},\n \"cidr\": {\"format\":\"CIDRv4\",\"type\":\"string\"},\n \"cidr6\": {\"format\":\"CIDRv6\",\"type\":\"string\"},\n \"gateway\": {\"format\":\"ipv4\",\"type\":\"string\"},\n \"gateway6\": {\"format\":\"ipv6\",\"type\":\"string\"},\n \"iface\": {\"format\":\"pve-iface\",\"maxLength\":20,\"minLength\":2,\"required\":true,\"type\":\"string\"},\n \"mtu\": {\"maximum\":65520,\"minimum\":1280,\"type\":\"integer\"},\n \"netmask\": {\"format\":\"ipv4mask\",\"type\":\"string\"},\n \"netmask6\": {\"maximum\":128,\"minimum\":0,\"type\":\"integer\"},\n \"ovs_bonds\": {\"format\":\"pve-iface-list\",\"type\":\"string\"},\n \"ovs_bridge\": {\"format\":\"pve-iface\",\"type\":\"string\"},\n \"ovs_options\": {\"maxLength\":1024,\"type\":\"string\"},\n \"ovs_ports\": {\"format\":\"pve-iface-list\",\"type\":\"string\"},\n \"ovs_tag\": {\"maximum\":4094,\"minimum\":1,\"type\":\"integer\"},\n \"slaves\": {\"format\":\"pve-iface-list\",\"type\":\"string\"},\n \"type\": {\"enum\":[\"bridge\",\"bond\",\"eth\",\"alias\",\"vlan\",\"fabric\",\"OVSBridge\",\"OVSBond\",\"OVSPort\",\"OVSIntPort\",\"vnet\",\"unknown\"],\"required\":true,\"type\":\"string\"},\n \"vlan-id\": {\"maximum\":4094,\"minimum\":1,\"type\":\"integer\"},\n \"vlan-raw-device\": {\"format\":\"pve-iface\",\"type\":\"string\"},\n },\n \"pve:PUT /nodes/{node}/network\": {},\n \"pve:PUT /nodes/{node}/network/{iface}\": {\n \"address\": {\"format\":\"ipv4\",\"type\":\"string\"},\n \"address6\": {\"format\":\"ipv6\",\"type\":\"string\"},\n \"bond-primary\": {\"format\":\"pve-iface\",\"type\":\"string\"},\n \"bond_mode\": {\"enum\":[\"balance-rr\",\"active-backup\",\"balance-xor\",\"broadcast\",\"802.3ad\",\"balance-tlb\",\"balance-alb\",\"balance-slb\",\"lacp-balance-slb\",\"lacp-balance-tcp\"],\"type\":\"string\"},\n \"bond_xmit_hash_policy\": {\"enum\":[\"layer2\",\"layer2+3\",\"layer3+4\"],\"type\":\"string\"},\n \"bridge_ports\": {\"format\":\"pve-iface-list\",\"type\":\"string\"},\n \"bridge_vids\": {\"format\":\"pve-vlan-id-or-range-list\",\"type\":\"string\"},\n \"cidr\": {\"format\":\"CIDRv4\",\"type\":\"string\"},\n \"cidr6\": {\"format\":\"CIDRv6\",\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"type\":\"string\"},\n \"gateway\": {\"format\":\"ipv4\",\"type\":\"string\"},\n \"gateway6\": {\"format\":\"ipv6\",\"type\":\"string\"},\n \"mtu\": {\"maximum\":65520,\"minimum\":1280,\"type\":\"integer\"},\n \"netmask\": {\"format\":\"ipv4mask\",\"type\":\"string\"},\n \"netmask6\": {\"maximum\":128,\"minimum\":0,\"type\":\"integer\"},\n \"ovs_bonds\": {\"format\":\"pve-iface-list\",\"type\":\"string\"},\n \"ovs_bridge\": {\"format\":\"pve-iface\",\"type\":\"string\"},\n \"ovs_options\": {\"maxLength\":1024,\"type\":\"string\"},\n \"ovs_ports\": {\"format\":\"pve-iface-list\",\"type\":\"string\"},\n \"ovs_tag\": {\"maximum\":4094,\"minimum\":1,\"type\":\"integer\"},\n \"slaves\": {\"format\":\"pve-iface-list\",\"type\":\"string\"},\n \"type\": {\"enum\":[\"bridge\",\"bond\",\"eth\",\"alias\",\"vlan\",\"fabric\",\"OVSBridge\",\"OVSBond\",\"OVSPort\",\"OVSIntPort\",\"vnet\",\"unknown\"],\"required\":true,\"type\":\"string\"},\n \"vlan-id\": {\"maximum\":4094,\"minimum\":1,\"type\":\"integer\"},\n \"vlan-raw-device\": {\"format\":\"pve-iface\",\"type\":\"string\"},\n },\n};\n",
|
|
27
|
+
"/**\n * Generated pve-manager parameter constraints for `/nodes/{node}/qemu` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_NODES_QEMU_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /nodes/{node}/qemu\": {\n \"affinity\": {\"format\":\"pve-cpuset\",\"type\":\"string\"},\n \"amd-sev\": {\"format\":\"pve-qemu-sev-fmt\",\"type\":\"string\"},\n \"arch\": {\"enum\":[\"x86_64\",\"aarch64\"],\"type\":\"string\"},\n \"archive\": {\"maxLength\":255,\"type\":\"string\"},\n \"balloon\": {\"minimum\":0,\"type\":\"integer\"},\n \"bios\": {\"default\":\"seabios\",\"enum\":[\"seabios\",\"ovmf\"],\"type\":\"string\"},\n \"boot\": {\"format\":\"pve-qm-boot\",\"type\":\"string\"},\n \"bootdisk\": {\"format\":\"pve-qm-bootdisk\",\"pattern\":\"^(ide|sata|scsi|virtio)\\\\d+\\\\n?$\",\"patternSource\":\"(ide|sata|scsi|virtio)\\\\d+\",\"type\":\"string\"},\n \"bwlimit\": {\"default\":\"restore limit from datacenter or storage config\",\"minimum\":0,\"type\":\"integer\"},\n \"cdrom\": {\"format\":\"pve-qm-ide\",\"type\":\"string\"},\n \"cicustom\": {\"format\":\"pve-qm-cicustom\",\"type\":\"string\"},\n \"citype\": {\"enum\":[\"configdrive2\",\"nocloud\",\"opennebula\"],\"type\":\"string\"},\n \"cores\": {\"default\":\"1\",\"minimum\":1,\"type\":\"integer\"},\n \"cpu\": {\"format\":\"pve-vm-cpu-conf\",\"type\":\"string\"},\n \"cpulimit\": {\"default\":\"0\",\"maximum\":128,\"minimum\":0,\"type\":\"number\"},\n \"cpuunits\": {\"default\":\"cgroup v1: 1024, cgroup v2: 100\",\"maximum\":262144,\"minimum\":1,\"type\":\"integer\"},\n \"description\": {\"maxLength\":8192,\"type\":\"string\"},\n \"hookscript\": {\"format\":\"pve-volume-id\",\"type\":\"string\"},\n \"hostpci[n]\": {\"format\":\"pve-qm-hostpci\",\"type\":\"string\"},\n \"hotplug\": {\"default\":\"network,disk,usb\",\"format\":\"pve-hotplug-features\",\"type\":\"string\"},\n \"hugepages\": {\"enum\":[\"any\",\"2\",\"1024\"],\"type\":\"string\"},\n \"import-working-storage\": {\"format\":\"pve-storage-id\",\"type\":\"string\"},\n \"intel-tdx\": {\"format\":\"pve-qemu-tdx-fmt\",\"type\":\"string\"},\n \"ipconfig[n]\": {\"format\":\"pve-qm-ipconfig\",\"type\":\"string\"},\n \"keyboard\": {\"enum\":[\"de\",\"de-ch\",\"da\",\"en-gb\",\"en-us\",\"es\",\"fi\",\"fr\",\"fr-be\",\"fr-ca\",\"fr-ch\",\"hu\",\"is\",\"it\",\"ja\",\"lt\",\"mk\",\"nl\",\"no\",\"pl\",\"pt\",\"pt-br\",\"sv\",\"sl\",\"tr\"],\"type\":\"string\"},\n \"lock\": {\"enum\":[\"backup\",\"clone\",\"create\",\"migrate\",\"rollback\",\"snapshot\",\"snapshot-delete\",\"suspending\",\"suspended\"],\"type\":\"string\"},\n \"migrate_downtime\": {\"default\":\"0.1\",\"minimum\":0,\"type\":\"number\"},\n \"migrate_speed\": {\"default\":\"0\",\"minimum\":0,\"type\":\"integer\"},\n \"name\": {\"format\":\"dns-name\",\"type\":\"string\"},\n \"nameserver\": {\"format\":\"address-list\",\"type\":\"string\"},\n \"ostype\": {\"default\":\"other\",\"enum\":[\"other\",\"wxp\",\"w2k\",\"w2k3\",\"w2k8\",\"wvista\",\"win7\",\"win8\",\"win10\",\"win11\",\"l24\",\"l26\",\"solaris\"],\"type\":\"string\"},\n \"parallel[n]\": {\"pattern\":\"^/dev/parport\\\\d+|/dev/usb/lp\\\\d+\\\\n?$\",\"patternSource\":\"/dev/parport\\\\d+|/dev/usb/lp\\\\d+\",\"type\":\"string\"},\n \"pool\": {\"format\":\"pve-poolid\",\"type\":\"string\"},\n \"rng0\": {\"format\":\"pve-qm-rng\",\"type\":\"string\"},\n \"scsihw\": {\"default\":\"lsi\",\"enum\":[\"lsi\",\"lsi53c810\",\"virtio-scsi-pci\",\"virtio-scsi-single\",\"megasas\",\"pvscsi\"],\"type\":\"string\"},\n \"serial[n]\": {\"pattern\":\"^(/dev/[^,]+|socket)\\\\n?$\",\"patternSource\":\"(/dev/[^,]+|socket)\",\"type\":\"string\"},\n \"shares\": {\"default\":\"1000\",\"maximum\":50000,\"minimum\":0,\"type\":\"integer\"},\n \"smbios1\": {\"format\":\"pve-qm-smbios1\",\"maxLength\":512,\"type\":\"string\"},\n \"smp\": {\"default\":\"1\",\"minimum\":1,\"type\":\"integer\"},\n \"sockets\": {\"default\":\"1\",\"minimum\":1,\"type\":\"integer\"},\n \"sshkeys\": {\"format\":\"urlencoded\",\"type\":\"string\"},\n \"startdate\": {\"default\":\"now\",\"pattern\":\"^(now|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(T\\\\d{1,2}:\\\\d{1,2}:\\\\d{1,2})?)\\\\n?$\",\"patternSource\":\"(now|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(T\\\\d{1,2}:\\\\d{1,2}:\\\\d{1,2})?)\",\"type\":\"string\"},\n \"startup\": {\"format\":\"pve-startup-order\",\"type\":\"string\"},\n \"storage\": {\"format\":\"pve-storage-id\",\"type\":\"string\"},\n \"tags\": {\"format\":\"pve-tag-list\",\"type\":\"string\"},\n \"vcpus\": {\"default\":\"0\",\"minimum\":1,\"type\":\"integer\"},\n \"vmgenid\": {\"default\":\"1 (autogenerated)\",\"pattern\":\"^(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])\\\\n?$\",\"patternSource\":\"(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])\",\"type\":\"string\"},\n \"vmid\": {\"format\":\"pve-vmid\",\"maximum\":999999999,\"minimum\":100,\"required\":true,\"type\":\"integer\"},\n \"vmstatestorage\": {\"format\":\"pve-storage-id\",\"type\":\"string\"},\n \"watchdog\": {\"format\":\"pve-qm-watchdog\",\"type\":\"string\"},\n },\n \"pve:PUT /nodes/{node}/qemu/{vmid}/config\": {\n \"affinity\": {\"format\":\"pve-cpuset\",\"type\":\"string\"},\n \"amd-sev\": {\"format\":\"pve-qemu-sev-fmt\",\"type\":\"string\"},\n \"arch\": {\"enum\":[\"x86_64\",\"aarch64\"],\"type\":\"string\"},\n \"balloon\": {\"minimum\":0,\"type\":\"integer\"},\n \"bios\": {\"default\":\"seabios\",\"enum\":[\"seabios\",\"ovmf\"],\"type\":\"string\"},\n \"boot\": {\"format\":\"pve-qm-boot\",\"type\":\"string\"},\n \"bootdisk\": {\"format\":\"pve-qm-bootdisk\",\"pattern\":\"^(ide|sata|scsi|virtio)\\\\d+\\\\n?$\",\"patternSource\":\"(ide|sata|scsi|virtio)\\\\d+\",\"type\":\"string\"},\n \"cdrom\": {\"format\":\"pve-qm-ide\",\"type\":\"string\"},\n \"cicustom\": {\"format\":\"pve-qm-cicustom\",\"type\":\"string\"},\n \"citype\": {\"enum\":[\"configdrive2\",\"nocloud\",\"opennebula\"],\"type\":\"string\"},\n \"cores\": {\"default\":\"1\",\"minimum\":1,\"type\":\"integer\"},\n \"cpu\": {\"format\":\"pve-vm-cpu-conf\",\"type\":\"string\"},\n \"cpulimit\": {\"default\":\"0\",\"maximum\":128,\"minimum\":0,\"type\":\"number\"},\n \"cpuunits\": {\"default\":\"cgroup v1: 1024, cgroup v2: 100\",\"maximum\":262144,\"minimum\":1,\"type\":\"integer\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"type\":\"string\"},\n \"description\": {\"maxLength\":8192,\"type\":\"string\"},\n \"digest\": {\"maxLength\":40,\"type\":\"string\"},\n \"hookscript\": {\"format\":\"pve-volume-id\",\"type\":\"string\"},\n \"hostpci[n]\": {\"format\":\"pve-qm-hostpci\",\"type\":\"string\"},\n \"hotplug\": {\"default\":\"network,disk,usb\",\"format\":\"pve-hotplug-features\",\"type\":\"string\"},\n \"hugepages\": {\"enum\":[\"any\",\"2\",\"1024\"],\"type\":\"string\"},\n \"intel-tdx\": {\"format\":\"pve-qemu-tdx-fmt\",\"type\":\"string\"},\n \"ipconfig[n]\": {\"format\":\"pve-qm-ipconfig\",\"type\":\"string\"},\n \"keyboard\": {\"enum\":[\"de\",\"de-ch\",\"da\",\"en-gb\",\"en-us\",\"es\",\"fi\",\"fr\",\"fr-be\",\"fr-ca\",\"fr-ch\",\"hu\",\"is\",\"it\",\"ja\",\"lt\",\"mk\",\"nl\",\"no\",\"pl\",\"pt\",\"pt-br\",\"sv\",\"sl\",\"tr\"],\"type\":\"string\"},\n \"lock\": {\"enum\":[\"backup\",\"clone\",\"create\",\"migrate\",\"rollback\",\"snapshot\",\"snapshot-delete\",\"suspending\",\"suspended\"],\"type\":\"string\"},\n \"migrate_downtime\": {\"default\":\"0.1\",\"minimum\":0,\"type\":\"number\"},\n \"migrate_speed\": {\"default\":\"0\",\"minimum\":0,\"type\":\"integer\"},\n \"name\": {\"format\":\"dns-name\",\"type\":\"string\"},\n \"nameserver\": {\"format\":\"address-list\",\"type\":\"string\"},\n \"ostype\": {\"default\":\"other\",\"enum\":[\"other\",\"wxp\",\"w2k\",\"w2k3\",\"w2k8\",\"wvista\",\"win7\",\"win8\",\"win10\",\"win11\",\"l24\",\"l26\",\"solaris\"],\"type\":\"string\"},\n \"parallel[n]\": {\"pattern\":\"^/dev/parport\\\\d+|/dev/usb/lp\\\\d+\\\\n?$\",\"patternSource\":\"/dev/parport\\\\d+|/dev/usb/lp\\\\d+\",\"type\":\"string\"},\n \"revert\": {\"format\":\"pve-configid-list\",\"type\":\"string\"},\n \"rng0\": {\"format\":\"pve-qm-rng\",\"type\":\"string\"},\n \"scsihw\": {\"default\":\"lsi\",\"enum\":[\"lsi\",\"lsi53c810\",\"virtio-scsi-pci\",\"virtio-scsi-single\",\"megasas\",\"pvscsi\"],\"type\":\"string\"},\n \"serial[n]\": {\"pattern\":\"^(/dev/[^,]+|socket)\\\\n?$\",\"patternSource\":\"(/dev/[^,]+|socket)\",\"type\":\"string\"},\n \"shares\": {\"default\":\"1000\",\"maximum\":50000,\"minimum\":0,\"type\":\"integer\"},\n \"smbios1\": {\"format\":\"pve-qm-smbios1\",\"maxLength\":512,\"type\":\"string\"},\n \"smp\": {\"default\":\"1\",\"minimum\":1,\"type\":\"integer\"},\n \"sockets\": {\"default\":\"1\",\"minimum\":1,\"type\":\"integer\"},\n \"sshkeys\": {\"format\":\"urlencoded\",\"type\":\"string\"},\n \"startdate\": {\"default\":\"now\",\"pattern\":\"^(now|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(T\\\\d{1,2}:\\\\d{1,2}:\\\\d{1,2})?)\\\\n?$\",\"patternSource\":\"(now|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(T\\\\d{1,2}:\\\\d{1,2}:\\\\d{1,2})?)\",\"type\":\"string\"},\n \"startup\": {\"format\":\"pve-startup-order\",\"type\":\"string\"},\n \"tags\": {\"format\":\"pve-tag-list\",\"type\":\"string\"},\n \"vcpus\": {\"default\":\"0\",\"minimum\":1,\"type\":\"integer\"},\n \"vmgenid\": {\"default\":\"1 (autogenerated)\",\"pattern\":\"^(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])\\\\n?$\",\"patternSource\":\"(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])\",\"type\":\"string\"},\n \"vmstatestorage\": {\"format\":\"pve-storage-id\",\"type\":\"string\"},\n \"watchdog\": {\"format\":\"pve-qm-watchdog\",\"type\":\"string\"},\n },\n};\n",
|
|
28
|
+
"/**\n * Generated pve-manager parameter constraints for `/pools` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_POOLS_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /pools\": {\n \"poolid\": {\"format\":\"pve-poolid\",\"required\":true,\"type\":\"string\"},\n },\n \"pve:PUT /pools/{poolid}\": {\n \"storage\": {\"format\":\"pve-storage-id-list\",\"type\":\"string\"},\n \"vms\": {\"format\":\"pve-vmid-list\",\"type\":\"string\"},\n },\n};\n",
|
|
29
|
+
"/**\n * Generated pve-manager parameter constraints for `/storage` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pve-apidoc` — pve-manager 9.2.11/f6997e698c7933ea\n * sha256 9def8f13611184ee, read on a PVE cluster node from\n * /usr/share/pve-docs/api-viewer/apidoc.js\n *\n * 59 of this product's 258 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PVE_STORAGE_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pve:POST /storage\": {\n \"base\": {\"format\":\"pve-volume-id\",\"type\":\"string\"},\n \"blocksize\": {\"format\":\"pve-storage-zfs-blocksize\",\"type\":\"string\"},\n \"content\": {\"format\":\"pve-storage-content-list\",\"type\":\"string\"},\n \"content-dirs\": {\"format\":\"pve-dir-override-list\",\"type\":\"string\"},\n \"domain\": {\"maxLength\":256,\"type\":\"string\"},\n \"export\": {\"format\":\"pve-storage-path\",\"type\":\"string\"},\n \"fingerprint\": {\"pattern\":\"^([A-Fa-f0-9]{2}:){31}[A-Fa-f0-9]{2}\\\\n?$\",\"patternSource\":\"([A-Fa-f0-9]{2}:){31}[A-Fa-f0-9]{2}\",\"type\":\"string\"},\n \"format\": {\"enum\":[\"raw\",\"qcow2\",\"subvol\",\"vmdk\"],\"type\":\"string\"},\n \"fs-name\": {\"format\":\"pve-configid\",\"type\":\"string\"},\n \"max-protected-backups\": {\"default\":\"Unlimited for users with Datastore.Allocate privilege, 5 for other users\",\"minimum\":-1,\"type\":\"integer\"},\n \"monhost\": {\"format\":\"pve-storage-portal-dns-list\",\"type\":\"string\"},\n \"mountpoint\": {\"format\":\"pve-storage-path\",\"type\":\"string\"},\n \"nodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"options\": {\"format\":\"pve-storage-options\",\"type\":\"string\"},\n \"password\": {\"maxLength\":256,\"type\":\"string\"},\n \"path\": {\"format\":\"pve-storage-path\",\"type\":\"string\"},\n \"port\": {\"maximum\":65535,\"minimum\":1,\"type\":\"integer\"},\n \"portal\": {\"format\":\"pve-storage-portal-dns\",\"type\":\"string\"},\n \"preallocation\": {\"default\":\"metadata\",\"enum\":[\"off\",\"metadata\",\"falloc\",\"full\"],\"type\":\"string\"},\n \"prune-backups\": {\"format\":\"prune-backups\",\"type\":\"string\"},\n \"saferemove-stepsize\": {\"default\":\"32\",\"enum\":[\"1\",\"2\",\"4\",\"8\",\"16\",\"32\"],\"type\":\"integer\"},\n \"server\": {\"format\":\"pve-storage-server\",\"type\":\"string\"},\n \"smbversion\": {\"default\":\"default\",\"enum\":[\"default\",\"2.0\",\"2.1\",\"3\",\"3.0\",\"3.11\"],\"type\":\"string\"},\n \"storage\": {\"format\":\"pve-storage-id\",\"required\":true,\"type\":\"string\"},\n \"subdir\": {\"format\":\"pve-storage-path\",\"type\":\"string\"},\n \"thinpool\": {\"format\":\"pve-storage-vgname\",\"type\":\"string\"},\n \"type\": {\"enum\":[\"btrfs\",\"cephfs\",\"cifs\",\"dir\",\"esxi\",\"iscsi\",\"iscsidirect\",\"lvm\",\"lvmthin\",\"nfs\",\"pbs\",\"rbd\",\"zfs\",\"zfspool\"],\"required\":true,\"type\":\"string\"},\n \"vgname\": {\"format\":\"pve-storage-vgname\",\"type\":\"string\"},\n \"zfs-base-path\": {\"format\":\"pve-storage-path\",\"type\":\"string\"},\n },\n \"pve:PUT /storage/{storage}\": {\n \"blocksize\": {\"format\":\"pve-storage-zfs-blocksize\",\"type\":\"string\"},\n \"content\": {\"format\":\"pve-storage-content-list\",\"type\":\"string\"},\n \"content-dirs\": {\"format\":\"pve-dir-override-list\",\"type\":\"string\"},\n \"delete\": {\"format\":\"pve-configid-list\",\"maxLength\":4096,\"type\":\"string\"},\n \"digest\": {\"maxLength\":64,\"type\":\"string\"},\n \"domain\": {\"maxLength\":256,\"type\":\"string\"},\n \"fingerprint\": {\"pattern\":\"^([A-Fa-f0-9]{2}:){31}[A-Fa-f0-9]{2}\\\\n?$\",\"patternSource\":\"([A-Fa-f0-9]{2}:){31}[A-Fa-f0-9]{2}\",\"type\":\"string\"},\n \"format\": {\"enum\":[\"raw\",\"qcow2\",\"subvol\",\"vmdk\"],\"type\":\"string\"},\n \"fs-name\": {\"format\":\"pve-configid\",\"type\":\"string\"},\n \"max-protected-backups\": {\"default\":\"Unlimited for users with Datastore.Allocate privilege, 5 for other users\",\"minimum\":-1,\"type\":\"integer\"},\n \"monhost\": {\"format\":\"pve-storage-portal-dns-list\",\"type\":\"string\"},\n \"mountpoint\": {\"format\":\"pve-storage-path\",\"type\":\"string\"},\n \"nodes\": {\"format\":\"pve-node-list\",\"type\":\"string\"},\n \"options\": {\"format\":\"pve-storage-options\",\"type\":\"string\"},\n \"password\": {\"maxLength\":256,\"type\":\"string\"},\n \"port\": {\"maximum\":65535,\"minimum\":1,\"type\":\"integer\"},\n \"preallocation\": {\"default\":\"metadata\",\"enum\":[\"off\",\"metadata\",\"falloc\",\"full\"],\"type\":\"string\"},\n \"prune-backups\": {\"format\":\"prune-backups\",\"type\":\"string\"},\n \"saferemove-stepsize\": {\"default\":\"32\",\"enum\":[\"1\",\"2\",\"4\",\"8\",\"16\",\"32\"],\"type\":\"integer\"},\n \"server\": {\"format\":\"pve-storage-server\",\"type\":\"string\"},\n \"smbversion\": {\"default\":\"default\",\"enum\":[\"default\",\"2.0\",\"2.1\",\"3\",\"3.0\",\"3.11\"],\"type\":\"string\"},\n \"subdir\": {\"format\":\"pve-storage-path\",\"type\":\"string\"},\n \"zfs-base-path\": {\"format\":\"pve-storage-path\",\"type\":\"string\"},\n },\n};\n",
|
|
30
|
+
"/**\n * Generated proxmox-backup-server parameter constraints for `/config` — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n * Manifest entry: `pbs-apidoc` — proxmox-backup-server 4.2.6-1 (running 4.2.3)\n * sha256 274ab9f6fc075aea, read on a PBS host from\n * /usr/share/doc/proxmox-backup/html/api-viewer/apidoc.js\n *\n * 16 of this product's 144 POST/PUT endpoints are tabled across all areas: the ones\n * this package writes to, named in its own source. Every other vendor write endpoint is UNTABLED\n * and therefore unchecked at plan time.\n *\n * ⚠️ A `patternSource` with no `pattern` beside it is a rule that could NOT be carried into a\n * JavaScript RegExp faithfully (codegen/pattern.ts). It is recorded and NOT enforced.\n * ⚠️ `pattern` IS NOT THE VENDOR'S SPELLING. For PVE it is anchored, because PVE applies\n * `m/^$pattern$/` itself (JSONSchema.pm); for PBS it is the vendor's own, which already\n * carries its anchors. `patternSource` is the spelling to quote at a human — param-rules.ts.\n * ⚠️ `each: true` means the value rules describe every ELEMENT of a repeated key, because the\n * parameter is an array and stated its limits on `items`.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\n\nexport const PBS_CONFIG_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n \"pbs:POST /config/datastore\": {\n \"backing-device\": {\"pattern\":\"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$\",\"patternSource\":\"/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/\",\"type\":\"string\"},\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"keep-daily\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-hourly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-monthly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-weekly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-yearly\": {\"minimum\":1,\"type\":\"integer\"},\n \"name\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"notification-mode\": {\"default\":\"notification-system\",\"enum\":[\"legacy-sendmail\",\"notification-system\"],\"type\":\"string\"},\n \"notify-user\": {\"maxLength\":64,\"minLength\":3,\"pattern\":\"^(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"path\": {\"maxLength\":4096,\"minLength\":1,\"required\":true,\"type\":\"string\"},\n },\n \"pbs:POST /config/notifications/endpoints/sendmail\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"mailto\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"array\"},\n \"mailto-user\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"array\"},\n \"name\": {\"maxLength\":32,\"minLength\":2,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"origin\": {\"enum\":[\"user-created\",\"builtin\",\"modified-builtin\"],\"type\":\"string\"},\n },\n \"pbs:POST /config/notifications/endpoints/smtp\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"from-address\": {\"required\":true,\"type\":\"string\"},\n \"mailto\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"array\"},\n \"mailto-user\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"array\"},\n \"mode\": {\"default\":\"tls\",\"enum\":[\"insecure\",\"starttls\",\"tls\"],\"type\":\"string\"},\n \"name\": {\"maxLength\":32,\"minLength\":2,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"origin\": {\"enum\":[\"user-created\",\"builtin\",\"modified-builtin\"],\"type\":\"string\"},\n \"port\": {\"maximum\":65535,\"minimum\":0,\"type\":\"integer\"},\n \"server\": {\"required\":true,\"type\":\"string\"},\n },\n \"pbs:POST /config/notifications/endpoints/webhook\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"method\": {\"default\":\"post\",\"enum\":[\"post\",\"put\",\"get\"],\"required\":true,\"type\":\"string\"},\n \"name\": {\"maxLength\":32,\"minLength\":2,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"origin\": {\"enum\":[\"user-created\",\"builtin\",\"modified-builtin\"],\"type\":\"string\"},\n \"url\": {\"pattern\":\"^https?://(?:(?:(?:(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\\\-]*[a-zA-Z0-9])?)\\\\.)*(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\\\-]*[a-zA-Z0-9])?))|(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|\\\\[(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){6})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:::(?:(?:[0-9a-fA-F]{1,4}):){5})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){4})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,1}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){3})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,2}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){2})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,3}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){1})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,4}(?:[0-9a-fA-F]{1,4}))?::)(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,5}(?:[0-9a-fA-F]{1,4}))?::)(?:[0-9a-fA-F]{1,4}))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,6}(?:[0-9a-fA-F]{1,4}))?::))))\\\\]))(?::(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?)|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){6})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:::(?:(?:[0-9a-fA-F]{1,4}):){5})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){4})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,1}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){3})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,2}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){2})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,3}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){1})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,4}(?:[0-9a-fA-F]{1,4}))?::)(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,5}(?:[0-9a-fA-F]{1,4}))?::)(?:[0-9a-fA-F]{1,4}))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,6}(?:[0-9a-fA-F]{1,4}))?::))))(?:/[^\\u0000-\\u001f]*)?$\",\"patternSource\":\"/^https?://(?:(?:(?:(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\\\-]*[a-zA-Z0-9])?)\\\\.)*(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\\\-]*[a-zA-Z0-9])?))|(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|\\\\[(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){6})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:::(?:(?:[0-9a-fA-F]{1,4}):){5})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){4})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,1}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){3})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,2}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){2})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,3}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){1})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,4}(?:[0-9a-fA-F]{1,4}))?::)(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,5}(?:[0-9a-fA-F]{1,4}))?::)(?:[0-9a-fA-F]{1,4}))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,6}(?:[0-9a-fA-F]{1,4}))?::))))\\\\]))(?::(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?)|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){6})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:::(?:(?:[0-9a-fA-F]{1,4}):){5})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){4})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,1}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){3})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,2}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){2})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,3}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){1})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,4}(?:[0-9a-fA-F]{1,4}))?::)(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,5}(?:[0-9a-fA-F]{1,4}))?::)(?:[0-9a-fA-F]{1,4}))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,6}(?:[0-9a-fA-F]{1,4}))?::))))(?:/[^\\u0000-\\u001f]*)?$/\",\"required\":true,\"type\":\"string\"},\n },\n \"pbs:POST /config/notifications/matchers\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"mode\": {\"default\":\"all\",\"enum\":[\"all\",\"any\"],\"type\":\"string\"},\n \"name\": {\"maxLength\":32,\"minLength\":2,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"origin\": {\"enum\":[\"user-created\",\"builtin\",\"modified-builtin\"],\"type\":\"string\"},\n \"target\": {\"each\":true,\"maxLength\":32,\"minLength\":2,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"array\"},\n },\n \"pbs:POST /config/prune\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"id\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"keep-daily\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-hourly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-monthly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-weekly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-yearly\": {\"minimum\":1,\"type\":\"integer\"},\n \"max-depth\": {\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"schedule\": {\"required\":true,\"type\":\"string\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n },\n \"pbs:POST /config/sync\": {\n \"active-encryption-key\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"associated-key\": {\"each\":true,\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"array\"},\n \"burst-in\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"burst-out\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"id\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"max-depth\": {\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"owner\": {\"maxLength\":64,\"minLength\":3,\"pattern\":\"^(?:(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)|(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)!(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))$\",\"patternSource\":\"/^(?:(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)|(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)!(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))$/\",\"type\":\"string\"},\n \"rate-in\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"rate-out\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"remote\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"remote-ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"remote-store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"sync-direction\": {\"default\":\"pull\",\"enum\":[\"pull\",\"push\"],\"type\":\"string\"},\n \"transfer-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"worker-threads\": {\"default\":\"1\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n },\n \"pbs:POST /config/verify\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"id\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"max-depth\": {\"default\":\"7\",\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"outdated-after\": {\"minimum\":0,\"type\":\"integer\"},\n \"read-threads\": {\"default\":\"1\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"required\":true,\"type\":\"string\"},\n \"verify-threads\": {\"default\":\"4\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n },\n \"pbs:PUT /config/datastore/{name}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"delete\": {\"each\":true,\"enum\":[\"comment\",\"gc-schedule\",\"gc-on-unmount\",\"prune-schedule\",\"keep-last\",\"keep-hourly\",\"keep-daily\",\"keep-weekly\",\"keep-monthly\",\"keep-yearly\",\"verify-new\",\"notify-user\",\"notify\",\"notification-mode\",\"tuning\",\"maintenance-mode\",\"notification-thresholds\",\"counter-reset-schedule\"],\"type\":\"array\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"keep-daily\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-hourly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-monthly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-weekly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-yearly\": {\"minimum\":1,\"type\":\"integer\"},\n \"notification-mode\": {\"default\":\"notification-system\",\"enum\":[\"legacy-sendmail\",\"notification-system\"],\"type\":\"string\"},\n \"notify-user\": {\"maxLength\":64,\"minLength\":3,\"pattern\":\"^(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n },\n \"pbs:PUT /config/notifications/endpoints/sendmail/{name}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"delete\": {\"each\":true,\"enum\":[\"author\",\"comment\",\"disable\",\"from-address\",\"mailto\",\"mailto-user\"],\"type\":\"array\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"mailto\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"array\"},\n \"mailto-user\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"array\"},\n },\n \"pbs:PUT /config/notifications/endpoints/smtp/{name}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"delete\": {\"each\":true,\"enum\":[\"author\",\"comment\",\"disable\",\"mailto\",\"mailto-user\",\"password\",\"port\",\"username\"],\"type\":\"array\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"mailto\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"array\"},\n \"mailto-user\": {\"each\":true,\"maxLength\":64,\"minLength\":2,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"array\"},\n \"mode\": {\"default\":\"tls\",\"enum\":[\"insecure\",\"starttls\",\"tls\"],\"type\":\"string\"},\n \"port\": {\"maximum\":65535,\"minimum\":0,\"type\":\"integer\"},\n },\n \"pbs:PUT /config/notifications/endpoints/webhook/{name}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"delete\": {\"each\":true,\"enum\":[\"comment\",\"disable\",\"header\",\"body\",\"secret\"],\"type\":\"array\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"method\": {\"default\":\"post\",\"enum\":[\"post\",\"put\",\"get\"],\"type\":\"string\"},\n \"url\": {\"pattern\":\"^https?://(?:(?:(?:(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\\\-]*[a-zA-Z0-9])?)\\\\.)*(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\\\-]*[a-zA-Z0-9])?))|(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|\\\\[(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){6})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:::(?:(?:[0-9a-fA-F]{1,4}):){5})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){4})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,1}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){3})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,2}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){2})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,3}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){1})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,4}(?:[0-9a-fA-F]{1,4}))?::)(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,5}(?:[0-9a-fA-F]{1,4}))?::)(?:[0-9a-fA-F]{1,4}))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,6}(?:[0-9a-fA-F]{1,4}))?::))))\\\\]))(?::(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?)|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){6})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:::(?:(?:[0-9a-fA-F]{1,4}):){5})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){4})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,1}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){3})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,2}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){2})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,3}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){1})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,4}(?:[0-9a-fA-F]{1,4}))?::)(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,5}(?:[0-9a-fA-F]{1,4}))?::)(?:[0-9a-fA-F]{1,4}))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,6}(?:[0-9a-fA-F]{1,4}))?::))))(?:/[^\\u0000-\\u001f]*)?$\",\"patternSource\":\"/^https?://(?:(?:(?:(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\\\-]*[a-zA-Z0-9])?)\\\\.)*(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\\\-]*[a-zA-Z0-9])?))|(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|\\\\[(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){6})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:::(?:(?:[0-9a-fA-F]{1,4}):){5})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){4})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,1}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){3})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,2}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){2})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,3}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){1})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,4}(?:[0-9a-fA-F]{1,4}))?::)(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,5}(?:[0-9a-fA-F]{1,4}))?::)(?:[0-9a-fA-F]{1,4}))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,6}(?:[0-9a-fA-F]{1,4}))?::))))\\\\]))(?::(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?)|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){6})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:::(?:(?:[0-9a-fA-F]{1,4}):){5})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){4})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,1}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){3})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,2}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){2})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,3}(?:[0-9a-fA-F]{1,4}))?::(?:(?:[0-9a-fA-F]{1,4}):){1})(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,4}(?:[0-9a-fA-F]{1,4}))?::)(?:(?:(?:(?:(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])\\\\.){3}(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9]))|(?:[0-9a-fA-F]{1,4}):(?:[0-9a-fA-F]{1,4}))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,5}(?:[0-9a-fA-F]{1,4}))?::)(?:[0-9a-fA-F]{1,4}))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4}):){0,6}(?:[0-9a-fA-F]{1,4}))?::))))(?:/[^\\u0000-\\u001f]*)?$/\",\"type\":\"string\"},\n },\n \"pbs:PUT /config/notifications/matchers/{name}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"delete\": {\"each\":true,\"enum\":[\"comment\",\"disable\",\"invert-match\",\"match-calendar\",\"match-field\",\"match-severity\",\"mode\",\"target\"],\"type\":\"array\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"mode\": {\"default\":\"all\",\"enum\":[\"all\",\"any\"],\"type\":\"string\"},\n \"target\": {\"each\":true,\"maxLength\":32,\"minLength\":2,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"array\"},\n },\n \"pbs:PUT /config/prune/{id}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"delete\": {\"each\":true,\"enum\":[\"comment\",\"disable\",\"ns\",\"max-depth\",\"keep-last\",\"keep-hourly\",\"keep-daily\",\"keep-weekly\",\"keep-monthly\",\"keep-yearly\"],\"type\":\"array\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"keep-daily\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-hourly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-monthly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-weekly\": {\"minimum\":1,\"type\":\"integer\"},\n \"keep-yearly\": {\"minimum\":1,\"type\":\"integer\"},\n \"max-depth\": {\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n },\n \"pbs:PUT /config/sync/{id}\": {\n \"active-encryption-key\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"associated-key\": {\"each\":true,\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"array\"},\n \"burst-in\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"burst-out\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"delete\": {\"each\":true,\"enum\":[\"remote\",\"owner\",\"comment\",\"schedule\",\"remove-vanished\",\"group-filter\",\"rate-in\",\"burst-in\",\"rate-out\",\"burst-out\",\"ns\",\"remote-ns\",\"max-depth\",\"transfer-last\",\"encrypted-only\",\"verified-only\",\"run-on-mount\",\"unmount-on-done\",\"sync-direction\",\"worker-threads\",\"active-encryption-key\",\"associated-key\"],\"type\":\"array\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"max-depth\": {\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"owner\": {\"maxLength\":64,\"minLength\":3,\"pattern\":\"^(?:(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)|(?:[^\\\\s:/\\\\x00-\\\\x1f\\\\x7f]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)!(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))$\",\"patternSource\":\"/^(?:(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)|(?:[^\\\\s:/[:cntrl:]]+)@(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)!(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))$/\",\"type\":\"string\"},\n \"rate-in\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"rate-out\": {\"maxLength\":64,\"minLength\":1,\"type\":\"string\"},\n \"remote\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"remote-ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"remote-store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"sync-direction\": {\"default\":\"pull\",\"enum\":[\"pull\",\"push\"],\"type\":\"string\"},\n \"transfer-last\": {\"minimum\":1,\"type\":\"integer\"},\n \"worker-threads\": {\"default\":\"1\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n },\n \"pbs:PUT /config/verify/{id}\": {\n \"comment\": {\"maxLength\":128,\"pattern\":\"^[^\\\\x00-\\\\x1f\\\\x7f]*$\",\"patternSource\":\"/^[[:^cntrl:]]*$/\",\"type\":\"string\"},\n \"delete\": {\"each\":true,\"enum\":[\"ignore-verified\",\"comment\",\"schedule\",\"outdated-after\",\"ns\",\"max-depth\",\"read-threads\",\"verify-threads\"],\"type\":\"array\"},\n \"digest\": {\"pattern\":\"^[a-f0-9]{64}$\",\"patternSource\":\"/^[a-f0-9]{64}$/\",\"type\":\"string\"},\n \"max-depth\": {\"default\":\"7\",\"maximum\":7,\"minimum\":0,\"type\":\"integer\"},\n \"ns\": {\"maxLength\":256,\"pattern\":\"^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$\",\"patternSource\":\"/^(?:(?:(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)/){0,7}(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*))?$/\",\"type\":\"string\"},\n \"outdated-after\": {\"minimum\":0,\"type\":\"integer\"},\n \"read-threads\": {\"default\":\"1\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n \"store\": {\"maxLength\":32,\"minLength\":3,\"pattern\":\"^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$\",\"patternSource\":\"/^(?:[A-Za-z0-9_][A-Za-z0-9._\\\\-]*)$/\",\"type\":\"string\"},\n \"verify-threads\": {\"default\":\"4\",\"maximum\":32,\"minimum\":1,\"type\":\"integer\"},\n },\n};\n",
|
|
31
|
+
"/**\n * Every generated Proxmox constraint table, merged — DO NOT EDIT BY HAND.\n *\n * Run: bun codegen/constraints.ts\n *\n * ⚠️ MERGING IS SAFE ONLY BECAUSE EVERY KEY IS PREFIXED `pve:`/`pbs:`. PVE and PBS both publish\n * `PUT /access/acl` and `PUT /access/password` with DIFFERENT rules; an unprefixed merge would\n * silently enforce one product's limits on the other's objects.\n */\nimport type { EndpointConstraints } from '../../constraints.ts';\nimport { PVE_ACCESS_CONSTRAINTS } from './pve-access.ts';\nimport { PVE_CLUSTER_BACKUP_CONSTRAINTS } from './pve-cluster-backup.ts';\nimport { PVE_CLUSTER_CEPH_CONSTRAINTS } from './pve-cluster-ceph.ts';\nimport { PVE_CLUSTER_FIREWALL_CONSTRAINTS } from './pve-cluster-firewall.ts';\nimport { PVE_CLUSTER_HA_CONSTRAINTS } from './pve-cluster-ha.ts';\nimport { PVE_CLUSTER_METRICS_CONSTRAINTS } from './pve-cluster-metrics.ts';\nimport { PVE_CLUSTER_NOTIFICATIONS_CONSTRAINTS } from './pve-cluster-notifications.ts';\nimport { PVE_CLUSTER_REPLICATION_CONSTRAINTS } from './pve-cluster-replication.ts';\nimport { PVE_CLUSTER_SDN_CONSTRAINTS } from './pve-cluster-sdn.ts';\nimport { PVE_NODES_CEPH_CONSTRAINTS } from './pve-nodes-ceph.ts';\nimport { PVE_NODES_DISKS_CONSTRAINTS } from './pve-nodes-disks.ts';\nimport { PVE_NODES_LXC_CONSTRAINTS } from './pve-nodes-lxc.ts';\nimport { PVE_NODES_NETWORK_CONSTRAINTS } from './pve-nodes-network.ts';\nimport { PVE_NODES_QEMU_CONSTRAINTS } from './pve-nodes-qemu.ts';\nimport { PVE_POOLS_CONSTRAINTS } from './pve-pools.ts';\nimport { PVE_STORAGE_CONSTRAINTS } from './pve-storage.ts';\nimport { PBS_CONFIG_CONSTRAINTS } from './pbs-config.ts';\n\nexport const PROXMOX_CONSTRAINTS: Readonly<Record<string, EndpointConstraints>> = {\n ...PVE_ACCESS_CONSTRAINTS,\n ...PVE_CLUSTER_BACKUP_CONSTRAINTS,\n ...PVE_CLUSTER_CEPH_CONSTRAINTS,\n ...PVE_CLUSTER_FIREWALL_CONSTRAINTS,\n ...PVE_CLUSTER_HA_CONSTRAINTS,\n ...PVE_CLUSTER_METRICS_CONSTRAINTS,\n ...PVE_CLUSTER_NOTIFICATIONS_CONSTRAINTS,\n ...PVE_CLUSTER_REPLICATION_CONSTRAINTS,\n ...PVE_CLUSTER_SDN_CONSTRAINTS,\n ...PVE_NODES_CEPH_CONSTRAINTS,\n ...PVE_NODES_DISKS_CONSTRAINTS,\n ...PVE_NODES_LXC_CONSTRAINTS,\n ...PVE_NODES_NETWORK_CONSTRAINTS,\n ...PVE_NODES_QEMU_CONSTRAINTS,\n ...PVE_POOLS_CONSTRAINTS,\n ...PVE_STORAGE_CONSTRAINTS,\n ...PBS_CONFIG_CONSTRAINTS,\n};\n\n/**\n * sha256 of the merged table, truncated.\n *\n * ★ A DIGEST OF THE DATA, NOT OF THE FILE TEXT, so reformatting is not a stale generation while\n * changing a 128 to a 129 by hand is. `tests/schema-manifest.test.ts` recomputes it.\n */\nexport const PROXMOX_CONSTRAINTS_DIGEST = '33b42b52d396c9e0';\n",
|
|
19
32
|
"/**\n * Whether an update has anything to write — decided ONCE for every PVE and PBS family.\n *\n * ⛔ AN ADOPTED ROW IS RECONCILED EVEN WHEN ITS DIFF SAID `noop`, SO THIS GUARD IS WHAT MAKES\n * ADOPTION FREE. alchemy beta.79 Plan.ts sets `forceUpdateAfterAdoption` after the cold-start\n * probe and turns a `noop` diff into an update; Apply.ts then calls `reconcile` for the\n * `adopted` action exactly as for `update`. The plan prints `adopted` either way, so nothing on\n * screen tells a matching object from a drifting one. docs/adopt-verify.md has the table of\n * what each family does on that path.\n *\n * 🔴 THE FAMILY THAT DID NOT HAVE IT. `Proxmox.CephPool` wrote its own reconcile and PUT the\n * declared set whenever the pool existed, so adopting a pool that already matched sent\n * `setpool` and forked a worker on the live cluster (found 2026-09-21 by tracing every family).\n * resource.ts and pbs-datastore.ts each had the guard inline; three copies of one predicate is\n * how the fourth family forgets it, so it lives here and all three call it.\n *\n * ★ `matches` IS THE SAME PREDICATE `diff` USES, so \"the diff said noop\" and \"reconcile writes\n * nothing\" cannot disagree. A field left OUT of `matches` is one the family does not manage, so\n * its drift is not this write's to repair.\n *\n * ⚠️ AN EMPTY FORM IS ALSO NOT A WRITE. `updateForm` can legitimately answer `{}` — a storage\n * declaring only `storage`, `type` and its locator has no mutable field at all — and PUTting an\n * empty body is a pointless write at best and a 400 that reads as a broken provider at worst.\n * (Moved here from resource.ts with the guard it explains.)\n */\nexport const formToSend = <Attributes, Props, Form extends object>(\n matches: (attributes: Attributes, props: Props) => boolean,\n live: Attributes,\n props: Props,\n form: Form,\n): Form | undefined => (matches(live, props) || Object.keys(form).length === 0 ? undefined : form);\n",
|
|
20
33
|
"/**\n * A PBS webhook body that posts one Alertmanager v2 alert — `POST /api/v2/alerts` takes a JSON\n * ARRAY of `postableAlert`, each with a required `labels` map and an optional `annotations` map,\n * every value a string (Alertmanager's api/v2/openapi.yaml, read at main 2026-09-22).\n *\n * ★ PURE TEXT, NOT A RENDERER. PBS renders it, with Handlebars, at send time: `{{ severity }}`,\n * `{{ title }}`, `{{ message }}`, `{{ timestamp }}` and `{{ fields.<name> }}` are the\n * notification, and `json`, `escape` and `url-encode` are the only helpers\n * (proxmox-notify endpoints/webhook.rs `setup_handlebars`, read at HEAD 2026-09-22).\n *\n * ⛔ THERE IS NO ESCAPING UNLESS THE TEMPLATE ASKS FOR IT. The webhook's Handlebars registers\n * `no_escape` (\"There is no escape.\"), so `\"{{ message }}\"` breaks the JSON the first time a\n * task log holds a quote or a newline — and a failed job's message is exactly a task log. Every\n * value below goes through `json`, which emits a complete, quoted JSON string.\n * ⛔ A FIELD THE NOTIFICATION DOES NOT CARRY MUST NOT BE REACHED FOR BARE. `escape` on a missing\n * field fails the whole render, and the notification is then lost — not degraded. MEASURED\n * (2026-09-22) with handlebars-rust 5.1.2 and the webhook's three helpers restated from source:\n * `{{ escape fields.job-id }}` over no fields errors \"param at index 0 required but not found\";\n * `{{ json fields.job-id }}` renders `null`, valid JSON but not a label value. Garbage\n * collection has no `job-id`, and the UI's \"Test\" button sends a notification with NO fields at\n * all (lib.rs `test_target`). So every field-derived label sits inside\n * `{{#if}}`, ends with its own comma, and the labels after them are always there: whichever\n * fields are present, the object stays valid JSON.\n *\n * ⚠️ ALERTMANAGER RESOLVES IT BY ITSELF. With no `endsAt` it sets `now + resolve_timeout` (5m by\n * default, api/v2/api.go `postAlertsHandler`), and PBS never sends a resolve — so a receiver\n * with `send_resolved: true` reports \"resolved\" five minutes after a failure that nobody fixed.\n * Page on the firing notification; do not read the resolve as a recovery.\n * ⚠️ `severity` IS PBS'S WORD, not Alertmanager convention: `info`, `notice`, `warning`, `error`,\n * `unknown`. Route on `severity=\"error\"`, or relabel it in the Alertmanager config.\n */\n\nexport interface AlertmanagerBodyOptions {\n /** The `alertname` label. Default `PbsNotification`; `job_type` says which job it was. */\n readonly alertname?: string;\n /** The `source` label. Default `pbs`. */\n readonly source?: string;\n /** Optional `generatorURL` — an absolute link back to the PBS UI. */\n readonly generatorURL?: string;\n}\n\n/**\n * ⛔ AN OPTION IS PASTED INTO THE TEMPLATE, SO IT MAY NOT CARRY TEMPLATE SYNTAX. A `{{` in an\n * alert name would be rendered by PBS as an expression; a brace or backslash is refused here\n * rather than escaped, because Handlebars' escape rules are the renderer's, not this file's.\n */\nconst literal = (label: string, value: string): string => {\n if (/[{}\\\\]/.test(value) || value === '') {\n throw new Error(\n `alertmanagerAlertBody: ${label} must be non-empty and hold no '{', '}' or '\\\\'`,\n );\n }\n return JSON.stringify(value);\n};\n\n/**\n * ⛔ AN ABSOLUTE http(s) URL OR NOTHING. Alertmanager validates `generatorURL` as `format: uri`\n * (api/v2/openapi.yaml) and answers 422 for the WHOLE post — so a typo here would not degrade\n * one field, it would lose every notification, and only at send time.\n */\nconst absoluteUrl = (value: string): string => {\n const text = literal('generatorURL', value);\n let protocol = '';\n try {\n protocol = new URL(value).protocol;\n } catch {\n // not a URL at all: refused below, like a relative one\n }\n if (protocol !== 'https:' && protocol !== 'http:') {\n throw new Error('alertmanagerAlertBody: generatorURL must be an absolute http(s) URL');\n }\n return text;\n};\n\n/** Label name → notification field. ⚠️ Label names must match `[a-zA-Z_][a-zA-Z0-9_]*`. */\nconst FIELD_LABELS: readonly (readonly [label: string, field: string])[] = [\n ['job_type', 'type'],\n ['job_id', 'job-id'],\n ['datastore', 'datastore'],\n ['hostname', 'hostname'],\n];\n\n/**\n * The body template. Declare it on a webhook target together with `Content-Type:\n * application/json`, method `post`, and the URL `<alertmanager>/api/v2/alerts`.\n *\n * ★ `fields.job-id` IS A VALID PATH: Handlebars-rust 5 (the version proxmox-notify pins) allows\n * `-` in an identifier (grammar.pest `symbol_char`), as handlebars.js does. The same scratch\n * build rendered this template for a GC failure, a verify failure and a field-less test\n * notification, and each parsed as JSON with string-only labels. The adversarial review re-ran\n * it over the test file's whole matrix — every PBS event's fields x five severities x hostile\n * text, 840 renders with and without `generatorURL` — and every one parsed (2026-09-22).\n * ⚠️ THAT BUILD IS NOT PBS. The first real notification is the measurement that counts: send one\n * with the target's \"Test\" button and read what Alertmanager received.\n */\nexport const alertmanagerAlertBody = (options: AlertmanagerBodyOptions = {}): string => {\n const alertname = literal('alertname', options.alertname ?? 'PbsNotification');\n const source = literal('source', options.source ?? 'pbs');\n const labels = FIELD_LABELS.map(\n ([label, field]) =>\n ` {{#if fields.${field}}}\"${label}\": {{ json fields.${field} }},{{/if}}`,\n );\n const link =\n options.generatorURL === undefined\n ? ''\n : `,\\n \"generatorURL\": ${absoluteUrl(options.generatorURL)}`;\n return [\n '[',\n ' {',\n ' \"labels\": {',\n ...labels,\n ' \"severity\": {{ json severity }},',\n ` \"alertname\": ${alertname},`,\n ` \"source\": ${source}`,\n ' },',\n ' \"annotations\": {',\n ' \"summary\": {{ json title }},',\n ' \"description\": {{ json message }},',\n ' \"timestamp\": \"{{ timestamp }}\"',\n ` }${link}`,\n ' }',\n ']',\n '',\n ].join('\\n');\n};\n",
|
|
21
34
|
"/**\n * `Proxmox.ApiToken` — a PVE API token: the thing a service actually authenticates as.\n *\n * ⛔ THE SECRET EXISTS FOR ONE HTTP RESPONSE AND THEN NOWHERE. MEASURED from the cluster's own\n * schema (`/usr/share/pve-docs/api-viewer/apidoc.js` on node-b, 2026-09-13): POST returns\n * `[\"full-tokenid\",\"info\",\"value\"]`; PUT returns `[\"comment\",\"expire\",\"full-tokenid\",\"privsep\",\n * \"value\"]` with `value` present ONLY when `regenerate` was set; GET returns\n * `[\"comment\",\"expire\",\"privsep\"]` and never the secret. A live token agrees —\n * `GET /access/users/metrics@pve/token/exporter` answers `{\"expire\":0,\"privsep\":0}`. PVE says\n * it plainly on the POST: the value \"needs to be stored as it cannot be retrieved afterwards\".\n *\n * ⛔ SO A TOKEN THIS RESOURCE CREATES IS UNUSABLE, AND SAYING SO IS THE POINT OF THIS BLOCK. The\n * secret may not become an attribute: Alchemy persists attributes UNENCRYPTED, and this estate's\n * state store is a Postgres dumped nightly (the ⛔ in credentials.ts). So `reconcile` drops the\n * value on the floor, and what lands is a live credential in `/etc/pve/user.cfg` whose secret\n * nobody holds. No `regenerate` rescues it, because a regenerated value is discarded the same\n * way. The only escape is an operator who captured the value out of band — which this provider\n * gives them no way to do.\n * ★ SO POINT THIS FAMILY AT TOKENS THAT ALREADY EXIST. `comment`, `expire` and `privsep` are the\n * whole of a token's policy, and declaring them is real work: it is how `expire` stops being\n * whatever somebody typed in 2024. Mint NEW tokens where the secret can be caught — OpenBao's\n * `proxmox-c1` mount, or a human at `pveum user token add`.\n * ⚠️ THE CREATE PATH IS LEFT REACHABLE RATHER THAN STUBBED. A create that silently did nothing\n * would be the same lie as the stubbed delete resource.ts's ★ refuses; the honest arrangement\n * is a create that works and a header that says what it produces.\n *\n * ⛔ THERE IS NO `regenerate` PROP, AND IT IS NOT AN OVERSIGHT. PVE's own description: \"All users\n * of the previous secret will lose access after this operation.\" A prop for it would revoke a\n * live credential during a deploy whose plan said `update`, and hand back a replacement this\n * provider is obliged to throw away — breakage with no recovery. Rotation belongs to the mount\n * that owns the lease, or to a human who is watching.\n *\n * ⚠️ THERE IS A SECOND WRITER TO THESE OBJECTS AND IT IS THE ONE THIS PROVIDER RUNS ON. OpenBao's\n * `proxmox-c1` engine mints PVE tokens under `hf-read@pve` and `hf-provision@pve` — read from\n * `<estate>/platform/secrets/vault/plugin-proxmox/proxmox/`: `privsep=0` (client.go), `expire` set\n * to the lease deadline, and an id the plugin CHOOSES,\n * `hf-<role>-<actor>-<entity6>-<stamp>-<nonce>` (tokenname.go). Revocation and WAL rollback look\n * up that one exact id (wal.go); neither sweeps a prefix. So the two systems cannot collide on\n * one object by accident — but they share the USER: `hf-read@pve` held three live leases when\n * this was written, and `Proxmox.User` deleting that account takes every one of them with it.\n * ⛔ DO NOT DECLARE A TOKEN UNDER A MINT USER. `expire` is what makes that dangerous rather than\n * untidy: point this resource at a lease token with the wrong expire and a 300-second\n * credential becomes a permanent one — and on `hf-provision@pve` that credential carries\n * `Permissions.Modify` at `/`. docs/privileges.md records what those 27 privileges buy.\n *\n * ⚠️ PRIVILEGES, AND THE READ LANE CANNOT DO IT. MEASURED: all four methods on this path check\n * `['or', ['userid-param','self'], ['userid-group', ['User.Modify']]]` — with no `Sys.Audit`\n * alternative, unlike `GET /access/users/{userid}`, which has one. And\n * `pvesh get /access/permissions --userid hf-read@pve --path /access/groups` answers the seven\n * PVEAuditor audit privileges and no `User.Modify`, while `hf-provision@pve` has it. So the\n * 3600s read lease reaches at most its OWN account's tokens and is refused for every other\n * userid — which `read` in resource.ts folds into \"absent\", so the plan says create and the POST\n * then fails with \"Token already exists\". Hence `readRole: 'provision'` below, for the same\n * reason storage.ts, sdn-zone.ts and sdn-vnet.ts set it, at the same cost: every plan mints a\n * 300s non-renewable provision lease just to read.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { apiTokenSpec } from './api-token-form.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers, pveOperations } from './resource.ts';\n\nexport interface ApiTokenProps extends WithTarget {\n /**\n * The account that owns the token, realm-qualified: `iac@pve`, `root@pam`. Identity.\n *\n * ⚠️ THE ACCOUNT MUST ALREADY EXIST. Every method here runs `check_user_exist` first, so a\n * token named under a missing user fails the READ (folded to \"absent\") and then fails the\n * create with \"no such user\" — the honest error, arriving one step after the misleading plan.\n * Sequence a `Proxmox.User` ahead of it and the ordering takes care of itself.\n */\n userid: string;\n /**\n * The token's own name, unique within the account. Identity.\n *\n * ⚠️ PVE HAS NO RENAME, AND HERE THAT COSTS MORE THAN IT DOES FOR A USER. Editing this prop (or\n * `userid`) points `path` at a DIFFERENT token, which reads as absent and is then created —\n * leaving the old token LIVE, with its secret still working, invisible to the plan, while the\n * new one is the unusable kind described at the top. Rename by declaring a delete and a\n * create, the way user.ts says for an account.\n * ⛔ `diff` IS NOT OVERRIDDEN TO SAY `replace` THE WAY acl.ts DOES, AND THE DIFFERENCE IS THE\n * OBJECT. There, replace removes a grant that can be rebuilt from its own tuple; here it\n * would DELETE a live token — destroying an irrecoverable secret, and everything using it —\n * to tidy up a name. Leaving the old token standing is the lesser harm, and saying so here\n * is what keeps it from looking like an omission.\n * ⚠️ PATTERN `[A-Za-z][A-Za-z0-9.\\-_]+`: a letter first, two characters minimum. The schema\n * declares no maxLength; the OpenBao plugin assumes a conservative 64 rather than finding the\n * real limit in production, and so should anything else.\n */\n tokenid: string;\n /**\n * Free text in `pveum user token list` and the UI. Empty is how it is cleared — see `shape`.\n * ⚠️ ANY CHARACTER IS SAFE HERE, MEASURED, and it was worth checking: `user.cfg` is a\n * colon-delimited line, so a comment holding a colon or a newline is the obvious place for a\n * round-trip to lose a character and diff forever. PVE escapes it — `encode_text` turns\n * `a:b\\nc%d` into `a%3Ab%0Ac%25d` and `decode_text` gives it back byte for byte.\n */\n comment?: string;\n /**\n * Seconds since the epoch, or 0 for \"never expires\".\n *\n * ⛔ REQUIRED, AND THE SCHEMA WILL TELL YOU IT NEED NOT BE. It declares the default \"same as\n * user\", and the code never implements it: `generate_token` writes `expire` only when the\n * parameter is defined, `user.cfg` stores a token as `token:<id>:<expire>:<privsep>:<comment>:`\n * with no absent state, and the parser does `$expire = 0 if !$expire` (AccessControl.pm:1624).\n * MEASURED end to end: every token on this cluster that was created without an expire reads\n * back `\"expire\":0`. So \"same as user\" is documentation of an intention, not behaviour.\n * ⛔ WHICH IS WHY IT IS NOT OPTIONAL-DEFAULTING-TO-0. That spelling reads identically and is far\n * worse: an omitted `expire` would then quietly WRITE 0 over a live deadline, turning an\n * expiring credential into a permanent one, and the plan would call it an update. Requiring\n * the field makes \"this token never expires\" a sentence somebody had to type.\n */\n expire: number;\n /**\n * True keeps the token's privileges separate from its owner's — it gets NOTHING until an ACL\n * names `fullTokenid`. False gives it the owner's privileges entire.\n *\n * ⛔ REQUIRED FOR THE SAME REASON AS `expire`, AND THE BLAST RADIUS IS LARGER. PVE's API default\n * is 1, every token on this cluster is 0 (measured), and `user.cfg` materialises it either way\n * — `$privsep = $privsep ? 1 : 0` (AccessControl.pm:1622), so there is no unset state to\n * preserve. Were this optional, adopting a live token without mentioning `privsep` would plan\n * an update to 1 and STRIP a working credential of every privilege it has, silently: PVE\n * returns 401/403 to the service, nothing errors here, and `iac@pve!apply` simply stops\n * working. Required, so adopting a token is a sentence that states what it is.\n * ⚠️ AND `false` IS NOT A SHRUG. It is a token with its owner's whole privilege set; under a\n * provisioning account that is the account's full authority with a separate secret.\n */\n privsep: boolean;\n}\n\nexport interface ApiTokenAttributes {\n userid: string;\n tokenid: string;\n /** `<userid>!<tokenid>` — what `Proxmox.Acl` binds as its `ugid`. Derived; never compared. */\n fullTokenid: string;\n comment: string;\n expire: number;\n privsep: boolean;\n /**\n * ⛔ THERE IS NO `value` FIELD HERE AND THERE NEVER MAY BE. Adding one would write a working PVE\n * credential, in clear, into the state Postgres and into every nightly dump of it — the exact\n * leak metric-server.ts types its `token` as `never` to prevent. The read cannot supply one\n * anyway; only create and regenerate can, and both of those responses are dropped.\n */\n}\n\nexport interface ProxmoxApiToken extends Resource<\n 'Proxmox.ApiToken',\n ApiTokenProps,\n ApiTokenAttributes,\n never,\n PveRequirements\n> {}\n\n/**\n * ★ `retain` BY DEFAULT, AND THIS IS THE PLAINEST CASE FOR IT IN THE PACKAGE. A token's contents\n * are one irreplaceable secret: delete it and the value is gone, a replacement is a DIFFERENT\n * value, and every holder loses access the instant `cfs_write_file` returns — with no error\n * raised anywhere near them. The live cluster's tokens are `metrics@pve!exporter` (the PVE\n * exporter feeding VictoriaMetrics), `agent@pve!executor`, `iac@pve!apply`, `iac@pve!ro`,\n * `app@pve!app` and `mint@pve!engine` — the last being the parent credential the\n * OpenBao mount itself authenticates with, so orphaning that one would stop every plan in this\n * package, this resource included. `delete` is FULLY IMPLEMENTED (DELETE is a real method on\n * this path, measured) and runs the moment a caller opts in with `.pipe(RemovalPolicy.destroy())`.\n * See the ★ in resource.ts, which explains the convention once.\n */\nexport const ProxmoxApiToken = Resource<ProxmoxApiToken>('Proxmox.ApiToken', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst ops = pveOperations(apiTokenSpec);\n\n/**\n * ⛔ THIS FAMILY ADOPTS AND MANAGES TOKENS; IT REFUSES TO MINT ONE, AND THAT IS A DECISION RATHER\n * THAN A GAP. The secret exists ONLY in the create response — MEASURED from the schema: `POST`\n * returns `[\"full-tokenid\",\"info\",\"value\"]`, where `value` is \"API token value used for\n * authentication\", while `GET` returns `[\"comment\",\"expire\",\"privsep\"]` and never the secret.\n * It may not become an attribute, because Alchemy writes attributes to its state store\n * UNENCRYPTED into a Postgres that is dumped nightly.\n *\n * So a token created here would be A LIVE CREDENTIAL ON THE CLUSTER THAT NOBODY HOLDS: valid,\n * privileged, unusable, and indistinguishable from one somebody meant to keep. Regenerating does\n * not rescue it — a `regenerate` PUT returns the new value down the same discarded path. So\n * `reconcile` refuses by name and says what to do instead.\n *\n * ★ EVERYTHING ELSE STILL WORKS, AND IT IS THE HALF WORTH HAVING. Adopting an existing token and\n * converging its `comment`, `expire` and `privsep` are real operations: C1 carries SEVEN tokens\n * with `expire=0` and `privsep=0` made by clicks nobody recorded. Declaring those freezes the\n * set, and an eighth appearing shows up as drift.\n *\n * ⚠️ IF DECLARATIVE MINTING IS EVER WANTED, the missing piece is a secret SINK — write the value\n * straight into an OpenBao kv path and return only its address. That is a different resource\n * with a different contract; it must not be bolted onto this one.\n */\nconst handlers = {\n ...pveHandlers(apiTokenSpec),\n reconcile: Effect.fn(function* ({ news }: { news: ApiTokenProps }) {\n const live = yield* ops.read(news);\n if (live === undefined) {\n return yield* Effect.die(\n new Error(\n `${news.userid}!${news.tokenid}: this resource does not create API tokens. PVE returns ` +\n 'the secret only in the create response and it cannot be stored, so a token made here ' +\n 'would be a live credential nobody holds. Create it with `pveum user token add` and ' +\n 'capture the value, or mint a short-lived one from the OpenBao proxmox mount ' +\n '(`bao read proxmox-c1/creds/<role>`) -- then declare it here to manage it.',\n ),\n );\n }\n return yield* ops.reconcile(news);\n }),\n};\n\n/**\n * ⛔ `list` IS EMPTY, AND FOR THIS FAMILY THAT MATTERS MORE THAN THE GENERIC ARGUMENT IN\n * resource.ts. A token index handed to Alchemy would offer up `mint@pve!engine` and every\n * live OpenBao lease for adoption — and adoption is what makes a later plan willing to delete.\n * The mount's leases in particular appear and vanish on their own; anything that adopted one\n * would report drift against a credential that was never its to hold.\n */\nexport const ProxmoxApiTokenProvider = () =>\n Provider.effect(ProxmoxApiToken, Effect.succeed(ProxmoxApiToken.Provider.of(handlers)));\n",
|
|
22
35
|
"/**\n * How PVE spells a value on the wire, and how to get it back to something comparable.\n *\n * ⛔ EVERY FUNCTION HERE WAS FOUND IN TWO OR MORE RESOURCE FILES, AND THE COPIES HAD DRIFTED.\n * `num` was byte-identical in three files, so merging it is free. The boolean was NOT: four\n * files declared `const flag` and meant four different things by it, including one that ran the\n * other way round. That is the whole reason this module exists — not to save lines, but because\n * a name that means four things is a bug waiting for the person who assumes it means the first\n * one they read.\n *\n * ★ THE TWO DIRECTIONS ARE NAMED APART, DELIBERATELY. `bool` reads the wire and answers a\n * TypeScript boolean; `flag` writes a form field and answers PVE's `'1'`/`'0'`. `storage.ts`\n * held both under one name — `flag` there was the write direction while `flag` in three other\n * files was the read direction — which typechecks in both files and is exactly the confusion\n * that survives review.\n *\n * ⚠️ NOTHING HERE IS RESOURCE-SPECIFIC. A coercion that only one PVE object needs belongs in that\n * object's file, where its reasoning sits next to the field it serves. `role.ts` keeps its own\n * `canonical` for the same reason: that one sorts a privilege SET, which is a different idea\n * from `canonicalToken` below however similar the names read.\n */\n\n/** ⚠️ `typeof value === 'number'` and not `Number(value)`: PVE sends `null` for an unset field. */\nexport const num = (value: unknown, fallback: number) =>\n typeof value === 'number' ? value : fallback;\n\nexport const text = (value: unknown, fallback = '') =>\n typeof value === 'string' ? value : fallback;\n\n/**\n * A PVE boolean off the wire.\n *\n * ⛔ THE FOUR SPELLINGS THIS REPLACES, AND WHY THIS ONE IS A SUPERSET OF ALL OF THEM. Measured in\n * the files they came from: `backup-job` accepted `1 | true | '1'` with a caller's fallback;\n * `sdn-vnet` the same three with no fallback; `storage` accepted only `1 | true`; `user` ran the\n * value through `Number()` and asked `!== 0`. For the values PVE actually returns on these\n * endpoints — `0` and `1` — all four agree, so accepting the union changes no live answer.\n *\n * ⚠️ THE ONE REAL DIFFERENCE IS `2`. `user.ts` would have read any non-zero number as true; this\n * reads only `1`. No PVE field in this package is documented to return anything but 0 or 1, and\n * a field that did would be a flag with more than two states — which is not a boolean, and\n * should be read with `num` and named for what it is rather than quietly coerced here.\n *\n * ⚠️ `null`, `undefined` AND `''` ALL TAKE THE FALLBACK. An absent key means PVE's documented\n * default for that field, which is not always `false` — so the default is the CALLER's to state,\n * and `false` is only the fallback's own default for fields that really do default off.\n *\n * ⛔ THE EMPTY STRING IS ABSENT, NOT FALSE, AND GETTING THAT WRONG WRITES THE WRONG VALUE. A\n * metric server is a SectionConfig section and can hand back `''` for an unset flag. Read as\n * `false`, the two fields in this package that default ON — `backup-job.enabled` and\n * `user.enable` — would come back disabled, `matches` would report an update nobody asked for,\n * and the deploy would then WRITE `enabled=0` onto a live job. An empty string is not a\n * boolean; it is the absence of one.\n */\nexport const bool = (value: unknown, fallback = false) =>\n value === undefined || value === null || value === ''\n ? fallback\n : value === 1 || value === true || value === '1';\n\n/**\n * A boolean on its way INTO a PVE form.\n *\n * ⚠️ `undefined` IN, `undefined` OUT — an undeclared field must not be sent at all. Sending `'0'`\n * for a prop the caller never set would write PVE's default over whatever is live, turning an\n * omission into an edit.\n */\nexport const flag = (value?: boolean) => (value === undefined ? undefined : value ? '1' : '0');\n\n/**\n * A comma list PVE does not promise to give back in the order it was handed.\n *\n * ⚠️ `nodes` IS A SET AND PVE DOES NOT PRESERVE ITS ORDER. The zone plugin decodes the list into a\n * hash and re-encodes it by joining that hash's keys, so the string you get back is not the one\n * you sent. Sorting both sides is what stops a plan reporting an update because two names came\n * back the other way round. `peers` gets the same treatment: it is a mesh, not a queue.\n */\nexport const csv = (value: readonly string[] | string | undefined) =>\n (typeof value === 'string' ? value.split(',') : (value ?? []))\n .map((part) => part.trim())\n .filter((part) => part !== '')\n .sort()\n .join(',');\n\n/**\n * A comma list of guest IDs, in numeric order.\n *\n * ⚠️ ORDER IS NOT MEANING IN A GUEST LIST, SO IT MUST NOT BE A DIFF. PVE stores `vmid` as the\n * comma-separated string it was handed and gives it back the same way, so a declaration listing\n * the same guests in another order would otherwise be an update that rewrites the job to say\n * exactly what it already said. Both sides are sorted numerically before they meet.\n *\n * ⚠️ NUMERIC, NOT LEXICAL — this is why it is not `csv`. Sorted as text, `101` precedes `99`.\n */\nexport const guestList = (value: unknown): string => {\n const parts =\n typeof value === 'string'\n ? value.split(',')\n : Array.isArray(value)\n ? value.map((entry: unknown) => String(entry))\n : [];\n return parts\n .map((part) => Number(part.trim()))\n .filter((id) => Number.isFinite(id))\n .sort((left, right) => left - right)\n .map((id) => String(id))\n .join(',');\n};\n\n/** `yes`/`true`/`on` and their opposites are the same value as 1/0 to PVE; make them one string. */\nexport const canonicalToken = (raw: string) => {\n const lowered = raw.trim().toLowerCase();\n if (['1', 'yes', 'true', 'on'].includes(lowered)) return '1';\n if (['0', 'no', 'false', 'off'].includes(lowered)) return '0';\n return lowered;\n};\n\n/**\n * A PVE property string, flattened to one comparable form.\n *\n * ⚠️ A PROPERTY STRING AND ITS PARSED FORM ARE ONE VALUE IN TWO SHAPES, AND PVE HANDS BACK\n * WHICHEVER IT LIKES. MEASURED: `fleecing` is WRITTEN as the property string `fleecing=enabled=0`\n * and READ BACK as the nested object {\"enabled\":0}. Comparing raw values would report an update\n * on every plan for a job nobody touched — the same forever-diff a create-only field causes. So\n * both sides are flattened here, sorted, and only then compared. `prune-backups` goes through\n * the same funnel: same kind of value, the normalisation is free, and guessing which shape it\n * comes back in is exactly the guess that costs a permanent diff.\n *\n * ⚠️ `defaultKey` EXISTS BECAUSE `fleecing=1` IS SHORTHAND FOR `fleecing=enabled=1`. PVE's format\n * for it is written `[enabled=]<1|0>`, so a bare token is legal on write and comes back expanded\n * on read; unexpanded, the short and long spellings never compare equal. `prune-backups` has no\n * default key, so a bare token there is passed through as-is rather than invented into one.\n */\nexport const propertyString = (value: unknown, defaultKey?: string): string => {\n const pairs: [string, string][] =\n typeof value === 'string'\n ? value.split(',').map((part) => {\n const [key, ...rest] = part.split('=');\n return rest.length === 0 && defaultKey !== undefined\n ? [defaultKey, key ?? '']\n : [key ?? '', rest.join('=')];\n })\n : typeof value === 'object' && value !== null\n ? Object.entries(value).map(([key, entry]) => [key, String(entry)])\n : [];\n return pairs\n .map(([key, entry]) => [key.trim(), canonicalToken(entry)] as const)\n .filter(([key]) => key !== '')\n .map(([key, entry]) => `${key}=${entry}`)\n .sort()\n .join(',');\n};\n\n/**\n * An integer PVE may have spelled as a string.\n *\n * ⚠️ PVE MAY HAND BACK EITHER `8086` OR `\"8086\"`. These objects are SectionConfig sections, and the\n * integer check VALIDATES the parsed string rather than converting it, so whether a number\n * arrives as a number is a property of the version you are talking to. `'8086' === 8086` is\n * false, and that is an update consisting of nothing, reported on every plan forever.\n */\nexport const int = (value: unknown, fallback: number) => {\n if (typeof value === 'number') return value;\n const parsed = Number.parseInt(text(value), 10);\n return Number.isNaN(parsed) ? fallback : parsed;\n};\n\n/**\n * A PVE update form with its clear-list attached, and the two guaranteed not to overlap.\n *\n * ⛔ PVE DIES IF A KEY IS SET AND DELETED IN ONE CALL, AND SIX FAMILIES HERE BUILT THAT FORM BY\n * HAND. Measured in the shipped Perl on node-b — `PVE::SectionConfig::delete_from_config`, line 1860:\n *\n * die \"cannot set and delete property '$k' at the same time!\\n\"\n * if defined($new_options->{$k});\n *\n * `defined` is the test, not truth, so `strict=0` or `comment=''` in the body counts as SET. The\n * concrete failure: a live node-affinity rule with `strict 1` and a declaration carrying\n * `strict: false`. `matches` reports an update; the clear-list has to name `strict`, because a\n * PUT of `strict=0` merges a falsy value the plugin still stores; and if the body also emits\n * `strict: '0'` the PUT is refused. The update never lands, the next plan reports the same\n * update, and every deploy 500s — forever.\n *\n * ★ SO DISJOINTNESS IS ENFORCED HERE RATHER THAN REASONED ABOUT SIX TIMES. A cleared key is\n * dropped from the body: CLEAR WINS. That is the right way round because a clear-list entry is\n * computed from \"this prop is absent, and absent means remove it\", which is a deliberate\n * statement, while the body's value for the same key is the coercion of that same absence —\n * `flag(false)` is `'0'`, and `'0'` is exactly what must not be sent.\n *\n * ⚠️ UNDEFINED VALUES ARE DROPPED TOO, so a form builder may emit `undefined` for a field it does\n * not set without the caller filtering first. `new URLSearchParams` would otherwise stringify it\n * to the literal text \"undefined\".\n */\nexport const withClears = (\n fields: Record<string, string | undefined>,\n clear: readonly string[],\n): Record<string, string> => {\n const cleared = new Set(clear);\n const kept: Record<string, string> = {};\n for (const [key, value] of Object.entries(fields)) {\n if (value !== undefined && !cleared.has(key)) kept[key] = value;\n }\n return cleared.size === 0 ? kept : { ...kept, delete: [...cleared].join(',') };\n};\n",
|
|
23
|
-
"import type { ApiTokenAttributes, ApiTokenProps } from './api-token.ts';\n/**\n * What a write to a PVE API token actually does, and the form that does it.\n *\n * ★ SPLIT OUT OF api-token.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is the one\n * metric-server-form.ts draws: this file answers \"what does PVE store when this form arrives\",\n * api-token.ts answers \"what a token is and when it has changed\". Nothing here reads the\n * cluster and nothing here decides a diff.\n *\n * ⚠️ THE `import type` BACK TO api-token.ts IS A CYCLE ON PAPER ONLY. It is type-only, so it is\n * erased before anything runs and `ApiTokenProps` stays in the file that declares the resource.\n *\n * ⛔ THE WRITE SEMANTICS BELOW ARE READ FROM THE CLUSTER'S OWN PERL RATHER THAN INFERRED FROM THE\n * API SCHEMA — `/usr/share/perl5/PVE/API2/User.pm`, subs `generate_token` and\n * `update_token_info`, read over SSH on node-b on 2026-09-13. The two disagree in exactly the place\n * that costs a forever-diff: the schema gives `expire` the default \"same as user\", and the code\n * never implements it. api-token.ts records what that means.\n */\nimport type { PveSpec } from './resource.ts';\nimport { bool, int, text } from './values.ts';\n\n/**\n * `<userid>!<tokenid>` — the name the rest of PVE calls this object by.\n *\n * ★ IT IS WHAT AN ACL BINDS. `Proxmox.Acl` with `type: 'token'` wants exactly this string as its\n * `ugid`, so reporting it as an attribute is what lets a privilege-separated token be granted\n * anything at all without somebody retyping the join by hand.\n *\n * ⚠️ DERIVED, NOT READ. MEASURED: `GET /access/users/metrics@pve/token/exporter` answers\n * `{\"expire\":0,\"privsep\":0}` and echoes back neither half of its own identity. So this is built\n * from props, cannot disagree with the path the read used, and is therefore true by\n * construction — which is why it is reported and never compared.\n */\nexport const fullTokenid = (props: ApiTokenProps) => `${props.userid}!${props.tokenid}`;\n\n/**\n * Everything a token has, in the form PVE wants. Create and update send exactly this.\n *\n * ⛔ `userid` AND `tokenid` ARE NOT IN IT, and that is not an omission. Both are path segments of\n * `access/users/{userid}/token/{tokenid}` — which is the path the POST goes to as well as the\n * PUT — so a second copy in the body can only ever disagree with the path it was sent to.\n * metric-server.ts omits its `id` for the same reason and says so there.\n *\n * ⚠️ EVERY FIELD IS SENT ON EVERY WRITE, THE EMPTY COMMENT INCLUDED, BECAUSE A PUT MERGES.\n * MEASURED in `update_token_info`: `$token->{comment} = $param->{comment} if defined(...)`\n * followed by `delete $token->{comment} if (!length $token->{comment})`. So `comment=` is how a\n * comment is CLEARED; omitting it leaves the old text in place while `matches` goes on asking\n * for an update the update cannot make. user.ts writes the same reasoning out for `groups`.\n * ⚠️ THE `delete=` PARAMETER IS NOT USED AND WOULD NOT HELP. Its whitelist in that sub is\n * literally `my $deletable = { comment => 1 };`, and anything else answers\n * \"unknown option '<k>'\" — so `expire` and `privsep` have no clear path at all, which is the\n * other half of why both are required props rather than optional ones.\n *\n * ⚠️ `flag()` FROM values.ts IS DELIBERATELY NOT USED FOR `privsep`. Its entire job is turning an\n * UNDECLARED boolean into `undefined` so the field is omitted from the form; `privsep` is a\n * required prop precisely so that it is never undeclared, so there is nothing for it to do and\n * its `string | undefined` return would have to be asserted away to fit `Record<string,string>`.\n * An assertion here would be a claim about the prop that the type already makes properly.\n */\nexport const shape = (props: ApiTokenProps): Record<string, string> => ({\n comment: props.comment ?? '',\n expire: String(props.expire),\n privsep: props.privsep ? '1' : '0',\n});\n\nexport const apiTokenSpec: PveSpec<ApiTokenProps, ApiTokenAttributes> = {\n // ⛔ THE ITEM READ NEEDS `User.Modify`, WHICH THE AUDITOR-SHAPED READ LEASE DOES NOT HAVE. The\n // measurement and the consequence are in the header's last ⚠️; the mechanism — a silent\n // \"absent\" rather than a 403 — is the ⛔ on `readRole` in resource.ts.\n readRole: 'provision',\n /**\n * ★ NOTHING PVE REPORTS HERE IS UNWRITABLE, WHICH IS UNUSUAL IN THIS PACKAGE AND IS WHY\n * `matches` can compare everything it reads. The GET returns exactly `comment`, `expire` and\n * `privsep`; PUT accepts exactly those three. There is no autoscaled field, no server-assigned\n * id, no set whose order PVE reshuffles — the three killers the neighbouring files carry\n * ⚠️s about do not arise. The two identity fields and `fullTokenid` come from props and are\n * true by construction, so comparing them would be theatre.\n * ⚠️ `undefined` WHEN NEITHER FLAG IS THERE, BECAUSE BOTH ARE ALWAYS THERE. `user.cfg`\n * materialises `expire` and `privsep` for every token it stores, so an answer carrying neither\n * is not a token — and without this guard `bool`/`int` would invent one out of their fallbacks\n * and the read-back guard in `reconcile` could never fire.\n */\n attributes: (live, props) => {\n const expire = live['expire'];\n const privsep = live['privsep'];\n if (expire === undefined && privsep === undefined) return undefined;\n return {\n comment: text(live['comment']),\n expire: int(expire, 0),\n fullTokenid: fullTokenid(props),\n /** ⚠️ FALLBACK `true`, MATCHING PVE'S API DEFAULT — unreachable given the guard above, but\n * wrong in the safe direction if a future release stops emitting the field. */\n privsep: bool(privsep, true),\n tokenid: props.tokenid,\n userid: props.userid,\n };\n },\n /**\n * ⛔ THE SAME STRING AS `path`, AND NOT A TYPO. A token is POSTed to its OWN url, not to the\n * collection: MEASURED, `/access/users/{userid}/token` carries a GET and nothing else, while\n * POST lives on `{tokenid}`. metric-server.ts has the identical shape for the identical\n * reason and records the experiment there.\n */\n collection: (props) => `access/users/${props.userid}/token/${props.tokenid}`,\n createForm: shape,\n /**\n * ⚠️ EXACTLY THE THREE FIELDS A PUT CAN PUT BACK, WHICH IS ALSO EXACTLY WHAT THE GET REPORTS.\n * MEASURED, by replaying these two functions over the live GET bodies of all four token\n * shapes this cluster has: `metrics@pve!exporter` (no comment), `iac@pve!apply` and\n * `app@pve!app` (commented), and a live OpenBao lease under `hf-read@pve` (a real\n * non-zero `expire`, offered as both `1789327175` and `\"1789327175\"` since `int` must not care\n * which). All four answer noop; flipping `privsep` answers update; an empty body answers\n * absent. ⚠️ THAT IS THE COMPARISON, NOT THE ENGINE — no `alchemy plan` was run against this\n * family, and nothing here was written to the cluster.\n */\n matches: (attributes, props) =>\n attributes.comment === (props.comment ?? '') &&\n attributes.expire === props.expire &&\n attributes.privsep === props.privsep,\n path: (props) => `access/users/${props.userid}/token/${props.tokenid}`,\n updateForm: shape,\n};\n",
|
|
36
|
+
"import type { ApiTokenAttributes, ApiTokenProps } from './api-token.ts';\n/**\n * What a write to a PVE API token actually does, and the form that does it.\n *\n * ★ SPLIT OUT OF api-token.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is the one\n * metric-server-form.ts draws: this file answers \"what does PVE store when this form arrives\",\n * api-token.ts answers \"what a token is and when it has changed\". Nothing here reads the\n * cluster and nothing here decides a diff.\n *\n * ⚠️ THE `import type` BACK TO api-token.ts IS A CYCLE ON PAPER ONLY. It is type-only, so it is\n * erased before anything runs and `ApiTokenProps` stays in the file that declares the resource.\n *\n * ⛔ THE WRITE SEMANTICS BELOW ARE READ FROM THE CLUSTER'S OWN PERL RATHER THAN INFERRED FROM THE\n * API SCHEMA — `/usr/share/perl5/PVE/API2/User.pm`, subs `generate_token` and\n * `update_token_info`, read over SSH on node-b on 2026-09-13. The two disagree in exactly the place\n * that costs a forever-diff: the schema gives `expire` the default \"same as user\", and the code\n * never implements it. api-token.ts records what that means.\n */\nimport type { PveSpec } from './resource.ts';\nimport { bool, int, text } from './values.ts';\n\n/**\n * `<userid>!<tokenid>` — the name the rest of PVE calls this object by.\n *\n * ★ IT IS WHAT AN ACL BINDS. `Proxmox.Acl` with `type: 'token'` wants exactly this string as its\n * `ugid`, so reporting it as an attribute is what lets a privilege-separated token be granted\n * anything at all without somebody retyping the join by hand.\n *\n * ⚠️ DERIVED, NOT READ. MEASURED: `GET /access/users/metrics@pve/token/exporter` answers\n * `{\"expire\":0,\"privsep\":0}` and echoes back neither half of its own identity. So this is built\n * from props, cannot disagree with the path the read used, and is therefore true by\n * construction — which is why it is reported and never compared.\n */\nexport const fullTokenid = (props: ApiTokenProps) => `${props.userid}!${props.tokenid}`;\n\n/**\n * Everything a token has, in the form PVE wants. Create and update send exactly this.\n *\n * ⛔ `userid` AND `tokenid` ARE NOT IN IT, and that is not an omission. Both are path segments of\n * `access/users/{userid}/token/{tokenid}` — which is the path the POST goes to as well as the\n * PUT — so a second copy in the body can only ever disagree with the path it was sent to.\n * metric-server.ts omits its `id` for the same reason and says so there.\n *\n * ⚠️ EVERY FIELD IS SENT ON EVERY WRITE, THE EMPTY COMMENT INCLUDED, BECAUSE A PUT MERGES.\n * MEASURED in `update_token_info`: `$token->{comment} = $param->{comment} if defined(...)`\n * followed by `delete $token->{comment} if (!length $token->{comment})`. So `comment=` is how a\n * comment is CLEARED; omitting it leaves the old text in place while `matches` goes on asking\n * for an update the update cannot make. user.ts writes the same reasoning out for `groups`.\n * ⚠️ THE `delete=` PARAMETER IS NOT USED AND WOULD NOT HELP. Its whitelist in that sub is\n * literally `my $deletable = { comment => 1 };`, and anything else answers\n * \"unknown option '<k>'\" — so `expire` and `privsep` have no clear path at all, which is the\n * other half of why both are required props rather than optional ones.\n *\n * ⚠️ `flag()` FROM values.ts IS DELIBERATELY NOT USED FOR `privsep`. Its entire job is turning an\n * UNDECLARED boolean into `undefined` so the field is omitted from the form; `privsep` is a\n * required prop precisely so that it is never undeclared, so there is nothing for it to do and\n * its `string | undefined` return would have to be asserted away to fit `Record<string,string>`.\n * An assertion here would be a claim about the prop that the type already makes properly.\n */\nexport const shape = (props: ApiTokenProps): Record<string, string> => ({\n comment: props.comment ?? '',\n expire: String(props.expire),\n privsep: props.privsep ? '1' : '0',\n});\n\nexport const apiTokenSpec: PveSpec<ApiTokenProps, ApiTokenAttributes> = {\n // ⛔ THE ITEM READ NEEDS `User.Modify`, WHICH THE AUDITOR-SHAPED READ LEASE DOES NOT HAVE. The\n // measurement and the consequence are in the header's last ⚠️; the mechanism — a silent\n // \"absent\" rather than a 403 — is the ⛔ on `readRole` in resource.ts.\n readRole: 'provision',\n /**\n * ★ NOTHING PVE REPORTS HERE IS UNWRITABLE, WHICH IS UNUSUAL IN THIS PACKAGE AND IS WHY\n * `matches` can compare everything it reads. The GET returns exactly `comment`, `expire` and\n * `privsep`; PUT accepts exactly those three. There is no autoscaled field, no server-assigned\n * id, no set whose order PVE reshuffles — the three killers the neighbouring files carry\n * ⚠️s about do not arise. The two identity fields and `fullTokenid` come from props and are\n * true by construction, so comparing them would be theatre.\n * ⚠️ `undefined` WHEN NEITHER FLAG IS THERE, BECAUSE BOTH ARE ALWAYS THERE. `user.cfg`\n * materialises `expire` and `privsep` for every token it stores, so an answer carrying neither\n * is not a token — and without this guard `bool`/`int` would invent one out of their fallbacks\n * and the read-back guard in `reconcile` could never fire.\n */\n attributes: (live, props) => {\n const expire = live['expire'];\n const privsep = live['privsep'];\n if (expire === undefined && privsep === undefined) return undefined;\n return {\n comment: text(live['comment']),\n expire: int(expire, 0),\n fullTokenid: fullTokenid(props),\n /** ⚠️ FALLBACK `true`, MATCHING PVE'S API DEFAULT — unreachable given the guard above, but\n * wrong in the safe direction if a future release stops emitting the field. */\n privsep: bool(privsep, true),\n tokenid: props.tokenid,\n userid: props.userid,\n };\n },\n /**\n * ⛔ THE SAME STRING AS `path`, AND NOT A TYPO. A token is POSTed to its OWN url, not to the\n * collection: MEASURED, `/access/users/{userid}/token` carries a GET and nothing else, while\n * POST lives on `{tokenid}`. metric-server.ts has the identical shape for the identical\n * reason and records the experiment there.\n */\n collection: (props) => `access/users/${props.userid}/token/${props.tokenid}`,\n createForm: shape,\n /** The vendor rules both forms are checked against at plan time — resource-spec.ts. */\n endpoint: {\n create: 'pve:POST /access/users/{userid}/token/{tokenid}',\n update: 'pve:PUT /access/users/{userid}/token/{tokenid}',\n },\n /**\n * ⚠️ EXACTLY THE THREE FIELDS A PUT CAN PUT BACK, WHICH IS ALSO EXACTLY WHAT THE GET REPORTS.\n * MEASURED, by replaying these two functions over the live GET bodies of all four token\n * shapes this cluster has: `metrics@pve!exporter` (no comment), `iac@pve!apply` and\n * `app@pve!app` (commented), and a live OpenBao lease under `hf-read@pve` (a real\n * non-zero `expire`, offered as both `1789327175` and `\"1789327175\"` since `int` must not care\n * which). All four answer noop; flipping `privsep` answers update; an empty body answers\n * absent. ⚠️ THAT IS THE COMPARISON, NOT THE ENGINE — no `alchemy plan` was run against this\n * family, and nothing here was written to the cluster.\n */\n matches: (attributes, props) =>\n attributes.comment === (props.comment ?? '') &&\n attributes.expire === props.expire &&\n attributes.privsep === props.privsep,\n path: (props) => `access/users/${props.userid}/token/${props.tokenid}`,\n updateForm: shape,\n};\n",
|
|
24
37
|
"/**\n * `Proxmox.BackupJob` — the vzdump schedule, declared. The one object whose drift nothing else on\n * the cluster reveals.\n *\n * ★ WHY THIS ONE EARNS ITS PLACE. A hand-made vzdump job drifts silently: a schedule moved by an\n * hour, a retention trimmed to `keep-last=1`, or `enabled 0` set during a maintenance window and\n * never set back. All three look like a healthy cluster from every angle except a restore.\n * Declared here, `diff` reads the LIVE job on every plan, so a hand edit surfaces as\n * `1 to update` that day rather than on the day somebody needs last night's backup and finds a\n * week-old one, or none.\n *\n * ⛔ PVE GENERATES `id` WHEN YOU OMIT IT, WHICH MAKES OMITTING IT THE WORST BUG THIS RESOURCE\n * COULD CARRY. PVE's own API schema says so in one parenthesis — `id`: \"Job ID (will be\n * autogenerated).\" — and a generated id looks like `backup-f56d8ab7-d524`. A resource that\n * POSTed without an id would create a SECOND job beside the first on EVERY reconcile, and the\n * read-back in `pveOperations` would still pass, because a job did appear: that check asks \"is\n * the object there\", not \"is it the one I meant\". So `id` is a REQUIRED prop and is always sent\n * on create. To adopt a job that already exists, declare its existing id; any other id is a new\n * job beside it, not an adoption.\n * ★ AN EXPLICIT id ALSO CLOSES A SECOND HOLE. `pveOperations.read` turns ANY failed GET into\n * \"absent\" — an expired lease, a 403, a node that did not answer — and reconcile then POSTs.\n * With an id, PVE refuses a duplicate section id and the deploy fails loudly; without one, that\n * same swallowed error would have quietly doubled the cluster's backup jobs.\n *\n * ⚠️ `Sys.Modify` ON `/` IS THE GATE AND IT IS NOT A SMALL GRANT. PVE has no Backup.* privilege:\n * create, update and delete of a vzdump job are all checked against `Sys.Modify` at `/` — the\n * same privilege that covers node network configuration. A credential that can declare backup\n * jobs can also rewrite the cluster's networking. That is PVE's granularity rather than a choice\n * made here, and it is exactly why widening a role for this should be a decision rather than a\n * reaction to a 403.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool, guestList, propertyString, text } from './values.ts';\n\n/**\n * ⚠️ THE PROPS ARE PVE'S OWN KEY NAMES, DASHES INCLUDED. `notes-template` rather than\n * `notesTemplate` is deliberate: `createForm` is then a copy rather than a translation table,\n * and a translation table is one more place for a key to be renamed and silently never sent. A\n * field that is never sent reads back as whatever it already was, which is indistinguishable\n * from \"PVE ignored me\".\n */\nexport interface BackupJobProps extends WithTarget {\n /** ⛔ The primary key, REQUIRED. Never let PVE generate it — see the ⛔ above. */\n id: string;\n /** A subset of systemd calendar events, e.g. `01:00` or `mon..fri 02:30`. */\n schedule: string;\n /** Target storage id. That storage's own retention applies unless `prune-backups` overrides it. */\n storage?: string;\n mode?: 'snapshot' | 'suspend' | 'stop';\n /** PVE's default is enabled. A disabled job is a backup that silently is not happening. */\n enabled?: boolean;\n /** Back up every guest. ⚠️ Pick exactly one of `all`, `vmid` and `pool` — see `shape` below. */\n all?: boolean;\n vmid?: readonly number[];\n pool?: string;\n /** Guest ids to skip. PVE reads this as \"all guests except these\", so it belongs with `all`. */\n exclude?: readonly number[];\n /** Retention, as a property string: `keep-daily=7,keep-weekly=4`. */\n 'prune-backups'?: string;\n /** Supports {{cluster}}, {{guestname}}, {{node}} and {{vmid}}. Single line only. */\n 'notes-template'?: string;\n 'notification-mode'?: 'auto' | 'legacy-sendmail' | 'notification-system';\n /** Run a missed job as soon as the scheduler is back. */\n 'repeat-missed'?: boolean;\n /** VM-only fleecing, as a property string: `enabled=1,storage=<id>`. See `propertyString`. */\n fleecing?: string;\n comment?: string;\n}\n\nexport interface BackupJobAttributes {\n id: string;\n schedule: string;\n storage: string;\n mode: string;\n enabled: boolean;\n all: boolean;\n /** Sorted and comma-joined, so re-ordering a declaration is not a diff. See `guestList`. */\n vmid: string;\n pool: string;\n exclude: string;\n /** Flattened and sorted — never the raw value PVE returned. See `propertyString`. */\n 'prune-backups': string;\n 'notes-template': string;\n 'notification-mode': string;\n 'repeat-missed': boolean;\n fleecing: string;\n comment: string;\n /**\n * PVE's own estimate, epoch seconds. REPORTED, NEVER COMPARED: `next-run` in `matches` would\n * report an update every time the clock passed a scheduled run. It is here so a plan can show\n * that a job is still due — a `next-run` of 0 is a job the scheduler will not run.\n */\n 'next-run': number;\n}\n\nexport interface ProxmoxBackupJob extends Resource<\n 'Proxmox.BackupJob',\n BackupJobProps,\n BackupJobAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxBackupJob = Resource<ProxmoxBackupJob>('Proxmox.BackupJob');\n\n/**\n * The form for both create and update.\n *\n * ⛔ A FIELD THE DECLARATION LEAVES OUT IS NOT SENT, AND THAT IS LOAD BEARING. PVE's PUT can only\n * SET a key; clearing one needs `delete=<key>`, which this resource deliberately never sends.\n * `updateForm` is handed the props and nothing else, so a `delete` list could only be \"every\n * field you did not declare\" — and on a job adopted out of the UI that would silently strip\n * settings this resource does not even model, `compress`, `mailto` and `bwlimit` among them, on\n * the first deploy. Leaving undeclared fields alone is the smaller lie, and `matches` tells the\n * same lie consistently: it does not compare what it cannot change.\n *\n * ⚠️ THE FIVE FIELDS PVE DOCUMENTS A DEFAULT FOR ARE ALWAYS SENT AND ALWAYS COMPARED — `enabled`\n * (1), `all` (0), `mode` (snapshot), `notification-mode` (auto), `repeat-missed` (0). That is\n * what makes a hand-flipped `enabled 0` show up as drift rather than as silence, and it is also\n * what stops those five looping: a field compared against a default must be sent as that\n * default, or every plan reports an update the PUT never performs. The cost is worth reading\n * before the first deploy against an adopted job — an omitted `all` is a declaration that this\n * job backs up NOTHING, and PVE accepts a job that selects nothing without a word. Read that\n * first plan; do not deploy it unseen.\n */\nconst shape = (props: BackupJobProps) => ({\n all: props.all === true ? '1' : '0',\n enabled: props.enabled === false ? '0' : '1',\n mode: props.mode ?? 'snapshot',\n 'notification-mode': props['notification-mode'] ?? 'auto',\n 'repeat-missed': props['repeat-missed'] === true ? '1' : '0',\n schedule: props.schedule,\n ...(props.comment === undefined ? {} : { comment: props.comment }),\n ...(props.exclude === undefined ? {} : { exclude: guestList(props.exclude) }),\n ...(props.fleecing === undefined ? {} : { fleecing: props.fleecing }),\n ...(props['notes-template'] === undefined ? {} : { 'notes-template': props['notes-template'] }),\n ...(props.pool === undefined ? {} : { pool: props.pool }),\n ...(props['prune-backups'] === undefined ? {} : { 'prune-backups': props['prune-backups'] }),\n ...(props.storage === undefined ? {} : { storage: props.storage }),\n ...(props.vmid === undefined ? {} : { vmid: guestList(props.vmid) }),\n});\n\nconst handlers = pveHandlers<BackupJobProps, BackupJobAttributes>({\n /**\n * ⛔ NO \"IS IT REALLY THERE\" GUARD, ON PURPOSE. Returning undefined for a job whose JSON is\n * missing some key would be a guess about which keys PVE echoes back, and a wrong guess here\n * does not read as absent-and-harmless: `reconcile` would POST, and POST creates ANOTHER job.\n * Absence is decided by the API declining to answer, which `pveOperations.read` already\n * handles — not by a key being missing from an answer that did arrive.\n */\n attributes: (live, props) => ({\n all: bool(live['all'], false),\n comment: text(live['comment'], ''),\n enabled: bool(live['enabled'], true),\n exclude: guestList(live['exclude']),\n fleecing: propertyString(live['fleecing'], 'enabled'),\n id: props.id,\n mode: text(live['mode'], 'snapshot'),\n 'next-run': typeof live['next-run'] === 'number' ? live['next-run'] : 0,\n 'notes-template': text(live['notes-template'], ''),\n 'notification-mode': text(live['notification-mode'], 'auto'),\n pool: text(live['pool'], ''),\n 'prune-backups': propertyString(live['prune-backups']),\n 'repeat-missed': bool(live['repeat-missed'], false),\n schedule: text(live['schedule'], ''),\n storage: text(live['storage'], ''),\n vmid: guestList(live['vmid']),\n }),\n collection: () => 'cluster/backup',\n /** ⛔ `id` IS SENT AND IS NOT OPTIONAL. Everything above depends on PVE not inventing one. */\n createForm: (props) => ({ ...shape(props), id: props.id }),\n /** Each line reads \"not declared, or equal\"; the five defaulted fields have no undeclared case. */\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: { create: 'pve:POST /cluster/backup', update: 'pve:PUT /cluster/backup/{id}' },\n matches: (attributes, props) =>\n attributes.schedule === props.schedule &&\n attributes.enabled === (props.enabled !== false) &&\n attributes.all === (props.all === true) &&\n attributes.mode === (props.mode ?? 'snapshot') &&\n attributes['notification-mode'] === (props['notification-mode'] ?? 'auto') &&\n attributes['repeat-missed'] === (props['repeat-missed'] === true) &&\n (props.storage === undefined || attributes.storage === props.storage) &&\n (props.pool === undefined || attributes.pool === props.pool) &&\n (props.comment === undefined || attributes.comment === props.comment) &&\n (props.vmid === undefined || attributes.vmid === guestList(props.vmid)) &&\n (props.exclude === undefined || attributes.exclude === guestList(props.exclude)) &&\n (props['notes-template'] === undefined ||\n attributes['notes-template'] === props['notes-template']) &&\n (props['prune-backups'] === undefined ||\n attributes['prune-backups'] === propertyString(props['prune-backups'])) &&\n (props.fleecing === undefined ||\n attributes.fleecing === propertyString(props.fleecing, 'enabled')),\n path: (props) => `cluster/backup/${props.id}`,\n updateForm: shape,\n});\n\n/**\n* ⛔ Empty for the same reason as every other resource here: adoption must be explicit.\n* `GET /cluster/backup` would hand back the job somebody made by hand, and adopting it\n* implicitly would give Alchemy the right to DELETE a cluster's only backup schedule the\n* first time a line left a stack file.\n \n *\n* ⚠️ DELETING THE JOB DOES NOT DELETE THE BACKUPS, AND THAT CUTS BOTH WAYS. The archives\n* stay on the storage, so this looks harmless today; it is destructive in six months,\n* when the schedule that should have replaced them has not run since. PVE will not\n* refuse this the way it refuses to delete a pool still holding guests — nothing depends\n* on a backup job — so the plan line is the only warning anyone gets.\n \n */\nexport const ProxmoxBackupJobProvider = () =>\n Provider.effect(ProxmoxBackupJob, Effect.succeed(ProxmoxBackupJob.Provider.of(handlers)));\n",
|
|
25
|
-
"/**\n * `Proxmox.CephDaemon` — a Ceph monitor, manager or metadata server on one node.\n *\n * ★ ONE RESOURCE FOR THREE KINDS, AND THE SCHEMAS WERE CHECKED BEFORE THAT WAS DECIDED. Parsed\n * from `/usr/share/pve-docs/api-viewer/apidoc.js` on node-b, 2026-09-13: mon, mgr and mds each\n * expose GET on the collection and POST + DELETE on the id below it, each returns\n * name/host/state/addr/ceph_version*, and the only divergence is the create parameter\n * (`mon-address` / none / `hotstandby`) and the read-only extras (mon: quorum, rank; mds: rank,\n * fs_name, standby_replay). Three near-identical files would have carried the traps below three\n * times over and let them drift — the argument resource.ts already makes for the factory itself.\n *\n * ⛔ THE ONLY GET IS THE COLLECTION'S, AND IT ANSWERS FOR THE WHOLE CLUSTER. MEASURED:\n * `GET /nodes/node-b/ceph/mon` returns node-b, node-c AND node-d, and `GET /nodes/node-c/ceph/mon` returns the same\n * three IN A DIFFERENT ORDER — [node-d,node-b,node-c] from node-b, [node-c,node-b,node-d] from node-c — so a row is found by\n * `name` and NEVER by position, and the `{node}` in the path is only the node being ASKED.\n * MEASURED too: there is no GET on the id path at all; `pvesh get /nodes/node-b/ceph/mon/node-b` answers\n * \"No 'get' handler defined for '/nodes/node-b/ceph/mon/node-b'\", and mgr and mds answer the same.\n * That is why `path()` below is the COLLECTION, and that in turn is why `delete` is the one\n * handler this file writes by hand: `pveOperations.destroy` sends its DELETE to `spec.path`,\n * which here would be the collection — a 501 reported as a failed destroy, with the daemon still\n * running. Same shape and same rule as the override in acl.ts: do not \"restore symmetry\" by\n * pointing it back at `ops.destroy`.\n *\n * ⛔ WHAT A DESTROY ACTUALLY DOES, BECAUSE THE PLAN LINE SAYS ONLY \"delete\". REASONED FROM PVE'S\n * AND CEPH'S DOCUMENTED BEHAVIOUR, NOT MEASURED — nothing on this cluster was destroyed to find\n * out, and nothing should be to check. `mon`: stops ceph-mon@id, removes it from the monmap and\n * strips its section from ceph.conf. THREE MONS TOLERATE ONE LOSS; TWO TOLERATE NONE. Removing\n * two in one deploy — or removing the mon on the node whose API is serving the call — loses\n * quorum, and a cluster without mon quorum BLOCKS every RBD and CephFS I/O rather than erroring\n * it, so on this cluster every guest on `rbd-c1` hangs.\n * `mds`: destroying a STANDBY is a non-event; destroying the ACTIVE one fails CephFS over,\n * and with no standby left `cephfs-c1` goes unavailable. `mgr`: no guest I/O depends on it, but\n * the last one takes the PG autoscaler, the dashboard and PVE's own Ceph status with it.\n * Live on 2026-09-13: mon, mgr AND mds on each of node-b, node-c, node-d.\n *\n * ★ AND THE ONLY WAY TO REACH THAT DESTROY IS TO DELETE THE DECLARATION. `matches` is deliberately\n * total (see its ⛔), so this resource CANNOT plan a `replace` — which is the one action that\n * would tear down a live mon off the back of an edited field rather than a removed line.\n *\n * ⚠️ THE PRIVILEGES ARE ASYMMETRIC AND THE READ SIDE IS THE CHEAP ONE. The GET checks `any` of\n * `Sys.Audit`/`Datastore.Audit` on `/`, and PVEAuditor holds both (MEASURED:\n * `GET /access/roles/PVEAuditor`), so the mount's `read` role can see this family — unlike\n * `Proxmox.Storage`, which 403s under that lease. POST and DELETE both check `Sys.Modify` on\n * `/`, and MEASURED on this cluster the provision role already held it (it is in `PROVISION_PRIVILEGES`), so the provision lane\n * needs no widening for once. ⚠️ `Sys.Modify` on the ROOT path is broad — it also buys\n * datacenter options and every other cluster-wide config write — so it is worth knowing that\n * this credential already has it rather than discovering it the next time something is scoped.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { collectionPath, createForm, daemonId, daemonPath, findRow } from './ceph-daemon-form.ts';\nimport { pve } from './client.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool, num, text } from './values.ts';\n\n/**\n * Which Ceph daemon this is. The discriminant picks the collection, the create parameters and\n * which of the read-only extras below will even be populated.\n *\n * ⚠️ `osd` IS NOT HERE AND MUST NOT BE ADDED TO THIS UNION. `/nodes/{node}/ceph/osd` takes a disk\n * device, wipes it, and its DELETE has a `cleanup` flag — a create that destroys data on the way\n * in is a different resource with a different set of refusals, not a fourth case in this one.\n */\nexport type CephDaemonKind = 'mon' | 'mgr' | 'mds';\n\nexport interface CephDaemonProps extends WithTarget {\n kind: CephDaemonKind;\n /**\n * The node the daemon RUNS on. Identity — a daemon cannot be moved, only destroyed and rebuilt.\n * ⚠️ It is also the node whose API is asked, and the read is only as available as that node: a\n * node that is down — or has no Ceph installed, or answers 403 — reads as nothing at all,\n * because `pveOperations.read` folds EVERY failure into \"absent\" (a 404 is a legitimate answer\n * there and it cannot tell the two apart). Its daemons then plan as `update`, and reconcile\n * POSTs a create for a mon that already exists. PVE refuses that with \"monitor already\n * exists\", and the read-back refuses again — so it fails loudly rather than damaging anything,\n * but the message will point at the daemon rather than at the node that would not answer.\n */\n node: string;\n /**\n * The daemon id. PVE defaults it to the nodename, and `daemonId` in ceph-daemon-form.ts resolves\n * that default HERE rather than leaving it to PVE, because the id is a PATH SEGMENT: create,\n * delete and the row lookup all need the same string, and a default resolved on the far side\n * would leave three call sites guessing. Live on this cluster every daemon's name is its node.\n */\n name?: string;\n /**\n * mon only. Overrides the autodetected monitor IP; must sit in Ceph's public network — on this\n * cluster that is 198.51.100.0/24, carried by `vmbr1.42`.\n * ⛔ CREATE-ONLY AND NEVER COMPARED. PVE takes a bare ip-list on write and hands back `addr` as\n * `198.51.100.12:6789/0` — an address, a port and a nonce. The two are not the same string and\n * never will be, so diffing them would report a mismatch on every plan; with no PUT on this\n * family that mismatch becomes a REPLACE, and a replace of a mon is the quorum loss in the\n * header. Left out of `matches` on purpose.\n */\n 'mon-address'?: string;\n /**\n * mds only. Makes this standby replay the active MDS's journal for a faster failover.\n * ⛔ CREATE-ONLY AND NEVER COMPARED, AND THE FIELD THAT LOOKS LIKE ITS READBACK IS NOT ONE.\n * `standby_replay` in the list is the daemon's CURRENT state, not its configuration: MEASURED\n * 2026-09-13, all three mds report `standby_replay: false`, node-c included — and node-c is the ACTIVE\n * mds, which is not a standby at all and so can never report true however it was created.\n * Comparing `hotstandby` to it would plan a replace against whichever mds Ceph happens to have\n * elected, i.e. against a value no declaration controls.\n * ⚠️ SET ON A mon OR mgr IT IS DROPPED RATHER THAN SENT, and `mon-address` likewise on an mds:\n * each POST schema lists only its own parameter, so the wrong one is a 400 rather than an\n * ignored hint. A field silently dropped is the lesser of the two, but it IS dropped.\n */\n hotstandby?: boolean;\n}\n\n/**\n * Everything the cluster says about this daemon — reported, and (bar its identity) none of it\n * compared. Read the ⛔ on `matches` for why that is the only safe split on this family.\n */\nexport interface CephDaemonAttributes {\n kind: CephDaemonKind;\n node: string;\n name: string;\n /**\n * The host Ceph says it runs on.\n * ⚠️ REPORTED, NOT COMPARED, AND THAT IS A DELIBERATE BLIND SPOT. A daemon found under this name\n * on a DIFFERENT node than `node` declares plans as `noop`, which does hide a misdeclaration.\n * The alternative is worse: `host` is `optional` in the schema, so an absent one would read as\n * a mismatch, and with no PUT a mismatch is a replace — i.e. a missing field in one API answer\n * would destroy a live mon. When in doubt, out of `matches`; fix a wrong node by hand.\n */\n host: string;\n /**\n * mon `running`/`stopped`/`unknown`, mgr `active`/`standby`, mds `up:active`/`up:standby`/…\n * ⛔ CEPH ELECTS THIS AND REWRITES IT WITHOUT ANYONE DECLARING ANYTHING. MEASURED: mgr node-b is\n * `active` while node-c and node-d are `standby`; mds node-c is `up:active` while node-b and node-d are\n * `up:standby`. Restart a daemon and the roles move. It is reported so a plan can show what is\n * live and kept out of `matches` so that an election is never a diff.\n * ⚠️ IT ALSO MEANS THIS RESOURCE DOES NOT MANAGE WHETHER THE DAEMON IS RUNNING. A configured but\n * stopped daemon is still a row in the list, so it reads as present and plans `noop`.\n */\n state: string;\n /** Ceph-formatted, e.g. `198.51.100.12:6789/0`. Reported; see `mon-address` for why never compared. */\n addr: string;\n /** `ceph_version_short`, e.g. `20.2.2`. Reported: a straggler after an upgrade is worth seeing. */\n version: string;\n /**\n * mon rank in the monmap, mds rank in the filesystem, `-1` when the daemon has neither.\n * ⛔ CEPH ASSIGNS IT AND NOTHING DECLARES IT. MEASURED: the mons are ranked node-d=0, node-c=1, node-b=2 —\n * monmap order, not declaration order — and the mds ranks are 0 for the active node-c and -1 for\n * the two standbys. Rank moves when a daemon is added, removed or restarted.\n */\n rank: number;\n /** mon only: in the current quorum. Reported. MEASURED: all three mons are in quorum. */\n quorum: boolean;\n /** mds only, and NOT the readback of `hotstandby` — see the ⛔ on that prop. */\n standbyReplay: boolean;\n /**\n * mds only: the CephFS this daemon currently serves — `cephfs-c1` here.\n * ⛔ PRESENT ONLY ON THE ACTIVE MDS. MEASURED: node-c carries `fs_name`, node-b and node-d have no such key\n * at all. So it is empty for two of three identical declarations, and comparing it would\n * report work on exactly the daemons that are healthy standbys.\n */\n fsName: string;\n /** A `ceph-<kind>@<id>` systemd unit is enabled on the host. Reported; absent reads as false. */\n service: boolean;\n}\n\nexport interface ProxmoxCephDaemon extends Resource<\n 'Proxmox.CephDaemon',\n CephDaemonProps,\n CephDaemonAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a mon carrying quorum cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxCephDaemon = Resource<ProxmoxCephDaemon>('Proxmox.CephDaemon', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst handlers = pveHandlers<CephDaemonProps, CephDaemonAttributes>({\n /** ⚠️ `undefined` when no row carries this name: that is how the factory learns to create. */\n attributes: (live, props) => {\n const row = findRow(live, props);\n if (row === undefined) return undefined;\n return {\n addr: text(row['addr']),\n fsName: text(row['fs_name']),\n host: text(row['host']),\n kind: props.kind,\n name: daemonId(props),\n node: props.node,\n quorum: bool(row['quorum']),\n rank: num(row['rank'], -1),\n service: bool(row['service']),\n standbyReplay: bool(row['standby_replay']),\n state: text(row['state']),\n version: text(row['ceph_version_short']),\n };\n },\n /** ⛔ The ID path, not the collection — POST is registered on `{id}`, exactly like a metric server. */\n collection: daemonPath,\n createForm,\n /**\n * ⛔ TOTAL, AND THIS IS THE REASON THE FILE EXISTS RATHER THAN A SHORTCUT PAST WRITING IT. Every\n * field a declaration can carry is CREATE-ONLY and unreadable — `mon-address` comes back as a\n * different kind of string, `hotstandby` comes back as a state Ceph elects — and every field\n * the read returns is assigned by Ceph: state, rank, quorum, fs_name, standby_replay, addr,\n * version. Nothing is left that is both declared and readable. So the honest comparison is\n * \"does a daemon of this kind and name exist\", which `attributes` has already answered by\n * returning a value at all, and anything further would be a diff no write could ever satisfy.\n * ⛔ AND ON THIS FAMILY THAT DIFF IS NOT MERELY NOISY. There is no PUT, so resource.ts answers\n * `replace` rather than `update` whenever `matches` is false — and replace on a mon is\n * delete-then-create against a live quorum. A forever-diff here is a forever-OUTAGE-RISK.\n * ⚠️ THE PRICE, STATED PLAINLY: editing `hotstandby` or `mon-address` on a declared daemon\n * plans as `noop` and never applies. Same bargain storage.ts strikes for a changed `type`.\n * Change one by removing the declaration and re-adding it, ONE DAEMON AT A TIME, reading the\n * ⛔ on destroy in the header first.\n */\n matches: () => true,\n /** ⛔ The COLLECTION, because there is no GET on the id path. See the second ⛔ in the header. */\n path: collectionPath,\n /** ⚠️ No `updateForm`: none of the three has a PUT, so nothing about a daemon is editable. */\n});\n\nexport const ProxmoxCephDaemonProvider = () =>\n Provider.effect(\n ProxmoxCephDaemon,\n Effect.succeed(\n ProxmoxCephDaemon.Provider.of({\n ...handlers,\n /**\n * ⛔ THE ONE HANDLER NOT TAKEN FROM THE FACTORY, AND THE ONLY REASON IS THE PATH SPLIT.\n * `ops.destroy` DELETEs `spec.path`, which this resource must point at the collection so\n * that `read` works at all; DELETE on the collection is not implemented and would answer\n * 501 while the daemon kept running — a failed destroy that reads like a permissions\n * problem. Everything else here is the factory's.\n * ⚠️ AND IT IS THE DANGEROUS ONE. Re-read the destroy ⛔ in the header before letting a\n * plan that removes a mon run: two gone at once is a cluster with no quorum and every\n * guest on `rbd-c1` blocked on I/O.\n */\n delete: ({ olds }: { olds: CephDaemonProps }) =>\n pve(olds.target, 'provision', 'DELETE', daemonPath(olds)),\n }),\n ),\n );\n",
|
|
26
|
-
"/**\n * Where a Ceph daemon lives on the API, and what a declaration of one looks like as a PVE form.\n *\n * ★ SPLIT OUT OF ceph-daemon.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a real\n * one rather than a convenient line number — the same seam metric-server-form.ts cuts on.\n * This file answers \"how does a declaration address ONE daemon\" — which URL, which body, and\n * which row of the collection's answer is it. ceph-daemon.ts answers \"what does the cluster\n * say about that daemon, and when has it changed\". Nothing here calls the API and nothing\n * here decides a diff; `findRow` picks a row out of an answer its caller already fetched.\n *\n * ⛔ THE TWO PATHS ARE DIFFERENT AND THAT IS THE WHOLE REASON THIS FILE HAS TWO OF THEM. MEASURED\n * on node-b, 2026-09-13: there is no GET on the id path — `pvesh get /nodes/node-b/ceph/mon/node-b` answers\n * \"No 'get' handler defined\" — while POST and DELETE are registered ONLY there. So the read uses\n * `collectionPath` and the two writes use `daemonPath`, and swapping either one is a silent\n * 501: a create that never lands, or a destroy that reports failure while the daemon runs on.\n *\n * ⚠️ THE `import type` BACK TO ceph-daemon.ts IS A CYCLE ON PAPER ONLY — type-only, so it is\n * erased before anything runs. `CephDaemonProps` stays the resource's public shape, in the file\n * that declares the resource.\n *\n * ⛔ POST AND DELETE RETURN A UPID, NOT AN OBJECT — both are `protected` task endpoints whose\n * schema `returns` is a bare string. REASONED, NOT MEASURED: this cluster is read-only to me, so\n * no create was ever run. The consequence to expect is that `reconcile` reads back immediately\n * after the POST, and if the worker has not yet written ceph.conf the read-back still says\n * absent and reconcile dies with \"the write returned no error but the object is still absent\".\n * That refusal is honest — nothing is recorded for an object that is not there — and a second\n * deploy converges once the task has finished, but it is a confusing FIRST-RUN failure. Watch\n * the task in the PVE UI before concluding the create failed.\n *\n */\nimport type { CephDaemonProps } from './ceph-daemon.ts';\nimport { flag } from './values.ts';\n\n/**\n * PVE defaults the id to the nodename for all three kinds, and that default is resolved HERE\n * rather than left to PVE, because the id is a PATH SEGMENT: create, delete and the row lookup in\n * ceph-daemon.ts all need the same string, and a default resolved on the far side would leave\n * three call sites guessing. Live on this cluster every daemon's name equals its node.\n */\nexport const daemonId = (props: CephDaemonProps) => props.name ?? props.node;\n\n/**\n * The read path. ⚠️ `{node}` HERE IS ONLY THE NODE BEING ASKED, not a filter. MEASURED:\n * `GET /nodes/node-b/ceph/mon` returns node-b, node-c AND node-d, and `GET /nodes/node-c/ceph/mon` returns the same\n * three IN A DIFFERENT ORDER — [node-d,node-b,node-c] from node-b against [node-c,node-b,node-d] from node-c. A caller that took\n * row zero, or assumed the list was this node's daemons, would be wrong on both counts.\n */\nexport const collectionPath = (props: CephDaemonProps) => `nodes/${props.node}/ceph/${props.kind}`;\n\n/** The write path: POST creates here, DELETE destroys here. Never read from — see the ⛔ above. */\nexport const daemonPath = (props: CephDaemonProps) => `${collectionPath(props)}/${daemonId(props)}`;\n\n/**\n * The create body, which is EMPTY for a mgr and nearly empty for the other two.\n *\n * ⚠️ THE ID IS NOT SENT, because it is already the last segment of the path being POSTed to — the\n * same reasoning as metric-server.ts, where a second copy of the key can only disagree with the\n * first. PVE's schema does accept `monid`/`id`/`name` as body parameters; the path wins, so\n * sending one would be a second source of truth for the object's identity and nothing else.\n *\n * ⚠️ AND THE BRANCHES CANNOT BE ONE SHARED BODY. `hotstandby` is an mds parameter and\n * `mon-address` a mon one; each POST schema lists only its own, so the wrong field is a 400\n * rather than an ignored hint. Both are create-only — the ⛔s on those props say why neither is\n * ever compared afterwards.\n */\nexport const createForm = (props: CephDaemonProps): Record<string, string> => {\n if (props.kind === 'mon' && props['mon-address'] !== undefined) {\n return { 'mon-address': props['mon-address'] };\n }\n const hotstandby = props.kind === 'mds' ? flag(props.hotstandby) : undefined;\n return hotstandby === undefined ? {} : { hotstandby };\n};\n\n/**\n * ⚠️ THE COLLECTION ANSWERS AN ARRAY while the factory types `attributes`' first parameter as the\n * `Record<string, unknown>` every other PVE read is shaped like — the same mismatch acl.ts\n * carries, handled the same way: narrow the rows, trust none of them, and match on `name`.\n * ⚠️ MATCHED ON `name` ALONE, NEVER ON POSITION — the list comes back in a different order\n * depending on which node was asked. See the ⚠️ on `collectionPath` above.\n */\nexport const findRow = (live: unknown, props: CephDaemonProps) =>\n (Array.isArray(live) ? live : [])\n .filter((row): row is Record<string, unknown> => typeof row === 'object' && row !== null)\n .find((row) => row['name'] === daemonId(props));\n",
|
|
27
|
-
"/**\n * `Proxmox.CephFlag` — one cluster-wide Ceph OSD flag, declared: `noout`, `pause`, `norebalance`.\n *\n * ★ ONE RESOURCE PER FLAG, NOT ONE CARRYING ALL ELEVEN, AND THE CLUSTER'S OWN API DECIDED IT.\n * MEASURED on C1 2026-09-13 (pve-manager 9.2.11, ceph tentacle 20.2.2) by reading\n * `/usr/share/perl5/PVE/API2/Cluster/Ceph.pm` and the published schema in\n * `/usr/share/pve-docs/api-viewer/apidoc.js`:\n * · `PUT /cluster/ceph/flags` ends in `fork_worker('cephsetflags', ...)` and returns a STRING\n * — a UPID. The bulk write is ASYNCHRONOUS, and this package has no task-polling machinery.\n * · `PUT /cluster/ceph/flags/{flag}` calls `$rados->mon_command` inline and returns null. The\n * per-flag write is SYNCHRONOUS.\n * `pveOperations.reconcile` READS BACK after every write and refuses to record a value it did\n * not see. Against the bulk endpoint that read-back races a worker which has not run yet, so it\n * would record the OLD value as the new attributes — the one honesty mechanism in this package\n * turned into a lie generator. Per-flag, the read-back means what it says.\n * ★ THE SECOND REASON IS SMALLER AND STILL REAL. The bulk GET answers rows of\n * `{name, description, value}`, and `description` is PVE's own English prose that no write\n * accepts: compare it and you have a forever-update, report it and this provider is pretending\n * to manage prose. `GET /cluster/ceph/flags/{flag}` answers a BARE BOOLEAN, so that trap\n * cannot be written here at all. The brief's plural `Proxmox.CephFlags` became singular for\n * those two reasons; the bulk endpoint is read by nothing in this package.\n *\n * ⛔ THESE ARE NOT CONFIGURATION. THEY ARE A HAND ON THE BRAKE, AND A DECLARATION CAN PULL IT OFF.\n * `noout` is what a person sets before pulling a disk. A stack saying `value: false` reasserts\n * that off on EVERY deploy — including a deploy somebody else runs, for an unrelated resource,\n * twenty minutes into a maintenance window — and the plan line reads `Proxmox.CephFlag noout\n * update`, which nobody reads as \"ceph is about to start rebalancing 128 PGs across three nodes\n * while a disk is out of the chassis\".\n * ★ SO `value` IS REQUIRED AND HAS NO DEFAULT, AND THAT IS THE MITIGATION. An optional `value`\n * defaulting to false would make `CephFlag('noout', { flag: 'noout', target })` mean \"clear\n * it\" — the dangerous direction, chosen by OMISSION. Here the dangerous sentence has to be\n * typed out by a person. Do not add a default, and do not declare a flag merely to document\n * that it is off: MEASURED 2026-09-13, all eleven flags on C1 read 0, so eleven\n * `value: false` lines would plan green forever and do nothing except take the brake off\n * whenever somebody happens to apply them.\n *\n * ⛔ `delete` DOES NOTHING, ON PURPOSE, AND THE ASYMMETRY OF THE TWO FAILURES IS THE ARGUMENT.\n * `flags/{flag}` has exactly two methods, GET and PUT, so the factory's `destroy` would answer\n * \"Method 'DELETE /cluster/ceph/flags/noout' not implemented\" on every teardown — whatever this\n * handler does is INVENTED. The obvious invention, \"clear the flag\", is refused: removing a line\n * from a stack file, or Alchemy collecting an old generation after a replace, would silently\n * take the brake off and nothing would warn. Leaving a flag set that nothing declares any more\n * is the opposite kind of failure — ceph reports `HEALTH_WARN ... flag(s) set` for every one of\n * these, on `ceph -s`, in the PVE UI and in the `pve_*` metrics this estate already scrapes.\n * A silent destructive failure loses to a loud inert one.\n * ⚠️ THE RESIDUAL RISK, NAMED RATHER THAN DENIED: deleting a `value: true` declaration LEAVES\n * THE FLAG SET. Retire a maintenance window by flipping the line to `value: false` and\n * deploying BEFORE the line is removed, or a `noout` outlives the window that needed it.\n * ⚠️ REASONED, NOT MEASURED: that health warning was not observed, because observing it means\n * setting a flag on a live cluster and this file was researched read-only. Undeclaring stops\n * MANAGING a flag; to clear one, declare `value: false` and deploy, where a plan says so.\n *\n * ⛔ `pause` IS TWO CEPH FLAGS AND THIS RESOURCE READS ONLY ONE OF THEM. MEASURED in\n * `PVE::Ceph::Tools::get_real_flag_name`, whose own comment reads \"the 'pause' flag gets always\n * set to both 'pauserd' and 'pausewr'\": PVE writes both and then decides the flag is set by\n * looking at `pauserd` alone. A cluster where somebody ran `ceph osd unset pauserd` by hand\n * still has `pausewr` — writes blocked, guests hung — and this resource reports `value: false`\n * and plans `noop` straight past it. It cannot express a half-pause, so never use it to prove\n * one is gone; `ceph osd dump | head -1` can.\n * ⛔ `pause` AND `noup` ARE ALSO THE TWO THAT TAKE THE CLUSTER DOWN FROM A TYPO. `pause: true`\n * stops all reads and writes, so every guest on `rbd-c1` — the cluster's only rbd pool,\n * MEASURED 2026-09-13 — freezes on its root disk, and so does `cephfs-c1`. `noup: true`\n * stops a rebooted OSD from ever rejoining. Neither is undone by deleting the line.\n *\n * ⚠️ PRIVILEGES, AND THE WRITE ONE IS A BIG ASK. Both GETs check `Sys.Audit` on `/`; both PUTs\n * check `Sys.Modify` on `/`, and all four carry `allowtoken => 1`, so an API token may make the\n * call at all. There is no `/ceph` ACL object to scope to — the check names the ROOT path\n * literally — so granting the provision role `Sys.Modify` here also buys datacenter options,\n * metric servers, notification targets and every other cluster-wide write, exactly as\n * metric-server.ts warns about its own family.\n * ⚠️ REASONED, NOT MEASURED: nothing here was exercised under a minted lease, because a mint\n * writes an API token into the cluster and the brief for this file was reads only. Expect\n * \"Permission check failed (/, Sys.Modify)\" on the first deploy, and widen deliberately.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool, flag } from './values.ts';\n\n/**\n * PVE's eleven flags, spelled as `PVE::Ceph::Tools::get_possible_osd_flags` spells them.\n *\n * ⚠️ THE ENUM IS CLOSED AND A TYPO IS A 400, WHICH IS WHY THIS IS A UNION AND NOT `string`. Both\n * the GET and the PUT declare `additionalProperties => 0` over exactly this list.\n * ⚠️ CEPH HAS FLAGS PVE DOES NOT MODEL, and four of them are always on. MEASURED on C1:\n * `ceph osd dump` reports `flags sortbitwise,recovery_deletes,purged_snapdirs,pglog_hardlimit`.\n * PVE reads that same string and answers only about its own eleven, so the others can neither\n * leak in here nor be set from here — `noautoscale` and `nosnaptrim` included.\n * ⛔ IF SOMEBODY LATER \"IMPROVES\" THE READ BY PARSING `osd dump` DIRECTLY, those four become\n * permanently-set flags that nothing declares: a forever-diff on a brand new cluster.\n */\nexport type CephFlagName =\n | 'nobackfill'\n | 'nodeep-scrub'\n | 'nodown'\n | 'noin'\n | 'noout'\n | 'norebalance'\n | 'norecover'\n | 'noscrub'\n | 'notieragent'\n | 'noup'\n | 'pause';\n\nexport interface CephFlagProps extends WithTarget {\n /**\n * Which flag. PVE's primary key here, and the last segment of the path.\n *\n * ⚠️ EDITING IT IN PLACE ORPHANS THE OLD FLAG RATHER THAN MOVING ANYTHING. `diff` reads the NEW\n * path and reconcile writes it, while the old flag keeps whatever this stack last put there.\n * There is deliberately no replace override for it — acl.ts needs one because its `delete`\n * removes a real grant, and `delete` here is inert by design, so a replace would do exactly\n * what an update already does. Declare a second resource and set the old one to `value: false`\n * rather than renaming this one.\n * ⛔ TWO RESOURCES DECLARING THE SAME FLAG ARE ONE CLUSTER OBJECT, and Alchemy sees two ids\n * rather than a collision — the acl.ts hazard exactly. Disagreeing, they take turns winning\n * and BOTH plan `update` for ever; agreeing, deleting either leaves the flag where the other\n * put it. One declaration per flag per cluster.\n */\n flag: CephFlagName;\n /**\n * Set the flag (`true`) or clear it (`false`).\n *\n * ⛔ REQUIRED, WITH NO DEFAULT — see the second ⛔ in the header, which is the whole safety\n * argument for this family. It is also the only field PVE accepts on this endpoint, and it is\n * not optional in the schema either: omitting it from the form is a 400, not an untouched\n * flag. (The BULK endpoint is the one where omission means \"leave it alone\"; this is not it.)\n */\n value: boolean;\n}\n\nexport interface CephFlagAttributes {\n /**\n * ⚠️ REPORTED, NEVER COMPARED. It is the path key the read was made WITH, copied back out of\n * props, so comparing it against props would be true by construction — the same reasoning\n * acl.ts gives for the four identity fields it also declines to diff.\n */\n flag: CephFlagName;\n /** Whether ceph has the flag set right now. The only field `matches` looks at. */\n value: boolean;\n}\n\nexport interface ProxmoxCephFlag extends Resource<\n 'Proxmox.CephFlag',\n CephFlagProps,\n CephFlagAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxCephFlag = Resource<ProxmoxCephFlag>('Proxmox.CephFlag');\n\nconst handlers = pveHandlers<CephFlagProps, CephFlagAttributes>({\n /**\n * ⚠️ `live` IS A BARE BOOLEAN HERE, NOT AN OBJECT, AND IT IS HANDED TO `bool` WHOLE ON PURPOSE.\n * `get_flag` returns perl `1` or `0` under a `type => 'boolean'` return schema, so the wire is\n * `{\"data\":0}` or `{\"data\":false}` depending on how the REST layer renders it. `bool` accepts\n * every one of those spellings, which is exactly why it is a shared coercion. The factory\n * types this parameter `Record<string, unknown>` because every OTHER PVE read is an object;\n * reaching for `live['value']` here would answer undefined on every plan and diff `false`\n * forever against a set flag.\n * ⚠️ THE SCALAR IS A MEASURED PROPERTY OF THIS PVE, NOT A PROMISE. A later version wrapping\n * the answer in an object would make `bool` read a SET flag as false, and `value: false`\n * would then plan `noop` over a live flag — silently. Re-measure\n * `GET /cluster/ceph/flags/noout` after a major upgrade; cheaper than a second parse path.\n *\n * ⛔ IT NEVER ANSWERS undefined, WHICH MAKES THE CREATE BRANCH UNREACHABLE, AND ITS ERROR WILL\n * MISLEAD YOU. All eleven flags always exist; there is nothing for \"absent\" to mean. So a POST\n * to `collection` means the READ failed — an expired 300s lease, node-b down, or ceph simply not\n * configured on the cluster (every one of these four handlers opens with\n * `check_ceph_configured()`, which dies). Read the resulting \"Method 'POST /cluster/ceph/flags'\n * not implemented\" as \"the read failed\" and go and look at the credential, not at ceph. Same\n * shape as the unreachable POST in acl.ts, same advice.\n */\n attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),\n collection: () => 'cluster/ceph/flags',\n /**\n * ⛔ UNREACHABLE — see the second ⛔ in `attributes`. It is written in the BULK endpoint's shape\n * (the flag name is the parameter, not `value`) rather than left empty, because that is the\n * only body `cluster/ceph/flags` has ever documented. Nothing sends it today.\n */\n createForm: (props) => ({ [props.flag]: flag(props.value) ?? '0' }),\n /**\n * ⚠️ `value` IS THE ONLY THING COMPARED, AND THE LIST OF THINGS DELIBERATELY NOT COMPARED IS THE\n * point of this resource. `flag` is props-derived (see `CephFlagAttributes`). `description`\n * and `name` exist only on the bulk GET, which this file does not read, so neither can be\n * compared by accident. Nothing PVE returns here is rewritten, re-ordered or re-typed by the\n * cluster, because all PVE returns here is one boolean.\n * ★ MEASURED: on C1, `GET /cluster/ceph/flags` answers value 0 for all eleven flags, so a\n * declaration of `value: false` reads back false and plans `noop`.\n */\n matches: (attributes, props) => attributes.value === props.value,\n path: (props) => `cluster/ceph/flags/${props.flag}`,\n /**\n * ⚠️ ONLY `value` GOES IN THE BODY. `flag` is already bound by the path and the PUT declares\n * `additionalProperties => 0`, so a second copy in the form could only disagree with the URL.\n * ⚠️ THE `?? '0'` IS UNREACHABLE, AND IS WRITTEN RATHER THAN CAST AWAY. `flag()` types its\n * answer `string | undefined` because it serves OPTIONAL props, and `value` here is required,\n * so the branch cannot be taken; a cast would claim a proof the type system has not made.\n */\n updateForm: (props) => ({ value: flag(props.value) ?? '0' }),\n});\n\nexport const ProxmoxCephFlagProvider = () =>\n Provider.effect(\n ProxmoxCephFlag,\n Effect.succeed(\n ProxmoxCephFlag.Provider.of({\n /**\n * ⚠️ NOT THE ONE-LINE `Provider.of(handlers)` EVERY OTHER FILE HERE USES, AND THE TWO\n * REASONS ARE BOTH acl.ts's. PVE implements no DELETE on this path, and the factory's\n * read-back guard is dead whenever `attributes` cannot answer undefined. Unlike acl.ts\n * this SPREADS the factory's handlers instead of retyping them, so `list`, `read` and\n * `diff` cannot drift from the shared ones — only the two that genuinely differ are\n * written out below.\n */\n ...handlers,\n /** ⛔ Inert, deliberately. The third ⛔ in the header is the whole argument; read it. */\n delete: () => Effect.void,\n /**\n * ⚠️ THE READ-BACK GUARD, RESTORED FOR A FAMILY WHOSE \"ABSENT\" DOES NOT EXIST.\n * `ops.reconcile` refuses when the object is still missing after a write, and a flag is\n * never missing, so that check can never fire here — exactly the hole acl.ts patches\n * with `bound`. PVE answers 200 with `{\"data\":null}` on calls that did nothing, and this\n * PUT's documented return IS null, so the status code carries no evidence at all.\n * ⚠️ THE PER-FLAG PUT IS SYNCHRONOUS (`$rados->mon_command` inline, MEASURED in Ceph.pm),\n * which is what makes this check fair rather than a race against a worker task. If it\n * ever fires spuriously the honest fix is to look at the mon, not to delete the guard.\n */\n reconcile: Effect.fn(function* ({ news }) {\n const after = yield* handlers.reconcile({ news });\n if (after.value !== news.value) {\n return yield* Effect.die(\n new Error(\n `cluster/ceph/flags/${news.flag}: the PUT returned no error but ceph still ` +\n `reports the flag ${after.value ? 'set' : 'clear'} rather than ` +\n `${news.value ? 'set' : 'clear'}. This endpoint's success is documented as a ` +\n 'null body, so the status code proves nothing -- check `ceph osd dump | ' +\n 'head -1` and the cluster log. `pause` in particular is two ceph flags read ' +\n 'as one; see the header.',\n ),\n );\n }\n return after;\n }),\n }),\n ),\n );\n",
|
|
28
|
-
"/**\n * `Proxmox.CephFs` — a CephFS on the cluster's own Ceph. The filesystem a `cephfs` storage mounts.\n *\n * ⛔ A REPLACE OF THIS RESOURCE DESTROYS A LIVE FILESYSTEM, AND THERE IS NO PUT TO SAVE YOU FROM\n * IT. MEASURED from the published schema on node-b, 2026-09-13: `/nodes/{node}/ceph/fs/{name}` has\n * exactly two methods, POST and DELETE. A CephFS cannot be edited in place, so `updateForm` is\n * left undefined and anything the factory can see as changed becomes a REPLACE — and Alchemy's\n * replace is create-then-delete, where the delete is `destroyfs`: the entry leaves the MDS map,\n * every mount on every node breaks, and whether the bytes survive rests on `remove-pools` alone.\n * ★ WHICH IS THE WHOLE REASON `matches` COMPARES NOTHING. Read its ⛔ before adding a field to\n * it. On every other resource in this package a forever-diff is an annoying plan line; on this\n * one it is a live filesystem destroyed on every deploy, forever, because a background daemon\n * moved a number nobody declared.\n *\n * ⛔ THERE IS NO SINGLE-OBJECT READ, WHICH INVERTS `path` AND `collection`. MEASURED on node-b:\n * pvesh get /nodes/node-b/ceph/fs/cephfs-c1\n * -> No 'get' handler defined for '/nodes/node-b/ceph/fs/cephfs-c1'\n * `GET /nodes/{node}/ceph/fs` is a directory index and is the only read there is, so `path()`\n * below returns the INDEX and `attributes` picks this filesystem out of the array client-side —\n * the shape acl.ts uses for the one flat `GET /access/acl`. The POST goes to the `{name}` path,\n * so `collection()` is the LONGER of the two strings, the mirror of metric-server.ts.\n * ⚠️ AND THAT IS WHY `delete` AND `reconcile` ARE HAND-WRITTEN BELOW rather than taken from\n * `pveHandlers`. `pveOperations.destroy` deletes `spec.path`, which here is the index:\n * `DELETE /nodes/{node}/ceph/fs` is not a route, so a factory delete would fail every destroy\n * while the filesystem stayed exactly where it was. `reconcile` is hand-written for a second,\n * independent reason — the forked worker — set out in ceph-fs-wire.ts. acl.ts is the\n * precedent for a family that genuinely does not fit; this is the second.\n *\n * ⛔ CREATING A CephFS CREATES TWO CEPH POOLS, AND A `Proxmox.CephPool` MUST NOT ALSO DECLARE THEM.\n * `createfs` builds `<name>_data` and `<name>_metadata` itself and refuses outright if either\n * exists (\"ceph pools '…_data' and/or '…_metadata' already exist\"). MEASURED on C1: the live\n * `cephfs-c1` owns `cephfs-c1_data` (id 6) and `cephfs-c1_metadata` (id 7), both of which\n * `GET /nodes/node-b/ceph/pool` lists like any other pool — and `/nodes/{node}/ceph/pool/{name}` DOES\n * have GET, PUT and DELETE, so a pool resource would be a clean `pveHandlers` fit and would\n * adopt them without noticing. Declaring both is double management of one object: whichever\n * reconciles first wins, and the loser either fails its create or PUTs `size`/`pg_num` onto pools\n * this resource believes it owns. Declare the filesystem here and leave its two pools alone.\n *\n * ⚠️ `add-storage` IS A SECOND SIDE EFFECT AND IT LANDS IN storage.cfg. Set, `createfs` also calls\n * `PVE::API2::Storage::Config->create` with type `cephfs`, content `backup,iso,vztmpl` and\n * `fs-name <name>`. MEASURED: /etc/pve/storage.cfg on node-b holds exactly that — `cephfs:\n * cephfs-c1` / `content backup,vztmpl,iso` / `fs-name cephfs-c1`. So a `Proxmox.Storage`\n * declaring `cephfs-c1` would be managing an entry this resource created. Declare ONE of them:\n * either `add-storage` here and no storage resource, or `add-storage` off and a\n * `Proxmox.Storage` that reads this resource's `name` attribute — which also gets the ordering\n * right for free, because Alchemy orders by data flow.\n *\n * ⚠️ `node` IS A ROUTE, NOT IDENTITY, AND MUST NEVER BE DIFFED. A CephFS is cluster-wide; the node\n * in the path only says which node PVE proxies the call to. MEASURED: `GET /nodes/node-b/ceph/fs`,\n * `…/node-c/…` and `…/node-d/…` returned the byte-identical array. So changing `node` plans `noop`, and\n * two resources naming the same `name` on different nodes are the SAME filesystem — the vmid\n * hazard from lxc.ts, with a filesystem on the end of it instead of a container.\n *\n * ⚠️ PRIVILEGES: the read lane needs `Sys.Audit` OR `Datastore.Audit` on `/`, which an auditor\n * role already holds — unlike storage.ts, nothing needs widening before the first plan. The\n * write side is in ceph-fs-wire.ts beside the calls, and in docs/privileges.md with the rest.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n createForm,\n createFs,\n destroyFs,\n notCreated,\n notDestroyed,\n objectPath,\n readRow,\n} from './ceph-fs-wire.ts';\nimport { type PveRequirements, type WithTarget, pveOperations } from './resource.ts';\n\nexport interface CephFsProps extends WithTarget {\n /** ⚠️ Which node answers the call, NOT which node holds the filesystem. See the header. */\n node: string;\n /**\n * The filesystem name, and the cluster-wide primary key. PVE's pattern is `^[^:/\\s]+$`.\n * ⛔ CHANGING IT IS A REPLACE, WHICH DESTROYS THE OLD FILESYSTEM. `diff` says so explicitly\n * below rather than letting the factory report a create-shaped `update` and orphan the old one.\n */\n name: string;\n /**\n * Placement groups for the backing data pool; the metadata pool gets a quarter of it, or 8 when\n * this is under 32. PVE's default is 128.\n *\n * ⛔ CREATE-TIME ONLY, UNREADABLE, AND OWNED BY THE AUTOSCALER FROM THE SECOND IT LANDS. It is\n * never compared — see the ⛔ on `matches` — and this is not caution, it is measured. C1's\n * `cephfs-c1` was created with the default 128 (the arithmetic proves it: `cephfs-c1_metadata`\n * sits at 32, which is 128/4), and `cephfs-c1_data` reads pg_num 32 TODAY. The autoscaler moved\n * it by a factor of four, and it is not finished: `pg_autoscale_mode` is `on` for every pool on\n * this cluster and `cephfs-c1_metadata` already reports `pg_num_final: 16` against its live 32.\n */\n pg_num?: number;\n /**\n * Also write a `cephfs` storage entry for this filesystem. ⚠️ Create-time only and never read\n * back — it is an ACTION, not a field. See the header for what it does to `Proxmox.Storage`.\n */\n 'add-storage'?: boolean;\n /** ⛔ DELETE-TIME ONLY, AND IT ERASES THE DATA. Read the ⛔ on `destroyFs` before setting it. */\n 'remove-pools'?: boolean;\n /**\n * Delete the pveceph-managed storage entries too. ⚠️ PVE refuses unless they are already\n * disabled (\"storage '…' is not disabled, make sure to disable and unmount the storage first\"),\n * so this fails the destroy rather than unmounting anything out from under a running guest.\n */\n 'remove-storages'?: boolean;\n}\n\n/**\n * ⚠️ EVERY FIELD HERE IS REPORTED AND NONE IS COMPARED. They exist so a plan, and the state entry\n * behind it, can say what the declaration actually points at — which pools hold the bytes.\n */\nexport interface CephFsAttributes {\n name: string;\n /** The first data pool. PVE keeps it for compatibility; `data_pools` is the real answer. */\n data_pool: string;\n /**\n * ⚠️ SORTED, AND IT IS A SET RATHER THAN A LIST. A CephFS can hold several data pools — added\n * with `ceph fs add_data_pool`, which this resource has no way to express — and PVE reports\n * them in the MDS map's own order. Sorting is what keeps this attribute stable across reads.\n */\n data_pools: string;\n metadata_pool: string;\n /**\n * ⚠️ AN INTEGER THAT NAMES SOMETHING WRITTEN AS A NAME, AND THEREFORE A CLASSIC FOREVER-DIFF IF\n * IT EVER REACHED `matches`. PVE answers `metadata_pool_id: 7` while every write in this family\n * spells a pool by name. It is here to be read, never to be compared. `-1` means absent: the\n * field is optional in the schema and older PVE omits it.\n */\n metadata_pool_id: number;\n}\n\nexport interface ProxmoxCephFs extends Resource<\n 'Proxmox.CephFs',\n CephFsProps,\n CephFsAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a filesystem holding data cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxCephFs = Resource<ProxmoxCephFs>('Proxmox.CephFs', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst ops = pveOperations<CephFsProps, CephFsAttributes>({\n attributes: readRow,\n /** ⚠️ The `{name}` path — longer than `path()`, deliberately. See the second ⛔ in the header. */\n collection: objectPath,\n createForm,\n /**\n * ⛔ NOTHING IS COMPARED, AND THAT IS THE MOST DELIBERATE LINE IN THIS FILE. A CephFS has no\n * readable, writable field: the index returns only the name and the pools behind it, and every\n * prop this resource accepts is create-time or delete-time. So \"present\" IS \"settled\", and the\n * only honest comparison is none at all.\n * ⚠️ THE TEMPTING ADDITION IS `pg_num`, AND IT WOULD BE THE WORST BUG THIS PACKAGE HAS SHIPPED.\n * It is not in the index at all, so it can only be read from the pool endpoint — where the\n * autoscaler owns it. MEASURED on C1 today: created at 128, live at 32, with the metadata\n * pool already scheduled down to 16. Compared, that is an eternal mismatch; and because\n * `updateForm` is undefined, the action it produces is REPLACE. The plan would destroy and\n * rebuild a live filesystem on every deploy, triggered by a daemon on its own schedule.\n * ⚠️ `data_pools` AND `metadata_pool_id` ARE OUT FOR THE ORDINARY REASONS: a set PVE returns\n * in its own order, and an integer id for something written as a name. `name` is out because\n * it is the FILTER that produced these attributes, so comparing it is true by construction —\n * an identity change is handled in `diff`, not here. `node` is out because it is a route.\n */\n matches: () => true,\n /** ⚠️ THE INDEX, NOT THE OBJECT. There is no GET on `{name}`; see the second ⛔ in the header. */\n path: (props) => `nodes/${props.node}/ceph/fs`,\n});\n\nexport const ProxmoxCephFsProvider = () =>\n Provider.effect(\n ProxmoxCephFs,\n Effect.succeed(\n ProxmoxCephFs.Provider.of({\n /**\n * ⛔ EMPTY, LIKE EVERY OTHER RESOURCE HERE. The index answers with every filesystem on the\n * cluster, `cephfs-c1` included — the one holding this estate's ISOs and templates.\n * Adopting it would put Alchemy one `destroy` away from it. Adoption stays explicit.\n */\n list: () => Effect.succeed([]),\n read: Effect.fn(function* ({ olds }) {\n return yield* ops.read(olds);\n }),\n /**\n * ⛔ A CHANGED `name` IS A REPLACE, AND THE FACTORY CANNOT SAY SO ALONE. Left to delegate,\n * a rename reads the NEW name, finds nothing, and returns the drift-shaped `update` that\n * `pveOperations.diff` uses for \"Alchemy has state, the cluster does not\" — so reconcile\n * would build the new filesystem and LEAVE THE OLD ONE, with its pools, its storage entry\n * and its bytes, owned by nobody and invisible in every later plan.\n * ⛔ SAYING `replace` IS HONEST, AND IT IS ALSO WHAT MAKES A TYPO EXPENSIVE. Alchemy\n * creates first and deletes second, so the old filesystem is destroyed AFTER the new\n * one exists — with `remove-pools` unset the bytes are stranded rather than erased, and\n * that default is the only thing between a mis-typed rename and an unrecoverable one.\n * Do not \"simplify\" this back to the factory: the alternative is not safer, it is\n * silent. ⚠️ `node` is NOT part of identity and must never join this comparison.\n */\n diff: Effect.fn(function* ({ news, output }) {\n if (output !== undefined && isResolved(news) && news.name !== output.name) {\n return { action: 'replace' } as const;\n }\n return yield* ops.diff(news, output);\n }),\n /**\n * ⛔ NOT `ops.reconcile`, BECAUSE ITS READ-BACK RACES A FORKED WORKER. The POST returns a\n * UPID and nothing else; `createFs` waits the task out and surfaces its real error. The\n * read-back the factory does is kept here rather than dropped — waiting proves the task\n * ENDED, reading proves the filesystem EXISTS, and PVE has shipped tasks that end OK\n * having done nothing.\n * ⚠️ AN EXISTING FILESYSTEM IS RETURNED UNTOUCHED. There is no update call to make, so the\n * only write this handler can perform is a create; `diff` has already decided that a\n * real change is a replace, and Alchemy calls delete for the old generation itself.\n */\n reconcile: Effect.fn(function* ({ news }) {\n const live = yield* ops.read(news);\n if (live !== undefined) return live;\n yield* createFs(news);\n const after = yield* ops.read(news);\n if (after === undefined) return yield* Effect.die(notCreated(news));\n return after;\n }),\n /**\n * ⛔ NOT `ops.destroy`, AND NOT AN OVERSIGHT. It would DELETE `spec.path`, which on this\n * family is the index — a route PVE does not implement — so every destroy would fail\n * while the filesystem stayed exactly where it was. The read-back afterwards is what\n * turns \"the task said OK\" into \"it is actually gone\": `destroyfs` refuses while a\n * non-disabled `cephfs` storage still references the filesystem, and that refusal must\n * fail the destroy rather than letting Alchemy drop the state entry for a live object.\n */\n delete: Effect.fn(function* ({ olds }) {\n /**\n * ⚠️ A FILESYSTEM SOMEBODY ALREADY REMOVED BY HAND IS NOT AN ERROR. `destroyfs` dies\n * synchronously with \"no such cephfs\", which would fail every destroy of a stack whose\n * filesystem was cleaned up outside Alchemy and leave the state entry unremovable\n * without editing the store. ⚠️ THE COST IS STATED PLAINLY: `ops.read` folds a FAILED\n * read into \"absent\" (the storage.ts trap), so a read this credential cannot perform\n * turns this into a destroy that reports success having done nothing. That is not a\n * new hazard — a broken read defeats the read-back below in exactly the same way — but\n * it is the reason a 403 on this family must be fixed rather than lived with.\n */\n if ((yield* ops.read(olds)) === undefined) return;\n yield* destroyFs(olds);\n const after = yield* ops.read(olds);\n if (after !== undefined) return yield* Effect.die(notDestroyed(olds));\n }),\n }),\n ),\n );\n",
|
|
38
|
+
"/**\n * `Proxmox.CephDaemon` — a Ceph monitor, manager or metadata server on one node.\n *\n * ★ ONE RESOURCE FOR THREE KINDS, AND THE SCHEMAS WERE CHECKED BEFORE THAT WAS DECIDED. Parsed\n * from `/usr/share/pve-docs/api-viewer/apidoc.js` on node-b, 2026-09-13: mon, mgr and mds each\n * expose GET on the collection and POST + DELETE on the id below it, each returns\n * name/host/state/addr/ceph_version*, and the only divergence is the create parameter\n * (`mon-address` / none / `hotstandby`) and the read-only extras (mon: quorum, rank; mds: rank,\n * fs_name, standby_replay). Three near-identical files would have carried the traps below three\n * times over and let them drift — the argument resource.ts already makes for the factory itself.\n *\n * ⛔ THE ONLY GET IS THE COLLECTION'S, AND IT ANSWERS FOR THE WHOLE CLUSTER. MEASURED:\n * `GET /nodes/node-b/ceph/mon` returns node-b, node-c AND node-d, and `GET /nodes/node-c/ceph/mon` returns the same\n * three IN A DIFFERENT ORDER — [node-d,node-b,node-c] from node-b, [node-c,node-b,node-d] from node-c — so a row is found by\n * `name` and NEVER by position, and the `{node}` in the path is only the node being ASKED.\n * MEASURED too: there is no GET on the id path at all; `pvesh get /nodes/node-b/ceph/mon/node-b` answers\n * \"No 'get' handler defined for '/nodes/node-b/ceph/mon/node-b'\", and mgr and mds answer the same.\n * That is why `path()` below is the COLLECTION, and that in turn is why `delete` is the one\n * handler this file writes by hand: `pveOperations.destroy` sends its DELETE to `spec.path`,\n * which here would be the collection — a 501 reported as a failed destroy, with the daemon still\n * running. Same shape and same rule as the override in acl.ts: do not \"restore symmetry\" by\n * pointing it back at `ops.destroy`.\n *\n * ⛔ WHAT A DESTROY ACTUALLY DOES, BECAUSE THE PLAN LINE SAYS ONLY \"delete\". REASONED FROM PVE'S\n * AND CEPH'S DOCUMENTED BEHAVIOUR, NOT MEASURED — nothing on this cluster was destroyed to find\n * out, and nothing should be to check. `mon`: stops ceph-mon@id, removes it from the monmap and\n * strips its section from ceph.conf. THREE MONS TOLERATE ONE LOSS; TWO TOLERATE NONE. Removing\n * two in one deploy — or removing the mon on the node whose API is serving the call — loses\n * quorum, and a cluster without mon quorum BLOCKS every RBD and CephFS I/O rather than erroring\n * it, so on this cluster every guest on `rbd-c1` hangs.\n * `mds`: destroying a STANDBY is a non-event; destroying the ACTIVE one fails CephFS over,\n * and with no standby left `cephfs-c1` goes unavailable. `mgr`: no guest I/O depends on it, but\n * the last one takes the PG autoscaler, the dashboard and PVE's own Ceph status with it.\n * Live on 2026-09-13: mon, mgr AND mds on each of node-b, node-c, node-d.\n *\n * ★ AND THE ONLY WAY TO REACH THAT DESTROY IS TO DELETE THE DECLARATION. `matches` is deliberately\n * total (see its ⛔), so this resource CANNOT plan a `replace` — which is the one action that\n * would tear down a live mon off the back of an edited field rather than a removed line.\n *\n * ⚠️ THE PRIVILEGES ARE ASYMMETRIC AND THE READ SIDE IS THE CHEAP ONE. The GET checks `any` of\n * `Sys.Audit`/`Datastore.Audit` on `/`, and PVEAuditor holds both (MEASURED:\n * `GET /access/roles/PVEAuditor`), so the mount's `read` role can see this family — unlike\n * `Proxmox.Storage`, which 403s under that lease. POST and DELETE both check `Sys.Modify` on\n * `/`, and MEASURED on this cluster the provision role already held it (it is in `PROVISION_PRIVILEGES`), so the provision lane\n * needs no widening for once. ⚠️ `Sys.Modify` on the ROOT path is broad — it also buys\n * datacenter options and every other cluster-wide config write — so it is worth knowing that\n * this credential already has it rather than discovering it the next time something is scoped.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n DAEMON_ENDPOINTS,\n collectionPath,\n createForm,\n daemonAttributes,\n daemonPath,\n} from './ceph-daemon-form.ts';\nimport { pve } from './client.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\n\n/**\n * Which Ceph daemon this is. The discriminant picks the collection, the create parameters and\n * which of the read-only extras below will even be populated.\n *\n * ⚠️ `osd` IS NOT HERE AND MUST NOT BE ADDED TO THIS UNION. `/nodes/{node}/ceph/osd` takes a disk\n * device, wipes it, and its DELETE has a `cleanup` flag — a create that destroys data on the way\n * in is a different resource with a different set of refusals, not a fourth case in this one.\n */\nexport type CephDaemonKind = 'mon' | 'mgr' | 'mds';\n\nexport interface CephDaemonProps extends WithTarget {\n kind: CephDaemonKind;\n /**\n * The node the daemon RUNS on. Identity — a daemon cannot be moved, only destroyed and rebuilt.\n * ⚠️ It is also the node whose API is asked, and the read is only as available as that node: a\n * node that is down — or has no Ceph installed, or answers 403 — reads as nothing at all,\n * because `pveOperations.read` folds EVERY failure into \"absent\" (a 404 is a legitimate answer\n * there and it cannot tell the two apart). Its daemons then plan as `update`, and reconcile\n * POSTs a create for a mon that already exists. PVE refuses that with \"monitor already\n * exists\", and the read-back refuses again — so it fails loudly rather than damaging anything,\n * but the message will point at the daemon rather than at the node that would not answer.\n */\n node: string;\n /**\n * The daemon id. PVE defaults it to the nodename, and `daemonId` in ceph-daemon-form.ts resolves\n * that default HERE rather than leaving it to PVE, because the id is a PATH SEGMENT: create,\n * delete and the row lookup all need the same string, and a default resolved on the far side\n * would leave three call sites guessing. Live on this cluster every daemon's name is its node.\n */\n name?: string;\n /**\n * mon only. Overrides the autodetected monitor IP; must sit in Ceph's public network — on this\n * cluster that is 198.51.100.0/24, carried by `vmbr1.42`.\n * ⛔ CREATE-ONLY AND NEVER COMPARED. PVE takes a bare ip-list on write and hands back `addr` as\n * `198.51.100.12:6789/0` — an address, a port and a nonce. The two are not the same string and\n * never will be, so diffing them would report a mismatch on every plan; with no PUT on this\n * family that mismatch becomes a REPLACE, and a replace of a mon is the quorum loss in the\n * header. Left out of `matches` on purpose.\n */\n 'mon-address'?: string;\n /**\n * mds only. Makes this standby replay the active MDS's journal for a faster failover.\n * ⛔ CREATE-ONLY AND NEVER COMPARED, AND THE FIELD THAT LOOKS LIKE ITS READBACK IS NOT ONE.\n * `standby_replay` in the list is the daemon's CURRENT state, not its configuration: MEASURED\n * 2026-09-13, all three mds report `standby_replay: false`, node-c included — and node-c is the ACTIVE\n * mds, which is not a standby at all and so can never report true however it was created.\n * Comparing `hotstandby` to it would plan a replace against whichever mds Ceph happens to have\n * elected, i.e. against a value no declaration controls.\n * ⚠️ SET ON A mon OR mgr IT IS DROPPED RATHER THAN SENT, and `mon-address` likewise on an mds:\n * each POST schema lists only its own parameter, so the wrong one is a 400 rather than an\n * ignored hint. A field silently dropped is the lesser of the two, but it IS dropped.\n */\n hotstandby?: boolean;\n}\n\n/**\n * Everything the cluster says about this daemon — reported, and (bar its identity) none of it\n * compared. Read the ⛔ on `matches` for why that is the only safe split on this family.\n */\nexport interface CephDaemonAttributes {\n kind: CephDaemonKind;\n node: string;\n name: string;\n /**\n * The host Ceph says it runs on.\n * ⚠️ REPORTED, NOT COMPARED, AND THAT IS A DELIBERATE BLIND SPOT. A daemon found under this name\n * on a DIFFERENT node than `node` declares plans as `noop`, which does hide a misdeclaration.\n * The alternative is worse: `host` is `optional` in the schema, so an absent one would read as\n * a mismatch, and with no PUT a mismatch is a replace — i.e. a missing field in one API answer\n * would destroy a live mon. When in doubt, out of `matches`; fix a wrong node by hand.\n */\n host: string;\n /**\n * mon `running`/`stopped`/`unknown`, mgr `active`/`standby`, mds `up:active`/`up:standby`/…\n * ⛔ CEPH ELECTS THIS AND REWRITES IT WITHOUT ANYONE DECLARING ANYTHING. MEASURED: mgr node-b is\n * `active` while node-c and node-d are `standby`; mds node-c is `up:active` while node-b and node-d are\n * `up:standby`. Restart a daemon and the roles move. It is reported so a plan can show what is\n * live and kept out of `matches` so that an election is never a diff.\n * ⚠️ IT ALSO MEANS THIS RESOURCE DOES NOT MANAGE WHETHER THE DAEMON IS RUNNING. A configured but\n * stopped daemon is still a row in the list, so it reads as present and plans `noop`.\n */\n state: string;\n /** Ceph-formatted, e.g. `198.51.100.12:6789/0`. Reported; see `mon-address` for why never compared. */\n addr: string;\n /** `ceph_version_short`, e.g. `20.2.2`. Reported: a straggler after an upgrade is worth seeing. */\n version: string;\n /**\n * mon rank in the monmap, mds rank in the filesystem, `-1` when the daemon has neither.\n * ⛔ CEPH ASSIGNS IT AND NOTHING DECLARES IT. MEASURED: the mons are ranked node-d=0, node-c=1, node-b=2 —\n * monmap order, not declaration order — and the mds ranks are 0 for the active node-c and -1 for\n * the two standbys. Rank moves when a daemon is added, removed or restarted.\n */\n rank: number;\n /** mon only: in the current quorum. Reported. MEASURED: all three mons are in quorum. */\n quorum: boolean;\n /** mds only, and NOT the readback of `hotstandby` — see the ⛔ on that prop. */\n standbyReplay: boolean;\n /**\n * mds only: the CephFS this daemon currently serves — `cephfs-c1` here.\n * ⛔ PRESENT ONLY ON THE ACTIVE MDS. MEASURED: node-c carries `fs_name`, node-b and node-d have no such key\n * at all. So it is empty for two of three identical declarations, and comparing it would\n * report work on exactly the daemons that are healthy standbys.\n */\n fsName: string;\n /** A `ceph-<kind>@<id>` systemd unit is enabled on the host. Reported; absent reads as false. */\n service: boolean;\n}\n\nexport interface ProxmoxCephDaemon extends Resource<\n 'Proxmox.CephDaemon',\n CephDaemonProps,\n CephDaemonAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a mon carrying quorum cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxCephDaemon = Resource<ProxmoxCephDaemon>('Proxmox.CephDaemon', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst handlers = pveHandlers<CephDaemonProps, CephDaemonAttributes>({\n attributes: daemonAttributes,\n /** ⛔ The ID path, not the collection — POST is registered on `{id}`, exactly like a metric server. */\n collection: daemonPath,\n createForm,\n /** ⚠️ A function, because the kind picks the endpoint — see `DAEMON_ENDPOINTS`. */\n endpoint: (props: CephDaemonProps) => DAEMON_ENDPOINTS[props.kind],\n /**\n * ⛔ TOTAL, AND THIS IS THE REASON THE FILE EXISTS RATHER THAN A SHORTCUT PAST WRITING IT. Every\n * field a declaration can carry is CREATE-ONLY and unreadable — `mon-address` comes back as a\n * different kind of string, `hotstandby` comes back as a state Ceph elects — and every field\n * the read returns is assigned by Ceph: state, rank, quorum, fs_name, standby_replay, addr,\n * version. Nothing is left that is both declared and readable. So the honest comparison is\n * \"does a daemon of this kind and name exist\", which `attributes` has already answered by\n * returning a value at all, and anything further would be a diff no write could ever satisfy.\n * ⛔ AND ON THIS FAMILY THAT DIFF IS NOT MERELY NOISY. There is no PUT, so resource.ts answers\n * `replace` rather than `update` whenever `matches` is false — and replace on a mon is\n * delete-then-create against a live quorum. A forever-diff here is a forever-OUTAGE-RISK.\n * ⚠️ THE PRICE, STATED PLAINLY: editing `hotstandby` or `mon-address` on a declared daemon\n * plans as `noop` and never applies. Same bargain storage.ts strikes for a changed `type`.\n * Change one by removing the declaration and re-adding it, ONE DAEMON AT A TIME, reading the\n * ⛔ on destroy in the header first.\n */\n matches: () => true,\n /** ⛔ The COLLECTION, because there is no GET on the id path. See the second ⛔ in the header. */\n path: collectionPath,\n /** ⚠️ No `updateForm`: none of the three has a PUT, so nothing about a daemon is editable. */\n});\n\nexport const ProxmoxCephDaemonProvider = () =>\n Provider.effect(\n ProxmoxCephDaemon,\n Effect.succeed(\n ProxmoxCephDaemon.Provider.of({\n ...handlers,\n /**\n * ⛔ THE ONE HANDLER NOT TAKEN FROM THE FACTORY, AND THE ONLY REASON IS THE PATH SPLIT.\n * `ops.destroy` DELETEs `spec.path`, which this resource must point at the collection so\n * that `read` works at all; DELETE on the collection is not implemented and would answer\n * 501 while the daemon kept running — a failed destroy that reads like a permissions\n * problem. Everything else here is the factory's.\n * ⚠️ AND IT IS THE DANGEROUS ONE. Re-read the destroy ⛔ in the header before letting a\n * plan that removes a mon run: two gone at once is a cluster with no quorum and every\n * guest on `rbd-c1` blocked on I/O.\n */\n delete: ({ olds }: { olds: CephDaemonProps }) =>\n pve(olds.target, 'provision', 'DELETE', daemonPath(olds)),\n }),\n ),\n );\n",
|
|
39
|
+
"/**\n * Where a Ceph daemon lives on the API, and what a declaration of one looks like as a PVE form.\n *\n * ★ SPLIT OUT OF ceph-daemon.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a real\n * one rather than a convenient line number — the same seam metric-server-form.ts cuts on.\n * This file answers \"how does a declaration address ONE daemon\" — which URL, which body, and\n * which row of the collection's answer is it. ceph-daemon.ts answers \"what does the cluster\n * say about that daemon, and when has it changed\". Nothing here calls the API and nothing\n * here decides a diff; `findRow` picks a row out of an answer its caller already fetched.\n *\n * ⛔ THE TWO PATHS ARE DIFFERENT AND THAT IS THE WHOLE REASON THIS FILE HAS TWO OF THEM. MEASURED\n * on node-b, 2026-09-13: there is no GET on the id path — `pvesh get /nodes/node-b/ceph/mon/node-b` answers\n * \"No 'get' handler defined\" — while POST and DELETE are registered ONLY there. So the read uses\n * `collectionPath` and the two writes use `daemonPath`, and swapping either one is a silent\n * 501: a create that never lands, or a destroy that reports failure while the daemon runs on.\n *\n * ⚠️ THE `import type` BACK TO ceph-daemon.ts IS A CYCLE ON PAPER ONLY — type-only, so it is\n * erased before anything runs. `CephDaemonProps` stays the resource's public shape, in the file\n * that declares the resource.\n *\n * ⛔ POST AND DELETE RETURN A UPID, NOT AN OBJECT — both are `protected` task endpoints whose\n * schema `returns` is a bare string. REASONED, NOT MEASURED: this cluster is read-only to me, so\n * no create was ever run. The consequence to expect is that `reconcile` reads back immediately\n * after the POST, and if the worker has not yet written ceph.conf the read-back still says\n * absent and reconcile dies with \"the write returned no error but the object is still absent\".\n * That refusal is honest — nothing is recorded for an object that is not there — and a second\n * deploy converges once the task has finished, but it is a confusing FIRST-RUN failure. Watch\n * the task in the PVE UI before concluding the create failed.\n *\n */\nimport type { CephDaemonAttributes, CephDaemonKind, CephDaemonProps } from './ceph-daemon.ts';\nimport type { EndpointPair } from './resource-spec.ts';\nimport { bool, flag, num, text } from './values.ts';\n\n/**\n * The vendor endpoint each kind is created at, as the generated tables key it.\n *\n * ⛔ ONE PER KIND, NOT A `{type}` PATH. PVE registers `mds`, `mgr` and `mon` as three separate\n * nodes with three separate parameter schemas — `hotstandby` belongs to the first and\n * `mon-address` to the third — so a single key would enforce one kind's rules on all three.\n * That is why `PveSpec['endpoint']` admits a function of props at all.\n * ⛔ THE STRINGS MUST STAY LITERALS. `codegen/constraints.ts` finds the endpoints to table by\n * SCANNING THIS PACKAGE'S TEXT, so a key assembled from `props.kind` would be tabled by\n * nothing and `constraintsFor` would throw on the first deploy that reached it.\n * ⚠️ THE PARAMETER NAMES DIFFER AND ARE THE VENDOR'S OWN — `{name}`, `{id}`, `{monid}`. The\n * generator resolves each against the schema, so a tidied-up spelling stops the build.\n * ⚠️ NO `update` ON ANY OF THEM: none of the three has a PUT, which is why the spec declares no\n * `updateForm` either.\n */\nexport const DAEMON_ENDPOINTS: Readonly<Record<CephDaemonKind, EndpointPair>> = {\n mds: { create: 'pve:POST /nodes/{node}/ceph/mds/{name}' },\n mgr: { create: 'pve:POST /nodes/{node}/ceph/mgr/{id}' },\n mon: { create: 'pve:POST /nodes/{node}/ceph/mon/{monid}' },\n};\n\n/**\n * PVE defaults the id to the nodename for all three kinds, and that default is resolved HERE\n * rather than left to PVE, because the id is a PATH SEGMENT: create, delete and the row lookup in\n * ceph-daemon.ts all need the same string, and a default resolved on the far side would leave\n * three call sites guessing. Live on this cluster every daemon's name equals its node.\n */\nexport const daemonId = (props: CephDaemonProps) => props.name ?? props.node;\n\n/**\n * The read path. ⚠️ `{node}` HERE IS ONLY THE NODE BEING ASKED, not a filter. MEASURED:\n * `GET /nodes/node-b/ceph/mon` returns node-b, node-c AND node-d, and `GET /nodes/node-c/ceph/mon` returns the same\n * three IN A DIFFERENT ORDER — [node-d,node-b,node-c] from node-b against [node-c,node-b,node-d] from node-c. A caller that took\n * row zero, or assumed the list was this node's daemons, would be wrong on both counts.\n */\nexport const collectionPath = (props: CephDaemonProps) => `nodes/${props.node}/ceph/${props.kind}`;\n\n/** The write path: POST creates here, DELETE destroys here. Never read from — see the ⛔ above. */\nexport const daemonPath = (props: CephDaemonProps) => `${collectionPath(props)}/${daemonId(props)}`;\n\n/**\n * The create body, which is EMPTY for a mgr and nearly empty for the other two.\n *\n * ⚠️ THE ID IS NOT SENT, because it is already the last segment of the path being POSTed to — the\n * same reasoning as metric-server.ts, where a second copy of the key can only disagree with the\n * first. PVE's schema does accept `monid`/`id`/`name` as body parameters; the path wins, so\n * sending one would be a second source of truth for the object's identity and nothing else.\n *\n * ⚠️ AND THE BRANCHES CANNOT BE ONE SHARED BODY. `hotstandby` is an mds parameter and\n * `mon-address` a mon one; each POST schema lists only its own, so the wrong field is a 400\n * rather than an ignored hint. Both are create-only — the ⛔s on those props say why neither is\n * ever compared afterwards.\n */\nexport const createForm = (props: CephDaemonProps): Record<string, string> => {\n if (props.kind === 'mon' && props['mon-address'] !== undefined) {\n return { 'mon-address': props['mon-address'] };\n }\n const hotstandby = props.kind === 'mds' ? flag(props.hotstandby) : undefined;\n return hotstandby === undefined ? {} : { hotstandby };\n};\n\n/**\n * ⚠️ THE COLLECTION ANSWERS AN ARRAY while the factory types `attributes`' first parameter as the\n * `Record<string, unknown>` every other PVE read is shaped like — the same mismatch acl.ts\n * carries, handled the same way: narrow the rows, trust none of them, and match on `name`.\n * ⚠️ MATCHED ON `name` ALONE, NEVER ON POSITION — the list comes back in a different order\n * depending on which node was asked. See the ⚠️ on `collectionPath` above.\n */\nexport const findRow = (live: unknown, props: CephDaemonProps) =>\n (Array.isArray(live) ? live : [])\n .filter((row): row is Record<string, unknown> => typeof row === 'object' && row !== null)\n .find((row) => row['name'] === daemonId(props));\n\n/**\n * One live row as attributes, or `undefined` — \"no daemon of this kind carries this name\".\n *\n * ★ HERE RATHER THAN IN ceph-daemon.ts FOR THE REASON THAT FILE'S SIBLING EXISTS AT ALL: the\n * 250-line cap, and a seam that is real. It reads the row `findRow` picked out of an answer its\n * caller already fetched, three lines above, so the lookup and the mapping now live together\n * instead of a function call apart.\n *\n * ⚠️ `undefined` WHEN NO ROW CARRIES THIS NAME: that is how the factory learns to create.\n */\nexport const daemonAttributes = (\n live: Record<string, unknown>,\n props: CephDaemonProps,\n): CephDaemonAttributes | undefined => {\n const row = findRow(live, props);\n if (row === undefined) return undefined;\n return {\n addr: text(row['addr']),\n fsName: text(row['fs_name']),\n host: text(row['host']),\n kind: props.kind,\n name: daemonId(props),\n node: props.node,\n quorum: bool(row['quorum']),\n rank: num(row['rank'], -1),\n service: bool(row['service']),\n standbyReplay: bool(row['standby_replay']),\n state: text(row['state']),\n version: text(row['ceph_version_short']),\n };\n};\n",
|
|
40
|
+
"/**\n * `Proxmox.CephFlag` — one cluster-wide Ceph OSD flag, declared: `noout`, `pause`, `norebalance`.\n *\n * ★ ONE RESOURCE PER FLAG, NOT ONE CARRYING ALL ELEVEN, AND THE CLUSTER'S OWN API DECIDED IT.\n * MEASURED on C1 2026-09-13 (pve-manager 9.2.11, ceph tentacle 20.2.2) by reading\n * `/usr/share/perl5/PVE/API2/Cluster/Ceph.pm` and the published schema in\n * `/usr/share/pve-docs/api-viewer/apidoc.js`:\n * · `PUT /cluster/ceph/flags` ends in `fork_worker('cephsetflags', ...)` and returns a STRING\n * — a UPID. The bulk write is ASYNCHRONOUS, and this package has no task-polling machinery.\n * · `PUT /cluster/ceph/flags/{flag}` calls `$rados->mon_command` inline and returns null. The\n * per-flag write is SYNCHRONOUS.\n * `pveOperations.reconcile` READS BACK after every write and refuses to record a value it did\n * not see. Against the bulk endpoint that read-back races a worker which has not run yet, so it\n * would record the OLD value as the new attributes — the one honesty mechanism in this package\n * turned into a lie generator. Per-flag, the read-back means what it says.\n * ★ THE SECOND REASON IS SMALLER AND STILL REAL. The bulk GET answers rows of\n * `{name, description, value}`, and `description` is PVE's own English prose that no write\n * accepts: compare it and you have a forever-update, report it and this provider is pretending\n * to manage prose. `GET /cluster/ceph/flags/{flag}` answers a BARE BOOLEAN, so that trap\n * cannot be written here at all. The brief's plural `Proxmox.CephFlags` became singular for\n * those two reasons; the bulk endpoint is read by nothing in this package.\n *\n * ⛔ THESE ARE NOT CONFIGURATION. THEY ARE A HAND ON THE BRAKE, AND A DECLARATION CAN PULL IT OFF.\n * `noout` is what a person sets before pulling a disk. A stack saying `value: false` reasserts\n * that off on EVERY deploy — including a deploy somebody else runs, for an unrelated resource,\n * twenty minutes into a maintenance window — and the plan line reads `Proxmox.CephFlag noout\n * update`, which nobody reads as \"ceph is about to start rebalancing 128 PGs across three nodes\n * while a disk is out of the chassis\".\n * ★ SO `value` IS REQUIRED AND HAS NO DEFAULT, AND THAT IS THE MITIGATION. An optional `value`\n * defaulting to false would make `CephFlag('noout', { flag: 'noout', target })` mean \"clear\n * it\" — the dangerous direction, chosen by OMISSION. Here the dangerous sentence has to be\n * typed out by a person. Do not add a default, and do not declare a flag merely to document\n * that it is off: MEASURED 2026-09-13, all eleven flags on C1 read 0, so eleven\n * `value: false` lines would plan green forever and do nothing except take the brake off\n * whenever somebody happens to apply them.\n *\n * ⛔ `delete` DOES NOTHING, ON PURPOSE, AND THE ASYMMETRY OF THE TWO FAILURES IS THE ARGUMENT.\n * `flags/{flag}` has exactly two methods, GET and PUT, so the factory's `destroy` would answer\n * \"Method 'DELETE /cluster/ceph/flags/noout' not implemented\" on every teardown — whatever this\n * handler does is INVENTED. The obvious invention, \"clear the flag\", is refused: removing a line\n * from a stack file, or Alchemy collecting an old generation after a replace, would silently\n * take the brake off and nothing would warn. Leaving a flag set that nothing declares any more\n * is the opposite kind of failure — ceph reports `HEALTH_WARN ... flag(s) set` for every one of\n * these, on `ceph -s`, in the PVE UI and in the `pve_*` metrics this estate already scrapes.\n * A silent destructive failure loses to a loud inert one.\n * ⚠️ THE RESIDUAL RISK, NAMED RATHER THAN DENIED: deleting a `value: true` declaration LEAVES\n * THE FLAG SET. Retire a maintenance window by flipping the line to `value: false` and\n * deploying BEFORE the line is removed, or a `noout` outlives the window that needed it.\n * ⚠️ REASONED, NOT MEASURED: that health warning was not observed, because observing it means\n * setting a flag on a live cluster and this file was researched read-only. Undeclaring stops\n * MANAGING a flag; to clear one, declare `value: false` and deploy, where a plan says so.\n *\n * ⛔ `pause` IS TWO CEPH FLAGS AND THIS RESOURCE READS ONLY ONE OF THEM. MEASURED in\n * `PVE::Ceph::Tools::get_real_flag_name`, whose own comment reads \"the 'pause' flag gets always\n * set to both 'pauserd' and 'pausewr'\": PVE writes both and then decides the flag is set by\n * looking at `pauserd` alone. A cluster where somebody ran `ceph osd unset pauserd` by hand\n * still has `pausewr` — writes blocked, guests hung — and this resource reports `value: false`\n * and plans `noop` straight past it. It cannot express a half-pause, so never use it to prove\n * one is gone; `ceph osd dump | head -1` can.\n * ⛔ `pause` AND `noup` ARE ALSO THE TWO THAT TAKE THE CLUSTER DOWN FROM A TYPO. `pause: true`\n * stops all reads and writes, so every guest on `rbd-c1` — the cluster's only rbd pool,\n * MEASURED 2026-09-13 — freezes on its root disk, and so does `cephfs-c1`. `noup: true`\n * stops a rebooted OSD from ever rejoining. Neither is undone by deleting the line.\n *\n * ⚠️ PRIVILEGES, AND THE WRITE ONE IS A BIG ASK. Both GETs check `Sys.Audit` on `/`; both PUTs\n * check `Sys.Modify` on `/`, and all four carry `allowtoken => 1`, so an API token may make the\n * call at all. There is no `/ceph` ACL object to scope to — the check names the ROOT path\n * literally — so granting the provision role `Sys.Modify` here also buys datacenter options,\n * metric servers, notification targets and every other cluster-wide write, exactly as\n * metric-server.ts warns about its own family.\n * ⚠️ REASONED, NOT MEASURED: nothing here was exercised under a minted lease, because a mint\n * writes an API token into the cluster and the brief for this file was reads only. Expect\n * \"Permission check failed (/, Sys.Modify)\" on the first deploy, and widen deliberately.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool, flag } from './values.ts';\n\n/**\n * PVE's eleven flags, spelled as `PVE::Ceph::Tools::get_possible_osd_flags` spells them.\n *\n * ⚠️ THE ENUM IS CLOSED AND A TYPO IS A 400, WHICH IS WHY THIS IS A UNION AND NOT `string`. Both\n * the GET and the PUT declare `additionalProperties => 0` over exactly this list.\n * ⚠️ CEPH HAS FLAGS PVE DOES NOT MODEL, and four of them are always on. MEASURED on C1:\n * `ceph osd dump` reports `flags sortbitwise,recovery_deletes,purged_snapdirs,pglog_hardlimit`.\n * PVE reads that same string and answers only about its own eleven, so the others can neither\n * leak in here nor be set from here — `noautoscale` and `nosnaptrim` included.\n * ⛔ IF SOMEBODY LATER \"IMPROVES\" THE READ BY PARSING `osd dump` DIRECTLY, those four become\n * permanently-set flags that nothing declares: a forever-diff on a brand new cluster.\n */\nexport type CephFlagName =\n | 'nobackfill'\n | 'nodeep-scrub'\n | 'nodown'\n | 'noin'\n | 'noout'\n | 'norebalance'\n | 'norecover'\n | 'noscrub'\n | 'notieragent'\n | 'noup'\n | 'pause';\n\nexport interface CephFlagProps extends WithTarget {\n /**\n * Which flag. PVE's primary key here, and the last segment of the path.\n *\n * ⚠️ EDITING IT IN PLACE ORPHANS THE OLD FLAG RATHER THAN MOVING ANYTHING. `diff` reads the NEW\n * path and reconcile writes it, while the old flag keeps whatever this stack last put there.\n * There is deliberately no replace override for it — acl.ts needs one because its `delete`\n * removes a real grant, and `delete` here is inert by design, so a replace would do exactly\n * what an update already does. Declare a second resource and set the old one to `value: false`\n * rather than renaming this one.\n * ⛔ TWO RESOURCES DECLARING THE SAME FLAG ARE ONE CLUSTER OBJECT, and Alchemy sees two ids\n * rather than a collision — the acl.ts hazard exactly. Disagreeing, they take turns winning\n * and BOTH plan `update` for ever; agreeing, deleting either leaves the flag where the other\n * put it. One declaration per flag per cluster.\n */\n flag: CephFlagName;\n /**\n * Set the flag (`true`) or clear it (`false`).\n *\n * ⛔ REQUIRED, WITH NO DEFAULT — see the second ⛔ in the header, which is the whole safety\n * argument for this family. It is also the only field PVE accepts on this endpoint, and it is\n * not optional in the schema either: omitting it from the form is a 400, not an untouched\n * flag. (The BULK endpoint is the one where omission means \"leave it alone\"; this is not it.)\n */\n value: boolean;\n}\n\nexport interface CephFlagAttributes {\n /**\n * ⚠️ REPORTED, NEVER COMPARED. It is the path key the read was made WITH, copied back out of\n * props, so comparing it against props would be true by construction — the same reasoning\n * acl.ts gives for the four identity fields it also declines to diff.\n */\n flag: CephFlagName;\n /** Whether ceph has the flag set right now. The only field `matches` looks at. */\n value: boolean;\n}\n\nexport interface ProxmoxCephFlag extends Resource<\n 'Proxmox.CephFlag',\n CephFlagProps,\n CephFlagAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxCephFlag = Resource<ProxmoxCephFlag>('Proxmox.CephFlag');\n\nconst handlers = pveHandlers<CephFlagProps, CephFlagAttributes>({\n /**\n * ⚠️ `live` IS A BARE BOOLEAN HERE, NOT AN OBJECT, AND IT IS HANDED TO `bool` WHOLE ON PURPOSE.\n * `get_flag` returns perl `1` or `0` under a `type => 'boolean'` return schema, so the wire is\n * `{\"data\":0}` or `{\"data\":false}` depending on how the REST layer renders it. `bool` accepts\n * every one of those spellings, which is exactly why it is a shared coercion. The factory\n * types this parameter `Record<string, unknown>` because every OTHER PVE read is an object;\n * reaching for `live['value']` here would answer undefined on every plan and diff `false`\n * forever against a set flag.\n * ⚠️ THE SCALAR IS A MEASURED PROPERTY OF THIS PVE, NOT A PROMISE. A later version wrapping\n * the answer in an object would make `bool` read a SET flag as false, and `value: false`\n * would then plan `noop` over a live flag — silently. Re-measure\n * `GET /cluster/ceph/flags/noout` after a major upgrade; cheaper than a second parse path.\n *\n * ⛔ IT NEVER ANSWERS undefined, WHICH MAKES THE CREATE BRANCH UNREACHABLE, AND ITS ERROR WILL\n * MISLEAD YOU. All eleven flags always exist; there is nothing for \"absent\" to mean. So a POST\n * to `collection` means the READ failed — an expired 300s lease, node-b down, or ceph simply not\n * configured on the cluster (every one of these four handlers opens with\n * `check_ceph_configured()`, which dies). Read the resulting \"Method 'POST /cluster/ceph/flags'\n * not implemented\" as \"the read failed\" and go and look at the credential, not at ceph. Same\n * shape as the unreachable POST in acl.ts, same advice.\n */\n attributes: (live, props) => ({ flag: props.flag, value: bool(live) }),\n collection: () => 'cluster/ceph/flags',\n /**\n * ⛔ UNREACHABLE — see the second ⛔ in `attributes`. It is written in the BULK endpoint's shape\n * (the flag name is the parameter, not `value`) rather than left empty, because that is the\n * only body `cluster/ceph/flags` has ever documented. Nothing sends it today.\n */\n createForm: (props) => ({ [props.flag]: flag(props.value) ?? '0' }),\n endpoint: { update: 'pve:PUT /cluster/ceph/flags/{flag}' }, // ⛔ No create: see above.\n /**\n * ⚠️ `value` IS THE ONLY THING COMPARED, AND THE LIST OF THINGS DELIBERATELY NOT COMPARED IS THE\n * point of this resource. `flag` is props-derived (see `CephFlagAttributes`). `description`\n * and `name` exist only on the bulk GET, which this file does not read, so neither can be\n * compared by accident. Nothing PVE returns here is rewritten, re-ordered or re-typed by the\n * cluster, because all PVE returns here is one boolean.\n * ★ MEASURED: on C1, `GET /cluster/ceph/flags` answers value 0 for all eleven flags, so a\n * declaration of `value: false` reads back false and plans `noop`.\n */\n matches: (attributes, props) => attributes.value === props.value,\n path: (props) => `cluster/ceph/flags/${props.flag}`,\n /**\n * ⚠️ ONLY `value` GOES IN THE BODY. `flag` is already bound by the path and the PUT declares\n * `additionalProperties => 0`, so a second copy in the form could only disagree with the URL.\n * ⚠️ THE `?? '0'` IS UNREACHABLE, AND IS WRITTEN RATHER THAN CAST AWAY. `flag()` types its\n * answer `string | undefined` because it serves OPTIONAL props, and `value` here is required,\n * so the branch cannot be taken; a cast would claim a proof the type system has not made.\n */\n updateForm: (props) => ({ value: flag(props.value) ?? '0' }),\n});\n\nexport const ProxmoxCephFlagProvider = () =>\n Provider.effect(\n ProxmoxCephFlag,\n Effect.succeed(\n ProxmoxCephFlag.Provider.of({\n /**\n * ⚠️ NOT THE ONE-LINE `Provider.of(handlers)` EVERY OTHER FILE HERE USES, AND THE TWO\n * REASONS ARE BOTH acl.ts's. PVE implements no DELETE on this path, and the factory's\n * read-back guard is dead whenever `attributes` cannot answer undefined. Unlike acl.ts\n * this SPREADS the factory's handlers instead of retyping them, so `list`, `read` and\n * `diff` cannot drift from the shared ones — only the two that genuinely differ are\n * written out below.\n */\n ...handlers,\n /** ⛔ Inert, deliberately. The third ⛔ in the header is the whole argument; read it. */\n delete: () => Effect.void,\n /**\n * ⚠️ THE READ-BACK GUARD, RESTORED FOR A FAMILY WHOSE \"ABSENT\" DOES NOT EXIST.\n * `ops.reconcile` refuses when the object is still missing after a write, and a flag is\n * never missing, so that check can never fire here — exactly the hole acl.ts patches\n * with `bound`. PVE answers 200 with `{\"data\":null}` on calls that did nothing, and this\n * PUT's documented return IS null, so the status code carries no evidence at all.\n * ⚠️ THE PER-FLAG PUT IS SYNCHRONOUS (`$rados->mon_command` inline, MEASURED in Ceph.pm),\n * which is what makes this check fair rather than a race against a worker task. If it\n * ever fires spuriously the honest fix is to look at the mon, not to delete the guard.\n */\n reconcile: Effect.fn(function* ({ news }) {\n const after = yield* handlers.reconcile({ news });\n if (after.value !== news.value) {\n return yield* Effect.die(\n new Error(\n `cluster/ceph/flags/${news.flag}: the PUT returned no error but ceph still ` +\n `reports the flag ${after.value ? 'set' : 'clear'} rather than ` +\n `${news.value ? 'set' : 'clear'}. This endpoint's success is documented as a ` +\n 'null body, so the status code proves nothing -- check `ceph osd dump | ' +\n 'head -1` and the cluster log. `pause` in particular is two ceph flags read ' +\n 'as one; see the header.',\n ),\n );\n }\n return after;\n }),\n }),\n ),\n );\n",
|
|
41
|
+
"/**\n * `Proxmox.CephFs` — a CephFS on the cluster's own Ceph. The filesystem a `cephfs` storage mounts.\n *\n * ⛔ A REPLACE OF THIS RESOURCE DESTROYS A LIVE FILESYSTEM, AND THERE IS NO PUT TO SAVE YOU FROM\n * IT. MEASURED from the published schema on node-b, 2026-09-13: `/nodes/{node}/ceph/fs/{name}` has\n * exactly two methods, POST and DELETE. A CephFS cannot be edited in place, so `updateForm` is\n * left undefined and anything the factory can see as changed becomes a REPLACE — and Alchemy's\n * replace is create-then-delete, where the delete is `destroyfs`: the entry leaves the MDS map,\n * every mount on every node breaks, and whether the bytes survive rests on `remove-pools` alone.\n * ★ WHICH IS THE WHOLE REASON `matches` COMPARES NOTHING. Read its ⛔ before adding a field to\n * it. On every other resource in this package a forever-diff is an annoying plan line; on this\n * one it is a live filesystem destroyed on every deploy, forever, because a background daemon\n * moved a number nobody declared.\n *\n * ⛔ THERE IS NO SINGLE-OBJECT READ, WHICH INVERTS `path` AND `collection`. MEASURED on node-b:\n * pvesh get /nodes/node-b/ceph/fs/cephfs-c1\n * -> No 'get' handler defined for '/nodes/node-b/ceph/fs/cephfs-c1'\n * `GET /nodes/{node}/ceph/fs` is a directory index and is the only read there is, so `path()`\n * below returns the INDEX and `attributes` picks this filesystem out of the array client-side —\n * the shape acl.ts uses for the one flat `GET /access/acl`. The POST goes to the `{name}` path,\n * so `collection()` is the LONGER of the two strings, the mirror of metric-server.ts.\n * ⚠️ AND THAT IS WHY `delete` AND `reconcile` ARE HAND-WRITTEN BELOW rather than taken from\n * `pveHandlers`. `pveOperations.destroy` deletes `spec.path`, which here is the index:\n * `DELETE /nodes/{node}/ceph/fs` is not a route, so a factory delete would fail every destroy\n * while the filesystem stayed exactly where it was. `reconcile` is hand-written for a second,\n * independent reason — the forked worker — set out in ceph-fs-wire.ts. acl.ts is the\n * precedent for a family that genuinely does not fit; this is the second.\n *\n * ⛔ CREATING A CephFS CREATES TWO CEPH POOLS, AND A `Proxmox.CephPool` MUST NOT ALSO DECLARE THEM.\n * `createfs` builds `<name>_data` and `<name>_metadata` itself and refuses outright if either\n * exists (\"ceph pools '…_data' and/or '…_metadata' already exist\"). MEASURED on C1: the live\n * `cephfs-c1` owns `cephfs-c1_data` (id 6) and `cephfs-c1_metadata` (id 7), both of which\n * `GET /nodes/node-b/ceph/pool` lists like any other pool — and `/nodes/{node}/ceph/pool/{name}` DOES\n * have GET, PUT and DELETE, so a pool resource would be a clean `pveHandlers` fit and would\n * adopt them without noticing. Declaring both is double management of one object: whichever\n * reconciles first wins, and the loser either fails its create or PUTs `size`/`pg_num` onto pools\n * this resource believes it owns. Declare the filesystem here and leave its two pools alone.\n *\n * ⚠️ `add-storage` IS A SECOND SIDE EFFECT AND IT LANDS IN storage.cfg. Set, `createfs` also calls\n * `PVE::API2::Storage::Config->create` with type `cephfs`, content `backup,iso,vztmpl` and\n * `fs-name <name>`. MEASURED: /etc/pve/storage.cfg on node-b holds exactly that — `cephfs:\n * cephfs-c1` / `content backup,vztmpl,iso` / `fs-name cephfs-c1`. So a `Proxmox.Storage`\n * declaring `cephfs-c1` would be managing an entry this resource created. Declare ONE of them:\n * either `add-storage` here and no storage resource, or `add-storage` off and a\n * `Proxmox.Storage` that reads this resource's `name` attribute — which also gets the ordering\n * right for free, because Alchemy orders by data flow.\n *\n * ⚠️ `node` IS A ROUTE, NOT IDENTITY, AND MUST NEVER BE DIFFED. A CephFS is cluster-wide; the node\n * in the path only says which node PVE proxies the call to. MEASURED: `GET /nodes/node-b/ceph/fs`,\n * `…/node-c/…` and `…/node-d/…` returned the byte-identical array. So changing `node` plans `noop`, and\n * two resources naming the same `name` on different nodes are the SAME filesystem — the vmid\n * hazard from lxc.ts, with a filesystem on the end of it instead of a container.\n *\n * ⚠️ PRIVILEGES: the read lane needs `Sys.Audit` OR `Datastore.Audit` on `/`, which an auditor\n * role already holds — unlike storage.ts, nothing needs widening before the first plan. The\n * write side is in ceph-fs-wire.ts beside the calls, and in docs/privileges.md with the rest.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n createForm,\n createFs,\n destroyFs,\n notCreated,\n notDestroyed,\n objectPath,\n readRow,\n} from './ceph-fs-wire.ts';\nimport { type PveRequirements, type WithTarget, pveOperations } from './resource.ts';\n\nexport interface CephFsProps extends WithTarget {\n /** ⚠️ Which node answers the call, NOT which node holds the filesystem. See the header. */\n node: string;\n /**\n * The filesystem name, and the cluster-wide primary key. PVE's pattern is `^[^:/\\s]+$`.\n * ⛔ CHANGING IT IS A REPLACE, WHICH DESTROYS THE OLD FILESYSTEM. `diff` says so explicitly\n * below rather than letting the factory report a create-shaped `update` and orphan the old one.\n */\n name: string;\n /**\n * Placement groups for the backing data pool; the metadata pool gets a quarter of it, or 8 when\n * this is under 32. PVE's default is 128.\n *\n * ⛔ CREATE-TIME ONLY, UNREADABLE, AND OWNED BY THE AUTOSCALER FROM THE SECOND IT LANDS. It is\n * never compared — see the ⛔ on `matches` — and this is not caution, it is measured. C1's\n * `cephfs-c1` was created with the default 128 (the arithmetic proves it: `cephfs-c1_metadata`\n * sits at 32, which is 128/4), and `cephfs-c1_data` reads pg_num 32 TODAY. The autoscaler moved\n * it by a factor of four, and it is not finished: `pg_autoscale_mode` is `on` for every pool on\n * this cluster and `cephfs-c1_metadata` already reports `pg_num_final: 16` against its live 32.\n */\n pg_num?: number;\n /**\n * Also write a `cephfs` storage entry for this filesystem. ⚠️ Create-time only and never read\n * back — it is an ACTION, not a field. See the header for what it does to `Proxmox.Storage`.\n */\n 'add-storage'?: boolean;\n /** ⛔ DELETE-TIME ONLY, AND IT ERASES THE DATA. Read the ⛔ on `destroyFs` before setting it. */\n 'remove-pools'?: boolean;\n /**\n * Delete the pveceph-managed storage entries too. ⚠️ PVE refuses unless they are already\n * disabled (\"storage '…' is not disabled, make sure to disable and unmount the storage first\"),\n * so this fails the destroy rather than unmounting anything out from under a running guest.\n */\n 'remove-storages'?: boolean;\n}\n\n/**\n * ⚠️ EVERY FIELD HERE IS REPORTED AND NONE IS COMPARED. They exist so a plan, and the state entry\n * behind it, can say what the declaration actually points at — which pools hold the bytes.\n */\nexport interface CephFsAttributes {\n name: string;\n /** The first data pool. PVE keeps it for compatibility; `data_pools` is the real answer. */\n data_pool: string;\n /**\n * ⚠️ SORTED, AND IT IS A SET RATHER THAN A LIST. A CephFS can hold several data pools — added\n * with `ceph fs add_data_pool`, which this resource has no way to express — and PVE reports\n * them in the MDS map's own order. Sorting is what keeps this attribute stable across reads.\n */\n data_pools: string;\n metadata_pool: string;\n /**\n * ⚠️ AN INTEGER THAT NAMES SOMETHING WRITTEN AS A NAME, AND THEREFORE A CLASSIC FOREVER-DIFF IF\n * IT EVER REACHED `matches`. PVE answers `metadata_pool_id: 7` while every write in this family\n * spells a pool by name. It is here to be read, never to be compared. `-1` means absent: the\n * field is optional in the schema and older PVE omits it.\n */\n metadata_pool_id: number;\n}\n\nexport interface ProxmoxCephFs extends Resource<\n 'Proxmox.CephFs',\n CephFsProps,\n CephFsAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a filesystem holding data cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxCephFs = Resource<ProxmoxCephFs>('Proxmox.CephFs', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst ops = pveOperations<CephFsProps, CephFsAttributes>({\n attributes: readRow,\n /** ⚠️ The `{name}` path — longer than `path()`, deliberately. See the second ⛔ in the header. */\n collection: objectPath,\n createForm,\n endpoint: { create: 'pve:POST /nodes/{node}/ceph/fs/{name}' }, // ⚠️ POSTed on the object.\n /**\n * ⛔ NOTHING IS COMPARED, AND THAT IS THE MOST DELIBERATE LINE IN THIS FILE. A CephFS has no\n * readable, writable field: the index returns only the name and the pools behind it, and every\n * prop this resource accepts is create-time or delete-time. So \"present\" IS \"settled\", and the\n * only honest comparison is none at all.\n * ⚠️ THE TEMPTING ADDITION IS `pg_num`, AND IT WOULD BE THE WORST BUG THIS PACKAGE HAS SHIPPED.\n * It is not in the index at all, so it can only be read from the pool endpoint — where the\n * autoscaler owns it. MEASURED on C1 today: created at 128, live at 32, with the metadata\n * pool already scheduled down to 16. Compared, that is an eternal mismatch; and because\n * `updateForm` is undefined, the action it produces is REPLACE. The plan would destroy and\n * rebuild a live filesystem on every deploy, triggered by a daemon on its own schedule.\n * ⚠️ `data_pools` AND `metadata_pool_id` ARE OUT FOR THE ORDINARY REASONS: a set PVE returns\n * in its own order, and an integer id for something written as a name. `name` is out because\n * it is the FILTER that produced these attributes, so comparing it is true by construction —\n * an identity change is handled in `diff`, not here. `node` is out because it is a route.\n */\n matches: () => true,\n /** ⚠️ THE INDEX, NOT THE OBJECT. There is no GET on `{name}`; see the second ⛔ in the header. */\n path: (props) => `nodes/${props.node}/ceph/fs`,\n});\n\nexport const ProxmoxCephFsProvider = () =>\n Provider.effect(\n ProxmoxCephFs,\n Effect.succeed(\n ProxmoxCephFs.Provider.of({\n /**\n * ⛔ EMPTY, LIKE EVERY OTHER RESOURCE HERE. The index answers with every filesystem on the\n * cluster, `cephfs-c1` included — the one holding this estate's ISOs and templates.\n * Adopting it would put Alchemy one `destroy` away from it. Adoption stays explicit.\n */\n list: () => Effect.succeed([]),\n read: Effect.fn(function* ({ olds }) {\n return yield* ops.read(olds);\n }),\n /**\n * ⛔ A CHANGED `name` IS A REPLACE, AND THE FACTORY CANNOT SAY SO ALONE. Left to delegate,\n * a rename reads the NEW name, finds nothing, and returns the drift-shaped `update` that\n * `pveOperations.diff` uses for \"Alchemy has state, the cluster does not\" — so reconcile\n * would build the new filesystem and LEAVE THE OLD ONE, with its pools, its storage entry\n * and its bytes, owned by nobody and invisible in every later plan.\n * ⛔ SAYING `replace` IS HONEST, AND IT IS ALSO WHAT MAKES A TYPO EXPENSIVE. Alchemy\n * creates first and deletes second, so the old filesystem is destroyed AFTER the new\n * one exists — with `remove-pools` unset the bytes are stranded rather than erased, and\n * that default is the only thing between a mis-typed rename and an unrecoverable one.\n * Do not \"simplify\" this back to the factory: the alternative is not safer, it is\n * silent. ⚠️ `node` is NOT part of identity and must never join this comparison.\n */\n diff: Effect.fn(function* ({ news, output }) {\n if (output !== undefined && isResolved(news) && news.name !== output.name) {\n return { action: 'replace' } as const;\n }\n return yield* ops.diff(news, output);\n }),\n /**\n * ⛔ NOT `ops.reconcile`, BECAUSE ITS READ-BACK RACES A FORKED WORKER. The POST returns a\n * UPID and nothing else; `createFs` waits the task out and surfaces its real error. The\n * read-back the factory does is kept here rather than dropped — waiting proves the task\n * ENDED, reading proves the filesystem EXISTS, and PVE has shipped tasks that end OK\n * having done nothing.\n * ⚠️ AN EXISTING FILESYSTEM IS RETURNED UNTOUCHED. There is no update call to make, so the\n * only write this handler can perform is a create; `diff` has already decided that a\n * real change is a replace, and Alchemy calls delete for the old generation itself.\n */\n reconcile: Effect.fn(function* ({ news }) {\n const live = yield* ops.read(news);\n if (live !== undefined) return live;\n yield* createFs(news);\n const after = yield* ops.read(news);\n if (after === undefined) return yield* Effect.die(notCreated(news));\n return after;\n }),\n /**\n * ⛔ NOT `ops.destroy`, AND NOT AN OVERSIGHT. It would DELETE `spec.path`, which on this\n * family is the index — a route PVE does not implement — so every destroy would fail\n * while the filesystem stayed exactly where it was. The read-back afterwards is what\n * turns \"the task said OK\" into \"it is actually gone\": `destroyfs` refuses while a\n * non-disabled `cephfs` storage still references the filesystem, and that refusal must\n * fail the destroy rather than letting Alchemy drop the state entry for a live object.\n */\n delete: Effect.fn(function* ({ olds }) {\n /**\n * ⚠️ A FILESYSTEM SOMEBODY ALREADY REMOVED BY HAND IS NOT AN ERROR. `destroyfs` dies\n * synchronously with \"no such cephfs\", which would fail every destroy of a stack whose\n * filesystem was cleaned up outside Alchemy and leave the state entry unremovable\n * without editing the store. ⚠️ THE COST IS STATED PLAINLY: `ops.read` folds a FAILED\n * read into \"absent\" (the storage.ts trap), so a read this credential cannot perform\n * turns this into a destroy that reports success having done nothing. That is not a\n * new hazard — a broken read defeats the read-back below in exactly the same way — but\n * it is the reason a 403 on this family must be fixed rather than lived with.\n */\n if ((yield* ops.read(olds)) === undefined) return;\n yield* destroyFs(olds);\n const after = yield* ops.read(olds);\n if (after !== undefined) return yield* Effect.die(notDestroyed(olds));\n }),\n }),\n ),\n );\n",
|
|
29
42
|
"/**\n * The wire lane of `Proxmox.CephFs`: how a PVE answer becomes attributes, how a declaration becomes\n * a PVE call, and how that call becomes a fact.\n *\n * ★ SPLIT OUT OF ceph-fs.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a real one\n * rather than a convenient line number. ceph-fs.ts answers \"what is a CephFS, and when has it\n * changed\"; this file answers \"what does PVE say, what does it want, and has it finished\". On\n * this family that is where all the strangeness lives — the read is an index and not an object,\n * the paths are inverted, the delete takes its arguments in the query string, and both writes\n * fork a worker — so keeping it in one place is what stops the next reader assuming the ordinary\n * shape from the other nineteen resources. ⚠️ NOTHING HERE DECIDES A DIFF.\n *\n * ⚠️ THE `import type` BACK TO ceph-fs.ts IS A CYCLE ON PAPER ONLY. It is type-only and erased\n * before anything runs, exactly as metric-server-form.ts imports `MetricServerProps`. The props\n * stay the resource's public shape, declared in the file that declares the resource.\n *\n * ⛔ POST AND DELETE ANSWER WITH A UPID, NOT A RESULT — THE WRITE IS ASYNCHRONOUS AND A FAILED ONE\n * IS STILL HTTP 200. Read from the cluster's own source, /usr/share/perl5/PVE/API2/Ceph/FS.pm on\n * node-b: `createfs` and `destroyfs` both end `return $rpcenv->fork_worker(...)`. Two different lies\n * come out of trusting that status code. A read-back straight after the POST reports \"still\n * absent\" about a filesystem that is being built as it says so — which is precisely the message\n * `pveOperations.reconcile` would print, pointing at the wrong cause. And a worker that dies\n * INSIDE — the pool create, or the add-storage step, both of which are wrapped in `eval` — is\n * never reported to the caller at all. `settle` below is the answer to both.\n * ⚠️ REASONED FROM THAT SOURCE, NOT MEASURED. Nothing was written to this cluster, so the race\n * has not been observed; only the `fork_worker` that guarantees it is there.\n *\n * ⛔ A DELETE BODY IS SILENTLY DISCARDED BY PVE, SO `remove-pools` GOES IN THE QUERY STRING.\n * MEASURED in /usr/share/perl5/PVE/APIServer/AnyEvent.pm on node-b: line 928 reads the request\n * content into params only `if ($method eq 'PUT' || $method eq 'POST')`, and line 1653 sends\n * every other method down a branch that parses `$request->url->query()` and nothing else. So\n * `pve(target, role, 'DELETE', path, form)` would send a body PVE never looks at: the flags would\n * read as unset, the pools would quietly survive a delete that asked for them, and no error would\n * be raised anywhere. sdn-apply.ts already puts `?pending=1` in the path for the same reason.\n *\n * ⚠️ THE TASK POLL RUNS ON THE `read` ROLE THOUGH IT SITS INSIDE reconcile, FOR TWO REASONS. It is\n * a read, and resource.ts already mints `read` for its own read-back inside reconcile. And the\n * leases differ where it matters: `provision` is 300s and explicitly non-renewable, so a create\n * that waits out a slow worker on that lease can expire mid-poll, while `read` is 3600s.\n *\n * ⛔ THE POLLER IS NEVER THE TASK'S OWNER, WHICH COSTS A PRIVILEGE, AND THIS ONE IS MEASURED\n * RATHER THAN FEARED. client.ts mints a fresh credential per call and the mount vends a NEW\n * token id each time, so the token reading the status is never the token recorded in the UPID —\n * and PVE compares them exactly. /usr/share/perl5/PVE/API2/Tasks.pm on node-b, `$check_task_user`:\n * `return $user eq $fulltoken || $user eq $task->{user};`, above it the comment \"token only sees\n * token tasks, user sees user + token tasks\". Token B of the same user matches neither branch.\n * So the fallback is the schema's other clause for `GET /nodes/{node}/tasks/{upid}/status`: \"The\n * user needs 'Sys.Audit' permissions on '/nodes/<node>' if they are not the owner of the task.\"\n * Grant it on the `read` role, or every create burns the full cap below and reports the wrong\n * cause.\n */\nimport * as Effect from 'effect/Effect';\nimport type { CephFsAttributes, CephFsProps } from './ceph-fs.ts';\nimport { pve } from './client.ts';\nimport type { PveTarget } from './credentials.ts';\nimport { csv, flag, int, text } from './values.ts';\n\n/**\n * This filesystem's row of the index, as attributes.\n *\n * ⚠️ `GET /nodes/{node}/ceph/fs` ANSWERS AN ARRAY, while the factory hands `attributes` the\n * `Record<string, unknown>` every other PVE read is shaped like. Rows are narrowed rather than\n * trusted, the same way acl.ts narrows its one flat grant list.\n *\n * ⛔ RETURNING undefined IS WHAT MAKES \"ABSENT\" MEAN ABSENT HERE, unlike acl.ts where the object is\n * always present-but-unbound. A filesystem missing from the index has not been created, and\n * `reconcile` takes the create branch on exactly that signal.\n */\nexport const readRow = (\n live: Record<string, unknown>,\n props: CephFsProps,\n): CephFsAttributes | undefined => {\n const row = (Array.isArray(live) ? live : [])\n .filter(\n (entry): entry is Record<string, unknown> => typeof entry === 'object' && entry !== null,\n )\n .find((entry) => entry['name'] === props.name);\n if (row === undefined) return undefined;\n return {\n data_pool: text(row['data_pool']),\n data_pools: csv(\n Array.isArray(row['data_pools'])\n ? row['data_pools'].map((entry: unknown) => String(entry))\n : [],\n ),\n metadata_pool: text(row['metadata_pool']),\n metadata_pool_id: int(row['metadata_pool_id'], -1),\n name: props.name,\n };\n};\n\n/** ⚠️ `exitstatus` is ABSENT while a task runs, and is the exact string `OK` only on success. */\ntype TaskStatus = { readonly status?: string; readonly exitstatus?: string };\n\n/**\n * ⚠️ SHORT POLLS, LOW CAP. Ninety seconds covers two pool creates plus the ten seconds `createfs`\n * itself spends waiting for an MDS to go active, and is short enough that a stuck task fails the\n * deploy rather than parking it for the afternoon.\n */\nconst POLL_SECONDS = 2;\nconst POLL_ATTEMPTS = 45;\n\n/** ⚠️ `nodes/{n}/ceph/fs/{name}` — the POST and DELETE target, and NOT where a read goes. */\nexport const objectPath = (props: CephFsProps) => `nodes/${props.node}/ceph/fs/${props.name}`;\n\n/** Only the declared flags. ⚠️ An omitted one is PVE's own default, not a value to send. */\nconst field = (name: string, value: string | undefined): Record<string, string> =>\n value === undefined ? {} : { [name]: value };\n\n/**\n * ⚠️ `name` IS DELIBERATELY ABSENT FROM THE FORM. It is already the last segment of the path being\n * POSTed to — the metric-server.ts case again — and a second copy can only ever disagree with it.\n */\nexport const createForm = (props: CephFsProps): Record<string, string> => ({\n ...field('add-storage', flag(props['add-storage'])),\n ...field('pg_num', props.pg_num === undefined ? undefined : String(props.pg_num)),\n});\n\n/** The delete target with its flags in the query string — see the ⛔ in the header. */\nconst destroyPath = (props: CephFsProps) => {\n const query = [\n props['remove-pools'] === true ? 'remove-pools=1' : undefined,\n props['remove-storages'] === true ? 'remove-storages=1' : undefined,\n ].filter((part) => part !== undefined);\n return query.length === 0 ? objectPath(props) : `${objectPath(props)}?${query.join('&')}`;\n};\n\n/**\n * Wait for a forked PVE task, and fail loudly rather than quietly.\n *\n * ⚠️ THE UPID IS PERCENT-ENCODED BECAUSE IT IS FULL OF COLONS — `UPID:node-b:00396D3A:…:root@pam:`\n * (MEASURED shape, from `GET /nodes/node-b/tasks`) is ONE path segment, not seven. Encoding is what\n * guarantees it arrives as one; PVE's router decodes each segment before matching. REASONED from\n * the URI handling rather than measured — no task of this provider's has been polled yet.\n */\nconst settle = (target: PveTarget, node: string, upid: string, what: string) =>\n Effect.gen(function* () {\n for (let attempt = 0; attempt < POLL_ATTEMPTS; attempt += 1) {\n const status = yield* pve<TaskStatus>(\n target,\n 'read',\n 'GET',\n `nodes/${node}/tasks/${encodeURIComponent(upid)}/status`,\n ).pipe(\n // ⚠️ AN UNREADABLE STATUS IS \"NOT SETTLED YET\", NOT A FAILURE. It is a missing Sys.Audit\n // (see the header) or a node mid-restart; either way the task is still the cluster's\n // business, and the cap is what ends the wait rather than one unlucky GET.\n Effect.orElseSucceed(() => undefined),\n );\n if (status?.status === 'stopped') {\n const exit = text(status.exitstatus);\n if (exit === 'OK') return;\n return yield* Effect.die(\n new Error(\n `${what}: PVE task ${upid} finished with \"${exit === '' ? 'no exit status' : exit}\". ` +\n 'The write returned HTTP 200 because it only forked the worker -- the real error is ' +\n `in \\`pvesh get /nodes/${node}/tasks/${upid}/log\\`.`,\n ),\n );\n }\n // ⚠️ A BARE NUMBER IS MILLISECONDS to Effect's Duration, not seconds. The cap in the\n // message below is derived from the same constant so the two cannot drift apart.\n yield* Effect.sleep(POLL_SECONDS * 1000);\n }\n return yield* Effect.die(\n new Error(\n `${what}: PVE task ${upid} was still running after ` +\n `${String(POLL_ATTEMPTS * POLL_SECONDS)}s, or its ` +\n 'status could not be read. Check the task log, and check that the `read` role holds ' +\n `Sys.Audit on /nodes/${node} -- each call mints a new token, so the poller is never the ` +\n 'task owner and is answered 403 rather than told to wait.',\n ),\n );\n });\n\n/**\n * POST the create, then wait for the worker. ⛔ The caller still reads back; see ceph-fs.ts.\n *\n * ⚠️ A CREATE NEEDS A RUNNING *AND* A STANDBY MDS, and refuses BEFORE it forks -- \"no running\n * Metadata Server (MDS) found!\" / \"no standby Metadata Server (MDS) found!\" are synchronous, so\n * they surface as a failed POST rather than as a silent worker. C1 has three (MEASURED: node-c\n * `up:active` for cephfs-c1, node-b and node-d `up:standby`), so this is a note for a smaller cluster.\n * ⚠️ BOTH WRITES CHECK `Sys.Modify` ON `/` -- the root, with the breadth metric-server.ts warns\n * about: granting it buys datacenter options and every other cluster-wide config write too.\n */\nexport const createFs = (props: CephFsProps) =>\n Effect.gen(function* () {\n const upid = text(\n yield* pve<string>(props.target, 'provision', 'POST', objectPath(props), createForm(props)),\n );\n // ⛔ NO UPID MEANS NO WORKER. PVE answers 200 with `{\"data\":null}` on calls that did nothing,\n // and here that is indistinguishable from success by status code alone. Refusing now is the\n // difference between a clear failure and a state entry for a filesystem nobody built.\n if (upid === '') {\n return yield* Effect.die(\n new Error(\n `${objectPath(props)}: POST returned no task id. PVE wraps every answer in ` +\n '{\"data\":...} and can report success on a call that did nothing.',\n ),\n );\n }\n yield* settle(props.target, props.node, upid, objectPath(props));\n });\n\n/**\n * DELETE the filesystem, then wait for the worker.\n *\n * ⛔ WHAT THIS TAKES AWAY DEPENDS ENTIRELY ON `remove-pools`, AND THE DEFAULT IS THE SAFE ONE. With\n * it unset PVE removes the filesystem from the MDS map and LEAVES the two pools on disk: every\n * mount breaks, but the bytes are still there for an operator to recover. With it set the pools\n * go too and nothing is recoverable. Leaving it unset also means a later create of the SAME name\n * fails with \"ceph pools '<name>_data' and/or '<name>_metadata' already exist\" -- that refusal is\n * a BRAKE, not a bug, and it is the last thing standing between a mis-typed rename and the data.\n */\nexport const destroyFs = (props: CephFsProps) =>\n Effect.gen(function* () {\n const upid = text(yield* pve<string>(props.target, 'provision', 'DELETE', destroyPath(props)));\n // ⚠️ A DESTROY THAT ANSWERS WITHOUT A UPID IS NOT REFUSED HERE. `destroyfs` dies synchronously\n // with \"no such cephfs\" when the filesystem is already gone, which surfaces as a failed\n // DELETE; an empty answer that is not an error is best treated as already-absent, and the\n // caller's read-back is what proves it either way.\n if (upid !== '') yield* settle(props.target, props.node, upid, destroyPath(props));\n });\n\n/**\n * The two read-back failures, as messages rather than as guesses.\n *\n * ⛔ WAITING PROVES THE TASK ENDED; ONLY A READ PROVES THE FILESYSTEM DID OR DID NOT. `createfs`\n * wraps its pool creates in an `eval` and CLEANS THEM UP on error before dying, so a worker can\n * plausibly end having built and then removed everything it made. `destroyfs` refuses outright\n * while a non-disabled `cephfs` storage still references the filesystem. Neither outcome is\n * visible in a status code, which is why ceph-fs.ts reads back after both and dies with these.\n */\nexport const notCreated = (props: CephFsProps) =>\n new Error(\n `${objectPath(props)}: the create task finished but the filesystem is not in ` +\n `GET nodes/${props.node}/ceph/fs. Check the task log -- a PVE worker can exit having ` +\n 'rolled back the pools it had just made.',\n );\n\nexport const notDestroyed = (props: CephFsProps) =>\n new Error(\n `${objectPath(props)}: the destroy task finished but the filesystem is still in ` +\n `GET nodes/${props.node}/ceph/fs. PVE refuses to destroy a CephFS while a PVE storage entry ` +\n 'of type cephfs still references it and is not disabled.',\n );\n",
|
|
30
43
|
"/**\n * `Proxmox.CephOsd` — one OSD in the cluster's CRUSH tree.\n *\n * ⛔ CREATE AND DELETE ARE IMPLEMENTED AND `root@pam` IS THE ONLY IDENTITY THAT CAN RUN THEM.\n * That is PVE's restriction, not this package's caution, and it is MEASURED from the cluster's\n * own schema (`/usr/share/pve-docs/api-viewer/apidoc.js`, 2026-09-13):\n *\n * GET /nodes/{node}/ceph/osd -> {\"check\":[\"perm\",\"/\",[\"Sys.Audit\",\"Datastore.Audit\"],\"any\",1]}\n * POST /nodes/{node}/ceph/osd -> *** no permissions block at all ***\n * DELETE /nodes/{node}/ceph/osd/{osdid} -> *** no permissions block at all ***\n *\n * A PVE method with no permissions block is refused to every identity except `root@pam`, so an\n * OpenBao-minted `hf-provision@pve!…` token is answered 403 however wide its role — widening\n * the provision role cannot fix it. Compare the sibling families, which DO carry a check and\n * therefore do work with a token: `ceph/fs`, `ceph/mon` and `disks/zfs` all want `Sys.Modify`.\n *\n * ⚠️ SO THE WRITE PATH IS HONEST RATHER THAN USEFUL. It is written out properly in\n * `ceph-osd-write.ts` — read that file for what each verb destroys — and it will work for an\n * operator running as root@pam. Through this package's credential it will 403, and a 403 is a\n * better answer than a `delete` that silently does nothing while the plan claims otherwise.\n *\n * ★ WHAT IT IS ACTUALLY FOR, THEN: ASSERTION. It turns \"node-b, node-c and node-d each hold two ssd OSDs\"\n * into a line a plan checks, and it gives Ceph-backed resources something real to depend on —\n * read `osd.osdid` into an `rbd` storage's props and Alchemy orders that storage after the\n * assertion, so a stack can no longer report a healthy Ceph storage on a cluster whose OSDs are\n * gone. A replica that disappears becomes work in `plan` and a loud failure on the deploy.\n *\n * ⛔ THE WRITE VERBS MAY NOT EVEN BE REACHABLE FOR THIS PACKAGE'S CREDENTIAL. MEASURED from the\n * cluster's own schema (`/usr/share/pve-docs/api-viewer/apidoc.js`, 2026-09-13): `index`,\n * `osdindex`, `metadata` and `lv-info` each declare a `permissions` block, while `createosd`\n * and `destroyosd` declare NONE — the key is absent, not empty. REASONED, NOT MEASURED: PVE\n * refuses a method with no permissions block to everyone but `root@pam`, so an OpenBao-minted\n * `hf-provision@pve!…` token would likely be answered 403 however wide its role. That is a\n * reason to leave the write lane closed, not something to confirm by trying: if the reasoning\n * is wrong, the confirmation costs a disk.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport type { CephOsdAttributes } from './ceph-osd-tree.ts';\nimport { readOsd } from './ceph-osd-tree.ts';\nimport { createOsd, destroyOsd } from './ceph-osd-write.ts';\nimport type { PveRequirements } from './resource.ts';\nimport type { WithTarget } from './resource.ts';\n\n/**\n * ⚠️ RE-EXPORTED, NOT DECLARED HERE — it is built in ceph-osd-tree.ts, next to the walk that fills\n * it in and to the reasoning for which of its fields may be compared. One name, one import.\n */\nexport type { CephOsdAttributes };\n\nexport interface CephOsdProps extends WithTarget {\n /**\n * Which node ANSWERS the read — not where the OSD lives. MEASURED: the tree node-b returns and the\n * tree node-c returns are the same six leaves, node-d's included. Point this at any node that is up;\n * `host` is the field that says where the disk is.\n */\n node: string;\n /** `2` for `osd.2`. Identity, and an INTEGER — see the ⛔ on `findOsd` in ceph-osd-tree.ts. */\n osdid: number;\n /**\n * The CRUSH host bucket this OSD must sit in. Undeclared means unmanaged, as in storage.ts.\n *\n * ⚠️ DECLARING IT IS AN ASSERTION WITH NO REPAIR. If osd.2 turns up under node-c, the plan reports\n * work every time and the deploy fails, because moving an OSD between failure domains is a\n * `ceph osd crush move` this provider will not perform. That is the intent: a replica in the\n * wrong failure domain is not something to converge quietly past.\n */\n host?: string;\n /**\n * `ssd` | `hdd` | `nvme`, asserted the same way as `host`.\n *\n * ⚠️ THE READ'S SPELLING IS KEPT rather than the create parameter's (`crush-device-class`).\n * This resource never writes, so the tree's name is the only one it has, and inventing a\n * second name for one field is exactly what values.ts exists to stop.\n */\n device_class?: string;\n /**\n * The block device to build this OSD from, e.g. `/dev/nvme1n1`. Declaring it makes the resource\n * able to CREATE; leaving it out keeps the resource a pure assertion over an OSD that exists.\n *\n * ⛔ EVERYTHING ON THIS DEVICE IS DESTROYED BY A CREATE. `POST /nodes/{node}/ceph/osd` hands it\n * to `ceph-volume lvm create`, which zaps it. Declare it only for a disk that is genuinely\n * blank, and read the ⛔ about id allocation below before assuming the create will land as the\n * `osdid` written above.\n */\n dev?: string;\n /**\n * Zap the OSD's logical volumes on destroy (`cleanup=1`). Default false: the LVs are left behind\n * so the disk can be re-added without a rebuild, which is the recoverable choice.\n */\n cleanup?: boolean;\n}\n\nexport interface ProxmoxCephOsd extends Resource<\n 'Proxmox.CephOsd',\n CephOsdProps,\n CephOsdAttributes,\n never,\n PveRequirements\n> {}\n\n/**\n * ★ `retain` BY DEFAULT, FOLLOWING ALCHEMY'S OWN PRECEDENT. `GitHub.Repository` and\n * `Cloudflare.Zone` default to retain \"because their contents are irreplaceable\"; an OSD is a\n * replica of live data and qualifies twice over. Retain means DELETING THE DECLARATION DOES NOT\n * DELETE THE OSD — Alchemy drops its state row and leaves the disk alone. `destroyOsd` is fully\n * implemented in ceph-osd-write.ts and runs when the caller opts in with\n * `.pipe(RemovalPolicy.destroy())`, so this is the Terraform `prevent_destroy` shape rather than\n * a missing operation — and through a minted token it 403s anyway, per the ⛔ at the top.\n */\nexport const ProxmoxCephOsd = Resource<ProxmoxCephOsd>('Proxmox.CephOsd', {\n defaultRemovalPolicy: 'retain',\n});\n\n/**\n * The live OSD, or undefined when the tree has no leaf with that id.\n *\n * ⛔ NO `Effect.orElseSucceed(() => undefined)`, AND THAT IS THE ONE DELIBERATE DEPARTURE FROM\n * `pveOperations.read`. The factory folds every failure into \"absent\" because a 404 is a\n * legitimate answer on a per-object path — but this path is a COLLECTION that always exists\n * while Ceph is installed, so a failure here is never \"the OSD is gone\". It is an unreachable\n * node, an expired lease, a role without `Sys.Audit`, or a cluster with no Ceph at all. Folding\n * those into \"absent\" would turn every one of them into the sentence \"osd.2 is missing\", which\n * is the most alarming thing this provider can say and would be a lie in all four cases. Left\n * to fail, the plan shows `PVE GET nodes/node-b/ceph/osd -> 500: …` and names the real problem.\n */\n\n/**\n * Whether the live OSD is the one that was declared.\n *\n * ⚠️ AN UNDECLARED FIELD IS NOT COMPARED, as in storage.ts: undeclared means unmanaged, and there\n * is no default to fall back on — the whole point of a CRUSH placement is that it is a choice.\n * Declaring nothing but `osdid` therefore asserts only presence, which is a perfectly good\n * thing to assert about a replica and the safest declaration this resource accepts.\n */\nconst settled = (live: CephOsdAttributes, props: CephOsdProps) =>\n (props.host === undefined || props.host === live.host) &&\n (props.device_class === undefined || props.device_class === live.device_class);\n\nconst describe = (props: CephOsdProps) =>\n `osd.${String(props.osdid)} (read via nodes/${props.node}/ceph/osd)`;\n\n/**\n * ⛔ HAND-WRITTEN RATHER THAN `pveHandlers(spec)`, and the reason is not that the shape does not\n * fit — it fits perfectly, which is the danger. Given a spec, the factory's `reconcile` POSTs\n * `createForm` at `collection` whenever the read says absent, and `destroy` DELETEs `path`.\n * Wired to this family that is: build an OSD over whatever block device the props named, and\n * remove a replica when somebody deletes a line from a stack file. acl.ts is the precedent for\n * overriding the factory because PVE lacks a verb; this file overrides it because PVE HAS the\n * verbs and they must not be reachable from a declaration.\n *\n * ⚠️ THERE IS NO `replace` PATH HERE AND THERE CANNOT BE ONE. Alchemy implements a replace as\n * create-then-delete (or the reverse); with both of those refusing, `replace` could only ever\n * be a failure wearing a different word. A changed `osdid` is simply a different assertion.\n */\nconst handlers = {\n /**\n * ⛔ EMPTY, as everywhere in this package, and here the stakes are at their highest. The tree is\n * the whole cluster's — adopting it would hand Alchemy six resources it never created, each\n * with a `delete` in its future. Adoption stays an explicit act, and for this family it stays\n * an act with no teeth: see `delete` below.\n */\n list: () => Effect.succeed([]),\n\n read: Effect.fn(function* ({ olds }: { olds: CephOsdProps }) {\n return yield* readOsd(olds);\n }),\n\n /**\n * ⚠️ `update` IS THE LOUDEST WORD AVAILABLE, NOT A PROMISE TO FIX ANYTHING. Alchemy's Diff\n * admits only noop/update/replace, so a missing or misplaced OSD has to be spelled `update`\n * — and the deploy that follows fails on purpose in `reconcile`. A resource that answered\n * `noop` over a replica that is not there would be the exact lie this package keeps writing\n * guards against; a permanent entry in `plan` until a human puts a disk back is the cost, and\n * it is the right cost for a degraded pool.\n */\n diff: Effect.fn(function* ({\n news,\n output,\n }: {\n news: Input<CephOsdProps>;\n output: CephOsdAttributes | undefined;\n }) {\n if (output === undefined || !isResolved(news)) return undefined;\n const live = yield* readOsd(news);\n if (live === undefined) return { action: 'update' } as const;\n /**\n * ⛔ THE RECORDED STATE MUST DESCRIBE THE OSD THE DECLARATION NOW NAMES. `settled` compares\n * only host and device_class, so changing `osdid: 2` to `3` reads osd.3, finds it on the\n * same host with the same class, and answers noop — which records nothing, leaving OSD 2's\n * crush_weight, pgs and name in state under a declaration that says 3. It is the OUTPUT's id\n * that must be checked: the LIVE id is found BY `news.osdid`, so comparing that is vacuous.\n */\n if (output.osdid !== news.osdid) return { action: 'update' } as const;\n return settled(live, news) ? ({ action: 'noop' } as const) : ({ action: 'update' } as const);\n }),\n\n /**\n * ⛔ THE ASSERTION, AND THE WHOLE REASON THIS PROVIDER IS HAND-WRITTEN. It reads, and then it\n * either records what it found or dies. It never POSTs. The first deploy of a declaration is\n * an adoption of an OSD that already exists; every later one is a check that it still does.\n */\n reconcile: Effect.fn(function* ({ news }: { news: CephOsdProps }) {\n const live = yield* readOsd(news);\n if (live === undefined) {\n /**\n * ⛔ CEPH ALLOCATES THE OSD ID; THE DECLARATION CANNOT CHOOSE IT. Measured from the schema:\n * `POST /nodes/{node}/ceph/osd` requires `dev` and takes `crush-device-class`, `db_dev`,\n * `wal_dev`, `encrypted` and `osds-per-device` — and NO id. The cluster assigns the next\n * free number. So a create only lands on the declared `osdid` when that number happens to\n * be the one Ceph picks, which for a fresh disk on a contiguous cluster it usually is.\n * The read-back below is what keeps that from being a silent mismatch: if Ceph assigned a\n * different id, the OSD this resource names is still absent and reconcile refuses.\n */\n return yield* createOsd(news);\n }\n if (!settled(live, news)) {\n return yield* Effect.die(\n new Error(\n `${describe(news)}: exists but not as declared -- live host=${live.host} ` +\n `device_class=${live.device_class}. Nothing here moves an OSD between hosts or ` +\n 'reclassifies it; fix the declaration, or move it deliberately with `ceph osd crush ' +\n 'move` / `ceph osd crush set-device-class` and deploy again.',\n ),\n );\n }\n return live;\n }),\n\n /**\n * ⛔ REMOVING THE DECLARATION REMOVES THE ASSERTION, NOT THE REPLICA — the resource defaults\n * to `retain`, so this runs only on `.pipe(RemovalPolicy.destroy())`. What the DELETE actually\n * does to a live pool is documented on `destroyOsd` in ceph-osd-write.ts.\n */\n delete: Effect.fn(function* ({ olds }: { olds: CephOsdProps }) {\n yield* destroyOsd(olds);\n }),\n};\n\nexport const ProxmoxCephOsdProvider = () =>\n Provider.effect(ProxmoxCephOsd, Effect.succeed(ProxmoxCephOsd.Provider.of(handlers)));\n",
|
|
31
44
|
"/**\n * The CRUSH tree, walked down to one OSD leaf.\n *\n * ★ SPLIT OUT OF ceph-osd.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is the same\n * one metric-server-form.ts cuts: this file answers \"what does the cluster say about osd.N\",\n * ceph-osd.ts answers \"what is an OSD and what may a plan do about it\". Nothing here calls the\n * API and nothing here decides a diff. `CephOsdAttributes` lives on this side of the seam\n * because it is a description of what the TREE says — keeping each field's\n * reported-never-compared reasoning against the line that builds it — and ceph-osd.ts re-exports\n * it so a consumer still imports one name from one place. The `import type` back for\n * `CephOsdProps` is a cycle on paper only: it is erased before anything runs.\n *\n * ⛔ `GET /nodes/{node}/ceph/osd` ANSWERS A TREE, NOT A LIST, AND IT IS THE WHOLE CLUSTER'S TREE.\n * MEASURED 2026-09-13: asked of node-b it answers `{\"flags\":…,\"root\":{\"children\":[…]}}` whose\n * leaves include osd.0 and osd.5 on node-d; asked of node-c it answers the identical six leaves. The\n * `{node}` in the path chooses WHO ANSWERS, not what is listed. A provider that read it as \"the\n * OSDs on this node\" would find osd.0 through node-b and then report it as living there.\n *\n * ⛔ AND THE SINGLE-OBJECT PATH CANNOT BE USED INSTEAD — THIS WALK EXISTS BECAUSE IT LIES.\n * MEASURED: `GET /nodes/node-b/ceph/osd/99`, an id with no OSD behind it, answers HTTP 200 with\n * `[{\"name\":\"metadata\"},{\"name\":\"lv-info\"}]` — byte for byte what `…/osd/2` answers for a real\n * one. It is a directory index (its schema says `permissions: {\"user\":\"all\"}`), not a read of\n * the OSD. Point `PveSpec.path` at it and every declared OSD is \"present\": `diff` reports\n * `noop` over a dead replica forever, and `reconcile`'s read-back guard — the one thing that\n * stops this package recording objects that do not exist — passes on an OSD that was never\n * built. The only endpoint that can tell present from absent is the tree.\n *\n * ★ MEASURED ON C1 ON 2026-09-13, `GET /nodes/node-b/ceph/osd`. Six leaves, all `up` and `in`, Ceph\n * 20.2.2 tentacle:\n *\n * id name host device_class crush_weight reweight pgs status\n * 0 osd.0 node-d ssd 1.86299133300781 1 99 up\n * 5 osd.5 node-d ssd 1.86299133300781 1 94 up\n * 1 osd.1 node-c ssd 1.86299133300781 1 96 up\n * 4 osd.4 node-c ssd 1.81939697265625 1 97 up\n * 2 osd.2 node-b ssd 1.81939697265625 1 88 up\n * 3 osd.3 node-b ssd 1.81939697265625 1 105 up\n *\n * `{ node: 'node-b', osdid: 2, host: 'node-b', device_class: 'ssd' }` plans `noop` against that row.\n * That is the acceptance test this file was written to pass.\n *\n * ⚠️ EVERY OTHER COLUMN IS REPORTED AND NEVER COMPARED, FOR FOUR DIFFERENT REASONS. `pgs` is the\n * autoscaler's placement rather than anybody's declaration — MEASURED, it spans 88 to 105 across\n * six identical disks, and Ceph moves PGs whenever it rebalances. `crush_weight` is a FLOAT\n * derived from device size (MEASURED: 1.86299133300781), so comparing it is float equality\n * against a number Ceph recomputes on `crush reweight`. `reweight` is what\n * `reweight-by-utilization` writes, unasked. `status` and `in` are daemon state: an OSD that\n * flaps down while a node reboots would otherwise put work in the plan that no write this\n * provider owns could ever clear. Each of those is the forever-update this package has already\n * been bitten by, and each is out of `settled` below.\n *\n * ⚠️ THE CLUSTER'S OSD FLAGS ARE NOT AN ATTRIBUTE EITHER. The read carries\n * `\"flags\":\"sortbitwise,recovery_deletes,purged_snapdirs,pglog_hardlimit\"` NEXT TO the root\n * rather than inside any leaf: they describe the OSDMap, not this OSD. Recording them per-OSD\n * would rewrite all six resources' state the moment somebody set `noout` for ten minutes of\n * maintenance — the churn-that-reads-like-drift that keeps `digest` out of storage.ts.\n *\n */\nimport * as Effect from 'effect/Effect';\nimport type { CephOsdProps } from './ceph-osd.ts';\nimport { pve } from './client.ts';\nimport { bool, int, num, text } from './values.ts';\n\n/** A CRUSH node. Buckets carry `children`; an OSD leaf carries `type: 'osd'`. */\ntype CrushNode = Record<string, unknown>;\n\nconst branches = (node: CrushNode): CrushNode[] =>\n (Array.isArray(node['children']) ? node['children'] : []).filter(\n (child): child is CrushNode => typeof child === 'object' && child !== null,\n );\n\n/**\n * Every OSD leaf under the tree root, at any depth.\n *\n * ⚠️ RECURSIVE BECAUSE THE TREE IS NOT THREE LEVELS DEEP BY LAW. C1 measures as\n * root(`default`) -> host(node-b|node-c|node-d) -> osd, but CRUSH admits datacenter, rack and chassis\n * buckets between them, and an OSD created outside a host bucket hangs off the root. Reaching\n * in as `root.children[].children[]` would read this cluster correctly and answer \"absent\" —\n * i.e. \"the replica is gone\" — on the first cluster that has a rack in it.\n *\n * ⚠️ THE OUTERMOST OBJECT IS NOT ITSELF A BUCKET. MEASURED: `root` is `{\"children\":[…],\"leaf\":0}`\n * with no `type` and no `name`; the bucket called `default` is its first child. So the walk\n * starts at `tree.root` and selects on `type === 'osd'` rather than assuming a shape.\n */\nexport const osdLeaves = (tree: CrushNode): CrushNode[] => {\n const root = tree['root'];\n const start: CrushNode = typeof root === 'object' && root !== null ? (root as CrushNode) : {};\n const walk = (node: CrushNode): CrushNode[] => [\n ...(node['type'] === 'osd' ? [node] : []),\n ...branches(node).flatMap(walk),\n ];\n return walk(start);\n};\n\n/**\n * The leaf for one OSD id, or undefined when nothing in the tree carries it.\n *\n * ⛔ `id` COMES BACK AS A STRING HERE AND AS AN INTEGER EVERYWHERE ELSE, which is the \"an id\n * returned one way and written another\" forever-diff in its most expensive form. MEASURED: the\n * leaf for osd.2 carries `\"id\":\"2\"` (and the host buckets carry `\"id\":\"-7\"`), while\n * `GET …/osd/2/metadata` carries `\"id\":2` and the `osdid` parameter on POST and DELETE is typed\n * integer. `'2' === 2` is false, so a provider comparing the raw value finds NOTHING, calls\n * every declared OSD absent — and, with a create path wired up, offers to build six OSDs over\n * six live disks. `int` from values.ts is the narrowing, and `Number.NaN` is the fallback\n * precisely because it equals nothing, id 0 included.\n *\n * ⚠️ FIRST MATCH WINS. OSD ids are unique cluster-wide, and PVE does not put the per-device-class\n * shadow buckets (`default~ssd`) in this tree — MEASURED: the whole tree is one `default` root,\n * three hosts, six leaves. If a future PVE did include them, taking the first match keeps the\n * answer stable instead of reporting drift depending on which copy was walked into first.\n */\nexport const findOsd = (tree: CrushNode, osdid: number) =>\n osdLeaves(tree).find((leaf) => int(leaf['id'], Number.NaN) === osdid);\n\nexport interface CephOsdAttributes {\n /** Normalised to an integer here even though the tree spells it `\"2\"`. */\n osdid: number;\n /** `osd.2` — how Ceph, the UI and every log line name it. */\n name: string;\n /** COMPARED, but only when `props.host` is declared. */\n host: string;\n /** COMPARED, but only when `props.device_class` is declared. */\n device_class: string;\n /**\n * ⚠️ REPORTED, NEVER COMPARED — `bluestore` on every live OSD and filestore has been gone for\n * years, so declaring it would add a field that cannot disagree. It is recorded because a\n * future objectstore change is exactly the kind of thing state should have caught in writing.\n */\n osdtype: string;\n /** ⚠️ REPORTED, NEVER COMPARED. Daemon state — see the second ⚠️ in the header. */\n status: string;\n /** ⚠️ REPORTED, NEVER COMPARED. Ceph itself sets this to 0 after `mon_osd_down_out_interval`. */\n in: boolean;\n /** ⚠️ REPORTED, NEVER COMPARED. A float Ceph recomputes; comparing it is a permanent diff. */\n crush_weight: number;\n /** ⚠️ REPORTED, NEVER COMPARED. `reweight-by-utilization` owns this number, not a declaration. */\n reweight: number;\n /** ⚠️ REPORTED, NEVER COMPARED. The autoscaler moves PGs between OSDs whenever it likes. */\n pgs: number;\n /** Device capacity in bytes. Stable per disk, and the honest answer to \"how big is this OSD\". */\n total_space: number;\n /** `20.2.2`. ⚠️ Reported, never compared — an upgrade is not this resource's drift. */\n ceph_version: string;\n}\n\n/**\n * One leaf, as the attributes a plan and the state store get to see.\n *\n * ⚠️ THE TELEMETRY IS DELIBERATELY DROPPED, NOT FORGOTTEN. The leaf also carries `bytes_used`,\n * `percent_used`, `apply_latency_ms` and `commit_latency_ms`. MEASURED, AND THE MEASUREMENT SAYS\n * THE OPPOSITE OF WHAT YOU WOULD EXPECT: across two reads minutes apart, all four were identical\n * on every OSD, and both latencies read 0 — C1 was idle, so nothing was caught in the act. That\n * these move is therefore REASONED, from what they count, not observed. Alchemy persists\n * attributes; on a cluster doing work, keeping them would rewrite all six resources' state on\n * every deploy and record a number that is stale by the time anybody reads it. What is kept is\n * what a human asking \"what is this replica, and is it healthy\" needs; it is not a metrics feed.\n *\n * ⚠️ `osdid` AND `name` ARE THE NORMALISED ID AND ITS SPELLING, taken from the leaf rather than\n * echoed from props, so that state records what the cluster said. They agree by construction\n * here — the leaf was found BY id — but echoing props into attributes is how a resource ends up\n * recording a declaration as though it were an observation.\n */\nexport const osdAttributes = (leaf: CrushNode, props: CephOsdProps): CephOsdAttributes => ({\n ceph_version: text(leaf['ceph_version_short']),\n crush_weight: num(leaf['crush_weight'], 0),\n device_class: text(leaf['device_class']),\n host: text(leaf['host']),\n in: bool(leaf['in']),\n name: text(leaf['name'], `osd.${String(props.osdid)}`),\n osdid: int(leaf['id'], props.osdid),\n osdtype: text(leaf['osdtype']),\n pgs: num(leaf['pgs'], 0),\n reweight: num(leaf['reweight'], 0),\n status: text(leaf['status']),\n total_space: num(leaf['total_space'], 0),\n});\n\n/**\n * The live OSD as attributes, or undefined.\n *\n * ★ IT LIVES HERE RATHER THAN IN ceph-osd.ts BECAUSE THE READ SIDE IS THIS FILE'S WHOLE JOB —\n * `findOsd` and `osdAttributes` are the two halves it composes, and keeping the caller beside\n * them is what let ceph-osd.ts come back under the 250-line cap when create and delete were\n * added. ⚠️ A 404 or a 403 both arrive here as `undefined`: see the ⛔ on `readRole` in\n * resource.ts for why that distinction is invisible and what it costs.\n */\nexport const readOsd = (props: CephOsdProps) =>\n pve<Record<string, unknown>>(props.target, 'read', 'GET', `nodes/${props.node}/ceph/osd`).pipe(\n Effect.map((t) => {\n const leaf = t === undefined ? undefined : findOsd(t, props.osdid);\n return leaf === undefined ? undefined : osdAttributes(leaf, props);\n }),\n );\n",
|
|
32
|
-
"/**\n * The two operations that change a disk: building an OSD and removing one.\n *\n * ★ SPLIT OUT OF ceph-osd.ts FOR THE 250-LINE CAP, and the seam is the honest one — this file is\n * everything that WRITES, and ceph-osd.ts is the declaration and the assertion. A reader asking\n * \"can this destroy my data\" has one file to read.\n *\n * ⛔ BOTH OF THESE ARE IRREVERSIBLE. `create` hands a block device to `ceph-volume lvm create`,\n * which zaps it; `delete` takes a replica out of a live pool and Ceph immediately begins\n * rewriting the missing copies across the remaining nodes. `Proxmox.CephOsd` therefore declares\n * `defaultRemovalPolicy: 'retain'`, so removing a declaration does NOT reach `destroyOsd` — a\n * caller opts in with `.pipe(RemovalPolicy.destroy())`.\n */\nimport * as Effect from 'effect/Effect';\nimport { readOsd } from './ceph-osd-tree.ts';\nimport type { CephOsdProps } from './ceph-osd.ts';\nimport { pve } from './client.ts';\n\nconst describe = (props: CephOsdProps) => `osd.${String(props.osdid)} on ${props.node}`;\n\nexport const createOsd = Effect.fn(function* (news: CephOsdProps) {\n if (news.dev === undefined) {\n return yield* Effect.die(\n new Error(\n `${describe(news)}: no such OSD in the CRUSH tree, and no \\`dev\\` was declared. ` +\n 'Without a device this resource is an ASSERTION over an OSD that must already ' +\n 'exist. Declare `dev` to let it create one -- which WIPES that device -- or fix ' +\n 'the declaration; if the disk was there yesterday, a replica is missing and Ceph ' +\n 'is degraded.',\n ),\n );\n }\n yield* pve(news.target, 'provision', 'POST', `nodes/${news.node}/ceph/osd`,
|
|
33
|
-
"/**\n * `Proxmox.CephPool` — a Ceph RADOS pool, declared. The richest Ceph family and the one a stack is\n * most likely to reach for, because every RBD disk in the cluster lives inside one.\n *\n * ⛔ IT DOES NOT USE `pveHandlers`, AND THE REASON IS MEASURED RATHER THAN STYLISTIC: THE READ AND\n * THE WRITES SIT AT DIFFERENT PATHS. `GET /nodes/{node}/ceph/pool/{name}` is `poolindex` and\n * answers `[{\"name\":\"status\"}]` — an INDEX OF CHILDREN, not the pool. The pool is one level\n * further down, at `.../status` (`getpool`), which accepts no PUT and no DELETE. A `PveSpec`\n * carries ONE `path` for read, update and delete, so this family cannot be spelled in it. `path`\n * below is therefore the READ path and nothing else consumes it: `ops.reconcile` and\n * `ops.destroy` go unused and the two write handlers name their own path. acl.ts is the\n * precedent for hand-writing the five handlers over `pveOperations`.\n *\n * ⛔ A CREATE FIRED AT A POOL THAT ALREADY EXISTS IS NOT A NO-OP, IT IS A PG MERGE — the second ⛔\n * in ceph-pool-form.ts has the evidence, and `confirmAbsent` below is the guard that stops it.\n *\n * ⛔ EVERY WRITE HERE IS A FORKED WORKER. createpool, setpool and destroypool each return a UPID\n * string, so HTTP 200 means \"the task started\" — an immediate read-back can see a pool that does\n * not exist yet, or values that have not landed. `settle` waits for the cluster to agree.\n *\n * ⛔ THE FOUR POOLS ON C1 INCLUDE CEPH'S OWN, AND NONE OF THEM MAY BE ADOPTED. Measured\n * 2026-09-13: `.mgr` (application `mgr`, created and owned by the manager daemons),\n * `cephfs-c1_data` and `cephfs-c1_metadata` (the two halves of the `cephfs-c1` filesystem —\n * destroying either destroys the filesystem), and `rbd-c1`, the rbd pool the guests sit on.\n * `list` answers empty like every resource in this package, so adoption stays an explicit act;\n * the `applications` attribute is reported so that a plan SHOWS which of those a declaration hit.\n *\n * ⚠️ DELETING A POOL DESTROYS EVERY OBJECT IN IT AND CEPH DOES NOT ASK TWICE. Neither `force` nor\n * `remove_storages` is ever sent, so PVE's refusal while a storage on the pool still lists RBD\n * images is left standing. That refusal does NOT cover an RBD user outside storage.cfg.\n *\n * ⚠️ PRIVILEGES, AND THE WRITE LANE IS A BIG ASK. Read is `Sys.Audit` OR `Datastore.Audit` on `/`\n * (PVEAuditor holds both, so the mount's `read` role covers read, diff and the guard). Create,\n * update and delete each check `Sys.Modify` on `/` — there is no `/ceph` object to scope to, so\n * granting it also buys datacenter options and every other cluster-wide write. Widen\n * deliberately, the way `Pool.Allocate` was widened for `Proxmox.Pool`.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n type CephPoolAttributes,\n type CephPoolProps,\n UNSET,\n applications,\n collection,\n createBody,\n hint,\n object,\n same,\n updateBody,\n} from './ceph-pool-form.ts';\nimport { confirmAbsent, settle } from './ceph-pool-settle.ts';\nimport { pve } from './client.ts';\nimport { type PveRequirements, type PveSpec, pveOperations } from './resource.ts';\nimport { formToSend } from './update-guard.ts';\nimport { bool, int, num, text } from './values.ts';\n\nexport type { CephPoolAttributes, CephPoolProps };\n\nexport interface ProxmoxCephPool extends Resource<\n 'Proxmox.CephPool',\n CephPoolProps,\n CephPoolAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a pool holding objects cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxCephPool = Resource<ProxmoxCephPool>('Proxmox.CephPool', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst spec: PveSpec<CephPoolProps, CephPoolAttributes> = {\n /**\n * ⛔ `name` IS THE PRESENCE TEST, AND IT IS ALSO HOW THE INDEX TRAP FAILS SAFE. Pointed at\n * `.../pool/{name}` rather than `.../status`, the factory would hand this function an ARRAY:\n * every field would read absent and the plan would report an update no write can satisfy.\n * `getpool` always returns `name` for a real pool, so that shape answers \"not there\" instead\n * — and \"not there\" cannot reach a create unless `confirmAbsent` agrees.\n */\n attributes: (live, props) => {\n if (typeof live['name'] !== 'string') return undefined;\n return {\n applications: applications(live['application_list']),\n crush_rule: text(live['crush_rule']),\n id: int(live['id'], UNSET),\n min_size: int(live['min_size'], UNSET),\n name: props.name,\n node: props.node,\n nodelete: bool(live['nodelete']),\n nopgchange: bool(live['nopgchange']),\n nosizechange: bool(live['nosizechange']),\n pg_autoscale_mode: text(live['pg_autoscale_mode']),\n pg_num: int(live['pg_num'], UNSET),\n pg_num_min: int(live['pg_num_min'], UNSET),\n size: int(live['size'], UNSET),\n target_size: int(live['target_size'], UNSET),\n target_size_ratio: num(live['target_size_ratio'], UNSET),\n };\n },\n collection,\n createForm: createBody,\n /**\n * ⛔ DECLARING WHAT IS LIVE MUST PLAN noop, AND EVERY OMISSION HERE IS WHY. Out, each with its\n * reason on the prop it belongs to: `pg_num` (the autoscaler rewrites it), `application` (the\n * write only ever ADDS to a list), `target_size_ratio` (float equality), `id`, `applications`\n * and the three `no*` flags (PVE returns them and accepts none of them on write), `node` and\n * `name` (the address the read was made at — true by construction, never a diff). In, and each\n * measured to round-trip unchanged against C1's four pools on 2026-09-13: size 3, min_size 2,\n * pg_autoscale_mode `on`, crush_rule `replicated_rule`, pg_num_min where it is set (16 on\n * cephfs-c1_metadata, absent on rbd-c1), target_size in bytes — and each of the last two\n * through `hint`, which drops a declared zero for the reason given on it.\n */\n matches: (attributes, props) =>\n same(props.size, attributes.size, attributes.nosizechange) &&\n same(props.min_size, attributes.min_size, attributes.nosizechange) &&\n same(props.pg_autoscale_mode, attributes.pg_autoscale_mode) &&\n same(hint(props.pg_num_min), attributes.pg_num_min, attributes.nopgchange) &&\n same(props.crush_rule, attributes.crush_rule, attributes.crush_rule === '') &&\n same(hint(props.target_size), attributes.target_size),\n /**\n * ⛔ THE READ PATH, AND ONLY THE READ PATH.\n * ⚠️ `?verbose=1` IS WHAT MAKES `applications` VISIBLE AT ALL — without it PVE omits the tags\n * entirely (MEASURED across all four pools; there is no `application` key either way, only\n * `application_list`, and only when verbose). The cost is that verbose adds two unguarded mon\n * commands, `df` and `osd pool application get`, so a degraded mgr can fail the read. That is\n * safe here rather than merely unlucky: a failed read is \"absent\", and \"absent\" cannot reach a\n * create unless `confirmAbsent` agrees.\n */\n path: (props) => `${object(props)}/status?verbose=1`,\n updateForm: updateBody,\n};\n\nconst ops = pveOperations(spec);\n\nexport const ProxmoxCephPoolProvider = () =>\n Provider.effect(\n ProxmoxCephPool,\n Effect.succeed(\n ProxmoxCephPool.Provider.of({\n /** ⛔ Empty, and here it is what keeps `.mgr` and the two cephfs pools out — see the header. */\n list: () => Effect.succeed([]),\n read: ({ olds }) => ops.read(olds),\n diff: ({ news, output }) => ops.diff(news, output),\n /**\n * ⚠️ NOT `ops.reconcile`: it POSTs the moment a read comes back empty, and reads back once,\n * immediately. Both are wrong for a family whose writes are forked workers and whose\n * create is destructive against a pool that is already there.\n */\n reconcile: Effect.fn(function* ({ news }) {\n const live = yield* ops.read(news);\n let upid: string | undefined;\n if (live === undefined) {\n yield* confirmAbsent(news, spec.collection(news), object(news));\n upid = yield* pve<string>(\n news.target,\n 'provision',\n 'POST',\n collection(news),\n createBody(news),\n );\n } else {\n /**\n * ⛔ ONLY WHEN `matches` IS FALSE. This PUT used to fire whenever the pool existed, so\n * adopting a pool that already matched — Plan.ts forces reconcile after the probe —\n * sent `setpool` and forked a worker on the live cluster. PVE skipping the unchanged\n * settings (ceph-pool-form.ts) made it harmless to Ceph, not free: it is still a\n * provision-lease write and a task in the cluster's list. update-guard.ts has the rest.\n */\n const form = formToSend(spec.matches, live, news, updateBody(news));\n if (form !== undefined) {\n upid = yield* pve<string>(news.target, 'provision', 'PUT', object(news), form);\n }\n }\n const after = yield* settle(\n news,\n ops.read,\n (row) => row !== undefined && spec.matches(row, news),\n );\n if (after === undefined || !spec.matches(after, news)) {\n return yield* Effect.die(\n new Error(\n `${object(news)}: the write returned no error but the pool still does not match ` +\n `the declaration after 60s. PVE answered with task ${upid ?? '(none)'} -- read ` +\n 'its log with `pvesh get /nodes/<node>/tasks/<upid>/log` to see what Ceph ' +\n 'refused.',\n ),\n );\n }\n return after;\n }),\n /**\n * ⛔ THIS DESTROYS EVERY OBJECT IN THE POOL. No `force`, so PVE's own check — it refuses\n * while a PVE storage on this pool still lists RBD images — is left standing; and no\n * `remove_storages`, so a storage.cfg section pointing here survives and stays somebody's\n * to remove deliberately.\n * ⚠️ AND THE REFUSAL CAN ARRIVE AFTER THE RESPONSE, WHICH IS WHY THIS WAITS. The DELETE\n * forks a worker and answers 200 with a UPID, so mons running with\n * `mon_allow_pool_delete` false fail once the call has already returned. MEASURED on C1\n * 2026-09-13: the config-db value is false while the running mons report true, i.e. it is\n * set in ceph.conf and the two sources disagree by design.\n */\n delete: Effect.fn(function* ({ olds }) {\n yield* pve<string>(olds.target, 'provision', 'DELETE', object(olds));\n const left = yield* settle(olds, ops.read, (row) => row === undefined);\n if (left !== undefined) {\n return yield* Effect.die(\n new Error(\n `${object(olds)}: the DELETE returned no error but the pool is still there after ` +\n '60s. Either a storage on it still holds RBD images, or the mons are running ' +\n 'with mon_allow_pool_delete false.',\n ),\n );\n }\n }),\n }),\n ),\n );\n",
|
|
45
|
+
"/**\n * The two operations that change a disk: building an OSD and removing one.\n *\n * ★ SPLIT OUT OF ceph-osd.ts FOR THE 250-LINE CAP, and the seam is the honest one — this file is\n * everything that WRITES, and ceph-osd.ts is the declaration and the assertion. A reader asking\n * \"can this destroy my data\" has one file to read.\n *\n * ⛔ BOTH OF THESE ARE IRREVERSIBLE. `create` hands a block device to `ceph-volume lvm create`,\n * which zaps it; `delete` takes a replica out of a live pool and Ceph immediately begins\n * rewriting the missing copies across the remaining nodes. `Proxmox.CephOsd` therefore declares\n * `defaultRemovalPolicy: 'retain'`, so removing a declaration does NOT reach `destroyOsd` — a\n * caller opts in with `.pipe(RemovalPolicy.destroy())`.\n */\nimport * as Effect from 'effect/Effect';\nimport { readOsd } from './ceph-osd-tree.ts';\nimport type { CephOsdProps } from './ceph-osd.ts';\nimport { pve } from './client.ts';\nimport { guardForm } from './constraint-guard.ts';\nimport type { EndpointKey } from './constraints.ts';\n\n/**\n * The vendor rules the create form is checked against at plan time.\n *\n * ⛔ DECLARED HERE RATHER THAN ON A SPEC BECAUSE THIS FAMILY HAS NO SPEC — `ceph-osd.ts` writes\n * all five handlers by hand (the ⛔ there says why), so it cannot inherit the shared guard and\n * would otherwise be the one family that writes to Ceph with nothing checking the body.\n * ⚠️ `dev` IS REQUIRED AND THE REFUSAL BELOW ALREADY COVERS IT, with a message about a missing\n * replica that is far more useful than `dev: required`. The table is still worth checking: it\n * also carries `db_dev_size` ≥ 1, `wal_dev_size` ≥ 0.5 and `osds-per-device` ≥ 1, and nothing\n * else in this package knows those numbers.\n * ⚠️ THERE IS NO UPDATE KEY: PVE registers no PUT under `ceph/osd`, and an OSD is not editable.\n */\nexport const OSD_CREATE_ENDPOINT: EndpointKey = 'pve:POST /nodes/{node}/ceph/osd';\n\nconst describe = (props: CephOsdProps) => `osd.${String(props.osdid)} on ${props.node}`;\n\n/**\n * The create body. ★ Exported so the constraint proof can run the REAL form.\n * ⚠️ `dev` is `string | undefined` on the props and non-optional here: `createOsd` refuses first.\n */\nexport const createOsdForm = (props: CephOsdProps, dev: string): Record<string, string> => ({\n dev,\n ...(props.device_class === undefined ? {} : { 'crush-device-class': props.device_class }),\n});\n\nexport const createOsd = Effect.fn(function* (news: CephOsdProps) {\n if (news.dev === undefined) {\n return yield* Effect.die(\n new Error(\n `${describe(news)}: no such OSD in the CRUSH tree, and no \\`dev\\` was declared. ` +\n 'Without a device this resource is an ASSERTION over an OSD that must already ' +\n 'exist. Declare `dev` to let it create one -- which WIPES that device -- or fix ' +\n 'the declaration; if the disk was there yesterday, a replica is missing and Ceph ' +\n 'is degraded.',\n ),\n );\n }\n const form = createOsdForm(news, news.dev);\n // ⛔ BEFORE THE POST, BECAUSE THE POST ZAPS A BLOCK DEVICE. A vendor rule broken here is worth\n // refusing at plan rather than discovering from a 400 after `ceph-volume` has run.\n yield* guardForm(OSD_CREATE_ENDPOINT, form, true);\n yield* pve(news.target, 'provision', 'POST', `nodes/${news.node}/ceph/osd`, form);\n const created = yield* readOsd(news);\n if (created === undefined) {\n return yield* Effect.die(\n new Error(\n `${describe(news)}: the create returned no error but osd.${String(news.osdid)} is ` +\n 'still absent. Ceph allocates OSD ids and cannot be told which to use, so it most ' +\n 'likely assigned a different number -- check `ceph osd tree`, and declare the id it ' +\n 'actually gave the device rather than deploying again.',\n ),\n );\n }\n return created;\n});\n\n/**\n * ⛔ REMOVING THE DECLARATION REMOVES THE ASSERTION, NOT THE REPLICA, AND THAT ASYMMETRY IS THE\n * POINT. `DELETE /nodes/{node}/ceph/osd/{osdid}` takes a replica out of a live pool — with\n * `nobackfill`/`norecover` unset, Ceph immediately starts rewriting the missing copies across\n * the two remaining nodes — and with `cleanup=1` it zaps the LVs as well. A stack file is\n * edited by people in a hurry; a disk is not a thing a deleted line gets to destroy. Take an\n * OSD out by hand, with the cluster healthy and `ceph -s` in front of you: `ceph osd out N`,\n * wait for the backfill to finish, then `pveceph osd destroy N`.\n *\n * ★ SO IT IS IMPLEMENTED, AND THE RESOURCE DEFAULTS TO `retain` INSTEAD. Removing a declaration\n * drops the state row and leaves the replica alone; a caller who really means it opts in with\n * `.pipe(RemovalPolicy.destroy())`. That is the same shape Terraform gives `prevent_destroy`,\n * and it beats a `delete` that silently does nothing — which is a lie to anyone reading the\n * plan.\n *\n * ⚠️ `cleanup` DEFAULTS TO FALSE, so the logical volumes survive and the disk can be re-added\n * without a rebuild. Ceph still starts backfilling the missing copies the moment the OSD goes.\n */\nexport const destroyOsd = (olds: CephOsdProps) =>\n pve(\n olds.target,\n 'provision',\n 'DELETE',\n `nodes/${olds.node}/ceph/osd/${String(olds.osdid)}`,\n olds.cleanup === true ? { cleanup: '1' } : {},\n );\n",
|
|
46
|
+
"/**\n * `Proxmox.CephPool` — a Ceph RADOS pool, declared. The richest Ceph family and the one a stack is\n * most likely to reach for, because every RBD disk in the cluster lives inside one.\n *\n * ⛔ IT DOES NOT USE `pveHandlers`, AND THE REASON IS MEASURED RATHER THAN STYLISTIC: THE READ AND\n * THE WRITES SIT AT DIFFERENT PATHS. `GET /nodes/{node}/ceph/pool/{name}` is `poolindex` and\n * answers `[{\"name\":\"status\"}]` — an INDEX OF CHILDREN, not the pool. The pool is one level\n * further down, at `.../status` (`getpool`), which accepts no PUT and no DELETE. A `PveSpec`\n * carries ONE `path` for read, update and delete, so this family cannot be spelled in it. `path`\n * below is therefore the READ path and nothing else consumes it: `ops.reconcile` and\n * `ops.destroy` go unused and the two write handlers name their own path. acl.ts is the\n * precedent for hand-writing the five handlers over `pveOperations`.\n *\n * ⛔ A CREATE FIRED AT A POOL THAT ALREADY EXISTS IS NOT A NO-OP, IT IS A PG MERGE — the second ⛔\n * in ceph-pool-form.ts has the evidence, and `confirmAbsent` below is the guard that stops it.\n *\n * ⛔ EVERY WRITE HERE IS A FORKED WORKER. createpool, setpool and destroypool each return a UPID\n * string, so HTTP 200 means \"the task started\" — an immediate read-back can see a pool that does\n * not exist yet, or values that have not landed. `settle` waits for the cluster to agree.\n *\n * ⛔ THE FOUR POOLS ON C1 INCLUDE CEPH'S OWN, AND NONE OF THEM MAY BE ADOPTED. Measured\n * 2026-09-13: `.mgr` (application `mgr`, created and owned by the manager daemons),\n * `cephfs-c1_data` and `cephfs-c1_metadata` (the two halves of the `cephfs-c1` filesystem —\n * destroying either destroys the filesystem), and `rbd-c1`, the rbd pool the guests sit on.\n * `list` answers empty like every resource in this package, so adoption stays an explicit act;\n * the `applications` attribute is reported so that a plan SHOWS which of those a declaration hit.\n *\n * ⚠️ DELETING A POOL DESTROYS EVERY OBJECT IN IT AND CEPH DOES NOT ASK TWICE. Neither `force` nor\n * `remove_storages` is ever sent, so PVE's refusal while a storage on the pool still lists RBD\n * images is left standing. That refusal does NOT cover an RBD user outside storage.cfg.\n *\n * ⚠️ PRIVILEGES, AND THE WRITE LANE IS A BIG ASK. Read is `Sys.Audit` OR `Datastore.Audit` on `/`\n * (PVEAuditor holds both, so the mount's `read` role covers read, diff and the guard). Create,\n * update and delete each check `Sys.Modify` on `/` — there is no `/ceph` object to scope to, so\n * granting it also buys datacenter options and every other cluster-wide write. Widen\n * deliberately, the way `Pool.Allocate` was widened for `Proxmox.Pool`.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n type CephPoolAttributes,\n type CephPoolProps,\n UNSET,\n applications,\n collection,\n createBody,\n hint,\n object,\n same,\n updateBody,\n} from './ceph-pool-form.ts';\nimport { confirmAbsent, settle } from './ceph-pool-settle.ts';\nimport { pve } from './client.ts';\nimport { type PveRequirements, type PveSpec, pveOperations } from './resource.ts';\nimport { formToSend } from './update-guard.ts';\nimport { bool, int, num, text } from './values.ts';\n\nexport type { CephPoolAttributes, CephPoolProps };\n\nexport interface ProxmoxCephPool extends Resource<\n 'Proxmox.CephPool',\n CephPoolProps,\n CephPoolAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a pool holding objects cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxCephPool = Resource<ProxmoxCephPool>('Proxmox.CephPool', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst spec: PveSpec<CephPoolProps, CephPoolAttributes> = {\n /**\n * ⛔ `name` IS THE PRESENCE TEST, AND IT IS ALSO HOW THE INDEX TRAP FAILS SAFE. Pointed at\n * `.../pool/{name}` rather than `.../status`, the factory would hand this function an ARRAY:\n * every field would read absent and the plan would report an update no write can satisfy.\n * `getpool` always returns `name` for a real pool, so that shape answers \"not there\" instead\n * — and \"not there\" cannot reach a create unless `confirmAbsent` agrees.\n */\n attributes: (live, props) => {\n if (typeof live['name'] !== 'string') return undefined;\n return {\n applications: applications(live['application_list']),\n crush_rule: text(live['crush_rule']),\n id: int(live['id'], UNSET),\n min_size: int(live['min_size'], UNSET),\n name: props.name,\n node: props.node,\n nodelete: bool(live['nodelete']),\n nopgchange: bool(live['nopgchange']),\n nosizechange: bool(live['nosizechange']),\n pg_autoscale_mode: text(live['pg_autoscale_mode']),\n pg_num: int(live['pg_num'], UNSET),\n pg_num_min: int(live['pg_num_min'], UNSET),\n size: int(live['size'], UNSET),\n target_size: int(live['target_size'], UNSET),\n target_size_ratio: num(live['target_size_ratio'], UNSET),\n };\n },\n collection,\n createForm: createBody,\n /** The vendor rules both forms are checked against at plan time — resource-spec.ts. */\n endpoint: {\n create: 'pve:POST /nodes/{node}/ceph/pool',\n update: 'pve:PUT /nodes/{node}/ceph/pool/{name}',\n },\n /**\n * ⛔ DECLARING WHAT IS LIVE MUST PLAN noop, AND EVERY OMISSION HERE IS WHY. Out, each with its\n * reason on the prop it belongs to: `pg_num` (the autoscaler rewrites it), `application` (the\n * write only ever ADDS to a list), `target_size_ratio` (float equality), `id`, `applications`\n * and the three `no*` flags (PVE returns them and accepts none of them on write), `node` and\n * `name` (the address the read was made at — true by construction, never a diff). In, and each\n * measured to round-trip unchanged against C1's four pools on 2026-09-13: size 3, min_size 2,\n * pg_autoscale_mode `on`, crush_rule `replicated_rule`, pg_num_min where it is set (16 on\n * cephfs-c1_metadata, absent on rbd-c1), target_size in bytes — and each of the last two\n * through `hint`, which drops a declared zero for the reason given on it.\n */\n matches: (attributes, props) =>\n same(props.size, attributes.size, attributes.nosizechange) &&\n same(props.min_size, attributes.min_size, attributes.nosizechange) &&\n same(props.pg_autoscale_mode, attributes.pg_autoscale_mode) &&\n same(hint(props.pg_num_min), attributes.pg_num_min, attributes.nopgchange) &&\n same(props.crush_rule, attributes.crush_rule, attributes.crush_rule === '') &&\n same(hint(props.target_size), attributes.target_size),\n /**\n * ⛔ THE READ PATH, AND ONLY THE READ PATH.\n * ⚠️ `?verbose=1` IS WHAT MAKES `applications` VISIBLE AT ALL — without it PVE omits the tags\n * entirely (MEASURED across all four pools; there is no `application` key either way, only\n * `application_list`, and only when verbose). The cost is that verbose adds two unguarded mon\n * commands, `df` and `osd pool application get`, so a degraded mgr can fail the read. That is\n * safe here rather than merely unlucky: a failed read is \"absent\", and \"absent\" cannot reach a\n * create unless `confirmAbsent` agrees.\n */\n path: (props) => `${object(props)}/status?verbose=1`,\n updateForm: updateBody,\n};\n\nconst ops = pveOperations(spec);\n\nexport const ProxmoxCephPoolProvider = () =>\n Provider.effect(\n ProxmoxCephPool,\n Effect.succeed(\n ProxmoxCephPool.Provider.of({\n /** ⛔ Empty, and here it is what keeps `.mgr` and the two cephfs pools out — see the header. */\n list: () => Effect.succeed([]),\n read: ({ olds }) => ops.read(olds),\n diff: ({ news, output }) => ops.diff(news, output),\n /**\n * ⚠️ NOT `ops.reconcile`: it POSTs the moment a read comes back empty, and reads back once,\n * immediately. Both are wrong for a family whose writes are forked workers and whose\n * create is destructive against a pool that is already there.\n */\n reconcile: Effect.fn(function* ({ news }) {\n const live = yield* ops.read(news);\n let upid: string | undefined;\n if (live === undefined) {\n // ⛔ THE GUARD `ops.reconcile` WOULD HAVE RUN, RESTORED. This handler replaces it\n // wholesale, so without these two calls an ADOPTED pool — whose diff answer Alchemy\n // discards — would reach the cluster with nothing having checked its body.\n yield* ops.guardCreate(news, true);\n yield* confirmAbsent(news, spec.collection(news), object(news));\n upid = yield* pve<string>(\n news.target,\n 'provision',\n 'POST',\n collection(news),\n createBody(news),\n );\n } else {\n /**\n * ⛔ ONLY WHEN `matches` IS FALSE. This PUT used to fire whenever the pool existed, so\n * adopting a pool that already matched — Plan.ts forces reconcile after the probe —\n * sent `setpool` and forked a worker on the live cluster. PVE skipping the unchanged\n * settings (ceph-pool-form.ts) made it harmless to Ceph, not free: it is still a\n * provision-lease write and a task in the cluster's list. update-guard.ts has the rest.\n */\n const form = formToSend(spec.matches, live, news, updateBody(news));\n if (form !== undefined) {\n yield* ops.guardUpdate(news);\n upid = yield* pve<string>(news.target, 'provision', 'PUT', object(news), form);\n }\n }\n const after = yield* settle(\n news,\n ops.read,\n (row) => row !== undefined && spec.matches(row, news),\n );\n if (after === undefined || !spec.matches(after, news)) {\n return yield* Effect.die(\n new Error(\n `${object(news)}: the write returned no error but the pool still does not match ` +\n `the declaration after 60s. PVE answered with task ${upid ?? '(none)'} -- read ` +\n 'its log with `pvesh get /nodes/<node>/tasks/<upid>/log` to see what Ceph ' +\n 'refused.',\n ),\n );\n }\n return after;\n }),\n /**\n * ⛔ THIS DESTROYS EVERY OBJECT IN THE POOL. No `force`, so PVE's own check — it refuses\n * while a PVE storage on this pool still lists RBD images — is left standing; and no\n * `remove_storages`, so a storage.cfg section pointing here survives and stays somebody's\n * to remove deliberately.\n * ⚠️ AND THE REFUSAL CAN ARRIVE AFTER THE RESPONSE, WHICH IS WHY THIS WAITS. The DELETE\n * forks a worker and answers 200 with a UPID, so mons running with\n * `mon_allow_pool_delete` false fail once the call has already returned. MEASURED on C1\n * 2026-09-13: the config-db value is false while the running mons report true, i.e. it is\n * set in ceph.conf and the two sources disagree by design.\n */\n delete: Effect.fn(function* ({ olds }) {\n yield* pve<string>(olds.target, 'provision', 'DELETE', object(olds));\n const left = yield* settle(olds, ops.read, (row) => row === undefined);\n if (left !== undefined) {\n return yield* Effect.die(\n new Error(\n `${object(olds)}: the DELETE returned no error but the pool is still there after ` +\n '60s. Either a storage on it still holds RBD images, or the mons are running ' +\n 'with mon_allow_pool_delete false.',\n ),\n );\n }\n }),\n }),\n ),\n );\n",
|
|
34
47
|
"/**\n * What a Ceph pool IS as a declaration, and the two different forms PVE wants it in.\n *\n * ★ SPLIT OUT OF ceph-pool.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is\n * \"does this touch the cluster\" — the same one metric-server-form.ts draws, just with more on\n * this side because this family needs it. Here: the declared shape, the reported shape, and the\n * coercions that turn one into the other. There: reading C1, deciding a diff, and the four\n * handlers. Nothing in this file makes a call or decides an action.\n * ⚠️ THE PROPS LIVE HERE RATHER THAN IN THE RESOURCE FILE, which is the one place this pair\n * differs from metric-server / notification-target. There is no `import type` cycle back as a\n * result; ceph-pool.ts re-exports both interfaces so the public surface is unchanged.\n *\n * ⛔ POST AND PUT DO NOT TAKE THE SAME PARAMETERS, AND BOTH EXTRAS ARE DESTRUCTIVE. MEASURED from\n * the cluster's own schema (/usr/share/pve-docs/api-viewer/apidoc.js on node-b, pve-manager 9.2.11,\n * 2026-09-13): POST adds `add_storages` and `erasure-coding` to the shared set, PUT takes\n * neither. Both are refused below, so what is left between the two bodies is `name`, which PUT\n * reads from the path, and `pg_num`.\n *\n * ⛔ `pg_num` IS IN THE CREATE BODY AND NOT IN THE UPDATE BODY, AND THAT IS THE MOST IMPORTANT\n * LINE IN THIS FILE. MEASURED in PVE::API2::Ceph::Pool::createpool on node-b: a POST carrying no\n * `pg_num` gets 128 substituted (`$param->{pg_num} //= 128`), and PVE::Ceph::Tools::create_pool\n * then runs `osd pool create` — which Ceph answers with SUCCESS for a pool that already exists,\n * before applying the rest of the body to it. So a create fired at a live pool SETS its pg_num,\n * and lowering pg_num starts a PG MERGE: hours of backfill across every OSD, on a cluster whose\n * Ceph traffic shares vmbr1.42 with everything else. ceph-pool.ts guards the create for exactly\n * that reason; this half simply never offers pg_num to an update.\n */\nimport type { WithTarget } from './resource.ts';\nimport { csv } from './values.ts';\n\nexport interface CephPoolProps extends WithTarget {\n /**\n * ⚠️ WHICH NODE THE CALL GOES THROUGH, NOT WHERE THE POOL LIVES. A pool is cluster-wide: node-b and\n * node-c answer byte-identically for `rbd-c1` (measured). The endpoint is node-scoped only\n * because PVE talks to the local RADOS socket, so this is a door, not a location — it is never\n * compared, and moving a declaration from node-b to node-c plans noop. Point it at a node you expect\n * to be up: a node that is down makes the read fail, and a failed read reads as \"absent\".\n */\n node: string;\n /**\n * Ceph's primary key, cluster-wide.\n * ⛔ CHANGING IT DOES NOT RENAME ANYTHING. There is no rename in this API: the plan reads the new\n * name, finds nothing, and CREATES AN EMPTY POOL, leaving the old one and its data behind,\n * untracked. `replace` was considered and rejected — it would DELETE the old pool and every\n * image in it to satisfy an edited string. Rename by hand, deliberately, or not at all.\n */\n name: string;\n /** Replicas per object, 1-7. ⚠️ Frozen when the pool carries Ceph's `nosizechange`. */\n size?: number;\n /** Replicas required to accept writes, 1-7. ⚠️ Frozen by `nosizechange` too. */\n min_size?: number;\n /**\n * ⛔ CREATE-TIME ONLY, AND NEVER COMPARED — the single biggest noop hazard in this family. With\n * `pg_autoscale_mode` on or warn the autoscaler owns this number: MEASURED on C1, `rbd-c1`\n * sits at pg_num 128 while the autoscaler's own `pg_num_final` is 256, so a declaration\n * diffing it reports an update the moment Ceph decides to act. A split is gradual even with\n * the autoscaler off, and `nopgchange` can refuse the write outright — which kills the WHOLE\n * PUT, not just this field. Declare it as the birth size; steer it afterwards with\n * `pg_num_min`, or by hand.\n */\n pg_num?: number;\n /** The floor the autoscaler may not go below. Operator-owned and stable, so it IS compared. */\n pg_num_min?: number;\n /** ⚠️ PVE's create default is `warn`; Ceph's own is `on`, which is what all four C1 pools have. */\n pg_autoscale_mode?: 'off' | 'on' | 'warn';\n /**\n * The CRUSH rule BY NAME, e.g. `replicated_rule`.\n * ★ THE SUSPECTED INTEGER-VS-NAME TRAP IS REAL BUT NOT ON THE PATH THIS RESOURCE READS, AND THAT\n * IS MEASURED BOTH WAYS. `GET .../ceph/pool` (the collection) reports `crush_rule: 0` with the\n * name beside it in `crush_rule_name`; `GET .../ceph/pool/{name}/status` reports\n * `crush_rule: \"replicated_rule\"` — the same spelling POST and PUT accept. Reading the\n * collection instead would reintroduce the forever-diff in a single edit.\n */\n crush_rule?: string;\n /**\n * ⛔ CREATE-TIME ONLY, AND NEVER COMPARED. PVE's PUT turns this into\n * `osd pool application enable` (Tools.pm:250-255), which ADDS a tag and never removes one,\n * and Ceph refuses a second application without `--yes-i-really-mean-it` — so an update would\n * either leave the pool tagged twice or die, and comparing one declared string against the\n * LIST that comes back is the set-in-arbitrary-order forever-diff. The live list is reported\n * as `applications` instead.\n */\n application?: 'cephfs' | 'rbd' | 'rgw';\n /** Autoscaler size hint, ⚠️ IN BYTES — see the ⚠️ on `updateBody`. A `1T` string is not accepted. */\n target_size?: number;\n /**\n * Autoscaler share of total capacity, 0-1.\n * ⚠️ WRITTEN BUT NEVER DIFFED, because a float is not a safe equality: Ceph stores a double and\n * PVE prints about 15 significant digits, so a ratio that is not exactly representable would\n * report an update on every plan for a pool nobody touched.\n */\n target_size_ratio?: number;\n /**\n * ⛔ `never` ON PURPOSE, BOTH OF THEM, AND THE COMPILE ERROR IS THE FEATURE.\n * `erasure-coding` does not configure this pool — it makes PVE build TWO pools and name\n * neither of them what you declared: `<name>-data` for the EC data and `<name>-metadata` for\n * the replicated half (Pool.pm:517-530). Nothing would then exist at `{name}`, so every plan\n * would read absent and try to create it again, forever.\n * `add_storages` writes a section into storage.cfg that this graph does not own; declare a\n * `Proxmox.Storage` and pass this pool's `name` into it instead, which makes the link real.\n */\n 'erasure-coding'?: never;\n add_storages?: never;\n}\n\n/**\n * ⚠️ THE THREE `no*` FLAGS ARE REPORTED BECAUSE THEY EXPLAIN A REFUSAL. Ceph can freeze a pool's\n * size or its PG shape, and `set_pool` dies on the whole PUT when any one parameter will not\n * apply (Tools.pm:310). `matches` refuses to diff the fields they freeze, so these attributes\n * are the only place a plan can show WHY a declared size is not being enforced. All three read\n * false on all four C1 pools.\n * ⚠️ `pg_num`, `id` AND `applications` ARE REPORTED AND NEVER COMPARED — see the props above.\n * ⛔ NO `statistics` AND NO `autoscale_status`, though the verbose read returns both. They change\n * every few seconds; persisting them would rewrite this resource's state on every deploy and\n * read like drift. `UNSET` means the pool carries no value for that field.\n */\nexport interface CephPoolAttributes {\n name: string;\n /** The node the read went through. A debugging aid, not a property of the pool. */\n node: string;\n id: number;\n size: number;\n min_size: number;\n pg_num: number;\n pg_num_min: number;\n pg_autoscale_mode: string;\n /** ⚠️ Empty when PVE answered with a rule id instead of a name; `matches` then stops diffing it. */\n crush_rule: string;\n /** Sorted comma list, e.g. `rbd` or `cephfs`. Readable only under `?verbose=1`. */\n applications: string;\n target_size: number;\n target_size_ratio: number;\n nodelete: boolean;\n nopgchange: boolean;\n nosizechange: boolean;\n}\n\n/**\n * Where one pool is WRITTEN, and where a new one is POSTed.\n *\n * ⚠️ NEITHER IS WHERE IT IS READ — the read path is a segment longer and lives in ceph-pool.ts\n * next to the ⛔ that explains it. These two are here because a path is wire knowledge, and\n * because the create guard needs the collection without needing anything else from the spec.\n */\nexport const object = (props: CephPoolProps) => `nodes/${props.node}/ceph/pool/${props.name}`;\nexport const collection = (props: CephPoolProps) => `nodes/${props.node}/ceph/pool`;\n\n/**\n * ⚠️ \"NOT SET\" IS -1 AND NOT 0, for the reason metric-server-form.ts gives: `pg_num_min`,\n * `target_size` and `target_size_ratio` each take 0 as a REAL value meaning \"no floor, no hint\",\n * so a provider using 0 as its absent-marker cannot tell a declared zero from an undeclared\n * field. Every one of these is >= 0, so -1 sits outside all of their ranges.\n */\nexport const UNSET = -1;\n\n/** ⚠️ PVE returns the tags as an ARRAY in Ceph's order, so `csv` sorts before anything sees them. */\nexport const applications = (value: unknown) =>\n csv(Array.isArray(value) ? value.map((entry: unknown) => String(entry)) : []);\n\n/**\n * Declared? compare it. Undeclared, or frozen by a flag the pool carries? leave it alone.\n *\n * ⚠️ `frozen` IS THE WHOLE ANSWER TO \"A FIELD PVE WILL NOT ACCEPT ON WRITE\". Comparing one can only\n * plan an update that no write can satisfy: work reported on every run, forever.\n */\nexport const same = <T>(declared: T | undefined, live: T, frozen = false) =>\n declared === undefined || frozen || declared === live;\n\n/**\n * A declared autoscaler hint, or nothing when it is zero.\n *\n * ⚠️ A DECLARED ZERO IS \"NO FLOOR, NO HINT\", AND IT IS DELIBERATELY LEFT UNCOMPARED. REASONED, NOT\n * MEASURED: all four C1 pools carry null for both `pg_num_min` and `target_size`, and finding\n * out for certain would mean writing to the cluster. Ceph CLEARS both when they are set to 0 and\n * `osd pool get all` then omits the key, which PVE reports as null and this pair reads as UNSET\n * — so a declared 0 would compare 0 against -1 on every plan, forever. Mapping 0 to UNSET\n * instead would be a guess in the OTHER direction, and wrong the same way if Ceph does report\n * the zero. Neither guess is made: a zero is still written, and then left unmanaged exactly like\n * an undeclared field.\n */\nexport const hint = (value: number | undefined) => (value === 0 ? undefined : value);\n\n/** One optional field, present in the form only when it was declared. */\nconst field = (name: string, value: string | undefined): Record<string, string> =>\n value === undefined ? {} : { [name]: value };\n\n/** ⚠️ Not named `digits`: `target_size_ratio` is a fraction, and `String` is right for both. */\nconst numeric = (value: number | undefined) => (value === undefined ? undefined : String(value));\n\n/**\n * The PUT body: everything POST and PUT both accept, `pg_num` excepted. `createBody` builds on it,\n * the way storage.ts's `mutable` serves both of its forms. `name` is the path's last segment on an\n * update and is not repeated here.\n *\n * ⚠️ SAFE TO RE-APPLY TO A POOL THAT ALREADY MATCHES (reconcile leaned on it until update-guard.ts).\n * MEASURED in PVE::Ceph::Tools::set_pool (Tools.pm:293-300): it reads `osd pool get all` first\n * and SKIPS every unchanged setting, so the undrifted fields that ride along cost one mon read.\n * ⛔ BUT A SETTING THAT DID CHANGE AND CANNOT BE APPLIED KILLS THE WHOLE PUT. Tools.pm:310 dies\n * with \"Could not set: <fields>\" if any parameter is left unapplied, so a pool carrying Ceph's\n * `nosizechange` fails an entire update over one field. That is why the `no*` flags are reported\n * and why `matches` refuses to diff what they freeze.\n *\n * ⚠️ AN UNDECLARED FIELD IS NEITHER SENT NOR COMPARED — storage.ts's rule, and the opposite of\n * Proxmox.Pool's. Undeclared means UNMANAGED here, because a pool has no one set of defaults to\n * fall back on: PVE's POST fills in size 3, min_size 2, application rbd and pg_autoscale_mode\n * 'warn' (MEASURED in createpool), while Ceph's own defaults — which is what every pool on C1\n * was actually born with — put pg_autoscale_mode at 'on'. Guessing either set would rewrite a\n * pool somebody tuned by hand.\n *\n * ⛔ THERE IS NO `delete=` HERE AND THERE MUST NOT BE ONE. `setpool` is not a SectionConfig write:\n * it turns each parameter into an `osd pool set`, and Ceph has no \"unset\" for size, min_size or\n * crush_rule. metric-server-form.ts clears an undeclared optional because status.cfg supports\n * it; the same move here would send a parameter PVE has no verb for.\n *\n * ⚠️ `target_size` IS BYTES ON THE WIRE IN BOTH DIRECTIONS, WHICH IS WHY THE PROP IS A NUMBER AND\n * NOT PVE's `1T` STRING. MEASURED: the API parses the string with PVE::JSONSchema::parse_size\n * and stores Ceph's `target_size_bytes` (Pool.pm:474-477 for POST, 683-686 for PUT), and\n * `GET .../status` hands that integer back under the name `target_size` (Pool.pm:878). Run on\n * node-b, `parse_size('1099511627776')` returns 1099511627776 — bare digits are bytes, no\n * multiplier — so a byte count survives the round trip exactly, while a declared `1T` would read\n * back as 1099511627776 and diff against itself on every plan, forever.\n */\nexport const updateBody = (props: CephPoolProps): Record<string, string> => ({\n ...field('crush_rule', props.crush_rule),\n ...field('min_size', numeric(props.min_size)),\n ...field('pg_autoscale_mode', props.pg_autoscale_mode),\n ...field('pg_num_min', numeric(props.pg_num_min)),\n ...field('size', numeric(props.size)),\n ...field('target_size', numeric(props.target_size)),\n ...field('target_size_ratio', numeric(props.target_size_ratio)),\n});\n\n/**\n * The POST body.\n *\n * ⛔ `add_storages=0` IS SENT EXPLICITLY RATHER THAN LEFT OUT, AND THE DEFAULT IS NOT WHAT IT LOOKS\n * LIKE. MEASURED at Pool.pm:481 — `$add_storages = 1 if $ec && !defined($add_storages)` — so for\n * an erasure-coded pool PVE turns it ON when the parameter is absent. A pool created with it\n * writes a NEW SECTION INTO storage.cfg that this graph does not own: `Proxmox.Storage` would\n * not know about it, `alchemy destroy` would leave it behind, and the first person to notice\n * would be whoever found a storage nobody declared.\n */\nexport const createBody = (props: CephPoolProps): Record<string, string> => ({\n ...updateBody(props),\n ...field('application', props.application),\n ...field('pg_num', numeric(props.pg_num)),\n add_storages: '0',\n name: props.name,\n});\n",
|
|
35
48
|
"/**\n * Waiting for Ceph to finish, which is a concern of its own.\n *\n * ★ SPLIT OUT OF ceph-pool.ts FOR THE 250-LINE CAP, and the seam is real: every other PVE family\n * in this package writes and reads back in one breath, because PVE's config endpoints are\n * synchronous. Ceph's are not — a pool delete returns before the PGs are gone — so this file is\n * the polling that difference forces, and ceph-pool.ts is the declaration.\n */\nimport * as Effect from 'effect/Effect';\nimport type { CephPoolAttributes, CephPoolProps } from './ceph-pool-form.ts';\nimport { pve } from './client.ts';\nimport type { PveRequirements } from './resource.ts';\n\n/**\n * ⚠️ THE DEPENDENCIES ARE PASSED IN RATHER THAN IMPORTED, and that is what made this file\n * separable at all. `collection`, `object` and `ops.read` are module-level bindings in\n * ceph-pool.ts; importing them back would be a runtime cycle, and duplicating them would be two\n * definitions of one path. Passing them is the pattern zfs-pool-write.ts already uses.\n */\nexport type PoolRead = (\n props: CephPoolProps,\n) => Effect.Effect<CephPoolAttributes | undefined, never, PveRequirements>;\n\n/**\n * ⛔ THE GUARD ON THE CREATE. `read` folds a 403, a timeout and a node that is down into the same\n * `undefined` as a pool that is genuinely missing — so \"absent\" is not evidence. This asks a\n * second, different question: does the cluster LIST this pool? A failure is left to propagate\n * rather than folded, so \"I could not ask\" fails the deploy instead of creating over live data.\n * An empty list is trusted: the collection 403s when the role is too narrow rather than\n * filtering, so `[]` really does mean a cluster with no pools.\n */\nexport const confirmAbsent = (props: CephPoolProps, collection: string, describe: string) =>\n pve<{ pool_name?: string }[]>(props.target, 'read', 'GET', collection).pipe(\n Effect.flatMap((rows) =>\n (rows ?? []).some((row) => row.pool_name === props.name)\n ? Effect.die(\n new Error(\n `${describe}: the cluster lists this pool but its status could not be read, ` +\n 'so this deploy will NOT create over it. Check that the node is up and that the ' +\n 'read role holds Sys.Audit or Datastore.Audit on / -- a create here would apply ' +\n 'pg_num to a live pool and start a PG merge.',\n ),\n )\n : Effect.void,\n ),\n );\n\n/**\n * Wait for the cluster to agree, then answer the last thing it said.\n *\n * ★ IT POLLS `matches`, THE SAME PREDICATE `diff` USES, so \"settled\" has exactly one definition in\n * this file. Polling `/nodes/{node}/tasks/{upid}/status` instead would read the worker's own\n * exit status, but it needs `Sys.Audit` on the node for a token that is NOT the one that started\n * the task — every `pve` call mints a fresh token, so the task's owner check cannot pass.\n * ⚠️ SHORT POLLS, LOW CAP: 2s apart, 30 tries, about a minute. A pool create takes seconds; one\n * still unsettled after a minute is a fault to surface, not a wait to lengthen.\n */\nexport const settle = (\n props: CephPoolProps,\n read: PoolRead,\n done: (live: CephPoolAttributes | undefined) => boolean,\n) =>\n Effect.gen(function* () {\n for (let attempt = 0; attempt < 30; attempt += 1) {\n const live = yield* read(props);\n if (done(live)) return live;\n yield* Effect.sleep('2 seconds');\n }\n return yield* read(props);\n });\n",
|
|
36
49
|
"/**\n * `Proxmox.FirewallAlias` — a named network the cluster firewall can be written in terms of.\n *\n * ★ WHY THIS FAMILY AND NOT FIREWALL RULES, WHICH IS THE ONE EVERYONE REACHES FOR FIRST. A rule is\n * addressed BY POSITION — `/cluster/firewall/rules/{pos}` — so inserting a rule renumbers every\n * rule below it. A position-keyed resource therefore RETARGETS ITSELF at somebody else's rule the\n * moment anything is inserted above it, and it does so while the plan looks clean: the path still\n * resolves, the read still succeeds, the diff is simply computed against the wrong object.\n * Aliases, ipsets and security groups are the firewall objects with STABLE IDENTITY — a name, not\n * an index — and they are the ones worth declaring. Rules belong in this package only once\n * somebody models the whole ordered list as ONE resource, which is a different shape from\n * `PveSpec` and should look different.\n *\n * ★ AND THE ALIAS IS THE PIECE THAT EARNS THE MOST BY BEING DECLARED. `10.1.0.0/16` spelled into\n * nine rules is nine places to edit and one to forget; `HomeLan` spelled into nine rules is one\n * place, resolved by PVE at compile time. The alias is the object that has to be right.\n *\n * ⛔ PVE REWRITES THE CIDR ON READ, AND THAT IS THE FOREVER-DIFF THIS RESOURCE IS SHAPED AROUND.\n * A declared `10.1.1.5/32` reads back as `10.1.1.5`. `cidr()` in `firewall-alias-form.ts` applies\n * exactly the cluster's own transformation to BOTH sides and carries the measurements; read that\n * comment before touching either side of `matches` below.\n *\n * ⛔ `ipversion` IS RETURNED BY THE READ AND REFUSED BY EVERY WRITE. `parse_alias` derives it from\n * the address and `read_alias` hands the parsed entry straight back, while POST and PUT declare\n * `additionalProperties: 0` with no such parameter — sending it is a 400, not an ignored hint. It\n * is REPORTED as an attribute so a plan can say whether an alias is v4 or v6, and kept OUT of\n * `matches`, exactly like `pool.members` and `user.tokens`.\n *\n * ⚠️ `digest` NEVER REACHES THIS PROVIDER, which is a better answer than keeping it out of\n * `matches`. The LIST read runs its entries through `copy_list_with_digest` and stamps one — and\n * it changes whenever ANYTHING else in the firewall config changes, so a resource that stored it\n * would diff on a rule somebody else edited. The SINGLE-OBJECT read this resource uses returns\n * `$aliases->{$name}` raw, with no digest in it at all. None is sent on write either:\n * `PVE::Tools::assert_if_modified` skips the comparison when either side is undef, and the API\n * handler already holds `lock_clusterfw_conf(10, ...)` for the whole read-modify-write. So\n * declining optimistic locking buys last-writer-wins between two concurrent editors, not a torn\n * file — and the alternative, storing a cluster-wide digest per alias, buys a permanent diff.\n *\n * ⚠️ THE NAME IS CASE-FOLDED FOR IDENTITY AND PRESERVED FOR DISPLAY, which reads as a bug until you\n * see both halves. The config is keyed `$aliases->{lc($data->{name})}` while the entry stores the\n * name as written, and the item GET lowers its path segment too — so `homelan` and `HomeLan` are\n * ONE alias, reachable by either spelling. `name` is therefore reported from the live config and\n * kept OUT of `matches`: rewriting an alias to change its capitalisation is a cosmetic write to\n * the file every node recompiles its firewall from.\n * ⛔ AND TWO DECLARATIONS DIFFERING ONLY IN CASE ARE THE SAME OBJECT, which is the `acl.ts`\n * hazard again. Alchemy sees two resource ids, the cluster sees one alias, and deleting either\n * takes it away from both.\n *\n * ⚠️ A RENAME IS MODELLED AS A REPLACE, NOT AS AN UPDATE, AND THE REJECTED OPTION WAS REAL. PVE's\n * PUT does take a `rename` parameter and Alchemy's `reconcile` does receive `olds`, so an\n * in-place rename was available. It was rejected because using it means hand-writing `reconcile`\n * as well as `diff` and branching on four states — old present or absent, new present or absent —\n * each branch a fresh way to write the wrong object, and all of it to buy only the gap between\n * the create and the delete. What a rename must NOT be is silent, which is what the factory alone\n * would make it: the read at the NEW path answers absent, `diff` says update, reconcile POSTs a\n * SECOND alias, and the old one stays in `cluster.fw` with its old CIDR — still resolving in\n * every rule that names it, and invisible to every later plan. `diff` below says `replace`.\n * ⚠️ CREATE-FIRST, as Alchemy defaults and as `acl.ts` argues: the new alias exists before the\n * old one goes, so a rules change can be sequenced between the two generations.\n *\n * ⚠️ PRIVILEGES, AND FOR ONCE NOTHING NEEDS WIDENING. MEASURED 2026-09-13 from the cluster's own\n * schema and its live ACL: the item GET checks `[\"perm\",\"/\",[\"Sys.Audit\"]]`, POST/PUT/DELETE\n * check `[\"perm\",\"/\",[\"Sys.Modify\"]]`, `hf-read@pve` holds `PVEAuditor` at `/` with propagate and\n * `hf-provision@pve` holds the provision role (provision-baseline.ts) at `/` with propagate. Both privileges are already\n * held, so `readRole` stays the default `read` lane — unlike storage/sdn-zone/sdn-vnet, whose\n * ITEM reads are gated on an allocate privilege the auditor cannot have.\n * ⚠️ `Sys.Modify` ON `/` IS STILL A BIG HAMMER — it is the same grant `metric-server.ts` warns\n * about, and there is no `/firewall` object to scope a narrower role to.\n *\n * ⚠️ DELETION IS FULLY IMPLEMENTED AND THE DEFAULT POLICY IS DELIBERATELY NOT `retain`. The ★ in\n * `resource.ts` reserves `retain` for objects whose CONTENTS cannot be rebuilt from a line of\n * TypeScript; an alias is a name, a CIDR and a comment, all three of them in the declaration, so\n * recreating one is exact and free. The real hazard is referential — PVE checks nothing before\n * removing an alias, and a rule naming a removed one fails to compile — and `retain` would not\n * fix that, it would only leave an undeclared alias on the cluster forever. Sequence the rules,\n * or pipe `RemovalPolicy.retain()` at the declaration site where the reason is visible.\n *\n * ⚠️ `local_network` IS THE ONE ALIAS NAME WITH CLUSTER-WIDE MEANING. `compile_ipsets` reads\n * `aliases->{local_network}` to decide what the `management` ipset covers, and SYNTHESISES one\n * from the node's own subnet when it is missing — in memory, never written back to the file.\n * Declaring it is stable, because PVE leaves a present one alone. DELETING it hands that decision\n * silently back to autodetection, which is drift no plan will ever show.\n *\n * ⚠️ NO SECRET LIVES IN THIS FAMILY, so nothing here has to be typed `never` to keep it out of\n * Alchemy's unencrypted state the way `metric-server.ts` does with its `token`. A name, a CIDR and\n * a free-text comment are the whole object, and all three are the declaration's own values read\n * back — not a credential held by the cluster.\n *\n * ⚠️ ACCEPTANCE, STATED HONESTLY: `GET /cluster/firewall/aliases` ANSWERS `[]` ON THIS CLUSTER and\n * the cluster firewall is disabled, so there was no live alias to declare and NO create/plan/noop\n * round trip was run. What is measured is the published schema, the API module's Perl, and the\n * cluster's own `parse_alias` exercised on sample lines. Whoever declares the first alias should\n * confirm the SECOND plan says noop before trusting this file.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { body, cidr, comment, fold } from './firewall-alias-form.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { int, text } from './values.ts';\n\nexport interface FirewallAliasProps extends WithTarget {\n /**\n * PVE's `pve-fw-alias`: a letter, then at least one more of letter/digit/`-`/`_`, to 64 chars.\n * ⚠️ IDENTITY, CASE-INSENSITIVELY — see the ⚠️ on folding in the header. A two-character minimum\n * is real: a one-letter name is refused by the pattern, not silently accepted.\n */\n name: string;\n /**\n * An IP or a network, e.g. `10.1.0.0/16`, `10.1.1.5`, `2001:db8::/32`.\n * ⚠️ A `/32` (or `/128`) SUFFIX IS DROPPED BY PVE AND BY `cidr()` ALIKE, so declaring one is\n * harmless — it simply is not what comes back. Anything `parse_ip_or_cidr` refuses is a 400.\n */\n cidr: string;\n /**\n * Free text, stored as a trailing `#` comment on the alias's line in `cluster.fw`.\n * ⚠️ TRIMMED, AND A LINE FEED IS REFUSED. A comment of exactly `0` cannot be stored at all — the\n * ⛔ in `firewall-alias-form.ts` measures why, and why declaring one plans as noop.\n */\n comment?: string;\n}\n\nexport interface FirewallAliasAttributes {\n /** The spelling held in `cluster.fw`, which need not be the declared one. Never compared. */\n name: string;\n /** Normalised the way PVE normalises it, so it is comparable with a declaration. */\n cidr: string;\n comment: string;\n /**\n * 4 or 6, derived by PVE from the address. Reported so a plan can show which stack an alias\n * covers; never compared, because no write accepts it.\n */\n ipversion: number;\n}\n\nexport interface ProxmoxFirewallAlias extends Resource<\n 'Proxmox.FirewallAlias',\n FirewallAliasProps,\n FirewallAliasAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxFirewallAlias = Resource<ProxmoxFirewallAlias>('Proxmox.FirewallAlias');\n\nconst handlers = pveHandlers<FirewallAliasProps, FirewallAliasAttributes>({\n /**\n * ⚠️ AN ENTRY WITH NO CIDR IS NOT AN ALIAS. `read_alias` raises for a name it does not hold, so\n * absence normally arrives as a failed call the factory folds into `undefined`; this guard\n * covers the other shape — a 200 carrying something that is not an alias — rather than\n * recording state for an object with an empty address. The next reconcile then POSTs and PVE\n * answers \"alias already exists\", which is loud and points at the read.\n */\n attributes: (live, props) => {\n const address = cidr(live['cidr']);\n return address === ''\n ? undefined\n : {\n cidr: address,\n comment: comment(live['comment']),\n ipversion: int(live['ipversion'], 0),\n /** ⚠️ FROM THE CLUSTER, falling back to props: the stored spelling is the interesting one. */\n name: text(live['name'], props.name),\n };\n },\n collection: () => 'cluster/firewall/aliases',\n createForm: (props) => ({ ...body(props), name: props.name }),\n /**\n * ⚠️ EXACTLY THE TWO FIELDS A PUT CAN PUT BACK, both sides through the same normalisers. `name`\n * is identity and case-only drift (see the header); `ipversion` is derived and unwritable.\n * Comparing either would report an update that no update can settle.\n */\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: {\n create: 'pve:POST /cluster/firewall/aliases',\n update: 'pve:PUT /cluster/firewall/aliases/{name}',\n },\n matches: (attributes, props) =>\n attributes.cidr === cidr(props.cidr) && attributes.comment === comment(props.comment),\n /**\n * ⚠️ UNESCAPED ON PURPOSE: `pve-fw-alias` admits only `[A-Za-z][A-Za-z0-9\\-\\_]+`, so there is no\n * character here that a URL would need to encode, and PVE lowercases the segment on arrival.\n */\n path: (props) => `cluster/firewall/aliases/${props.name}`,\n updateForm: body,\n});\n\n/**\n * ⛔ ONE HANDLER IS OVERRIDDEN AND THE REST COME FROM THE FACTORY, for the reason `acl.ts` spells\n * out at length: the factory answers `replace` only for an object with no update path, and this\n * one has a PUT. Left to delegate, a changed `name` would read absent at the new path, plan as an\n * update, and quietly leave the old alias behind. Everything else — the empty `list`, the read,\n * the read-back-guarded reconcile, the delete — is the factory's, unchanged.\n */\nexport const ProxmoxFirewallAliasProvider = () =>\n Provider.effect(\n ProxmoxFirewallAlias,\n Effect.succeed(\n ProxmoxFirewallAlias.Provider.of({\n ...handlers,\n diff: Effect.fn(function* ({ news, output }) {\n // ⚠️ `isResolved` FIRST: at plan time `news.name` can still be an unresolved Output, and\n // folding a placeholder would report a replace nobody asked for — the worst possible\n // false positive on this family, since a replace here DELETES an alias.\n if (output !== undefined && isResolved(news) && fold(news.name) !== fold(output.name)) {\n return { action: 'replace' } as const;\n }\n return yield* handlers.diff({ news, output });\n }),\n }),\n ),\n );\n",
|
|
@@ -53,25 +66,28 @@
|
|
|
53
66
|
"/**\n * What makes a `Proxmox.Lxc` a DIFFERENT guest: a new vmid, or another template. Judged by `diff`\n * and `reconcile` in lxc.ts before anything else, and never planned as a replace — see lxc.ts.\n */\nimport type { LxcAttributes, LxcProps } from './lxc-props.ts';\n\n/** A prop's value if it is already plain data, else undefined (an unresolved Output). */\nconst plain = (news: unknown, key: string): unknown => {\n const value: unknown = (news as Record<string, unknown>)[key];\n return typeof value === 'string' || typeof value === 'number' ? value : undefined;\n};\n\n/**\n * ⛔ IDENTITY IS JUDGED BEFORE `isResolved(news)`. That guard returns early when ANY prop is still\n * an Output, and the engine then plans `update` — which, for a new vmid, would build a second\n * guest and orphan the first (the openbao rename lesson, rename.ts). A template is identity\n * too: it is consumed at create, so a different one is a different guest.\n * ★ `node` IS NOT JUDGED HERE, BECAUSE A NODE CHANGE CAN BE THE DECLARATION CATCHING UP. HA fails\n * a guest over, or somebody runs `pct migrate`; the guest is on its new node before any line\n * says so. `readLive` at the declared node settles both cases: the guest is there (follow it,\n * and re-record state with a write-free update), or it is elsewhere and the read FAILS naming\n * where — asking to move a guest is a migration, and this resource does not migrate.\n */\nexport const identityRefusals = (\n news: unknown,\n olds: LxcProps,\n output: LxcAttributes | undefined,\n): string[] => {\n const was = output?.vmid ?? olds.vmid;\n const vmid = plain(news, 'vmid');\n const template = plain(news, 'ostemplate');\n const reasons: string[] = [];\n if (vmid !== undefined && vmid !== was) {\n reasons.push(\n `vmid: ${String(was)} -> ${String(vmid)} is a different guest. Declare it as a new ` +\n 'resource; this one is retained when its declaration goes.',\n );\n }\n if (template !== undefined && olds.ostemplate !== undefined && template !== olds.ostemplate) {\n reasons.push('ostemplate: a template is consumed at create; another one is a new guest.');\n }\n return reasons;\n};\n",
|
|
54
67
|
"/**\n * The strict adoption gate for `Proxmox.Lxc`: AN ADOPTION NEVER CHANGES A GUEST (decided\n * 2026-09-21). A guest this stack adopts — no recorded instance, or one the plan only kept because\n * `--adopt` said so (ownership/adopting.ts) — is taken over exactly as it runs, or not at all.\n *\n * ⛔ ANY DIFFERENCE FAILS, AT PLAN AND AGAIN AT APPLY. Before this, a cold adoption planned `adopted`\n * whatever it would write (alchemy beta.79 prints no property diff for it) and only LOGGED the\n * keys; `deploy --adopt --yes` then wrote them. A `net0` pasted without the live `tag=` moved the\n * guest off its VLAN on the deploy that was meant to change nothing. Now the plan fails naming\n * the keys, and reconcile re-asks against a fresh read, so a hand edit between plan and deploy\n * is refused too rather than written back.\n * ⛔ KEYS ONLY, NEVER VALUES. The refusal names what differs and nothing it differs by: a live\n * config can carry anything (`description` is where people paste credentials), and plan output\n * lands in CI logs.\n * ★ ASKED BEFORE `judge`'s OWN REFUSALS. Those name a declared value (the `pct set` to run), and\n * for an adoption the fix is the other direction: declare what is live.\n * ★ THE STORE IS READ ONLY WHEN SOMETHING DIFFERS. A clean adoption — the only kind that proceeds\n * — costs nothing here.\n */\nimport * as Effect from 'effect/Effect';\nimport type { Owner } from '../ownership/adopt.ts';\nimport { adopting } from '../ownership/adopting.ts';\nimport { type LxcChange, judge } from './lxc-judge.ts';\nimport { LxcRefusedError } from './lxc-lifecycle.ts';\nimport type { LxcProps } from './lxc-props.ts';\n\n/** The refusal: keys, where, and what to do — never a value. */\nexport const adoptionRefusal = (props: LxcProps, drift: readonly string[]): string =>\n `CT ${String(props.vmid)} on ${props.node}: adopting it would change ${drift.join(', ')}. An ` +\n 'adoption never changes a guest, so nothing is written. Declare what is live for these keys ' +\n '(the plan names keys, never values) and plan again; change them after the adoption, as an ' +\n 'update.';\n\n/**\n * Fails with `adoptionRefusal` when `change` would write to a guest `owner`'s call adopts.\n * `settled` for a recovered create is the same judge, of the recording row's props against `live`.\n */\nexport const refuseAdoptedDrift = (\n owner: Owner,\n props: LxcProps,\n live: Record<string, unknown>,\n change: LxcChange,\n): Effect.Effect<void, LxcRefusedError> =>\n Effect.gen(function* () {\n if (change.drift.length === 0) return;\n const settled = (recorded: unknown) =>\n Effect.sync(() => judge(recorded as LxcProps, live).drift.length === 0);\n if (!(yield* adopting(owner, settled))) return;\n return yield* Effect.fail(new LxcRefusedError(adoptionRefusal(props, change.drift)));\n });\n",
|
|
55
68
|
"/**\n * Whether a `diff` or `reconcile` that holds attributes is looking at an ADOPTION — a live object\n * that reached this stack's state by being FOUND, not by this resource's own create — so a family\n * whose adoption must never change what it takes over (`Proxmox.Lxc`) can refuse any difference\n * instead of writing it. adopt.ts and probe.ts decide WHETHER an object may be claimed; this says,\n * once it is claimed, whether the claim is an adoption.\n *\n * ★ STATE ALREADY SAYS WHICH, AT BOTH HALVES OF A DEPLOY (alchemy 2.0.0-beta.79):\n * · PLAN, A COLD ADOPTION. The probe mints an instance id no row can hold (Plan.ts\n * `generateInstanceId()`), and `diff` then runs with that id and the attributes the probe\n * read. So no row holds the id.\n * · APPLY, THE SAME ADOPTION. Apply commits an `updating` row with `adopting: true` under that id\n * BEFORE it calls reconcile, and the flag stays until the update commits (Apply.ts). An\n * adoption whose apply was interrupted keeps it, so the next plan's `diff` sees it too.\n * · A RECOVERED CREATE. A `creating` row with no attributes, whose live object the recovery read\n * found. It is this resource's own create when the row proves it (probe.ts `provenOurs`, the\n * same proof the read asked). Otherwise the plan went on only because `--adopt` said so — an\n * adoption like any other.\n * · Any other row with attributes is state vouching for the object: not an adoption.\n * ⚠️ OUTSIDE A STACK — no Stack or State in context, or a store that errors (rows.ts) — no row\n * holds anything, so every call is an adoption: the strict answer, never the permissive one. A\n * family then refuses an ordinary update's write as if it were an adoption's. `alchemy deploy`\n * and the Alchemist session provide both services.\n */\nimport * as Effect from 'effect/Effect';\nimport type { Owner } from './adopt.ts';\nimport { provenOurs } from './probe.ts';\nimport { recordedGeneration } from './rows.ts';\n\n/**\n * Whether `owner`'s call is an adoption (header). `settled` is the family's \"live matches these\n * props\", asked only for a recovered create, with the props of the row that recorded it.\n * ⛔ A CREATE IS NEVER ONE HERE. `output` undefined is reconcile's create path, which has its own\n * rule for a live object it finds (adopt.ts `refuseTakeover`).\n */\nexport const adopting = <E, R>(\n owner: Owner,\n settled: (recorded: unknown) => Effect.Effect<boolean, E, R>,\n): Effect.Effect<boolean, never, R> =>\n Effect.gen(function* () {\n if (owner.output === undefined) return false;\n const generation = yield* recordedGeneration(owner.fqn, owner.instanceId);\n if (generation === undefined || generation.adopting === true) return true;\n if (generation.attr !== undefined && generation.attr !== null) return false;\n return !(yield* provenOurs(owner, settled(generation.props)));\n });\n",
|
|
56
|
-
"/**\n * The calls a `Proxmox.Lxc` makes: read one config, create, update in place, grow, delete.\n *\n * ⛔ A READ FAILURE IS A FAILURE HERE, NOT \"ABSENT\" — THE ONE PLACE THIS PACKAGE DEPARTS FROM\n * resource.ts. `pveOperations.read` folds every error into `undefined`, which for a guest means a\n * 403 or a node that did not answer plans a CREATE over a running container. PVE answers a\n * missing guest with HTTP 500, `Configuration file 'nodes/<node>/lxc/<vmid>.conf' does not\n * exist` (pve-guest-common `AbstractConfig::load_config`), so only a 500 may mean absent — and\n * even then only after the cluster says so, below. Anything else fails the plan.\n *\n * ⛔ \"ABSENT\" IS DECIDED BY THE WHOLE CLUSTER, NOT BY THE ERROR TEXT. A config lives under its\n * node, so a guest migrated to another node — or a QEMU VM holding the same cluster-wide vmid —\n * also reads as missing at the declared path. `GET /cluster/resources?type=vm` settles it: the\n * vmid nowhere is absent; the vmid anywhere else FAILS the read with where it is; the vmid on\n * the declared node fails with PVE's own error. ★ Keyed on the cluster rather than on matching\n * the message, so a PVE that words it differently still cannot turn into a create.\n *\n * ⚠️ CREATE, RESIZE AND DELETE ARE ASYNCHRONOUS; A CONFIG PUT IS NOT. The first three answer a\n * UPID the moment PVE forks the worker (`NodesNodeLxcPostReturn`, `…ResizePutReturn`,\n * `…VmidDeleteReturn` are all `string`), so each waits for its task's exitstatus. `PUT …/config`\n * answers `null` after writing (`…ConfigPutReturn`), so it needs no wait.\n * ⛔ EACH TASK IS POLLED WITH THE CREDENTIAL THAT STARTED IT — see `awaitTask`: only the owner\n * skips `Sys.Audit`. A fresh `provision` mint per write gives the full 300s lease a create needs;\n * a cached lease can be handed out with 60s left (lease-cache.ts).\n */\nimport * as Effect from 'effect/Effect';\nimport { PveError, pve, pveWith } from './client.ts';\nimport { type PveTarget, mint } from './credentials.ts';\nimport { createForm } from './lxc-create-form.ts';\nimport { type LxcChange, judge } from './lxc-judge.ts';\nimport type { LxcAttributes, LxcProps } from './lxc-props.ts';\nimport { rawKeysOf, storedConfig } from './lxc-props.ts';\nimport { awaitTask } from './network-apply-read.ts';\nimport { text, withClears } from './values.ts';\n\n/** A plan-time or apply-time refusal. Nothing was written when one of these is raised. */\nexport class LxcRefusedError extends Error {\n override readonly name = 'LxcRefusedError';\n}\n\n/** Where one guest lives. Its identity, and nothing else. */\nexport type LxcWhere = { readonly target: PveTarget; readonly node: string; readonly vmid: number };\n\nexport const guestPath = (where: LxcWhere) => `nodes/${where.node}/lxc/${String(where.vmid)}`;\n\n/** How long each task may run, in one-second polls. ⚠️ A cap, not an estimate. */\nconst CREATE_POLLS = 180;\nconst TASK_POLLS = 60;\n\nconst isServerError = (error: unknown): error is PveError =>\n error instanceof PveError && error.status === 500;\n\n/** One `GET /cluster/resources?type=vm` row. */\ntype GuestRow = { readonly vmid?: unknown; readonly node?: unknown; readonly type?: unknown };\n\n/** The live config, or undefined when no guest holds this vmid anywhere in the cluster. */\nexport const readLive = (where: LxcWhere) =>\n Effect.gen(function* () {\n const answer = yield* pve<Record<string, unknown> | null>(\n where.target,\n 'read',\n 'GET',\n `${guestPath(where)}/config`,\n ).pipe(\n Effect.map((data) => ({ data, error: undefined })),\n Effect.catchIf(isServerError, (error) => Effect.succeed({ data: undefined, error })),\n );\n if (answer.data !== undefined && answer.data !== null) return answer.data;\n const rows = yield* pve<readonly GuestRow[]>(\n where.target,\n 'read',\n 'GET',\n 'cluster/resources?type=vm',\n );\n // ⚠️ `Number()`, NOT `===` ON THE RAW VALUE: a vmid that came back as `\"900\"` would otherwise\n // match nothing, and \"listed nowhere\" is the one answer that turns into a create.\n const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);\n if (found !== undefined && found.node === where.node && found.type === 'lxc') {\n return yield* Effect.fail(\n answer.error ??\n new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`),\n );\n }\n if (found !== undefined) {\n return yield* Effect.fail(\n new LxcRefusedError(\n `vmid ${String(where.vmid)} is a ${text(found.type, 'guest')} on ` +\n `${text(found.node, '?')}, not a container on ${where.node}. A migration is not an ` +\n 'update and vmids are cluster-wide: declare the node it is on, or pick a free vmid.',\n ),\n );\n }\n return undefined;\n });\n\nexport const attributesOf = (where: LxcWhere, live: Record<string, unknown>): LxcAttributes => ({\n config: storedConfig(live),\n node: where.node,\n rawKeys: rawKeysOf(live),\n vmid: where.vmid,\n});\n\n/** Run one task-starting call and wait for it on the same credential. Fails unless `OK`. */\nconst task = (\n where: LxcWhere,\n method: 'POST' | 'PUT' | 'DELETE',\n path: string,\n what: string,\n polls: number,\n form?: Record<string, string>,\n) =>\n Effect.gen(function* () {\n const credential = yield* mint(where.target, 'provision');\n const upid = text(yield* pveWith<string>(where.target, credential, method, path, form));\n if (upid === '') {\n return yield* Effect.fail(\n new LxcRefusedError(\n `${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`,\n ),\n );\n }\n const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);\n if (exit !== 'OK') {\n return yield* Effect.fail(\n new LxcRefusedError(\n `${what}: task ${upid} ended \"${exit}\". Read \\`pvesh get /nodes/${where.node}/tasks/` +\n `${upid}/log\\` before running the deploy again.`,\n ),\n );\n }\n });\n\n/** POST the create and wait for the template to unpack. ⛔ Refusals are the caller's to check. */\nexport const createGuest = (props: LxcProps) =>\n task(\n props,\n 'POST',\n `nodes/${props.node}/lxc`,\n `create CT ${String(props.vmid)}`,\n CREATE_POLLS,\n createForm(props),\n );\n\n/**\n * Apply a judged change: the config PUT first, then each resize.\n * ⚠️ IN THAT ORDER BECAUSE A MOUNT POINT'S PUT CARRIES ITS LIVE `size=`, and the resize rewrites it.\n * ⛔ THE PUT CARRIES THE `digest` OF THE CONFIG IT WAS JUDGED AGAINST. PVE refuses the write if the\n * file changed since (`assert_if_modified`), so an edit landing between reconcile's own read and\n * its PUT fails the deploy instead of being overwritten by a change computed from the older file.\n * ⚠️ THE DIGEST IS THE DEPLOY'S READ, NOT THE PLAN'S. A hand edit between `plan` and `deploy` is\n * seen by reconcile's fresh judge instead: a declared key edited meanwhile is written back to the\n * declaration (even after a plan that warned of nothing), an undeclared one is left alone.\n */\nexport const updateGuest = (props: LxcProps, change: LxcChange, digest: string) =>\n Effect.gen(function* () {\n if (Object.keys(change.put).length > 0 || change.clear.length > 0) {\n const form = withClears(\n { ...change.put, ...(digest === '' ? {} : { digest }) },\n change.clear,\n );\n yield* pve(props.target, 'provision', 'PUT', `${guestPath(props)}/config`, form);\n }\n for (const grow of change.resize) {\n yield* task(\n props,\n 'PUT',\n `${guestPath(props)}/resize`,\n `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`,\n TASK_POLLS,\n { disk: grow.disk, size: grow.size },\n );\n }\n });\n\n/**\n * ⛔ NO `force`, NO `purge`, NO STOP FIRST. PVE refuses to destroy a running or protected guest,\n * and that refusal is kept: stopping a guest so a delete can proceed is an operator's decision.\n * Reached only under `RemovalPolicy.destroy()` — this resource retains by default.\n * ⛔ AND ONLY THE GUEST THIS STACK DECLARED. State holds a vmid, and a vmid is reusable: after a\n * hand `pct destroy` and a new `pct create` at the same id, \"delete CT 150\" is somebody else's\n * container and all its volumes. So the live guest is judged against the last declaration\n * first; one that no longer matches is refused with the keys that differ, and deleting it (or\n * declaring it again) is the operator's call.\n */\nexport const destroyGuest = (where: LxcWhere, declared: LxcProps) =>\n Effect.gen(function* () {\n const live = yield* readLive(where);\n if (live === undefined) return;\n const drift = judge(declared, live).drift;\n if (drift.length > 0) {\n return yield* Effect.fail(\n new LxcRefusedError(\n `destroy CT ${String(where.vmid)}: the guest on ${where.node} no longer matches its last ` +\n `declaration (${drift.join(', ')}), so it may not be the guest this stack made. ` +\n 'Nothing was deleted. Delete it by hand if it should go, or declare it again.',\n ),\n );\n }\n yield* task(where, 'DELETE', guestPath(where), `destroy CT ${String(where.vmid)}`, TASK_POLLS);\n });\n",
|
|
69
|
+
"/**\n * The calls a `Proxmox.Lxc` makes: read one config, create, update in place, grow, delete.\n *\n * ⛔ A READ FAILURE IS A FAILURE HERE, NOT \"ABSENT\" — THE ONE PLACE THIS PACKAGE DEPARTS FROM\n * resource.ts. `pveOperations.read` folds every error into `undefined`, which for a guest means a\n * 403 or a node that did not answer plans a CREATE over a running container. PVE answers a\n * missing guest with HTTP 500, `Configuration file 'nodes/<node>/lxc/<vmid>.conf' does not\n * exist` (pve-guest-common `AbstractConfig::load_config`), so only a 500 may mean absent — and\n * even then only after the cluster says so, below. Anything else fails the plan.\n *\n * ⛔ \"ABSENT\" IS DECIDED BY THE WHOLE CLUSTER, NOT BY THE ERROR TEXT. A config lives under its\n * node, so a guest migrated to another node — or a QEMU VM holding the same cluster-wide vmid —\n * also reads as missing at the declared path. `GET /cluster/resources?type=vm` settles it: the\n * vmid nowhere is absent; the vmid anywhere else FAILS the read with where it is; the vmid on\n * the declared node fails with PVE's own error. ★ Keyed on the cluster rather than on matching\n * the message, so a PVE that words it differently still cannot turn into a create.\n *\n * ⚠️ CREATE, RESIZE AND DELETE ARE ASYNCHRONOUS; A CONFIG PUT IS NOT. The first three answer a\n * UPID the moment PVE forks the worker (`NodesNodeLxcPostReturn`, `…ResizePutReturn`,\n * `…VmidDeleteReturn` are all `string`), so each waits for its task's exitstatus. `PUT …/config`\n * answers `null` after writing (`…ConfigPutReturn`), so it needs no wait.\n * ⛔ EACH TASK IS POLLED WITH THE CREDENTIAL THAT STARTED IT — see `awaitTask`: only the owner\n * skips `Sys.Audit`. A fresh `provision` mint per write gives the full 300s lease a create needs;\n * a cached lease can be handed out with 60s left (lease-cache.ts).\n */\nimport * as Effect from 'effect/Effect';\nimport { PveError, pve, pveWith } from './client.ts';\nimport { guardForm } from './constraint-guard.ts';\nimport type { EndpointKey } from './constraints.ts';\nimport { type PveTarget, mint } from './credentials.ts';\nimport { createForm } from './lxc-create-form.ts';\nimport { type LxcChange, judge } from './lxc-judge.ts';\nimport type { LxcAttributes, LxcProps } from './lxc-props.ts';\nimport { rawKeysOf, storedConfig } from './lxc-props.ts';\nimport { awaitTask } from './network-apply-read.ts';\nimport { text, withClears } from './values.ts';\n\n/** A plan-time or apply-time refusal. Nothing was written when one of these is raised. */\nexport class LxcRefusedError extends Error {\n override readonly name = 'LxcRefusedError';\n}\n\n/** Where one guest lives. Its identity, and nothing else. */\nexport type LxcWhere = { readonly target: PveTarget; readonly node: string; readonly vmid: number };\n\nexport const guestPath = (where: LxcWhere) => `nodes/${where.node}/lxc/${String(where.vmid)}`;\n\n/**\n * The three vendor endpoints a guest is written through, as the generated tables key them.\n *\n * ⛔ DECLARED HERE RATHER THAN ON A SPEC BECAUSE `Proxmox.Lxc` HAS NO SPEC — lxc.ts writes all\n * five handlers by hand (a guest reads failures as failures, waits on tasks and grows disks\n * through a second endpoint), so it cannot inherit the shared guard from resource.ts. Without\n * these three it would be the largest write surface in the package with nothing checking the\n * body: `POST /nodes/{node}/lxc` alone carries 26 constrained parameters.\n * ⚠️ THREE, NOT TWO. `resize` is a separate PVE endpoint with its own schema — `disk` against an\n * enum of `rootfs` and `mp0..mp255`, `size` against PVE's `+<n>` / `<n>` pattern — and it is\n * the one lxc-volume.ts drives.\n * ⛔ `{vmid}` AND `{node}` ARE PATH SEGMENTS, NOT FORM KEYS, on the two PUTs. On the POST `vmid`\n * IS a form key and is required, which is why `createForm` sends it.\n */\nconst LXC_ENDPOINTS = {\n create: 'pve:POST /nodes/{node}/lxc' as EndpointKey,\n resize: 'pve:PUT /nodes/{node}/lxc/{vmid}/resize' as EndpointKey,\n update: 'pve:PUT /nodes/{node}/lxc/{vmid}/config' as EndpointKey,\n};\n\n/** How long each task may run, in one-second polls. ⚠️ A cap, not an estimate. */\nconst CREATE_POLLS = 180;\nconst TASK_POLLS = 60;\n\nconst isServerError = (error: unknown): error is PveError =>\n error instanceof PveError && error.status === 500;\n\n/** One `GET /cluster/resources?type=vm` row. */\ntype GuestRow = { readonly vmid?: unknown; readonly node?: unknown; readonly type?: unknown };\n\n/** The live config, or undefined when no guest holds this vmid anywhere in the cluster. */\nexport const readLive = (where: LxcWhere) =>\n Effect.gen(function* () {\n const answer = yield* pve<Record<string, unknown> | null>(\n where.target,\n 'read',\n 'GET',\n `${guestPath(where)}/config`,\n ).pipe(\n Effect.map((data) => ({ data, error: undefined })),\n Effect.catchIf(isServerError, (error) => Effect.succeed({ data: undefined, error })),\n );\n if (answer.data !== undefined && answer.data !== null) return answer.data;\n const rows = yield* pve<readonly GuestRow[]>(\n where.target,\n 'read',\n 'GET',\n 'cluster/resources?type=vm',\n );\n // ⚠️ `Number()`, NOT `===` ON THE RAW VALUE: a vmid that came back as `\"900\"` would otherwise\n // match nothing, and \"listed nowhere\" is the one answer that turns into a create.\n const found = (rows ?? []).find((row) => Number(row.vmid) === where.vmid);\n if (found !== undefined && found.node === where.node && found.type === 'lxc') {\n return yield* Effect.fail(\n answer.error ??\n new LxcRefusedError(`CT ${String(where.vmid)} is listed but its config read empty.`),\n );\n }\n if (found !== undefined) {\n return yield* Effect.fail(\n new LxcRefusedError(\n `vmid ${String(where.vmid)} is a ${text(found.type, 'guest')} on ` +\n `${text(found.node, '?')}, not a container on ${where.node}. A migration is not an ` +\n 'update and vmids are cluster-wide: declare the node it is on, or pick a free vmid.',\n ),\n );\n }\n return undefined;\n });\n\nexport const attributesOf = (where: LxcWhere, live: Record<string, unknown>): LxcAttributes => ({\n config: storedConfig(live),\n node: where.node,\n rawKeys: rawKeysOf(live),\n vmid: where.vmid,\n});\n\n/** Run one task-starting call and wait for it on the same credential. Fails unless `OK`. */\nconst task = (\n where: LxcWhere,\n method: 'POST' | 'PUT' | 'DELETE',\n path: string,\n what: string,\n polls: number,\n form?: Record<string, string>,\n) =>\n Effect.gen(function* () {\n const credential = yield* mint(where.target, 'provision');\n const upid = text(yield* pveWith<string>(where.target, credential, method, path, form));\n if (upid === '') {\n return yield* Effect.fail(\n new LxcRefusedError(\n `${what}: PVE returned no task id, so the write cannot be confirmed and is not claimed.`,\n ),\n );\n }\n const exit = yield* awaitTask(where.target, credential, where.node, upid, polls);\n if (exit !== 'OK') {\n return yield* Effect.fail(\n new LxcRefusedError(\n `${what}: task ${upid} ended \"${exit}\". Read \\`pvesh get /nodes/${where.node}/tasks/` +\n `${upid}/log\\` before running the deploy again.`,\n ),\n );\n }\n });\n\n/** POST the create and wait for the template to unpack. ⛔ Refusals are the caller's to check. */\nexport const createGuest = (props: LxcProps) =>\n Effect.gen(function* () {\n const form = createForm(props);\n /**\n * ⛔ BEFORE THE POST, AND WITH PRESENCE, BECAUSE THIS IS UNCONDITIONALLY A CREATE. Reached\n * only when the guest is absent (lxc.ts's reconcile), so the vendor's required parameters\n * really are about to go on the wire — `ostemplate` and `vmid`, both of which\n * `createRefusals` already insists on, plus every bound the schema carries and nothing here\n * knew: `cores` 1..8192, `memory` ≥ 16, `hostname` as a dns-name, `tags`, `startup`.\n */\n yield* guardForm(LXC_ENDPOINTS.create, form, true);\n return yield* task(\n props,\n 'POST',\n `nodes/${props.node}/lxc`,\n `create CT ${String(props.vmid)}`,\n CREATE_POLLS,\n form,\n );\n });\n\n/**\n * Apply a judged change: the config PUT first, then each resize.\n * ⚠️ IN THAT ORDER BECAUSE A MOUNT POINT'S PUT CARRIES ITS LIVE `size=`, and the resize rewrites it.\n * ⛔ THE PUT CARRIES THE `digest` OF THE CONFIG IT WAS JUDGED AGAINST. PVE refuses the write if the\n * file changed since (`assert_if_modified`), so an edit landing between reconcile's own read and\n * its PUT fails the deploy instead of being overwritten by a change computed from the older file.\n * ⚠️ THE DIGEST IS THE DEPLOY'S READ, NOT THE PLAN'S. A hand edit between `plan` and `deploy` is\n * seen by reconcile's fresh judge instead: a declared key edited meanwhile is written back to the\n * declaration (even after a plan that warned of nothing), an undeclared one is left alone.\n */\nexport const updateGuest = (props: LxcProps, change: LxcChange, digest: string) =>\n Effect.gen(function* () {\n if (Object.keys(change.put).length > 0 || change.clear.length > 0) {\n const form = withClears(\n { ...change.put, ...(digest === '' ? {} : { digest }) },\n change.clear,\n );\n /** ⚠️ NO PRESENCE: this form is partial by construction — it is `judge`'s answer. */\n yield* guardForm(LXC_ENDPOINTS.update, form, false);\n yield* pve(props.target, 'provision', 'PUT', `${guestPath(props)}/config`, form);\n }\n for (const grow of change.resize) {\n const grown = { disk: grow.disk, size: grow.size };\n // ⛔ WITH PRESENCE: `disk` and `size` are both required and both always sent, so this is a\n // value check over PVE's own `+<n>` size pattern and its `rootfs|mp0..mp255` enum.\n yield* guardForm(LXC_ENDPOINTS.resize, grown, true);\n yield* task(\n props,\n 'PUT',\n `${guestPath(props)}/resize`,\n `grow ${grow.disk} of CT ${String(props.vmid)} to ${grow.size}`,\n TASK_POLLS,\n grown,\n );\n }\n });\n\n/**\n * ⛔ NO `force`, NO `purge`, NO STOP FIRST. PVE refuses to destroy a running or protected guest,\n * and that refusal is kept: stopping a guest so a delete can proceed is an operator's decision.\n * Reached only under `RemovalPolicy.destroy()` — this resource retains by default.\n * ⛔ AND ONLY THE GUEST THIS STACK DECLARED. State holds a vmid, and a vmid is reusable: after a\n * hand `pct destroy` and a new `pct create` at the same id, \"delete CT 150\" is somebody else's\n * container and all its volumes. So the live guest is judged against the last declaration\n * first; one that no longer matches is refused with the keys that differ, and deleting it (or\n * declaring it again) is the operator's call.\n */\nexport const destroyGuest = (where: LxcWhere, declared: LxcProps) =>\n Effect.gen(function* () {\n const live = yield* readLive(where);\n if (live === undefined) return;\n const drift = judge(declared, live).drift;\n if (drift.length > 0) {\n return yield* Effect.fail(\n new LxcRefusedError(\n `destroy CT ${String(where.vmid)}: the guest on ${where.node} no longer matches its last ` +\n `declaration (${drift.join(', ')}), so it may not be the guest this stack made. ` +\n 'Nothing was deleted. Delete it by hand if it should go, or declare it again.',\n ),\n );\n }\n yield* task(where, 'DELETE', guestPath(where), `destroy CT ${String(where.vmid)}`, TASK_POLLS);\n });\n",
|
|
57
70
|
"/**\n * How this package finds out what a node has STAGED, and whether the cluster survived an apply.\n *\n * ★ SPLIT OUT OF network-apply.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a real\n * one rather than a convenient line number: this file answers \"what is true on the cluster right\n * now\", network-apply.ts answers \"what does the resource do about it\". Nothing here decides a\n * diff, and the only write in either file is the apply itself.\n *\n * ⛔ THE PENDING STATE IS NOT IN `data`, WHICH IS WHY THIS FILE DOES ITS OWN FETCH. MEASURED in the\n * cluster's own source on node-b, 2026-09-13 — not inferred from documentation:\n * PVE/API2/Network.pm my $tmp = PVE::INotify::read_file('interfaces', 1);\n * $rpcenv->set_result_attrib('changes', $changes) if $changes;\n * PVE/HTTPServer.pm if (my $diff = $rpcenv->get_result_attrib('changes')) {\n * $resp->{changes} = $diff;\n * }\n * So the answer is `{\"data\":[…interfaces…],\"changes\":\"<unified diff>\"}`: `changes` is a SIBLING\n * of `data`, set only when the diff is non-empty. `client.ts`'s `pve()` returns `body.data` and\n * drops the envelope, so the one readable pending signal is invisible to every other resource in\n * this package. Without the fetch below, `Proxmox.NetworkApply` could not diff honestly at all.\n * ★ `pveEnvelopeWith` in client.ts now carries the same member failover as every other call.\n *\n * ⛔ AND THE INTERFACE ROWS CANNOT TELL YOU EITHER. MEASURED in PVE/INotify.pm `read_file`: when\n * `/etc/network/interfaces.new` exists, THAT is the file that gets parsed, so\n * `GET /nodes/{node}/network` answers with the STAGED config and a node about to be reconfigured\n * looks identical to one already running it. Same trap `sdn-apply.ts` documents for `?running=1`.\n *\n * ⚠️ `active` IS NOT A PENDING SIGNAL, AND THE OBVIOUS SHORTCUT IS MEASURABLY WRONG. On node-b and node-c,\n * with nothing staged anywhere (VERIFIED 2026-09-13: `/etc/network/interfaces.new` absent on node-b,\n * node-c and node-d), `wlan0` already comes back with no `active` key at all. Counting interfaces that\n * are not up as \"pending\" would report an update on two of the three nodes forever.\n *\n * ⚠️ ONE THING GENUINELY CANNOT BE SEEN FROM HERE, AND IT IS SMALL. The diff is computed as\n * `diff -b -N -u`, so a staged file differing from the live one ONLY in whitespace produces no\n * `changes` at all. This module reports that node as settled while `interfaces.new` still sits\n * there waiting for the next reload anybody triggers. The file's existence is not exposed by the\n * API in any other form, so the alternative is not a better read — it is SSH.\n */\nimport * as Effect from 'effect/Effect';\nimport { pve, pveEnvelopeWith, pveWith } from './client.ts';\nimport { type PveCredential, type PveTarget } from './credentials.ts';\nimport { leased } from './lease-cache.ts';\nimport { bool, text } from './values.ts';\n\n/** The `GET /nodes/{node}/network` envelope. ⛔ `changes` is beside `data`, never inside it. */\ntype NetworkEnvelope = { changes?: string };\n\n/** One row of `GET /cluster/status` — a `cluster` row, or one `node` row per member. */\ntype StatusRow = { type?: string; name?: string; quorate?: unknown; online?: unknown };\n\n/** One `GET /nodes/{node}/tasks/{upid}/status` answer. `status` is `running` or `stopped`. */\ntype TaskStatus = { status?: string; exitstatus?: string };\n\n/** What the cluster looked like the moment it was asked. Never persisted — see network-apply.ts. */\nexport type ClusterHealth = { quorate: boolean; offline: readonly string[] };\n\n/**\n * The unified diff between a node's applied interfaces file and its staged one, or `''`.\n *\n * ⛔ THIS STRING NEVER LEAVES THIS MODULE, AND THAT IS A SECRET RULE, NOT A STYLE ONE. Alchemy\n * persists attributes UNENCRYPTED (credentials.ts spells out why), and `/etc/network/interfaces`\n * is a file that can legally contain `wpa-psk` — node-b and node-c both carry a `wlan0`, MEASURED — as\n * well as any `pre-up` command somebody wrote. A diff of that file in a state store is a secret\n * in a state store. Only the COUNT below ever escapes.\n */\nconst stagedDiff = (target: PveTarget, node: string) =>\n Effect.gen(function* () {\n const path = `nodes/${node}/network`;\n // ⚠️ THE `read` ROLE IS ENOUGH, AND DELIBERATELY SO. The schema's permission for this GET is\n // `\"user\": \"all\"` — no privilege check at all — so the pending read never needs the\n // provisioning lease, and a plan stays a plan even if the write role is missing entirely.\n // ⛔ THROUGH THE LEASE CACHE, NOT `mint`. This read needs no particular identity — only the\n // apply and its task poll do, and network-apply.ts mints for those itself. Minting here left\n // SIX extra `hf-read@pve` tokens on node-b per C1 plan (one per node, for read and for diff),\n // MEASURED 2026-09-14 while the cache itself made one read mint for the whole run.\n const credential = yield* leased(target, 'read');\n const envelope = (yield* pveEnvelopeWith(target, credential, 'GET', path)) as NetworkEnvelope;\n return text(envelope.changes);\n });\n\n/**\n * Changed lines in a unified diff.\n *\n * ⚠️ A NON-EMPTY DIFF IS NEVER REPORTED AS ZERO, WHICH IS WHAT `Math.max` IS FOR. The `+++`/`---`\n * headers have to be skipped, and a removed line whose own text starts with `---` is skipped with\n * them; a staged change consisting of nothing else would otherwise count 0 and be reported as\n * \"settled\", which is the exact lie this resource exists to prevent. The count is informational —\n * the resource only ever compares it to zero — so an occasional undercount of 1 is harmless while\n * an undercount to zero is not.\n */\nexport const pendingLines = (diff: string) => {\n if (diff === '') return 0;\n const changed = diff\n .split('\\n')\n .filter(\n (line) =>\n (line.startsWith('+') || line.startsWith('-')) &&\n !line.startsWith('+++') &&\n !line.startsWith('---'),\n ).length;\n return Math.max(changed, 1);\n};\n\n/** How many staged lines this node is carrying. Zero is the only settled value. */\nexport const pendingCount = (target: PveTarget, node: string) =>\n stagedDiff(target, node).pipe(Effect.map(pendingLines));\n\n/**\n * Quorum, and every member that is not online.\n *\n * ⛔ QUORUM ALONE IS NOT THE CHECK, AND ON A THREE-NODE CLUSTER IT WOULD MISS THE ACCIDENT THIS\n * RESOURCE IS AFRAID OF. Losing ONE of three leaves `quorate: 1` — MEASURED shape on C1:\n * `{\"id\":\"cluster\",\"name\":\"HF-C1\",\"nodes\":3,\"quorate\":1,…}` plus one row per node with\n * `online: 1`. So a reload that drops node-b off the network is invisible to `quorate` and visible\n * only as `online: 0` on that row. Both are checked, and the node names come back so the failure\n * message can say WHICH member went away.\n *\n * ⚠️ AN UNCLUSTERED NODE IS QUORATE BY DEFINITION. A standalone PVE answers with node rows and no\n * `cluster` row; reading that absence as \"not quorate\" would make this resource undeclarable on\n * every single-node install.\n *\n * ⚠️ THIS ONE NEEDS `Sys.Audit` ON `/` — the only privilege in this family beyond the apply itself.\n * It is asked of the `read` role, which is auditor-shaped, rather than of `provision`.\n */\nconst clusterHealth = (target: PveTarget) =>\n pve<StatusRow[]>(target, 'read', 'GET', 'cluster/status').pipe(\n Effect.map((rows) => {\n const all = rows ?? [];\n const cluster = all.find((row) => row.type === 'cluster');\n return {\n offline: all\n .filter((row) => row.type === 'node' && !bool(row.online))\n .map((row) => text(row.name, '<unnamed>')),\n quorate: cluster === undefined ? true : bool(cluster.quorate),\n } satisfies ClusterHealth;\n }),\n );\n\n/**\n * Why the cluster is not healthy, or `undefined` when it is.\n *\n * ★ A REASON RATHER THAN A BOOLEAN, BECAUSE THE CALLER'S ONLY USE FOR IT IS A REFUSAL MESSAGE.\n * \"The deploy stopped because the cluster was degraded\" is not actionable at three in the\n * morning; \"quorate=true offline=[node-c]\" is. The two callers in network-apply.ts ask this before\n * the reload and again after it, and the second answer is the one that stops the chain from\n * reaching the next node.\n *\n * ⚠️ A MEMBER OFFLINE FOR AN UNRELATED REASON BLOCKS EVERY APPLY, AND THAT IS THE INTENDED\n * BEHAVIOUR RATHER THAN AN OVERSIGHT. A node down for maintenance is one failure away from the\n * pools losing min_size; reloading another node's networking while it is out is the move that\n * completes the outage. Bring it back, or apply by hand with the cluster in front of you.\n */\nexport const degradedReason = (target: PveTarget) =>\n clusterHealth(target).pipe(\n Effect.map((health) =>\n health.quorate && health.offline.length === 0\n ? undefined\n : `quorate=${String(health.quorate)} offline=[${health.offline.join(', ')}]`,\n ),\n );\n\n/**\n * ⛔ NOT AN EXITSTATUS PVE CAN RETURN, AND THAT IS THE POINT. The caller compares against `'OK'`;\n * these two say WHY the answer is not OK without needing a second branch for \"we do not know\".\n */\nexport const UNREACHABLE =\n 'the task could not be read -- the node stopped answering, or this lease cannot see it';\nconst STILL_RUNNING = 'still running when this provider stopped waiting';\n\n/**\n * How long to wait on `ifreload`, in one-second polls.\n *\n * ⚠️ THIRTY SECONDS IS A CEILING, NOT AN EXPECTATION. A reload of a handful of interfaces finishes\n * in a second or two; past thirty, something is wrong in a way a deploy cannot fix by waiting,\n * and the honest move is to stop and name the task rather than park the stack on it.\n */\nconst ATTEMPTS = 30;\n\n/**\n * Wait for one `srvreload` task and answer its exitstatus.\n *\n * ⛔ THE APPLY IS ASYNCHRONOUS AND ITS 200 MEANS NOTHING. MEASURED: `reload_network_config` ends\n * `return $rpcenv->fork_worker('srvreload', 'networking', $authuser, $worker)`, so the PUT\n * returns a UPID the instant the worker is forked — before `ifreload -a` has run, let alone\n * succeeded. Waiting for the task is the only way to learn the outcome.\n *\n * ⛔ AND IT MUST BE POLLED WITH THE CREDENTIAL THAT STARTED IT. The task-status schema says\n * `\"user\": \"all\"` with the note \"needs 'Sys.Audit' on '/nodes/<node>' if they are not the owner\n * of the task\". Every mint returns a NEW token id, so a second mint is a different identity and\n * is not the owner — which is why this takes a `PveCredential` and the caller keeps one lease\n * across both calls, instead of widening the provisioning role to read its own tasks.\n */\nexport const awaitTask = (\n target: PveTarget,\n credential: PveCredential,\n node: string,\n upid: string,\n // ★ A CALLER WHOSE TASK IS SLOWER THAN A RELOAD SAYS SO: an LXC create unpacks a template.\n attempts: number = ATTEMPTS,\n) =>\n Effect.gen(function* () {\n /**\n * ⚠️ THE UPID GOES IN RAW, NOT PERCENT-ENCODED, AND THE CHOICE IS DELIBERATE. A UPID is\n * `UPID:<node>:<hex>:<hex>:<hex>:<type>:<id>:<user>:` — its only punctuation is `:`, `@`, `!`\n * and `-`, every one of which is a legal path character, so raw needs nothing from the\n * router. Encoding them instead RELIES on PVE unescaping each path segment, which is\n * plausible and was NOT measured here (measuring it means starting a task, i.e. writing). If\n * that assumption were wrong the poll would 404, this would report the outcome as unknown,\n * and a perfectly good reload would stop the deploy. Raw removes the assumption.\n */\n const path = `nodes/${node}/tasks/${upid}/status`;\n for (let attempt = 0; attempt < attempts; attempt += 1) {\n /**\n * ⛔ A FAILED POLL IS NOT A FAILED RELOAD, AND CALLING IT ONE WOULD BE THE WORST KIND OF\n * WRONG. If the member serving the poll is the node being reloaded, `ifreload -a` can drop\n * the very connection this poll rides on — see network-apply.ts.\n * The reload may well have succeeded. So the call is collapsed to \"we cannot see the task\"\n * and the caller reports the outcome as UNKNOWN, with the UPID, rather than guessing.\n * ⚠️ A 403 LANDS HERE TOO, AND IT READS THE SAME. If the polling identity is somehow not the\n * task's owner, the endpoint wants `Sys.Audit` on the node and this sees only \"no answer\" —\n * which is why `UNREACHABLE` names both possibilities rather than blaming the network.\n */\n const status = yield* pveWith<TaskStatus>(target, credential, 'GET', path).pipe(\n Effect.orElseSucceed(() => undefined),\n );\n if (status === undefined) return UNREACHABLE;\n if (status.status === 'stopped') return text(status.exitstatus, 'stopped with no exitstatus');\n yield* Effect.sleep('1 second');\n }\n return STILL_RUNNING;\n });\n",
|
|
58
|
-
"/**\n * `Proxmox.MetricServer` — where the cluster ships its metrics, declared.\n *\n * ★ THE FAILURE THIS PREVENTS IS A SILENT ONE, WHICH IS THE WHOLE ARGUMENT FOR DECLARING IT.\n * Undeclared, the target exists only as a section of `/etc/pve/status.cfg`: rebuild a node,\n * re-add one to the cluster, or restore a config from before it was configured, and the cluster\n * stops shipping. Nothing fails, no task errors, no alert fires — the graphs go flat, and the\n * first to find out is whoever went looking for a number that was not there.\n *\n * ⛔ CREATE IS `POST cluster/metrics/server/{id}` — THE OBJECT'S OWN PATH — SO `collection()`\n * RETURNS THE SAME STRING AS `path()`, AND THAT IS NOT A TYPO. MEASURED: a POST to the\n * collection answers \"Method 'POST /cluster/metrics/server' not implemented\", while the id path\n * gets as far as a permission check (`/`, Sys.Modify); the published schema agrees, giving the\n * collection a GET and nothing else. `createForm` omits `id` for the same reason — it is already\n * the last segment of the path being POSTed to, and a second copy can only disagree with it.\n *\n * ⛔ THE `token` IS WRITE-ONLY AND MUST NEVER BECOME AN ATTRIBUTE. Alchemy persists attributes\n * UNENCRYPTED, so one that reached state would outlive the change that set it, in the state\n * store and in every backup of it. It is `never` in the props below — declaring one is a COMPILE\n * ERROR rather than a leak found months later — and absent from `optional`, for the reason\n * given there. Set it out of band; PVE never returns it on a read anyway. `otel-headers`, where an\n * opentelemetry server's bearer token lives, gets the same treatment in metric-server-otel.ts.\n *\n * ⚠️ PRIVILEGES, AND THIS IS A BIGGER ASK THAN IT LOOKS. Read and diff are fine under an auditor\n * lease — the single-object GET checks `Sys.Audit` on `/` — while create, update and delete all\n * check `Sys.Modify` on `/` and answer \"Permission check failed (/, Sys.Modify)\" until the role\n * is widened. Widen it knowingly: the ACL path is the ROOT and there is no `/metrics` object to\n * scope to, so `Sys.Modify` also buys datacenter options, notification targets and every other\n * cluster-wide config write. A separate role for metric writes is the narrower answer.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { UNSET, body, update } from './metric-server-form.ts';\nimport {\n type MetricServerOtelAttributes,\n type MetricServerOtelProps,\n otelAttributes,\n otelMatches,\n} from './metric-server-otel.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool, int, text } from './values.ts';\n\n/**\n * PVE's status plugins. Which one a server is decides which fields it will even accept.\n *\n * ★ `opentelemetry` IS OFFERED SINCE 2026-09-14, AND THE REASON IT WAS NOT STILL HOLDS FOR ANY FIELD\n * LEFT OUT. Its whole configuration is the `otel-*` family; declared without managing that family\n * it would build a target whose every meaningful setting sat outside the graph, and report `noop`\n * over it. metric-server-otel.ts manages every `otel-*` field but two, and says why for each.\n */\nexport type MetricServerType = 'graphite' | 'influxdb' | 'opentelemetry';\n\nexport interface MetricServerProps extends WithTarget, MetricServerOtelProps {\n /** ⚠️ A `pve-configid` — a letter, then letters, digits, `-` and `_`. A rejected one fails with\n * \"invalid configuration ID\", which reads as a broken request rather than as a naming rule. */\n id: string;\n /**\n * ⛔ CREATE-TIME ONLY. The PUT schema has no `type` parameter at all: a section's type is fixed\n * when it is written, so the ⛔ in `attributes` turns a changed type into a refusal rather than\n * a silent no-op over the wrong target.\n */\n type: MetricServerType;\n /** ⚠️ REQUIRED ON EVERY WRITE, UPDATE INCLUDED — see `required`. */\n server: string;\n port: number;\n /** Keep the section but stop shipping. Unset is an ENABLED target, not an absent one. */\n disable?: boolean;\n /**\n * graphite and influxdb only: UDP MTU (512-65536) and socket/HTTP timeout. Unset leaves PVE's\n * defaults, 1500 and 1. ⚠️ opentelemetry has neither; its timeout is `otel-timeout`.\n */\n mtu?: number;\n timeout?: number;\n /** graphite only: the root path metrics are published under, e.g. `proxmox.mycluster`. */\n path?: string;\n /**\n * influxdb only. `udp` is PVE's default and writes line protocol; `http`/`https` use the v2 API,\n * the only place `organization` and `bucket` mean anything. An unset `verify-certificate` means\n * VERIFY — PVE's default, and the safe one.\n * ⚠️ PVE's hyphenated names are kept rather than camelCased, so nothing between here and the form\n * body has to remember a mapping.\n */\n influxdbproto?: 'udp' | 'http' | 'https';\n organization?: string;\n bucket?: string;\n 'api-path-prefix'?: string;\n 'max-body-size'?: number;\n 'verify-certificate'?: boolean;\n /** ⛔ `never` ON PURPOSE — see the ⛔ in the header. The compile error is the feature. */\n token?: never;\n}\n\n/**\n * ⛔ NO `digest`, ON PURPOSE. PVE returns one with every read, but it is the digest of the WHOLE\n * `status.cfg` rather than of this section — declaring a SECOND metric server would rewrite this\n * one's stored attributes, and comparing it would report an update on a target nobody touched.\n * ⛔ And no `token`, for the reason the header gives at length.\n * ⚠️ `mtu`, `timeout` and `max-body-size` report `UNSET` when the section carries none, i.e. when\n * PVE's own default is what the target will use; `verify-certificate` reports that default\n * directly, because \"absent\" there means the target verifies. `port` reports 0 only for a\n * malformed section — PVE requires a port — so a plan should say so loudly. The `otel-*` half\n * follows the same rules; see metric-server-otel.ts.\n */\nexport interface MetricServerAttributes extends MetricServerOtelAttributes {\n id: string;\n type: MetricServerType;\n server: string;\n port: number;\n disable: boolean;\n mtu: number;\n timeout: number;\n path: string;\n influxdbproto: string;\n organization: string;\n bucket: string;\n 'api-path-prefix': string;\n 'max-body-size': number;\n 'verify-certificate': boolean;\n}\n\nexport interface ProxmoxMetricServer extends Resource<\n 'Proxmox.MetricServer',\n MetricServerProps,\n MetricServerAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxMetricServer = Resource<ProxmoxMetricServer>('Proxmox.MetricServer');\n\n/**\n * The type-specific half of `matches`.\n *\n * ⛔ ONLY FIELDS THIS TYPE ACTUALLY SENDS ARE COMPARED. `verify-certificate` is not in graphite's\n * option set and `path` is not in influxdb's, so neither is ever sent to the wrong plugin — and\n * diffing one anyway would report an update that the PUT it triggers cannot satisfy, forever.\n * `mtu` and `timeout` are compared for graphite and influxdb only, because the opentelemetry\n * plugin has neither and `optional` never sends them to it.\n */\nconst matchesType = (attributes: MetricServerAttributes, props: MetricServerProps): boolean => {\n if (props.type === 'opentelemetry') return otelMatches(attributes, props);\n const socket =\n attributes.mtu === (props.mtu ?? UNSET) && attributes.timeout === (props.timeout ?? UNSET);\n if (props.type === 'graphite') return socket && attributes.path === (props.path ?? '');\n return (\n socket &&\n attributes.influxdbproto === (props.influxdbproto ?? '') &&\n attributes.organization === (props.organization ?? '') &&\n attributes.bucket === (props.bucket ?? '') &&\n attributes['api-path-prefix'] === (props['api-path-prefix'] ?? '') &&\n attributes['max-body-size'] === (props['max-body-size'] ?? UNSET) &&\n attributes['verify-certificate'] === (props['verify-certificate'] !== false)\n );\n};\n\nconst handlers = pveHandlers<MetricServerProps, MetricServerAttributes>({\n attributes: (live, props) => {\n /**\n * ⛔ A SERVER OF ANOTHER TYPE IS ANOTHER OBJECT, AND \"ABSENT\" IS THE HONEST ANSWER. `type`\n * cannot be changed by a PUT, so reporting the foreign section as missing makes reconcile\n * POST instead and PVE refuses because the id is taken — a loud, accurate error rather than a\n * PUT pushing influxdb fields at a graphite section. An older PVE that omits `type` from the\n * read falls through and is trusted.\n */\n const liveType = text(live['type']);\n if (liveType !== '' && liveType !== props.type) return undefined;\n return {\n ...otelAttributes(live),\n 'api-path-prefix': text(live['api-path-prefix']),\n bucket: text(live['bucket']),\n disable: bool(live['disable'], false),\n id: props.id,\n influxdbproto: text(live['influxdbproto']),\n 'max-body-size': int(live['max-body-size'], UNSET),\n mtu: int(live['mtu'], UNSET),\n organization: text(live['organization']),\n path: text(live['path']),\n port: int(live['port'], 0),\n server: text(live['server']),\n timeout: int(live['timeout'], UNSET),\n type: props.type,\n 'verify-certificate': bool(live['verify-certificate'], true),\n };\n },\n /** ⛔ THE SAME STRING AS `path`, DELIBERATELY — see the ⛔ in the header. */\n collection: (props) => `cluster/metrics/server/${props.id}`,\n /** ⚠️ `type` IS REQUIRED ON CREATE; `id` IS NOT SENT, because the id is the path. */\n createForm: (props) => ({ ...body(props), type: props.type }),\n /**\n * ⛔ `id` and `type` are absent: one is the key the object was read by, the other is refused\n * rather than updated. Everything type-specific is `matchesType`'s, above.\n */\n matches: (attributes, props) =>\n attributes.server === props.server &&\n attributes.port === props.port &&\n attributes.disable === (props.disable === true) &&\n matchesType(attributes, props),\n path: (props) => `cluster/metrics/server/${props.id}`,\n /** The form and its clear-list, from the one table in metric-server-form.ts. */\n updateForm: update,\n});\n\n/**\n * ⛔ THE EMPTY `list` FROM `pveHandlers` EARNS ITS KEEP HERE. `GET cluster/metrics/server` answers\n * with every target the cluster already ships to, including the one somebody configured by hand\n * years ago; adopting that is how a later `alchemy destroy` silences a graph nobody declared.\n * ⚠️ AND NOTHING BRAKES THIS DESTROY. A pool refuses while it holds guests, a container while it\n * runs; a metric server just leaves `status.cfg`, the cluster stops shipping on the next\n * interval, and no guest or task is affected. The plan diff is the only warning anyone gets —\n * the top of this file again, this time caused by a deploy.\n */\nexport const ProxmoxMetricServerProvider = () =>\n Provider.effect(ProxmoxMetricServer, Effect.succeed(ProxmoxMetricServer.Provider.of(handlers)));\n",
|
|
59
|
-
"/**\n * A metric server's props, as the form PVE wants.\n *\n * ★ SPLIT OUT OF metric-server.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a\n * real one rather than a convenient line number: this file answers \"how does a declaration\n * become a PVE form\", and metric-server.ts answers \"what is a metric server and when has it\n * changed\". Nothing here reads the cluster and nothing here decides a diff.\n *\n * ⚠️ THE `import type` BACK TO metric-server.ts IS A CYCLE ON PAPER ONLY. It is type-only, so it\n * is erased before anything runs — `MetricServerProps` stays the resource's public shape, in the\n * file that declares the resource, rather than being moved somewhere odd to dodge the arrow.\n * metric-server-otel.ts imports `UNSET` from here and this file imports nothing back from it,\n * so no runtime cycle exists either.\n */\nimport type { MetricServerProps } from './metric-server.ts';\nimport { withClears } from './values.ts';\n\n/**\n * ⚠️ \"NOT SET\" CANNOT BE 0 HERE, WHICH IS WHY IT IS -1. `timeout` has a minimum of 0 in PVE's\n * schema, so a deliberate `timeout 0` exists; a provider using 0 as its absent-marker reads that\n * zero as \"unset\" and never clears it. -1 is outside every one of these fields' ranges.\n */\nexport const UNSET = -1;\n\nconst str = (value: number | undefined) => (value === undefined ? undefined : String(value));\n\n/**\n * Every managed optional, per type: the value to send, or undefined for \"clear it\".\n *\n * ⛔ ONE TABLE FEEDS BOTH HALVES OF AN UPDATE, AND THAT IS THE POINT. A PUT THAT OMITS A FIELD DOES\n * NOT CLEAR IT — PVE merges the form into the existing section — so a managed optional needs a\n * matching `delete=` the moment its prop goes away, or `matches` reports an update on every plan\n * forever. Deriving the form and the delete list from one map makes \"managed but not clearable\"\n * impossible to write here, which a separate clear-list would not.\n * ⛔ NOTHING SET OUT OF BAND MAY BE LISTED HERE, `token` FIRST AMONG THEM: a field in this map is\n * CLEARED whenever it is undeclared, so adding the token would strip a working target's\n * credential on the next plan — metrics stop, nothing errors, the graph goes flat. `otel-headers`\n * is absent for the same reason: it is where an OTLP server's bearer token lives.\n * ⚠️ AND THE BRANCHES CANNOT BE ONE SHARED MAP. PVE validates a section against that plugin's own\n * option set, so `path` on an influxdb server is a hard 400 and `delete=path` there answers \"no\n * such option\" — neither is an ignored hint. The opentelemetry plugin has no `mtu` and no\n * `timeout` at all (its option list is `server`, `port`, `disable` and the `otel-*` family), so\n * that branch carries neither.\n * ⚠️ graphite's `proto` (udp or tcp) is deliberately absent: a transport chosen by hand stays put\n * and no plan diffs it. Managing it means adding it HERE, where it is clearable by construction.\n * `otel-resource-attributes` is absent for the reason metric-server-otel.ts gives.\n */\nexport const optional = (props: MetricServerProps): Record<string, string | undefined> => {\n switch (props.type) {\n case 'graphite':\n return { mtu: str(props.mtu), path: props.path, timeout: str(props.timeout) };\n case 'influxdb':\n return {\n 'api-path-prefix': props['api-path-prefix'],\n bucket: props.bucket,\n influxdbproto: props.influxdbproto,\n 'max-body-size': str(props['max-body-size']),\n mtu: str(props.mtu),\n organization: props.organization,\n timeout: str(props.timeout),\n };\n case 'opentelemetry':\n return {\n 'otel-compression': props['otel-compression'],\n 'otel-max-body-size': str(props['otel-max-body-size']),\n 'otel-path': props['otel-path'],\n 'otel-protocol': props['otel-protocol'],\n 'otel-timeout': str(props['otel-timeout']),\n };\n }\n};\n\n/**\n * The certificate-verification flag, for the one plugin that owns it.\n *\n * ⛔ EACH FLAG GOES ONLY TO ITS OWN PLUGIN. `verify-certificate` is influxdb's, `otel-verify-ssl` is\n * opentelemetry's, graphite has neither — and PVE refuses an option outside the section's plugin\n * rather than ignoring it, the same way it refuses `path` on influxdb.\n */\nconst verification = (props: MetricServerProps): Record<string, string> => {\n switch (props.type) {\n case 'graphite':\n return {};\n case 'influxdb':\n return { 'verify-certificate': props['verify-certificate'] === false ? '0' : '1' };\n case 'opentelemetry':\n return { 'otel-verify-ssl': props['otel-verify-ssl'] === false ? '0' : '1' };\n }\n};\n\n/**\n * The fields sent on EVERY write, create and update alike.\n *\n * ⚠️ `server` AND `port` ARE REQUIRED BY THE PUT TOO, not only by the POST: a PUT that omits either\n * fails parameter verification rather than leaving them alone. PVE also refuses to clear them —\n * \"unable to delete required option\" — which is why they are here and not in `optional`.\n * ⚠️ `disable` AND THE VERIFY FLAG ARE SENT WITH THEIR DEFAULTS rather than left out. Writing the\n * value PVE would have assumed makes the read-back match on the first plan after the create,\n * whether or not that PVE materialises defaults into the file.\n */\nconst required = (props: MetricServerProps): Record<string, string> => ({\n disable: props.disable === true ? '1' : '0',\n port: String(props.port),\n server: props.server,\n ...verification(props),\n});\n\nexport const body = (props: MetricServerProps) => {\n const fields = required(props);\n for (const [option, value] of Object.entries(optional(props))) {\n if (value !== undefined) fields[option] = value;\n }\n return fields;\n};\n\n/**\n * An update's form: everything `body` sends, plus a `delete=` naming each managed optional that is\n * no longer declared.\n *\n * ★ MOVED HERE FROM metric-server.ts UNCHANGED, so the test beside this file can reach the form an\n * update really sends instead of re-deriving it and testing its own copy.\n */\nexport const update = (props: MetricServerProps) => {\n const clear = Object.entries(optional(props))\n .filter(([, value]) => value === undefined)\n .map(([option]) => option);\n return withClears(body(props), clear);\n};\n",
|
|
71
|
+
"/**\n * `Proxmox.MetricServer` — where the cluster ships its metrics, declared.\n *\n * ★ THE FAILURE THIS PREVENTS IS A SILENT ONE, WHICH IS THE WHOLE ARGUMENT FOR DECLARING IT.\n * Undeclared, the target exists only as a section of `/etc/pve/status.cfg`: rebuild a node,\n * re-add one to the cluster, or restore a config from before it was configured, and the cluster\n * stops shipping. Nothing fails, no task errors, no alert fires — the graphs go flat, and the\n * first to find out is whoever went looking for a number that was not there.\n *\n * ⛔ CREATE IS `POST cluster/metrics/server/{id}` — THE OBJECT'S OWN PATH — SO `collection()`\n * RETURNS THE SAME STRING AS `path()`, AND THAT IS NOT A TYPO. MEASURED: a POST to the\n * collection answers \"Method 'POST /cluster/metrics/server' not implemented\", while the id path\n * gets as far as a permission check (`/`, Sys.Modify); the published schema agrees, giving the\n * collection a GET and nothing else. `createForm` omits `id` for the same reason — it is already\n * the last segment of the path being POSTed to, and a second copy can only disagree with it.\n *\n * ⛔ THE `token` IS WRITE-ONLY AND MUST NEVER BECOME AN ATTRIBUTE. Alchemy persists attributes\n * UNENCRYPTED, so one that reached state would outlive the change that set it, in the state\n * store and in every backup of it. It is `never` in the props below — declaring one is a COMPILE\n * ERROR rather than a leak found months later — and absent from `optional`, for the reason\n * given there. Set it out of band; PVE never returns it on a read anyway. `otel-headers`, where an\n * opentelemetry server's bearer token lives, gets the same treatment in metric-server-otel.ts.\n *\n * ⚠️ PRIVILEGES, AND THIS IS A BIGGER ASK THAN IT LOOKS. Read and diff are fine under an auditor\n * lease — the single-object GET checks `Sys.Audit` on `/` — while create, update and delete all\n * check `Sys.Modify` on `/` and answer \"Permission check failed (/, Sys.Modify)\" until the role\n * is widened. Widen it knowingly: the ACL path is the ROOT and there is no `/metrics` object to\n * scope to, so `Sys.Modify` also buys datacenter options, notification targets and every other\n * cluster-wide config write. A separate role for metric writes is the narrower answer.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { UNSET, createBody, update } from './metric-server-form.ts';\nimport {\n type MetricServerOtelAttributes,\n type MetricServerOtelProps,\n otelAttributes,\n otelMatches,\n} from './metric-server-otel.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool, int, text } from './values.ts';\n\n/**\n * PVE's status plugins. Which one a server is decides which fields it will even accept.\n *\n * ★ `opentelemetry` IS OFFERED SINCE 2026-09-14, AND THE REASON IT WAS NOT STILL HOLDS FOR ANY FIELD\n * LEFT OUT. Its whole configuration is the `otel-*` family; declared without managing that family\n * it would build a target whose every meaningful setting sat outside the graph, and report `noop`\n * over it. metric-server-otel.ts manages every `otel-*` field but two, and says why for each.\n */\nexport type MetricServerType = 'graphite' | 'influxdb' | 'opentelemetry';\n\nexport interface MetricServerProps extends WithTarget, MetricServerOtelProps {\n /** ⚠️ A `pve-configid` — a letter, then letters, digits, `-` and `_`. A rejected one fails with\n * \"invalid configuration ID\", which reads as a broken request rather than as a naming rule. */\n id: string;\n /**\n * ⛔ CREATE-TIME ONLY. The PUT schema has no `type` parameter at all: a section's type is fixed\n * when it is written, so the ⛔ in `attributes` turns a changed type into a refusal rather than\n * a silent no-op over the wrong target.\n */\n type: MetricServerType;\n /** ⚠️ REQUIRED ON EVERY WRITE, UPDATE INCLUDED — see `required`. */\n server: string;\n port: number;\n /** Keep the section but stop shipping. Unset is an ENABLED target, not an absent one. */\n disable?: boolean;\n /**\n * graphite and influxdb only: UDP MTU (512-65536) and socket/HTTP timeout. Unset leaves PVE's\n * defaults, 1500 and 1. ⚠️ opentelemetry has neither; its timeout is `otel-timeout`.\n */\n mtu?: number;\n timeout?: number;\n /** graphite only: the root path metrics are published under, e.g. `proxmox.mycluster`. */\n path?: string;\n /**\n * influxdb only. `udp` is PVE's default and writes line protocol; `http`/`https` use the v2 API,\n * the only place `organization` and `bucket` mean anything. An unset `verify-certificate` means\n * VERIFY — PVE's default, and the safe one.\n * ⚠️ PVE's hyphenated names are kept rather than camelCased, so nothing between here and the form\n * body has to remember a mapping.\n */\n influxdbproto?: 'udp' | 'http' | 'https';\n organization?: string;\n bucket?: string;\n 'api-path-prefix'?: string;\n 'max-body-size'?: number;\n 'verify-certificate'?: boolean;\n /** ⛔ `never` ON PURPOSE — see the ⛔ in the header. The compile error is the feature. */\n token?: never;\n}\n\n/**\n * ⛔ NO `digest`, ON PURPOSE. PVE returns one with every read, but it is the digest of the WHOLE\n * `status.cfg` rather than of this section — declaring a SECOND metric server would rewrite this\n * one's stored attributes, and comparing it would report an update on a target nobody touched.\n * ⛔ And no `token`, for the reason the header gives at length.\n * ⚠️ `mtu`, `timeout` and `max-body-size` report `UNSET` when the section carries none, i.e. when\n * PVE's own default is what the target will use; `verify-certificate` reports that default\n * directly, because \"absent\" there means the target verifies. `port` reports 0 only for a\n * malformed section — PVE requires a port — so a plan should say so loudly. The `otel-*` half\n * follows the same rules; see metric-server-otel.ts.\n */\nexport interface MetricServerAttributes extends MetricServerOtelAttributes {\n id: string;\n type: MetricServerType;\n server: string;\n port: number;\n disable: boolean;\n mtu: number;\n timeout: number;\n path: string;\n influxdbproto: string;\n organization: string;\n bucket: string;\n 'api-path-prefix': string;\n 'max-body-size': number;\n 'verify-certificate': boolean;\n}\n\nexport interface ProxmoxMetricServer extends Resource<\n 'Proxmox.MetricServer',\n MetricServerProps,\n MetricServerAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxMetricServer = Resource<ProxmoxMetricServer>('Proxmox.MetricServer');\n\n/**\n * The type-specific half of `matches`.\n *\n * ⛔ ONLY FIELDS THIS TYPE ACTUALLY SENDS ARE COMPARED. `verify-certificate` is not in graphite's\n * option set and `path` is not in influxdb's, so neither is ever sent to the wrong plugin — and\n * diffing one anyway would report an update that the PUT it triggers cannot satisfy, forever.\n * `mtu` and `timeout` are compared for graphite and influxdb only, because the opentelemetry\n * plugin has neither and `optional` never sends them to it.\n */\nconst matchesType = (attributes: MetricServerAttributes, props: MetricServerProps): boolean => {\n if (props.type === 'opentelemetry') return otelMatches(attributes, props);\n const socket =\n attributes.mtu === (props.mtu ?? UNSET) && attributes.timeout === (props.timeout ?? UNSET);\n if (props.type === 'graphite') return socket && attributes.path === (props.path ?? '');\n return (\n socket &&\n attributes.influxdbproto === (props.influxdbproto ?? '') &&\n attributes.organization === (props.organization ?? '') &&\n attributes.bucket === (props.bucket ?? '') &&\n attributes['api-path-prefix'] === (props['api-path-prefix'] ?? '') &&\n attributes['max-body-size'] === (props['max-body-size'] ?? UNSET) &&\n attributes['verify-certificate'] === (props['verify-certificate'] !== false)\n );\n};\n\nconst handlers = pveHandlers<MetricServerProps, MetricServerAttributes>({\n attributes: (live, props) => {\n /**\n * ⛔ A SERVER OF ANOTHER TYPE IS ANOTHER OBJECT, AND \"ABSENT\" IS THE HONEST ANSWER. `type`\n * cannot be changed by a PUT, so reporting the foreign section as missing makes reconcile\n * POST instead and PVE refuses because the id is taken — a loud, accurate error rather than a\n * PUT pushing influxdb fields at a graphite section. An older PVE that omits `type` from the\n * read falls through and is trusted.\n */\n const liveType = text(live['type']);\n if (liveType !== '' && liveType !== props.type) return undefined;\n return {\n ...otelAttributes(live),\n 'api-path-prefix': text(live['api-path-prefix']),\n bucket: text(live['bucket']),\n disable: bool(live['disable'], false),\n id: props.id,\n influxdbproto: text(live['influxdbproto']),\n 'max-body-size': int(live['max-body-size'], UNSET),\n mtu: int(live['mtu'], UNSET),\n organization: text(live['organization']),\n path: text(live['path']),\n port: int(live['port'], 0),\n server: text(live['server']),\n timeout: int(live['timeout'], UNSET),\n type: props.type,\n 'verify-certificate': bool(live['verify-certificate'], true),\n };\n },\n /** ⛔ THE SAME STRING AS `path`, DELIBERATELY — see the ⛔ in the header. */\n collection: (props) => `cluster/metrics/server/${props.id}`,\n /** ⚠️ `type` IS REQUIRED ON CREATE; `id` IS NOT SENT, because the id is the path. */\n createForm: createBody,\n /**\n * The vendor rules both forms are checked against at plan time — resource-spec.ts.\n * ⚠️ POST AND PUT ARE THE SAME PATH HERE, and they are still two different tables: only the\n * POST marks `type` required, and only the PUT accepts `delete` and `digest`.\n */\n endpoint: {\n create: 'pve:POST /cluster/metrics/server/{id}',\n update: 'pve:PUT /cluster/metrics/server/{id}',\n },\n /**\n * ⛔ `id` and `type` are absent: one is the key the object was read by, the other is refused\n * rather than updated. Everything type-specific is `matchesType`'s, above.\n */\n matches: (attributes, props) =>\n attributes.server === props.server &&\n attributes.port === props.port &&\n attributes.disable === (props.disable === true) &&\n matchesType(attributes, props),\n path: (props) => `cluster/metrics/server/${props.id}`,\n /** The form and its clear-list, from the one table in metric-server-form.ts. */\n updateForm: update,\n});\n\n/**\n * ⛔ THE EMPTY `list` FROM `pveHandlers` EARNS ITS KEEP HERE. `GET cluster/metrics/server` answers\n * with every target the cluster already ships to, including the one somebody configured by hand\n * years ago; adopting that is how a later `alchemy destroy` silences a graph nobody declared.\n * ⚠️ AND NOTHING BRAKES THIS DESTROY. A pool refuses while it holds guests, a container while it\n * runs; a metric server just leaves `status.cfg`, the cluster stops shipping on the next\n * interval, and no guest or task is affected. The plan diff is the only warning anyone gets —\n * the top of this file again, this time caused by a deploy.\n */\nexport const ProxmoxMetricServerProvider = () =>\n Provider.effect(ProxmoxMetricServer, Effect.succeed(ProxmoxMetricServer.Provider.of(handlers)));\n",
|
|
72
|
+
"/**\n * A metric server's props, as the form PVE wants.\n *\n * ★ SPLIT OUT OF metric-server.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a\n * real one rather than a convenient line number: this file answers \"how does a declaration\n * become a PVE form\", and metric-server.ts answers \"what is a metric server and when has it\n * changed\". Nothing here reads the cluster and nothing here decides a diff.\n *\n * ⚠️ THE `import type` BACK TO metric-server.ts IS A CYCLE ON PAPER ONLY. It is type-only, so it\n * is erased before anything runs — `MetricServerProps` stays the resource's public shape, in the\n * file that declares the resource, rather than being moved somewhere odd to dodge the arrow.\n * metric-server-otel.ts imports `UNSET` from here and this file imports nothing back from it,\n * so no runtime cycle exists either.\n */\nimport type { MetricServerProps } from './metric-server.ts';\nimport { withClears } from './values.ts';\n\n/**\n * ⚠️ \"NOT SET\" CANNOT BE 0 HERE, WHICH IS WHY IT IS -1. `timeout` has a minimum of 0 in PVE's\n * schema, so a deliberate `timeout 0` exists; a provider using 0 as its absent-marker reads that\n * zero as \"unset\" and never clears it. -1 is outside every one of these fields' ranges.\n */\nexport const UNSET = -1;\n\nconst str = (value: number | undefined) => (value === undefined ? undefined : String(value));\n\n/**\n * Every managed optional, per type: the value to send, or undefined for \"clear it\".\n *\n * ⛔ ONE TABLE FEEDS BOTH HALVES OF AN UPDATE, AND THAT IS THE POINT. A PUT THAT OMITS A FIELD DOES\n * NOT CLEAR IT — PVE merges the form into the existing section — so a managed optional needs a\n * matching `delete=` the moment its prop goes away, or `matches` reports an update on every plan\n * forever. Deriving the form and the delete list from one map makes \"managed but not clearable\"\n * impossible to write here, which a separate clear-list would not.\n * ⛔ NOTHING SET OUT OF BAND MAY BE LISTED HERE, `token` FIRST AMONG THEM: a field in this map is\n * CLEARED whenever it is undeclared, so adding the token would strip a working target's\n * credential on the next plan — metrics stop, nothing errors, the graph goes flat. `otel-headers`\n * is absent for the same reason: it is where an OTLP server's bearer token lives.\n * ⚠️ AND THE BRANCHES CANNOT BE ONE SHARED MAP. PVE validates a section against that plugin's own\n * option set, so `path` on an influxdb server is a hard 400 and `delete=path` there answers \"no\n * such option\" — neither is an ignored hint. The opentelemetry plugin has no `mtu` and no\n * `timeout` at all (its option list is `server`, `port`, `disable` and the `otel-*` family), so\n * that branch carries neither.\n * ⚠️ graphite's `proto` (udp or tcp) is deliberately absent: a transport chosen by hand stays put\n * and no plan diffs it. Managing it means adding it HERE, where it is clearable by construction.\n * `otel-resource-attributes` is absent for the reason metric-server-otel.ts gives.\n */\nexport const optional = (props: MetricServerProps): Record<string, string | undefined> => {\n switch (props.type) {\n case 'graphite':\n return { mtu: str(props.mtu), path: props.path, timeout: str(props.timeout) };\n case 'influxdb':\n return {\n 'api-path-prefix': props['api-path-prefix'],\n bucket: props.bucket,\n influxdbproto: props.influxdbproto,\n 'max-body-size': str(props['max-body-size']),\n mtu: str(props.mtu),\n organization: props.organization,\n timeout: str(props.timeout),\n };\n case 'opentelemetry':\n return {\n 'otel-compression': props['otel-compression'],\n 'otel-max-body-size': str(props['otel-max-body-size']),\n 'otel-path': props['otel-path'],\n 'otel-protocol': props['otel-protocol'],\n 'otel-timeout': str(props['otel-timeout']),\n };\n }\n};\n\n/**\n * The certificate-verification flag, for the one plugin that owns it.\n *\n * ⛔ EACH FLAG GOES ONLY TO ITS OWN PLUGIN. `verify-certificate` is influxdb's, `otel-verify-ssl` is\n * opentelemetry's, graphite has neither — and PVE refuses an option outside the section's plugin\n * rather than ignoring it, the same way it refuses `path` on influxdb.\n */\nconst verification = (props: MetricServerProps): Record<string, string> => {\n switch (props.type) {\n case 'graphite':\n return {};\n case 'influxdb':\n return { 'verify-certificate': props['verify-certificate'] === false ? '0' : '1' };\n case 'opentelemetry':\n return { 'otel-verify-ssl': props['otel-verify-ssl'] === false ? '0' : '1' };\n }\n};\n\n/**\n * The fields sent on EVERY write, create and update alike.\n *\n * ⚠️ `server` AND `port` ARE REQUIRED BY THE PUT TOO, not only by the POST: a PUT that omits either\n * fails parameter verification rather than leaving them alone. PVE also refuses to clear them —\n * \"unable to delete required option\" — which is why they are here and not in `optional`.\n * ⚠️ `disable` AND THE VERIFY FLAG ARE SENT WITH THEIR DEFAULTS rather than left out. Writing the\n * value PVE would have assumed makes the read-back match on the first plan after the create,\n * whether or not that PVE materialises defaults into the file.\n */\nconst required = (props: MetricServerProps): Record<string, string> => ({\n disable: props.disable === true ? '1' : '0',\n port: String(props.port),\n server: props.server,\n ...verification(props),\n});\n\nexport const body = (props: MetricServerProps) => {\n const fields = required(props);\n for (const [option, value] of Object.entries(optional(props))) {\n if (value !== undefined) fields[option] = value;\n }\n return fields;\n};\n\n/**\n * An update's form: everything `body` sends, plus a `delete=` naming each managed optional that is\n * no longer declared.\n *\n * ★ MOVED HERE FROM metric-server.ts UNCHANGED, so the test beside this file can reach the form an\n * update really sends instead of re-deriving it and testing its own copy.\n */\nexport const update = (props: MetricServerProps) => {\n const clear = Object.entries(optional(props))\n .filter(([, value]) => value === undefined)\n .map(([option]) => option);\n return withClears(body(props), clear);\n};\n\n/**\n * The create form: `body` plus the one field only a POST takes.\n *\n * ★ EXPORTED SO THE CONSTRAINT PROOF CAN RUN THE REAL FORM. `metric-server.ts` built this inline\n * in its spec, which meant the only way to check it against PVE's table was to retype it in a\n * test — and a retyped form proves the test, not the provider.\n * ⚠️ `type` IS REQUIRED ON CREATE AND REFUSED ON UPDATE; `id` is the path, never the body.\n */\nexport const createBody = (props: MetricServerProps) => ({ ...body(props), type: props.type });\n",
|
|
60
73
|
"/**\n * The `opentelemetry` plugin's option set: what a metric server of that type accepts, how it reads\n * back, and when it has changed.\n *\n * ★ SPLIT OUT OF metric-server.ts TO KEEP IT UNDER THE 250-LINE CAP, AND THE SEAM IS THE PLUGIN.\n * PVE validates a section against ONE plugin's option set, so these fields mean nothing to a\n * graphite or influxdb section and theirs mean nothing here. This file answers \"what is an OTLP\n * metric server\"; metric-server-form.ts still builds every form, so the clear-list keeps coming\n * from the one table that also builds the body.\n *\n * ⛔ PVE'S OTLP EXPORTER SPEAKS JSON AND NOTHING ELSE. `PVE::Status::OpenTelemetry::send` sets\n * `Content-Type: application/json` (pve-manager PVE/Status/OpenTelemetry.pm:643) and gzips by\n * default; there is no protobuf option. vector's opentelemetry source and VictoriaMetrics both\n * take protobuf only — measured 2026-09-14 — so a server pointed straight at either fails every\n * push. That is why C1's declaration points at each node's own vector bridge\n * (the estate's per-node vector config) rather than at the estate's intake.\n *\n * ⛔ `otel-headers` IS `never`, FOR THE REASON `token` IS. It is where an OTLP bearer token goes —\n * base64 of a JSON object of arbitrary HTTP headers — and Alchemy persists attributes UNENCRYPTED.\n * It is also absent from `optional` in metric-server-form.ts, so an undeclared one is never\n * cleared off a working target on the next plan.\n *\n * ⚠️ `otel-resource-attributes` IS NOT MANAGED AT ALL: not a prop, not read, not compared, not\n * cleared. It is base64 of a JSON object whose key order nothing canonicalises, so comparing it\n * means decoding both sides — work with no consumer yet, since nothing sets it and PVE already\n * stamps `service.name`, `proxmox.cluster` and `proxmox.node` on every request\n * (`_build_otlp_metrics`, lines 207-228). A hand-set one therefore survives every plan and never\n * shows as drift. The day one matters, it goes into `optional` and here together.\n */\nimport { UNSET } from './metric-server-form.ts';\nimport { bool, int, text } from './values.ts';\n\nexport interface MetricServerOtelProps {\n /**\n * ⚠️ PVE'S DEFAULT IS `https`. `http` is for a receiver on the node itself, where the plaintext\n * never leaves the host; anything further away belongs on `https`.\n */\n 'otel-protocol'?: 'http' | 'https';\n /** Unset is PVE's `/v1/metrics`. */\n 'otel-path'?: string;\n /** Unset is PVE's `gzip`, sent as `Content-Encoding: gzip`. */\n 'otel-compression'?: 'none' | 'gzip';\n /**\n * Per-request HTTP timeout, 1-10 seconds; unset is PVE's 5.\n * ⚠️ pvestatd WAITS THIS LONG, PER REQUEST, AGAINST A RECEIVER THAT HANGS — the send is inline in\n * its update loop (`send`, line 635) — so this is also how long a broken receiver can stretch\n * one status cycle.\n */\n 'otel-timeout'?: number;\n /** Minimum 1024; unset is PVE's 10000000. pvestatd splits one flush into requests under it. */\n 'otel-max-body-size'?: number;\n /** ⚠️ An unset `otel-verify-ssl` means VERIFY — PVE's default, and the safe one. */\n 'otel-verify-ssl'?: boolean;\n /** ⛔ `never` ON PURPOSE — see the ⛔ in the header. The compile error is the feature. */\n 'otel-headers'?: never;\n}\n\n/**\n * ⚠️ THE SAME RULES AS THE REST OF `MetricServerAttributes`. The strings report `''` when the\n * section carries none — PVE's default is then what the target uses — the numbers report `UNSET`\n * for the same reason, and `otel-verify-ssl` reports its default directly, because \"absent\"\n * there means the target verifies.\n */\nexport interface MetricServerOtelAttributes {\n 'otel-compression': string;\n 'otel-max-body-size': number;\n 'otel-path': string;\n 'otel-protocol': string;\n 'otel-timeout': number;\n 'otel-verify-ssl': boolean;\n}\n\n/** The `otel-*` half of a live section. Read for every type; compared only for this one. */\nexport const otelAttributes = (live: Record<string, unknown>): MetricServerOtelAttributes => ({\n 'otel-compression': text(live['otel-compression']),\n 'otel-max-body-size': int(live['otel-max-body-size'], UNSET),\n 'otel-path': text(live['otel-path']),\n 'otel-protocol': text(live['otel-protocol']),\n 'otel-timeout': int(live['otel-timeout'], UNSET),\n 'otel-verify-ssl': bool(live['otel-verify-ssl'], true),\n});\n\n/**\n * True when every managed `otel-*` field already says what the declaration says.\n *\n * ⛔ EVERY FIELD `optional` SENDS OR CLEARS IS COMPARED HERE, AND NOTHING ELSE IS. A field sent but\n * not compared is drift a plan can never show; a field compared but not sent is an update the\n * PUT it triggers can never satisfy, reported forever.\n */\nexport const otelMatches = (\n attributes: MetricServerOtelAttributes,\n props: MetricServerOtelProps,\n): boolean =>\n attributes['otel-compression'] === (props['otel-compression'] ?? '') &&\n attributes['otel-max-body-size'] === (props['otel-max-body-size'] ?? UNSET) &&\n attributes['otel-path'] === (props['otel-path'] ?? '') &&\n attributes['otel-protocol'] === (props['otel-protocol'] ?? '') &&\n attributes['otel-timeout'] === (props['otel-timeout'] ?? UNSET) &&\n attributes['otel-verify-ssl'] === (props['otel-verify-ssl'] !== false);\n",
|
|
61
|
-
"/**\n * `Proxmox.NetworkApply` — the call that makes a declared interfaces file real, ON ONE NODE.\n *\n * ⛔ WITHOUT IT EVERY NETWORK DECLARATION IS A GREEN PLAN THAT CHANGES NO NETWORK; WITH IT A GREEN\n * PLAN CAN TAKE A NODE OFF THE NETWORK. Writes under `nodes/{node}/network/{iface}` edit\n * `/etc/network/interfaces.new` and touch no interface. `PUT /nodes/{node}/network` is the apply.\n * MEASURED in the cluster's own source on node-b, 2026-09-13 — `PVE/API2/Network.pm`:\n * rename($new_config_file, $current_config_file) if -e $new_config_file;\n * PVE::Tools::run_command(['ifreload', '-a'], errfunc => $err);\n * return $rpcenv->fork_worker('srvreload', 'networking', $authuser, $worker);\n *\n * ⛔ THIS IS THE MOST DANGEROUS RESOURCE IN THE PACKAGE, AND ON C1 THE DANGER IS NOT ABSTRACT.\n * Ceph runs over `vmbr1.42` on node-b, node-c AND node-d (MEASURED 2026-09-13: four pools, size 3 /\n * min_size 2, mon+mgr+mds on all three). `ifreload -a` takes that node's OSDs, mons and MDS off\n * the cluster network for the length of the reload; two nodes at once puts every pool below\n * min_size and blocks IO for every guest with an RBD disk. The management address the API is\n * reached on, `vmbr0.41`, is reloaded by the same call.\n *\n * ⛔ AND UNLIKE `SdnApply` IT IS PER NODE, SO A HALF-RECONFIGURED CLUSTER IS THE DEFAULT FAILURE\n * MODE, NOT AN EDGE CASE. `PUT /cluster/sdn` publishes the whole cluster in one call; this\n * publishes exactly one node, so a stack holding three of these can apply node-b, fail, and leave node-c\n * and node-d on the old layout with Ceph spanning all three. Four things here exist only for that:\n * 1. `after` SERIALISES THEM — chain node-c after node-b and node-d after node-c, so at most one node is ever\n * mid-reload. Three declared in parallel is the outage, and nothing in Alchemy stops you.\n * 2. `reconcile` REFUSES TO START on a cluster that is already degraded.\n * 3. `reconcile` RE-CHECKS quorum and every member's `online` flag AFTER the reload and dies if\n * the cluster came back worse — which stops the chain before it reaches the second node.\n * 4. NOTHING IS EVER APPLIED WITH NOTHING STAGED. See the first branch of `apply`.\n *\n * ⚠️ IF THE MEMBER SERVING THE POLL IS THE NODE BEING RELOADED, the reload can drop the connection\n * carrying its own status poll — see network-apply-read.ts. Every endpoint is `proxyto =>\n * 'node'`, so any cluster member can serve any other node's paths; failover in client.ts picks\n * whichever member is up, not necessarily one you are not applying.\n *\n * ⚠️ `DELETE /nodes/{node}/network` IS THE REVERT, AND IT IS DELIBERATELY NOT WIRED TO `delete`.\n * MEASURED: the whole body of `revert_network_changes` is `unlink \"/etc/network/interfaces.new\"`.\n * It discards STAGED edits and cannot undo an APPLIED one, so pointing `delete` at it would throw\n * away whatever happened to be staged the moment somebody removed a line from a stack file — the\n * worst possible time to do it. Revert by hand, deliberately: `pvesh delete /nodes/<node>/network`.\n *\n * ⚠️ PRIVILEGES, so widening stays a deliberate act: the `read` role needs nothing for the pending\n * read (`GET /nodes/{node}/network` is `\"user\": \"all\"`) and `Sys.Audit` on `/` for the health\n * gate; the `provision` role needs `Sys.Modify` on `/nodes/{node}` for the apply, and owns the\n * task it starts so it can poll it without `Sys.Audit`. ⛔ `Sys.Modify` ON A NODE IS NOT A SMALL\n * GRANT — it also carries that node's DNS, hosts file, time and service configuration. Granting\n * it cluster-wide to run this resource widens the provisioning role well beyond networking.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { pveWith } from './client.ts';\nimport { mint } from './credentials.ts';\nimport { awaitTask, degradedReason, pendingCount } from './network-apply-read.ts';\nimport type { PveRequirements } from './resource.ts';\nimport type { WithTarget } from './resource.ts';\n\nexport interface NetworkApplyProps extends WithTarget {\n /**\n * The node whose staged interfaces file this applies — `node-b`.\n *\n * ⚠️ ONE APPLY PER NODE, AND NEVER TWO FOR THE SAME NODE. Two would each publish the other's\n * staged half, and both would run `ifreload` on a file the other had not finished writing.\n */\n node: string;\n\n /**\n * The staged interface changes this apply publishes, and the apply that must go before it.\n *\n * ⚠️ IT IS NEVER READ, AND IT IS NOT DECORATION — the same contract as `SdnApply.after`. Alchemy\n * orders resources by DATA FLOW, so consuming an attribute is the only way to say \"after\".\n * Pass the interfaces this node declares AND the previous node's apply:\n * `after: [vmbr1.iface, n2Apply.pending]`. Leave the previous apply out and the three nodes\n * reload in parallel, which is the one thing this resource is built to prevent.\n */\n after?: readonly unknown[];\n}\n\nexport interface NetworkApplyAttributes {\n /** The node this applies. Identity, echoed so a plan line says which member it will reload. */\n node: string;\n\n /**\n * Changed lines still staged on this node.\n *\n * ⚠️ LINES OF A UNIFIED DIFF, NOT INTERFACES, and the number is reported rather than trusted —\n * `pendingLines` explains why it can undercount by one and never to zero. Zero is the only\n * settled value: anything above it means the running config and the staged config disagree,\n * whether this stack staged it or somebody edited the node in the UI and walked away.\n *\n * ⛔ THE DIFF ITSELF IS NEVER AN ATTRIBUTE. Alchemy writes attributes to its state store\n * UNENCRYPTED, and `/etc/network/interfaces` can carry a `wpa-psk` or a `pre-up` command. Only\n * the count leaves `network-apply-read.ts`.\n *\n * ⚠️ AND NOTHING OUT OF THE INTERFACE ROWS IS AN ATTRIBUTE EITHER, WHICH IS WHAT KEEPS THIS\n * FAMILY NOOP. `GET /nodes/{node}/network` returns eighteen fields it will not accept on write\n * — MEASURED by differencing the schema's GET return properties against its POST parameters:\n * `active`, `exists`, `families`, `method`, `method6`, `options`, `priority`, `link-type` and\n * ten more; `altnames` is returned and is not even in the schema. `priority` is the worst of\n * them: PVE ASSIGNS it from the order of the interfaces file, so the same logical layout\n * carries different numbers on different nodes (MEASURED: node-d's differ from node-b/node-c's by one).\n * Every one of those is a forever-diff waiting for whoever writes `Proxmox.NetworkInterface`.\n * This resource compares a single integer against zero and never looks at a row at all.\n */\n pending: number;\n}\n\nexport interface ProxmoxNetworkApply extends Resource<\n 'Proxmox.NetworkApply',\n NetworkApplyProps,\n NetworkApplyAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxNetworkApply = Resource<ProxmoxNetworkApply>('Proxmox.NetworkApply');\n\nconst read = (props: NetworkApplyProps) =>\n pendingCount(props.target, props.node).pipe(\n Effect.map((pending) => ({ node: props.node, pending })),\n );\n\n/**\n * ⚠️ THE DIFF IS ABOUT THE NODE, NOT ABOUT THE PROPS — the same reasoning as `SdnApply`. This\n * resource has no settable field (`node` is identity, `after` is an ordering edge), so comparing\n * props to props would report `noop` forever and the apply would run exactly once, ever. What\n * decides it is whether the node is carrying a staged change right now.\n *\n * ★ THIS IS ALSO WHY DECLARING THE LIVE CLUSTER PLANS AS `noop`. VERIFIED 2026-09-13:\n * `/etc/network/interfaces.new` is absent on node-b, node-c and node-d, so `changes` is absent from all three\n * answers, so `pending` is 0 on all three and every one of them plans `noop`.\n */\nconst diff = (news: Input<NetworkApplyProps>) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n const { pending } = yield* read(news);\n return pending === 0 ? ({ action: 'noop' } as const) : ({ action: 'update' } as const);\n });\n\n/** Stop the deploy, loudly, with the node in the message. ⚠️ A die here is the SAFE outcome. */\nconst refuse = (node: string, why: string) =>\n Effect.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));\n\nconst apply = (props: NetworkApplyProps) =>\n Effect.gen(function* () {\n const node = props.node;\n const staged = yield* pendingCount(props.target, node);\n /**\n * ⛔ NOTHING STAGED MEANS NOTHING TO APPLY, AND THIS BRANCH IS A SAFETY PROPERTY RATHER THAN AN\n * OPTIMISATION. `reconcile` also runs on CREATE — the first time this resource appears in a\n * stack, before `diff` has ever been consulted — and the PVE worker runs `ifreload -a`\n * UNCONDITIONALLY, staged file or not. Without this branch, adding `Proxmox.NetworkApply` to\n * a stack describing the cluster as it already is would reload networking on every node it\n * names, for nothing. Adoption must cost nothing; that is true here in the strongest sense.\n */\n if (staged === 0) return { node, pending: 0 };\n\n const before = yield* degradedReason(props.target);\n if (before !== undefined) {\n return yield* refuse(\n node,\n `refusing to reload networking on a cluster that is already degraded (${before}). ` +\n 'Ceph spans these nodes -- fix the cluster first, then re-run the deploy.',\n );\n }\n\n /**\n * ⚠️ ONE LEASE FOR BOTH CALLS, VIA `pveWith` RATHER THAN `pve`. Each mint returns a NEW token\n * id, and the task-status endpoint only skips its `Sys.Audit` check for the task's OWNER —\n * so polling with a fresh mint is a different identity and needs a privilege this role does\n * not have. The `provision` lease is 300s and non-renewable, which is far longer than a\n * reload and is the reason no renew path is reached for.\n */\n const credential = yield* mint(props.target, 'provision');\n const upid = yield* pveWith<string>(props.target, credential, 'PUT', `nodes/${node}/network`);\n if (upid === undefined) {\n return yield* refuse(\n node,\n 'the apply returned no UPID. PVE wraps every answer in {\"data\":...} and can report success ' +\n 'on a call that did nothing -- the reload cannot be confirmed, so it is not claimed.',\n );\n }\n\n /**\n * ⛔ POLLING `pending` INSTEAD OF THE TASK WOULD REPORT SUCCESS BEFORE THE NETWORK WAS TOUCHED.\n * MEASURED in the worker above: the `rename` happens FIRST and `ifreload -a` second, so the\n * staged file — and therefore `changes`, and therefore `pending` — disappears while the\n * reload has not yet started. A provider that watched the count would call a reload that had\n * not run, and a reload that then FAILED, a success. The task's exitstatus is the only\n * honest answer.\n */\n const exitstatus = yield* awaitTask(props.target, credential, node, upid);\n if (exitstatus !== 'OK') {\n return yield* refuse(\n node,\n `the reload ended \"${exitstatus}\" (${upid}). Read it from ANOTHER node -- ` +\n `\\`pvesh get /nodes/${node}/tasks/${upid}/log\\` -- and do not apply the next node until ` +\n 'this one is understood.',\n );\n }\n\n const after = yield* degradedReason(props.target);\n if (after !== undefined) {\n return yield* refuse(\n node,\n `the reload reported OK but the cluster is now degraded (${after}). The next ` +\n 'node has NOT been applied. Restore this one before continuing -- Ceph runs over these ' +\n 'links and a second reload now would take pools below min_size.',\n );\n }\n\n const left = yield* pendingCount(props.target, node);\n if (left !== 0) {\n return yield* refuse(\n node,\n `the reload reported OK but ${String(left)} staged line(s) remain. The running config does ` +\n 'not match the declared one, and saying otherwise is the lie this resource exists to stop.',\n );\n }\n return { node, pending: left };\n });\n\n/**\n * ⛔ HAND-WRITTEN RATHER THAN `pveHandlers`, FOR THE SAME REASON `sdn-apply.ts` IS. The factory's\n * spec is create/update/delete over ONE object at ONE path, and an apply is not an object: there\n * is nothing to POST, nothing to DELETE, and `matches` would compare a declaration to itself and\n * answer `noop` forever, so the apply would run exactly once in the life of the stack. `list` is\n * empty for the reason it is everywhere in this package — adoption stays an explicit act.\n *\n * ⛔ `delete` APPLIES NOTHING AND UNDOES NOTHING, AND THAT IS CORRECT. There is no un-apply: the\n * config is already live on the node, and the way to remove an interface is to delete it and\n * apply AGAIN. See the ⚠️ about `DELETE /nodes/{node}/network` at the top of this file.\n */\nconst handlers = {\n delete: () => Effect.void,\n diff: ({ news }: { news: Input<NetworkApplyProps> }) => diff(news),\n list: () => Effect.succeed([]),\n read: ({ olds }: { olds: NetworkApplyProps }) => read(olds),\n reconcile: ({ news }: { news: NetworkApplyProps }) => apply(news),\n};\n\nexport const ProxmoxNetworkApplyProvider = () =>\n Provider.effect(ProxmoxNetworkApply, Effect.succeed(ProxmoxNetworkApply.Provider.of(handlers)));\n",
|
|
62
|
-
"/**\n * `Proxmox.NodeNetwork` — one stanza of a node's `/etc/network/interfaces`, declared.\n *\n * ⛔ DECLARING THIS RESOURCE IS SAFE. APPLYING IT IS A SEPARATE, DELIBERATE ACT, AND IT IS NOT\n * PERFORMED HERE. Every write under `/nodes/{node}/network/{iface}` lands in\n * `/etc/network/interfaces.new` and changes no running interface. MEASURED, from PVE::INotify:\n * `$shadowfiles = { '/etc/network/interfaces' => '/etc/network/interfaces.new' }`, and\n * `read_file` reads the SHADOW when it exists. The apply is `PUT /nodes/{node}/network`, which\n * renames the shadow over the real file and runs `ifreload -a`; the revert is\n * `DELETE /nodes/{node}/network`, which simply unlinks the shadow. A sibling resource models\n * that apply — this one never calls it, exactly as `Proxmox.SdnZone` never calls\n * `Proxmox.SdnApply`.\n *\n * ⛔ CEPH ON C1 RIDES TWO NETWORKS AND THIS API ONLY SEES ONE OF THEM. Measured from\n * `/etc/pve/ceph.conf` on 2026-09-13:\n *\n * public_network = 198.51.100.0/24 -> vmbr1.42, which this resource DOES see\n * cluster_network = 203.0.113.0/24 -> every OSD's cluster_addr: .102 / .103 / .104\n *\n * The cluster network — OSD REPLICATION, the traffic that rebuilds a lost replica — lives on\n * `203.0.113.10X/32` addresses carried by `tb0`, `tb1` and `dummy_c1`: the Thunderbolt mesh.\n *\n * ⛔ AND THOSE ADDRESSES ARE INVISIBLE HERE, WHICH IS THE TRAP. They are declared in\n * `/etc/network/interfaces.d/sdn`, and PVE's network API does not parse that directory.\n * MEASURED: `GET /nodes/node-b/network/tb0` answers `{\"method\":\"manual\",\"type\":\"eth\",…}` with NO\n * address, while `ip -br -4 addr` shows `tb0 UP 203.0.113.102/32`. So this resource reports\n * tb0 and tb1 as address-less manual ports and will plan `noop` over them — implicitly\n * asserting they carry no address, which is FALSE.\n *\n * The consequence, stated plainly: declaring tb0/tb1 from what this API reports and then\n * DESTROYING or applying over them takes out OSD replication on a cluster whose pools are\n * size 3 / min_size 2. They read as the most boring interfaces on the node and are the two most\n * dangerous. Leave them undeclared; if they must be managed, manage the file that actually\n * owns them, not this resource.\n *\n * ⚠️ Two ways this file limits the damage on the interfaces it CAN see: `cidr` is compared\n * unconditionally (the ⛔ on `matches`), and a live interface whose `type` disagrees with the\n * declaration is reported ABSENT rather than retyped (the ⛔ in `attributes`).\n *\n * ★ THE REASON IT EXISTS IS THAT THE STAGED READ CANNOT TELL YOU THE NETWORK IS RIGHT — it can\n * only tell you the FILE is. Once anything is staged, `GET /nodes/{node}/network/{iface}`\n * answers from `interfaces.new`, so `matches` compares a declaration against a pending file and\n * reports `noop` over a node whose running config has not moved. The apply resource is what\n * closes that gap; this resource's `noop` means \"the file says what you said\", no more.\n *\n * ⚠️ PRIVILEGES ARE COARSER HERE THAN ANYWHERE ELSE IN THE PACKAGE: reads want `Sys.Audit` on\n * `/nodes/{node}`, every write `Sys.Modify` on it — and there is no `/nodes/{node}/network` ACL\n * object to scope to, so that also buys the node's services, certificates, DNS and time.\n * Collected with every other family in docs/privileges.md.\n *\n * ★ A RECONCILE OVER A MATCHING INTERFACE IS A NO-WRITE, AND AN EARLIER DRAFT OF THIS COMMENT\n * SAID THE OPPOSITE. `pveOperations.reconcile` gates on `matches` BEFORE the PUT\n * (`resource.ts`: `else if (spec.updateForm !== undefined && !spec.matches(live, news))`), and\n * the ⛔ above that line names THIS family as the reason the guard was added — adopting an\n * interface a node already has would otherwise leave it holding a pending network change.\n *\n * ⚠️ WHAT IS STILL TRUE: when `matches` is false the PUT rewrites the whole stanza, and that can\n * ADD lines the file did not have — a `vlan-raw-device vmbr1` under `vmbr1.42`, where PVE had\n * been deriving it from the name. Harmless in content, and it costs nothing until somebody\n * applies; one more reason the apply is a separate resource.\n *\n * ⚠️ `list` IS EMPTY, AND HERE THAT MATTERS MORE THAN ANYWHERE ELSE IN THIS PACKAGE. The index\n * returns every interface the node has, `vmbr0`, `bond0` and the Ceph vlan included. Adopting\n * them would put Alchemy one `destroy` away from staging the removal of a node's uplink — and\n * `delete_network` has NO in-use check at all: MEASURED, it deletes the hash entry and writes\n * the file. Adoption stays explicit.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n body,\n readAttributes,\n same,\n sameComment,\n sameList,\n updateBody,\n} from './node-network-form.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\n\n/**\n * PVE's interface types. `unknown` is what a NIC it does not manage reports — node-b and node-c both\n * report `wlan0` that way, and node-d has no such interface at all.\n *\n * ⚠️ THAT ASYMMETRY IS A TRAP FOR A DECLARATION SHARED ACROSS NODES. On node-d the read answers 404,\n * which this provider cannot tell from \"deleted\", so reconcile POSTs — writing a stanza for a\n * card that is not in the machine. Declare per node what each node actually has; a physical\n * interface is discovered, not decided.\n */\nexport type NodeNetworkType =\n | 'OVSBond'\n | 'OVSBridge'\n | 'OVSIntPort'\n | 'OVSPort'\n | 'alias'\n | 'bond'\n | 'bridge'\n | 'eth'\n | 'fabric'\n | 'unknown'\n | 'vlan'\n | 'vnet';\n\nexport interface NodeNetworkProps extends WithTarget {\n /** Which node's file this stanza lives in. Interfaces are per node, never cluster-wide. */\n node: string;\n /** `vmbr0`, `bond0`, `vmbr1.42`. 2-20 characters, PVE's `pve-iface` format. */\n iface: string;\n /** ⛔ REQUIRED ON EVERY WRITE, update included, never used to retype — see `readAttributes`. */\n type: NodeNetworkType;\n /**\n * `198.51.100.12/24`. ⛔ THE ONE FIELD WITH NO UNMANAGED MODE: leaving it out is an instruction\n * to make the interface `manual`, not an instruction to leave its address alone. The ⛔ on\n * `updateBody` in node-network-form.ts has the measurement and the consequence.\n */\n cidr?: string;\n /** ⚠️ PVE allows exactly ONE default gateway per node and refuses a second with \"Default\n * gateway already exists on interface '<other>'\". On C1 it is vmbr0.41's. */\n gateway?: string;\n /**\n * `auto <iface>` in the file. Absent on read means off, which is why `bool`'s fallback is used.\n * ⚠️ UNDECLARED IS UNMANAGED ON AN UPDATE AND OFF ON A CREATE — the same asymmetry storage.ts\n * has, and it bites harder here: an interface created without `autostart` is one the node\n * will not bring up at boot. Every C1 bridge, bond and vlan carries it.\n */\n autostart?: boolean;\n /** 1280-65520. Unset leaves the file without an `mtu` line and the kernel default in force. */\n mtu?: number;\n /** ⚠️ Round-trips only after normalisation — see `comment` in node-network-form.ts. */\n comments?: string;\n /** Space-separated, a SET: `enp87s0`, or `bond0`. ⚠️ PVE refuses a port already used elsewhere. */\n bridge_ports?: string;\n /** `2-4094`, or `2 100-200`. Only written when `bridge_vlan_aware` is on. */\n bridge_vids?: string;\n /** ⛔ A `false` here is sent as `delete=`, never as `0` — the ⛔ on `body` says why. */\n bridge_vlan_aware?: boolean;\n /**\n * A bond's members, space separated. ⚠️ THE PARAMETER IS `slaves`, NOT `bond_slaves`, in BOTH\n * directions on this PVE: MEASURED, the POST/PUT schema names only `slaves` and the GET returns\n * `\"slaves\":\"enp2s0f0np0 enp2s0f1np1\"`. `bond_slaves` appears nowhere in Network.pm here.\n */\n slaves?: string;\n bond_mode?: string;\n /** ⚠️ Only written when `bond_mode` is `balance-xor` or `802.3ad`; ignored otherwise. */\n bond_xmit_hash_policy?: string;\n /** active-backup only. Kept hyphenated because that is the wire name. */\n 'bond-primary'?: string;\n /** ⚠️ DERIVED FROM A DOTTED NAME. `vmbr1.42` reports `vlan-id` 42 with no such line in the file;\n * declaring it there is harmless but adds a line the file did not have. */\n 'vlan-id'?: number;\n 'vlan-raw-device'?: string;\n}\n\n/**\n * ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED — each is a MEASURED forever-diff, and the\n * evidence for every one of them is in node-network-form.ts, beside the code that reads them.\n */\nexport interface NodeNetworkAttributes {\n node: string;\n iface: string;\n type: string;\n cidr: string;\n gateway: string;\n autostart: boolean;\n mtu: number;\n comments: string;\n bridge_ports: string;\n bridge_vids: string;\n bridge_vlan_aware: boolean;\n slaves: string;\n bond_mode: string;\n bond_xmit_hash_policy: string;\n 'bond-primary': string;\n 'vlan-id': number;\n 'vlan-raw-device': string;\n priority: number;\n method: string;\n families: string;\n active: boolean;\n exists: boolean;\n bond_miimon: string;\n bridge_stp: string;\n bridge_fd: string;\n}\n\nexport interface ProxmoxNodeNetwork extends Resource<\n 'Proxmox.NodeNetwork',\n NodeNetworkProps,\n NodeNetworkAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a node's only uplink cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxNodeNetwork = Resource<ProxmoxNodeNetwork>('Proxmox.NodeNetwork', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst handlers = pveHandlers<NodeNetworkProps, NodeNetworkAttributes>({\n attributes: readAttributes,\n collection: (props) => `nodes/${props.node}/network`,\n /** ⚠️ No `delete` parameter on a POST — see the ⚠️ on `updateBody`. */\n createForm: body,\n /**\n * ⛔ `cidr` IS THE ONLY FIELD COMPARED WHEN UNDECLARED, AND THAT ASYMMETRY IS THE POINT. Every\n * other field here follows storage.ts: undeclared means unmanaged, so it is neither sent nor\n * compared and a plan cannot report an update no write could satisfy. `cidr` cannot be\n * unmanaged, because PVE derives `method` from the presence of an address IN THE FORM — an\n * omission is an edit. Comparing it unconditionally is what makes that edit visible in `plan`\n * instead of at `ifreload` time.\n *\n * ⚠️ NOTHING IN THE REPORTED-ONLY BLOCK IS HERE, and that is what makes the C1 declaration\n * plan as `noop` on all three nodes at once — the ⛔ above `NodeNetworkAttributes` lists them\n * and the measurement behind each.\n */\n matches: (attributes, props) =>\n attributes.cidr === (props.cidr ?? '') &&\n same(props.gateway, attributes.gateway) &&\n same(props.autostart, attributes.autostart) &&\n same(props.mtu, attributes.mtu) &&\n sameComment(props.comments, attributes.comments) &&\n sameList(props.bridge_ports, attributes.bridge_ports) &&\n sameList(props.bridge_vids, attributes.bridge_vids) &&\n same(props.bridge_vlan_aware, attributes.bridge_vlan_aware) &&\n sameList(props.slaves, attributes.slaves) &&\n same(props.bond_mode, attributes.bond_mode) &&\n same(props.bond_xmit_hash_policy, attributes.bond_xmit_hash_policy) &&\n same(props['bond-primary'], attributes['bond-primary']) &&\n same(props['vlan-id'], attributes['vlan-id']) &&\n same(props['vlan-raw-device'], attributes['vlan-raw-device']),\n path: (props) => `nodes/${props.node}/network/${props.iface}`,\n updateForm: updateBody,\n});\n\n/**\n * ⚠️ THE DESTROY IS STAGED LIKE EVERY OTHER WRITE, WHICH MAKES IT THE ONE RECOVERABLE DESTROY IN\n * THIS PACKAGE. It removes the stanza from `interfaces.new` and nothing else;\n * `DELETE /nodes/{node}/network` puts the node back by unlinking that file. It becomes\n * irreversible the moment somebody applies — and PVE performs NO in-use check before removing\n * an interface other bridges or Ceph still stand on.\n */\nexport const ProxmoxNodeNetworkProvider = () =>\n Provider.effect(ProxmoxNodeNetwork, Effect.succeed(ProxmoxNodeNetwork.Provider.of(handlers)));\n",
|
|
63
|
-
"/**\n * How a node interface crosses the wire in both directions — props to a PVE form, a PVE answer to\n * a comparable attribute — and when two of those values count as the same value.\n *\n * ★ SPLIT OUT OF node-network.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is the\n * same one metric-server-form.ts uses, one field wider: this file owns the COERCIONS, in both\n * directions, because a read that normalises differently from the write that produced it is\n * exactly how a forever-diff is born — keeping `ifaceList` next to both ends is what stops that.\n * node-network.ts answers \"what is an interface, and which of its fields are safe to compare at\n * all\". Nothing here calls the cluster.\n *\n * ⚠️ THE `import type` BACK TO node-network.ts IS A CYCLE ON PAPER ONLY — type-only, erased before\n * anything runs, so `NodeNetworkProps` stays in the file that declares the resource.\n *\n * * ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED, AND EACH ONE IS A MEASURED FOREVER-DIFF.\n * `priority` is assigned by PVE FROM FILE ORDER — `my $priority = 2; ... $d->{priority} =\n * $priority++` — and it is not a POST or PUT parameter at all. MEASURED ACROSS C1: vmbr1.42 is\n * priority 16 on node-b and node-c and 15 on node-d, because node-d has no `wlan0` above it. One declaration\n * reused across three nodes would diff on exactly one of them, forever, over nothing.\n * `method` is RECOMPUTED on every write from whether the form carried an address, `families` is\n * recomputed the same way and never written to the file, `active` and `exists` describe the\n * kernel rather than the config, and `bond_miimon`, `bridge_stp` and `bridge_fd` are emitted by\n * PVE's writer with defaults (100, `off`, 0) while appearing in NEITHER write schema —\n * `additionalProperties => 0` means sending one is a 400. Live proof of all three: node-b's bond0\n * returns `bond_miimon \"100\"` and vmbr1 returns `bridge_stp \"off\"`, `bridge_fd \"0\"`, none of\n * which any declaration can set.\n */\nimport type { NodeNetworkAttributes, NodeNetworkProps } from './node-network.ts';\nimport { bool, flag, int, text, withClears } from './values.ts';\n\n/**\n * \"Not set\", for the three integers this family reports.\n *\n * ⚠️ 0 CANNOT BE THE ABSENT-MARKER AND -1 CAN. `mtu` is 1280-65520, `vlan-id` 1-4094 and PVE's\n * `priority` starts at 1 (1 is reserved for `lo`), so -1 is outside every one of their ranges\n * while 0 is merely outside today's. metric-server-form.ts keeps its own constant for the same\n * reason and against different ranges; one shared UNSET would have to be right for both.\n */\nexport const UNSET = -1;\n\n/**\n * A space-separated interface list, flattened to one comparable form.\n *\n * ⚠️ `bridge_ports` AND `slaves` ARE SETS, AND PVE NORMALISES THEIR SEPARATORS BUT NOT THEIR ORDER.\n * MEASURED in PVE::Network::Interfaces: the writer does `s/[;,\\s]+/ /g` on both before emitting\n * them, and the reader hands the resulting string straight back — so `enp2s0f1np1 enp2s0f0np0`\n * survives a round trip in the order it was written. Order carries no meaning for a bridge's\n * ports or a bond's slaves, so a declaration that lists them the other way round is the SAME\n * declaration; comparing raw strings would report an update that rewrites the file to say\n * exactly what it already said. Both sides are sorted here and only then compared.\n *\n * ⚠️ IT IS NOT `csv` FROM values.ts. That one joins with commas, and a comma in\n * `/etc/network/interfaces` is not a separator — PVE's own comment above the writer says the\n * list \"must be space separated! other separators will cause problems\". Sending a comma-joined\n * value would be accepted by `split_list` and then written back as spaces, which is a second\n * spelling of one value and therefore a second chance to diff.\n *\n * ⚠️ `bridge_vids` GOES THROUGH THE SAME FUNNEL and its sort is LEXICAL, which is correct only\n * because a vid list is a set: `2-4094` is one token, and `100-200 2` and `2 100-200` allow the\n * same VLANs. Nothing here should be reused for a list whose order is meaning.\n */\nexport const ifaceList = (value: unknown) =>\n text(value)\n .split(/[\\s,;]+/)\n .filter((part) => part !== '')\n .sort()\n .join(' ');\n\n/**\n * A comment off the wire, with the newline PVE adds taken back off.\n *\n * ⛔ `comments` NEVER ROUND-TRIPS RAW, AND THAT IS A FOREVER-DIFF IF IT IS NOT NORMALISED.\n * MEASURED in the reader: each `#...` line is appended as `$comment . \"\\n\"`, so a declared\n * `ceph transport` comes back as `ceph transport\\n` and the two never compare equal.\n * ⚠️ AND THE READER FOLDS `comments6` INTO `comments` (`$d->{comments} .= $comments6`), so on an\n * interface that carries an IPv6 comment a declared `comments` CANNOT match what comes back.\n * C1 has none; on an interface that does, leave `comments` undeclared rather than fighting it.\n */\nexport const comment = (value: unknown) => text(value).replace(/\\s+$/, '');\n\n/** Undeclared is unmanaged: neither sent nor compared. See the ⚠️ on `body`. */\nexport const same = <T>(declared: T | undefined, live: T) =>\n declared === undefined || declared === live;\n\nexport const sameList = (declared: string | undefined, live: string) =>\n declared === undefined || ifaceList(declared) === live;\n\nexport const sameComment = (declared: string | undefined, live: string) =>\n declared === undefined || comment(declared) === live;\n\nconst field = (name: string, value: string | undefined): Record<string, string> =>\n value === undefined ? {} : { [name]: value };\n\n/**\n * The fields sent on EVERY write, create and update alike.\n *\n * ⚠️ UNDECLARED IS UNMANAGED — not sent, and not compared either. PVE's PUT MERGES the form into\n * the existing stanza (`foreach my $k (keys %$param) { $ifaces->{$iface}->{$k} = $param->{$k} }`),\n * so an omitted parameter is left exactly as it was. `cidr` is the one exception and it is not\n * a small one — see the ⛔ in node-network.ts.\n *\n * ⛔ `type` IS REQUIRED ON THE PUT, NOT ONLY ON THE POST. It carries no `optional` in either\n * schema, and the merge above writes it straight into the stanza — so a PUT naming the wrong\n * type would RETYPE A LIVE INTERFACE in the file and the writer would then emit a bridge stanza\n * for what is really a vlan. node-network.ts refuses that case before it can reach here.\n *\n * ⛔ `bridge_vlan_aware` IS SENT ONLY WHEN TRUE, AND A FALSE ONE IS A `delete=`. MEASURED in the\n * writer: the test is `if (defined($d->{bridge_vlan_aware}))`, not a truth test, so `0` is\n * defined and PVE writes `bridge-vlan-aware yes` anyway. Sending `0` would therefore turn the\n * flag ON, read back as `1`, and diff forever while lying about which way it went.\n *\n * ⚠️ `netmask` IS A WRITABLE PARAMETER AND IS DELIBERATELY NOT OFFERED. PVE raises\n * \"netmask conflicts with cidr\" when both are sent, and the reader rewrites whatever was written\n * into prefix form — MEASURED: node-b's vmbr1.42 reports `\"netmask\":\"24\"`, never `255.255.255.0`.\n * A resource accepting both spellings would let a declaration diff against its own value.\n */\nexport const body = (props: NodeNetworkProps): Record<string, string> => ({\n ...field('autostart', flag(props.autostart)),\n ...field('bond-primary', props['bond-primary']),\n ...field('bond_mode', props.bond_mode),\n ...field('bond_xmit_hash_policy', props.bond_xmit_hash_policy),\n ...field('bridge_ports', props.bridge_ports),\n ...field('bridge_vids', props.bridge_vids),\n ...field('bridge_vlan_aware', props.bridge_vlan_aware === true ? '1' : undefined),\n ...field('cidr', props.cidr),\n ...field('comments', props.comments),\n ...field('gateway', props.gateway),\n ...field('mtu', props.mtu === undefined ? undefined : String(props.mtu)),\n ...field('slaves', props.slaves),\n ...field('vlan-id', props['vlan-id'] === undefined ? undefined : String(props['vlan-id'])),\n ...field('vlan-raw-device', props['vlan-raw-device']),\n type: props.type,\n});\n\n/**\n * The update form: `body` plus the two fields that can only be cleared explicitly.\n *\n * ⛔ `delete=cidr` IS SENT WHENEVER `cidr` IS UNDECLARED, AND IT IS THE LESSER OF TWO EVILS RATHER\n * THAN A GOOD OUTCOME. PVE recomputes the method on every write from the form alone —\n * `$param->{method} = $param->{address} ? 'static' : 'manual'` — so a PUT WITHOUT an address\n * makes the interface manual no matter what the caller intended. Left at that, the merge would\n * leave the old `address` and `netmask` in the hash and the writer would emit an `address` line\n * under `iface … inet manual`: a stanza PVE's own UI cannot produce, whose behaviour under\n * `ifreload -a` I did NOT measure. Clearing cidr explicitly at least produces a clean, honest\n * manual interface — and `matches` compares `cidr` UNCONDITIONALLY, so the plan says so first.\n *\n * ⛔ ON vmbr1.42 THAT IS THE CEPH TRANSPORT. A declaration of that interface without its `cidr` is\n * a declaration that it should have no address, applied across three nodes. The plan will read\n * `1 to update` rather than `noop`; do not wave it through.\n *\n * ⚠️ `delete` IS A PUT-ONLY PARAMETER. The POST schema is `additionalProperties => 0` and has no\n * `delete`, so sending it on a create is a 400 — which is why `createForm` calls `body` directly.\n */\nexport const updateBody = (props: NodeNetworkProps): Record<string, string> => {\n const clear = [\n ...(props.cidr === undefined ? ['cidr'] : []),\n ...(props.bridge_vlan_aware === false ? ['bridge_vlan_aware'] : []),\n ];\n const fields = body(props);\n return withClears(fields, clear);\n};\n\n/**\n * One live interface as attributes, or `undefined` — \"this is not really there\".\n *\n * ⚠️ EVERY FIELD PVE REPORTS IS CARRIED HERE, INCLUDING THE SEVEN NO DECLARATION CAN SET. A plan\n * that cannot show `priority`, `method` or `bond_miimon` cannot explain why it is ignoring them,\n * and the next person to read a forever-diff would start by adding them to `matches`. They are\n * reported precisely so that they are visibly OUT of it — see the ⛔ above the attribute type.\n */\nexport const readAttributes = (\n live: Record<string, unknown>,\n props: NodeNetworkProps,\n): NodeNetworkAttributes | undefined => {\n /**\n * ⛔ AN INTERFACE OF ANOTHER TYPE IS ANOTHER OBJECT, AND \"ABSENT\" IS THE SAFE ANSWER — the\n * same call metric-server.ts makes, for a worse reason. `type` is required on the PUT and\n * the PUT MERGES, so a declaration naming `bridge` for what is really a vlan would write\n * `type bridge` into the stanza and PVE's writer would then emit bridge-ports and\n * bridge-stp lines for vmbr1.42. Reporting absent instead makes reconcile POST a create,\n * which PVE refuses with \"interface already exists\": loud, and it changes nothing.\n */\n const liveType = text(live['type']);\n if (liveType !== '' && liveType !== props.type) return undefined;\n return {\n active: bool(live['active']),\n autostart: bool(live['autostart']),\n 'bond-primary': text(live['bond-primary']),\n bond_miimon: text(live['bond_miimon']),\n bond_mode: text(live['bond_mode']),\n bond_xmit_hash_policy: text(live['bond_xmit_hash_policy']),\n bridge_fd: text(live['bridge_fd']),\n bridge_ports: ifaceList(live['bridge_ports']),\n bridge_stp: text(live['bridge_stp']),\n bridge_vids: ifaceList(live['bridge_vids']),\n bridge_vlan_aware: bool(live['bridge_vlan_aware']),\n /** ⚠️ DERIVED BY THE READER from address+netmask, and always present when an address is. */\n cidr: text(live['cidr']),\n comments: comment(live['comments']),\n exists: bool(live['exists']),\n families: Array.isArray(live['families']) ? live['families'].join(',') : '',\n gateway: text(live['gateway']),\n iface: props.iface,\n method: text(live['method']),\n mtu: int(live['mtu'], UNSET),\n node: props.node,\n priority: int(live['priority'], UNSET),\n slaves: ifaceList(live['slaves']),\n type: liveType === '' ? props.type : liveType,\n 'vlan-id': int(live['vlan-id'], UNSET),\n 'vlan-raw-device': text(live['vlan-raw-device']),\n };\n};\n",
|
|
74
|
+
"/**\n * `Proxmox.NetworkApply` — the call that makes a declared interfaces file real, ON ONE NODE.\n *\n * ⛔ WITHOUT IT EVERY NETWORK DECLARATION IS A GREEN PLAN THAT CHANGES NO NETWORK; WITH IT A GREEN\n * PLAN CAN TAKE A NODE OFF THE NETWORK. Writes under `nodes/{node}/network/{iface}` edit\n * `/etc/network/interfaces.new` and touch no interface. `PUT /nodes/{node}/network` is the apply.\n * MEASURED in the cluster's own source on node-b, 2026-09-13 — `PVE/API2/Network.pm`:\n * rename($new_config_file, $current_config_file) if -e $new_config_file;\n * PVE::Tools::run_command(['ifreload', '-a'], errfunc => $err);\n * return $rpcenv->fork_worker('srvreload', 'networking', $authuser, $worker);\n *\n * ⛔ THIS IS THE MOST DANGEROUS RESOURCE IN THE PACKAGE, AND ON C1 THE DANGER IS NOT ABSTRACT.\n * Ceph runs over `vmbr1.42` on node-b, node-c AND node-d (MEASURED 2026-09-13: four pools, size 3 /\n * min_size 2, mon+mgr+mds on all three). `ifreload -a` takes that node's OSDs, mons and MDS off\n * the cluster network for the length of the reload; two nodes at once puts every pool below\n * min_size and blocks IO for every guest with an RBD disk. The management address the API is\n * reached on, `vmbr0.41`, is reloaded by the same call.\n *\n * ⛔ AND UNLIKE `SdnApply` IT IS PER NODE, SO A HALF-RECONFIGURED CLUSTER IS THE DEFAULT FAILURE\n * MODE, NOT AN EDGE CASE. `PUT /cluster/sdn` publishes the whole cluster in one call; this\n * publishes exactly one node, so a stack holding three of these can apply node-b, fail, and leave node-c\n * and node-d on the old layout with Ceph spanning all three. Four things here exist only for that:\n * 1. `after` SERIALISES THEM — chain node-c after node-b and node-d after node-c, so at most one node is ever\n * mid-reload. Three declared in parallel is the outage, and nothing in Alchemy stops you.\n * 2. `reconcile` REFUSES TO START on a cluster that is already degraded.\n * 3. `reconcile` RE-CHECKS quorum and every member's `online` flag AFTER the reload and dies if\n * the cluster came back worse — which stops the chain before it reaches the second node.\n * 4. NOTHING IS EVER APPLIED WITH NOTHING STAGED. See the first branch of `apply`.\n *\n * ⚠️ IF THE MEMBER SERVING THE POLL IS THE NODE BEING RELOADED, the reload can drop the connection\n * carrying its own status poll — see network-apply-read.ts. Every endpoint is `proxyto =>\n * 'node'`, so any cluster member can serve any other node's paths; failover in client.ts picks\n * whichever member is up, not necessarily one you are not applying.\n *\n * ⚠️ `DELETE /nodes/{node}/network` IS THE REVERT, AND IT IS DELIBERATELY NOT WIRED TO `delete`.\n * MEASURED: the whole body of `revert_network_changes` is `unlink \"/etc/network/interfaces.new\"`.\n * It discards STAGED edits and cannot undo an APPLIED one, so pointing `delete` at it would throw\n * away whatever happened to be staged the moment somebody removed a line from a stack file — the\n * worst possible time to do it. Revert by hand, deliberately: `pvesh delete /nodes/<node>/network`.\n *\n * ⚠️ PRIVILEGES, so widening stays a deliberate act: the `read` role needs nothing for the pending\n * read (`GET /nodes/{node}/network` is `\"user\": \"all\"`) and `Sys.Audit` on `/` for the health\n * gate; the `provision` role needs `Sys.Modify` on `/nodes/{node}` for the apply, and owns the\n * task it starts so it can poll it without `Sys.Audit`. ⛔ `Sys.Modify` ON A NODE IS NOT A SMALL\n * GRANT — it also carries that node's DNS, hosts file, time and service configuration. Granting\n * it cluster-wide to run this resource widens the provisioning role well beyond networking.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { guardNetworkApply } from './apply-endpoints.ts';\nimport { pveWith } from './client.ts';\nimport { mint } from './credentials.ts';\nimport { awaitTask, degradedReason, pendingCount } from './network-apply-read.ts';\nimport type { PveRequirements } from './resource.ts';\nimport type { WithTarget } from './resource.ts';\n\nexport interface NetworkApplyProps extends WithTarget {\n /**\n * The node whose staged interfaces file this applies — `node-b`.\n *\n * ⚠️ ONE APPLY PER NODE, AND NEVER TWO FOR THE SAME NODE. Two would each publish the other's\n * staged half, and both would run `ifreload` on a file the other had not finished writing.\n */\n node: string;\n\n /**\n * The staged interface changes this apply publishes, and the apply that must go before it.\n *\n * ⚠️ IT IS NEVER READ, AND IT IS NOT DECORATION — the same contract as `SdnApply.after`. Alchemy\n * orders resources by DATA FLOW, so consuming an attribute is the only way to say \"after\".\n * Pass the interfaces this node declares AND the previous node's apply:\n * `after: [vmbr1.iface, n2Apply.pending]`. Leave the previous apply out and the three nodes\n * reload in parallel, which is the one thing this resource is built to prevent.\n */\n after?: readonly unknown[];\n}\n\nexport interface NetworkApplyAttributes {\n /** The node this applies. Identity, echoed so a plan line says which member it will reload. */\n node: string;\n\n /**\n * Changed lines still staged on this node.\n *\n * ⚠️ LINES OF A UNIFIED DIFF, NOT INTERFACES, and the number is reported rather than trusted —\n * `pendingLines` explains why it can undercount by one and never to zero. Zero is the only\n * settled value: anything above it means the running config and the staged config disagree,\n * whether this stack staged it or somebody edited the node in the UI and walked away.\n *\n * ⛔ THE DIFF ITSELF IS NEVER AN ATTRIBUTE. Alchemy writes attributes to its state store\n * UNENCRYPTED, and `/etc/network/interfaces` can carry a `wpa-psk` or a `pre-up` command. Only\n * the count leaves `network-apply-read.ts`.\n *\n * ⚠️ AND NOTHING OUT OF THE INTERFACE ROWS IS AN ATTRIBUTE EITHER, WHICH IS WHAT KEEPS THIS\n * FAMILY NOOP. `GET /nodes/{node}/network` returns eighteen fields it will not accept on write\n * — MEASURED by differencing the schema's GET return properties against its POST parameters:\n * `active`, `exists`, `families`, `method`, `method6`, `options`, `priority`, `link-type` and\n * ten more; `altnames` is returned and is not even in the schema. `priority` is the worst of\n * them: PVE ASSIGNS it from the order of the interfaces file, so the same logical layout\n * carries different numbers on different nodes (MEASURED: node-d's differ from node-b/node-c's by one).\n * Every one of those is a forever-diff waiting for whoever writes `Proxmox.NetworkInterface`.\n * This resource compares a single integer against zero and never looks at a row at all.\n */\n pending: number;\n}\n\nexport interface ProxmoxNetworkApply extends Resource<\n 'Proxmox.NetworkApply',\n NetworkApplyProps,\n NetworkApplyAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxNetworkApply = Resource<ProxmoxNetworkApply>('Proxmox.NetworkApply');\n\nconst read = (props: NetworkApplyProps) =>\n pendingCount(props.target, props.node).pipe(\n Effect.map((pending) => ({ node: props.node, pending })),\n );\n\n/**\n * ⚠️ THE DIFF IS ABOUT THE NODE, NOT ABOUT THE PROPS — the same reasoning as `SdnApply`. This\n * resource has no settable field (`node` is identity, `after` is an ordering edge), so comparing\n * props to props would report `noop` forever and the apply would run exactly once, ever. What\n * decides it is whether the node is carrying a staged change right now.\n *\n * ★ THIS IS ALSO WHY DECLARING THE LIVE CLUSTER PLANS AS `noop`. VERIFIED 2026-09-13:\n * `/etc/network/interfaces.new` is absent on node-b, node-c and node-d, so `changes` is absent from all three\n * answers, so `pending` is 0 on all three and every one of them plans `noop`.\n */\nconst diff = (news: Input<NetworkApplyProps>) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n const { pending } = yield* read(news);\n return pending === 0 ? ({ action: 'noop' } as const) : ({ action: 'update' } as const);\n });\n\n/** Stop the deploy, loudly, with the node in the message. ⚠️ A die here is the SAFE outcome. */\nconst refuse = (node: string, why: string) =>\n Effect.die(new Error(`Proxmox.NetworkApply ${node}: ${why}`));\n\nconst apply = (props: NetworkApplyProps) =>\n Effect.gen(function* () {\n const node = props.node;\n const staged = yield* pendingCount(props.target, node);\n /**\n * ⛔ NOTHING STAGED MEANS NOTHING TO APPLY, AND THIS BRANCH IS A SAFETY PROPERTY RATHER THAN AN\n * OPTIMISATION. `reconcile` also runs on CREATE — the first time this resource appears in a\n * stack, before `diff` has ever been consulted — and the PVE worker runs `ifreload -a`\n * UNCONDITIONALLY, staged file or not. Without this branch, adding `Proxmox.NetworkApply` to\n * a stack describing the cluster as it already is would reload networking on every node it\n * names, for nothing. Adoption must cost nothing; that is true here in the strongest sense.\n */\n if (staged === 0) return { node, pending: 0 };\n\n const before = yield* degradedReason(props.target);\n if (before !== undefined) {\n return yield* refuse(\n node,\n `refusing to reload networking on a cluster that is already degraded (${before}). ` +\n 'Ceph spans these nodes -- fix the cluster first, then re-run the deploy.',\n );\n }\n\n /**\n * ⚠️ ONE LEASE FOR BOTH CALLS, VIA `pveWith` RATHER THAN `pve`. Each mint returns a NEW token\n * id, and the task-status endpoint only skips its `Sys.Audit` check for the task's OWNER —\n * so polling with a fresh mint is a different identity and needs a privilege this role does\n * not have. The `provision` lease is 300s and non-renewable, which is far longer than a\n * reload and is the reason no renew path is reached for.\n */\n // ⛔ BEFORE THE MINT AND THE RELOAD. The apply takes no body, so this is the lookup rather\n // than a value check: a vendor that moved this endpoint fails here, not mid-ifreload.\n yield* guardNetworkApply;\n const credential = yield* mint(props.target, 'provision');\n const upid = yield* pveWith<string>(props.target, credential, 'PUT', `nodes/${node}/network`);\n if (upid === undefined) {\n return yield* refuse(\n node,\n 'the apply returned no UPID. PVE wraps every answer in {\"data\":...} and can report success ' +\n 'on a call that did nothing -- the reload cannot be confirmed, so it is not claimed.',\n );\n }\n\n /**\n * ⛔ POLLING `pending` INSTEAD OF THE TASK WOULD REPORT SUCCESS BEFORE THE NETWORK WAS TOUCHED.\n * MEASURED in the worker above: the `rename` happens FIRST and `ifreload -a` second, so the\n * staged file — and therefore `changes`, and therefore `pending` — disappears while the\n * reload has not yet started. A provider that watched the count would call a reload that had\n * not run, and a reload that then FAILED, a success. The task's exitstatus is the only\n * honest answer.\n */\n const exitstatus = yield* awaitTask(props.target, credential, node, upid);\n if (exitstatus !== 'OK') {\n return yield* refuse(\n node,\n `the reload ended \"${exitstatus}\" (${upid}). Read it from ANOTHER node -- ` +\n `\\`pvesh get /nodes/${node}/tasks/${upid}/log\\` -- and do not apply the next node until ` +\n 'this one is understood.',\n );\n }\n\n const after = yield* degradedReason(props.target);\n if (after !== undefined) {\n return yield* refuse(\n node,\n `the reload reported OK but the cluster is now degraded (${after}). The next ` +\n 'node has NOT been applied. Restore this one before continuing -- Ceph runs over these ' +\n 'links and a second reload now would take pools below min_size.',\n );\n }\n\n const left = yield* pendingCount(props.target, node);\n if (left !== 0) {\n return yield* refuse(\n node,\n `the reload reported OK but ${String(left)} staged line(s) remain. The running config does ` +\n 'not match the declared one, and saying otherwise is the lie this resource exists to stop.',\n );\n }\n return { node, pending: left };\n });\n\n/**\n * ⛔ HAND-WRITTEN RATHER THAN `pveHandlers`, FOR THE SAME REASON `sdn-apply.ts` IS. The factory's\n * spec is create/update/delete over ONE object at ONE path, and an apply is not an object: there\n * is nothing to POST, nothing to DELETE, and `matches` would compare a declaration to itself and\n * answer `noop` forever, so the apply would run exactly once in the life of the stack. `list` is\n * empty for the reason it is everywhere in this package — adoption stays an explicit act.\n *\n * ⛔ `delete` APPLIES NOTHING AND UNDOES NOTHING, AND THAT IS CORRECT. There is no un-apply: the\n * config is already live on the node, and the way to remove an interface is to delete it and\n * apply AGAIN. See the ⚠️ about `DELETE /nodes/{node}/network` at the top of this file.\n */\nconst handlers = {\n delete: () => Effect.void,\n diff: ({ news }: { news: Input<NetworkApplyProps> }) => diff(news),\n list: () => Effect.succeed([]),\n read: ({ olds }: { olds: NetworkApplyProps }) => read(olds),\n reconcile: ({ news }: { news: NetworkApplyProps }) => apply(news),\n};\n\nexport const ProxmoxNetworkApplyProvider = () =>\n Provider.effect(ProxmoxNetworkApply, Effect.succeed(ProxmoxNetworkApply.Provider.of(handlers)));\n",
|
|
75
|
+
"/**\n * The two applies, and the vendor endpoints they publish through.\n *\n * ★ AN ACTION ENDPOINT IS STILL WIRED, EVEN THOUGH ITS TABLE IS EMPTY, AND THE REASON IS THE\n * GENERATOR RATHER THAN THE VALIDATOR. `PUT /nodes/{node}/network` and `PUT /cluster/sdn` take\n * no body at all — there is nothing for a length or a range to be wrong about — but\n * `codegen/constraints.ts` RESOLVES every key named in this package against the vendor's own\n * schema and STOPS when one is absent. So the key buys the thing these two families most need:\n * a PVE that moves, renames or withdraws the apply fails `bun run check` instead of failing an\n * `ifreload -a` on three nodes at once.\n * ⚠️ AND THE LOOKUP IS NOT FREE EITHER. `constraintsFor` throws when a key has no table, so a\n * checkout whose generated tables were never refreshed refuses the apply with the command to\n * run rather than quietly doing nothing — the same defect `constraint-guard.ts` names.\n *\n * ⛔ NEITHER OF THESE IS A CREATE, AND NEITHER FAMILY HAS A `PveSpec`. Both write their own five\n * handlers (an apply is not an object — see the ⛔ in each file), so they reach the shared check\n * through `guardForm` by name, exactly as `pbs-datastore-endpoint.ts` does.\n */\nimport type * as Effect from 'effect/Effect';\nimport { guardForm } from './constraint-guard.ts';\nimport type { EndpointKey } from './constraints.ts';\n\n/** ⚠️ `{node}` is a path segment, so this really does take no form keys at all. */\nexport const NETWORK_APPLY_ENDPOINT: EndpointKey = 'pve:PUT /nodes/{node}/network';\n\n/**\n * ⛔ CLUSTER-WIDE. One PUT publishes every staged zone, vnet and subnet on every node; there is no\n * per-object apply and no pre-apply veto. `sdn-apply.ts` is the whole argument.\n */\nexport const SDN_APPLY_ENDPOINT: EndpointKey = 'pve:PUT /cluster/sdn';\n\nexport const guardNetworkApply: Effect.Effect<void> = guardForm(NETWORK_APPLY_ENDPOINT, {}, true);\n\nexport const guardSdnApply: Effect.Effect<void> = guardForm(SDN_APPLY_ENDPOINT, {}, true);\n",
|
|
76
|
+
"/**\n * `Proxmox.NodeNetwork` — one stanza of a node's `/etc/network/interfaces`, declared.\n *\n * ⛔ DECLARING THIS RESOURCE IS SAFE. APPLYING IT IS A SEPARATE, DELIBERATE ACT, AND IT IS NOT\n * PERFORMED HERE. Every write under `/nodes/{node}/network/{iface}` lands in\n * `/etc/network/interfaces.new` and changes no running interface. MEASURED, from PVE::INotify:\n * `$shadowfiles = { '/etc/network/interfaces' => '/etc/network/interfaces.new' }`, and\n * `read_file` reads the SHADOW when it exists. The apply is `PUT /nodes/{node}/network`, which\n * renames the shadow over the real file and runs `ifreload -a`; the revert is\n * `DELETE /nodes/{node}/network`, which simply unlinks the shadow. A sibling resource models\n * that apply — this one never calls it, exactly as `Proxmox.SdnZone` never calls\n * `Proxmox.SdnApply`.\n *\n * ⛔ CEPH ON C1 RIDES TWO NETWORKS AND THIS API ONLY SEES ONE OF THEM. Measured from\n * `/etc/pve/ceph.conf` on 2026-09-13:\n *\n * public_network = 198.51.100.0/24 -> vmbr1.42, which this resource DOES see\n * cluster_network = 203.0.113.0/24 -> every OSD's cluster_addr: .102 / .103 / .104\n *\n * The cluster network — OSD REPLICATION, the traffic that rebuilds a lost replica — lives on\n * `203.0.113.10X/32` addresses carried by `tb0`, `tb1` and `dummy_c1`: the Thunderbolt mesh.\n *\n * ⛔ AND THOSE ADDRESSES ARE INVISIBLE HERE, WHICH IS THE TRAP. They are declared in\n * `/etc/network/interfaces.d/sdn`, and PVE's network API does not parse that directory.\n * MEASURED: `GET /nodes/node-b/network/tb0` answers `{\"method\":\"manual\",\"type\":\"eth\",…}` with NO\n * address, while `ip -br -4 addr` shows `tb0 UP 203.0.113.102/32`. So this resource reports\n * tb0 and tb1 as address-less manual ports and will plan `noop` over them — implicitly\n * asserting they carry no address, which is FALSE.\n *\n * The consequence, stated plainly: declaring tb0/tb1 from what this API reports and then\n * DESTROYING or applying over them takes out OSD replication on a cluster whose pools are\n * size 3 / min_size 2. They read as the most boring interfaces on the node and are the two most\n * dangerous. Leave them undeclared; if they must be managed, manage the file that actually\n * owns them, not this resource.\n *\n * ⚠️ Two ways this file limits the damage on the interfaces it CAN see: `cidr` is compared\n * unconditionally (the ⛔ on `matches`), and a live interface whose `type` disagrees with the\n * declaration is reported ABSENT rather than retyped (the ⛔ in `attributes`).\n *\n * ★ THE REASON IT EXISTS IS THAT THE STAGED READ CANNOT TELL YOU THE NETWORK IS RIGHT — it can\n * only tell you the FILE is. Once anything is staged, `GET /nodes/{node}/network/{iface}`\n * answers from `interfaces.new`, so `matches` compares a declaration against a pending file and\n * reports `noop` over a node whose running config has not moved. The apply resource is what\n * closes that gap; this resource's `noop` means \"the file says what you said\", no more.\n *\n * ⚠️ PRIVILEGES ARE COARSER HERE THAN ANYWHERE ELSE IN THE PACKAGE: reads want `Sys.Audit` on\n * `/nodes/{node}`, every write `Sys.Modify` on it — and there is no `/nodes/{node}/network` ACL\n * object to scope to, so that also buys the node's services, certificates, DNS and time.\n * Collected with every other family in docs/privileges.md.\n *\n * ★ A RECONCILE OVER A MATCHING INTERFACE IS A NO-WRITE, AND AN EARLIER DRAFT OF THIS COMMENT\n * SAID THE OPPOSITE. `pveOperations.reconcile` gates on `matches` BEFORE the PUT\n * (`resource.ts`: `else if (spec.updateForm !== undefined && !spec.matches(live, news))`), and\n * the ⛔ above that line names THIS family as the reason the guard was added — adopting an\n * interface a node already has would otherwise leave it holding a pending network change.\n *\n * ⚠️ WHAT IS STILL TRUE: when `matches` is false the PUT rewrites the whole stanza, and that can\n * ADD lines the file did not have — a `vlan-raw-device vmbr1` under `vmbr1.42`, where PVE had\n * been deriving it from the name. Harmless in content, and it costs nothing until somebody\n * applies; one more reason the apply is a separate resource.\n *\n * ⚠️ `list` IS EMPTY, AND HERE THAT MATTERS MORE THAN ANYWHERE ELSE IN THIS PACKAGE. The index\n * returns every interface the node has, `vmbr0`, `bond0` and the Ceph vlan included. Adopting\n * them would put Alchemy one `destroy` away from staging the removal of a node's uplink — and\n * `delete_network` has NO in-use check at all: MEASURED, it deletes the hash entry and writes\n * the file. Adoption stays explicit.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n createBody,\n readAttributes,\n same,\n sameComment,\n sameList,\n updateBody,\n} from './node-network-form.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\n\n/**\n * PVE's interface types. `unknown` is what a NIC it does not manage reports — node-b and node-c both\n * report `wlan0` that way, and node-d has no such interface at all.\n *\n * ⚠️ THAT ASYMMETRY IS A TRAP FOR A DECLARATION SHARED ACROSS NODES. On node-d the read answers 404,\n * which this provider cannot tell from \"deleted\", so reconcile POSTs — writing a stanza for a\n * card that is not in the machine. Declare per node what each node actually has; a physical\n * interface is discovered, not decided.\n */\nexport type NodeNetworkType =\n | 'OVSBond'\n | 'OVSBridge'\n | 'OVSIntPort'\n | 'OVSPort'\n | 'alias'\n | 'bond'\n | 'bridge'\n | 'eth'\n | 'fabric'\n | 'unknown'\n | 'vlan'\n | 'vnet';\n\nexport interface NodeNetworkProps extends WithTarget {\n /** Which node's file this stanza lives in. Interfaces are per node, never cluster-wide. */\n node: string;\n /** `vmbr0`, `bond0`, `vmbr1.42`. 2-20 characters, PVE's `pve-iface` format. */\n iface: string;\n /** ⛔ REQUIRED ON EVERY WRITE, update included, never used to retype — see `readAttributes`. */\n type: NodeNetworkType;\n /**\n * `198.51.100.12/24`. ⛔ THE ONE FIELD WITH NO UNMANAGED MODE: leaving it out is an instruction\n * to make the interface `manual`, not an instruction to leave its address alone. The ⛔ on\n * `updateBody` in node-network-form.ts has the measurement and the consequence.\n */\n cidr?: string;\n /** ⚠️ PVE allows exactly ONE default gateway per node and refuses a second with \"Default\n * gateway already exists on interface '<other>'\". On C1 it is vmbr0.41's. */\n gateway?: string;\n /**\n * `auto <iface>` in the file. Absent on read means off, which is why `bool`'s fallback is used.\n * ⚠️ UNDECLARED IS UNMANAGED ON AN UPDATE AND OFF ON A CREATE — the same asymmetry storage.ts\n * has, and it bites harder here: an interface created without `autostart` is one the node\n * will not bring up at boot. Every C1 bridge, bond and vlan carries it.\n */\n autostart?: boolean;\n /** 1280-65520. Unset leaves the file without an `mtu` line and the kernel default in force. */\n mtu?: number;\n /** ⚠️ Round-trips only after normalisation — see `comment` in node-network-form.ts. */\n comments?: string;\n /** Space-separated, a SET: `enp87s0`, or `bond0`. ⚠️ PVE refuses a port already used elsewhere. */\n bridge_ports?: string;\n /** `2-4094`, or `2 100-200`. Only written when `bridge_vlan_aware` is on. */\n bridge_vids?: string;\n /** ⛔ A `false` here is sent as `delete=`, never as `0` — the ⛔ on `body` says why. */\n bridge_vlan_aware?: boolean;\n /**\n * A bond's members, space separated. ⚠️ THE PARAMETER IS `slaves`, NOT `bond_slaves`, in BOTH\n * directions on this PVE: MEASURED, the POST/PUT schema names only `slaves` and the GET returns\n * `\"slaves\":\"enp2s0f0np0 enp2s0f1np1\"`. `bond_slaves` appears nowhere in Network.pm here.\n */\n slaves?: string;\n bond_mode?: string;\n /** ⚠️ Only written when `bond_mode` is `balance-xor` or `802.3ad`; ignored otherwise. */\n bond_xmit_hash_policy?: string;\n /** active-backup only. Kept hyphenated because that is the wire name. */\n 'bond-primary'?: string;\n /** ⚠️ DERIVED FROM A DOTTED NAME. `vmbr1.42` reports `vlan-id` 42 with no such line in the file;\n * declaring it there is harmless but adds a line the file did not have. */\n 'vlan-id'?: number;\n 'vlan-raw-device'?: string;\n}\n\n/**\n * ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED — each is a MEASURED forever-diff, and the\n * evidence for every one of them is in node-network-form.ts, beside the code that reads them.\n */\nexport interface NodeNetworkAttributes {\n node: string;\n iface: string;\n type: string;\n cidr: string;\n gateway: string;\n autostart: boolean;\n mtu: number;\n comments: string;\n bridge_ports: string;\n bridge_vids: string;\n bridge_vlan_aware: boolean;\n slaves: string;\n bond_mode: string;\n bond_xmit_hash_policy: string;\n 'bond-primary': string;\n 'vlan-id': number;\n 'vlan-raw-device': string;\n priority: number;\n method: string;\n families: string;\n active: boolean;\n exists: boolean;\n bond_miimon: string;\n bridge_stp: string;\n bridge_fd: string;\n}\n\nexport interface ProxmoxNodeNetwork extends Resource<\n 'Proxmox.NodeNetwork',\n NodeNetworkProps,\n NodeNetworkAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a node's only uplink cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxNodeNetwork = Resource<ProxmoxNodeNetwork>('Proxmox.NodeNetwork', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst handlers = pveHandlers<NodeNetworkProps, NodeNetworkAttributes>({\n attributes: readAttributes,\n collection: (props) => `nodes/${props.node}/network`,\n /** ⚠️ No `delete` parameter on a POST — see the ⚠️ on `updateBody`. `iface` IS in the create\n * body though, and was missing until 2026-09-22 — the 🔴 on `createBody`. */\n createForm: createBody,\n /** The vendor rules both forms are checked against at plan time — resource-spec.ts. */\n endpoint: {\n create: 'pve:POST /nodes/{node}/network',\n update: 'pve:PUT /nodes/{node}/network/{iface}',\n },\n /**\n * ⛔ `cidr` IS THE ONLY FIELD COMPARED WHEN UNDECLARED, AND THAT ASYMMETRY IS THE POINT. Every\n * other field here follows storage.ts: undeclared means unmanaged, so it is neither sent nor\n * compared and a plan cannot report an update no write could satisfy. `cidr` cannot be\n * unmanaged, because PVE derives `method` from the presence of an address IN THE FORM — an\n * omission is an edit. Comparing it unconditionally is what makes that edit visible in `plan`\n * instead of at `ifreload` time.\n *\n * ⚠️ NOTHING IN THE REPORTED-ONLY BLOCK IS HERE, and that is what makes the C1 declaration\n * plan as `noop` on all three nodes at once — the ⛔ above `NodeNetworkAttributes` lists them\n * and the measurement behind each.\n */\n matches: (attributes, props) =>\n attributes.cidr === (props.cidr ?? '') &&\n same(props.gateway, attributes.gateway) &&\n same(props.autostart, attributes.autostart) &&\n same(props.mtu, attributes.mtu) &&\n sameComment(props.comments, attributes.comments) &&\n sameList(props.bridge_ports, attributes.bridge_ports) &&\n sameList(props.bridge_vids, attributes.bridge_vids) &&\n same(props.bridge_vlan_aware, attributes.bridge_vlan_aware) &&\n sameList(props.slaves, attributes.slaves) &&\n same(props.bond_mode, attributes.bond_mode) &&\n same(props.bond_xmit_hash_policy, attributes.bond_xmit_hash_policy) &&\n same(props['bond-primary'], attributes['bond-primary']) &&\n same(props['vlan-id'], attributes['vlan-id']) &&\n same(props['vlan-raw-device'], attributes['vlan-raw-device']),\n path: (props) => `nodes/${props.node}/network/${props.iface}`,\n updateForm: updateBody,\n});\n\n/**\n * ⚠️ THE DESTROY IS STAGED LIKE EVERY OTHER WRITE, WHICH MAKES IT THE ONE RECOVERABLE DESTROY IN\n * THIS PACKAGE. It removes the stanza from `interfaces.new` and nothing else;\n * `DELETE /nodes/{node}/network` puts the node back by unlinking that file. It becomes\n * irreversible the moment somebody applies — and PVE performs NO in-use check before removing\n * an interface other bridges or Ceph still stand on.\n */\nexport const ProxmoxNodeNetworkProvider = () =>\n Provider.effect(ProxmoxNodeNetwork, Effect.succeed(ProxmoxNodeNetwork.Provider.of(handlers)));\n",
|
|
77
|
+
"/**\n * How a node interface crosses the wire in both directions — props to a PVE form, a PVE answer to\n * a comparable attribute — and when two of those values count as the same value.\n *\n * ★ SPLIT OUT OF node-network.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is the\n * same one metric-server-form.ts uses, one field wider: this file owns the COERCIONS, in both\n * directions, because a read that normalises differently from the write that produced it is\n * exactly how a forever-diff is born — keeping `ifaceList` next to both ends is what stops that.\n * node-network.ts answers \"what is an interface, and which of its fields are safe to compare at\n * all\". Nothing here calls the cluster.\n *\n * ⚠️ THE `import type` BACK TO node-network.ts IS A CYCLE ON PAPER ONLY — type-only, erased before\n * anything runs, so `NodeNetworkProps` stays in the file that declares the resource.\n *\n * * ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED, AND EACH ONE IS A MEASURED FOREVER-DIFF.\n * `priority` is assigned by PVE FROM FILE ORDER — `my $priority = 2; ... $d->{priority} =\n * $priority++` — and it is not a POST or PUT parameter at all. MEASURED ACROSS C1: vmbr1.42 is\n * priority 16 on node-b and node-c and 15 on node-d, because node-d has no `wlan0` above it. One declaration\n * reused across three nodes would diff on exactly one of them, forever, over nothing.\n * `method` is RECOMPUTED on every write from whether the form carried an address, `families` is\n * recomputed the same way and never written to the file, `active` and `exists` describe the\n * kernel rather than the config, and `bond_miimon`, `bridge_stp` and `bridge_fd` are emitted by\n * PVE's writer with defaults (100, `off`, 0) while appearing in NEITHER write schema —\n * `additionalProperties => 0` means sending one is a 400. Live proof of all three: node-b's bond0\n * returns `bond_miimon \"100\"` and vmbr1 returns `bridge_stp \"off\"`, `bridge_fd \"0\"`, none of\n * which any declaration can set.\n */\nimport type { NodeNetworkAttributes, NodeNetworkProps } from './node-network.ts';\nimport { bool, flag, int, text, withClears } from './values.ts';\n\n/**\n * \"Not set\", for the three integers this family reports.\n *\n * ⚠️ 0 CANNOT BE THE ABSENT-MARKER AND -1 CAN. `mtu` is 1280-65520, `vlan-id` 1-4094 and PVE's\n * `priority` starts at 1 (1 is reserved for `lo`), so -1 is outside every one of their ranges\n * while 0 is merely outside today's. metric-server-form.ts keeps its own constant for the same\n * reason and against different ranges; one shared UNSET would have to be right for both.\n */\nexport const UNSET = -1;\n\n/**\n * A space-separated interface list, flattened to one comparable form.\n *\n * ⚠️ `bridge_ports` AND `slaves` ARE SETS, AND PVE NORMALISES THEIR SEPARATORS BUT NOT THEIR ORDER.\n * MEASURED in PVE::Network::Interfaces: the writer does `s/[;,\\s]+/ /g` on both before emitting\n * them, and the reader hands the resulting string straight back — so `enp2s0f1np1 enp2s0f0np0`\n * survives a round trip in the order it was written. Order carries no meaning for a bridge's\n * ports or a bond's slaves, so a declaration that lists them the other way round is the SAME\n * declaration; comparing raw strings would report an update that rewrites the file to say\n * exactly what it already said. Both sides are sorted here and only then compared.\n *\n * ⚠️ IT IS NOT `csv` FROM values.ts. That one joins with commas, and a comma in\n * `/etc/network/interfaces` is not a separator — PVE's own comment above the writer says the\n * list \"must be space separated! other separators will cause problems\". Sending a comma-joined\n * value would be accepted by `split_list` and then written back as spaces, which is a second\n * spelling of one value and therefore a second chance to diff.\n *\n * ⚠️ `bridge_vids` GOES THROUGH THE SAME FUNNEL and its sort is LEXICAL, which is correct only\n * because a vid list is a set: `2-4094` is one token, and `100-200 2` and `2 100-200` allow the\n * same VLANs. Nothing here should be reused for a list whose order is meaning.\n */\nexport const ifaceList = (value: unknown) =>\n text(value)\n .split(/[\\s,;]+/)\n .filter((part) => part !== '')\n .sort()\n .join(' ');\n\n/**\n * A comment off the wire, with the newline PVE adds taken back off.\n *\n * ⛔ `comments` NEVER ROUND-TRIPS RAW, AND THAT IS A FOREVER-DIFF IF IT IS NOT NORMALISED.\n * MEASURED in the reader: each `#...` line is appended as `$comment . \"\\n\"`, so a declared\n * `ceph transport` comes back as `ceph transport\\n` and the two never compare equal.\n * ⚠️ AND THE READER FOLDS `comments6` INTO `comments` (`$d->{comments} .= $comments6`), so on an\n * interface that carries an IPv6 comment a declared `comments` CANNOT match what comes back.\n * C1 has none; on an interface that does, leave `comments` undeclared rather than fighting it.\n */\nexport const comment = (value: unknown) => text(value).replace(/\\s+$/, '');\n\n/** Undeclared is unmanaged: neither sent nor compared. See the ⚠️ on `body`. */\nexport const same = <T>(declared: T | undefined, live: T) =>\n declared === undefined || declared === live;\n\nexport const sameList = (declared: string | undefined, live: string) =>\n declared === undefined || ifaceList(declared) === live;\n\nexport const sameComment = (declared: string | undefined, live: string) =>\n declared === undefined || comment(declared) === live;\n\nconst field = (name: string, value: string | undefined): Record<string, string> =>\n value === undefined ? {} : { [name]: value };\n\n/**\n * The fields sent on EVERY write, create and update alike.\n *\n * ⚠️ UNDECLARED IS UNMANAGED — not sent, and not compared either. PVE's PUT MERGES the form into\n * the existing stanza (`foreach my $k (keys %$param) { $ifaces->{$iface}->{$k} = $param->{$k} }`),\n * so an omitted parameter is left exactly as it was. `cidr` is the one exception and it is not\n * a small one — see the ⛔ in node-network.ts.\n *\n * ⛔ `type` IS REQUIRED ON THE PUT, NOT ONLY ON THE POST. It carries no `optional` in either\n * schema, and the merge above writes it straight into the stanza — so a PUT naming the wrong\n * type would RETYPE A LIVE INTERFACE in the file and the writer would then emit a bridge stanza\n * for what is really a vlan. node-network.ts refuses that case before it can reach here.\n *\n * ⛔ `bridge_vlan_aware` IS SENT ONLY WHEN TRUE, AND A FALSE ONE IS A `delete=`. MEASURED in the\n * writer: the test is `if (defined($d->{bridge_vlan_aware}))`, not a truth test, so `0` is\n * defined and PVE writes `bridge-vlan-aware yes` anyway. Sending `0` would therefore turn the\n * flag ON, read back as `1`, and diff forever while lying about which way it went.\n *\n * ⚠️ `netmask` IS A WRITABLE PARAMETER AND IS DELIBERATELY NOT OFFERED. PVE raises\n * \"netmask conflicts with cidr\" when both are sent, and the reader rewrites whatever was written\n * into prefix form — MEASURED: node-b's vmbr1.42 reports `\"netmask\":\"24\"`, never `255.255.255.0`.\n * A resource accepting both spellings would let a declaration diff against its own value.\n */\nexport const body = (props: NodeNetworkProps): Record<string, string> => ({\n ...field('autostart', flag(props.autostart)),\n ...field('bond-primary', props['bond-primary']),\n ...field('bond_mode', props.bond_mode),\n ...field('bond_xmit_hash_policy', props.bond_xmit_hash_policy),\n ...field('bridge_ports', props.bridge_ports),\n ...field('bridge_vids', props.bridge_vids),\n ...field('bridge_vlan_aware', props.bridge_vlan_aware === true ? '1' : undefined),\n ...field('cidr', props.cidr),\n ...field('comments', props.comments),\n ...field('gateway', props.gateway),\n ...field('mtu', props.mtu === undefined ? undefined : String(props.mtu)),\n ...field('slaves', props.slaves),\n ...field('vlan-id', props['vlan-id'] === undefined ? undefined : String(props['vlan-id'])),\n ...field('vlan-raw-device', props['vlan-raw-device']),\n type: props.type,\n});\n\n/**\n * The create form: `body` plus the name of the interface being created.\n *\n * 🔴 `iface` WAS MISSING UNTIL 2026-09-22, AND EVERY CREATE THIS FAMILY HAS EVER PLANNED WOULD\n * HAVE 400ed. `POST /nodes/{node}/network` declares `iface` REQUIRED (`pve-iface`, 2..20) and\n * `{node}` is its only path parameter, so the name has to travel in the BODY — unlike the PUT,\n * where `/network/{iface}` carries it and `updateBody` is right to leave it out. Nothing caught\n * it because no NodeNetwork has been created from a declaration yet: C1's three nodes were all\n * adopted, which takes the PUT path. Found by wiring this family to the vendor's own table,\n * which is exactly the 128-character comment again with a different parameter.\n *\n * ⚠️ NOT ADDED TO `body`, BECAUSE THE PUT MUST NOT CARRY IT. `additionalProperties => 0` on the\n * update schema makes a second copy of the name a 400 rather than an ignored hint.\n */\nexport const createBody = (props: NodeNetworkProps): Record<string, string> => ({\n ...body(props),\n iface: props.iface,\n});\n\n/**\n * The update form: `body` plus the two fields that can only be cleared explicitly.\n *\n * ⛔ `delete=cidr` IS SENT WHENEVER `cidr` IS UNDECLARED, AND IT IS THE LESSER OF TWO EVILS RATHER\n * THAN A GOOD OUTCOME. PVE recomputes the method on every write from the form alone —\n * `$param->{method} = $param->{address} ? 'static' : 'manual'` — so a PUT WITHOUT an address\n * makes the interface manual no matter what the caller intended. Left at that, the merge would\n * leave the old `address` and `netmask` in the hash and the writer would emit an `address` line\n * under `iface … inet manual`: a stanza PVE's own UI cannot produce, whose behaviour under\n * `ifreload -a` I did NOT measure. Clearing cidr explicitly at least produces a clean, honest\n * manual interface — and `matches` compares `cidr` UNCONDITIONALLY, so the plan says so first.\n *\n * ⛔ ON vmbr1.42 THAT IS THE CEPH TRANSPORT. A declaration of that interface without its `cidr` is\n * a declaration that it should have no address, applied across three nodes. The plan will read\n * `1 to update` rather than `noop`; do not wave it through.\n *\n * ⚠️ `delete` IS A PUT-ONLY PARAMETER. The POST schema is `additionalProperties => 0` and has no\n * `delete`, so sending it on a create is a 400 — which is why `createForm` calls `body` directly.\n */\nexport const updateBody = (props: NodeNetworkProps): Record<string, string> => {\n const clear = [\n ...(props.cidr === undefined ? ['cidr'] : []),\n ...(props.bridge_vlan_aware === false ? ['bridge_vlan_aware'] : []),\n ];\n const fields = body(props);\n return withClears(fields, clear);\n};\n\n/**\n * One live interface as attributes, or `undefined` — \"this is not really there\".\n *\n * ⚠️ EVERY FIELD PVE REPORTS IS CARRIED HERE, INCLUDING THE SEVEN NO DECLARATION CAN SET. A plan\n * that cannot show `priority`, `method` or `bond_miimon` cannot explain why it is ignoring them,\n * and the next person to read a forever-diff would start by adding them to `matches`. They are\n * reported precisely so that they are visibly OUT of it — see the ⛔ above the attribute type.\n */\nexport const readAttributes = (\n live: Record<string, unknown>,\n props: NodeNetworkProps,\n): NodeNetworkAttributes | undefined => {\n /**\n * ⛔ AN INTERFACE OF ANOTHER TYPE IS ANOTHER OBJECT, AND \"ABSENT\" IS THE SAFE ANSWER — the\n * same call metric-server.ts makes, for a worse reason. `type` is required on the PUT and\n * the PUT MERGES, so a declaration naming `bridge` for what is really a vlan would write\n * `type bridge` into the stanza and PVE's writer would then emit bridge-ports and\n * bridge-stp lines for vmbr1.42. Reporting absent instead makes reconcile POST a create,\n * which PVE refuses with \"interface already exists\": loud, and it changes nothing.\n */\n const liveType = text(live['type']);\n if (liveType !== '' && liveType !== props.type) return undefined;\n return {\n active: bool(live['active']),\n autostart: bool(live['autostart']),\n 'bond-primary': text(live['bond-primary']),\n bond_miimon: text(live['bond_miimon']),\n bond_mode: text(live['bond_mode']),\n bond_xmit_hash_policy: text(live['bond_xmit_hash_policy']),\n bridge_fd: text(live['bridge_fd']),\n bridge_ports: ifaceList(live['bridge_ports']),\n bridge_stp: text(live['bridge_stp']),\n bridge_vids: ifaceList(live['bridge_vids']),\n bridge_vlan_aware: bool(live['bridge_vlan_aware']),\n /** ⚠️ DERIVED BY THE READER from address+netmask, and always present when an address is. */\n cidr: text(live['cidr']),\n comments: comment(live['comments']),\n exists: bool(live['exists']),\n families: Array.isArray(live['families']) ? live['families'].join(',') : '',\n gateway: text(live['gateway']),\n iface: props.iface,\n method: text(live['method']),\n mtu: int(live['mtu'], UNSET),\n node: props.node,\n priority: int(live['priority'], UNSET),\n slaves: ifaceList(live['slaves']),\n type: liveType === '' ? props.type : liveType,\n 'vlan-id': int(live['vlan-id'], UNSET),\n 'vlan-raw-device': text(live['vlan-raw-device']),\n };\n};\n",
|
|
64
78
|
"/**\n * `Proxmox.NotificationMatcher` — which notifications a PVE cluster sends to which targets.\n *\n * ★ THE HALF OF THE NOTIFICATION SYSTEM notification-target.ts LEAVES OPEN. A target is where a\n * message CAN go; a matcher decides whether it DOES. A cluster ships one, `default-matcher`,\n * routing everything to `mail-to-root`, and a target no matcher names receives nothing — so a\n * declared webhook with no matcher is a delivery path that is never used, and looks finished.\n *\n * ★ ADOPTING THE BUILT-IN AS IT IS: declare what it holds and nothing more. MEASURED on PVE\n * 9.2.11 (2026-09-22, `pvesh get /cluster/notifications/matchers/default-matcher`): mode `all`,\n * one target `mail-to-root`, no rules, the comment \"Route all notifications to mail-to-root\".\n * ```ts\n * ProxmoxNotificationMatcher('default-matcher', {\n * target: cluster, name: 'default-matcher', targets: ['mail-to-root'],\n * });\n * ```\n * That plans `noop` and deploys with no write (notification-matcher.test.ts). Leaving `comment`\n * out leaves it alone; every rule field is compared — notification-matcher-form.ts says why.\n *\n * ★ WHAT THIS NEEDS, READ OFF THE CLUSTER'S OWN SCHEMA (generated/pve.ts source, 9.2.11):\n * read GET matchers/{name} Mapping.Audit or Mapping.Modify on /mapping/notifications\n * write POST, PUT, DELETE Mapping.Modify on /mapping/notifications\n * The same pair as `Proxmox.NotificationTarget`, with that file's ⚠️ about granting it on `/`.\n * ⛔ `Mapping.Use` IS NOT ENOUGH, though the COLLECTION GET accepts it: the per-object read this\n * family makes lists only Audit and Modify (pve-manager API2/Cluster/Notifications.pm\n * `get_matcher`, read at HEAD 2026-09-22 — corrected here; PR 95 said Use sufficed).\n * ⛔ A MISSING READ PRIVILEGE LOOKS LIKE \"ABSENT\", not like a 403 — see the same file. For\n * `default-matcher` that turns an adoption into a planned CREATE that PVE refuses as a\n * duplicate: the noop below needs a read lease holding Mapping.Audit.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n type NotificationMatcherAttributes,\n type NotificationMatcherFields,\n matcherAttributes,\n matcherCreateForm,\n matcherMatches,\n matcherUpdateForm,\n} from './notification-matcher-form.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\n\nexport interface NotificationMatcherProps extends NotificationMatcherFields, WithTarget {}\n\nexport interface ProxmoxNotificationMatcher extends Resource<\n 'Proxmox.NotificationMatcher',\n NotificationMatcherProps,\n NotificationMatcherAttributes,\n never,\n PveRequirements\n> {}\n\n/**\n * ⚠️ NO `defaultRemovalPolicy: 'retain'`, for notification-target.ts's reason: a matcher holds no\n * data, and a plan line is the only warning a delete gets. On `default-matcher` the delete\n * REVERTS it to the shipped rule instead — see `origin` on the attributes.\n */\nexport const ProxmoxNotificationMatcher = Resource<ProxmoxNotificationMatcher>(\n 'Proxmox.NotificationMatcher',\n);\n\nconst handlers = pveHandlers<NotificationMatcherProps, NotificationMatcherAttributes>({\n attributes: (live, props) => matcherAttributes(live, props.name),\n collection: () => 'cluster/notifications/matchers',\n createForm: matcherCreateForm,\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: {\n create: 'pve:POST /cluster/notifications/matchers',\n update: 'pve:PUT /cluster/notifications/matchers/{name}',\n },\n matches: matcherMatches,\n path: (props) => `cluster/notifications/matchers/${props.name}`,\n updateForm: matcherUpdateForm,\n});\n\nexport const ProxmoxNotificationMatcherProvider = () =>\n Provider.effect(\n ProxmoxNotificationMatcher,\n Effect.succeed(ProxmoxNotificationMatcher.Provider.of(handlers)),\n );\n",
|
|
65
79
|
"/**\n * A notification matcher — the rule that routes a notification to its targets — as ONE shape\n * shared by PVE (`Proxmox.NotificationMatcher`) and PBS (`Pbs.NotificationMatcher`).\n *\n * ★ ONE FILE FOR BOTH PRODUCTS BECAUSE IT IS ONE IMPLEMENTATION. Both servers run the same Rust\n * crate, `proxmox-notify` (matcher.rs), PVE through its Perl bindings; the schemas in\n * generated/pve.ts and generated/pbs.ts carry the same nine fields, and the forms below are\n * typed against BOTH, so a drift on either side fails `tsc` here rather than on a server.\n *\n * ⚠️ A MATCHER IS ITS WHOLE RULE, SO EVERY RULE FIELD IS COMPARED, DECLARED OR NOT. Most families\n * here leave an undeclared field alone (backup-job.ts's trade). A matcher cannot: an undeclared\n * `match-field` on a matcher that has one is a declaration claiming \"route everything\" over a\n * server that routes one job type — and `invert-match` flips the meaning of every other line.\n * So `match-*` default to empty, `mode` to `all`, `invert-match` and `disable` to false, all are\n * compared, and a PUT sends all of them (an emptied list as `delete`). Only `comment` keeps the\n * usual \"undeclared is unmanaged\" rule, because nothing routes on it.\n * ★ THE COST IS THAT ADOPTING A MATCHER MEANS DECLARING IT AS IT IS. PBS ships `default-matcher`\n * with `invert-match`, a `match-field` and a `match-severity` (measured on PBS 4.2, 2026-09-22),\n * so adopting it as-is means writing those three down. A declaration that leaves them out\n * plans an UPDATE, which is the plan telling the truth about what the deploy would do.\n */\nimport type {\n ClusterNotificationsMatchersNamePutParams,\n ClusterNotificationsMatchersPostParams,\n} from './generated/pve.ts';\nimport type {\n ConfigNotificationsMatchersNamePutParams,\n ConfigNotificationsMatchersPostParams,\n} from './generated/pbs.ts';\nimport { bool, text } from './values.ts';\n\n/** Everything a matcher declares except where it lives — the resource files add `target`. */\nexport interface NotificationMatcherFields {\n /** The primary key, a safe id (2–32 chars on PBS). Changing it is a replace. */\n name: string;\n /**\n * ⛔ THE TARGETS, BY NAME, AND REQUIRED: a matcher that notifies nobody is a routing rule for\n * silence. `[]` is still declarable, and clears the list.\n * ⚠️ `targets`, NOT THE SERVER'S `target`: every resource here already spends `target` on the\n * HOST (resource.ts `WithTarget`), and one key cannot mean both. The wire still says `target`.\n */\n targets: readonly string[];\n /**\n * Severity rules. EACH ITEM IS ITSELF A COMMA LIST: `'warning,error'` matches either.\n * ⛔ `['warning', 'error']` IS NOT THE SAME RULE AND UNDER `mode: 'all'` NEVER MATCHES. Items are\n * combined with `mode`; the severities inside one item are always OR-ed (matcher.rs\n * `SeverityMatcher`). A notification has one severity, so two items under AND ask for a\n * notification that is both — and routing nothing is the quietest failure a matcher has.\n * Severities: `info`, `notice`, `warning`, `error`, `unknown`.\n */\n 'match-severity'?: readonly string[];\n /**\n * Metadata rules, `exact:<field>=<v1>,<v2>` or `regex:<field>=<re>`. ⚠️ THE PREFIX IS REQUIRED:\n * the schema's pattern makes it optional and the parser then refuses it. A notification WITHOUT\n * the field never matches its rule — so `exact:job-id=…` excludes every garbage collection.\n */\n 'match-field'?: readonly string[];\n /** Time windows, e.g. `mon-fri 9:00-17:00`. Compared as written; the server keeps the text. */\n 'match-calendar'?: readonly string[];\n /** How the rule items combine. The server's default, and this package's, is `all`. */\n mode?: 'all' | 'any';\n /** Invert the whole rule. ⚠️ IGNORED WHEN THERE ARE NO RULES — an empty matcher always matches. */\n 'invert-match'?: boolean;\n /** A disabled matcher routes nothing. */\n disable?: boolean;\n /** Free text. The one field left alone when undeclared; `''` clears it. */\n comment?: string;\n}\n\n/** ⚠️ Lists are CANONICAL and SORTED, so a re-ordered declaration is not a diff. */\nexport interface NotificationMatcherAttributes {\n name: string;\n /**\n * `builtin`, `modified-builtin` or `user-created`. REPORTED, NEVER COMPARED.\n * ⛔ ON A BUILT-IN A DELETE REVERTS RATHER THAN REMOVES, and reports success either way — Alchemy\n * drops the row while `default-matcher` goes on routing. Read this before destroying one.\n * ★ MEASURED ON THE PER-OBJECT READ, which the published schema does not list: PVE 9.2.11 and\n * PBS 4.2 both return it from `…/matchers/default-matcher` (2026-09-22).\n */\n origin: string;\n comment: string;\n disable: boolean;\n 'invert-match': boolean;\n mode: string;\n targets: readonly string[];\n 'match-severity': readonly string[];\n 'match-field': readonly string[];\n 'match-calendar': readonly string[];\n}\n\nconst list = (value: unknown): readonly string[] =>\n Array.isArray(value) ? value.map((item: unknown) => String(item)) : [];\n\n/** Trimmed items, empty ones dropped. */\nconst items = (values: readonly string[], canonical: (item: string) => string): string[] =>\n values.map((item) => canonical(item.trim())).filter((item) => item !== '');\n\n/**\n * ⚠️ THE SERVER WRITES A RULE BACK IN ITS OWN SPELLING, SO BOTH SIDES ARE SPELLED ITS WAY FIRST.\n * matcher.rs parses `exact:type=gc, verify` into values and `Display`s them joined by a bare\n * comma; a severity item is split, trimmed and re-joined the same way. Without this a\n * declaration with a space after a comma would plan an update forever.\n */\nconst severity = (item: string): string =>\n item\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part !== '')\n .sort()\n .join(',');\n\nconst field = (item: string): string => {\n if (!item.startsWith('exact:')) return item;\n const at = item.indexOf('=');\n if (at < 0) return item;\n const values = item.slice(at + 1).split(',');\n return `${item.slice(0, at)}=${values.map((value) => value.trim()).join(',')}`;\n};\n\nconst same = (item: string): string => item;\n\n/** A declaration's lists, canonical and sorted — the form `matches` compares. */\nexport const canonicalRules = (fields: NotificationMatcherFields) => ({\n 'match-calendar': items(fields['match-calendar'] ?? [], same).sort(),\n 'match-field': items(fields['match-field'] ?? [], field).sort(),\n 'match-severity': items(fields['match-severity'] ?? [], severity).sort(),\n targets: items(fields.targets, same).sort(),\n});\n\nexport const matcherAttributes = (\n live: Record<string, unknown>,\n name: string,\n): NotificationMatcherAttributes => ({\n comment: text(live['comment']),\n disable: bool(live['disable']),\n 'invert-match': bool(live['invert-match']),\n 'match-calendar': items(list(live['match-calendar']), same).sort(),\n 'match-field': items(list(live['match-field']), field).sort(),\n 'match-severity': items(list(live['match-severity']), severity).sort(),\n // ⚠️ ABSENT IS `all` — matcher.rs `mode.unwrap_or_default()`, and `All` is the default variant.\n mode: text(live['mode'], 'all') || 'all',\n name,\n origin: text(live['origin']),\n targets: items(list(live['target']), same).sort(),\n});\n\nconst equal = (a: readonly string[], b: readonly string[]): boolean =>\n a.length === b.length && a.every((item, index) => item === b[index]);\n\nexport const matcherMatches = (\n attributes: NotificationMatcherAttributes,\n fields: NotificationMatcherFields,\n): boolean => {\n const rules = canonicalRules(fields);\n return (\n attributes.disable === (fields.disable === true) &&\n attributes['invert-match'] === (fields['invert-match'] === true) &&\n attributes.mode === (fields.mode ?? 'all') &&\n equal(attributes.targets, rules.targets) &&\n equal(attributes['match-severity'], rules['match-severity']) &&\n equal(attributes['match-field'], rules['match-field']) &&\n equal(attributes['match-calendar'], rules['match-calendar']) &&\n (fields.comment === undefined || attributes.comment === fields.comment)\n );\n};\n\ntype Deletable = NonNullable<ConfigNotificationsMatchersNamePutParams['delete']>[number];\ntype Lists = 'match-calendar' | 'match-field' | 'match-severity' | 'target';\n\n/**\n * The fields both forms send. ⚠️ LISTS GO AS ARRAYS — REPEATED KEYS ON THE WIRE (client.ts\n * `encode`) — because every list here is `type: array` on both servers, and a comma inside a\n * severity item or an `exact:` rule means a comma-joined string could not carry them anyway.\n */\nconst always = (fields: NotificationMatcherFields) => ({\n disable: fields.disable === true ? ('1' as const) : ('0' as const),\n 'invert-match': fields['invert-match'] === true ? ('1' as const) : ('0' as const),\n mode: fields.mode ?? ('all' as const),\n});\n\nconst lists = (fields: NotificationMatcherFields): Partial<Record<Lists, readonly string[]>> => {\n const rules = canonicalRules(fields);\n const out: Partial<Record<Lists, readonly string[]>> = {};\n if (rules['match-calendar'].length > 0) out['match-calendar'] = rules['match-calendar'];\n if (rules['match-field'].length > 0) out['match-field'] = rules['match-field'];\n if (rules['match-severity'].length > 0) out['match-severity'] = rules['match-severity'];\n if (rules.targets.length > 0) out.target = rules.targets;\n return out;\n};\n\nexport type MatcherCreateForm = ConfigNotificationsMatchersPostParams &\n ClusterNotificationsMatchersPostParams;\nexport type MatcherUpdateForm = ConfigNotificationsMatchersNamePutParams &\n ClusterNotificationsMatchersNamePutParams;\n\n/** ⛔ `name` is create-only: the body carries it once and the path forever after. */\nexport const matcherCreateForm = (fields: NotificationMatcherFields): MatcherCreateForm => ({\n ...always(fields),\n ...lists(fields),\n ...(fields.comment === undefined || fields.comment === '' ? {} : { comment: fields.comment }),\n name: fields.name,\n});\n\n/**\n * ⛔ AN EMPTIED LIST IS A `delete`, NOT AN EMPTY VALUE. A form cannot carry an empty array — no\n * key is sent at all — so without the `delete` entry a declaration of `'match-field': []`\n * would plan an update, PUT nothing for that field, and plan the same update forever.\n */\nexport const matcherUpdateForm = (fields: NotificationMatcherFields): MatcherUpdateForm => {\n const present = lists(fields);\n const cleared: Deletable[] = (\n ['match-calendar', 'match-field', 'match-severity', 'target'] as const\n ).filter((key) => present[key] === undefined);\n if (fields.comment === '') cleared.push('comment');\n return {\n ...always(fields),\n ...present,\n ...(fields.comment === undefined || fields.comment === '' ? {} : { comment: fields.comment }),\n ...(cleared.length === 0 ? {} : { delete: cleared }),\n };\n};\n",
|
|
66
|
-
"/**\n * `Proxmox.NotificationTarget` — where a failed backup goes to be seen.\n *\n * ★ THE FAILURE MODE THIS REMOVES IS SILENCE. A failed vzdump, a lost HA service and a broken\n * replication job all reach a human through one mechanism: a notification target. Out of the box\n * a cluster has exactly one, the built-in `mail-to-root` — one mailbox, on one node, delivered by\n * that node's local MTA. Declared, a failure becomes visible; undeclared, nothing looks wrong\n * until somebody goes looking for a backup that was never taken. Every other resource in this\n * package fails loudly. This is the one whose absence fails quietly.\n *\n * ⛔ THE PER-OBJECT READ GOES THROUGH `endpoints/{type}/{name}`, NEVER `targets/{name}`.\n * `GET /cluster/notifications/targets/{name}` answers \"Method not implemented\" — MEASURED, and\n * corroborated by the cluster's own apidoc, where that path node carries no methods at all.\n * `/targets` is a READ-ONLY UNION VIEW implementing the collection GET and `{name}/test` and\n * nothing else. So `type` is IDENTITY here, not a setting: it appears in both `path()` and\n * `collection()`, and changing it is a replace rather than an update.\n *\n * ⚠️ A BUILT-IN TARGET CAN BE ADOPTED AND STILL NOT BE OWNED. The shipped entries report\n * `origin: builtin` or `modified-builtin`, and a DELETE on one REVERTS it to shipped defaults\n * instead of removing it: Alchemy drops the resource from state while the target is still on the\n * cluster delivering mail. Check `origin` on `/cluster/notifications/targets` before declaring a\n * name you did not create. ⚠️ `origin` IS DELIBERATELY NOT AN ATTRIBUTE — the schema returns it\n * on the collection view only, never on the per-object read, so an attribute would read \"unknown\"\n * for every object forever, and an attribute that always lies is worse than a true comment.\n *\n * ⛔ NO SECRET IS A PROP, LET ALONE AN ATTRIBUTE. smtp `password`, gotify `token` and webhook\n * `secret` are write-only and none is declarable here. Props are persisted too, not just\n * attributes — `delete` is handed `olds`, which can only have come from the state store — so a\n * secret prop would sit unencrypted in the state Postgres for as long as the resource exists.\n * ⚠️ THE COST IS REAL: `token` is REQUIRED on a gotify create, so a gotify target cannot be\n * created from here at all. Create one out of band with its secret and then declare it — the\n * read finds it, reconcile takes the PUT path, and PVE leaves any field the body does not\n * mention alone. The secret half stays a human's; the rest is declared. Same for an\n * authenticated smtp target and a signed webhook.\n *\n * ★ WHAT THIS NEEDS, READ OFF THE CLUSTER'S OWN SCHEMA:\n * read/diff GET endpoints/{type}/{name} Mapping.Audit (or Mapping.Modify) on\n * /mapping/notifications\n * reconcile POST + PUT Mapping.Modify on /mapping/notifications AND one of\n * Sys.Audit / Sys.Modify / Sys.AccessNetwork on /\n * delete DELETE Mapping.Modify on /mapping/notifications\n * The provision role already carried Sys.Audit on `/` (see lxc.ts), so the delta was the two\n * Mapping privileges — notably NOT the `Sys.Modify` a backup job costs. `PROVISION_PRIVILEGES`\n * carries both on `/`: the baseline took the trade below knowingly.\n * ⚠️ PREFER A SECOND ROLE GRANTED AT `/mapping/notifications` OVER WIDENING THE PROVISIONING ROLE\n * ON `/`. `/mapping` also holds the PCI and USB passthrough maps, so Mapping.Modify at the root\n * lets a credential scoped to make containers rewire somebody's hardware.\n * ⛔ A MISSING READ PRIVILEGE DOES NOT LOOK LIKE A 403. `pveOperations.read` ends in\n * `orElseSucceed(() => undefined)`, so a 403 on the GET is indistinguishable from \"absent\": the\n * plan says create, PVE answers \"already exists\", and the fault was an ACL all along. That pair\n * is the signature — grant Mapping.Audit to the READ role before changing anything else.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { addressList, headerList, portOf, shape } from './notification-target-form.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { text } from './values.ts';\n\n/** The four families PVE ships. `GET /cluster/notifications/endpoints` lists exactly these. */\nexport type NotificationTargetType = 'gotify' | 'sendmail' | 'smtp' | 'webhook';\n\n/**\n * ⚠️ PVE'S OWN KEY NAMES, DASHES INCLUDED, for the reason backup-job.ts gives: the form is then a\n * copy rather than a translation table, and a translation table is one more place for a key to be\n * renamed and silently never sent.\n *\n * ⚠️ A FIELD THAT DOES NOT BELONG TO `type` IS STILL SENT, and PVE refuses it by name. That 400\n * beats this file quietly dropping a `mailto` somebody wrote on a webhook.\n */\nexport interface NotificationTargetProps extends WithTarget {\n /** ⛔ IDENTITY: it picks the family AND the URL, so changing it is a replace. */\n type: NotificationTargetType;\n /**\n * PVE's primary key, unique ACROSS ALL FOUR FAMILIES: a webhook cannot be called `mail-to-root`,\n * because a sendmail target already is. Format `pve-configid`.\n */\n name: string;\n /** Free text in the UI. */\n comment?: string;\n /** Configured but not delivering. Always sent and always compared — see `shape`. */\n disable?: boolean;\n /** sendmail, smtp: recipients. ⚠️ Order is not meaning — see `addressList`. */\n mailto?: readonly string[];\n /** sendmail, smtp: PVE users, whose own configured address receives the mail. */\n 'mailto-user'?: readonly string[];\n /** smtp: the SMTP host. gotify: the server URL. PVE requires it on create for both. */\n server?: string;\n /** smtp only. Unset lets PVE pick from `mode`: 465 tls, 587 starttls, 25 insecure. */\n port?: number;\n /** smtp only. PVE's schema default is `tls`. */\n mode?: 'insecure' | 'starttls' | 'tls';\n /** smtp only. ⛔ The matching `password` is not declarable — see the header. */\n username?: string;\n /** smtp: PVE requires it on create. sendmail: optional. */\n 'from-address'?: string;\n /** Display name on the mail. PVE's smtp schema defaults it to `Proxmox VE`. */\n author?: string;\n /** webhook: PVE requires it on create. */\n url?: string;\n /** webhook: PVE requires it on create. Lower case — PVE's enum is `post|put|get`. */\n method?: 'get' | 'post' | 'put';\n /**\n * webhook: `name=<name>,value=<base64 of value>` property strings, as PVE stores them.\n * ⚠️ MORE THAN ONE HEADER IS THE ONE THING HERE NOT PROVEN ON A WIRE — see `REPEATED_KEY`.\n */\n header?: readonly string[];\n /**\n * webhook: the request body, base64, as PVE stores it.\n * ⚠️ THE UI SHOWS THE DECODED BODY AND THIS DOES NOT. Declaring the base64 is what keeps\n * `matches` symmetric with the read; decoding for display would mean encoding on write, and a\n * round trip PVE normalises differently is a forever-update. The cost is an unreadable diff.\n */\n body?: string;\n}\n\n/**\n * ⚠️ EVERY FIELD IS PRESENT FOR EVERY FAMILY, with `''` / `0` meaning \"PVE holds nothing\". A\n * webhook reads back an empty `mailto`, which is also what a webhook declares, so the\n * cross-family fields cost one comparison each and never a false update.\n */\nexport interface NotificationTargetAttributes {\n name: string;\n type: NotificationTargetType;\n comment: string;\n disable: boolean;\n /** Sorted and comma-joined, so re-ordering a declaration is not a diff. See `addressList`. */\n mailto: string;\n 'mailto-user': string;\n server: string;\n /** 0 means PVE holds no port and will pick one from `mode`. */\n port: number;\n mode: string;\n username: string;\n 'from-address': string;\n author: string;\n url: string;\n method: string;\n /** Sorted and comma-joined for COMPARISON only; the wire form differs. See `headerList`. */\n header: string;\n body: string;\n}\n\nexport interface ProxmoxNotificationTarget extends Resource<\n 'Proxmox.NotificationTarget',\n NotificationTargetProps,\n NotificationTargetAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxNotificationTarget = Resource<ProxmoxNotificationTarget>(\n 'Proxmox.NotificationTarget',\n);\n\nconst handlers = pveHandlers<NotificationTargetProps, NotificationTargetAttributes>({\n /**\n * ⛔ NAMED FIELDS, NEVER A SPREAD OF `live`. gotify's `token` and smtp's `password` really are\n * absent from their read schemas — but webhook's read schema DECLARES `secret`, so \"PVE will\n * not hand it to us\" is not a defence. Listing what is kept is what keeps secret material out\n * of a state row, whatever a future PVE decides to start returning.\n */\n attributes: (live, props) => ({\n author: text(live['author']),\n body: text(live['body']),\n comment: text(live['comment']),\n disable: live['disable'] === 1 || live['disable'] === true,\n 'from-address': text(live['from-address']),\n header: headerList(live['header']),\n mailto: addressList(live['mailto']),\n 'mailto-user': addressList(live['mailto-user']),\n method: text(live['method']),\n mode: text(live['mode']),\n name: props.name,\n port: portOf(live['port']),\n server: text(live['server']),\n type: props.type,\n url: text(live['url']),\n username: text(live['username']),\n }),\n collection: (props) => `cluster/notifications/endpoints/${props.type}`,\n /** ⛔ `name` is create-only: PVE takes it in the body once, and in the path forever after. */\n createForm: (props) => ({ ...shape(props), name: props.name }),\n /**\n * Each line reads \"not declared, or equal\"; `disable` is the one field with no undeclared case.\n *\n * ⚠️ IF A PLAN NEVER SETTLES, SUSPECT A SCHEMA DEFAULT COMING BACK AS A VALUE. PVE defaults smtp\n * `mode` to `tls` and smtp `author` to `Proxmox VE`, and applies both when the mail is sent\n * rather than writing them into the config — so an undeclared field reads back empty and its\n * line is skipped. A version that STORED them would answer with a field nobody declared. The\n * fix is to declare the field, never to stop comparing it.\n */\n matches: (attributes, props) =>\n attributes.disable === (props.disable === true) &&\n (props.comment === undefined || attributes.comment === props.comment) &&\n (props.server === undefined || attributes.server === props.server) &&\n (props.port === undefined || attributes.port === props.port) &&\n (props.mode === undefined || attributes.mode === props.mode) &&\n (props.username === undefined || attributes.username === props.username) &&\n (props['from-address'] === undefined || attributes['from-address'] === props['from-address']) &&\n (props.author === undefined || attributes.author === props.author) &&\n (props.url === undefined || attributes.url === props.url) &&\n (props.method === undefined || attributes.method === props.method) &&\n (props.body === undefined || attributes.body === props.body) &&\n (props.mailto === undefined || attributes.mailto === addressList(props.mailto)) &&\n (props['mailto-user'] === undefined ||\n attributes['mailto-user'] === addressList(props['mailto-user'])) &&\n (props.header === undefined || attributes.header === headerList(props.header)),\n path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,\n updateForm: shape,\n});\n\n/**\n * ⚠️ ON A BUILT-IN TARGET A DELETE REVERTS RATHER THAN REMOVES, and PVE reports success either way.\n * It is passed through unguarded because everywhere else in this package the cluster's answer is\n * the answer — but this is the one case where a green plan and the cluster disagree, and the\n * object left standing is the one carrying the estate's failure mail.\n */\nexport const ProxmoxNotificationTargetProvider = () =>\n Provider.effect(\n ProxmoxNotificationTarget,\n Effect.succeed(ProxmoxNotificationTarget.Provider.of(handlers)),\n );\n",
|
|
67
|
-
"/**\n *
|
|
80
|
+
"/**\n * `Proxmox.NotificationTarget` — where a failed backup goes to be seen.\n *\n * ★ THE FAILURE MODE THIS REMOVES IS SILENCE. A failed vzdump, a lost HA service and a broken\n * replication job all reach a human through one mechanism: a notification target. Out of the box\n * a cluster has exactly one, the built-in `mail-to-root` — one mailbox, on one node, delivered by\n * that node's local MTA. Declared, a failure becomes visible; undeclared, nothing looks wrong\n * until somebody goes looking for a backup that was never taken. Every other resource in this\n * package fails loudly. This is the one whose absence fails quietly.\n *\n * ⛔ THE PER-OBJECT READ GOES THROUGH `endpoints/{type}/{name}`, NEVER `targets/{name}`.\n * `GET /cluster/notifications/targets/{name}` answers \"Method not implemented\" — MEASURED, and\n * corroborated by the cluster's own apidoc, where that path node carries no methods at all.\n * `/targets` is a READ-ONLY UNION VIEW implementing the collection GET and `{name}/test` and\n * nothing else. So `type` is IDENTITY here, not a setting: it appears in both `path()` and\n * `collection()`, and changing it is a replace rather than an update.\n *\n * ⚠️ A BUILT-IN TARGET CAN BE ADOPTED AND STILL NOT BE OWNED. The shipped entries report\n * `origin: builtin` or `modified-builtin`, and a DELETE on one REVERTS it to shipped defaults\n * instead of removing it: Alchemy drops the resource from state while the target is still on the\n * cluster delivering mail. Check `origin` on `/cluster/notifications/targets` before declaring a\n * name you did not create. ⚠️ `origin` IS DELIBERATELY NOT AN ATTRIBUTE — the schema returns it\n * on the collection view only, never on the per-object read, so an attribute would read \"unknown\"\n * for every object forever, and an attribute that always lies is worse than a true comment.\n *\n * ⛔ NO SECRET IS A PROP, LET ALONE AN ATTRIBUTE. smtp `password`, gotify `token` and webhook\n * `secret` are write-only and none is declarable here. Props are persisted too, not just\n * attributes — `delete` is handed `olds`, which can only have come from the state store — so a\n * secret prop would sit unencrypted in the state Postgres for as long as the resource exists.\n * ⚠️ THE COST IS REAL: `token` is REQUIRED on a gotify create, so a gotify target cannot be\n * created from here at all. Create one out of band with its secret and then declare it — the\n * read finds it, reconcile takes the PUT path, and PVE leaves any field the body does not\n * mention alone. The secret half stays a human's; the rest is declared. Same for an\n * authenticated smtp target and a signed webhook.\n *\n * ★ WHAT THIS NEEDS, READ OFF THE CLUSTER'S OWN SCHEMA:\n * read/diff GET endpoints/{type}/{name} Mapping.Audit (or Mapping.Modify) on\n * /mapping/notifications\n * reconcile POST + PUT Mapping.Modify on /mapping/notifications AND one of\n * Sys.Audit / Sys.Modify / Sys.AccessNetwork on /\n * delete DELETE Mapping.Modify on /mapping/notifications\n * The provision role already carried Sys.Audit on `/` (see lxc.ts), so the delta was the two\n * Mapping privileges — notably NOT the `Sys.Modify` a backup job costs. `PROVISION_PRIVILEGES`\n * carries both on `/`: the baseline took the trade below knowingly.\n * ⚠️ PREFER A SECOND ROLE GRANTED AT `/mapping/notifications` OVER WIDENING THE PROVISIONING ROLE\n * ON `/`. `/mapping` also holds the PCI and USB passthrough maps, so Mapping.Modify at the root\n * lets a credential scoped to make containers rewire somebody's hardware.\n * ⛔ A MISSING READ PRIVILEGE DOES NOT LOOK LIKE A 403. `pveOperations.read` ends in\n * `orElseSucceed(() => undefined)`, so a 403 on the GET is indistinguishable from \"absent\": the\n * plan says create, PVE answers \"already exists\", and the fault was an ACL all along. That pair\n * is the signature — grant Mapping.Audit to the READ role before changing anything else.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { targetEndpoint } from './notification-target-endpoint.ts';\nimport { addressList, createShape, headerList, portOf, shape } from './notification-target-form.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { text } from './values.ts';\n\n/** The four families PVE ships. `GET /cluster/notifications/endpoints` lists exactly these. */\nexport type NotificationTargetType = 'gotify' | 'sendmail' | 'smtp' | 'webhook';\n\n/**\n * ⚠️ PVE'S OWN KEY NAMES, DASHES INCLUDED, for the reason backup-job.ts gives: the form is then a\n * copy rather than a translation table, and a translation table is one more place for a key to be\n * renamed and silently never sent.\n *\n * ⚠️ A FIELD THAT DOES NOT BELONG TO `type` IS STILL SENT, and PVE refuses it by name. That 400\n * beats this file quietly dropping a `mailto` somebody wrote on a webhook.\n */\nexport interface NotificationTargetProps extends WithTarget {\n /** ⛔ IDENTITY: it picks the family AND the URL, so changing it is a replace. */\n type: NotificationTargetType;\n /**\n * PVE's primary key, unique ACROSS ALL FOUR FAMILIES: a webhook cannot be called `mail-to-root`,\n * because a sendmail target already is. Format `pve-configid`.\n */\n name: string;\n /** Free text in the UI. */\n comment?: string;\n /** Configured but not delivering. Always sent and always compared — see `shape`. */\n disable?: boolean;\n /** sendmail, smtp: recipients. ⚠️ Order is not meaning — see `addressList`. */\n mailto?: readonly string[];\n /** sendmail, smtp: PVE users, whose own configured address receives the mail. */\n 'mailto-user'?: readonly string[];\n /** smtp: the SMTP host. gotify: the server URL. PVE requires it on create for both. */\n server?: string;\n /** smtp only. Unset lets PVE pick from `mode`: 465 tls, 587 starttls, 25 insecure. */\n port?: number;\n /** smtp only. PVE's schema default is `tls`. */\n mode?: 'insecure' | 'starttls' | 'tls';\n /** smtp only. ⛔ The matching `password` is not declarable — see the header. */\n username?: string;\n /** smtp: PVE requires it on create. sendmail: optional. */\n 'from-address'?: string;\n /** Display name on the mail. PVE's smtp schema defaults it to `Proxmox VE`. */\n author?: string;\n /** webhook: PVE requires it on create. */\n url?: string;\n /** webhook: PVE requires it on create. Lower case — PVE's enum is `post|put|get`. */\n method?: 'get' | 'post' | 'put';\n /**\n * webhook: `name=<name>,value=<base64 of value>` property strings, as PVE stores them.\n * ⚠️ MORE THAN ONE HEADER IS THE ONE THING HERE NOT PROVEN ON A WIRE — see `REPEATED_KEY`.\n */\n header?: readonly string[];\n /**\n * webhook: the request body, base64, as PVE stores it.\n * ⚠️ THE UI SHOWS THE DECODED BODY AND THIS DOES NOT. Declaring the base64 is what keeps\n * `matches` symmetric with the read; decoding for display would mean encoding on write, and a\n * round trip PVE normalises differently is a forever-update. The cost is an unreadable diff.\n */\n body?: string;\n}\n\n/**\n * ⚠️ EVERY FIELD IS PRESENT FOR EVERY FAMILY, with `''` / `0` meaning \"PVE holds nothing\". A\n * webhook reads back an empty `mailto`, which is also what a webhook declares, so the\n * cross-family fields cost one comparison each and never a false update.\n */\nexport interface NotificationTargetAttributes {\n name: string;\n type: NotificationTargetType;\n comment: string;\n disable: boolean;\n /** Sorted and comma-joined, so re-ordering a declaration is not a diff. See `addressList`. */\n mailto: string;\n 'mailto-user': string;\n server: string;\n /** 0 means PVE holds no port and will pick one from `mode`. */\n port: number;\n mode: string;\n username: string;\n 'from-address': string;\n author: string;\n url: string;\n method: string;\n /** Sorted and comma-joined for COMPARISON only; the wire form differs. See `headerList`. */\n header: string;\n body: string;\n}\n\nexport interface ProxmoxNotificationTarget extends Resource<\n 'Proxmox.NotificationTarget',\n NotificationTargetProps,\n NotificationTargetAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxNotificationTarget = Resource<ProxmoxNotificationTarget>(\n 'Proxmox.NotificationTarget',\n);\n\nconst handlers = pveHandlers<NotificationTargetProps, NotificationTargetAttributes>({\n /**\n * ⛔ NAMED FIELDS, NEVER A SPREAD OF `live`. gotify's `token` and smtp's `password` really are\n * absent from their read schemas — but webhook's read schema DECLARES `secret`, so \"PVE will\n * not hand it to us\" is not a defence. Listing what is kept is what keeps secret material out\n * of a state row, whatever a future PVE decides to start returning.\n */\n attributes: (live, props) => ({\n author: text(live['author']),\n body: text(live['body']),\n comment: text(live['comment']),\n disable: live['disable'] === 1 || live['disable'] === true,\n 'from-address': text(live['from-address']),\n header: headerList(live['header']),\n mailto: addressList(live['mailto']),\n 'mailto-user': addressList(live['mailto-user']),\n method: text(live['method']),\n mode: text(live['mode']),\n name: props.name,\n port: portOf(live['port']),\n server: text(live['server']),\n type: props.type,\n url: text(live['url']),\n username: text(live['username']),\n }),\n collection: (props) => `cluster/notifications/endpoints/${props.type}`,\n /** ⛔ `name` is create-only: PVE takes it in the body once, and in the path forever after. */\n createForm: createShape,\n /** ⚠️ A function, because the type picks the endpoint — see `targetEndpoint`. */\n endpoint: targetEndpoint,\n /**\n * Each line reads \"not declared, or equal\"; `disable` is the one field with no undeclared case.\n *\n * ⚠️ IF A PLAN NEVER SETTLES, SUSPECT A SCHEMA DEFAULT COMING BACK AS A VALUE. PVE defaults smtp\n * `mode` to `tls` and smtp `author` to `Proxmox VE`, and applies both when the mail is sent\n * rather than writing them into the config — so an undeclared field reads back empty and its\n * line is skipped. A version that STORED them would answer with a field nobody declared. The\n * fix is to declare the field, never to stop comparing it.\n */\n matches: (attributes, props) =>\n attributes.disable === (props.disable === true) &&\n (props.comment === undefined || attributes.comment === props.comment) &&\n (props.server === undefined || attributes.server === props.server) &&\n (props.port === undefined || attributes.port === props.port) &&\n (props.mode === undefined || attributes.mode === props.mode) &&\n (props.username === undefined || attributes.username === props.username) &&\n (props['from-address'] === undefined || attributes['from-address'] === props['from-address']) &&\n (props.author === undefined || attributes.author === props.author) &&\n (props.url === undefined || attributes.url === props.url) &&\n (props.method === undefined || attributes.method === props.method) &&\n (props.body === undefined || attributes.body === props.body) &&\n (props.mailto === undefined || attributes.mailto === addressList(props.mailto)) &&\n (props['mailto-user'] === undefined ||\n attributes['mailto-user'] === addressList(props['mailto-user'])) &&\n (props.header === undefined || attributes.header === headerList(props.header)),\n path: (props) => `cluster/notifications/endpoints/${props.type}/${props.name}`,\n updateForm: shape,\n});\n\n/**\n * ⚠️ ON A BUILT-IN TARGET A DELETE REVERTS RATHER THAN REMOVES, and PVE reports success either way.\n * It is passed through unguarded because everywhere else in this package the cluster's answer is\n * the answer — but this is the one case where a green plan and the cluster disagree, and the\n * object left standing is the one carrying the estate's failure mail.\n */\nexport const ProxmoxNotificationTargetProvider = () =>\n Provider.effect(\n ProxmoxNotificationTarget,\n Effect.succeed(ProxmoxNotificationTarget.Provider.of(handlers)),\n );\n",
|
|
81
|
+
"/**\n * `Proxmox.NotificationTarget`'s eight vendor endpoints — one create and one update per family.\n *\n * ⛔ PVE REGISTERS ONE PATH PER TYPE, NOT A `{type}` PARAMETER, AND THE FOUR SCHEMAS DIFFER IN\n * WHAT THEY REQUIRE: gotify wants `server` and `token`, smtp `server` and `from-address`,\n * webhook `url` and `method`, sendmail nothing but its name. One key for all four would enforce\n * gotify's rules on a sendmail target and let three families through unchecked, which is why\n * `PveSpec['endpoint']` admits a function of props.\n *\n * ⛔ THE STRINGS ARE LITERALS AND MUST STAY LITERALS. `codegen/constraints.ts` finds the endpoints\n * to table by SCANNING THIS PACKAGE'S TEXT, so `` `pve:POST …/${props.type}` `` would be tabled\n * by nothing and `constraintsFor` would throw on the first deploy that reached it.\n *\n * ⚠️ GOTIFY'S CREATE CAN NEVER SATISFY ITS OWN TABLE, AND THAT IS THE TRUTH RATHER THAN A BUG\n * HERE. `token` is required by PVE and is a write-only secret, so it is not a prop — props are\n * persisted to the state store unencrypted (the third ⛔ in notification-target.ts). A gotify\n * target is created out of band and then DECLARED, which is an adopt followed by updates; the\n * shared guard only demands the create form's required parameters when a create is really about\n * to happen (`resource-guard.ts`), so declaring an existing gotify target still plans clean and\n * asking to CREATE one is refused at plan time with `token: required` instead of by a 400.\n */\nimport type { NotificationTargetProps } from './notification-target.ts';\nimport type { EndpointPair } from './resource-spec.ts';\n\nconst ENDPOINTS: Readonly<Record<NotificationTargetProps['type'], EndpointPair>> = {\n gotify: {\n create: 'pve:POST /cluster/notifications/endpoints/gotify',\n update: 'pve:PUT /cluster/notifications/endpoints/gotify/{name}',\n },\n sendmail: {\n create: 'pve:POST /cluster/notifications/endpoints/sendmail',\n update: 'pve:PUT /cluster/notifications/endpoints/sendmail/{name}',\n },\n smtp: {\n create: 'pve:POST /cluster/notifications/endpoints/smtp',\n update: 'pve:PUT /cluster/notifications/endpoints/smtp/{name}',\n },\n webhook: {\n create: 'pve:POST /cluster/notifications/endpoints/webhook',\n update: 'pve:PUT /cluster/notifications/endpoints/webhook/{name}',\n },\n};\n\n/** The pair this declaration writes. ⚠️ `type` is identity here, so it never changes under a row. */\nexport const targetEndpoint = (props: NotificationTargetProps): EndpointPair =>\n ENDPOINTS[props.type];\n",
|
|
82
|
+
"/**\n * A notification target's props, as the form PVE wants — including the one field in this package\n * that cannot be spelled as a comma list.\n *\n * ★ SPLIT OUT OF notification-target.ts TO KEEP BOTH UNDER THE 250-LINE CAP. The seam is the same\n * one metric-server uses: this file turns a declaration into a form, and the resource file says\n * what a target IS and when it has changed. The `import type` back is erased, so it is a cycle\n * on paper only.\n */\nimport type { NotificationTargetProps } from './notification-target.ts';\nimport { text } from './values.ts';\n\n/**\n * ⛔ A MULTI-VALUED PARAMETER GOES ON THE WIRE DIFFERENTLY HERE THAN ANYWHERE ELSE IN THIS PACKAGE,\n * AND THE DIFFERENCE IS MEASURED. Every other PVE endpoint spells a list `type: string, format:\n * pve-configid-list` — comma-joined, which is what sdn-vnet.ts sends. On the notification\n * endpoints `mailto`, `mailto-user`, `header` and `delete` are `type: array`, and PVE's own web\n * UI submits those as REPEATED KEYS (`header=a&header=b`). `Record<string, string>` cannot repeat\n * a key, so one value carrying a NUL goes instead: it percent-encodes to `%00`, and PVE's\n * urlencoded decoder joins repeated keys with exactly that byte, rebuilding the same parameter.\n * ⚠️ ONLY `header` NEEDS IT, and only past one entry. A header item is ITSELF the property string\n * `name=<name>,value=<base64>`, so a comma cannot express even one of them. Addresses hold no\n * commas and PVE's UI submits `mailto` as a comma-separated text field, so those go that way.\n * ⚠️ REASONED FROM THE DECODER AND THE UI, NOT PROVEN BY A LIVE MULTI-HEADER WRITE. One header\n * carries no separator and is unaffected either way; a longer list, if rejected, fails with a\n * 400 naming `header` rather than quietly dropping one, and the fix is one line here.\n */\nconst REPEATED_KEY = '\\0';\n\n/** ⚠️ `0` is \"PVE holds no port\". Integers have come back as strings elsewhere, so parse both. */\nexport const portOf = (value: unknown): number => {\n const parsed = typeof value === 'number' ? value : Number.parseInt(String(value ?? ''), 10);\n return Number.isNaN(parsed) ? 0 : parsed;\n};\n\nconst sorted = (parts: readonly string[]): string =>\n parts\n .map((part) => part.trim())\n .filter((part) => part !== '')\n .sort()\n .join(',');\n\n/**\n * ⚠️ ORDER IS NOT MEANING IN A RECIPIENT LIST, SO IT MUST NOT BE A DIFF — backup-job.ts's reasoning\n * about guest ids, applied to addresses. Both sides are sorted before they meet.\n *\n * ⚠️ PVE ANSWERS WITH AN ARRAY AND ACCEPTS A DELIMITED STRING, and this package has already been\n * bitten by a list coming back flattened on one version and structured on another (`groups` in\n * user.ts). PVE's UI calls `mailto` \"separated by spaces, commas or semicolons\", so all three are\n * split here and both shapes funnel into one comparable value.\n */\nexport const addressList = (value: unknown): string =>\n sorted(\n Array.isArray(value)\n ? value.map((item: unknown) => String(item))\n : typeof value === 'string'\n ? value.split(/[\\s,;]+/)\n : [],\n );\n\n/** ⛔ NEVER SPLIT ON A COMMA: a header item contains one. A lone string is one item, not a list. */\nexport const headerList = (value: unknown): string =>\n sorted(Array.isArray(value) ? value.map((item: unknown) => String(item)) : [text(value)]);\n\nconst set = (key: string, value: string | undefined): Record<string, string> =>\n value === undefined ? {} : { [key]: value };\n\n/**\n * The form for both create and update.\n *\n * ⛔ A FIELD THE DECLARATION LEAVES OUT IS NOT SENT AND NOT COMPARED — backup-job.ts's rule rather\n * than sdn-vnet.ts's `delete` list, and here the choice is not close. That list would have to\n * name `mailto`, so a target adopted from the UI and declared without its recipients would have\n * them stripped on the first deploy: the one object that makes failures visible, turned back into\n * silence. `delete` on this family is also `type: array` rather than the comma string every other\n * endpoint takes, so sdn-vnet's encoding would not even carry.\n * ⚠️ THE PRICE IS THAT REMOVING A LINE DOES NOT CLEAR A FIELD. Declare it as `''` to clear it,\n * which IS sent: an empty string is a value, an absent prop is no request at all.\n *\n * ⚠️ `disable` IS ALWAYS SENT AND ALWAYS COMPARED, `0` INCLUDED. PVE documents the default as 0, so\n * sending it explicitly is what makes \"not declared\" and \"declared false\" one state instead of a\n * plan that asks for an update the PUT never performs. It is also the drift that matters most\n * here: a target disabled by hand during a maintenance window and never re-enabled looks exactly\n * like a healthy cluster.\n */\nexport const shape = (props: NotificationTargetProps): Record<string, string> => ({\n disable: props.disable === true ? '1' : '0',\n ...set('author', props.author),\n ...set('body', props.body),\n ...set('comment', props.comment),\n ...set('from-address', props['from-address']),\n ...set('header', props.header === undefined ? undefined : props.header.join(REPEATED_KEY)),\n ...set('mailto', props.mailto === undefined ? undefined : addressList(props.mailto)),\n ...set(\n 'mailto-user',\n props['mailto-user'] === undefined ? undefined : addressList(props['mailto-user']),\n ),\n ...set('method', props.method),\n ...set('mode', props.mode),\n ...set('port', props.port === undefined ? undefined : String(props.port)),\n ...set('server', props.server),\n ...set('url', props.url),\n ...set('username', props.username),\n});\n\n/**\n * The create form: `shape` plus the name PVE takes in the body exactly once.\n *\n * ★ EXPORTED SO THE CONSTRAINT PROOF CAN RUN THE REAL FORM rather than a retyped copy of it.\n * ⛔ `name` IS CREATE-ONLY: PVE takes it in the body on the POST and in the path forever after.\n */\nexport const createShape = (props: NotificationTargetProps): Record<string, string> => ({\n ...shape(props),\n name: props.name,\n});\n",
|
|
68
83
|
"/**\n * `Pbs.Datastore` — one section of Proxmox Backup Server's `datastore.cfg`, declared.\n *\n * ★ PBS IS A SIBLING OF PVE, NOT A PART OF IT. The estate runs both — PVE carries a storage of\n * `type: pbs` — so declaring the datastore turns `Proxmox.Storage`'s `locator.datastore` from a\n * string nothing checks into a reference Alchemy can order, as `Proxmox.SdnVnet` did for a bridge.\n *\n * ⛔ DIFFERENT HOST, DIFFERENT REALM, DIFFERENT AUTH HEADER. MEASURED 2026-09-13, strict TLS, no\n * `-k` anywhere: `GET https://pbs.example.com:8007/api2/json/ping` answers 200\n * `{\"data\":{\"pong\":true}}` against a Let's Encrypt certificate for CN=pbs.example.com;\n * `/version` and `/config/datastore` answer 401 \"authentication failed\";\n * `pbs.mgmt.example.com:8007` answers 401 and its certificate verifies too; and\n * `/access/domains` lists the realms `pbs`, `pam` and an openid realm. So the\n * prefix, the `{\"data\": …}` envelope and the strict-TLS story are PVE's exactly — and only the\n * credential differs. There is no `pve` realm here; a PVE token cannot authenticate at all.\n * The header differs too, which is why `pve()` exists rather than `pve()` being reused — see the\n * ⛔ on `pbsAuthorization` in pbs-datastore-form.ts.\n *\n * ⛔ THERE IS NO OPENBAO MOUNT FOR PBS TODAY, SO NOTHING HERE CAN DEPLOY YET. `PbsTarget.mount` is\n * a parameter for the reason `PveTarget.mount` is — the provider must be able to leave this\n * estate — but the estate has nothing to put in it. MEASURED: under the agent's approle,\n * `bao token capabilities` answers `deny` for `proxmox-c1/creds/{read,provision}`,\n * `proxmox-c2/creds/read` and every `…pbs…` path tried, and `sys/mounts` is 403 — so I could\n * not enumerate the mounts to PROVE a PBS one is absent. That it is absent is the brief's\n * statement, not my measurement.\n * ★ WHAT THE MOUNT MUST VEND, so `mint()` is reused unchanged: `bao read -format=json\n * <mount>/creds/<role>` answering `{\"data\":{\"token_id\":…,\"secret\":…}}`, where `token_id` is a\n * PBS token id `user@realm!tokenname` — `hf-read@pbs!…` and `hf-provision@pbs!…`. Same short,\n * non-renewable lease contract as `proxmox-c1`.\n * ⚠️ PBS HAS NO DYNAMIC-SECRETS PLUGIN OF ITS OWN — OpenBao's Proxmox support is a PVE thing — so\n * the mount is a small custom vendor against `POST /access/users/{id}/token/{name}`. Say which\n * mount and role you needed rather than reaching for the read-only `metrics@pbs` token on\n * the kv shelf: widening that would delete the outer lock for every reader of the shelf.\n *\n * ⛔ A DATASTORE HOLDS THE BACKUPS, SO `retain` IS THE DEFAULT AND `delete` IS FULLY IMPLEMENTED —\n * the ★ on removal policy in resource.ts has the whole reasoning. A stub returning `Effect.void`\n * would report a deletion that never happened, and this is the family where that lie costs most.\n *\n * ⚠️ PRIVILEGES, READ OFF THE PUBLISHED SCHEMA RATHER THAN OFF THIS HOST:\n * read GET config/datastore/{name} Datastore.Audit on /datastore/{name}\n * create POST config/datastore Datastore.Allocate on /datastore\n * update PUT config/datastore/{name} Datastore.Modify on /datastore/{name}\n * delete DELETE config/datastore/{name} Datastore.Allocate on /datastore/{name}\n * Built-in `DatastoreAdmin` at `/datastore` covers all four; `DatastoreAudit` covers the read\n * lane alone, which is what the `read` role should hold.\n *\n * ⛔ NO SECRET IS A PROP OR AN ATTRIBUTE, AND THIS FAMILY IS CLEAN BY CONSTRUCTION: a datastore\n * section holds no password, token or key — encryption keys belong to the backup CLIENT. Every\n * field below is a name, a path, a schedule, a count or a policy string, all of which are safe in\n * a state store Alchemy writes UNENCRYPTED and this estate copies to a backup guest nightly.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { pve } from './client.ts';\nimport {\n type PbsDatastoreAttributes,\n type PbsTarget,\n createForm,\n matches,\n object,\n readOne,\n updateForm,\n} from './pbs-datastore-form.ts';\nimport { guardBackend, guardPath, settle } from './pbs-datastore-guard.ts';\nimport { guardDatastoreForms } from './pbs-datastore-endpoint.ts';\nimport type { PveRequirements } from './resource.ts';\nimport { formToSend } from './update-guard.ts';\n\nexport type { PbsDatastoreAttributes, PbsTarget };\n\n/**\n * ⚠️ PBS'S OWN KEY NAMES, HYPHENS INCLUDED — backup-job.ts's rule: the form is then a copy rather\n * than a translation table, and a translation table is one more place for a key to be renamed and\n * silently never sent.\n * ⚠️ AN UNDECLARED FIELD IS NEITHER SENT NOR COMPARED — storage.ts's rule, not sdn-vnet.ts's, and\n * for a retention policy the choice is not close: see the ⛔ on `updateForm`.\n */\nexport interface PbsDatastoreProps {\n target: PbsTarget;\n /** PBS's primary key. Create-only: there is no rename, and a new name is a new datastore. */\n name: string;\n /**\n * Absolute path to the datastore directory.\n *\n * ⛔ CREATE-ONLY, AND THE ONE FIELD THIS FILE REFUSES TO LET DRIFT QUIETLY. PBS's update endpoint\n * does not accept `path`, so comparing it in `matches` could only plan an update no PUT can\n * apply — the forever-diff storage.ts avoids by letting its create-only `type` plan as NOOP.\n * Here a silent noop is the wrong trade: the operator would read a green plan while PBS kept\n * writing backups to the old directory. So `diff` and `reconcile` compare it themselves and DIE\n * with both values named. A path change is remove-and-redeclare, deliberately.\n */\n path: string;\n comment?: string;\n /** Calendar event for garbage collection, e.g. `daily` or `sat 18:15`. */\n 'gc-schedule'?: string;\n /** Calendar event for the built-in prune job. Retention itself is the `keep-*` fields below. */\n 'prune-schedule'?: string;\n /** ⚠️ PBS's minimum is 1 for every keep-*. A declared `0` is refused, and does not mean \"off\". */\n 'keep-last'?: number;\n 'keep-hourly'?: number;\n 'keep-daily'?: number;\n 'keep-weekly'?: number;\n 'keep-monthly'?: number;\n 'keep-yearly'?: number;\n /**\n * Legacy notification routing: `gc=<never|always|error>`, and the same for `verify` and `sync`.\n * ⚠️ PBS 3.2+ picks between this and the notification system with `notification-mode`, which is\n * NOT declarable here: flipping a live datastore between the two silently changes who hears\n * about a failed verify, and silence is the failure mode that costs most (notification-target.ts).\n */\n notify?: string;\n /** PBS user id legacy notifications go to. PBS's schema default is `root@pam`. */\n 'notify-user'?: string;\n /** Verify every new backup the moment it completes. PBS's default is false. */\n 'verify-new'?: boolean;\n /** Property string: `chunk-order=<none|inode>,sync-level=<none|file|filesystem>`, and more. */\n tuning?: string;\n /**\n * Takes the datastore out of service: `type=<offline|read-only|unmount>`, optional\n * `message=<text>`. PBS's default key is `type`, so the bare `offline` normalises to the same\n * string here and either spelling is safe.\n * ⛔ NEVER DECLARE `type=delete`. PBS sets that itself while destroying a datastore's contents.\n * ⚠️ A `message` CONTAINING A COMMA BREAKS THE COMPARISON, not the write: `propertyString` splits\n * on commas and does not honour PBS's quoting, so the two sides never compare equal and the\n * plan asks for the same update forever. Keep the message comma-free.\n */\n 'maintenance-mode'?: string;\n /**\n * ⛔ CREATE-ONLY, LIKE `path`, AND FOR A SHARPER REASON. MEASURED on the live 4.2 host:\n * `datastore create --help` offers `--backend`, `datastore update --help` does NOT, and\n * `backend` is absent from update's `--delete` enum. PBS will not move a datastore between\n * local disk and object storage, ever.\n * ★ IT IS A PROP AT ALL BECAUSE THE ESTATE HAS ONE. `offsite` holds\n * `type=s3,client=cloudflare-r2,bucket=example-pbs`. Leaving it undeclared would still plan\n * noop — undeclared is unmanaged here — but the declaration would then describe a LOCAL\n * datastore, and anyone recreating from it would get exactly that: an empty directory on the\n * host's disk where the offsite copy used to be, behind a green plan the whole way.\n * ⚠️ A property string. Compared canonically, so key order is never a diff.\n */\n backend?: string;\n /** ⚠️ S3 request counters reset on this calendar event. Mutable; in update's `--delete` enum. */\n 'counter-reset-schedule'?: string;\n /**\n * ⚠️ S3 request-count thresholds that raise a notification, e.g. `s3-put=700000,s3-get=200000`.\n * A property string, compared canonically. Mutable.\n */\n 'notification-thresholds'?: string;\n}\n\nexport interface PbsDatastore extends Resource<\n 'Pbs.Datastore',\n PbsDatastoreProps,\n PbsDatastoreAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a datastore holds the backups. See the ★ in resource.ts. */\nexport const PbsDatastore = Resource<PbsDatastore>('Pbs.Datastore', {\n defaultRemovalPolicy: 'retain',\n});\n\nexport const PbsDatastoreProvider = () =>\n Provider.effect(\n PbsDatastore,\n Effect.succeed(\n PbsDatastore.Provider.of({\n /**\n * ⛔ EMPTY, AND HERE IT GUARDS THE ESTATE'S BACKUPS. `GET /config/datastore` returns every\n * datastore on the host, the one PVE's `type: pbs` storage writes into included. See\n * `pveHandlers` in resource.ts for why adoption stays an explicit act.\n */\n list: () => Effect.succeed([]),\n read: ({ olds }) => readOne(olds),\n /**\n * ⚠️ `isResolved` IS THE NARROWING, resource.ts's reason: at plan time a prop can still be an\n * unresolved Output, and comparing a placeholder to a live value reports a phantom update.\n * ⛔ `guardPath` IS A DELIBERATE DEPARTURE FROM storage.ts, which lets its create-only field\n * plan as noop. Dying fails `alchemy plan` with both paths named — see the ⛔ on the prop.\n */\n diff: ({ news, output }) =>\n Effect.gen(function* () {\n if (!isResolved(news)) return undefined;\n // ⛔ BEFORE the `output === undefined` return: that branch IS the create, which is the\n // one this family has never checked. resource.ts carries the argument in full.\n yield* guardDatastoreForms(news);\n if (output === undefined) return undefined;\n const live = yield* readOne(news);\n // ⚠️ `update`, not `create` — Alchemy's Diff admits only noop/update/replace, and an\n // object Alchemy has state for but PBS does not is drift for reconcile to repair.\n if (live === undefined) return { action: 'update' } as const;\n yield* guardPath(live, news);\n yield* guardBackend(live, news);\n return matches(live, news)\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n /**\n * ⚠️ NOT THE FACTORY'S reconcile: it reads back ONCE, immediately — see the ⛔ on `settle`.\n * ⛔ `guardPath` RUNS HERE TOO, AND THAT IS NOT BELT-AND-BRACES — see the ⛔ on it.\n * ⛔ THE `matches` GUARD BEFORE THE PUT IS resource.ts's, AND IT IS WHY ADOPTION IS FREE.\n */\n reconcile: Effect.fn(function* ({ news }) {\n // ⚠️ Again here: an ADOPTED row's diff answer is discarded by Alchemy and reconcile runs.\n yield* guardDatastoreForms(news);\n const live = yield* readOne(news);\n let upid: string | undefined;\n if (live === undefined) {\n upid = yield* pve<string>(\n news.target,\n 'provision',\n 'POST',\n 'config/datastore',\n createForm(news),\n );\n } else {\n yield* guardPath(live, news);\n yield* guardBackend(live, news);\n // ⚠️ An empty form is not a write, and a match is not one either — update-guard.ts.\n const form = formToSend(matches, live, news, updateForm(news));\n if (form !== undefined) {\n yield* pve(news.target, 'provision', 'PUT', object(news), form);\n }\n }\n const after = yield* settle(\n readOne(news),\n (row) => row !== undefined && matches(row, news),\n );\n if (after === undefined || !matches(after, news)) {\n /**\n * ⛔ REFUSE RATHER THAN RETURN THE PROPS AS THOUGH THEY LANDED — resource.ts's rule.\n * A create answers with a task id, so \"no error\" is not evidence of a datastore.\n */\n return yield* Effect.die(\n new Error(\n `${object(news)}: the write returned no error but the datastore still does not ` +\n `match the declaration after 120s. PBS answered with task ${upid ?? '(none)'} ` +\n '-- read it with `proxmox-backup-manager task log <upid>`. A slow disk is a ' +\n 'plausible cause (see `settle`); a create over an existing chunk store is not, ' +\n 'because PBS refuses that outright rather than timing out.',\n ),\n );\n }\n return after;\n }),\n /**\n * ⛔ FULLY IMPLEMENTED, NEVER A STUB, and it only ever runs on an explicit\n * `.pipe(RemovalPolicy.destroy())` because this family defaults to `retain`.\n * ⛔ NO `destroy-data`. PBS's DELETE drops the section from `datastore.cfg` and leaves every\n * chunk and snapshot on disk; `destroy-data=1` erases the contents. It is not a prop and\n * not a flag here, so this provider can un-declare a datastore and can never erase one.\n * ⚠️ IT IS STILL A ONE-WAY DOOR FOR A DECLARATION: re-adding a datastore over an existing\n * chunk store needs `reuse-datastore`, which is deliberately not declarable — see the\n * ⚠️ on `createForm`. Re-adopt with `proxmox-backup-manager datastore create …`.\n * ⚠️ NO `keep-job-configs` EITHER, SO THE JOBS GO TOO: PBS defaults it false and removes the\n * verify, sync and prune jobs referencing this datastore — objects nothing here declares.\n * PBS's default stands, so the blast radius is what a human clicking Remove would get.\n * ⚠️ AND THE REFUSAL CAN ARRIVE AFTER THE RESPONSE: the delete forks a worker too, so a store\n * held open by a running backup can fail once the call has already returned 200.\n */\n delete: Effect.fn(function* ({ olds }) {\n yield* pve<string>(olds.target, 'provision', 'DELETE', object(olds));\n const left = yield* settle(readOne(olds), (row) => row === undefined);\n if (left !== undefined) {\n return yield* Effect.die(\n new Error(\n `${object(olds)}: the DELETE returned no error but the datastore is still in ` +\n 'datastore.cfg after 120s. Either a backup, verify or GC task still holds it, ' +\n 'or the provision role lacks Datastore.Allocate on /datastore/' +\n `${olds.name}. Read the task log with \\`proxmox-backup-manager task log <upid>\\`.`,\n ),\n );\n }\n }),\n }),\n ),\n );\n",
|
|
69
84
|
"/**\n * A PBS datastore on the wire: the transport that reaches PBS, the form it wants, and the coercion\n * back to something comparable.\n *\n * ★ SPLIT OUT OF pbs-datastore.ts FOR THE 250-LINE CAP, the seam notification-target-form.ts uses:\n * this file is everything that touches PBS's encoding, and the resource file is the declaration\n * and the five handlers. The `import type` back is erased, so it is a cycle on paper only.\n *\n * ⛔ THIS FILE IS OVER THE 250-LINE CAP AND THE EXTRACTION IS NAMED RATHER THAN FUDGED. `PbsTarget`,\n * `PbsRole`, `PbsError`, `pbsAuthorization`, `PveForm`, `encode`, `pbs` and `settle` — about 150\n * lines — are a PBS CLIENT, not a datastore form, and belong in `pbs-client.ts`. Lifting them\n * leaves this file near 200 and costs nothing but a move. It was not done here only because the\n * family was commissioned as a two-file split; ceph-pool is the in-package precedent for three\n * (ceph-pool.ts + ceph-pool-form.ts + ceph-pool-settle.ts, 532 lines for one family). Everything\n * in that list is also what Pbs.SyncJob, Pbs.PruneJob and Pbs.Remote will each need unchanged,\n * so the second PBS family forces the move anyway. ⛔ DO NOT MEET THE CAP BY CUTTING COMMENTS.\n */\nimport * as Effect from 'effect/Effect';\nimport { type PveForm, pve } from './client.ts';\nimport type { PbsTarget as ApiPbsTarget } from './credentials.ts';\nimport type { PbsDatastoreProps } from './pbs-datastore.ts';\nimport { bool, flag, int, propertyString, text } from './values.ts';\n\n/**\n * ★ PBS REUSES THE PVE CLIENT RATHER THAN OWNING ONE. An earlier draft of this file carried its\n * own `PbsTarget`, `PbsError`, `pbsAuthorization`, form encoder and `pve()` — a near-copy of\n * client.ts differing in one header. They are gone: `PveTarget` now carries a `scheme`, and\n * `authorization()` spells the header `PBSAPIToken=<id>:<secret>` when it is `'pbs'`. The\n * insight that survived is the FORM TYPE — PBS decodes a multi-valued field from repeated keys\n * where PVE wants a comma string — and that now lives on `PveForm` in client.ts, so both\n * products share one encoder and neither can reach for the other's convention by accident.\n *\n * 🔴 THIS ALIAS SAID `= PveTarget` AND IT WAS WRONG — Pbs.Datastore REQUIRED A PVE TARGET. When\n * `credentials.ts` grew a real `PbsTarget` with `scheme: 'pbs'`, this line was left pointing at\n * the PVE type, which pins `scheme: 'pve'`. So the one family that most needs the discriminant\n * was the one family that refused the correct value: declaring a datastore against the PBS host\n * failed to compile with `Type '\"pbs\"' is not assignable to type 'Input<\"pve\">'`, and passing a\n * PVE target compiled fine and would have 401'd every call — which `read` folds into \"absent\",\n * so the plan would say CREATE for a datastore holding the estate's backups.\n * ⛔ IT SURVIVED BECAUSE NOTHING EVER DECLARED ONE. The provider was registered in\n * alchemy.run.ts from the day it was written and `pveHandlers`' `list` answers empty, so\n * registration exercises no call site at all. Three sibling families import `PbsTarget` from\n * credentials.ts directly and were always correct; only the one with its own alias drifted.\n * ★ THE LESSON, WHICH IS NOT ABOUT THIS TYPE: a provider nobody has declared an instance of is\n * not tested by anything, including tsc.\n */\nexport type PbsTarget = ApiPbsTarget;\n\nexport interface PbsDatastoreAttributes {\n name: string;\n /**\n * ⚠️ READ FROM PBS, NEVER ECHOED FROM PROPS. Echoing the declared path would make a divergent or\n * unreadable datastore look like agreement, and this is the field whose divergence means the\n * backups are going somewhere nobody declared. `''` when PBS did not say.\n */\n path: string;\n comment: string;\n 'gc-schedule': string;\n 'prune-schedule': string;\n 'keep-last': number;\n 'keep-hourly': number;\n 'keep-daily': number;\n 'keep-weekly': number;\n 'keep-monthly': number;\n 'keep-yearly': number;\n /** Normalised property strings — sorted and canonicalised, so key order is never a diff. */\n notify: string;\n 'notify-user': string;\n 'verify-new': boolean;\n tuning: string;\n 'maintenance-mode': string;\n /** ⚠️ Create-only. Canonicalised property string; `''` on a plain local datastore. */\n backend: string;\n 'counter-reset-schedule': string;\n 'notification-thresholds': string;\n}\n\n/**\n * ⚠️ `digest` IS DELIBERATELY NOT AN ATTRIBUTE, for storage.ts's reason exactly: PBS returns one on\n * every read and it covers `datastore.cfg` as a FILE, not this section of it, so keeping it would\n * rewrite this resource's state whenever an unrelated datastore was edited — churn that reads\n * like drift. It is not sent on writes either, so this family never joins PBS's optimistic-locking\n * dance and last write wins, as everywhere else in this package.\n */\nexport const attributes = (\n live: Record<string, unknown>,\n props: PbsDatastoreProps,\n): PbsDatastoreAttributes => ({\n backend: propertyString(live['backend']),\n comment: text(live['comment']),\n 'counter-reset-schedule': text(live['counter-reset-schedule']).trim(),\n 'gc-schedule': text(live['gc-schedule']).trim(),\n 'keep-daily': int(live['keep-daily'], 0),\n 'keep-hourly': int(live['keep-hourly'], 0),\n 'keep-last': int(live['keep-last'], 0),\n 'keep-monthly': int(live['keep-monthly'], 0),\n 'keep-weekly': int(live['keep-weekly'], 0),\n 'keep-yearly': int(live['keep-yearly'], 0),\n 'maintenance-mode': propertyString(live['maintenance-mode'], 'type'),\n name: props.name,\n 'notification-thresholds': propertyString(live['notification-thresholds']),\n notify: propertyString(live['notify']),\n 'notify-user': text(live['notify-user']),\n path: text(live['path']),\n 'prune-schedule': text(live['prune-schedule']).trim(),\n tuning: propertyString(live['tuning']),\n 'verify-new': bool(live['verify-new']),\n});\n\nconst same = <T>(declared: T | undefined, live: T) => declared === undefined || declared === live;\n\n/**\n * ⛔ DECLARING WHAT IS LIVE MUST PLAN noop, AND EVERY OMISSION IS DELIBERATE. Out: `name` (the\n * address the read was made at — true by construction, never a diff); `path` (create-only, so\n * comparing it could only plan an update no PUT can apply — pbs-datastore.ts guards it by dying\n * instead, see the ⛔ on the prop); `digest` (not an attribute, above); and every field the\n * declaration does not mention, because undeclared is UNMANAGED here.\n * ⚠️ A SCHEDULE THAT NEVER SETTLES MEANS PBS NORMALISED IT. Calendar events are compared as trimmed\n * strings; if a version rewrites `sat 18:15` as `Sat 18:15` the plan asks for the same update\n * forever. The fix is to declare the spelling PBS stores, never to stop comparing it.\n */\nexport const matches = (live: PbsDatastoreAttributes, props: PbsDatastoreProps) =>\n same(props.comment, live.comment) &&\n same(props['gc-schedule']?.trim(), live['gc-schedule']) &&\n same(props['prune-schedule']?.trim(), live['prune-schedule']) &&\n same(props['keep-last'], live['keep-last']) &&\n same(props['keep-hourly'], live['keep-hourly']) &&\n same(props['keep-daily'], live['keep-daily']) &&\n same(props['keep-weekly'], live['keep-weekly']) &&\n same(props['keep-monthly'], live['keep-monthly']) &&\n same(props['keep-yearly'], live['keep-yearly']) &&\n same(props['notify-user'], live['notify-user']) &&\n same(props['counter-reset-schedule']?.trim(), live['counter-reset-schedule']) &&\n (props['notification-thresholds'] === undefined ||\n propertyString(props['notification-thresholds']) === live['notification-thresholds']) &&\n same(props['verify-new'], live['verify-new']) &&\n (props.notify === undefined || propertyString(props.notify) === live.notify) &&\n (props.tuning === undefined || propertyString(props.tuning) === live.tuning) &&\n (props['maintenance-mode'] === undefined ||\n propertyString(props['maintenance-mode'], 'type') === live['maintenance-mode']);\n\nconst set = (key: string, value: string | undefined): Record<string, string> =>\n value === undefined ? {} : { [key]: value };\n\n/** ⚠️ PBS's minimum for every keep-* is 1, so a declared `0` is refused rather than meaning \"off\". */\nconst count = (value: number | undefined) => (value === undefined ? undefined : String(value));\n\n/**\n * The mutable half, shared by create and update.\n *\n * ⚠️ AN UNDECLARED FIELD IS NOT SENT — storage.ts's rule. PBS leaves a parameter it was not given\n * alone, so this form is safe to re-apply to a datastore that already matches, which is what the\n * adoption path does.\n * ⛔ `name` AND `path` ARE ABSENT ON PURPOSE. Both are create-only: `name` is the address forever\n * after, and PBS's update endpoint does not accept `path` at all, so including it here would turn\n * every single update into a 400.\n */\nconst mutable = (props: PbsDatastoreProps): Record<string, string> => ({\n ...set('comment', props.comment),\n ...set('gc-schedule', props['gc-schedule']?.trim()),\n ...set('keep-daily', count(props['keep-daily'])),\n ...set('keep-hourly', count(props['keep-hourly'])),\n ...set('keep-last', count(props['keep-last'])),\n ...set('keep-monthly', count(props['keep-monthly'])),\n ...set('keep-weekly', count(props['keep-weekly'])),\n ...set('keep-yearly', count(props['keep-yearly'])),\n ...set('counter-reset-schedule', props['counter-reset-schedule']),\n ...set('maintenance-mode', props['maintenance-mode']),\n ...set('notification-thresholds', props['notification-thresholds']),\n ...set('notify', props.notify),\n ...set('notify-user', props['notify-user']),\n ...set('prune-schedule', props['prune-schedule']?.trim()),\n ...set('tuning', props.tuning),\n ...set('verify-new', flag(props['verify-new'])),\n});\n\n/**\n * ⚠️ `path` IS SENT ONCE, HERE, AND NEVER AGAIN — the fact the whole `path` guard hangs off.\n * ⚠️ `reuse-datastore` IS NOT SENT, AND THE REFUSAL IT CAUSES IS A FEATURE: without it PBS refuses\n * a create against a directory that already holds a chunk store, which is the difference between\n * building a new store and silently adopting somebody else's backups. Adopt out of band.\n * ⚠️ NEITHER IS `backing-device`. A removable datastore's UUID is a property of the hardware in the\n * slot, not of the declaration; one that could re-point it is one that can send backups to the\n * wrong drive.\n */\nexport const createForm = (props: PbsDatastoreProps): PveForm => ({\n ...mutable(props),\n name: props.name,\n path: props.path,\n // ⛔ CREATE ONLY, AND ABSENT FROM `mutable` ON PURPOSE. PBS has no `--backend` on update and no\n // `backend` in update's `--delete` enum (measured on 4.2), so sending it on a PUT is a\n // parameter error at best. Divergence is caught by guardBackend, which DIES — the same shape\n // as `path`, for a bigger consequence.\n ...(props.backend === undefined ? {} : { backend: props.backend }),\n});\n\n/**\n * ⛔ THERE IS NO `delete` LIST HERE, AND THAT IS A CHOICE ABOUT BACKUPS RATHER THAN AN OMISSION.\n * sdn-vnet.ts derives one from absent props, which is right for an alias and wrong for a\n * retention policy: a declaration that simply fails to mention `keep-daily` would strip it, and a\n * prune job whose keeps have all been stripped is one PBS version away from a prune job that\n * keeps nothing. So undeclared is UNMANAGED, as in storage.ts and backup-job.ts.\n * ⚠️ THE PRICE IS THAT DROPPING A LINE DOES NOT CLEAR A FIELD. Clear one out of band:\n * `proxmox-backup-manager datastore update <name> --delete keep-daily`. It does not cost a\n * forever-diff, because an undeclared field is not compared either.\n * ⛔ WHEN SOMEBODY ADDS ONE, IT TAKES TWO STEPS AND SKIPPING EITHER IS A SILENT BUG. First build\n * it with `values.withClears`, which drops a cleared key from the body — CLEAR WINS — because\n * PBS applies its deletes BEFORE its sets, so a key both set and deleted keeps its value: the\n * declaration would say \"unset this\", the PUT would succeed, and the next plan would ask for\n * the very same update forever. (PVE instead DIES on that pair; the two behaviours want the\n * same guard.) Second, split the comma-joined value `withClears` produces into a real array —\n * PBS's `delete` is `type: array` and wants REPEATED KEYS, and a comma string arrives as one\n * element that fails the enum check. `PveForm` and `encode` already carry arrays for exactly\n * this. ⚠️ REASONED FROM THE PUBLISHED SCHEMA, NOT PROVEN ON A WIRE.\n */\nexport const updateForm = (props: PbsDatastoreProps): PveForm => mutable(props);\n\nexport const object = (props: PbsDatastoreProps) => `config/datastore/${props.name}`;\n\n/**\n * The live datastore, or undefined.\n *\n * ⛔ `name` IS THE PRESENCE TEST, NOT THE STATUS CODE — PBS does not answer 404 for a section that\n * is missing from `datastore.cfg`, it answers a generic error, so there is no code to key off.\n * ⛔ A MISSING READ PRIVILEGE THEREFORE LOOKS EXACTLY LIKE ABSENCE, because the fold below cannot\n * tell them apart. The signature is a plan that says CREATE followed by PBS answering\n * \"datastore '<name>' already exists.\" — that pair means Datastore.Audit, not a missing\n * datastore. It is loud and non-destructive, which is the only reason the fold is tolerable here;\n * see the ⛔ on `readRole` in resource.ts for the PVE families where it is not.\n * ⛔ ON THE DELETE PATH THE SAME FOLD IS A LIE, AND THIS IS THE ONE HAZARD IN THIS FAMILY I COULD\n * NOT CLOSE. `delete` settles by polling this read until it answers `undefined` — and a `read`\n * role that lacks `Datastore.Audit` answers `undefined` from the very first poll, so EVERY delete\n * would report success while the datastore stood. Worse, the two lanes are different leases: the\n * DELETE goes out under `provision` and may genuinely fail, and this read cannot see that it did.\n * ★ THE SHAPE OF THE FIX IS ALREADY IN THIS PACKAGE: `confirmAbsent` in ceph-pool-settle.ts asks\n * a SECOND, DIFFERENT question — does the collection LIST this object? — and lets a failure\n * PROPAGATE rather than folding it, so \"I could not ask\" fails the deploy instead of passing\n * it. Add the same over `GET /config/datastore` before anyone relies on a reported deletion.\n * ⚠️ UNTIL THEN THE PRECONDITION IS EXPLICIT: the `read` role MUST hold `Datastore.Audit` on the\n * datastore, or this provider cannot tell a deletion from a refusal.\n */\nexport const readOne = (props: PbsDatastoreProps) =>\n pve<Record<string, unknown>>(props.target, 'read', 'GET', object(props)).pipe(\n Effect.map((live) =>\n live === undefined || typeof live['name'] !== 'string' ? undefined : attributes(live, props),\n ),\n Effect.orElseSucceed(() => undefined),\n );\n",
|
|
70
85
|
"/**\n * The two things a datastore write needs that are not form-building: refusing a moved `path`, and\n * waiting for PBS to finish.\n *\n * ★ SPLIT OUT OF pbs-datastore-form.ts FOR THE 250-LINE CAP, and the seam is real. That file turns\n * a declaration into a form; this one is the SAFETY GUARD and the POLLING. A reader asking \"can\n * this destroy my backups\" has one short file to read rather than a long one to search.\n */\nimport * as Effect from 'effect/Effect';\nimport { type PbsDatastoreAttributes, object } from './pbs-datastore-form.ts';\nimport type { PbsDatastoreProps } from './pbs-datastore.ts';\nimport { propertyString } from './values.ts';\n\n/**\n * ⛔ A TRAILING SLASH MUST NOT DEADLOCK EVERY PLAN, WHICH IS WHY THIS IS NOT `===`. `guardPath`\n * DIES, so a false positive is not a spurious update — it is an `alchemy plan` that cannot run at\n * all until somebody edits the declaration, on a family whose whole point is that the operator\n * can see the state of their backups. `/mnt/backups/` and `/mnt/backups` are one directory, and\n * whether PBS canonicalises the path it was handed is a property of the version you are talking\n * to. Both sides are trimmed here so that guess cannot cost an outage of the plan itself.\n * ⚠️ THAT IS THE ONLY NORMALISATION APPLIED. A symlink, a bind mount or `/mnt/./backups` still\n * reads as divergent, and deliberately so: this guard's job is to refuse to GUESS that two\n * spellings are the same directory, and only the trailing slash is certain.\n */\nconst samePath = (left: string, right: string) => {\n const trim = (value: string) => (value.length > 1 ? value.replace(/\\/+$/, '') : value);\n return trim(left) === trim(right);\n};\n\n/**\n * ⛔ THE `path` GUARD, YIELDED FROM BOTH `diff` AND `reconcile`. Both need it: `diff` catches a\n * changed declaration, and `reconcile` catches an ADOPTION, which Alchemy routes straight past\n * `diff` (Apply.ts:980, :1045). `''` is skipped — an unreadable path is not a divergent one.\n */\nexport const guardPath = (live: PbsDatastoreAttributes, props: PbsDatastoreProps) =>\n live.path === '' || samePath(live.path, props.path)\n ? Effect.void\n : Effect.die(\n new Error(\n `${object(props)}: declared path ${props.path} but PBS holds ${live.path}. \\`path\\` ` +\n 'is create-only, so this cannot be reconciled and will NOT be planned as a silent ' +\n 'noop: backups would keep landing in the old directory behind a green plan. Correct ' +\n 'the declaration, or remove the datastore and declare a new one -- the contents live, ' +\n 'because `destroy-data` is never sent.',\n ),\n );\n\n/**\n * ⛔ THE `backend` GUARD, AND IT MATTERS MORE THAN `path`. A wrong `path` sends backups to the\n * wrong directory on the same machine; a wrong `backend` is the difference between a datastore\n * on local disk and one in a Cloudflare R2 bucket. MEASURED on 4.2: `datastore create` takes\n * `--backend`, `datastore update` does not, and `backend` is not in update's `--delete` enum —\n * so divergence CANNOT be reconciled, and the only honest options are to refuse or to lie.\n *\n * ⚠️ AN UNDECLARED `backend` IS NOT A DIVERGENCE. Undeclared is unmanaged everywhere in this\n * family, and this guard keeps that rule: it fires only when the declaration says something and\n * PBS says something else. Adopting `offsite` without mentioning `backend` still plans noop.\n *\n * ⚠️ COMPARED AS A CANONICAL PROPERTY STRING, so `bucket=x,type=s3` and `type=s3,bucket=x` are the\n * same backend and key order cannot deadlock a plan — the reason `samePath` trims a slash.\n */\nexport const guardBackend = (live: PbsDatastoreAttributes, props: PbsDatastoreProps) =>\n props.backend === undefined || propertyString(props.backend) === live.backend\n ? Effect.void\n : Effect.die(\n new Error(\n `${object(props)}: declared backend ${props.backend} but PBS holds ` +\n `${live.backend === '' ? '(none — a local datastore)' : live.backend}. \\`backend\\` ` +\n 'is create-only: PBS has no update for it, so this cannot be reconciled and will NOT ' +\n 'be planned as a silent noop. Correct the declaration to match what is really there, ' +\n 'or remove the datastore and declare a new one -- the contents live, because ' +\n '`destroy-data` is never sent.',\n ),\n );\n\n/**\n * Wait for PBS to finish, then answer the last thing it said.\n *\n * ⛔ IT EXISTS BECAUSE A PBS CREATE IS A FORKED WORKER AND THE CONFIG SECTION IS WRITTEN LAST.\n * `POST /config/datastore` answers with a UPID while a `create-datastore` task builds the chunk\n * store's 65536 directories; only when that finishes does the section reach `datastore.cfg`. An\n * immediate read-back therefore sees a datastore that is not there yet, and the factory's \"the\n * write returned no error but the object is still absent\" would fire on a create going perfectly.\n * The DELETE forks a worker too. ⚠️ DOCUMENTED FROM THE PUBLISHED SCHEMA, NOT MEASURED — I hold\n * no PBS token. If a create turns out to be synchronous, the first poll simply succeeds.\n * ★ IT TAKES THE READ AS AN EFFECT RATHER THAN A FUNCTION — the one simplification over\n * ceph-pool-settle.ts: an Effect is a description, so re-running it re-reads and no dependency\n * has to be threaded back in to avoid a cycle.\n * ⚠️ 2s APART, 60 TRIES, ABOUT TWO MINUTES — longer than ceph-pool's minute, and REASONED rather\n * than measured: 65536 `mkdir`s is seconds on NVMe and can be a minute on a slow or USB disk.\n * Anything still unsettled after two minutes is a fault to surface, not a wait to lengthen, and\n * the caller's message points at the task log, which is where the real answer is. `Effect.sleep`\n * yields between polls; nothing here parks a call on one long wait.\n * ⚠️ POLLING THE TASK STATUS INSTEAD WOULD BE WORSE, for ceph-pool-settle.ts's reason: every call\n * mints a FRESH token, so the token asking about the task is not the one that started it.\n */\nexport const settle = <A, E, R>(read: Effect.Effect<A, E, R>, done: (live: A) => boolean) =>\n Effect.gen(function* () {\n for (let attempt = 0; attempt < 60; attempt += 1) {\n const live = yield* read;\n if (done(live)) return live;\n yield* Effect.sleep('2 seconds');\n }\n return yield* read;\n });\n",
|
|
71
86
|
"/**\n * `Pbs.Datastore`'s two vendor endpoints, and the plan-time check over its two forms.\n *\n * ★ A FILE OF ITS OWN BECAUSE THIS FAMILY DOES NOT USE `pveHandlers`. It writes its own `diff` and\n * `reconcile` (a create answers with a task id, so it settles rather than reading back once), so\n * it cannot inherit the guard from resource.ts — and a copy of the two calls inside that already\n * long file is exactly the per-resource copy-paste the shared handler exists to prevent.\n *\n * ⛔ THE FOUR FIELDS THAT MADE THIS WORTH DOING, from the PBS 4.2.6 schema: `comment` is\n * maxLength 128 with a no-control-characters pattern, `name` is 3..32 with a safe-id pattern,\n * and every `keep-*` has minimum 1. All four were `string`/`number` with the limits in prose.\n */\nimport * as Effect from 'effect/Effect';\nimport { guardForm } from './constraint-guard.ts';\nimport type { EndpointKey } from './constraints.ts';\nimport { createForm, updateForm } from './pbs-datastore-form.ts';\nimport type { PbsDatastoreProps } from './pbs-datastore.ts';\n\nexport const DATASTORE_ENDPOINT: { readonly create: EndpointKey; readonly update: EndpointKey } = {\n create: 'pbs:POST /config/datastore',\n update: 'pbs:PUT /config/datastore/{name}',\n};\n\n/** ⚠️ Presence on create only — an update form is partial by design (update-guard.ts). */\nexport const guardDatastoreForms = (props: PbsDatastoreProps): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* guardForm(DATASTORE_ENDPOINT.create, createForm(props), true);\n yield* guardForm(DATASTORE_ENDPOINT.update, updateForm(props), false);\n });\n",
|
|
72
87
|
"/**\n * `Pbs.NotificationMatcher` — which Proxmox Backup Server notifications reach which targets.\n *\n * ★ THIS IS WHERE A FAILED VERIFY, SYNC OR GARBAGE COLLECTION BECOMES A PAGE. pbs-verify-job.ts\n * explains why verification is the only thing that notices rotten chunks; a matcher is what\n * turns what it notices into a message. Route `match-severity: ['error']` to a webhook target\n * (pbs-notification-target.ts, docs/pbs-notifications.md) and a failure pages someone.\n *\n * ⛔ A DATASTORE IN `legacy-sendmail` MODE BYPASSES EVERY MATCHER. PBS reads the datastore's\n * `notification-mode` before it builds anything (src/server/notifications/mod.rs\n * `lookup_datastore_notify_settings`, read at HEAD 2026-09-22): `legacy-sendmail` mails the\n * `notify-user` directly and never consults a matcher. The default is `notification-system`\n * today (pbs-api-types `NotificationMode`), but a datastore created before PBS 3.2 may say\n * `legacy-sendmail` explicitly. `Pbs.Datastore` deliberately does not declare the mode (see\n * `notify` in pbs-datastore.ts), so check it by hand before trusting a matcher with a datastore:\n * `proxmox-backup-manager datastore show <name>`.\n *\n * ⚠️ THE EVENT `type` FOR A VERIFICATION JOB IS `verify`, NOT `verification`. PBS's own\n * notifications chapter lists `verification` in its event table and `verify` in its example,\n * and the source settles it: `send_verify_status` sets `(\"type\", \"verify\")` (same file). A\n * `match-field: ['exact:type=verification']` rule matches nothing, silently.\n * Types at HEAD: `gc`, `prune`, `sync`, `verify`, `tape-backup`, `tape-load`,\n * `package-updates`, `acme`, `thresholds`, `system-mail`. GC carries `datastore` and `hostname`\n * but NO `job-id`; prune, sync and verify carry all three; the UI's \"Test\" carries no fields.\n *\n * ★ ADOPTING THE BUILT-IN AS IT IS. MEASURED on PBS 4.2 (2026-09-22, `proxmox-backup-manager\n * notification matcher show default-matcher`): it routes everything EXCEPT successful prune\n * jobs to `mail-to-root` — `invert-match` over `exact:type=prune` AND severity `info`:\n * ```ts\n * PbsNotificationMatcher('default-matcher', {\n * target: pbs, name: 'default-matcher', targets: ['mail-to-root'],\n * 'invert-match': true, 'match-field': ['exact:type=prune'], 'match-severity': ['info'],\n * });\n * ```\n *\n * ★ WHAT THIS NEEDS, READ OFF THE SERVER'S SCHEMA (generated/pbs.ts source):\n * read GET Sys.Audit on /system/notifications\n * write POST, PUT, DELETE Sys.Modify on /system/notifications\n * ⚠️ NOT A DATASTORE PRIVILEGE. A PBS credential scoped to `/datastore/...` reads every matcher\n * as absent — the \"absent, then already exists\" signature notification-target.ts describes.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n type NotificationMatcherAttributes,\n type NotificationMatcherFields,\n matcherAttributes,\n matcherCreateForm,\n matcherMatches,\n matcherUpdateForm,\n} from './notification-matcher-form.ts';\nimport { type PveRequirements, type WithPbsTarget, pveHandlers } from './resource.ts';\n\nexport interface PbsNotificationMatcherProps extends NotificationMatcherFields, WithPbsTarget {}\n\nexport interface PbsNotificationMatcher extends Resource<\n 'Pbs.NotificationMatcher',\n PbsNotificationMatcherProps,\n NotificationMatcherAttributes,\n never,\n PveRequirements\n> {}\n\n/** ⚠️ No `retain`, for notification-matcher.ts's reason; a built-in REVERTS on delete. */\nexport const PbsNotificationMatcher = Resource<PbsNotificationMatcher>('Pbs.NotificationMatcher');\n\nconst handlers = pveHandlers<PbsNotificationMatcherProps, NotificationMatcherAttributes>({\n attributes: (live, props) => matcherAttributes(live, props.name),\n collection: () => 'config/notifications/matchers',\n createForm: matcherCreateForm,\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: {\n create: 'pbs:POST /config/notifications/matchers',\n update: 'pbs:PUT /config/notifications/matchers/{name}',\n },\n matches: matcherMatches,\n path: (props) => `config/notifications/matchers/${props.name}`,\n updateForm: matcherUpdateForm,\n});\n\nexport const PbsNotificationMatcherProvider = () =>\n Provider.effect(\n PbsNotificationMatcher,\n Effect.succeed(PbsNotificationMatcher.Provider.of(handlers)),\n );\n",
|
|
73
88
|
"/**\n * `Pbs.NotificationTarget` — where a Proxmox Backup Server sends a notification: a `webhook`, or\n * mail through `sendmail` or `smtp`.\n *\n * ★ THE WEBHOOK IS THE POINT. A failed verify, sync or garbage collection reaches a human through a\n * target a matcher names (pbs-notification-matcher.ts). Out of the box that is `mail-to-root`,\n * one mailbox on one host; a webhook can post an Alertmanager alert instead, and then the failure\n * pages like everything else. docs/pbs-alertmanager-body.md has the body template.\n *\n * ⛔ SECRETS ARE DECLARED BY THE NAME OF AN ENVIRONMENT VARIABLE, NEVER BY VALUE, and NO VALUE EVER\n * REACHES STATE — not as a prop, not as an attribute. write-only.ts has the whole argument; the\n * short form: Alchemy stores props and attributes unencrypted, so `secret`, `password` and any\n * header holding a credential take `{ fromEnv: 'NAME' }`, read by the deploying process at call\n * time. What the store keeps is presence (names) and digests, and the plan diffs on those.\n * ★ THIS IS WHERE THE PBS FAMILY PARTS FROM notification-target.ts (PVE), which refuses every\n * secret and so cannot create a signed webhook at all.\n *\n * ⛔ THE PER-OBJECT PATH CARRIES THE FAMILY: `config/notifications/endpoints/{type}/{name}`. `type`\n * is identity, and changing it or `name` plans a REPLACE here — `diff` compares them with the\n * recorded props. `/config/notifications/targets` is a read-only union view, as on PVE.\n *\n * ⚠️ A BUILT-IN TARGET REVERTS ON DELETE instead of disappearing (`origin: builtin`, measured on\n * PBS 4.2 for `mail-to-root`), and PBS reports success either way — notification-target.ts has\n * the consequence. Read `origin` before declaring a name you did not create.\n *\n * ★ WHAT THIS NEEDS (generated/pbs.ts source): Sys.Audit on /system/notifications to read,\n * Sys.Modify there to write. A datastore-scoped credential reads every target as absent.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { handlers } from './pbs-notification-target-lifecycle.ts';\nimport type { PveRequirements, WithPbsTarget } from './resource.ts';\nimport type { FromEnv } from './write-only.ts';\n\nexport type PbsNotificationTargetType = 'sendmail' | 'smtp' | 'webhook';\n\n/**\n * ⚠️ PBS'S OWN KEY NAMES, DASHES INCLUDED, for the reason backup-job.ts gives. A field outside its\n * family (a `url` on a sendmail target) is refused at plan by name, not sent for a 400.\n * ⚠️ AN UNDECLARED FIELD IS NEITHER SENT NOR COMPARED; `''` or `[]` (or `{}`) CLEARS ONE.\n */\nexport interface PbsNotificationTargetProps extends WithPbsTarget {\n /** ⛔ IDENTITY: it picks the family and the URL. Changing it is a replace. */\n type: PbsNotificationTargetType;\n /** Unique across ALL families on the host: a webhook cannot be called `mail-to-root`. */\n name: string;\n comment?: string;\n /** Configured but not delivering. Always sent and always compared. */\n disable?: boolean;\n /** sendmail, smtp: recipient addresses. Order is not meaning. */\n mailto?: readonly string[];\n /** sendmail, smtp: PBS users whose configured address receives the mail. */\n 'mailto-user'?: readonly string[];\n /** smtp: required. sendmail: optional. */\n 'from-address'?: string;\n author?: string;\n /** smtp: the relay host. Required. */\n server?: string;\n /** smtp. Unset lets PBS pick from `mode`. */\n port?: number;\n /** smtp. PBS's default is `tls`. */\n mode?: 'insecure' | 'starttls' | 'tls';\n /** smtp. */\n username?: string;\n /** smtp: ⛔ WRITE-ONLY. The server never returns it; the plan compares its seal. */\n password?: FromEnv;\n /** webhook: required. Supports templating: `{{ secrets.<name> }}` keeps a token out of it. */\n url?: string;\n /** webhook: required. */\n method?: 'get' | 'post' | 'put';\n /**\n * webhook: header name → template text, or `{ fromEnv }` for a value that is itself a secret.\n * ⚠️ PLAIN VALUES ARE PROPS, AND PROPS ARE STORED: `Content-Type` is fine, a token is not. Put a\n * credential in `secret` and write `'Bearer {{ secrets.token }}'` here instead.\n */\n header?: Readonly<Record<string, FromEnv | string>>;\n /**\n * webhook: the body TEMPLATE, as text. ★ Declared DECODED — the wire is base64, and this family\n * encodes on write and decodes on read, so a plan shows a readable diff.\n */\n body?: string;\n /** webhook: ⛔ WRITE-ONLY. Name → variable. Referenced as `{{ secrets.<name> }}`. */\n secret?: Readonly<Record<string, FromEnv>>;\n}\n\n/** ⛔ NO VALUE OF A SECRET, PASSWORD OR HEADER — names and digests only. */\nexport interface PbsNotificationTargetAttributes {\n name: string;\n type: PbsNotificationTargetType;\n /** `builtin`, `modified-builtin`, `user-created` — reported, never compared. */\n origin: string;\n comment: string;\n disable: boolean;\n mailto: string;\n 'mailto-user': string;\n 'from-address': string;\n author: string;\n server: string;\n /** 0 means PBS holds no port. */\n port: number;\n mode: string;\n username: string;\n url: string;\n method: string;\n /** The decoded body template. */\n body: string;\n /** Header NAMES, sorted — presence. */\n header: string;\n /** scrypt digest of the live header pairs, fixed salt per target. */\n headerDigest: string;\n /** Secret NAMES, sorted — all PBS will say. */\n secret: string;\n /**\n * Seal of the secret values and password THIS PROVIDER LAST WROTE; `''` if it never wrote them\n * (an adoption). ⚠️ `''` makes those values presence-only until the first write that carries them.\n */\n sealed: string;\n}\n\nexport interface PbsNotificationTarget extends Resource<\n 'Pbs.NotificationTarget',\n PbsNotificationTargetProps,\n PbsNotificationTargetAttributes,\n never,\n PveRequirements\n> {}\n\nexport const PbsNotificationTarget = Resource<PbsNotificationTarget>('Pbs.NotificationTarget');\n\n/**\n * ⛔ `Provider.of` STAYS HERE, for resource.ts's reason: it is the one place the handlers are\n * checked against the concrete resource types, so the lifecycle file cannot drift from them.\n */\nexport const PbsNotificationTargetProvider = () =>\n Provider.effect(\n PbsNotificationTarget,\n Effect.succeed(PbsNotificationTarget.Provider.of(handlers)),\n );\n",
|
|
74
|
-
"/**\n * The lifecycle of a `Pbs.NotificationTarget`: read, diff, reconcile, delete.\n *\n * ★ SPLIT OUT OF pbs-notification-target.ts FOR THE 250-LINE CAP. That file says what a target is\n * and holds `Provider.of`; this one says what each operation does. The `import type` back is\n * erased, so the cycle is on paper only.\n *\n * ⚠️ NOT `pveHandlers`, AND IT SHOULD LOOK DIFFERENT (resource.ts asks for exactly that). The\n * factory's `diff` sees only the live object, and a secret's value is not in the live object:\n * the seal lives in the PREVIOUS STATE, which only a hand-written `diff` and `reconcile` receive\n * (`output`). Everything else — the read, the read-back refusal, the `matches` guard that keeps\n * adoption free — is the factory's behaviour, written out.\n */\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Effect from 'effect/Effect';\nimport { pve } from './client.ts';\nimport type {\n PbsNotificationTargetAttributes,\n PbsNotificationTargetProps,\n} from './pbs-notification-target.ts';\nimport {\n type Carry,\n type Groups,\n headerState,\n plainMatches,\n refusals,\n resolveGroups,\n sealedState,\n targetForm,\n} from './pbs-notification-target-form.ts';\nimport { targetAttributes } from './pbs-notification-target-wire.ts';\nimport { seal } from './write-only.ts';\n\ntype Props = PbsNotificationTargetProps;\ntype Attributes = PbsNotificationTargetAttributes;\n\nconst path = (props: Props) => `config/notifications/endpoints/${props.type}/${props.name}`;\n\n/**\n * ⚠️ A 404 and `{\"data\": null}` are answers here — and so, resource.ts's ⛔, is a 403. `pve()`\n * returns `null` for the second, not `undefined`, so the test is `== null`.\n */\nconst readLive = (props: Props) =>\n pve<Record<string, unknown>>(props.target, 'read', 'GET', path(props)).pipe(\n Effect.map((data) => (data == null ? undefined : targetAttributes(data, props))),\n Effect.orElseSucceed(() => undefined),\n );\n\nconst refuse = (props: Props) => {\n const reasons = refusals(props);\n return reasons.length === 0\n ? Effect.void\n : Effect.die(new Error(`${path(props)}: ${reasons.join('; ')}.`));\n};\n\n/** What a write must carry, given what is stale. `undefined` means nothing to write. */\nconst toCarry = (live: Attributes, props: Props, groups: Groups, sealed: string) => {\n const header = headerState(live, props, groups) === 'stale';\n const sealedStale = sealedState(live, props, groups, sealed) === 'stale';\n return plainMatches(live, props) && !header && !sealedStale\n ? undefined\n : ({ header, sealed: sealedStale } satisfies Carry);\n};\n\n/** ⛔ Every variable a carried group needs, or a failure naming the missing ones — never a value. */\nconst requireValues = (props: Props, groups: Groups, carry: Carry) => {\n const missing = [\n ...(carry.header ? groups.header.missing : []),\n ...(carry.sealed ? groups.sealed.missing : []),\n ];\n return missing.length === 0\n ? Effect.void\n : Effect.die(\n new Error(\n `${path(props)}: this write must send write-only values, and ${missing.join(', ')} ` +\n 'is unset or empty in the deploying environment. Export it and deploy again.',\n ),\n );\n};\n\nexport const handlers = {\n /** ⛔ EMPTY: `mail-to-root` is on every host, and adoption stays an explicit act (resource.ts). */\n list: () => Effect.succeed([]),\n /**\n * ⛔ WITH NO `output` THIS IS ALCHEMY'S ADOPTION PROBE, AND THE ONLY PLAN-TIME HOOK A NEW TARGET\n * GETS — so the refusals run here too. Measured on beta.79 (Plan.ts, the `oldState ===\n * undefined` branch): a declaration with no state row is `read` with `olds: news` and never\n * `diff`ed, and Apply then commits its props as `creating` BEFORE `reconcile` runs. A refusal\n * left to `reconcile` fired after the props — a literal token included — were in the store.\n * ⚠️ ONLY WHEN THE PROPS ARE RESOLVED: Plan skips the probe while any prop is an unresolved\n * Output, and then `reconcile` is the first check. docs/pbs-notifications.md says so.\n * ★ A DEFECT (`Effect.die`), NOT A FAILURE, AND THAT IS WHAT KEEPS IT FROM WEDGING A STAGE. The\n * same `read`-with-no-output shape is Alchemy's RECOVERY read of an interrupted create, at plan\n * and before a delete (Plan.ts, Apply.ts), with the STORED props — and both wrap it in\n * `catchDefect`, degrading to \"nothing recovered\". A typed failure would pass that catch and\n * block the plan or the delete on props the user already fixed. The cold-start probe has no\n * catch, so there the defect fails the plan, as intended. A refused create sent no request,\n * so \"nothing recovered\" is also the truth.\n */\n read: ({ olds, output }: { olds: Props; output: Attributes | undefined }) =>\n (output === undefined ? refuse(olds) : Effect.void).pipe(\n Effect.andThen(readLive(olds)),\n Effect.map((live) =>\n live === undefined ? undefined : { ...live, sealed: output?.sealed ?? '' },\n ),\n ),\n /**\n * ⚠️ `Input<Props>` and `isResolved`, resource.ts's reason: at plan time a prop can still be an\n * unresolved Output, and comparing a placeholder to a live value reports a phantom update.\n * ⛔ A NEW `type` UNDER THE SAME `name` REPLACES DELETE-FIRST: names are unique across every\n * family on the host (api `ensure_unique`), so creating the new one first would be refused.\n */\n diff: ({\n news,\n olds,\n output,\n }: {\n news: Input<Props>;\n olds: Props;\n output: Attributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (output === undefined || !isResolved(news)) return undefined;\n if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {\n return { action: 'replace', deleteFirst: olds.name === news.name } as const;\n }\n yield* refuse(news);\n const live = yield* readLive(news);\n if (live === undefined) return { action: 'update' } as const;\n return toCarry(live, news, resolveGroups(news), output.sealed) === undefined\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n reconcile: ({ news, output }: { news: Props; output: Attributes | undefined }) =>\n Effect.gen(function* () {\n yield* refuse(news);\n const groups = resolveGroups(news);\n const live = yield* readLive(news);\n let sealed = output?.sealed ?? '';\n if (live === undefined) {\n const carry = { header: true, sealed: true } as const;\n yield* requireValues(news, groups, carry);\n const collection = `config/notifications/endpoints/${news.type}`;\n
|
|
89
|
+
"/**\n * The lifecycle of a `Pbs.NotificationTarget`: read, diff, reconcile, delete.\n *\n * ★ SPLIT OUT OF pbs-notification-target.ts FOR THE 250-LINE CAP. That file says what a target is\n * and holds `Provider.of`; this one says what each operation does. The `import type` back is\n * erased, so the cycle is on paper only.\n *\n * ⚠️ NOT `pveHandlers`, AND IT SHOULD LOOK DIFFERENT (resource.ts asks for exactly that). The\n * factory's `diff` sees only the live object, and a secret's value is not in the live object:\n * the seal lives in the PREVIOUS STATE, which only a hand-written `diff` and `reconcile` receive\n * (`output`). Everything else — the read, the read-back refusal, the `matches` guard that keeps\n * adoption free — is the factory's behaviour, written out.\n */\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Effect from 'effect/Effect';\nimport { pve } from './client.ts';\nimport { guardForm } from './constraint-guard.ts';\nimport { pbsTargetEndpoint } from './pbs-notification-target-endpoint.ts';\nimport type {\n PbsNotificationTargetAttributes,\n PbsNotificationTargetProps,\n} from './pbs-notification-target.ts';\nimport {\n type Carry,\n type Groups,\n headerState,\n plainMatches,\n refusals,\n resolveGroups,\n sealedState,\n targetForm,\n} from './pbs-notification-target-form.ts';\nimport { targetAttributes } from './pbs-notification-target-wire.ts';\nimport { seal } from './write-only.ts';\n\ntype Props = PbsNotificationTargetProps;\ntype Attributes = PbsNotificationTargetAttributes;\n\nconst path = (props: Props) => `config/notifications/endpoints/${props.type}/${props.name}`;\n\n/**\n * ⚠️ A 404 and `{\"data\": null}` are answers here — and so, resource.ts's ⛔, is a 403. `pve()`\n * returns `null` for the second, not `undefined`, so the test is `== null`.\n */\nconst readLive = (props: Props) =>\n pve<Record<string, unknown>>(props.target, 'read', 'GET', path(props)).pipe(\n Effect.map((data) => (data == null ? undefined : targetAttributes(data, props))),\n Effect.orElseSucceed(() => undefined),\n );\n\nconst refuse = (props: Props) => {\n const reasons = refusals(props);\n return reasons.length === 0\n ? Effect.void\n : Effect.die(new Error(`${path(props)}: ${reasons.join('; ')}.`));\n};\n\n/** What a write must carry, given what is stale. `undefined` means nothing to write. */\nconst toCarry = (live: Attributes, props: Props, groups: Groups, sealed: string) => {\n const header = headerState(live, props, groups) === 'stale';\n const sealedStale = sealedState(live, props, groups, sealed) === 'stale';\n return plainMatches(live, props) && !header && !sealedStale\n ? undefined\n : ({ header, sealed: sealedStale } satisfies Carry);\n};\n\n/** ⛔ Every variable a carried group needs, or a failure naming the missing ones — never a value. */\nconst requireValues = (props: Props, groups: Groups, carry: Carry) => {\n const missing = [\n ...(carry.header ? groups.header.missing : []),\n ...(carry.sealed ? groups.sealed.missing : []),\n ];\n return missing.length === 0\n ? Effect.void\n : Effect.die(\n new Error(\n `${path(props)}: this write must send write-only values, and ${missing.join(', ')} ` +\n 'is unset or empty in the deploying environment. Export it and deploy again.',\n ),\n );\n};\n\nexport const handlers = {\n /** ⛔ EMPTY: `mail-to-root` is on every host, and adoption stays an explicit act (resource.ts). */\n list: () => Effect.succeed([]),\n /**\n * ⛔ WITH NO `output` THIS IS ALCHEMY'S ADOPTION PROBE, AND THE ONLY PLAN-TIME HOOK A NEW TARGET\n * GETS — so the refusals run here too. Measured on beta.79 (Plan.ts, the `oldState ===\n * undefined` branch): a declaration with no state row is `read` with `olds: news` and never\n * `diff`ed, and Apply then commits its props as `creating` BEFORE `reconcile` runs. A refusal\n * left to `reconcile` fired after the props — a literal token included — were in the store.\n * ⚠️ ONLY WHEN THE PROPS ARE RESOLVED: Plan skips the probe while any prop is an unresolved\n * Output, and then `reconcile` is the first check. docs/pbs-notifications.md says so.\n * ★ A DEFECT (`Effect.die`), NOT A FAILURE, AND THAT IS WHAT KEEPS IT FROM WEDGING A STAGE. The\n * same `read`-with-no-output shape is Alchemy's RECOVERY read of an interrupted create, at plan\n * and before a delete (Plan.ts, Apply.ts), with the STORED props — and both wrap it in\n * `catchDefect`, degrading to \"nothing recovered\". A typed failure would pass that catch and\n * block the plan or the delete on props the user already fixed. The cold-start probe has no\n * catch, so there the defect fails the plan, as intended. A refused create sent no request,\n * so \"nothing recovered\" is also the truth.\n */\n read: ({ olds, output }: { olds: Props; output: Attributes | undefined }) =>\n (output === undefined ? refuse(olds) : Effect.void).pipe(\n Effect.andThen(readLive(olds)),\n Effect.map((live) =>\n live === undefined ? undefined : { ...live, sealed: output?.sealed ?? '' },\n ),\n ),\n /**\n * ⚠️ `Input<Props>` and `isResolved`, resource.ts's reason: at plan time a prop can still be an\n * unresolved Output, and comparing a placeholder to a live value reports a phantom update.\n * ⛔ A NEW `type` UNDER THE SAME `name` REPLACES DELETE-FIRST: names are unique across every\n * family on the host (api `ensure_unique`), so creating the new one first would be refused.\n */\n diff: ({\n news,\n olds,\n output,\n }: {\n news: Input<Props>;\n olds: Props;\n output: Attributes | undefined;\n }) =>\n Effect.gen(function* () {\n if (output === undefined || !isResolved(news)) return undefined;\n if (olds !== undefined && (olds.type !== news.type || olds.name !== news.name)) {\n return { action: 'replace', deleteFirst: olds.name === news.name } as const;\n }\n yield* refuse(news);\n const live = yield* readLive(news);\n if (live === undefined) return { action: 'update' } as const;\n return toCarry(live, news, resolveGroups(news), output.sealed) === undefined\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n reconcile: ({ news, output }: { news: Props; output: Attributes | undefined }) =>\n Effect.gen(function* () {\n yield* refuse(news);\n const groups = resolveGroups(news);\n const live = yield* readLive(news);\n let sealed = output?.sealed ?? '';\n if (live === undefined) {\n const carry = { header: true, sealed: true } as const;\n yield* requireValues(news, groups, carry);\n const collection = `config/notifications/endpoints/${news.type}`;\n const form = targetForm(news, groups, carry, 'create');\n /**\n * ⛔ WITH PRESENCE, BECAUSE THIS BRANCH IS UNCONDITIONALLY THE CREATE. PBS marks `name` on\n * every family and `server`/`from-address` on smtp, `url`/`method` on webhook — and it\n * is the same `comment: maxLength 128` that failed `POST /config/verify`.\n * ⚠️ THE FORM CARRIES SECRETS ON THIS PATH. `violations` reads values and builds only the\n * parameter NAME into its message (constraints.ts), so a refusal never quotes one.\n */\n yield* guardForm(pbsTargetEndpoint(news).create, form, true);\n yield* pve(news.target, 'provision', 'POST', collection, form);\n sealed = seal(groups.sealed.values);\n } else {\n const carry = toCarry(live, news, groups, sealed);\n if (carry !== undefined) {\n yield* requireValues(news, groups, carry);\n const form = targetForm(news, groups, carry, 'update');\n /** ⚠️ NO PRESENCE: an update form is partial by design — it carries what `toCarry` said. */\n yield* guardForm(pbsTargetEndpoint(news).update, form, false);\n yield* pve(news.target, 'provision', 'PUT', path(news), form);\n if (carry.sealed) sealed = seal(groups.sealed.values);\n }\n }\n const after = yield* readLive(news);\n if (after === undefined) {\n // ⛔ resource.ts's rule: \"no error\" is not evidence of a write. Read back, or refuse.\n return yield* Effect.die(\n new Error(`${path(news)}: the write returned no error but the target is still absent.`),\n );\n }\n return { ...after, sealed } satisfies Attributes;\n }),\n delete: ({ olds }: { olds: Props }) => pve(olds.target, 'provision', 'DELETE', path(olds)),\n};\n",
|
|
90
|
+
"/**\n * `Pbs.NotificationTarget`'s six vendor endpoints — one create and one update per family.\n *\n * 🔴 THIS FAMILY WAS THE ONE THE 2026-09-22 SWEEP NEARLY MISSED, and it carries the very\n * parameter the incident was about: `comment` is `maxLength: 128` with a no-control-characters\n * pattern on all three PBS creates, exactly as it is on `POST /config/verify`. A 129-character\n * comment on a notification target would have failed the same way, half a deploy in, on the\n * family whose whole purpose is to make failures visible.\n *\n * ⛔ PBS REGISTERS ONE PATH PER TYPE, NOT A `{type}` PARAMETER, and the three schemas differ:\n * smtp requires `server` and `from-address`, webhook `url` and `method`, sendmail nothing but\n * its name. One key would enforce one family's rules on all three.\n * ⛔ THE STRINGS MUST STAY LITERALS. `codegen/constraints.ts` finds the endpoints to table by\n * SCANNING THIS PACKAGE'S TEXT, so a key built from `props.type` would be tabled by nothing.\n *\n * ⚠️ NO GOTIFY KEY, BECAUSE THIS RESOURCE HAS NO GOTIFY TYPE. `PbsNotificationTargetType` is\n * sendmail | smtp | webhook; PBS 4.2 does publish a gotify endpoint, and nothing here writes to\n * it. ⛔ `scripts/proxmox-ownership-pbs.ts` says otherwise — it claims all four families\n * through the shared `notificationEndpoints` helper — so `docs/api-coverage.md` credits this\n * resource with twelve write endpoints where it really makes nine. That is an overclaim in the\n * hand-written ownership ledger, recorded here rather than fixed in passing: correcting it\n * regenerates a report built from a DIFFERENT (older, 9.2.4) schema manifest, which is its own\n * change with its own diff.\n */\nimport type { PbsNotificationTargetProps } from './pbs-notification-target.ts';\nimport type { EndpointPair } from './resource-spec.ts';\n\nconst ENDPOINTS: Readonly<Record<PbsNotificationTargetProps['type'], EndpointPair>> = {\n sendmail: {\n create: 'pbs:POST /config/notifications/endpoints/sendmail',\n update: 'pbs:PUT /config/notifications/endpoints/sendmail/{name}',\n },\n smtp: {\n create: 'pbs:POST /config/notifications/endpoints/smtp',\n update: 'pbs:PUT /config/notifications/endpoints/smtp/{name}',\n },\n webhook: {\n create: 'pbs:POST /config/notifications/endpoints/webhook',\n update: 'pbs:PUT /config/notifications/endpoints/webhook/{name}',\n },\n};\n\n/** The pair this declaration writes. ⚠️ `type` is identity here — changing it is a replace. */\nexport const pbsTargetEndpoint = (props: PbsNotificationTargetProps): EndpointPair =>\n ENDPOINTS[props.type];\n",
|
|
75
91
|
"/**\n * A credential written as a LITERAL in a plain prop — refused, because a plain prop is a value in\n * the state store.\n *\n * ⛔ THE HOLE THIS CLOSES, MEASURED 2026-09-22 (adversarial review of PR 95). write-only.ts keeps\n * every `{ fromEnv }` value out of state, and the doc WARNED that a plain header value is a prop.\n * A warning is not a guard: `header: { Authorization: 'Bearer <token>' }` planned, deployed, and\n * left the token in the dumped state store — and so did `url: 'https://…?token=<token>'`. Alchemy\n * stores props unencrypted (write-only.ts has the StateEncoding reading), so the only safe\n * literal is one that is not a credential.\n *\n * ★ WHAT COUNTS AS A CREDENTIAL IS A NAME, NOT A VALUE. A value cannot be recognised — a token is\n * any string — but where it sits can: an `Authorization`/`Cookie` header, a header or query\n * parameter whose name has a word like `token`, `key`, `secret`, `password` or `signature`, and a\n * password in the URL's userinfo. A false positive costs one edit (declare it `{ fromEnv }`, or\n * hold it in `secret` and write `{{ secrets.<name> }}`); a false negative costs a leaked token.\n * ★ A TEMPLATE THAT READS A SECRET IS NOT A LITERAL. `Bearer {{ secrets.token }}`, and\n * `{{ url-encode secrets.token }}` in a query, render on PBS from the root-only secret store —\n * the prop holds the reference, not the value.\n *\n * ⚠️ WHAT THIS CANNOT SEE: a token in a URL PATH (a Slack-style `/services/T…/B…/<token>`), in the\n * `body`, or in a `comment`. Those are plain props too; put the token in `secret` and reference\n * it from the template. docs/pbs-notifications.md says the same to the reader.\n * ⛔ A REFUSAL NAMES THE FIELD, NEVER THE VALUE — the message lands in a terminal and a CI log.\n */\nimport type { FromEnv } from './write-only.ts';\n\n/** `{{ secrets.x }}`, `{{secrets.x}}`, `{{ url-encode secrets.x }}` — a reference, not a value. */\nconst READS_SECRET = /\\{\\{[^}]*\\bsecrets\\./;\n\n/** A whole-name match, or one dash/underscore-separated word of the name. */\nconst CREDENTIAL_WORD =\n /(^|[-_])(api[-_]?key|apikey|key|token|secret|password|passwd|pwd|pass|auth|signature|sig|code)($|[-_])/i;\nconst CREDENTIAL_HEADER = /^(proxy-authorization|authorization|cookie)$/i;\n\nexport const isCredentialName = (name: string): boolean =>\n CREDENTIAL_HEADER.test(name) || CREDENTIAL_WORD.test(name);\n\nconst literal = (value: string): boolean => value !== '' && !READS_SECRET.test(value);\n\n/** Plain header values that are credentials by their header's name. `{ fromEnv }` is never one. */\nexport const literalCredentialHeaders = (\n header: Readonly<Record<string, FromEnv | string>> | undefined,\n): string[] =>\n Object.entries(header ?? {})\n .filter(([name, value]) => typeof value === 'string' && isCredentialName(name))\n .filter(([, value]) => literal(value as string))\n .map(([name]) => name)\n .sort();\n\n/**\n * Where a URL carries a literal credential: `userinfo` (a password before the `@`), or the names\n * of credential-shaped query (or fragment) parameters. Parsed by hand, not with `new URL`: a URL here is a\n * TEMPLATE, and `{{ … }}` in the host or the userinfo is not a URL `new URL` will accept.\n */\nexport const literalCredentialsInUrl = (url: string | undefined): string[] => {\n if (url === undefined) return [];\n const out: string[] = [];\n const authority = /^[a-z][a-z0-9+.-]*:\\/\\/([^/?#]*)/i.exec(url)?.[1] ?? '';\n const at = authority.lastIndexOf('@');\n if (at >= 0) {\n const userinfo = authority.slice(0, at);\n const colon = userinfo.indexOf(':');\n if (colon >= 0 && literal(userinfo.slice(colon + 1))) out.push('userinfo');\n }\n // ⚠️ THE FRAGMENT TOO: it is never sent, but it is still a prop — and so still in state.\n const params = url.replace(/^[^?#]*/, '');\n for (const pair of params.split(/[?&#]/)) {\n const eq = pair.indexOf('=');\n const name = eq < 0 ? pair : pair.slice(0, eq);\n if (eq >= 0 && isCredentialName(name) && literal(pair.slice(eq + 1))) out.push(`?${name}`);\n }\n return out;\n};\n",
|
|
76
92
|
"/**\n * How a PBS notification target looks on the wire, and what of it may be remembered.\n *\n * ★ SPLIT OUT OF pbs-notification-target.ts FOR THE 250-LINE CAP, on the seam\n * notification-target-form.ts cut for PVE: the resource file says what a target IS and when it\n * changed; this file turns a live answer into attributes and a value into a wire item. The\n * `import type` back is erased, so the cycle is on paper only.\n *\n * ⛔ THE LIVE ANSWER HOLDS HEADER VALUES, AND NONE OF THEM REACHES AN ATTRIBUTE. PBS returns a\n * webhook's headers in full — `name=Authorization,value=<base64>` — to anyone with Sys.Audit on\n * /system/notifications (proxmox-notify api/webhook.rs `get_endpoint`, read at HEAD 2026-09-22).\n * Copying them into attributes would copy whatever a human pasted into a header, a bearer token\n * included, into the unencrypted state store. So attributes keep header NAMES and a fixed-salt\n * scrypt digest of the pairs (write-only.ts), enough to notice a change and nothing to send.\n * ⚠️ SECRETS ARE SAFER ON THE SERVER, AND THE READ SHOWS IT: `get_endpoint` blanks every secret's\n * value and returns names only, from a root-only `notifications-priv.cfg`. A credential belongs\n * in `secret` and is referenced as `{{ secrets.<name> }}` from a header, the URL or the body.\n */\nimport { Buffer } from 'node:buffer';\nimport { addressList, portOf } from './notification-target-form.ts';\nimport type {\n PbsNotificationTargetAttributes,\n PbsNotificationTargetProps,\n} from './pbs-notification-target.ts';\nimport { bool, text } from './values.ts';\nimport { seal } from './write-only.ts';\n\nexport const base64 = (value: string): string => Buffer.from(value, 'utf8').toString('base64');\n\n/** ⚠️ Node's decoder skips characters it does not know rather than throwing, so this never throws. */\nexport const unbase64 = (value: string): string => Buffer.from(value, 'base64').toString('utf8');\n\n/**\n * One `KeyAndBase64Val` property string — `name=<name>,value=<base64>` — split into its halves.\n * ⚠️ KEYED BY NAME, NOT BY POSITION: the server serialises `name` first today, and a parser that\n * relied on that would read a reordered item as a header called `value=…`.\n * ★ A comma cannot occur inside either half: base64 has none, and `refusals` keeps names clean.\n */\nexport const keyAndValue = (item: string): { name: string; value?: string } => {\n const parts: Record<string, string> = {};\n for (const part of item.split(',')) {\n const at = part.indexOf('=');\n if (at > 0) parts[part.slice(0, at).trim()] = part.slice(at + 1).trim();\n }\n const value = parts['value'];\n return value === undefined ? { name: parts['name'] ?? '' } : { name: parts['name'] ?? '', value };\n};\n\n/** The wire item for one header or secret. The value is base64 — the server decodes it to use it. */\nexport const wireItem = (name: string, value: string): string =>\n `name=${name},value=${base64(value)}`;\n\nconst list = (value: unknown): readonly string[] =>\n Array.isArray(value) ? value.map((item: unknown) => String(item)) : [];\n\n/** Sorted, comma-joined names — presence, the only thing the store may know about the values. */\nexport const names = (keys: readonly string[]): string =>\n [...keys]\n .filter((key) => key !== '')\n .sort()\n .join(',');\n\n/**\n * ★ A FIXED SALT PER TARGET, SO AN UNCHANGED HEADER SET DIGESTS THE SAME ON EVERY READ. A random\n * one would rewrite the state row on every plan. The server shows these values to its auditors\n * anyway; what the salt and scrypt buy is that the STORE does not become a second, weaker copy.\n */\nexport const headerDigest = (target: string, pairs: Readonly<Record<string, string>>): string =>\n seal(pairs, `hf:pbs-notification-target:${target}:header`);\n\n/**\n * Live JSON to attributes. `sealed` is left empty here: it is not the server's to report, and the\n * handlers in pbs-notification-target.ts carry it forward from the previous state.\n */\nexport const targetAttributes = (\n live: Record<string, unknown>,\n props: PbsNotificationTargetProps,\n): PbsNotificationTargetAttributes => {\n const headers: Record<string, string> = {};\n for (const item of list(live['header'])) {\n const { name, value } = keyAndValue(item);\n if (name !== '') headers[name] = unbase64(value ?? '');\n }\n const secrets = list(live['secret']).map((item) => keyAndValue(item).name);\n return {\n author: text(live['author']),\n // ⚠️ DECODED, unlike the PVE family's: the server stores exactly the base64 it was handed and\n // decodes it to render (endpoints/webhook.rs `build_request`), so decoding here is a pure\n // function of what was written and `matches` compares readable text rather than base64.\n body: unbase64(text(live['body'])),\n comment: text(live['comment']),\n disable: bool(live['disable']),\n 'from-address': text(live['from-address']),\n header: names(Object.keys(headers)),\n headerDigest: headerDigest(props.name, headers),\n mailto: addressList(live['mailto']),\n 'mailto-user': addressList(live['mailto-user']),\n method: text(live['method']),\n mode: text(live['mode']),\n name: props.name,\n // ★ ON THE PER-OBJECT READ, MEASURED on PBS 4.2 (`notification endpoint sendmail show\n // mail-to-root` answers `origin: builtin`). Reported, never compared.\n origin: text(live['origin']),\n port: portOf(live['port']),\n secret: names(secrets),\n sealed: '',\n server: text(live['server']),\n type: props.type,\n url: text(live['url']),\n username: text(live['username']),\n };\n};\n",
|
|
77
93
|
"/**\n * Values a declaration may SEND and must never STORE — a webhook secret, a header carrying a\n * credential, an smtp password — and the one form in which a plan may still remember them.\n *\n * ⛔ A SECRET PROP IS A SECRET IN THE STATE STORE, WHATEVER TYPE IT HAS. Alchemy persists props as\n * well as attributes — `delete` is handed `olds`, which can only have come from the store — and\n * it does not encrypt either: `StateEncoding.ts` TAGS a `Redacted` value and writes the inner\n * value beside the tag. So `Redacted` hides a secret from a log line and from nothing else.\n * notification-target.ts (PVE) answered that by making no secret declarable at all, and paid\n * for it: an authenticated smtp target or a signed webhook could not be created from code.\n * ★ SO THE PROP HOLDS A NAME, NOT A VALUE. `{ fromEnv: 'PBS_HOOK_TOKEN' }` names the variable the\n * DEPLOYING process reads at call time — the shape forgejo/org-actions-secrets.ts already uses.\n * The name is what lands in state; the value exists only in that process's memory and on the\n * wire to the server.\n *\n * ⚠️ A PLAN STILL NEEDS TO KNOW WHETHER A VALUE CHANGED, AND IT CANNOT ASK THE SERVER. PBS returns a\n * webhook secret's NAME and never its value, and never returns an smtp password at all. So what\n * was last written is remembered as a SEAL: a salted scrypt digest, which lets the next plan\n * check \"is the value in my environment the one I wrote\" without the store holding anything a\n * reader could send to PBS.\n * ⚠️ A DIGEST OF A GUESSABLE SECRET IS STILL GUESSABLE. The salt defeats precomputed tables and\n * scrypt makes each guess cost memory and time; neither makes a dictionary word safe. The\n * remedy is a secret worth the name — a random token — not a faster hash.\n */\nimport { Buffer } from 'node:buffer';\nimport { randomBytes, scryptSync, timingSafeEqual } from 'node:crypto';\n\n/** A write-only value, by the NAME of the environment variable holding it. Never the value. */\nexport interface FromEnv {\n readonly fromEnv: string;\n}\n\n/** Where a value is looked up. Defaults to `process.env`, read at call time, never at load. */\nexport type Environment = Readonly<Record<string, string | undefined>>;\n\n/**\n * Each `name → FromEnv` resolved, or the variable names that could not be.\n *\n * ⚠️ AN EMPTY VARIABLE IS A MISSING ONE. A secret renderer that failed writes `NAME=` rather than\n * omitting the line, and sending `''` would store an empty secret on the server — a webhook that\n * authenticates with nothing, reported as a successful deploy.\n * ⚠️ THE VALUE IS NOT TRIMMED. A token with a trailing newline is a broken header either way, and\n * trimming here would make the seal disagree with a value written by any other tool.\n */\nexport const resolveAll = (\n refs: Readonly<Record<string, FromEnv>>,\n env: Environment = process.env,\n): { readonly values: Readonly<Record<string, string>>; readonly missing: readonly string[] } => {\n const values: Record<string, string> = {};\n const missing: string[] = [];\n for (const [key, ref] of Object.entries(refs)) {\n const value = env[ref.fromEnv];\n if (value === undefined || value === '') missing.push(ref.fromEnv);\n else values[key] = value;\n }\n return { missing: [...new Set(missing)].sort(), values };\n};\n\n/**\n * ★ PARAMETERS ARE NODE'S OWN SCRYPT DEFAULTS (N 2^14, r 8, p 1) — 16 MiB and tens of\n * milliseconds per digest. Plan runs it a handful of times per resource, which is noise, and a\n * guesser runs it once per guess, which is the point.\n */\nconst COST = { N: 16_384, maxmem: 64 * 1024 * 1024, p: 1, r: 8 } as const;\nconst PREFIX = 'scrypt';\n\n/** Key order must not change a digest, so entries are sorted and serialised as JSON pairs. */\nconst canonical = (values: Readonly<Record<string, string>>): string =>\n JSON.stringify(\n Object.keys(values)\n .sort()\n .map((key) => [key, values[key]]),\n );\n\nconst digest = (salt: Buffer, values: Readonly<Record<string, string>>): Buffer =>\n scryptSync(canonical(values), salt, 32, COST);\n\n/**\n * `scrypt:<salt>:<digest>`, or `''` when there is nothing to seal.\n *\n * ★ A RANDOM SALT BY DEFAULT, FOR WHAT THIS PROVIDER WROTE. Pass a fixed one only for a value the\n * server returns anyway (a header), where a fresh salt per read would churn the state row on\n * every plan without protecting anything the server does not already show its auditors.\n */\nexport const seal = (values: Readonly<Record<string, string>>, salt?: string): string => {\n if (Object.keys(values).length === 0) return '';\n const bytes = salt === undefined ? randomBytes(16) : Buffer.from(salt, 'utf8');\n return `${PREFIX}:${bytes.toString('base64url')}:${digest(bytes, values).toString('base64url')}`;\n};\n\n/**\n * Whether `values` are the ones `sealed` was made from.\n *\n * ⚠️ A MALFORMED OR EMPTY SEAL ANSWERS FALSE, never throws: callers decide what \"no evidence\"\n * means, and every caller here treats it as \"cannot tell\" rather than as drift.\n */\nexport const sealMatches = (sealed: string, values: Readonly<Record<string, string>>): boolean => {\n if (sealed === '') return Object.keys(values).length === 0;\n const [prefix, salt, stored, extra] = sealed.split(':');\n if (prefix !== PREFIX || salt === undefined || stored === undefined || extra !== undefined) {\n return false;\n }\n const expected = Buffer.from(stored, 'base64url');\n const actual = digest(Buffer.from(salt, 'base64url'), values);\n return expected.length === actual.length && timingSafeEqual(expected, actual);\n};\n",
|
|
@@ -88,20 +104,20 @@
|
|
|
88
104
|
"/**\n * `declareProvisionBaseline` — the provisioning baseline (provision-baseline.ts) as resources, so a\n * stack adopts what the bootstrap made and keeps it: the role, the mint group, one user per lane\n * (its group membership included) and the grant binding each user to its role on `/`.\n *\n * const lane = yield* declareProvisionBaseline('pve', target);\n * // names are generic by default; a site passes its own:\n * const lane = yield* declareProvisionBaseline('pve', target, { role: 'Provisioner' });\n *\n * Provide `ProxmoxRoleProvider()`, `ProxmoxGroupProvider()`, `ProxmoxUserProvider()` and\n * `ProxmoxAclProvider()` with `FetchHttpClient.layer`.\n *\n * ⛔ EVERY RESOURCE HERE RETAINS. This is the branch the whole package sits on (user.ts): dropping\n * the call from a stack must drop state, never the role, the users or their grants — deleting the\n * provision user revokes every lease at once, the plan that would recreate it included.\n * ⛔ ADOPTION IS THE OPERATOR'S TO STATE, AS EVERYWHERE IN THE KIT (docs/ownership.md). The first\n * deploy after the bootstrap finds all of this live with no state: run it with `--adopt`, or pass\n * `{ adopt: true }`, which pipes `adopt(true)` onto each resource. `{ adopt: false }` pins them\n * refused under a deploy-wide `--adopt`. Left out, the deploy's own policy decides.\n * ⚠️ TODAY THE FOUR FAMILIES STILL READ A LIVE OBJECT WITH NO STATE AS OURS (the `pveHandlers`\n * limit in ownership.md), so the flag is not yet what stops a claim. It is stated here so that\n * when they follow the rule, a stack built on this helper already says what it means.\n * ★ THE BOOTSTRAP MAKES EXACTLY THIS, so a clean adoption writes nothing: the same privileges, the\n * same comment, the same single group per user (provision-bootstrap.ts reads the same baseline).\n * ★ PLAIN NAMES, NOT ONE RESOURCE'S OUTPUT IN ANOTHER'S PROPS. An Output would order the creates,\n * but Alchemy skips the adoption probe while any prop is one (Plan.ts), so the first deploy after\n * the bootstrap would plan `create` for objects that are all there. Order never matters here:\n * nothing in this package can run before the bootstrap, and after it everything exists.\n */\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as RemovalPolicy from 'alchemy/RemovalPolicy';\nimport * as Effect from 'effect/Effect';\nimport { ProxmoxAcl } from './acl.ts';\nimport type { PveTarget } from './credentials.ts';\nimport { ProxmoxGroup } from './group.ts';\nimport { type ProvisionNames, provisionBaseline } from './provision-baseline.ts';\nimport { ProxmoxRole } from './role.ts';\nimport { ProxmoxUser } from './user.ts';\n\nexport interface DeclareProvisionOptions {\n /** `true`/`false` pipe `adopt(…)` onto every resource; omitted, the deploy's policy decides. */\n readonly adopt?: boolean;\n}\n\n/**\n * The baseline for one cluster. `id` prefixes every logical id (`<id>-role`, `<id>-mint-group`,\n * `<id>-provision-user`, `<id>-provision-grant`, and the `read` pair), so one stack can declare a\n * baseline per cluster. `users` and `grants` come back in `baseline.users` / `baseline.grants`\n * order: provision, then read. Fails, before declaring anything, on a name the baseline refuses.\n */\nexport const declareProvisionBaseline = (\n id: string,\n target: PveTarget,\n names: ProvisionNames = {},\n options: DeclareProvisionOptions = {},\n) =>\n Effect.gen(function* () {\n const baseline = yield* Effect.try({\n catch: (cause) => (cause instanceof Error ? cause : new Error(String(cause))),\n try: () => provisionBaseline(names),\n });\n const owned = <A, R>(resource: Effect.Effect<A, never, R>) => {\n const kept = resource.pipe(RemovalPolicy.retain());\n return options.adopt === undefined ? kept : kept.pipe(adopt(options.adopt));\n };\n const role = yield* owned(\n ProxmoxRole(`${id}-role`, {\n privs: [...baseline.role.privs],\n roleid: baseline.role.roleid,\n target,\n }),\n );\n const group = yield* owned(ProxmoxGroup(`${id}-mint-group`, { ...baseline.group, target }));\n // ★ `Effect.all` over mapped declarations (sequential), as cloudflare-permission-groups.ts does:\n // oxlint's unicorn/no-array-for-each cannot tell `Effect.forEach` from `Array#forEach`.\n const users = yield* Effect.all(\n baseline.users.map((user) =>\n owned(\n ProxmoxUser(`${id}-${user.lane}-user`, {\n comment: user.comment,\n groups: [...user.groups],\n target,\n userid: user.userid,\n }),\n ),\n ),\n );\n const grants = yield* Effect.all(\n baseline.grants.map((grant) =>\n owned(\n ProxmoxAcl(`${id}-${grant.lane}-grant`, {\n path: '/',\n propagate: true,\n roleid: grant.roleid,\n target,\n type: 'user',\n ugid: grant.userid,\n }),\n ),\n ),\n );\n return { baseline, grants, group, role, users };\n });\n",
|
|
89
105
|
"/**\n * `Proxmox.Role` — a PVE role: one name, one set of privileges. The object every other resource\n * in this package stands on, because a role is what the provision credential actually holds.\n *\n * ★ THIS IS WHERE pool.ts's ⚠️ STOPS BEING A MEMORY. That file records, in prose, that the\n * provision role lacked `Pool.Allocate`, that reconcile answered \"Permission check failed\", and\n * that a human widened the role over SSH with `pveum role modify`. Prose cannot be planned\n * against: it goes stale the first time somebody edits the role in the UI and tells nobody.\n * Declared here, the same fact is a diff — the privileges the role is SUPPOSED to hold are read\n * off the cluster on every plan, and a hand edit shows up as `1 to update` rather than as a 403\n * six weeks later in the middle of something else.\n *\n * ⛔ THE INDEX AND THE ITEM DISAGREE ABOUT WHAT `privs` IS, AND MISSING THAT COSTS A DIFF THAT\n * NEVER CONVERGES. `GET /access/roles` (the index) reports each role's privileges as a COMMA\n * STRING. `GET /access/roles/{roleid}` — the path this resource reads — returns a privilege MAP\n * instead, `{\"VM.Allocate\":1,\"Sys.Audit\":1,…}`, and its key order is a Perl hash's order, so it\n * is not stable between two calls to the same endpoint. Compare the raw string, or the raw\n * object, or the keys in the order they arrived, and every plan reports an update forever.\n * `canonical` below is the whole answer: both sides become a sorted, de-duplicated set before\n * anything is compared, and `attributes` STORES the sorted form so Alchemy's state does not\n * churn either.\n *\n * ⚠️ A DECLARATION REPLACES THE PRIVILEGE SET; IT DOES NOT ADD TO IT. `PUT /access/roles/{roleid}`\n * accepts an `append` flag and this resource deliberately never sends it — with append a role\n * could only ever grow, so `matches` would report an update forever whenever props were a subset\n * of live, which is the same perpetual diff by a different road. The consequence is the one that\n * bites: anything a human added by hand is REVOKED on the next deploy unless it is in `privs`.\n * Copy the live set out of `pveum role list` before declaring a role that already exists; do not\n * type it from memory.\n *\n * ⛔ A ROLE CAN LOCK ITS OWN PROVIDER OUT, AND NOTHING IN THIS PACKAGE CAN UNDO IT. Reconcile runs\n * as the provision credential, and that credential holds a role. Declare THAT role without the\n * privileges the provider needs — `Sys.Modify` on `/access` above all — and the write succeeds,\n * after which every later plan reads 403 and the repair has to happen out of band as `root@pam`\n * over SSH. It is a one-way door, which is why the privileges a reconcile needs are written\n * down at the bottom of this comment rather than left to be rediscovered from an error message.\n *\n * ⚠️ PVE REFUSES TO EDIT ITS OWN BUILT-IN ROLES (`Administrator`, `NoAccess`, the `PVE*` set), and\n * the item endpoint gives no sign of which those are — only the index carries the `special`\n * flag. A declaration aimed at a built-in role therefore READS BACK CLEANLY and fails at write\n * time, which reads like a broken provider rather than like a refusal. Declare roles you own.\n *\n * ★ PRIVILEGES A RECONCILE NEEDS: `Sys.Audit` on `/access` to read and diff, `Sys.Modify` on\n * `/access` to create, update or delete. Not `Permissions.Modify` — binding a role to a user is\n * `/access/acl`, a different object, and nothing here writes it. An auditor-shaped credential\n * can already PLAN this resource and cannot DEPLOY it, which is the honest failure: the plan is\n * true and the deploy says exactly which privilege is missing.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\n\nexport interface RoleProps extends WithTarget {\n /**\n * PVE's primary key for a role.\n *\n * ⚠️ THERE IS NO RENAME. `path` is built from this, so changing it makes the factory read\n * nothing live, create a role under the new name, and leave the old one on the cluster with\n * nobody managing it. Delete the resource and declare a new one rather than editing this.\n */\n roleid: string;\n /**\n * The COMPLETE privilege set, e.g. `['VM.Allocate', 'VM.Audit', 'Sys.Audit']`. Order and\n * duplicates do not matter — see the ⛔ in the header — but completeness does: this list is the\n * role, not an addition to it.\n *\n * ⚠️ DECLARE AT LEAST ONE. A role with no privileges grants nothing and reads back as an empty\n * document, which is the one shape the factory cannot tell apart from an object that is not\n * there — reconcile would then refuse with its \"the write returned no error but the object is\n * still absent\" message, which is true of the read and misleading about the cause.\n */\n privs: string[];\n}\n\nexport interface RoleAttributes {\n roleid: string;\n /** Sorted and de-duplicated on the way in, so persisted state is identical across plans. */\n privs: string[];\n}\n\nexport interface ProxmoxRole extends Resource<\n 'Proxmox.Role',\n RoleProps,\n RoleAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxRole = Resource<ProxmoxRole>('Proxmox.Role');\n\n/**\n * The only shape two privilege sets may be compared in: sorted, de-duplicated, blanks dropped.\n *\n * ⚠️ THE TRIM AND THE EMPTY FILTER ARE NOT DECORATION. `'A,B,'.split(',')` yields a trailing `''`,\n * and a hand-written list is quite likely to carry a stray space after a comma. Either one turns\n * into a phantom member that no live answer can contain, so `matches` would be false on every\n * plan and the deploy would rewrite the role to exactly what it already was.\n */\nconst canonical = (privs: readonly string[]): string[] =>\n [...new Set(privs.map((priv) => priv.trim()).filter((priv) => priv.length > 0))].sort();\n\n/**\n * The privileges in a live answer, from EITHER shape PVE uses for them.\n *\n * ⚠️ THE MAP BRANCH IS THE ONE THAT RUNS HERE — the item endpoint answers `{\"VM.Allocate\":1,…}`,\n * so the privilege names are the KEYS and the `1`s carry no information. The string branch\n * exists because the index answers `{\"privs\":\"VM.Allocate,Sys.Audit\",…}` for that same role, and\n * a reader that assumed the map shape for an index entry would compare the words `privs`,\n * `roleid` and `special` against real privileges and report drift forever.\n *\n * ⚠️ A KEY WITH NO DOT IS NOT A PRIVILEGE. Every PVE privilege is `Category.Name` — VM.Allocate,\n * Sys.Modify, Datastore.AllocateSpace, Pool.Audit — while the metadata PVE mixes into role\n * answers (`special`, marking a built-in) is a bare word. Filtering on the dot keeps a flag from\n * being diffed as though somebody had granted it.\n */\nconst livePrivs = (live: Record<string, unknown>): string[] => {\n const listed = live['privs'];\n if (typeof listed === 'string') return canonical(listed.split(','));\n return canonical(Object.keys(live).filter((key) => key.includes('.')));\n};\n\nconst handlers = pveHandlers<RoleProps, RoleAttributes>({\n attributes: (live, props) => ({ privs: livePrivs(live), roleid: props.roleid }),\n collection: () => 'access/roles',\n /** ⚠️ PVE wants ONE comma string here, not a repeated field — `privs=A,B,C`. */\n createForm: (props) => ({ privs: canonical(props.privs).join(','), roleid: props.roleid }),\n /**\n * ⛔ NORMALISED ON BOTH SIDES, even though `attributes` already arrives sorted. This is a\n * predicate, not a fast path: the cost of trusting one side's order is not a slow plan, it is\n * a plan that reports an update every single time and a deploy that writes the same role back\n * forever. Cheap insurance against the exact trap named in the header.\n */\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: { create: 'pve:POST /access/roles', update: 'pve:PUT /access/roles/{roleid}' },\n matches: (attributes, props) =>\n canonical(attributes.privs).join(',') === canonical(props.privs).join(','),\n path: (props) => `access/roles/${props.roleid}`,\n /**\n * ⚠️ NO `append` FIELD, DELIBERATELY. Sending `append=1` would make every update additive, so a\n * privilege could be granted from here but never taken away — and a role that cannot narrow\n * is not a declaration. See the ⚠️ in the header for what that means for hand edits.\n */\n updateForm: (props) => ({ privs: canonical(props.privs).join(',') }),\n});\n\n/**\n* ⛔ AN EMPTY LIST, AND NOWHERE DOES IT MATTER MORE. `GET /access/roles` returns every\n* role on the cluster, PVE's own built-ins included. Returning them would invite Alchemy\n* to adopt `Administrator` — and therefore one day to narrow or delete it. Adoption is\n* an explicit act, here as everywhere else in this package.\n \n *\n* ⛔ DO NOT CARRY THE FACTORY'S REASSURANCE OVER TO THIS ONE. `destroy` there notes that\n* PVE refuses to delete things still in use — a pool holding guests, a storage with\n* volumes — and a role is NOT protected that way. Whoever is bound to the role loses\n* those privileges the moment it goes, and the credential this provider runs with is\n* bound through exactly such a binding. Checking who holds a role before removing it is\n* an operator's job, and it is not done here on their behalf while they read a diff.\n \n */\nexport const ProxmoxRoleProvider = () =>\n Provider.effect(ProxmoxRole, Effect.succeed(ProxmoxRole.Provider.of(handlers)));\n",
|
|
90
106
|
"/**\n * `Proxmox.User` — a PVE account. The identity every other resource in this package borrows.\n *\n * ★ THIS IS THE ONE OBJECT THAT CAN CUT THE BRANCH IT SITS ON. The user the credential mount vends\n * tokens for, and the group whose members are allowed to mint, are what `src/credentials.ts`\n * ultimately asks OpenBao for. A cluster rebuilt without them cannot plan ANY resource here —\n * including the plan that would recreate them, which needs a credential to run. So this resource\n * is drift repair for an account that already exists (comment, email, enable, expire, groups),\n * not a bootstrap: the first mint user is made on a node with `pveum user add`, by a human, once.\n *\n * ⛔ THERE IS NO `password` PROP, AND ADDING ONE WOULD BE A LEAK RATHER THAN A FEATURE. Alchemy\n * writes resource state WITHOUT encryption — see the ⛔ in `credentials.ts` — so a password prop\n * would sit in clear in whatever database the stack points `state` at, and in every backup of it.\n * It is also unnecessary: `password` is accepted only by the CREATE call, changing one afterwards\n * is `PUT /access/password` (a different endpoint, a different privilege), and an account in a\n * realm such as `@pam`, `@ldap` or `@openid` has no PVE-side password at all — the realm holds\n * it. Out of scope by design. `keys` (TFA) is omitted for the same reason.\n *\n * ⚠️ `groups` COMES BACK AS AN ARRAY AND GOES OUT AS A COMMA STRING, and the asymmetry is the whole\n * trap. GET answers `[\"a\",\"b\"]`, POST/PUT want `a,b`, and PVE returns the list in ITS order, not\n * the declared one. Compared naively, `matches` reads ['b','a'] against 'a,b' and every plan for\n * the rest of time says \"1 to update\". Both sides are normalised to a sorted, deduplicated set\n * below. Joining is a safe comparison precisely because a group id cannot contain a comma — the\n * wire format is comma-separated, so PVE refuses one.\n *\n * ⚠️ `tokens` IS NESTED AND IS NOT SETTABLE THROUGH THIS ENDPOINT. The read hands back a map of the\n * account's API tokens; `PUT /access/users/{userid}` has no parameter for them, because they are\n * their own objects under `.../token/{tokenid}`. So they are reported as an attribute — a plan\n * can then say what a delete would take with it — and kept OUT of `matches`, exactly like\n * `pool.members`. Reported, never declared.\n *\n * ⚠️ RECONCILE NEEDS PRIVILEGES A GUEST-ONLY ROLE DOES NOT HOLD, so they are stated rather than met\n * as a 403 in the middle of a deploy (pool.ts records how that went the last time).\n * `PROVISION_PRIVILEGES` (provision-baseline.ts) carries every one of them:\n * · `Realm.AllocateUser` on `/access/realm/<realm>` — create and delete.\n * · `User.Modify` on `/access/groups`, and on `/access/groups/<group>` for EVERY group named in\n * `groups` — create, update and delete. PVE checks the groups you are granting, not just the\n * user, so a role wide enough to edit the account can still be refused for one group in the\n * list.\n * · `Sys.Audit` (or `User.Modify`) for the read, which the `read` role's auditor already has.\n * `Permissions.Modify` is NOT on this list: that governs ACL entries, which are a different PVE\n * object and would be a different resource here.\n *\n * ⚠️ A MISSING USER IS A 500, NOT A 404 — \"no such user ('x@pve')\" — and the factory turns any\n * failed read into \"absent\". A 403 therefore also reads as absent, and the honest error arrives\n * one step later, from the create: \"Permission check failed (/access/groups, User.Modify)\". That\n * string is the one to grep for when a plan insists on creating an account that plainly exists.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool } from './values.ts';\n\nexport interface UserProps extends WithTarget {\n /**\n * ⛔ REALM-QUALIFIED, ALWAYS: `someone@pve`, `someone@pam`. The realm is part of PVE's primary\n * key, and a bare name is refused with \"value does not look like a valid user id\".\n *\n * ⚠️ PVE HAS NO RENAME. Editing this prop makes the path point at a DIFFERENT account, which\n * reads as absent and is then created — while the old one stays on the cluster with its tokens\n * and its ACL entries. Renaming is a delete and a create, and should be declared as one.\n */\n userid: string;\n /** Free text shown in the UI. */\n comment?: string;\n /** PVE accepts the empty string here; that is how an address is cleared. */\n email?: string;\n /** Default true, matching PVE's own `enable=1`. False disables login without deleting anything. */\n enable?: boolean;\n /** Seconds since the epoch. 0 — the default — means the account never expires. */\n expire?: number;\n /** Group memberships. Order and duplicates carry no meaning — see the ⚠️ in the header. */\n groups?: string[];\n firstname?: string;\n lastname?: string;\n}\n\nexport interface UserAttributes {\n userid: string;\n comment: string;\n email: string;\n enable: boolean;\n expire: number;\n /** Normalised: sorted, deduplicated, whatever shape the cluster used on the wire. */\n groups: string[];\n firstname: string;\n lastname: string;\n /**\n * API token names this account owns. Reported so a plan can show what a delete would revoke;\n * never compared, because this endpoint cannot set them.\n */\n tokens: string[];\n}\n\nexport interface ProxmoxUser extends Resource<\n 'Proxmox.User',\n UserProps,\n UserAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxUser = Resource<ProxmoxUser>('Proxmox.User');\n\nconst str = (value: unknown) => (typeof value === 'string' ? value : '');\n\n/**\n * ⚠️ MORE FORGIVING THAN `lxc.ts`'s `num`, ON PURPOSE. PVE's JSON is generated from a Perl schema\n * and an integer field can arrive as `0` or as `\"0\"` depending on the release and the endpoint. A\n * strict `typeof === 'number'` would silently fall back to 0 for the string form, so a declared\n * `expire` would never equal the live one and the plan would report an update it cannot settle.\n */\nconst int = (value: unknown) => {\n const parsed = typeof value === 'number' ? value : Number(value ?? 0);\n return Number.isFinite(parsed) ? parsed : 0;\n};\n\n/**\n * An array (what the read returns), a comma string (what older releases and the write side use), or\n * nothing — in; a sorted, deduplicated list out. Both sides of `matches` go through this, which is\n * the only reason the diff ever settles.\n */\nconst groupSet = (value: unknown): string[] => {\n const raw: unknown[] = Array.isArray(value)\n ? value\n : typeof value === 'string'\n ? value.split(',')\n : [];\n const names = raw.map((group) => str(group).trim()).filter((group) => group !== '');\n return [...new Set(names)].sort();\n};\n\n/**\n * ⚠️ THE SHAPE DEPENDS ON WHICH ENDPOINT YOU ASKED. Reading ONE user gives an object keyed by token\n * name; the collection read gives an array of records. This provider only ever reads the single\n * user path, so the map is what arrives — anything else is reported as \"no tokens\" rather than\n * guessed at, because a wrong guess here would show up as a phantom revocation in a plan.\n */\nconst tokenNames = (value: unknown): string[] =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n ? Object.keys(value).sort()\n : [];\n\n/** Everything mutable, in the form PVE wants. Create adds `userid`; update sends exactly this. */\nconst shape = (props: UserProps) => ({\n comment: props.comment ?? '',\n email: props.email ?? '',\n enable: props.enable === false ? '0' : '1',\n expire: String(props.expire ?? 0),\n firstname: props.firstname ?? '',\n /**\n * ⚠️ ALWAYS SENT, EVEN EMPTY. Omitting `groups` on a PUT leaves the existing memberships in\n * place, so a group removed from the declaration would never actually be removed — and\n * `matches` would keep reporting an update that the update cannot fix. Same for `email` and\n * `comment`: the empty string is how PVE is told to clear a field.\n * ⚠️ AND `append` IS DELIBERATELY NOT SET. Its default (0) REPLACES the list; setting it to 1\n * would turn every reconcile into an add-only merge, which is the same non-settling diff.\n */\n groups: groupSet(props.groups).join(','),\n lastname: props.lastname ?? '',\n});\n\nconst handlers = pveHandlers<UserProps, UserAttributes>({\n attributes: (live, props) => ({\n comment: str(live['comment']),\n email: str(live['email']),\n /**\n * ⚠️ ABSENT MEANS ENABLED. PVE's schema defaults `enable` to 1 and does not always write the\n * key back for an enabled account. Reading absence as `false` would report an update on every\n * plan and then DISABLE the account on the deploy that \"fixed\" it.\n */\n enable: bool(live['enable'], true),\n expire: int(live['expire']),\n firstname: str(live['firstname']),\n groups: groupSet(live['groups']),\n lastname: str(live['lastname']),\n tokens: tokenNames(live['tokens']),\n /** ⚠️ FROM PROPS: `GET /access/users/{userid}` does not echo the userid back in its data. */\n userid: props.userid,\n }),\n collection: () => 'access/users',\n createForm: (props) => ({ ...shape(props), userid: props.userid }),\n /**\n * ⚠️ `tokens` IS NOT COMPARED, and neither is anything create-only. What is compared is exactly\n * what a PUT can put back; a field that PVE reports but will not accept is drift nobody can\n * fix, and diffing it would report an update on every plan forever.\n */\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: { create: 'pve:POST /access/users', update: 'pve:PUT /access/users/{userid}' },\n matches: (attributes, props) =>\n attributes.comment === (props.comment ?? '') &&\n attributes.email === (props.email ?? '') &&\n attributes.enable === (props.enable !== false) &&\n attributes.expire === (props.expire ?? 0) &&\n attributes.firstname === (props.firstname ?? '') &&\n attributes.lastname === (props.lastname ?? '') &&\n attributes.groups.join(',') === groupSet(props.groups).join(','),\n path: (props) => `access/users/${props.userid}`,\n updateForm: shape,\n});\n\n/**\n* ⛔ EMPTY, AND MORE POINTEDLY SO HERE THAN ANYWHERE ELSE. `GET /access/users` answers with\n* every account on the cluster: `root@pam`, every human who logs in, every service\n* identity someone made years ago. Handing that list to Alchemy would invite it to adopt\n* — and therefore one day DELETE — people. Adoption is an explicit act.\n \n *\n* ⛔ DELETING A USER TAKES ITS API TOKENS AND ITS ACL ENTRIES WITH IT, in one call and\n* without a confirmation. For the identity a credential mount vends from, that revokes\n* every outstanding lease at once and every plan in this package stops working — this one\n* included. PVE refuses only for `root@pam`; for everyone else it simply obeys.\n \n */\nexport const ProxmoxUserProvider = () =>\n Provider.effect(ProxmoxUser, Effect.succeed(ProxmoxUser.Provider.of(handlers)));\n",
|
|
91
|
-
"/**\n * `Proxmox.Vm` — a QEMU virtual machine, declared.\n *\n * ⚠️ THE SAME FOUR OPERATIONS AS AN LXC OVER A DIFFERENT PATH, which is the point of the factory:\n * `nodes/{node}/qemu` to create, `nodes/{node}/qemu/{vmid}/config` to read and update. A VM and a\n * container differ in what they carry, not in how they are reconciled.\n *\n * ⛔ `vmid` IS CLUSTER-WIDE AND SHARED WITH CONTAINERS. A VM and an LXC cannot hold the same id, so\n * these two resources compete for one number space. Declaring both with vmid 101 is not a\n * collision Alchemy can see -- they are different resource types -- and PVE refuses the second\n * with \"already exists\". Pick ids per cluster, not per kind.\n *\n * ⚠️ POWER STATE IS REPORTED, NEVER DECLARED. `status` is an attribute so a plan can show it; there\n * is no `running` prop. Starting and stopping a VM from a plan would make a deploy a maintenance\n * window, and the estate has one of those already.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { num } from './values.ts';\n\nexport interface VmProps extends WithTarget {\n node: string;\n /** ⛔ Cluster-wide, and shared with LXC. See the ⛔ above. */\n vmid: number;\n name?: string;\n /** MiB. */\n memory?: number;\n cores?: number;\n sockets?: number;\n /** e.g. `virtio=<mac>,bridge=vmbr0`. */\n net0?: string;\n onboot?: boolean;\n}\n\nexport interface VmAttributes {\n vmid: number;\n node: string;\n name: string;\n memory: number;\n cores: number;\n sockets: number;\n onboot: boolean;\n}\n\nexport interface ProxmoxVm extends Resource<\n 'Proxmox.Vm',\n VmProps,\n VmAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxVm = Resource<ProxmoxVm>('Proxmox.Vm');\n\nconst shape = (props: VmProps) => ({\n cores: String(props.cores ?? 1),\n memory: String(props.memory ?? 512),\n name: props.name ?? `vm${String(props.vmid)}`,\n onboot: props.onboot === true ? '1' : '0',\n sockets: String(props.sockets ?? 1),\n ...(props.net0 === undefined ? {} : { net0: props.net0 }),\n});\n\nconst handlers = pveHandlers<VmProps, VmAttributes>({\n attributes: (live, props) => ({\n cores: num(live['cores'], 1),\n memory: num(live['memory'], 512),\n name: typeof live['name'] === 'string' ? live['name'] : '',\n node: props.node,\n onboot: live['onboot'] === 1 || live['onboot'] === true,\n sockets: num(live['sockets'], 1),\n vmid: props.vmid,\n }),\n collection: (props) => `nodes/${props.node}/qemu`,\n createForm
|
|
107
|
+
"/**\n * `Proxmox.Vm` — a QEMU virtual machine, declared.\n *\n * ⚠️ THE SAME FOUR OPERATIONS AS AN LXC OVER A DIFFERENT PATH, which is the point of the factory:\n * `nodes/{node}/qemu` to create, `nodes/{node}/qemu/{vmid}/config` to read and update. A VM and a\n * container differ in what they carry, not in how they are reconciled.\n *\n * ⛔ `vmid` IS CLUSTER-WIDE AND SHARED WITH CONTAINERS. A VM and an LXC cannot hold the same id, so\n * these two resources compete for one number space. Declaring both with vmid 101 is not a\n * collision Alchemy can see -- they are different resource types -- and PVE refuses the second\n * with \"already exists\". Pick ids per cluster, not per kind.\n *\n * ⚠️ POWER STATE IS REPORTED, NEVER DECLARED. `status` is an attribute so a plan can show it; there\n * is no `running` prop. Starting and stopping a VM from a plan would make a deploy a maintenance\n * window, and the estate has one of those already.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { num } from './values.ts';\n\nexport interface VmProps extends WithTarget {\n node: string;\n /** ⛔ Cluster-wide, and shared with LXC. See the ⛔ above. */\n vmid: number;\n name?: string;\n /** MiB. */\n memory?: number;\n cores?: number;\n sockets?: number;\n /** e.g. `virtio=<mac>,bridge=vmbr0`. */\n net0?: string;\n onboot?: boolean;\n}\n\nexport interface VmAttributes {\n vmid: number;\n node: string;\n name: string;\n memory: number;\n cores: number;\n sockets: number;\n onboot: boolean;\n}\n\nexport interface ProxmoxVm extends Resource<\n 'Proxmox.Vm',\n VmProps,\n VmAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxVm = Resource<ProxmoxVm>('Proxmox.Vm');\n\n/** ★ Exported so the constraint proof can run the REAL create form, not a retyped copy of it. */\nexport const createForm = (props: VmProps) => ({ ...shape(props), vmid: String(props.vmid) });\n\nconst shape = (props: VmProps) => ({\n cores: String(props.cores ?? 1),\n memory: String(props.memory ?? 512),\n name: props.name ?? `vm${String(props.vmid)}`,\n onboot: props.onboot === true ? '1' : '0',\n sockets: String(props.sockets ?? 1),\n ...(props.net0 === undefined ? {} : { net0: props.net0 }),\n});\n\nconst handlers = pveHandlers<VmProps, VmAttributes>({\n attributes: (live, props) => ({\n cores: num(live['cores'], 1),\n memory: num(live['memory'], 512),\n name: typeof live['name'] === 'string' ? live['name'] : '',\n node: props.node,\n onboot: live['onboot'] === 1 || live['onboot'] === true,\n sockets: num(live['sockets'], 1),\n vmid: props.vmid,\n }),\n collection: (props) => `nodes/${props.node}/qemu`,\n createForm,\n /** The vendor rules both forms are checked against at plan time — resource-spec.ts. */\n endpoint: {\n create: 'pve:POST /nodes/{node}/qemu',\n update: 'pve:PUT /nodes/{node}/qemu/{vmid}/config',\n },\n matches: (attributes, props) =>\n attributes.name === (props.name ?? `vm${String(props.vmid)}`) &&\n attributes.memory === (props.memory ?? 512) &&\n attributes.cores === (props.cores ?? 1) &&\n attributes.sockets === (props.sockets ?? 1) &&\n attributes.onboot === (props.onboot === true),\n path: (props) => `nodes/${props.node}/qemu/${String(props.vmid)}/config`,\n updateForm: shape,\n});\n\nexport const ProxmoxVmProvider = () =>\n Provider.effect(ProxmoxVm, Effect.succeed(ProxmoxVm.Provider.of(handlers)));\n",
|
|
92
108
|
"/**\n * `Proxmox.ReplicationJob` — a guest's ZFS storage replication to a second node, declared.\n *\n * ⛔ NOTHING ON C1 CAN USE THIS FAMILY, AND THAT IS SAID HERE RATHER THAN FOUND OUT LATER. PVE\n * storage replication is ZFS-only: `POST /cluster/replication` calls `get_replicatable_volumes`\n * and refuses a guest whose disks are anywhere else with \"No replicatable volumes found\". C1 is\n * Ceph/RBD-backed — shared storage, where replication means nothing because every node already\n * sees the volume — so `GET /cluster/replication` answers `[]` (MEASURED on node-b, 2026-09-13) and\n * on this cluster always will. The file is written for a ZFS cluster; against C1 a declaration\n * is a create that fails with PVE's own sentence, not a plan that quietly does nothing.\n *\n * ★ WHAT IS MEASURED HERE AND WHAT IS REASONED. Measured on node-b (pve-manager 9.2.11), read-only:\n * the published schema for all five methods; `PVE/ReplicationConfig.pm` and\n * `PVE/API2/ReplicationConfig.pm` line by line; `GET /cluster/replication` -> `[]`; and the ACL\n * row `{\"path\":\"/\",\"propagate\":1,\"roleid\":\"PVEAuditor\",\"ugid\":\"hf-read@pve\"}`. NOT measured: any\n * round trip. There is no live job to adopt and none can be created on this cluster, so the\n * argument that a matching declaration plans as `noop` rests on the schema and on PVE's own\n * source — not on a plan anybody ran. Nobody should claim otherwise until a ZFS cluster exists.\n *\n * ⛔ THE ID IS STRUCTURED AND PVE RE-SPELLS IT, so this resource is keyed on the two numbers rather\n * than on the string — a deliberate break from `backup-job.ts`'s \"props are PVE's own key names\".\n * MEASURED: `parse_replication_job_id` matches `^(\\d+)-(\\d+)$`, runs BOTH halves through `int()`\n * and returns \"$guest-$jobnum\", and `parse_section_header` does the same when reading\n * replication.cfg. So a declared `100-007` is read back as `100-7`, and a provider holding the\n * raw string would compare two spellings of one id forever. Two numbers cannot be spelled two\n * ways. PVE also refuses `guest < 100` (\"guest IDs < 100 are reserved\") and the API pattern\n * `[1-9][0-9]{2,8}-\\d{1,9}` caps both halves at nine digits.\n * ⛔ BOTH HALVES ARE REQUIRED AND NEITHER IS DEFAULTED, for `backup-job.ts`'s reason: a provider\n * that picks a job number picks a DIFFERENT job from the one a person meant, and then adopts or\n * creates beside the real one. Declare the numbers of the job you mean.\n *\n * ⛔ PVE'S `target` AND THIS PACKAGE'S `target` ARE DIFFERENT THINGS, AND THE COMPILER SAID SO\n * BEFORE A REVIEWER DID. Every resource here extends `WithTarget`, whose `target` is the CLUSTER\n * a call goes to (`PveTarget`, carrying the API base and the credential mount). PVE's replication\n * `target` is a NODE NAME. They cannot both be called `target` in one props interface — tsc:\n * \"Interface 'ReplicationJobProps' incorrectly extends interface 'WithTarget'\" — so the prop and\n * the attribute are `targetNode`, and PVE's spelling appears exactly once, in `createBody`, where\n * it is the wire key. That is the only safe shape: had the collision been resolvable by silence,\n * `createForm` would have serialised a whole `PveTarget` object into a form field called `target`\n * and PVE would have answered that the node does not exist.\n *\n * ⛔ ONE JOB PER (GUEST, TARGET), ENFORCED BY PVE AND NOT BY THIS FILE. MEASURED: `write_config`\n * dies \"replication job for guest '$vmid' to target '$tid' already exists\" where `$tid` is\n * `local/<target>`, and `parse_config` DELETES the loser on read if two ever reach the file. Two\n * declarations differing only in `jobnum` but naming the same target are not two jobs; the second\n * is a create that fails.\n *\n * ⚠️ RECONCILE NEEDS `VM.Replicate` ON `/vms/<guest>`, WHICH THE PROVISION ROLE DOES NOT HOLD. All\n * three writes check it (MEASURED in API2/ReplicationConfig.pm: create, update and delete each\n * call `$rpcenv->check($authuser, \"/vms/$vmid\", ['VM.Replicate'])`), and the role's 27 privileges\n * include `VM.Allocate`, `VM.Audit`, `VM.Backup`, `VM.Config.*` and `VM.PowerMgmt` but not\n * `VM.Replicate` (`PROVISION_PRIVILEGES`). Do NOT widen it by hand: the baseline declares the\n * role's exact set, so a hand-added privilege is removed on the next deploy. Grant a second role\n * holding `VM.Replicate` on `/vms/<guest>` instead.\n * ★ THE READ LANE NEEDS NOTHING EXTRA, which is why there is no `readRole` below. The ITEM read\n * checks `VM.Audit` on `/vms/<guest>`, `hf-read@pve` holds the built-in `PVEAuditor` at `/`\n * with propagate — both MEASURED — and `PVEAuditor` carries `VM.Audit`. This family is not a\n * fourth one that must borrow the provision lease.\n * ⚠️ A MISSING JOB IS A 500, NOT A 404 — \"no such replication job '100-0'\" — and the factory\n * folds any failed read into \"absent\", so a 403 reads as absent too. A plan insisting on\n * creating a job that plainly exists is a credential problem; the honest error arrives one step\n * later, from the create.\n *\n * ⛔ DELETE DOES NOT DELETE, AND A DESTROY IS NOT FREE. MEASURED: with neither `force` nor `keep` —\n * and the factory's `destroy` sends no form at all — the handler only sets `remove_job = 'full'`\n * on the job and writes the config back. The job stays in replication.cfg until the SOURCE node's\n * `pvesr` timer next runs it, at which point it removes the local replication snapshots, removes\n * the replicated volumes on the target, and finally removes itself. So a destroy costs the\n * standby copy and the incremental base: re-declaring the job afterwards is a FULL send, not a\n * resume. The guest's own disks are untouched, which is exactly why this family does NOT carry\n * `defaultRemovalPolicy: 'retain'` the way `ZfsPool` and `Storage` do — what it destroys is a\n * copy a re-sync rebuilds, expensively, rather than something irreplaceable.\n * ⚠️ AND IF THE SOURCE NODE IS DOWN THE REMOVAL NEVER RUNS. Alchemy drops the state row on a\n * DELETE that returned no error, so a destroy against a dead source leaves a marked job on the\n * cluster that nothing declares any more. `GET /cluster/replication` is where it shows up.\n *\n * ⚠️ THE RUNTIME STATUS IS NOT HERE, AND NOT BECAUSE IT WAS FORGOTTEN. `last_sync`, `last_try`,\n * `fail_count`, `error`, `duration` and `pid` come from the per-node replication STATE file and\n * are merged in by `GET /nodes/{node}/replication` (MEASURED: API2/Replication.pm line 179,\n * `foreach my $k (qw(last_sync last_try fail_count error duration))`). `GET\n * /cluster/replication/{id}` — the only endpoint this resource reads — returns not one of them.\n * Reporting them would mean a second, per-node call on the read path answering a different value\n * every fifteen minutes, and a plan whose output changes when nothing changed is a plan people\n * stop reading. Ask the node for status; ask the cluster for configuration.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { DEFAULT_SCHEDULE, createBody, jobId, updateBody } from './replication-job-form.ts';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool, int, text } from './values.ts';\n\nexport interface ReplicationJobProps extends WithTarget {\n /** ⛔ The guest being replicated. 100 or above — PVE reserves everything below. Identity. */\n guest: number;\n /** ⛔ The job number within that guest, 0 upwards. Identity. Never generated — see the header. */\n jobnum: number;\n /**\n * ⛔ THE NODE THE COPY LANDS ON — PVE's `target`, RENAMED; see the ⛔ in the header — AND IT IS\n * CREATE-ONLY. MEASURED: the plugin declares `target => { fixed => 1, optional => 0 }`, so\n * `updateSchema()` omits it entirely — `PUT` has no `target` parameter at all, and\n * `delete=target` answers \"unable to delete fixed option\". It is therefore NOT compared; see\n * the ⚠️ on `matches`. Changing it in a declaration plans as NOOP. Retire the declaration and\n * add another, exactly as `user.ts` says for `userid`.\n * ⚠️ PVE refuses a target equal to the guest's current node (\"Source and target must not be\n * identical\") and one that is not a cluster member. Neither is checkable from here.\n */\n targetNode: string;\n /**\n * A subset of systemd calendar events. Unset means PVE's own default — `DEFAULT_SCHEDULE`,\n * every fifteen minutes — and that default is what gets SENT and compared, not silence.\n * ⚠️ THIS COMPARES STRINGS, NOT MEANINGS. A calendar event has many equivalent spellings and\n * PVE stores the one it was handed, verbatim, so declare the spelling the cluster echoes.\n */\n schedule?: string;\n /** Rate limit in mbps as a float, 1 or above. Unset sends nothing and compares nothing. */\n rate?: number;\n /** Keep the job but stop replicating. Unset is an ACTIVE job, and that is what is compared. */\n disable?: boolean;\n /** Free text shown in the UI. Round-trips through PVE's `encode_text`/`decode_text`. */\n comment?: string;\n}\n\nexport interface ReplicationJobAttributes {\n /** `100-0`, in PVE's own int-parsed spelling. */\n id: string;\n /**\n * ⚠️ REPORTED AS INTEGERS, NEVER COMPARED, AND THIS IS THE \"id written as a name\" TRAP. MEASURED:\n * `parse_config` sets both from the section header via `int()`, so they are the id re-spelled\n * as numbers and no write accepts either. Compared against anything they would be an update\n * forever; reported, they let a plan say which guest is about to lose a replica.\n */\n guest: number;\n jobnum: number;\n /** Always `local` — the only section type PVE registers. Create-only, reported, not compared. */\n type: string;\n /**\n * ⚠️ REPORTED, NEVER COMPARED. Create-only AND rewritten by PVE — see `matches`. Named for the\n * prop rather than for PVE's wire key `target`, so that one file never means two things by one\n * word; the header says why the wire name could not be kept.\n */\n targetNode: string;\n /** ⚠️ REPORTED, NEVER SENT. PVE's own bookkeeping — see the ⛔ in replication-job-form.ts. */\n source: string;\n schedule: string;\n /** 0 when unset. See `rateOf`. */\n rate: number;\n disable: boolean;\n comment: string;\n /**\n * `''`, `local` or `full`. ⛔ NOT COSMETIC: a non-empty value is a job that is deleting itself,\n * so `matches` refuses it and `updateBody` clears it. See both.\n */\n remove_job: string;\n}\n\nexport interface ProxmoxReplicationJob extends Resource<\n 'Proxmox.ReplicationJob',\n ReplicationJobProps,\n ReplicationJobAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxReplicationJob = Resource<ProxmoxReplicationJob>('Proxmox.ReplicationJob');\n\n/**\n * ⚠️ `rate` IS THE ONLY FLOAT IN THIS PACKAGE, so it gets a coercion of its own rather than a sixth\n * entry in `values.ts` — the rule that module states for itself: a coercion only one PVE object\n * needs belongs in that object's file, next to the field it serves. `int` cannot serve it, since\n * `Number.parseInt('10.5')` is 10 and a rate silently rounded down is both a limit nobody chose\n * and a comparison that never settles. It accepts a string for the reason `int` does: these are\n * SectionConfig sections, and whether `10` arrives as a number is a property of the release.\n * ⚠️ 0 MEANS UNSET, and is safe as the marker because PVE's schema gives `rate` a minimum of 1 —\n * the same trick as `metric-server.ts`'s `UNSET = -1`, for the same reason.\n */\nconst rateOf = (value: unknown): number => {\n if (typeof value === 'number') return value;\n const parsed = Number.parseFloat(text(value));\n return Number.isFinite(parsed) ? parsed : 0;\n};\n\nconst handlers = pveHandlers<ReplicationJobProps, ReplicationJobAttributes>({\n /**\n * ⚠️ EVERY FALLBACK IS PVE'S DOCUMENTED DEFAULT, NOT `false`/`''` PICKED FOR CONVENIENCE, so the\n * answer does not depend on whether a given release echoes back a key it never wrote.\n * SectionConfig stores what it was handed; a job created in the UI without a schedule has no\n * schedule line, and reading that absence as anything but `DEFAULT_SCHEDULE` would report\n * drift on a job nobody touched and then WRITE the reading back.\n * ⛔ NO \"IS IT REALLY THERE\" GUARD, for `backup-job.ts`'s reason: absence is the API declining to\n * answer, which `pveOperations.read` already handles, not a key missing from an answer that did\n * arrive. `digest` is deliberately absent too — it changes when ANY job in the file changes,\n * including one somebody else declared (`ha-resource.ts` hit exactly that).\n */\n attributes: (live, props) => ({\n comment: text(live['comment'], ''),\n disable: bool(live['disable'], false),\n guest: int(live['guest'], props.guest),\n id: text(live['id'], jobId(props)),\n jobnum: int(live['jobnum'], props.jobnum),\n rate: rateOf(live['rate']),\n remove_job: text(live['remove_job'], ''),\n schedule: text(live['schedule'], DEFAULT_SCHEDULE),\n source: text(live['source'], ''),\n targetNode: text(live['target'], props.targetNode),\n type: text(live['type'], 'local'),\n }),\n collection: () => 'cluster/replication',\n createForm: createBody,\n /**\n * ⚠️ `targetNode` AND `source` ARE OUT OF THIS COMPARISON FOR TWO REASONS EACH, AND EITHER ALONE\n * WOULD BE ENOUGH. They are create-only — `target` is a `fixed` option, `source` has no honest\n * declared value — AND PVE REWRITES BOTH BEHIND YOUR BACK. MEASURED in\n * `switch_replication_job_target_nolock`: when a guest migrates, PVE sets\n * `$jobcfg->{target} = $new_target` and `$jobcfg->{source} = $old_target`, so the job follows\n * the guest. Compared, a single migration would make every later plan report an update that the\n * PUT cannot perform, forever. The cost of leaving them out is stated on the `targetNode` prop:\n * a changed target plans as noop, and a migrated job is not dragged back.\n * ⚠️ `guest`, `jobnum`, `type` AND `id` ARE NOT COMPARED EITHER: the first two are the id in\n * integer form, `type` has one legal value, and `id` is the path — a change there is a\n * different object, which reads as absent and is created.\n * ⛔ `remove_job` IS COMPARED, AND IT IS THE ONE FIELD HERE THAT LOOKS LIKE A FOREVER-UPDATE AND\n * IS NOT. A DELETE marks the job rather than removing it, so a re-declared id can name a job\n * that is busy deleting itself, and matching that would be a noop over a vanishing object. It\n * settles because `updateBody` actually clears the marker — see there.\n */\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: {\n create: 'pve:POST /cluster/replication',\n update: 'pve:PUT /cluster/replication/{id}',\n },\n matches: (attributes, props) =>\n attributes.remove_job === '' &&\n attributes.schedule === (props.schedule ?? DEFAULT_SCHEDULE) &&\n attributes.disable === (props.disable === true) &&\n attributes.comment === (props.comment ?? '') &&\n (props.rate === undefined || attributes.rate === props.rate),\n path: (props) => `cluster/replication/${jobId(props)}`,\n updateForm: updateBody,\n});\n\n/**\n * ⛔ Empty `list` like every other resource here: `GET /cluster/replication` hands back every job an\n * operator ever made, and adopting one is how a later `alchemy destroy` takes away a standby copy\n * nobody declared. Adoption is an explicit act — declare the `guest` and `jobnum` you mean.\n * ⚠️ NOTHING BRAKES THIS DESTROY. PVE refuses to delete a pool that still holds guests; it accepts\n * this without a word, because nothing depends on a replication job. Re-read the DELETE ⛔ at the\n * top of this file before approving a plan line that removes one.\n */\nexport const ProxmoxReplicationJobProvider = () =>\n Provider.effect(\n ProxmoxReplicationJob,\n Effect.succeed(ProxmoxReplicationJob.Provider.of(handlers)),\n );\n",
|
|
93
109
|
"/**\n * A replication job's props, as the id and the forms PVE wants.\n *\n * ★ SPLIT OUT OF replication-job.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, on the same seam\n * metric-server-form.ts uses and for the same reason: this file answers \"how does a declaration\n * become a PVE write\", and replication-job.ts answers \"what is a replication job and when has it\n * changed\". Nothing here reads the cluster and nothing here decides a diff.\n *\n * ⚠️ THE `import type` BACK TO replication-job.ts IS A CYCLE ON PAPER ONLY. It is type-only, so it\n * is erased before anything runs, and `ReplicationJobProps` stays the resource's public shape in\n * the file that declares the resource rather than being moved somewhere odd to dodge the arrow.\n */\nimport type { ReplicationJobProps } from './replication-job.ts';\n\n/**\n * PVE's own documented default for `schedule`, applied on BOTH sides of the comparison.\n *\n * ⚠️ IT LIVES HERE BECAUSE BOTH HALVES NEED THE SAME STRING. `shape` sends it and `attributes`\n * falls back to it; two copies of that literal in two files is exactly the drift that makes a\n * plan report an update over a value nobody set.\n * ⚠️ AND IT IS NEVER SPELLED OUT IN A COMMENT, because the string PVE chose ENDS A BLOCK COMMENT:\n * the two characters before the `15` are the comment terminator, and tsc says so loudly. It is\n * referred to by name everywhere else in these two files for that reason, not for style.\n */\nexport const DEFAULT_SCHEDULE = '*/15';\n\n/**\n * `{guest, jobnum}` -> `100-0`, the id in the only spelling PVE ever answers with.\n *\n * ⚠️ NUMBERS IN, SO CANONICAL OUT. PVE runs both halves of a job id through `int()` on parse — see\n * the ⛔ in replication-job.ts — and a JavaScript number has no leading-zero spelling to lose.\n * That is the whole reason this resource is keyed on two numbers instead of one string.\n */\nexport const jobId = (props: ReplicationJobProps) =>\n `${String(props.guest)}-${String(props.jobnum)}`;\n\n/**\n * The mutable half, shared by create and update.\n *\n * ⚠️ `schedule`, `disable` AND `comment` ARE ALWAYS SENT, EVEN AT THEIR DEFAULTS, because they are\n * always compared — and a field compared against a default but not SENT as that default is an\n * update reported on every plan that the PUT never performs (backup-job.ts records the same\n * trade). The empty string is how PVE is told to clear a comment. `rate` is the opposite case:\n * not declared, not sent, not compared, so a rate limit set by hand survives adoption untouched.\n * ⛔ `source` IS NEVER SENT, AND SENDING IT WOULD BE WORSE THAN REDUNDANT. MEASURED in\n * API2/ReplicationConfig.pm: create does `$param->{source} //= $guest_info->{node}` — PVE fills\n * it from wherever the guest actually is — and DIES with \"Source '<x>' does not match current\n * node of guest\" when a declaration disagrees. There is no correct value to hold here.\n */\nconst shape = (props: ReplicationJobProps) => ({\n comment: props.comment ?? '',\n disable: props.disable === true ? '1' : '0',\n schedule: props.schedule ?? DEFAULT_SCHEDULE,\n ...(props.rate === undefined ? {} : { rate: String(props.rate) }),\n});\n\n/**\n * ⚠️ `type` IS A CONSTANT RATHER THAN A PROP: `local` is the only section type PVE registers, and\n * an enum of one value is a prop that can only ever be wrong. `id` and `target` are both required\n * by the create schema and neither belongs in `shape` — `target` because PUT will not take it.\n * ⛔ THIS LINE IS THE ONLY PLACE PVE'S KEY `target` IS SPELLED, and the value beside it comes from\n * `targetNode`. The two words are different objects — a node name here, the whole cluster in\n * `WithTarget.target` — and the ⛔ in replication-job.ts explains why the rename was forced.\n */\nexport const createBody = (props: ReplicationJobProps) => ({\n ...shape(props),\n id: jobId(props),\n target: props.targetNode,\n type: 'local',\n});\n\n/**\n * ⛔ `delete=remove_job` ON EVERY UPDATE IS THE DELIBERATE PART. PVE's PUT merges the form into the\n * section and cannot unset anything, so without this the removal marker a DELETE wrote would\n * survive every reconcile and `matches` would report an update that never lands. MEASURED as\n * legal: the update handler refuses `delete` only for required or `fixed` options, and\n * `remove_job => { optional => 1 }` is neither; deleting a key that is not set is a no-op, so\n * this is safe to send unconditionally.\n * ⛔ AND THE DELETE LIST IS THIS ONE CONSTANT KEY, NEVER A COMPUTED \"everything you did not\n * declare\". backup-job.ts spells out why the computed form is dangerous — it strips settings the\n * resource does not even model off a job adopted from the UI. `remove_job` cannot be declared\n * here at all, so clearing it can only ever undo a removal this stack did not ask for.\n * ⚠️ IT CANCELS A REMOVAL SOMEBODY STARTED, WHICH IS THE POINT AND IS STILL WORTH KNOWING. If the\n * removal pass already ran its local-snapshot half, the revived job re-sends in full.\n */\nexport const updateBody = (props: ReplicationJobProps) => ({\n ...shape(props),\n delete: 'remove_job',\n});\n",
|
|
94
|
-
"/**\n * `Proxmox.SdnApply` — the call that makes a declared network real.\n *\n * ⛔ WITHOUT THIS RESOURCE, EVERY SDN DEPLOY IS A GREEN PLAN THAT CHANGES NO NETWORK. Writes under\n * `cluster/sdn/*` edit files in `/etc/pve/sdn/` and touch nothing on any node. `PUT /cluster/sdn`\n * is the apply, and it is CLUSTER-WIDE — it publishes every staged object at once, not the one\n * that happened to reconcile.\n *\n * ★ MEASURED ON C1 ON 2026-09-13, NOT INFERRED. A probe zone was created, inspected and removed:\n *\n * pvesh create /cluster/sdn/zones --zone zzprobe --type simple\n * GET /cluster/sdn/zones/zzprobe -> {\"digest\":\"c438…\",\"type\":\"simple\",\"zone\":\"zzprobe\"}\n * GET /cluster/sdn/zones/zzprobe?pending=1 -> {\"digest\":null,\"pending\":{},\"state\":\"new\",…}\n * GET /cluster/sdn/zones?running=1 -> [] ⬅ THE APPLIED CONFIG WAS EMPTY\n *\n * So the plain read — the one `pveOperations` performs — answers from the STAGED file and cannot\n * tell staged from applied. `state` appears only under `?pending=1`, and `?running=1` is the\n * applied view. That is the whole basis for this resource.\n *\n * ★ IT IS A RESOURCE RATHER THAN A HOOK SO THAT THE GRAPH ORDERS IT. Pass the zones and vnets it\n * should follow in `after`; Alchemy orders by data flow, so reading their attributes is what\n * puts the apply last. A stack-level hook would fire on every run, including runs where no SDN\n * object changed, and would be invisible in `plan`.\n *\n * ⚠️ ONE APPLY PER STACK. Two of these would each publish the other's staged half, so whichever\n * ran first would apply a zone its own declaration had not finished writing. Declare one.\n *\n * ⚠️ THE COLLECTION READ IS DELIBERATE, AND IT IS WHAT LETS THE READ LEASE SEE ANYTHING AT ALL.\n * `GET /cluster/sdn/zones/{zone}` is checked against SDN.Allocate — read the ⛔ in sdn-zone.ts —\n * but `GET /cluster/sdn/zones` is \"list entries where you have SDN.Audit or SDN.Allocate\", so\n * the collection answers an auditor-shaped credential. This resource only ever reads collections.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { pve } from './client.ts';\nimport type { PveRequirements } from './resource.ts';\nimport type { WithTarget } from './resource.ts';\nimport { sdnPendingCount } from './sdn-apply-read.ts';\n\nexport interface SdnApplyProps extends WithTarget {\n /**\n * The staged objects this apply publishes.\n *\n * ⚠️ IT IS NEVER READ, AND IT IS NOT DECORATION. Alchemy orders resources by DATA FLOW, so the\n * only way to say \"after the zones\" is to consume something the zones produced. Pass their\n * attributes here — `after: [zone.zone, vnet.vnet]` — and the apply lands last. Leave it out\n * and the apply may run BEFORE the zone it was meant to publish, which plans green and\n * changes nothing.\n */\n after?: readonly unknown[];\n}\n\nexport interface SdnApplyAttributes {\n /**\n * How many SDN objects are still staged.\n *\n * ⚠️ ZERO IS THE ONLY SETTLED VALUE. Anything above it means the running config and the staged\n * config disagree — which is drift whether this stack caused it or somebody staged a zone in\n * the UI and walked away.\n */\n pending: number;\n}\n\nexport interface ProxmoxSdnApply extends Resource<\n 'Proxmox.SdnApply',\n SdnApplyProps,\n SdnApplyAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxSdnApply = Resource<ProxmoxSdnApply>('Proxmox.SdnApply');\n\nconst read = (props: SdnApplyProps) =>\n sdnPendingCount(props.target).pipe(Effect.map((pending) => ({ pending })));\n\nexport const ProxmoxSdnApplyProvider = () =>\n Provider.effect(\n ProxmoxSdnApply,\n Effect.succeed(\n ProxmoxSdnApply.Provider.of({\n /** ⛔ Empty for the same reason as every other resource here: adoption must be explicit. */\n list: () => Effect.succeed([]),\n\n read: Effect.fn(function* ({ olds }) {\n return yield* read(olds);\n }),\n\n /**\n * ⚠️ THE DIFF IS ABOUT THE CLUSTER, NOT ABOUT THE PROPS. This resource has no settable\n * field — `after` exists only to create an ordering edge — so comparing props to props\n * would report `noop` forever and the apply would run exactly once, ever. What decides\n * it is whether anything is staged right now.\n */\n diff: Effect.fn(function* ({ news }: { news: Input<SdnApplyProps> }) {\n if (!isResolved(news)) return undefined;\n const { pending } = yield* read(news);\n return pending === 0 ? ({ action: 'noop' } as const) : ({ action: 'update' } as const);\n }),\n\n reconcile: Effect.fn(function* ({ news }) {\n /**\n * ⛔ NOTHING STAGED MEANS NOTHING TO PUBLISH, AND THIS BRANCH IS A SAFETY PROPERTY RATHER\n * THAN AN OPTIMISATION — the same one network-apply.ts has carried all along, and the\n * absence of which is why this resource could not be declared at all.\n *\n * 🔴 WHAT IT PREVENTS. `reconcile` runs on CREATE, the first time this resource appears\n * in a stack, before `diff` has ever been consulted. `PUT /cluster/sdn` regenerates\n * `/etc/network/interfaces.d/sdn` on EVERY node at once — and on C1 that file carries\n * the OSPF fabric holding Ceph's `cluster_network` on tb0/tb1. Without this branch,\n * adding one line to a stack file reloads networking on all three nodes simultaneously,\n * which is precisely what the serialised NetworkApply chain exists to prevent.\n *\n * ⚠️ IT DEPENDS ON sdn-apply-read.ts BEING HONEST ABOUT THE WHOLE SURFACE. A count that\n * only looks at zones and vnets answers zero on a cluster with a staged fabric, and\n * this branch would then wave through the exact publish it is here to stop.\n */\n const staged = yield* read(news);\n if (staged.pending === 0) return staged;\n\n yield* pve(news.target, 'provision', 'PUT', 'cluster/sdn');\n /**\n * ⛔ READ BACK, FOR THE SAME REASON THE FACTORY DOES. PVE answers 200 on writes that did\n * nothing, and here \"nothing\" is indistinguishable from success by status code alone.\n * If objects are still staged after an apply, the network does NOT match the\n * declaration and saying otherwise is the exact lie this resource was added to stop.\n */\n const after = yield* read(news);\n if (after.pending !== 0) {\n return yield* Effect.die(\n new Error(\n `PUT /cluster/sdn returned no error but ${String(after.pending)} SDN object(s) ` +\n 'are still staged. The running config does not match the declared one -- check ' +\n '`pvesh get /cluster/sdn/zones --pending 1` for the objects PVE refused.',\n ),\n );\n }\n return after;\n }),\n\n /**\n * ⛔ DELETING THIS RESOURCE APPLIES NOTHING AND UNDOES NOTHING, AND THAT IS CORRECT. There\n * is no un-apply in PVE: the running config is already published on every node, and the\n * way to remove a network is to delete the zone or vnet and apply AGAIN. Making this\n * destroy call `PUT /cluster/sdn` would publish whatever happened to be staged at the\n * moment somebody removed a line from a stack file, which is the worst possible time.\n */\n delete: () => Effect.void,\n }),\n ),\n );\n",
|
|
110
|
+
"/**\n * `Proxmox.SdnApply` — the call that makes a declared network real.\n *\n * ⛔ WITHOUT THIS RESOURCE, EVERY SDN DEPLOY IS A GREEN PLAN THAT CHANGES NO NETWORK. Writes under\n * `cluster/sdn/*` edit files in `/etc/pve/sdn/` and touch nothing on any node. `PUT /cluster/sdn`\n * is the apply, and it is CLUSTER-WIDE — it publishes every staged object at once, not the one\n * that happened to reconcile.\n *\n * ★ MEASURED ON C1 ON 2026-09-13, NOT INFERRED. A probe zone was created, inspected and removed:\n *\n * pvesh create /cluster/sdn/zones --zone zzprobe --type simple\n * GET /cluster/sdn/zones/zzprobe -> {\"digest\":\"c438…\",\"type\":\"simple\",\"zone\":\"zzprobe\"}\n * GET /cluster/sdn/zones/zzprobe?pending=1 -> {\"digest\":null,\"pending\":{},\"state\":\"new\",…}\n * GET /cluster/sdn/zones?running=1 -> [] ⬅ THE APPLIED CONFIG WAS EMPTY\n *\n * So the plain read — the one `pveOperations` performs — answers from the STAGED file and cannot\n * tell staged from applied. `state` appears only under `?pending=1`, and `?running=1` is the\n * applied view. That is the whole basis for this resource.\n *\n * ★ IT IS A RESOURCE RATHER THAN A HOOK SO THAT THE GRAPH ORDERS IT. Pass the zones and vnets it\n * should follow in `after`; Alchemy orders by data flow, so reading their attributes is what\n * puts the apply last. A stack-level hook would fire on every run, including runs where no SDN\n * object changed, and would be invisible in `plan`.\n *\n * ⚠️ ONE APPLY PER STACK. Two of these would each publish the other's staged half, so whichever\n * ran first would apply a zone its own declaration had not finished writing. Declare one.\n *\n * ⚠️ THE COLLECTION READ IS DELIBERATE, AND IT IS WHAT LETS THE READ LEASE SEE ANYTHING AT ALL.\n * `GET /cluster/sdn/zones/{zone}` is checked against SDN.Allocate — read the ⛔ in sdn-zone.ts —\n * but `GET /cluster/sdn/zones` is \"list entries where you have SDN.Audit or SDN.Allocate\", so\n * the collection answers an auditor-shaped credential. This resource only ever reads collections.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { guardSdnApply } from './apply-endpoints.ts';\nimport { pve } from './client.ts';\nimport type { PveRequirements } from './resource.ts';\nimport type { WithTarget } from './resource.ts';\nimport { sdnPendingCount } from './sdn-apply-read.ts';\n\nexport interface SdnApplyProps extends WithTarget {\n /**\n * The staged objects this apply publishes.\n *\n * ⚠️ IT IS NEVER READ, AND IT IS NOT DECORATION. Alchemy orders resources by DATA FLOW, so the\n * only way to say \"after the zones\" is to consume something the zones produced. Pass their\n * attributes here — `after: [zone.zone, vnet.vnet]` — and the apply lands last. Leave it out\n * and the apply may run BEFORE the zone it was meant to publish, which plans green and\n * changes nothing.\n */\n after?: readonly unknown[];\n}\n\nexport interface SdnApplyAttributes {\n /**\n * How many SDN objects are still staged.\n *\n * ⚠️ ZERO IS THE ONLY SETTLED VALUE. Anything above it means the running config and the staged\n * config disagree — which is drift whether this stack caused it or somebody staged a zone in\n * the UI and walked away.\n */\n pending: number;\n}\n\nexport interface ProxmoxSdnApply extends Resource<\n 'Proxmox.SdnApply',\n SdnApplyProps,\n SdnApplyAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxSdnApply = Resource<ProxmoxSdnApply>('Proxmox.SdnApply');\n\nconst read = (props: SdnApplyProps) =>\n sdnPendingCount(props.target).pipe(Effect.map((pending) => ({ pending })));\n\nexport const ProxmoxSdnApplyProvider = () =>\n Provider.effect(\n ProxmoxSdnApply,\n Effect.succeed(\n ProxmoxSdnApply.Provider.of({\n /** ⛔ Empty for the same reason as every other resource here: adoption must be explicit. */\n list: () => Effect.succeed([]),\n\n read: Effect.fn(function* ({ olds }) {\n return yield* read(olds);\n }),\n\n /**\n * ⚠️ THE DIFF IS ABOUT THE CLUSTER, NOT ABOUT THE PROPS. This resource has no settable\n * field — `after` exists only to create an ordering edge — so comparing props to props\n * would report `noop` forever and the apply would run exactly once, ever. What decides\n * it is whether anything is staged right now.\n */\n diff: Effect.fn(function* ({ news }: { news: Input<SdnApplyProps> }) {\n if (!isResolved(news)) return undefined;\n const { pending } = yield* read(news);\n return pending === 0 ? ({ action: 'noop' } as const) : ({ action: 'update' } as const);\n }),\n\n reconcile: Effect.fn(function* ({ news }) {\n /**\n * ⛔ NOTHING STAGED MEANS NOTHING TO PUBLISH, AND THIS BRANCH IS A SAFETY PROPERTY RATHER\n * THAN AN OPTIMISATION — the same one network-apply.ts has carried all along, and the\n * absence of which is why this resource could not be declared at all.\n *\n * 🔴 WHAT IT PREVENTS. `reconcile` runs on CREATE, the first time this resource appears\n * in a stack, before `diff` has ever been consulted. `PUT /cluster/sdn` regenerates\n * `/etc/network/interfaces.d/sdn` on EVERY node at once — and on C1 that file carries\n * the OSPF fabric holding Ceph's `cluster_network` on tb0/tb1. Without this branch,\n * adding one line to a stack file reloads networking on all three nodes simultaneously,\n * which is precisely what the serialised NetworkApply chain exists to prevent.\n *\n * ⚠️ IT DEPENDS ON sdn-apply-read.ts BEING HONEST ABOUT THE WHOLE SURFACE. A count that\n * only looks at zones and vnets answers zero on a cluster with a staged fabric, and\n * this branch would then wave through the exact publish it is here to stop.\n */\n const staged = yield* read(news);\n if (staged.pending === 0) return staged;\n\n // ⛔ BEFORE THE PUBLISH. The apply takes no body, so this is the lookup rather than a\n // value check: a vendor that moved this endpoint fails here, not on three nodes.\n yield* guardSdnApply;\n yield* pve(news.target, 'provision', 'PUT', 'cluster/sdn');\n /**\n * ⛔ READ BACK, FOR THE SAME REASON THE FACTORY DOES. PVE answers 200 on writes that did\n * nothing, and here \"nothing\" is indistinguishable from success by status code alone.\n * If objects are still staged after an apply, the network does NOT match the\n * declaration and saying otherwise is the exact lie this resource was added to stop.\n */\n const after = yield* read(news);\n if (after.pending !== 0) {\n return yield* Effect.die(\n new Error(\n `PUT /cluster/sdn returned no error but ${String(after.pending)} SDN object(s) ` +\n 'are still staged. The running config does not match the declared one -- check ' +\n '`pvesh get /cluster/sdn/zones --pending 1` for the objects PVE refused.',\n ),\n );\n }\n return after;\n }),\n\n /**\n * ⛔ DELETING THIS RESOURCE APPLIES NOTHING AND UNDOES NOTHING, AND THAT IS CORRECT. There\n * is no un-apply in PVE: the running config is already published on every node, and the\n * way to remove a network is to delete the zone or vnet and apply AGAIN. Making this\n * destroy call `PUT /cluster/sdn` would publish whatever happened to be staged at the\n * moment somebody removed a line from a stack file, which is the worst possible time.\n */\n delete: () => Effect.void,\n }),\n ),\n );\n",
|
|
95
111
|
"/**\n * What is staged on the SDN, across the WHOLE surface the apply publishes — not just the two\n * collections this package declares resources for.\n *\n * 🔴 THE GAP THIS FILE CLOSES, AND IT WAS FOUND BY ALMOST DECLARING THE APPLY. `pendingCount` in\n * sdn-apply.ts counted `cluster/sdn/zones` and `cluster/sdn/vnets`. C1 has zero of each — and a\n * staged OSPF FABRIC that nothing counted. MEASURED 2026-09-13 on node-b:\n *\n * /etc/pve/sdn/fabrics.cfg ospf_fabric: c1, area 1, ip_prefix 203.0.113.0/24\n * ospf_node: c1_n2/node-c/node-d, interfaces tb0 + tb1,\n * ip 203.0.113.102 / .103 / .104\n * /etc/network/interfaces.d/sdn the file PVE GENERATES from it, carrying those /32s\n *\n * Those addresses are Ceph's `cluster_network`. So `diff` would have answered `noop` for ever\n * while `PUT /cluster/sdn` republished the fabric and reloaded networking on all three nodes at\n * once — the simultaneity the NetworkApply chain exists to prevent, arriving through the door\n * nobody was watching.\n *\n * ⛔ THE SURFACE IS NOT UNIFORM, AND ASSUMING IT WAS IS WHAT MADE THE GAP INVISIBLE. Measured, one\n * request per collection, per query:\n *\n * zones, vnets, controllers, prefix-lists, route-maps accept ?pending=1 and ?running=1,\n * and mark a staged row with `state`\n * fabrics accepts both, and its rows carry NO\n * `state` marker at all\n * ipams, dns REJECT both with HTTP 400,\n * \"property is not defined in schema\"\n *\n * ⛔ SO A `state`-ONLY TEST IS WRONG FOR FABRICS, AND `orElseSucceed(() => 0)` IS WORSE THAN WRONG\n * FOR ipams AND dns. The old code folded an unreadable collection to zero — right for a cluster\n * with no SDN at all, and indistinguishable from \"this collection rejects the question\". Adding\n * ipams and dns to the old list would have counted them as 0 for ever and read as coverage.\n */\nimport * as Effect from 'effect/Effect';\nimport { PveError, pve } from './client.ts';\nimport type { PveTarget } from './credentials.ts';\n\n/** A staged row. `state` is present only under `?pending=1`, and only when it differs from running. */\ntype PendingRow = { state?: string };\n\n/**\n * Collections that answer `?pending=1` AND mark a staged row with `state`.\n *\n * ⚠️ THIS LIST IS WIDER THAN THE RESOURCES THIS PACKAGE SHIPS, ON PURPOSE, AND THAT REVERSES THE\n * OLD COMMENT. It used to say counting a collection nothing declares would turn somebody else's\n * hand-staged object into a permanent `update`. True — and the alternative is publishing that\n * object without ever showing it in a plan. An apply that is cluster-wide must be honest about\n * the whole cluster: a controller staged by hand in the UI is a thing this resource WILL push,\n * so it has to be a thing this resource ADMITS to. A permanent `update` on somebody's abandoned\n * half-edit is a loud, safe failure; publishing it silently is not.\n */\nconst MARKED = [\n 'cluster/sdn/zones',\n 'cluster/sdn/vnets',\n 'cluster/sdn/controllers',\n 'cluster/sdn/prefix-lists',\n 'cluster/sdn/route-maps',\n] as const;\n\n/**\n * Collections with NO pending view at all.\n *\n * ⚠️ NAMED RATHER THAN SILENTLY SKIPPED. `ipams` and `dns` answer HTTP 400 to both `?pending=1` and\n * `?running=1`, so there is no way to ask them what is staged. They are a REAL blind spot: an\n * ipam edited by hand is published by the apply and cannot be seen from here. C1 has one ipam\n * (`pve`, the built-in) and zero dns entries, so the blind spot is currently empty — which is a\n * fact about today, not a property of the design.\n */\nexport const UNDIFFABLE = ['cluster/sdn/ipams', 'cluster/sdn/dns'] as const;\n\n/**\n * Whether a failed read means \"this SDN subsystem does not exist here\" — the ONE failure that may\n * read as nothing staged.\n *\n * 🔴 IT USED TO BE EVERY FAILURE. `orElseSucceed` caught 401, 403, 5xx and a dropped connection\n * along with the 501 it was written for, so an expired read lease or a missing grant answered\n * \"nothing staged\": `diff` said noop, and the read-back after a real apply said settled while\n * objects were still staged. The fabric views were worse because they are COMPARED — one view\n * failing while the other succeeded read as a DIFFERENCE, which is `PUT /cluster/sdn` and a\n * network reload carrying Ceph's cluster network on all three nodes, over a timeout. Found in\n * review on 2026-09-14 and confirmed by reading.\n * ⛔ SO ANYTHING ELSE FAILS THE READ. A plan that stops on an unreadable SDN is loud and safe; a\n * plan that guesses is neither.\n */\nexport const subsystemAbsent = (error: unknown): boolean =>\n error instanceof PveError && error.status === 501;\n\n/** Staged rows in the collections that mark them. */\nconst markedPending = (target: PveTarget) =>\n Effect.all(\n MARKED.map((collection) =>\n pve<PendingRow[]>(target, 'read', 'GET', `${collection}?pending=1`).pipe(\n Effect.map((rows) => (rows ?? []).filter((row) => row.state !== undefined).length),\n /**\n * ⚠️ AN ABSENT COLLECTION COUNTS AS ZERO, AND THAT IS STILL RIGHT HERE — but ONLY an absent\n * one. Some versions answer 501 for an SDN subsystem that has never been configured, and\n * failing the whole plan over it would make this resource undeclarable on a cluster with\n * no SDN. The collections that REJECT the question are handled by not asking them — see\n * UNDIFFABLE — and every other failure is a failure; see `subsystemAbsent`.\n */\n Effect.catchIf(subsystemAbsent, () => Effect.succeed(0)),\n ),\n ),\n { concurrency: 'unbounded' },\n ).pipe(Effect.map((counts) => counts.reduce((total, count) => total + count, 0)));\n\n/**\n * Staged fabric changes from the two canonical views, where `undefined` is a view whose subsystem\n * answered 501.\n *\n * ⛔ ONE VIEW ABSENT AND ONE PRESENT IS NOT A DIFFERENCE. It is one endpoint answering two\n * questions inconsistently, and a difference is exactly what triggers the publish — so it is\n * refused rather than counted.\n */\nexport const fabricStaged = (\n staged: string | undefined,\n running: string | undefined,\n): number | 'inconsistent' => {\n if (staged === undefined && running === undefined) return 0;\n if (staged === undefined || running === undefined) return 'inconsistent';\n return staged === running ? 0 : 1;\n};\n\n/**\n * Whether the fabric config differs between staged and applied.\n *\n * ★ COMPARING THE TWO VIEWS IS THE TEST, BECAUSE THERE IS NO `state` TO READ. `?pending=1` is the\n * staged config and `?running=1` is what the nodes are actually running; equal means nothing to\n * publish. `cluster/sdn/fabrics/all` returns both fabrics and nodes in ONE call, which is why it\n * is asked rather than the two subdirectories.\n *\n * ⚠️ `digest` IS STRIPPED BEFORE COMPARING. The pending view carries one and the running view does\n * not — MEASURED — so comparing raw bodies reports a difference on a cluster where nothing has\n * changed, which is exactly the forever-diff this package has fixed three times elsewhere.\n * ⚠️ AND THE ORDER OF THE NODE LIST IS NOT STABLE between the two reads, so both sides are\n * canonically sorted. Order is not meaning in a set of fabric nodes.\n */\nconst fabricPending = (target: PveTarget) =>\n Effect.all(\n ['pending', 'running'].map((view) =>\n pve<unknown>(target, 'read', 'GET', `cluster/sdn/fabrics/all?${view}=1`).pipe(\n Effect.map(canonical),\n Effect.catchIf(subsystemAbsent, () => Effect.succeed(undefined)),\n ),\n ),\n { concurrency: 'unbounded' },\n ).pipe(\n Effect.flatMap(([staged, running]) => {\n const verdict = fabricStaged(staged, running);\n return verdict === 'inconsistent'\n ? Effect.die(\n new Error(\n 'cluster/sdn/fabrics/all answered 501 for one of ?pending=1 and ?running=1 and not ' +\n 'the other. Refusing to read that as a staged fabric change.',\n ),\n )\n : Effect.succeed(verdict);\n }),\n );\n\n/** Stable JSON with `digest` removed and every array sorted, so only real differences show. */\nexport const canonical = (value: unknown): string => JSON.stringify(sortDeep(value));\n\nconst sortDeep = (value: unknown): unknown => {\n if (Array.isArray(value)) {\n return value\n .map(sortDeep)\n .sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));\n }\n if (value !== null && typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([key]) => key !== 'digest')\n .sort(([left], [right]) => left.localeCompare(right));\n return Object.fromEntries(entries.map(([key, inner]) => [key, sortDeep(inner)]));\n }\n return value;\n};\n\n/**\n * How many staged changes the apply would publish, across everything it is capable of publishing.\n *\n * ⛔ ZERO HERE IS THE ONLY THING THAT MAKES DECLARING THE APPLY SAFE. `reconcile` runs on CREATE —\n * Alchemy always calls it the first time a resource appears — and `PUT /cluster/sdn` regenerates\n * `/etc/network/interfaces.d/sdn` on every node. On C1 that file carries Ceph's cluster network.\n */\nexport const sdnPendingCount = (target: PveTarget) =>\n Effect.all([markedPending(target), fabricPending(target)], { concurrency: 'unbounded' }).pipe(\n Effect.map(([marked, fabric]) => marked + fabric),\n );\n",
|
|
96
|
-
"/**\n * `Proxmox.SdnSubnet` — the addresses on a declared bridge: the CIDR, its gateway, its DHCP pool.\n *\n * ★ THE THIRD LINK OF ONE CHAIN, AND THE ORDER IS DATA FLOW RATHER THAN CONVENTION. A subnet\n * belongs to a vnet and a vnet belongs to a zone. MEASURED and recorded in sdn-vnet.ts: creating\n * a vnet is permission-checked at `/sdn/zones/-`, so a missing zone surfaces as a PERMISSION\n * error rather than as \"no such zone\". One level deeper the same shape holds — PVE's\n * `check_vnet_access` looks the vnet's ZONE up and checks `/sdn/zones/<zone>/<vnet>` — so a\n * subnet declared against a vnet that does not exist yet dies inside that lookup, with the vnet\n * named and the zone not. Pass the zone and vnet resources' OWN attributes into `zone` and\n * `vnet`; a literal buys no ordering, and here it also buys a wrong path.\n *\n * ⛔ THE ID PVE FILES A SUBNET UNDER IS NOT THE ONE YOU WRITE, AND IT IS THIS FAMILY'S SHARPEST\n * EDGE. `POST` takes `subnet=10.0.0.0/24` while GET, PUT and DELETE address `lab-10.0.0.0-24`.\n * One PVE parameter name, `subnet`, carrying two different values — so they are named APART here,\n * `cidr` for the declared one and `subnetId` for the derived one, for the reason values.ts gives\n * about `flag`: a name that means two things is a bug waiting for whoever reads one meaning. The\n * derivation, and its measurement, live in `subnetId` in sdn-subnet-form.ts.\n *\n * ⛔ WHICH IS WHY `zone` IS A PROP THOUGH PVE NEVER ACCEPTS IT ON A SUBNET WRITE. The path cannot\n * be built without it and it is knowable only from the VNET. Declare the wrong zone and the id is\n * wrong: the POST still succeeds, because PVE computes the real id itself from the vnet, and the\n * read-back then looks for a subnet that is not there. The factory's guard in resource.ts DIES on\n * that first deploy — \"the write returned no error but the object is still absent\" — which is\n * loud and immediate, and much better than the alternative it replaces: a plan that says create,\n * forever, against a subnet PVE insists is already defined.\n *\n * ⛔ SDN WRITES ARE STAGED, NOT APPLIED — the same trap as sdn-zone.ts and sdn-vnet.ts, and it\n * walks straight through the factory's read-back, which proves the staging file and nothing more.\n * Nothing here applies. `Proxmox.SdnApply` (sdn-apply.ts) publishes the whole staged config with\n * `PUT /cluster/sdn`; declare one and pass this resource's `subnetId` in its `after`.\n *\n * ⛔ AND `Proxmox.SdnApply` CANNOT YET SEE A STAGED SUBNET, SO ITS PLAN WILL LIE ABOUT THIS FAMILY.\n * MEASURED: its `COLLECTIONS` list is `cluster/sdn/zones` and `cluster/sdn/vnets`, while\n * `PVE::Network::SDN::pending_config` keys the pending view on a SEPARATE `subnets` section of\n * the running config. A run that stages only a subnet therefore leaves both counted collections\n * at zero, the apply diffs as `noop`, and the subnet never reaches a node. It is NOT fixable by\n * adding a string to that list: the subnet index is PER VNET\n * (`cluster/sdn/vnets/{vnet}/subnets?pending=1`), so SdnApply needs the vnet names before it can\n * count them. Until that lands, a subnet-only change needs `pvesh set /cluster/sdn` by hand.\n *\n * ★ THE READ LANE IS ENOUGH HERE, AND THAT IS A REAL DIFFERENCE FROM BOTH SIBLINGS. The zone and\n * vnet ITEM reads are gated on SDN.Allocate, which is why each sets `readRole: 'provision'`. This\n * one is not. MEASURED from the cluster's own schema on 2026-09-13:\n *\n * GET /cluster/sdn/vnets/{vnet}/subnets/{subnet}\n * -> \"Require 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/zones/<zone>/<vnet>'\"\n *\n * and `GET /access/acl` the same day shows `hf-read@pve` holding the built-in PVEAuditor — whose\n * seven privileges include SDN.Audit — at `/` with propagate=1. So the default 3600s auditor\n * lease reads this family, and a `readRole: 'provision'` here would be privilege nobody needed.\n * Writes are a different answer: POST, PUT and DELETE all require SDN.Allocate on\n * `/sdn/zones/<zone>/<vnet>`, which the provision role gained on 2026-09-13 (`PROVISION_PRIVILEGES`).\n *\n * ⚠️ NO LIVE SUBNET EXISTS ON C1 TO ROUND-TRIP AGAINST — there are no SDN zones and no vnets, so\n * there can be no subnets. Every claim above is read off PVE 9.2.11's own schema and Perl sources\n * on node-b, and the noop argument below rests on those rather than on a plan this file has watched.\n *\n * ⚠️ TWO REFUSALS TO EXPECT, both from `SubnetPlugin::on_update_hook`: PVE will not put a subnet on\n * a vnet with `vlanaware` set (\"you can't add a subnet on a vlanaware vnet\"), and it rejects a\n * gateway outside the CIDR unless the mask is /32. Both are create-time 400s, not drift.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport {\n type DhcpRange,\n createForm,\n declaredRanges,\n dhcpRanges,\n subnetId,\n updateForm,\n} from './sdn-subnet-form.ts';\nimport { bool, int, text } from './values.ts';\n\nexport interface SdnSubnetProps extends WithTarget {\n /**\n * The vnet whose bridge carries these addresses. Identity AND a written field.\n *\n * ⛔ MOVING A SUBNET BETWEEN VNETS IS NOT SOMETHING THIS RESOURCE CAN DO, and the symptom is\n * worth knowing before you meet it. The id is built from the zone and the CIDR, so it does not\n * change; but the read goes to `.../vnets/<declared>/subnets/<id>`, and PVE answers a subnet\n * filed under another vnet with `raise_param_exc({ vnet => \"wrong vnet\" })`. `read` folds that\n * into \"absent\", reconcile POSTs, and PVE refuses with \"sdn subnet object ID ... already\n * defined\". Loud and accurate, but it does not converge: change the vnet by removing the\n * declaration and writing a new one.\n */\n vnet: string;\n /**\n * The zone that vnet lives in. ⚠️ PATH INPUT ONLY — PVE accepts no `zone` on a subnet write, and\n * nothing here sends one. It is a prop because the path cannot be spelled without it.\n */\n zone: string;\n /** `10.0.0.0/24`. Identity: PVE builds the id from it, so a new CIDR is a new subnet. */\n cidr: string;\n /** Handed to guests as their default route, and registered in the zone's IPAM as `<vnet>-gw`. */\n gateway?: string;\n /**\n * Masquerade this subnet. ⚠️ INERT UNTIL THE CLUSTER FIREWALL IS ON — PVE's own description is\n * \"enable masquerade for this subnet if pve-firewall\", and C1's cluster firewall was empty and\n * disabled when this was written. It still round-trips, so declaring it does not diff.\n */\n snat?: boolean;\n /** `adm` -> `<hostname>.adm.example.internal` for records the zone's DNS plugin registers. */\n dnszoneprefix?: string;\n /** ⚠️ WIRE NAME `dhcp-dns-server`; see the ⚠️ on `isolatePorts` in sdn-vnet.ts about mappings. */\n dhcpDnsServer?: string;\n /**\n * ⛔ ONE POOL, NOT A LIST, AND THE LIMIT IS THE CLIENT'S RATHER THAN PVE'S. `dhcp-range` is a PVE\n * `type => 'array'` parameter, and an array reaches a form-encoded body only as a REPEATED KEY\n * — which `client.ts` cannot express, because `pve()` takes `Record<string, string>` and one\n * key holds one value. A single occurrence works today because `PVE::RESTHandler`'s\n * `$normalize_legacy_param_formats` wraps a scalar into `[$value]` for an array parameter, so\n * the type is declared as one range and a second one is unspellable rather than silently lost.\n * ⛔ THAT COERCION IS MARKED FOR REMOVAL. Its own comment in RESTHandler.pm reads \"mark the\n * behaviour deprecated with 9.x, and remove it with 10.x\", and this cluster runs 9.2.11 — so on\n * PVE 10 every write carrying a range becomes a hard 400. The fix is not here: it is a form\n * type in client.ts that admits repeated keys.\n * ⚠️ A LIVE SUBNET CARRYING TWO POOLS IS THEREFORE NARROWED TO ONE by the first deploy that\n * declares one. `matches` compares the WHOLE live list, so the narrowing is visible in the plan\n * as an update rather than happening quietly, and it converges.\n */\n dhcpRange?: DhcpRange;\n}\n\n/**\n * ⛔ NO `digest` HERE, for the reason sdn-zone.ts gives: PVE returns the digest of the WHOLE\n * subnets.cfg, so declaring a SECOND subnet would rewrite this one's stored attributes and\n * comparing it would report an update on a subnet nobody touched.\n */\nexport interface SdnSubnetAttributes {\n /** `<zone>-<network>-<mask>` — the path segment, and the name to use in `pvesh`. */\n subnetId: string;\n vnet: string;\n zone: string;\n /** `10.0.0.0/24`, as PVE reassembles it from the id. */\n cidr: string;\n /** Prefix length, so a guest's `ip=10.0.0.5/24` can be built from a reference. */\n mask: number;\n gateway: string;\n snat: boolean;\n dnszoneprefix: string;\n dhcpDnsServer: string;\n /** Normalised and sorted — never compare a raw PVE value to it. See `dhcpRanges`. */\n dhcpRange: string;\n /** ⚠️ THE REMINDER IN THE STATE ITSELF: staged config. No value here says a node has the subnet. */\n readonly staged: true;\n}\n\nexport interface ProxmoxSdnSubnet extends Resource<\n 'Proxmox.SdnSubnet',\n SdnSubnetProps,\n SdnSubnetAttributes,\n never,\n PveRequirements\n> {}\n\n/**\n * ⚠️ NO `defaultRemovalPolicy: 'retain'`, UNLIKE THE SEVEN FAMILIES THAT CARRY IT, AND THE ★ IN\n * resource.ts IS THE TEST IT FAILS: a subnet's whole content is a CIDR, a gateway and a pool,\n * which a line of TypeScript rebuilds exactly. The part that is NOT rebuildable is the guest\n * addresses the zone's IPAM handed out, and PVE guards that itself — MEASURED in the built-in\n * `pve` IPAM plugin, which answers a delete with \"cannot delete subnet '<cidr>', not empty\"\n * whenever anything beyond the gateway still holds an address. That refusal is surfaced as-is.\n */\nexport const ProxmoxSdnSubnet = Resource<ProxmoxSdnSubnet>('Proxmox.SdnSubnet');\n\nconst handlers = pveHandlers<SdnSubnetProps, SdnSubnetAttributes>({\n attributes: (live, props) => {\n /**\n * ⚠️ UNREACHABLE ON A HEALTHY CLUSTER, AND KEPT BECAUSE \"UNREACHABLE\" IS A FACT ABOUT TODAY'S\n * PVE. The read handler already refuses a subnet filed under another vnet, so this row should\n * never arrive — see the ⛔ on `vnet` above for what that refusal costs. If a future PVE\n * answers instead of raising, reporting the foreign subnet as absent makes reconcile POST and\n * PVE say \"already defined\": still loud, rather than a silent noop over somebody else's\n * addresses.\n */\n const liveVnet = text(live['vnet']);\n if (liveVnet !== '' && liveVnet !== props.vnet) return undefined;\n return {\n cidr: text(live['cidr']),\n dhcpDnsServer: text(live['dhcp-dns-server']),\n dhcpRange: dhcpRanges(live['dhcp-range']),\n dnszoneprefix: text(live['dnszoneprefix']),\n gateway: text(live['gateway']),\n mask: int(live['mask'], 0),\n snat: bool(live['snat']),\n staged: true,\n subnetId: subnetId(props),\n vnet: props.vnet,\n zone: text(live['zone']),\n };\n },\n collection: (props) => `cluster/sdn/vnets/${props.vnet}/subnets`,\n createForm,\n /**\n * ⛔ FIVE REPORTED FIELDS ARE DELIBERATELY ABSENT FROM THIS COMPARISON, and each is a\n * forever-diff if it goes back in.\n * `subnetId`, `cidr`, `zone` and `mask` are all DERIVED BY PVE FROM THE ID — measured in\n * `Subnets::sdn_subnets_config`, which splits the id and synthesises cidr/zone/network/mask on\n * every read. None of the four is a POST or PUT parameter, so a difference could never be\n * written; and since the id IS the path this read used, a difference cannot arise either — the\n * GET would have failed and the factory would report \"absent\" instead.\n * `vnet` is the FILTER that produced this row rather than a reading of it: PVE refuses to\n * answer with a subnet on another vnet, so comparing it can only ever be true.\n * `staged` is a fact about the cluster, not about the declaration: diffing it would report an\n * update on every plan until somebody applied, then another the moment anyone staged anything.\n * `digest` is not even reported — see the ⛔ above the attributes.\n */\n matches: (attributes, props) =>\n attributes.gateway === (props.gateway ?? '') &&\n attributes.snat === (props.snat === true) &&\n attributes.dnszoneprefix === (props.dnszoneprefix ?? '') &&\n attributes.dhcpDnsServer === (props.dhcpDnsServer ?? '') &&\n attributes.dhcpRange === declaredRanges(props.dhcpRange),\n /**\n * ⚠️ NO `?pending=1` AND NO `?running=1`, HOWEVER TEMPTING — the same refusal sdn-vnet.ts makes.\n * The factory uses this one path for GET, PUT and DELETE alike, every one of those schemas\n * declares `additionalProperties => 0`, and PVE answers an unexpected parameter on a write with\n * \"parameter verification failed\". Staged-ness is documented at the top of this file instead.\n */\n path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,\n updateForm,\n});\n\n/**\n * ⛔ `list` IS EMPTY LIKE EVERY OTHER RESOURCE HERE. `GET /cluster/sdn/vnets/{vnet}/subnets` answers\n * with every subnet on that bridge, guest addresses and all; adopting one would invite Alchemy to\n * narrow or delete a network nobody declared. Adoption stays an explicit act.\n *\n * ⛔ AND A DELETE IS STAGED TOO, WHICH IS THE HALF PEOPLE FORGET. The section leaves subnets.cfg and\n * Alchemy drops the resource, but the gateway stays on the bridge and DHCP keeps answering until\n * something applies `PUT /cluster/sdn`: a destroy nobody applies reads as complete in the plan and\n * has not happened on the cluster.\n */\nexport const ProxmoxSdnSubnetProvider = () =>\n Provider.effect(ProxmoxSdnSubnet, Effect.succeed(ProxmoxSdnSubnet.Provider.of(handlers)));\n",
|
|
112
|
+
"/**\n * `Proxmox.SdnSubnet` — the addresses on a declared bridge: the CIDR, its gateway, its DHCP pool.\n *\n * ★ THE THIRD LINK OF ONE CHAIN, AND THE ORDER IS DATA FLOW RATHER THAN CONVENTION. A subnet\n * belongs to a vnet and a vnet belongs to a zone. MEASURED and recorded in sdn-vnet.ts: creating\n * a vnet is permission-checked at `/sdn/zones/-`, so a missing zone surfaces as a PERMISSION\n * error rather than as \"no such zone\". One level deeper the same shape holds — PVE's\n * `check_vnet_access` looks the vnet's ZONE up and checks `/sdn/zones/<zone>/<vnet>` — so a\n * subnet declared against a vnet that does not exist yet dies inside that lookup, with the vnet\n * named and the zone not. Pass the zone and vnet resources' OWN attributes into `zone` and\n * `vnet`; a literal buys no ordering, and here it also buys a wrong path.\n *\n * ⛔ THE ID PVE FILES A SUBNET UNDER IS NOT THE ONE YOU WRITE, AND IT IS THIS FAMILY'S SHARPEST\n * EDGE. `POST` takes `subnet=10.0.0.0/24` while GET, PUT and DELETE address `lab-10.0.0.0-24`.\n * One PVE parameter name, `subnet`, carrying two different values — so they are named APART here,\n * `cidr` for the declared one and `subnetId` for the derived one, for the reason values.ts gives\n * about `flag`: a name that means two things is a bug waiting for whoever reads one meaning. The\n * derivation, and its measurement, live in `subnetId` in sdn-subnet-form.ts.\n *\n * ⛔ WHICH IS WHY `zone` IS A PROP THOUGH PVE NEVER ACCEPTS IT ON A SUBNET WRITE. The path cannot\n * be built without it and it is knowable only from the VNET. Declare the wrong zone and the id is\n * wrong: the POST still succeeds, because PVE computes the real id itself from the vnet, and the\n * read-back then looks for a subnet that is not there. The factory's guard in resource.ts DIES on\n * that first deploy — \"the write returned no error but the object is still absent\" — which is\n * loud and immediate, and much better than the alternative it replaces: a plan that says create,\n * forever, against a subnet PVE insists is already defined.\n *\n * ⛔ SDN WRITES ARE STAGED, NOT APPLIED — the same trap as sdn-zone.ts and sdn-vnet.ts, and it\n * walks straight through the factory's read-back, which proves the staging file and nothing more.\n * Nothing here applies. `Proxmox.SdnApply` (sdn-apply.ts) publishes the whole staged config with\n * `PUT /cluster/sdn`; declare one and pass this resource's `subnetId` in its `after`.\n *\n * ⛔ AND `Proxmox.SdnApply` CANNOT YET SEE A STAGED SUBNET, SO ITS PLAN WILL LIE ABOUT THIS FAMILY.\n * MEASURED: its `COLLECTIONS` list is `cluster/sdn/zones` and `cluster/sdn/vnets`, while\n * `PVE::Network::SDN::pending_config` keys the pending view on a SEPARATE `subnets` section of\n * the running config. A run that stages only a subnet therefore leaves both counted collections\n * at zero, the apply diffs as `noop`, and the subnet never reaches a node. It is NOT fixable by\n * adding a string to that list: the subnet index is PER VNET\n * (`cluster/sdn/vnets/{vnet}/subnets?pending=1`), so SdnApply needs the vnet names before it can\n * count them. Until that lands, a subnet-only change needs `pvesh set /cluster/sdn` by hand.\n *\n * ★ THE READ LANE IS ENOUGH HERE, AND THAT IS A REAL DIFFERENCE FROM BOTH SIBLINGS. The zone and\n * vnet ITEM reads are gated on SDN.Allocate, which is why each sets `readRole: 'provision'`. This\n * one is not. MEASURED from the cluster's own schema on 2026-09-13:\n *\n * GET /cluster/sdn/vnets/{vnet}/subnets/{subnet}\n * -> \"Require 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/zones/<zone>/<vnet>'\"\n *\n * and `GET /access/acl` the same day shows `hf-read@pve` holding the built-in PVEAuditor — whose\n * seven privileges include SDN.Audit — at `/` with propagate=1. So the default 3600s auditor\n * lease reads this family, and a `readRole: 'provision'` here would be privilege nobody needed.\n * Writes are a different answer: POST, PUT and DELETE all require SDN.Allocate on\n * `/sdn/zones/<zone>/<vnet>`, which the provision role gained on 2026-09-13 (`PROVISION_PRIVILEGES`).\n *\n * ⚠️ NO LIVE SUBNET EXISTS ON C1 TO ROUND-TRIP AGAINST — there are no SDN zones and no vnets, so\n * there can be no subnets. Every claim above is read off PVE 9.2.11's own schema and Perl sources\n * on node-b, and the noop argument below rests on those rather than on a plan this file has watched.\n *\n * ⚠️ TWO REFUSALS TO EXPECT, both from `SubnetPlugin::on_update_hook`: PVE will not put a subnet on\n * a vnet with `vlanaware` set (\"you can't add a subnet on a vlanaware vnet\"), and it rejects a\n * gateway outside the CIDR unless the mask is /32. Both are create-time 400s, not drift.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport {\n type DhcpRange,\n createForm,\n declaredRanges,\n dhcpRanges,\n subnetId,\n updateForm,\n} from './sdn-subnet-form.ts';\nimport { bool, int, text } from './values.ts';\n\nexport interface SdnSubnetProps extends WithTarget {\n /**\n * The vnet whose bridge carries these addresses. Identity AND a written field.\n *\n * ⛔ MOVING A SUBNET BETWEEN VNETS IS NOT SOMETHING THIS RESOURCE CAN DO, and the symptom is\n * worth knowing before you meet it. The id is built from the zone and the CIDR, so it does not\n * change; but the read goes to `.../vnets/<declared>/subnets/<id>`, and PVE answers a subnet\n * filed under another vnet with `raise_param_exc({ vnet => \"wrong vnet\" })`. `read` folds that\n * into \"absent\", reconcile POSTs, and PVE refuses with \"sdn subnet object ID ... already\n * defined\". Loud and accurate, but it does not converge: change the vnet by removing the\n * declaration and writing a new one.\n */\n vnet: string;\n /**\n * The zone that vnet lives in. ⚠️ PATH INPUT ONLY — PVE accepts no `zone` on a subnet write, and\n * nothing here sends one. It is a prop because the path cannot be spelled without it.\n */\n zone: string;\n /** `10.0.0.0/24`. Identity: PVE builds the id from it, so a new CIDR is a new subnet. */\n cidr: string;\n /** Handed to guests as their default route, and registered in the zone's IPAM as `<vnet>-gw`. */\n gateway?: string;\n /**\n * Masquerade this subnet. ⚠️ INERT UNTIL THE CLUSTER FIREWALL IS ON — PVE's own description is\n * \"enable masquerade for this subnet if pve-firewall\", and C1's cluster firewall was empty and\n * disabled when this was written. It still round-trips, so declaring it does not diff.\n */\n snat?: boolean;\n /** `adm` -> `<hostname>.adm.example.internal` for records the zone's DNS plugin registers. */\n dnszoneprefix?: string;\n /** ⚠️ WIRE NAME `dhcp-dns-server`; see the ⚠️ on `isolatePorts` in sdn-vnet.ts about mappings. */\n dhcpDnsServer?: string;\n /**\n * ⛔ ONE POOL, NOT A LIST, AND THE LIMIT IS THE CLIENT'S RATHER THAN PVE'S. `dhcp-range` is a PVE\n * `type => 'array'` parameter, and an array reaches a form-encoded body only as a REPEATED KEY\n * — which `client.ts` cannot express, because `pve()` takes `Record<string, string>` and one\n * key holds one value. A single occurrence works today because `PVE::RESTHandler`'s\n * `$normalize_legacy_param_formats` wraps a scalar into `[$value]` for an array parameter, so\n * the type is declared as one range and a second one is unspellable rather than silently lost.\n * ⛔ THAT COERCION IS MARKED FOR REMOVAL. Its own comment in RESTHandler.pm reads \"mark the\n * behaviour deprecated with 9.x, and remove it with 10.x\", and this cluster runs 9.2.11 — so on\n * PVE 10 every write carrying a range becomes a hard 400. The fix is not here: it is a form\n * type in client.ts that admits repeated keys.\n * ⚠️ A LIVE SUBNET CARRYING TWO POOLS IS THEREFORE NARROWED TO ONE by the first deploy that\n * declares one. `matches` compares the WHOLE live list, so the narrowing is visible in the plan\n * as an update rather than happening quietly, and it converges.\n */\n dhcpRange?: DhcpRange;\n}\n\n/**\n * ⛔ NO `digest` HERE, for the reason sdn-zone.ts gives: PVE returns the digest of the WHOLE\n * subnets.cfg, so declaring a SECOND subnet would rewrite this one's stored attributes and\n * comparing it would report an update on a subnet nobody touched.\n */\nexport interface SdnSubnetAttributes {\n /** `<zone>-<network>-<mask>` — the path segment, and the name to use in `pvesh`. */\n subnetId: string;\n vnet: string;\n zone: string;\n /** `10.0.0.0/24`, as PVE reassembles it from the id. */\n cidr: string;\n /** Prefix length, so a guest's `ip=10.0.0.5/24` can be built from a reference. */\n mask: number;\n gateway: string;\n snat: boolean;\n dnszoneprefix: string;\n dhcpDnsServer: string;\n /** Normalised and sorted — never compare a raw PVE value to it. See `dhcpRanges`. */\n dhcpRange: string;\n /** ⚠️ THE REMINDER IN THE STATE ITSELF: staged config. No value here says a node has the subnet. */\n readonly staged: true;\n}\n\nexport interface ProxmoxSdnSubnet extends Resource<\n 'Proxmox.SdnSubnet',\n SdnSubnetProps,\n SdnSubnetAttributes,\n never,\n PveRequirements\n> {}\n\n/**\n * ⚠️ NO `defaultRemovalPolicy: 'retain'`, UNLIKE THE SEVEN FAMILIES THAT CARRY IT, AND THE ★ IN\n * resource.ts IS THE TEST IT FAILS: a subnet's whole content is a CIDR, a gateway and a pool,\n * which a line of TypeScript rebuilds exactly. The part that is NOT rebuildable is the guest\n * addresses the zone's IPAM handed out, and PVE guards that itself — MEASURED in the built-in\n * `pve` IPAM plugin, which answers a delete with \"cannot delete subnet '<cidr>', not empty\"\n * whenever anything beyond the gateway still holds an address. That refusal is surfaced as-is.\n */\nexport const ProxmoxSdnSubnet = Resource<ProxmoxSdnSubnet>('Proxmox.SdnSubnet');\n\nconst handlers = pveHandlers<SdnSubnetProps, SdnSubnetAttributes>({\n attributes: (live, props) => {\n /**\n * ⚠️ UNREACHABLE ON A HEALTHY CLUSTER, AND KEPT BECAUSE \"UNREACHABLE\" IS A FACT ABOUT TODAY'S\n * PVE. The read handler already refuses a subnet filed under another vnet, so this row should\n * never arrive — see the ⛔ on `vnet` above for what that refusal costs. If a future PVE\n * answers instead of raising, reporting the foreign subnet as absent makes reconcile POST and\n * PVE say \"already defined\": still loud, rather than a silent noop over somebody else's\n * addresses.\n */\n const liveVnet = text(live['vnet']);\n if (liveVnet !== '' && liveVnet !== props.vnet) return undefined;\n return {\n cidr: text(live['cidr']),\n dhcpDnsServer: text(live['dhcp-dns-server']),\n dhcpRange: dhcpRanges(live['dhcp-range']),\n dnszoneprefix: text(live['dnszoneprefix']),\n gateway: text(live['gateway']),\n mask: int(live['mask'], 0),\n snat: bool(live['snat']),\n staged: true,\n subnetId: subnetId(props),\n vnet: props.vnet,\n zone: text(live['zone']),\n };\n },\n collection: (props) => `cluster/sdn/vnets/${props.vnet}/subnets`,\n createForm,\n /** The vendor rules both forms are checked against at plan time — resource-spec.ts. */\n endpoint: {\n create: 'pve:POST /cluster/sdn/vnets/{vnet}/subnets',\n update: 'pve:PUT /cluster/sdn/vnets/{vnet}/subnets/{subnet}',\n },\n /**\n * ⛔ FIVE REPORTED FIELDS ARE DELIBERATELY ABSENT FROM THIS COMPARISON, and each is a\n * forever-diff if it goes back in.\n * `subnetId`, `cidr`, `zone` and `mask` are all DERIVED BY PVE FROM THE ID — measured in\n * `Subnets::sdn_subnets_config`, which splits the id and synthesises cidr/zone/network/mask on\n * every read. None of the four is a POST or PUT parameter, so a difference could never be\n * written; and since the id IS the path this read used, a difference cannot arise either — the\n * GET would have failed and the factory would report \"absent\" instead.\n * `vnet` is the FILTER that produced this row rather than a reading of it: PVE refuses to\n * answer with a subnet on another vnet, so comparing it can only ever be true.\n * `staged` is a fact about the cluster, not about the declaration: diffing it would report an\n * update on every plan until somebody applied, then another the moment anyone staged anything.\n * `digest` is not even reported — see the ⛔ above the attributes.\n */\n matches: (attributes, props) =>\n attributes.gateway === (props.gateway ?? '') &&\n attributes.snat === (props.snat === true) &&\n attributes.dnszoneprefix === (props.dnszoneprefix ?? '') &&\n attributes.dhcpDnsServer === (props.dhcpDnsServer ?? '') &&\n attributes.dhcpRange === declaredRanges(props.dhcpRange),\n /**\n * ⚠️ NO `?pending=1` AND NO `?running=1`, HOWEVER TEMPTING — the same refusal sdn-vnet.ts makes.\n * The factory uses this one path for GET, PUT and DELETE alike, every one of those schemas\n * declares `additionalProperties => 0`, and PVE answers an unexpected parameter on a write with\n * \"parameter verification failed\". Staged-ness is documented at the top of this file instead.\n */\n path: (props) => `cluster/sdn/vnets/${props.vnet}/subnets/${subnetId(props)}`,\n updateForm,\n});\n\n/**\n * ⛔ `list` IS EMPTY LIKE EVERY OTHER RESOURCE HERE. `GET /cluster/sdn/vnets/{vnet}/subnets` answers\n * with every subnet on that bridge, guest addresses and all; adopting one would invite Alchemy to\n * narrow or delete a network nobody declared. Adoption stays an explicit act.\n *\n * ⛔ AND A DELETE IS STAGED TOO, WHICH IS THE HALF PEOPLE FORGET. The section leaves subnets.cfg and\n * Alchemy drops the resource, but the gateway stays on the bridge and DHCP keeps answering until\n * something applies `PUT /cluster/sdn`: a destroy nobody applies reads as complete in the plan and\n * has not happened on the cluster.\n */\nexport const ProxmoxSdnSubnetProvider = () =>\n Provider.effect(ProxmoxSdnSubnet, Effect.succeed(ProxmoxSdnSubnet.Provider.of(handlers)));\n",
|
|
97
113
|
"/**\n * How a subnet crosses the wire in both directions, and what PVE actually calls its path.\n *\n * ★ SPLIT OUT OF sdn-subnet.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, on the seam\n * node-network-form.ts cuts rather than at a convenient line number: this file owns the\n * COERCIONS in BOTH directions plus the derived id, because a read that normalises differently\n * from the write that produced it is exactly how a forever-diff is born. sdn-subnet.ts answers\n * \"what is a subnet, and when has it changed\". Nothing here calls the cluster.\n *\n * ⚠️ THE `import type` BACK TO sdn-subnet.ts IS A CYCLE ON PAPER ONLY — type-only, so it is erased\n * before anything runs and `SdnSubnetProps` stays in the file that declares the resource.\n */\nimport type { SdnSubnetProps } from './sdn-subnet.ts';\nimport { propertyString, withClears } from './values.ts';\n\n/** One DHCP pool. PVE's property-string keys are `start-address`/`end-address`; see `rangeString`. */\nexport type DhcpRange = { start: string; end: string };\n\n/**\n * The id PVE files a subnet under — and therefore the path segment GET, PUT and DELETE address it\n * by, which is NOT the value POST is handed.\n *\n * ⛔ MEASURED from `/usr/share/perl5/PVE/API2/Network/SDN/Subnets.pm` on pve-manager 9.2.11, in\n * the create handler: `my $id = $cidr =~ s/\\//-/r; $id = \"$zoneid-$id\";`. So `subnet=10.0.0.0/24`\n * goes out and `lab-10.0.0.0-24` is what exists afterwards. A CIDR cannot be a path segment\n * anyway — it carries a slash — so there is no spelling of this endpoint that takes the CIDR.\n *\n * ⚠️ `.replace`, NOT `.replaceAll`, AND THE PARITY IS DELIBERATE. Perl's `s/\\//-/r` has no `/g`, so\n * it rewrites the FIRST slash only. A CIDR has exactly one, so the two agree on every legal\n * input; they would stop agreeing on an illegal one, and agreeing with PVE is the point.\n */\nexport const subnetId = (props: Pick<SdnSubnetProps, 'cidr' | 'zone'>) =>\n `${props.zone}-${props.cidr.replace('/', '-')}`;\n\n/** A range on its way OUT. PVE stores the string verbatim; nothing rewrites it. */\nconst rangeString = (range: DhcpRange) => `start-address=${range.start},end-address=${range.end}`;\n\n/**\n * DHCP ranges, from either shape, as one comparable string.\n *\n * ⛔ THIS FAMILY'S WRITE SHAPE AND READ SHAPE ARE DIFFERENT, WHICH IS THE CLASSIC FOREVER-DIFF.\n * MEASURED from the API module: `$scfg->{'dhcp-range'} = get_dhcp_ranges($scfg)` runs\n * `parse_property_string` over every stored element, so a range WRITTEN as the string\n * `start-address=10.0.0.10,end-address=10.0.0.20` is READ BACK as the object\n * `{\"start-address\":\"10.0.0.10\",\"end-address\":\"10.0.0.20\"}`. `propertyString` in values.ts\n * flattens both shapes to one sorted form and exists for exactly this — see its own ⚠️.\n *\n * ⚠️ AND IT IS A LIST, SO ORDER MUST NOT BE MEANING. PVE validates only that ranges do not overlap\n * and stores them in the order given; two declarations naming the same pools the other way round\n * are the same subnet. Sorting is what stops that being an update, exactly as `csv` does for a\n * zone's `nodes`.\n *\n * ⚠️ `;` IS THE JOINER BECAUSE `,` AND `=` ARE BOTH INSIDE THE VALUES. It never appears in an IPv4\n * or IPv6 address or in a property-string key, so the joined form cannot be ambiguous.\n */\nexport const dhcpRanges = (value: unknown): string =>\n (Array.isArray(value) ? value : [])\n .map((entry: unknown) => propertyString(entry))\n .filter((entry) => entry !== '')\n .sort()\n .join(';');\n\n/** The declared side of that same comparison, through the same funnel so it cannot drift. */\nexport const declaredRanges = (range: DhcpRange | undefined) =>\n range === undefined ? '' : dhcpRanges([rangeString(range)]);\n\n/**\n * ⚠️ AN EMPTY STRING IS HOW A CALLER SPELLS \"UNSET\", AND IT MUST NOT REACH PVE. `gateway` and\n * `dhcp-dns-server` are `format => 'ip'` and `dnszoneprefix` is `format => 'dns-name'`, so `''`\n * is a parameter-verification 400 rather than a clear. Clearing is a separate verb — `delete=`.\n */\nconst unset = (value: string | undefined) =>\n value === undefined || value === '' ? undefined : value;\n\n/**\n * Every optional this resource MANAGES: the value to send, or `undefined` meaning \"clear it\".\n *\n * ⛔ ONE TABLE FEEDS BOTH HALVES OF AN UPDATE, the way metric-server-form.ts does it, and for the\n * same measured reason: A PUT THAT OMITS A FIELD DOES NOT CLEAR IT. PVE merges the form into the\n * existing section (`$data->{$_} = $opts->{$_} for keys $opts->%*`), so dropping `gateway` from a\n * declaration leaves the old gateway in place and `matches` asks for the same update forever.\n * Deriving the form and the `delete=` list from one map makes \"managed but not clearable\"\n * impossible to write here.\n *\n * ⛔ AND THE TWO HALVES MUST NOT OVERLAP. MEASURED in `PVE::SectionConfig::delete_from_config`\n * (SectionConfig.pm:1853): naming a key in BOTH the form and `delete=` dies with \"cannot set and\n * delete property '<k>' at the same time!\". Because both halves are read off this one map, an\n * entry is in exactly one of them by construction.\n *\n * ⛔ NOTHING SET OUT OF BAND MAY BE LISTED HERE. A field in this map is CLEARED the moment it is\n * undeclared, so a live subnet whose DHCP pool an operator widened in the UI is narrowed back on\n * the next deploy. That is full ownership, the same bargain sdn-vnet.ts strikes over `alias`, and\n * it is a bargain rather than an accident only while this list stays deliberate.\n *\n * ⚠️ `snat` IS ABSENT ON PURPOSE AND LIVES IN `body` INSTEAD. It is a boolean, and `0` and \"never\n * declared\" are the same subnet to PVE and to `matches`, so sending an explicit `0` converges\n * without a `delete=` — the same choice sdn-vnet.ts makes for `vlanaware` and `isolate-ports`.\n */\nexport const optional = (props: SdnSubnetProps): Record<string, string | undefined> => ({\n 'dhcp-dns-server': unset(props.dhcpDnsServer),\n 'dhcp-range': props.dhcpRange === undefined ? undefined : rangeString(props.dhcpRange),\n dnszoneprefix: unset(props.dnszoneprefix),\n gateway: unset(props.gateway),\n});\n\n/**\n * The fields sent on EVERY write, create and update alike.\n *\n * ⚠️ `vnet` IS SENT THOUGH THE URL ALREADY CARRIES IT. PVE's handler reads `$param->{vnet}` to\n * resolve the zone for its permission check, and it is a declared parameter of both the create\n * and the update schema, so sending the same value the path carries is accepted and removes this\n * file's dependence on the router injecting a path placeholder into the parameter hash.\n */\nexport const body = (props: SdnSubnetProps): Record<string, string> => ({\n snat: props.snat === true ? '1' : '0',\n vnet: props.vnet,\n ...Object.fromEntries(\n Object.entries(optional(props)).filter(\n (entry): entry is [string, string] => entry[1] !== undefined,\n ),\n ),\n});\n\n/**\n * ⚠️ `type` IS HARDCODED, NOT A PROP. PVE's create schema declares it as an enum with exactly one\n * member, `subnet`, so a prop for it could only ever hold one value — and the POST refuses to\n * proceed without it, which is the parameter-verification error sdn-vnet.ts records.\n */\nexport const createForm = (props: SdnSubnetProps): Record<string, string> => ({\n ...body(props),\n subnet: props.cidr,\n type: 'subnet',\n});\n\n/**\n * ★ CLEARING AN OPTION THAT WAS NEVER SET IS A SILENT NO-OP, AND THIS IS MEASURED RATHER THAN\n * HOPED FOR. sdn-vnet.ts flags the same `delete=` habit as REASONED-not-measured and warns that a\n * first update on a bare object would carry `delete=alias,tag`. Read off the code both endpoints\n * share — `PVE::SectionConfig::delete_from_config`, SectionConfig.pm:1853-1866 — the loop dies\n * only for an option that is unknown to the plugin, not optional, fixed, or also being set; for\n * an option simply absent from the section it runs `delete $config->{$k}` on a missing key and\n * moves on. All four keys above are `optional => 1` in `SubnetPlugin::options`, so a bare\n * subnet's first update carries `delete=dhcp-dns-server,dhcp-range,dnszoneprefix,gateway` and\n * PVE accepts it. `vnet` is `optional => 0` there and is never in this list.\n *\n * ⚠️ TWO OF THOSE FOUR NAMES ARE HYPHENATED, WHICH NO OTHER FAMILY HERE HAS PUT IN A `delete=`.\n * The parameter's format is `pve-configid-list`, and a name that failed it would 400 the whole\n * update rather than skip one field. MEASURED: `$CONFIGID_RE` is `qr/[a-z][a-z0-9_-]+/i`\n * (JSONSchema.pm:30), so a hyphen after the first character is legal and all four pass.\n */\nexport const updateForm = (props: SdnSubnetProps): Record<string, string> => {\n const clear = Object.entries(optional(props))\n .filter(([, value]) => value === undefined)\n .map(([key]) => key);\n return withClears(body(props), clear);\n};\n",
|
|
98
114
|
"/**\n * `Proxmox.SdnVnet` — the bridge a guest actually attaches to.\n *\n * ★ THIS IS THE OBJECT THAT CLOSES THE GAP BETWEEN A DECLARED CONTAINER AND A DECLARED NETWORK.\n * `Proxmox.Lxc` takes `net0: name=eth0,bridge=vmbr0,ip=dhcp`, in which the bridge is a string\n * nothing checks. A vnet's id IS the bridge PVE builds on every node of its zone, so declaring\n * the vnet and passing its `bridge` attribute into `net0` turns that string into a reference\n * Alchemy can order and a plan can show.\n *\n * ⚠️ MEASURED: CREATING A VNET CHECKS PERMISSION AT `/sdn/zones/-`, NOT AT THE VNET. The ACL path\n * is the ZONE's, so a vnet whose zone does not exist yet fails as a PERMISSION error rather than\n * as \"no such zone\" — and you go off widening a role that was never the problem. `zone` is\n * therefore a hard dependency edge, not a convention: pass the zone resource's own attribute so\n * Alchemy creates the zone first. A zone name typed in as a literal buys no ordering at all.\n *\n * ⛔ THE READ-BACK AFTER A WRITE PROVES THE STAGING FILE AND NOTHING MORE — the same trap as\n * `sdn-zone`, and it walks straight through the factory's guard. PVE's SDN endpoints write\n * `/etc/pve/sdn/vnets.cfg`; nothing reaches a node's `/etc/network/interfaces` until\n * `PUT /cluster/sdn` applies the staged config. `pveOperations.reconcile` reads back and refuses\n * when an object is absent, and a vnet that exists only on paper SATISFIES that read: reconcile\n * reports success, the next plan says noop, and no bridge exists anywhere on the cluster.\n * This resource does NOT issue the apply, deliberately. One vnet's reconcile cannot decide to\n * commit every other pending SDN change in the cluster, including ones a human staged in the UI\n * and has not finished. Apply stays an operator's act — `pvesh set /cluster/sdn`.\n *\n * ★ SUBNETS ARE A SIBLING RESOURCE, NOT A FIELD HERE. The gateway and the DHCP ranges guests take\n * addresses from hang off `cluster/sdn/vnets/{vnet}/subnets`, which MEASURED refuses a POST that\n * omits `type` or `subnet` (\"parameter verification failed\"). Same factory, one path deeper, and\n * it depends on this resource the way this one depends on the zone.\n *\n * ⛔ read/diff NEED SDN.Allocate TOO, NOT SDN.Audit — corrected 2026-09-13 against the cluster's\n * schema. `GET /cluster/sdn/vnets/{vnet}` is checked against SDN.Allocate exactly as the zone\n * item endpoint is, while only the COLLECTION read accepts SDN.Audit. An auditor-shaped `read`\n * lease is therefore refused, and `pveOperations.read` turns that refusal into \"absent\" rather\n * than an error — see the ⛔ in sdn-zone.ts and docs/privileges.md.\n *\n * ⚠️ PRIVILEGES, AND THE ESTATE'S CREDENTIAL HELD NONE OF THEM UNTIL 2026-09-13. reconcile and\n * delete mint `provision` and need SDN.Allocate on `/sdn/zones`, or on the one zone if scoped. The provision role granted\n * only SDN.Use then, which is what a GUEST needs in order to attach to a vnet and NOT what\n * creating one needs, so a container on a vnet worked while the vnet itself 403'd. It was widened\n * the way Pool.Allocate was, and `PROVISION_PRIVILEGES` (provision-baseline.ts) now carries\n * SDN.Allocate and SDN.Audit.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool, withClears } from './values.ts';\n\nexport interface SdnVnetProps extends WithTarget {\n /**\n * ⛔ THE PRIMARY KEY AND THE BRIDGE NAME AT ONCE. PVE caps it at eight alphanumeric characters\n * because it becomes a real interface on every node in the zone. There is no rename: a new id\n * is a new bridge, and every guest whose `net0` still names the old one stays on the old one.\n */\n vnet: string;\n /**\n * The SDN zone that owns it. Mutable — a vnet can be moved between zones — but see the ⚠️ at the\n * top of this file: the zone has to exist first, and passing a zone resource's attribute instead\n * of a literal is the only thing that makes Alchemy order them.\n */\n zone: string;\n /** Free text shown in the UI. Mutable; clear it by dropping the prop, never by passing `''`. */\n alias?: string;\n /** VLAN id or VXLAN VNI, depending on the zone's type. Mutable. Absent means untagged. */\n tag?: number;\n /** Let guests on this bridge carry their own VLAN tags. Mutable. */\n vlanaware?: boolean;\n /**\n * Stop guests on this bridge from reaching each other directly. Mutable.\n * ⚠️ The wire name is `isolate-ports`, with a hyphen — see the ⚠️ on `shape` for why that costs\n * attention in two places rather than one.\n */\n isolatePorts?: boolean;\n}\n\nexport interface SdnVnetAttributes {\n vnet: string;\n /**\n * The same string as `vnet`, duplicated on purpose: it is the name a guest's `net0` must carry.\n * Referencing it from a `Proxmox.Lxc`'s props is what makes Alchemy build the network before the\n * container. A hardcoded `vmbr1` in `net0` is a string nothing checks and nothing orders.\n */\n bridge: string;\n zone: string;\n alias: string;\n /**\n * Absent when the vnet is untagged — see `tagOf` for why this is optional and not `0`.\n * ⚠️ `| undefined` IS LOAD-BEARING under exactOptionalPropertyTypes: the attributes\n * builder always sets this key, writing `undefined` for an untagged vnet, so the type\n * must permit a present-but-undefined value as well as an absent one. Without it the\n * read path does not typecheck, and 0 is a real VLAN id so it cannot be the sentinel.\n */\n tag?: number | undefined;\n vlanaware: boolean;\n isolatePorts: boolean;\n}\n\nexport interface ProxmoxSdnVnet extends Resource<\n 'Proxmox.SdnVnet',\n SdnVnetProps,\n SdnVnetAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxSdnVnet = Resource<ProxmoxSdnVnet>('Proxmox.SdnVnet');\n\n/**\n * ⚠️ AN ABSENT TAG IS `undefined`, NOT 0, which is why `SdnVnetAttributes.tag` is optional rather\n * than falling back the way `memory` does in lxc.ts. VLAN id 0 and VXLAN VNI 0 are real values,\n * so a 0 fallback would report an untagged vnet as tagged 0 and then diff forever against a\n * declaration that asks for no tag at all.\n */\nconst tagOf = (value: unknown) => (typeof value === 'number' ? value : undefined);\n\n/**\n * The fields PVE accepts on create and on update alike.\n *\n * ⚠️ THE WIRE NAME IS `isolate-ports`, WITH A HYPHEN; the prop is `isolatePorts` so no call site\n * has to quote it. Both halves of that mapping have to be right: the form key here AND the read\n * in `attributes`. `live['isolatePorts']` would be `undefined` forever, which reads as \"the\n * cluster does not have it set\" and makes every plan ask for the same update, for good.\n */\nconst shape = (props: SdnVnetProps): Record<string, string> => {\n const alias = props.alias ?? '';\n return {\n // ⚠️ THE TWO BOOLEANS ARE ALWAYS SENT, `0` INCLUDED, AND THAT IS NOT AN OVERSIGHT. `0` and\n // \"never declared\" mean the same thing to PVE and to `matches` below, so an explicit `0`\n // makes the two converge — which is why neither ever appears in the `delete` list.\n 'isolate-ports': props.isolatePorts === true ? '1' : '0',\n vlanaware: props.vlanaware === true ? '1' : '0',\n zone: props.zone,\n // ⚠️ AN EMPTY ALIAS IS OMITTED, NOT SENT AS `''`. `''` is how this file spells \"unset\", and\n // unset goes through the `delete` parameter below; sending it as a value would ask PVE to\n // store an empty description, which is a different request from having none.\n ...(alias === '' ? {} : { alias }),\n ...(props.tag === undefined ? {} : { tag: String(props.tag) }),\n };\n};\n\n/**\n * ⛔ OMITTING A FIELD DOES NOT CLEAR IT. PVE keeps whatever the config already holds, so a\n * declaration that DROPS its alias would diff as `update` forever: `matches` compares `''`\n * against the live text, the PUT says nothing about alias, and the next plan asks for the very\n * same update. PVE's own answer is the `delete` parameter — a comma-separated list of properties\n * to unset — which exists on the update endpoint only. That is why create does not come through\n * here and calls `shape` directly.\n *\n * ⚠️ REASONED FROM PVE'S UPDATE SCHEMA, NOT MEASURED ON A CLUSTER — unlike the two MEASURED facts\n * at the top of this file, and the difference is worth a reader's attention. `delete` is what\n * `SectionConfig::updateSchema` adds to every PVE update endpoint, and unsetting a property that\n * was never set is a no-op there. If THIS endpoint turns out to disagree, the blast radius is\n * wider than the feature: a plain `{ vnet, zone }` vnet has neither alias nor tag, so its very\n * first update carries `delete=alias,tag` and every update fails, not just an unset. The symptom\n * is a 400 \"parameter verification failed\" naming `delete`; the fix is to drop this list and\n * accept the forever-update on unset instead. Verify it once against a live vnet.\n */\nconst updateForm = (props: SdnVnetProps): Record<string, string> => {\n const unset = [\n ...((props.alias ?? '') === '' ? ['alias'] : []),\n ...(props.tag === undefined ? ['tag'] : []),\n ];\n return withClears(shape(props), unset);\n};\n\nconst handlers = pveHandlers<SdnVnetProps, SdnVnetAttributes>({\n // ⛔ PVE gates this family's ITEM read on the allocate privilege, not the audit one — see\n // `readRole` in resource.ts. The auditor-shaped lease reads 403, which `read` turns into\n // \"absent\", and the plan then says create for an object that is plainly there.\n readRole: 'provision',\n attributes: (live, props) => ({\n alias: typeof live['alias'] === 'string' ? live['alias'] : '',\n bridge: props.vnet,\n isolatePorts: bool(live['isolate-ports']),\n tag: tagOf(live['tag']),\n vlanaware: bool(live['vlanaware']),\n vnet: props.vnet,\n /**\n * ⚠️ `''` RATHER THAN `props.zone` WHEN PVE DOES NOT SAY. Echoing the declared zone back would\n * make an unreadable zone look like agreement; `''` matches no declared zone, so the plan\n * reports drift instead. A vnet in the wrong zone is attached to the wrong network, and this\n * is the one field here where a spurious update costs less than a missed one.\n */\n zone: typeof live['zone'] === 'string' ? live['zone'] : '',\n }),\n collection: () => 'cluster/sdn/vnets',\n createForm: (props) => ({ ...shape(props), vnet: props.vnet }),\n /**\n * ⚠️ `vnet`, `bridge` AND WHETHER THE CONFIG IS APPLIED ARE ABSENT FROM THIS COMPARISON ON\n * PURPOSE. The first two are the path — a changed id is a different object, not an edit — and\n * staged-vs-running is a fact about the cluster rather than about the declaration. Diffing it\n * would report an update on every plan until somebody ran the apply, and then report another\n * one the moment anybody else staged anything at all.\n */\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: { create: 'pve:POST /cluster/sdn/vnets', update: 'pve:PUT /cluster/sdn/vnets/{vnet}' },\n matches: (attributes, props) =>\n attributes.zone === props.zone &&\n attributes.alias === (props.alias ?? '') &&\n attributes.tag === props.tag &&\n attributes.vlanaware === (props.vlanaware === true) &&\n attributes.isolatePorts === (props.isolatePorts === true),\n /**\n * ⚠️ NO QUERY STRING HERE, HOWEVER TEMPTING. `?pending=1` would surface whether the staged\n * config has been applied, but the factory uses this one path for GET, PUT and DELETE alike,\n * and PVE answers an unexpected parameter on a write with \"parameter verification failed\".\n * That is why staged-ness is documented at the top of this file rather than made an attribute.\n */\n path: (props) => `cluster/sdn/vnets/${props.vnet}`,\n updateForm,\n});\n\n/**\n* ⛔ EMPTY, LIKE EVERY OTHER RESOURCE IN THIS PACKAGE. `GET /cluster/sdn/vnets` answers\n* with every vnet on the cluster, and on an estate already running SDN those are\n* load-bearing bridges with guests on them. Returning them would invite Alchemy to\n* adopt — and therefore one day delete — a network nobody declared here. Adoption is an\n* explicit act.\n \n *\n* ⛔ A DELETE IS STAGED LIKE EVERY OTHER SDN WRITE. The vnet leaves `vnets.cfg` at once and\n* the bridge stays up on every node until `PUT /cluster/sdn` applies the removal, so a\n* plan reporting \"1 deleted\" has taken nothing off the network yet. Nothing here applies\n* on the operator's behalf: see the ⛔ at the top of this file.\n \n */\nexport const ProxmoxSdnVnetProvider = () =>\n Provider.effect(ProxmoxSdnVnet, Effect.succeed(ProxmoxSdnVnet.Provider.of(handlers)));\n",
|
|
99
115
|
"/**\n * `Proxmox.SdnZone` — the network a declared guest sits on, finally declared too.\n *\n * ★ WHY THIS ONE MATTERS MORE THAN IT LOOKS. Every guest NIC here names a bridge —\n * `net0=name=eth0,bridge=vmbr0` — that nothing in this package declares: a hand-built interface,\n * repeated per node, that a container silently depends on. A zone is the first piece of that\n * dependency PVE will let a plan own.\n *\n * ⛔ SDN WRITES ARE STAGED, NOT APPLIED, AND THAT DEFEATS THE FACTORY'S READ-BACK GUARANTEE.\n * POST and PUT under `cluster/sdn/zones` edit `/etc/pve/sdn/zones.cfg` and change NOTHING on any\n * node. The plain GET reads that same staged file back, so `pveOperations` sees its own write,\n * finds the object present, and reports a success in which no node gained an interface. The\n * applied state is a different document — `.running-config` beside it — and `?pending=1` is the\n * read that tells them apart: it returns a merged view with a `state` of new/changed/deleted.\n *\n * ⛔ THE APPLY CANNOT LIVE IN THIS RECONCILE, so it is absent rather than forgotten.\n * `PUT /cluster/sdn` is the atomic pending-to-apply and it applies the WHOLE staged config —\n * every zone, vnet and subnet, on every node. Called from a per-resource reconcile it would push\n * some OTHER resource's half-written zone out to the cluster at whatever point the plan ordering\n * reached this one, and PVE has no pre-apply hook to veto that. The apply belongs to ONE object\n * that depends on all of them — a sibling `Proxmox.SdnApply` over `cluster/sdn`, or a stack-level\n * hook that runs last — so that \"declared\" and \"live on the nodes\" stay two visible steps.\n *\n * ⚠️ SO THESE ATTRIBUTES DESCRIBE THE STAGED ZONE, and there is deliberately no `applied` flag:\n * the factory uses ONE path for GET, PUT and DELETE, and PVE's parameter schemas are closed, so\n * an unexpected `pending` on the PUT is a 400 rather than an ignored hint. Reading `?pending=1`\n * means re-implementing the operations this file exists to share, and an `applied: true` that\n * was never measured is worse than an absent one. Deploying this resource does not change the\n * network; it changes what the next apply will do.\n *\n * ⛔ THE READ THIS RESOURCE PERFORMS NEEDS SDN.Allocate, NOT SDN.Audit, AND AN EARLIER DRAFT OF\n * THIS COMMENT SAID OTHERWISE. Read off the cluster's own schema on 2026-09-13:\n *\n * GET /cluster/sdn/zones -> \"list entries where you have SDN.Audit or SDN.Allocate\"\n * GET /cluster/sdn/zones/{zone} -> {\"check\": [\"perm\", \"/sdn/zones/{zone}\", [\"SDN.Allocate\"]]}\n *\n * `pveOperations.read` calls the ITEM endpoint, so an auditor-shaped `read` lease is REFUSED —\n * and the refusal does not surface as a 403. `read` folds every failure into `undefined`, which\n * the factory reads as \"absent\", so the plan says create, the POST goes out, and PVE answers\n * that the zone already exists. This is the same class as Proxmox.Storage; see\n * docs/privileges.md, \"The failure that does not look like a permission problem\".\n *\n * ⚠️ THE REST OF THE PRIVILEGES. Writes need SDN.Allocate (`/sdn/zones` to create,\n * `/sdn/zones/{zone}` to update or delete); the apply — `Proxmox.SdnApply`, which is what\n * actually publishes any of this — needs SDN.Allocate on `/sdn` itself. SDN.Use is NOT among\n * them: it permits attaching a guest to an existing vnet and nothing else.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { csv, int, text, withClears } from './values.ts';\n\n/** PVE's zone plugins. Which one a zone is decides which fields below it will even accept. */\nexport type SdnZoneType = 'evpn' | 'qinq' | 'simple' | 'vlan' | 'vxlan';\n\nexport interface SdnZoneProps extends WithTarget {\n /**\n * PVE's primary key. ⚠️ AT MOST EIGHT LOWERCASE ALPHANUMERICS, first one a letter: the id lands\n * in generated interface names, so PVE enforces it with a regex whose rejection reads as\n * \"value does not match the regex pattern\" rather than as \"your name is too long\".\n */\n zone: string;\n /**\n * ⛔ CREATE-TIME ONLY. `PUT /cluster/sdn/zones/{zone}` has no `type` parameter at all — a\n * section's type is fixed when it is written. Changing it here is not an update PVE can\n * perform; the ⛔ in `attributes` turns that edit into a refusal instead of a silent no-op.\n */\n type: SdnZoneType;\n /** Accepted by every zone type. Unset means the zone inherits the underlying bridge's MTU. */\n mtu?: number;\n /** ⚠️ WHICH NODES THE ZONE IS DEPLOYED TO. Unset means EVERY node, not none. */\n nodes?: string[];\n /** IPAM plugin id. Unset is the built-in `pve` one — the two are one zone, see `sameIpam`. */\n ipam?: string;\n /** DNS plugin id used to register guest records. */\n dns?: string;\n /** The domain those records are registered under, e.g. `example.internal`. */\n dnszone?: string;\n /** vxlan only, and REQUIRED there: the peer addresses the tunnel mesh is built from. */\n peers?: string[];\n /** evpn only, and required there. PVE's own hyphenated name, so no mapping to get wrong. */\n 'vrf-vxlan'?: number;\n /** qinq only, and required there: the service VLAN tag the zone's vnets are stacked inside. */\n tag?: number;\n /** vlan and qinq only, and required in both: the existing bridge the zone is carved out of. */\n bridge?: string;\n}\n\n/**\n * ⛔ NO `digest` HERE, ON PURPOSE. PVE returns one, but it is the digest of the WHOLE zones.cfg\n * file rather than of this section — declaring a SECOND zone would rewrite this one's stored\n * attributes, and comparing it would report an update on a zone nobody touched.\n */\nexport interface SdnZoneAttributes {\n zone: string;\n type: SdnZoneType;\n /** 0 when unset, which is PVE's \"inherit from the bridge\" rather than an MTU of zero. */\n mtu: number;\n /** Normalised: split, sorted, rejoined. Never compare a raw PVE string to it — see `csv`. */\n nodes: string;\n /** The EFFECTIVE ipam: an absent one is reported as `pve`, because that is what will be used. */\n ipam: string;\n dns: string;\n dnszone: string;\n peers: string;\n 'vrf-vxlan': number;\n tag: number;\n bridge: string;\n /** ⚠️ THE REMINDER IN THE STATE ITSELF: staged config. No value here says a node has the zone. */\n readonly staged: true;\n}\n\nexport interface ProxmoxSdnZone extends Resource<\n 'Proxmox.SdnZone',\n SdnZoneProps,\n SdnZoneAttributes,\n never,\n PveRequirements\n> {}\n\nexport const ProxmoxSdnZone = Resource<ProxmoxSdnZone>('Proxmox.SdnZone');\n\n/**\n * ⚠️ AN ABSENT `ipam` AND `ipam pve` ARE THE SAME ZONE. `pve` is the built-in default, and whether\n * PVE materialises that word into zones.cfg on create is version-dependent — so comparing the\n * raw values reports an update on the plan immediately AFTER the create, on a zone this provider\n * just wrote itself. Normalising both sides is loop-proof under either behaviour.\n */\nconst sameIpam = (live: string, declared: string | undefined) =>\n (live === '' ? 'pve' : live) === (declared === undefined || declared === '' ? 'pve' : declared);\n\n/**\n * The mutable fields, spread in only when defined.\n *\n * ⚠️ AN EMPTY STRING IS NOT \"UNSET\" FOR AN INTEGER FIELD. `mtu=` fails PVE's parameter check\n * outright instead of clearing the value, so the pool.ts habit of sending `''` for an absent\n * optional does not carry over here. Clearing is a separate verb — see `clearable`.\n */\nconst body = (props: SdnZoneProps): Record<string, string> => ({\n ...(props.bridge === undefined ? {} : { bridge: props.bridge }),\n ...(props.dns === undefined ? {} : { dns: props.dns }),\n ...(props.dnszone === undefined ? {} : { dnszone: props.dnszone }),\n ...(props.ipam === undefined ? {} : { ipam: props.ipam }),\n ...(props.mtu === undefined ? {} : { mtu: String(props.mtu) }),\n ...(props.nodes === undefined ? {} : { nodes: csv(props.nodes) }),\n ...(props.peers === undefined ? {} : { peers: csv(props.peers) }),\n ...(props.tag === undefined ? {} : { tag: String(props.tag) }),\n ...(props['vrf-vxlan'] === undefined ? {} : { 'vrf-vxlan': String(props['vrf-vxlan']) }),\n});\n\n/**\n * Which managed fields PVE will let `delete=` clear, per zone type.\n *\n * ⛔ A PUT THAT SIMPLY OMITS A FIELD DOES NOT CLEAR IT. PVE merges the form into the existing\n * section, so dropping `mtu` from a declaration leaves the old MTU in place and `matches`\n * reports an update on every plan forever — the exact bug this table exists to prevent.\n * ⛔ AND THE LIST CANNOT BE THE SAME FOR EVERY TYPE. `delete=<opt>` dies with \"no such option\"\n * when the option is not in that plugin's schema (`peers` on a simple zone) and with \"unable to\n * delete required option\" when it is required there. `bridge`, `tag`, `peers` and `vrf-vxlan`\n * are required by the only types that accept them, so none of the four is ever clearable: a\n * zone that lost one is a replace, not an update.\n * ⚠️ `ipam` IS OMITTED FOR evpn DELIBERATELY. It is optional on the other four; on evpn its\n * optionality has moved between releases, and a wrong entry here is a hard 400 on EVERY update\n * rather than a cosmetic diff. Dropping `ipam` from an evpn declaration therefore reports an\n * update that does not converge — visible, unlike a wrong delete that breaks the zone.\n */\nconst clearable: Record<SdnZoneType, readonly string[]> = {\n evpn: ['dns', 'dnszone', 'mtu', 'nodes'],\n qinq: ['dns', 'dnszone', 'ipam', 'mtu', 'nodes'],\n simple: ['dns', 'dnszone', 'ipam', 'mtu', 'nodes'],\n vlan: ['dns', 'dnszone', 'ipam', 'mtu', 'nodes'],\n vxlan: ['dns', 'dnszone', 'ipam', 'mtu', 'nodes'],\n};\n\nconst handlers = pveHandlers<SdnZoneProps, SdnZoneAttributes>({\n // ⛔ PVE gates this family's ITEM read on the allocate privilege, not the audit one — see\n // `readRole` in resource.ts. The auditor-shaped lease reads 403, which `read` turns into\n // \"absent\", and the plan then says create for an object that is plainly there.\n readRole: 'provision',\n attributes: (live, props) => {\n /**\n * ⛔ A ZONE OF ANOTHER TYPE IS ANOTHER OBJECT, AND \"ABSENT\" IS THE HONEST ANSWER. A PUT cannot\n * change `type`, and the factory only calls a change `replace` for a resource with no update\n * path — which this is not. Reporting the foreign zone as missing makes reconcile POST, and\n * PVE refuses with \"sdn zone ID 'x' already defined\": a loud, accurate error instead of a\n * silent noop over somebody else's zone. An older PVE that omits `type` falls through.\n */\n const liveType = text(live['type']);\n if (liveType !== '' && liveType !== props.type) return undefined;\n return {\n bridge: text(live['bridge']),\n dns: text(live['dns']),\n dnszone: text(live['dnszone']),\n ipam: text(live['ipam']) === '' ? 'pve' : text(live['ipam']),\n mtu: int(live['mtu'], 0),\n nodes: csv(text(live['nodes'])),\n peers: csv(text(live['peers'])),\n staged: true,\n tag: int(live['tag'], 0),\n type: props.type,\n 'vrf-vxlan': int(live['vrf-vxlan'], 0),\n zone: props.zone,\n };\n },\n collection: () => 'cluster/sdn/zones',\n createForm: (props) => ({ ...body(props), type: props.type, zone: props.zone }),\n /**\n * ⚠️ `type` AND `zone` ARE NOT COMPARED. `zone` is the key the object was read by, and `type` is\n * handled above by refusing to recognise a zone of the wrong one. Comparing either here would\n * produce a diff whose update can never satisfy it.\n */\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: { create: 'pve:POST /cluster/sdn/zones', update: 'pve:PUT /cluster/sdn/zones/{zone}' },\n matches: (attributes, props) =>\n attributes.mtu === (props.mtu ?? 0) &&\n attributes.nodes === csv(props.nodes) &&\n sameIpam(attributes.ipam, props.ipam) &&\n attributes.dns === (props.dns ?? '') &&\n attributes.dnszone === (props.dnszone ?? '') &&\n attributes.peers === csv(props.peers) &&\n attributes['vrf-vxlan'] === (props['vrf-vxlan'] ?? 0) &&\n attributes.tag === (props.tag ?? 0) &&\n attributes.bridge === (props.bridge ?? ''),\n path: (props) => `cluster/sdn/zones/${props.zone}`,\n updateForm: (props) => {\n const fields = body(props);\n const clear = clearable[props.type].filter((option) => fields[option] === undefined);\n return withClears(fields, clear);\n },\n});\n\n/**\n* ⛔ Empty for the same reason as every other resource here, and with extra force for SDN:\n* `GET /cluster/sdn/zones` answers with the zones the cluster's whole network already\n* runs on. Adopting those would make a later `alchemy destroy` a cluster-wide outage.\n \n *\n* ⛔ A DELETE IS STAGED TOO, AND THAT IS THE HALF PEOPLE FORGET. The section leaves\n* zones.cfg and Alchemy drops the resource, but the zone KEEPS RUNNING on every node\n* until something applies `PUT /cluster/sdn`: a destroy nobody applies reads as complete\n* in the plan and has not happened on the cluster.\n* ⚠️ PVE also refuses to remove a zone that still holds vnets. That refusal is kept — it is\n* the cluster declining to orphan a network because a line left a file.\n \n */\nexport const ProxmoxSdnZoneProvider = () =>\n Provider.effect(ProxmoxSdnZone, Effect.succeed(ProxmoxSdnZone.Provider.of(handlers)));\n",
|
|
100
116
|
"/**\n * `Proxmox.Storage` — one entry in `/etc/pve/storage.cfg`, declared. Cluster-scoped like a pool,\n * which is why `path` and `collection` carry no node segment though a storage is used per node.\n *\n * ★ THIS IS THE OBJECT EVERY CONTAINER ALREADY DEPENDS ON AND NOTHING DECLARES. `LxcProps.storage`\n * and the prefix of `LxcProps.ostemplate` (`local:vztmpl/...`) are bare strings, so a container's\n * rootfs points at something outside the graph: rebuild a node and the plan still reads `noop`\n * right up to the create that fails because the storage is not there. Passing this resource's\n * `storage` attribute into `LxcProps.storage` makes the dependency real — Alchemy orders by data\n * flow, so the storage reconciles first because the container reads a value out of it.\n *\n * ⛔ ONE PRIVILEGE COVERS THIS WHOLE FAMILY AND IT IS `Datastore.Allocate` ON `/storage`. Create,\n * update, delete AND the single-object read all check it — `Datastore.Audit` is not enough for\n * any of them. MEASURED: `GET /storage/local` under the auditor-scoped `read` lease answers 403\n * \"Permission check failed (/storage/local, Datastore.Allocate)\". `pveOperations.read` folds\n * every failure into `undefined`, i.e. \"absent\", because a 404 is a legitimate answer there and\n * it cannot tell the two apart — so under that lease this resource NEVER CONVERGES, in two\n * directions: reconcile reads \"absent\" and POSTs a create over a storage that is already defined,\n * and where a create does land the read-back is 403 too, so reconcile dies with \"the write\n * returned no error but the object is still absent\" about an object it has just built. Both\n * messages point away from the cause.\n * ★ SO WIDEN OR RE-POINT BEFORE THE FIRST PLAN, outside this file: give the credential mount's\n * `read` role `Datastore.Allocate` on `/storage`, or read this family with `provision`. The\n * provision role in the estate this was written for held only `Datastore.AllocateSpace` and\n * `Datastore.Audit`, and NEITHER IS IT — AllocateSpace writes volumes INTO a storage, Allocate\n * DEFINES one — so it 403'd here until widened the way `Pool.Allocate` was for `Proxmox.Pool`.\n * The provisioning baseline (`provision-baseline.ts`) carries `Datastore.Allocate`.\n *\n * ⛔ THERE IS NO `password` PROP AND THERE MUST NEVER BE ONE. PBS and CIFS storages take one at\n * create time, and Alchemy persists attributes UNENCRYPTED — a secret in props is one careless\n * line away from being an attribute, and from there it is in the state store's nightly dump. So\n * `StorageLocator` types `password`, `keyring` and `encryption-key` as `never`: declaring one is\n * a COMPILE ERROR rather than a leak somebody finds in a backup months later. Set it once out of\n * band (`pvesm set <id> --password`); PVE never returns it on read, so nothing here could diff it\n * anyway.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';\nimport { bool, flag } from './values.ts';\n\n/**\n * The create-time, plugin-specific half of a storage: `dir` wants `path`, `nfs` `server`+`export`,\n * `cifs` `server`+`share`, `pbs` `server`+`datastore`, `rbd` `pool`+`monhost`, `lvmthin`\n * `vgname`+`thinpool`, `zfspool` `pool`. A bag rather than two dozen named fields, because PVE\n * ships about that many plugins and each brings its own locator. ⚠️ Never diffed — see `matches`.\n *\n * ⛔ `password`, `keyring` AND `encryption-key` ARE `never` ON PURPOSE — see the ⛔ in the header.\n * POSITIVE-CONTROLLED rather than assumed: all three are a compile error, `TS2322: Type 'string'\n * is not assignable to type 'undefined'`.\n */\nexport type StorageLocator = Record<string, string> & {\n password?: never;\n keyring?: never;\n 'encryption-key'?: never;\n};\n\nexport interface StorageProps extends WithTarget {\n /** PVE's primary key, cluster-wide — the string an LXC's `storage` and `ostemplate` name. */\n storage: string;\n /**\n * `dir` | `nfs` | `cifs` | `pbs` | `zfspool` | `lvmthin` | `rbd` | … ⚠️ CREATE-ONLY, so a changed\n * `type` plans as `noop` (see `matches`). PVE cannot retype a storage: remove and redeclare.\n */\n type: string;\n /** ⚠️ Create-only, one shape per plugin. See `StorageLocator`. */\n locator?: StorageLocator;\n /** `images,rootdir,vztmpl,iso,backup,snippets`. PVE reports `none` for an empty set. */\n content?: string;\n /**\n * Node restriction, e.g. `node-a,node-b`. ⚠️ A STORAGE WITH A `nodes` LIST DOES NOT EXIST ANYWHERE ELSE:\n * an LXC declared on a node outside it fails at create with \"storage not available on node\".\n */\n nodes?: string;\n /** Keeps the definition but stops PVE using it. */\n disable?: boolean;\n /** Tells PVE the same volumes are visible from every node — a claim, not a mechanism. */\n shared?: boolean;\n /** `off` | `metadata` | `falloc` | `full`. File-based plugins only. */\n preallocation?: string;\n /**\n * `keep-last=3,keep-daily=7,…`. ⚠️ THE WIRE NAME IS KEPT, HYPHEN AND ALL: a `pruneBackups` that\n * quietly becomes `prune-backups` in the form body is a second name for one thing.\n */\n 'prune-backups'?: string;\n comment?: string;\n /**\n * ⚠️ THE DEPRECATED PREDECESSOR OF `prune-backups`, and the two do not mix — declare one or the\n * other. `0` means UNLIMITED to PVE rather than \"unset\", hence the `-1` in the attribute.\n */\n maxfiles?: number;\n}\n\nexport interface StorageAttributes {\n storage: string;\n /** Reported so a plan can show what it points at; never diffed — see `type` above. */\n type: string;\n content: string;\n nodes: string;\n disable: boolean;\n shared: boolean;\n preallocation: string;\n 'prune-backups': string;\n comment: string;\n /** ⚠️ `-1` means \"no maxfiles in storage.cfg\". `0` is a real setting meaning unlimited. */\n maxfiles: number;\n}\n\nexport interface ProxmoxStorage extends Resource<\n 'Proxmox.Storage',\n StorageProps,\n StorageAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a storage holding volumes cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxStorage = Resource<ProxmoxStorage>('Proxmox.Storage', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst str = (value: unknown, fallback = '') => (typeof value === 'string' ? value : fallback);\n\nconst field = (name: string, value: string | undefined): Record<string, string> =>\n value === undefined ? {} : { [name]: value };\n\n/**\n * ⚠️ THE CAST DROPS THE THREE `never` GUARDS AND NOTHING ELSE, and it is load-bearing rather than\n * lazy: an intersection carrying OPTIONAL properties is not assignable to `Record<string,string>`\n * at all, so the guards and the form body cannot both exist without one conversion. At runtime a\n * locator is a plain string map; the guards only ever existed to fail a declaration.\n */\nconst locatorForm = (locator: StorageLocator | undefined) =>\n ({ ...locator }) as Record<string, string>;\n\n/**\n * ⚠️ PVE RE-SERIALISES ITS LIST FIELDS IN ITS OWN ORDER — `content` and `nodes` are parsed into a\n * set and written back sorted, `prune-backups` is a property string that comes back in schema\n * order. Comparing raw strings would report an update for `iso,backup` against `backup,iso` on\n * every plan, and the PUT would \"fix\" it into the identical string. Trimming matters too: a\n * declared `iso, backup` would otherwise never equal what PVE stores.\n */\nconst tokens = (value: string) =>\n value\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part !== '')\n .sort()\n .join(',');\n\nconst sameList = (declared: string | undefined, live: string) =>\n declared === undefined || tokens(declared) === tokens(live);\n\nconst same = <T>(declared: T | undefined, live: T) => declared === undefined || declared === live;\n\n/**\n * The mutable half, for create and update alike.\n *\n * ⚠️ AN UNDECLARED FIELD IS NEITHER SENT NOR COMPARED: undeclared means UNMANAGED here, unlike\n * `Proxmox.Pool` where an absent comment means `''`. A storage has no single default to fall back\n * on — `content` defaults per plugin, and clearing a field needs an explicit `delete=` parameter\n * rather than an empty value — so a guessed default would rewrite a storage somebody tuned by\n * hand, and comparing one would report drift nobody declared.\n *\n * ⚠️ RECONCILE PUTs UNCONDITIONALLY whenever the object exists (resource.ts), so this form must be\n * safe to re-apply to a storage that already matches. It is: the declared set and nothing else,\n * and PVE leaves absent parameters alone.\n */\nconst mutable = (props: StorageProps): Record<string, string> => ({\n ...field('comment', props.comment),\n ...field('content', props.content),\n ...field('disable', flag(props.disable)),\n ...field('maxfiles', props.maxfiles === undefined ? undefined : String(props.maxfiles)),\n ...field('nodes', props.nodes),\n ...field('preallocation', props.preallocation),\n ...field('prune-backups', props['prune-backups']),\n ...field('shared', flag(props.shared)),\n});\n\nconst handlers = pveHandlers<StorageProps, StorageAttributes>({\n // ⛔ PVE gates this family's ITEM read on the allocate privilege, not the audit one — see\n // `readRole` in resource.ts. The auditor-shaped lease reads 403, which `read` turns into\n // \"absent\", and the plan then says create for an object that is plainly there.\n readRole: 'provision',\n /**\n * ⚠️ `digest` IS DELIBERATELY NOT AN ATTRIBUTE. PVE returns one on every storage read and it\n * covers storage.cfg as a FILE, not this section of it, so keeping it would rewrite this\n * resource's state whenever an unrelated storage was edited — churn that reads like drift.\n */\n attributes: (live, props) => ({\n comment: str(live['comment']),\n content: str(live['content']),\n disable: bool(live['disable']),\n maxfiles: typeof live['maxfiles'] === 'number' ? live['maxfiles'] : -1,\n nodes: str(live['nodes']),\n preallocation: str(live['preallocation']),\n 'prune-backups': str(live['prune-backups']),\n shared: bool(live['shared']),\n storage: props.storage,\n type: str(live['type'], props.type),\n }),\n collection: () => 'storage',\n /**\n * ⚠️ THE BAG IS SPREAD FIRST SO NOTHING IN IT CAN SHADOW A FIELD THIS RESOURCE MANAGES. A stray\n * `storage` or `type` in a locator would otherwise rename the object being created, and PVE\n * would build the wrong thing under a name Alchemy then records as the declared one.\n */\n createForm: (props) => ({\n ...locatorForm(props.locator),\n ...mutable(props),\n storage: props.storage,\n type: props.type,\n }),\n /**\n * ⚠️ `type` AND THE LOCATOR ARE NEVER COMPARED, AND AN UNDECLARED FIELD IS NOT COMPARED EITHER.\n * PVE returns the create-only fields on read but refuses them on PUT, so diffing one could only\n * plan an update that no write can apply: a plan that reports work on every run, forever.\n */\n /** The vendor rules these forms are checked against at plan time — resource-spec.ts. */\n endpoint: { create: 'pve:POST /storage', update: 'pve:PUT /storage/{storage}' },\n matches: (attributes, props) =>\n sameList(props.content, attributes.content) &&\n sameList(props.nodes, attributes.nodes) &&\n sameList(props['prune-backups'], attributes['prune-backups']) &&\n same(props.disable, attributes.disable) &&\n same(props.shared, attributes.shared) &&\n same(props.preallocation, attributes.preallocation) &&\n same(props.comment, attributes.comment) &&\n same(props.maxfiles, attributes.maxfiles),\n path: (props) => `storage/${props.storage}`,\n updateForm: mutable,\n});\n\n/**\n* ⛔ EMPTY, AND HERE IT MATTERS MORE THAN ANYWHERE ELSE IN THIS PACKAGE. `GET /storage`\n* returns every definition on the cluster, `local` and `local-lvm`/`local-zfs` included:\n* the installer's own, that every guest's rootfs sits on. Adopting those would put\n* Alchemy one `delete` away from the cluster's disks. Adoption is explicit, always.\n \n *\n* ⚠️ THIS REMOVES THE DEFINITION, NOT THE DATA. PVE drops the section from storage.cfg and\n* leaves the volumes on disk untouched — so the loss is not the bytes, it is that guests\n* whose config still names this storage can no longer resolve their own disks.\n \n */\nexport const ProxmoxStorageProvider = () =>\n Provider.effect(ProxmoxStorage, Effect.succeed(ProxmoxStorage.Provider.of(handlers)));\n",
|
|
101
|
-
"/**\n * `Proxmox.ZfsPool` — a zpool built on one node's physical disks, declared.\n *\n * ⛔ A CREATE HERE WIPES THE DISKS IT IS GIVEN AND THERE IS NO UNDO. `POST /nodes/{node}/disks/zfs`\n * runs `zpool create` on the block devices named in `devices`; whatever was on them is gone the\n * moment the worker runs. Every other decision in this file follows from that one sentence, and\n * each of them is written out below rather than left to be inferred.\n *\n * ⛔ DESTROY IS REFUSED. `delete` MAKES NO API CALL — removing this resource from a stack leaves\n * the zpool and its data exactly where they are, and Alchemy simply forgets about them. The\n * asymmetry is the whole argument: an ORPHANED pool is recoverable (declare it again, or destroy\n * it by hand once a human has looked at which disks are in it), a DESTROYED pool is not. Alchemy\n * deletes for reasons that have nothing to do with anybody intending it — a line removed from a\n * stack file, a resource renamed so the old id is dropped, a whole stack torn down by the wrong\n * command, a replace triggered by a prop nobody meant to touch. Handing `zpool destroy` to any\n * of those is not a feature. MEASURED in the node's own source: the DELETE handler forks\n * `zfsremove`, which runs `zpool destroy <name>` unconditionally, and with `cleanup-disks` then\n * calls `wipe_blockdev` on every member. Neither flag is offered as a prop, because there is no\n * call here for them to reach.\n * ★ THE OPERATOR'S PATH IS `pvesh delete /nodes/<node>/disks/zfs/<name>`, typed by a person who\n * has just looked at the pool. Do not \"complete the CRUD\" by wiring `delete` to `ops.destroy`:\n * that one line turns a text edit into an unrecoverable one.\n *\n * ⛔ THERE IS NO PUT ON THIS FAMILY AT ALL. MEASURED from the cluster's own published schema\n * (`/usr/share/pve-docs/api-viewer/apidoc.js`, read on node-b 2026-09-13): `/nodes/{node}/disks/zfs`\n * has GET and POST, `/nodes/{node}/disks/zfs/{name}` has GET and DELETE, and that is the whole\n * surface. So `updateForm` is omitted — which, per `resource.ts`, makes any `matches` = false a\n * REPLACE. A replace is a delete followed by a create, and on this family the delete is the\n * thing above. Read the ⛔ on `matches` before adding anything to it.\n *\n * ⛔ NOT ONE CREATE PARAMETER COMES BACK ON READ, SO `matches` COMPARES NOTHING. MEASURED against\n * the live cluster: `GET /nodes/node-b/disks/zfs/rpool` answers exactly\n * `{action, children, errors, leaf, name, scan, state, status}` — no `ashift`, no `compression`,\n * no `raidlevel`, no `devices`, no `add_storage`. Every create parameter is write-only. There is\n * therefore no field a declaration and a live pool can both be asked about, and a `matches` that\n * invented one would plan a replace — i.e. a `zpool destroy` — over a difference it could never\n * verify in the first place.\n *\n * ⚠️ AND WHAT THE INDEX RETURNS IS LIVE TELEMETRY, NOT CONFIGURATION. `GET /nodes/node-b/disks/zfs`\n * gives `alloc`, `free`, `frag`, `dedup`, `size`, `health`. MEASURED: two reads of node-b seconds\n * apart returned `alloc` 14474944512 then 14472740864 — it moves on its own, with no declaration\n * anywhere near it. `scan` in the detail read moves the same way (it carries the last scrub), and\n * `status`/`action` appear and vanish as ZFS feature flags and faults come and go. None of them\n * is compared, and the three churning strings are kept out of the state store entirely rather\n * than rewriting this resource's row every time somebody writes a file.\n *\n * ⚠️ TWO HANDLERS ARE OVERRIDDEN AND THE OTHER THREE COME FROM THE FACTORY — see the ★ above\n * `handlers`. `delete` is the ⛔ above. `reconcile` is because the create is a FORKED WORKER, so\n * the factory's immediate read-back would report a successful create as a failure; the measured\n * detail is in the ⛔ on `createPool` in zfs-pool-create.ts.\n *\n * ⚠️ PRIVILEGES, FROM THE SCHEMA. Read and diff need `Sys.Audit` on `/` — both GETs check it.\n * Reconcile needs `Sys.Modify` on `/` for the POST. `Datastore.Allocate` on `/storage` is NOT\n * needed, because `add_storage` is not offered — see the ⛔ on `ZfsPoolProps`. `delete` needs\n * nothing at all, since it calls nothing.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n type PveRequirements,\n type PveSpec,\n type WithTarget,\n pveHandlers,\n pveOperations,\n} from './resource.ts';\nimport { text } from './values.ts';\nimport { createForm, createPool, destroyPool } from './zfs-pool-write.ts';\n\n/** PVE's layouts. ⚠️ Each has a minimum disk count PVE enforces, and `raid10` needs an even one. */\nexport type ZfsRaidLevel =\n | 'single'\n | 'mirror'\n | 'raid10'\n | 'raidz'\n | 'raidz2'\n | 'raidz3'\n | 'draid'\n | 'draid2'\n | 'draid3';\n\nexport type ZfsCompression = 'on' | 'off' | 'gzip' | 'lz4' | 'lzjb' | 'zle' | 'zstd';\n\n/**\n * ⛔ THERE IS NO `add_storage`, `cleanup-config` OR `cleanup-disks` PROP, AND THAT IS NOT AN\n * OVERSIGHT. The last two belong to the DELETE this resource refuses to make. `add_storage=1`\n * would have the POST build a `Proxmox.Storage`-shaped object this resource does not own, cannot\n * see on any later read, and cannot remove — invisible to every diff forever, and a collision\n * waiting for the day somebody also declares the storage properly. Declare a `Proxmox.Storage`\n * with `type: 'zfspool'` and `locator: { pool: <this pool's name> }` instead, reading the `name`\n * attribute below so Alchemy orders the storage after the pool.\n */\nexport interface ZfsPoolProps extends WithTarget {\n /**\n * ⛔ A ZPOOL IS NODE-LOCAL AND THIS IS HALF ITS IDENTITY. Changing it moves nothing: `path` then\n * points at a different node, the read answers absent, and reconcile tries to CREATE the pool\n * over there — on whatever `/dev/...` happens to answer to those names on that machine.\n * Declare a separate resource per node instead.\n */\n node: string;\n /** The zpool name, and PVE's key for it on this node. ⚠️ Changing it creates a SECOND pool. */\n name: string;\n /**\n * The block devices to build it on.\n *\n * ⛔ NAME THEM BY `/dev/disk/by-id/...`, NOT `/dev/sdb`. Kernel names are not stable across\n * reboots; a by-id link is. MEASURED in the node's source: PVE resolves what it is handed to a\n * by-id link only AFTER it has decided the device is free, so a stale `/dev/sdb` in a stack\n * file is checked against, and then wiped, whichever disk currently answers to that name.\n * ⚠️ ORDER IS MEANING FOR `raid10` AND IS PRESERVED — PVE walks the list two at a time and makes\n * each pair a mirror. This is why the form uses a plain join and NOT `csv()` from values.ts:\n * `csv` sorts, and sorting this list silently re-pairs the mirrors into a different pool.\n * ⚠️ PVE's only guard is `assert_disk_unused`, i.e. its own `disk_is_used` over the same `used`\n * column the Disks view shows. A blank disk it considers free is wiped with no confirmation.\n */\n devices: readonly string[];\n raidlevel: ZfsRaidLevel;\n /**\n * Sector size exponent, 9–16, PVE default 12.\n *\n * ⛔ WRITE-ONLY AND IMMUTABLE FOR THE LIFE OF THE POOL. It is consumed by `zpool create -o\n * ashift=` and never returned by any read on this family, so changing it later plans as `noop`\n * and changes nothing. A different ashift means destroying and rebuilding the pool, by hand,\n * with the data moved off first.\n */\n ashift?: number;\n /**\n * ⚠️ Write-only in the same way as `ashift`: applied by `zfs set compression=` at create time and\n * never read back, so a later change plans `noop`. Change it with `zfs set` on the node.\n */\n compression?: ZfsCompression;\n /**\n * `data=<integer>,spares=<integer>`. ⚠️ THE WIRE NAME IS KEPT, HYPHEN AND ALL, for the reason\n * `storage.ts` keeps `prune-backups`: a second spelling is a second name for one thing.\n * ⚠️ PVE refuses it outright unless `raidlevel` is one of the `draid*` levels.\n */\n 'draid-config'?: string;\n}\n\n/**\n * ⚠️ EVERYTHING HERE IS REPORTED, NOTHING HERE IS COMPARED — see the ⛔ on `matches`. These exist\n * so a plan and the state store can say what the pool actually is, on a family where the\n * declaration and the live object share no comparable field at all.\n */\nexport interface ZfsPoolAttributes {\n /** ★ The value a `Proxmox.Storage` should read to order itself after this pool. */\n name: string;\n node: string;\n /** `ONLINE` | `DEGRADED` | `FAULTED` | … Health, not configuration. */\n state: string;\n /** ZFS's own error summary, e.g. `No known data errors`. */\n errors: string;\n /**\n * The leaf devices ZFS reports, in vdev order, comma-joined.\n *\n * ⚠️ THIS IS WHAT ZFS RESOLVED, NOT WHAT WAS DECLARED, and the two normally differ: PVE rewrites\n * a `/dev/sdb` into a by-id link before creating, and ZFS reports partition paths (`…-part3`)\n * for a pool built on partitions — MEASURED, that is exactly what node-b's `rpool` reports. Never\n * compare it with `props.devices`.\n */\n devices: string;\n}\n\nexport interface ProxmoxZfsPool extends Resource<\n 'Proxmox.ZfsPool',\n ZfsPoolProps,\n ZfsPoolAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a pool holding datasets cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxZfsPool = Resource<ProxmoxZfsPool>('Proxmox.ZfsPool', {\n defaultRemovalPolicy: 'retain',\n});\n\n/**\n * The device paths at the bottom of PVE's vdev tree, in the order ZFS reports them.\n *\n * ⚠️ \"NO CHILDREN\" IS THE LEAF TEST, AND IT IS NOT A GUESS — MEASURED in the node's `preparetree`,\n * which sets `leaf` to 0 when there are children and 1 otherwise. Reading the flag would work\n * equally well; recursing on `children` needs no second field to be present and is what makes\n * nested sections (mirrors inside a raid10, `spares`, `cache`) flatten correctly.\n */\nconst leafDevices = (children: unknown): string[] =>\n Array.isArray(children)\n ? children.flatMap((entry: unknown) => {\n const vdev = entry as { children?: unknown; name?: unknown };\n const nested = leafDevices(vdev.children);\n return nested.length > 0 ? nested : [text(vdev.name)].filter((name) => name !== '');\n })\n : [];\n\nconst spec: PveSpec<ZfsPoolProps, ZfsPoolAttributes> = {\n /**\n * ⚠️ `scan`, `status` AND `action` ARE DELIBERATELY NOT ATTRIBUTES. All three are advisory\n * strings ZFS rewrites on its own — `scan` on every scrub, the other two as feature flags and\n * faults come and go — so keeping them would rewrite this resource's state row for reasons no\n * declaration caused. Same reasoning as `digest` in storage.ts.\n */\n attributes: (live, props) => {\n // ⚠️ A detail answer with no `name` is not a pool. `name` is non-optional in PVE's schema, so\n // this is a shape check rather than a default — and \"absent\" is the honest reading of a\n // reply that does not describe the object that was asked for.\n if (text(live['name']) === '') return undefined;\n return {\n devices: leafDevices(live['children']).join(','),\n errors: text(live['errors'], 'unknown'),\n name: props.name,\n node: props.node,\n state: text(live['state'], 'unknown'),\n };\n },\n collection: (props) => `nodes/${props.node}/disks/zfs`,\n createForm,\n /**\n * ⛔ IT ALWAYS ANSWERS TRUE, AND THAT IS THE POINT OF THIS FILE. A pool that is there under the\n * declared name on the declared node IS the declaration, because there is nothing else the two\n * can be compared on: every create parameter is write-only (see the ⛔ in the header) and\n * everything the read does return is telemetry that moves by itself. So declaring what is live\n * plans as `noop` — the only honest answer available, and the one the live cluster needs:\n * `rpool` exists on node-b, node-c and node-d with nothing about its construction readable.\n * ⛔ ANYTHING ADDED HERE IS A REPLACE, NOT AN UPDATE. `updateForm` is omitted because PVE has\n * no PUT, so `resource.ts` turns a false into `{action:'replace'}` — delete then create — on\n * a family whose delete is `zpool destroy`. A comparison added here would be one plan away\n * from destroying a pool over a field it cannot even read back. If a future PVE grows a PUT,\n * add `updateForm` FIRST and only then consider comparing what that PUT accepts.\n */\n matches: () => true,\n path: (props) => `nodes/${props.node}/disks/zfs/${props.name}`,\n};\n\nconst ops = pveOperations(spec);\n\n/**\n * ★ THREE HANDLERS COME FROM THE FACTORY AND TWO DO NOT, SO THE TWO ARE THE ONLY ONES WRITTEN OUT.\n * `list`, `read` and `diff` are the factory's, unchanged — the spread is what proves it, rather\n * than three re-typed delegations nobody rereads. `reconcile` and `delete` are the two the header\n * explains, and they are the only places this family departs from every other resource here.\n * This is the acl.ts precedent, for a different reason: there the factory's delete hits an\n * endpoint PVE does not implement, here it hits one that works and destroys the data.\n */\nconst handlers = {\n ...pveHandlers(spec),\n delete: Effect.fn(function* ({ olds }: { olds: ZfsPoolProps }) {\n yield* destroyPool(olds, spec.path(olds));\n }),\n reconcile: ({ news }: { news: ZfsPoolProps }) =>\n createPool(news, spec.collection(news), spec.path(news), ops.read),\n};\n\nexport const ProxmoxZfsPoolProvider = () =>\n Provider.effect(ProxmoxZfsPool, Effect.succeed(ProxmoxZfsPool.Provider.of(handlers)));\n",
|
|
102
|
-
"/**\n * The create lane for `Proxmox.ZfsPool`: the form PVE wants, and the wait for the worker it forks.\n *\n * ★ SPLIT OUT OF zfs-pool.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a real one\n * rather than a convenient line number. This file answers \"how does a declaration become a pool\n * on disks, and how do we know it landed\". zfs-pool.ts answers \"what is a zpool, what can be\n * said about a live one, and when has it changed\" — and on this family the answer to the last of\n * those is \"never\", which is why the two halves have so little to say to each other.\n *\n * ⚠️ THE `import type` BACK TO zfs-pool.ts IS A CYCLE ON PAPER ONLY — it is type-only, so it is\n * erased before anything runs, and the resource's public shape stays in the file that declares\n * the resource rather than being moved somewhere odd to dodge the arrow. Same as\n * metric-server-form.ts.\n *\n * ⚠️ `createPool` TAKES THE READ AS A PARAMETER, GENERIC IN ITS ERROR AND REQUIREMENT CHANNELS,\n * rather than importing `pveOperations` and rebuilding the spec here. Two `pveOperations` over\n * one spec would be two closures nobody can prove are the same; passing the one the resource\n * already built means this file cannot accidentally read the cluster differently from `diff`.\n */\nimport * as Effect from 'effect/Effect';\nimport { pve } from './client.ts';\nimport { text } from './values.ts';\nimport type { ZfsPoolAttributes, ZfsPoolProps } from './zfs-pool.ts';\n\n/**\n * The POST body. Every field in it is write-only — nothing here is ever read back.\n *\n * ⚠️ `node` IS NOT IN THE BODY: it is already the `nodes/{node}` segment of the URL being POSTed\n * to, and a second copy can only disagree with it. Same reasoning as `id` in metric-server.ts.\n * ⚠️ `devices` IS JOINED IN DECLARED ORDER AND NOT PUT THROUGH `csv()` FROM values.ts. `csv` sorts,\n * and PVE walks this list two at a time to pair `raid10` mirrors — MEASURED in the node's own\n * `PVE::API2::Disks::ZFS` — so sorting it builds a different pool from the one declared. The\n * list is never compared against anything, so it needs no normalisation, only faithful order.\n * ⚠️ AN OMITTED OPTIONAL IS NOT SENT. PVE's own defaults (ashift 12, compression on) are then\n * applied by the node. Sending a guessed default would be indistinguishable here — the values\n * are unreadable afterwards either way — but it would put a number in the request that no\n * declaration asked for, and this is the one call that writes to physical disks.\n */\nexport const createForm = (props: ZfsPoolProps): Record<string, string> => ({\n devices: props.devices.join(','),\n name: props.name,\n raidlevel: props.raidlevel,\n ...(props.ashift === undefined ? {} : { ashift: String(props.ashift) }),\n ...(props.compression === undefined ? {} : { compression: props.compression }),\n ...(props['draid-config'] === undefined ? {} : { 'draid-config': props['draid-config'] }),\n});\n\n/** 30 reads, 2s apart. A create that has not landed in a minute has gone wrong, not gone slow. */\nconst SETTLE_ATTEMPTS = 30;\n\n/**\n * Create the pool if it is absent, and otherwise leave it completely alone.\n *\n * ⛔ THE POST ONLY FORKS A WORKER. MEASURED in `/usr/share/perl5/PVE/API2/Disks/ZFS.pm` on node-b: the\n * create handler ends in `$rpcenv->fork_worker('zfscreate', ...)` and its HTTP answer is a UPID\n * returned the instant the worker is forked — before `zpool create` has run, let alone finished.\n * A read-back taken immediately, as `pveOperations.reconcile` takes it, finds nothing and would\n * report a successful create as the failure \"the write returned no error but the object is still\n * absent\". THAT is why this function exists instead of the factory's reconcile.\n *\n * ⚠️ THERE IS NO UPDATE BRANCH BECAUSE PVE HAS NO PUT HERE. An existing pool is returned exactly as\n * read, unwritten: the only write this resource ever makes is the create of a pool that is not\n * there. Do not add a \"repair\" write; there is no field it could set.\n *\n * ⛔ AND IF THE READ IS WRONG, THE POST IS STILL SAFE — measured, not hoped for. `read` folds a 403\n * or an unreachable node into \"absent\", so a too-narrow lease would send this down the create\n * path over a live pool. PVE's handler calls `get_pool_data()` before it forks anything and dies\n * with \"pool '<name>' already exists on node '<node>'\", and `assert_disk_unused` refuses every\n * device that pool is holding. A create aimed at a pool that is really there fails loudly rather\n * than wiping it.\n *\n * ⚠️ SHORT POLLS WITH A HARD CAP, NOT ONE LONG WAIT. `read` cannot tell \"not yet\" from \"forbidden\"\n * — both are `undefined` — so the loop gives up with the UPID rather than retrying forever, and\n * the UPID is the only thing that leads to the worker's real error.\n */\nexport const createPool = <E, R>(\n props: ZfsPoolProps,\n collection: string,\n path: string,\n read: (props: ZfsPoolProps) => Effect.Effect<ZfsPoolAttributes | undefined, E, R>,\n) =>\n Effect.gen(function* () {\n const existing = yield* read(props);\n if (existing !== undefined) return existing;\n\n const upid = yield* pve<string>(\n props.target,\n 'provision',\n 'POST',\n collection,\n createForm(props),\n );\n\n const settle = (attempts: number): Effect.Effect<ZfsPoolAttributes | undefined, E, R> =>\n Effect.gen(function* () {\n const live = yield* read(props);\n if (live !== undefined || attempts <= 0) return live;\n yield* Effect.sleep('2 seconds');\n return yield* settle(attempts - 1);\n });\n\n const after = yield* settle(SETTLE_ATTEMPTS);\n if (after === undefined) {\n return yield* Effect.die(\n new Error(\n `${path}: POST returned ${text(upid, '(no UPID)')} but the pool has not appeared. That ` +\n 'POST only forks a `zfscreate` worker, so the task holds the real error -- read it ' +\n `with \\`pvesh get /nodes/${props.node}/tasks/<upid>/log\\`. NOTHING WAS RETRIED AND ` +\n 'NOTHING WAS CLEANED UP: if the worker reached `zpool create`, the declared devices ' +\n 'have already been written to.',\n ),\n );\n }\n return after;\n });\n\n/**\n * ⛔ `DELETE /nodes/{node}/disks/zfs/{name}` RUNS `zpool destroy` AND THE DATASETS GO WITH IT.\n * It is implemented rather than stubbed, because a `delete` that silently does nothing lies to\n * whoever reads the plan. What keeps a deleted line from destroying a pool is the resource's\n * `defaultRemovalPolicy: 'retain'` above — an orphaned pool is forgotten, not destroyed, and a\n * caller who means it says `.pipe(RemovalPolicy.destroy())`.\n *\n * ⚠️ `cleanup-config` IS NOT SENT. It would additionally remove the PVE storage entry pointing\n * at this pool, which is a different object with its own resource (`Proxmox.Storage`); one\n * resource reaching over to delete another's is how a stack ends up with state describing\n * something that is gone.\n */\nexport const destroyPool = (olds: ZfsPoolProps, path: string) =>\n pve(olds.target, 'provision', 'DELETE', path);\n"
|
|
117
|
+
"/**\n * `Proxmox.ZfsPool` — a zpool built on one node's physical disks, declared.\n *\n * ⛔ A CREATE HERE WIPES THE DISKS IT IS GIVEN AND THERE IS NO UNDO. `POST /nodes/{node}/disks/zfs`\n * runs `zpool create` on the block devices named in `devices`; whatever was on them is gone the\n * moment the worker runs. Every other decision in this file follows from that one sentence, and\n * each of them is written out below rather than left to be inferred.\n *\n * ⛔ DESTROY IS REFUSED. `delete` MAKES NO API CALL — removing this resource from a stack leaves\n * the zpool and its data exactly where they are, and Alchemy simply forgets about them. The\n * asymmetry is the whole argument: an ORPHANED pool is recoverable (declare it again, or destroy\n * it by hand once a human has looked at which disks are in it), a DESTROYED pool is not. Alchemy\n * deletes for reasons that have nothing to do with anybody intending it — a line removed from a\n * stack file, a resource renamed so the old id is dropped, a whole stack torn down by the wrong\n * command, a replace triggered by a prop nobody meant to touch. Handing `zpool destroy` to any\n * of those is not a feature. MEASURED in the node's own source: the DELETE handler forks\n * `zfsremove`, which runs `zpool destroy <name>` unconditionally, and with `cleanup-disks` then\n * calls `wipe_blockdev` on every member. Neither flag is offered as a prop, because there is no\n * call here for them to reach.\n * ★ THE OPERATOR'S PATH IS `pvesh delete /nodes/<node>/disks/zfs/<name>`, typed by a person who\n * has just looked at the pool. Do not \"complete the CRUD\" by wiring `delete` to `ops.destroy`:\n * that one line turns a text edit into an unrecoverable one.\n *\n * ⛔ THERE IS NO PUT ON THIS FAMILY AT ALL. MEASURED from the cluster's own published schema\n * (`/usr/share/pve-docs/api-viewer/apidoc.js`, read on node-b 2026-09-13): `/nodes/{node}/disks/zfs`\n * has GET and POST, `/nodes/{node}/disks/zfs/{name}` has GET and DELETE, and that is the whole\n * surface. So `updateForm` is omitted — which, per `resource.ts`, makes any `matches` = false a\n * REPLACE. A replace is a delete followed by a create, and on this family the delete is the\n * thing above. Read the ⛔ on `matches` before adding anything to it.\n *\n * ⛔ NOT ONE CREATE PARAMETER COMES BACK ON READ, SO `matches` COMPARES NOTHING. MEASURED against\n * the live cluster: `GET /nodes/node-b/disks/zfs/rpool` answers exactly\n * `{action, children, errors, leaf, name, scan, state, status}` — no `ashift`, no `compression`,\n * no `raidlevel`, no `devices`, no `add_storage`. Every create parameter is write-only. There is\n * therefore no field a declaration and a live pool can both be asked about, and a `matches` that\n * invented one would plan a replace — i.e. a `zpool destroy` — over a difference it could never\n * verify in the first place.\n *\n * ⚠️ AND WHAT THE INDEX RETURNS IS LIVE TELEMETRY, NOT CONFIGURATION. `GET /nodes/node-b/disks/zfs`\n * gives `alloc`, `free`, `frag`, `dedup`, `size`, `health`. MEASURED: two reads of node-b seconds\n * apart returned `alloc` 14474944512 then 14472740864 — it moves on its own, with no declaration\n * anywhere near it. `scan` in the detail read moves the same way (it carries the last scrub), and\n * `status`/`action` appear and vanish as ZFS feature flags and faults come and go. None of them\n * is compared, and the three churning strings are kept out of the state store entirely rather\n * than rewriting this resource's row every time somebody writes a file.\n *\n * ⚠️ TWO HANDLERS ARE OVERRIDDEN AND THE OTHER THREE COME FROM THE FACTORY — see the ★ above\n * `handlers`. `delete` is the ⛔ above. `reconcile` is because the create is a FORKED WORKER, so\n * the factory's immediate read-back would report a successful create as a failure; the measured\n * detail is in the ⛔ on `createPool` in zfs-pool-create.ts.\n *\n * ⚠️ PRIVILEGES, FROM THE SCHEMA. Read and diff need `Sys.Audit` on `/` — both GETs check it.\n * Reconcile needs `Sys.Modify` on `/` for the POST. `Datastore.Allocate` on `/storage` is NOT\n * needed, because `add_storage` is not offered — see the ⛔ on `ZfsPoolProps`. `delete` needs\n * nothing at all, since it calls nothing.\n */\nimport { Resource } from 'alchemy';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport {\n type PveRequirements,\n type PveSpec,\n type WithTarget,\n pveHandlers,\n pveOperations,\n} from './resource.ts';\nimport { text } from './values.ts';\nimport { ZFS_POOL_ENDPOINT, createForm, createPool, destroyPool } from './zfs-pool-write.ts';\n\n/** PVE's layouts. ⚠️ Each has a minimum disk count PVE enforces, and `raid10` needs an even one. */\nexport type ZfsRaidLevel =\n | 'single'\n | 'mirror'\n | 'raid10'\n | 'raidz'\n | 'raidz2'\n | 'raidz3'\n | 'draid'\n | 'draid2'\n | 'draid3';\n\nexport type ZfsCompression = 'on' | 'off' | 'gzip' | 'lz4' | 'lzjb' | 'zle' | 'zstd';\n\n/**\n * ⛔ THERE IS NO `add_storage`, `cleanup-config` OR `cleanup-disks` PROP, AND THAT IS NOT AN\n * OVERSIGHT. The last two belong to the DELETE this resource refuses to make. `add_storage=1`\n * would have the POST build a `Proxmox.Storage`-shaped object this resource does not own, cannot\n * see on any later read, and cannot remove — invisible to every diff forever, and a collision\n * waiting for the day somebody also declares the storage properly. Declare a `Proxmox.Storage`\n * with `type: 'zfspool'` and `locator: { pool: <this pool's name> }` instead, reading the `name`\n * attribute below so Alchemy orders the storage after the pool.\n */\nexport interface ZfsPoolProps extends WithTarget {\n /**\n * ⛔ A ZPOOL IS NODE-LOCAL AND THIS IS HALF ITS IDENTITY. Changing it moves nothing: `path` then\n * points at a different node, the read answers absent, and reconcile tries to CREATE the pool\n * over there — on whatever `/dev/...` happens to answer to those names on that machine.\n * Declare a separate resource per node instead.\n */\n node: string;\n /** The zpool name, and PVE's key for it on this node. ⚠️ Changing it creates a SECOND pool. */\n name: string;\n /**\n * The block devices to build it on.\n *\n * ⛔ NAME THEM BY `/dev/disk/by-id/...`, NOT `/dev/sdb`. Kernel names are not stable across\n * reboots; a by-id link is. MEASURED in the node's source: PVE resolves what it is handed to a\n * by-id link only AFTER it has decided the device is free, so a stale `/dev/sdb` in a stack\n * file is checked against, and then wiped, whichever disk currently answers to that name.\n * ⚠️ ORDER IS MEANING FOR `raid10` AND IS PRESERVED — PVE walks the list two at a time and makes\n * each pair a mirror. This is why the form uses a plain join and NOT `csv()` from values.ts:\n * `csv` sorts, and sorting this list silently re-pairs the mirrors into a different pool.\n * ⚠️ PVE's only guard is `assert_disk_unused`, i.e. its own `disk_is_used` over the same `used`\n * column the Disks view shows. A blank disk it considers free is wiped with no confirmation.\n */\n devices: readonly string[];\n raidlevel: ZfsRaidLevel;\n /**\n * Sector size exponent, 9–16, PVE default 12.\n *\n * ⛔ WRITE-ONLY AND IMMUTABLE FOR THE LIFE OF THE POOL. It is consumed by `zpool create -o\n * ashift=` and never returned by any read on this family, so changing it later plans as `noop`\n * and changes nothing. A different ashift means destroying and rebuilding the pool, by hand,\n * with the data moved off first.\n */\n ashift?: number;\n /**\n * ⚠️ Write-only in the same way as `ashift`: applied by `zfs set compression=` at create time and\n * never read back, so a later change plans `noop`. Change it with `zfs set` on the node.\n */\n compression?: ZfsCompression;\n /**\n * `data=<integer>,spares=<integer>`. ⚠️ THE WIRE NAME IS KEPT, HYPHEN AND ALL, for the reason\n * `storage.ts` keeps `prune-backups`: a second spelling is a second name for one thing.\n * ⚠️ PVE refuses it outright unless `raidlevel` is one of the `draid*` levels.\n */\n 'draid-config'?: string;\n}\n\n/**\n * ⚠️ EVERYTHING HERE IS REPORTED, NOTHING HERE IS COMPARED — see the ⛔ on `matches`. These exist\n * so a plan and the state store can say what the pool actually is, on a family where the\n * declaration and the live object share no comparable field at all.\n */\nexport interface ZfsPoolAttributes {\n /** ★ The value a `Proxmox.Storage` should read to order itself after this pool. */\n name: string;\n node: string;\n /** `ONLINE` | `DEGRADED` | `FAULTED` | … Health, not configuration. */\n state: string;\n /** ZFS's own error summary, e.g. `No known data errors`. */\n errors: string;\n /**\n * The leaf devices ZFS reports, in vdev order, comma-joined.\n *\n * ⚠️ THIS IS WHAT ZFS RESOLVED, NOT WHAT WAS DECLARED, and the two normally differ: PVE rewrites\n * a `/dev/sdb` into a by-id link before creating, and ZFS reports partition paths (`…-part3`)\n * for a pool built on partitions — MEASURED, that is exactly what node-b's `rpool` reports. Never\n * compare it with `props.devices`.\n */\n devices: string;\n}\n\nexport interface ProxmoxZfsPool extends Resource<\n 'Proxmox.ZfsPool',\n ZfsPoolProps,\n ZfsPoolAttributes,\n never,\n PveRequirements\n> {}\n\n/** ★ `retain` by default — a pool holding datasets cannot be rebuilt. See the ★ in resource.ts. */\nexport const ProxmoxZfsPool = Resource<ProxmoxZfsPool>('Proxmox.ZfsPool', {\n defaultRemovalPolicy: 'retain',\n});\n\n/**\n * The device paths at the bottom of PVE's vdev tree, in the order ZFS reports them.\n *\n * ⚠️ \"NO CHILDREN\" IS THE LEAF TEST, AND IT IS NOT A GUESS — MEASURED in the node's `preparetree`,\n * which sets `leaf` to 0 when there are children and 1 otherwise. Reading the flag would work\n * equally well; recursing on `children` needs no second field to be present and is what makes\n * nested sections (mirrors inside a raid10, `spares`, `cache`) flatten correctly.\n */\nconst leafDevices = (children: unknown): string[] =>\n Array.isArray(children)\n ? children.flatMap((entry: unknown) => {\n const vdev = entry as { children?: unknown; name?: unknown };\n const nested = leafDevices(vdev.children);\n return nested.length > 0 ? nested : [text(vdev.name)].filter((name) => name !== '');\n })\n : [];\n\nconst spec: PveSpec<ZfsPoolProps, ZfsPoolAttributes> = {\n /**\n * ⚠️ `scan`, `status` AND `action` ARE DELIBERATELY NOT ATTRIBUTES. All three are advisory\n * strings ZFS rewrites on its own — `scan` on every scrub, the other two as feature flags and\n * faults come and go — so keeping them would rewrite this resource's state row for reasons no\n * declaration caused. Same reasoning as `digest` in storage.ts.\n */\n attributes: (live, props) => {\n // ⚠️ A detail answer with no `name` is not a pool. `name` is non-optional in PVE's schema, so\n // this is a shape check rather than a default — and \"absent\" is the honest reading of a\n // reply that does not describe the object that was asked for.\n if (text(live['name']) === '') return undefined;\n return {\n devices: leafDevices(live['children']).join(','),\n errors: text(live['errors'], 'unknown'),\n name: props.name,\n node: props.node,\n state: text(live['state'], 'unknown'),\n };\n },\n collection: (props) => `nodes/${props.node}/disks/zfs`,\n createForm,\n endpoint: ZFS_POOL_ENDPOINT,\n /**\n * ⛔ IT ALWAYS ANSWERS TRUE, AND THAT IS THE POINT OF THIS FILE. A pool that is there under the\n * declared name on the declared node IS the declaration, because there is nothing else the two\n * can be compared on: every create parameter is write-only (see the ⛔ in the header) and\n * everything the read does return is telemetry that moves by itself. So declaring what is live\n * plans as `noop` — the only honest answer available, and the one the live cluster needs:\n * `rpool` exists on node-b, node-c and node-d with nothing about its construction readable.\n * ⛔ ANYTHING ADDED HERE IS A REPLACE, NOT AN UPDATE. `updateForm` is omitted because PVE has\n * no PUT, so `resource.ts` turns a false into `{action:'replace'}` — delete then create — on\n * a family whose delete is `zpool destroy`. A comparison added here would be one plan away\n * from destroying a pool over a field it cannot even read back. If a future PVE grows a PUT,\n * add `updateForm` FIRST and only then consider comparing what that PUT accepts.\n */\n matches: () => true,\n path: (props) => `nodes/${props.node}/disks/zfs/${props.name}`,\n};\n\nconst ops = pveOperations(spec);\n\n/**\n * ★ THREE HANDLERS COME FROM THE FACTORY AND TWO DO NOT, SO THE TWO ARE THE ONLY ONES WRITTEN OUT.\n * `list`, `read` and `diff` are the factory's, unchanged — the spread is what proves it, rather\n * than three re-typed delegations nobody rereads. `reconcile` and `delete` are the two the header\n * explains, and they are the only places this family departs from every other resource here.\n * This is the acl.ts precedent, for a different reason: there the factory's delete hits an\n * endpoint PVE does not implement, here it hits one that works and destroys the data.\n */\nconst handlers = {\n ...pveHandlers(spec),\n delete: Effect.fn(function* ({ olds }: { olds: ZfsPoolProps }) {\n yield* destroyPool(olds, spec.path(olds));\n }),\n reconcile: ({ news }: { news: ZfsPoolProps }) =>\n createPool(news, spec.collection(news), spec.path(news), ops.read),\n};\n\nexport const ProxmoxZfsPoolProvider = () =>\n Provider.effect(ProxmoxZfsPool, Effect.succeed(ProxmoxZfsPool.Provider.of(handlers)));\n",
|
|
118
|
+
"/**\n * The create lane for `Proxmox.ZfsPool`: the form PVE wants, and the wait for the worker it forks.\n *\n * ★ SPLIT OUT OF zfs-pool.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a real one\n * rather than a convenient line number. This file answers \"how does a declaration become a pool\n * on disks, and how do we know it landed\". zfs-pool.ts answers \"what is a zpool, what can be\n * said about a live one, and when has it changed\" — and on this family the answer to the last of\n * those is \"never\", which is why the two halves have so little to say to each other.\n *\n * ⚠️ THE `import type` BACK TO zfs-pool.ts IS A CYCLE ON PAPER ONLY — it is type-only, so it is\n * erased before anything runs, and the resource's public shape stays in the file that declares\n * the resource rather than being moved somewhere odd to dodge the arrow. Same as\n * metric-server-form.ts.\n *\n * ⚠️ `createPool` TAKES THE READ AS A PARAMETER, GENERIC IN ITS ERROR AND REQUIREMENT CHANNELS,\n * rather than importing `pveOperations` and rebuilding the spec here. Two `pveOperations` over\n * one spec would be two closures nobody can prove are the same; passing the one the resource\n * already built means this file cannot accidentally read the cluster differently from `diff`.\n */\nimport * as Effect from 'effect/Effect';\nimport { pve } from './client.ts';\nimport type { EndpointPair } from './resource-spec.ts';\nimport { text } from './values.ts';\nimport type { ZfsPoolAttributes, ZfsPoolProps } from './zfs-pool.ts';\n\n/**\n * The POST body. Every field in it is write-only — nothing here is ever read back.\n *\n * ⚠️ `node` IS NOT IN THE BODY: it is already the `nodes/{node}` segment of the URL being POSTed\n * to, and a second copy can only disagree with it. Same reasoning as `id` in metric-server.ts.\n * ⚠️ `devices` IS JOINED IN DECLARED ORDER AND NOT PUT THROUGH `csv()` FROM values.ts. `csv` sorts,\n * and PVE walks this list two at a time to pair `raid10` mirrors — MEASURED in the node's own\n * `PVE::API2::Disks::ZFS` — so sorting it builds a different pool from the one declared. The\n * list is never compared against anything, so it needs no normalisation, only faithful order.\n * ⚠️ AN OMITTED OPTIONAL IS NOT SENT. PVE's own defaults (ashift 12, compression on) are then\n * applied by the node. Sending a guessed default would be indistinguishable here — the values\n * are unreadable afterwards either way — but it would put a number in the request that no\n * declaration asked for, and this is the one call that writes to physical disks.\n */\n/**\n * The vendor rules the create form is checked against at plan time.\n *\n * ⛔ HERE RATHER THAN INLINE IN zfs-pool.ts BECAUSE THAT FILE IS ALREADY OVER THE 250-LINE CAP,\n * and a `pve:POST …` literal has to live in a non-test source file for the generator to table\n * it at all. The spec imports it on the line it already imports `createForm` from.\n * ⚠️ NO UPDATE KEY: PVE registers no PUT under `/nodes/{node}/disks/zfs`, which is exactly why\n * the spec declares no `updateForm` and a changed prop is a REPLACE.\n */\nexport const ZFS_POOL_ENDPOINT: EndpointPair = { create: 'pve:POST /nodes/{node}/disks/zfs' };\n\nexport const createForm = (props: ZfsPoolProps): Record<string, string> => ({\n devices: props.devices.join(','),\n name: props.name,\n raidlevel: props.raidlevel,\n ...(props.ashift === undefined ? {} : { ashift: String(props.ashift) }),\n ...(props.compression === undefined ? {} : { compression: props.compression }),\n ...(props['draid-config'] === undefined ? {} : { 'draid-config': props['draid-config'] }),\n});\n\n/** 30 reads, 2s apart. A create that has not landed in a minute has gone wrong, not gone slow. */\nconst SETTLE_ATTEMPTS = 30;\n\n/**\n * Create the pool if it is absent, and otherwise leave it completely alone.\n *\n * ⛔ THE POST ONLY FORKS A WORKER. MEASURED in `/usr/share/perl5/PVE/API2/Disks/ZFS.pm` on node-b: the\n * create handler ends in `$rpcenv->fork_worker('zfscreate', ...)` and its HTTP answer is a UPID\n * returned the instant the worker is forked — before `zpool create` has run, let alone finished.\n * A read-back taken immediately, as `pveOperations.reconcile` takes it, finds nothing and would\n * report a successful create as the failure \"the write returned no error but the object is still\n * absent\". THAT is why this function exists instead of the factory's reconcile.\n *\n * ⚠️ THERE IS NO UPDATE BRANCH BECAUSE PVE HAS NO PUT HERE. An existing pool is returned exactly as\n * read, unwritten: the only write this resource ever makes is the create of a pool that is not\n * there. Do not add a \"repair\" write; there is no field it could set.\n *\n * ⛔ AND IF THE READ IS WRONG, THE POST IS STILL SAFE — measured, not hoped for. `read` folds a 403\n * or an unreachable node into \"absent\", so a too-narrow lease would send this down the create\n * path over a live pool. PVE's handler calls `get_pool_data()` before it forks anything and dies\n * with \"pool '<name>' already exists on node '<node>'\", and `assert_disk_unused` refuses every\n * device that pool is holding. A create aimed at a pool that is really there fails loudly rather\n * than wiping it.\n *\n * ⚠️ SHORT POLLS WITH A HARD CAP, NOT ONE LONG WAIT. `read` cannot tell \"not yet\" from \"forbidden\"\n * — both are `undefined` — so the loop gives up with the UPID rather than retrying forever, and\n * the UPID is the only thing that leads to the worker's real error.\n */\nexport const createPool = <E, R>(\n props: ZfsPoolProps,\n collection: string,\n path: string,\n read: (props: ZfsPoolProps) => Effect.Effect<ZfsPoolAttributes | undefined, E, R>,\n) =>\n Effect.gen(function* () {\n const existing = yield* read(props);\n if (existing !== undefined) return existing;\n\n const upid = yield* pve<string>(\n props.target,\n 'provision',\n 'POST',\n collection,\n createForm(props),\n );\n\n const settle = (attempts: number): Effect.Effect<ZfsPoolAttributes | undefined, E, R> =>\n Effect.gen(function* () {\n const live = yield* read(props);\n if (live !== undefined || attempts <= 0) return live;\n yield* Effect.sleep('2 seconds');\n return yield* settle(attempts - 1);\n });\n\n const after = yield* settle(SETTLE_ATTEMPTS);\n if (after === undefined) {\n return yield* Effect.die(\n new Error(\n `${path}: POST returned ${text(upid, '(no UPID)')} but the pool has not appeared. That ` +\n 'POST only forks a `zfscreate` worker, so the task holds the real error -- read it ' +\n `with \\`pvesh get /nodes/${props.node}/tasks/<upid>/log\\`. NOTHING WAS RETRIED AND ` +\n 'NOTHING WAS CLEANED UP: if the worker reached `zpool create`, the declared devices ' +\n 'have already been written to.',\n ),\n );\n }\n return after;\n });\n\n/**\n * ⛔ `DELETE /nodes/{node}/disks/zfs/{name}` RUNS `zpool destroy` AND THE DATASETS GO WITH IT.\n * It is implemented rather than stubbed, because a `delete` that silently does nothing lies to\n * whoever reads the plan. What keeps a deleted line from destroying a pool is the resource's\n * `defaultRemovalPolicy: 'retain'` above — an orphaned pool is forgotten, not destroyed, and a\n * caller who means it says `.pipe(RemovalPolicy.destroy())`.\n *\n * ⚠️ `cleanup-config` IS NOT SENT. It would additionally remove the PVE storage entry pointing\n * at this pool, which is a different object with its own resource (`Proxmox.Storage`); one\n * resource reaching over to delete another's is how a stack ends up with state describing\n * something that is gone.\n */\nexport const destroyPool = (olds: ZfsPoolProps, path: string) =>\n pve(olds.target, 'provision', 'DELETE', path);\n"
|
|
103
119
|
],
|
|
104
|
-
"mappings": ";AA+BA;AACA,uBAAS;AACT;AACA;;;ACpBA;AACA;AACA;;;ACcA;AACA;AACA;AACA;AACA;AACA;AA6FA,IAAM,cAAc,CAAC,KAAqB,SACxC,IAAI,OAAO,WAAW,IAAI,SAAS,WAAW;AA0BzC,IAAM,OAAO,CAAC,QAAmB,MAAe,MAAsB,QAAQ,QAC5E,WAAI,UAAU,GAAG;AAAA,EACtB,MAAM,OAAO,GAAG,OAAO,eAAe;AAAA,EAGtC,MAAM,SAAS,CAAC,SAAgB,WAC9B,IAAI,MAAM,mBAAmB,WAAW,YAAW,IAAI,gBAAgB,YAAW,QAAQ;AAAA,EAC5F,MAAM,UACJ,YAAY,KAAK,YAAY,KAAK,YAAY,KAAK,MAAM,KAAK;AAAA,EAChE,MAAM,SAAS,QAAQ,WAAW,SAAS,IAAI,QAAQ,MAAM,UAAU,MAAM,IAAI;AAAA,EACjF,MAAM,OAAO,WAAW,YAAY,QAAQ,QAAQ,QAAQ,EAAE,IAAI;AAAA,EAClE,MAAM,UAAkC,EAAE,mBAAmB,OAAO;AAAA,EACpE,MAAM,YAAY,YAAY,KAAK,WAAW;AAAA,EAC9C,IAAI,cAAc;AAAA,IAAI,QAAQ,uBAAuB;AAAA,EACrD,MAAM,QAAQ,YAAY,KAAK,OAAO;AAAA,EACtC,IAAI,UAAU;AAAA,IAAI,QAAQ,mBAAmB;AAAA,EAC7C,IAAI,WAAW,aAAa,EAAE,SAAS,aAAa;AAAA,IAClD,OAAO,OAAc,YAAK,OAAO,GAAG,oDAAoD,CAAC;AAAA,EAC3F;AAAA,EAEA,MAAM,SAAS,OAAkB;AAAA,EAGjC,MAAM,WAAW,OAAe;AAAA,EAChC,MAAM,WAAkB,iBACtB,OAAc,qBAA8B,2BAAW,GACvD,OAAO,CAAC,EACV;AAAA,EACA,MAAM,UAA4B,sBAAI,GAAG,WAAW,MAAM,EAAE,KACxC,6BAAW,OAAO,CACtC;AAAA,EACA,MAAM,WAAW,OAAO,QAAQ,OAAO,EAAE,KAChC,eAAQ,CAAC,aACP,WAAI,SAAS,MAAM,CAAC,WAAU,EAAE,QAAQ,SAAS,QAAQ,YAAK,EAAE,CACzE,GAEO,eAAQ,YAAY,GACpB,sBAAuB,8BAAsB,CAAC,GAAG,UAAU,eAAe,CAAC,CACpF;AAAA,EAKA,MAAM,OAAoD,KAAK,UAAU,MAAM,OAAO;AAAA,EACtF,QAAQ,QAAQ,SAAS,QACvB,WAAW,YACP,WACO,sBAAe,UAA0B,6BAAa,IAAI,GACrE,KAEO,gBAAS,CAAC,UAAU,OAAO,GAAG,MAAM,OAAO,CAAC,CACrD;AAAA,EAEA,IAAI;AAAA,EAOJ,IAAI;AAAA,IACF,SAAS,KAAK,MAAM,IAAI;AAAA,IACxB,MAAM;AAAA,IACN,SAAS;AAAA;AAAA,EAEX,IAAI,SAAS,OAAO,UAAU,KAAK;AAAA,IACjC,MAAM,SAAS,QAAQ;AAAA,IACvB,MAAM,SAAS,MAAM,QAAQ,MAAM,IAC/B,OAAO,IAAI,MAAM,EAAE,KAAK,IAAI,IAC5B;AAAA,IACJ,OAAO,OAAc,YAAK,OAAO,QAAQ,UAAU,mBAAmB,CAAC;AAAA,EACzE;AAAA,EACA,MAAM,UAAU,QAAQ,MAAM;AAAA,EAC9B,MAAM,SAAS,QAAQ,MAAM;AAAA,EAC7B,IAAI,OAAO,YAAY,YAAY,OAAO,WAAW,UAAU;AAAA,IAE7D,OAAO,OAAc,YACnB,IAAI,MACF,GAAG,OAAO,eAAe,iEACvB,kCAAkC,OAAO,gDACzC,kFACJ,CACF;AAAA,EACF;AAAA,EACA,MAAM,QAAQ,QAAQ;AAAA,EACtB,MAAM,eAAe,OAAO,UAAU,WAAW,QAAQ;AAAA,EACzD,OAAO,EAAE,cAAc,QAAQ,QAAQ;AAAA,CACxC;AAQI,IAAM,gBAAgB,CAAC,YAA2B,WACvD,OAAO,WAAW,QACd,eAAe,WAAW,WAAW,WAAW,WAChD,eAAe,WAAW,WAAW,WAAW;;;AChNtD;AACA;AACA;AACA;AAcA,IAAM,wBAAwB;AAmB9B,IAAM,QAAQ,CAAC,QAAmB,UAA6B;AAAA,EAC7D,OAAO,OAAO;AAAA,EACd;AAAA,EACA,QAAQ,OAAO;AACjB;AAWO,IAAM,aAAa,CAAC,SACpB,eAAU,IAAI,KAAK,KAAK,MAAM,eAAe,wBACrC,iBAAQ,KAAK,MAAM,eAAe,qBAAqB,IACvD;AAcR,IAAM,aAAa,CAAO,YACzB,eAAS,SAAS,EAAE,UAAU,IAAI,mBAAmB,UAAU,WAAW,CAAC;AAEnF,IAAM,eAAe,CAAC,QACpB,IAAI,WAAW,QACX,EAAE,KAAK,iCAAiC,OAAO,IAAI,OAAO,QAAQ,MAAM,IACxE,EAAE,SAAS,CAAC,GAAG,OAAO,IAAI,OAAO,QAAQ,MAAM;AAErD,IAAM,SAAgB,gBAAQ,WAAW,CAAC,QAAQ,KAAK,aAAa,GAAG,GAAG,IAAI,IAAI,CAAC,CAAC;AAS7E,IAAM,SAAS,CAAC,QAAmB,SAAwB,UAAI,QAAQ,MAAM,QAAQ,IAAI,CAAC;;;AC1HjG;AACA;AACA;AACA;AAKO,IAAM,eAAe;AAGrB,IAAM,aAAa,CAAC,WAAmB,WAAW,UAAU,OAAO,YAAY;AAGtF,IAAM,kBAAkB,IAAI;AAKrB,IAAM,iBAAiB,CAAC,QAAmB,WAAmB;AAAA,EACnE,gBAAgB,IAAI,OAAO,OAAO,MAAM;AAAA;AAInC,IAAM,iBAAiB,CAAC,WAAyC;AAAA,EACtE,MAAM,YAAY,gBAAgB,IAAI,OAAO,KAAK;AAAA,EAClD,IAAI,cAAc,aAAa,CAAC,OAAO,QAAQ,SAAS,SAAS;AAAA,IAAG,OAAO,OAAO;AAAA,EAClF,OAAO,CAAC,WAAW,GAAG,OAAO,QAAQ,OAAO,CAAC,WAAW,WAAW,SAAS,CAAC;AAAA;AAG/E,IAAM,YAAY,CAAC,UAAuC;AAAA,EACxD,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAAA,IAC/C,IAAI,UAAU,SAAS,OAAO,MAAM,SAAS;AAAA,MAAU,OAAO,MAAM;AAAA,IACpE,IAAI,WAAW;AAAA,MAAO,OAAO,UAAW,MAA6B,KAAK;AAAA,EAC5E;AAAA,EACA;AAAA;AAGF,IAAM,iBAAiB,CAAC,UAA4B;AAAA,EAClD,MAAM,OAAO,UAAU,KAAK;AAAA,EAC5B,IAAI,SAAS,gBAAgB,SAAS,eAAe,SAAS,2BAA2B;AAAA,IACvF,OAAO;AAAA,EACT;AAAA,EACA,OAAO,iBAAiB,SAAS,qBAAqB,KAAK,MAAM,OAAO;AAAA;AAkB1E,IAAM,iBAAiB,IAAI,IAAI,CAAC,qBAAqB,gBAAgB,aAAa,WAAW,CAAC;AAE9F,IAAM,oBAAoB,CAAC,SACzB,mFAAmF,KAAK,IAAI;AAE9F,IAAM,iBAAiB,CAAC,UAA4B;AAAA,EAClD,MAAM,OAAO,UAAU,KAAK;AAAA,EAC5B,OAAO,SAAS,cAAc,eAAe,IAAI,IAAI,KAAK,kBAAkB,IAAI;AAAA;AAS3E,IAAM,qBAAqB,CAAC,UAA4B;AAAA,EAC7D,IAAI,CAAiB,kCAAkB,KAAK;AAAA,IAAG,OAAO;AAAA,EACtD,MAAM,MAAM,MAAM,OAAO;AAAA,EACzB,OAAO,QAAQ,oBAAoB,QAAQ,qBAAqB,QAAQ;AAAA;AAInE,IAAM,qBAAqB,CAAC,UAA4B;AAAA,EAC7D,IAAI,CAAiB,kCAAkB,KAAK;AAAA,IAAG,OAAO;AAAA,EACtD,QAAQ,WAAW;AAAA,EACnB,IAAI,OAAO,SAAS,qBAAqB,OAAO,SAAS;AAAA,IAAe,OAAO;AAAA,EAC/E,IAAI,OAAO,SAAS,kBAAkB;AAAA,IACpC,IAAI,eAAe,OAAO,KAAK;AAAA,MAAG,OAAO;AAAA,IACzC,OAAO,eAAe,OAAO,KAAK;AAAA,EACpC;AAAA,EACA,OAAO;AAAA;AAGF,IAAM,oBAAoB,CAAC,UAA2B;AAAA,EAC3D,IAAoB,kCAAkB,KAAK;AAAA,IAAG,OAAO,MAAM;AAAA,EAC3D,OAAO,OAAO,KAAK;AAAA;AAoBd,IAAM,mBAAmB,CAC9B,QACA,QACA,MACA,UAWO,YAAI,UAAU,GAAG;AAAA,EActB,MAAM,SAAS,OAAkB;AAAA,EACjC,MAAM,UAAU,WAAW;AAAA,EAC3B,MAAM,WAA4B,CAAC;AAAA,EACnC,WAAW,UAAU,eAAe,MAAM,GAAG;AAAA,IAC3C,MAAM,UAAU,OAAO,OAAO,QAAQ,MAAM,WAAW,MAAM,CAAC,CAAC,EAAE,KAAY,cAAM;AAAA,IACnF,IAAW,iBAAU,OAAO,GAAG;AAAA,MAC7B,eAAe,QAAQ,MAAM;AAAA,MAC7B,OAAO,QAAQ;AAAA,IACjB;AAAA,IACA,MAAM,MAAM,kBAAkB,QAAQ,OAAO;AAAA,IAC7C,SAAS,KAAK,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC7B,MAAM,WAAW,UACb,mBAAmB,QAAQ,OAAO,IAClC,mBAAmB,QAAQ,OAAO;AAAA,IACtC,IAAI,CAAC,UAAU;AAAA,MACb,OAAO,OAAc,aAAK,EAAE,UAAU,QAAQ,MAAM,KAAK,UAAmB,CAAC;AAAA,IAC/E;AAAA,EACF;AAAA,EACA,OAAO,OAAc,aAAK,EAAE,UAAU,QAAQ,MAAM,KAAK,UAAmB,CAAC;AAAA,CAC9E;;;AHlJI,MAAM,iBAAiB,MAAM;AAAA,EAEvB;AAAA,EACA;AAAA,EACA;AAAA,EAHX,WAAW,CACA,QACA,QACA,MACT,QACA;AAAA,IACA,MAAM,OAAO,UAAU,WAAW,OAAO,MAAM,MAAM,QAAQ;AAAA,IALpD;AAAA,IACA;AAAA,IACA;AAAA,IAIT,KAAK,OAAO;AAAA;AAEhB;AAEA,IAAM,mBAAmB,CACvB,QACA,MACA,aAEA,IAAI,SACF,GACA,QACA,MACA,uBAAuB,SAAS,IAAI,CAAC,MAAM,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,GAChF;AAgBF,IAAM,SAAS,CAAC,SAA0B;AAAA,EACxC,MAAM,SAAS,IAAI;AAAA,EACnB,YAAY,KAAK,UAAU,OAAO,QAAQ,IAAI,GAAG;AAAA,IAC/C,IAAI,OAAO,UAAU;AAAA,MAAU,OAAO,OAAO,KAAK,KAAK;AAAA,IAClD;AAAA,iBAAW,QAAQ;AAAA,QAAO,OAAO,OAAO,KAAK,IAAI;AAAA,EACxD;AAAA,EACA,OAAO,OAAO,SAAS;AAAA;AAGzB,IAAM,eAAe,CACnB,QACA,SACA,MACA,YACA,QACA,SACG;AAAA,EACH,MAAM,MAAM,GAAG,WAAW;AAAA,EAsB1B,OAAyB,wBAAK,MAAM,EAAE,GAAG,EAAE,KACvB,8BAAW,EAAE,eAAe,cAAc,YAAY,MAAM,EAAE,CAAC,GACjF,SAAS,YACL,CAAC,SAAS,OACQ,4BAAS,OAAO,IAAI,GAAG,mCAAmC,CAClF;AAAA;AAGF,IAAM,aAAa,CACjB,QACA,YACA,QACA,MACA,MACA,YAEO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,SAAS,OAAkB;AAAA,EACjC,MAAM,UAAU,aAAa,QAAQ,SAAS,MAAM,YAAY,QAAQ,IAAI;AAAA,EAC5E,OAAO,OAAO,OACX,QAAQ,OAAO,EACf,KAAY,iBAAS,CAAC,UAAU,IAAI,SAAS,GAAG,QAAQ,MAAM,OAAO,KAAK,CAAC,CAAC,CAAC;AAAA,CACjF;AAEH,IAAM,eAAe,CACnB,UAKA,QACA,SAEO,YAAI,UAAU,GAAG;AAAA,EAOtB,IAAI,SAAS,SAAS,OAAO,SAAS,UAAU,KAAK;AAAA,IAGnD,MAAM,SAAS,OAAO,SAAS,KAAK,KAAY,sBAAc,MAAM,EAAE,CAAC;AAAA,IACvE,OAAO,OAAc,aAAK,IAAI,SAAS,SAAS,QAAQ,QAAQ,MAAM,OAAO,MAAM,GAAG,GAAG,CAAC,CAAC;AAAA,EAC7F;AAAA,EACA,MAAM,OAAQ,OAAO,SAAS,KAAK,KAC1B,iBAAS,CAAC,UAAU,IAAI,SAAS,GAAG,QAAQ,MAAM,OAAO,KAAK,CAAC,CAAC,CACzE;AAAA,EACA,OAAO,KAAK;AAAA,CACb;AAGI,IAAM,UAAU,CACrB,QACA,YACA,QACA,MACA,SAEO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WACJ,OAAO,WAAW,QACd,OAAO,iBAAiB,QAAQ,QAAQ,MAAM,CAAC,YAC7C,aAAa,QAAQ,SAAS,MAAM,YAAY,QAAQ,IAAI,CAC9D,EAAE,KACO,iBAAS,CAAC,YACf,QAAQ,QAAQ,YACZ,iBAAiB,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,IAC/D,IAAI,SAAS,GAAG,QAAQ,MAAM,OAAO,OAAO,CAAC,CACnD,CACF,IACA,OAAO,WAAW,QAAQ,YAAY,QAAQ,MAAM,MAAM,OAAO,GAAG;AAAA,EAC1E,OAAO,OAAO,aAAgB,UAAU,QAAQ,IAAI;AAAA,CACrD;AAQI,IAAM,kBAAkB,CAC7B,QACA,YACA,QACA,SAEO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WAAW,OAAO,iBAAiB,QAAQ,QAAQ,MAAM,CAAC,YAC9D,aAAa,QAAQ,SAAS,MAAM,YAAY,MAAM,CACxD,EAAE,KACO,iBAAS,CAAC,YACf,QAAQ,QAAQ,YACZ,iBAAiB,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,IAC/D,IAAI,SAAS,GAAG,QAAQ,MAAM,OAAO,OAAO,CAAC,CACnD,CACF;AAAA,EACA,IAAI,SAAS,SAAS,OAAO,SAAS,UAAU,KAAK;AAAA,IACnD,MAAM,SAAS,OAAO,SAAS,KAAK,KAAY,sBAAc,MAAM,EAAE,CAAC;AAAA,IACvE,OAAO,OAAc,aAAK,IAAI,SAAS,SAAS,QAAQ,QAAQ,MAAM,OAAO,MAAM,GAAG,GAAG,CAAC,CAAC;AAAA,EAC7F;AAAA,EACA,OAAQ,OAAO,SAAS,KAAK,KACpB,iBAAS,CAAC,UAAU,IAAI,SAAS,GAAG,QAAQ,MAAM,OAAO,KAAK,CAAC,CAAC,CACzE;AAAA,CACD;AAoBI,IAAM,MAAM,CACjB,QACA,MACA,QACA,MACA,SAEO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,aAAa,OAAO,OAAO,QAAQ,IAAI;AAAA,EAC7C,OAAO,OAAO,QAAW,QAAQ,YAAY,QAAQ,MAAM,IAAI;AAAA,CAChE;;;AInOH;AAEA;;;ACFA;;;ACqDA,IAAM,WAAW,CAAC,UAA0B,CAAC,GAAG,KAAK,EAAE;AAQvD,IAAM,aAAa,CAAC,MAAc,OAAe,SAAoC;AAAA,EACnF,MAAM,MAAgB,CAAC;AAAA,EACvB,IAAI,KAAK,cAAc,aAAa,SAAS,KAAK,IAAI,KAAK,WAAW;AAAA,IACpE,IAAI,KAAK,GAAG,iBAAiB,KAAK,sBAAsB;AAAA,EAC1D;AAAA,EACA,IAAI,KAAK,cAAc,aAAa,SAAS,KAAK,IAAI,KAAK,WAAW;AAAA,IACpE,IAAI,KAAK,GAAG,kBAAkB,KAAK,sBAAsB;AAAA,EAC3D;AAAA,EACA,IAAI,KAAK,SAAS,aAAa,CAAC,KAAK,KAAK,SAAS,KAAK,GAAG;AAAA,IACzD,IAAI,KAAK,GAAG,wBAAwB,KAAK,KAAK,KAAK,IAAI,GAAG;AAAA,EAC5D;AAAA,EACA,IAAI,KAAK,YAAY,aAAa,CAAC,IAAI,OAAO,KAAK,OAAO,EAAE,KAAK,KAAK,GAAG;AAAA,IACvE,IAAI,KAAK,GAAG,oBAAoB,KAAK,iBAAiB,KAAK,SAAS;AAAA,EACtE;AAAA,EAIA,MAAM,UAAU,OAAO,KAAK;AAAA,EAC5B,MAAM,UAAU,KAAK,YAAY,aAAa,KAAK,YAAY;AAAA,EAC/D,IAAI,WAAW,UAAU,MAAM,OAAO,SAAS,OAAO,GAAG;AAAA,IACvD,IAAI,KAAK,YAAY,aAAa,UAAU,KAAK,SAAS;AAAA,MACxD,IAAI,KAAK,GAAG,kBAAkB,KAAK,SAAS;AAAA,IAC9C;AAAA,IACA,IAAI,KAAK,YAAY,aAAa,UAAU,KAAK,SAAS;AAAA,MACxD,IAAI,KAAK,GAAG,iBAAiB,KAAK,SAAS;AAAA,IAC7C;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAUF,IAAM,aAAa,CACxB,OACA,MACA,UAA4B,CAAC,MACP;AAAA,EACtB,MAAM,MAAgB,CAAC;AAAA,EACvB,YAAY,MAAM,SAAS,OAAO,QAAQ,KAAK,GAAG;AAAA,IAChD,MAAM,QAAQ,KAAK;AAAA,IACnB,IAAI,UAAU,WAAW;AAAA,MACvB,IAAI,QAAQ,aAAa,QAAQ,KAAK,aAAa;AAAA,QAAM,IAAI,KAAK,GAAG,gBAAgB;AAAA,MACrF;AAAA,IACF;AAAA,IAEA,WAAW,QAAQ,OAAO,UAAU,WAAW,CAAC,KAAK,IAAI,OAAO;AAAA,MAC9D,IAAI,KAAK,GAAG,WAAW,MAAM,MAAM,IAAI,CAAC;AAAA,IAC1C;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAIF,IAAM,UAAU,CAAC,UAAuB,UAC7C,GAAG;AAAA,MAAkD,MAAM,KAAK;AAAA,KAAQ;AAAA,IACxE,uEACA;;;ACrHK,IAAM,yBAAwE;AAAA,EACnF,2BAA2B;AAAA,IACzB,SAAW,EAAC,QAAS,eAAc,UAAW,MAAK,MAAO,SAAQ;AAAA,EACpE;AAAA,EACA,0BAA0B;AAAA,IACxB,OAAS,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IAClD,QAAU,EAAC,QAAS,cAAa,UAAW,MAAK,MAAO,SAAQ;AAAA,EAClE;AAAA,EACA,0BAA0B;AAAA,IACxB,SAAW,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IAC5C,OAAS,EAAC,QAAS,aAAY,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC9D,QAAU,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IACvC,WAAa,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IAC9C,QAAU,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IACtD,MAAQ,EAAC,SAAU,yBAAwB,eAAgB,yBAAwB,MAAO,SAAQ;AAAA,IAClG,UAAY,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IAC7C,UAAY,EAAC,WAAY,IAAG,WAAY,GAAE,MAAO,SAAQ;AAAA,IACzD,QAAU,EAAC,QAAS,cAAa,WAAY,IAAG,UAAW,MAAK,MAAO,SAAQ;AAAA,EACjF;AAAA,EACA,uBAAuB;AAAA,IACrB,QAAU,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IACtD,MAAQ,EAAC,UAAW,MAAK,MAAO,SAAQ;AAAA,IACxC,OAAS,EAAC,QAAS,mBAAkB,UAAW,MAAK,MAAO,SAAQ;AAAA,IACpE,QAAU,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IACtD,OAAS,EAAC,QAAS,mBAAkB,MAAO,SAAQ;AAAA,EACtD;AAAA,EACA,oCAAoC,CAAC;AAAA,EACrC,kCAAkC;AAAA,IAChC,OAAS,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,EACpD;AAAA,EACA,kCAAkC;AAAA,IAChC,SAAW,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IAC5C,OAAS,EAAC,QAAS,aAAY,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC9D,QAAU,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IACvC,WAAa,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IAC9C,QAAU,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IACtD,MAAQ,EAAC,SAAU,yBAAwB,eAAgB,yBAAwB,MAAO,SAAQ;AAAA,IAClG,UAAY,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,EAC/C;AACF;;;ACvCO,IAAM,0BAAyE;AAAA,EACpF,4BAA4B;AAAA,IAC1B,SAAW,EAAC,SAAU,KAAI,SAAU,GAAE,MAAO,UAAS;AAAA,IACtD,SAAW,EAAC,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC3C,UAAY,EAAC,SAAU,KAAI,MAAO,CAAC,KAAI,KAAI,QAAO,OAAM,MAAM,GAAE,MAAO,SAAQ;AAAA,IAC/E,KAAO,EAAC,SAAU,+BAA8B,QAAS,wBAAuB,MAAO,SAAQ;AAAA,IAC/F,SAAW,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IACpD,UAAY,EAAC,QAAS,mBAAkB,MAAO,SAAQ;AAAA,IACvD,IAAM,EAAC,QAAS,gBAAe,MAAO,SAAQ;AAAA,IAC9C,QAAU,EAAC,SAAU,KAAI,SAAU,GAAE,SAAU,GAAE,MAAO,UAAS;AAAA,IACjE,UAAY,EAAC,SAAU,OAAM,SAAU,GAAE,MAAO,UAAS;AAAA,IACzD,kBAAoB,EAAC,SAAU,UAAS,MAAO,CAAC,UAAS,SAAS,GAAE,MAAO,SAAQ;AAAA,IACnF,QAAU,EAAC,QAAS,0BAAyB,MAAO,SAAQ;AAAA,IAC5D,MAAQ,EAAC,SAAU,YAAW,MAAO,CAAC,YAAW,WAAU,MAAM,GAAE,MAAO,SAAQ;AAAA,IAClF,MAAQ,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,IAC5C,kBAAkB,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IACnD,qBAAqB,EAAC,SAAU,QAAO,MAAO,CAAC,QAAO,mBAAkB,qBAAqB,GAAE,MAAO,SAAQ;AAAA,IAC9G,6BAA6B,EAAC,MAAO,CAAC,UAAS,QAAO,UAAU,GAAE,MAAO,SAAQ;AAAA,IACjF,aAAe,EAAC,QAAS,sBAAqB,MAAO,SAAQ;AAAA,IAC7D,iBAAiB,EAAC,SAAU,cAAa,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IACjF,UAAY,EAAC,QAAS,sBAAqB,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC1E,WAAa,EAAC,SAAU,qBAAoB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAC/F,UAAY,EAAC,SAAU,MAAK,SAAU,GAAE,MAAO,UAAS;AAAA,IACxD,SAAW,EAAC,QAAS,kBAAiB,MAAO,SAAQ;AAAA,IACrD,MAAQ,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,EACnD;AAAA,EACA,sCAAsC;AAAA,IACpC,MAAQ,EAAC,QAAS,YAAW,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC5D,SAAW,EAAC,QAAS,uBAAsB,MAAO,SAAQ;AAAA,IAC1D,MAAQ,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,8BAA6B,eAAgB,8BAA6B,UAAW,MAAK,MAAO,SAAQ;AAAA,EAC3J;AAAA,EACA,kCAAkC;AAAA,IAChC,SAAW,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IAC5C,OAAS,EAAC,QAAS,gBAAe,MAAO,SAAQ;AAAA,IACjD,cAAgB,EAAC,SAAU,KAAI,SAAU,GAAE,MAAO,UAAS;AAAA,IAC3D,aAAe,EAAC,SAAU,KAAI,SAAU,GAAE,MAAO,UAAS;AAAA,IAC1D,KAAO,EAAC,QAAS,4BAA2B,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC3E,OAAS,EAAC,SAAU,WAAU,MAAO,CAAC,WAAU,WAAU,WAAU,YAAW,SAAS,GAAE,MAAO,SAAQ;AAAA,IACzG,MAAQ,EAAC,MAAO,CAAC,MAAK,IAAI,GAAE,MAAO,SAAQ;AAAA,EAC7C;AAAA,EACA,8BAA8B,CAAC;AAAA,EAC/B,4CAA4C;AAAA,IAC1C,MAAQ,EAAC,SAAU,OAAM,MAAO,CAAC,OAAM,KAAK,GAAE,MAAO,SAAQ;AAAA,IAC7D,MAAQ,EAAC,QAAS,gBAAe,UAAW,MAAK,MAAO,SAAQ;AAAA,EAClE;AAAA,EACA,iCAAiC;AAAA,IAC/B,SAAW,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IAC5C,IAAM,EAAC,QAAS,0BAAyB,SAAU,4BAA2B,eAAgB,4BAA2B,UAAW,MAAK,MAAO,SAAQ;AAAA,IACxJ,MAAQ,EAAC,SAAU,GAAE,MAAO,SAAQ;AAAA,IACpC,YAAc,EAAC,MAAO,CAAC,SAAQ,MAAM,GAAE,MAAO,SAAQ;AAAA,IACtD,UAAY,EAAC,SAAU,QAAO,QAAS,sBAAqB,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC3F,QAAU,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,IAC9C,QAAU,EAAC,QAAS,YAAW,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC9D,MAAQ,EAAC,MAAO,CAAC,OAAO,GAAE,UAAW,MAAK,MAAO,SAAQ;AAAA,EAC3D;AAAA,EACA,+BAA+B;AAAA,IAC7B,OAAS,EAAC,WAAY,KAAI,eAAgB,qCAAoC,MAAO,SAAQ;AAAA,IAC7F,KAAO,EAAC,SAAU,UAAS,SAAU,GAAE,MAAO,UAAS;AAAA,IACvD,MAAQ,EAAC,MAAO,CAAC,MAAM,GAAE,MAAO,SAAQ;AAAA,IACxC,MAAQ,EAAC,WAAY,GAAE,WAAY,GAAE,SAAU,mCAAkC,eAAgB,mCAAkC,UAAW,MAAK,MAAO,SAAQ;AAAA,IAClK,MAAQ,EAAC,UAAW,MAAK,MAAO,SAAQ;AAAA,EAC1C;AAAA,EACA,+BAA+B;AAAA,IAC7B,MAAQ,EAAC,MAAO,CAAC,SAAS,GAAE,MAAO,SAAQ;AAAA,IAC3C,SAAW,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,IAC/C,WAAa,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IACtD,qBAAqB,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,IACzD,QAAU,EAAC,QAAS,qBAAoB,MAAO,SAAQ;AAAA,IACvD,KAAO,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,IAC3C,OAAS,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IAClD,OAAS,EAAC,QAAS,WAAU,MAAO,SAAQ;AAAA,IAC5C,aAAa,EAAC,QAAS,uBAAsB,MAAO,SAAQ;AAAA,IAC5D,KAAO,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IACpC,MAAQ,EAAC,MAAO,CAAC,QAAO,UAAS,QAAO,UAAS,QAAO,OAAO,GAAE,QAAS,gBAAe,UAAW,MAAK,MAAO,SAAQ;AAAA,IACxH,iBAAiB,EAAC,SAAU,UAAS,MAAO,CAAC,UAAS,SAAS,GAAE,MAAO,SAAQ;AAAA,IAChF,aAAa,EAAC,SAAU,UAAS,SAAU,GAAE,MAAO,UAAS;AAAA,IAC7D,cAAc,EAAC,SAAU,QAAO,SAAU,OAAM,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5E,MAAQ,EAAC,WAAY,GAAE,WAAY,GAAE,SAAU,mCAAkC,eAAgB,mCAAkC,UAAW,MAAK,MAAO,SAAQ;AAAA,EACpK;AAAA,EACA,gCAAgC;AAAA,IAC9B,SAAW,EAAC,SAAU,KAAI,SAAU,GAAE,MAAO,UAAS;AAAA,IACtD,SAAW,EAAC,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC3C,UAAY,EAAC,SAAU,KAAI,MAAO,CAAC,KAAI,KAAI,QAAO,OAAM,MAAM,GAAE,MAAO,SAAQ;AAAA,IAC/E,QAAU,EAAC,QAAS,qBAAoB,MAAO,SAAQ;AAAA,IACvD,KAAO,EAAC,QAAS,wBAAuB,MAAO,SAAQ;AAAA,IACvD,SAAW,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IACpD,UAAY,EAAC,QAAS,mBAAkB,MAAO,SAAQ;AAAA,IACvD,QAAU,EAAC,SAAU,KAAI,SAAU,GAAE,SAAU,GAAE,MAAO,UAAS;AAAA,IACjE,UAAY,EAAC,SAAU,OAAM,SAAU,GAAE,MAAO,UAAS;AAAA,IACzD,kBAAoB,EAAC,SAAU,UAAS,MAAO,CAAC,UAAS,SAAS,GAAE,MAAO,SAAQ;AAAA,IACnF,QAAU,EAAC,QAAS,0BAAyB,MAAO,SAAQ;AAAA,IAC5D,MAAQ,EAAC,SAAU,YAAW,MAAO,CAAC,YAAW,WAAU,MAAM,GAAE,MAAO,SAAQ;AAAA,IAClF,MAAQ,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,IAC5C,kBAAkB,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IACnD,qBAAqB,EAAC,SAAU,QAAO,MAAO,CAAC,QAAO,mBAAkB,qBAAqB,GAAE,MAAO,SAAQ;AAAA,IAC9G,6BAA6B,EAAC,MAAO,CAAC,UAAS,QAAO,UAAU,GAAE,MAAO,SAAQ;AAAA,IACjF,aAAe,EAAC,QAAS,sBAAqB,MAAO,SAAQ;AAAA,IAC7D,iBAAiB,EAAC,SAAU,cAAa,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IACjF,UAAY,EAAC,QAAS,sBAAqB,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC1E,WAAa,EAAC,SAAU,qBAAoB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAC/F,UAAY,EAAC,SAAU,MAAK,SAAU,GAAE,MAAO,UAAS;AAAA,IACxD,SAAW,EAAC,QAAS,kBAAiB,MAAO,SAAQ;AAAA,IACrD,MAAQ,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,EACnD;AAAA,EACA,4CAA4C;AAAA,IAC1C,MAAQ,EAAC,QAAS,YAAW,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC5D,SAAW,EAAC,QAAS,uBAAsB,MAAO,SAAQ;AAAA,IAC1D,QAAU,EAAC,WAAY,IAAG,MAAO,SAAQ;AAAA,IACzC,QAAU,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,8BAA6B,eAAgB,8BAA6B,MAAO,SAAQ;AAAA,EAC7I;AAAA,EACA,uCAAuC;AAAA,IACrC,SAAW,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IAC5C,QAAU,EAAC,QAAS,qBAAoB,WAAY,MAAK,MAAO,SAAQ;AAAA,IACxE,QAAU,EAAC,WAAY,IAAG,MAAO,SAAQ;AAAA,IACzC,OAAS,EAAC,QAAS,gBAAe,MAAO,SAAQ;AAAA,IACjD,cAAgB,EAAC,SAAU,KAAI,SAAU,GAAE,MAAO,UAAS;AAAA,IAC3D,aAAe,EAAC,SAAU,KAAI,SAAU,GAAE,MAAO,UAAS;AAAA,IAC1D,OAAS,EAAC,SAAU,WAAU,MAAO,CAAC,WAAU,WAAU,WAAU,YAAW,SAAS,GAAE,MAAO,SAAQ;AAAA,EAC3G;AAAA,EACA,oCAAoC,CAAC;AAAA,EACrC,kDAAkD;AAAA,IAChD,QAAU,EAAC,WAAY,IAAG,MAAO,SAAQ;AAAA,IACzC,MAAQ,EAAC,SAAU,OAAM,MAAO,CAAC,OAAM,KAAK,GAAE,MAAO,SAAQ;AAAA,EAC/D;AAAA,EACA,qCAAqC;AAAA,IACnC,SAAW,EAAC,WAAY,MAAK,MAAO,SAAQ;AAAA,IAC5C,QAAU,EAAC,QAAS,qBAAoB,WAAY,MAAK,MAAO,SAAQ;AAAA,IACxE,QAAU,EAAC,WAAY,IAAG,MAAO,SAAQ;AAAA,IACzC,MAAQ,EAAC,SAAU,GAAE,MAAO,SAAQ;AAAA,IACpC,YAAc,EAAC,MAAO,CAAC,SAAQ,MAAM,GAAE,MAAO,SAAQ;AAAA,IACtD,UAAY,EAAC,SAAU,QAAO,QAAS,sBAAqB,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC3F,QAAU,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,EAChD;AAAA,EACA,qCAAqC;AAAA,IACnC,OAAS,EAAC,WAAY,KAAI,eAAgB,qCAAoC,MAAO,SAAQ;AAAA,IAC7F,QAAU,EAAC,QAAS,qBAAoB,WAAY,MAAK,MAAO,SAAQ;AAAA,IACxE,QAAU,EAAC,WAAY,IAAG,MAAO,SAAQ;AAAA,IACzC,KAAO,EAAC,SAAU,UAAS,SAAU,GAAE,MAAO,UAAS;AAAA,EACzD;AAAA,EACA,qCAAqC;AAAA,IACnC,QAAU,EAAC,QAAS,qBAAoB,WAAY,MAAK,MAAO,SAAQ;AAAA,IACxE,MAAQ,EAAC,MAAO,CAAC,SAAS,GAAE,MAAO,SAAQ;AAAA,IAC3C,QAAU,EAAC,WAAY,IAAG,MAAO,SAAQ;AAAA,IACzC,SAAW,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,IAC/C,WAAa,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IACtD,qBAAqB,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,IACzD,QAAU,EAAC,QAAS,qBAAoB,MAAO,SAAQ;AAAA,IACvD,KAAO,EAAC,QAAS,YAAW,MAAO,SAAQ;AAAA,IAC3C,OAAS,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IAClD,OAAS,EAAC,QAAS,WAAU,MAAO,SAAQ;AAAA,IAC5C,aAAa,EAAC,QAAS,uBAAsB,MAAO,SAAQ;AAAA,IAC5D,KAAO,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IACpC,iBAAiB,EAAC,SAAU,UAAS,MAAO,CAAC,UAAS,SAAS,GAAE,MAAO,SAAQ;AAAA,IAChF,aAAa,EAAC,SAAU,UAAS,SAAU,GAAE,MAAO,UAAS;AAAA,IAC7D,cAAc,EAAC,SAAU,QAAO,SAAU,OAAM,SAAU,GAAE,MAAO,UAAS;AAAA,EAC9E;AACF;;;AC5JO,IAAM,wBAAuE;AAAA,EAClF,mBAAmB;AAAA,IACjB,QAAU,EAAC,QAAS,cAAa,UAAW,MAAK,MAAO,SAAQ;AAAA,EAClE;AAAA,EACA,2BAA2B;AAAA,IACzB,SAAW,EAAC,QAAS,uBAAsB,MAAO,SAAQ;AAAA,IAC1D,KAAO,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,EAClD;AACF;;;ACRO,IAAM,0BAAyE;AAAA,EACpF,qBAAqB;AAAA,IACnB,MAAQ,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IACjD,WAAa,EAAC,QAAS,6BAA4B,MAAO,SAAQ;AAAA,IAClE,SAAW,EAAC,QAAS,4BAA2B,MAAO,SAAQ;AAAA,IAC/D,gBAAgB,EAAC,QAAS,yBAAwB,MAAO,SAAQ;AAAA,IACjE,QAAU,EAAC,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC1C,QAAU,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IACtD,aAAe,EAAC,SAAU,uCAAsC,eAAgB,uCAAsC,MAAO,SAAQ;AAAA,IACrI,QAAU,EAAC,MAAO,CAAC,OAAM,SAAQ,UAAS,MAAM,GAAE,MAAO,SAAQ;AAAA,IACjE,WAAW,EAAC,QAAS,gBAAe,MAAO,SAAQ;AAAA,IACnD,yBAAyB,EAAC,SAAU,4EAA2E,SAAU,IAAG,MAAO,UAAS;AAAA,IAC5I,SAAW,EAAC,QAAS,+BAA8B,MAAO,SAAQ;AAAA,IAClE,YAAc,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IAC1D,OAAS,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IAClD,SAAW,EAAC,QAAS,uBAAsB,MAAO,SAAQ;AAAA,IAC1D,UAAY,EAAC,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC5C,MAAQ,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IACpD,MAAQ,EAAC,SAAU,OAAM,SAAU,GAAE,MAAO,UAAS;AAAA,IACrD,QAAU,EAAC,QAAS,0BAAyB,MAAO,SAAQ;AAAA,IAC5D,eAAiB,EAAC,SAAU,YAAW,MAAO,CAAC,OAAM,YAAW,UAAS,MAAM,GAAE,MAAO,SAAQ;AAAA,IAChG,iBAAiB,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IAC1D,uBAAuB,EAAC,SAAU,MAAK,MAAO,CAAC,KAAI,KAAI,KAAI,KAAI,MAAK,IAAI,GAAE,MAAO,UAAS;AAAA,IAC1F,QAAU,EAAC,QAAS,sBAAqB,MAAO,SAAQ;AAAA,IACxD,YAAc,EAAC,SAAU,WAAU,MAAO,CAAC,WAAU,OAAM,OAAM,KAAI,OAAM,MAAM,GAAE,MAAO,SAAQ;AAAA,IAClG,SAAW,EAAC,QAAS,kBAAiB,UAAW,MAAK,MAAO,SAAQ;AAAA,IACrE,QAAU,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IACtD,UAAY,EAAC,QAAS,sBAAqB,MAAO,SAAQ;AAAA,IAC1D,MAAQ,EAAC,MAAO,CAAC,SAAQ,UAAS,QAAO,OAAM,QAAO,SAAQ,eAAc,OAAM,WAAU,OAAM,OAAM,OAAM,OAAM,SAAS,GAAE,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC9J,QAAU,EAAC,QAAS,sBAAqB,MAAO,SAAQ;AAAA,IACxD,iBAAiB,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,EAC/D;AAAA,EACA,8BAA8B;AAAA,IAC5B,WAAa,EAAC,QAAS,6BAA4B,MAAO,SAAQ;AAAA,IAClE,SAAW,EAAC,QAAS,4BAA2B,MAAO,SAAQ;AAAA,IAC/D,gBAAgB,EAAC,QAAS,yBAAwB,MAAO,SAAQ;AAAA,IACjE,QAAU,EAAC,QAAS,qBAAoB,WAAY,MAAK,MAAO,SAAQ;AAAA,IACxE,QAAU,EAAC,WAAY,IAAG,MAAO,SAAQ;AAAA,IACzC,QAAU,EAAC,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC1C,aAAe,EAAC,SAAU,uCAAsC,eAAgB,uCAAsC,MAAO,SAAQ;AAAA,IACrI,QAAU,EAAC,MAAO,CAAC,OAAM,SAAQ,UAAS,MAAM,GAAE,MAAO,SAAQ;AAAA,IACjE,WAAW,EAAC,QAAS,gBAAe,MAAO,SAAQ;AAAA,IACnD,yBAAyB,EAAC,SAAU,4EAA2E,SAAU,IAAG,MAAO,UAAS;AAAA,IAC5I,SAAW,EAAC,QAAS,+BAA8B,MAAO,SAAQ;AAAA,IAClE,YAAc,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IAC1D,OAAS,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IAClD,SAAW,EAAC,QAAS,uBAAsB,MAAO,SAAQ;AAAA,IAC1D,UAAY,EAAC,WAAY,KAAI,MAAO,SAAQ;AAAA,IAC5C,MAAQ,EAAC,SAAU,OAAM,SAAU,GAAE,MAAO,UAAS;AAAA,IACrD,eAAiB,EAAC,SAAU,YAAW,MAAO,CAAC,OAAM,YAAW,UAAS,MAAM,GAAE,MAAO,SAAQ;AAAA,IAChG,iBAAiB,EAAC,QAAS,iBAAgB,MAAO,SAAQ;AAAA,IAC1D,uBAAuB,EAAC,SAAU,MAAK,MAAO,CAAC,KAAI,KAAI,KAAI,KAAI,MAAK,IAAI,GAAE,MAAO,UAAS;AAAA,IAC1F,QAAU,EAAC,QAAS,sBAAqB,MAAO,SAAQ;AAAA,IACxD,YAAc,EAAC,SAAU,WAAU,MAAO,CAAC,WAAU,OAAM,OAAM,KAAI,OAAM,MAAM,GAAE,MAAO,SAAQ;AAAA,IAClG,QAAU,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,IACtD,iBAAiB,EAAC,QAAS,oBAAmB,MAAO,SAAQ;AAAA,EAC/D;AACF;;;ACzDO,IAAM,yBAAwE;AAAA,EACnF,8BAA8B;AAAA,IAC5B,kBAAkB,EAAC,SAAU,iDAAgD,eAAgB,mDAAkD,MAAO,SAAQ;AAAA,IAC9J,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,cAAc,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC3C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,aAAa,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC1C,gBAAgB,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC7C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,MAAQ,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC7K,qBAAqB,EAAC,SAAU,uBAAsB,MAAO,CAAC,mBAAkB,qBAAqB,GAAE,MAAO,SAAQ;AAAA,IACtH,eAAe,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,qEAAoE,eAAgB,gEAA+D,MAAO,SAAQ;AAAA,IACzN,MAAQ,EAAC,WAAY,MAAK,WAAY,GAAE,UAAW,MAAK,MAAO,SAAQ;AAAA,EACzE;AAAA,EACA,2CAA2C;AAAA,IACzC,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,MAAQ,EAAC,SAAU,OAAM,MAAO,CAAC,OAAM,KAAK,GAAE,MAAO,SAAQ;AAAA,IAC7D,MAAQ,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC7K,QAAU,EAAC,MAAO,CAAC,gBAAe,WAAU,kBAAkB,GAAE,MAAO,SAAQ;AAAA,EACjF;AAAA,EACA,0BAA0B;AAAA,IACxB,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,IAAM,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC3K,cAAc,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC3C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,aAAa,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC1C,gBAAgB,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC7C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,aAAa,EAAC,SAAU,GAAE,SAAU,GAAE,MAAO,UAAS;AAAA,IACtD,IAAM,EAAC,WAAY,KAAI,SAAU,uFAAsF,eAAgB,yFAAwF,MAAO,SAAQ;AAAA,IAC9O,UAAY,EAAC,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC5C,OAAS,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,UAAW,MAAK,MAAO,SAAQ;AAAA,EAChL;AAAA,EACA,yBAAyB;AAAA,IACvB,yBAAyB,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,MAAO,SAAQ;AAAA,IAC9K,YAAY,EAAC,WAAY,IAAG,WAAY,GAAE,MAAO,SAAQ;AAAA,IACzD,aAAa,EAAC,WAAY,IAAG,WAAY,GAAE,MAAO,SAAQ;AAAA,IAC1D,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,IAAM,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC3K,aAAa,EAAC,SAAU,GAAE,SAAU,GAAE,MAAO,UAAS;AAAA,IACtD,IAAM,EAAC,WAAY,KAAI,SAAU,uFAAsF,eAAgB,yFAAwF,MAAO,SAAQ;AAAA,IAC9O,OAAS,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,2KAA0K,eAAgB,+JAA8J,MAAO,SAAQ;AAAA,IACxZ,WAAW,EAAC,WAAY,IAAG,WAAY,GAAE,MAAO,SAAQ;AAAA,IACxD,YAAY,EAAC,WAAY,IAAG,WAAY,GAAE,MAAO,SAAQ;AAAA,IACzD,QAAU,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,MAAO,SAAQ;AAAA,IAC/J,aAAa,EAAC,WAAY,KAAI,SAAU,uFAAsF,eAAgB,yFAAwF,MAAO,SAAQ;AAAA,IACrP,gBAAgB,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,UAAW,MAAK,MAAO,SAAQ;AAAA,IACrL,OAAS,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC9K,kBAAkB,EAAC,SAAU,QAAO,MAAO,CAAC,QAAO,MAAM,GAAE,MAAO,SAAQ;AAAA,IAC1E,iBAAiB,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC9C,kBAAkB,EAAC,SAAU,KAAI,SAAU,IAAG,SAAU,GAAE,MAAO,UAAS;AAAA,EAC5E;AAAA,EACA,2BAA2B;AAAA,IACzB,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,IAAM,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC3K,aAAa,EAAC,SAAU,KAAI,SAAU,GAAE,SAAU,GAAE,MAAO,UAAS;AAAA,IACpE,IAAM,EAAC,WAAY,KAAI,SAAU,uFAAsF,eAAgB,yFAAwF,MAAO,SAAQ;AAAA,IAC9O,kBAAkB,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC/C,gBAAgB,EAAC,SAAU,KAAI,SAAU,IAAG,SAAU,GAAE,MAAO,UAAS;AAAA,IACxE,OAAS,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,UAAW,MAAK,MAAO,SAAQ;AAAA,IAC9K,kBAAkB,EAAC,SAAU,KAAI,SAAU,IAAG,SAAU,GAAE,MAAO,UAAS;AAAA,EAC5E;AAAA,EACA,oCAAoC;AAAA,IAClC,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,QAAU,EAAC,SAAU,kBAAiB,eAAgB,oBAAmB,MAAO,SAAQ;AAAA,IACxF,cAAc,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC3C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,aAAa,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC1C,gBAAgB,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC7C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,qBAAqB,EAAC,SAAU,uBAAsB,MAAO,CAAC,mBAAkB,qBAAqB,GAAE,MAAO,SAAQ;AAAA,IACtH,eAAe,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,qEAAoE,eAAgB,gEAA+D,MAAO,SAAQ;AAAA,EAC3N;AAAA,EACA,iDAAiD;AAAA,IAC/C,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,QAAU,EAAC,SAAU,kBAAiB,eAAgB,oBAAmB,MAAO,SAAQ;AAAA,IACxF,MAAQ,EAAC,SAAU,OAAM,MAAO,CAAC,OAAM,KAAK,GAAE,MAAO,SAAQ;AAAA,EAC/D;AAAA,EACA,8BAA8B;AAAA,IAC5B,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,QAAU,EAAC,SAAU,kBAAiB,eAAgB,oBAAmB,MAAO,SAAQ;AAAA,IACxF,cAAc,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC3C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,aAAa,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC1C,gBAAgB,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC7C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,eAAe,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC5C,aAAa,EAAC,SAAU,GAAE,SAAU,GAAE,MAAO,UAAS;AAAA,IACtD,IAAM,EAAC,WAAY,KAAI,SAAU,uFAAsF,eAAgB,yFAAwF,MAAO,SAAQ;AAAA,IAC9O,OAAS,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,MAAO,SAAQ;AAAA,EAChK;AAAA,EACA,6BAA6B;AAAA,IAC3B,yBAAyB,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,MAAO,SAAQ;AAAA,IAC9K,YAAY,EAAC,WAAY,IAAG,WAAY,GAAE,MAAO,SAAQ;AAAA,IACzD,aAAa,EAAC,WAAY,IAAG,WAAY,GAAE,MAAO,SAAQ;AAAA,IAC1D,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,QAAU,EAAC,SAAU,kBAAiB,eAAgB,oBAAmB,MAAO,SAAQ;AAAA,IACxF,aAAa,EAAC,SAAU,GAAE,SAAU,GAAE,MAAO,UAAS;AAAA,IACtD,IAAM,EAAC,WAAY,KAAI,SAAU,uFAAsF,eAAgB,yFAAwF,MAAO,SAAQ;AAAA,IAC9O,OAAS,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,2KAA0K,eAAgB,+JAA8J,MAAO,SAAQ;AAAA,IACxZ,WAAW,EAAC,WAAY,IAAG,WAAY,GAAE,MAAO,SAAQ;AAAA,IACxD,YAAY,EAAC,WAAY,IAAG,WAAY,GAAE,MAAO,SAAQ;AAAA,IACzD,QAAU,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,MAAO,SAAQ;AAAA,IAC/J,aAAa,EAAC,WAAY,KAAI,SAAU,uFAAsF,eAAgB,yFAAwF,MAAO,SAAQ;AAAA,IACrP,gBAAgB,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,MAAO,SAAQ;AAAA,IACrK,OAAS,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,MAAO,SAAQ;AAAA,IAC9J,kBAAkB,EAAC,SAAU,QAAO,MAAO,CAAC,QAAO,MAAM,GAAE,MAAO,SAAQ;AAAA,IAC1E,iBAAiB,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC9C,kBAAkB,EAAC,SAAU,KAAI,SAAU,IAAG,SAAU,GAAE,MAAO,UAAS;AAAA,EAC5E;AAAA,EACA,+BAA+B;AAAA,IAC7B,SAAW,EAAC,WAAY,KAAI,SAAU,0BAAyB,eAAgB,qBAAoB,MAAO,SAAQ;AAAA,IAClH,QAAU,EAAC,SAAU,kBAAiB,eAAgB,oBAAmB,MAAO,SAAQ;AAAA,IACxF,aAAa,EAAC,SAAU,KAAI,SAAU,GAAE,SAAU,GAAE,MAAO,UAAS;AAAA,IACpE,IAAM,EAAC,WAAY,KAAI,SAAU,uFAAsF,eAAgB,yFAAwF,MAAO,SAAQ;AAAA,IAC9O,kBAAkB,EAAC,SAAU,GAAE,MAAO,UAAS;AAAA,IAC/C,gBAAgB,EAAC,SAAU,KAAI,SAAU,IAAG,SAAU,GAAE,MAAO,UAAS;AAAA,IACxE,OAAS,EAAC,WAAY,IAAG,WAAY,GAAE,SAAU,uCAAsC,eAAgB,yCAAwC,MAAO,SAAQ;AAAA,IAC9J,kBAAkB,EAAC,SAAU,KAAI,SAAU,IAAG,SAAU,GAAE,MAAO,UAAS;AAAA,EAC5E;AACF;;;AC5HO,IAAM,sBAAqE;AAAA,KAC7E;AAAA,KACA;AAAA,KACA;AAAA,KACA;AAAA,KACA;AACL;;;APHO,IAAM,iBAAiB,CAAC,QAA0C;AAAA,EACvE,MAAM,QAAQ,oBAAoB;AAAA,EAClC,IAAI,UAAU,WAAW;AAAA,IACvB,MAAM,IAAI,MACR,mCAAmC,0DACjC,2DACJ;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAIF,IAAM,iBAAiB,CAC5B,KACA,MACA,aACsB,WAAW,eAAe,GAAG,GAAG,MAAM,EAAE,SAAS,CAAC;AAQnE,IAAM,YAAY,CACvB,KACA,MACA,aAEO,gBAAQ,MAAM;AAAA,EACnB,IAAI,QAAQ;AAAA,IAAW,OAAc;AAAA,EACrC,MAAM,QAAQ,eAAe,KAAK,MAAM,QAAQ;AAAA,EAChD,OAAO,MAAM,WAAW,IAAW,eAAc,YAAI,IAAI,MAAM,QAAQ,KAAK,KAAK,CAAC,CAAC;AAAA,CACpF;;;AQ3BI,IAAM,aAAa,CACxB,SACA,MACA,OACA,SACsB,QAAQ,MAAM,KAAK,KAAK,OAAO,KAAK,IAAI,EAAE,WAAW,IAAI,YAAY;;;ATFtF,IAAM,gBAAgB,CAC3B,SACG;AAAA,EAQH,MAAM,aAAa,CAAC,UACX,YAAI,UAAU,GAAG;AAAA,IAOtB,MAAM,SAAS,KAAK,UAAU;AAAA,IAC9B,IAAI,WAAW;AAAA,MAAW,OAAO,UAAU,QAAQ,KAAK,WAAW,KAAK,GAAG,IAAI;AAAA,IAC/E,MAAM,SAAS,KAAK,UAAU;AAAA,IAC9B,IAAI,WAAW;AAAA,MAAW;AAAA,IAC1B,MAAM,OAAO,KAAK,aAAa,KAAK;AAAA,IACpC,IAAI,SAAS;AAAA,MAAW,OAAO,UAAU,QAAQ,MAAM,KAAK;AAAA,GAC7D;AAAA,EAGH,MAAM,OAAO,CAAC,UACZ,IACE,MAAM,QACN,KAAK,YAAY,QACjB,OACA,KAAK,KAAK,KAAK,CACjB,EAAE,KACO,YAAI,CAAC,SAAU,QAAQ,OAAO,YAAY,KAAK,WAAW,MAAM,KAAK,CAAE,GACvE,sBAAc,MAAG;AAAA,IAAG;AAAA,GAAS,CACtC;AAAA,EAEF,OAAO;AAAA,IACL;AAAA,IAKA,MAAM,CAAC,MAAoB,WAClB,YAAI,UAAU,GAAG;AAAA,MAItB,IAAI,CAAC,WAAW,IAAI;AAAA,QAAG;AAAA,MAOvB,OAAO,WAAW,IAAI;AAAA,MACtB,IAAI,WAAW;AAAA,QAAW;AAAA,MAC1B,MAAM,OAAO,OAAO,KAAK,IAAI;AAAA,MAG7B,IAAI,SAAS;AAAA,QAAW,OAAO,EAAE,QAAQ,SAAS;AAAA,MAClD,IAAI,KAAK,QAAQ,MAAM,IAAI;AAAA,QAAG,OAAO,EAAE,QAAQ,OAAO;AAAA,MAEtD,OAAO,KAAK,eAAe,YACtB,EAAE,QAAQ,UAAU,IACpB,EAAE,QAAQ,SAAS;AAAA,KACzB;AAAA,IAEH,WAAW,CAAC,SACH,YAAI,UAAU,GAAG;AAAA,MAItB,OAAO,WAAW,IAAI;AAAA,MACtB,MAAM,OAAO,OAAO,KAAK,IAAI;AAAA,MAC7B,IAAI,SAAS,WAAW;AAAA,QACtB,OAAO,IACL,KAAK,QACL,aACA,QACA,KAAK,WAAW,IAAI,GACpB,KAAK,WAAW,IAAI,CACtB;AAAA,MACF,EAAO,SAAI,KAAK,eAAe,WAAW;AAAA,QAyBxC,MAAM,OAAO,WAAW,KAAK,SAAS,MAAM,MAAM,KAAK,WAAW,IAAI,CAAC;AAAA,QACvE,IAAI,SAAS,WAAW;AAAA,UACtB,OAAO,IAAI,KAAK,QAAQ,aAAa,OAAO,KAAK,KAAK,IAAI,GAAG,IAAI;AAAA,QACnE;AAAA,MACF;AAAA,MACA,MAAM,QAAQ,OAAO,KAAK,IAAI;AAAA,MAC9B,IAAI,UAAU,WAAW;AAAA,QAOvB,OAAO,OAAc,YACnB,IAAI,MACF,GAAG,KAAK,KAAK,IAAI,oEACf,sFACA,4DACJ,CACF;AAAA,MACF;AAAA,MACA,OAAO;AAAA,KACR;AAAA,IAOH,SAAS,CAAC,SAAgB,IAAI,KAAK,QAAQ,aAAa,UAAU,KAAK,KAAK,IAAI,CAAC;AAAA,EACnF;AAAA;AAoCK,IAAM,cAAc,CACzB,SACG;AAAA,EACH,MAAM,MAAM,cAAc,IAAI;AAAA,EAC9B,OAAO;AAAA,IACL,MAAM,MAAa,gBAAQ,CAAC,CAAC;AAAA,IAC7B,MAAM,GAAG,WAA4B,IAAI,KAAK,IAAI;AAAA,IAClD,MAAM,GAAG,MAAM,aACb,IAAI,KAAK,MAAM,MAAM;AAAA,IACvB,WAAW,GAAG,WAA4B,IAAI,UAAU,IAAI;AAAA,IAC5D,QAAQ,GAAG,WAA4B,IAAI,QAAQ,IAAI;AAAA,EACzD;AAAA;;;ALpIK,IAAM,aAAa,SAAqB,aAAa;AAS5D,IAAM,YAAY,CAAC,QAAgB,IAAI,IAAI,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAQ9E,IAAM,gBAAgB,EAAE,OAAO,UAAU,OAAO,UAAU,MAAM,QAAQ;AAGxE,IAAM,QAAQ,CAAC,WAA6C;AAAA,EAC1D,MAAM,UAAU,MAAM,IAAI;AAAA,EAC1B,OAAO,MAAM;AAAA,GACZ,cAAc,MAAM,QAAQ,MAAM;AACrC;AAGA,IAAM,OAAO,CAAC,WAA6C;AAAA,EACzD,WAAW,MAAM,cAAc,QAAQ,MAAM;AAAA,KAC1C,MAAM,KAAK;AAChB;AAGA,IAAM,WAAW,CAAC,UAChB,CAAC,UAAU,MAAM,IAAI,GAAG,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,EAAE,KAAK,GAAG;AAOxE,IAAM,OAAO,CAAC,MAAe,WAC1B,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,GAC5B,OAAO,CAAC,QAAwC,OAAO,QAAQ,YAAY,QAAQ,IAAI,EACvF,KACC,CAAC,QACC,IAAI,YAAY,UAAU,MAAM,IAAI,KACpC,IAAI,YAAY,MAAM,QACtB,IAAI,YAAY,MAAM,QACtB,IAAI,cAAc,MAAM,MAC5B;AAOJ,IAAM,aAAa,CAAC,QAAiC;AAAA,EACnD,MAAM,QAAQ,IAAI;AAAA,EAClB,OAAO,UAAU,aAAa,UAAU,KAAK,UAAU,QAAQ,UAAU;AAAA;AAG3E,IAAM,MAAM,cAAuC;AAAA,EACjD,YAAY,CAAC,MAAM,UAAU;AAAA,IAC3B,MAAM,MAAM,KAAK,MAAM,KAAK;AAAA,IAC5B,OAAO;AAAA,MAGL,OAAO,QAAQ;AAAA,MACf,MAAM,UAAU,MAAM,IAAI;AAAA,MAC1B,WAAW,QAAQ,aAAa,WAAW,GAAG;AAAA,MAC9C,QAAQ,MAAM;AAAA,MACd,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,IACd;AAAA;AAAA,EA0BF,UAAU;AAAA,EACV,YAAY,MAAM;AAAA,EAClB,YAAY;AAAA,EAQZ,UAAU,EAAE,QAAQ,uBAAuB,QAAQ,sBAAsB;AAAA,EACzE,SAAS,CAAC,YAAY,UACpB,WAAW,SAAS,WAAW,eAAe,MAAM,cAAc;AAAA,EACpE,MAAM,MAAM;AAAA,EACZ,YAAY;AACd,CAAC;AAEM,IAAM,qBAAqB,MACvB,gBACP,YACO,gBACL,WAAW,SAAS,GAAG;AAAA,EAMrB,MAAM,MAAa,gBAAQ,CAAC,CAAC;AAAA,EAC7B,MAAa,WAAG,UAAU,GAAG,QAAQ;AAAA,IACnC,OAAO,OAAO,IAAI,KAAK,IAAI;AAAA,GAC5B;AAAA,EAaD,MAAa,WAAG,UAAU,GAAG,MAAM,UAAU;AAAA,IAC3C,IAAI,WAAW,aAAa,YAAW,IAAI,KAAK,SAAS,IAAI,MAAM,SAAS,MAAM,GAAG;AAAA,MACnF,OAAO,EAAE,QAAQ,UAAU;AAAA,IAC7B;AAAA,IACA,OAAO,OAAO,IAAI,KAAK,MAAM,MAAM;AAAA,GACpC;AAAA,EAQD,WAAkB,WAAG,UAAU,GAAG,QAAQ;AAAA,IACxC,MAAM,QAAQ,OAAO,IAAI,UAAU,IAAI;AAAA,IACvC,IAAI,CAAC,MAAM,OAAO;AAAA,MAChB,OAAO,OAAc,YACnB,IAAI,MACF,+CAA+C,SAAS,IAAI,oBAC1D,8EACA,kFACA,qDACJ,CACF;AAAA,IACF;AAAA,IACA,OAAO;AAAA,GACR;AAAA,EAWD,QAAe,WAAG,UAAU,GAAG,QAAQ;AAAA,IACrC,MAAM,UAAU,EAAE,QAAQ,QAAQ,MAAM,IAAI,EAAE;AAAA,IAC9C,OAAO,IAAI,KAAK,QAAQ,aAAa,OAAO,cAAc,OAAO;AAAA,GAClE;AACH,CAAC,CACH,CACF;;Ae9NF,IAAM,UAAU,CAAC,OAAe,UAA0B;AAAA,EACxD,IAAI,SAAS,KAAK,KAAK,KAAK,UAAU,IAAI;AAAA,IACxC,MAAM,IAAI,MACR,0BAA0B,sDAC5B;AAAA,EACF;AAAA,EACA,OAAO,KAAK,UAAU,KAAK;AAAA;AAQ7B,IAAM,cAAc,CAAC,UAA0B;AAAA,EAC7C,MAAM,OAAO,QAAQ,gBAAgB,KAAK;AAAA,EAC1C,IAAI,WAAW;AAAA,EACf,IAAI;AAAA,IACF,WAAW,IAAI,IAAI,KAAK,EAAE;AAAA,IAC1B,MAAM;AAAA,EAGR,IAAI,aAAa,YAAY,aAAa,SAAS;AAAA,IACjD,MAAM,IAAI,MAAM,qEAAqE;AAAA,EACvF;AAAA,EACA,OAAO;AAAA;AAIT,IAAM,eAAqE;AAAA,EACzE,CAAC,YAAY,MAAM;AAAA,EACnB,CAAC,UAAU,QAAQ;AAAA,EACnB,CAAC,aAAa,WAAW;AAAA,EACzB,CAAC,YAAY,UAAU;AACzB;AAeO,IAAM,wBAAwB,CAAC,UAAmC,CAAC,MAAc;AAAA,EACtF,MAAM,YAAY,QAAQ,aAAa,QAAQ,aAAa,iBAAiB;AAAA,EAC7E,MAAM,SAAS,QAAQ,UAAU,QAAQ,UAAU,KAAK;AAAA,EACxD,MAAM,SAAS,aAAa,IAC1B,EAAE,OAAO,WACP,sBAAsB,WAAW,0BAA0B,kBAC/D;AAAA,EACA,MAAM,OACJ,QAAQ,iBAAiB,YACrB,KACA;AAAA,sBAA0B,YAAY,QAAQ,YAAY;AAAA,EAChE,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH;AAAA,IACA,sBAAsB;AAAA,IACtB,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK;AAAA,CAAI;AAAA;;ACnEb,qBAAS;AACT;AACA;;;ACnCO,IAAM,MAAM,CAAC,OAAgB,aAClC,OAAO,UAAU,WAAW,QAAQ;AAE/B,IAAM,OAAO,CAAC,OAAgB,WAAW,OAC9C,OAAO,UAAU,WAAW,QAAQ;AA2B/B,IAAM,OAAO,CAAC,OAAgB,WAAW,UAC9C,UAAU,aAAa,UAAU,QAAQ,UAAU,KAC/C,WACA,UAAU,KAAK,UAAU,QAAQ,UAAU;AAS1C,IAAM,OAAO,CAAC,UAAqB,UAAU,YAAY,YAAY,QAAQ,MAAM;AAUnF,IAAM,MAAM,CAAC,WACjB,OAAO,UAAU,WAAW,MAAM,MAAM,GAAG,IAAK,SAAS,CAAC,GACxD,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,SAAS,EAAE,EAC5B,KAAK,EACL,KAAK,GAAG;AAYN,IAAM,YAAY,CAAC,UAA2B;AAAA,EACnD,MAAM,QACJ,OAAO,UAAU,WACb,MAAM,MAAM,GAAG,IACf,MAAM,QAAQ,KAAK,IACjB,MAAM,IAAI,CAAC,UAAmB,OAAO,KAAK,CAAC,IAC3C,CAAC;AAAA,EACT,OAAO,MACJ,IAAI,CAAC,SAAS,OAAO,KAAK,KAAK,CAAC,CAAC,EACjC,OAAO,CAAC,OAAO,OAAO,SAAS,EAAE,CAAC,EAClC,KAAK,CAAC,MAAM,UAAU,OAAO,KAAK,EAClC,IAAI,CAAC,OAAO,OAAO,EAAE,CAAC,EACtB,KAAK,GAAG;AAAA;AAIN,IAAM,iBAAiB,CAAC,QAAgB;AAAA,EAC7C,MAAM,UAAU,IAAI,KAAK,EAAE,YAAY;AAAA,EACvC,IAAI,CAAC,KAAK,OAAO,QAAQ,IAAI,EAAE,SAAS,OAAO;AAAA,IAAG,OAAO;AAAA,EACzD,IAAI,CAAC,KAAK,MAAM,SAAS,KAAK,EAAE,SAAS,OAAO;AAAA,IAAG,OAAO;AAAA,EAC1D,OAAO;AAAA;AAmBF,IAAM,iBAAiB,CAAC,OAAgB,eAAgC;AAAA,EAC7E,MAAM,QACJ,OAAO,UAAU,WACb,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS;AAAA,IAC7B,OAAO,QAAQ,QAAQ,KAAK,MAAM,GAAG;AAAA,IACrC,OAAO,KAAK,WAAW,KAAK,eAAe,YACvC,CAAC,YAAY,OAAO,EAAE,IACtB,CAAC,OAAO,IAAI,KAAK,KAAK,GAAG,CAAC;AAAA,GAC/B,IACD,OAAO,UAAU,YAAY,UAAU,OACrC,OAAO,QAAQ,KAAK,EAAE,IAAI,EAAE,KAAK,WAAW,CAAC,KAAK,OAAO,KAAK,CAAC,CAAC,IAChE,CAAC;AAAA,EACT,OAAO,MACJ,IAAI,EAAE,KAAK,WAAW,CAAC,IAAI,KAAK,GAAG,eAAe,KAAK,CAAC,CAAU,EAClE,OAAO,EAAE,SAAS,QAAQ,EAAE,EAC5B,IAAI,EAAE,KAAK,WAAW,GAAG,OAAO,OAAO,EACvC,KAAK,EACL,KAAK,GAAG;AAAA;AAWN,IAAM,MAAM,CAAC,OAAgB,aAAqB;AAAA,EACvD,IAAI,OAAO,UAAU;AAAA,IAAU,OAAO;AAAA,EACtC,MAAM,SAAS,OAAO,SAAS,KAAK,KAAK,GAAG,EAAE;AAAA,EAC9C,OAAO,OAAO,MAAM,MAAM,IAAI,WAAW;AAAA;AA6BpC,IAAM,aAAa,CACxB,QACA,UAC2B;AAAA,EAC3B,MAAM,UAAU,IAAI,IAAI,KAAK;AAAA,EAC7B,MAAM,OAA+B,CAAC;AAAA,EACtC,YAAY,KAAK,UAAU,OAAO,QAAQ,MAAM,GAAG;AAAA,IACjD,IAAI,UAAU,aAAa,CAAC,QAAQ,IAAI,GAAG;AAAA,MAAG,KAAK,OAAO;AAAA,EAC5D;AAAA,EACA,OAAO,QAAQ,SAAS,IAAI,OAAO,KAAK,MAAM,QAAQ,CAAC,GAAG,OAAO,EAAE,KAAK,GAAG,EAAE;AAAA;;;ACxKxE,IAAM,cAAc,CAAC,UAAyB,GAAG,MAAM,UAAU,MAAM;AA0BvE,IAAM,QAAQ,CAAC,WAAkD;AAAA,EACtE,SAAS,MAAM,WAAW;AAAA,EAC1B,QAAQ,OAAO,MAAM,MAAM;AAAA,EAC3B,SAAS,MAAM,UAAU,MAAM;AACjC;AAEO,IAAM,eAA2D;AAAA,EAItE,UAAU;AAAA,EAaV,YAAY,CAAC,MAAM,UAAU;AAAA,IAC3B,MAAM,SAAS,KAAK;AAAA,IACpB,MAAM,UAAU,KAAK;AAAA,IACrB,IAAI,WAAW,aAAa,YAAY;AAAA,MAAW;AAAA,IACnD,OAAO;AAAA,MACL,SAAS,KAAK,KAAK,UAAU;AAAA,MAC7B,QAAQ,IAAI,QAAQ,CAAC;AAAA,MACrB,aAAa,YAAY,KAAK;AAAA,MAG9B,SAAS,KAAK,SAAS,IAAI;AAAA,MAC3B,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,IAChB;AAAA;AAAA,EAQF,YAAY,CAAC,UAAU,gBAAgB,MAAM,gBAAgB,MAAM;AAAA,EACnE,YAAY;AAAA,EAWZ,SAAS,CAAC,YAAY,UACpB,WAAW,aAAa,MAAM,WAAW,OACzC,WAAW,WAAW,MAAM,UAC5B,WAAW,YAAY,MAAM;AAAA,EAC/B,MAAM,CAAC,UAAU,gBAAgB,MAAM,gBAAgB,MAAM;AAAA,EAC7D,YAAY;AACd;;;AF6CO,IAAM,kBAAkB,UAA0B,oBAAoB;AAAA,EAC3E,sBAAsB;AACxB,CAAC;AAED,IAAM,OAAM,cAAc,YAAY;AAwBtC,IAAM,WAAW;AAAA,KACZ,YAAY,YAAY;AAAA,EAC3B,WAAkB,WAAG,UAAU,GAAG,QAAiC;AAAA,IACjE,MAAM,OAAO,OAAO,KAAI,KAAK,IAAI;AAAA,IACjC,IAAI,SAAS,WAAW;AAAA,MACtB,OAAO,OAAc,YACnB,IAAI,MACF,GAAG,KAAK,UAAU,KAAK,oEACrB,0FACA,wFACA,iFACA,4EACJ,CACF;AAAA,IACF;AAAA,IACA,OAAO,OAAO,KAAI,UAAU,IAAI;AAAA,GACjC;AACH;AASO,IAAM,0BAA0B,MAC5B,iBAAO,iBAAwB,gBAAQ,gBAAgB,SAAS,GAAG,QAAQ,CAAC,CAAC;;AG9LxF,qBAAS;AACT;AACA;AAyEO,IAAM,mBAAmB,UAA2B,mBAAmB;AAsB9E,IAAM,SAAQ,CAAC,WAA2B;AAAA,EACxC,KAAK,MAAM,QAAQ,OAAO,MAAM;AAAA,EAChC,SAAS,MAAM,YAAY,QAAQ,MAAM;AAAA,EACzC,MAAM,MAAM,QAAQ;AAAA,EACpB,qBAAqB,MAAM,wBAAwB;AAAA,EACnD,iBAAiB,MAAM,qBAAqB,OAAO,MAAM;AAAA,EACzD,UAAU,MAAM;AAAA,KACZ,MAAM,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;AAAA,KAC5D,MAAM,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,UAAU,MAAM,OAAO,EAAE;AAAA,KACvE,MAAM,aAAa,YAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;AAAA,KAC/D,MAAM,sBAAsB,YAAY,CAAC,IAAI,EAAE,kBAAkB,MAAM,kBAAkB;AAAA,KACzF,MAAM,SAAS,YAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;AAAA,KACnD,MAAM,qBAAqB,YAAY,CAAC,IAAI,EAAE,iBAAiB,MAAM,iBAAiB;AAAA,KACtF,MAAM,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;AAAA,KAC5D,MAAM,SAAS,YAAY,CAAC,IAAI,EAAE,MAAM,UAAU,MAAM,IAAI,EAAE;AACpE;AAEA,IAAM,YAAW,YAAiD;AAAA,EAQhE,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,KAAK,KAAK,KAAK,QAAQ,KAAK;AAAA,IAC5B,SAAS,KAAK,KAAK,YAAY,EAAE;AAAA,IACjC,SAAS,KAAK,KAAK,YAAY,IAAI;AAAA,IACnC,SAAS,UAAU,KAAK,UAAU;AAAA,IAClC,UAAU,eAAe,KAAK,aAAa,SAAS;AAAA,IACpD,IAAI,MAAM;AAAA,IACV,MAAM,KAAK,KAAK,SAAS,UAAU;AAAA,IACnC,YAAY,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc;AAAA,IACtE,kBAAkB,KAAK,KAAK,mBAAmB,EAAE;AAAA,IACjD,qBAAqB,KAAK,KAAK,sBAAsB,MAAM;AAAA,IAC3D,MAAM,KAAK,KAAK,SAAS,EAAE;AAAA,IAC3B,iBAAiB,eAAe,KAAK,gBAAgB;AAAA,IACrD,iBAAiB,KAAK,KAAK,kBAAkB,KAAK;AAAA,IAClD,UAAU,KAAK,KAAK,aAAa,EAAE;AAAA,IACnC,SAAS,KAAK,KAAK,YAAY,EAAE;AAAA,IACjC,MAAM,UAAU,KAAK,OAAO;AAAA,EAC9B;AAAA,EACA,YAAY,MAAM;AAAA,EAElB,YAAY,CAAC,WAAW,KAAK,OAAM,KAAK,GAAG,IAAI,MAAM,GAAG;AAAA,EAGxD,UAAU,EAAE,QAAQ,4BAA4B,QAAQ,+BAA+B;AAAA,EACvF,SAAS,CAAC,YAAY,UACpB,WAAW,aAAa,MAAM,YAC9B,WAAW,aAAa,MAAM,YAAY,UAC1C,WAAW,SAAS,MAAM,QAAQ,SAClC,WAAW,UAAU,MAAM,QAAQ,eACnC,WAAW,0BAA0B,MAAM,wBAAwB,WACnE,WAAW,sBAAsB,MAAM,qBAAqB,UAC3D,MAAM,YAAY,aAAa,WAAW,YAAY,MAAM,aAC5D,MAAM,SAAS,aAAa,WAAW,SAAS,MAAM,UACtD,MAAM,YAAY,aAAa,WAAW,YAAY,MAAM,aAC5D,MAAM,SAAS,aAAa,WAAW,SAAS,UAAU,MAAM,IAAI,OACpE,MAAM,YAAY,aAAa,WAAW,YAAY,UAAU,MAAM,OAAO,OAC7E,MAAM,sBAAsB,aAC3B,WAAW,sBAAsB,MAAM,uBACxC,MAAM,qBAAqB,aAC1B,WAAW,qBAAqB,eAAe,MAAM,gBAAgB,OACtE,MAAM,aAAa,aAClB,WAAW,aAAa,eAAe,MAAM,UAAU,SAAS;AAAA,EACpE,MAAM,CAAC,UAAU,kBAAkB,MAAM;AAAA,EACzC,YAAY;AACd,CAAC;AAgBM,IAAM,2BAA2B,MAC7B,iBAAO,kBAAyB,gBAAQ,iBAAiB,SAAS,GAAG,SAAQ,CAAC,CAAC;;ACtK1F,qBAAS;AACT;AACA;;;ACXO,IAAM,WAAW,CAAC,UAA2B,MAAM,QAAQ,MAAM;AAQjE,IAAM,iBAAiB,CAAC,UAA2B,SAAS,MAAM,aAAa,MAAM;AAGrF,IAAM,aAAa,CAAC,UAA2B,GAAG,eAAe,KAAK,KAAK,SAAS,KAAK;AAezF,IAAM,aAAa,CAAC,UAAmD;AAAA,EAC5E,IAAI,MAAM,SAAS,SAAS,MAAM,mBAAmB,WAAW;AAAA,IAC9D,OAAO,EAAE,eAAe,MAAM,eAAe;AAAA,EAC/C;AAAA,EACA,MAAM,aAAa,MAAM,SAAS,QAAQ,KAAK,MAAM,UAAU,IAAI;AAAA,EACnE,OAAO,eAAe,YAAY,CAAC,IAAI,EAAE,WAAW;AAAA;AAU/C,IAAM,UAAU,CAAC,MAAe,WACpC,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,GAC5B,OAAO,CAAC,QAAwC,OAAO,QAAQ,YAAY,QAAQ,IAAI,EACvF,KAAK,CAAC,QAAQ,IAAI,YAAY,SAAS,KAAK,CAAC;;;AD0F3C,IAAM,oBAAoB,UAA4B,sBAAsB;AAAA,EACjF,sBAAsB;AACxB,CAAC;AAED,IAAM,YAAW,YAAmD;AAAA,EAElE,YAAY,CAAC,MAAM,UAAU;AAAA,IAC3B,MAAM,MAAM,QAAQ,MAAM,KAAK;AAAA,IAC/B,IAAI,QAAQ;AAAA,MAAW;AAAA,IACvB,OAAO;AAAA,MACL,MAAM,KAAK,IAAI,OAAO;AAAA,MACtB,QAAQ,KAAK,IAAI,UAAU;AAAA,MAC3B,MAAM,KAAK,IAAI,OAAO;AAAA,MACtB,MAAM,MAAM;AAAA,MACZ,MAAM,SAAS,KAAK;AAAA,MACpB,MAAM,MAAM;AAAA,MACZ,QAAQ,KAAK,IAAI,SAAS;AAAA,MAC1B,MAAM,IAAI,IAAI,SAAS,EAAE;AAAA,MACzB,SAAS,KAAK,IAAI,UAAU;AAAA,MAC5B,eAAe,KAAK,IAAI,iBAAiB;AAAA,MACzC,OAAO,KAAK,IAAI,QAAQ;AAAA,MACxB,SAAS,KAAK,IAAI,qBAAqB;AAAA,IACzC;AAAA;AAAA,EAGF,YAAY;AAAA,EACZ;AAAA,EAiBA,SAAS,MAAM;AAAA,EAEf,MAAM;AAER,CAAC;AAEM,IAAM,4BAA4B,MAC9B,iBACP,mBACO,iBACL,kBAAkB,SAAS,GAAG;AAAA,KACzB;AAAA,EAWH,QAAQ,GAAG,WACT,IAAI,KAAK,QAAQ,aAAa,UAAU,WAAW,IAAI,CAAC;AAC5D,CAAC,CACH,CACF;;AExKF,qBAAS;AACT;AACA;AA2EO,IAAM,kBAAkB,UAA0B,kBAAkB;AAE3E,IAAM,YAAW,YAA+C;AAAA,EAsB9D,YAAY,CAAC,MAAM,WAAW,EAAE,MAAM,MAAM,MAAM,OAAO,KAAK,IAAI,EAAE;AAAA,EACpE,YAAY,MAAM;AAAA,EAMlB,YAAY,CAAC,WAAW,GAAG,MAAM,OAAO,KAAK,MAAM,KAAK,KAAK,IAAI;AAAA,EAUjE,SAAS,CAAC,YAAY,UAAU,WAAW,UAAU,MAAM;AAAA,EAC3D,MAAM,CAAC,UAAU,sBAAsB,MAAM;AAAA,EAQ7C,YAAY,CAAC,WAAW,EAAE,OAAO,KAAK,MAAM,KAAK,KAAK,IAAI;AAC5D,CAAC;AAEM,IAAM,0BAA0B,MAC5B,iBACP,iBACO,iBACL,gBAAgB,SAAS,GAAG;AAAA,KASvB;AAAA,EAEH,QAAQ,MAAa;AAAA,EAWrB,WAAkB,YAAG,UAAU,GAAG,QAAQ;AAAA,IACxC,MAAM,QAAQ,OAAO,UAAS,UAAU,EAAE,KAAK,CAAC;AAAA,IAChD,IAAI,MAAM,UAAU,KAAK,OAAO;AAAA,MAC9B,OAAO,OAAc,aACnB,IAAI,MACF,sBAAsB,KAAK,oDACzB,oBAAoB,MAAM,QAAQ,QAAQ,yBAC1C,GAAG,KAAK,QAAQ,QAAQ,yDACxB,4EACA,gFACA,yBACJ,CACF;AAAA,IACF;AAAA,IACA,OAAO;AAAA,GACR;AACH,CAAC,CACH,CACF;;AC/LF,qBAAS;AACT,uBAAS;AACT;AACA;;;ACTA;AAiBO,IAAM,UAAU,CACrB,MACA,UACiC;AAAA,EACjC,MAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,GACxC,OACC,CAAC,UAA4C,OAAO,UAAU,YAAY,UAAU,IACtF,EACC,KAAK,CAAC,UAAU,MAAM,YAAY,MAAM,IAAI;AAAA,EAC/C,IAAI,QAAQ;AAAA,IAAW;AAAA,EACvB,OAAO;AAAA,IACL,WAAW,KAAK,IAAI,YAAY;AAAA,IAChC,YAAY,IACV,MAAM,QAAQ,IAAI,aAAa,IAC3B,IAAI,cAAc,IAAI,CAAC,UAAmB,OAAO,KAAK,CAAC,IACvD,CAAC,CACP;AAAA,IACA,eAAe,KAAK,IAAI,gBAAgB;AAAA,IACxC,kBAAkB,IAAI,IAAI,qBAAqB,EAAE;AAAA,IACjD,MAAM,MAAM;AAAA,EACd;AAAA;AAWF,IAAM,eAAe;AACrB,IAAM,gBAAgB;AAGf,IAAM,aAAa,CAAC,UAAuB,SAAS,MAAM,gBAAgB,MAAM;AAGvF,IAAM,QAAQ,CAAC,MAAc,UAC3B,UAAU,YAAY,CAAC,IAAI,GAAG,OAAO,MAAM;AAMtC,IAAM,cAAa,CAAC,WAAgD;AAAA,KACtE,MAAM,eAAe,KAAK,MAAM,cAAc,CAAC;AAAA,KAC/C,MAAM,UAAU,MAAM,WAAW,YAAY,YAAY,OAAO,MAAM,MAAM,CAAC;AAClF;AAGA,IAAM,cAAc,CAAC,UAAuB;AAAA,EAC1C,MAAM,QAAQ;AAAA,IACZ,MAAM,oBAAoB,OAAO,mBAAmB;AAAA,IACpD,MAAM,uBAAuB,OAAO,sBAAsB;AAAA,EAC5D,EAAE,OAAO,CAAC,SAAS,SAAS,SAAS;AAAA,EACrC,OAAO,MAAM,WAAW,IAAI,WAAW,KAAK,IAAI,GAAG,WAAW,KAAK,KAAK,MAAM,KAAK,GAAG;AAAA;AAWxF,IAAM,SAAS,CAAC,QAAmB,MAAc,MAAc,SACtD,aAAI,UAAU,GAAG;AAAA,EACtB,SAAS,UAAU,EAAG,UAAU,eAAe,WAAW,GAAG;AAAA,IAC3D,MAAM,SAAS,OAAO,IACpB,QACA,QACA,OACA,SAAS,cAAc,mBAAmB,IAAI,UAChD,EAAE,KAIO,uBAAc,MAAG;AAAA,MAAG;AAAA,KAAS,CACtC;AAAA,IACA,IAAI,QAAQ,WAAW,WAAW;AAAA,MAChC,MAAM,OAAO,KAAK,OAAO,UAAU;AAAA,MACnC,IAAI,SAAS;AAAA,QAAM;AAAA,MACnB,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,kBAAkB,uBAAuB,SAAS,KAAK,mBAAmB,YAC3E,wFACA,yBAAyB,cAAc,aAC3C,CACF;AAAA,IACF;AAAA,IAGA,OAAc,eAAM,eAAe,IAAI;AAAA,EACzC;AAAA,EACA,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,kBAAkB,kCACnB,GAAG,OAAO,gBAAgB,YAAY,gBACtC,wFACA,uBAAuB,qEACvB,0DACJ,CACF;AAAA,CACD;AAYI,IAAM,WAAW,CAAC,UAChB,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,OAAO,KACX,OAAO,IAAY,MAAM,QAAQ,aAAa,QAAQ,WAAW,KAAK,GAAG,YAAW,KAAK,CAAC,CAC5F;AAAA,EAIA,IAAI,SAAS,IAAI;AAAA,IACf,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,WAAW,KAAK,4DACjB,iEACJ,CACF;AAAA,EACF;AAAA,EACA,OAAO,OAAO,MAAM,QAAQ,MAAM,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,CAChE;AAYI,IAAM,YAAY,CAAC,UACjB,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,OAAO,KAAK,OAAO,IAAY,MAAM,QAAQ,aAAa,UAAU,YAAY,KAAK,CAAC,CAAC;AAAA,EAK7F,IAAI,SAAS;AAAA,IAAI,OAAO,OAAO,MAAM,QAAQ,MAAM,MAAM,MAAM,YAAY,KAAK,CAAC;AAAA,CAClF;AAWI,IAAM,aAAa,CAAC,UACzB,IAAI,MACF,GAAG,WAAW,KAAK,8DACjB,aAAa,MAAM,sEACnB,yCACJ;AAEK,IAAM,eAAe,CAAC,UAC3B,IAAI,MACF,GAAG,WAAW,KAAK,iEACjB,aAAa,MAAM,6EACnB,yDACJ;;;ADxGK,IAAM,gBAAgB,UAAwB,kBAAkB;AAAA,EACrE,sBAAsB;AACxB,CAAC;AAED,IAAM,OAAM,cAA6C;AAAA,EACvD,YAAY;AAAA,EAEZ,YAAY;AAAA,EACZ;AAAA,EAiBA,SAAS,MAAM;AAAA,EAEf,MAAM,CAAC,UAAU,SAAS,MAAM;AAClC,CAAC;AAEM,IAAM,wBAAwB,MAC1B,iBACP,eACO,iBACL,cAAc,SAAS,GAAG;AAAA,EAMxB,MAAM,MAAa,iBAAQ,CAAC,CAAC;AAAA,EAC7B,MAAa,YAAG,UAAU,GAAG,QAAQ;AAAA,IACnC,OAAO,OAAO,KAAI,KAAK,IAAI;AAAA,GAC5B;AAAA,EAcD,MAAa,YAAG,UAAU,GAAG,MAAM,UAAU;AAAA,IAC3C,IAAI,WAAW,aAAa,YAAW,IAAI,KAAK,KAAK,SAAS,OAAO,MAAM;AAAA,MACzE,OAAO,EAAE,QAAQ,UAAU;AAAA,IAC7B;AAAA,IACA,OAAO,OAAO,KAAI,KAAK,MAAM,MAAM;AAAA,GACpC;AAAA,EAWD,WAAkB,YAAG,UAAU,GAAG,QAAQ;AAAA,IACxC,MAAM,OAAO,OAAO,KAAI,KAAK,IAAI;AAAA,IACjC,IAAI,SAAS;AAAA,MAAW,OAAO;AAAA,IAC/B,OAAO,SAAS,IAAI;AAAA,IACpB,MAAM,QAAQ,OAAO,KAAI,KAAK,IAAI;AAAA,IAClC,IAAI,UAAU;AAAA,MAAW,OAAO,OAAc,aAAI,WAAW,IAAI,CAAC;AAAA,IAClE,OAAO;AAAA,GACR;AAAA,EASD,QAAe,YAAG,UAAU,GAAG,QAAQ;AAAA,IAWrC,KAAK,OAAO,KAAI,KAAK,IAAI,OAAO;AAAA,MAAW;AAAA,IAC3C,OAAO,UAAU,IAAI;AAAA,IACrB,MAAM,QAAQ,OAAO,KAAI,KAAK,IAAI;AAAA,IAClC,IAAI,UAAU;AAAA,MAAW,OAAO,OAAc,aAAI,aAAa,IAAI,CAAC;AAAA,GACrE;AACH,CAAC,CACH,CACF;;AEpNF,qBAAS;AACT,uBAAS;AAET;AACA;;;ACkBA;AAQA,IAAM,WAAW,CAAC,UACf,MAAM,QAAQ,KAAK,WAAW,IAAI,KAAK,cAAc,CAAC,GAAG,OACxD,CAAC,UAA8B,OAAO,UAAU,YAAY,UAAU,IACxE;AAeK,IAAM,YAAY,CAAC,SAAiC;AAAA,EACzD,MAAM,OAAO,KAAK;AAAA,EAClB,MAAM,QAAmB,OAAO,SAAS,YAAY,SAAS,OAAQ,OAAqB,CAAC;AAAA,EAC5F,MAAM,OAAO,CAAC,SAAiC;AAAA,IAC7C,GAAI,KAAK,YAAY,QAAQ,CAAC,IAAI,IAAI,CAAC;AAAA,IACvC,GAAG,SAAS,IAAI,EAAE,QAAQ,IAAI;AAAA,EAChC;AAAA,EACA,OAAO,KAAK,KAAK;AAAA;AAoBZ,IAAM,UAAU,CAAC,MAAiB,UACvC,UAAU,IAAI,EAAE,KAAK,CAAC,SAAS,IAAI,KAAK,OAAO,OAAO,GAAG,MAAM,KAAK;AAkD/D,IAAM,gBAAgB,CAAC,MAAiB,WAA4C;AAAA,EACzF,cAAc,KAAK,KAAK,qBAAqB;AAAA,EAC7C,cAAc,IAAI,KAAK,iBAAiB,CAAC;AAAA,EACzC,cAAc,KAAK,KAAK,eAAe;AAAA,EACvC,MAAM,KAAK,KAAK,OAAO;AAAA,EACvB,IAAI,KAAK,KAAK,KAAK;AAAA,EACnB,MAAM,KAAK,KAAK,SAAS,OAAO,OAAO,MAAM,KAAK,GAAG;AAAA,EACrD,OAAO,IAAI,KAAK,OAAO,MAAM,KAAK;AAAA,EAClC,SAAS,KAAK,KAAK,UAAU;AAAA,EAC7B,KAAK,IAAI,KAAK,QAAQ,CAAC;AAAA,EACvB,UAAU,IAAI,KAAK,aAAa,CAAC;AAAA,EACjC,QAAQ,KAAK,KAAK,SAAS;AAAA,EAC3B,aAAa,IAAI,KAAK,gBAAgB,CAAC;AACzC;AAWO,IAAM,UAAU,CAAC,UACtB,IAA6B,MAAM,QAAQ,QAAQ,OAAO,SAAS,MAAM,eAAe,EAAE,KACjF,aAAI,CAAC,MAAM;AAAA,EAChB,MAAM,OAAO,MAAM,YAAY,YAAY,QAAQ,GAAG,MAAM,KAAK;AAAA,EACjE,OAAO,SAAS,YAAY,YAAY,cAAc,MAAM,KAAK;AAAA,CAClE,CACH;;;ACnLF;AAKA,IAAM,WAAW,CAAC,UAAwB,OAAO,OAAO,MAAM,KAAK,QAAQ,MAAM;AAE1E,IAAM,YAAmB,YAAG,UAAU,CAAC,MAAoB;AAAA,EAChE,IAAI,KAAK,QAAQ,WAAW;AAAA,IAC1B,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,SAAS,IAAI,oEACd,kFACA,oFACA,qFACA,cACJ,CACF;AAAA,EACF;AAAA,EACA,OAAO,IAAI,KAAK,QAAQ,aAAa,QAAQ,SAAS,KAAK,iBAAiB;AAAA,IAC1E,KAAK,KAAK;AAAA,OACN,KAAK,iBAAiB,YAAY,CAAC,IAAI,EAAE,sBAAsB,KAAK,aAAa;AAAA,EACvF,CAAC;AAAA,EACD,MAAM,UAAU,OAAO,QAAQ,IAAI;AAAA,EACnC,IAAI,YAAY,WAAW;AAAA,IACzB,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,SAAS,IAAI,2CAA2C,OAAO,KAAK,KAAK,UAC1E,sFACA,wFACA,uDACJ,CACF;AAAA,EACF;AAAA,EACA,OAAO;AAAA,CACR;AAoBM,IAAM,aAAa,CAAC,SACzB,IACE,KAAK,QACL,aACA,UACA,SAAS,KAAK,iBAAiB,OAAO,KAAK,KAAK,KAChD,KAAK,YAAY,OAAO,EAAE,SAAS,IAAI,IAAI,CAAC,CAC9C;;;AFsCK,IAAM,iBAAiB,UAAyB,mBAAmB;AAAA,EACxE,sBAAsB;AACxB,CAAC;AAuBD,IAAM,UAAU,CAAC,MAAyB,WACvC,MAAM,SAAS,aAAa,MAAM,SAAS,KAAK,UAChD,MAAM,iBAAiB,aAAa,MAAM,iBAAiB,KAAK;AAEnE,IAAM,YAAW,CAAC,UAChB,OAAO,OAAO,MAAM,KAAK,qBAAqB,MAAM;AAetD,IAAM,YAAW;AAAA,EAOf,MAAM,MAAa,iBAAQ,CAAC,CAAC;AAAA,EAE7B,MAAa,YAAG,UAAU,GAAG,QAAgC;AAAA,IAC3D,OAAO,OAAO,QAAQ,IAAI;AAAA,GAC3B;AAAA,EAUD,MAAa,YAAG,UAAU;AAAA,IACxB;AAAA,IACA;AAAA,KAIC;AAAA,IACD,IAAI,WAAW,aAAa,CAAC,YAAW,IAAI;AAAA,MAAG;AAAA,IAC/C,MAAM,OAAO,OAAO,QAAQ,IAAI;AAAA,IAChC,IAAI,SAAS;AAAA,MAAW,OAAO,EAAE,QAAQ,SAAS;AAAA,IAQlD,IAAI,OAAO,UAAU,KAAK;AAAA,MAAO,OAAO,EAAE,QAAQ,SAAS;AAAA,IAC3D,OAAO,QAAQ,MAAM,IAAI,IAAK,EAAE,QAAQ,OAAO,IAAe,EAAE,QAAQ,SAAS;AAAA,GAClF;AAAA,EAOD,WAAkB,YAAG,UAAU,GAAG,QAAgC;AAAA,IAChE,MAAM,OAAO,OAAO,QAAQ,IAAI;AAAA,IAChC,IAAI,SAAS,WAAW;AAAA,MAUtB,OAAO,OAAO,UAAU,IAAI;AAAA,IAC9B;AAAA,IACA,IAAI,CAAC,QAAQ,MAAM,IAAI,GAAG;AAAA,MACxB,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,UAAS,IAAI,8CAA8C,KAAK,UACjE,gBAAgB,KAAK,8DACrB,wFACA,6DACJ,CACF;AAAA,IACF;AAAA,IACA,OAAO;AAAA,GACR;AAAA,EAOD,QAAe,YAAG,UAAU,GAAG,QAAgC;AAAA,IAC7D,OAAO,WAAW,IAAI;AAAA,GACvB;AACH;AAEO,IAAM,yBAAyB,MAC3B,iBAAO,gBAAuB,iBAAQ,eAAe,SAAS,GAAG,SAAQ,CAAC,CAAC;;AG9MtF,qBAAS;AACT;AACA;;;ACyGO,IAAM,SAAS,CAAC,UAAyB,SAAS,MAAM,kBAAkB,MAAM;AAChF,IAAM,aAAa,CAAC,UAAyB,SAAS,MAAM;AAQ5D,IAAM,QAAQ;AAGd,IAAM,eAAe,CAAC,UAC3B,IAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC,UAAmB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;AAQvE,IAAM,OAAO,CAAI,UAAyB,MAAS,SAAS,UACjE,aAAa,aAAa,UAAU,aAAa;AAc5C,IAAM,OAAO,CAAC,UAA+B,UAAU,IAAI,YAAY;AAG9E,IAAM,SAAQ,CAAC,MAAc,UAC3B,UAAU,YAAY,CAAC,IAAI,GAAG,OAAO,MAAM;AAG7C,IAAM,UAAU,CAAC,UAA+B,UAAU,YAAY,YAAY,OAAO,KAAK;AAmCvF,IAAM,aAAa,CAAC,WAAkD;AAAA,KACxE,OAAM,cAAc,MAAM,UAAU;AAAA,KACpC,OAAM,YAAY,QAAQ,MAAM,QAAQ,CAAC;AAAA,KACzC,OAAM,qBAAqB,MAAM,iBAAiB;AAAA,KAClD,OAAM,cAAc,QAAQ,MAAM,UAAU,CAAC;AAAA,KAC7C,OAAM,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAAA,KACjC,OAAM,eAAe,QAAQ,MAAM,WAAW,CAAC;AAAA,KAC/C,OAAM,qBAAqB,QAAQ,MAAM,iBAAiB,CAAC;AAChE;AAYO,IAAM,aAAa,CAAC,WAAkD;AAAA,KACxE,WAAW,KAAK;AAAA,KAChB,OAAM,eAAe,MAAM,WAAW;AAAA,KACtC,OAAM,UAAU,QAAQ,MAAM,MAAM,CAAC;AAAA,EACxC,cAAc;AAAA,EACd,MAAM,MAAM;AACd;;;AChPA;AAuBO,IAAM,gBAAgB,CAAC,OAAsB,aAAoB,cACtE,IAA8B,MAAM,QAAQ,QAAQ,OAAO,WAAU,EAAE,KAC9D,iBAAQ,CAAC,UACb,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,cAAc,MAAM,IAAI,IAC5C,aACL,IAAI,MACF,GAAG,8EACD,oFACA,oFACA,6CACJ,CACF,IACO,aACb,CACF;AAYK,IAAM,UAAS,CACpB,OACA,MACA,SAEO,aAAI,UAAU,GAAG;AAAA,EACtB,SAAS,UAAU,EAAG,UAAU,IAAI,WAAW,GAAG;AAAA,IAChD,MAAM,OAAO,OAAO,KAAK,KAAK;AAAA,IAC9B,IAAI,KAAK,IAAI;AAAA,MAAG,OAAO;AAAA,IACvB,OAAc,eAAM,WAAW;AAAA,EACjC;AAAA,EACA,OAAO,OAAO,KAAK,KAAK;AAAA,CACzB;;;AFAI,IAAM,kBAAkB,UAA0B,oBAAoB;AAAA,EAC3E,sBAAsB;AACxB,CAAC;AAED,IAAM,OAAmD;AAAA,EAQvD,YAAY,CAAC,MAAM,UAAU;AAAA,IAC3B,IAAI,OAAO,KAAK,YAAY;AAAA,MAAU;AAAA,IACtC,OAAO;AAAA,MACL,cAAc,aAAa,KAAK,mBAAmB;AAAA,MACnD,YAAY,KAAK,KAAK,aAAa;AAAA,MACnC,IAAI,IAAI,KAAK,OAAO,KAAK;AAAA,MACzB,UAAU,IAAI,KAAK,aAAa,KAAK;AAAA,MACrC,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,UAAU,KAAK,KAAK,WAAW;AAAA,MAC/B,YAAY,KAAK,KAAK,aAAa;AAAA,MACnC,cAAc,KAAK,KAAK,eAAe;AAAA,MACvC,mBAAmB,KAAK,KAAK,oBAAoB;AAAA,MACjD,QAAQ,IAAI,KAAK,WAAW,KAAK;AAAA,MACjC,YAAY,IAAI,KAAK,eAAe,KAAK;AAAA,MACzC,MAAM,IAAI,KAAK,SAAS,KAAK;AAAA,MAC7B,aAAa,IAAI,KAAK,gBAAgB,KAAK;AAAA,MAC3C,mBAAmB,IAAI,KAAK,sBAAsB,KAAK;AAAA,IACzD;AAAA;AAAA,EAEF;AAAA,EACA,YAAY;AAAA,EAYZ,SAAS,CAAC,YAAY,UACpB,KAAK,MAAM,MAAM,WAAW,MAAM,WAAW,YAAY,KACzD,KAAK,MAAM,UAAU,WAAW,UAAU,WAAW,YAAY,KACjE,KAAK,MAAM,mBAAmB,WAAW,iBAAiB,KAC1D,KAAK,KAAK,MAAM,UAAU,GAAG,WAAW,YAAY,WAAW,UAAU,KACzE,KAAK,MAAM,YAAY,WAAW,YAAY,WAAW,eAAe,EAAE,KAC1E,KAAK,KAAK,MAAM,WAAW,GAAG,WAAW,WAAW;AAAA,EAUtD,MAAM,CAAC,UAAU,GAAG,OAAO,KAAK;AAAA,EAChC,YAAY;AACd;AAEA,IAAM,OAAM,cAAc,IAAI;AAEvB,IAAM,0BAA0B,MAC5B,iBACP,iBACO,iBACL,gBAAgB,SAAS,GAAG;AAAA,EAE1B,MAAM,MAAa,iBAAQ,CAAC,CAAC;AAAA,EAC7B,MAAM,GAAG,WAAW,KAAI,KAAK,IAAI;AAAA,EACjC,MAAM,GAAG,MAAM,aAAa,KAAI,KAAK,MAAM,MAAM;AAAA,EAMjD,WAAkB,YAAG,UAAU,GAAG,QAAQ;AAAA,IACxC,MAAM,OAAO,OAAO,KAAI,KAAK,IAAI;AAAA,IACjC,IAAI;AAAA,IACJ,IAAI,SAAS,WAAW;AAAA,MACtB,OAAO,cAAc,MAAM,KAAK,WAAW,IAAI,GAAG,OAAO,IAAI,CAAC;AAAA,MAC9D,OAAO,OAAO,IACZ,KAAK,QACL,aACA,QACA,WAAW,IAAI,GACf,WAAW,IAAI,CACjB;AAAA,IACF,EAAO;AAAA,MAQL,MAAM,OAAO,WAAW,KAAK,SAAS,MAAM,MAAM,WAAW,IAAI,CAAC;AAAA,MAClE,IAAI,SAAS,WAAW;AAAA,QACtB,OAAO,OAAO,IAAY,KAAK,QAAQ,aAAa,OAAO,OAAO,IAAI,GAAG,IAAI;AAAA,MAC/E;AAAA;AAAA,IAEF,MAAM,QAAQ,OAAO,QACnB,MACA,KAAI,MACJ,CAAC,QAAQ,QAAQ,aAAa,KAAK,QAAQ,KAAK,IAAI,CACtD;AAAA,IACA,IAAI,UAAU,aAAa,CAAC,KAAK,QAAQ,OAAO,IAAI,GAAG;AAAA,MACrD,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,OAAO,IAAI,sEACZ,qDAAqD,QAAQ,sBAC7D,8EACA,UACJ,CACF;AAAA,IACF;AAAA,IACA,OAAO;AAAA,GACR;AAAA,EAYD,QAAe,YAAG,UAAU,GAAG,QAAQ;AAAA,IACrC,OAAO,IAAY,KAAK,QAAQ,aAAa,UAAU,OAAO,IAAI,CAAC;AAAA,IACnE,MAAM,OAAO,OAAO,QAAO,MAAM,KAAI,MAAM,CAAC,QAAQ,QAAQ,SAAS;AAAA,IACrE,IAAI,SAAS,WAAW;AAAA,MACtB,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,OAAO,IAAI,uEACZ,iFACA,mCACJ,CACF;AAAA,IACF;AAAA,GACD;AACH,CAAC,CACH,CACF;;AG5HF,qBAAS;AACT,uBAAS;AACT;AACA;;;AChEO,IAAM,OAAO,CAAC,SAAiB,KAAK,KAAK,EAAE,YAAY;AAyBvD,IAAM,OAAO,CAAC,UAAmB;AAAA,EACtC,MAAM,MAAM,KAAK,KAAK,EAAE,KAAK;AAAA,EAC7B,MAAM,OAAO,IAAI,SAAS,GAAG,IAAI,SAAS;AAAA,EAC1C,OAAO,IAAI,SAAS,IAAI,IAAI,IAAI,MAAM,GAAG,CAAC,KAAK,MAAM,IAAI;AAAA;AAwBpD,IAAM,UAAU,CAAC,UAAmB;AAAA,EACzC,MAAM,UAAU,KAAK,KAAK,EAAE,KAAK;AAAA,EACjC,OAAO,YAAY,MAAM,KAAK;AAAA;AAqBzB,IAAM,OAAO,CAAC,UAAsD;AAAA,EACzE,MAAM,OAAO,QAAQ,MAAM,OAAO;AAAA,EAClC,OAAO,EAAE,MAAM,KAAK,MAAM,IAAI,MAAO,SAAS,KAAK,CAAC,IAAI,EAAE,SAAS,KAAK,EAAG;AAAA;;;ADkCtE,IAAM,uBAAuB,UAA+B,uBAAuB;AAE1F,IAAM,YAAW,YAAyD;AAAA,EAQxE,YAAY,CAAC,MAAM,UAAU;AAAA,IAC3B,MAAM,UAAU,KAAK,KAAK,OAAO;AAAA,IACjC,OAAO,YAAY,KACf,YACA;AAAA,MACE,MAAM;AAAA,MACN,SAAS,QAAQ,KAAK,UAAU;AAAA,MAChC,WAAW,IAAI,KAAK,cAAc,CAAC;AAAA,MAEnC,MAAM,KAAK,KAAK,SAAS,MAAM,IAAI;AAAA,IACrC;AAAA;AAAA,EAEN,YAAY,MAAM;AAAA,EAClB,YAAY,CAAC,WAAW,KAAK,KAAK,KAAK,GAAG,MAAM,MAAM,KAAK;AAAA,EAO3D,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,SAAS,CAAC,YAAY,UACpB,WAAW,SAAS,KAAK,MAAM,IAAI,KAAK,WAAW,YAAY,QAAQ,MAAM,OAAO;AAAA,EAKtF,MAAM,CAAC,UAAU,4BAA4B,MAAM;AAAA,EACnD,YAAY;AACd,CAAC;AASM,IAAM,+BAA+B,MACjC,iBACP,sBACO,iBACL,qBAAqB,SAAS,GAAG;AAAA,KAC5B;AAAA,EACH,MAAa,YAAG,UAAU,GAAG,MAAM,UAAU;AAAA,IAI3C,IAAI,WAAW,aAAa,YAAW,IAAI,KAAK,KAAK,KAAK,IAAI,MAAM,KAAK,OAAO,IAAI,GAAG;AAAA,MACrF,OAAO,EAAE,QAAQ,UAAU;AAAA,IAC7B;AAAA,IACA,OAAO,OAAO,UAAS,KAAK,EAAE,MAAM,OAAO,CAAC;AAAA,GAC7C;AACH,CAAC,CACH,CACF;;AElJF,qBAAS;AACT;AACA;AAqCO,IAAM,eAAe,WAAuB,iBAAiB;AAAA,EAClE,sBAAsB;AACxB,CAAC;AAwBD,IAAM,gBAAgB,CAAC,aAAgC;AAAA,EACrD,MAAM,QAAQ,YAAW;AAAA,EACzB,OAAO,UAAU,MAAM,KAAK;AAAA;AAiB9B,IAAM,aAAa,CAAC,SAA4B;AAAA,EAC9C,MAAM,SAAS,KAAK,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,CAAC,WAAoB,KAAK,MAAM,CAAC,CAAC;AAAA,EAC3F,OAAO,WAAW,KAAK,CAAC,IAAI,OAAO,MAAM,GAAG;AAAA;AAG9C,IAAM,YAAW,YAAyC;AAAA,EACxD,YAAY,CAAC,MAAM,WAAW;AAAA,IAS5B,SAAS,KAAK,KAAK,UAAU;AAAA,IAE7B,SAAS,MAAM;AAAA,IACf,SAAS,WAAW,KAAK,UAAU;AAAA,EACrC;AAAA,EACA,YAAY,MAAM;AAAA,EAClB,YAAY,CAAC,WAAW,EAAE,SAAS,cAAc,MAAM,OAAO,GAAG,SAAS,MAAM,QAAQ;AAAA,EAQxF,UAAU,EAAE,QAAQ,2BAA2B,QAAQ,mCAAmC;AAAA,EAC1F,SAAS,CAAC,YAAY,UAAU,WAAW,YAAY,cAAc,MAAM,OAAO;AAAA,EAClF,MAAM,CAAC,UAAU,iBAAiB,MAAM;AAAA,EAQxC,YAAY,CAAC,WAAW,EAAE,SAAS,cAAc,MAAM,OAAO,EAAE;AAClE,CAAC;AASM,IAAM,uBAAuB,MACzB,kBAAO,cAAqB,iBAAQ,aAAa,SAAS,GAAG,SAAQ,CAAC,CAAC;;AC1JlF,qBAAS;AACT;AACA;AAmDO,IAAM,oBAAoB,WAA4B,oBAAoB;AAGjF,IAAM,OAAO,CAAC,QAAgB,IAAI,MAAM,GAAG,EAAE,MAAM;AAOnD,IAAM,SAAQ,CAAC,WAA4B;AAAA,EACzC,SAAS,MAAM,WAAW;AAAA,EAC1B,cAAc,OAAO,MAAM,gBAAgB,CAAC;AAAA,EAC5C,aAAa,OAAO,MAAM,eAAe,CAAC;AAAA,EAC1C,OAAO,MAAM,SAAS;AACxB;AAEA,IAAM,YAAW,YAAmD;AAAA,EAClE,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,SAAS,OAAO,KAAK,eAAe,WAAW,KAAK,aAAa;AAAA,IACjE,cAAc,IAAI,KAAK,iBAAiB,CAAC;AAAA,IACzC,aAAa,IAAI,KAAK,gBAAgB,CAAC;AAAA,IACvC,KAAK,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS,MAAM;AAAA,IAC3D,OAAO,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;AAAA,IAC3D,MAAM,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU,KAAK,MAAM,GAAG;AAAA,EACxE;AAAA,EAMA,YAAY,MAAM;AAAA,EAClB,YAAY,CAAC,WAAW,KAAK,OAAM,KAAK,GAAG,KAAK,MAAM,IAAI;AAAA,EAS1D,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,SAAS,CAAC,YAAY,UACpB,WAAW,WAAW,MAAM,SAAS,cACrC,WAAW,aAAa,MAAM,WAAW,OACzC,WAAW,iBAAiB,MAAM,eAAe,MACjD,WAAW,kBAAkB,MAAM,gBAAgB;AAAA,EAMrD,MAAM,CAAC,UAAU,wBAAwB,MAAM;AAAA,EAC/C,YAAY;AACd,CAAC;AAiBM,IAAM,4BAA4B,MAC9B,kBAAO,mBAA0B,iBAAQ,kBAAkB,SAAS,GAAG,SAAQ,CAAC,CAAC;;ACpG5F,qBAAS;AACT;AACA;;;ACzCO,IAAM,WAAW,CAAC,UAAkD;AAAA,EACzE,MAAM,SAAS,IAAI;AAAA,EACnB,WAAW,SAAS,OAAO,UAAU,WAAW,MAAM,MAAM,GAAG,IAAK,SAAS,CAAC,GAAI;AAAA,IAChF,OAAO,MAAM,YAAY,MAAM,KAAK,EAAE,MAAM,GAAG;AAAA,IAC/C,IAAI,SAAS,aAAa,SAAS;AAAA,MAAI;AAAA,IACvC,MAAM,OAAO,OAAO,SAAS,YAAY,KAAK,EAAE;AAAA,IAChD,OAAO,IAAI,MAAM,OAAO,MAAM,IAAI,IAAI,IAAI,IAAI;AAAA,EAChD;AAAA,EACA,OAAO,CAAC,GAAG,OAAO,KAAK,CAAC,EACrB,KAAK,EACL,IAAI,CAAC,SAAS;AAAA,IACb,MAAM,OAAO,OAAO,IAAI,IAAI,KAAK;AAAA,IACjC,OAAO,SAAS,IAAI,OAAO,GAAG,QAAQ,OAAO,IAAI;AAAA,GAClD,EACA,KAAK,GAAG;AAAA;AAYN,IAAM,WAAW,CAAC,UACvB,OAAO,UAAU,YAAY,UAAU,OACnC,OAAO,QAAQ,KAAK,EACjB,IAAI,EAAE,QAAQ,aAAa,GAAG,WAAW,OAAO,OAAO,GAAG,EAC1D,KAAK,EACL,KAAK,IAAI,IACZ;AAqBC,IAAM,cAAc,CAAC,QAC1B,IAAI,QAAQ,yBAAyB,CAAC,YAAY;AAAA,EAChD,IAAI;AAAA,IACF,OAAO,mBAAmB,OAAO;AAAA,IACjC,MAAM;AAAA,IACN,OAAO;AAAA;AAAA,CAEV;AAuBI,IAAM,WAAW,CAAC,WAA4D;AAAA,EACnF,SAAS,MAAM,YAAY,aAAa,MAAM,YAAY,KAAK,YAAY,MAAM;AAAA,EACjF,SAAS,MAAM,YAAY,OAAO,MAAM;AAAA,KACpC,MAAM,SAAS,kBAAkB,EAAE,QAAQ,MAAM,WAAW,OAAO,MAAM,UAAU,IAAI,CAAC;AAC9F;AA8BA,IAAM,WAAW,CAAC,WAAgD;AAAA,EAChE,UAAU,MAAM;AAAA,EAChB,WAAW,IAAI,MAAM,SAAS;AAAA,EAC9B,MAAM,MAAM;AAAA,KACR,MAAM,SAAS,kBAAkB,EAAE,OAAO,SAAS,MAAM,KAAK,EAAE,IAAI,CAAC;AAC3E;AAEO,IAAM,QAAO,CAAC,UAAuB;AAAA,EAC1C,MAAM,SAAS,SAAS,KAAK;AAAA,EAC7B,YAAY,QAAQ,UAAU,OAAO,QAAQ,SAAS,KAAK,CAAC,GAAG;AAAA,IAC7D,IAAI,UAAU;AAAA,MAAW,OAAO,UAAU;AAAA,EAC5C;AAAA,EACA,OAAO;AAAA;AAiBF,IAAM,YAAY,CAAC,UACxB,OAAO,QAAQ,SAAS,KAAK,CAAC,EAC3B,OAAO,IAAI,WAAW,UAAU,SAAS,EACzC,IAAI,EAAE,YAAY,MAAM;;;ADtBtB,IAAM,gBAAgB,WAAwB,gBAAgB;AAErE,IAAM,YAAW,YAA2C;AAAA,EAC1D,YAAY,CAAC,MAAM,UAAU;AAAA,IAO3B,MAAM,WAAW,KAAK,KAAK,OAAO;AAAA,IAClC,IAAI,aAAa,MAAM,aAAa,MAAM;AAAA,MAAM;AAAA,IAChD,OAAO;AAAA,MACL,UAAU,KAAK,KAAK,WAAW;AAAA,MAC/B,SAAS,KAAK,KAAK,UAAU;AAAA,MAO7B,SAAS,KAAK,KAAK,YAAY,KAAK;AAAA,MACpC,QAAQ,SAAS,KAAK,SAAS;AAAA,MAC/B,OAAO,SAAS,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW,SAAS;AAAA,MAC7E,OAAO,IAAI,KAAK,UAAU,CAAC;AAAA,MAC3B,WAAW,IAAI,OAAO,KAAK,iBAAiB,WAAW,KAAK,eAAe,SAAS;AAAA,MACpF,MAAM,KAAK,KAAK,SAAS,MAAM,IAAI;AAAA,MACnC,QAAQ,KAAK,KAAK,WAAW,KAAK;AAAA,MAClC,MAAM,MAAM;AAAA,IACd;AAAA;AAAA,EAEF,YAAY,MAAM;AAAA,EAGlB,YAAY,CAAC,WAAW,KAAK,MAAK,KAAK,GAAG,MAAM,MAAM,KAAK;AAAA,EAa3D,UAAU,EAAE,QAAQ,8BAA8B,QAAQ,mCAAmC;AAAA,EAC7F,SAAS,CAAC,YAAY,UACpB,WAAW,cAAc,IAAI,MAAM,SAAS,KAC5C,WAAW,aAAa,MAAM,YAC9B,WAAW,YAAY,YAAY,MAAM,WAAW,EAAE,KACtD,WAAW,aAAa,MAAM,YAAY,UACzC,MAAM,SAAS,mBACb,WAAW,UAAU,SAAS,MAAM,KAAK,KACxC,WAAW,YAAY,MAAM,WAAW;AAAA,EAE9C,MAAM,CAAC,UAAU,oBAAoB,MAAM;AAAA,EAO3C,YAAY,CAAC,UAAU;AAAA,IACrB,MAAM,QAAQ,UAAU,KAAK;AAAA,IAC7B,MAAM,SAAS,MAAK,KAAK;AAAA,IACzB,OAAO,WAAW,QAAQ,KAAK;AAAA;AAEnC,CAAC;AASM,IAAM,wBAAwB,MAC1B,kBAAO,eAAsB,iBAAQ,cAAc,SAAS,GAAG,SAAQ,CAAC,CAAC;;AErNpF,qBAAS;AACT,uBAAS;AAET;AACA;;;ACvBA;AACA;AACA,kBAAS;AACT;AACA;;;ACiBA;AACA;AACA;AACA;AAEA,IAAM,SAAS;AACf,IAAM,aAAa;AAEnB,IAAM,OAAO,CAAC,KAAa,eAClB,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,MAAM,OAAc,uBAAc,SAAS;AAAA,EACjD,IAAW,eAAO,GAAG;AAAA,IAAG,OAAO,IAAI,MAAM,IAAI,KAAK,UAAU;AAAA,CAC7D;AAEH,IAAM,QAAQ,CAAC,KAAa,eACnB,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,MAAM,OAAc,uBAAc,SAAS;AAAA,EACjD,IAAW,eAAO,GAAG;AAAA,IAAG,OAAO;AAAA,EAC/B,QAAQ,OAAO,IAAI,MAAM,IAAY,GAAG,OAAO;AAAA,CAChD;AAOI,IAAM,iBAAiB,CAAC,eACtB,YAAG,KAAK,YAAY,UAAU,GAAG,SAAS;AAQ5C,IAAM,aAAa,CACxB,YACA,cAEO,aAAI,UAAU,GAAG;AAAA,EACtB,OAAO,eAAe,UAAU;AAAA,EAChC,MAAM,WAAW,MAAa,iBAAQ,SAAS;AAAA,EAC/C,MAAM,QAAQ,OAAO,UAAU,KAAY,eAAM,QAAQ,GAAU,qBAAY,QAAQ,CAAC;AAAA,EACxF,IAAI,UAAU,aAAa,UAAU,QAAQ,QAAQ,GAAG,KAAK;AAAA,IAAG;AAAA,EAChE,OAAO,KAAK,QAAQ,UAAU;AAAA,CAC/B;AAGI,IAAM,UAAU,CAAC,eAA+C,MAAM,QAAQ,UAAU;AAGxF,IAAM,aAAa,CAAC,eACzB,MAAM,YAAY,UAAU;AAuBvB,IAAM,iBAAiB,CAAsB,YAAkB;AAAA,EACpE,QAAQ,MAAM,SAAS;AAAA,EACvB,IAAI,SAAS,aAAa,SAAS;AAAA,IAAW,OAAO;AAAA,EACrD,MAAM,UAA6B,CAAC,UAC3B,aAAI,UAAU,GAAG;AAAA,IACtB,IAAI,MAAM,WAAW,WAAW;AAAA,MAC9B,OAAO,WAAW,MAAM,YAAY,KAAK,KAAK,OAAO,QAAQ,UAAU,CAAC,CAAC;AAAA,IAC3E;AAAA,IACA,OAAO,OAAO,KAAK,KAAK;AAAA,GACzB;AAAA,EAEH,OAAO,KAAK,SAAS,MAAM,QAAQ;AAAA;;;ACxGrC,kBAAS;AACT;AACA;AACA;;;ACIA;AACA;AACA;AAEA,IAAM,SAAS,CAAC,UAA4B,UAAU,aAAa,UAAU;AAE7E,IAAM,gBAAgB,CAAC,UAAqD;AAAA,EAC1E,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK;AAAA,IAAG,OAAO;AAAA,EAChF,MAAM,QAAiB,OAAO,eAAe,KAAK;AAAA,EAClD,OAAO,UAAU,OAAO,aAAa,UAAU;AAAA;AAO1C,IAAM,UAAU,CAAC,UAAmB,aAA+B;AAAA,EACxE,IAAI,OAAO,QAAQ;AAAA,IAAG,OAAO;AAAA,EAC7B,IAAI,OAAO,QAAQ;AAAA,IAAG,OAAO;AAAA,EAC7B,IAAI,MAAM,QAAQ,QAAQ,GAAG;AAAA,IAC3B,IAAI,CAAC,MAAM,QAAQ,QAAQ;AAAA,MAAG,OAAO;AAAA,IACrC,OAAO,SAAS,MAAM,CAAC,MAAM,UAAU,QAAQ,MAAM,SAAS,MAAM,CAAC;AAAA,EACvE;AAAA,EACA,IAAI,cAAc,QAAQ,GAAG;AAAA,IAC3B,IAAI,CAAC,cAAc,QAAQ;AAAA,MAAG,OAAO;AAAA,IACrC,OAAO,OAAO,QAAQ,QAAQ,EAAE,MAAM,EAAE,KAAK,UAAU,QAAQ,MAAM,SAAS,IAAI,CAAC;AAAA,EACrF;AAAA,EACA,OAAO;AAAA;AASF,IAAM,mBAAmB,CAAC,KAAa,UACrC,aAAW,uBAAc,KAAK,GAAG,CAAC,UAAU;AAAA,EACjD,MAAM,WAAkB,eAAO,KAAK,IAAI,MAAM,MAAM,UAAU,OAAO;AAAA,EACrE,OAAO,aAAa,aAAa,QAAQ,SAAS,OAAO,KAAK;AAAA,CAC/D;;;ADtCH,IAAM,UAAiB,aAAI,UAAU,GAAG;AAAA,EACtC,MAAM,QAAQ,OAAc,uBAAc,MAAK;AAAA,EAC/C,MAAM,QAAQ,OAAc,uBAAc,KAAK;AAAA,EAC/C,IAAW,eAAO,KAAK,KAAY,eAAO,KAAK;AAAA,IAAG;AAAA,EAClD,MAAM,QAAsB,OAAO,MAAM;AAAA,EACzC,MAAM,QAAe,EAAE,OAAO,MAAM,MAAM,MAAM,OAAO,MAAM,MAAM,MAAM;AAAA,EACzE,OAAO,EAAE,WAAW,MAAM,MAAM,WAAW,OAAO,MAAM;AAAA,CACzD;AAED,IAAM,QAAQ,CAAC,OAAqB,OAAc,QAChD,MAAM,IAAI,KAAK,OAAO,IAAI,CAAC,EAAE,KAAY,uBAAc,MAAG;AAAA,EAAG;AAAA,CAAS,CAAC;AAczE,IAAM,eAAe,CAAC,KAAc,eAA+C;AAAA,EACjF,IAAI,KAAK;AAAA,EACT,OAAO,OAAO,OAAO,YAAY,OAAO,QAAQ,CAAC,cAAc,EAAW,GAAG;AAAA,IAC3E,MAAM,aAAa;AAAA,IACnB,IAAI,WAAW,eAAe;AAAA,MAAY,OAAO;AAAA,IACjD,KAAK,WAAW;AAAA,EAClB;AAAA,EACA;AAAA;AAIK,IAAM,qBAAqB,CAChC,KACA,eAEO,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,QAAQ,OAAO;AAAA,EACrB,IAAI,UAAU;AAAA,IAAW;AAAA,EACzB,WAAW,MAAM,CAAC,KAAK,GAAI,MAAM,UAAU,MAAM,cAAc,CAAC,CAAE,GAAG;AAAA,IACnE,MAAM,aAAa,aAAa,OAAO,MAAM,MAAM,OAAO,MAAM,OAAO,EAAE,GAAG,UAAU;AAAA,IACtF,IAAI,eAAe;AAAA,MAAW,OAAO;AAAA,EACvC;AAAA,EACA;AAAA,CACD;AAcI,IAAM,mBAAmB,CAAC,KAAa,eACrC,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,aAAa,OAAO,mBAAmB,KAAK,UAAU;AAAA,EAC5D,IAAI,eAAe;AAAA,IAAW,OAAO;AAAA,EACrC,QAAQ,OAAO,iBAAiB,KAAK,WAAW,KAAK,KAAK,UAAU;AAAA,CACrE;AAQI,IAAM,WAAW,CAAC,KAAa,eAC7B,aACL,mBAAmB,KAAK,UAAU,GAClC,CAAC,eAAe,YAAY,WAAW,UACzC;AAaK,IAAM,sBAAsB,CAAC,KAAa,eACxC,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,QAAQ,OAAO;AAAA,EACrB,IAAI,UAAU;AAAA,IAAW;AAAA,EACzB,MAAM,MAAM,OAAO,MAAM,MAAM,OAAO,MAAM,OAAO,GAAG;AAAA,EACtD,MAAM,OAAO;AAAA,EACb,IAAI,MAAM,WAAW,cAAc,KAAK,eAAe,cAAc,KAAK,SAAS,WAAW;AAAA,IAC5F;AAAA,EACF;AAAA,EACA,OAAO,MAAM,MAAM,OAAO,KAAK,MAAM,OAAO,IAAI,CAAC,EAAE,KAAY,eAAM;AAAA,CACtE;;;AFxFI,IAAM,eAAe,CAAC,QACpB,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,QAAQ,OAAc,uBAAc,MAAK;AAAA,EAC/C,MAAM,SAAgB,eAAO,KAAK,IAAI,MAAM,MAAM,UAAU,MAAM,QAAQ;AAAA,EAC1E,IAAI,WAAW;AAAA,IAAW,OAAO;AAAA,EACjC,MAAM,SAAS,OAAc,uBAAc,WAAW;AAAA,EACtD,IAAW,eAAO,MAAM;AAAA,IAAG,OAAO,OAAO;AAAA,EACzC,MAAM,UAAU,OAAc,uBAAc,cAAc;AAAA,EAC1D,OAAc,eAAO,OAAO,IAAI,QAAQ,MAAM,QAAQ;AAAA,CACvD;AAqBH,IAAM,YAAY,CAAC,UACV,aAAI,UAAU,GAAG;AAAA,EACtB,IAAI,OAAO,SAAS,MAAM,KAAK,MAAM,UAAU;AAAA,IAAG,OAAO;AAAA,EACzD,OAAO,OAAO,WAAW,MAAM,UAAU;AAAA,CAC1C;AAGI,IAAM,gBAAgB,CAAC,UACrB,aAAI,UAAU,GAAG;AAAA,EACtB,IAAI,EAAE,OAAO,aAAa,MAAM,GAAG;AAAA,IAAI,OAAO;AAAA,EAC9C,OAAO,OAAO,UAAU,KAAK;AAAA,CAC9B;AAuBI,IAAM,iBAAiB,CAAC,OAAc,SACpC,aAAI,UAAU,GAAG;AAAA,EACtB,IAAI,MAAM,WAAW;AAAA,IAAW;AAAA,EAChC,IAAI,OAAO,QAAQ,MAAM,UAAU;AAAA,IAAG;AAAA,EACtC,MAAM,MAAM,OAAO,UAAU,KAAK;AAAA,EAClC,IAAI,QAAQ,OAAO,aAAa,MAAM,GAAG;AAAA,IAAI;AAAA,EAC7C,OAAO,oBAAoB,MAAM,KAAK,MAAM,UAAU;AAAA,EACtD,MAAM,SAAS,MACX,mGACA;AAAA,EACJ,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,yFACD,0FACA,+BAA+B,QACnC,CACF;AAAA,CACD;AAGI,IAAM,WACX,CAAC,UACD,CAAC,SACC,eAAe,OAAO,IAAI;;;AIjG9B,oBAAS;AACT;AAcO,IAAM,aAAa,CACxB,KACA,aAEO,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WAAW,OAAO,iBAAiB,IAAI,KAAK,IAAI,UAAU;AAAA,EAChE,IAAI,aAAa;AAAA,IAAU,OAAO;AAAA,EAClC,IAAI,aAAa,WAAW;AAAA,IAC1B,OAAc,oBACZ,GAAG,IAAI,mFACL,iFACJ;AAAA,IACA,OAAO;AAAA,EACT;AAAA,EACA,MAAM,WAAW,CAAC,WACT,YACE,oBACL,GAAG,IAAI,+EACL,IAAI,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,kCAChE,GACA,KACF;AAAA,EACF,OAAO,OAAO,SAAQ,KAAY,eAAM,QAAQ,GAAU,qBAAY,QAAQ,CAAC;AAAA,CAChF;AAOI,IAAM,YAAY,CACvB,KACA,OACA,aAEO,aAAI,UAAU,GAAG;AAAA,EACtB,IAAI,UAAU,aAAa,IAAI,WAAW;AAAA,IAAW,OAAO;AAAA,EAC5D,QAAQ,OAAO,WAAW,KAAK,QAAO,KAAK,QAAQ,SAAQ,KAAK;AAAA,CACjE;;;AC3DI,IAAM,QAAQ,CAAC,OAAe,eACnC,MACG,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,SAAS,EAAE,EAC5B,IAAI,CAAC,SAA2B;AAAA,EAC/B,MAAM,KAAK,KAAK,QAAQ,GAAG;AAAA,EAC3B,IAAI,OAAO;AAAA,IAAI,OAAO,CAAC,cAAc,MAAM,eAAe,YAAY,KAAK,IAAI;AAAA,EAC/E,OAAO,CAAC,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK,CAAC;AAAA,CAC5D;AAOL,IAAM,WAAuE;AAAA,EAC3E,KAAK,EAAE,cAAc,IAAI;AAAA,EACzB,UAAU,EAAE,cAAc,KAAK,MAAM,KAAK,QAAQ,KAAK,OAAO,KAAK,SAAS,IAAI;AAAA,EAChF,KAAK,EAAE,UAAU,KAAK,gBAAgB,KAAK,WAAW,KAAK,MAAM,OAAO;AAAA,EACxE,QAAQ,EAAE,QAAQ,KAAK,WAAW,KAAK,OAAO,KAAK,WAAW,KAAK,IAAI,KAAK,QAAQ,IAAI;AAC1F;AAGA,IAAM,QAAQ,IAAI,IAAI;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,IAAM,OAAO,IAAI,IAAI,CAAC,SAAS,cAAc,CAAC;AAG9C,IAAM,iBAAiB,CAAC,KAAa,UAAkB;AAAA,EACrD,IAAI,MAAM,IAAI,GAAG;AAAA,IAAG,OAAO,eAAe,KAAK;AAAA,EAC/C,IAAI,KAAK,IAAI,GAAG;AAAA,IACd,OAAO,CAAC,GAAG,IAAI,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG;AAAA,EAClF,IAAI,QAAQ;AAAA,IAAU,OAAO,MAAM,YAAY;AAAA,EAC/C,OAAO;AAAA;AAIF,IAAM,YAAY,CACvB,OACA,QACA,eACwB;AAAA,EACxB,MAAM,WAAW,SAAS,WAAW,CAAC;AAAA,EACtC,MAAM,MAAM,IAAI;AAAA,EAChB,YAAY,KAAK,QAAQ,MAAM,OAAO,UAAU,GAAG;AAAA,IACjD,MAAM,YAAY,eAAe,KAAK,GAAG;AAAA,IACzC,IAAI,SAAS,SAAS;AAAA,MAAW,IAAI,IAAI,KAAK,SAAS;AAAA,EACzD;AAAA,EACA,OAAO;AAAA;AAGF,IAAM,UAAU,CAAC,MAA2B,UACjD,KAAK,SAAS,MAAM,QAAQ,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,WAAW,MAAM,IAAI,GAAG,MAAM,KAAK;AAGjF,IAAM,aAAa,CAAC,SACzB,CAAC,GAAG,IAAG,EACJ,IAAI,EAAE,KAAK,WAAW,GAAG,OAAO,OAAO,EACvC,KAAK,EACL,KAAK,GAAG;AAGN,IAAM,WAAW,CAAC,MAA0B,OAA0B,CAAC,MAC5E,CAAC,GAAG,KAAK,OAAO,CAAC,QAAQ,KAAI,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,KAAI,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EACxF,IAAI,CAAC,QAAQ,GAAG,OAAO,KAAI,IAAI,GAAG,KAAK,IAAI,EAC3C,KAAK,GAAG;AAMN,IAAM,UAAU,CAAC,UAAkB,SAAiB;AAAA,EACzD,MAAM,OAAO,UAAU,UAAU,KAAK;AAAA,EACtC,MAAM,OAAO,UAAU,MAAM,KAAK;AAAA,EAClC,IAAI,CAAC,KAAK,IAAI,QAAQ;AAAA,IAAG,KAAK,OAAO,QAAQ;AAAA,EAC7C,OAAO,EAAE,MAAM,KAAK;AAAA;AAQf,IAAM,WAAW,CAAC,UAAkB,SAA6B;AAAA,EACtE,MAAM,OAAO,IAAI,IAAI,MAAM,QAAQ,CAAC;AAAA,EACpC,MAAM,MAAM,SAAS,YAAY,YAAY,IAAI,IAAI,MAAM,IAAI,CAAC,EAAE,IAAI,QAAQ;AAAA,EAC9E,IAAI,CAAC,KAAK,IAAI,QAAQ,KAAK,QAAQ;AAAA,IAAW,KAAK,IAAI,UAAU,GAAG;AAAA,EACpE,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC;AAAA;AAIzB,IAAM,SAAS,CAAC,UACrB,CAAC,GAAG,IAAI,IAAI,MAAM,MAAM,SAAS,EAAE,OAAO,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,EAC5D,IAAI,CAAC,QAAQ,IAAI,YAAY,CAAC,EAC9B,KAAK,EACL,KAAK,GAAG;AAGN,IAAM,cAAc,CAAC,UAC1B,MACG,MAAM,SAAS,EACf,OAAO,CAAC,SAAS,SAAS,EAAE,EAC5B,KAAK,GAAG;AAMN,IAAM,kBAAkB,CAAC,UAAkB,MAAM,QAAQ,QAAQ,EAAE;AAGnE,IAAM,eAA8E;AAAA,EACzF,MAAM;AAAA,EACN,OAAO;AAAA,EACP,SAAS;AAAA,EACT,OAAO;AAAA,EACP,UAAU;AAAA,EACV,UAAU;AAAA,EACV,aAAa;AAAA,EACb,UAAU;AAAA,EACV,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AAAA,EACN,UAAU;AAAA,EACV,KAAK;AACP;AAGO,IAAM,YAAiC,IAAI,IAAI;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMM,IAAM,kBAAoD;AAAA,EAC/D,MAAM;AAAA,EACN,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,KAAK;AACP;AAGO,IAAM,aAAa,CAAC,KAAa,UAA0B;AAAA,EAChE,MAAM,QAAO,aAAa;AAAA,EAC1B,IAAI,UAAS;AAAA,IAAU,OAAO,OAAO,OAAO,KAAK,CAAC;AAAA,EAClD,IAAI,UAAS;AAAA,IAAQ,OAAO,eAAe,KAAK;AAAA,EAChD,IAAI,QAAQ;AAAA,IAAe,OAAO,gBAAgB,KAAK;AAAA,EACvD,IAAI,QAAQ;AAAA,IAAY,OAAO,WAAW,UAAU,OAAO,UAAU,CAAC;AAAA,EACtE,IAAI,QAAQ,gBAAgB,QAAQ;AAAA,IAAgB,OAAO,YAAY,KAAK;AAAA,EAC5E,IAAI,QAAQ;AAAA,IAAW,OAAO,eAAe,OAAO,OAAO;AAAA,EAC3D,IAAI,QAAQ;AAAA,IAAQ,OAAO,OAAO,KAAK;AAAA,EACvC,OAAO;AAAA;;;ACrFF,IAAM,WAAgC,IAAI,IAAI;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,UAAU;AAAA,EACrB,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AACP;AAKO,IAAM,aAAa,CAAC,QAA8D;AAAA,EACvF,MAAM,QAAQ,6BAA6B,KAAK,GAAG;AAAA,EACnD,IAAI,UAAU;AAAA,IAAM;AAAA,EACpB,MAAM,SAAS,MAAM;AAAA,EACrB,MAAM,QAAQ,OAAO,MAAM,EAAE;AAAA,EAC7B,OAAO,QAAQ,QAAQ,UAAU,CAAC,QAAQ,KAAK,IAAI;AAAA;AAI9C,IAAM,aAAkC,IAAI,IAAI;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMM,IAAM,gBAAgB,CAAC,OAAiB,QAC5C,MAA6C;AAGzC,IAAM,eAAe,CAAC,UAC3B,OAAO,KAAK,KAAK,EACd,OAAO,CAAC,QAAQ,CAAC,WAAW,IAAI,GAAG,KAAK,cAAc,OAAO,GAAG,MAAM,SAAS,EAC/E,KAAK;AAGH,IAAM,YAAY,CAAC,UACxB,UAAU,OAAO,MAAM,UAAU,QAAQ,MAAM,OAAO,KAAK;AAStD,IAAM,eAAe,CAAC,QAC3B,aAAa,SAAS,aACtB,QAAQ,kBACR,QAAQ,YACR,WAAW,GAAG,MAAM;AAUf,IAAM,eAAe,CAAC,SAA0D;AAAA,EACrF,MAAM,MAA8B,CAAC;AAAA,EACrC,YAAY,KAAK,UAAU,OAAO,QAAQ,IAAI,GAAG;AAAA,IAC/C,IAAI,CAAC,aAAa,GAAG,KAAK,SAAS,IAAI,GAAG;AAAA,MAAG;AAAA,IAC7C,IAAI,UAAU,QAAQ,UAAU,aAAa,OAAO,UAAU;AAAA,MAAU;AAAA,IACxE,IAAI,OAAO,UAAU,KAAK;AAAA,EAC5B;AAAA,EACA,OAAO;AAAA;AAIF,IAAM,YAAY,CAAC,SAA4C;AAAA,EACpE,MAAM,MAAM,KAAK;AAAA,EACjB,IAAI,CAAC,MAAM,QAAQ,GAAG;AAAA,IAAG,OAAO,CAAC;AAAA,EACjC,OAAO,IACJ,IAAI,CAAC,SAAmB,MAAM,QAAQ,IAAI,IAAI,OAAO,KAAK,EAAE,IAAI,EAAG,EACnE,OAAO,CAAC,QAAQ,QAAQ,EAAE;AAAA;;;AC9L/B,IAAM,WAAW;AAEjB,IAAM,QAA0C;AAAA,EAC9C,GAAG,QAAQ;AAAA,EACX,GAAG;AAAA,EACH,GAAG,QAAQ;AAAA,EACX,GAAG,QAAQ;AACb;AAGO,IAAM,YAAY,CAAC,UAAqC;AAAA,EAC7D,MAAM,QAAQ,6BAA6B,KAAK,MAAK,KAAK,CAAC;AAAA,EAC3D,IAAI,UAAU;AAAA,IAAM;AAAA,EACpB,OAAO,KAAK,MAAM,OAAO,MAAM,EAAE,KAAK,MAAM,OAAO,YAAY,IAAK,MAAM,MAAM,OAAO,EAAG;AAAA;AAkBrF,IAAM,cAAc,CAAC,UAAyB;AAAA,EACnD,OAAO,SAAS,MAAM,OAAM,QAAQ;AAAA,EACpC,MAAM,QAAO,QAAQ,OAAO,WAAW,MAAM,KAAK;AAAA,EAClD,MAAM,UAAU,UAAU,OAAM,UAAU,QAAQ;AAAA,EAClD,QAAQ,OAAO,QAAQ;AAAA,EACvB,MAAM,WAAW,QAAQ,IAAI,MAAM;AAAA,EACnC,QAAQ,OAAO,MAAM;AAAA,EACrB,MAAM,QAAQ,SAAS,KAAK,KAAI;AAAA,EAChC,IAAI,UAAU,MAAM;AAAA,IAClB,MAAM,MAAM,GAAG,MAAM,MAAM;AAAA,IAC3B,OAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,MAAM,UAAU,GAAG;AAAA,MACnB,UAAU;AAAA,MACV,QAAQ,MAAM,MAAM;AAAA,MACpB,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,MAAM,OAAO,aAAa,YAAY,YAAY,UAAU,QAAQ;AAAA,EACpE,IAAI,MAAK,WAAW,GAAG,GAAG;AAAA,IACxB,MAAM,QAAO,MAAK,WAAW,OAAO,IAAI,WAAW;AAAA,IACnD,OAAO,EAAE,aAAM,SAAS,MAAM,UAAU,QAAQ,OAAM,SAAS,GAAG;AAAA,EACpE;AAAA,EACA,MAAM,KAAK,MAAK,QAAQ,GAAG;AAAA,EAC3B,OAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,OAAO,KAAK,QAAO,MAAK,MAAM,GAAG,EAAE;AAAA,IAC3C,SAAS,OAAO,KAAK,KAAK,MAAK,MAAM,KAAK,CAAC;AAAA,EAC7C;AAAA;AAaK,IAAM,aAAa,CAAC,WAAmB,OAAO,SAAS,UAAU,OAAO,SAAS;AAOxF,IAAM,UAAU,CAAC,MAAc,aAAqB;AAAA,EAClD,MAAM,QAAQ,CAAC,GAAG,KAAK,UAAU,KAAK,SAAS;AAAA,EAC/C,YAAY,KAAK,UAAU,SAAS;AAAA,IAAS,MAAM,KAAK,GAAG,OAAO,OAAO;AAAA,EACzE,IAAI,KAAK,aAAa;AAAA,IAAW,MAAM,KAAK,QAAQ,KAAK,UAAU;AAAA,EACnE,OAAO,MAAM,KAAK,GAAG;AAAA;AAGhB,IAAM,cAAc,CAAC,KAAa,UAAkB,SAAgC;AAAA,EACzF,MAAM,OAAO,YAAY,QAAQ;AAAA,EACjC,MAAM,OAAO,YAAY,IAAI;AAAA,EAC7B,IAAI,WAAW,IAAI,KAAK,WAAW,IAAI,GAAG;AAAA,IACxC,MAAM,QACJ,KAAK,SAAS,KAAK,QAAQ,KAAK,WAAW,KAAK,UAAU,QAAQ,KAAK,SAAS,KAAK,OAAO;AAAA,IAC9F,OAAO,QAAO,CAAC,IAAI,EAAE,UAAU,KAAK;AAAA,EACtC;AAAA,EACA,IAAI,KAAK,WAAW,KAAK,QAAQ;AAAA,IAC/B,OAAO;AAAA,MACL,QACE,GAAG,yBAAyB,KAAK,uBAAuB,KAAK,0BAC7D;AAAA,IACJ;AAAA,EACF;AAAA,EACA,IAAI,KAAK,SAAS,YAAY,KAAK,YAAY,KAAK,SAAS;AAAA,IAC3D,OAAO;AAAA,MACL,QACE,GAAG,wBAAwB,KAAK,2BAA2B,KAAK,+BAChE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,IAAI;AAAA,EACJ,IAAI,KAAK,SAAS,aAAa,KAAK,SAAS,aAAa,KAAK,SAAS,KAAK,MAAM;AAAA,IACjF,IAAI,KAAK,OAAO,KAAK,MAAM;AAAA,MACzB,OAAO;AAAA,QACL,QACE,GAAG,iBAAiB,KAAK,YAAY,kCACrC,GAAG,KAAK,YAAY,uEACpB;AAAA,MACJ;AAAA,IACF;AAAA,IACA,SAAS,KAAK;AAAA,EAChB;AAAA,EACA,MAAM,MAAM,QAAQ,KAAK,SAAS,KAAK,OAAO,IAAI,YAAY,QAAQ,MAAM,IAAI;AAAA,EAChF,OAAO;AAAA,OACD,QAAQ,YAAY,CAAC,IAAI,EAAE,IAAI;AAAA,OAC/B,WAAW,YAAY,CAAC,IAAI,EAAE,OAAO;AAAA,EAC3C;AAAA;;;ACnGF,IAAM,QAAO,CAAC,UACZ,UAAU,aAAa,UAAU,OAAO,YAAY,UAAU,KAAK;AAG9D,IAAM,WAAW,CAAC,MAAc,KAAa,OAAe,QACjE,GAAG,QAAQ,kFACX,aAAa,OAAO,IAAI,KAAK,UAAU,KAAK,YAAY,QAAQ,KAAK,QAAQ,gBAC7E;AAGF,IAAM,cAAc,CAAC,UACnB,IAAI,IACF,MAAM,KAAK,EAAE,IAAI,EAAE,KAAK,WAAW,CAAC,KAAK,QAAQ,UAAU,QAAQ,eAAe,KAAK,CAAC,CAAC,CAC3F;AASK,IAAM,mBAAmB,CAC9B,MACA,MACA,iBACY;AAAA,EACZ,IAAI,CAAC;AAAA,IAAc,OAAO;AAAA,EAC1B,MAAM,OAAO,YAAY,IAAI;AAAA,EAC7B,IAAI,SAAS;AAAA,IAAW,OAAO,EAAE,KAAK,SAAS,KAAM,KAAK,SAAS,KAAK,KAAK,IAAI,SAAS;AAAA,EAC1F,MAAM,OAAO,YAAY,IAAI;AAAA,EAC7B,MAAM,OAAO,IAAI,IAAI,CAAC,GAAG,KAAK,KAAK,GAAG,GAAG,KAAK,KAAK,CAAC,CAAC;AAAA,EACrD,OAAO,CAAC,GAAG,IAAI,EAAE,KACf,CAAC,QAAQ,QAAQ,cAAc,KAAK,IAAI,GAAG,KAAK,SAAS,KAAK,IAAI,GAAG,KAAK,GAC5E;AAAA;AAGK,IAAM,aAAa,CAAC,iBACzB,eAAe,kCAAkC;AAE5C,IAAM,QAAQ,CAAC,OAAiB,SAA6C;AAAA,EAClF,MAAM,MAA8B,CAAC;AAAA,EACrC,MAAM,QAAkB,CAAC;AAAA,EACzB,MAAM,SAA2C,CAAC;AAAA,EAClD,MAAM,SAAmB,CAAC;AAAA,EAC1B,MAAM,QAAQ,IAAI;AAAA,EAClB,MAAM,eAAe,KAAK,KAAK,iBAAiB,KAAK;AAAA,EAErD,WAAW,OAAO,aAAa,KAAK,GAAG;AAAA,IACrC,MAAM,OAAO,UAAU,cAAc,OAAO,GAAG,CAAC;AAAA,IAChD,MAAM,OAAO,MAAK,KAAK,IAAI;AAAA,IAC3B,MAAM,UAAU,WAAW,GAAG;AAAA,IAE9B,IAAI,QAAQ,gBAAgB;AAAA,MAC1B,IAAI,eAAe,IAAI,MAAM,eAAe,QAAQ,GAAG,GAAG;AAAA,QACxD,MAAM,IAAI,GAAG;AAAA,QACb,OAAO,KACL,sBAAsB,QAAQ,iBAAiB,6CAC7C,uFACJ;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,IAEA,IAAI,QAAQ,YAAY,UAAU,OAAO,MAAM;AAAA,MAC7C,IAAI,SAAS,IAAI;AAAA,QACf,IAAI,SAAS;AAAA,UAAW;AAAA,QACxB,MAAM,IAAI,GAAG;AAAA,QACb,OAAO,KACL,GAAG,4EACL;AAAA,MACF,EAAO,SAAI,SAAS,WAAW;AAAA,QAC7B,MAAM,IAAI,GAAG;AAAA,QACb,IAAI,WAAW,YAAY,IAAI,CAAC,GAAG;AAAA,UACjC,OAAO,KAAK,SAAS,MAAM,MAAM,KAAK,MAAM,+BAA+B,CAAC;AAAA,QAC9E,EAAO;AAAA,cAAI,OAAO;AAAA,MACpB,EAAO;AAAA,QACL,MAAM,UAAU,YAAY,KAAK,MAAM,IAAI;AAAA,QAC3C,IAAI,QAAQ,WAAW;AAAA,UAAW,OAAO,KAAK,QAAQ,MAAM;AAAA,QAC5D,IAAI,QAAQ,aAAa,MAAM;AAAA,UAC7B,OAAO,KAAK,SAAS,MAAM,MAAM,KAAK,MAAM,+BAA+B,CAAC;AAAA,QAC9E;AAAA,QACA,IAAI,QAAQ,QAAQ;AAAA,UAAW,IAAI,OAAO,QAAQ;AAAA,QAClD,IAAI,QAAQ,WAAW;AAAA,UAAW,OAAO,KAAK,EAAE,MAAM,KAAK,MAAM,QAAQ,OAAO,CAAC;AAAA,QACjF,IAAI,OAAO,KAAK,OAAO,EAAE,SAAS;AAAA,UAAG,MAAM,IAAI,GAAG;AAAA;AAAA,MAEpD;AAAA,IACF;AAAA,IAEA,IAAI,UAAU,OAAO,OAAO;AAAA,MAC1B,MAAM,QAAO,SAAS,KAAK,SAAS,YAAY,SAAS,aAAa,QAAQ,MAAM,IAAI;AAAA,MACxF,IAAI,CAAC,OAAM;AAAA,QACT,MAAM,IAAI,GAAG;AAAA,QACb,OAAO,KAAK,SAAS,MAAM,MAAM,KAAK,MAAM,qBAAqB,CAAC;AAAA,MACpE;AAAA,MACA;AAAA,IACF;AAAA,IAEA,IAAI,UAAU,OAAO,OAAO;AAAA,MAC1B,IAAI,SAAS,IAAI;AAAA,QACf,IAAI,SAAS,WAAW;AAAA,UACtB,MAAM,IAAI,GAAG;AAAA,UACb,MAAM,KAAK,GAAG;AAAA,QAChB;AAAA,MACF,EAAO,SAAI,SAAS,WAAW;AAAA,QAC7B,MAAM,IAAI,GAAG;AAAA,QACb,IAAI,OAAO;AAAA,MACb,EAAO;AAAA,QACL,MAAM,OAAO,QAAQ,MAAM,IAAI;AAAA,QAC/B,IAAI,CAAC,QAAQ,KAAK,MAAM,KAAK,IAAI,GAAG;AAAA,UAClC,MAAM,IAAI,GAAG;AAAA,UACb,IAAI,OAAO,SAAS,MAAM,IAAI;AAAA,QAChC;AAAA;AAAA,MAEF;AAAA,IACF;AAAA,IAEA,IAAI,CAAC,aAAa,GAAG,GAAG;AAAA,MACtB,MAAM,IAAI,GAAG;AAAA,MACb,OAAO,KAAK,GAAG,8CAA8C;AAAA,MAC7D;AAAA,IACF;AAAA,IAEA,MAAM,SAAS,SAAS,KAAK,KAAK,WAAW,KAAK,IAAI;AAAA,IACtD,MAAM,UAAU,QAAQ,gBAAgB;AAAA,IACxC,MAAM,MAAM,YAAY,YAAY,KAAK,WAAW,KAAK,OAAO;AAAA,IAChE,IAAI,WAAW;AAAA,MAAK;AAAA,IACpB,MAAM,IAAI,GAAG;AAAA,IAEb,MAAM,OAAO,QAAQ,aAAa,SAAS;AAAA,IAC3C,IAAI,QAAQ,cAAc,iBAAiB,QAAQ,IAAI,QAAQ,WAAW,YAAY,GAAG;AAAA,MACvF,OAAO,KAAK,SAAS,MAAM,MAAM,KAAK,MAAM,WAAW,YAAY,CAAC,CAAC;AAAA,IACvE,EAAO,SAAI,SAAS,MAAM,UAAU,IAAI,GAAG,GAAG;AAAA,MAC5C,MAAM,KAAK,GAAG;AAAA,IAChB,EAAO,SAAI,SAAS,IAAI;AAAA,MACtB,OAAO,KAAK,GAAG,2DAA2D;AAAA,IAC5E,EAAO;AAAA,UAAI,OAAO;AAAA,EACpB;AAAA,EACA,OAAO,EAAE,OAAO,OAAO,CAAC,GAAG,KAAK,EAAE,KAAK,GAAG,KAAK,QAAQ,OAAO;AAAA;AAIhE,IAAM,UAAU,CAAC,MAAc,SAC7B,WAAW,UAAU,MAAM,OAAO,MAAM,CAAC,MAAM,WAAW,UAAU,MAAM,OAAO,MAAM,CAAC;;;ACtK1F,IAAM,iBAAiB,CAAC,KAAa,UACnC,GAAG,QAAQ,kFACX,gGACA;AAGK,IAAM,iBAAiB,CAAC,UAA8B;AAAA,EAC3D,MAAM,SAAmB,CAAC;AAAA,EAC1B,IAAI,MAAM,eAAe,aAAa,MAAM,eAAe,IAAI;AAAA,IAC7D,OAAO,KACL,MAAM,OAAO,MAAM,IAAI,uBAAuB,MAAM,sCAClD,kFACJ;AAAA,EACF;AAAA,EACA,MAAM,eAAe,MAAM,iBAAiB,YAAY,OAAO,KAAK,MAAM,YAAY;AAAA,EACtF,WAAW,OAAO,aAAa,KAAK,GAAG;AAAA,IACrC,MAAM,QAAQ,UAAU,cAAc,OAAO,GAAG,CAAC;AAAA,IACjD,MAAM,SAAS,WAAW,GAAG,IAAI;AAAA,IAGjC,IAAI,CAAC,aAAa,GAAG;AAAA,MAAG,OAAO,KAAK,GAAG,8CAA8C;AAAA,IACrF,IAAI,UAAU;AAAA,MAAI;AAAA,IAClB,IAAI,WAAW;AAAA,MAAO,OAAO,KAAK,SAAS,MAAM,MAAM,KAAK,OAAO,qBAAqB,CAAC;AAAA,IACzF,MAAM,SAAS,WAAW,QAAQ,QAAQ,WAAW,YAAY,KAAK,IAAI;AAAA,IAC1E,IAAI,WAAW,aAAa,WAAW,MAAM,GAAG;AAAA,MAC9C,OAAO,KAAK,SAAS,MAAM,MAAM,KAAK,OAAO,+BAA+B,CAAC;AAAA,IAC/E;AAAA,IACA,IAAI,QAAQ,SAAS;AAAA,MAAU,OAAO,KAAK,eAAe,KAAK,KAAK,CAAC;AAAA,IACrE,IAAI,QAAQ,cAAc,iBAAiB,IAAI,OAAO,YAAY,GAAG;AAAA,MACnE,OAAO,KAAK,SAAS,MAAM,MAAM,KAAK,OAAO,WAAW,YAAY,CAAC,CAAC;AAAA,IACxE;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAIF,IAAM,cAAa,CAAC,UAA4C;AAAA,EACrE,MAAM,OAA+B,EAAE,MAAM,OAAO,MAAM,IAAI,EAAE;AAAA,EAChE,IAAI,MAAM,eAAe;AAAA,IAAW,KAAK,gBAAgB,MAAM;AAAA,EAC/D,IAAI,MAAM,UAAU;AAAA,IAAW,KAAK,WAAW,UAAU,MAAM,KAAK;AAAA,EACpE,WAAW,OAAO,aAAa,KAAK,GAAG;AAAA,IACrC,MAAM,QAAQ,UAAU,cAAc,OAAO,GAAG,CAAC;AAAA,IACjD,IAAI,UAAU;AAAA,MAAI;AAAA,IAClB,KAAK,OAAO,QAAQ,aAAa,WAAW,KAAK,KAAK,IAAI;AAAA,EAC5D;AAAA,EACA,OAAO;AAAA;;;ACnET,IAAM,QAAQ,CAAC,MAAe,QAAyB;AAAA,EACrD,MAAM,QAAkB,KAAiC;AAAA,EACzD,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW,QAAQ;AAAA;AAcnE,IAAM,mBAAmB,CAC9B,MACA,MACA,WACa;AAAA,EACb,MAAM,MAAM,QAAQ,QAAQ,KAAK;AAAA,EACjC,MAAM,OAAO,MAAM,MAAM,MAAM;AAAA,EAC/B,MAAM,WAAW,MAAM,MAAM,YAAY;AAAA,EACzC,MAAM,UAAoB,CAAC;AAAA,EAC3B,IAAI,SAAS,aAAa,SAAS,KAAK;AAAA,IACtC,QAAQ,KACN,SAAS,OAAO,GAAG,QAAQ,OAAO,IAAI,iDACpC,2DACJ;AAAA,EACF;AAAA,EACA,IAAI,aAAa,aAAa,KAAK,eAAe,aAAa,aAAa,KAAK,YAAY;AAAA,IAC3F,QAAQ,KAAK,2EAA2E;AAAA,EAC1F;AAAA,EACA,OAAO;AAAA;;;ACtBT;;;ACKA;AAWO,IAAM,WAAW,CACtB,OACA,aAEO,aAAI,UAAU,GAAG;AAAA,EACtB,IAAI,MAAM,WAAW;AAAA,IAAW,OAAO;AAAA,EACvC,MAAM,aAAa,OAAO,mBAAmB,MAAM,KAAK,MAAM,UAAU;AAAA,EACxE,IAAI,eAAe,aAAa,WAAW,aAAa;AAAA,IAAM,OAAO;AAAA,EACrE,IAAI,WAAW,SAAS,aAAa,WAAW,SAAS;AAAA,IAAM,OAAO;AAAA,EACtE,OAAO,EAAE,OAAO,WAAW,OAAO,SAAQ,WAAW,KAAK,CAAC;AAAA,CAC5D;;;ACpBH;;;ACYA;AA2BA,IAAM,aAAa,CAAC,QAAmB,SAC9B,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,OAAO,SAAS;AAAA,EAQtB,MAAM,aAAa,OAAO,OAAO,QAAQ,MAAM;AAAA,EAC/C,MAAM,WAAY,OAAO,gBAAgB,QAAQ,YAAY,OAAO,IAAI;AAAA,EACxE,OAAO,KAAK,SAAS,OAAO;AAAA,CAC7B;AAYI,IAAM,eAAe,CAAC,SAAiB;AAAA,EAC5C,IAAI,SAAS;AAAA,IAAI,OAAO;AAAA,EACxB,MAAM,UAAU,KACb,MAAM;AAAA,CAAI,EACV,OACC,CAAC,UACE,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG,MAC5C,CAAC,KAAK,WAAW,KAAK,KACtB,CAAC,KAAK,WAAW,KAAK,CAC1B,EAAE;AAAA,EACJ,OAAO,KAAK,IAAI,SAAS,CAAC;AAAA;AAIrB,IAAM,eAAe,CAAC,QAAmB,SAC9C,WAAW,QAAQ,IAAI,EAAE,KAAY,aAAI,YAAY,CAAC;AAmBxD,IAAM,gBAAgB,CAAC,WACrB,IAAiB,QAAQ,QAAQ,OAAO,gBAAgB,EAAE,KACjD,aAAI,CAAC,SAAS;AAAA,EACnB,MAAM,MAAM,QAAQ,CAAC;AAAA,EACrB,MAAM,UAAU,IAAI,KAAK,CAAC,QAAQ,IAAI,SAAS,SAAS;AAAA,EACxD,OAAO;AAAA,IACL,SAAS,IACN,OAAO,CAAC,QAAQ,IAAI,SAAS,UAAU,CAAC,KAAK,IAAI,MAAM,CAAC,EACxD,IAAI,CAAC,QAAQ,KAAK,IAAI,MAAM,WAAW,CAAC;AAAA,IAC3C,SAAS,YAAY,YAAY,OAAO,KAAK,QAAQ,OAAO;AAAA,EAC9D;AAAA,CACD,CACH;AAgBK,IAAM,iBAAiB,CAAC,WAC7B,cAAc,MAAM,EAAE,KACb,aAAI,CAAC,WACV,OAAO,WAAW,OAAO,QAAQ,WAAW,IACxC,YACA,WAAW,OAAO,OAAO,OAAO,cAAc,OAAO,QAAQ,KAAK,IAAI,IAC5E,CACF;AAMK,IAAM,cACX;AACF,IAAM,gBAAgB;AAStB,IAAM,WAAW;AAgBV,IAAM,YAAY,CACvB,QACA,YACA,MACA,MAEA,WAAmB,aAEZ,aAAI,UAAU,GAAG;AAAA,EAUtB,MAAM,OAAO,SAAS,cAAc;AAAA,EACpC,SAAS,UAAU,EAAG,UAAU,UAAU,WAAW,GAAG;AAAA,IAWtD,MAAM,SAAS,OAAO,QAAoB,QAAQ,YAAY,OAAO,IAAI,EAAE,KAClE,uBAAc,MAAG;AAAA,MAAG;AAAA,KAAS,CACtC;AAAA,IACA,IAAI,WAAW;AAAA,MAAW,OAAO;AAAA,IACjC,IAAI,OAAO,WAAW;AAAA,MAAW,OAAO,KAAK,OAAO,YAAY,4BAA4B;AAAA,IAC5F,OAAc,eAAM,UAAU;AAAA,EAChC;AAAA,EACA,OAAO;AAAA,CACR;;;ADjMI,MAAM,wBAAwB,MAAM;AAAA,EACvB,OAAO;AAC3B;AAKO,IAAM,YAAY,CAAC,UAAoB,SAAS,MAAM,YAAY,OAAO,MAAM,IAAI;AAG1F,IAAM,eAAe;AACrB,IAAM,aAAa;AAEnB,IAAM,gBAAgB,CAAC,UACrB,iBAAiB,YAAY,MAAM,WAAW;AAMzC,IAAM,WAAW,CAAC,UAChB,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,SAAS,OAAO,IACpB,MAAM,QACN,QACA,OACA,GAAG,UAAU,KAAK,UACpB,EAAE,KACO,aAAI,CAAC,UAAU,EAAE,MAAM,OAAO,UAAU,EAAE,GAC1C,iBAAQ,eAAe,CAAC,UAAiB,iBAAQ,EAAE,MAAM,WAAW,MAAM,CAAC,CAAC,CACrF;AAAA,EACA,IAAI,OAAO,SAAS,aAAa,OAAO,SAAS;AAAA,IAAM,OAAO,OAAO;AAAA,EACrE,MAAM,OAAO,OAAO,IAClB,MAAM,QACN,QACA,OACA,2BACF;AAAA,EAGA,MAAM,SAAS,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,OAAO,IAAI,IAAI,MAAM,MAAM,IAAI;AAAA,EACxE,IAAI,UAAU,aAAa,MAAM,SAAS,MAAM,QAAQ,MAAM,SAAS,OAAO;AAAA,IAC5E,OAAO,OAAc,cACnB,OAAO,SACL,IAAI,gBAAgB,MAAM,OAAO,MAAM,IAAI,wCAAwC,CACvF;AAAA,EACF;AAAA,EACA,IAAI,UAAU,WAAW;AAAA,IACvB,OAAO,OAAc,cACnB,IAAI,gBACF,QAAQ,OAAO,MAAM,IAAI,UAAU,KAAK,MAAM,MAAM,OAAO,UACzD,GAAG,KAAK,MAAM,MAAM,GAAG,yBAAyB,MAAM,iCACtD,oFACJ,CACF;AAAA,EACF;AAAA,EACA;AAAA,CACD;AAEI,IAAM,eAAe,CAAC,OAAiB,UAAkD;AAAA,EAC9F,QAAQ,aAAa,IAAI;AAAA,EACzB,MAAM,MAAM;AAAA,EACZ,SAAS,UAAU,IAAI;AAAA,EACvB,MAAM,MAAM;AACd;AAGA,IAAM,OAAO,CACX,OACA,QACA,MACA,MACA,OACA,SAEO,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,aAAa,OAAO,KAAK,MAAM,QAAQ,WAAW;AAAA,EACxD,MAAM,OAAO,KAAK,OAAO,QAAgB,MAAM,QAAQ,YAAY,QAAQ,MAAM,IAAI,CAAC;AAAA,EACtF,IAAI,SAAS,IAAI;AAAA,IACf,OAAO,OAAc,cACnB,IAAI,gBACF,GAAG,qFACL,CACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,OAAO,UAAU,MAAM,QAAQ,YAAY,MAAM,MAAM,MAAM,KAAK;AAAA,EAC/E,IAAI,SAAS,MAAM;AAAA,IACjB,OAAO,OAAc,cACnB,IAAI,gBACF,GAAG,cAAc,eAAe,kCAAkC,MAAM,gBACtE,GAAG,6CACP,CACF;AAAA,EACF;AAAA,CACD;AAGI,IAAM,cAAc,CAAC,UAC1B,KACE,OACA,QACA,SAAS,MAAM,YACf,aAAa,OAAO,MAAM,IAAI,KAC9B,cACA,YAAW,KAAK,CAClB;AAYK,IAAM,cAAc,CAAC,OAAiB,QAAmB,WACvD,aAAI,UAAU,GAAG;AAAA,EACtB,IAAI,OAAO,KAAK,OAAO,GAAG,EAAE,SAAS,KAAK,OAAO,MAAM,SAAS,GAAG;AAAA,IACjE,MAAM,OAAO,WACX,KAAK,OAAO,QAAS,WAAW,KAAK,CAAC,IAAI,EAAE,OAAO,EAAG,GACtD,OAAO,KACT;AAAA,IACA,OAAO,IAAI,MAAM,QAAQ,aAAa,OAAO,GAAG,UAAU,KAAK,YAAY,IAAI;AAAA,EACjF;AAAA,EACA,WAAW,QAAQ,OAAO,QAAQ;AAAA,IAChC,OAAO,KACL,OACA,OACA,GAAG,UAAU,KAAK,YAClB,QAAQ,KAAK,cAAc,OAAO,MAAM,IAAI,QAAQ,KAAK,QACzD,YACA,EAAE,MAAM,KAAK,MAAM,MAAM,KAAK,KAAK,CACrC;AAAA,EACF;AAAA,CACD;AAYI,IAAM,eAAe,CAAC,OAAiB,aACrC,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,OAAO,OAAO,SAAS,KAAK;AAAA,EAClC,IAAI,SAAS;AAAA,IAAW;AAAA,EACxB,MAAM,QAAQ,MAAM,UAAU,IAAI,EAAE;AAAA,EACpC,IAAI,MAAM,SAAS,GAAG;AAAA,IACpB,OAAO,OAAc,cACnB,IAAI,gBACF,cAAc,OAAO,MAAM,IAAI,mBAAmB,MAAM,qCACtD,gBAAgB,MAAM,KAAK,IAAI,qDAC/B,8EACJ,CACF;AAAA,EACF;AAAA,EACA,OAAO,KAAK,OAAO,UAAU,UAAU,KAAK,GAAG,cAAc,OAAO,MAAM,IAAI,KAAK,UAAU;AAAA,CAC9F;;;AF5KI,IAAM,kBAAkB,CAAC,OAAiB,UAC/C,MAAM,OAAO,MAAM,IAAI,QAAQ,MAAM,kCAAkC,MAAM,KAAK,IAAI,WACtF,gGACA,+FACA;AAMK,IAAM,qBAAqB,CAChC,OACA,OACA,MACA,WAEO,aAAI,UAAU,GAAG;AAAA,EACtB,IAAI,OAAO,MAAM,WAAW;AAAA,IAAG;AAAA,EAC/B,MAAM,WAAU,CAAC,aACR,cAAK,MAAM,MAAM,UAAsB,IAAI,EAAE,MAAM,WAAW,CAAC;AAAA,EACxE,IAAI,EAAE,OAAO,SAAS,OAAO,QAAO;AAAA,IAAI;AAAA,EACxC,OAAO,OAAc,cAAK,IAAI,gBAAgB,gBAAgB,OAAO,OAAO,KAAK,CAAC,CAAC;AAAA,CACpF;;;AZeI,IAAM,aAAa,WAAqB,eAAe,EAAE,sBAAsB,SAAS,CAAC;AAKhG,IAAM,UAAU,CAAC,YACR,cAAK,IAAI,gBAAgB,QAAQ,KAAK;AAAA,CAAI,CAAC,CAAC;AAQrD,IAAM,OAAO,CAAC,OAAiB,MAAuB,SAC7C,aAAI,UAAU,GAAG;AAAA,EACtB,QAAQ,WAAW;AAAA,EACnB,MAAM,YAAW,iBAAiB,MAAM,MAAM,MAAM;AAAA,EACpD,IAAI,UAAS,SAAS;AAAA,IAAG,OAAO,OAAO,QAAQ,SAAQ;AAAA,EACvD,IAAI,WAAW;AAAA,IAAW;AAAA,EAC1B,IAAI,CAAC,YAAW,IAAI;AAAA,IAAG;AAAA,EACvB,MAAM,OAAO,OAAO,SAAS,IAAI;AAAA,EAKjC,IAAI,SAAS,WAAW;AAAA,IACtB,MAAM,WAAW,eAAe,IAAI;AAAA,IACpC,IAAI,SAAS,SAAS;AAAA,MAAG,OAAO,OAAO,QAAQ,QAAQ;AAAA,IACvD,OAAc,oBACZ,eAAe,KAAK,QAAQ,OAAO,KAAK,IAAI,6CAC1C,6EACJ;AAAA,IACA,OAAO,EAAE,QAAQ,SAAS;AAAA,EAC5B;AAAA,EACA,MAAM,SAAS,MAAM,MAAM,IAAI;AAAA,EAC/B,OAAO,mBAAmB,OAAO,MAAM,MAAM,MAAM;AAAA,EACnD,IAAI,OAAO,OAAO,SAAS;AAAA,IAAG,OAAO,OAAO,QAAQ,OAAO,MAAM;AAAA,EACjE,MAAM,QAAQ,KAAK,SAAS,OAAO;AAAA,EACnC,IAAI,OAAO,MAAM,WAAW,KAAK,CAAC;AAAA,IAAO,OAAO,EAAE,QAAQ,OAAO;AAAA,EACjE,MAAM,OACJ,OAAO,MAAM,WAAW,IACpB,mBAAmB,KAAK,iBAAiB,OAAO,qCAChD,qBACA,+CAA+C,OAAO,MAAM,KAAK,IAAI,YACrE;AAAA,EACN,OAAc,oBAAW,eAAe,KAAK,QAAQ,OAAO,KAAK,IAAI,MAAM,MAAM;AAAA,EACjF,OAAO,EAAE,QAAQ,SAAS;AAAA,CAC3B;AAoBH,IAAM,YAAY,CAAC,OAAiB,SAC3B,aAAI,UAAU,GAAG;AAAA,EACtB,QAAQ,WAAW;AAAA,EACnB,MAAM,YAAW,iBAAiB,MAAM,MAAM,MAAM;AAAA,EACpD,IAAI,UAAS,SAAS;AAAA,IAAG,OAAO,OAAO,QAAQ,SAAQ;AAAA,EACvD,MAAM,OAAO,OAAO,SAAS,IAAI;AAAA,EACjC,IAAI,SAAS,WAAW;AAAA,IACtB,MAAM,WAAW,eAAe,IAAI;AAAA,IACpC,IAAI,SAAS,SAAS;AAAA,MAAG,OAAO,OAAO,QAAQ,QAAQ;AAAA,IACvD,OAAO,YAAY,IAAI;AAAA,EACzB,EAAO;AAAA,IACL,IAAI,WAAW,WAAW;AAAA,MACxB,OAAO,eAAe,OAAO,kBAAkB,OAAO,KAAK,IAAI,QAAQ,KAAK,MAAM;AAAA,IACpF;AAAA,IACA,MAAM,SAAS,MAAM,MAAM,IAAI;AAAA,IAC/B,IAAI,WAAW,aAAa,OAAO,MAAM,SAAS,GAAG;AAAA,MACnD,OAAO,OAAO,QAAQ;AAAA,QACpB,MAAM,OAAO,KAAK,IAAI,uBAAuB,KAAK,yBAChD,GAAG,OAAO,MAAM,KAAK,IAAI,gEACzB;AAAA,MACJ,CAAC;AAAA,IACH;AAAA,IACA,OAAO,mBAAmB,OAAO,MAAM,MAAM,MAAM;AAAA,IACnD,IAAI,OAAO,OAAO,SAAS;AAAA,MAAG,OAAO,OAAO,QAAQ,OAAO,MAAM;AAAA,IACjE,IAAI,OAAO,MAAM,SAAS;AAAA,MAAG,OAAO,YAAY,MAAM,QAAQ,KAAK,KAAK,SAAS,CAAC;AAAA;AAAA,EAEpF,MAAM,QAAQ,OAAO,SAAS,IAAI;AAAA,EAClC,IAAI,UAAU,WAAW;AAAA,IACvB,OAAO,OAAO,QAAQ;AAAA,MACpB,MAAM,OAAO,KAAK,IAAI;AAAA,IACxB,CAAC;AAAA,EACH;AAAA,EACA,MAAM,OAAO,MAAM,MAAM,KAAK,EAAE;AAAA,EAChC,IAAI,KAAK,SAAS,GAAG;AAAA,IACnB,OAAO,OAAO,QAAQ;AAAA,MACpB,MAAM,OAAO,KAAK,IAAI,uBAAuB,KAAK,KAAK,IAAI,6BACzD,yFACA;AAAA,IACJ,CAAC;AAAA,EACH;AAAA,EACA,OAAO,aAAa,MAAM,KAAK;AAAA,CAChC;AAEH,IAAM,UAAU,CAAC,MAAgB,YAAiD;AAAA,EAChF,MAAM,QAAQ,QAAQ,KAAK;AAAA,EAC3B,QAAQ,KAAK;AAAA,EACb,MAAM,QAAQ,QAAQ,KAAK;AAC7B;AAeA,IAAM,OAAO,CAAC,OAAiB,SACtB,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,QAAQ,QAAQ,MAAM,MAAM,MAAM;AAAA,EACxC,MAAM,OAAO,OAAO,SAAS,KAAK;AAAA,EAClC,IAAI,SAAS;AAAA,IAAW;AAAA,EACxB,MAAM,WAAiB,cAAK,MAAM,MAAM,MAAM,IAAI,EAAE,MAAM,WAAW,CAAC;AAAA,EACtE,OAAO,OAAO,UAAU,OAAO,aAAa,OAAO,IAAI,GAAG,QAAO;AAAA,CAClE;AAUH,IAAM,aAAW;AAAA,EACf,QAAQ,GAAG,MAAM,aACf,aAAa,QAAQ,MAAM,MAAM,GAAG,IAAI;AAAA,EAC1C,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,KAAK,EAAE,KAAK,YAAY,OAAO,GAAG,MAAM,IAAI;AAAA,EAC9C,MAAM,MAAa,iBAAQ,CAAC,CAAC;AAAA,EAC7B,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,KAAK,EAAE,KAAK,YAAY,OAAO,GAAG,IAAI;AAAA,EACxC,WAAW;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,QAEA,UAAU,EAAE,KAAK,YAAY,OAAO,GAAG,IAAI;AAC/C;AAEO,IAAM,qBAAqB,MACvB,kBACP,YACO,iBAAQ,WAAW,SAAS,GAAG,UAAQ,CAAC,EAAE,KAAY,aAAI,cAAc,CAAC,CAClF;;AgBrNF,qBAAS;AACT;AACA;;;ACVO,IAAM,SAAQ;AAErB,IAAM,MAAM,CAAC,UAA+B,UAAU,YAAY,YAAY,OAAO,KAAK;AAuBnF,IAAM,YAAW,CAAC,UAAiE;AAAA,EACxF,QAAQ,MAAM;AAAA,SACP;AAAA,MACH,OAAO,EAAE,KAAK,IAAI,MAAM,GAAG,GAAG,MAAM,MAAM,MAAM,SAAS,IAAI,MAAM,OAAO,EAAE;AAAA,SACzE;AAAA,MACH,OAAO;AAAA,QACL,mBAAmB,MAAM;AAAA,QACzB,QAAQ,MAAM;AAAA,QACd,eAAe,MAAM;AAAA,QACrB,iBAAiB,IAAI,MAAM,gBAAgB;AAAA,QAC3C,KAAK,IAAI,MAAM,GAAG;AAAA,QAClB,cAAc,MAAM;AAAA,QACpB,SAAS,IAAI,MAAM,OAAO;AAAA,MAC5B;AAAA,SACG;AAAA,MACH,OAAO;AAAA,QACL,oBAAoB,MAAM;AAAA,QAC1B,sBAAsB,IAAI,MAAM,qBAAqB;AAAA,QACrD,aAAa,MAAM;AAAA,QACnB,iBAAiB,MAAM;AAAA,QACvB,gBAAgB,IAAI,MAAM,eAAe;AAAA,MAC3C;AAAA;AAAA;AAWN,IAAM,eAAe,CAAC,UAAqD;AAAA,EACzE,QAAQ,MAAM;AAAA,SACP;AAAA,MACH,OAAO,CAAC;AAAA,SACL;AAAA,MACH,OAAO,EAAE,sBAAsB,MAAM,0BAA0B,QAAQ,MAAM,IAAI;AAAA,SAC9E;AAAA,MACH,OAAO,EAAE,mBAAmB,MAAM,uBAAuB,QAAQ,MAAM,IAAI;AAAA;AAAA;AAcjF,IAAM,YAAW,CAAC,WAAsD;AAAA,EACtE,SAAS,MAAM,YAAY,OAAO,MAAM;AAAA,EACxC,MAAM,OAAO,MAAM,IAAI;AAAA,EACvB,QAAQ,MAAM;AAAA,KACX,aAAa,KAAK;AACvB;AAEO,IAAM,QAAO,CAAC,UAA6B;AAAA,EAChD,MAAM,SAAS,UAAS,KAAK;AAAA,EAC7B,YAAY,QAAQ,UAAU,OAAO,QAAQ,UAAS,KAAK,CAAC,GAAG;AAAA,IAC7D,IAAI,UAAU;AAAA,MAAW,OAAO,UAAU;AAAA,EAC5C;AAAA,EACA,OAAO;AAAA;AAUF,IAAM,SAAS,CAAC,UAA6B;AAAA,EAClD,MAAM,QAAQ,OAAO,QAAQ,UAAS,KAAK,CAAC,EACzC,OAAO,IAAI,WAAW,UAAU,SAAS,EACzC,IAAI,EAAE,YAAY,MAAM;AAAA,EAC3B,OAAO,WAAW,MAAK,KAAK,GAAG,KAAK;AAAA;;;ACrD/B,IAAM,iBAAiB,CAAC,UAA+D;AAAA,EAC5F,oBAAoB,KAAK,KAAK,mBAAmB;AAAA,EACjD,sBAAsB,IAAI,KAAK,uBAAuB,MAAK;AAAA,EAC3D,aAAa,KAAK,KAAK,YAAY;AAAA,EACnC,iBAAiB,KAAK,KAAK,gBAAgB;AAAA,EAC3C,gBAAgB,IAAI,KAAK,iBAAiB,MAAK;AAAA,EAC/C,mBAAmB,KAAK,KAAK,oBAAoB,IAAI;AACvD;AASO,IAAM,cAAc,CACzB,YACA,UAEA,WAAW,yBAAyB,MAAM,uBAAuB,OACjE,WAAW,2BAA2B,MAAM,yBAAyB,WACrE,WAAW,kBAAkB,MAAM,gBAAgB,OACnD,WAAW,sBAAsB,MAAM,oBAAoB,OAC3D,WAAW,qBAAqB,MAAM,mBAAmB,WACzD,WAAW,wBAAwB,MAAM,uBAAuB;;;AF+B3D,IAAM,sBAAsB,WAA8B,sBAAsB;AAWvF,IAAM,cAAc,CAAC,YAAoC,UAAsC;AAAA,EAC7F,IAAI,MAAM,SAAS;AAAA,IAAiB,OAAO,YAAY,YAAY,KAAK;AAAA,EACxE,MAAM,SACJ,WAAW,SAAS,MAAM,OAAO,WAAU,WAAW,aAAa,MAAM,WAAW;AAAA,EACtF,IAAI,MAAM,SAAS;AAAA,IAAY,OAAO,UAAU,WAAW,UAAU,MAAM,QAAQ;AAAA,EACnF,OACE,UACA,WAAW,mBAAmB,MAAM,iBAAiB,OACrD,WAAW,kBAAkB,MAAM,gBAAgB,OACnD,WAAW,YAAY,MAAM,UAAU,OACvC,WAAW,wBAAwB,MAAM,sBAAsB,OAC/D,WAAW,sBAAsB,MAAM,oBAAoB,WAC3D,WAAW,2BAA2B,MAAM,0BAA0B;AAAA;AAI1E,IAAM,aAAW,YAAuD;AAAA,EACtE,YAAY,CAAC,MAAM,UAAU;AAAA,IAQ3B,MAAM,WAAW,KAAK,KAAK,OAAO;AAAA,IAClC,IAAI,aAAa,MAAM,aAAa,MAAM;AAAA,MAAM;AAAA,IAChD,OAAO;AAAA,SACF,eAAe,IAAI;AAAA,MACtB,mBAAmB,KAAK,KAAK,kBAAkB;AAAA,MAC/C,QAAQ,KAAK,KAAK,SAAS;AAAA,MAC3B,SAAS,KAAK,KAAK,YAAY,KAAK;AAAA,MACpC,IAAI,MAAM;AAAA,MACV,eAAe,KAAK,KAAK,gBAAgB;AAAA,MACzC,iBAAiB,IAAI,KAAK,kBAAkB,MAAK;AAAA,MACjD,KAAK,IAAI,KAAK,QAAQ,MAAK;AAAA,MAC3B,cAAc,KAAK,KAAK,eAAe;AAAA,MACvC,MAAM,KAAK,KAAK,OAAO;AAAA,MACvB,MAAM,IAAI,KAAK,SAAS,CAAC;AAAA,MACzB,QAAQ,KAAK,KAAK,SAAS;AAAA,MAC3B,SAAS,IAAI,KAAK,YAAY,MAAK;AAAA,MACnC,MAAM,MAAM;AAAA,MACZ,sBAAsB,KAAK,KAAK,uBAAuB,IAAI;AAAA,IAC7D;AAAA;AAAA,EAGF,YAAY,CAAC,UAAU,0BAA0B,MAAM;AAAA,EAEvD,YAAY,CAAC,WAAW,KAAK,MAAK,KAAK,GAAG,MAAM,MAAM,KAAK;AAAA,EAK3D,SAAS,CAAC,YAAY,UACpB,WAAW,WAAW,MAAM,UAC5B,WAAW,SAAS,MAAM,QAC1B,WAAW,aAAa,MAAM,YAAY,SAC1C,YAAY,YAAY,KAAK;AAAA,EAC/B,MAAM,CAAC,UAAU,0BAA0B,MAAM;AAAA,EAEjD,YAAY;AACd,CAAC;AAWM,IAAM,8BAA8B,MAChC,kBAAO,qBAA4B,iBAAQ,oBAAoB,SAAS,GAAG,UAAQ,CAAC,CAAC;;AGtKhG,qBAAS;AACT,uBAAS;AAET;AACA;AAiEO,IAAM,sBAAsB,WAA8B,sBAAsB;AAEvF,IAAM,QAAO,CAAC,UACZ,aAAa,MAAM,QAAQ,MAAM,IAAI,EAAE,KAC9B,aAAI,CAAC,aAAa,EAAE,MAAM,MAAM,MAAM,QAAQ,EAAE,CACzD;AAYF,IAAM,QAAO,CAAC,SACL,aAAI,UAAU,GAAG;AAAA,EACtB,IAAI,CAAC,YAAW,IAAI;AAAA,IAAG;AAAA,EACvB,QAAQ,YAAY,OAAO,MAAK,IAAI;AAAA,EACpC,OAAO,YAAY,IAAK,EAAE,QAAQ,OAAO,IAAe,EAAE,QAAQ,SAAS;AAAA,CAC5E;AAGH,IAAM,SAAS,CAAC,MAAc,QACrB,aAAI,IAAI,MAAM,wBAAwB,SAAS,KAAK,CAAC;AAE9D,IAAM,QAAQ,CAAC,UACN,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,OAAO,MAAM;AAAA,EACnB,MAAM,SAAS,OAAO,aAAa,MAAM,QAAQ,IAAI;AAAA,EASrD,IAAI,WAAW;AAAA,IAAG,OAAO,EAAE,MAAM,SAAS,EAAE;AAAA,EAE5C,MAAM,SAAS,OAAO,eAAe,MAAM,MAAM;AAAA,EACjD,IAAI,WAAW,WAAW;AAAA,IACxB,OAAO,OAAO,OACZ,MACA,wEAAwE,cACtE,0EACJ;AAAA,EACF;AAAA,EASA,MAAM,aAAa,OAAO,KAAK,MAAM,QAAQ,WAAW;AAAA,EACxD,MAAM,OAAO,OAAO,QAAgB,MAAM,QAAQ,YAAY,OAAO,SAAS,cAAc;AAAA,EAC5F,IAAI,SAAS,WAAW;AAAA,IACtB,OAAO,OAAO,OACZ,MACA,+FACE,qFACJ;AAAA,EACF;AAAA,EAUA,MAAM,aAAa,OAAO,UAAU,MAAM,QAAQ,YAAY,MAAM,IAAI;AAAA,EACxE,IAAI,eAAe,MAAM;AAAA,IACvB,OAAO,OAAO,OACZ,MACA,qBAAqB,gBAAgB,yCACnC,sBAAsB,cAAc,wDACpC,yBACJ;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,OAAO,eAAe,MAAM,MAAM;AAAA,EAChD,IAAI,UAAU,WAAW;AAAA,IACvB,OAAO,OAAO,OACZ,MACA,2DAA2D,sBACzD,2FACA,gEACJ;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,OAAO,aAAa,MAAM,QAAQ,IAAI;AAAA,EACnD,IAAI,SAAS,GAAG;AAAA,IACd,OAAO,OAAO,OACZ,MACA,8BAA8B,OAAO,IAAI,sDACvC,2FACJ;AAAA,EACF;AAAA,EACA,OAAO,EAAE,MAAM,SAAS,KAAK;AAAA,CAC9B;AAaH,IAAM,aAAW;AAAA,EACf,QAAQ,MAAa;AAAA,EACrB,MAAM,GAAG,WAA+C,MAAK,IAAI;AAAA,EACjE,MAAM,MAAa,iBAAQ,CAAC,CAAC;AAAA,EAC7B,MAAM,GAAG,WAAwC,MAAK,IAAI;AAAA,EAC1D,WAAW,GAAG,WAAwC,MAAM,IAAI;AAClE;AAEO,IAAM,8BAA8B,MAChC,kBAAO,qBAA4B,iBAAQ,oBAAoB,SAAS,GAAG,UAAQ,CAAC,CAAC;;AChLhG,qBAAS;AACT;AACA;;;AC/BO,IAAM,SAAQ;AAuBd,IAAM,YAAY,CAAC,UACxB,KAAK,KAAK,EACP,MAAM,SAAS,EACf,OAAO,CAAC,SAAS,SAAS,EAAE,EAC5B,KAAK,EACL,KAAK,GAAG;AAYN,IAAM,WAAU,CAAC,UAAmB,KAAK,KAAK,EAAE,QAAQ,QAAQ,EAAE;AAGlE,IAAM,QAAO,CAAI,UAAyB,SAC/C,aAAa,aAAa,aAAa;AAElC,IAAM,WAAW,CAAC,UAA8B,SACrD,aAAa,aAAa,UAAU,QAAQ,MAAM;AAE7C,IAAM,cAAc,CAAC,UAA8B,SACxD,aAAa,aAAa,SAAQ,QAAQ,MAAM;AAElD,IAAM,SAAQ,CAAC,MAAc,UAC3B,UAAU,YAAY,CAAC,IAAI,GAAG,OAAO,MAAM;AAyBtC,IAAM,QAAO,CAAC,WAAqD;AAAA,KACrE,OAAM,aAAa,KAAK,MAAM,SAAS,CAAC;AAAA,KACxC,OAAM,gBAAgB,MAAM,eAAe;AAAA,KAC3C,OAAM,aAAa,MAAM,SAAS;AAAA,KAClC,OAAM,yBAAyB,MAAM,qBAAqB;AAAA,KAC1D,OAAM,gBAAgB,MAAM,YAAY;AAAA,KACxC,OAAM,eAAe,MAAM,WAAW;AAAA,KACtC,OAAM,qBAAqB,MAAM,sBAAsB,OAAO,MAAM,SAAS;AAAA,KAC7E,OAAM,QAAQ,MAAM,IAAI;AAAA,KACxB,OAAM,YAAY,MAAM,QAAQ;AAAA,KAChC,OAAM,WAAW,MAAM,OAAO;AAAA,KAC9B,OAAM,OAAO,MAAM,QAAQ,YAAY,YAAY,OAAO,MAAM,GAAG,CAAC;AAAA,KACpE,OAAM,UAAU,MAAM,MAAM;AAAA,KAC5B,OAAM,WAAW,MAAM,eAAe,YAAY,YAAY,OAAO,MAAM,UAAU,CAAC;AAAA,KACtF,OAAM,mBAAmB,MAAM,kBAAkB;AAAA,EACpD,MAAM,MAAM;AACd;AAqBO,IAAM,cAAa,CAAC,UAAoD;AAAA,EAC7E,MAAM,QAAQ;AAAA,IACZ,GAAI,MAAM,SAAS,YAAY,CAAC,MAAM,IAAI,CAAC;AAAA,IAC3C,GAAI,MAAM,sBAAsB,QAAQ,CAAC,mBAAmB,IAAI,CAAC;AAAA,EACnE;AAAA,EACA,MAAM,SAAS,MAAK,KAAK;AAAA,EACzB,OAAO,WAAW,QAAQ,KAAK;AAAA;AAW1B,IAAM,iBAAiB,CAC5B,MACA,UACsC;AAAA,EAStC,MAAM,WAAW,KAAK,KAAK,OAAO;AAAA,EAClC,IAAI,aAAa,MAAM,aAAa,MAAM;AAAA,IAAM;AAAA,EAChD,OAAO;AAAA,IACL,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,WAAW,KAAK,KAAK,YAAY;AAAA,IACjC,gBAAgB,KAAK,KAAK,eAAe;AAAA,IACzC,aAAa,KAAK,KAAK,cAAc;AAAA,IACrC,WAAW,KAAK,KAAK,YAAY;AAAA,IACjC,uBAAuB,KAAK,KAAK,wBAAwB;AAAA,IACzD,WAAW,KAAK,KAAK,YAAY;AAAA,IACjC,cAAc,UAAU,KAAK,eAAe;AAAA,IAC5C,YAAY,KAAK,KAAK,aAAa;AAAA,IACnC,aAAa,UAAU,KAAK,cAAc;AAAA,IAC1C,mBAAmB,KAAK,KAAK,oBAAoB;AAAA,IAEjD,MAAM,KAAK,KAAK,OAAO;AAAA,IACvB,UAAU,SAAQ,KAAK,WAAW;AAAA,IAClC,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,UAAU,MAAM,QAAQ,KAAK,WAAW,IAAI,KAAK,YAAY,KAAK,GAAG,IAAI;AAAA,IACzE,SAAS,KAAK,KAAK,UAAU;AAAA,IAC7B,OAAO,MAAM;AAAA,IACb,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,KAAK,IAAI,KAAK,QAAQ,MAAK;AAAA,IAC3B,MAAM,MAAM;AAAA,IACZ,UAAU,IAAI,KAAK,aAAa,MAAK;AAAA,IACrC,QAAQ,UAAU,KAAK,SAAS;AAAA,IAChC,MAAM,aAAa,KAAK,MAAM,OAAO;AAAA,IACrC,WAAW,IAAI,KAAK,YAAY,MAAK;AAAA,IACrC,mBAAmB,KAAK,KAAK,kBAAkB;AAAA,EACjD;AAAA;;;ADjBK,IAAM,qBAAqB,WAA6B,uBAAuB;AAAA,EACpF,sBAAsB;AACxB,CAAC;AAED,IAAM,aAAW,YAAqD;AAAA,EACpE,YAAY;AAAA,EACZ,YAAY,CAAC,UAAU,SAAS,MAAM;AAAA,EAEtC,YAAY;AAAA,EAaZ,SAAS,CAAC,YAAY,UACpB,WAAW,UAAU,MAAM,QAAQ,OACnC,MAAK,MAAM,SAAS,WAAW,OAAO,KACtC,MAAK,MAAM,WAAW,WAAW,SAAS,KAC1C,MAAK,MAAM,KAAK,WAAW,GAAG,KAC9B,YAAY,MAAM,UAAU,WAAW,QAAQ,KAC/C,SAAS,MAAM,cAAc,WAAW,YAAY,KACpD,SAAS,MAAM,aAAa,WAAW,WAAW,KAClD,MAAK,MAAM,mBAAmB,WAAW,iBAAiB,KAC1D,SAAS,MAAM,QAAQ,WAAW,MAAM,KACxC,MAAK,MAAM,WAAW,WAAW,SAAS,KAC1C,MAAK,MAAM,uBAAuB,WAAW,qBAAqB,KAClE,MAAK,MAAM,iBAAiB,WAAW,eAAe,KACtD,MAAK,MAAM,YAAY,WAAW,UAAU,KAC5C,MAAK,MAAM,oBAAoB,WAAW,kBAAkB;AAAA,EAC9D,MAAM,CAAC,UAAU,SAAS,MAAM,gBAAgB,MAAM;AAAA,EACtD,YAAY;AACd,CAAC;AASM,IAAM,6BAA6B,MAC/B,kBAAO,oBAA2B,iBAAQ,mBAAmB,SAAS,GAAG,UAAQ,CAAC,CAAC;;AEpN9F,qBAAS;AACT;AACA;;;AC0DA,IAAM,OAAO,CAAC,UACZ,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC,SAAkB,OAAO,IAAI,CAAC,IAAI,CAAC;AAGvE,IAAM,QAAQ,CAAC,QAA2B,cACxC,OAAO,IAAI,CAAC,SAAS,UAAU,KAAK,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,SAAS,SAAS,EAAE;AAQ3E,IAAM,WAAW,CAAC,SAChB,KACG,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,SAAS,EAAE,EAC5B,KAAK,EACL,KAAK,GAAG;AAEb,IAAM,SAAQ,CAAC,SAAyB;AAAA,EACtC,IAAI,CAAC,KAAK,WAAW,QAAQ;AAAA,IAAG,OAAO;AAAA,EACvC,MAAM,KAAK,KAAK,QAAQ,GAAG;AAAA,EAC3B,IAAI,KAAK;AAAA,IAAG,OAAO;AAAA,EACnB,MAAM,SAAS,KAAK,MAAM,KAAK,CAAC,EAAE,MAAM,GAAG;AAAA,EAC3C,OAAO,GAAG,KAAK,MAAM,GAAG,EAAE,KAAK,OAAO,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAAE,KAAK,GAAG;AAAA;AAG7E,IAAM,QAAO,CAAC,SAAyB;AAGhC,IAAM,iBAAiB,CAAC,YAAuC;AAAA,EACpE,kBAAkB,MAAM,OAAO,qBAAqB,CAAC,GAAG,KAAI,EAAE,KAAK;AAAA,EACnE,eAAe,MAAM,OAAO,kBAAkB,CAAC,GAAG,MAAK,EAAE,KAAK;AAAA,EAC9D,kBAAkB,MAAM,OAAO,qBAAqB,CAAC,GAAG,QAAQ,EAAE,KAAK;AAAA,EACvE,SAAS,MAAM,OAAO,SAAS,KAAI,EAAE,KAAK;AAC5C;AAEO,IAAM,oBAAoB,CAC/B,MACA,UACmC;AAAA,EACnC,SAAS,KAAK,KAAK,UAAU;AAAA,EAC7B,SAAS,KAAK,KAAK,UAAU;AAAA,EAC7B,gBAAgB,KAAK,KAAK,eAAe;AAAA,EACzC,kBAAkB,MAAM,KAAK,KAAK,iBAAiB,GAAG,KAAI,EAAE,KAAK;AAAA,EACjE,eAAe,MAAM,KAAK,KAAK,cAAc,GAAG,MAAK,EAAE,KAAK;AAAA,EAC5D,kBAAkB,MAAM,KAAK,KAAK,iBAAiB,GAAG,QAAQ,EAAE,KAAK;AAAA,EAErE,MAAM,KAAK,KAAK,SAAS,KAAK,KAAK;AAAA,EACnC;AAAA,EACA,QAAQ,KAAK,KAAK,SAAS;AAAA,EAC3B,SAAS,MAAM,KAAK,KAAK,SAAS,GAAG,KAAI,EAAE,KAAK;AAClD;AAEA,IAAM,QAAQ,CAAC,GAAsB,MACnC,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,UAAU,SAAS,EAAE,MAAM;AAE9D,IAAM,iBAAiB,CAC5B,YACA,WACY;AAAA,EACZ,MAAM,QAAQ,eAAe,MAAM;AAAA,EACnC,OACE,WAAW,aAAa,OAAO,YAAY,SAC3C,WAAW,qBAAqB,OAAO,oBAAoB,SAC3D,WAAW,UAAU,OAAO,QAAQ,UACpC,MAAM,WAAW,SAAS,MAAM,OAAO,KACvC,MAAM,WAAW,mBAAmB,MAAM,iBAAiB,KAC3D,MAAM,WAAW,gBAAgB,MAAM,cAAc,KACrD,MAAM,WAAW,mBAAmB,MAAM,iBAAiB,MAC1D,OAAO,YAAY,aAAa,WAAW,YAAY,OAAO;AAAA;AAYnE,IAAM,SAAS,CAAC,YAAuC;AAAA,EACrD,SAAS,OAAO,YAAY,OAAQ,MAAiB;AAAA,EACrD,gBAAgB,OAAO,oBAAoB,OAAQ,MAAiB;AAAA,EACpE,MAAM,OAAO,QAAS;AACxB;AAEA,IAAM,QAAQ,CAAC,WAAiF;AAAA,EAC9F,MAAM,QAAQ,eAAe,MAAM;AAAA,EACnC,MAAM,MAAiD,CAAC;AAAA,EACxD,IAAI,MAAM,kBAAkB,SAAS;AAAA,IAAG,IAAI,oBAAoB,MAAM;AAAA,EACtE,IAAI,MAAM,eAAe,SAAS;AAAA,IAAG,IAAI,iBAAiB,MAAM;AAAA,EAChE,IAAI,MAAM,kBAAkB,SAAS;AAAA,IAAG,IAAI,oBAAoB,MAAM;AAAA,EACtE,IAAI,MAAM,QAAQ,SAAS;AAAA,IAAG,IAAI,SAAS,MAAM;AAAA,EACjD,OAAO;AAAA;AASF,IAAM,oBAAoB,CAAC,YAA0D;AAAA,KACvF,OAAO,MAAM;AAAA,KACb,MAAM,MAAM;AAAA,KACX,OAAO,YAAY,aAAa,OAAO,YAAY,KAAK,CAAC,IAAI,EAAE,SAAS,OAAO,QAAQ;AAAA,EAC3F,MAAM,OAAO;AACf;AAOO,IAAM,oBAAoB,CAAC,WAAyD;AAAA,EACzF,MAAM,UAAU,MAAM,MAAM;AAAA,EAC5B,MAAM,UACJ,CAAC,kBAAkB,eAAe,kBAAkB,QAAQ,EAC5D,OAAO,CAAC,QAAQ,QAAQ,SAAS,SAAS;AAAA,EAC5C,IAAI,OAAO,YAAY;AAAA,IAAI,QAAQ,KAAK,SAAS;AAAA,EACjD,OAAO;AAAA,OACF,OAAO,MAAM;AAAA,OACb;AAAA,OACC,OAAO,YAAY,aAAa,OAAO,YAAY,KAAK,CAAC,IAAI,EAAE,SAAS,OAAO,QAAQ;AAAA,OACvF,QAAQ,WAAW,IAAI,CAAC,IAAI,EAAE,QAAQ,QAAQ;AAAA,EACpD;AAAA;;;ADjKK,IAAM,6BAA6B,WACxC,6BACF;AAEA,IAAM,aAAW,YAAqE;AAAA,EACpF,YAAY,CAAC,MAAM,UAAU,kBAAkB,MAAM,MAAM,IAAI;AAAA,EAC/D,YAAY,MAAM;AAAA,EAClB,YAAY;AAAA,EAEZ,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,EACT,MAAM,CAAC,UAAU,kCAAkC,MAAM;AAAA,EACzD,YAAY;AACd,CAAC;AAEM,IAAM,qCAAqC,MACvC,kBACP,4BACO,iBAAQ,2BAA2B,SAAS,GAAG,UAAQ,CAAC,CACjE;;AE5BF,qBAAS;AACT;AACA;;;AC3BA,IAAM,eAAe;AAGd,IAAM,SAAS,CAAC,UAA2B;AAAA,EAChD,MAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,SAAS,OAAO,SAAS,EAAE,GAAG,EAAE;AAAA,EAC1F,OAAO,OAAO,MAAM,MAAM,IAAI,IAAI;AAAA;AAGpC,IAAM,SAAS,CAAC,UACd,MACG,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,SAAS,EAAE,EAC5B,KAAK,EACL,KAAK,GAAG;AAWN,IAAM,cAAc,CAAC,UAC1B,OACE,MAAM,QAAQ,KAAK,IACf,MAAM,IAAI,CAAC,SAAkB,OAAO,IAAI,CAAC,IACzC,OAAO,UAAU,WACf,MAAM,MAAM,SAAS,IACrB,CAAC,CACT;AAGK,IAAM,aAAa,CAAC,UACzB,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC,SAAkB,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;AAE1F,IAAM,MAAM,CAAC,KAAa,UACxB,UAAU,YAAY,CAAC,IAAI,GAAG,MAAM,MAAM;AAoBrC,IAAM,SAAQ,CAAC,WAA4D;AAAA,EAChF,SAAS,MAAM,YAAY,OAAO,MAAM;AAAA,KACrC,IAAI,UAAU,MAAM,MAAM;AAAA,KAC1B,IAAI,QAAQ,MAAM,IAAI;AAAA,KACtB,IAAI,WAAW,MAAM,OAAO;AAAA,KAC5B,IAAI,gBAAgB,MAAM,eAAe;AAAA,KACzC,IAAI,UAAU,MAAM,WAAW,YAAY,YAAY,MAAM,OAAO,KAAK,YAAY,CAAC;AAAA,KACtF,IAAI,UAAU,MAAM,WAAW,YAAY,YAAY,YAAY,MAAM,MAAM,CAAC;AAAA,KAChF,IACD,eACA,MAAM,mBAAmB,YAAY,YAAY,YAAY,MAAM,cAAc,CACnF;AAAA,KACG,IAAI,UAAU,MAAM,MAAM;AAAA,KAC1B,IAAI,QAAQ,MAAM,IAAI;AAAA,KACtB,IAAI,QAAQ,MAAM,SAAS,YAAY,YAAY,OAAO,MAAM,IAAI,CAAC;AAAA,KACrE,IAAI,UAAU,MAAM,MAAM;AAAA,KAC1B,IAAI,OAAO,MAAM,GAAG;AAAA,KACpB,IAAI,YAAY,MAAM,QAAQ;AACnC;;;ADgDO,IAAM,4BAA4B,WACvC,4BACF;AAEA,IAAM,aAAW,YAAmE;AAAA,EAOlF,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,MAAM,KAAK,KAAK,OAAO;AAAA,IACvB,SAAS,KAAK,KAAK,UAAU;AAAA,IAC7B,SAAS,KAAK,eAAe,KAAK,KAAK,eAAe;AAAA,IACtD,gBAAgB,KAAK,KAAK,eAAe;AAAA,IACzC,QAAQ,WAAW,KAAK,SAAS;AAAA,IACjC,QAAQ,YAAY,KAAK,SAAS;AAAA,IAClC,eAAe,YAAY,KAAK,cAAc;AAAA,IAC9C,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,MAAM,KAAK,KAAK,OAAO;AAAA,IACvB,MAAM,MAAM;AAAA,IACZ,MAAM,OAAO,KAAK,OAAO;AAAA,IACzB,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,MAAM,MAAM;AAAA,IACZ,KAAK,KAAK,KAAK,MAAM;AAAA,IACrB,UAAU,KAAK,KAAK,WAAW;AAAA,EACjC;AAAA,EACA,YAAY,CAAC,UAAU,mCAAmC,MAAM;AAAA,EAEhE,YAAY,CAAC,WAAW,KAAK,OAAM,KAAK,GAAG,MAAM,MAAM,KAAK;AAAA,EAU5D,SAAS,CAAC,YAAY,UACpB,WAAW,aAAa,MAAM,YAAY,UACzC,MAAM,YAAY,aAAa,WAAW,YAAY,MAAM,aAC5D,MAAM,WAAW,aAAa,WAAW,WAAW,MAAM,YAC1D,MAAM,SAAS,aAAa,WAAW,SAAS,MAAM,UACtD,MAAM,SAAS,aAAa,WAAW,SAAS,MAAM,UACtD,MAAM,aAAa,aAAa,WAAW,aAAa,MAAM,cAC9D,MAAM,oBAAoB,aAAa,WAAW,oBAAoB,MAAM,qBAC5E,MAAM,WAAW,aAAa,WAAW,WAAW,MAAM,YAC1D,MAAM,QAAQ,aAAa,WAAW,QAAQ,MAAM,SACpD,MAAM,WAAW,aAAa,WAAW,WAAW,MAAM,YAC1D,MAAM,SAAS,aAAa,WAAW,SAAS,MAAM,UACtD,MAAM,WAAW,aAAa,WAAW,WAAW,YAAY,MAAM,MAAM,OAC5E,MAAM,mBAAmB,aACxB,WAAW,mBAAmB,YAAY,MAAM,cAAc,OAC/D,MAAM,WAAW,aAAa,WAAW,WAAW,WAAW,MAAM,MAAM;AAAA,EAC9E,MAAM,CAAC,UAAU,mCAAmC,MAAM,QAAQ,MAAM;AAAA,EACxE,YAAY;AACd,CAAC;AAQM,IAAM,oCAAoC,MACtC,kBACP,2BACO,iBAAQ,0BAA0B,SAAS,GAAG,UAAQ,CAAC,CAChE;;AE3KF,qBAAS;AACT,uBAAS;AACT;AACA;;;ACrCA;AAmEO,IAAM,aAAa,CACxB,MACA,WAC4B;AAAA,EAC5B,SAAS,eAAe,KAAK,UAAU;AAAA,EACvC,SAAS,KAAK,KAAK,UAAU;AAAA,EAC7B,0BAA0B,KAAK,KAAK,yBAAyB,EAAE,KAAK;AAAA,EACpE,eAAe,KAAK,KAAK,cAAc,EAAE,KAAK;AAAA,EAC9C,cAAc,IAAI,KAAK,eAAe,CAAC;AAAA,EACvC,eAAe,IAAI,KAAK,gBAAgB,CAAC;AAAA,EACzC,aAAa,IAAI,KAAK,cAAc,CAAC;AAAA,EACrC,gBAAgB,IAAI,KAAK,iBAAiB,CAAC;AAAA,EAC3C,eAAe,IAAI,KAAK,gBAAgB,CAAC;AAAA,EACzC,eAAe,IAAI,KAAK,gBAAgB,CAAC;AAAA,EACzC,oBAAoB,eAAe,KAAK,qBAAqB,MAAM;AAAA,EACnE,MAAM,MAAM;AAAA,EACZ,2BAA2B,eAAe,KAAK,0BAA0B;AAAA,EACzE,QAAQ,eAAe,KAAK,SAAS;AAAA,EACrC,eAAe,KAAK,KAAK,cAAc;AAAA,EACvC,MAAM,KAAK,KAAK,OAAO;AAAA,EACvB,kBAAkB,KAAK,KAAK,iBAAiB,EAAE,KAAK;AAAA,EACpD,QAAQ,eAAe,KAAK,SAAS;AAAA,EACrC,cAAc,KAAK,KAAK,aAAa;AACvC;AAEA,IAAM,QAAO,CAAI,UAAyB,SAAY,aAAa,aAAa,aAAa;AAYtF,IAAM,UAAU,CAAC,MAA8B,UACpD,MAAK,MAAM,SAAS,KAAK,OAAO,KAChC,MAAK,MAAM,gBAAgB,KAAK,GAAG,KAAK,cAAc,KACtD,MAAK,MAAM,mBAAmB,KAAK,GAAG,KAAK,iBAAiB,KAC5D,MAAK,MAAM,cAAc,KAAK,YAAY,KAC1C,MAAK,MAAM,gBAAgB,KAAK,cAAc,KAC9C,MAAK,MAAM,eAAe,KAAK,aAAa,KAC5C,MAAK,MAAM,gBAAgB,KAAK,cAAc,KAC9C,MAAK,MAAM,iBAAiB,KAAK,eAAe,KAChD,MAAK,MAAM,gBAAgB,KAAK,cAAc,KAC9C,MAAK,MAAM,gBAAgB,KAAK,cAAc,KAC9C,MAAK,MAAM,2BAA2B,KAAK,GAAG,KAAK,yBAAyB,MAC3E,MAAM,+BAA+B,aACpC,eAAe,MAAM,0BAA0B,MAAM,KAAK,+BAC5D,MAAK,MAAM,eAAe,KAAK,aAAa,MAC3C,MAAM,WAAW,aAAa,eAAe,MAAM,MAAM,MAAM,KAAK,YACpE,MAAM,WAAW,aAAa,eAAe,MAAM,MAAM,MAAM,KAAK,YACpE,MAAM,wBAAwB,aAC7B,eAAe,MAAM,qBAAqB,MAAM,MAAM,KAAK;AAE/D,IAAM,OAAM,CAAC,KAAa,UACxB,UAAU,YAAY,CAAC,IAAI,GAAG,MAAM,MAAM;AAG5C,IAAM,QAAQ,CAAC,UAA+B,UAAU,YAAY,YAAY,OAAO,KAAK;AAY5F,IAAM,UAAU,CAAC,WAAsD;AAAA,KAClE,KAAI,WAAW,MAAM,OAAO;AAAA,KAC5B,KAAI,eAAe,MAAM,gBAAgB,KAAK,CAAC;AAAA,KAC/C,KAAI,cAAc,MAAM,MAAM,aAAa,CAAC;AAAA,KAC5C,KAAI,eAAe,MAAM,MAAM,cAAc,CAAC;AAAA,KAC9C,KAAI,aAAa,MAAM,MAAM,YAAY,CAAC;AAAA,KAC1C,KAAI,gBAAgB,MAAM,MAAM,eAAe,CAAC;AAAA,KAChD,KAAI,eAAe,MAAM,MAAM,cAAc,CAAC;AAAA,KAC9C,KAAI,eAAe,MAAM,MAAM,cAAc,CAAC;AAAA,KAC9C,KAAI,0BAA0B,MAAM,yBAAyB;AAAA,KAC7D,KAAI,oBAAoB,MAAM,mBAAmB;AAAA,KACjD,KAAI,2BAA2B,MAAM,0BAA0B;AAAA,KAC/D,KAAI,UAAU,MAAM,MAAM;AAAA,KAC1B,KAAI,eAAe,MAAM,cAAc;AAAA,KACvC,KAAI,kBAAkB,MAAM,mBAAmB,KAAK,CAAC;AAAA,KACrD,KAAI,UAAU,MAAM,MAAM;AAAA,KAC1B,KAAI,cAAc,KAAK,MAAM,aAAa,CAAC;AAChD;AAWO,IAAM,cAAa,CAAC,WAAuC;AAAA,KAC7D,QAAQ,KAAK;AAAA,EAChB,MAAM,MAAM;AAAA,EACZ,MAAM,MAAM;AAAA,KAKR,MAAM,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;AAClE;AAqBO,IAAM,aAAa,CAAC,UAAsC,QAAQ,KAAK;AAEvE,IAAM,UAAS,CAAC,UAA6B,oBAAoB,MAAM;AAwBvE,IAAM,UAAU,CAAC,UACtB,IAA6B,MAAM,QAAQ,QAAQ,OAAO,QAAO,KAAK,CAAC,EAAE,KAChE,aAAI,CAAC,SACV,SAAS,aAAa,OAAO,KAAK,YAAY,WAAW,YAAY,WAAW,MAAM,KAAK,CAC7F,GACO,uBAAc,MAAG;AAAA,EAAG;AAAA,CAAS,CACtC;;;AC/OF;AAgBA,IAAM,WAAW,CAAC,MAAc,UAAkB;AAAA,EAChD,MAAM,OAAO,CAAC,UAAmB,MAAM,SAAS,IAAI,MAAM,QAAQ,QAAQ,EAAE,IAAI;AAAA,EAChF,OAAO,KAAK,IAAI,MAAM,KAAK,KAAK;AAAA;AAQ3B,IAAM,YAAY,CAAC,MAA8B,UACtD,KAAK,SAAS,MAAM,SAAS,KAAK,MAAM,MAAM,IAAI,IACvC,gBACA,aACL,IAAI,MACF,GAAG,QAAO,KAAK,oBAAoB,MAAM,sBAAsB,KAAK,oBAClE,sFACA,wFACA,0FACA,uCACJ,CACF;AAgBC,IAAM,eAAe,CAAC,MAA8B,UACzD,MAAM,YAAY,aAAa,eAAe,MAAM,OAAO,MAAM,KAAK,UAC3D,gBACA,aACL,IAAI,MACF,GAAG,QAAO,KAAK,uBAAuB,MAAM,2BAC1C,GAAG,KAAK,YAAY,KAAK,+BAA+B,KAAK,0BAC7D,yFACA,yFACA,iFACA,+BACJ,CACF;AAuBC,IAAM,UAAS,CAAU,OAA8B,SACrD,aAAI,UAAU,GAAG;AAAA,EACtB,SAAS,UAAU,EAAG,UAAU,IAAI,WAAW,GAAG;AAAA,IAChD,MAAM,OAAO,OAAO;AAAA,IACpB,IAAI,KAAK,IAAI;AAAA,MAAG,OAAO;AAAA,IACvB,OAAc,eAAM,WAAW;AAAA,EACjC;AAAA,EACA,OAAO,OAAO;AAAA,CACf;;;AC5FH;AAMO,IAAM,qBAAqF;AAAA,EAChG,QAAQ;AAAA,EACR,QAAQ;AACV;AAGO,IAAM,sBAAsB,CAAC,UAC3B,aAAI,UAAU,GAAG;AAAA,EACtB,OAAO,UAAU,mBAAmB,QAAQ,YAAW,KAAK,GAAG,IAAI;AAAA,EACnE,OAAO,UAAU,mBAAmB,QAAQ,WAAW,KAAK,GAAG,KAAK;AAAA,CACrE;;;AHoII,IAAM,eAAe,WAAuB,iBAAiB;AAAA,EAClE,sBAAsB;AACxB,CAAC;AAEM,IAAM,uBAAuB,MACzB,kBACP,cACO,iBACL,aAAa,SAAS,GAAG;AAAA,EAMvB,MAAM,MAAa,iBAAQ,CAAC,CAAC;AAAA,EAC7B,MAAM,GAAG,WAAW,QAAQ,IAAI;AAAA,EAOhC,MAAM,GAAG,MAAM,aACN,aAAI,UAAU,GAAG;AAAA,IACtB,IAAI,CAAC,YAAW,IAAI;AAAA,MAAG;AAAA,IAGvB,OAAO,oBAAoB,IAAI;AAAA,IAC/B,IAAI,WAAW;AAAA,MAAW;AAAA,IAC1B,MAAM,OAAO,OAAO,QAAQ,IAAI;AAAA,IAGhC,IAAI,SAAS;AAAA,MAAW,OAAO,EAAE,QAAQ,SAAS;AAAA,IAClD,OAAO,UAAU,MAAM,IAAI;AAAA,IAC3B,OAAO,aAAa,MAAM,IAAI;AAAA,IAC9B,OAAO,QAAQ,MAAM,IAAI,IACpB,EAAE,QAAQ,OAAO,IACjB,EAAE,QAAQ,SAAS;AAAA,GACzB;AAAA,EAMH,WAAkB,YAAG,UAAU,GAAG,QAAQ;AAAA,IAExC,OAAO,oBAAoB,IAAI;AAAA,IAC/B,MAAM,OAAO,OAAO,QAAQ,IAAI;AAAA,IAChC,IAAI;AAAA,IACJ,IAAI,SAAS,WAAW;AAAA,MACtB,OAAO,OAAO,IACZ,KAAK,QACL,aACA,QACA,oBACA,YAAW,IAAI,CACjB;AAAA,IACF,EAAO;AAAA,MACL,OAAO,UAAU,MAAM,IAAI;AAAA,MAC3B,OAAO,aAAa,MAAM,IAAI;AAAA,MAE9B,MAAM,OAAO,WAAW,SAAS,MAAM,MAAM,WAAW,IAAI,CAAC;AAAA,MAC7D,IAAI,SAAS,WAAW;AAAA,QACtB,OAAO,IAAI,KAAK,QAAQ,aAAa,OAAO,QAAO,IAAI,GAAG,IAAI;AAAA,MAChE;AAAA;AAAA,IAEF,MAAM,QAAQ,OAAO,QACnB,QAAQ,IAAI,GACZ,CAAC,QAAQ,QAAQ,aAAa,QAAQ,KAAK,IAAI,CACjD;AAAA,IACA,IAAI,UAAU,aAAa,CAAC,QAAQ,OAAO,IAAI,GAAG;AAAA,MAKhD,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,QAAO,IAAI,qEACZ,4DAA4D,QAAQ,cACpE,gFACA,mFACA,2DACJ,CACF;AAAA,IACF;AAAA,IACA,OAAO;AAAA,GACR;AAAA,EAgBD,QAAe,YAAG,UAAU,GAAG,QAAQ;AAAA,IACrC,OAAO,IAAY,KAAK,QAAQ,aAAa,UAAU,QAAO,IAAI,CAAC;AAAA,IACnE,MAAM,OAAO,OAAO,QAAO,QAAQ,IAAI,GAAG,CAAC,QAAQ,QAAQ,SAAS;AAAA,IACpE,IAAI,SAAS,WAAW;AAAA,MACtB,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,QAAO,IAAI,mEACZ,kFACA,kEACA,GAAG,KAAK,0EACZ,CACF;AAAA,IACF;AAAA,GACD;AACH,CAAC,CACH,CACF;;AI7OF,qBAAS;AACT;AACA;AAsBO,IAAM,yBAAyB,WAAiC,yBAAyB;AAEhG,IAAM,aAAW,YAAwE;AAAA,EACvF,YAAY,CAAC,MAAM,UAAU,kBAAkB,MAAM,MAAM,IAAI;AAAA,EAC/D,YAAY,MAAM;AAAA,EAClB,YAAY;AAAA,EAEZ,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,EACT,MAAM,CAAC,UAAU,iCAAiC,MAAM;AAAA,EACxD,YAAY;AACd,CAAC;AAEM,IAAM,iCAAiC,MACnC,kBACP,wBACO,iBAAQ,uBAAuB,SAAS,GAAG,UAAQ,CAAC,CAC7D;;ACzDF,qBAAS;AACT;AACA;;;ACjBA,uBAAS;AAET;;;ACaA,IAAM,eAAe;AAGrB,IAAM,kBACJ;AACF,IAAM,oBAAoB;AAEnB,IAAM,mBAAmB,CAAC,SAC/B,kBAAkB,KAAK,IAAI,KAAK,gBAAgB,KAAK,IAAI;AAE3D,IAAM,WAAU,CAAC,UAA2B,UAAU,MAAM,CAAC,aAAa,KAAK,KAAK;AAG7E,IAAM,2BAA2B,CACtC,WAEA,OAAO,QAAQ,UAAU,CAAC,CAAC,EACxB,OAAO,EAAE,MAAM,WAAW,OAAO,UAAU,YAAY,iBAAiB,IAAI,CAAC,EAC7E,OAAO,IAAI,WAAW,SAAQ,KAAe,CAAC,EAC9C,IAAI,EAAE,UAAU,IAAI,EACpB,KAAK;AAOH,IAAM,0BAA0B,CAAC,QAAsC;AAAA,EAC5E,IAAI,QAAQ;AAAA,IAAW,OAAO,CAAC;AAAA,EAC/B,MAAM,MAAgB,CAAC;AAAA,EACvB,MAAM,YAAY,oCAAoC,KAAK,GAAG,IAAI,MAAM;AAAA,EACxE,MAAM,KAAK,UAAU,YAAY,GAAG;AAAA,EACpC,IAAI,MAAM,GAAG;AAAA,IACX,MAAM,WAAW,UAAU,MAAM,GAAG,EAAE;AAAA,IACtC,MAAM,QAAQ,SAAS,QAAQ,GAAG;AAAA,IAClC,IAAI,SAAS,KAAK,SAAQ,SAAS,MAAM,QAAQ,CAAC,CAAC;AAAA,MAAG,IAAI,KAAK,UAAU;AAAA,EAC3E;AAAA,EAEA,MAAM,SAAS,IAAI,QAAQ,WAAW,EAAE;AAAA,EACxC,WAAW,QAAQ,OAAO,MAAM,OAAO,GAAG;AAAA,IACxC,MAAM,KAAK,KAAK,QAAQ,GAAG;AAAA,IAC3B,MAAM,OAAO,KAAK,IAAI,OAAO,KAAK,MAAM,GAAG,EAAE;AAAA,IAC7C,IAAI,MAAM,KAAK,iBAAiB,IAAI,KAAK,SAAQ,KAAK,MAAM,KAAK,CAAC,CAAC;AAAA,MAAG,IAAI,KAAK,IAAI,MAAM;AAAA,EAC3F;AAAA,EACA,OAAO;AAAA;;;ACtDT,mBAAS;;;ACMT;AACA;AAmBO,IAAM,aAAa,CACxB,MACA,MAAmB,QAAQ,QACoE;AAAA,EAC/F,MAAM,SAAiC,CAAC;AAAA,EACxC,MAAM,UAAoB,CAAC;AAAA,EAC3B,YAAY,KAAK,QAAQ,OAAO,QAAQ,IAAI,GAAG;AAAA,IAC7C,MAAM,QAAQ,IAAI,IAAI;AAAA,IACtB,IAAI,UAAU,aAAa,UAAU;AAAA,MAAI,QAAQ,KAAK,IAAI,OAAO;AAAA,IAC5D;AAAA,aAAO,OAAO;AAAA,EACrB;AAAA,EACA,OAAO,EAAE,SAAS,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG,OAAO;AAAA;AAQzD,IAAM,OAAO,EAAE,GAAG,OAAQ,QAAQ,KAAK,OAAO,MAAM,GAAG,GAAG,GAAG,EAAE;AAC/D,IAAM,SAAS;AAGf,IAAM,YAAY,CAAC,WACjB,KAAK,UACH,OAAO,KAAK,MAAM,EACf,KAAK,EACL,IAAI,CAAC,QAAQ,CAAC,KAAK,OAAO,IAAI,CAAC,CACpC;AAEF,IAAM,SAAS,CAAC,MAAc,WAC5B,WAAW,UAAU,MAAM,GAAG,MAAM,IAAI,IAAI;AASvC,IAAM,OAAO,CAAC,QAA0C,SAA0B;AAAA,EACvF,IAAI,OAAO,KAAK,MAAM,EAAE,WAAW;AAAA,IAAG,OAAO;AAAA,EAC7C,MAAM,QAAQ,SAAS,YAAY,YAAY,EAAE,IAAI,OAAO,KAAK,MAAM,MAAM;AAAA,EAC7E,OAAO,GAAG,UAAU,MAAM,SAAS,WAAW,KAAK,OAAO,OAAO,MAAM,EAAE,SAAS,WAAW;AAAA;AASxF,IAAM,cAAc,CAAC,QAAgB,WAAsD;AAAA,EAChG,IAAI,WAAW;AAAA,IAAI,OAAO,OAAO,KAAK,MAAM,EAAE,WAAW;AAAA,EACzD,OAAO,QAAQ,MAAM,QAAQ,SAAS,OAAO,MAAM,GAAG;AAAA,EACtD,IAAI,WAAW,UAAU,SAAS,aAAa,WAAW,aAAa,UAAU,WAAW;AAAA,IAC1F,OAAO;AAAA,EACT;AAAA,EACA,MAAM,WAAW,OAAO,KAAK,QAAQ,WAAW;AAAA,EAChD,MAAM,SAAS,OAAO,OAAO,KAAK,MAAM,WAAW,GAAG,MAAM;AAAA,EAC5D,OAAO,SAAS,WAAW,OAAO,UAAU,gBAAgB,UAAU,MAAM;AAAA;;;AD7EvE,IAAM,SAAS,CAAC,UAA0B,QAAO,KAAK,OAAO,MAAM,EAAE,SAAS,QAAQ;AAGtF,IAAM,WAAW,CAAC,UAA0B,QAAO,KAAK,OAAO,QAAQ,EAAE,SAAS,MAAM;AAQxF,IAAM,cAAc,CAAC,SAAmD;AAAA,EAC7E,MAAM,QAAgC,CAAC;AAAA,EACvC,WAAW,QAAQ,KAAK,MAAM,GAAG,GAAG;AAAA,IAClC,MAAM,KAAK,KAAK,QAAQ,GAAG;AAAA,IAC3B,IAAI,KAAK;AAAA,MAAG,MAAM,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK;AAAA,EACxE;AAAA,EACA,MAAM,QAAQ,MAAM;AAAA,EACpB,OAAO,UAAU,YAAY,EAAE,MAAM,MAAM,WAAW,GAAG,IAAI,EAAE,MAAM,MAAM,WAAW,IAAI,MAAM;AAAA;AAI3F,IAAM,WAAW,CAAC,MAAc,UACrC,QAAQ,cAAc,OAAO,KAAK;AAEpC,IAAM,QAAO,CAAC,UACZ,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC,SAAkB,OAAO,IAAI,CAAC,IAAI,CAAC;AAGhE,IAAM,QAAQ,CAAC,SACpB,CAAC,GAAG,IAAI,EACL,OAAO,CAAC,QAAQ,QAAQ,EAAE,EAC1B,KAAK,EACL,KAAK,GAAG;AAON,IAAM,eAAe,CAAC,QAAgB,WAC3C,KAAK,QAAO,8BAA8B,eAAe;AAMpD,IAAM,mBAAmB,CAC9B,MACA,UACoC;AAAA,EACpC,MAAM,UAAkC,CAAC;AAAA,EACzC,WAAW,QAAQ,MAAK,KAAK,SAAS,GAAG;AAAA,IACvC,QAAQ,MAAM,UAAU,YAAY,IAAI;AAAA,IACxC,IAAI,SAAS;AAAA,MAAI,QAAQ,QAAQ,SAAS,SAAS,EAAE;AAAA,EACvD;AAAA,EACA,MAAM,UAAU,MAAK,KAAK,SAAS,EAAE,IAAI,CAAC,SAAS,YAAY,IAAI,EAAE,IAAI;AAAA,EACzE,OAAO;AAAA,IACL,QAAQ,KAAK,KAAK,SAAS;AAAA,IAI3B,MAAM,SAAS,KAAK,KAAK,OAAO,CAAC;AAAA,IACjC,SAAS,KAAK,KAAK,UAAU;AAAA,IAC7B,SAAS,KAAK,KAAK,UAAU;AAAA,IAC7B,gBAAgB,KAAK,KAAK,eAAe;AAAA,IACzC,QAAQ,MAAM,OAAO,KAAK,OAAO,CAAC;AAAA,IAClC,cAAc,aAAa,MAAM,MAAM,OAAO;AAAA,IAC9C,QAAQ,YAAY,KAAK,SAAS;AAAA,IAClC,eAAe,YAAY,KAAK,cAAc;AAAA,IAC9C,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,MAAM,KAAK,KAAK,OAAO;AAAA,IACvB,MAAM,MAAM;AAAA,IAGZ,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,MAAM,OAAO,KAAK,OAAO;AAAA,IACzB,QAAQ,MAAM,OAAO;AAAA,IACrB,QAAQ;AAAA,IACR,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,MAAM,MAAM;AAAA,IACZ,KAAK,KAAK,KAAK,MAAM;AAAA,IACrB,UAAU,KAAK,KAAK,WAAW;AAAA,EACjC;AAAA;;;AE7EF,IAAM,QAAoE;AAAA,EACxE,QAAQ,CAAC,YAAY,MAAM;AAAA,EAC3B,MAAM,CAAC,SAAS;AAAA,EAChB,gBAAgB,CAAC,YAAY,MAAM;AAAA,EACnC,QAAQ,CAAC,SAAS;AAAA,EAClB,QAAQ,CAAC,YAAY,MAAM;AAAA,EAC3B,eAAe,CAAC,YAAY,MAAM;AAAA,EAClC,QAAQ,CAAC,SAAS;AAAA,EAClB,MAAM,CAAC,MAAM;AAAA,EACb,UAAU,CAAC,MAAM;AAAA,EACjB,MAAM,CAAC,MAAM;AAAA,EACb,QAAQ,CAAC,SAAS;AAAA,EAClB,QAAQ,CAAC,MAAM;AAAA,EACf,KAAK,CAAC,SAAS;AAAA,EACf,UAAU,CAAC,MAAM;AACnB;AAEA,IAAM,WAA8D;AAAA,EAClE,UAAU,CAAC;AAAA,EACX,MAAM,CAAC,UAAU,cAAc;AAAA,EAC/B,SAAS,CAAC,OAAO,QAAQ;AAC3B;AAGA,IAAM,aAAa;AAGnB,IAAM,aACJ;AAQK,IAAM,WAAW,CAAC,UAA2B;AAAA,EAClD,MAAM,MAAgB,CAAC;AAAA,EACvB,YAAY,QAAO,UAAU,OAAO,QAAQ,KAAK,GAA0C;AAAA,IACzF,IAAI,MAAM,YAAW,aAAa,CAAC,MAAM,SAAS,MAAM,IAAI,GAAG;AAAA,MAC7D,IAAI,KAAK,IAAI,sBAAqB,MAAM,KAAK,GAAG,kBAAkB,MAAM,MAAM;AAAA,IAChF;AAAA,EACF;AAAA,EACA,WAAW,UAAS,SAAS,MAAM,OAAO;AAAA,IACxC,IAAI,MAAM,YAAW,aAAa,MAAM,YAAW;AAAA,MACjD,IAAI,KAAK,GAAG,MAAM,eAAe,SAAQ;AAAA,EAC7C;AAAA,EACA,WAAW,OAAO,CAAC,GAAG,OAAO,KAAK,MAAM,UAAU,CAAC,CAAC,GAAG,GAAG,OAAO,KAAK,MAAM,UAAU,CAAC,CAAC,CAAC,GAAG;AAAA,IAC1F,IAAI,CAAC,WAAW,KAAK,GAAG;AAAA,MAAG,IAAI,KAAK,IAAI,wCAAwC;AAAA,EAClF;AAAA,EAEA,WAAW,QAAQ,yBAAyB,MAAM,MAAM,GAAG;AAAA,IACzD,IAAI,KAAK,WAAW,qCAAqC,YAAY;AAAA,EACvE;AAAA,EACA,WAAW,SAAS,wBAAwB,MAAM,GAAG,GAAG;AAAA,IACtD,IAAI,KAAK,SAAS,qCAAqC,YAAY;AAAA,EACrE;AAAA,EACA,OAAO;AAAA;AAGT,IAAM,YAAY,CAAC,UAA8C,OAAO,UAAU;AAG3E,IAAM,gBAAgB,CAAC,OAAc,MAAmB,QAAQ,QAAQ;AAAA,EAC7E,MAAM,aAAsC,CAAC;AAAA,EAC7C,MAAM,UAAkC,CAAC;AAAA,EACzC,YAAY,MAAM,UAAU,OAAO,QAAQ,MAAM,UAAU,CAAC,CAAC,GAAG;AAAA,IAC9D,IAAI,UAAU,KAAK;AAAA,MAAG,WAAW,QAAQ;AAAA,IACpC;AAAA,cAAQ,QAAQ;AAAA,EACvB;AAAA,EACA,MAAM,SAAS,WAAW,YAAY,GAAG;AAAA,EACzC,MAAM,aAAsC,CAAC;AAAA,EAC7C,YAAY,MAAM,QAAQ,OAAO,QAAQ,MAAM,UAAU,CAAC,CAAC;AAAA,IAAG,WAAW,UAAU,UAAU;AAAA,EAC7F,IAAI,MAAM,aAAa;AAAA,IAAW,WAAW,cAAc,MAAM;AAAA,EACjE,MAAM,SAAS,WAAW,YAAY,GAAG;AAAA,EACzC,OAAO;AAAA,IACL,QAAQ,EAAE,SAAS,OAAO,SAAS,QAAQ,KAAK,YAAY,OAAO,OAAO,EAAE;AAAA,IAC5E;AAAA,EACF;AAAA;AAMF,IAAM,QAAO,CAAC,UAA8B,SAC1C,aAAa,aAAa,aAAa;AAGlC,IAAM,eAAe,CAAC,MAAuC,UAClE,KAAK,aAAa,MAAM,YAAY,SACpC,MAAK,MAAM,SAAS,KAAK,OAAO,KAChC,MAAK,MAAM,QAAQ,KAAK,MAAM,KAC9B,MAAK,MAAM,iBAAiB,KAAK,eAAe,KAChD,MAAK,MAAM,QAAQ,KAAK,MAAM,MAC7B,MAAM,SAAS,aAAa,MAAM,SAAS,KAAK,SACjD,MAAK,MAAM,MAAM,KAAK,IAAI,KAC1B,MAAK,MAAM,UAAU,KAAK,QAAQ,KAClC,MAAK,MAAM,KAAK,KAAK,GAAG,KACxB,MAAK,MAAM,QAAQ,KAAK,MAAM,KAC9B,MAAK,MAAM,MAAM,KAAK,IAAI,MACzB,MAAM,WAAW,aAAa,YAAY,MAAM,MAAM,MAAM,KAAK,YACjE,MAAM,mBAAmB,aAAa,YAAY,MAAM,cAAc,MAAM,KAAK;AAE7E,IAAM,cAAc,CACzB,MACA,OACA,WACe;AAAA,EACf,IAAI,MAAM,WAAW;AAAA,IAAW,OAAO;AAAA,EACvC,IAAI,MAAM,OAAO,KAAK,MAAM,MAAM,CAAC,MAAM,KAAK;AAAA,IAAQ,OAAO;AAAA,EAC7D,IAAI,OAAO,OAAO,QAAQ,SAAS;AAAA,IAAG,OAAO;AAAA,EAC7C,OAAO,aAAa,MAAM,MAAM,OAAO,OAAO,MAAM,MAAM,KAAK,eAAe,UAAU;AAAA;AAGnF,IAAM,cAAc,CACzB,MACA,OACA,QACA,WACe;AAAA,EACf,IAAI,MAAM,WAAW,aAAa,MAAM,aAAa;AAAA,IAAW,OAAO;AAAA,EACvE,IAAI,MAAM,WAAW,aAAa,MAAM,OAAO,KAAK,MAAM,MAAM,CAAC,MAAM,KAAK;AAAA,IAC1E,OAAO;AAAA,EACT,IAAI,OAAO,OAAO,QAAQ,SAAS,KAAK,WAAW;AAAA,IAAI,OAAO;AAAA,EAC9D,OAAO,YAAY,QAAQ,OAAO,OAAO,MAAM,IAAI,UAAU;AAAA;AAS/D,IAAM,cAAc,CAAC,OAAc,WACjC,OAAO,KAAK,MAAM,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,SACnC,SAAS,MAAM,OAAO,OAAO,OAAO,UAAU,WAAW,EAAE,CAC7D;AAEF,IAAM,cAAc,CAAC,OAAc,WACjC,OAAO,KAAK,MAAM,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,SAAS,MAAM,OAAO,OAAO,OAAO,SAAS,EAAE,CAAC;AAGhG,IAAM,aAAa,CAAC,UAAyD;AAAA,EAC3E,CAAC,UAAU,MAAM,UAAU,EAAE;AAAA,EAC7B,CAAC,QAAQ,MAAM,SAAS,YAAY,KAAK,OAAO,MAAM,IAAI,CAAC;AAAA,EAC3D,CAAC,WAAW,MAAM,WAAW,EAAE;AAAA,EAC/B,CAAC,gBAAgB,MAAM,mBAAmB,EAAE;AAAA,EAC5C,CAAC,UAAU,MAAM,UAAU,EAAE;AAAA,EAC7B,CAAC,QAAQ,MAAM,QAAQ,EAAE;AAAA,EACzB,CAAC,QAAQ,MAAM,SAAS,YAAY,KAAK,OAAO,MAAM,IAAI,CAAC;AAAA,EAC3D,CAAC,UAAU,MAAM,UAAU,EAAE;AAAA,EAC7B,CAAC,OAAO,MAAM,OAAO,EAAE;AAAA,EACvB,CAAC,YAAY,MAAM,YAAY,EAAE;AAAA,EACjC,CAAC,UAAU,MAAM,WAAW,YAAY,CAAC,IAAI,YAAY,MAAM,MAAM,EAAE,MAAM,GAAG,CAAC;AAAA,EACjF;AAAA,IACE;AAAA,IACA,MAAM,mBAAmB,YAAY,CAAC,IAAI,YAAY,MAAM,cAAc,EAAE,MAAM,GAAG;AAAA,EACvF;AACF;AAEA,IAAM,WAAW,CAAC,OAAc,QAC7B,MAA6C,SAAS;AAEzD,IAAM,QAAQ,CAAC,UACb,OAAO,UAAU,WAAW,UAAU,KAAK,MAAM,OAAO,CAAC,SAAS,SAAS,EAAE,EAAE,WAAW;AAQrF,IAAM,aAAa,CACxB,OACA,QACA,OACA,SACY;AAAA,EACZ,MAAM,OAAmD;AAAA,IACvD,SAAS,MAAM,YAAY,OAAO,MAAM;AAAA,EAC1C;AAAA,EACA,MAAM,UAAoB,CAAC;AAAA,EAC3B,MAAM,MAAM,CAAC,KAAa,UAAsC;AAAA,IAC9D,IAAI,CAAC,MAAM,KAAK;AAAA,MAAG,KAAK,OAAO;AAAA,IAC1B,SAAI,SAAS;AAAA,MAAU,QAAQ,KAAK,GAAG;AAAA;AAAA,EAE9C,YAAY,KAAK,UAAU,WAAW,KAAK;AAAA,IAAG,IAAI,SAAS,OAAO,GAAG;AAAA,MAAG,IAAI,KAAK,KAAK;AAAA,EACtF,IAAI,MAAM,UAAU,MAAM,WAAW;AAAA,IAAW,IAAI,UAAU,YAAY,OAAO,MAAM,CAAC;AAAA,EACxF,IAAI,MAAM,UAAU,MAAM,WAAW;AAAA,IAAW,IAAI,UAAU,YAAY,OAAO,MAAM,CAAC;AAAA,EACxF,IAAI,MAAM,UAAU,MAAM,aAAa,WAAW;AAAA,IAChD,KAAK,cAAc,OAAO,OAAO,OAAO,eAAe;AAAA,EACzD;AAAA,EACA,IAAI,SAAS;AAAA,IAAU,KAAK,UAAU,MAAM;AAAA,EAC5C,IAAI,QAAQ,SAAS;AAAA,IAAG,KAAK,YAAY;AAAA,EACzC,OAAO;AAAA;;;AJ9LT,IAAM,OAAO,CAAC,UAAiB,kCAAkC,MAAM,QAAQ,MAAM;AAMrF,IAAM,YAAW,CAAC,UAChB,IAA6B,MAAM,QAAQ,QAAQ,OAAO,KAAK,KAAK,CAAC,EAAE,KAC9D,aAAI,CAAC,SAAU,QAAQ,OAAO,YAAY,iBAAiB,MAAM,KAAK,CAAE,GACxE,uBAAc,MAAG;AAAA,EAAG;AAAA,CAAS,CACtC;AAEF,IAAM,UAAS,CAAC,UAAiB;AAAA,EAC/B,MAAM,UAAU,SAAS,KAAK;AAAA,EAC9B,OAAO,QAAQ,WAAW,IACf,gBACA,aAAI,IAAI,MAAM,GAAG,KAAK,KAAK,MAAM,QAAQ,KAAK,IAAI,IAAI,CAAC;AAAA;AAIpE,IAAM,UAAU,CAAC,MAAkB,OAAc,QAAgB,WAAmB;AAAA,EAClF,MAAM,SAAS,YAAY,MAAM,OAAO,MAAM,MAAM;AAAA,EACpD,MAAM,cAAc,YAAY,MAAM,OAAO,QAAQ,MAAM,MAAM;AAAA,EACjE,OAAO,aAAa,MAAM,KAAK,KAAK,CAAC,UAAU,CAAC,cAC5C,YACC,EAAE,QAAQ,QAAQ,YAAY;AAAA;AAIrC,IAAM,gBAAgB,CAAC,OAAc,QAAgB,UAAiB;AAAA,EACpE,MAAM,UAAU;AAAA,IACd,GAAI,MAAM,SAAS,OAAO,OAAO,UAAU,CAAC;AAAA,IAC5C,GAAI,MAAM,SAAS,OAAO,OAAO,UAAU,CAAC;AAAA,EAC9C;AAAA,EACA,OAAO,QAAQ,WAAW,IACf,gBACA,aACL,IAAI,MACF,GAAG,KAAK,KAAK,kDAAkD,QAAQ,KAAK,IAAI,OAC9E,6EACJ,CACF;AAAA;AAGC,IAAM,aAAW;AAAA,EAEtB,MAAM,MAAa,iBAAQ,CAAC,CAAC;AAAA,EAiB7B,MAAM,GAAG,MAAM,cACZ,WAAW,YAAY,QAAO,IAAI,IAAW,eAAM,KAC3C,iBAAQ,UAAS,IAAI,CAAC,GACtB,aAAI,CAAC,SACV,SAAS,YAAY,YAAY,KAAK,MAAM,QAAQ,QAAQ,UAAU,GAAG,CAC3E,CACF;AAAA,EAOF,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,QAMO,aAAI,UAAU,GAAG;AAAA,IACtB,IAAI,WAAW,aAAa,CAAC,YAAW,IAAI;AAAA,MAAG;AAAA,IAC/C,IAAI,SAAS,cAAc,KAAK,SAAS,KAAK,QAAQ,KAAK,SAAS,KAAK,OAAO;AAAA,MAC9E,OAAO,EAAE,QAAQ,WAAW,aAAa,KAAK,SAAS,KAAK,KAAK;AAAA,IACnE;AAAA,IACA,OAAO,QAAO,IAAI;AAAA,IAClB,MAAM,OAAO,OAAO,UAAS,IAAI;AAAA,IACjC,IAAI,SAAS;AAAA,MAAW,OAAO,EAAE,QAAQ,SAAS;AAAA,IAClD,OAAO,QAAQ,MAAM,MAAM,cAAc,IAAI,GAAG,OAAO,MAAM,MAAM,YAC9D,EAAE,QAAQ,OAAO,IACjB,EAAE,QAAQ,SAAS;AAAA,GACzB;AAAA,EACH,WAAW,GAAG,MAAM,aACX,aAAI,UAAU,GAAG;AAAA,IACtB,OAAO,QAAO,IAAI;AAAA,IAClB,MAAM,SAAS,cAAc,IAAI;AAAA,IACjC,MAAM,OAAO,OAAO,UAAS,IAAI;AAAA,IACjC,IAAI,SAAS,QAAQ,UAAU;AAAA,IAC/B,IAAI,SAAS,WAAW;AAAA,MACtB,MAAM,QAAQ,EAAE,QAAQ,MAAM,QAAQ,KAAK;AAAA,MAC3C,OAAO,cAAc,MAAM,QAAQ,KAAK;AAAA,MACxC,MAAM,cAAa,kCAAkC,KAAK;AAAA,MAC1D,OAAO,IACL,KAAK,QACL,aACA,QACA,aACA,WAAW,MAAM,QAAQ,OAAO,QAAQ,CAC1C;AAAA,MACA,SAAS,KAAK,OAAO,OAAO,MAAM;AAAA,IACpC,EAAO;AAAA,MACL,MAAM,QAAQ,QAAQ,MAAM,MAAM,QAAQ,MAAM;AAAA,MAChD,IAAI,UAAU,WAAW;AAAA,QACvB,OAAO,cAAc,MAAM,QAAQ,KAAK;AAAA,QACxC,OAAO,IACL,KAAK,QACL,aACA,OACA,KAAK,IAAI,GACT,WAAW,MAAM,QAAQ,OAAO,QAAQ,CAC1C;AAAA,QACA,IAAI,MAAM;AAAA,UAAQ,SAAS,KAAK,OAAO,OAAO,MAAM;AAAA,MACtD;AAAA;AAAA,IAEF,MAAM,QAAQ,OAAO,UAAS,IAAI;AAAA,IAClC,IAAI,UAAU,WAAW;AAAA,MAEvB,OAAO,OAAc,aACnB,IAAI,MAAM,GAAG,KAAK,IAAI,gEAAgE,CACxF;AAAA,IACF;AAAA,IACA,OAAO,KAAK,OAAO,OAAO;AAAA,GAC3B;AAAA,EACH,QAAQ,GAAG,WAA4B,IAAI,KAAK,QAAQ,aAAa,UAAU,KAAK,IAAI,CAAC;AAC3F;;;ADhDO,IAAM,wBAAwB,WAAgC,wBAAwB;AAMtF,IAAM,gCAAgC,MAClC,kBACP,uBACO,iBAAQ,sBAAsB,SAAS,GAAG,UAAQ,CAAC,CAC5D;;AM5DF,qBAAS;AACT;AACA;;;ACzCO,IAAM,YAAY;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKA,IAAM,WAAW,CAAC,UAAiD;AAAA,EACjE,MAAM,OAA+B,CAAC;AAAA,EACtC,WAAW,OAAO,WAAW;AAAA,IAC3B,MAAM,QAAQ,MAAM;AAAA,IACpB,IAAI,UAAU;AAAA,MAAW,KAAK,OAAO,OAAO,KAAK;AAAA,EACnD;AAAA,EACA,OAAO;AAAA;AAYF,IAAM,iBAAiB,CAC5B,UACuC;AAAA,EACvC,cAAc,IAAI,KAAK,eAAe,CAAC;AAAA,EACvC,eAAe,IAAI,KAAK,gBAAgB,CAAC;AAAA,EACzC,aAAa,IAAI,KAAK,cAAc,CAAC;AAAA,EACrC,gBAAgB,IAAI,KAAK,iBAAiB,CAAC;AAAA,EAC3C,eAAe,IAAI,KAAK,gBAAgB,CAAC;AAAA,EACzC,eAAe,IAAI,KAAK,gBAAgB,CAAC;AAC3C;AASO,IAAM,aAAa,CAAC,aAA+C,UACxE,UAAU,MAAM,CAAC,QAAQ,MAAM,SAAS,aAAa,MAAM,SAAS,YAAW,IAAI;AA2BrF,IAAM,iBAAiB,CAAC,UAAyB,UAAU,KAAK,CAAC,QAAQ,MAAM,SAAS,SAAS;AAEjG,IAAM,eAAe,CAAC,UAAyB;AAAA,EAC7C,IAAI,eAAe,KAAK;AAAA,IAAG;AAAA,EAC3B,MAAM,IAAI,MACR,gBAAgB,MAAM,uEACpB,cAAc,MAAM,wEACpB,gGACA,uFACJ;AAAA;AAIF,IAAM,SAAQ,CAAC,MAAc,UAC3B,UAAU,YAAY,CAAC,IAAI,GAAG,OAAO,MAAM;AAG7C,IAAM,UAAU,CAAC,UAA+B,UAAU,YAAY,YAAY,OAAO,KAAK;AAoB9F,IAAM,SAAQ,CAAC,WAAkD;AAAA,KAC5D,SAAS,KAAK;AAAA,KACd,OAAM,WAAW,MAAM,OAAO;AAAA,KAC9B,OAAM,WAAW,KAAK,MAAM,OAAO,CAAC;AAAA,KACpC,OAAM,aAAa,QAAQ,MAAM,YAAY,CAAC;AAAA,KAC9C,OAAM,MAAM,MAAM,EAAE;AAAA,EACvB,UAAU,MAAM;AAAA,EAChB,OAAO,MAAM;AACf;AAWO,IAAM,cAAa,CAAC,UAAiD;AAAA,EAC1E,aAAa,KAAK;AAAA,EAClB,OAAO,KAAK,OAAM,KAAK,GAAG,IAAI,MAAM,GAAG;AAAA;AA+BlC,IAAM,cAAa,CAAC,UAAiD;AAAA,EAC1E,aAAa,KAAK;AAAA,EAClB,OAAO,OAAM,KAAK;AAAA;;;AD9Cb,IAAM,cAAc,WAAsB,cAAc;AAG/D,IAAM,QAAO,CAAI,WAAyB,SAAY,cAAa,aAAa,cAAa;AAE7F,IAAM,aAAW,YAA+C;AAAA,EAU9D,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,SAAS,KAAK,KAAK,YAAY,EAAE;AAAA,IACjC,SAAS,KAAK,KAAK,YAAY,KAAK;AAAA,IACpC,IAAI,MAAM;AAAA,OACP,eAAe,IAAI;AAAA,IACtB,aAAa,IAAI,KAAK,cAAc,EAAE;AAAA,IACtC,IAAI,KAAK,KAAK,OAAO,EAAE;AAAA,IACvB,UAAU,KAAK,KAAK,aAAa,EAAE;AAAA,IACnC,OAAO,KAAK,KAAK,UAAU,MAAM,KAAK;AAAA,EACxC;AAAA,EACA,YAAY,MAAM;AAAA,EAClB,YAAY;AAAA,EAWZ,UAAU,EAAE,QAAQ,0BAA0B,QAAQ,6BAA6B;AAAA,EACnF,SAAS,CAAC,aAAY,UACpB,YAAW,aAAa,MAAM,YAC9B,YAAW,UAAU,MAAM,SAC3B,MAAK,MAAM,IAAI,YAAW,EAAE,KAC5B,MAAK,MAAM,cAAc,YAAW,YAAY,KAChD,MAAK,MAAM,SAAS,YAAW,OAAO,KACtC,MAAK,MAAM,SAAS,YAAW,OAAO,KACtC,WAAW,aAAY,KAAK;AAAA,EAC9B,MAAM,CAAC,UAAU,gBAAgB,MAAM;AAAA,EACvC,YAAY;AACd,CAAC;AAmBM,IAAM,sBAAsB,MACxB,kBAAO,aAAoB,iBAAQ,YAAY,SAAS,GAAG,UAAQ,CAAC,CAAC;;AE9KhF,qBAAS;AACT;AACA;;;ACvBO,IAAM,SAAQ;AAGrB,IAAM,SAAgC;AAAA,EACpC,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,KAAK;AACP;AAkBO,IAAM,QAAQ,CAAC,UAA2B;AAAA,EAC/C,IAAI,OAAO,UAAU;AAAA,IAAU,OAAO;AAAA,EACtC,MAAM,MAAM,KAAK,KAAK,EAAE,KAAK;AAAA,EAC7B,MAAM,QAAQ,wCAAwC,KAAK,GAAG;AAAA,EAC9D,IAAI,UAAU;AAAA,IAAM,OAAO;AAAA,EAC3B,MAAM,OAAO,OAAM,MAAM,MAAM;AAAA,EAC/B,OAAO,SAAS,YAAY,SAAQ,KAAK,MAAM,OAAO,MAAM,MAAM,GAAG,IAAI,IAAI;AAAA;AAYxE,IAAM,UAAU,CAAC,UACtB,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC,UAAmB,OAAO,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,KAAK,KAAK;AAsB9F,IAAM,SAAQ,CAAC,WAAyB;AAAA,EACtC,gBAAgB,MAAM;AAAA,EACtB,mBAAmB,MAAM,uBAAuB,OAAO,MAAM;AAAA,EAC7D,OAAO,MAAM;AAAA,KACT,MAAM,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;AAAA,KAC5D,MAAM,iBAAiB,YAAY,CAAC,IAAI,EAAE,aAAa,OAAO,MAAM,YAAY,EAAE;AAAA,KAClF,MAAM,OAAO,YAAY,CAAC,IAAI,EAAE,IAAI,MAAM,GAAG;AAAA,KAC7C,MAAM,UAAU,YAAY,CAAC,IAAI,EAAE,OAAO,MAAM,MAAM;AAAA,KACtD,MAAM,eAAe,YAAY,CAAC,IAAI,EAAE,WAAW,OAAO,MAAM,UAAU,EAAE;AAAA,KAC5E,MAAM,WAAW,YAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;AAAA,KACzD,MAAM,iBAAiB,YAAY,CAAC,IAAI,EAAE,aAAa,MAAM,aAAa;AAAA,KAC1E,MAAM,aAAa,YAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;AAAA,KAC/D,MAAM,qBAAqB,YAC3B,CAAC,IACD,EAAE,iBAAiB,MAAM,mBAAmB,MAAM,IAAI;AAAA,KACtD,MAAM,qBAAqB,YAC3B,CAAC,IACD,EAAE,iBAAiB,OAAO,MAAM,gBAAgB,EAAE;AACxD;AAYO,IAAM,cAAa,CAAC,WAAiD;AAAA,KACvE,OAAM,KAAK;AAAA,EACd,IAAI,MAAM;AAAA,KACN,MAAM,sBAAsB,YAAY,CAAC,IAAI,EAAE,kBAAkB,MAAM,kBAAkB;AAC/F;AAUO,IAAM,cAAa,CAAC,UAAgD,OAAM,KAAK;AAE/E,IAAM,aAAW,YAA6C;AAAA,EAiBnE,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,SAAS,KAAK,KAAK,YAAY,EAAE;AAAA,IACjC,gBAAgB,QAAQ,KAAK,eAAe;AAAA,IAC5C,IAAI,MAAM;AAAA,IACV,aAAa,IAAI,KAAK,cAAc,MAAK;AAAA,IACzC,IAAI,KAAK,KAAK,OAAO,EAAE;AAAA,IACvB,OAAO,KAAK,KAAK,UAAU,EAAE;AAAA,IAC7B,WAAW,MAAM,KAAK,UAAU;AAAA,IAChC,QAAQ,KAAK,KAAK,WAAW,EAAE;AAAA,IAC/B,aAAa,KAAK,KAAK,cAAc,EAAE;AAAA,IACvC,gBAAgB,KAAK,KAAK,iBAAiB,EAAE;AAAA,IAG7C,mBAAmB,KAAK,KAAK,oBAAoB,KAAK;AAAA,IACtD,UAAU,KAAK,KAAK,aAAa,EAAE;AAAA,IAGnC,iBAAiB,KAAK,KAAK,kBAAkB,KAAK;AAAA,IAClD,OAAO,KAAK,KAAK,UAAU,EAAE;AAAA,IAC7B,kBAAkB,KAAK,KAAK,mBAAmB,MAAM;AAAA,IACrD,iBAAiB,IAAI,KAAK,kBAAkB,MAAK;AAAA,EACnD;AAAA,EACA,YAAY,MAAM;AAAA,EAClB,YAAY;AAAA,EAoBZ,UAAU,EAAE,QAAQ,yBAAyB,QAAQ,4BAA4B;AAAA,EACjF,SAAS,CAAC,aAAY,UACpB,YAAW,wBAAwB,MAAM,uBAAuB,SAChE,YAAW,UAAU,MAAM,SAC3B,YAAW,oBAAoB,MAAM,oBACpC,MAAM,WAAW,aAAa,YAAW,WAAW,MAAM,YAC1D,MAAM,OAAO,aAAa,YAAW,OAAO,MAAM,QAClD,MAAM,iBAAiB,aAAa,YAAW,iBAAiB,MAAM,kBACtE,MAAM,aAAa,aAAa,YAAW,aAAa,MAAM,cAC9D,MAAM,YAAY,aAAa,YAAW,YAAY,MAAM,aAC5D,MAAM,UAAU,aAAa,YAAW,UAAU,MAAM,WAIxD,MAAM,qBAAqB,aAC1B,YAAW,qBAAqB,MAAM,sBACvC,MAAM,eAAe,aAAa,YAAW,eAAe,MAAM,gBAClE,MAAM,iBAAiB,aAAa,YAAW,iBAAiB,MAAM,kBACtE,MAAM,qBAAqB,aAC1B,YAAW,qBAAqB,MAAM;AAAA,EAE1C,MAAM,CAAC,UAAU,eAAe,MAAM;AAAA,EACtC,YAAY;AACd,CAAC;;;ADUM,IAAM,aAAa,WAAqB,aAAa;AAWrD,IAAM,qBAAqB,MACvB,kBAAO,YAAmB,iBAAQ,WAAW,SAAS,GAAG,UAAQ,CAAC,CAAC;;AEhO9E,qBAAS;AACT;AACA;;;AC6CO,IAAM,WAAW,CAAC,gBAAyB,kBAAkC;AAAA,EAClF,IAAI,CAAC;AAAA,IAAgB,OAAO;AAAA,EAG5B,IAAI,gBAAgB;AAAA,IAAG,OAAO;AAAA,EAC9B,OAAO,oCAAoC,OAAO,aAAa;AAAA;AAmC1D,IAAM,SAAQ,CAAC,WAAsD;AAAA,EAC1E,mBAAmB,MAAM,uBAAuB,QAAQ,MAAM;AAAA,EAC9D,OAAO,MAAM;AAAA,KACT,MAAM,aAAa,OAAO,CAAC,IAAI,EAAE,UAAU,MAAM,SAAS;AAAA,KAC1D,MAAM,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;AAAA,KAC5D,MAAM,iBAAiB,YAAY,CAAC,IAAI,EAAE,aAAa,OAAO,MAAM,YAAY,EAAE;AAAA,KAClF,MAAM,OAAO,aAAa,MAAM,OAAO,KAAK,CAAC,IAAI,EAAE,IAAI,MAAM,GAAG;AAAA,KAChE,MAAM,sBAAsB,YAC5B,CAAC,IACD,EAAE,kBAAkB,OAAO,MAAM,iBAAiB,EAAE;AAC1D;;;ADUO,IAAM,eAAe,WAAuB,eAAe;AAalE,IAAM,aAAW,YAAuD;AAAA,EACtE,YAAY,CAAC,MAAM,UAAU;AAAA,IAC3B,MAAM,iBAAiB,KAAK,KAAK,oBAAoB,IAAI;AAAA,IACzD,MAAM,gBAAgB,IAAI,KAAK,mBAAmB,EAAE;AAAA,IACpD,OAAO;AAAA,MACL,SAAS,KAAK,KAAK,YAAY,EAAE;AAAA,MACjC,IAAI,MAAM;AAAA,MACV,mBAAmB;AAAA,MACnB,aAAa,IAAI,KAAK,cAAc,EAAE;AAAA,MACtC,IAAI,KAAK,KAAK,OAAO,EAAE;AAAA,MACvB,kBAAkB;AAAA,MAClB,UAAU,SAAS,gBAAgB,aAAa;AAAA,MAChD,UAAU,KAAK,KAAK,aAAa,EAAE;AAAA,MACnC,OAAO,KAAK,KAAK,UAAU,MAAM,KAAK;AAAA,IACxC;AAAA;AAAA,EAcF,YAAY,MAAM;AAAA,EAElB,YAAY,CAAC,WAAW,KAAK,OAAM,KAAK,GAAG,IAAI,MAAM,GAAG;AAAA,EAaxD,UAAU,EAAE,QAAQ,2BAA2B,QAAQ,8BAA8B;AAAA,EACrF,SAAS,CAAC,aAAY,UACpB,YAAW,UAAU,MAAM,SAI3B,YAAW,cAAc,MAAM,YAAY,OAC3C,YAAW,wBAAwB,MAAM,uBAAuB,WAC/D,MAAM,sBAAsB,aAC3B,YAAW,sBAAsB,MAAM,uBACxC,MAAM,iBAAiB,aAAa,YAAW,iBAAiB,MAAM,kBACtE,MAAM,OAAO,aAAa,YAAW,OAAO,MAAM,QAClD,MAAM,YAAY,aAAa,YAAW,YAAY,MAAM;AAAA,EAC/D,MAAM,CAAC,UAAU,iBAAiB,MAAM;AAAA,EACxC,YAAY;AACd,CAAC;AAgBM,IAAM,uBAAuB,MACzB,kBAAO,cAAqB,iBAAQ,aAAa,SAAS,GAAG,UAAQ,CAAC,CAAC;;AEnNlF,qBAAS;AACT;AACA;AA8BO,IAAM,cAAc,WAAsB,cAAc;AAGxD,IAAM,iBAAiB,CAAC,WAAuC;AAAA,EACpE,SAAS,MAAM,WAAW;AAAA,EAC1B,QAAQ,MAAM;AAChB;AAGO,IAAM,iBAAiB,CAAC,WAA6D;AAAA,EAC1F,SAAS,MAAM,WAAW;AAC5B;AAEA,IAAM,aAAW,YAAuC;AAAA,EACtD,YAAY,CAAC,MAAM,UAAU;AAAA,IAC3B,MAAM,MAAM;AAAA,IACZ,OAAO;AAAA,MACL,SAAS,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU;AAAA,MACzD,SAAS,MAAM,QAAQ,IAAI,OAAO,IAAI,IAAI,QAAQ,SAAS;AAAA,MAC3D,QAAQ,MAAM;AAAA,IAChB;AAAA;AAAA,EAEF,YAAY,MAAM;AAAA,EAClB,YAAY;AAAA,EAEZ,UAAU,EAAE,QAAQ,mBAAmB,QAAQ,0BAA0B;AAAA,EACzE,SAAS,CAAC,aAAY,UAAU,YAAW,aAAa,MAAM,WAAW;AAAA,EACzE,MAAM,CAAC,UAAU,SAAS,MAAM;AAAA,EAChC,YAAY;AACd,CAAC;AAEM,IAAM,sBAAsB,MACxB,kBAAO,aAAoB,iBAAQ,YAAY,SAAS,GAAG,UAAQ,CAAC,CAAC;;ACxDzE,IAAM,uBAA0C,OAAO,OAAO;AAAA,EACnE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAsDM,IAAM,qBAA6C,OAAO,OAAO;AAAA,EACtE,SACE;AAAA,EACF,WAAW;AAAA,EACX,eAAe;AAAA,EACf,UAAU;AAAA,EACV,UAAU;AAAA,EACV,MAAM;AACR,CAAC;AAyBD,IAAM,KAAK;AACX,IAAM,SAAS;AACf,IAAM,UAAU;AAEhB,IAAM,YAAW,CACf,QACA,UACA,UACa;AAAA,EACb,MAAM,QAAkB,CAAC;AAAA,EACzB,IAAI,CAAC,GAAG,KAAK,OAAM,IAAI;AAAA,IAAG,MAAM,KAAK,UAAU,OAAM,6BAA6B;AAAA,EAClF,IAAI,CAAC,GAAG,KAAK,OAAM,SAAS;AAAA,IAAG,MAAM,KAAK,eAAe,OAAM,+BAA+B;AAAA,EAC9F,IAAI,CAAC,GAAG,KAAK,OAAM,QAAQ;AAAA,IAAG,MAAM,KAAK,cAAc,OAAM,iCAAiC;AAAA,EAC9F,YAAY,QAAO,WAAW;AAAA,IAC5B,CAAC,iBAAiB,OAAM,aAAa;AAAA,IACrC,CAAC,YAAY,OAAM,QAAQ;AAAA,EAC7B,GAAY;AAAA,IACV,IAAI,WAAW,QAAQ,CAAC,OAAO,KAAK,MAAM,GAAG;AAAA,MAC3C,MAAM,KAAK,GAAG,YAAW,wDAAwD;AAAA,IACnF;AAAA,EACF;AAAA,EACA,IAAI,OAAM,aAAa,OAAM;AAAA,IAAe,MAAM,KAAK,yCAAyC;AAAA,EAMhG,MAAM,SAAS,CAAC,UAA8B,WAC5C,aAAa,YAAY,YAAY;AAAA,EACvC,MAAM,UAAyC;AAAA,IAC7C,CAAC,OAAO,MAAM,cAAc,cAAc,GAAG,SAAS,KAAK;AAAA,IAC3D,CAAC,OAAO,MAAM,kBAAkB,kBAAkB,GAAG,SAAS,SAAS;AAAA,EACzE;AAAA,EACA,IAAI,OAAM,aAAa,MAAM;AAAA,IAC3B,QAAQ,KAAK,CAAC,OAAO,MAAM,aAAa,aAAa,GAAG,SAAS,IAAI,CAAC;AAAA,EACxE;AAAA,EACA,MAAM,OAAO,IAAI;AAAA,EACjB,YAAY,QAAO,aAAY,SAAS;AAAA,IACtC,IAAI,QAAQ,KAAK,QAAO,KAAK,KAAK,IAAI,MAAK;AAAA,MAAG;AAAA,IAC9C,KAAK,IAAI,MAAK;AAAA,IACd,MAAM,KAAK,GAAG,uDAAsD;AAAA,EACtE;AAAA,EACA,OAAO;AAAA;AAIF,IAAM,oBAAoB,CAAC,SAAwB,CAAC,MAAyB;AAAA,EAGlF,QAAQ,cAAc,kBAAkB,gBAAgB,SAAS;AAAA,EAEjE,MAAM,QAAQ,OAAO,QAAQ,IAAI,EAAE,OAAO,IAAI,WAAW,UAAU,SAAS;AAAA,EAC5E,MAAM,WAAmC;AAAA,OACpC;AAAA,OACA,OAAO,YAAY,KAAK;AAAA,EAC7B;AAAA,EAGA,MAAM,WAA8B;AAAA,IAClC,OAAO,gBAAgB,SAAS;AAAA,IAChC,WAAW,oBAAoB,SAAS;AAAA,IACxC,MAAM,eAAe,SAAS;AAAA,EAChC;AAAA,EACA,MAAM,QAAQ,UAAS,UAAU,UAAU,MAAK;AAAA,EAChD,IAAI,MAAM,SAAS;AAAA,IAAG,MAAM,IAAI,MAAM,uBAAuB,MAAM,KAAK,IAAI,GAAG;AAAA,EAC/E,MAAM,QAA2C;AAAA,IAC/C,CAAC,aAAa,SAAS,eAAe,SAAS,IAAI;AAAA,EACrD;AAAA,EACA,IAAI,SAAS,aAAa;AAAA,IAAM,MAAM,KAAK,CAAC,QAAQ,SAAS,UAAU,SAAS,QAAQ,CAAC;AAAA,EACzF,OAAO;AAAA,IACL,QAAQ,MAAM,IAAI,EAAE,MAAM,QAAQ,aAAa,EAAE,MAAM,QAAQ,OAAO,EAAE;AAAA,IACxE,OAAO,EAAE,SAAS,SAAS,OAAO,SAAS,SAAS,UAAU;AAAA,IAC9D,MAAM,EAAE,OAAO,sBAAsB,QAAQ,SAAS,KAAK;AAAA,IAC3D,OAAO,MAAM,IAAI,EAAE,MAAM,aAAa;AAAA,MACpC,SAAS,SAAS;AAAA,MAClB,QAAQ,CAAC,SAAS,SAAS;AAAA,MAC3B;AAAA,MACA;AAAA,IACF,EAAE;AAAA,EACJ;AAAA;;AC3LF,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBf,IAAM,WAAW,CAAC,UAAiB,KAAK;AAGxC,IAAM,aACJ,qEACA,iGACA,sEACA,mFACA;AAEF,IAAM,WAAW,GAAG,WAAsC;AAAA,EACxD,MAAM,QAAQ,CAAC,GAAG,KAAK,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG;AAAA,EAC7C,OAAO;AAAA,SACA,KAAK;AAAA,QACN;AAAA,8BACsB,KAAK;AAAA,8CACW,KAAK;AAAA,oBAC/B,KAAK,+DAA+D,KAAK;AAAA;AAAA,eAE9E,KAAK,mCAAmC,KAAK;AAAA;AAAA;AAAA;AAK5D,IAAM,YAAY,GAAG,YAAuC;AAAA,UAClD,MAAM;AAAA,+BACe,MAAM;AAAA,oBACjB,MAAM,gCAAgC,MAAM;AAAA,qBAC3C,MAAM,qDAAqD,MAAM,sBAAsB,MAAM;AAAA;AAAA,gBAElG,MAAM,qCAAqC,MAAM,sBAAsB,MAAM;AAAA;AAAA;AAI7F,IAAM,WAAW,CAAC,SAAqD;AAAA,EACrE,MAAM,SAAS,CAAC,GAAG,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG;AAAA,EAC/C,MAAM,OAAO,UAAU,gEAAgE,KAAK;AAAA,EAC5F,MAAM,SAAS,YAAY,qBAAqB,KAAK;AAAA,EACrD,OAAO;AAAA,SACA,KAAK,eAAe,KAAK,kBAAkB;AAAA,8BACtB,KAAK,WAAW;AAAA,oBAC1B,4BAA4B,KAAK;AAAA,oBACjC,KAAK;AAAA,wBACD,KAAK,UAAU;AAAA;AAAA,eAExB,KAAK,mCAAmC,KAAK,UAAU;AAAA;AAAA;AAAA;AAKtE,IAAM,YAAY,CAAC,UAAuD;AAAA,EACxE,MAAM,QACJ,sEACA,iBAAiB,SAAS,MAAM,MAAM,wBAAwB,SAAS,MAAM,MAAM,UACnF;AAAA,EACF,OAAO;AAAA,WACE,MAAM,gBAAgB,MAAM,gCAAgC,MAAM;AAAA,2BAClD,oCAAoC,MAAM,UAAU,MAAM;AAAA,mBAClE,MAAM,UAAU,MAAM,iDAAiD,MAAM,kBAAkB,MAAM;AAAA;AAAA;AAKxH,IAAM,cAAc,CAAC,WAA2B;AAAA,oBAC5B,0DAA0D;AAAA;AAOvE,IAAM,qBAAqB,CAAC,SAAwB,CAAC,MAAc;AAAA,EACxE,MAAM,WAAW,kBAAkB,MAAK;AAAA,EACxC,MAAM,WAAW,SAAS,OACvB,IAAI,CAAC,UAAU,MAAM,MAAM,EAC3B,OAAO,CAAC,WAAW,WAAW,SAAS,KAAK,MAAM;AAAA,EACrD,OAAO;AAAA,IACL;AAAA,IACA,GAAG,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC,EAAE,IAAI,WAAW;AAAA,IACzC,SAAS,QAAQ;AAAA,IACjB,UAAU,QAAQ;AAAA,IAClB,GAAG,SAAS,MAAM,IAAI,QAAQ;AAAA,IAC9B,GAAG,SAAS,OAAO,IAAI,SAAS;AAAA,IAChC;AAAA;AAAA;AAAA,EACF,EAAE,KAAK,EAAE;AAAA;;ACxGX;AACA;AACA;;;ACiBA,qBAAS;AACT;AACA;AAuCO,IAAM,cAAc,WAAsB,cAAc;AAU/D,IAAM,aAAY,CAAC,UACjB,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK;AAgBxF,IAAM,YAAY,CAAC,SAA4C;AAAA,EAC7D,MAAM,SAAS,KAAK;AAAA,EACpB,IAAI,OAAO,WAAW;AAAA,IAAU,OAAO,WAAU,OAAO,MAAM,GAAG,CAAC;AAAA,EAClE,OAAO,WAAU,OAAO,KAAK,IAAI,EAAE,OAAO,CAAC,QAAQ,IAAI,SAAS,GAAG,CAAC,CAAC;AAAA;AAGvE,IAAM,aAAW,YAAuC;AAAA,EACtD,YAAY,CAAC,MAAM,WAAW,EAAE,OAAO,UAAU,IAAI,GAAG,QAAQ,MAAM,OAAO;AAAA,EAC7E,YAAY,MAAM;AAAA,EAElB,YAAY,CAAC,WAAW,EAAE,OAAO,WAAU,MAAM,KAAK,EAAE,KAAK,GAAG,GAAG,QAAQ,MAAM,OAAO;AAAA,EAQxF,UAAU,EAAE,QAAQ,0BAA0B,QAAQ,iCAAiC;AAAA,EACvF,SAAS,CAAC,aAAY,UACpB,WAAU,YAAW,KAAK,EAAE,KAAK,GAAG,MAAM,WAAU,MAAM,KAAK,EAAE,KAAK,GAAG;AAAA,EAC3E,MAAM,CAAC,UAAU,gBAAgB,MAAM;AAAA,EAMvC,YAAY,CAAC,WAAW,EAAE,OAAO,WAAU,MAAM,KAAK,EAAE,KAAK,GAAG,EAAE;AACpE,CAAC;AAiBM,IAAM,sBAAsB,MACxB,kBAAO,aAAoB,iBAAQ,YAAY,SAAS,GAAG,UAAQ,CAAC,CAAC;;;AClHhF,qBAAS;AACT;AACA;AAqDO,IAAM,cAAc,WAAsB,cAAc;AAE/D,IAAM,OAAM,CAAC,UAAoB,OAAO,UAAU,WAAW,QAAQ;AAQrE,IAAM,OAAM,CAAC,UAAmB;AAAA,EAC9B,MAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,SAAS,CAAC;AAAA,EACpE,OAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAAA;AAQ5C,IAAM,WAAW,CAAC,UAA6B;AAAA,EAC7C,MAAM,MAAiB,MAAM,QAAQ,KAAK,IACtC,QACA,OAAO,UAAU,WACf,MAAM,MAAM,GAAG,IACf,CAAC;AAAA,EACP,MAAM,SAAQ,IAAI,IAAI,CAAC,UAAU,KAAI,KAAK,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,UAAU,EAAE;AAAA,EAClF,OAAO,CAAC,GAAG,IAAI,IAAI,MAAK,CAAC,EAAE,KAAK;AAAA;AASlC,IAAM,aAAa,CAAC,UAClB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAC/D,OAAO,KAAK,KAAK,EAAE,KAAK,IACxB,CAAC;AAGP,IAAM,SAAQ,CAAC,WAAsB;AAAA,EACnC,SAAS,MAAM,WAAW;AAAA,EAC1B,OAAO,MAAM,SAAS;AAAA,EACtB,QAAQ,MAAM,WAAW,QAAQ,MAAM;AAAA,EACvC,QAAQ,OAAO,MAAM,UAAU,CAAC;AAAA,EAChC,WAAW,MAAM,aAAa;AAAA,EAS9B,QAAQ,SAAS,MAAM,MAAM,EAAE,KAAK,GAAG;AAAA,EACvC,UAAU,MAAM,YAAY;AAC9B;AAEA,IAAM,aAAW,YAAuC;AAAA,EACtD,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,SAAS,KAAI,KAAK,UAAU;AAAA,IAC5B,OAAO,KAAI,KAAK,QAAQ;AAAA,IAMxB,QAAQ,KAAK,KAAK,WAAW,IAAI;AAAA,IACjC,QAAQ,KAAI,KAAK,SAAS;AAAA,IAC1B,WAAW,KAAI,KAAK,YAAY;AAAA,IAChC,QAAQ,SAAS,KAAK,SAAS;AAAA,IAC/B,UAAU,KAAI,KAAK,WAAW;AAAA,IAC9B,QAAQ,WAAW,KAAK,SAAS;AAAA,IAEjC,QAAQ,MAAM;AAAA,EAChB;AAAA,EACA,YAAY,MAAM;AAAA,EAClB,YAAY,CAAC,WAAW,KAAK,OAAM,KAAK,GAAG,QAAQ,MAAM,OAAO;AAAA,EAOhE,UAAU,EAAE,QAAQ,0BAA0B,QAAQ,iCAAiC;AAAA,EACvF,SAAS,CAAC,aAAY,UACpB,YAAW,aAAa,MAAM,WAAW,OACzC,YAAW,WAAW,MAAM,SAAS,OACrC,YAAW,YAAY,MAAM,WAAW,UACxC,YAAW,YAAY,MAAM,UAAU,MACvC,YAAW,eAAe,MAAM,aAAa,OAC7C,YAAW,cAAc,MAAM,YAAY,OAC3C,YAAW,OAAO,KAAK,GAAG,MAAM,SAAS,MAAM,MAAM,EAAE,KAAK,GAAG;AAAA,EACjE,MAAM,CAAC,UAAU,gBAAgB,MAAM;AAAA,EACvC,YAAY;AACd,CAAC;AAeM,IAAM,sBAAsB,MACxB,kBAAO,aAAoB,iBAAQ,YAAY,SAAS,GAAG,UAAQ,CAAC,CAAC;;;AFtKzE,IAAM,2BAA2B,CACtC,IACA,QACA,SAAwB,CAAC,GACzB,UAAmC,CAAC,MAE7B,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WAAW,OAAc,aAAI;AAAA,IACjC,OAAO,CAAC,UAAW,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,IAC3E,KAAK,MAAM,kBAAkB,MAAK;AAAA,EACpC,CAAC;AAAA,EACD,MAAM,QAAQ,CAAO,aAAyC;AAAA,IAC5D,MAAM,OAAO,SAAS,KAAmB,qBAAO,CAAC;AAAA,IACjD,OAAO,QAAQ,UAAU,YAAY,OAAO,KAAK,KAAK,MAAM,QAAQ,KAAK,CAAC;AAAA;AAAA,EAE5E,MAAM,OAAO,OAAO,MAClB,YAAY,GAAG,WAAW;AAAA,IACxB,OAAO,CAAC,GAAG,SAAS,KAAK,KAAK;AAAA,IAC9B,QAAQ,SAAS,KAAK;AAAA,IACtB;AAAA,EACF,CAAC,CACH;AAAA,EACA,MAAM,QAAQ,OAAO,MAAM,aAAa,GAAG,iBAAiB,KAAK,SAAS,OAAO,OAAO,CAAC,CAAC;AAAA,EAG1F,MAAM,QAAQ,OAAc,aAC1B,SAAS,MAAM,IAAI,CAAC,SAClB,MACE,YAAY,GAAG,MAAM,KAAK,aAAa;AAAA,IACrC,SAAS,KAAK;AAAA,IACd,QAAQ,CAAC,GAAG,KAAK,MAAM;AAAA,IACvB;AAAA,IACA,QAAQ,KAAK;AAAA,EACf,CAAC,CACH,CACF,CACF;AAAA,EACA,MAAM,SAAS,OAAc,aAC3B,SAAS,OAAO,IAAI,CAAC,UACnB,MACE,WAAW,GAAG,MAAM,MAAM,cAAc;AAAA,IACtC,MAAM;AAAA,IACN,WAAW;AAAA,IACX,QAAQ,MAAM;AAAA,IACd;AAAA,IACA,MAAM;AAAA,IACN,MAAM,MAAM;AAAA,EACd,CAAC,CACH,CACF,CACF;AAAA,EACA,OAAO,EAAE,UAAU,QAAQ,OAAO,MAAM,MAAM;AAAA,CAC/C;;AGtFH,qBAAS;AACT;AACA;AAoCO,IAAM,YAAY,WAAoB,YAAY;AAEzD,IAAM,SAAQ,CAAC,WAAoB;AAAA,EACjC,OAAO,OAAO,MAAM,SAAS,CAAC;AAAA,EAC9B,QAAQ,OAAO,MAAM,UAAU,GAAG;AAAA,EAClC,MAAM,MAAM,QAAQ,KAAK,OAAO,MAAM,IAAI;AAAA,EAC1C,QAAQ,MAAM,WAAW,OAAO,MAAM;AAAA,EACtC,SAAS,OAAO,MAAM,WAAW,CAAC;AAAA,KAC9B,MAAM,SAAS,YAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;AACzD;AAEA,IAAM,aAAW,YAAmC;AAAA,EAClD,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,OAAO,IAAI,KAAK,UAAU,CAAC;AAAA,IAC3B,QAAQ,IAAI,KAAK,WAAW,GAAG;AAAA,IAC/B,MAAM,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;AAAA,IACxD,MAAM,MAAM;AAAA,IACZ,QAAQ,KAAK,cAAc,KAAK,KAAK,cAAc;AAAA,IACnD,SAAS,IAAI,KAAK,YAAY,CAAC;AAAA,IAC/B,MAAM,MAAM;AAAA,EACd;AAAA,EACA,YAAY,CAAC,UAAU,SAAS,MAAM;AAAA,EACtC,YAAY,CAAC,WAAW,KAAK,OAAM,KAAK,GAAG,MAAM,OAAO,MAAM,IAAI,EAAE;AAAA,EACpE,SAAS,CAAC,aAAY,UACpB,YAAW,UAAU,MAAM,QAAQ,KAAK,OAAO,MAAM,IAAI,QACzD,YAAW,YAAY,MAAM,UAAU,QACvC,YAAW,WAAW,MAAM,SAAS,MACrC,YAAW,aAAa,MAAM,WAAW,MACzC,YAAW,YAAY,MAAM,WAAW;AAAA,EAC1C,MAAM,CAAC,UAAU,SAAS,MAAM,aAAa,OAAO,MAAM,IAAI;AAAA,EAC9D,YAAY;AACd,CAAC;AAEM,IAAM,oBAAoB,MACtB,kBAAO,WAAkB,iBAAQ,UAAU,SAAS,GAAG,UAAQ,CAAC,CAAC;;ACH5E,qBAAS;AACT;AACA;;;AC/DO,IAAM,mBAAmB;AASzB,IAAM,QAAQ,CAAC,UACpB,GAAG,OAAO,MAAM,KAAK,KAAK,OAAO,MAAM,MAAM;AAe/C,IAAM,UAAQ,CAAC,WAAgC;AAAA,EAC7C,SAAS,MAAM,WAAW;AAAA,EAC1B,SAAS,MAAM,YAAY,OAAO,MAAM;AAAA,EACxC,UAAU,MAAM,YAAY;AAAA,KACxB,MAAM,SAAS,YAAY,CAAC,IAAI,EAAE,MAAM,OAAO,MAAM,IAAI,EAAE;AACjE;AAUO,IAAM,cAAa,CAAC,WAAgC;AAAA,KACtD,QAAM,KAAK;AAAA,EACd,IAAI,MAAM,KAAK;AAAA,EACf,QAAQ,MAAM;AAAA,EACd,MAAM;AACR;AAgBO,IAAM,cAAa,CAAC,WAAgC;AAAA,KACtD,QAAM,KAAK;AAAA,EACd,QAAQ;AACV;;;AD4EO,IAAM,wBAAwB,WAAgC,wBAAwB;AAY7F,IAAM,SAAS,CAAC,UAA2B;AAAA,EACzC,IAAI,OAAO,UAAU;AAAA,IAAU,OAAO;AAAA,EACtC,MAAM,SAAS,OAAO,WAAW,KAAK,KAAK,CAAC;AAAA,EAC5C,OAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAAA;AAG5C,IAAM,aAAW,YAA2D;AAAA,EAY1E,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,SAAS,KAAK,KAAK,YAAY,EAAE;AAAA,IACjC,SAAS,KAAK,KAAK,YAAY,KAAK;AAAA,IACpC,OAAO,IAAI,KAAK,UAAU,MAAM,KAAK;AAAA,IACrC,IAAI,KAAK,KAAK,OAAO,MAAM,KAAK,CAAC;AAAA,IACjC,QAAQ,IAAI,KAAK,WAAW,MAAM,MAAM;AAAA,IACxC,MAAM,OAAO,KAAK,OAAO;AAAA,IACzB,YAAY,KAAK,KAAK,eAAe,EAAE;AAAA,IACvC,UAAU,KAAK,KAAK,aAAa,gBAAgB;AAAA,IACjD,QAAQ,KAAK,KAAK,WAAW,EAAE;AAAA,IAC/B,YAAY,KAAK,KAAK,WAAW,MAAM,UAAU;AAAA,IACjD,MAAM,KAAK,KAAK,SAAS,OAAO;AAAA,EAClC;AAAA,EACA,YAAY,MAAM;AAAA,EAClB,YAAY;AAAA,EAmBZ,UAAU;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AAAA,EACA,SAAS,CAAC,aAAY,UACpB,YAAW,eAAe,MAC1B,YAAW,cAAc,MAAM,YAAY,qBAC3C,YAAW,aAAa,MAAM,YAAY,SAC1C,YAAW,aAAa,MAAM,WAAW,QACxC,MAAM,SAAS,aAAa,YAAW,SAAS,MAAM;AAAA,EACzD,MAAM,CAAC,UAAU,uBAAuB,MAAM,KAAK;AAAA,EACnD,YAAY;AACd,CAAC;AAUM,IAAM,gCAAgC,MAClC,kBACP,uBACO,iBAAQ,sBAAsB,SAAS,GAAG,UAAQ,CAAC,CAC5D;;AE7NF,qBAAS;AACT,uBAAS;AAET;AACA;;;ACHA;AAkBA,IAAM,SAAS;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA2BO,IAAM,kBAAkB,CAAC,UAC9B,iBAAiB,YAAY,MAAM,WAAW;AAGhD,IAAM,gBAAgB,CAAC,WACd,aACL,OAAO,IAAI,CAAC,gBACV,IAAkB,QAAQ,QAAQ,OAAO,GAAG,uBAAsB,EAAE,KAC3D,aAAI,CAAC,UAAU,QAAQ,CAAC,GAAG,OAAO,CAAC,QAAQ,IAAI,UAAU,SAAS,EAAE,MAAM,GAQ1E,iBAAQ,iBAAiB,MAAa,iBAAQ,CAAC,CAAC,CACzD,CACF,GACA,EAAE,aAAa,YAAY,CAC7B,EAAE,KAAY,aAAI,CAAC,WAAW,OAAO,OAAO,CAAC,OAAO,WAAU,QAAQ,QAAO,CAAC,CAAC,CAAC;AAU3E,IAAM,eAAe,CAC1B,QACA,YAC4B;AAAA,EAC5B,IAAI,WAAW,aAAa,YAAY;AAAA,IAAW,OAAO;AAAA,EAC1D,IAAI,WAAW,aAAa,YAAY;AAAA,IAAW,OAAO;AAAA,EAC1D,OAAO,WAAW,UAAU,IAAI;AAAA;AAiBlC,IAAM,gBAAgB,CAAC,WACd,aACL,CAAC,WAAW,SAAS,EAAE,IAAI,CAAC,SAC1B,IAAa,QAAQ,QAAQ,OAAO,2BAA2B,QAAQ,EAAE,KAChE,aAAI,UAAS,GACb,iBAAQ,iBAAiB,MAAa,iBAAQ,SAAS,CAAC,CACjE,CACF,GACA,EAAE,aAAa,YAAY,CAC7B,EAAE,KACO,iBAAQ,EAAE,QAAQ,aAAa;AAAA,EACpC,MAAM,UAAU,aAAa,QAAQ,OAAO;AAAA,EAC5C,OAAO,YAAY,iBACR,aACL,IAAI,MACF,uFACE,6DACJ,CACF,IACO,iBAAQ,OAAO;AAAA,CAC3B,CACH;AAGK,IAAM,aAAY,CAAC,UAA2B,KAAK,UAAU,SAAS,KAAK,CAAC;AAEnF,IAAM,WAAW,CAAC,UAA4B;AAAA,EAC5C,IAAI,MAAM,QAAQ,KAAK,GAAG;AAAA,IACxB,OAAO,MACJ,IAAI,QAAQ,EACZ,KAAK,CAAC,MAAM,UAAU,KAAK,UAAU,IAAI,EAAE,cAAc,KAAK,UAAU,KAAK,CAAC,CAAC;AAAA,EACpF;AAAA,EACA,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAAA,IAC/C,MAAM,UAAU,OAAO,QAAQ,KAAgC,EAC5D,OAAO,EAAE,SAAS,QAAQ,QAAQ,EAClC,KAAK,EAAE,QAAQ,WAAW,KAAK,cAAc,KAAK,CAAC;AAAA,IACtD,OAAO,OAAO,YAAY,QAAQ,IAAI,EAAE,KAAK,WAAW,CAAC,KAAK,SAAS,KAAK,CAAC,CAAC,CAAC;AAAA,EACjF;AAAA,EACA,OAAO;AAAA;AAUF,IAAM,kBAAkB,CAAC,WACvB,aAAI,CAAC,cAAc,MAAM,GAAG,cAAc,MAAM,CAAC,GAAG,EAAE,aAAa,YAAY,CAAC,EAAE,KAChF,aAAI,EAAE,QAAQ,YAAY,SAAS,MAAM,CAClD;;;ADlHK,IAAM,kBAAkB,WAA0B,kBAAkB;AAE3E,IAAM,QAAO,CAAC,UACZ,gBAAgB,MAAM,MAAM,EAAE,KAAY,aAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC;AAEpE,IAAM,0BAA0B,MAC5B,kBACP,iBACO,iBACL,gBAAgB,SAAS,GAAG;AAAA,EAE1B,MAAM,MAAa,iBAAQ,CAAC,CAAC;AAAA,EAE7B,MAAa,YAAG,UAAU,GAAG,QAAQ;AAAA,IACnC,OAAO,OAAO,MAAK,IAAI;AAAA,GACxB;AAAA,EAQD,MAAa,YAAG,UAAU,GAAG,QAAwC;AAAA,IACnE,IAAI,CAAC,aAAW,IAAI;AAAA,MAAG;AAAA,IACvB,QAAQ,YAAY,OAAO,MAAK,IAAI;AAAA,IACpC,OAAO,YAAY,IAAK,EAAE,QAAQ,OAAO,IAAe,EAAE,QAAQ,SAAS;AAAA,GAC5E;AAAA,EAED,WAAkB,YAAG,UAAU,GAAG,QAAQ;AAAA,IAiBxC,MAAM,SAAS,OAAO,MAAK,IAAI;AAAA,IAC/B,IAAI,OAAO,YAAY;AAAA,MAAG,OAAO;AAAA,IAEjC,OAAO,IAAI,KAAK,QAAQ,aAAa,OAAO,aAAa;AAAA,IAOzD,MAAM,QAAQ,OAAO,MAAK,IAAI;AAAA,IAC9B,IAAI,MAAM,YAAY,GAAG;AAAA,MACvB,OAAO,OAAc,aACnB,IAAI,MACF,0CAA0C,OAAO,MAAM,OAAO,qBAC5D,mFACA,yEACJ,CACF;AAAA,IACF;AAAA,IACA,OAAO;AAAA,GACR;AAAA,EASD,QAAQ,MAAa;AACvB,CAAC,CACH,CACF;;AE3FF,qBAAS;AACT;AACA;;;ACjCO,IAAM,WAAW,CAAC,UACvB,GAAG,MAAM,QAAQ,MAAM,KAAK,QAAQ,KAAK,GAAG;AAG9C,IAAM,cAAc,CAAC,UAAqB,iBAAiB,MAAM,qBAAqB,MAAM;AAoBrF,IAAM,aAAa,CAAC,WACxB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,GAC9B,IAAI,CAAC,UAAmB,eAAe,KAAK,CAAC,EAC7C,OAAO,CAAC,UAAU,UAAU,EAAE,EAC9B,KAAK,EACL,KAAK,GAAG;AAGN,IAAM,iBAAiB,CAAC,UAC7B,UAAU,YAAY,KAAK,WAAW,CAAC,YAAY,KAAK,CAAC,CAAC;AAO5D,IAAM,QAAQ,CAAC,UACb,UAAU,aAAa,UAAU,KAAK,YAAY;AA0B7C,IAAM,YAAW,CAAC,WAA+D;AAAA,EACtF,mBAAmB,MAAM,MAAM,aAAa;AAAA,EAC5C,cAAc,MAAM,cAAc,YAAY,YAAY,YAAY,MAAM,SAAS;AAAA,EACrF,eAAe,MAAM,MAAM,aAAa;AAAA,EACxC,SAAS,MAAM,MAAM,OAAO;AAC9B;AAUO,IAAM,QAAO,CAAC,WAAmD;AAAA,EACtE,MAAM,MAAM,SAAS,OAAO,MAAM;AAAA,EAClC,MAAM,MAAM;AAAA,KACT,OAAO,YACR,OAAO,QAAQ,UAAS,KAAK,CAAC,EAAE,OAC9B,CAAC,UAAqC,MAAM,OAAO,SACrD,CACF;AACF;AAOO,IAAM,cAAa,CAAC,WAAmD;AAAA,KACzE,MAAK,KAAK;AAAA,EACb,QAAQ,MAAM;AAAA,EACd,MAAM;AACR;AAkBO,IAAM,cAAa,CAAC,UAAkD;AAAA,EAC3E,MAAM,QAAQ,OAAO,QAAQ,UAAS,KAAK,CAAC,EACzC,OAAO,IAAI,WAAW,UAAU,SAAS,EACzC,IAAI,EAAE,SAAS,GAAG;AAAA,EACrB,OAAO,WAAW,MAAK,KAAK,GAAG,KAAK;AAAA;;;ADY/B,IAAM,mBAAmB,WAA2B,mBAAmB;AAE9E,IAAM,aAAW,YAAiD;AAAA,EAChE,YAAY,CAAC,MAAM,UAAU;AAAA,IAS3B,MAAM,WAAW,KAAK,KAAK,OAAO;AAAA,IAClC,IAAI,aAAa,MAAM,aAAa,MAAM;AAAA,MAAM;AAAA,IAChD,OAAO;AAAA,MACL,MAAM,KAAK,KAAK,OAAO;AAAA,MACvB,eAAe,KAAK,KAAK,kBAAkB;AAAA,MAC3C,WAAW,WAAW,KAAK,aAAa;AAAA,MACxC,eAAe,KAAK,KAAK,gBAAgB;AAAA,MACzC,SAAS,KAAK,KAAK,UAAU;AAAA,MAC7B,MAAM,IAAI,KAAK,SAAS,CAAC;AAAA,MACzB,MAAM,KAAK,KAAK,OAAO;AAAA,MACvB,QAAQ;AAAA,MACR,UAAU,SAAS,KAAK;AAAA,MACxB,MAAM,MAAM;AAAA,MACZ,MAAM,KAAK,KAAK,OAAO;AAAA,IACzB;AAAA;AAAA,EAEF,YAAY,CAAC,UAAU,qBAAqB,MAAM;AAAA,EAClD;AAAA,EAeA,SAAS,CAAC,aAAY,UACpB,YAAW,aAAa,MAAM,WAAW,OACzC,YAAW,UAAU,MAAM,SAAS,SACpC,YAAW,mBAAmB,MAAM,iBAAiB,OACrD,YAAW,mBAAmB,MAAM,iBAAiB,OACrD,YAAW,cAAc,eAAe,MAAM,SAAS;AAAA,EAOzD,MAAM,CAAC,UAAU,qBAAqB,MAAM,gBAAgB,SAAS,KAAK;AAAA,EAC1E;AACF,CAAC;AAYM,IAAM,2BAA2B,MAC7B,kBAAO,kBAAyB,iBAAQ,iBAAiB,SAAS,GAAG,UAAQ,CAAC,CAAC;;AElM1F,qBAAS;AACT;AACA;AA6DO,IAAM,iBAAiB,WAAyB,iBAAiB;AAQxE,IAAM,QAAQ,CAAC,UAAoB,OAAO,UAAU,WAAW,QAAQ;AAUvE,IAAM,UAAQ,CAAC,UAAgD;AAAA,EAC7D,MAAM,QAAQ,MAAM,SAAS;AAAA,EAC7B,OAAO;AAAA,IAIL,iBAAiB,MAAM,iBAAiB,OAAO,MAAM;AAAA,IACrD,WAAW,MAAM,cAAc,OAAO,MAAM;AAAA,IAC5C,MAAM,MAAM;AAAA,OAIR,UAAU,KAAK,CAAC,IAAI,EAAE,MAAM;AAAA,OAC5B,MAAM,QAAQ,YAAY,CAAC,IAAI,EAAE,KAAK,OAAO,MAAM,GAAG,EAAE;AAAA,EAC9D;AAAA;AAoBF,IAAM,cAAa,CAAC,UAAgD;AAAA,EAClE,MAAM,SAAQ;AAAA,IACZ,IAAK,MAAM,SAAS,QAAQ,KAAK,CAAC,OAAO,IAAI,CAAC;AAAA,IAC9C,GAAI,MAAM,QAAQ,YAAY,CAAC,KAAK,IAAI,CAAC;AAAA,EAC3C;AAAA,EACA,OAAO,WAAW,QAAM,KAAK,GAAG,MAAK;AAAA;AAGvC,IAAM,aAAW,YAA6C;AAAA,EAI5D,UAAU;AAAA,EACV,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,OAAO,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;AAAA,IAC3D,QAAQ,MAAM;AAAA,IACd,cAAc,KAAK,KAAK,gBAAgB;AAAA,IACxC,KAAK,MAAM,KAAK,MAAM;AAAA,IACtB,WAAW,KAAK,KAAK,YAAY;AAAA,IACjC,MAAM,MAAM;AAAA,IAOZ,MAAM,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;AAAA,EAC1D;AAAA,EACA,YAAY,MAAM;AAAA,EAClB,YAAY,CAAC,WAAW,KAAK,QAAM,KAAK,GAAG,MAAM,MAAM,KAAK;AAAA,EAS5D,UAAU,EAAE,QAAQ,+BAA+B,QAAQ,oCAAoC;AAAA,EAC/F,SAAS,CAAC,aAAY,UACpB,YAAW,SAAS,MAAM,QAC1B,YAAW,WAAW,MAAM,SAAS,OACrC,YAAW,QAAQ,MAAM,OACzB,YAAW,eAAe,MAAM,cAAc,SAC9C,YAAW,kBAAkB,MAAM,iBAAiB;AAAA,EAOtD,MAAM,CAAC,UAAU,qBAAqB,MAAM;AAAA,EAC5C;AACF,CAAC;AAgBM,IAAM,yBAAyB,MAC3B,kBAAO,gBAAuB,iBAAQ,eAAe,SAAS,GAAG,UAAQ,CAAC,CAAC;;ACrLtF,qBAAS;AACT;AACA;AAwEO,IAAM,iBAAiB,WAAyB,iBAAiB;AAQxE,IAAM,WAAW,CAAC,MAAc,eAC7B,SAAS,KAAK,QAAQ,WAAW,cAAa,aAAa,cAAa,KAAK,QAAQ;AASxF,IAAM,QAAO,CAAC,WAAiD;AAAA,KACzD,MAAM,WAAW,YAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;AAAA,KACzD,MAAM,QAAQ,YAAY,CAAC,IAAI,EAAE,KAAK,MAAM,IAAI;AAAA,KAChD,MAAM,YAAY,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;AAAA,KAC5D,MAAM,SAAS,YAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;AAAA,KACnD,MAAM,QAAQ,YAAY,CAAC,IAAI,EAAE,KAAK,OAAO,MAAM,GAAG,EAAE;AAAA,KACxD,MAAM,UAAU,YAAY,CAAC,IAAI,EAAE,OAAO,IAAI,MAAM,KAAK,EAAE;AAAA,KAC3D,MAAM,UAAU,YAAY,CAAC,IAAI,EAAE,OAAO,IAAI,MAAM,KAAK,EAAE;AAAA,KAC3D,MAAM,QAAQ,YAAY,CAAC,IAAI,EAAE,KAAK,OAAO,MAAM,GAAG,EAAE;AAAA,KACxD,MAAM,iBAAiB,YAAY,CAAC,IAAI,EAAE,aAAa,OAAO,MAAM,YAAY,EAAE;AACxF;AAkBA,IAAM,YAAoD;AAAA,EACxD,MAAM,CAAC,OAAO,WAAW,OAAO,OAAO;AAAA,EACvC,MAAM,CAAC,OAAO,WAAW,QAAQ,OAAO,OAAO;AAAA,EAC/C,QAAQ,CAAC,OAAO,WAAW,QAAQ,OAAO,OAAO;AAAA,EACjD,MAAM,CAAC,OAAO,WAAW,QAAQ,OAAO,OAAO;AAAA,EAC/C,OAAO,CAAC,OAAO,WAAW,QAAQ,OAAO,OAAO;AAClD;AAEA,IAAM,aAAW,YAA6C;AAAA,EAI5D,UAAU;AAAA,EACV,YAAY,CAAC,MAAM,UAAU;AAAA,IAQ3B,MAAM,WAAW,KAAK,KAAK,OAAO;AAAA,IAClC,IAAI,aAAa,MAAM,aAAa,MAAM;AAAA,MAAM;AAAA,IAChD,OAAO;AAAA,MACL,QAAQ,KAAK,KAAK,SAAS;AAAA,MAC3B,KAAK,KAAK,KAAK,MAAM;AAAA,MACrB,SAAS,KAAK,KAAK,UAAU;AAAA,MAC7B,MAAM,KAAK,KAAK,OAAO,MAAM,KAAK,QAAQ,KAAK,KAAK,OAAO;AAAA,MAC3D,KAAK,IAAI,KAAK,QAAQ,CAAC;AAAA,MACvB,OAAO,IAAI,KAAK,KAAK,QAAQ,CAAC;AAAA,MAC9B,OAAO,IAAI,KAAK,KAAK,QAAQ,CAAC;AAAA,MAC9B,QAAQ;AAAA,MACR,KAAK,IAAI,KAAK,QAAQ,CAAC;AAAA,MACvB,MAAM,MAAM;AAAA,MACZ,aAAa,IAAI,KAAK,cAAc,CAAC;AAAA,MACrC,MAAM,MAAM;AAAA,IACd;AAAA;AAAA,EAEF,YAAY,MAAM;AAAA,EAClB,YAAY,CAAC,WAAW,KAAK,MAAK,KAAK,GAAG,MAAM,MAAM,MAAM,MAAM,MAAM,KAAK;AAAA,EAO7E,UAAU,EAAE,QAAQ,+BAA+B,QAAQ,oCAAoC;AAAA,EAC/F,SAAS,CAAC,aAAY,UACpB,YAAW,SAAS,MAAM,OAAO,MACjC,YAAW,UAAU,IAAI,MAAM,KAAK,KACpC,SAAS,YAAW,MAAM,MAAM,IAAI,KACpC,YAAW,SAAS,MAAM,OAAO,OACjC,YAAW,aAAa,MAAM,WAAW,OACzC,YAAW,UAAU,IAAI,MAAM,KAAK,KACpC,YAAW,kBAAkB,MAAM,gBAAgB,MACnD,YAAW,SAAS,MAAM,OAAO,MACjC,YAAW,YAAY,MAAM,UAAU;AAAA,EACzC,MAAM,CAAC,UAAU,qBAAqB,MAAM;AAAA,EAC5C,YAAY,CAAC,UAAU;AAAA,IACrB,MAAM,SAAS,MAAK,KAAK;AAAA,IACzB,MAAM,QAAQ,UAAU,MAAM,MAAM,OAAO,CAAC,WAAW,OAAO,YAAY,SAAS;AAAA,IACnF,OAAO,WAAW,QAAQ,KAAK;AAAA;AAEnC,CAAC;AAgBM,IAAM,yBAAyB,MAC3B,kBAAO,gBAAuB,iBAAQ,eAAe,SAAS,GAAG,UAAQ,CAAC,CAAC;;ACnNtF,qBAAS;AACT;AACA;AAgFO,IAAM,iBAAiB,WAAyB,mBAAmB;AAAA,EACxE,sBAAsB;AACxB,CAAC;AAED,IAAM,OAAM,CAAC,OAAgB,WAAW,OAAQ,OAAO,UAAU,WAAW,QAAQ;AAEpF,IAAM,SAAQ,CAAC,MAAc,UAC3B,UAAU,YAAY,CAAC,IAAI,GAAG,OAAO,MAAM;AAQ7C,IAAM,cAAc,CAAC,aAClB,KAAK,QAAQ;AAShB,IAAM,SAAS,CAAC,UACd,MACG,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,SAAS,EAAE,EAC5B,KAAK,EACL,KAAK,GAAG;AAEb,IAAM,YAAW,CAAC,WAA8B,SAC9C,cAAa,aAAa,OAAO,SAAQ,MAAM,OAAO,IAAI;AAE5D,IAAM,QAAO,CAAI,WAAyB,SAAY,cAAa,aAAa,cAAa;AAe7F,IAAM,WAAU,CAAC,WAAiD;AAAA,KAC7D,OAAM,WAAW,MAAM,OAAO;AAAA,KAC9B,OAAM,WAAW,MAAM,OAAO;AAAA,KAC9B,OAAM,WAAW,KAAK,MAAM,OAAO,CAAC;AAAA,KACpC,OAAM,YAAY,MAAM,aAAa,YAAY,YAAY,OAAO,MAAM,QAAQ,CAAC;AAAA,KACnF,OAAM,SAAS,MAAM,KAAK;AAAA,KAC1B,OAAM,iBAAiB,MAAM,aAAa;AAAA,KAC1C,OAAM,iBAAiB,MAAM,gBAAgB;AAAA,KAC7C,OAAM,UAAU,KAAK,MAAM,MAAM,CAAC;AACvC;AAEA,IAAM,aAAW,YAA6C;AAAA,EAI5D,UAAU;AAAA,EAMV,YAAY,CAAC,MAAM,WAAW;AAAA,IAC5B,SAAS,KAAI,KAAK,UAAU;AAAA,IAC5B,SAAS,KAAI,KAAK,UAAU;AAAA,IAC5B,SAAS,KAAK,KAAK,UAAU;AAAA,IAC7B,UAAU,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc;AAAA,IACpE,OAAO,KAAI,KAAK,QAAQ;AAAA,IACxB,eAAe,KAAI,KAAK,gBAAgB;AAAA,IACxC,iBAAiB,KAAI,KAAK,gBAAgB;AAAA,IAC1C,QAAQ,KAAK,KAAK,SAAS;AAAA,IAC3B,SAAS,MAAM;AAAA,IACf,MAAM,KAAI,KAAK,SAAS,MAAM,IAAI;AAAA,EACpC;AAAA,EACA,YAAY,MAAM;AAAA,EAMlB,YAAY,CAAC,WAAW;AAAA,OACnB,YAAY,MAAM,OAAO;AAAA,OACzB,SAAQ,KAAK;AAAA,IAChB,SAAS,MAAM;AAAA,IACf,MAAM,MAAM;AAAA,EACd;AAAA,EAOA,UAAU,EAAE,QAAQ,qBAAqB,QAAQ,6BAA6B;AAAA,EAC9E,SAAS,CAAC,aAAY,UACpB,UAAS,MAAM,SAAS,YAAW,OAAO,KAC1C,UAAS,MAAM,OAAO,YAAW,KAAK,KACtC,UAAS,MAAM,kBAAkB,YAAW,gBAAgB,KAC5D,MAAK,MAAM,SAAS,YAAW,OAAO,KACtC,MAAK,MAAM,QAAQ,YAAW,MAAM,KACpC,MAAK,MAAM,eAAe,YAAW,aAAa,KAClD,MAAK,MAAM,SAAS,YAAW,OAAO,KACtC,MAAK,MAAM,UAAU,YAAW,QAAQ;AAAA,EAC1C,MAAM,CAAC,UAAU,WAAW,MAAM;AAAA,EAClC,YAAY;AACd,CAAC;AAcM,IAAM,yBAAyB,MAC3B,kBAAO,gBAAuB,iBAAQ,eAAe,SAAS,GAAG,UAAQ,CAAC,CAAC;;AC/LtF,qBAAS;AACT;AACA;;;ACvCA;AAmBO,IAAM,cAAa,CAAC,WAAiD;AAAA,EAC1E,SAAS,MAAM,QAAQ,KAAK,GAAG;AAAA,EAC/B,MAAM,MAAM;AAAA,EACZ,WAAW,MAAM;AAAA,KACb,MAAM,WAAW,YAAY,CAAC,IAAI,EAAE,QAAQ,OAAO,MAAM,MAAM,EAAE;AAAA,KACjE,MAAM,gBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,MAAM,YAAY;AAAA,KACxE,MAAM,oBAAoB,YAAY,CAAC,IAAI,EAAE,gBAAgB,MAAM,gBAAgB;AACzF;AAGA,IAAM,kBAAkB;AA2BjB,IAAM,aAAa,CACxB,OACA,aACA,OACA,UAEO,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WAAW,OAAO,MAAK,KAAK;AAAA,EAClC,IAAI,aAAa;AAAA,IAAW,OAAO;AAAA,EAEnC,MAAM,OAAO,OAAO,IAClB,MAAM,QACN,aACA,QACA,aACA,YAAW,KAAK,CAClB;AAAA,EAEA,MAAM,UAAS,CAAC,aACP,aAAI,UAAU,GAAG;AAAA,IACtB,MAAM,OAAO,OAAO,MAAK,KAAK;AAAA,IAC9B,IAAI,SAAS,aAAa,YAAY;AAAA,MAAG,OAAO;AAAA,IAChD,OAAc,eAAM,WAAW;AAAA,IAC/B,OAAO,OAAO,QAAO,WAAW,CAAC;AAAA,GAClC;AAAA,EAEH,MAAM,QAAQ,OAAO,QAAO,eAAe;AAAA,EAC3C,IAAI,UAAU,WAAW;AAAA,IACvB,OAAO,OAAc,aACnB,IAAI,MACF,GAAG,wBAAuB,KAAK,MAAM,WAAW,2CAC9C,uFACA,2BAA2B,MAAM,sDACjC,wFACA,+BACJ,CACF;AAAA,EACF;AAAA,EACA,OAAO;AAAA,CACR;AAcI,IAAM,cAAc,CAAC,MAAoB,UAC9C,IAAI,KAAK,QAAQ,aAAa,UAAU,KAAI;;;AD2CvC,IAAM,iBAAiB,WAAyB,mBAAmB;AAAA,EACxE,sBAAsB;AACxB,CAAC;AAUD,IAAM,cAAc,CAAC,aACnB,MAAM,QAAQ,QAAQ,IAClB,SAAS,QAAQ,CAAC,UAAmB;AAAA,EACnC,MAAM,OAAO;AAAA,EACb,MAAM,SAAS,YAAY,KAAK,QAAQ;AAAA,EACxC,OAAO,OAAO,SAAS,IAAI,SAAS,CAAC,KAAK,KAAK,IAAI,CAAC,EAAE,OAAO,CAAC,SAAS,SAAS,EAAE;AAAA,CACnF,IACD,CAAC;AAEP,IAAM,QAAiD;AAAA,EAOrD,YAAY,CAAC,MAAM,UAAU;AAAA,IAI3B,IAAI,KAAK,KAAK,OAAO,MAAM;AAAA,MAAI;AAAA,IAC/B,OAAO;AAAA,MACL,SAAS,YAAY,KAAK,WAAW,EAAE,KAAK,GAAG;AAAA,MAC/C,QAAQ,KAAK,KAAK,WAAW,SAAS;AAAA,MACtC,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,OAAO,KAAK,KAAK,UAAU,SAAS;AAAA,IACtC;AAAA;AAAA,EAEF,YAAY,CAAC,UAAU,SAAS,MAAM;AAAA,EACtC;AAAA,EAcA,SAAS,MAAM;AAAA,EACf,MAAM,CAAC,UAAU,SAAS,MAAM,kBAAkB,MAAM;AAC1D;AAEA,IAAM,OAAM,cAAc,KAAI;AAU9B,IAAM,aAAW;AAAA,KACZ,YAAY,KAAI;AAAA,EACnB,QAAe,YAAG,UAAU,GAAG,QAAgC;AAAA,IAC7D,OAAO,YAAY,MAAM,MAAK,KAAK,IAAI,CAAC;AAAA,GACzC;AAAA,EACD,WAAW,GAAG,WACZ,WAAW,MAAM,MAAK,WAAW,IAAI,GAAG,MAAK,KAAK,IAAI,GAAG,KAAI,IAAI;AACrE;AAEO,IAAM,yBAAyB,MAC3B,kBAAO,gBAAuB,iBAAQ,eAAe,SAAS,GAAG,UAAQ,CAAC,CAAC;",
|
|
105
|
-
"debugId": "
|
|
120
|
+
"mappings": "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",
|
|
121
|
+
"debugId": "35B6C615E87D256B64756E2164756E21",
|
|
106
122
|
"names": []
|
|
107
123
|
}
|