@homeflare/alchemy 0.56.0 → 0.57.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/dist/unifi/acl-rule-ordering.d.ts +1 -1
- package/dist/unifi/acl-rule.d.ts +1 -1
- package/dist/unifi/dns-policy.d.ts +1 -1
- package/dist/unifi/firewall-policy-create.d.ts +55 -0
- package/dist/unifi/firewall-policy-create.d.ts.map +1 -0
- package/dist/unifi/firewall-policy-form.d.ts +9 -2
- package/dist/unifi/firewall-policy-form.d.ts.map +1 -1
- package/dist/unifi/firewall-policy-ordering.d.ts +1 -1
- package/dist/unifi/firewall-policy.d.ts +45 -12
- package/dist/unifi/firewall-policy.d.ts.map +1 -1
- package/dist/unifi/firewall-zone.d.ts +1 -1
- package/dist/unifi/index.d.ts +1 -1
- package/dist/unifi/index.d.ts.map +1 -1
- package/dist/unifi/index.js +112 -21
- package/dist/unifi/index.js.map +10 -9
- package/dist/unifi/network.d.ts +1 -1
- package/dist/unifi/policy.d.ts +22 -0
- package/dist/unifi/policy.d.ts.map +1 -1
- package/dist/unifi/resource.d.ts +26 -7
- package/dist/unifi/resource.d.ts.map +1 -1
- package/dist/unifi/traffic-matching-list.d.ts +1 -1
- package/dist/unifi/update-reconcile.d.ts +36 -2
- package/dist/unifi/update-reconcile.d.ts.map +1 -1
- package/dist/unifi/wifi-broadcast.d.ts +1 -1
- package/dist/unifi/wire-guard.d.ts +1 -1
- package/dist/unifi/wire-guard.d.ts.map +1 -1
- package/docs/distilled-interim.md +4 -2
- package/package.json +1 -1
- package/src/unifi/firewall-policy-create.ts +94 -0
- package/src/unifi/firewall-policy-form.ts +9 -2
- package/src/unifi/firewall-policy.test.ts +188 -1
- package/src/unifi/firewall-policy.ts +108 -12
- package/src/unifi/index.ts +1 -0
- package/src/unifi/policy.ts +45 -12
- package/src/unifi/resource.test.ts +103 -0
- package/src/unifi/resource.ts +73 -21
- package/src/unifi/update-reconcile.ts +72 -2
- package/src/unifi/wire-guard.test.ts +86 -1
- package/src/unifi/wire-guard.ts +8 -1
- package/src/unifi/write-op-reference.test.ts +21 -7
package/dist/unifi/index.js.map
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/unifi/policy.ts", "../src/unifi/wire-guard.ts", "../src/unifi/network.ts", "../src/unifi/network-form.ts", "../src/unifi/drift.ts", "../src/unifi/network-drift.ts", "../src/unifi/network-scope.ts", "../src/unifi/network-update.ts", "../src/unifi/resource.ts", "../src/unifi/update-reconcile.ts", "../src/unifi/firewall-zone.ts", "../src/unifi/firewall-zone-form.ts", "../src/unifi/dns-policy.ts", "../src/unifi/dns-policy-form.ts", "../src/unifi/dns-policy-drift.ts", "../src/unifi/acl-rule.ts", "../src/unifi/acl-rule-form.ts", "../src/unifi/acl-rule-drift.ts", "../src/unifi/acl-rule-ordering.ts", "../src/unifi/acl-rule-ordering-form.ts", "../src/unifi/wifi-broadcast.ts", "../src/unifi/wifi-broadcast-form.ts", "../src/unifi/wifi-broadcast-drift.ts", "../src/unifi/paginate.ts", "../src/unifi/firewall-policy.ts", "../src/unifi/firewall-policy-form.ts", "../src/unifi/firewall-policy-drift.ts", "../src/unifi/firewall-policy-ordering.ts", "../src/unifi/firewall-policy-ordering-form.ts", "../src/unifi/traffic-matching-list.ts", "../src/unifi/traffic-matching-list-form.ts", "../src/unifi/traffic-matching-list-drift.ts", "../src/unifi/providers.ts"],
|
|
3
|
+
"sources": ["../src/unifi/policy.ts", "../src/unifi/wire-guard.ts", "../src/unifi/network.ts", "../src/unifi/network-form.ts", "../src/unifi/drift.ts", "../src/unifi/network-drift.ts", "../src/unifi/network-scope.ts", "../src/unifi/network-update.ts", "../src/unifi/resource.ts", "../src/unifi/update-reconcile.ts", "../src/unifi/firewall-zone.ts", "../src/unifi/firewall-zone-form.ts", "../src/unifi/dns-policy.ts", "../src/unifi/dns-policy-form.ts", "../src/unifi/dns-policy-drift.ts", "../src/unifi/acl-rule.ts", "../src/unifi/acl-rule-form.ts", "../src/unifi/acl-rule-drift.ts", "../src/unifi/acl-rule-ordering.ts", "../src/unifi/acl-rule-ordering-form.ts", "../src/unifi/wifi-broadcast.ts", "../src/unifi/wifi-broadcast-form.ts", "../src/unifi/wifi-broadcast-drift.ts", "../src/unifi/paginate.ts", "../src/unifi/firewall-policy.ts", "../src/unifi/firewall-policy-create.ts", "../src/unifi/firewall-policy-form.ts", "../src/unifi/firewall-policy-drift.ts", "../src/unifi/firewall-policy-ordering.ts", "../src/unifi/firewall-policy-ordering-form.ts", "../src/unifi/traffic-matching-list.ts", "../src/unifi/traffic-matching-list-form.ts", "../src/unifi/traffic-matching-list-drift.ts", "../src/unifi/providers.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * The one refusal every refused `Unifi.*` write returns. The family makes exactly
|
|
6
|
-
"/**\n * B0a / T12 DEFENSE IN DEPTH, AT THE WIRE. `policy.ts`'s `UnifiWriteRefused` stops write INTENT at\n * `reconcile`/`destroy`; this guard stops the same thing one layer lower. It wraps whatever\n * `HttpClient` the caller already provides (the stack's `FetchHttpClient.layer` in production,\n * `fakeUnifiLayer` in tests — `openbao/bao-http.ts`'s header names this same \"wrap, don't\n * replace\" seam), so an SDK write operation called by mistake, bypassing `unifiOperations`, still\n * never reaches the vendor API. `write-op-reference.test.ts` proves no such call exists outside\n * `network-update.ts`; this is what stops one from ever taking effect.\n *\n * ★ A GET ALWAYS PASSES THE REQUEST GUARD; ANY OTHER METHOD PASSES ONLY WHEN `(method, pathname)`\n * MATCHES AN ENTRY OF THE `allow` LIST. `unifiHandlers` builds that list PER ROW\n * (`spec.update.allowedWrite(news)`): the one PUT that can pass is to this row's own\n * `/v1/sites/<siteId>/networks/<networkId>` under the CONFIGURED base URL (local and\n * cloud-connector base paths differ, so the base is passed in and compared exactly, `isAllowed`).\n * A PUT to another network id, the list route, or another family dies here.\n *\n * ⚠️ `Effect.die`, NOT A TYPED FAILURE — every generated SDK operation declares its OWN closed\n * error union from the pinned OpenAPI spec and generated files are never hand-edited, so a typed\n * `Effect.fail` here would be a failure absent from every op's declared type. A defect is the\n * sound way to add a \"structurally impossible\" failure underneath types this package does not\n * own, like `openbao`'s `refuse()` helpers.\n *\n * ⛔ REDIRECTS (LOW-7), REFUSED FOR EVERY METHOD — GETs included, 2026-10-10. The layer sets fetch\n * `redirect: 'manual'` on EVERY guarded request and the guard dies on any 3xx answer. Default\n * fetch follows a redirect and re-sends the request to a `Location` this guard never saw — a PUT\n * with its body on 307/308, a GET still carrying the API-key header — so following is never\n * safe. Scoping `manual` to a non-empty allow list (the pre-2026-10-10 shape) also made the two\n * postures DIVERGE by accident: reconcile's own GETs ran manual, so a 3xx surfaced raw and failed\n * the SDK decode, while read/diff under `GetOnlyHttpClient` followed the redirect silently. One\n * rule for both postures now: a redirect is refused everywhere, fail closed.\n */\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport * as FetchHttpClient from 'effect/http/FetchHttpClient';\nimport * as HttpClient from 'effect/http/HttpClient';\nimport * as HttpClientRequest from 'effect/http/HttpClientRequest';\nimport type * as HttpClientResponse from 'effect/http/HttpClientResponse';\nimport { UNIFI_WRITE_POLICY } from './policy.ts';\n\n/**\n * One non-GET request the guard may let through. `tail` is the exact route below the configured\n * base URL (`/v1/sites/<site>/networks/<id>`): the guard compares the request's origin and path to\n * `baseUrl + tail` and requires an empty query and fragment (`isAllowed`).\n */\nexport type AllowedWrite = { readonly method: 'PUT'; readonly tail: string };\n\nexport class UnifiRefusedRequest extends Error {\n constructor(\n readonly method: string,\n /** PATH ONLY, never the full URL — see `requestPath` below. */\n readonly path: string,\n detail = \"not on this row's allow list\",\n ) {\n super(\n `UniFi HttpClient guard: refused ${method} ${path} (${detail}) -- ${UNIFI_WRITE_POLICY}. ` +\n 'This is a defect, not a recoverable condition: something under src/unifi sent a request ' +\n \"policy.ts's per-operation refusal should already have made impossible. Fix the kit \" +\n 'code that produced this request.',\n );\n this.name = 'UnifiRefusedRequest';\n }\n}\n\n/** @deprecated renamed `UnifiRefusedRequest`; kept so existing consumer imports do not break. */\nexport const UnifiNonGetRequest = UnifiRefusedRequest;\nexport type UnifiNonGetRequest = UnifiRefusedRequest;\n\n/**\n * ⛔ PATH ONLY, NEVER THE HOST. A cloud connector's base URL embeds the account's Console ID\n * (`docs/unifi.md`'s Credentials section, T3) — a defect message can still reach a log or a CI\n * failure body. Falls back to a fixed placeholder on a parse failure so a malformed URL can\n * never leak through unredacted.\n */\nconst requestPath = (url: string): string => {\n try {\n // ⛔ The cloud connector path is `/v1/connector/consoles/<consoleId>/...`: the id is dropped too.\n return new URL(url).pathname.replace(/(\\/consoles\\/)[^/]*/g, '$1<redacted>');\n } catch {\n return '<unparseable request url>';\n }\n};\n\n/**\n * ⛔ ANCHORED TO THE CONFIGURED BASE URL, NOT A SUFFIX. The request must share the base's origin,\n * its path must be exactly `<base path><tail>` (so `//v1/...`, another base path or a nested\n * `/v1/sites/X/v1/sites/s/...` all miss), and it must carry no query or fragment (`?force=true`).\n * Without a base URL nothing is allowed: fail closed.\n */\nconst isAllowed = (\n allow: ReadonlyArray<AllowedWrite>,\n baseUrl: string | undefined,\n method: string,\n request: HttpClientRequest.HttpClientRequest,\n) => {\n if (baseUrl === undefined) return false;\n try {\n const base = new URL(baseUrl);\n // ⛔ `toUrl`, NOT `request.url`: `setUrlParam` keeps its parameters OUTSIDE `request.url`, so\n // the bare string looked query-free while the wire request carried `?force=true` (round 2, F5).\n const resolved = HttpClientRequest.toUrl(request);\n if (Option.isNone(resolved)) return false;\n const target = resolved.value;\n if (target.origin !== base.origin || target.search !== '' || target.hash !== '') return false;\n const basePath = base.pathname.replace(/\\/+$/, '');\n return allow.some(\n (entry) => entry.method === method && target.pathname === `${basePath}${entry.tail}`,\n );\n } catch {\n return false;\n }\n};\n\n/** True when the response says it came from a different URL than the request named. */\nconst followedElsewhere = (res: HttpClientResponse.HttpClientResponse): boolean => {\n const sent = HttpClientRequest.toUrl(res.request);\n return res.url !== '' && Option.isSome(sent) && res.url !== sent.value.href.split('#')[0];\n};\n\nconst guard = (\n client: HttpClient.HttpClient,\n allow: ReadonlyArray<AllowedWrite>,\n baseUrl: string | undefined,\n) =>\n client.pipe(\n HttpClient.mapRequestEffect((request) =>\n request.method === 'GET' || isAllowed(allow, baseUrl, request.method, request)\n ? Effect.succeed(request)\n : Effect.die(new UnifiRefusedRequest(request.method, requestPath(request.url))),\n ),\n HttpClient.transformResponse((response) =>\n Effect.flatMap(response, (res) =>\n // ⛔ ANY METHOD: a GET's 3xx is refused exactly like a PUT's — see REDIRECTS in the header.\n res.status >= 300 && res.status < 400\n ? Effect.die(\n new UnifiRefusedRequest(\n res.request.method,\n requestPath(res.request.url),\n `answered a ${res.status} redirect`,\n ),\n )\n : // ⛔ BACKSTOP (round 2, F3): if a consumer's own transform replaced `manual` and fetch\n // followed a redirect, the response URL is no longer the request's. Die on that.\n followedElsewhere(res)\n ? Effect.die(\n new UnifiRefusedRequest(\n res.request.method,\n requestPath(res.request.url),\n 'the response URL differs from the request URL (a redirect was followed)',\n ),\n )\n : Effect.succeed(res),\n ),\n ),\n );\n\n/**\n * A `Layer` that reads whatever `HttpClient` is already in the calling context and replaces it,\n * for everything downstream, with the guarded version. Exported so `wire-guard.test.ts` can prove\n * the mechanism against a bare `HttpClient`, without `CredentialsFromEnv` or any env var.\n */\nexport const guardedHttpClient = (allow: ReadonlyArray<AllowedWrite>, baseUrl?: string) => {\n return HttpClient.layerMergedContext(\n Effect.map(HttpClient.HttpClient, (client) =>\n // ⛔ MANUAL REDIRECT ON BOTH POSTURES, not just an allow list: the response guard refuses a\n // 3xx for GETs too, and under the default 'follow' a GET redirect is silently followed —\n // the guard would never see it, and read/diff would diverge from reconcile again.\n HttpClient.transform(guard(client, allow, baseUrl), manualRedirect),\n ),\n );\n};\n\n/**\n * ⛔ `redirect: 'manual'` IS SET PER REQUEST, ON THE CALLING FIBER'S `RequestInit`. A layer-provided\n * `RequestInit` is silently REPLACED by an outer one (Effect 4.0.1 `layerMergedContext` merges the\n * caller's context over the layer's), which would turn redirects back on. Same read-merge-provide\n * pattern as `openbao/bao-http.ts`'s `overSocket`; outer fetch options are kept.\n */\nconst manualRedirect = <E, R>(\n effect: Effect.Effect<HttpClientResponse.HttpClientResponse, E, R>,\n): Effect.Effect<HttpClientResponse.HttpClientResponse, E, R> =>\n Effect.flatMap(Effect.serviceOption(FetchHttpClient.RequestInit), (existing) =>\n Effect.provideService(effect, FetchHttpClient.RequestInit, {\n ...Option.getOrElse(existing, () => ({})),\n redirect: 'manual',\n }),\n );\n\n/**\n * No write may pass; the posture of `read`/`diff`/`delete` and of every non-updatable family. A 3xx\n * answer to a GET is refused here exactly as under the allow-list posture — see REDIRECTS above.\n */\nexport const GetOnlyHttpClient = guardedHttpClient([]);\n",
|
|
5
|
+
"/**\n * The one refusal every refused `Unifi.*` write returns. The family makes exactly two SDK write\n * calls — `networks.updateNetwork` (`network-update.ts`) and, as of the lift below,\n * `firewall.createFirewallPolicy` (`firewall-policy-create.ts`) — and those are the only writes\n * that do not see this error.\n *\n * ⛔ TIM'S RULE, 2026-09-24: UniFi (and OPNsense) are READ-ONLY. LIFTED FOR TWO CASES SINCE:\n * 2026-10-10, `Unifi.Network` update, through `update-reconcile.ts` + `network-update.ts`; and\n * 2026-10-19 ~16:20Z (\"Allow kit to create hf: rules\"), `Unifi.FirewallPolicy` CREATE, and ONLY\n * when the declared `name` starts `hf: ` (`isHfPolicyName` below). Every other write — every\n * other family's create, every update of an `hf:` row (or any other), every delete — still fails\n * with this typed error, and `resource.test.ts`/`wire-guard.test.ts`/`firewall-policy.test.ts`\n * prove it with a fake `HttpClient` that records every request. Widening the policy again is a\n * kit change (a new PR, reviewed as one), not a flag a stack can pass.\n *\n * ★ A TYPED TAG, NOT A THROWN STRING (S21). `Effect.catchTag('UnifiWriteRefused', …)` lets a\n * caller (a future import script, a test) branch on the refusal without parsing a message.\n */\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\n\nexport const UNIFI_WRITE_POLICY =\n 'UniFi is adopt-only except (1) Unifi.Network update: one whole-object PUT built from the live ' +\n 'object plus the declared field changes, only on a row with prior Alchemy state, only through ' +\n 'deploy-approve; and (2) Unifi.FirewallPolicy create, only when the declared name starts \"hf: \", ' +\n 'one POST built from only the declared fields, read back and checked to match after; every ' +\n 'other create, every update of an hf: row, every delete and every other family stay refused -- ' +\n 'widening is a kit change (Tim 2026-10-10 lifts the 2026-09-24 rule for Network update, Tim ' +\n '2026-10-19 lifts it again for hf:-named FirewallPolicy create)';\n\n/**\n * ⛔ THE ONE GATE THAT DECIDES \"hf:\" (Tim 2026-10-19, `nas-firewall`'s own naming:\n * `hf: CT100 to NAS https`). The space after the colon is deliberate and checked: `\"hf:foo\"`\n * (no space) is NOT an hf: name under this policy, so a typo can never silently slip past the\n * create gate by omitting it. Exported so `firewall-policy-create.ts`'s `checkScope` and every\n * test that proves the boundary use this ONE function, never a second regex hand-copied nearby.\n */\nexport const isHfPolicyName = (name: string): boolean => name.startsWith('hf: ');\n\n/** @deprecated renamed `UNIFI_WRITE_POLICY`; kept so existing consumer imports do not break. */\nexport const UNIFI_READ_ONLY_POLICY = UNIFI_WRITE_POLICY;\n\n/** What kind of write was asked for and refused — for the message only, never acted on. */\nexport type UnifiWriteAction = 'create' | 'update' | 'delete';\n\nexport class UnifiWriteRefused extends Data.TaggedError('UnifiWriteRefused')<{\n /** The vendor type string, e.g. `Unifi.Network`. */\n readonly type: string;\n /** `spec.describe(props)` — the object the write would have touched. */\n readonly identity: string;\n readonly action: UnifiWriteAction;\n}> {\n override get message(): string {\n return `${this.type} ${this.identity}: refusing to ${this.action} -- ${UNIFI_WRITE_POLICY}.`;\n }\n}\n\n/** The only thing `reconcile`/`delete` ever return for a write this family cannot make. */\nexport const refuseWrite = (type: string, identity: string, action: UnifiWriteAction) =>\n Effect.fail(new UnifiWriteRefused({ type, identity, action }));\n\n// ⛔ THE THREE ERRORS BELOW CARRY FIELD NAMES ONLY, NEVER VALUES: a network row holds addressing\n// detail, and an error message reaches logs, CI bodies and pasted reports.\nconst fieldList = (fields: ReadonlyArray<string>) => `[${fields.join(', ')}]`;\n\n/** Live no longer equals what the last deploy wrote: a hand edit is never overwritten. */\nexport class UnifiLiveDriftedSinceDeploy extends Data.TaggedError('UnifiLiveDriftedSinceDeploy')<{\n readonly type: string;\n readonly identity: string;\n readonly fields: ReadonlyArray<string>;\n}> {\n override get message(): string {\n return (\n `${this.type} ${this.identity}: live differs from the last deployed state on ` +\n `${fieldList(this.fields)}; refusing to PUT over it. Two causes look identical from here: a ` +\n 'hand edit on the console, or an earlier PUT that landed but did not converge ' +\n '(UnifiUpdateDidNotConverge). Re-import the live object first.'\n );\n }\n}\n\n/** The merged body equals live: a PUT would change nothing (keeps a re-PUT loop impossible). */\nexport class UnifiUpdateWouldBeNoop extends Data.TaggedError('UnifiUpdateWouldBeNoop')<{\n readonly type: string;\n readonly identity: string;\n}> {\n override get message(): string {\n return `${this.type} ${this.identity}: the merged update body equals live; refusing a no-op PUT.`;\n }\n}\n\n/** The PUT answered, yet the response still differs from the declaration. */\nexport class UnifiUpdateDidNotConverge extends Data.TaggedError('UnifiUpdateDidNotConverge')<{\n readonly type: string;\n readonly identity: string;\n readonly fields: ReadonlyArray<string>;\n}> {\n override get message(): string {\n return (\n `${this.type} ${this.identity}: the PUT LANDED, but live now differs from the declaration on ` +\n `${fieldList(this.fields)}. The change is on the console and is not rolled back; the next ` +\n 'deploy will see this as drift. Recovery is a re-import of the live object.'\n );\n }\n}\n\n/** The POST answered, a read-back ran, yet the read-back object still differs from the\n * declaration — the hf: create path's own convergence check (`create-reconcile.ts`'s sibling in\n * `update-reconcile.ts`), same shape as `UnifiUpdateDidNotConverge` for the same reason: the\n * object now exists on the console, unrolled-back, so the operator needs to know and re-import. */\nexport class UnifiCreateDidNotConverge extends Data.TaggedError('UnifiCreateDidNotConverge')<{\n readonly type: string;\n readonly identity: string;\n readonly fields: ReadonlyArray<string>;\n}> {\n override get message(): string {\n return (\n `${this.type} ${this.identity}: the POST created an object, but the read-back differs from ` +\n `the declaration on ${fieldList(this.fields)}. The object is on the console and is not ` +\n 'rolled back; the next deploy will see this as drift. Recovery is a re-import of the live object.'\n );\n }\n}\n\n// ⛔ THE FIVE ERRORS BELOW BOUND THE WRITE SCOPE (`network-scope.ts`, `update-reconcile.ts`): the\n// one intended consumer is a single VLAN's `ipv6Configuration`. Field names only, as above.\n\n/** A patch would remove a key outside the removable allowlist (an omitted block is turned off). */\nexport class UnifiFieldNotRemovable extends Data.TaggedError('UnifiFieldNotRemovable')<{\n readonly type: string;\n readonly identity: string;\n readonly fields: ReadonlyArray<string>;\n}> {\n override get message(): string {\n return (\n `${this.type} ${this.identity}: refusing to remove ${fieldList(this.fields)}; a whole-object ` +\n 'PUT turns an omitted block off, and only the removable allowlist may be dropped.'\n );\n }\n}\n\n/** A patch would SET a key outside the settable allowlist (only `ipv6Configuration` may be written). */\nexport class UnifiFieldNotSettable extends Data.TaggedError('UnifiFieldNotSettable')<{\n readonly type: string;\n readonly identity: string;\n readonly fields: ReadonlyArray<string>;\n}> {\n override get message(): string {\n return (\n `${this.type} ${this.identity}: refusing to set ${fieldList(this.fields)}; this write path ` +\n 'only sets the settable allowlist (ipv6Configuration).'\n );\n }\n}\n\n/** The live object is the site's default (management) network: never written. */\nexport class UnifiManagementNetworkRefused extends Data.TaggedError(\n 'UnifiManagementNetworkRefused',\n)<{\n readonly type: string;\n readonly identity: string;\n}> {\n override get message(): string {\n return `${this.type} ${this.identity}: live is the default (management) network; refusing any write to it.`;\n }\n}\n\n/** A patch changes a field that decides where the network lives or what serves it. */\nexport class UnifiImmutableFieldChanged extends Data.TaggedError('UnifiImmutableFieldChanged')<{\n readonly type: string;\n readonly identity: string;\n readonly fields: ReadonlyArray<string>;\n}> {\n override get message(): string {\n return `${this.type} ${this.identity}: refusing to change ${fieldList(this.fields)}; those fields are never written.`;\n }\n}\n\n/** `olds` and `news` name different objects: the prior state is no baseline for this write. */\nexport class UnifiIdentityChanged extends Data.TaggedError('UnifiIdentityChanged')<{\n readonly type: string;\n readonly identity: string;\n readonly previous: string;\n}> {\n override get message(): string {\n return `${this.type} ${this.identity}: the last deploy named ${this.previous}; refusing to PUT against another object.`;\n }\n}\n",
|
|
6
|
+
"/**\n * B0a / T12 DEFENSE IN DEPTH, AT THE WIRE. `policy.ts`'s `UnifiWriteRefused` stops write INTENT at\n * `reconcile`/`destroy`; this guard stops the same thing one layer lower. It wraps whatever\n * `HttpClient` the caller already provides (the stack's `FetchHttpClient.layer` in production,\n * `fakeUnifiLayer` in tests — `openbao/bao-http.ts`'s header names this same \"wrap, don't\n * replace\" seam), so an SDK write operation called by mistake, bypassing `unifiOperations`, still\n * never reaches the vendor API. `write-op-reference.test.ts` proves no such call exists outside\n * `network-update.ts`; this is what stops one from ever taking effect.\n *\n * ★ A GET ALWAYS PASSES THE REQUEST GUARD; ANY OTHER METHOD PASSES ONLY WHEN `(method, pathname)`\n * MATCHES AN ENTRY OF THE `allow` LIST. `unifiHandlers` builds that list PER ROW\n * (`spec.update.allowedWrite(news)`): the one PUT that can pass is to this row's own\n * `/v1/sites/<siteId>/networks/<networkId>` under the CONFIGURED base URL (local and\n * cloud-connector base paths differ, so the base is passed in and compared exactly, `isAllowed`).\n * A PUT to another network id, the list route, or another family dies here.\n *\n * ⛔ POST, ADDED 2026-10-19 FOR THE hf: CREATE LIFT (`policy.ts`'s `isHfPolicyName`). The allow\n * entry `unifiHandlers` builds for a create attempt (`spec.create.allowedWrite()`) names the\n * site's fixed `/v1/sites/<siteId>/firewall/policies` COLLECTION route, never a per-row path —\n * there is no row id before the POST creates one. This list is still the ONLY gate on method and\n * path; the hf: NAME check is `create-reconcile.ts`'s `checkScope`, run before the POST is even\n * attempted, not something this guard can see (it has no view of the request body).\n *\n * ⚠️ `Effect.die`, NOT A TYPED FAILURE — every generated SDK operation declares its OWN closed\n * error union from the pinned OpenAPI spec and generated files are never hand-edited, so a typed\n * `Effect.fail` here would be a failure absent from every op's declared type. A defect is the\n * sound way to add a \"structurally impossible\" failure underneath types this package does not\n * own, like `openbao`'s `refuse()` helpers.\n *\n * ⛔ REDIRECTS (LOW-7), REFUSED FOR EVERY METHOD — GETs included, 2026-10-10. The layer sets fetch\n * `redirect: 'manual'` on EVERY guarded request and the guard dies on any 3xx answer. Default\n * fetch follows a redirect and re-sends the request to a `Location` this guard never saw — a PUT\n * with its body on 307/308, a GET still carrying the API-key header — so following is never\n * safe. Scoping `manual` to a non-empty allow list (the pre-2026-10-10 shape) also made the two\n * postures DIVERGE by accident: reconcile's own GETs ran manual, so a 3xx surfaced raw and failed\n * the SDK decode, while read/diff under `GetOnlyHttpClient` followed the redirect silently. One\n * rule for both postures now: a redirect is refused everywhere, fail closed.\n */\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\nimport * as FetchHttpClient from 'effect/http/FetchHttpClient';\nimport * as HttpClient from 'effect/http/HttpClient';\nimport * as HttpClientRequest from 'effect/http/HttpClientRequest';\nimport type * as HttpClientResponse from 'effect/http/HttpClientResponse';\nimport { UNIFI_WRITE_POLICY } from './policy.ts';\n\n/**\n * One non-GET request the guard may let through. `tail` is the exact route below the configured\n * base URL (`/v1/sites/<site>/networks/<id>`): the guard compares the request's origin and path to\n * `baseUrl + tail` and requires an empty query and fragment (`isAllowed`).\n */\nexport type AllowedWrite = { readonly method: 'PUT' | 'POST'; readonly tail: string };\n\nexport class UnifiRefusedRequest extends Error {\n constructor(\n readonly method: string,\n /** PATH ONLY, never the full URL — see `requestPath` below. */\n readonly path: string,\n detail = \"not on this row's allow list\",\n ) {\n super(\n `UniFi HttpClient guard: refused ${method} ${path} (${detail}) -- ${UNIFI_WRITE_POLICY}. ` +\n 'This is a defect, not a recoverable condition: something under src/unifi sent a request ' +\n \"policy.ts's per-operation refusal should already have made impossible. Fix the kit \" +\n 'code that produced this request.',\n );\n this.name = 'UnifiRefusedRequest';\n }\n}\n\n/** @deprecated renamed `UnifiRefusedRequest`; kept so existing consumer imports do not break. */\nexport const UnifiNonGetRequest = UnifiRefusedRequest;\nexport type UnifiNonGetRequest = UnifiRefusedRequest;\n\n/**\n * ⛔ PATH ONLY, NEVER THE HOST. A cloud connector's base URL embeds the account's Console ID\n * (`docs/unifi.md`'s Credentials section, T3) — a defect message can still reach a log or a CI\n * failure body. Falls back to a fixed placeholder on a parse failure so a malformed URL can\n * never leak through unredacted.\n */\nconst requestPath = (url: string): string => {\n try {\n // ⛔ The cloud connector path is `/v1/connector/consoles/<consoleId>/...`: the id is dropped too.\n return new URL(url).pathname.replace(/(\\/consoles\\/)[^/]*/g, '$1<redacted>');\n } catch {\n return '<unparseable request url>';\n }\n};\n\n/**\n * ⛔ ANCHORED TO THE CONFIGURED BASE URL, NOT A SUFFIX. The request must share the base's origin,\n * its path must be exactly `<base path><tail>` (so `//v1/...`, another base path or a nested\n * `/v1/sites/X/v1/sites/s/...` all miss), and it must carry no query or fragment (`?force=true`).\n * Without a base URL nothing is allowed: fail closed.\n */\nconst isAllowed = (\n allow: ReadonlyArray<AllowedWrite>,\n baseUrl: string | undefined,\n method: string,\n request: HttpClientRequest.HttpClientRequest,\n) => {\n if (baseUrl === undefined) return false;\n try {\n const base = new URL(baseUrl);\n // ⛔ `toUrl`, NOT `request.url`: `setUrlParam` keeps its parameters OUTSIDE `request.url`, so\n // the bare string looked query-free while the wire request carried `?force=true` (round 2, F5).\n const resolved = HttpClientRequest.toUrl(request);\n if (Option.isNone(resolved)) return false;\n const target = resolved.value;\n if (target.origin !== base.origin || target.search !== '' || target.hash !== '') return false;\n const basePath = base.pathname.replace(/\\/+$/, '');\n return allow.some(\n (entry) => entry.method === method && target.pathname === `${basePath}${entry.tail}`,\n );\n } catch {\n return false;\n }\n};\n\n/** True when the response says it came from a different URL than the request named. */\nconst followedElsewhere = (res: HttpClientResponse.HttpClientResponse): boolean => {\n const sent = HttpClientRequest.toUrl(res.request);\n return res.url !== '' && Option.isSome(sent) && res.url !== sent.value.href.split('#')[0];\n};\n\nconst guard = (\n client: HttpClient.HttpClient,\n allow: ReadonlyArray<AllowedWrite>,\n baseUrl: string | undefined,\n) =>\n client.pipe(\n HttpClient.mapRequestEffect((request) =>\n request.method === 'GET' || isAllowed(allow, baseUrl, request.method, request)\n ? Effect.succeed(request)\n : Effect.die(new UnifiRefusedRequest(request.method, requestPath(request.url))),\n ),\n HttpClient.transformResponse((response) =>\n Effect.flatMap(response, (res) =>\n // ⛔ ANY METHOD: a GET's 3xx is refused exactly like a PUT's — see REDIRECTS in the header.\n res.status >= 300 && res.status < 400\n ? Effect.die(\n new UnifiRefusedRequest(\n res.request.method,\n requestPath(res.request.url),\n `answered a ${res.status} redirect`,\n ),\n )\n : // ⛔ BACKSTOP (round 2, F3): if a consumer's own transform replaced `manual` and fetch\n // followed a redirect, the response URL is no longer the request's. Die on that.\n followedElsewhere(res)\n ? Effect.die(\n new UnifiRefusedRequest(\n res.request.method,\n requestPath(res.request.url),\n 'the response URL differs from the request URL (a redirect was followed)',\n ),\n )\n : Effect.succeed(res),\n ),\n ),\n );\n\n/**\n * A `Layer` that reads whatever `HttpClient` is already in the calling context and replaces it,\n * for everything downstream, with the guarded version. Exported so `wire-guard.test.ts` can prove\n * the mechanism against a bare `HttpClient`, without `CredentialsFromEnv` or any env var.\n */\nexport const guardedHttpClient = (allow: ReadonlyArray<AllowedWrite>, baseUrl?: string) => {\n return HttpClient.layerMergedContext(\n Effect.map(HttpClient.HttpClient, (client) =>\n // ⛔ MANUAL REDIRECT ON BOTH POSTURES, not just an allow list: the response guard refuses a\n // 3xx for GETs too, and under the default 'follow' a GET redirect is silently followed —\n // the guard would never see it, and read/diff would diverge from reconcile again.\n HttpClient.transform(guard(client, allow, baseUrl), manualRedirect),\n ),\n );\n};\n\n/**\n * ⛔ `redirect: 'manual'` IS SET PER REQUEST, ON THE CALLING FIBER'S `RequestInit`. A layer-provided\n * `RequestInit` is silently REPLACED by an outer one (Effect 4.0.1 `layerMergedContext` merges the\n * caller's context over the layer's), which would turn redirects back on. Same read-merge-provide\n * pattern as `openbao/bao-http.ts`'s `overSocket`; outer fetch options are kept.\n */\nconst manualRedirect = <E, R>(\n effect: Effect.Effect<HttpClientResponse.HttpClientResponse, E, R>,\n): Effect.Effect<HttpClientResponse.HttpClientResponse, E, R> =>\n Effect.flatMap(Effect.serviceOption(FetchHttpClient.RequestInit), (existing) =>\n Effect.provideService(effect, FetchHttpClient.RequestInit, {\n ...Option.getOrElse(existing, () => ({})),\n redirect: 'manual',\n }),\n );\n\n/**\n * No write may pass; the posture of `read`/`diff`/`delete` and of every non-updatable family. A 3xx\n * answer to a GET is refused here exactly as under the allow-list posture — see REDIRECTS above.\n */\nexport const GetOnlyHttpClient = guardedHttpClient([]);\n",
|
|
7
7
|
"/**\n * `Unifi.Network` — one network on a UniFi Network site, the ONE family with a write path\n * (`policy.ts` lifts Tim's read-only rule for exactly this update; see `network-update.ts`).\n *\n * ★ WHY NETWORK IS IN THE FIRST IMPORT SET. `getNetworkDetails`/`getNetworksOverviewPage`\n * (`../../distilled-unifi-network/src/services/networks.ts`) is a list+get pair over\n * genuine configuration — VLAN, DHCP guarding, IPv4/IPv6 addressing, isolation — not runtime\n * state like a connected client, a device statistic or a hotspot voucher. It is also the SDK\n * package's own README recommendation for a first read/adopt-only resource, alongside `Site`\n * (skipped here — no `getSite`, only the list operation, so it fails this task's own \"both a\n * list and a get\" bar) and `FirewallZone`/`FirewallPolicy` (`firewall-zone.ts` ships\n * `FirewallZone`; `FirewallPolicy`'s several discriminated filter variants and its\n * list-replacing ordering endpoint are a bigger, separate PR).\n *\n * ⛔ THE WHOLE-OBJECT-PUT TRAP (packages/distilled-unifi-network/README.md) FIRES HERE, THROUGH `network-update.ts`;\n * see its header. `updateNetwork` is a `PUT` that disables `dhcpGuarding`/`ipv4Configuration`/\n * `ipv6Configuration` outright when the caller omits them, so the body is the raw live object\n * plus only the fields the declaration changed since the last deploy — never props alone.\n * This is the ONE family with a write path (Tim 2026-10-10, `policy.ts`); create/delete refuse.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as networks from '@distilled.cloud/unifi-network/networks';\nimport * as Effect from 'effect/Effect';\nimport { type NetworkAttributes, type NetworkProps, attributesOf } from './network-form.ts';\nimport { driftOf, matches } from './network-drift.ts';\nimport { type ScopeError, checkNetworkWriteScope } from './network-scope.ts';\nimport { writeNetwork } from './network-update.ts';\nimport type { UnifiUpdateWouldBeNoop } from './policy.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\nimport type { AllowedWrite } from './wire-guard.ts';\n\ntype UpdateError = networks.UpdateNetworkError | UnifiUpdateWouldBeNoop | ScopeError;\n\nexport type { NetworkAttributes, NetworkProps } from './network-form.ts';\nexport { declareNetwork } from './network-form.ts';\n// MEDIUM-4: `matches` moved here from `network-form.ts` — see network-drift.ts's own header. Not\n// re-exported from this barrel file either, same as before the move: `spec.matches` below is the\n// only consumer a provider needs.\nexport { driftOf } from './network-drift.ts';\n\nexport interface UnifiNetwork extends Resource<\n 'Unifi.Network',\n NetworkProps,\n NetworkAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiNetwork = Resource<UnifiNetwork>('Unifi.Network', {\n defaultRemovalPolicy: 'retain',\n});\n\n/**\n * `network('lan', props)` — `adopt(true)` piped on by default (H5). A cold first deploy against an\n * existing network can still only bind to it or refuse `OwnedBySomeoneElse` — the update path\n * needs prior Alchemy state (`update-reconcile.ts`), so it cannot fire on adoption. Piping\n * `adopt(true)` here picks the bind without a stack having to remember `--adopt`, mirroring\n * `discord/application-command.ts`'s `applicationCommand`.\n */\nexport const network = (id: string, props: NetworkProps) =>\n UnifiNetwork(id, props).pipe(adopt(true));\n\n/**\n * ★ EXPORTED for direct testing against a fake `Credentials` layer (`fake-unifi.ts`), the same\n * seam `netbox/prefix.ts`'s `spec` and `discord/application-command.ts`'s `spec` use — `handlers`\n * bakes in `CredentialsFromEnv`, which resolves environment variables a test cannot repoint.\n */\n/**\n * Every declarable key: `NetworkProps` minus the two identity ids. A test pins this against a\n * fully-populated `NetworkProps` literal so a new prop cannot be silently unpatchable.\n */\nexport const NETWORK_PATCH_KEYS: ReadonlyArray<keyof NetworkProps> = [\n 'name',\n 'enabled',\n 'management',\n 'vlanId',\n 'dhcpGuarding',\n 'cellularBackupEnabled',\n 'internetAccessEnabled',\n 'ipv4Configuration',\n 'ipv6Configuration',\n 'isolationEnabled',\n 'mdnsForwardingEnabled',\n 'zoneId',\n 'deviceId',\n];\n\n/**\n * The only PUT that may pass for this row: its own `/v1/sites/<siteId>/networks/<networkId>` under\n * the configured base URL. ⛔ Built from the row's NEW props (the ids the write is about to use) and\n * compared by exact string against the request path, so no id can act as a pattern.\n */\nexport const networkAllowedWrite = (props: NetworkProps): AllowedWrite => ({\n method: 'PUT',\n tail: `/v1/sites/${props.siteId}/networks/${props.networkId}`,\n});\n\nexport const spec: UnifiSpec<\n NetworkProps,\n networks.NetworkDetails,\n NetworkAttributes,\n networks.GetNetworkDetailsError,\n UpdateError\n> = {\n type: 'Unifi.Network',\n describe: (props) => `sites/${props.siteId}/networks/${props.networkId}`,\n fetchLive: (props) =>\n networks\n .getNetworkDetails({ siteId: props.siteId, networkId: props.networkId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n update: {\n allowedWrite: networkAllowedWrite,\n patchKeys: NETWORK_PATCH_KEYS,\n driftOf,\n checkScope: checkNetworkWriteScope,\n write: writeNetwork,\n },\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiNetworkProvider = () =>\n Provider.effect(UnifiNetwork, Effect.succeed(UnifiNetwork.Provider.of(handlers)));\n",
|
|
8
8
|
"/**\n * `Unifi.Network`'s wire shape: the declarable subset of `NetworkDetails`, and how a live read\n * becomes both plan attributes and a rendered declaration.\n *\n * ⚠️ PROPS MIRROR `UpdateNetworkRequest`, NOT `NetworkDetails`. `default` and `metadata` are\n * server-derived — no writable endpoint in `networks.ts` accepts either — so they are\n * attributes-only, reported but never compared or declared. Everything else in `NetworkProps`\n * is exactly what `updateNetwork`/`createNetwork` would accept, even though this family never\n * calls either: the shape is what a future write path would need, and what today's read-only\n * `matches` compares against.\n *\n * ⛔ `ipv4Configuration` STAYS `unknown`, ON THE SDK'S OWN AUTHORITY. `networks.ts`'s own comment\n * (\"Shape varies by variant — widened by scripts/convert.ts; see README\") says the OpenAPI\n * discriminator this field varies over was flattened at generation time, not decoded per\n * variant. Comparing it as an opaque JSON value (below) is honest about that; guessing a\n * narrower shape here would silently stop matching the day a new variant's key order differs.\n * ⚠️ It may ALSO hide an unordered array the way `dhcpGuarding` and `ipv6Configuration` do\n * below — this family just can't know, because the shape is never decoded. If a future variant\n * plans spurious `update`s, suspect this field first and widen it into a typed, order-normalized\n * shape rather than adding another opaque comparison.\n *\n * ⛔ THREE FIELDS ARE SETS, NOT SEQUENCES: `dhcpGuarding.trustedDhcpServerIpAddresses`,\n * `ipv6Configuration.additionalHostIpSubnets`, and `ipv6Configuration.dnsServerIpAddressesOverride`.\n * The vendor document gives no indication any of the three is ordered — same reasoning\n * `firewall-zone-form.ts` gives for `networkIds`, and `proxmox/user-wire.ts`'s `groupSet` gives\n * for PVE's `groups` — but `matches` compares via alchemy's `deepEqual`, which sorts object KEYS\n * but NOT ARRAY ELEMENTS (`node_modules/alchemy/lib/Diff.js`). Left alone, a console that\n * returns the same set in a different order between calls plans a spurious `update`, and the\n * read-only reconcile then refuses every deploy. `sortedSet` (dedupe + sort) neutralizes that\n * before any of the three is attributed, declared, or compared. Every OTHER array field in this\n * family (there are none today besides these three) would stay declaration-order as written.\n */\nimport type * as networks from '@distilled.cloud/unifi-network/networks';\n\nconst sortedSet = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\n// ⚠️ `value == null` (NOT `=== undefined`) ON PURPOSE, IN BOTH NORMALIZERS BELOW. The SDK types\n// `dhcpGuarding`/`ipv6Configuration` `T | undefined`, but `matches`'s own header says UniFi's\n// JSON answers `null` for an unset optional field — a reality `deepEqual`'s `stripNullish`\n// already treats as equivalent to `undefined` (`value == null` in alchemy's `Diff.js`), which\n// this family's props/attributes types don't model at the TS level. Destructuring a `null` with\n// `=== undefined`'s false branch (`{ ...value, field: value.field }`) would throw\n// `TypeError: Cannot read properties of null` the first time a console omits either field —\n// `== null` catches both and returns the value through untouched, matching what the rest of\n// this file already does for every other optional field.\n// ★ EXPORTED (not just used below): `network-drift.ts`'s `driftOf` reuses these exact two\n// normalizers so it can never quietly disagree with `matches` about what counts as a change —\n// see that file's own header.\nexport const normalizeDhcpGuarding = (\n value: networks.NetworkDHCPGuarding | undefined,\n): networks.NetworkDHCPGuarding | undefined =>\n value == null\n ? value\n : { ...value, trustedDhcpServerIpAddresses: sortedSet(value.trustedDhcpServerIpAddresses) };\n\n// ⚠️ `exactOptionalPropertyTypes` (see the props header) means these two subfields must be\n// OMITTED, not set to an explicit `undefined`, when the live/declared value has no override —\n// `NetworkIPv6Configuration` types them `field?: T`, not `field?: T | undefined`. Conditional\n// spread (rather than `field: value.field === undefined ? undefined : sortedSet(...)`) is what\n// keeps an absent key absent.\nexport const normalizeIpv6Configuration = (\n value: networks.NetworkIPv6Configuration | undefined,\n): networks.NetworkIPv6Configuration | undefined =>\n value == null\n ? value\n : {\n ...value,\n ...(value.additionalHostIpSubnets !== undefined\n ? { additionalHostIpSubnets: sortedSet(value.additionalHostIpSubnets) }\n : {}),\n ...(value.dnsServerIpAddressesOverride !== undefined\n ? { dnsServerIpAddressesOverride: sortedSet(value.dnsServerIpAddressesOverride) }\n : {}),\n };\n\n/**\n * ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` ON PURPOSE. `tsconfig.json` sets\n * `exactOptionalPropertyTypes`, which treats `field?: T` (absent-or-T) and\n * `field?: T | undefined` (absent-or-explicitly-undefined) as different types. `declareNetwork`\n * below assigns `live.dhcpGuarding` (typed `T | undefined`) straight through — the whole point\n * of a renderer that echoes a live optional field structurally rather than defaulting it — so\n * the target has to admit an explicit `undefined`, not just omission.\n */\nexport interface NetworkProps {\n siteId: string;\n networkId: string;\n name: string;\n enabled: boolean;\n management: string;\n /** VLAN ID. Must be 1 for the default network and >= 2 for additional networks. */\n vlanId: number;\n dhcpGuarding?: networks.NetworkDHCPGuarding | undefined;\n cellularBackupEnabled?: boolean | undefined;\n internetAccessEnabled?: boolean | undefined;\n /** Opaque — see the header. */\n ipv4Configuration?: unknown;\n ipv6Configuration?: networks.NetworkIPv6Configuration | undefined;\n isolationEnabled?: boolean | undefined;\n mdnsForwardingEnabled?: boolean | undefined;\n zoneId?: string | undefined;\n deviceId?: string | undefined;\n}\n\nexport interface NetworkAttributes {\n siteId: string;\n networkId: string;\n /** Whether this is the site's undeletable default network — server-derived, never declared. */\n default: boolean;\n name: string;\n enabled: boolean;\n management: string;\n vlanId: number;\n dhcpGuarding: networks.NetworkDHCPGuarding | undefined;\n cellularBackupEnabled: boolean | undefined;\n internetAccessEnabled: boolean | undefined;\n ipv4Configuration: unknown;\n ipv6Configuration: networks.NetworkIPv6Configuration | undefined;\n isolationEnabled: boolean | undefined;\n mdnsForwardingEnabled: boolean | undefined;\n zoneId: string | undefined;\n deviceId: string | undefined;\n}\n\nexport const attributesOf = (\n live: networks.NetworkDetails,\n props: NetworkProps,\n): NetworkAttributes => ({\n siteId: props.siteId,\n networkId: live.id,\n default: live.default,\n name: live.name,\n enabled: live.enabled,\n management: live.management,\n vlanId: live.vlanId,\n dhcpGuarding: normalizeDhcpGuarding(live.dhcpGuarding),\n cellularBackupEnabled: live.cellularBackupEnabled,\n internetAccessEnabled: live.internetAccessEnabled,\n ipv4Configuration: live.ipv4Configuration,\n ipv6Configuration: normalizeIpv6Configuration(live.ipv6Configuration),\n isolationEnabled: live.isolationEnabled,\n mdnsForwardingEnabled: live.mdnsForwardingEnabled,\n zoneId: live.zoneId,\n deviceId: live.deviceId,\n});\n\n/**\n * MEDIUM-4 (red team, 2026-09-26): `matches` used to be a hand-written boolean expression here,\n * kept in sync BY HAND with `network-drift.ts`'s separately-maintained field list — two comparisons\n * that could (and, per a red-team mutant, silently did) drift apart. `matches` now lives in\n * `network-drift.ts`, defined as `fieldDrift(...).length === 0` — the SAME comparison `driftOf`\n * runs, not a second one. See that file's own header for the `stripNullish`/normalizer reasoning\n * this expression used to carry.\n */\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getNetworkDetails`'s own response\n * straight in and writes the result into `alchemy.run.ts` as `network('<id>', declareNetwork(live,\n * siteId))` — no field is invented or defaulted here beyond what `attributesOf` already reports,\n * so `matches(attributesOf(live, props), declareNetwork(live, siteId))` is `true` by construction\n * (`network.test.ts` proves it) for any live object this SDK can decode.\n */\nexport const declareNetwork = (live: networks.NetworkDetails, siteId: string): NetworkProps => ({\n siteId,\n networkId: live.id,\n name: live.name,\n enabled: live.enabled,\n management: live.management,\n vlanId: live.vlanId,\n dhcpGuarding: normalizeDhcpGuarding(live.dhcpGuarding),\n cellularBackupEnabled: live.cellularBackupEnabled,\n internetAccessEnabled: live.internetAccessEnabled,\n ipv4Configuration: live.ipv4Configuration,\n ipv6Configuration: normalizeIpv6Configuration(live.ipv6Configuration),\n isolationEnabled: live.isolationEnabled,\n mdnsForwardingEnabled: live.mdnsForwardingEnabled,\n zoneId: live.zoneId,\n deviceId: live.deviceId,\n});\n",
|
|
9
9
|
"/**\n * B6: the field-level drift framework every family's own `driftOf(live, props)` is built on\n * (`network-form.ts`, `firewall-zone-form.ts`, and whatever family follows). Consumed by\n * `homeflare-network`'s pre-import drift check (its own C4, not built here) to show WHICH fields\n * a live object and a committed declaration disagree on, before that script overwrites the\n * committed module — never by a `Unifi.*` provider itself, which only ever needs `matches`'s\n * boolean.\n *\n * ★ BUILT ON `matches`, NOT A SEPARATE COMPARISON. Each family's `driftOf` reuses the SAME\n * normalizers (`sortedSet`, the `dhcpGuarding`/`ipv6Configuration` null-vs-undefined handling)\n * its own `matches` already applies — so this reports NOTHING `matches` would call a noop, and\n * everything `matches` would call `update`, broken down by field instead of collapsed to one\n * boolean. Two comparisons drifting apart would be its own bug class; there is exactly one.\n */\nimport { deepEqual } from 'alchemy/Diff';\n\n/** One field whose live and declared values differ, as `makeDriftOf` reports it. */\nexport interface FieldDrift {\n readonly field: string;\n readonly live: unknown;\n readonly declared: unknown;\n}\n\n/** One field's comparison recipe for `makeDriftOf`. */\nexport interface DriftField<Attributes, Props> {\n readonly field: string;\n readonly live: (attributes: Attributes) => unknown;\n readonly declared: (props: Props) => unknown;\n /**\n * Defaults to `alchemy/Diff`'s `deepEqual` with `stripNullish: true` — the same tolerance\n * every `matches` in this directory already gives UniFi's `null`-for-unset optionals (see\n * `network-form.ts`'s header). Pass one explicitly for a field `matches` compares as a\n * normalized SET (`firewall-zone-form.ts`'s `networkIds`), where plain `deepEqual` would\n * report order as drift `matches` never would.\n */\n readonly equal?: (live: unknown, declared: unknown) => boolean;\n}\n\nconst defaultEqual = (live: unknown, declared: unknown) =>\n deepEqual(live, declared, { stripNullish: true });\n\n/**\n * Builds a `(attributes, props) => FieldDrift[]` from a field list — the framework a family's own\n * `*-form.ts` calls once, then wraps as `driftOf(live, props)` by feeding it `attributesOf(live,\n * props)` (the task's own API: callers have a live SDK object, not `Attributes`, on hand).\n */\nexport const makeDriftOf =\n <Attributes, Props>(fields: readonly DriftField<Attributes, Props>[]) =>\n (attributes: Attributes, props: Props): FieldDrift[] =>\n fields.flatMap(({ field, live, declared, equal = defaultEqual }) => {\n const liveValue = live(attributes);\n const declaredValue = declared(props);\n return equal(liveValue, declaredValue)\n ? []\n : [{ field, live: liveValue, declared: declaredValue }];\n });\n",
|
|
10
10
|
"/**\n * `Unifi.Network`'s `matches` AND `driftOf` (B6) — both built on the ONE field list below, not two\n * hand-kept-in-sync comparisons. `matches` used to be a separate boolean expression in\n * `network-form.ts`; MEDIUM-4 (red team, 2026-09-26) found that a `fieldDrift` entry could be\n * deleted entirely and every test still passed, because `matches` never consulted `fieldDrift` at\n * all. `matches(attrs, props) === (driftOf(live, props).length === 0)` is now true BY\n * CONSTRUCTION — there is exactly one comparison — and every `fieldDrift` entry names ONE field\n * plus its own live/declared value instead of collapsing straight to a boolean.\n *\n * ⚠️ `stripNullish: true` ON EVERY FIELD BY DEFAULT (`drift.ts`'s `defaultEqual`). UniFi's JSON\n * answers `null` for an unset optional field; a declaration that omits the same prop carries\n * `undefined`. Without this, every plan against an object with one unset optional field would\n * report `update` forever — the exact \"read and write are different shapes\" trap\n * `netbox/values.ts` and `proxmox/user-wire.ts` both work around, here solved once by\n * `makeDriftOf`'s own default instead of a per-field coercion.\n */\nimport type * as networks from '@distilled.cloud/unifi-network/networks';\nimport { makeDriftOf } from './drift.ts';\nimport {\n type NetworkAttributes,\n type NetworkProps,\n attributesOf,\n normalizeDhcpGuarding,\n normalizeIpv6Configuration,\n} from './network-form.ts';\n\nconst fieldDrift = makeDriftOf<NetworkAttributes, NetworkProps>([\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n { field: 'enabled', live: (a) => a.enabled, declared: (p) => p.enabled },\n { field: 'management', live: (a) => a.management, declared: (p) => p.management },\n { field: 'vlanId', live: (a) => a.vlanId, declared: (p) => p.vlanId },\n {\n field: 'dhcpGuarding',\n live: (a) => normalizeDhcpGuarding(a.dhcpGuarding),\n declared: (p) => normalizeDhcpGuarding(p.dhcpGuarding),\n },\n {\n field: 'cellularBackupEnabled',\n live: (a) => a.cellularBackupEnabled,\n declared: (p) => p.cellularBackupEnabled,\n },\n {\n field: 'internetAccessEnabled',\n live: (a) => a.internetAccessEnabled,\n declared: (p) => p.internetAccessEnabled,\n },\n {\n field: 'ipv4Configuration',\n live: (a) => a.ipv4Configuration,\n declared: (p) => p.ipv4Configuration,\n },\n {\n field: 'ipv6Configuration',\n live: (a) => normalizeIpv6Configuration(a.ipv6Configuration),\n declared: (p) => normalizeIpv6Configuration(p.ipv6Configuration),\n },\n {\n field: 'isolationEnabled',\n live: (a) => a.isolationEnabled,\n declared: (p) => p.isolationEnabled,\n },\n {\n field: 'mdnsForwardingEnabled',\n live: (a) => a.mdnsForwardingEnabled,\n declared: (p) => p.mdnsForwardingEnabled,\n },\n { field: 'zoneId', live: (a) => a.zoneId, declared: (p) => p.zoneId },\n { field: 'deviceId', live: (a) => a.deviceId, declared: (p) => p.deviceId },\n]);\n\nexport const matches = (attributes: NetworkAttributes, props: NetworkProps): boolean =>\n fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: networks.NetworkDetails, props: NetworkProps) =>\n fieldDrift(attributesOf(live, props), props);\n",
|
|
11
11
|
"/**\n * The write SCOPE of `Unifi.Network`: the one intended consumer is a single VLAN's\n * `ipv6Configuration`, so everything else a whole-object PUT could reach is refused BEFORE any\n * request, with a typed error that carries field names only.\n *\n * ⛔ SETTABLE ALLOWLIST. A key in `patch.set` is written into the PUT body, so only\n * `ipv6Configuration` may be set: `enabled`, `internetAccessEnabled`, `ipv4Configuration`,\n * `dhcpGuarding` and every other declared key are refused with `UnifiFieldNotSettable` (a touched\n * immutable key gets `UnifiImmutableFieldChanged` first). Widening it is a kit change.\n * ⛔ REMOVABLE ALLOWLIST. A key in `patch.unset` is deleted from the PUT body, and the controller\n * reads an omitted `dhcpGuarding`/`ipv4Configuration`/`ipv6Configuration` as \"off\". Dropping a\n * declaration key therefore must not silently turn a live block off: only `ipv6Configuration`\n * (the block this write path exists for, and the revert target) may be removed. A required key\n * (`name`, `vlanId`, …) removed from a declaration would fail the SDK schema or send a body the\n * controller rejects, so it is refused here with the same error.\n * ⛔ `default: true` IS THE MANAGEMENT LAN. Writing it can cut the admin off the console.\n * ⛔ `zoneId`/`vlanId`/`management`/`deviceId` decide where the network lives and what serves it;\n * a patch (set OR unset) touching one is refused. Non-USER `metadata.origin` is deliberately NOT\n * handled yet: no rule exists for it.\n */\nimport type * as networks from '@distilled.cloud/unifi-network/networks';\nimport * as Effect from 'effect/Effect';\nimport type { NetworkProps } from './network-form.ts';\nimport {\n UnifiFieldNotRemovable,\n UnifiFieldNotSettable,\n UnifiImmutableFieldChanged,\n UnifiManagementNetworkRefused,\n} from './policy.ts';\nimport type { Patch } from './update-reconcile.ts';\n\nexport const SETTABLE_KEYS: ReadonlyArray<keyof NetworkProps> = ['ipv6Configuration'];\nexport const REMOVABLE_KEYS: ReadonlyArray<keyof NetworkProps> = ['ipv6Configuration'];\nexport const IMMUTABLE_KEYS: ReadonlyArray<keyof NetworkProps> = [\n 'zoneId',\n 'vlanId',\n 'management',\n 'deviceId',\n];\n\nexport type ScopeError =\n | UnifiFieldNotRemovable\n | UnifiFieldNotSettable\n | UnifiImmutableFieldChanged\n | UnifiManagementNetworkRefused;\n\nconst isIn = (list: ReadonlyArray<string>) => (key: string) => list.includes(key);\n\nexport const checkNetworkWriteScope = (\n live: networks.NetworkDetails,\n patch: Patch<NetworkProps>,\n props: NetworkProps,\n): Effect.Effect<void, ScopeError> => {\n const type = 'Unifi.Network';\n const identity = `sites/${props.siteId}/networks/${props.networkId}`;\n if (live.default === true) {\n return Effect.fail(new UnifiManagementNetworkRefused({ type, identity }));\n }\n const touched = [...Object.keys(patch.set), ...patch.unset.map(String)];\n const immutable = touched.filter(isIn(IMMUTABLE_KEYS as ReadonlyArray<string>));\n if (immutable.length > 0) {\n return Effect.fail(new UnifiImmutableFieldChanged({ type, identity, fields: immutable }));\n }\n const removable = isIn(REMOVABLE_KEYS as ReadonlyArray<string>);\n const notRemovable = patch.unset.map(String).filter((k) => !removable(k));\n if (notRemovable.length > 0) {\n return Effect.fail(new UnifiFieldNotRemovable({ type, identity, fields: notRemovable }));\n }\n const settable = isIn(SETTABLE_KEYS as ReadonlyArray<string>);\n const notSettable = Object.keys(patch.set).filter((k) => !settable(k));\n if (notSettable.length > 0) {\n return Effect.fail(new UnifiFieldNotSettable({ type, identity, fields: notSettable }));\n }\n return Effect.void;\n};\n",
|
|
12
12
|
"/**\n * `Unifi.Network`'s one write: `updateNetwork`. THE ONLY FILE UNDER `src/unifi` ALLOWED TO NAME IT\n * (`write-op-reference.test.ts`'s `WRITE_OP_ALLOWLIST` pins that, and still refuses\n * `createNetwork`/`deleteNetwork` here).\n *\n * ⛔ THE WHOLE-OBJECT-PUT TRAP (packages/distilled-unifi-network/README.md). `PUT .../networks/{id}` disables\n * `dhcpGuarding`/`ipv4Configuration`/`ipv6Configuration` outright when the body omits them. So\n * the body is never `props`: it is the RAW live object (as `fetchLive` decoded it) minus the\n * server fields (`SERVER_FIELDS`: `id`/`default`/`metadata`), plus ONLY the patch (HIGH-2). A\n * hand edit on a field\n * the declaration never touched rides through byte-for-byte; `update-reconcile.ts` has already\n * refused any hand edit on a declared field.\n *\n * ★ WHY RAW LIVE, NOT THE ATTRIBUTES VIEW (LOW-6). `attributesOf` normalizes (sorted sets) and\n * drops keys this kit does not model. Unknown keys must survive the round trip or the PUT would\n * silently delete a setting the console added after the SDK was generated (F2). Measured by\n * reading `@distilled.cloud/core` `protocol-http.ts`: `validateResponse` returns the ORIGINAL\n * decoded value and `mapKeys` passes unknown keys through at every level, so top-level and\n * nested unknown keys both survive; `network-update.test.ts` is the alarm if that ever changes.\n *\n * ★ WHY `unset` EXISTS (HIGH-1). Reverting a declaration (dropping `ipv6Configuration`) must plan\n * and execute a real update back to the pre-image. A key in `patch.unset` is DELETED from the\n * body, which is exactly \"absent = off\" for the optional blocks above.\n *\n * ⛔ NO-OP GUARD. If the merged body equals live (ignoring the ids), the PUT would change nothing;\n * `UnifiUpdateWouldBeNoop` refuses so a normalizer gap can never become a re-PUT loop.\n *\n * ⛔ NO RETRY ON THE PUT (red team round 1, reversing the earlier Q7 decision). The SDK's default\n * policy would replay a write whose first attempt may already have landed (a dropped response\n * during a reprovision); a replay then runs against a controller state this reconcile never\n * read. One attempt, and a failure surfaces for the operator. Reads keep the default policy.\n *\n * ⛔ SCOPE FIRST (`network-scope.ts`): the management LAN, immutable fields and non-removable keys\n * are refused before the body is even built, so none of them can reach the wire.\n */\nimport * as networks from '@distilled.cloud/unifi-network/networks';\nimport * as Retry from '@distilled.cloud/unifi-network/Retry';\nimport { deepEqual } from 'alchemy/Diff';\nimport * as Effect from 'effect/Effect';\nimport { type NetworkProps } from './network-form.ts';\nimport { checkNetworkWriteScope } from './network-scope.ts';\nimport { UnifiUpdateWouldBeNoop } from './policy.ts';\nimport type { Patch } from './update-reconcile.ts';\n\nexport const SERVER_FIELDS = ['id', 'default', 'metadata'] as const;\n\n/**\n * The live object minus `SERVER_FIELDS` — ONE list drives the strip. The destructure this\n * replaced hard-coded the same three names a second time, so a key added to the export was\n * silently NOT stripped (and a removed one still was): two lists that could drift apart. Own\n * enumerable string keys only, exactly what object-rest destructure copies; a decoded JSON body\n * has no symbol keys.\n */\nconst withoutServerFields = (live: networks.NetworkDetails): Record<string, unknown> => {\n const server = new Set<string>(SERVER_FIELDS);\n const rest: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(live)) {\n if (!server.has(key)) rest[key] = value;\n }\n return rest;\n};\n\nexport const mergeForUpdate = (\n live: networks.NetworkDetails,\n patch: Patch<NetworkProps>,\n props: NetworkProps,\n): Effect.Effect<networks.UpdateNetworkRequest, UnifiUpdateWouldBeNoop> => {\n // Raw copy minus the server fields: unknown top-level keys stay in `rest` (F2).\n const rest = withoutServerFields(live);\n const body: Record<string, unknown> = { ...rest, ...patch.set };\n for (const key of patch.unset) delete body[key];\n if (deepEqual(body, rest, { stripNullish: true })) {\n return Effect.fail(\n new UnifiUpdateWouldBeNoop({\n type: 'Unifi.Network',\n identity: `sites/${props.siteId}/networks/${props.networkId}`,\n }),\n );\n }\n return Effect.succeed({\n ...body,\n siteId: props.siteId,\n networkId: live.id,\n } as networks.UpdateNetworkRequest);\n};\n\nexport const writeNetwork = (\n live: networks.NetworkDetails,\n patch: Patch<NetworkProps>,\n props: NetworkProps,\n) =>\n checkNetworkWriteScope(live, patch, props).pipe(\n Effect.andThen(mergeForUpdate(live, patch, props)),\n Effect.flatMap((body) => networks.updateNetwork(body).pipe(Retry.none)),\n );\n",
|
|
13
|
-
"/**\n * One shape for every UniFi Network object — `Unifi.Network`, `Unifi.FirewallZone`, and whatever\n * import adds next. ADOPT-ONLY, except the one `update` a spec may opt into (`Unifi.Network`).\n *\n * ★ MIRRORS `../discord/resource.ts` (S7-S10, H1, H5), NOT `../netbox/resource.ts`. Both are\n * \"marker-less API\" families (S8): UniFi Network objects carry no tag or metadata field this\n * stack could stamp as ownership, so a cold read of a match is `Unowned(attrs)` exactly like\n * `Cloudflare/Snippets/Snippet.ts@v2.0.0-beta.81#read`, the reference implementation S8 and H1\n * both cite. `adopt(true)`, piped on by each resource's convenience constructor (H5), turns\n * that into a silent one-time bind instead of an `OwnedBySomeoneElse` refusal.\n *\n * ⛔ THE POSTURE (`policy.ts`, Tim 2026-09-24, lifted for ONE case 2026-10-10): there is no\n * `create` and no `destroy` in this spec at all, and `update` exists only when a spec carries an\n * `update` block (`update-reconcile.ts`). `reconcile` is `updateReconcile`: refuse a create,\n * report an exact match with zero writes, and otherwise either refuse or run the three-way PUT.\n * A spec without `update` (every family but Network) can only ever refuse or report a match.\n *\n * ⚠️ RECONCILE STILL HAS TO SUCCEED ON AN EXACT-MATCH ADOPTION, NOT JUST REFUSE UNCONDITIONALLY.\n * Measured against `Plan.ts@v2.0.0-beta.81` (`forceUpdateAfterAdoption`, H6): beta.81 forces\n * ONE reconcile call after every cold adoption, whether or not the diff said noop — so a\n * resource whose `reconcile` always failed would break the ordinary \"adopt an unchanged\n * object\" deploy, not just a real write. The fix, exactly like `discordOperations.reconcile`:\n * read the live object again and return it on a match. S10's \"zero write calls when there is\n * no drift\" holds even under the forced post-adoption call.\n *\n * ⚠️ `NotFound` IS FOLDED TO \"ABSENT\" INSIDE EACH RESOURCE FILE'S OWN `fetchLive`, NOT HERE —\n * same split netbox's `resource.ts` uses and for the same reason: `Effect.catchTag`'s\n * tag-literal inference needs a concrete error union to resolve `\"NotFound\"` against, and a\n * spec's `E` is only concrete at each resource file's own call site. Every OTHER failure (a\n * real 5xx, a network error, `Forbidden`) is NOT folded anywhere — it fails the plan loudly,\n * per the task's own rule: only a genuine not-found may mean absent.\n *\n * ★ The wire guard (`wire-guard.ts`) and the three-way reconcile (`update-reconcile.ts`) live in\n * their own files; `read`/`diff`/`delete` run under `GetOnlyHttpClient`, only `reconcile` gets\n * the per-row allow entry of a spec that declares `update`.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { Credentials, CredentialsFromEnv } from '@distilled.cloud/unifi-network/Credentials';\nimport type { UnifiNetworkOpContext } from '@distilled.cloud/unifi-network/Protocol';\nimport * as Effect from 'effect/Effect';\nimport type * as HttpClient from 'effect/http/HttpClient';\nimport { refuseWrite } from './policy.ts';\nimport { type ReconcileArgs, type UnifiUpdate, updateReconcile } from './update-reconcile.ts';\nimport { type AllowedWrite, guardedHttpClient } from './wire-guard.ts';\n\n/** What every handler needs from the caller's runtime once `CredentialsFromEnv` is baked in. */\nexport type UnifiRequirements = HttpClient.HttpClient;\n\n/**\n * One UniFi Network object's read. `Live` is whatever the SDK decodes (a `NetworkDetails`, a\n * `FirewallZone`, …); `E` is left to each resource file to declare (its own `fetchLive`'s error\n * union, with `NotFound` already folded away) so the precise per-operation error union flows\n * through instead of being widened by hand here. `E2` is the error union of the optional write.\n */\nexport type UnifiSpec<Props extends object, Live, Attributes extends object, E, E2 = never> = {\n /** The vendor type string, e.g. `Unifi.Network` — for the refusal message only. */\n readonly type: string;\n /** Already folds a genuine not-found to `undefined`; nothing else is folded (see header). */\n readonly fetchLive: (props: Props) => Effect.Effect<Live | undefined, E, UnifiNetworkOpContext>;\n readonly attributes: (live: Live, props: Props) => Attributes;\n readonly matches: (attributes: Attributes, props: Props) => boolean;\n /** For the refusal message and read-back failures. */\n readonly describe: (props: Props) => string;\n /** Present only on a family allowed to write (`Unifi.Network`); see `update-reconcile.ts`. */\n readonly update?: UnifiUpdate<Props, Live, E2>;\n};\n\nexport const unifiOperations = <Props extends object, Live, Attributes extends object, E, E2>(\n spec: UnifiSpec<Props, Live, Attributes, E, E2>,\n) => {\n const read = (props: Props) =>\n spec\n .fetchLive(props)\n .pipe(Effect.map((live) => (live === undefined ? undefined : spec.attributes(live, props))));\n\n return {\n read,\n\n readHandler: ({ olds, output }: { olds: Props; output: Attributes | undefined }) =>\n Effect.gen(function* () {\n const attrs = yield* read(olds);\n if (attrs === undefined) return undefined;\n // Warm read (persisted `output` says a prior apply already adopted this exact object):\n // plain attributes. Cold read (fresh store, or state lost): no ownership marker on this\n // vendor exists to tell \"ours\" from \"someone else's\" apart, so Unowned until adopt(true).\n return output !== undefined ? attrs : Unowned(attrs);\n }),\n\n diff: (news: Input<Props>, output: Attributes | undefined) =>\n Effect.gen(function* () {\n if (output === undefined || !isResolved(news)) return undefined;\n const live = yield* read(news);\n if (live === undefined) return { action: 'update' } as const;\n return spec.matches(live, news)\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n /** Pre-state callers (tests, adopt-only probes) pass just `news`: `olds`/`output` unset. */\n reconcile: (news: Props, rest: Partial<Omit<ReconcileArgs<Props>, 'news'>> = {}) =>\n updateReconcile(spec, {\n news,\n olds: rest.olds,\n output: rest.output,\n note: rest.note ?? (() => Effect.void),\n }),\n\n destroy: (olds: Props) => refuseWrite(spec.type, spec.describe(olds), 'delete'),\n };\n};\n\n/**\n * The four provider handlers for a spec'd UniFi object, wired once.\n *\n * ⛔ `list` ANSWERS EMPTY. `GET /v1/sites/{siteId}/networks` answers every network on the site;\n * adoption stays explicit, the same reasoning `Proxmox.User`'s and NetBox's `list` give.\n */\nexport const unifiHandlers = <Props extends object, Live, Attributes extends object, E, E2>(\n spec: UnifiSpec<Props, Live, Attributes, E, E2>,\n) => {\n const ops = unifiOperations(spec);\n const withCredentials = <A, Err>(\n effect: Effect.Effect<A, Err, UnifiNetworkOpContext>,\n allow: ReadonlyArray<AllowedWrite> = [],\n ) =>\n Effect.gen(function* () {\n // ⛔ The guard anchors the allowed PUT to the CONFIGURED base URL (`wire-guard.ts`), so it is\n // read from the same credentials the SDK uses.\n const { apiBaseUrl } = yield* yield* Credentials;\n return yield* effect.pipe(Effect.provide(guardedHttpClient(allow, apiBaseUrl)));\n }).pipe(Effect.provide(CredentialsFromEnv)) as Effect.Effect<A, Err, HttpClient.HttpClient>;\n\n return {\n list: () => Effect.succeed([]),\n read: (args: { olds: Props; output: Attributes | undefined }) =>\n withCredentials(ops.readHandler(args)),\n diff: (args: { news: Input<Props>; output: Attributes | undefined }) =>\n withCredentials(ops.diff(args.news, args.output)),\n // ⛔ ONLY `reconcile` gets the per-row allow entry, and only when the spec declares `update`.\n reconcile: (args: {\n news: Props;\n olds: Props | undefined;\n output: Attributes | undefined;\n session: { note: (message: string) => Effect.Effect<void, never, never> };\n }) =>\n withCredentials(\n ops.reconcile(args.news, {\n olds: args.olds,\n output: args.output,\n note: (message) => args.session.note(message),\n }),\n // ⛔ From `news` (the ids the write will use), never `olds`; reconcile also refuses when\n // `olds` names another object (`update-reconcile.ts`).\n spec.update ? [spec.update.allowedWrite(args.news)] : [],\n ),\n delete: (args: { olds: Props }) => withCredentials(ops.destroy(args.olds)),\n };\n};\n",
|
|
14
|
-
"/**\n * The three-way `reconcile` every updatable `Unifi.*` family shares (today: `Unifi.Network`).\n *\n * ⛔ THREE INPUTS, NOT TWO. `news` is the declaration now, `olds` is the declaration the LAST\n * DEPLOY wrote (Alchemy state), `live` is the raw object read right now. The PUT body is built\n * from `live` plus ONLY the fields where `news` differs from `olds` (`computePatch`) — never\n * from `news` alone. So (HIGH-2) a hand edit on a field the declaration never touched is carried\n * through unchanged instead of reverted, and a hand edit on ANY declared field is refused\n * outright (step 4) rather than overwritten.\n *\n * ★ `unset` IS WHAT MAKES A REVERT REAL (HIGH-1). Deleting an override from a declaration makes\n * `news[k]` absent while `olds[k]` is set; a patch that only carried `set` would see \"nothing\n * changed\" and the revert would never write. A key present in `olds` and null/absent in `news`\n * lands in `unset`, and the family's `write` removes it from the body.\n *\n * ⛔ FIRST DEPLOY IS ADOPT-ONLY BY CONSTRUCTION (F1, LOW-8). Without BOTH `olds` and `output` there\n * is no last-deployed baseline to patch against, so the write refuses. That also covers beta.81's\n * forced post-adoption reconcile (H6): on a match (step 2) it returns live with zero writes.\n * `olds` must also name the SAME object as `news` (`UnifiIdentityChanged`).\n *\n * ⛔ SCOPE (`update.checkScope`) runs before the note and any request: the family decides what a\n * patch may touch (`network-scope.ts`). An omitted optional block is turned OFF by a whole-object\n * PUT, so removing a key is allowed only for an explicit allowlist.\n *\n * ⚠️ NOTES AND ERRORS CARRY FIELD NAMES ONLY, NEVER VALUES.\n */\nimport { deepEqual } from 'alchemy/Diff';\nimport type { UnifiNetworkOpContext } from '@distilled.cloud/unifi-network/Protocol';\nimport * as Effect from 'effect/Effect';\nimport type { FieldDrift } from './drift.ts';\nimport {\n UnifiIdentityChanged,\n UnifiLiveDriftedSinceDeploy,\n UnifiUpdateDidNotConverge,\n refuseWrite,\n} from './policy.ts';\nimport type { UnifiSpec } from './resource.ts';\nimport type { AllowedWrite } from './wire-guard.ts';\n\nexport type Patch<Props> = { set: Partial<Props>; unset: ReadonlyArray<keyof Props> };\n\n/** The write half of a spec: everything needed to PUT one object, and nothing to create/delete. */\nexport type UnifiUpdate<Props extends object, Live, E2> = {\n /** The one PUT the wire guard may pass for this row (path pinned to the row's own ids). */\n readonly allowedWrite: (props: Props) => AllowedWrite;\n /** Declarable keys; never the identity keys. */\n readonly patchKeys: ReadonlyArray<keyof Props>;\n readonly driftOf: (live: Live, props: Props) => ReadonlyArray<FieldDrift>;\n /** Refuses (typed, before any note or request) a patch outside this family's write scope. */\n readonly checkScope: (live: Live, patch: Patch<Props>, props: Props) => Effect.Effect<void, E2>;\n readonly write: (\n live: Live,\n patch: Patch<Props>,\n props: Props,\n ) => Effect.Effect<Live, E2, UnifiNetworkOpContext>;\n};\n\n/** Keys whose `news` value differs from `olds`: set when still declared, unset when dropped. */\nexport const computePatch = <Props extends object>(\n news: Props,\n olds: Props,\n patchKeys: ReadonlyArray<keyof Props>,\n): Patch<Props> => {\n const set: Partial<Props> = {};\n const unset: Array<keyof Props> = [];\n for (const key of patchKeys) {\n if (deepEqual(news[key], olds[key], { stripNullish: true })) continue;\n if (news[key] == null) unset.push(key);\n else set[key] = news[key];\n }\n return { set, unset };\n};\n\nexport type ReconcileArgs<Props extends object> = {\n readonly news: Props;\n readonly olds: Props | undefined;\n readonly output: unknown;\n readonly note: (message: string) => Effect.Effect<void>;\n};\n\nexport const updateReconcile = <Props extends object, Live, Attributes extends object, E, E2>(\n spec: UnifiSpec<Props, Live, Attributes, E, E2>,\n { news, olds, output, note }: ReconcileArgs<Props>,\n) =>\n Effect.gen(function* () {\n const identity = spec.describe(news);\n const live = yield* spec.fetchLive(news);\n if (live === undefined) return yield* refuseWrite(spec.type, identity, 'create');\n const attrs = spec.attributes(live, news);\n // Exact match: zero writes, correct for a routine deploy and the forced post-adoption call.\n if (spec.matches(attrs, news)) return attrs;\n const update = spec.update;\n if (update === undefined || olds === undefined || output === undefined) {\n return yield* refuseWrite(spec.type, identity, 'update');\n }\n // ⛔ `olds` is the baseline for THIS object only; another site or network id is no baseline.\n const previous = spec.describe(olds);\n if (previous !== identity) {\n return yield* new UnifiIdentityChanged({ type: spec.type, identity, previous });\n }\n const drifted = update.driftOf(live, olds).map((d) => d.field);\n if (drifted.length > 0) {\n return yield* new UnifiLiveDriftedSinceDeploy({\n type: spec.type,\n identity,\n fields: drifted,\n });\n }\n const patch = computePatch(news, olds, update.patchKeys);\n if (Object.keys(patch.set).length === 0 && patch.unset.length === 0) {\n return yield* refuseWrite(spec.type, identity, 'update');\n }\n yield* update.checkScope(live, patch, news);\n yield* note(\n `${spec.type} ${identity}: PUT changes [${Object.keys(patch.set).join(', ')}] ` +\n `removes [${patch.unset.map(String).join(', ')}]`,\n );\n const written = yield* update.write(live, patch, news);\n const writtenAttrs = spec.attributes(written, news);\n if (spec.matches(writtenAttrs, news)) return writtenAttrs;\n return yield* new UnifiUpdateDidNotConverge({\n type: spec.type,\n identity,\n fields: update.driftOf(written, news).map((d) => d.field),\n });\n });\n",
|
|
13
|
+
"/**\n * One shape for every UniFi Network object — `Unifi.Network`, `Unifi.FirewallZone`, and whatever\n * import adds next. ADOPT-ONLY, except the one `update` a spec may opt into (`Unifi.Network`).\n *\n * ★ MIRRORS `../discord/resource.ts` (S7-S10, H1, H5), NOT `../netbox/resource.ts`. Both are\n * \"marker-less API\" families (S8): UniFi Network objects carry no tag or metadata field this\n * stack could stamp as ownership, so a cold read of a match is `Unowned(attrs)` exactly like\n * `Cloudflare/Snippets/Snippet.ts@v2.0.0-beta.81#read`, the reference implementation S8 and H1\n * both cite. `adopt(true)`, piped on by each resource's convenience constructor (H5), turns\n * that into a silent one-time bind instead of an `OwnedBySomeoneElse` refusal.\n *\n * ⛔ THE POSTURE (`policy.ts`, Tim 2026-09-24, lifted for TWO cases since): there is no `destroy`\n * in this spec at all, ever. `update` exists only when a spec carries an `update` block\n * (`update-reconcile.ts`'s `updateReconcile`, `Unifi.Network` only). `create` exists only when a\n * spec carries a `create` block (`update-reconcile.ts`'s `createReconcile`, `Unifi.FirewallPolicy`\n * only, and only for a row whose declared name passes `create.checkScope`). `reconcile`: an exact\n * match reports with zero writes; otherwise a family with `create` and no prior state (`olds`/\n * `output` both absent) AND an absent live read attempts the create branch; everything else\n * either refuses or runs the three-way PUT. A spec with neither block can only ever refuse or\n * report a match.\n *\n * ⚠️ RECONCILE STILL HAS TO SUCCEED ON AN EXACT-MATCH ADOPTION, NOT JUST REFUSE UNCONDITIONALLY.\n * Measured against `Plan.ts@v2.0.0-beta.81` (`forceUpdateAfterAdoption`, H6): beta.81 forces\n * ONE reconcile call after every cold adoption, whether or not the diff said noop — so a\n * resource whose `reconcile` always failed would break the ordinary \"adopt an unchanged\n * object\" deploy, not just a real write. The fix, exactly like `discordOperations.reconcile`:\n * read the live object again and return it on a match. S10's \"zero write calls when there is\n * no drift\" holds even under the forced post-adoption call.\n *\n * ⚠️ `NotFound` IS FOLDED TO \"ABSENT\" INSIDE EACH RESOURCE FILE'S OWN `fetchLive`, NOT HERE —\n * same split netbox's `resource.ts` uses and for the same reason: `Effect.catchTag`'s\n * tag-literal inference needs a concrete error union to resolve `\"NotFound\"` against, and a\n * spec's `E` is only concrete at each resource file's own call site. Every OTHER failure (a\n * real 5xx, a network error, `Forbidden`) is NOT folded anywhere — it fails the plan loudly,\n * per the task's own rule: only a genuine not-found may mean absent.\n *\n * ★ The wire guard (`wire-guard.ts`) and the three-way reconcile (`update-reconcile.ts`) live in\n * their own files; `read`/`diff`/`delete` run under `GetOnlyHttpClient`, only `reconcile` gets\n * the per-row allow entry of a spec that declares `update`.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { Credentials, CredentialsFromEnv } from '@distilled.cloud/unifi-network/Credentials';\nimport type { UnifiNetworkOpContext } from '@distilled.cloud/unifi-network/Protocol';\nimport * as Effect from 'effect/Effect';\nimport type * as HttpClient from 'effect/http/HttpClient';\nimport { refuseWrite } from './policy.ts';\nimport {\n type ReconcileArgs,\n type UnifiCreate,\n type UnifiUpdate,\n createReconcile,\n updateReconcile,\n} from './update-reconcile.ts';\nimport { type AllowedWrite, guardedHttpClient } from './wire-guard.ts';\n\n/** What every handler needs from the caller's runtime once `CredentialsFromEnv` is baked in. */\nexport type UnifiRequirements = HttpClient.HttpClient;\n\n/**\n * One UniFi Network object's read. `Live` is whatever the SDK decodes (a `NetworkDetails`, a\n * `FirewallZone`, …); `E` is left to each resource file to declare (its own `fetchLive`'s error\n * union, with `NotFound` already folded away) so the precise per-operation error union flows\n * through instead of being widened by hand here. `E2` is the error union of the optional write.\n */\nexport type UnifiSpec<\n Props extends object,\n Live,\n Attributes extends object,\n E,\n E2 = never,\n E3 = never,\n> = {\n /** The vendor type string, e.g. `Unifi.Network` — for the refusal message only. */\n readonly type: string;\n /** Already folds a genuine not-found to `undefined`; nothing else is folded (see header). */\n readonly fetchLive: (props: Props) => Effect.Effect<Live | undefined, E, UnifiNetworkOpContext>;\n readonly attributes: (live: Live, props: Props) => Attributes;\n readonly matches: (attributes: Attributes, props: Props) => boolean;\n /** For the refusal message and read-back failures. */\n readonly describe: (props: Props) => string;\n /** Present only on a family allowed to write (`Unifi.Network`); see `update-reconcile.ts`. */\n readonly update?: UnifiUpdate<Props, Live, E2>;\n /** Present only on a family allowed to create (`Unifi.FirewallPolicy`, `hf:` rows only); see\n * `update-reconcile.ts`'s `createReconcile`. */\n readonly create?: UnifiCreate<Props, Live, E3>;\n};\n\nexport const unifiOperations = <Props extends object, Live, Attributes extends object, E, E2, E3>(\n spec: UnifiSpec<Props, Live, Attributes, E, E2, E3>,\n) => {\n const read = (props: Props) =>\n spec\n .fetchLive(props)\n .pipe(Effect.map((live) => (live === undefined ? undefined : spec.attributes(live, props))));\n\n return {\n read,\n\n readHandler: ({ olds, output }: { olds: Props; output: Attributes | undefined }) =>\n Effect.gen(function* () {\n const attrs = yield* read(olds);\n if (attrs === undefined) return undefined;\n // Warm read (persisted `output` says a prior apply already adopted this exact object):\n // plain attributes. Cold read (fresh store, or state lost): no ownership marker on this\n // vendor exists to tell \"ours\" from \"someone else's\" apart, so Unowned until adopt(true).\n return output !== undefined ? attrs : Unowned(attrs);\n }),\n\n diff: (news: Input<Props>, output: Attributes | undefined) =>\n Effect.gen(function* () {\n if (output === undefined || !isResolved(news)) return undefined;\n const live = yield* read(news);\n if (live === undefined) return { action: 'update' } as const;\n return spec.matches(live, news)\n ? ({ action: 'noop' } as const)\n : ({ action: 'update' } as const);\n }),\n\n /**\n * Pre-state callers (tests, adopt-only probes) pass just `news`: `olds`/`output` unset — the\n * SAME calling convention a plain cold adoption already uses (H6), so the existence GET must\n * run FIRST, for every name, exactly as it always has: a row that already exists is adopted\n * (exact match, zero writes) regardless of its name, and ONLY an absent row ever reaches\n * `create.checkScope`. \"A non-hf: create sends nothing\" therefore means no WRITE (no\n * POST/PUT/DELETE) — the same guarantee every other refusal in this engine gives, and the one\n * `wire-guard.ts`'s allow list enforces at the wire; the existence GET is not a write.\n *\n * ⛔ THE CREATE BRANCH ONLY RUNS WHEN THERE IS NO PRIOR STATE AT ALL (`olds`/`output` both\n * absent — a genuine first deploy, never a re-declare of an adopted row) AND the family\n * declares `create` AND the live read comes back absent. A row that already exists (someone\n * else's object with a colliding identity, or a retried deploy that already created it)\n * takes the ordinary `updateReconcile` path instead — exact match reports with zero writes,\n * anything else refuses (there is no `olds` to PUT a patch against, so it can never silently\n * fall through to an update of an existing row).\n */\n reconcile: (news: Props, rest: Partial<Omit<ReconcileArgs<Props>, 'news'>> = {}) => {\n const note = rest.note ?? (() => Effect.void);\n const create = spec.create;\n if (create !== undefined && rest.olds === undefined && rest.output === undefined) {\n return Effect.gen(function* () {\n const live = yield* spec.fetchLive(news);\n return live === undefined\n ? yield* createReconcile(spec, news, note)\n : yield* updateReconcile(spec, { news, olds: undefined, output: undefined, note });\n });\n }\n return updateReconcile(spec, { news, olds: rest.olds, output: rest.output, note });\n },\n\n destroy: (olds: Props) => refuseWrite(spec.type, spec.describe(olds), 'delete'),\n };\n};\n\n/**\n * The four provider handlers for a spec'd UniFi object, wired once.\n *\n * ⛔ `list` ANSWERS EMPTY. `GET /v1/sites/{siteId}/networks` answers every network on the site;\n * adoption stays explicit, the same reasoning `Proxmox.User`'s and NetBox's `list` give.\n */\nexport const unifiHandlers = <Props extends object, Live, Attributes extends object, E, E2, E3>(\n spec: UnifiSpec<Props, Live, Attributes, E, E2, E3>,\n) => {\n const ops = unifiOperations(spec);\n const withCredentials = <A, Err>(\n effect: Effect.Effect<A, Err, UnifiNetworkOpContext>,\n allow: ReadonlyArray<AllowedWrite> = [],\n ) =>\n Effect.gen(function* () {\n // ⛔ The guard anchors the allowed PUT to the CONFIGURED base URL (`wire-guard.ts`), so it is\n // read from the same credentials the SDK uses.\n const { apiBaseUrl } = yield* yield* Credentials;\n return yield* effect.pipe(Effect.provide(guardedHttpClient(allow, apiBaseUrl)));\n }).pipe(Effect.provide(CredentialsFromEnv)) as Effect.Effect<A, Err, HttpClient.HttpClient>;\n\n return {\n list: () => Effect.succeed([]),\n read: (args: { olds: Props; output: Attributes | undefined }) =>\n withCredentials(ops.readHandler(args)),\n diff: (args: { news: Input<Props>; output: Attributes | undefined }) =>\n withCredentials(ops.diff(args.news, args.output)),\n // ⛔ ONLY `reconcile` gets the per-row allow entry, and only when the spec declares `update`.\n reconcile: (args: {\n news: Props;\n olds: Props | undefined;\n output: Attributes | undefined;\n session: { note: (message: string) => Effect.Effect<void, never, never> };\n }) =>\n withCredentials(\n ops.reconcile(args.news, {\n olds: args.olds,\n output: args.output,\n note: (message) => args.session.note(message),\n }),\n // ⛔ From `news` (the ids the write will use), never `olds`; reconcile also refuses when\n // `olds` names another object (`update-reconcile.ts`). The create entry (a POST to the\n // site's fixed collection route, never a per-row path) is listed ONLY when there is no\n // prior state at all — the exact same condition `ops.reconcile` itself gates the create\n // branch on, so the wire guard never allows a POST wider than what the engine could ever\n // attempt. The hf: NAME check is NOT this list's job (`create.checkScope`'s, before the\n // POST): this only bounds METHOD and PATH.\n [\n ...(spec.update ? [spec.update.allowedWrite(args.news)] : []),\n ...(spec.create && args.olds === undefined && args.output === undefined\n ? [spec.create.allowedWrite(args.news)]\n : []),\n ],\n ),\n delete: (args: { olds: Props }) => withCredentials(ops.destroy(args.olds)),\n };\n};\n",
|
|
14
|
+
"/**\n * The three-way `reconcile` every updatable `Unifi.*` family shares (today: `Unifi.Network`).\n *\n * ⛔ THREE INPUTS, NOT TWO. `news` is the declaration now, `olds` is the declaration the LAST\n * DEPLOY wrote (Alchemy state), `live` is the raw object read right now. The PUT body is built\n * from `live` plus ONLY the fields where `news` differs from `olds` (`computePatch`) — never\n * from `news` alone. So (HIGH-2) a hand edit on a field the declaration never touched is carried\n * through unchanged instead of reverted, and a hand edit on ANY declared field is refused\n * outright (step 4) rather than overwritten.\n *\n * ★ `unset` IS WHAT MAKES A REVERT REAL (HIGH-1). Deleting an override from a declaration makes\n * `news[k]` absent while `olds[k]` is set; a patch that only carried `set` would see \"nothing\n * changed\" and the revert would never write. A key present in `olds` and null/absent in `news`\n * lands in `unset`, and the family's `write` removes it from the body.\n *\n * ⛔ FIRST DEPLOY IS ADOPT-ONLY BY CONSTRUCTION (F1, LOW-8). Without BOTH `olds` and `output` there\n * is no last-deployed baseline to patch against, so the write refuses. That also covers beta.81's\n * forced post-adoption reconcile (H6): on a match (step 2) it returns live with zero writes.\n * `olds` must also name the SAME object as `news` (`UnifiIdentityChanged`).\n *\n * ⛔ SCOPE (`update.checkScope`) runs before the note and any request: the family decides what a\n * patch may touch (`network-scope.ts`). An omitted optional block is turned OFF by a whole-object\n * PUT, so removing a key is allowed only for an explicit allowlist.\n *\n * ⚠️ NOTES AND ERRORS CARRY FIELD NAMES ONLY, NEVER VALUES.\n */\nimport { deepEqual } from 'alchemy/Diff';\nimport type { UnifiNetworkOpContext } from '@distilled.cloud/unifi-network/Protocol';\nimport * as Effect from 'effect/Effect';\nimport type { FieldDrift } from './drift.ts';\nimport {\n UnifiCreateDidNotConverge,\n UnifiIdentityChanged,\n UnifiLiveDriftedSinceDeploy,\n UnifiUpdateDidNotConverge,\n refuseWrite,\n} from './policy.ts';\nimport type { UnifiSpec } from './resource.ts';\nimport type { AllowedWrite } from './wire-guard.ts';\n\nexport type Patch<Props> = { set: Partial<Props>; unset: ReadonlyArray<keyof Props> };\n\n/** The write half of a spec: everything needed to PUT one object, and nothing to create/delete. */\nexport type UnifiUpdate<Props extends object, Live, E2> = {\n /** The one PUT the wire guard may pass for this row (path pinned to the row's own ids). */\n readonly allowedWrite: (props: Props) => AllowedWrite;\n /** Declarable keys; never the identity keys. */\n readonly patchKeys: ReadonlyArray<keyof Props>;\n readonly driftOf: (live: Live, props: Props) => ReadonlyArray<FieldDrift>;\n /** Refuses (typed, before any note or request) a patch outside this family's write scope. */\n readonly checkScope: (live: Live, patch: Patch<Props>, props: Props) => Effect.Effect<void, E2>;\n readonly write: (\n live: Live,\n patch: Patch<Props>,\n props: Props,\n ) => Effect.Effect<Live, E2, UnifiNetworkOpContext>;\n};\n\n/** Keys whose `news` value differs from `olds`: set when still declared, unset when dropped. */\nexport const computePatch = <Props extends object>(\n news: Props,\n olds: Props,\n patchKeys: ReadonlyArray<keyof Props>,\n): Patch<Props> => {\n const set: Partial<Props> = {};\n const unset: Array<keyof Props> = [];\n for (const key of patchKeys) {\n if (deepEqual(news[key], olds[key], { stripNullish: true })) continue;\n if (news[key] == null) unset.push(key);\n else set[key] = news[key];\n }\n return { set, unset };\n};\n\nexport type ReconcileArgs<Props extends object> = {\n readonly news: Props;\n readonly olds: Props | undefined;\n readonly output: unknown;\n readonly note: (message: string) => Effect.Effect<void>;\n};\n\nexport const updateReconcile = <Props extends object, Live, Attributes extends object, E, E2, E3>(\n spec: UnifiSpec<Props, Live, Attributes, E, E2, E3>,\n { news, olds, output, note }: ReconcileArgs<Props>,\n) =>\n Effect.gen(function* () {\n const identity = spec.describe(news);\n const live = yield* spec.fetchLive(news);\n if (live === undefined) return yield* refuseWrite(spec.type, identity, 'create');\n const attrs = spec.attributes(live, news);\n // Exact match: zero writes, correct for a routine deploy and the forced post-adoption call.\n if (spec.matches(attrs, news)) return attrs;\n const update = spec.update;\n if (update === undefined || olds === undefined || output === undefined) {\n return yield* refuseWrite(spec.type, identity, 'update');\n }\n // ⛔ `olds` is the baseline for THIS object only; another site or network id is no baseline.\n const previous = spec.describe(olds);\n if (previous !== identity) {\n return yield* new UnifiIdentityChanged({ type: spec.type, identity, previous });\n }\n const drifted = update.driftOf(live, olds).map((d) => d.field);\n if (drifted.length > 0) {\n return yield* new UnifiLiveDriftedSinceDeploy({\n type: spec.type,\n identity,\n fields: drifted,\n });\n }\n const patch = computePatch(news, olds, update.patchKeys);\n if (Object.keys(patch.set).length === 0 && patch.unset.length === 0) {\n return yield* refuseWrite(spec.type, identity, 'update');\n }\n yield* update.checkScope(live, patch, news);\n yield* note(\n `${spec.type} ${identity}: PUT changes [${Object.keys(patch.set).join(', ')}] ` +\n `removes [${patch.unset.map(String).join(', ')}]`,\n );\n const written = yield* update.write(live, patch, news);\n const writtenAttrs = spec.attributes(written, news);\n if (spec.matches(writtenAttrs, news)) return writtenAttrs;\n return yield* new UnifiUpdateDidNotConverge({\n type: spec.type,\n identity,\n fields: update.driftOf(written, news).map((d) => d.field),\n });\n });\n\n/**\n * The write half of a CREATE-capable family: today, only `Unifi.FirewallPolicy`, and only for an\n * `hf:`-named row (`policy.ts`'s `isHfPolicyName`). Deliberately NOT a merge-with-live update —\n * there is no live object yet — so there is no `patchKeys`/`checkScope(live, patch, …)` here: a\n * create sends exactly the fields the declaration owns, nothing echoed from elsewhere, nothing to\n * accidentally reset.\n *\n * ⛔ `checkScope` RUNS BEFORE THE POST (same ordering `update.checkScope` already has): a\n * non-`hf:` name is refused here, typed, never reaching the wire at all.\n * ★ `identify` CLOSES THE LOOP FROM THE POST'S RESPONSE TO A FRESH GET. The vendor assigns the id\n * on create; `write`'s response carries it, but a response body is not proof the object is\n * actually readable back (`createReconcile` re-fetches with the id `identify` folds in, rather\n * than trusting the POST's own echo — same reasoning `network-update.ts`'s header gives for\n * never trusting a write's own response over a real read).\n */\nexport type UnifiCreate<Props extends object, Live, E3> = {\n /** The one POST the wire guard may pass for this family (the site's fixed collection route,\n * built from `props` the same way `UnifiUpdate.allowedWrite` reads a row's ids from `props`). */\n readonly allowedWrite: (props: Props) => AllowedWrite;\n /** Refuses (typed, before any note or request) a create outside this family's write scope. */\n readonly checkScope: (props: Props) => Effect.Effect<void, E3>;\n readonly write: (props: Props) => Effect.Effect<Live, E3, UnifiNetworkOpContext>;\n /** `props` plus the vendor id the POST's response assigned, for the read-back `fetchLive`. */\n readonly identify: (props: Props, written: Live) => Props;\n readonly driftOf: (live: Live, props: Props) => ReadonlyArray<FieldDrift>;\n};\n\n/**\n * CREATE for a family whose live read came back absent AND which has no prior Alchemy state\n * (`resource.ts`'s `unifiOperations.reconcile` is the only caller, and only under that same\n * condition — see its own header). Refuses (untyped-to-typed, same `refuseWrite` every other\n * refusal uses) when the family declares no `create` at all; `create.checkScope` then refuses\n * again, more specifically, for a declared row the create scope itself does not allow.\n */\nexport const createReconcile = <Props extends object, Live, Attributes extends object, E, E2, E3>(\n spec: UnifiSpec<Props, Live, Attributes, E, E2, E3>,\n news: Props,\n note: (message: string) => Effect.Effect<void, never, never>,\n) =>\n Effect.gen(function* () {\n const identity = spec.describe(news);\n const create = spec.create;\n if (create === undefined) return yield* refuseWrite(spec.type, identity, 'create');\n yield* create.checkScope(news);\n yield* note(`${spec.type} ${identity}: POST create`);\n const written = yield* create.write(news);\n const withId = create.identify(news, written);\n // ⛔ A FRESH GET, NOT THE POST'S OWN RESPONSE (see the header): `fetchLive` is the same read\n // every adoption and every later plan uses, so the convergence check and every future read\n // of this row go through one path.\n const confirmed = yield* spec.fetchLive(withId);\n if (confirmed === undefined) {\n return yield* Effect.die(\n new Error(\n `${spec.type} ${identity}: the create POST answered, but the read-back found nothing ` +\n 'at the id it assigned. The object may or may not exist on the console; re-import ' +\n 'before trying again rather than retrying this create blindly.',\n ),\n );\n }\n const attrs = spec.attributes(confirmed, withId);\n if (spec.matches(attrs, withId)) return attrs;\n return yield* new UnifiCreateDidNotConverge({\n type: spec.type,\n identity,\n fields: create.driftOf(confirmed, withId).map((d) => d.field),\n });\n });\n",
|
|
15
15
|
"/**\n * `Unifi.FirewallZone` — one firewall zone on a UniFi Network site, READ-ONLY (`policy.ts`).\n *\n * ★ WHY FIREWALL ZONE IS IN THE FIRST IMPORT SET AND `Unifi.FirewallPolicy` IS NOT.\n * `getFirewallZone`/`getFirewallZones` (`firewall.ts`) is a list+get pair over genuine,\n * simply-shaped configuration: `{id, metadata, name, networkIds}`, four fields, no nested\n * discriminated filter. `FirewallPolicy` (same file) is also list+get and also configuration,\n * but its `source`/`destination`/`ipProtocolScope` fields carry several of `docs/unifi-api-notes.md`'s\n * \"converter-flattened discriminator variants\" each, and `firewall/policies/ordering` replaces\n * the whole ordered list — modelling those correctly is a bigger, separate PR (`network.ts`'s\n * header note names it explicitly) rather than something to rush alongside this one.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport * as Effect from 'effect/Effect';\nimport {\n type FirewallZoneAttributes,\n type FirewallZoneProps,\n attributesOf,\n matches,\n} from './firewall-zone-form.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { FirewallZoneAttributes, FirewallZoneProps } from './firewall-zone-form.ts';\nexport { declareFirewallZone, driftOf } from './firewall-zone-form.ts';\n\nexport interface UnifiFirewallZone extends Resource<\n 'Unifi.FirewallZone',\n FirewallZoneProps,\n FirewallZoneAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiFirewallZone = Resource<UnifiFirewallZone>('Unifi.FirewallZone', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `firewallZone('lan', props)` — `adopt(true)` piped on by default (H5); see `network.ts`. */\nexport const firewallZone = (id: string, props: FirewallZoneProps) =>\n UnifiFirewallZone(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n FirewallZoneProps,\n firewall.FirewallZone,\n FirewallZoneAttributes,\n firewall.GetFirewallZoneError\n> = {\n type: 'Unifi.FirewallZone',\n describe: (props) => `sites/${props.siteId}/firewall/zones/${props.firewallZoneId}`,\n fetchLive: (props) =>\n firewall\n .getFirewallZone({ siteId: props.siteId, firewallZoneId: props.firewallZoneId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiFirewallZoneProvider = () =>\n Provider.effect(UnifiFirewallZone, Effect.succeed(UnifiFirewallZone.Provider.of(handlers)));\n",
|
|
16
16
|
"/**\n * `Unifi.FirewallZone`'s wire shape — the declarable subset of `FirewallZone`, and how a live\n * read becomes both plan attributes and a rendered declaration.\n *\n * ⚠️ ONLY `name` AND `networkIds` ARE DECLARABLE. `CreateFirewallZoneRequest`\n * (`firewall.ts`) accepts exactly those two beyond `siteId`; `metadata` (origin/source) and\n * `id` are server-derived, so — same split as `network-form.ts`'s `default`/`metadata` —\n * they are attributes-only, reported but never compared or declared.\n *\n * ⛔ `networkIds` ORDER IS NOT PART OF IDENTITY. The vendor document gives no indication the\n * list is ordered (it is a set of attached networks, not a sequence like the ordering\n * endpoints' rule lists), so `matches` compares it as a set — sorted before comparing —\n * the same reasoning `proxmox/user-wire.ts`'s `groupSet` gives for PVE's `groups`.\n */\nimport type * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport { makeDriftOf } from './drift.ts';\n\nexport interface FirewallZoneProps {\n siteId: string;\n firewallZoneId: string;\n /** Name of a firewall zone. */\n name: string;\n /** Network IDs attached to this zone — compared as a set, see the header. */\n networkIds: string[];\n}\n\nexport interface FirewallZoneAttributes {\n siteId: string;\n firewallZoneId: string;\n name: string;\n networkIds: string[];\n /** `origin`/`source` (e.g. `SDWAN`) — server-derived, never declared. */\n metadataOrigin: string;\n}\n\nconst sortedSet = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\nexport const attributesOf = (\n live: firewall.FirewallZone,\n props: FirewallZoneProps,\n): FirewallZoneAttributes => ({\n siteId: props.siteId,\n firewallZoneId: live.id,\n name: live.name,\n networkIds: sortedSet(live.networkIds),\n metadataOrigin: live.metadata.origin,\n});\n\n/**\n * B6: `fieldDrift` (`makeDriftOf`, `drift.ts`) is now the ONLY comparison this family has — MEDIUM-4\n * (red team, 2026-09-26): a hand-written `matches` kept beside `fieldDrift` is two field lists an\n * edit to one can silently leave out of sync with the other (a mutant deleting a `fieldDrift` entry\n * left every test green, because `matches` never consulted it). `matches(attrs, props) ===\n * (driftOf(live, props).length === 0)` is now true BY CONSTRUCTION, not just by a test that happens\n * to check both. `networkIds`' custom `equal` is the set comparison the family has always used —\n * see the header — and its reported `live`/`declared` values in a `driftOf` result are the SORTED\n * arrays, not the joined strings this `equal` compares internally, so a real diff reads as a set\n * difference, not two opaque strings.\n */\nconst fieldDrift = makeDriftOf<FirewallZoneAttributes, FirewallZoneProps>([\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n {\n field: 'networkIds',\n live: (a) => a.networkIds,\n declared: (p) => sortedSet(p.networkIds),\n equal: (live, declared) => (live as string[]).join(',') === (declared as string[]).join(','),\n },\n]);\n\nexport const matches = (attributes: FirewallZoneAttributes, props: FirewallZoneProps): boolean =>\n fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: firewall.FirewallZone, props: FirewallZoneProps) =>\n fieldDrift(attributesOf(live, props), props);\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). `matches(attributesOf(live, props), declareFirewallZone(live,\n * siteId))` is `true` by construction (`firewall-zone.test.ts` proves it).\n */\nexport const declareFirewallZone = (\n live: firewall.FirewallZone,\n siteId: string,\n): FirewallZoneProps => ({\n siteId,\n firewallZoneId: live.id,\n name: live.name,\n networkIds: sortedSet(live.networkIds),\n});\n",
|
|
17
17
|
"/**\n * `Unifi.DnsPolicy` — one DNS policy on a UniFi Network site, READ-ONLY (`policy.ts`).\n *\n * ★ T11 GATE, CHECKED 2026-09-26, CORRECTED SAME DAY (red team, Important finding 1): `DNS\n * Policies` is 5 operations, `Access Control (ACL Rules)` (`acl-rule.ts`) is 7 — 12 total.\n * Diffed the pinned 10.4.57 spec against a 10.6.97 mirror (`beezly/unifi-apis`, diffing aid\n * only, `docs/unifi-acl-rule.md` records the sha256 + mirror commit): all 12 full operation\n * objects (parameters, request body, responses, unresolved `$ref`s included) are byte-identical\n * across versions. ⚠️ The schema closure is 46, not 17 — a `$ref`-only walk misses every schema\n * reachable only via a `discriminator.mapping` (both `DNS policy` and `ACL rule` discriminate on\n * `type`); the 46-schema closure, `$ref` + `mapping` together, shares zero names with the 14\n * schemas that DID change between the two versions (all in `Switching`, `Filtering`, or one\n * `UniFi Devices` enum — none of it DNS- or ACL-shaped). Same reachability method\n * `spec-version-provenance.md` (distilled package) used for Networks/FirewallZones, extended to\n * follow `mapping` too; full breakdown in `docs/unifi-acl-rule.md`, not repeated per family.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as dnsPolicies from '@distilled.cloud/unifi-network/dns_policies';\nimport * as Effect from 'effect/Effect';\nimport { type DnsPolicyAttributes, type DnsPolicyProps, attributesOf } from './dns-policy-form.ts';\nimport { matches } from './dns-policy-drift.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type { DnsPolicyAttributes, DnsPolicyProps } from './dns-policy-form.ts';\nexport { declareDnsPolicy } from './dns-policy-form.ts';\nexport { driftOf } from './dns-policy-drift.ts';\n\nexport interface UnifiDnsPolicy extends Resource<\n 'Unifi.DnsPolicy',\n DnsPolicyProps,\n DnsPolicyAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiDnsPolicy = Resource<UnifiDnsPolicy>('Unifi.DnsPolicy', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `dnsPolicy('lan-forwarding', props)` — `adopt(true)` piped on by default (H5); see `network.ts`. */\nexport const dnsPolicy = (id: string, props: DnsPolicyProps) =>\n UnifiDnsPolicy(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n DnsPolicyProps,\n dnsPolicies.DNSPolicy,\n DnsPolicyAttributes,\n dnsPolicies.GetDnsPolicyError\n> = {\n type: 'Unifi.DnsPolicy',\n describe: (props) => `sites/${props.siteId}/dns/policies/${props.dnsPolicyId}`,\n fetchLive: (props) =>\n dnsPolicies\n .getDnsPolicy({ siteId: props.siteId, dnsPolicyId: props.dnsPolicyId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiDnsPolicyProvider = () =>\n Provider.effect(UnifiDnsPolicy, Effect.succeed(UnifiDnsPolicy.Provider.of(handlers)));\n",
|
|
@@ -26,8 +26,9 @@
|
|
|
26
26
|
"/**\n * `Unifi.WifiBroadcast`'s wire shape — the declarable subset of `WifiBroadcastOverview`, and how\n * a live read becomes both plan attributes and a rendered declaration.\n *\n * ⛔ T23, THE WHOLE REASON THIS SHAPE DIFFERS FROM EVERY OTHER FAMILY HERE. Every other family's\n * `*-form.ts` (`network-form.ts`, `dns-policy-form.ts`, …) types its props against the vendor's\n * CREATE/UPDATE request — the fullest declarable shape, even though this stack never calls it.\n * `WifiBroadcastProps`/`WifiBroadcastAttributes` do NOT: `securityConfiguration` is typed\n * `wifiBroadcasts.WifiSecurityConfigurationOverview` — the PAGE OVERVIEW's own type, `{type,\n * presharedKeyNetworkIds}` — never `WifiSecurityConfigurationDetailObject` (the\n * `getWifiBroadcastDetails`/create/update shape, which carries `passphrase` and\n * `radiusConfiguration`). There is no DECLARED field on `WifiSecurityConfigurationOverview` for\n * `attributesOf`/`declareWifiBroadcast` below to name a passphrase into. See `wifi-broadcast.ts`'s\n * header for why `fetchLive` can only ever produce a `WifiBroadcastOverview` in the first place\n * (no get-by-id call exists for it).\n *\n * ⛔ THE TYPE SYSTEM ALONE DOES NOT ENFORCE THIS (red team, IMPORTANT-1, 2026-09-26). `@distilled\n * .cloud/core`'s wire decode does not strip a key a schema doesn't declare (this file's next\n * header block); the TS types above describe what a CORRECTLY-shaped response looks like, not\n * what the decoder actually returns at runtime, so a stray key nested inside `network`,\n * `hotspotConfiguration`, `broadcastingDeviceFilter`, or an element of `presharedKeyNetworkIds`\n * would ride along through a naive `{...live}`/pass-through copy exactly like the top-level\n * `securityConfiguration` object does. `normalizeRef`/`normalizeHotspot`/`normalizeDeviceFilter`\n * below rebuild EVERY nested object field-by-field for the same reason\n * `normalizeSecurityConfiguration` does — enumeration, not the type annotation, is what actually\n * stops it. `wifi-broadcast-secrets.test.ts` plants a sentinel inside each nested shape\n * (including one `presharedKeyNetworkIds` element) to prove it, not just the top level.\n *\n * ⚠️ EVERY OTHER FIELD REUSES THE SDK'S OWN OVERVIEW TYPES DIRECTLY (`WifiNetworkReference`,\n * `BroadcastingDeviceFilter`, `IntegrationWifiHotspotConfigurationOverviewDto`) rather than\n * redeclaring them — same reasoning `network-form.ts` gives for reusing `NetworkDHCPGuarding`/\n * `NetworkIPv6Configuration` verbatim: one shape to keep in sync with the SDK's own regen.\n *\n * ⛔ THREE FIELDS ARE SETS, NOT SEQUENCES, UNCONFIRMED AGAINST A LIVE CONSOLE (same caveat\n * `network-form.ts`'s header gives for its own three): `broadcastingDeviceFilter.deviceIds`/\n * `.deviceTagIds` and `securityConfiguration.presharedKeyNetworkIds` are membership sets by the\n * vendor description's own wording (\"Defines the custom scope of devices…\"), not ordered\n * sequences. `normalizeDeviceFilter`/`normalizeSecurityConfiguration`/`normalizeFrequencies`\n * neutralize the same \"spurious update, refused by a read-only reconcile\" failure mode\n * `network-form.ts`'s header describes, exactly like `firewall-zone-form.ts`'s `networkIds`.\n */\nimport type * as wifiBroadcasts from '@distilled.cloud/unifi-network/wifi_broadcasts';\n\nconst sortedStrings = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\nconst sortedFrequencies = (values: readonly number[]): number[] =>\n [...new Set(values)].sort((a, b) => a - b);\n\n/** Canonical sort key for a `{type, networkId?}` reference — stable regardless of vendor order. */\nconst refKey = (ref: wifiBroadcasts.WifiNetworkReference): string =>\n `${ref.type}:${ref.networkId ?? ''}`;\n\n/**\n * Rebuilds a reference field-by-field — never returns the object as received. IMPORTANT-1: this\n * runs on EVERY reference this file handles (`network`, and each `presharedKeyNetworkIds`\n * element) precisely because a reference is exactly the shape (`{network: WifiNetworkReference,\n * passphrase}`) the details call uses to carry a PPSK passphrase (`IntegrationWifiPresharedKeyDto`,\n * SDK `wifi_broadcasts.ts`); it is the most plausible place for a stray passphrase-shaped key to\n * appear on the wire if the vendor ever leaked one onto the overview response.\n */\nexport const normalizeRef = (\n ref: wifiBroadcasts.WifiNetworkReference | undefined,\n): wifiBroadcasts.WifiNetworkReference | undefined =>\n ref === undefined\n ? undefined\n : { type: ref.type, ...(ref.networkId !== undefined ? { networkId: ref.networkId } : {}) };\n\nconst sortedRefs = (\n values: readonly wifiBroadcasts.WifiNetworkReference[],\n): wifiBroadcasts.WifiNetworkReference[] => {\n const byKey = new Map(\n values.map(\n (ref) => [refKey(ref), normalizeRef(ref) as wifiBroadcasts.WifiNetworkReference] as const,\n ),\n );\n return [...byKey.values()].sort((a, b) => refKey(a).localeCompare(refKey(b)));\n};\n\n/**\n * Rebuilds the hotspot configuration field-by-field. `IntegrationWifiHotspotConfigurationOverviewDto`\n * declares only `type` (SDK `wifi_broadcasts.ts:743-753`) — a PASSPOINT/RADIUS secret would live on\n * a wire key this type doesn't name, exactly the IMPORTANT-1 risk `normalizeRef` guards against.\n */\nexport const normalizeHotspot = (\n value: wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined,\n): wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined =>\n value === undefined ? undefined : { type: value.type };\n\n/**\n * ★ EXPORTED: `wifi-broadcast-drift.ts` reuses these exact normalizers so `matches`/`driftOf` can\n * never quietly disagree about what counts as a change (MEDIUM-4, `network-drift.ts`'s header).\n *\n * ⛔ NO `{...value}` SPREAD (red team, IMPORTANT-1) — `BroadcastingDeviceFilter` declares exactly\n * `type`/`deviceIds`/`deviceTagIds` (SDK `wifi_broadcasts.ts:125-129`); enumerating exactly those\n * three is what stops a stray wire key here, the same reason `normalizeSecurityConfiguration`\n * (below) never spreads its input.\n */\nexport const normalizeDeviceFilter = (\n value: wifiBroadcasts.BroadcastingDeviceFilter | undefined,\n): wifiBroadcasts.BroadcastingDeviceFilter | undefined =>\n value == null\n ? value\n : {\n type: value.type,\n ...(value.deviceIds !== undefined ? { deviceIds: sortedStrings(value.deviceIds) } : {}),\n ...(value.deviceTagIds !== undefined\n ? { deviceTagIds: sortedStrings(value.deviceTagIds) }\n : {}),\n };\n\nexport const normalizeFrequencies = (value: readonly number[] | undefined): number[] | undefined =>\n value === undefined ? value : sortedFrequencies(value);\n\n/**\n * ⛔ NO `{...value}` SPREAD, UNLIKE EVERY OTHER NORMALIZER IN THIS DIRECTORY (`network-form.ts`'s\n * `normalizeDhcpGuarding`/`normalizeIpv6Configuration` both spread). This is the one field in\n * the whole `Unifi.*` tree with a spec-confirmed passphrase risk (T23): nothing in\n * `@distilled.cloud/core`'s wire key-mapping strips an object key the schema doesn't declare —\n * it only renames declared ones — so a stray extra key on the WIRE `securityConfiguration`\n * object (a vendor bug, or a future console version) survives `getWifiBroadcastPage`'s own\n * decode untyped, and `{...value}` would carry it straight through into `Attributes`/`Props`/the\n * rendered declaration. Enumerating exactly the two fields\n * `WifiSecurityConfigurationOverview` declares is what actually stops that — proved by\n * `wifi-broadcast-secrets.test.ts`'s sentinel test, which injects exactly this kind of stray key.\n */\nexport const normalizeSecurityConfiguration = (\n value: wifiBroadcasts.WifiSecurityConfigurationOverview,\n): wifiBroadcasts.WifiSecurityConfigurationOverview => ({\n type: value.type,\n ...(value.presharedKeyNetworkIds !== undefined\n ? { presharedKeyNetworkIds: sortedRefs(value.presharedKeyNetworkIds) }\n : {}),\n});\n\n/**\n * ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` ON PURPOSE — `exactOptionalPropertyTypes`; see\n * `network-form.ts`'s own header. `attributesOf`/`declareWifiBroadcast` echo a live optional\n * field structurally rather than defaulting it, so the target type must admit an explicit\n * `undefined`.\n */\nexport interface WifiBroadcastProps {\n siteId: string;\n wifiBroadcastId: string;\n name: string;\n enabled: boolean;\n type: string;\n network?: wifiBroadcasts.WifiNetworkReference | undefined;\n broadcastingDeviceFilter?: wifiBroadcasts.BroadcastingDeviceFilter | undefined;\n broadcastingFrequenciesGHz?: number[] | undefined;\n hotspotConfiguration?: wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined;\n /** ⛔ T23: the PAGE OVERVIEW's security shape ONLY — see this file's header. */\n securityConfiguration: wifiBroadcasts.WifiSecurityConfigurationOverview;\n}\n\nexport interface WifiBroadcastAttributes {\n siteId: string;\n wifiBroadcastId: string;\n name: string;\n enabled: boolean;\n type: string;\n network: wifiBroadcasts.WifiNetworkReference | undefined;\n broadcastingDeviceFilter: wifiBroadcasts.BroadcastingDeviceFilter | undefined;\n broadcastingFrequenciesGHz: number[] | undefined;\n hotspotConfiguration: wifiBroadcasts.IntegrationWifiHotspotConfigurationOverviewDto | undefined;\n securityConfiguration: wifiBroadcasts.WifiSecurityConfigurationOverview;\n /** `metadata.origin` — server-derived, never declared; same split as every other family. */\n metadataOrigin: string;\n}\n\nexport const attributesOf = (\n live: wifiBroadcasts.WifiBroadcastOverview,\n props: WifiBroadcastProps,\n): WifiBroadcastAttributes => ({\n siteId: props.siteId,\n wifiBroadcastId: live.id,\n name: live.name,\n enabled: live.enabled,\n type: live.type,\n network: normalizeRef(live.network),\n broadcastingDeviceFilter: normalizeDeviceFilter(live.broadcastingDeviceFilter),\n broadcastingFrequenciesGHz: normalizeFrequencies(live.broadcastingFrequenciesGHz),\n hotspotConfiguration: normalizeHotspot(live.hotspotConfiguration),\n securityConfiguration: normalizeSecurityConfiguration(live.securityConfiguration),\n metadataOrigin: live.metadata.origin,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getWifiBroadcastPage`'s own per-row\n * response straight in and writes the result into `alchemy.run.ts` as `wifiBroadcast('<id>',\n * declareWifiBroadcast(live, siteId))` — no field is invented or defaulted here beyond what\n * `attributesOf` already reports, so `matches(attributesOf(live, props), declareWifiBroadcast(live,\n * siteId))` is `true` by construction (`wifi-broadcast.test.ts` proves it) for any live object\n * this SDK can decode. Builds ONLY from fields `WifiBroadcastOverview` has — never a passphrase,\n * which that type cannot express (see this file's header).\n */\nexport const declareWifiBroadcast = (\n live: wifiBroadcasts.WifiBroadcastOverview,\n siteId: string,\n): WifiBroadcastProps => ({\n siteId,\n wifiBroadcastId: live.id,\n name: live.name,\n enabled: live.enabled,\n type: live.type,\n network: normalizeRef(live.network),\n broadcastingDeviceFilter: normalizeDeviceFilter(live.broadcastingDeviceFilter),\n broadcastingFrequenciesGHz: normalizeFrequencies(live.broadcastingFrequenciesGHz),\n hotspotConfiguration: normalizeHotspot(live.hotspotConfiguration),\n securityConfiguration: normalizeSecurityConfiguration(live.securityConfiguration),\n});\n",
|
|
27
27
|
"/**\n * `Unifi.WifiBroadcast`'s `matches` AND `driftOf` (B6) — built on the ONE field list below, the\n * same `fieldDrift`-is-the-only-comparison shape `dns-policy-drift.ts`'s header explains\n * (MEDIUM-4): `matches(attrs, props) === (driftOf(live, props).length === 0)` is true BY\n * CONSTRUCTION, never two hand-kept-in-sync comparisons.\n *\n * ★ THREE FIELDS REUSE `wifi-broadcast-form.ts`'s OWN NORMALIZERS ON THE `declared` SIDE TOO, not\n * just the `live` side — `firewall-zone-form.ts`'s `networkIds` gives the same reason: comparing\n * a freshly-normalized live value against an un-normalized declared value would report spurious\n * drift the moment a committed declaration's array happens to be in a different (still valid)\n * order than what `declareWifiBroadcast` would itself produce today.\n */\nimport type * as wifiBroadcasts from '@distilled.cloud/unifi-network/wifi_broadcasts';\nimport { makeDriftOf } from './drift.ts';\nimport {\n type WifiBroadcastAttributes,\n type WifiBroadcastProps,\n attributesOf,\n normalizeDeviceFilter,\n normalizeFrequencies,\n normalizeHotspot,\n normalizeRef,\n normalizeSecurityConfiguration,\n} from './wifi-broadcast-form.ts';\n\nconst fieldDrift = makeDriftOf<WifiBroadcastAttributes, WifiBroadcastProps>([\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n { field: 'enabled', live: (a) => a.enabled, declared: (p) => p.enabled },\n { field: 'type', live: (a) => a.type, declared: (p) => p.type },\n {\n // IMPORTANT-1: `a.network` is already rebuilt field-by-field inside `attributesOf`; the\n // declared side gets the same treatment so a hand-written declaration carrying a stray key\n // cannot report phantom drift against a live side that never could.\n field: 'network',\n live: (a) => a.network,\n declared: (p) => normalizeRef(p.network),\n },\n {\n field: 'broadcastingDeviceFilter',\n live: (a) => a.broadcastingDeviceFilter,\n declared: (p) => normalizeDeviceFilter(p.broadcastingDeviceFilter),\n },\n {\n field: 'broadcastingFrequenciesGHz',\n live: (a) => a.broadcastingFrequenciesGHz,\n declared: (p) => normalizeFrequencies(p.broadcastingFrequenciesGHz),\n },\n {\n field: 'hotspotConfiguration',\n live: (a) => a.hotspotConfiguration,\n declared: (p) => normalizeHotspot(p.hotspotConfiguration),\n },\n {\n field: 'securityConfiguration',\n live: (a) => a.securityConfiguration,\n declared: (p) => normalizeSecurityConfiguration(p.securityConfiguration),\n },\n]);\n\nexport const matches = (attributes: WifiBroadcastAttributes, props: WifiBroadcastProps): boolean =>\n fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: wifiBroadcasts.WifiBroadcastOverview, props: WifiBroadcastProps) =>\n fieldDrift(attributesOf(live, props), props);\n",
|
|
28
28
|
"/**\n * B0b / T9: a CONSUMER-SIDE sequential offset-pager for the SDK's list operations. None of them\n * paginate on their own — `getNetworksOverviewPage`, `getFirewallZones`, `getAclRules`, … all take\n * `{ ..., offset?, limit? }` and answer `{ count, data, limit, offset, totalCount }` (the shape\n * `protocol.ts`'s own header names); there is no `smithy.api#paginated` trait in the pinned spec\n * to generate a helper from.\n *\n * ⛔ NO SDK OR `@distilled.cloud/core` CHANGE (M1 — upstream's own convention: pagination stays\n * out of the 59 packages walked down for `distilled-doctrine`). This is a plain function over\n * whatever `Effect.Effect<OffsetPage<T>, E, R>` a resource file's own `fetchPage` already is.\n * ⛔ STOPS ON `totalCount`, NEVER ON `data.length < limit` (T9's own wording). A page can come back\n * short of the requested `limit` for reasons unrelated to \"this was the last page\" (a vendor-side\n * per-call cap, a narrow filter) — `totalCount` is the one number the response commits to, so it\n * is the only thing this loop trusts to stop early. An empty page (`data.length === 0`) is the\n * other legitimate stop, and the only one checked before `totalCount` is read.\n * ⚠️ NOT SEQUENCED AGAINST A LIVE CONSOLE THAT MUTATES BETWEEN CALLS. `totalCount` is whatever the\n * MOST RECENT page reported; a page walk racing a live edit could see a row twice, miss one, or\n * just see a moving target. `homeflare-network`'s own import already treats one family's fetch\n * as one sequenced run for this reason (T27's neighboring trap) — nothing here can fix a\n * concurrent mutation, only DETECT the common ways one shows up (below) instead of returning a\n * silently wrong row set.\n *\n * ⚠️ RED TEAM (2026-09-26, IMPORTANT-3): the first version of this file trusted the vendor's\n * paged response completely — a lying or drifting `totalCount`, an ignored `offset`, or a row\n * added/removed mid-walk all produced a WRONG row set with no signal at all (silent truncation,\n * an infinite loop, or a subtly incomplete list an importer would commit as if it were correct).\n * Every list op's response already carries `offset` alongside `totalCount` (`networks.ts`'s\n * `NetworkOverviewPage`, and `protocol.ts`'s header for every OTHER list op too); the three\n * checks below are consistency proofs built entirely from fields the vendor already sends, plus\n * a hard ceiling as a backstop against a walk that can never converge. These are typed failures,\n * not `Effect.die`: an inconsistent paged response is a VENDOR-DATA condition a caller may\n * legitimately want to retry or report on (distilled-doctrine: a defect is for \"structurally\n * impossible\" code paths, not for data the wire actually sent).\n */\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\n\n/** The one response shape every UniFi Network list operation already has (`protocol.ts`). */\nexport interface OffsetPage<T> {\n readonly data: readonly T[];\n readonly offset: number;\n readonly totalCount: number;\n}\n\n/** The one request shape every UniFi Network list operation already accepts, plus its own fields. */\nexport type OffsetPageRequest = { readonly offset?: number; readonly limit?: number };\n\n/** Well past any family's real row count, even at a one-row page size (`docs/unifi.md`: a real\n * console's largest family is in the hundreds, not thousands) — a walk that reaches this many\n * pages is not looking at a real UniFi site, it is looking at a response that never lets `offset`\n * converge on `totalCount`. */\nconst MAX_PAGES = 1000;\n\nexport class UnifiPaginationInconsistent extends Data.TaggedError('UnifiPaginationInconsistent')<{\n readonly reason:\n | 'offset-mismatch'\n | 'total-count-changed'\n | 'row-count-mismatch'\n | 'page-ceiling';\n readonly detail: string;\n}> {\n override get message(): string {\n return (\n `UniFi pager: ${this.reason} -- ${this.detail}. The paged response contradicted itself (or ` +\n 'the walk ran implausibly long); the rows collected so far cannot be trusted and are not returned.'\n );\n }\n}\n\n/**\n * Walks every page of `fetchPage`, starting at `request.offset ?? 0`, and returns every row in\n * order. `request.limit`, if given, is kept as the page size for every call; the vendor's own\n * default applies otherwise — this loop never invents one (T18: \"pass ops only their own fields\").\n *\n * Three self-consistency checks run against fields the vendor already sends with every page —\n * detection, not a fix, for the \"live console mutates mid-walk\" case the header above accepts as\n * unfixable from the client alone: a row added AND removed in the same window can still net out to\n * a `totalCount` and final row count that agree by coincidence. What this closes is the SILENT\n * version of every case actually observed in review: an ignored `offset`, a `totalCount` that\n * drifts between calls, and an undercounted `totalCount` that truncates the walk early.\n *\n * ⛔ `numOrPlaceholder` (red team, MINOR-3, 2026-09-26): nothing in `@distilled.cloud/core` decodes\n * or validates a page response's shape (this file's own `wifi-broadcast.ts` neighbor documents\n * why), so `page.offset`/`page.totalCount` can be ANY JSON value, not just a number, if the wire\n * ever sends something malformed. Every `detail` string below prints these through\n * `numOrPlaceholder` rather than raw template interpolation, so a non-numeric value — however it\n * got there — can never inject arbitrary response text into an error message a caller might log.\n */\n/** A finite number renders as itself; anything else (a malformed wire value) renders as a fixed\n * placeholder, never as the value itself — see this file's header (MINOR-3). */\nconst numOrPlaceholder = (value: unknown): string =>\n typeof value === 'number' && Number.isFinite(value) ? String(value) : '<non-numeric>';\n\nexport const pageAll = <Req extends OffsetPageRequest, T, E, R>(\n fetchPage: (request: Req) => Effect.Effect<OffsetPage<T>, E, R>,\n request: Req,\n): Effect.Effect<T[], E | UnifiPaginationInconsistent, R> =>\n Effect.gen(function* () {\n const rows: T[] = [];\n const startOffset = request.offset ?? 0;\n let offset = startOffset;\n let totalCount: number | undefined;\n for (let pageCount = 1; ; pageCount++) {\n if (pageCount > MAX_PAGES) {\n return yield* new UnifiPaginationInconsistent({\n reason: 'page-ceiling',\n detail: `stopped after ${MAX_PAGES} pages at offset ${offset} without reaching totalCount`,\n });\n }\n // ⚠️ THE CAST IS SOUND, NOT A SHORTCUT: `Req` is a generic parameter, so TS cannot see that\n // spreading a value already typed `Req` and overriding one of `OffsetPageRequest`'s own\n // fields reconstructs a `Req` — but every field except `offset` is untouched from the\n // caller's own `request`, so it does.\n const page = yield* fetchPage({ ...request, offset } as Req);\n if (page.offset !== offset) {\n return yield* new UnifiPaginationInconsistent({\n reason: 'offset-mismatch',\n detail: `requested offset ${offset}, page echoed ${numOrPlaceholder(page.offset)} -- rows may be duplicated or skipped`,\n });\n }\n if (totalCount !== undefined && page.totalCount !== totalCount) {\n return yield* new UnifiPaginationInconsistent({\n reason: 'total-count-changed',\n detail: `totalCount changed from ${numOrPlaceholder(totalCount)} to ${numOrPlaceholder(page.totalCount)} between page calls`,\n });\n }\n totalCount = page.totalCount;\n rows.push(...page.data);\n if (page.data.length === 0) break;\n offset += page.data.length;\n if (offset >= totalCount) break;\n }\n const expected = totalCount - startOffset;\n if (rows.length !== expected) {\n return yield* new UnifiPaginationInconsistent({\n reason: 'row-count-mismatch',\n detail: `collected ${rows.length} rows but totalCount(${numOrPlaceholder(totalCount)}) - startOffset(${startOffset}) = ${numOrPlaceholder(expected)}`,\n });\n }\n return rows;\n });\n",
|
|
29
|
-
"/**\n * `Unifi.FirewallPolicy` — one firewall policy on a UniFi Network site
|
|
30
|
-
"/**\n * `Unifi.FirewallPolicy`'s
|
|
29
|
+
"/**\n * `Unifi.FirewallPolicy` — one firewall policy on a UniFi Network site. READ/ADOPT-ONLY for every\n * row (`policy.ts`), EXCEPT create for a row whose declared `name` starts `hf: `\n * (`isHfPolicyName`, Tim 2026-10-19 ~16:20Z) — `update` and `delete` stay refused even for an\n * `hf:` row. `Unifi.FirewallPolicyOrdering` (`firewall-policy-ordering.ts`) is the sibling\n * resource for policy ORDER within a zone pair (T5) — one per `(sourceFirewallZoneId,\n * destinationFirewallZoneId)`, still read-only; this lift does not touch it, so a created `hf:`\n * row lands wherever the controller places a new policy, and placing it ahead of its zone pair's\n * \"Block All Traffic\" (if the controller does not already do that) would need an ordering WRITE —\n * a second, separate lift this PR does not make (see `docs/unifi-firewall-policy.md`).\n *\n * ★ T11 GATE, CHECKED 2026-09-26: the `Firewall` tag's 13 operations (5 already covered for\n * `Unifi.FirewallZone` in `docs/unifi.md`, 8 new here for `FirewallPolicy` — `getFirewallPolicies`,\n * `createFirewallPolicy`, `getFirewallPolicyOrdering`, `updateFirewallPolicyOrdering`,\n * `deleteFirewallPolicy`, `getFirewallPolicy`, `patchFirewallPolicy`, `updateFirewallPolicy`) are\n * byte-identical between 10.4.57 and 10.6.97, full raw operation objects; the 106-schema closure\n * reachable from all 13 (`$ref` + `discriminator.mapping`) shares zero names with the 14 schemas\n * that DID change elsewhere in the document. Corroborated independently by the A3 changeset's own\n * broader 25-operation/139-schema re-check (`.changeset/unifi-network-a3-typed-discriminator-filters.md`).\n * Full breakdown: `docs/unifi-firewall-policy.md`.\n *\n * ⛔ `fetchLive` LOCATES BY `name` WHEN `firewallPolicyId` IS ABSENT (an `hf:` creatable\n * declaration never has the vendor id before its first successful create). A real site's policy\n * count is in the hundreds (T9, 424 live policies), so this walks every page with `pageAll`\n * rather than trusting any one page to hold the match — same reasoning netbox's `locateOne`\n * gives for a server-side filter that cannot be trusted alone, except here there IS no\n * server-side name filter at all, so every row is paged. More than one row sharing a name is a\n * defect, not a reason to take the first: `policy.ts`'s naming convention is exactly `hf: <desc>`\n * because a free-text description makes a collision astronomically unlikely, but if the console\n * ever has one, this engine refuses to guess which row is \"the\" declared one.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport * as Effect from 'effect/Effect';\nimport {\n checkHfCreateScope,\n firewallPolicyCreateAllowedWrite,\n writeFirewallPolicyCreate,\n} from './firewall-policy-create.ts';\nimport { driftOf } from './firewall-policy-drift.ts';\nimport {\n type FirewallPolicyAttributes,\n type FirewallPolicyProps,\n attributesOf,\n} from './firewall-policy-form.ts';\nimport { matches } from './firewall-policy-drift.ts';\nimport { type UnifiPaginationInconsistent, pageAll } from './paginate.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\nimport type { UnifiWriteRefused } from './policy.ts';\n\nexport type { FirewallPolicyAttributes, FirewallPolicyProps } from './firewall-policy-form.ts';\nexport { declareFirewallPolicy } from './firewall-policy-form.ts';\nexport { driftOf } from './firewall-policy-drift.ts';\n\n/** `checkHfCreateScope`'s typed refusal, union'd with the real create call's own error, same\n * shape `network.ts`'s own `UpdateError` gives its write half. */\ntype CreateError = UnifiWriteRefused | firewall.CreateFirewallPolicyError;\n\nexport interface UnifiFirewallPolicy extends Resource<\n 'Unifi.FirewallPolicy',\n FirewallPolicyProps,\n FirewallPolicyAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiFirewallPolicy = Resource<UnifiFirewallPolicy>('Unifi.FirewallPolicy', {\n defaultRemovalPolicy: 'retain',\n});\n\n/** `firewallPolicy('block-iot-wan', props)` — for an IMPORTED, adopt-only row (always carries the\n * vendor's `firewallPolicyId`). `adopt(true)` piped on by default (H5); see `network.ts`. */\nexport const firewallPolicy = (id: string, props: FirewallPolicyProps) =>\n UnifiFirewallPolicy(id, props).pipe(adopt(true));\n\n/**\n * `hfFirewallPolicy('ct100-to-nas-https', props)` — for a NEW, kit-creatable row: `props` has no\n * `firewallPolicyId` (the vendor assigns one), and `name` MUST start `hf: ` or this throws\n * immediately, at declaration time, rather than waiting for `createReconcile`'s own typed refusal\n * at deploy time. That typed refusal (`checkHfCreateScope`) is still the authoritative gate — this\n * is a second, earlier check for the common case of a typo, not a replacement for it; a caller\n * that builds `FirewallPolicyProps` by hand and calls `UnifiFirewallPolicy` directly still goes\n * through `checkHfCreateScope` and is still refused the same way.\n *\n * ⛔ NO `adopt(true)` HERE. This is a create path, not an adoption: a cold read finding an\n * existing row of this exact name is `Unowned`, same as any other family, and a plan that adopts\n * one silently is not what \"create\" means for this lift.\n */\nexport const hfFirewallPolicy = (\n id: string,\n props: Omit<FirewallPolicyProps, 'firewallPolicyId'>,\n) => {\n // ⚠️ `Omit` only rejects an excess property on a FRESH object literal passed at the call site —\n // a variable typed `FirewallPolicyProps` (which still has the field) passes the parameter type\n // check silently (Opus red team, 2026-10-19, SECONDARY FINDING). Checked at runtime so this\n // constructor cannot be the route past `checkHfCreateScope`'s own identity check either.\n if (\n 'firewallPolicyId' in props &&\n (props as { firewallPolicyId?: unknown }).firewallPolicyId !== undefined\n ) {\n throw new Error(\n `hfFirewallPolicy(${id}): props already carries a firewallPolicyId -- this constructor is ` +\n 'for a row that does not exist yet. A row with a vendor id is adopted or updated, never ' +\n 'created; use firewallPolicy() instead.',\n );\n }\n if (!props.name.startsWith('hf: ')) {\n throw new Error(\n `hfFirewallPolicy(${id}): name \"${props.name}\" does not start \"hf: \" -- only hf:-named ` +\n 'policies may be created by this kit (Tim 2026-10-19); rename the declaration.',\n );\n }\n return UnifiFirewallPolicy(id, props as FirewallPolicyProps);\n};\n\n/** One page of `getFirewallPolicies`, walked end to end and narrowed to the sole row named\n * `name` — the locate path `fetchLive` uses when `firewallPolicyId` is not yet known. */\nconst locateByName = (siteId: string, name: string) =>\n pageAll(firewall.getFirewallPolicies, { siteId }).pipe(\n Effect.map((rows) => {\n const matching = rows.filter((row) => row.name === name);\n if (matching.length > 1) {\n throw new Error(\n `Unifi.FirewallPolicy: ${String(matching.length)} live policies are named \"${name}\" on ` +\n `site ${siteId}. A name this engine locates by must be unique; rename one on the console.`,\n );\n }\n return matching[0];\n }),\n );\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n FirewallPolicyProps,\n firewall.FirewallPolicy,\n FirewallPolicyAttributes,\n firewall.GetFirewallPolicyError | firewall.GetFirewallPoliciesError | UnifiPaginationInconsistent,\n never,\n CreateError\n> = {\n type: 'Unifi.FirewallPolicy',\n describe: (props) =>\n props.firewallPolicyId === undefined\n ? `sites/${props.siteId}/firewall/policies/by-name/${props.name}`\n : `sites/${props.siteId}/firewall/policies/${props.firewallPolicyId}`,\n fetchLive: (props) =>\n props.firewallPolicyId === undefined\n ? locateByName(props.siteId, props.name)\n : firewall\n .getFirewallPolicy({ siteId: props.siteId, firewallPolicyId: props.firewallPolicyId })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n create: {\n allowedWrite: firewallPolicyCreateAllowedWrite,\n checkScope: checkHfCreateScope,\n write: writeFirewallPolicyCreate,\n identify: (props, written) => ({ ...props, firewallPolicyId: written.id }),\n driftOf,\n },\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiFirewallPolicyProvider = () =>\n Provider.effect(UnifiFirewallPolicy, Effect.succeed(UnifiFirewallPolicy.Provider.of(handlers)));\n",
|
|
30
|
+
"/**\n * `Unifi.FirewallPolicy`'s CREATE — the one write this family may make, and only for a row whose\n * declared `name` passes `policy.ts`'s `isHfPolicyName` (Tim 2026-10-19 ~16:20Z, \"Allow kit to\n * create hf: rules\"). THE ONLY FILE UNDER `src/unifi` ALLOWED TO NAME `createFirewallPolicy`\n * (`write-op-reference.test.ts`'s `WRITE_OP_ALLOWLIST` pins that, alongside `network-update.ts`'s\n * own `updateNetwork` entry).\n *\n * ⛔ A CREATE SENDS ONLY THE FIELDS THE DECLARATION OWNS — NEVER A SPREAD OF ANYTHING ELSE. Unlike\n * `network-update.ts`'s whole-object PUT (which must carry the untouched live object forward so\n * an omitted optional block is not read by the controller as \"turn it off\"), there IS no live\n * object yet: `createBody` builds `CreateFirewallPolicyRequest` field by field from `props`,\n * omitting every optional key `props` itself leaves `undefined` rather than sending it as\n * `null`/`undefined` (UniFi's \"missing = off\" rule cuts both ways — a key that is actually absent\n * from the JSON body is what \"this policy does not restrict on this axis\" means here, same as a\n * declaration that never sets `connectionStateFilter` means \"match every connection state\").\n * ⛔ `checkScope` RUNS BEFORE THE REQUEST, TYPED, NEVER A STRING COMPARISON INLINE AT THE CALL\n * SITE — one function (`policy.ts`'s `isHfPolicyName`) decides \"hf:\" everywhere this boundary is\n * checked, so a future second caller cannot drift from this one with a slightly different regex.\n *\n * 🔴 `checkScope` ALSO REFUSES A `firewallPolicyId` THAT IS ALREADY SET (Opus red team,\n * 2026-10-19, PRIMARY FINDING). `resource.ts`'s `create` branch runs for EVERY\n * `Unifi.FirewallPolicy` declaration with no prior Alchemy state, not only ones meant to\n * create — including every `firewallPolicy()` import `.pipe(adopt(true))`, and Tim's own live\n * console ALREADY names hand-made rules `hf: <desc>` (this file's sibling `policy.ts` cites\n * `hf: Vpn to MGMT (Magic WAN site-to-site)` as the existing convention). Before this check, an\n * hf:-named IMPORTED row whose `firewallPolicyId` stopped resolving (state lost, the rule\n * hand-deleted and recreated with a new id, a wrong id in a generated declaration) would GET\n * 404, fall into `createReconcile`, pass the name check, and POST A DUPLICATE — silently, with\n * a green plan, where every other family still refuses \"declared row absent\" outright\n * (`update-reconcile.ts`'s original behavior, unchanged for every family but this one). A\n * declaration carrying a vendor id is BY DEFINITION an existing row, adopted or updated, never\n * a fresh create; `firewallPolicyId !== undefined` is refused here before `isHfPolicyName` is\n * even checked, so this holds regardless of what the name says.\n */\nimport * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport * as Effect from 'effect/Effect';\nimport { isHfPolicyName, refuseWrite } from './policy.ts';\nimport type { FirewallPolicyProps } from './firewall-policy-form.ts';\nimport type { AllowedWrite } from './wire-guard.ts';\n\n/** The site's fixed firewall-policies COLLECTION route — never a per-row path, since there is no\n * row id before this POST creates one. Takes `props` (not just a bare `siteId`) to match\n * `UnifiUpdate.allowedWrite`'s own shape, so `resource.ts`'s wiring treats both the same way. */\nexport const firewallPolicyCreateAllowedWrite = (props: FirewallPolicyProps): AllowedWrite => ({\n method: 'POST',\n tail: `/v1/sites/${props.siteId}/firewall/policies`,\n});\n\nconst describeByName = (props: FirewallPolicyProps) =>\n `sites/${props.siteId}/firewall/policies/by-name/${props.name}`;\n\n/** Refuses, typed, before any request: the one gate `createReconcile` calls before `write`.\n * Order matters for the message only (both refuse the same way) — the identity check runs\n * first because it is the one that must hold regardless of what the name says (see header). */\nexport const checkHfCreateScope = (props: FirewallPolicyProps) => {\n if (props.firewallPolicyId !== undefined) {\n return refuseWrite(\n 'Unifi.FirewallPolicy',\n `sites/${props.siteId}/firewall/policies/${props.firewallPolicyId}`,\n 'create',\n );\n }\n return isHfPolicyName(props.name)\n ? Effect.void\n : refuseWrite('Unifi.FirewallPolicy', describeByName(props), 'create');\n};\n\n/** Every field `CreateFirewallPolicyRequest` accepts, straight from the declaration, with an\n * absent optional field left OUT of the body entirely (never sent as `undefined`/`null`) —\n * `JSON.stringify` already drops an `undefined` value, but building the object field-by-field\n * rather than spreading `props` keeps this list the single place that enumerates exactly what a\n * create may send, so a future prop added to `FirewallPolicyProps` for some OTHER purpose cannot\n * silently become a wire field here without this file changing too. */\nexport const firewallPolicyCreateBody = (\n props: FirewallPolicyProps,\n): firewall.CreateFirewallPolicyRequest => ({\n siteId: props.siteId,\n action: props.action,\n ...(props.connectionStateFilter === undefined\n ? {}\n : { connectionStateFilter: props.connectionStateFilter }),\n ...(props.description === undefined ? {} : { description: props.description }),\n destination: props.destination,\n enabled: props.enabled,\n ipProtocolScope: props.ipProtocolScope,\n ...(props.ipsecFilter === undefined ? {} : { ipsecFilter: props.ipsecFilter }),\n loggingEnabled: props.loggingEnabled,\n name: props.name,\n ...(props.schedule === undefined ? {} : { schedule: props.schedule }),\n source: props.source,\n});\n\nexport const writeFirewallPolicyCreate = (props: FirewallPolicyProps) =>\n firewall.createFirewallPolicy(firewallPolicyCreateBody(props));\n",
|
|
31
|
+
"/**\n * `Unifi.FirewallPolicy`'s wire shape — the declarable subset of `FirewallPolicy` (`getFirewallPolicy`'s\n * own decode target), and how a live read becomes both plan attributes and a rendered declaration.\n * Rule ORDER is a separate resource (`firewall-policy-ordering.ts`, T5) — this file is one policy's\n * own fields only.\n *\n * ★ `source`/`destination`/`ipProtocolScope`/`action`/`schedule` STAY COMPOUND, COMPARED WHOLESALE —\n * post-A3 (`8ee11f1`, \"type FirewallPolicy/ACL/TrafficMatchingList filters per discriminator\"),\n * these decode as real per-discriminator TS unions instead of `unknown`\n * (`discriminated-filter-decode.test.ts` proves the wire path keeps every key, including one this\n * pinned spec doesn't yet declare), so this file is honest about their real shape rather than\n * casting to `unknown` the way `acl-rule-form.ts` still does for its own opaque filters. It does\n * NOT follow that up with a leaf-by-leaf declarable schema for all 12 filter variants, though:\n * that is new modelling work this PR does not attempt, so each field is compared with plain\n * `deepEqual` (`stripNullish`) as ONE value, same proportion `acl-rule-form.ts`'s\n * `enforcingDeviceFilter` gets (one level of structure, not exploded into scalars).\n *\n * ⚠️ KNOWN GAP (T15, same shape as `acl-rule-form.ts`'s own — accepted, not fixed, here either):\n * several of the vendor's filter variants nested inside `source`/`destination`/`ipProtocolScope`\n * are themselves SET-LIKE arrays with no documented order —\n * `FirewallPolicyMACAddressFilter.macAddresses`, `FirewallPolicyNetworkFilter.networkIds`,\n * `FirewallPolicyApplicationFilter.applicationIds`, `FirewallPolicyIPAddressFilter.items`,\n * `FirewallPolicyRegionFilter.regions`, `FirewallPolicyVPNServerFilter.vpnServerIds`,\n * `FirewallSchedule.repeatOnDays` — `deepEqual` compares every one of them order-sensitively, so a\n * console-side reorder with no membership change would report a spurious drift this read-only\n * family's `reconcile` then refuses forever. Not fixed here for the same reason ACL's own gap\n * isn't: normalizing 12 filter variants' own nested arrays is a bigger, separate change, not a\n * proportional extension of typing per-discriminator (A3) into set-aware comparison too.\n *\n * ⛔ `connectionStateFilter` IS THE ONE TOP-LEVEL PROPS ARRAY, AND IS A SET — \"Match on firewall\n * connection state. If null, matches all connection states\" describes membership, not a sequence\n * (unlike `firewall-policy-ordering-form.ts`'s own `beforeSystemDefined`/`afterSystemDefined`,\n * where position IS the value). Normalized with the same `sortedSet` every other family in this\n * directory uses for a set-shaped array (`firewall-zone-form.ts`'s `networkIds`,\n * `acl-rule-form.ts`'s `protocolFilter`).\n *\n * ⛔ `index` IS ATTRIBUTES-ONLY, NEVER DECLARED. `CreateFirewallPolicyRequest` has no `index`\n * field at all — the live object's current position among ALL policies on the site is reported,\n * but a policy's PRIORITY relative to its own zone pair is `firewall-policy-ordering.ts`'s concern\n * (`OrderedFirewallPolicyIDs`), same split `acl-rule-form.ts`'s own `index` documents.\n *\n * ⛔ `metadata.origin` IS ATTRIBUTES-ONLY, same split every other family here uses\n * (`firewall-zone-form.ts`'s `metadataOrigin`) — `UserOrSystemDefinedOrDerivedEntityMetadata` is\n * server-derived, no writable field accepts it.\n */\nimport type * as firewall from '@distilled.cloud/unifi-network/firewall';\n\nconst sortedSet = (values: readonly string[]): string[] => [...new Set(values)].sort();\n\n/** ★ EXPORTED so `firewall-policy-drift.ts` normalizes both live and declared sides identically —\n * see `acl-rule-form.ts`'s own header for why a normalizer applied to only one side is a bug\n * class (MEDIUM-4). */\nexport const normalizeConnectionStateFilter = (\n value: readonly string[] | undefined,\n): string[] | undefined => (value == null ? undefined : sortedSet(value));\n\n/** ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` — `exactOptionalPropertyTypes`; see\n * `network-form.ts`'s own header.\n *\n * ⛔ `firewallPolicyId` IS OPTIONAL SINCE THE 2026-10-19 hf: CREATE LIFT. An adopt-only\n * declaration (`declareFirewallPolicy`, every imported row) always carries the vendor id it was\n * read from. A fresh `hf:` creatable declaration (`declareHfFirewallPolicy` below) does not have\n * one yet — the vendor assigns it on `createFirewallPolicy`'s response — so `spec.fetchLive`\n * (`firewall-policy.ts`) locates by `name` instead of `id` whenever this is absent (netbox's\n * `locateOne` is the precedent for identity-by-natural-key over identity-by-vendor-id). */\nexport interface FirewallPolicyProps {\n siteId: string;\n firewallPolicyId?: string | undefined;\n action: firewall.FirewallPolicyAction;\n /** Connection states this policy matches — compared as a SET, see the header. */\n connectionStateFilter?: string[] | undefined;\n description?: string | undefined;\n /** Traffic destination — compound, compared wholesale; see the header's known gap. */\n destination: firewall.FirewallPolicyDestination;\n enabled: boolean;\n /** IP version + protocol scope — compound, compared wholesale; see the header's known gap. */\n ipProtocolScope: firewall.FirewallPolicyIPProtocolScope;\n ipsecFilter?: string | undefined;\n loggingEnabled: boolean;\n name: string;\n /** Schedule this policy is active on — compound, compared wholesale; see the header's known gap. */\n schedule?: firewall.FirewallSchedule | undefined;\n /** Traffic source — compound, compared wholesale; see the header's known gap. */\n source: firewall.FirewallPolicySource;\n}\n\nexport interface FirewallPolicyAttributes {\n siteId: string;\n firewallPolicyId: string;\n action: firewall.FirewallPolicyAction;\n connectionStateFilter: string[] | undefined;\n description: string | undefined;\n destination: firewall.FirewallPolicyDestination;\n enabled: boolean;\n /** Current position among ALL policies on the site — server-derived, never declared; see header. */\n index: number;\n ipProtocolScope: firewall.FirewallPolicyIPProtocolScope;\n ipsecFilter: string | undefined;\n loggingEnabled: boolean;\n name: string;\n schedule: firewall.FirewallSchedule | undefined;\n source: firewall.FirewallPolicySource;\n /** `metadata.origin` — server-derived, never declared; see `firewall-zone-form.ts`'s own. */\n metadataOrigin: string;\n}\n\nexport const attributesOf = (\n live: firewall.FirewallPolicy,\n props: FirewallPolicyProps,\n): FirewallPolicyAttributes => ({\n siteId: props.siteId,\n firewallPolicyId: live.id,\n action: live.action,\n connectionStateFilter: normalizeConnectionStateFilter(live.connectionStateFilter),\n description: live.description,\n destination: live.destination,\n enabled: live.enabled,\n index: live.index,\n ipProtocolScope: live.ipProtocolScope,\n ipsecFilter: live.ipsecFilter,\n loggingEnabled: live.loggingEnabled,\n name: live.name,\n schedule: live.schedule,\n source: live.source,\n metadataOrigin: live.metadata.origin,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getFirewallPolicy`'s own response\n * straight in and writes the result as `firewallPolicy('<id>', declareFirewallPolicy(live, siteId))`\n * — no field is invented or defaulted beyond what `attributesOf` already reports, so\n * `matches(attributesOf(live, props), declareFirewallPolicy(live, siteId))` is `true` by\n * construction (`firewall-policy.test.ts` proves it).\n */\nexport const declareFirewallPolicy = (\n live: firewall.FirewallPolicy,\n siteId: string,\n): FirewallPolicyProps => ({\n siteId,\n firewallPolicyId: live.id,\n action: live.action,\n connectionStateFilter: normalizeConnectionStateFilter(live.connectionStateFilter),\n description: live.description,\n destination: live.destination,\n enabled: live.enabled,\n ipProtocolScope: live.ipProtocolScope,\n ipsecFilter: live.ipsecFilter,\n loggingEnabled: live.loggingEnabled,\n name: live.name,\n schedule: live.schedule,\n source: live.source,\n});\n",
|
|
31
32
|
"/**\n * `Unifi.FirewallPolicy`'s `matches` AND `driftOf` (B6) — both built on the ONE field list below,\n * the same `fieldDrift`-is-the-only-comparison shape `network-drift.ts`'s header explains (MEDIUM-4,\n * red team, 2026-09-26): `matches(attrs, props) === (driftOf(live, props).length === 0)` is true\n * BY CONSTRUCTION, never two hand-kept-in-sync comparisons.\n *\n * ⚠️ `stripNullish: true` ON EVERY FIELD BY DEFAULT (`drift.ts`'s `defaultEqual`) — same \"UniFi's\n * JSON answers `null`, a declaration omits\" tolerance every other family here relies on.\n * ⛔ `connectionStateFilter` RUNS THROUGH `firewall-policy-form.ts`'s normalizer on BOTH sides\n * before comparing — never applied to only `live` or only `declared` (the specific gap MEDIUM-4's\n * own mutant found in `network-drift.ts`; see its header, and `acl-rule-drift.ts`'s own repeat of\n * this same rule for `protocolFilter`/`enforcingDeviceFilter`).\n * ★ `action`/`destination`/`ipProtocolScope`/`schedule`/`source` compare with the plain default\n * `equal` (whole-value `deepEqual`) — see `firewall-policy-form.ts`'s header for the accepted\n * known gap this leaves in their own nested set-like arrays.\n */\nimport type * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport { makeDriftOf } from './drift.ts';\nimport {\n type FirewallPolicyAttributes,\n type FirewallPolicyProps,\n attributesOf,\n normalizeConnectionStateFilter,\n} from './firewall-policy-form.ts';\n\nconst fieldDrift = makeDriftOf<FirewallPolicyAttributes, FirewallPolicyProps>([\n { field: 'action', live: (a) => a.action, declared: (p) => p.action },\n {\n field: 'connectionStateFilter',\n live: (a) => normalizeConnectionStateFilter(a.connectionStateFilter),\n declared: (p) => normalizeConnectionStateFilter(p.connectionStateFilter),\n },\n { field: 'description', live: (a) => a.description, declared: (p) => p.description },\n { field: 'destination', live: (a) => a.destination, declared: (p) => p.destination },\n { field: 'enabled', live: (a) => a.enabled, declared: (p) => p.enabled },\n { field: 'ipProtocolScope', live: (a) => a.ipProtocolScope, declared: (p) => p.ipProtocolScope },\n { field: 'ipsecFilter', live: (a) => a.ipsecFilter, declared: (p) => p.ipsecFilter },\n { field: 'loggingEnabled', live: (a) => a.loggingEnabled, declared: (p) => p.loggingEnabled },\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n { field: 'schedule', live: (a) => a.schedule, declared: (p) => p.schedule },\n { field: 'source', live: (a) => a.source, declared: (p) => p.source },\n]);\n\nexport const matches = (\n attributes: FirewallPolicyAttributes,\n props: FirewallPolicyProps,\n): boolean => fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (live: firewall.FirewallPolicy, props: FirewallPolicyProps) =>\n fieldDrift(attributesOf(live, props), props);\n",
|
|
32
33
|
"/**\n * `Unifi.FirewallPolicyOrdering` — the ordered `before`/`afterSystemDefined` policy-id lists for\n * ONE `(sourceFirewallZoneId, destinationFirewallZoneId)` zone pair (`getFirewallPolicyOrdering`,\n * `GET /v1/sites/{siteId}/firewall/policies/ordering`), READ-ONLY (`policy.ts`). T5/I4's per-family\n * variant: `docs/unifi.md`'s own header named this \"a future `Unifi.FirewallPolicyOrdering`\" as a\n * separate, bigger PR (see `acl-rule-ordering.ts`'s header for the same forward reference, now\n * resolved) — this is that resource.\n *\n * ⚠️ NOT ONE OBJECT PER SITE, UNLIKE `acl-rule-ordering.ts` — the identity is the TRIPLE\n * `(siteId, sourceFirewallZoneId, destinationFirewallZoneId)`, both zone ids passed as QUERY\n * parameters on the same path every time (`firewall.ts`'s `GetFirewallPolicyOrderingRequest`).\n * There is no \"list every zone pair with an ordering\" endpoint — a caller (a future Lane C import)\n * has to already know which pairs exist, typically by walking `firewall.getFirewallPolicies`\n * (T9's own 424-live-policy scale — B0b's `pageAll`, see `firewall-policy.test.ts`'s own decode\n * proof) and grouping by each policy's `source.zoneId`/`destination.zoneId`. Nothing here performs\n * that discovery — this resource is one already-known pair's ordering, same as\n * `firewall-policy.ts`/`acl-rule.ts` are one already-known object's own fields.\n */\nimport { Resource } from 'alchemy';\nimport { adopt } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport * as Effect from 'effect/Effect';\nimport {\n type FirewallPolicyOrderingAttributes,\n type FirewallPolicyOrderingProps,\n attributesOf,\n matches,\n} from './firewall-policy-ordering-form.ts';\nimport type { UnifiRequirements, UnifiSpec } from './resource.ts';\nimport { unifiHandlers } from './resource.ts';\n\nexport type {\n FirewallPolicyOrderingAttributes,\n FirewallPolicyOrderingProps,\n} from './firewall-policy-ordering-form.ts';\nexport { declareFirewallPolicyOrdering, driftOf } from './firewall-policy-ordering-form.ts';\n\nexport interface UnifiFirewallPolicyOrdering extends Resource<\n 'Unifi.FirewallPolicyOrdering',\n FirewallPolicyOrderingProps,\n FirewallPolicyOrderingAttributes,\n never,\n UnifiRequirements\n> {}\n\nexport const UnifiFirewallPolicyOrdering = Resource<UnifiFirewallPolicyOrdering>(\n 'Unifi.FirewallPolicyOrdering',\n { defaultRemovalPolicy: 'retain' },\n);\n\n/** `firewallPolicyOrdering('iot-to-external', props)` — `adopt(true)` piped on by default (H5); see\n * `network.ts`. */\nexport const firewallPolicyOrdering = (id: string, props: FirewallPolicyOrderingProps) =>\n UnifiFirewallPolicyOrdering(id, props).pipe(adopt(true));\n\n/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */\nexport const spec: UnifiSpec<\n FirewallPolicyOrderingProps,\n firewall.IntegrationFirewallPolicyOrderingDto,\n FirewallPolicyOrderingAttributes,\n firewall.GetFirewallPolicyOrderingError\n> = {\n type: 'Unifi.FirewallPolicyOrdering',\n describe: (props) =>\n `sites/${props.siteId}/firewall/policies/ordering` +\n `?sourceFirewallZoneId=${props.sourceFirewallZoneId}` +\n `&destinationFirewallZoneId=${props.destinationFirewallZoneId}`,\n fetchLive: (props) =>\n firewall\n .getFirewallPolicyOrdering({\n siteId: props.siteId,\n sourceFirewallZoneId: props.sourceFirewallZoneId,\n destinationFirewallZoneId: props.destinationFirewallZoneId,\n })\n .pipe(Effect.catchTag('NotFound', () => Effect.succeed(undefined))),\n attributes: attributesOf,\n matches,\n};\n\nexport const handlers = unifiHandlers(spec);\n\nexport const UnifiFirewallPolicyOrderingProvider = () =>\n Provider.effect(\n UnifiFirewallPolicyOrdering,\n Effect.succeed(UnifiFirewallPolicyOrdering.Provider.of(handlers)),\n );\n",
|
|
33
34
|
"/**\n * `Unifi.FirewallPolicyOrdering`'s wire shape — ONE resource per `(sourceFirewallZoneId,\n * destinationFirewallZoneId)` ZONE PAIR (`getFirewallPolicyOrdering`'s own scope, `GET\n * /v1/sites/{siteId}/firewall/policies/ordering?sourceFirewallZoneId=…&destinationFirewallZoneId=…`),\n * not per policy and not site-wide. Compare with `acl-rule-ordering-form.ts`'s own header: ACL rule\n * order is ONE list for the whole site because ACL rules have no zone concept; firewall policies are\n * scoped to a zone pair (`firewall-zone.ts`'s own `Unifi.FirewallZone`), so the vendor keys ordering\n * the same way — a console can have as many `Unifi.FirewallPolicyOrdering` resources as it has\n * zone pairs with at least one user-defined policy between them.\n *\n * ⛔ T5 — `beforeSystemDefined`/`afterSystemDefined` ARE EACH A SEQUENCE, NEVER RUN THROUGH\n * `sortedSet`. `OrderedFirewallPolicyIDs`' own shape splits a zone pair's user-defined policies\n * into the ones ordered BEFORE the system-defined policies and the ones ordered AFTER them —\n * within each half, position is priority, the same \"lower index/earlier position wins\" reasoning\n * `acl-rule-ordering-form.ts` gives for `orderedAclRuleIds`. `drift.ts`'s `defaultEqual`\n * (`deepEqual`) already compares arrays element-by-element without sorting them, so leaving both\n * fields un-normalized is what makes the comparison order-sensitive, not an oversight.\n *\n * ★ TWO SEQUENCES, NOT ONE — the one real difference from `acl-rule-ordering-form.ts`'s single\n * `orderedAclRuleIds`. A policy ordered \"before system-defined\" and one ordered \"after\" can never\n * swap sides through this endpoint (there is no single combined list position that would let one\n * policy move from one half to the other) — `matches`/`driftOf` below compare each half\n * independently, so a policy moving from `beforeSystemDefined` to `afterSystemDefined` reports as\n * drift on BOTH fields, not a single reordering of one array.\n *\n * ★ `makeDriftOf`, NO CUSTOM `equal` — same framework every other family's `matches`/`driftOf`\n * share (MEDIUM-4). Its default `deepEqual` is already order-sensitive for arrays, so the absence\n * of a custom `equal` here is itself the \"never sortedSet\" rule in force, exactly as\n * `acl-rule-ordering-form.ts`'s own header explains for its one field.\n */\nimport type * as firewall from '@distilled.cloud/unifi-network/firewall';\nimport { makeDriftOf } from './drift.ts';\n\nexport interface FirewallPolicyOrderingProps {\n siteId: string;\n sourceFirewallZoneId: string;\n destinationFirewallZoneId: string;\n /** Policy IDs ordered before the zone pair's system-defined policies, highest priority first —\n * never compared as a set (see header). */\n beforeSystemDefined: string[];\n /** Policy IDs ordered after the zone pair's system-defined policies, highest priority first —\n * never compared as a set (see header). */\n afterSystemDefined: string[];\n}\n\nexport interface FirewallPolicyOrderingAttributes {\n siteId: string;\n sourceFirewallZoneId: string;\n destinationFirewallZoneId: string;\n beforeSystemDefined: string[];\n afterSystemDefined: string[];\n}\n\nexport const attributesOf = (\n live: firewall.IntegrationFirewallPolicyOrderingDto,\n props: FirewallPolicyOrderingProps,\n): FirewallPolicyOrderingAttributes => ({\n siteId: props.siteId,\n sourceFirewallZoneId: props.sourceFirewallZoneId,\n destinationFirewallZoneId: props.destinationFirewallZoneId,\n beforeSystemDefined: live.orderedFirewallPolicyIds.beforeSystemDefined,\n afterSystemDefined: live.orderedFirewallPolicyIds.afterSystemDefined,\n});\n\n/**\n * The declaration renderer (task spec: \"given one live object, return the props a declaration\n * needs so its plan is noop\"). A later import script feeds `getFirewallPolicyOrdering`'s own\n * response straight in and writes the result as `firewallPolicyOrdering('<id>',\n * declareFirewallPolicyOrdering(live, siteId, sourceZoneId, destZoneId))` —\n * `matches(attributesOf(live, props), declareFirewallPolicyOrdering(live, …))` is `true` by\n * construction (`firewall-policy-ordering.test.ts` proves it).\n */\nexport const declareFirewallPolicyOrdering = (\n live: firewall.IntegrationFirewallPolicyOrderingDto,\n siteId: string,\n sourceFirewallZoneId: string,\n destinationFirewallZoneId: string,\n): FirewallPolicyOrderingProps => ({\n siteId,\n sourceFirewallZoneId,\n destinationFirewallZoneId,\n beforeSystemDefined: live.orderedFirewallPolicyIds.beforeSystemDefined,\n afterSystemDefined: live.orderedFirewallPolicyIds.afterSystemDefined,\n});\n\nconst fieldDrift = makeDriftOf<FirewallPolicyOrderingAttributes, FirewallPolicyOrderingProps>([\n {\n field: 'beforeSystemDefined',\n live: (a) => a.beforeSystemDefined,\n declared: (p) => p.beforeSystemDefined,\n },\n {\n field: 'afterSystemDefined',\n live: (a) => a.afterSystemDefined,\n declared: (p) => p.afterSystemDefined,\n },\n]);\n\nexport const matches = (\n attributes: FirewallPolicyOrderingAttributes,\n props: FirewallPolicyOrderingProps,\n): boolean => fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`).\n */\nexport const driftOf = (\n live: firewall.IntegrationFirewallPolicyOrderingDto,\n props: FirewallPolicyOrderingProps,\n) => fieldDrift(attributesOf(live, props), props);\n",
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
"/**\n * `Unifi.TrafficMatchingList`'s `matches` AND `driftOf` (B6) — both built on the ONE field list\n * below, the same `fieldDrift`-is-the-only-comparison shape `network-drift.ts`'s header explains\n * (MEDIUM-4, red team, 2026-09-26): `matches(attrs, props) === (driftOf(live, props).length === 0)`\n * is true BY CONSTRUCTION, never two hand-kept-in-sync comparisons.\n *\n * ⚠️ `stripNullish: true` ON EVERY FIELD BY DEFAULT (`drift.ts`'s `defaultEqual`) — same \"UniFi's\n * JSON answers `null`, a declaration omits\" tolerance every other family here relies on.\n * ★ `items` compares with the plain default `equal` (whole-value `deepEqual`) — see\n * `traffic-matching-list-form.ts`'s header for the accepted known gap this leaves for a\n * membership-preserving reorder.\n */\nimport type * as trafficMatchingLists from '@distilled.cloud/unifi-network/traffic_matching_lists';\nimport { makeDriftOf } from './drift.ts';\nimport {\n type TrafficMatchingListAttributes,\n type TrafficMatchingListProps,\n attributesOf,\n} from './traffic-matching-list-form.ts';\n\nconst fieldDrift = makeDriftOf<TrafficMatchingListAttributes, TrafficMatchingListProps>([\n { field: 'name', live: (a) => a.name, declared: (p) => p.name },\n { field: 'type', live: (a) => a.type, declared: (p) => p.type },\n { field: 'items', live: (a) => a.items, declared: (p) => p.items },\n]);\n\nexport const matches = (\n attributes: TrafficMatchingListAttributes,\n props: TrafficMatchingListProps,\n): boolean => fieldDrift(attributes, props).length === 0;\n\n/**\n * Per-field live-vs-declared drift straight from one live read — the task's own API\n * (`driftOf(live, props)`), so `homeflare-network`'s pre-import drift check never has to derive\n * `attributesOf` itself just to call this.\n */\nexport const driftOf = (\n live: trafficMatchingLists.TrafficMatchingList,\n props: TrafficMatchingListProps,\n) => fieldDrift(attributesOf(live, props), props);\n",
|
|
37
38
|
"/**\n * Every `Unifi.*` provider as one layer for a stack.\n *\n * Layer.mergeAll(unifiProviders(), …the stack's other providers)\n * .pipe(Layer.provide(FetchHttpClient.layer))\n *\n * S16: a subpath's `providers()` plays the role upstream's own `Providers.ts` does — one minimal\n * insertion per family. `FetchHttpClient.layer` is the stack's own responsibility to provide,\n * mirroring `../discord/providers.ts`.\n */\nimport * as Layer from 'effect/Layer';\nimport { UnifiAclRuleProvider } from './acl-rule.ts';\nimport { UnifiAclRuleOrderingProvider } from './acl-rule-ordering.ts';\nimport { UnifiDnsPolicyProvider } from './dns-policy.ts';\nimport { UnifiFirewallPolicyProvider } from './firewall-policy.ts';\nimport { UnifiFirewallPolicyOrderingProvider } from './firewall-policy-ordering.ts';\nimport { UnifiFirewallZoneProvider } from './firewall-zone.ts';\nimport { UnifiNetworkProvider } from './network.ts';\nimport { UnifiTrafficMatchingListProvider } from './traffic-matching-list.ts';\nimport { UnifiWifiBroadcastProvider } from './wifi-broadcast.ts';\n\nexport const providers = () =>\n Layer.mergeAll(\n UnifiNetworkProvider(),\n UnifiFirewallZoneProvider(),\n UnifiDnsPolicyProvider(),\n UnifiAclRuleProvider(),\n UnifiAclRuleOrderingProvider(),\n UnifiWifiBroadcastProvider(),\n UnifiFirewallPolicyProvider(),\n UnifiFirewallPolicyOrderingProvider(),\n UnifiTrafficMatchingListProvider(),\n );\n"
|
|
38
39
|
],
|
|
39
|
-
"mappings": "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",
|
|
40
|
-
"debugId": "
|
|
40
|
+
"mappings": "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",
|
|
41
|
+
"debugId": "F58E0BCA65985B3464756E2164756E21",
|
|
41
42
|
"names": []
|
|
42
43
|
}
|