@homeflare/alchemy 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/cloudflare/client.d.ts +45 -0
- package/dist/cloudflare/client.d.ts.map +1 -0
- package/dist/cloudflare/index.d.ts +11 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +129 -0
- package/dist/cloudflare/index.js.map +13 -0
- package/dist/cloudflare/lock-rules.d.ts +50 -0
- package/dist/cloudflare/lock-rules.d.ts.map +1 -0
- package/dist/cloudflare/providers.d.ts +19 -0
- package/dist/cloudflare/providers.d.ts.map +1 -0
- package/dist/cloudflare/r2-bucket-lock.d.ts +100 -0
- package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -0
- package/dist/forgejo/branch-protection-form.d.ts +19 -0
- package/dist/forgejo/branch-protection-form.d.ts.map +1 -0
- package/dist/forgejo/branch-protection.d.ts +47 -0
- package/dist/forgejo/branch-protection.d.ts.map +1 -0
- package/dist/forgejo/client.d.ts +37 -0
- package/dist/forgejo/client.d.ts.map +1 -0
- package/dist/forgejo/index.d.ts +18 -0
- package/dist/forgejo/index.d.ts.map +1 -0
- package/dist/forgejo/index.js +479 -0
- package/dist/forgejo/index.js.map +21 -0
- package/dist/forgejo/org-actions-secrets.d.ts +45 -0
- package/dist/forgejo/org-actions-secrets.d.ts.map +1 -0
- package/dist/forgejo/org-label.d.ts +46 -0
- package/dist/forgejo/org-label.d.ts.map +1 -0
- package/dist/forgejo/org-team.d.ts +42 -0
- package/dist/forgejo/org-team.d.ts.map +1 -0
- package/dist/forgejo/repo-webhook-form.d.ts +27 -0
- package/dist/forgejo/repo-webhook-form.d.ts.map +1 -0
- package/dist/forgejo/repo-webhook.d.ts +46 -0
- package/dist/forgejo/repo-webhook.d.ts.map +1 -0
- package/dist/forgejo/repository.d.ts +45 -0
- package/dist/forgejo/repository.d.ts.map +1 -0
- package/dist/forgejo/resource.d.ts +83 -0
- package/dist/forgejo/resource.d.ts.map +1 -0
- package/dist/forgejo/team-member.d.ts +39 -0
- package/dist/forgejo/team-member.d.ts.map +1 -0
- package/dist/forgejo/values.d.ts +24 -0
- package/dist/forgejo/values.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +9 -0
- package/dist/openbao/auth-role-form.d.ts +38 -0
- package/dist/openbao/auth-role-form.d.ts.map +1 -0
- package/dist/openbao/auth-role.d.ts +20 -0
- package/dist/openbao/auth-role.d.ts.map +1 -0
- package/dist/openbao/bao-address.d.ts +72 -0
- package/dist/openbao/bao-address.d.ts.map +1 -0
- package/dist/openbao/bao-http.d.ts +71 -0
- package/dist/openbao/bao-http.d.ts.map +1 -0
- package/dist/openbao/bao-status.d.ts +74 -0
- package/dist/openbao/bao-status.d.ts.map +1 -0
- package/dist/openbao/cloudflare-group-scope.d.ts +73 -0
- package/dist/openbao/cloudflare-group-scope.d.ts.map +1 -0
- package/dist/openbao/cloudflare-parity-catalog.d.ts +28 -0
- package/dist/openbao/cloudflare-parity-catalog.d.ts.map +1 -0
- package/dist/openbao/cloudflare-parity-snapshot.d.ts +31 -0
- package/dist/openbao/cloudflare-parity-snapshot.d.ts.map +1 -0
- package/dist/openbao/cloudflare-parity.d.ts +14 -0
- package/dist/openbao/cloudflare-parity.d.ts.map +1 -0
- package/dist/openbao/cloudflare-permission-groups-solve.d.ts +85 -0
- package/dist/openbao/cloudflare-permission-groups-solve.d.ts.map +1 -0
- package/dist/openbao/cloudflare-permission-groups.d.ts +87 -0
- package/dist/openbao/cloudflare-permission-groups.d.ts.map +1 -0
- package/dist/openbao/cloudflare-policy.d.ts +65 -0
- package/dist/openbao/cloudflare-policy.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role-form.d.ts +110 -0
- package/dist/openbao/cloudflare-role-form.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role-wire.d.ts +27 -0
- package/dist/openbao/cloudflare-role-wire.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role.d.ts +35 -0
- package/dist/openbao/cloudflare-role.d.ts.map +1 -0
- package/dist/openbao/cloudflare-roles-config.d.ts +67 -0
- package/dist/openbao/cloudflare-roles-config.d.ts.map +1 -0
- package/dist/openbao/cloudflare-roles-expand.d.ts +76 -0
- package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -0
- package/dist/openbao/digest.d.ts +24 -0
- package/dist/openbao/digest.d.ts.map +1 -0
- package/dist/openbao/fake-bao.d.ts +40 -0
- package/dist/openbao/fake-bao.d.ts.map +1 -0
- package/dist/openbao/forgejo-bootstrap-parse.d.ts +46 -0
- package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -0
- package/dist/openbao/forgejo-bootstrap.d.ts +29 -0
- package/dist/openbao/forgejo-bootstrap.d.ts.map +1 -0
- package/dist/openbao/index.d.ts +18 -0
- package/dist/openbao/index.d.ts.map +1 -0
- package/dist/openbao/index.js +1158 -0
- package/dist/openbao/index.js.map +33 -0
- package/dist/openbao/mount-form.d.ts +60 -0
- package/dist/openbao/mount-form.d.ts.map +1 -0
- package/dist/openbao/mount-wire.d.ts +24 -0
- package/dist/openbao/mount-wire.d.ts.map +1 -0
- package/dist/openbao/mount.d.ts +20 -0
- package/dist/openbao/mount.d.ts.map +1 -0
- package/dist/openbao/pki-role-form.d.ts +118 -0
- package/dist/openbao/pki-role-form.d.ts.map +1 -0
- package/dist/openbao/pki-role.d.ts +50 -0
- package/dist/openbao/pki-role.d.ts.map +1 -0
- package/dist/openbao/policy-assembly.d.ts +13 -0
- package/dist/openbao/policy-assembly.d.ts.map +1 -0
- package/dist/openbao/policy-wire.d.ts +41 -0
- package/dist/openbao/policy-wire.d.ts.map +1 -0
- package/dist/openbao/policy.d.ts +83 -0
- package/dist/openbao/policy.d.ts.map +1 -0
- package/dist/openbao/proxmox-role-form.d.ts +92 -0
- package/dist/openbao/proxmox-role-form.d.ts.map +1 -0
- package/dist/openbao/proxmox-role.d.ts +47 -0
- package/dist/openbao/proxmox-role.d.ts.map +1 -0
- package/dist/openbao/ssh-role-form.d.ts +99 -0
- package/dist/openbao/ssh-role-form.d.ts.map +1 -0
- package/dist/openbao/ssh-role.d.ts +43 -0
- package/dist/openbao/ssh-role.d.ts.map +1 -0
- package/dist/proxmox/acl.d.ts +72 -0
- package/dist/proxmox/acl.d.ts.map +1 -0
- package/dist/proxmox/api-token-form.d.ts +59 -0
- package/dist/proxmox/api-token-form.d.ts.map +1 -0
- package/dist/proxmox/api-token.d.ts +159 -0
- package/dist/proxmox/api-token.d.ts.map +1 -0
- package/dist/proxmox/backup-job.d.ts +112 -0
- package/dist/proxmox/backup-job.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon-form.d.ts +70 -0
- package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon.d.ts +162 -0
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -0
- package/dist/proxmox/ceph-flag.d.ts +131 -0
- package/dist/proxmox/ceph-flag.d.ts.map +1 -0
- package/dist/proxmox/ceph-fs-wire.d.ts +106 -0
- package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -0
- package/dist/proxmox/ceph-fs.d.ts +124 -0
- package/dist/proxmox/ceph-fs.d.ts.map +1 -0
- package/dist/proxmox/ceph-osd-tree.d.ts +154 -0
- package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -0
- package/dist/proxmox/ceph-osd-write.d.ts +36 -0
- package/dist/proxmox/ceph-osd-write.d.ts.map +1 -0
- package/dist/proxmox/ceph-osd.d.ts +102 -0
- package/dist/proxmox/ceph-osd.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool-form.d.ts +217 -0
- package/dist/proxmox/ceph-pool-form.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool-settle.d.ts +39 -0
- package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool.d.ts +48 -0
- package/dist/proxmox/ceph-pool.d.ts.map +1 -0
- package/dist/proxmox/client.d.ts +64 -0
- package/dist/proxmox/client.d.ts.map +1 -0
- package/dist/proxmox/credentials.d.ts +153 -0
- package/dist/proxmox/credentials.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias-form.d.ts +98 -0
- package/dist/proxmox/firewall-alias-form.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias.d.ts +141 -0
- package/dist/proxmox/firewall-alias.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs.d.ts +388 -0
- package/dist/proxmox/generated/pbs.d.ts.map +1 -0
- package/dist/proxmox/generated/pve.d.ts +4713 -0
- package/dist/proxmox/generated/pve.d.ts.map +1 -0
- package/dist/proxmox/group.d.ts +104 -0
- package/dist/proxmox/group.d.ts.map +1 -0
- package/dist/proxmox/ha-resource.d.ts +104 -0
- package/dist/proxmox/ha-resource.d.ts.map +1 -0
- package/dist/proxmox/ha-rule-form.d.ts +103 -0
- package/dist/proxmox/ha-rule-form.d.ts.map +1 -0
- package/dist/proxmox/ha-rule.d.ts +156 -0
- package/dist/proxmox/ha-rule.d.ts.map +1 -0
- package/dist/proxmox/index.d.ts +43 -0
- package/dist/proxmox/index.d.ts.map +1 -0
- package/dist/proxmox/index.js +2322 -0
- package/dist/proxmox/index.js.map +70 -0
- package/dist/proxmox/lease-cache.d.ts +98 -0
- package/dist/proxmox/lease-cache.d.ts.map +1 -0
- package/dist/proxmox/lxc.d.ts +59 -0
- package/dist/proxmox/lxc.d.ts.map +1 -0
- package/dist/proxmox/members.d.ts +56 -0
- package/dist/proxmox/members.d.ts.map +1 -0
- package/dist/proxmox/metric-server-form.d.ts +53 -0
- package/dist/proxmox/metric-server-form.d.ts.map +1 -0
- package/dist/proxmox/metric-server-otel.d.ts +49 -0
- package/dist/proxmox/metric-server-otel.d.ts.map +1 -0
- package/dist/proxmox/metric-server.d.ts +123 -0
- package/dist/proxmox/metric-server.d.ts.map +1 -0
- package/dist/proxmox/network-apply-read.d.ts +93 -0
- package/dist/proxmox/network-apply-read.d.ts.map +1 -0
- package/dist/proxmox/network-apply.d.ts +102 -0
- package/dist/proxmox/network-apply.d.ts.map +1 -0
- package/dist/proxmox/node-network-form.d.ts +128 -0
- package/dist/proxmox/node-network-form.d.ts.map +1 -0
- package/dist/proxmox/node-network.d.ts +173 -0
- package/dist/proxmox/node-network.d.ts.map +1 -0
- package/dist/proxmox/notification-target-form.d.ts +44 -0
- package/dist/proxmox/notification-target-form.d.ts.map +1 -0
- package/dist/proxmox/notification-target.d.ts +146 -0
- package/dist/proxmox/notification-target.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore-form.d.ts +147 -0
- package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore-guard.d.ts +55 -0
- package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore.d.ts +140 -0
- package/dist/proxmox/pbs-datastore.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job-form.d.ts +97 -0
- package/dist/proxmox/pbs-prune-job-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job.d.ts +168 -0
- package/dist/proxmox/pbs-prune-job.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job-form.d.ts +83 -0
- package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job.d.ts +246 -0
- package/dist/proxmox/pbs-sync-job.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job-form.d.ts +114 -0
- package/dist/proxmox/pbs-verify-job-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job.d.ts +145 -0
- package/dist/proxmox/pbs-verify-job.d.ts.map +1 -0
- package/dist/proxmox/pool.d.ts +42 -0
- package/dist/proxmox/pool.d.ts.map +1 -0
- package/dist/proxmox/qemu.d.ts +46 -0
- package/dist/proxmox/qemu.d.ts.map +1 -0
- package/dist/proxmox/replication-job-form.d.ts +71 -0
- package/dist/proxmox/replication-job-form.d.ts.map +1 -0
- package/dist/proxmox/replication-job.d.ts +162 -0
- package/dist/proxmox/replication-job.d.ts.map +1 -0
- package/dist/proxmox/resource.d.ts +157 -0
- package/dist/proxmox/resource.d.ts.map +1 -0
- package/dist/proxmox/role.d.ts +97 -0
- package/dist/proxmox/role.d.ts.map +1 -0
- package/dist/proxmox/sdn-apply-read.d.ts +80 -0
- package/dist/proxmox/sdn-apply-read.d.ts.map +1 -0
- package/dist/proxmox/sdn-apply.d.ts +63 -0
- package/dist/proxmox/sdn-apply.d.ts.map +1 -0
- package/dist/proxmox/sdn-subnet-form.d.ts +111 -0
- package/dist/proxmox/sdn-subnet-form.d.ts.map +1 -0
- package/dist/proxmox/sdn-subnet.d.ts +161 -0
- package/dist/proxmox/sdn-subnet.d.ts.map +1 -0
- package/dist/proxmox/sdn-vnet.d.ts +113 -0
- package/dist/proxmox/sdn-vnet.d.ts.map +1 -0
- package/dist/proxmox/sdn-zone.d.ts +126 -0
- package/dist/proxmox/sdn-zone.d.ts.map +1 -0
- package/dist/proxmox/storage.d.ts +120 -0
- package/dist/proxmox/storage.d.ts.map +1 -0
- package/dist/proxmox/user.d.ts +107 -0
- package/dist/proxmox/user.d.ts.map +1 -0
- package/dist/proxmox/values.d.ts +134 -0
- package/dist/proxmox/values.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool-write.d.ts +76 -0
- package/dist/proxmox/zfs-pool-write.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool.d.ts +146 -0
- package/dist/proxmox/zfs-pool.d.ts.map +1 -0
- package/dist/talos/credentials.d.ts +30 -0
- package/dist/talos/credentials.d.ts.map +1 -0
- package/dist/talos/index.d.ts +15 -0
- package/dist/talos/index.d.ts.map +1 -0
- package/dist/talos/index.js +392 -0
- package/dist/talos/index.js.map +16 -0
- package/dist/talos/kubeconfig.d.ts +53 -0
- package/dist/talos/kubeconfig.d.ts.map +1 -0
- package/dist/talos/resource.d.ts +32 -0
- package/dist/talos/resource.d.ts.map +1 -0
- package/dist/talos/talos-bootstrap.d.ts +27 -0
- package/dist/talos/talos-bootstrap.d.ts.map +1 -0
- package/dist/talos/talos-cluster-health.d.ts +36 -0
- package/dist/talos/talos-cluster-health.d.ts.map +1 -0
- package/dist/talos/talos-machine-config.d.ts +41 -0
- package/dist/talos/talos-machine-config.d.ts.map +1 -0
- package/dist/talos/talosctl.d.ts +29 -0
- package/dist/talos/talosctl.d.ts.map +1 -0
- package/dist/talos/values.d.ts +20 -0
- package/dist/talos/values.d.ts.map +1 -0
- package/package.json +57 -0
- package/src/cloudflare/client.ts +76 -0
- package/src/cloudflare/index.ts +10 -0
- package/src/cloudflare/lock-rules.ts +60 -0
- package/src/cloudflare/providers.ts +23 -0
- package/src/cloudflare/r2-bucket-lock.ts +220 -0
- package/src/forgejo/branch-protection-form.ts +29 -0
- package/src/forgejo/branch-protection.ts +101 -0
- package/src/forgejo/client.ts +136 -0
- package/src/forgejo/index.ts +17 -0
- package/src/forgejo/org-actions-secrets.ts +99 -0
- package/src/forgejo/org-label.ts +100 -0
- package/src/forgejo/org-team.ts +105 -0
- package/src/forgejo/repo-webhook-form.ts +47 -0
- package/src/forgejo/repo-webhook.ts +118 -0
- package/src/forgejo/repository.ts +126 -0
- package/src/forgejo/resource.ts +171 -0
- package/src/forgejo/team-member.ts +97 -0
- package/src/forgejo/values.test.ts +48 -0
- package/src/forgejo/values.ts +61 -0
- package/src/index.ts +18 -0
- package/src/openbao/auth-role-form.ts +112 -0
- package/src/openbao/auth-role.ts +101 -0
- package/src/openbao/bao-address.test.ts +89 -0
- package/src/openbao/bao-address.ts +97 -0
- package/src/openbao/bao-http-gate.test.ts +88 -0
- package/src/openbao/bao-http-unix.test.ts +70 -0
- package/src/openbao/bao-http.test.ts +161 -0
- package/src/openbao/bao-http.ts +185 -0
- package/src/openbao/bao-status.test.ts +114 -0
- package/src/openbao/bao-status.ts +137 -0
- package/src/openbao/cloudflare-group-scope.test.ts +103 -0
- package/src/openbao/cloudflare-group-scope.ts +83 -0
- package/src/openbao/cloudflare-parity-catalog.ts +104 -0
- package/src/openbao/cloudflare-parity-snapshot.ts +84 -0
- package/src/openbao/cloudflare-parity.ts +152 -0
- package/src/openbao/cloudflare-permission-groups-solve.test.ts +171 -0
- package/src/openbao/cloudflare-permission-groups-solve.ts +197 -0
- package/src/openbao/cloudflare-permission-groups.test.ts +369 -0
- package/src/openbao/cloudflare-permission-groups.ts +299 -0
- package/src/openbao/cloudflare-policy.ts +180 -0
- package/src/openbao/cloudflare-role-form.test.ts +177 -0
- package/src/openbao/cloudflare-role-form.ts +183 -0
- package/src/openbao/cloudflare-role-wire.test.ts +132 -0
- package/src/openbao/cloudflare-role-wire.ts +41 -0
- package/src/openbao/cloudflare-role.ts +189 -0
- package/src/openbao/cloudflare-roles-config.ts +230 -0
- package/src/openbao/cloudflare-roles-expand-bucket.test.ts +134 -0
- package/src/openbao/cloudflare-roles-expand.ts +231 -0
- package/src/openbao/digest.ts +26 -0
- package/src/openbao/fake-bao.ts +84 -0
- package/src/openbao/forgejo-bootstrap-parse.test.ts +76 -0
- package/src/openbao/forgejo-bootstrap-parse.ts +90 -0
- package/src/openbao/forgejo-bootstrap.ts +181 -0
- package/src/openbao/index.ts +17 -0
- package/src/openbao/mount-form.ts +147 -0
- package/src/openbao/mount-wire.test.ts +108 -0
- package/src/openbao/mount-wire.ts +79 -0
- package/src/openbao/mount.ts +132 -0
- package/src/openbao/pki-role-form.ts +248 -0
- package/src/openbao/pki-role.ts +202 -0
- package/src/openbao/policy-assembly.test.ts +28 -0
- package/src/openbao/policy-assembly.ts +13 -0
- package/src/openbao/policy-wire.test.ts +90 -0
- package/src/openbao/policy-wire.ts +53 -0
- package/src/openbao/policy.ts +212 -0
- package/src/openbao/proxmox-role-form.ts +174 -0
- package/src/openbao/proxmox-role.ts +214 -0
- package/src/openbao/ssh-role-form.ts +248 -0
- package/src/openbao/ssh-role.ts +240 -0
- package/src/proxmox/acl.ts +267 -0
- package/src/proxmox/api-token-form.ts +122 -0
- package/src/proxmox/api-token.ts +222 -0
- package/src/proxmox/backup-job.ts +213 -0
- package/src/proxmox/ceph-daemon-form.ts +84 -0
- package/src/proxmox/ceph-daemon.ts +243 -0
- package/src/proxmox/ceph-flag.ts +249 -0
- package/src/proxmox/ceph-fs-wire.ts +246 -0
- package/src/proxmox/ceph-fs.ts +249 -0
- package/src/proxmox/ceph-osd-tree.ts +193 -0
- package/src/proxmox/ceph-osd-write.ts +76 -0
- package/src/proxmox/ceph-osd.ts +244 -0
- package/src/proxmox/ceph-pool-form.ts +249 -0
- package/src/proxmox/ceph-pool-settle.ts +70 -0
- package/src/proxmox/ceph-pool.ts +212 -0
- package/src/proxmox/client.test.ts +185 -0
- package/src/proxmox/client.ts +240 -0
- package/src/proxmox/credentials.test.ts +144 -0
- package/src/proxmox/credentials.ts +254 -0
- package/src/proxmox/firewall-alias-form.ts +112 -0
- package/src/proxmox/firewall-alias.ts +208 -0
- package/src/proxmox/generated/pbs.ts +461 -0
- package/src/proxmox/generated/pve.ts +7344 -0
- package/src/proxmox/group.ts +199 -0
- package/src/proxmox/ha-resource.ts +169 -0
- package/src/proxmox/ha-rule-form.ts +185 -0
- package/src/proxmox/ha-rule.ts +240 -0
- package/src/proxmox/index.ts +42 -0
- package/src/proxmox/lease-cache.test.ts +163 -0
- package/src/proxmox/lease-cache.ts +131 -0
- package/src/proxmox/lxc.ts +117 -0
- package/src/proxmox/members.test.ts +54 -0
- package/src/proxmox/members.ts +174 -0
- package/src/proxmox/metric-server-form.ts +128 -0
- package/src/proxmox/metric-server-otel.ts +99 -0
- package/src/proxmox/metric-server.ts +214 -0
- package/src/proxmox/network-apply-read.ts +228 -0
- package/src/proxmox/network-apply.ts +244 -0
- package/src/proxmox/node-network-form.ts +213 -0
- package/src/proxmox/node-network.ts +243 -0
- package/src/proxmox/notification-target-form.ts +104 -0
- package/src/proxmox/notification-target.ts +222 -0
- package/src/proxmox/pbs-datastore-form.ts +248 -0
- package/src/proxmox/pbs-datastore-guard.ts +105 -0
- package/src/proxmox/pbs-datastore.ts +271 -0
- package/src/proxmox/pbs-prune-job-form.ts +208 -0
- package/src/proxmox/pbs-prune-job.ts +227 -0
- package/src/proxmox/pbs-sync-job-form.ts +234 -0
- package/src/proxmox/pbs-sync-job.ts +258 -0
- package/src/proxmox/pbs-verify-job-form.ts +131 -0
- package/src/proxmox/pbs-verify-job.ts +227 -0
- package/src/proxmox/pool.test.ts +42 -0
- package/src/proxmox/pool.ts +80 -0
- package/src/proxmox/qemu.ts +89 -0
- package/src/proxmox/replication-job-form.ts +89 -0
- package/src/proxmox/replication-job.ts +248 -0
- package/src/proxmox/resource.ts +257 -0
- package/src/proxmox/role.ts +161 -0
- package/src/proxmox/sdn-apply-read.test.ts +152 -0
- package/src/proxmox/sdn-apply-read.ts +189 -0
- package/src/proxmox/sdn-apply.ts +154 -0
- package/src/proxmox/sdn-subnet-form.ts +156 -0
- package/src/proxmox/sdn-subnet.ts +238 -0
- package/src/proxmox/sdn-vnet.ts +228 -0
- package/src/proxmox/sdn-zone.ts +246 -0
- package/src/proxmox/storage.ts +245 -0
- package/src/proxmox/user.ts +214 -0
- package/src/proxmox/values.test.ts +193 -0
- package/src/proxmox/values.ts +203 -0
- package/src/proxmox/zfs-pool-write.ts +130 -0
- package/src/proxmox/zfs-pool.ts +253 -0
- package/src/talos/credentials.ts +136 -0
- package/src/talos/index.ts +14 -0
- package/src/talos/kubeconfig.ts +170 -0
- package/src/talos/resource.ts +31 -0
- package/src/talos/talos-bootstrap.ts +109 -0
- package/src/talos/talos-cluster-health.ts +119 -0
- package/src/talos/talos-machine-config.ts +146 -0
- package/src/talos/talosctl.ts +94 -0
- package/src/talos/values.test.ts +80 -0
- package/src/talos/values.ts +70 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bootstrap solver — ⚠️ temporary, like the code it tests. Resolvable by single-group pairs and
|
|
3
|
+
* by elimination; ambiguous in both directions; unresolved when nothing isolates a name;
|
|
4
|
+
* contradicted when an entry declares a known name without its id; and never a pair inferred from
|
|
5
|
+
* position. Unknown names at lookup time are in cloudflare-permission-groups.test.ts, because that
|
|
6
|
+
* is where a lookup happens.
|
|
7
|
+
*/
|
|
8
|
+
import assert from 'node:assert/strict';
|
|
9
|
+
import { describe, it } from 'node:test';
|
|
10
|
+
import {
|
|
11
|
+
type Observation,
|
|
12
|
+
describeProblems,
|
|
13
|
+
observationsOf,
|
|
14
|
+
solveGroups,
|
|
15
|
+
} from './cloudflare-permission-groups-solve.ts';
|
|
16
|
+
import type { WirePolicy } from './cloudflare-policy.ts';
|
|
17
|
+
import type { ExpandedRole } from './cloudflare-roles-expand.ts';
|
|
18
|
+
|
|
19
|
+
const obs = (where: string, names: string[], ids: string[]): Observation => ({ ids, names, where });
|
|
20
|
+
|
|
21
|
+
describe('solveGroups', () => {
|
|
22
|
+
it('pairs single-group entries, then eliminates known pairs until nothing changes', () => {
|
|
23
|
+
const solved = solveGroups([
|
|
24
|
+
// Out of order on purpose: the chain below needs two passes to settle.
|
|
25
|
+
obs('platform/union', ['Email Routing', 'Queues Write'], ['id-queues', 'id-email']),
|
|
26
|
+
obs(
|
|
27
|
+
'platform/create[0]',
|
|
28
|
+
['DNS Write', 'Email Routing', 'DNS Read'],
|
|
29
|
+
['id-email', 'id-read', 'id-write'],
|
|
30
|
+
),
|
|
31
|
+
obs('dns/read', ['DNS Read'], ['id-read']),
|
|
32
|
+
obs('dns/edit', ['DNS Write'], ['id-write']),
|
|
33
|
+
]);
|
|
34
|
+
assert.deepEqual(Object.fromEntries(solved.groups), {
|
|
35
|
+
'DNS Read': 'id-read',
|
|
36
|
+
'DNS Write': 'id-write',
|
|
37
|
+
'Email Routing': 'id-email',
|
|
38
|
+
'Queues Write': 'id-queues',
|
|
39
|
+
});
|
|
40
|
+
assert.deepEqual([solved.ambiguous, solved.unresolved, solved.contradicted], [[], [], []]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// ⛔ The Python kept yaml order in a declared entry, and it would have paired these correctly.
|
|
44
|
+
// A hand-edited role would pair them wrongly with the same confidence, so position is never used.
|
|
45
|
+
it('never pairs by position', () => {
|
|
46
|
+
const solved = solveGroups([
|
|
47
|
+
obs('platform/run', ['AI Gateway Read', 'AI Gateway Run'], ['a', 'b']),
|
|
48
|
+
]);
|
|
49
|
+
assert.equal(solved.groups.size, 0);
|
|
50
|
+
assert.deepEqual(solved.unresolved, ['AI Gateway Read', 'AI Gateway Run']);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('reports a name bound to two ids as ambiguous and keeps it out of the map', () => {
|
|
54
|
+
const solved = solveGroups([
|
|
55
|
+
obs('dns/a-dns-read', ['DNS Read'], ['id-read']),
|
|
56
|
+
obs('dns/b-dns-read', ['DNS Read'], ['id-write']),
|
|
57
|
+
]);
|
|
58
|
+
assert.equal(solved.groups.has('DNS Read'), false);
|
|
59
|
+
assert.deepEqual(solved.ambiguous, [
|
|
60
|
+
{ bound: ['id-read', 'id-write'], kind: 'name', value: 'DNS Read' },
|
|
61
|
+
]);
|
|
62
|
+
assert.match(describeProblems(solved)[0] ?? '', /^ambiguous: name "DNS Read" is bound to ids/);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('reports an id bound to two names as ambiguous', () => {
|
|
66
|
+
const solved = solveGroups([
|
|
67
|
+
obs('dns/edit', ['DNS Write'], ['id-1']),
|
|
68
|
+
obs('dns/also-edit', ['Zone DNS Write'], ['id-1']),
|
|
69
|
+
]);
|
|
70
|
+
assert.equal(solved.groups.size, 0);
|
|
71
|
+
assert.deepEqual(solved.ambiguous, [
|
|
72
|
+
{ bound: ['DNS Write', 'Zone DNS Write'], kind: 'id', value: 'id-1' },
|
|
73
|
+
]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// ⚠️ A contradicting single-group role must surface even when its name was already learned.
|
|
77
|
+
it('records a contradicting single-group entry wherever it sits', () => {
|
|
78
|
+
const solved = solveGroups([
|
|
79
|
+
obs('dns/x', ['DNS Read'], ['id-read']),
|
|
80
|
+
obs('dns/union', ['DNS Read', 'Cache Purge'], ['id-read', 'id-purge']),
|
|
81
|
+
obs('dns/hand-edited', ['DNS Read'], ['id-other']),
|
|
82
|
+
]);
|
|
83
|
+
assert.equal(solved.ambiguous.length, 1);
|
|
84
|
+
assert.equal(solved.groups.has('DNS Read'), false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('binds nothing from an entry that contradicts what is known, and reports it', () => {
|
|
88
|
+
const solved = solveGroups([
|
|
89
|
+
obs('dns/read', ['A'], ['a']),
|
|
90
|
+
obs('platform/drifted', ['A', 'B'], ['x', 'y']),
|
|
91
|
+
]);
|
|
92
|
+
assert.deepEqual(solved.unresolved, ['B']);
|
|
93
|
+
assert.deepEqual(solved.contradicted, [{ id: 'a', name: 'A', where: 'platform/drifted' }]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// ⛔ THE BUG THIS CHECK EXISTS FOR. `workers-deploy` is the only single-group witness of its
|
|
97
|
+
// group on an account. Edited by hand, it teaches the map the edited id, and without this every
|
|
98
|
+
// union role carrying the real id would plan an update TO the edited one.
|
|
99
|
+
it('reports every entry that contradicts a hand-edited sole witness', () => {
|
|
100
|
+
const solved = solveGroups([
|
|
101
|
+
obs('platform/workers-deploy', ['Scripts Write'], ['id-edited']),
|
|
102
|
+
obs('platform/containers', ['Containers Write'], ['id-containers']),
|
|
103
|
+
obs(
|
|
104
|
+
'platform/workers-deploy-containers',
|
|
105
|
+
['Scripts Write', 'Containers Write'],
|
|
106
|
+
['id-containers', 'id-scripts'],
|
|
107
|
+
),
|
|
108
|
+
]);
|
|
109
|
+
assert.deepEqual(solved.contradicted, [
|
|
110
|
+
{ id: 'id-edited', name: 'Scripts Write', where: 'platform/workers-deploy-containers' },
|
|
111
|
+
]);
|
|
112
|
+
assert.match(
|
|
113
|
+
describeProblems(solved).at(-1) ?? '',
|
|
114
|
+
/^contradicted: platform\/workers-deploy-containers declares "Scripts Write" but lacks its id id-edited$/,
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('skips an entry whose name and id counts differ, without calling it a contradiction', () => {
|
|
119
|
+
const solved = solveGroups([
|
|
120
|
+
obs('dns/read', ['A'], ['a']),
|
|
121
|
+
obs('dns/short', ['A', 'B'], ['a']),
|
|
122
|
+
]);
|
|
123
|
+
assert.deepEqual([solved.unresolved, solved.contradicted], [['B'], []]);
|
|
124
|
+
assert.match(describeProblems(solved)[0] ?? '', /^unresolved: no live role isolates "B"/);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const ZONE = 'com.cloudflare.api.account.zone.z1';
|
|
129
|
+
const ACCOUNT = 'com.cloudflare.api.account.acct';
|
|
130
|
+
|
|
131
|
+
const declaredRole: ExpandedRole = {
|
|
132
|
+
account: 'acme',
|
|
133
|
+
accountId: 'acct',
|
|
134
|
+
catalog: { description: '', maxTtl: '1h', permissions: [], ttl: '5m', zone: 'example.com' },
|
|
135
|
+
description: ' (zone: example.com)',
|
|
136
|
+
maxTtl: '1h',
|
|
137
|
+
mount: 'cloudflare-acme-platform',
|
|
138
|
+
name: 'example-com-create',
|
|
139
|
+
policies: [
|
|
140
|
+
{ effect: 'allow', groupOrder: 'declared', groups: ['Routes'], resource: ZONE },
|
|
141
|
+
{ effect: 'allow', groupOrder: 'by-id', groups: ['Scripts', 'Queues'], resource: ACCOUNT },
|
|
142
|
+
],
|
|
143
|
+
surface: 'platform',
|
|
144
|
+
ttl: '5m',
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
describe('observationsOf', () => {
|
|
148
|
+
it('pairs declared and live entries by resource, whatever order live stored them in', () => {
|
|
149
|
+
const live = new Map<string, readonly WirePolicy[]>([
|
|
150
|
+
[
|
|
151
|
+
'cloudflare-acme-platform/example-com-create',
|
|
152
|
+
[
|
|
153
|
+
{ effect: 'allow', groupIds: ['q', 's'], resources: { [ACCOUNT]: '*' } },
|
|
154
|
+
{ effect: 'allow', groupIds: ['r'], resources: { [ZONE]: '*' } },
|
|
155
|
+
],
|
|
156
|
+
],
|
|
157
|
+
]);
|
|
158
|
+
assert.deepEqual(observationsOf([declaredRole], live), [
|
|
159
|
+
{ ids: ['r'], names: ['Routes'], where: 'cloudflare-acme-platform/example-com-create[0]' },
|
|
160
|
+
{
|
|
161
|
+
ids: ['q', 's'],
|
|
162
|
+
names: ['Scripts', 'Queues'],
|
|
163
|
+
where: 'cloudflare-acme-platform/example-com-create[1]',
|
|
164
|
+
},
|
|
165
|
+
]);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('observes nothing for a role that is not live', () => {
|
|
169
|
+
assert.deepEqual(observationsOf([declaredRole], new Map()), []);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠️ TEMPORARY — DELETE WITH `permissionGroupsFromLiveRoles` (cloudflare-permission-groups.ts) ONCE
|
|
3
|
+
* `GET <mount>/permission-groups` IS DEPLOYED ON THE CLOUDFLARE ENGINE.
|
|
4
|
+
*
|
|
5
|
+
* The pure half of the bootstrap: recover permission-group name → ID from roles whose names are
|
|
6
|
+
* declared (roles.yaml) and whose IDs are live (the engine).
|
|
7
|
+
*
|
|
8
|
+
* ★ WHY IT EXISTS AT ALL. apply-roles.py resolved names with
|
|
9
|
+
* `GET /accounts/<id>/tokens/permission_groups` (cfhf.py:147-148) under a Cloudflare parent
|
|
10
|
+
* credential, which agents may not use. The engine is gaining an endpoint that answers under its
|
|
11
|
+
* own credential; until it ships, the only other witness is the roles apply-roles.py already
|
|
12
|
+
* wrote, each pairing a list of names with a list of IDs.
|
|
13
|
+
*
|
|
14
|
+
* ⛔ NEVER A GUESS.
|
|
15
|
+
* 1. Every entry with exactly one name and one ID binds that pair.
|
|
16
|
+
* 2. Then, until nothing changes: an entry left with exactly ONE name and ONE ID not yet known
|
|
17
|
+
* binds those two.
|
|
18
|
+
* 3. Once settled, every entry is checked against the map: one that declares a known name without
|
|
19
|
+
* carrying that name's ID CONTRADICTS it.
|
|
20
|
+
* POSITIONS ARE NEVER USED, even where the Python kept yaml order — a hand-edited role would
|
|
21
|
+
* otherwise teach the map a wrong pair in silence. A name bound to two IDs, or an ID bound to two
|
|
22
|
+
* names, is AMBIGUOUS; a name nothing isolates is UNRESOLVED; and a contradiction is exactly
|
|
23
|
+
* that. Each is an error for the whole account, not a best effort.
|
|
24
|
+
*
|
|
25
|
+
* ⛔ THE CONTRADICTION CHECK IS THE ONE THAT BITES, AND IT WAS FOUND BY A TEST, NOT BY DESIGN. When a
|
|
26
|
+
* name has ONE single-group witness and that role was edited by hand, step 1 learns the edited ID,
|
|
27
|
+
* and the multi-group roles carrying the real one merely fail to eliminate. Unchecked, diff would
|
|
28
|
+
* then plan every one of those roles as an update TO THE EDITED ID — a hand edit propagated by the
|
|
29
|
+
* tool meant to catch it. Which side is wrong is unknowable here; only Cloudflare knows.
|
|
30
|
+
* ⚠️ THE PRICE: SOURCE (b) CANNOT PLAN A GROUP ADDED TO, OR SWAPPED IN, AN EXISTING ROLE. That edit
|
|
31
|
+
* looks exactly like the hand edit above — declared names disagreeing with live IDs — so it fails
|
|
32
|
+
* the account. New roles and removed groups still plan. (b) is for ADOPTION; changes wait for (a).
|
|
33
|
+
*
|
|
34
|
+
* ⛔ IT KEYS BY NAME AND THE ENGINE SOURCE KEYS BY (NAME, SCOPE) — DELIBERATELY UNCHANGED, 2026-09-15.
|
|
35
|
+
* A live role is a list of ids with no scopes, so a name is all this can key on; seven names the
|
|
36
|
+
* engine lists TWICE (once account-scoped, once zone-scoped) are one entry here, holding whichever
|
|
37
|
+
* id the estate happens to carry. cloudflare-policy.ts `atEveryScope` is where that map is widened
|
|
38
|
+
* to the scoped key the resolver now uses, and it is the honest shape of this blind spot: this file
|
|
39
|
+
* is the OFFLINE PARITY PROOF against a production export, and rewriting it to a key it cannot
|
|
40
|
+
* observe would make it agree with the engine by construction instead of by measurement.
|
|
41
|
+
*
|
|
42
|
+
* ⚠️ WHAT IT CANNOT CATCH. The names come from roles.yaml itself, so a group misnamed CONSISTENTLY
|
|
43
|
+
* — every use of `DNS Read` spelled `DNS Reed` — pairs the wrong name with the right ID and
|
|
44
|
+
* resolves cleanly, where Cloudflare would have refused it. cloudflare/parity.ts cross-checks the
|
|
45
|
+
* catalog documents apply-roles.py wrote, which hold names that DID resolve against Cloudflare at
|
|
46
|
+
* the last apply, for exactly that reason. The engine endpoint has no such blind spot.
|
|
47
|
+
*/
|
|
48
|
+
import type { WirePolicy } from './cloudflare-policy.ts';
|
|
49
|
+
import type { ExpandedRole } from './cloudflare-roles-expand.ts';
|
|
50
|
+
|
|
51
|
+
export interface Observation {
|
|
52
|
+
/** `<mount>/<role>[<entry>]`, for messages. */
|
|
53
|
+
readonly where: string;
|
|
54
|
+
readonly names: readonly string[];
|
|
55
|
+
readonly ids: readonly string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface Conflict {
|
|
59
|
+
/** `name` bound to several IDs, or `id` bound to several names. */
|
|
60
|
+
readonly kind: 'id' | 'name';
|
|
61
|
+
readonly value: string;
|
|
62
|
+
readonly bound: readonly string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** An entry declaring `name`, whose ID the map knows as `id`, without carrying `id`. */
|
|
66
|
+
export interface Contradiction {
|
|
67
|
+
readonly where: string;
|
|
68
|
+
readonly name: string;
|
|
69
|
+
readonly id: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface Solved {
|
|
73
|
+
readonly groups: ReadonlyMap<string, string>;
|
|
74
|
+
readonly ambiguous: readonly Conflict[];
|
|
75
|
+
readonly unresolved: readonly string[];
|
|
76
|
+
readonly contradicted: readonly Contradiction[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* One observation per declared entry whose resource matches exactly one live entry of that role.
|
|
81
|
+
*
|
|
82
|
+
* ★ PAIRED BY RESOURCE, NOT BY POSITION. A zone role's account groups sit on the account resource
|
|
83
|
+
* and nowhere else, whatever order the entries were stored in. An entry that shares its resource
|
|
84
|
+
* with a sibling, or finds no single live match, observes nothing — the diff reports it instead.
|
|
85
|
+
*/
|
|
86
|
+
export const observationsOf = (
|
|
87
|
+
declared: readonly ExpandedRole[],
|
|
88
|
+
live: ReadonlyMap<string, readonly WirePolicy[]>,
|
|
89
|
+
): Observation[] =>
|
|
90
|
+
declared.flatMap((role) => {
|
|
91
|
+
const entries = live.get(`${role.mount}/${role.name}`);
|
|
92
|
+
if (entries === undefined) return [];
|
|
93
|
+
return role.policies.flatMap((policy, i) => {
|
|
94
|
+
const twins = role.policies.filter((other) => other.resource === policy.resource);
|
|
95
|
+
const matching = entries.filter(
|
|
96
|
+
(entry) => Object.keys(entry.resources).length === 1 && policy.resource in entry.resources,
|
|
97
|
+
);
|
|
98
|
+
const [only] = matching;
|
|
99
|
+
if (twins.length !== 1 || matching.length !== 1 || only === undefined) return [];
|
|
100
|
+
return [
|
|
101
|
+
{
|
|
102
|
+
ids: only.groupIds,
|
|
103
|
+
names: policy.groups,
|
|
104
|
+
where: `${role.mount}/${role.name}[${String(i)}]`,
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const unique = (items: readonly string[]) => [...new Set(items)];
|
|
111
|
+
|
|
112
|
+
export const solveGroups = (observations: readonly Observation[]): Solved => {
|
|
113
|
+
const idsOf = new Map<string, Set<string>>();
|
|
114
|
+
const namesOf = new Map<string, Set<string>>();
|
|
115
|
+
const bind = (name: string, id: string) => {
|
|
116
|
+
const ids = idsOf.get(name) ?? new Set<string>();
|
|
117
|
+
const fresh = !ids.has(id);
|
|
118
|
+
idsOf.set(name, ids.add(id));
|
|
119
|
+
namesOf.set(id, (namesOf.get(id) ?? new Set<string>()).add(name));
|
|
120
|
+
return fresh;
|
|
121
|
+
};
|
|
122
|
+
/** Known only while the name has one ID and that ID has only this name. */
|
|
123
|
+
const known = (name: string) => {
|
|
124
|
+
const ids = idsOf.get(name);
|
|
125
|
+
const [id] = ids ?? [];
|
|
126
|
+
return ids?.size === 1 && id !== undefined && namesOf.get(id)?.size === 1 ? id : undefined;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const shaped = observations.map((o) => ({ ids: unique(o.ids), names: unique(o.names) }));
|
|
130
|
+
// 1. Every single-group entry binds, unconditionally — so a contradicting one is RECORDED as a
|
|
131
|
+
// conflict rather than skipped because its name happened to be learned first.
|
|
132
|
+
for (const { ids, names } of shaped) {
|
|
133
|
+
const [name] = names;
|
|
134
|
+
const [id] = ids;
|
|
135
|
+
if (names.length === 1 && ids.length === 1 && name !== undefined && id !== undefined) {
|
|
136
|
+
bind(name, id);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// 2. Elimination. ⚠️ A count mismatch proves nothing about which name is which ID, so it is skipped.
|
|
140
|
+
for (let changed = true; changed;) {
|
|
141
|
+
changed = false;
|
|
142
|
+
for (const { ids, names } of shaped) {
|
|
143
|
+
if (names.length !== ids.length) continue;
|
|
144
|
+
const knownIds = new Set(names.map(known));
|
|
145
|
+
const openNames = names.filter((name) => known(name) === undefined);
|
|
146
|
+
const openIds = ids.filter((id) => !knownIds.has(id));
|
|
147
|
+
const [name] = openNames;
|
|
148
|
+
const [id] = openIds;
|
|
149
|
+
if (
|
|
150
|
+
openNames.length === 1 &&
|
|
151
|
+
openIds.length === 1 &&
|
|
152
|
+
name !== undefined &&
|
|
153
|
+
id !== undefined
|
|
154
|
+
) {
|
|
155
|
+
changed = bind(name, id) || changed;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// 3. Contradictions — including entries step 2 skipped for a count mismatch.
|
|
160
|
+
const contradicted: Contradiction[] = [];
|
|
161
|
+
for (const o of observations) {
|
|
162
|
+
for (const name of unique(o.names)) {
|
|
163
|
+
const id = known(name);
|
|
164
|
+
if (id !== undefined && !o.ids.includes(id)) contradicted.push({ id, name, where: o.where });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const groups = new Map<string, string>();
|
|
169
|
+
const ambiguous: Conflict[] = [];
|
|
170
|
+
const unresolved: string[] = [];
|
|
171
|
+
for (const name of unique(observations.flatMap((o) => o.names)).sort()) {
|
|
172
|
+
const ids = idsOf.get(name);
|
|
173
|
+
const id = known(name);
|
|
174
|
+
if (ids === undefined) unresolved.push(name);
|
|
175
|
+
else if (ids.size > 1) ambiguous.push({ bound: [...ids].sort(), kind: 'name', value: name });
|
|
176
|
+
else if (id !== undefined) groups.set(name, id);
|
|
177
|
+
}
|
|
178
|
+
for (const [id, names] of [...namesOf].sort(([a], [b]) => (a < b ? -1 : 1))) {
|
|
179
|
+
if (names.size > 1) ambiguous.push({ bound: [...names].sort(), kind: 'id', value: id });
|
|
180
|
+
}
|
|
181
|
+
return { ambiguous, contradicted, groups, unresolved };
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/** Every conflict, unresolved name and contradiction, one line each. */
|
|
185
|
+
export const describeProblems = (solved: Solved): string[] => [
|
|
186
|
+
...solved.ambiguous.map((c) =>
|
|
187
|
+
c.kind === 'name'
|
|
188
|
+
? `ambiguous: name ${JSON.stringify(c.value)} is bound to ids ${c.bound.join(', ')}`
|
|
189
|
+
: `ambiguous: id ${c.value} is bound to names ${c.bound.map((n) => JSON.stringify(n)).join(', ')}`,
|
|
190
|
+
),
|
|
191
|
+
...solved.unresolved.map(
|
|
192
|
+
(name) => `unresolved: no live role isolates ${JSON.stringify(name)} to one id`,
|
|
193
|
+
),
|
|
194
|
+
...solved.contradicted.map(
|
|
195
|
+
(c) => `contradicted: ${c.where} declares ${JSON.stringify(c.name)} but lacks its id ${c.id}`,
|
|
196
|
+
),
|
|
197
|
+
];
|