@homeflare/alchemy 0.39.0 → 0.41.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/README.md +10 -8
- package/dist/caddy/index.js.map +2 -2
- package/dist/ceph/auth-entity.d.ts +34 -0
- package/dist/ceph/auth-entity.d.ts.map +1 -0
- package/dist/ceph/ceph-argv-config.d.ts +10 -0
- package/dist/ceph/ceph-argv-config.d.ts.map +1 -0
- package/dist/ceph/ceph-argv.d.ts +19 -0
- package/dist/ceph/ceph-argv.d.ts.map +1 -0
- package/dist/ceph/ceph-auth-form.d.ts +54 -0
- package/dist/ceph/ceph-auth-form.d.ts.map +1 -0
- package/dist/ceph/ceph-auth-parse.d.ts +56 -0
- package/dist/ceph/ceph-auth-parse.d.ts.map +1 -0
- package/dist/ceph/ceph-auth-reconcile.d.ts +35 -0
- package/dist/ceph/ceph-auth-reconcile.d.ts.map +1 -0
- package/dist/ceph/ceph-config-options.d.ts +23 -0
- package/dist/ceph/ceph-config-options.d.ts.map +1 -0
- package/dist/ceph/ceph-key-leak-assert.d.ts +25 -0
- package/dist/ceph/ceph-key-leak-assert.d.ts.map +1 -0
- package/dist/ceph/ceph-transport.d.ts +37 -0
- package/dist/ceph/ceph-transport.d.ts.map +1 -0
- package/dist/ceph/fake-ceph-dial.d.ts +35 -0
- package/dist/ceph/fake-ceph-dial.d.ts.map +1 -0
- package/dist/ceph/index.d.ts +13 -0
- package/dist/ceph/index.d.ts.map +1 -0
- package/dist/ceph/index.js +1610 -0
- package/dist/ceph/index.js.map +38 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/launchd/fake-runner.d.ts.map +1 -1
- package/dist/launchd/index.d.ts +1 -1
- package/dist/launchd/index.d.ts.map +1 -1
- package/dist/launchd/index.js +7 -1
- package/dist/launchd/index.js.map +5 -5
- package/dist/launchd/local-runner.d.ts.map +1 -1
- package/dist/launchd/runner.d.ts +21 -0
- package/dist/launchd/runner.d.ts.map +1 -1
- package/dist/launchd/sudo-runner.d.ts.map +1 -1
- package/dist/linux/directory-lifecycle.d.ts.map +1 -1
- package/dist/linux/fake-linux-host.d.ts.map +1 -1
- package/dist/linux/index.d.ts +1 -1
- package/dist/linux/index.d.ts.map +1 -1
- package/dist/linux/index.js +16 -4
- package/dist/linux/index.js.map +7 -7
- package/dist/linux/ssh-runner.d.ts.map +1 -1
- package/dist/linux/sudo-runner.d.ts.map +1 -1
- package/dist/opnsense/alias-form.d.ts +14 -1
- package/dist/opnsense/alias-form.d.ts.map +1 -1
- package/dist/opnsense/alias.d.ts +1 -1
- package/dist/opnsense/alias.d.ts.map +1 -1
- package/dist/opnsense/group-form.d.ts +8 -2
- package/dist/opnsense/group-form.d.ts.map +1 -1
- package/dist/opnsense/group.d.ts +1 -1
- package/dist/opnsense/group.d.ts.map +1 -1
- package/dist/opnsense/index.js +15 -7
- package/dist/opnsense/index.js.map +7 -7
- package/dist/opnsense/wire.d.ts +42 -12
- package/dist/opnsense/wire.d.ts.map +1 -1
- package/dist/proxmox/index.js +62 -39
- package/dist/proxmox/index.js.map +8 -7
- package/dist/proxmox/qemu-volume.d.ts +12 -2
- package/dist/proxmox/qemu-volume.d.ts.map +1 -1
- package/dist/proxmox/storage-form.d.ts +28 -3
- package/dist/proxmox/storage-form.d.ts.map +1 -1
- package/dist/proxmox/storage-plugin-options.d.ts +2 -0
- package/dist/proxmox/storage-plugin-options.d.ts.map +1 -0
- package/dist/proxmox/storage-wire.d.ts +28 -1
- package/dist/proxmox/storage-wire.d.ts.map +1 -1
- package/dist/proxmox/storage.d.ts +6 -1
- package/dist/proxmox/storage.d.ts.map +1 -1
- package/dist/release/index.d.ts +1 -1
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js.map +2 -2
- package/dist/talos/credentials-write.d.ts +46 -0
- package/dist/talos/credentials-write.d.ts.map +1 -0
- package/dist/talos/credentials.d.ts +51 -9
- package/dist/talos/credentials.d.ts.map +1 -1
- package/dist/talos/fake-process.d.ts +56 -0
- package/dist/talos/fake-process.d.ts.map +1 -0
- package/dist/talos/index.d.ts +19 -4
- package/dist/talos/index.d.ts.map +1 -1
- package/dist/talos/index.js +357 -173
- package/dist/talos/index.js.map +16 -10
- package/dist/talos/kubeconfig-attrs.d.ts +4 -0
- package/dist/talos/kubeconfig-attrs.d.ts.map +1 -0
- package/dist/talos/kubeconfig.d.ts +41 -23
- package/dist/talos/kubeconfig.d.ts.map +1 -1
- package/dist/talos/machine-config-fixtures.d.ts +60 -0
- package/dist/talos/machine-config-fixtures.d.ts.map +1 -0
- package/dist/talos/machine-config-poll.d.ts +43 -0
- package/dist/talos/machine-config-poll.d.ts.map +1 -0
- package/dist/talos/machine-config-read.d.ts +49 -0
- package/dist/talos/machine-config-read.d.ts.map +1 -0
- package/dist/talos/resource.d.ts +18 -0
- package/dist/talos/resource.d.ts.map +1 -1
- package/dist/talos/talos-bootstrap-etcd.d.ts +38 -0
- package/dist/talos/talos-bootstrap-etcd.d.ts.map +1 -0
- package/dist/talos/talos-bootstrap.d.ts +87 -0
- package/dist/talos/talos-bootstrap.d.ts.map +1 -1
- package/dist/talos/talos-cluster-health.d.ts +43 -1
- package/dist/talos/talos-cluster-health.d.ts.map +1 -1
- package/dist/talos/talos-errors.d.ts +68 -0
- package/dist/talos/talos-errors.d.ts.map +1 -0
- package/dist/talos/talos-machine-config.d.ts +51 -15
- package/dist/talos/talos-machine-config.d.ts.map +1 -1
- package/dist/talos/talosctl.d.ts +7 -0
- package/dist/talos/talosctl.d.ts.map +1 -1
- package/dist/talos/values.d.ts +23 -3
- package/dist/talos/values.d.ts.map +1 -1
- package/dist/unifi/acl-rule-drift.d.ts +22 -0
- package/dist/unifi/acl-rule-drift.d.ts.map +1 -0
- package/dist/unifi/acl-rule-form.d.ts +90 -0
- package/dist/unifi/acl-rule-form.d.ts.map +1 -0
- package/dist/unifi/acl-rule-ordering-form.d.ts +49 -0
- package/dist/unifi/acl-rule-ordering-form.d.ts.map +1 -0
- package/dist/unifi/acl-rule-ordering.d.ts +54 -0
- package/dist/unifi/acl-rule-ordering.d.ts.map +1 -0
- package/dist/unifi/acl-rule.d.ts +49 -0
- package/dist/unifi/acl-rule.d.ts.map +1 -0
- package/dist/unifi/dns-policy-drift.d.ts +21 -0
- package/dist/unifi/dns-policy-drift.d.ts.map +1 -0
- package/dist/unifi/dns-policy-form.d.ts +97 -0
- package/dist/unifi/dns-policy-form.d.ts.map +1 -0
- package/dist/unifi/dns-policy.d.ts +55 -0
- package/dist/unifi/dns-policy.d.ts.map +1 -0
- package/dist/unifi/firewall-policy-drift.d.ts +26 -0
- package/dist/unifi/firewall-policy-drift.d.ts.map +1 -0
- package/dist/unifi/firewall-policy-form.d.ts +102 -0
- package/dist/unifi/firewall-policy-form.d.ts.map +1 -0
- package/dist/unifi/firewall-policy-ordering-form.d.ts +66 -0
- package/dist/unifi/firewall-policy-ordering-form.d.ts.map +1 -0
- package/dist/unifi/firewall-policy-ordering.d.ts +57 -0
- package/dist/unifi/firewall-policy-ordering.d.ts.map +1 -0
- package/dist/unifi/firewall-policy.d.ts +57 -0
- package/dist/unifi/firewall-policy.d.ts.map +1 -0
- package/dist/unifi/index.d.ts +7 -0
- package/dist/unifi/index.d.ts.map +1 -1
- package/dist/unifi/index.js +588 -3
- package/dist/unifi/index.js.map +24 -4
- package/dist/unifi/paginate.d.ts +0 -12
- package/dist/unifi/paginate.d.ts.map +1 -1
- package/dist/unifi/providers.d.ts +1 -1
- package/dist/unifi/providers.d.ts.map +1 -1
- package/dist/unifi/scan-source.d.ts +11 -0
- package/dist/unifi/scan-source.d.ts.map +1 -0
- package/dist/unifi/traffic-matching-list-drift.d.ts +22 -0
- package/dist/unifi/traffic-matching-list-drift.d.ts.map +1 -0
- package/dist/unifi/traffic-matching-list-form.d.ts +56 -0
- package/dist/unifi/traffic-matching-list-form.d.ts.map +1 -0
- package/dist/unifi/traffic-matching-list.d.ts +51 -0
- package/dist/unifi/traffic-matching-list.d.ts.map +1 -0
- package/dist/unifi/wifi-broadcast-drift.d.ts +22 -0
- package/dist/unifi/wifi-broadcast-drift.d.ts.map +1 -0
- package/dist/unifi/wifi-broadcast-form.d.ts +127 -0
- package/dist/unifi/wifi-broadcast-form.d.ts.map +1 -0
- package/dist/unifi/wifi-broadcast.d.ts +73 -0
- package/dist/unifi/wifi-broadcast.d.ts.map +1 -0
- package/docs/distilled-interim.md +7 -0
- package/docs/launchd.md +5 -3
- package/docs/provider-standard.md +5 -1
- package/docs/talos-argocd-dogfood.md +160 -0
- package/docs/unifi-acl-rule.md +122 -0
- package/docs/unifi-dns-policy.md +75 -0
- package/docs/unifi-firewall-policy.md +109 -0
- package/docs/unifi-traffic-matching-list.md +82 -0
- package/docs/unifi-wifi.md +120 -0
- package/docs/unifi.md +10 -5
- package/package.json +7 -3
- package/src/ceph/auth-entity.ts +74 -0
- package/src/ceph/ceph-argv-config.ts +78 -0
- package/src/ceph/ceph-argv.test.ts +226 -0
- package/src/ceph/ceph-argv.ts +189 -0
- package/src/ceph/ceph-auth-form.test.ts +103 -0
- package/src/ceph/ceph-auth-form.ts +107 -0
- package/src/ceph/ceph-auth-parse.test.ts +155 -0
- package/src/ceph/ceph-auth-parse.ts +121 -0
- package/src/ceph/ceph-auth-reconcile-secrecy.test.ts +82 -0
- package/src/ceph/ceph-auth-reconcile.test.ts +233 -0
- package/src/ceph/ceph-auth-reconcile.ts +119 -0
- package/src/ceph/ceph-config-options.test.ts +44 -0
- package/src/ceph/ceph-config-options.ts +36 -0
- package/src/ceph/ceph-key-leak-assert.ts +35 -0
- package/src/ceph/ceph-no-output-logging.test.ts +52 -0
- package/src/ceph/ceph-transport.test.ts +125 -0
- package/src/ceph/ceph-transport.ts +117 -0
- package/src/ceph/fake-ceph-dial.ts +55 -0
- package/src/ceph/index.ts +12 -0
- package/src/index.ts +1 -0
- package/src/launchd/fake-runner.ts +2 -0
- package/src/launchd/index.ts +8 -1
- package/src/launchd/local-runner.ts +16 -1
- package/src/launchd/runner.ts +22 -0
- package/src/launchd/sudo-runner.ts +3 -0
- package/src/linux/directory-lifecycle.ts +18 -3
- package/src/linux/directory-sudo.test.ts +59 -0
- package/src/linux/directory.test.ts +34 -8
- package/src/linux/fake-linux-host.ts +2 -0
- package/src/linux/index.ts +1 -0
- package/src/linux/ssh-runner.ts +4 -0
- package/src/linux/sudo-runner.ts +3 -0
- package/src/opnsense/alias-form.test.ts +24 -12
- package/src/opnsense/alias-form.ts +20 -7
- package/src/opnsense/alias.test.ts +13 -6
- package/src/opnsense/alias.ts +16 -10
- package/src/opnsense/group-form.test.ts +12 -8
- package/src/opnsense/group-form.ts +13 -4
- package/src/opnsense/group.test.ts +18 -7
- package/src/opnsense/group.ts +10 -3
- package/src/opnsense/wire.ts +57 -12
- package/src/proxmox/qemu-volume.test.ts +153 -4
- package/src/proxmox/qemu-volume.ts +71 -14
- package/src/proxmox/storage-form.ts +48 -8
- package/src/proxmox/storage-plugin-options.ts +45 -0
- package/src/proxmox/storage-update.test.ts +183 -0
- package/src/proxmox/storage-wire.ts +54 -7
- package/src/proxmox/storage.ts +18 -6
- package/src/release/index.ts +1 -0
- package/src/talos/credentials-write.test.ts +81 -0
- package/src/talos/credentials-write.ts +95 -0
- package/src/talos/credentials.test.ts +157 -0
- package/src/talos/credentials.ts +160 -90
- package/src/talos/fake-process.ts +94 -0
- package/src/talos/index.ts +39 -4
- package/src/talos/kubeconfig-attrs.ts +55 -0
- package/src/talos/kubeconfig.test.ts +256 -0
- package/src/talos/kubeconfig.ts +170 -90
- package/src/talos/machine-config-fixtures.ts +86 -0
- package/src/talos/machine-config-poll.test.ts +57 -0
- package/src/talos/machine-config-poll.ts +82 -0
- package/src/talos/machine-config-read.ts +118 -0
- package/src/talos/resource.ts +19 -0
- package/src/talos/secrets-gate.test.ts +63 -0
- package/src/talos/talos-bootstrap-etcd.ts +71 -0
- package/src/talos/talos-bootstrap.test.ts +218 -0
- package/src/talos/talos-bootstrap.ts +151 -45
- package/src/talos/talos-cluster-health.test.ts +143 -0
- package/src/talos/talos-cluster-health.ts +91 -28
- package/src/talos/talos-errors.ts +85 -0
- package/src/talos/talos-machine-config.test.ts +189 -0
- package/src/talos/talos-machine-config.ts +102 -88
- package/src/talos/talosctl.ts +8 -0
- package/src/talos/values.test.ts +34 -6
- package/src/talos/values.ts +41 -7
- package/src/unifi/acl-rule-drift.ts +57 -0
- package/src/unifi/acl-rule-form.ts +135 -0
- package/src/unifi/acl-rule-ordering-form.ts +79 -0
- package/src/unifi/acl-rule-ordering.test.ts +141 -0
- package/src/unifi/acl-rule-ordering.ts +70 -0
- package/src/unifi/acl-rule.test.ts +183 -0
- package/src/unifi/acl-rule.ts +61 -0
- package/src/unifi/discriminated-filter-decode.test.ts +134 -0
- package/src/unifi/dns-policy-drift.ts +48 -0
- package/src/unifi/dns-policy-form.ts +142 -0
- package/src/unifi/dns-policy.test.ts +154 -0
- package/src/unifi/dns-policy.ts +67 -0
- package/src/unifi/firewall-policy-drift.test.ts +151 -0
- package/src/unifi/firewall-policy-drift.ts +55 -0
- package/src/unifi/firewall-policy-filter-roundtrip.test.ts +143 -0
- package/src/unifi/firewall-policy-form.ts +146 -0
- package/src/unifi/firewall-policy-ordering-form.ts +111 -0
- package/src/unifi/firewall-policy-ordering.test.ts +185 -0
- package/src/unifi/firewall-policy-ordering.ts +87 -0
- package/src/unifi/firewall-policy.test.ts +183 -0
- package/src/unifi/firewall-policy.ts +73 -0
- package/src/unifi/index.ts +63 -0
- package/src/unifi/paginate.test.ts +24 -0
- package/src/unifi/paginate.ts +15 -3
- package/src/unifi/providers.ts +19 -1
- package/src/unifi/scan-source.ts +64 -0
- package/src/unifi/traffic-matching-list-drift.ts +40 -0
- package/src/unifi/traffic-matching-list-form.ts +77 -0
- package/src/unifi/traffic-matching-list.test.ts +202 -0
- package/src/unifi/traffic-matching-list.ts +78 -0
- package/src/unifi/wifi-broadcast-details-call.test.ts +92 -0
- package/src/unifi/wifi-broadcast-drift.ts +69 -0
- package/src/unifi/wifi-broadcast-form.ts +211 -0
- package/src/unifi/wifi-broadcast-secrets.test.ts +145 -0
- package/src/unifi/wifi-broadcast.test.ts +155 -0
- package/src/unifi/wifi-broadcast.ts +90 -0
- package/src/unifi/write-op-reference.test.ts +8 -54
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Unifi.TrafficMatchingList` — one traffic matching list (IPv4/IPv6/port match entries) on a UniFi
|
|
3
|
+
* Network site, READ-ONLY (`policy.ts`).
|
|
4
|
+
*
|
|
5
|
+
* ★ T11 GATE, CHECKED 2026-09-26: `Traffic Matching Lists`' 5 operations are byte-identical between
|
|
6
|
+
* 10.4.57 and 10.6.97, full raw operation objects; the 19-schema closure reachable from all 5
|
|
7
|
+
* (`$ref` + `discriminator.mapping`) shares zero names with the 14 schemas that DID change
|
|
8
|
+
* elsewhere in the document. Corroborated independently by the A3 changeset's own broader
|
|
9
|
+
* 25-operation/139-schema re-check (`.changeset/unifi-network-a3-typed-discriminator-filters.md`).
|
|
10
|
+
* Full breakdown: `docs/unifi-traffic-matching-list.md`.
|
|
11
|
+
*/
|
|
12
|
+
import { Resource } from 'alchemy';
|
|
13
|
+
import * as Provider from 'alchemy/Provider';
|
|
14
|
+
import * as trafficMatchingLists from '@distilled.cloud/unifi-network/traffic_matching_lists';
|
|
15
|
+
import * as Effect from 'effect/Effect';
|
|
16
|
+
import { type TrafficMatchingListAttributes, type TrafficMatchingListProps } from './traffic-matching-list-form.ts';
|
|
17
|
+
import type { UnifiRequirements, UnifiSpec } from './resource.ts';
|
|
18
|
+
export type { TrafficMatchingListAttributes, TrafficMatchingListProps, } from './traffic-matching-list-form.ts';
|
|
19
|
+
export { declareTrafficMatchingList } from './traffic-matching-list-form.ts';
|
|
20
|
+
export { driftOf } from './traffic-matching-list-drift.ts';
|
|
21
|
+
export interface UnifiTrafficMatchingList extends Resource<'Unifi.TrafficMatchingList', TrafficMatchingListProps, TrafficMatchingListAttributes, never, UnifiRequirements> {
|
|
22
|
+
}
|
|
23
|
+
export declare const UnifiTrafficMatchingList: import("alchemy").ResourceClass<UnifiTrafficMatchingList>;
|
|
24
|
+
/** `trafficMatchingList('office-subnets', props)` — `adopt(true)` piped on by default (H5); see
|
|
25
|
+
* `network.ts`. */
|
|
26
|
+
export declare const trafficMatchingList: (id: string, props: TrafficMatchingListProps) => Effect.Effect<UnifiTrafficMatchingList, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
27
|
+
/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */
|
|
28
|
+
export declare const spec: UnifiSpec<TrafficMatchingListProps, trafficMatchingLists.TrafficMatchingList, TrafficMatchingListAttributes, trafficMatchingLists.GetTrafficMatchingListError>;
|
|
29
|
+
export declare const handlers: {
|
|
30
|
+
list: () => Effect.Effect<never[], never, never>;
|
|
31
|
+
read: (args: {
|
|
32
|
+
olds: TrafficMatchingListProps;
|
|
33
|
+
output: TrafficMatchingListAttributes | undefined;
|
|
34
|
+
}) => Effect.Effect<TrafficMatchingListAttributes | undefined, trafficMatchingLists.UnifiNetworkOpError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
35
|
+
diff: (args: {
|
|
36
|
+
news: import("alchemy").Input<TrafficMatchingListProps>;
|
|
37
|
+
output: TrafficMatchingListAttributes | undefined;
|
|
38
|
+
}) => Effect.Effect<{
|
|
39
|
+
readonly action: 'update';
|
|
40
|
+
} | {
|
|
41
|
+
readonly action: 'noop';
|
|
42
|
+
} | undefined, trafficMatchingLists.UnifiNetworkOpError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
43
|
+
reconcile: (args: {
|
|
44
|
+
news: TrafficMatchingListProps;
|
|
45
|
+
}) => Effect.Effect<TrafficMatchingListAttributes, import("./policy.ts").UnifiWriteRefused | trafficMatchingLists.UnifiNetworkOpError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
46
|
+
delete: (args: {
|
|
47
|
+
olds: TrafficMatchingListProps;
|
|
48
|
+
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
49
|
+
};
|
|
50
|
+
export declare const UnifiTrafficMatchingListProvider: () => import("effect/Layer").Layer<Provider.Provider<UnifiTrafficMatchingList>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
51
|
+
//# sourceMappingURL=traffic-matching-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traffic-matching-list.d.ts","sourceRoot":"","sources":["../../src/unifi/traffic-matching-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,uDAAuD,CAAC;AAC9F,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAE9B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlE,YAAY,EACV,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAE3D,MAAM,WAAW,wBAAyB,SAAQ,QAAQ,CACxD,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,EAC7B,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,wBAAwB,2DAGpC,CAAC;AAEF;oBACoB;AACpB,eAAO,MAAM,mBAAmB,OAAQ,MAAM,SAAS,wBAAwB,yGACxB,CAAC;AAExD,iGAAiG;AACjG,eAAO,MAAM,IAAI,EAAE,SAAS,CAC1B,wBAAwB,EACxB,oBAAoB,CAAC,mBAAmB,EACxC,6BAA6B,EAC7B,oBAAoB,CAAC,2BAA2B,CAcjD,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;CAAsB,CAAC;AAE5C,eAAO,MAAM,gCAAgC,8IAI1C,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Unifi.WifiBroadcast`'s `matches` AND `driftOf` (B6) — built on the ONE field list below, the
|
|
3
|
+
* same `fieldDrift`-is-the-only-comparison shape `dns-policy-drift.ts`'s header explains
|
|
4
|
+
* (MEDIUM-4): `matches(attrs, props) === (driftOf(live, props).length === 0)` is true BY
|
|
5
|
+
* CONSTRUCTION, never two hand-kept-in-sync comparisons.
|
|
6
|
+
*
|
|
7
|
+
* ★ THREE FIELDS REUSE `wifi-broadcast-form.ts`'s OWN NORMALIZERS ON THE `declared` SIDE TOO, not
|
|
8
|
+
* just the `live` side — `firewall-zone-form.ts`'s `networkIds` gives the same reason: comparing
|
|
9
|
+
* a freshly-normalized live value against an un-normalized declared value would report spurious
|
|
10
|
+
* drift the moment a committed declaration's array happens to be in a different (still valid)
|
|
11
|
+
* order than what `declareWifiBroadcast` would itself produce today.
|
|
12
|
+
*/
|
|
13
|
+
import type * as wifiBroadcasts from '@distilled.cloud/unifi-network/wifi_broadcasts';
|
|
14
|
+
import { type WifiBroadcastAttributes, type WifiBroadcastProps } from './wifi-broadcast-form.ts';
|
|
15
|
+
export declare const matches: (attributes: WifiBroadcastAttributes, props: WifiBroadcastProps) => boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Per-field live-vs-declared drift straight from one live read — the task's own API
|
|
18
|
+
* (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive
|
|
19
|
+
* `attributesOf` itself just to call this.
|
|
20
|
+
*/
|
|
21
|
+
export declare const driftOf: (live: wifiBroadcasts.WifiBroadcastOverview, props: WifiBroadcastProps) => import("./drift.ts").FieldDrift[];
|
|
22
|
+
//# sourceMappingURL=wifi-broadcast-drift.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wifi-broadcast-drift.d.ts","sourceRoot":"","sources":["../../src/unifi/wifi-broadcast-drift.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,KAAK,cAAc,MAAM,gDAAgD,CAAC;AAEtF,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAOxB,MAAM,0BAA0B,CAAC;AAoClC,eAAO,MAAM,OAAO,eAAgB,uBAAuB,SAAS,kBAAkB,KAAG,OAC7C,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,OAAO,SAAU,cAAc,CAAC,qBAAqB,SAAS,kBAAkB,sCAC/C,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Unifi.WifiBroadcast`'s wire shape — the declarable subset of `WifiBroadcastOverview`, and how
|
|
3
|
+
* a live read becomes both plan attributes and a rendered declaration.
|
|
4
|
+
*
|
|
5
|
+
* ⛔ T23, THE WHOLE REASON THIS SHAPE DIFFERS FROM EVERY OTHER FAMILY HERE. Every other family's
|
|
6
|
+
* `*-form.ts` (`network-form.ts`, `dns-policy-form.ts`, …) types its props against the vendor's
|
|
7
|
+
* CREATE/UPDATE request — the fullest declarable shape, even though this stack never calls it.
|
|
8
|
+
* `WifiBroadcastProps`/`WifiBroadcastAttributes` do NOT: `securityConfiguration` is typed
|
|
9
|
+
* `wifiBroadcasts.WifiSecurityConfigurationOverview` — the PAGE OVERVIEW's own type, `{type,
|
|
10
|
+
* presharedKeyNetworkIds}` — never `WifiSecurityConfigurationDetailObject` (the
|
|
11
|
+
* `getWifiBroadcastDetails`/create/update shape, which carries `passphrase` and
|
|
12
|
+
* `radiusConfiguration`). There is no DECLARED field on `WifiSecurityConfigurationOverview` for
|
|
13
|
+
* `attributesOf`/`declareWifiBroadcast` below to name a passphrase into. See `wifi-broadcast.ts`'s
|
|
14
|
+
* header for why `fetchLive` can only ever produce a `WifiBroadcastOverview` in the first place
|
|
15
|
+
* (no get-by-id call exists for it).
|
|
16
|
+
*
|
|
17
|
+
* ⛔ THE TYPE SYSTEM ALONE DOES NOT ENFORCE THIS (red team, IMPORTANT-1, 2026-09-26). `@distilled
|
|
18
|
+
* .cloud/core`'s wire decode does not strip a key a schema doesn't declare (this file's next
|
|
19
|
+
* header block); the TS types above describe what a CORRECTLY-shaped response looks like, not
|
|
20
|
+
* what the decoder actually returns at runtime, so a stray key nested inside `network`,
|
|
21
|
+
* `hotspotConfiguration`, `broadcastingDeviceFilter`, or an element of `presharedKeyNetworkIds`
|
|
22
|
+
* would ride along through a naive `{...live}`/pass-through copy exactly like the top-level
|
|
23
|
+
* `securityConfiguration` object does. `normalizeRef`/`normalizeHotspot`/`normalizeDeviceFilter`
|
|
24
|
+
* below rebuild EVERY nested object field-by-field for the same reason
|
|
25
|
+
* `normalizeSecurityConfiguration` does — enumeration, not the type annotation, is what actually
|
|
26
|
+
* stops it. `wifi-broadcast-secrets.test.ts` plants a sentinel inside each nested shape
|
|
27
|
+
* (including one `presharedKeyNetworkIds` element) to prove it, not just the top level.
|
|
28
|
+
*
|
|
29
|
+
* ⚠️ EVERY OTHER FIELD REUSES THE SDK'S OWN OVERVIEW TYPES DIRECTLY (`WifiNetworkReference`,
|
|
30
|
+
* `BroadcastingDeviceFilter`, `IntegrationWifiHotspotConfigurationOverviewDto`) rather than
|
|
31
|
+
* redeclaring them — same reasoning `network-form.ts` gives for reusing `NetworkDHCPGuarding`/
|
|
32
|
+
* `NetworkIPv6Configuration` verbatim: one shape to keep in sync with the SDK's own regen.
|
|
33
|
+
*
|
|
34
|
+
* ⛔ THREE FIELDS ARE SETS, NOT SEQUENCES, UNCONFIRMED AGAINST A LIVE CONSOLE (same caveat
|
|
35
|
+
* `network-form.ts`'s header gives for its own three): `broadcastingDeviceFilter.deviceIds`/
|
|
36
|
+
* `.deviceTagIds` and `securityConfiguration.presharedKeyNetworkIds` are membership sets by the
|
|
37
|
+
* vendor description's own wording ("Defines the custom scope of devices…"), not ordered
|
|
38
|
+
* sequences. `normalizeDeviceFilter`/`normalizeSecurityConfiguration`/`normalizeFrequencies`
|
|
39
|
+
* neutralize the same "spurious update, refused by a read-only reconcile" failure mode
|
|
40
|
+
* `network-form.ts`'s header describes, exactly like `firewall-zone-form.ts`'s `networkIds`.
|
|
41
|
+
*/
|
|
42
|
+
import type * as wifiBroadcasts from '@distilled.cloud/unifi-network/wifi_broadcasts';
|
|
43
|
+
/**
|
|
44
|
+
* Rebuilds a reference field-by-field — never returns the object as received. IMPORTANT-1: this
|
|
45
|
+
* runs on EVERY reference this file handles (`network`, and each `presharedKeyNetworkIds`
|
|
46
|
+
* element) precisely because a reference is exactly the shape (`{network: WifiNetworkReference,
|
|
47
|
+
* passphrase}`) the details call uses to carry a PPSK passphrase (`IntegrationWifiPresharedKeyDto`,
|
|
48
|
+
* SDK `wifi_broadcasts.ts`); it is the most plausible place for a stray passphrase-shaped key to
|
|
49
|
+
* appear on the wire if the vendor ever leaked one onto the overview response.
|
|
50
|
+
*/
|
|
51
|
+
export declare const normalizeRef: (ref: wifiBroadcasts.WifiNetworkReference | undefined) => wifiBroadcasts.WifiNetworkReference | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Rebuilds the hotspot configuration field-by-field. `IntegrationWifiHotspotConfigurationOverviewDto`
|
|
54
|
+
* declares only `type` (SDK `wifi_broadcasts.ts:743-753`) — a PASSPOINT/RADIUS secret would live on
|
|
55
|
+
* a wire key this type doesn't name, exactly the IMPORTANT-1 risk `normalizeRef` guards against.
|
|
56
|
+
*/
|
|
57
|
+
export declare const normalizeHotspot: (value: wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined) => wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* ★ EXPORTED: `wifi-broadcast-drift.ts` reuses these exact normalizers so `matches`/`driftOf` can
|
|
60
|
+
* never quietly disagree about what counts as a change (MEDIUM-4, `network-drift.ts`'s header).
|
|
61
|
+
*
|
|
62
|
+
* ⛔ NO `{...value}` SPREAD (red team, IMPORTANT-1) — `BroadcastingDeviceFilter` declares exactly
|
|
63
|
+
* `type`/`deviceIds`/`deviceTagIds` (SDK `wifi_broadcasts.ts:125-129`); enumerating exactly those
|
|
64
|
+
* three is what stops a stray wire key here, the same reason `normalizeSecurityConfiguration`
|
|
65
|
+
* (below) never spreads its input.
|
|
66
|
+
*/
|
|
67
|
+
export declare const normalizeDeviceFilter: (value: wifiBroadcasts.BroadcastingDeviceFilter | undefined) => wifiBroadcasts.BroadcastingDeviceFilter | undefined;
|
|
68
|
+
export declare const normalizeFrequencies: (value: readonly number[] | undefined) => number[] | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* ⛔ NO `{...value}` SPREAD, UNLIKE EVERY OTHER NORMALIZER IN THIS DIRECTORY (`network-form.ts`'s
|
|
71
|
+
* `normalizeDhcpGuarding`/`normalizeIpv6Configuration` both spread). This is the one field in
|
|
72
|
+
* the whole `Unifi.*` tree with a spec-confirmed passphrase risk (T23): nothing in
|
|
73
|
+
* `@distilled.cloud/core`'s wire key-mapping strips an object key the schema doesn't declare —
|
|
74
|
+
* it only renames declared ones — so a stray extra key on the WIRE `securityConfiguration`
|
|
75
|
+
* object (a vendor bug, or a future console version) survives `getWifiBroadcastPage`'s own
|
|
76
|
+
* decode untyped, and `{...value}` would carry it straight through into `Attributes`/`Props`/the
|
|
77
|
+
* rendered declaration. Enumerating exactly the two fields
|
|
78
|
+
* `WifiSecurityConfigurationOverview` declares is what actually stops that — proved by
|
|
79
|
+
* `wifi-broadcast-secrets.test.ts`'s sentinel test, which injects exactly this kind of stray key.
|
|
80
|
+
*/
|
|
81
|
+
export declare const normalizeSecurityConfiguration: (value: wifiBroadcasts.WifiSecurityConfigurationOverview) => wifiBroadcasts.WifiSecurityConfigurationOverview;
|
|
82
|
+
/**
|
|
83
|
+
* ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` ON PURPOSE — `exactOptionalPropertyTypes`; see
|
|
84
|
+
* `network-form.ts`'s own header. `attributesOf`/`declareWifiBroadcast` echo a live optional
|
|
85
|
+
* field structurally rather than defaulting it, so the target type must admit an explicit
|
|
86
|
+
* `undefined`.
|
|
87
|
+
*/
|
|
88
|
+
export interface WifiBroadcastProps {
|
|
89
|
+
siteId: string;
|
|
90
|
+
wifiBroadcastId: string;
|
|
91
|
+
name: string;
|
|
92
|
+
enabled: boolean;
|
|
93
|
+
type: string;
|
|
94
|
+
network?: wifiBroadcasts.WifiNetworkReference | undefined;
|
|
95
|
+
broadcastingDeviceFilter?: wifiBroadcasts.BroadcastingDeviceFilter | undefined;
|
|
96
|
+
broadcastingFrequenciesGHz?: number[] | undefined;
|
|
97
|
+
hotspotConfiguration?: wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined;
|
|
98
|
+
/** ⛔ T23: the PAGE OVERVIEW's security shape ONLY — see this file's header. */
|
|
99
|
+
securityConfiguration: wifiBroadcasts.WifiSecurityConfigurationOverview;
|
|
100
|
+
}
|
|
101
|
+
export interface WifiBroadcastAttributes {
|
|
102
|
+
siteId: string;
|
|
103
|
+
wifiBroadcastId: string;
|
|
104
|
+
name: string;
|
|
105
|
+
enabled: boolean;
|
|
106
|
+
type: string;
|
|
107
|
+
network: wifiBroadcasts.WifiNetworkReference | undefined;
|
|
108
|
+
broadcastingDeviceFilter: wifiBroadcasts.BroadcastingDeviceFilter | undefined;
|
|
109
|
+
broadcastingFrequenciesGHz: number[] | undefined;
|
|
110
|
+
hotspotConfiguration: wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined;
|
|
111
|
+
securityConfiguration: wifiBroadcasts.WifiSecurityConfigurationOverview;
|
|
112
|
+
/** `metadata.origin` — server-derived, never declared; same split as every other family. */
|
|
113
|
+
metadataOrigin: string;
|
|
114
|
+
}
|
|
115
|
+
export declare const attributesOf: (live: wifiBroadcasts.WifiBroadcastOverview, props: WifiBroadcastProps) => WifiBroadcastAttributes;
|
|
116
|
+
/**
|
|
117
|
+
* The declaration renderer (task spec: "given one live object, return the props a declaration
|
|
118
|
+
* needs so its plan is noop"). A later import script feeds `getWifiBroadcastPage`'s own per-row
|
|
119
|
+
* response straight in and writes the result into `alchemy.run.ts` as `wifiBroadcast('<id>',
|
|
120
|
+
* declareWifiBroadcast(live, siteId))` — no field is invented or defaulted here beyond what
|
|
121
|
+
* `attributesOf` already reports, so `matches(attributesOf(live, props), declareWifiBroadcast(live,
|
|
122
|
+
* siteId))` is `true` by construction (`wifi-broadcast.test.ts` proves it) for any live object
|
|
123
|
+
* this SDK can decode. Builds ONLY from fields `WifiBroadcastOverview` has — never a passphrase,
|
|
124
|
+
* which that type cannot express (see this file's header).
|
|
125
|
+
*/
|
|
126
|
+
export declare const declareWifiBroadcast: (live: wifiBroadcasts.WifiBroadcastOverview, siteId: string) => WifiBroadcastProps;
|
|
127
|
+
//# sourceMappingURL=wifi-broadcast-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wifi-broadcast-form.d.ts","sourceRoot":"","sources":["../../src/unifi/wifi-broadcast-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,OAAO,KAAK,KAAK,cAAc,MAAM,gDAAgD,CAAC;AAWtF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,QAClB,cAAc,CAAC,oBAAoB,GAAG,SAAS,KACnD,cAAc,CAAC,oBAAoB,GAAG,SAGqD,CAAC;AAa/F;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,UACpB,cAAc,CAAC,8CAA8C,GAAG,SAAS,KAC/E,cAAc,CAAC,8CAA8C,GAAG,SACX,CAAC;AAEzD;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,UACzB,cAAc,CAAC,wBAAwB,GAAG,SAAS,KACzD,cAAc,CAAC,wBAAwB,GAAG,SAStC,CAAC;AAER,eAAO,MAAM,oBAAoB,UAAW,SAAS,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,EAAE,GAAG,SAC/B,CAAC;AAEzD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,8BAA8B,UAClC,cAAc,CAAC,iCAAiC,KACtD,cAAc,CAAC,iCAKhB,CAAC;AAEH;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,cAAc,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAC1D,wBAAwB,CAAC,EAAE,cAAc,CAAC,wBAAwB,GAAG,SAAS,CAAC;IAC/E,0BAA0B,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAClD,oBAAoB,CAAC,EAAE,cAAc,CAAC,8CAA8C,GAAG,SAAS,CAAC;IACjG,+EAA+E;IAC/E,qBAAqB,EAAE,cAAc,CAAC,iCAAiC,CAAC;CACzE;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,cAAc,CAAC,oBAAoB,GAAG,SAAS,CAAC;IACzD,wBAAwB,EAAE,cAAc,CAAC,wBAAwB,GAAG,SAAS,CAAC;IAC9E,0BAA0B,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACjD,oBAAoB,EAAE,cAAc,CAAC,8CAA8C,GAAG,SAAS,CAAC;IAChG,qBAAqB,EAAE,cAAc,CAAC,iCAAiC,CAAC;IACxE,4FAA4F;IAC5F,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,YAAY,SACjB,cAAc,CAAC,qBAAqB,SACnC,kBAAkB,KACxB,uBAYD,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,SACzB,cAAc,CAAC,qBAAqB,UAClC,MAAM,KACb,kBAWD,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Unifi.WifiBroadcast` — one WiFi network (SSID) broadcast on a UniFi Network site, READ-ONLY
|
|
3
|
+
* (`policy.ts`).
|
|
4
|
+
*
|
|
5
|
+
* ⛔ T23, THE REASON THIS FAMILY NEVER CALLS `getWifiBroadcastDetails`. That single-object GET
|
|
6
|
+
* returns `WifiBroadcastDetails`, whose `securityConfiguration`
|
|
7
|
+
* (`WifiSecurityConfigurationDetailObject`) carries the WPA passphrase AND every PPSK entry's
|
|
8
|
+
* passphrase — the pinned spec has NO `writeOnly` flag on either (verified: 4 `passphrase`
|
|
9
|
+
* props, 0 `writeOnly`; A2 already redacts them to `Redacted.Redacted<string>` at the SDK layer,
|
|
10
|
+
* `errors-and-secrets.test.ts`). This family goes further and never fetches that shape AT ALL:
|
|
11
|
+
* the only call below is `getWifiBroadcastPage`, which answers `WifiBroadcastOverview` instead —
|
|
12
|
+
* whose own `securityConfiguration` (`WifiSecurityConfigurationOverview`) is `{type,
|
|
13
|
+
* presharedKeyNetworkIds}`. No passphrase field exists in that type, so `WifiBroadcastProps`/
|
|
14
|
+
* `Attributes` (`wifi-broadcast-form.ts`) cannot carry one even by a future mistake.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ NO GET-BY-ID CALL EXISTS FOR THE OVERVIEW SHAPE — unlike every other family in this
|
|
17
|
+
* directory (`getNetworkDetails`, `getFirewallZone`, `getDnsPolicy`, `getAclRule`),
|
|
18
|
+
* `wifi_broadcasts.ts` only has `getWifiBroadcastPage` (list) and `getWifiBroadcastDetails`
|
|
19
|
+
* (single, passphrase-bearing — forbidden above). `fetchLive` below walks every page with B0b's
|
|
20
|
+
* `pageAll` and finds the one row matching `wifiBroadcastId` — the first real resource-level
|
|
21
|
+
* consumer of that pager (previously exercised only by `paginate.test.ts` in isolation).
|
|
22
|
+
*
|
|
23
|
+
* ⚠️ THE `NotFound` CATCH BELOW IS FOR THE WHOLE WALK, NOT JUST THE FIRST PAGE CALL (red team,
|
|
24
|
+
* MINOR-4, 2026-09-26 — corrected from an earlier claim that it only applied to the first call).
|
|
25
|
+
* `pageAll` makes its sequential page calls inside one Effect pipeline, and `catchTag` here wraps
|
|
26
|
+
* that entire pipeline; a 404 on ANY page of the walk — not only the first — folds to `undefined`
|
|
27
|
+
* and discards whatever rows the walk had already collected. In practice this means "the site
|
|
28
|
+
* vanished partway through listing its broadcasts" is indistinguishable from "the site was never
|
|
29
|
+
* there" and from "the target id just isn't among the collected rows" — none of these three cases
|
|
30
|
+
* has a separate vendor signal, and no other family's `fetchLive` distinguishes its own analogous
|
|
31
|
+
* cases either. Read-only reporting treats all three the same conservative way (absent, not an
|
|
32
|
+
* error); a write path would need to tell them apart.
|
|
33
|
+
*/
|
|
34
|
+
import { Resource } from 'alchemy';
|
|
35
|
+
import * as Provider from 'alchemy/Provider';
|
|
36
|
+
import * as wifiBroadcasts from '@distilled.cloud/unifi-network/wifi_broadcasts';
|
|
37
|
+
import * as Effect from 'effect/Effect';
|
|
38
|
+
import { type WifiBroadcastAttributes, type WifiBroadcastProps } from './wifi-broadcast-form.ts';
|
|
39
|
+
import { type UnifiPaginationInconsistent } from './paginate.ts';
|
|
40
|
+
import type { UnifiRequirements, UnifiSpec } from './resource.ts';
|
|
41
|
+
export type { WifiBroadcastAttributes, WifiBroadcastProps } from './wifi-broadcast-form.ts';
|
|
42
|
+
export { declareWifiBroadcast } from './wifi-broadcast-form.ts';
|
|
43
|
+
export { driftOf } from './wifi-broadcast-drift.ts';
|
|
44
|
+
export interface UnifiWifiBroadcast extends Resource<'Unifi.WifiBroadcast', WifiBroadcastProps, WifiBroadcastAttributes, never, UnifiRequirements> {
|
|
45
|
+
}
|
|
46
|
+
export declare const UnifiWifiBroadcast: import("alchemy").ResourceClass<UnifiWifiBroadcast>;
|
|
47
|
+
/** `wifiBroadcast('guest', props)` — `adopt(true)` piped on by default (H5); see `network.ts`. */
|
|
48
|
+
export declare const wifiBroadcast: (id: string, props: WifiBroadcastProps) => Effect.Effect<UnifiWifiBroadcast, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
49
|
+
/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */
|
|
50
|
+
export declare const spec: UnifiSpec<WifiBroadcastProps, wifiBroadcasts.WifiBroadcastOverview, WifiBroadcastAttributes, wifiBroadcasts.GetWifiBroadcastPageError | UnifiPaginationInconsistent>;
|
|
51
|
+
export declare const handlers: {
|
|
52
|
+
list: () => Effect.Effect<never[], never, never>;
|
|
53
|
+
read: (args: {
|
|
54
|
+
olds: WifiBroadcastProps;
|
|
55
|
+
output: WifiBroadcastAttributes | undefined;
|
|
56
|
+
}) => Effect.Effect<WifiBroadcastAttributes | undefined, UnifiPaginationInconsistent | wifiBroadcasts.UnifiNetworkOpError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
57
|
+
diff: (args: {
|
|
58
|
+
news: import("alchemy").Input<WifiBroadcastProps>;
|
|
59
|
+
output: WifiBroadcastAttributes | undefined;
|
|
60
|
+
}) => Effect.Effect<{
|
|
61
|
+
readonly action: 'update';
|
|
62
|
+
} | {
|
|
63
|
+
readonly action: 'noop';
|
|
64
|
+
} | undefined, UnifiPaginationInconsistent | wifiBroadcasts.UnifiNetworkOpError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
65
|
+
reconcile: (args: {
|
|
66
|
+
news: WifiBroadcastProps;
|
|
67
|
+
}) => Effect.Effect<WifiBroadcastAttributes, UnifiPaginationInconsistent | import("./policy.ts").UnifiWriteRefused | wifiBroadcasts.UnifiNetworkOpError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
68
|
+
delete: (args: {
|
|
69
|
+
olds: WifiBroadcastProps;
|
|
70
|
+
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
71
|
+
};
|
|
72
|
+
export declare const UnifiWifiBroadcastProvider: () => import("effect/Layer").Layer<Provider.Provider<UnifiWifiBroadcast>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
73
|
+
//# sourceMappingURL=wifi-broadcast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wifi-broadcast.d.ts","sourceRoot":"","sources":["../../src/unifi/wifi-broadcast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,cAAc,MAAM,gDAAgD,CAAC;AACjF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAExB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,2BAA2B,EAAW,MAAM,eAAe,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlE,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEpD,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAE7B,CAAC;AAEH,kGAAkG;AAClG,eAAO,MAAM,aAAa,OAAQ,MAAM,SAAS,kBAAkB,mGAClB,CAAC;AAElD,iGAAiG;AACjG,eAAO,MAAM,IAAI,EAAE,SAAS,CAC1B,kBAAkB,EAClB,cAAc,CAAC,qBAAqB,EACpC,uBAAuB,EACvB,cAAc,CAAC,yBAAyB,GAAG,2BAA2B,CAWvE,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;CAAsB,CAAC;AAE5C,eAAO,MAAM,0BAA0B,wIACwD,CAAC"}
|
|
@@ -161,3 +161,10 @@ itself holds everywhere else.
|
|
|
161
161
|
published — `bun install` would fail to resolve it.
|
|
162
162
|
- ⛔ Don't hand-fix a bug inside `packages/distilled-<vendor>/src/`. Fix it
|
|
163
163
|
upstream (the distilled clone), regenerate, copy again.
|
|
164
|
+
- ⛔ Don't invent `src/` by hand so a `@distilled.cloud/<vendor>` name can
|
|
165
|
+
publish. `src/` is generated, never hand-written — from a real vendor
|
|
166
|
+
schema (steps 1–2) when one exists, or from the blocked-`SpecRepos`-entry
|
|
167
|
+
plus hand-authored-model route (step 1's Caddy paragraph above) when it
|
|
168
|
+
doesn't. A missing schema is not a licence to stub the client; it is not a
|
|
169
|
+
reason to skip the vendor either. Talos + Argo CD dogfood vendors that skip
|
|
170
|
+
both routes: [talos-argocd-dogfood.md](./talos-argocd-dogfood.md).
|
package/docs/launchd.md
CHANGED
|
@@ -174,6 +174,8 @@ job is declared: [launchd-ports.md](./launchd-ports.md).
|
|
|
174
174
|
|
|
175
175
|
`HostRunner` (see `src/launchd/runner.ts`) is `exec(argv)` (never through a shell), `readFile`,
|
|
176
176
|
`stat` (lstat), `writeFileAtomic`, `removeFile`, `lookupUser`, `lookupGroup`, `sleep`, plus
|
|
177
|
-
`
|
|
178
|
-
`
|
|
179
|
-
|
|
177
|
+
`platform` (`'darwin' | 'linux'`, the target host's own OS — declared, never inferred from
|
|
178
|
+
`process.platform`), `privileged` and `effectiveUid()`. `localRunner()` implements it with
|
|
179
|
+
`node:fs` and `node:child_process`; `sudoRunner()` wraps it and elevates the allowlist. Tests use
|
|
180
|
+
an in-memory fake, so nothing in the test suite runs `launchctl` or `sudo`, or writes outside a
|
|
181
|
+
temp directory.
|
|
@@ -30,7 +30,11 @@ already ships is a finding, even when it works.
|
|
|
30
30
|
with its typed error unions and Credentials layer. A second SDK or a hand-rolled client
|
|
31
31
|
is a finding. On 2026-09-22 npm had distilled packages for Forgejo, GitHub, Kubernetes
|
|
32
32
|
and Docker, among others, and none for Proxmox, OpenBao, Caddy, Talos, NetBox or UniFi
|
|
33
|
-
(S23).
|
|
33
|
+
(S23). A missing package is not a licence to hand-write one — build it by
|
|
34
|
+
the generate+copy (or blocked-`SpecRepos`) route in
|
|
35
|
+
[distilled-interim.md](./distilled-interim.md); see
|
|
36
|
+
[talos-argocd-dogfood.md](./talos-argocd-dogfood.md) for this stack's
|
|
37
|
+
per-vendor calls.
|
|
34
38
|
3. **Does Alchemy ship the helper?** Hashing (`alchemy/Util/sha256`), polling
|
|
35
39
|
(`alchemy/Util/poll`), atomic writes (`alchemy/Util/AtomicFile`), equality
|
|
36
40
|
(`deepEqual` in `alchemy/Diff`), names (`alchemy/PhysicalName`), tags (`alchemy/Tags`),
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Talos + Argo CD dogfood — Distilled and Alchemy path
|
|
2
|
+
|
|
3
|
+
Status: decision record — fixes the per-vendor Distilled/Alchemy path for the
|
|
4
|
+
Talos + Argo CD dogfood stack; retire when the stack ships and each row is
|
|
5
|
+
proven live.
|
|
6
|
+
Verified: 2026-09-27 (LAND pass: CRD ownership, Talos K-A3, Cilium CNI
|
|
7
|
+
ordering, topology and the interim-package rule checked against merged PRs
|
|
8
|
+
262/295/307/311; see each section below).
|
|
9
|
+
|
|
10
|
+
Opened 2026-09-24 after
|
|
11
|
+
[PR 242](https://github.com/taslabs-net/homeflare-kit/pull/242) closed without
|
|
12
|
+
merge.
|
|
13
|
+
|
|
14
|
+
Placeholder stack: Talos Kubernetes on Proxmox VE, **Argo CD owns GitOps**.
|
|
15
|
+
This page is the Distilled / Alchemy path for each vendor — not a stack file,
|
|
16
|
+
and not a licence to invent SDKs.
|
|
17
|
+
|
|
18
|
+
## Why there are no interim packages
|
|
19
|
+
|
|
20
|
+
[distilled-interim.md](./distilled-interim.md) is generate+copy from a real
|
|
21
|
+
vendor schema (distilled clone Steps 1–8, then copy `src/` into
|
|
22
|
+
`packages/distilled-<vendor>`). ⛔ **`src/` is copied, never hand-written.**
|
|
23
|
+
A stub invented so a `@distilled.cloud/<vendor>` name can publish is the
|
|
24
|
+
failure #242 shipped: Alchemy would `catchTag` errors from a client nobody
|
|
25
|
+
can regenerate. ⛔ Do not add npm aliases for packages that do not exist.
|
|
26
|
+
|
|
27
|
+
## Per-vendor decision
|
|
28
|
+
|
|
29
|
+
| vendor | decision |
|
|
30
|
+
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| Headlamp | Skip Distilled / Alchemy vendor family — Argo Application / Helm |
|
|
32
|
+
| Traefik | No Distilled SDK (no official OpenAPI; do not invent) |
|
|
33
|
+
| Cilium | No hand-written interim — install vehicle is D-S2 (open); CRDs via upstream Alchemy `Kubernetes.Manifest` |
|
|
34
|
+
| CNPG | `postgresql.cnpg.io/v1` CRDs — upstream Alchemy `Kubernetes.Manifest` (S1), not `@distilled.cloud/kubernetes` (built-in API groups only) |
|
|
35
|
+
| Valkey | operator CRDs — upstream Alchemy `Kubernetes.Manifest` (S1); Sentinel is RESP, not HTTP |
|
|
36
|
+
| Argo CD | `Application`/`AppProject`/`ApplicationSet` — upstream Alchemy `Kubernetes.Manifest` (decision 49); `Repository`/`RepoCreds`/`Cluster` — `Argocd.Repository`/`RepoCreds`/`Cluster` (kit, built over `@distilled.cloud/argocd`, [PR 262](https://github.com/taslabs-net/homeflare-kit/pull/262), merged) |
|
|
37
|
+
| Talos CLI | No Distilled Talos SDK — stays on `talosctl`, reworked under K-A3 ([PR 307](https://github.com/taslabs-net/homeflare-kit/pull/307)/[311](https://github.com/taslabs-net/homeflare-kit/pull/311): `insecure` prop dropped, `Bootstrap.peers` added) |
|
|
38
|
+
|
|
39
|
+
Headlamp's `/config` and cluster proxy are not a vendor control plane.
|
|
40
|
+
|
|
41
|
+
Traefik declarative config is Kubernetes CRDs (`IngressRoute`, `Middleware`, …
|
|
42
|
+
— `traefik.io`, not a built-in group) / Helm values (experimental flags as
|
|
43
|
+
Helm props). CRDs go through upstream Alchemy `Kubernetes.Manifest` (S1), the
|
|
44
|
+
same as every other vendor's CRDs on this page, never
|
|
45
|
+
`@distilled.cloud/kubernetes`. The dashboard `GET /api/…` is read-only
|
|
46
|
+
runtime — optional later, not the dogfood install path.
|
|
47
|
+
|
|
48
|
+
Cilium's BGP and network-policy objects are genuine CRDs, not a built-in
|
|
49
|
+
Kubernetes API group, so they go through upstream Alchemy's
|
|
50
|
+
`Kubernetes.Manifest` (S1) — never `@distilled.cloud/kubernetes`: its 24
|
|
51
|
+
service files (checked at the kit's pin, `c2a78002`) cover only built-in
|
|
52
|
+
apiserver groups, and its `apiextensions` service defines CRD _schemas_, not
|
|
53
|
+
CRD _instances_. The install vehicle itself — inline manifests in the seeded
|
|
54
|
+
Talos machine config vs. a Helm step between `Kubeconfig` and `ClusterHealth`
|
|
55
|
+
— is **D-S2, still open**
|
|
56
|
+
([first-boot doc](https://github.com/taslabs-net/homeflare-kit/blob/main/docs/plans/2026-09-26-talos-stack-first-boot.md#tim-must-decide),
|
|
57
|
+
"Tim must decide"; inline manifests is the doc's recommendation, not yet
|
|
58
|
+
accepted). Either vehicle must set `cluster.network.cni.name: none` and
|
|
59
|
+
`cluster.proxy.disabled: true` (kube-proxy replacement) in the seeded config,
|
|
60
|
+
because Argo CD cannot install Cilium — Argo CD itself needs pod networking
|
|
61
|
+
first. Official swagger exists (`cilium/cilium` `api/v1/openapi.yaml`) but
|
|
62
|
+
the default transport is the unix socket `/var/run/cilium/cilium.sock`;
|
|
63
|
+
generate+copy a Distilled SDK later only if node-local agent health over
|
|
64
|
+
that socket is needed.
|
|
65
|
+
|
|
66
|
+
Argo CD's own storage — `Repository`, `RepoCreds` and `Cluster` — is a `Secret`
|
|
67
|
+
labeled with Argo CD's own convention, not a published Kubernetes API type, so
|
|
68
|
+
the kit's `Argocd.Repository`/`RepoCreds`/`Cluster` (built over
|
|
69
|
+
`@distilled.cloud/argocd`, already on npm) are the correct owner
|
|
70
|
+
([PR 262](https://github.com/taslabs-net/homeflare-kit/pull/262), merged). The
|
|
71
|
+
objects Argo CD reconciles — `Application`, `AppProject`, `ApplicationSet` —
|
|
72
|
+
are genuine `argoproj.io/v1alpha1` CRDs, so upstream Alchemy's
|
|
73
|
+
`Kubernetes.Manifest` owns those (decision 49, "upstream wins"), not a house
|
|
74
|
+
wrapper — see [argocd.md](./argocd.md) and
|
|
75
|
+
[argocd-kubernetes.md](./argocd-kubernetes.md). No Distilled Talos API in this
|
|
76
|
+
path.
|
|
77
|
+
|
|
78
|
+
## Gates
|
|
79
|
+
|
|
80
|
+
- ⛔ Do not publish an interim Distilled package until generate+copy from a
|
|
81
|
+
real vendor schema ([distilled-interim.md](./distilled-interim.md) steps 1–2).
|
|
82
|
+
- ⛔ Do not invent a Traefik Distilled SDK without an official schema.
|
|
83
|
+
- `Argocd.Repository`/`RepoCreds`/`Cluster` were built ahead of a live
|
|
84
|
+
Talos-on-PVE cluster ([PR 262](https://github.com/taslabs-net/homeflare-kit/pull/262),
|
|
85
|
+
merged 2026-09-24; tested against `fake-argocd.ts`, a fake in-memory Argo CD
|
|
86
|
+
API — the HTTP client and Distilled protocol underneath it are real, only
|
|
87
|
+
the Argo CD server is faked; never exercised against a real instance) —
|
|
88
|
+
Tim's build-ahead-of-need call. Declaring and applying
|
|
89
|
+
`Application`/`AppProject`/`ApplicationSet` objects in a real stack still
|
|
90
|
+
waits on that cluster existing.
|
|
91
|
+
- Prefer upstream Alchemy Kubernetes resources when they exist (S1).
|
|
92
|
+
|
|
93
|
+
## Placeholder variables
|
|
94
|
+
|
|
95
|
+
Names a consuming stack substitutes. Origins use RFC 2606 `*.example.com`.
|
|
96
|
+
Addresses use RFC 5737 documentation ranges. Secret _values_ never appear —
|
|
97
|
+
only env-var NAMES (S25). Nothing here is applied.
|
|
98
|
+
|
|
99
|
+
### Topology — 3 control plane, no workers today
|
|
100
|
+
|
|
101
|
+
`homeflare-proxmox`'s `src/talos-shape.ts` declares exactly 3 control-plane
|
|
102
|
+
VMs (`n2`/`n3`/`n4`, vmids 10000–10002) and no worker pool. The
|
|
103
|
+
`TALOS_WORKER_*` rows are placeholders for if/when a worker pool is added;
|
|
104
|
+
nothing consumes them yet.
|
|
105
|
+
|
|
106
|
+
| variable | placeholder |
|
|
107
|
+
| ------------------------------------- | ------------------------------- |
|
|
108
|
+
| `TALOS_CLUSTER_NAME` | `dogfood` |
|
|
109
|
+
| `TALOS_CP_NODES` | `cp-a`, `cp-b`, `cp-c` |
|
|
110
|
+
| `TALOS_WORKER_NODES` (none today) | `wk-a`, `wk-b`, `wk-c` |
|
|
111
|
+
| `TALOS_CP_ADDRESSES` | `192.0.2.10/24` … `.12` |
|
|
112
|
+
| `TALOS_WORKER_ADDRESSES` (none today) | `192.0.2.20/24` … `.22` |
|
|
113
|
+
| `TALOS_CONTROL_PLANE_ENDPOINT` | `https://k8s.example.com:6443` |
|
|
114
|
+
| `TALOS_VERSION` | `v1.13.x` (never measured live) |
|
|
115
|
+
| `STORAGE_CLASS` | `STORAGE_CLASS` |
|
|
116
|
+
| `STORAGE_SIZE` | `STORAGE_SIZE` |
|
|
117
|
+
|
|
118
|
+
### Cilium BGP
|
|
119
|
+
|
|
120
|
+
| variable | placeholder |
|
|
121
|
+
| ------------------------- | ----------------- |
|
|
122
|
+
| `CILIUM_BGP_CLUSTER_ASN` | `64512` (private) |
|
|
123
|
+
| `CILIUM_BGP_PEER_ASN` | `64513` |
|
|
124
|
+
| `CILIUM_BGP_PEER_ADDRESS` | `198.51.100.1` |
|
|
125
|
+
| `CILIUM_BGP_VIP_CIDR` | `203.0.113.0/24` |
|
|
126
|
+
|
|
127
|
+
### Traefik
|
|
128
|
+
|
|
129
|
+
| variable | placeholder |
|
|
130
|
+
| ---------------------------- | ----------------------------------------------- |
|
|
131
|
+
| `TRAEFIK_ENTRYPOINTS` | `web`, `websecure` |
|
|
132
|
+
| `TRAEFIK_EXPERIMENTAL_FLAGS` | Helm props, e.g. `kubernetesGateway`, `knative` |
|
|
133
|
+
| `TRAEFIK_NAMESPACE` | `traefik` |
|
|
134
|
+
|
|
135
|
+
### Installs Argo owns
|
|
136
|
+
|
|
137
|
+
| variable | placeholder |
|
|
138
|
+
| --------------------------- | ---------------------------- |
|
|
139
|
+
| `HEADLAMP_INGRESS_HOST` | `headlamp.example.com` |
|
|
140
|
+
| `CNPG_CLUSTER_NAME` | `dogfood-pg` |
|
|
141
|
+
| `CNPG_INSTANCES` | `3` |
|
|
142
|
+
| `CNPG_SUPERUSER_SECRET_ENV` | `CNPG_SUPERUSER_PASSWORD` |
|
|
143
|
+
| `VALKEY_SENTINEL_NAME` | `dogfood-valkey` |
|
|
144
|
+
| `VALKEY_PASSWORD_ENV` | `VALKEY_PASSWORD` |
|
|
145
|
+
| `ARGOCD_SERVER` | `https://argocd.example.com` |
|
|
146
|
+
| `ARGOCD_TOKEN_ENV` | `ARGOCD_TOKEN` |
|
|
147
|
+
|
|
148
|
+
## Non-goals
|
|
149
|
+
|
|
150
|
+
- No Flux. Argo CD is the GitOps controller.
|
|
151
|
+
- No new Distilled Talos SDK from this page — `@homeflare/alchemy/talos`
|
|
152
|
+
already reworked under K-A3 ([PR 307](https://github.com/taslabs-net/homeflare-kit/pull/307),
|
|
153
|
+
[PR 311](https://github.com/taslabs-net/homeflare-kit/pull/311)); it stays on
|
|
154
|
+
`talosctl`.
|
|
155
|
+
- No new Alchemy provider code from this page itself — `Argocd.*` (PR 262)
|
|
156
|
+
and upstream `Kubernetes.Manifest` already cover this stack's objects.
|
|
157
|
+
Declaring and applying them against a real cluster still waits on
|
|
158
|
+
Talos-on-PVE existing (see Gates).
|
|
159
|
+
- No `packages/distilled-traefik`, `packages/distilled-cilium`, or other
|
|
160
|
+
interim Distilled packages for this stack.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# UniFi ACL Rules — `Unifi.AclRule` / `Unifi.AclRuleOrdering`
|
|
2
|
+
|
|
3
|
+
⛔ **READ-ONLY, BY TIM'S RULE (2026-09-24).** Same posture as `docs/unifi.md`'s `Unifi.Network`/
|
|
4
|
+
`Unifi.FirewallZone`: `reconcile`/`delete` both fail with a typed `UnifiWriteRefused`, and the
|
|
5
|
+
`GetOnlyHttpClient` wire guard (`resource.ts`) refuses any non-`GET` request before it reaches the
|
|
6
|
+
transport. Neither family here calls `createAclRule`, `updateAclRule`, `deleteAclRule` or
|
|
7
|
+
`updateAclRuleOrdering` anywhere — `write-op-reference.test.ts` proves it statically.
|
|
8
|
+
|
|
9
|
+
## Two resources, because rule order is not a rule's own property (T5)
|
|
10
|
+
|
|
11
|
+
`Unifi.AclRule` is one user-defined ACL rule (`getAclRule`/`getAclRulePage`).
|
|
12
|
+
`Unifi.AclRuleOrdering` is the site's ONE ordered list of rule IDs (`getAclRuleOrdering`,
|
|
13
|
+
`GET /v1/sites/{siteId}/acl-rules/ordering`) — not one-per-rule and not one-per-zone-pair the way a
|
|
14
|
+
future `Unifi.FirewallPolicyOrdering` will be (`network.ts`'s header names that as a separate,
|
|
15
|
+
bigger PR), because ACL rule ordering is site-wide, not keyed by a source/destination zone pair.
|
|
16
|
+
|
|
17
|
+
⛔ **`orderedAclRuleIds` IS NEVER RUN THROUGH `sortedSet`.** Every other multi-value field in this
|
|
18
|
+
directory that gets normalized (`firewall-zone-form.ts`'s `networkIds`, this family's own
|
|
19
|
+
`enforcingDeviceFilter.deviceIds`/`protocolFilter` below) is a SET with no documented order.
|
|
20
|
+
`orderedAclRuleIds` is the opposite: its position IS the value — `ACLRule.index`'s own field
|
|
21
|
+
comment says "Lower index has higher priority," and the whole point of `updateAclRuleOrdering` is
|
|
22
|
+
to change that priority by reordering the list. `acl-rule-ordering-form.ts`'s `matches`/`driftOf`
|
|
23
|
+
compare it with plain `deepEqual`, which — per `drift.ts`'s own header — sorts object keys but
|
|
24
|
+
never array elements, so leaving this one field un-normalized is what makes the resource actually
|
|
25
|
+
represent order.
|
|
26
|
+
|
|
27
|
+
## `Unifi.AclRule`'s own fields
|
|
28
|
+
|
|
29
|
+
- **`sourceFilter`/`destinationFilter` stay opaque `unknown`**, on the SDK's own authority
|
|
30
|
+
(`access_control_acl_rules.ts`'s comment on both: "Shape varies by variant — widened by
|
|
31
|
+
scripts/convert.ts"). This is the same converter-flattening trap (T10) `network-form.ts` already
|
|
32
|
+
documents for `ipv4Configuration`; typing these per-discriminator (A3) is explicitly scoped OUT
|
|
33
|
+
of this PR. Compared with plain `deepEqual` — honest about not being decoded, not a guess at a
|
|
34
|
+
narrower shape. ⚠️ **Known gap until A3 (T15, red team, Minor finding 4)**: the vendor's own
|
|
35
|
+
filter variants (`IntegrationIpAclRuleNetworkEndpointFilterDto`'s `networkIds`,
|
|
36
|
+
`IntegrationIpAclRuleSubnetEndpointFilterDto`'s `ipAddressesOrSubnets`, both variants'
|
|
37
|
+
`portFilter`, `IntegrationMacAclRuleMacAddressEndpointFilterDto`'s `macAddresses`) are themselves
|
|
38
|
+
set-like arrays — `deepEqual` compares them order-sensitively, the same false-`update` risk
|
|
39
|
+
already fixed for `deviceIds`/`protocolFilter` below, just not fixable here without decoding the
|
|
40
|
+
filter first.
|
|
41
|
+
- **`enforcingDeviceFilter.deviceIds` and `protocolFilter` are SETS**, normalized with the same
|
|
42
|
+
`sortedSet` `firewall-zone-form.ts` uses for `networkIds`: neither field's own vendor description
|
|
43
|
+
("IDs of the Switch-capable devices used to enforce the ACL rule," "Protocols this ACL rule will
|
|
44
|
+
be applied to") indicates order, and leaving either un-normalized would let a console-side
|
|
45
|
+
reorder plan a spurious `update` this read-only family's `reconcile` then refuses forever.
|
|
46
|
+
- **`index` is attributes-only, never declared.** `CreateAclRuleRequest`'s own comment on the
|
|
47
|
+
field: "This property is deprecated and has no effect. Use the dedicated ACL rule reordering
|
|
48
|
+
endpoint." Reported so a reader can see a rule's current position, but never compared —
|
|
49
|
+
`Unifi.AclRuleOrdering` above is where that position is actually declared.
|
|
50
|
+
- **`metadata.origin` is attributes-only**, same split `firewall-zone-form.ts`'s `metadataOrigin`
|
|
51
|
+
uses — `UserDefinedOrDerivedEntityMetadata` is server-derived, no writable field accepts it.
|
|
52
|
+
|
|
53
|
+
## T11 gate: per-tag closure diff, 10.4.57 vs 10.6.97 (checked 2026-09-26)
|
|
54
|
+
|
|
55
|
+
Same method `spec-version-provenance.md` (distilled package) used for Networks/FirewallZones,
|
|
56
|
+
run here for `DNS Policies` (5 ops) and `Access Control (ACL Rules)` (7 ops) — 12 operations total,
|
|
57
|
+
covering both families in this PR plus `Unifi.DnsPolicy` (`docs/unifi-dns-policy.md`).
|
|
58
|
+
|
|
59
|
+
- **Pin**: `~/.cache/homeflare/schemas/unifi/network_v10.4.57_openapi.json`, re-verified against
|
|
60
|
+
its own `SHA256SUMS` today (`3773947b…c9df59d`).
|
|
61
|
+
- **Mirror**: `raw.githubusercontent.com/beezly/unifi-apis/main/unifi-network/10.6.97.json`,
|
|
62
|
+
diffing aid only, never a spec of record (Q5/M9) — `info.version: "10.6.97"`,
|
|
63
|
+
`sha256: 44fd0e7f81603bb1279889a528a443d8f1a0102436e4a29675d8451adf32bd78`, mirror commit
|
|
64
|
+
`f979dd64ec0ea6008c2a698c278fe49a10e102c8` (Andrew Beresford, 2026-08-20, "Add UniFi API specs:
|
|
65
|
+
Network 10.6.97", GitHub-verified signature).
|
|
66
|
+
- **Operation count**: both versions carry exactly the same 12 operations for these two tags — zero
|
|
67
|
+
added, zero removed, in either direction.
|
|
68
|
+
- **Full raw operation objects** (parameters, request body, responses, unresolved `$ref`s
|
|
69
|
+
included — not just named schemas, the same reason `unifi.md`'s own check goes past a
|
|
70
|
+
schema-name diff): byte-identical for all 12, both versions.
|
|
71
|
+
- **14 component schemas differ somewhere in the whole document** (of 379/380 total) — the SAME 14
|
|
72
|
+
`unifi.md`'s own Networks/FirewallZones check found: 9 switch-stack/LAG schemas (`Switching`
|
|
73
|
+
tag), 4 generic `filter`-query-syntax schemas (`FilterExpression` and its three siblings,
|
|
74
|
+
matching the new `Filtering` tag), and one mDNS enum addition (`SHELLY`, `UniFi Devices` tag).
|
|
75
|
+
Full per-schema breakdown: the distilled package's own `docs/spec-version-provenance.md`.
|
|
76
|
+
- **Reachability, corrected 2026-09-26 (red team, Important finding 1)**: the original pass
|
|
77
|
+
resolved only plain `$ref`s reachable from the 12 operations' full parameter/body/response trees
|
|
78
|
+
— 17 schema names. ⛔ **That walk is wrong for any discriminated schema**: both `DNS policy`
|
|
79
|
+
(`type`: `A_RECORD`…`TXT_RECORD`, `FORWARD_DOMAIN` — 7-way) and `ACL rule` (`type`: `IPV4`, `MAC`
|
|
80
|
+
— 2-way) are `discriminator`-typed, and a variant reachable only via `discriminator.mapping`
|
|
81
|
+
carries no `$ref` from its parent at all. Re-walked following `$ref` **and** `mapping` targets:
|
|
82
|
+
the closure is **46 schema names**, identical set in both versions. The 29 names the `$ref`-only
|
|
83
|
+
walk missed: all 7 DNS record variant DTOs (`IntegrationDnsARecordDto`,
|
|
84
|
+
`IntegrationDnsAaaaRecordDto`, `IntegrationDnsCnameRecordDto`, `IntegrationDnsForwardDomainPolicyDto`,
|
|
85
|
+
`IntegrationDnsMxRecordDto`, `IntegrationDnsSrvRecordDto`, `IntegrationDnsTxtRecordDto`) plus their
|
|
86
|
+
7 `*CreateUpdateDto` siblings; both ACL rule variant DTOs (`IntegrationIpAclRuleDto`,
|
|
87
|
+
`IntegrationMacAclRuleDto`) plus their 2 `*CreateUpdateDto` siblings; the IP/MAC endpoint-filter
|
|
88
|
+
DTOs (`IP ACL rule endpoint`, `MAC ACL rule endpoint`, `IntegrationIpAclRuleNetworkEndpointFilterDto`,
|
|
89
|
+
`IntegrationIpAclRulePortEndpointFilterDto`, `IntegrationIpAclRuleSubnetEndpointFilterDto`,
|
|
90
|
+
`IntegrationMacAclRuleMacAddressEndpointFilterDto`); `IntegrationAclRuleDevicesFilterDto`
|
|
91
|
+
(the device-filter's own `DEVICES` mapping target); and 2 more entity-metadata variants beyond the
|
|
92
|
+
five the original count already had. Re-diffed all 46 against the mirror with an order-independent
|
|
93
|
+
structural compare (not a byte/string compare — JSON key order differs between the pin and the
|
|
94
|
+
mirror file): **zero differ.** **Zero overlap** with the 14 schemas that changed elsewhere in the
|
|
95
|
+
document.
|
|
96
|
+
|
|
97
|
+
**Answer unchanged: neither `DnsPolicy` nor `AclRule`/`AclRuleOrdering` would decode differently
|
|
98
|
+
against 10.6.97** — the conclusion survives the correction; only the closure method and the schema
|
|
99
|
+
count were wrong. **This does not generalize past 10.6.97 or to any other tag — re-run this check,
|
|
100
|
+
following BOTH `$ref` and `discriminator.mapping`, before importing or declaring against `Clients`,
|
|
101
|
+
`WiFi Broadcasts`, `Traffic Matching Lists`, `Switching`, or `Firewall` (`FirewallPolicy`); a
|
|
102
|
+
`$ref`-only walk on `WiFi Broadcasts` finds 15 schemas and would miss
|
|
103
|
+
`IntegrationWifiMdnsProxyPredefinedServiceDto`, which DID change (10.6.97 added the `SHELLY` enum
|
|
104
|
+
member) and is reachable only through a `mapping`.**
|
|
105
|
+
|
|
106
|
+
## The declaration renderers
|
|
107
|
+
|
|
108
|
+
`declareAclRule(live, siteId)` and `declareAclRuleOrdering(live, siteId)` are pure functions, same
|
|
109
|
+
contract as `declareNetwork`/`declareFirewallZone` (`docs/unifi.md`): given one live read, they
|
|
110
|
+
return the `Props` a declaration needs so its plan is a no-op. Neither performs the read itself.
|
|
111
|
+
|
|
112
|
+
## Field-level drift
|
|
113
|
+
|
|
114
|
+
`acl-rule.ts`/`acl-rule-ordering.ts` each export a pure `driftOf(live, props)`
|
|
115
|
+
(`acl-rule-drift.ts`, `acl-rule-ordering-form.ts`), built on the same `drift.ts`'s `makeDriftOf`
|
|
116
|
+
framework as `Unifi.Network`/`Unifi.FirewallZone` — `matches` is exactly
|
|
117
|
+
`fieldDrift(...).length === 0`, never a second hand-written comparison (MEDIUM-4).
|
|
118
|
+
|
|
119
|
+
## Vendor version
|
|
120
|
+
|
|
121
|
+
UniFi Network Integration API **10.4.57**, same pin as `docs/unifi.md`, via
|
|
122
|
+
`@distilled.cloud/unifi-network` aliased onto `@homeflare/distilled-unifi-network@0.2.0`.
|