@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
|
@@ -50,7 +50,7 @@ export declare const handlers: {
|
|
|
50
50
|
session: {
|
|
51
51
|
note: (message: string) => Effect.Effect<void, never, never>;
|
|
52
52
|
};
|
|
53
|
-
}) => Effect.Effect<AclRuleOrderingAttributes, import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | aclRules.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
53
|
+
}) => Effect.Effect<AclRuleOrderingAttributes, import("./policy.ts").UnifiCreateDidNotConverge | import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | aclRules.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
54
54
|
delete: (args: {
|
|
55
55
|
olds: AclRuleOrderingProps;
|
|
56
56
|
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, import("effect/http/HttpClient").HttpClient>;
|
package/dist/unifi/acl-rule.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export declare const handlers: {
|
|
|
45
45
|
session: {
|
|
46
46
|
note: (message: string) => Effect.Effect<void, never, never>;
|
|
47
47
|
};
|
|
48
|
-
}) => Effect.Effect<AclRuleAttributes, import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | aclRules.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
48
|
+
}) => Effect.Effect<AclRuleAttributes, import("./policy.ts").UnifiCreateDidNotConverge | import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | aclRules.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
49
49
|
delete: (args: {
|
|
50
50
|
olds: AclRuleProps;
|
|
51
51
|
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, import("effect/http/HttpClient").HttpClient>;
|
|
@@ -51,7 +51,7 @@ export declare const handlers: {
|
|
|
51
51
|
session: {
|
|
52
52
|
note: (message: string) => Effect.Effect<void, never, never>;
|
|
53
53
|
};
|
|
54
|
-
}) => Effect.Effect<DnsPolicyAttributes, import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | dnsPolicies.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
54
|
+
}) => Effect.Effect<DnsPolicyAttributes, import("./policy.ts").UnifiCreateDidNotConverge | import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | dnsPolicies.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
55
55
|
delete: (args: {
|
|
56
56
|
olds: DnsPolicyProps;
|
|
57
57
|
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, import("effect/http/HttpClient").HttpClient>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Unifi.FirewallPolicy`'s CREATE — the one write this family may make, and only for a row whose
|
|
3
|
+
* declared `name` passes `policy.ts`'s `isHfPolicyName` (Tim 2026-10-19 ~16:20Z, "Allow kit to
|
|
4
|
+
* create hf: rules"). THE ONLY FILE UNDER `src/unifi` ALLOWED TO NAME `createFirewallPolicy`
|
|
5
|
+
* (`write-op-reference.test.ts`'s `WRITE_OP_ALLOWLIST` pins that, alongside `network-update.ts`'s
|
|
6
|
+
* own `updateNetwork` entry).
|
|
7
|
+
*
|
|
8
|
+
* ⛔ A CREATE SENDS ONLY THE FIELDS THE DECLARATION OWNS — NEVER A SPREAD OF ANYTHING ELSE. Unlike
|
|
9
|
+
* `network-update.ts`'s whole-object PUT (which must carry the untouched live object forward so
|
|
10
|
+
* an omitted optional block is not read by the controller as "turn it off"), there IS no live
|
|
11
|
+
* object yet: `createBody` builds `CreateFirewallPolicyRequest` field by field from `props`,
|
|
12
|
+
* omitting every optional key `props` itself leaves `undefined` rather than sending it as
|
|
13
|
+
* `null`/`undefined` (UniFi's "missing = off" rule cuts both ways — a key that is actually absent
|
|
14
|
+
* from the JSON body is what "this policy does not restrict on this axis" means here, same as a
|
|
15
|
+
* declaration that never sets `connectionStateFilter` means "match every connection state").
|
|
16
|
+
* ⛔ `checkScope` RUNS BEFORE THE REQUEST, TYPED, NEVER A STRING COMPARISON INLINE AT THE CALL
|
|
17
|
+
* SITE — one function (`policy.ts`'s `isHfPolicyName`) decides "hf:" everywhere this boundary is
|
|
18
|
+
* checked, so a future second caller cannot drift from this one with a slightly different regex.
|
|
19
|
+
*
|
|
20
|
+
* 🔴 `checkScope` ALSO REFUSES A `firewallPolicyId` THAT IS ALREADY SET (Opus red team,
|
|
21
|
+
* 2026-10-19, PRIMARY FINDING). `resource.ts`'s `create` branch runs for EVERY
|
|
22
|
+
* `Unifi.FirewallPolicy` declaration with no prior Alchemy state, not only ones meant to
|
|
23
|
+
* create — including every `firewallPolicy()` import `.pipe(adopt(true))`, and Tim's own live
|
|
24
|
+
* console ALREADY names hand-made rules `hf: <desc>` (this file's sibling `policy.ts` cites
|
|
25
|
+
* `hf: Vpn to MGMT (Magic WAN site-to-site)` as the existing convention). Before this check, an
|
|
26
|
+
* hf:-named IMPORTED row whose `firewallPolicyId` stopped resolving (state lost, the rule
|
|
27
|
+
* hand-deleted and recreated with a new id, a wrong id in a generated declaration) would GET
|
|
28
|
+
* 404, fall into `createReconcile`, pass the name check, and POST A DUPLICATE — silently, with
|
|
29
|
+
* a green plan, where every other family still refuses "declared row absent" outright
|
|
30
|
+
* (`update-reconcile.ts`'s original behavior, unchanged for every family but this one). A
|
|
31
|
+
* declaration carrying a vendor id is BY DEFINITION an existing row, adopted or updated, never
|
|
32
|
+
* a fresh create; `firewallPolicyId !== undefined` is refused here before `isHfPolicyName` is
|
|
33
|
+
* even checked, so this holds regardless of what the name says.
|
|
34
|
+
*/
|
|
35
|
+
import * as firewall from '@distilled.cloud/unifi-network/firewall';
|
|
36
|
+
import * as Effect from 'effect/Effect';
|
|
37
|
+
import type { FirewallPolicyProps } from './firewall-policy-form.ts';
|
|
38
|
+
import type { AllowedWrite } from './wire-guard.ts';
|
|
39
|
+
/** The site's fixed firewall-policies COLLECTION route — never a per-row path, since there is no
|
|
40
|
+
* row id before this POST creates one. Takes `props` (not just a bare `siteId`) to match
|
|
41
|
+
* `UnifiUpdate.allowedWrite`'s own shape, so `resource.ts`'s wiring treats both the same way. */
|
|
42
|
+
export declare const firewallPolicyCreateAllowedWrite: (props: FirewallPolicyProps) => AllowedWrite;
|
|
43
|
+
/** Refuses, typed, before any request: the one gate `createReconcile` calls before `write`.
|
|
44
|
+
* Order matters for the message only (both refuse the same way) — the identity check runs
|
|
45
|
+
* first because it is the one that must hold regardless of what the name says (see header). */
|
|
46
|
+
export declare const checkHfCreateScope: (props: FirewallPolicyProps) => Effect.Effect<void, never, never> | Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, never>;
|
|
47
|
+
/** Every field `CreateFirewallPolicyRequest` accepts, straight from the declaration, with an
|
|
48
|
+
* absent optional field left OUT of the body entirely (never sent as `undefined`/`null`) —
|
|
49
|
+
* `JSON.stringify` already drops an `undefined` value, but building the object field-by-field
|
|
50
|
+
* rather than spreading `props` keeps this list the single place that enumerates exactly what a
|
|
51
|
+
* create may send, so a future prop added to `FirewallPolicyProps` for some OTHER purpose cannot
|
|
52
|
+
* silently become a wire field here without this file changing too. */
|
|
53
|
+
export declare const firewallPolicyCreateBody: (props: FirewallPolicyProps) => firewall.CreateFirewallPolicyRequest;
|
|
54
|
+
export declare const writeFirewallPolicyCreate: (props: FirewallPolicyProps) => Effect.Effect<firewall.FirewallPolicy, firewall.UnifiNetworkOpError, firewall.UnifiNetworkOpContext>;
|
|
55
|
+
//# sourceMappingURL=firewall-policy-create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firewall-policy-create.d.ts","sourceRoot":"","sources":["../../src/unifi/firewall-policy-create.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,QAAQ,MAAM,yCAAyC,CAAC;AACpE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;kGAEkG;AAClG,eAAO,MAAM,gCAAgC,UAAW,mBAAmB,KAAG,YAG5E,CAAC;AAKH;;gGAEgG;AAChG,eAAO,MAAM,kBAAkB,UAAW,mBAAmB,6GAW5D,CAAC;AAEF;;;;;wEAKwE;AACxE,eAAO,MAAM,wBAAwB,UAC5B,mBAAmB,KACzB,QAAQ,CAAC,2BAeV,CAAC;AAEH,eAAO,MAAM,yBAAyB,UAAW,mBAAmB,yGACJ,CAAC"}
|
|
@@ -49,10 +49,17 @@ import type * as firewall from '@distilled.cloud/unifi-network/firewall';
|
|
|
49
49
|
* class (MEDIUM-4). */
|
|
50
50
|
export declare const normalizeConnectionStateFilter: (value: readonly string[] | undefined) => string[] | undefined;
|
|
51
51
|
/** ⚠️ EVERY OPTIONAL FIELD SPELLS OUT `| undefined` — `exactOptionalPropertyTypes`; see
|
|
52
|
-
* `network-form.ts`'s own header.
|
|
52
|
+
* `network-form.ts`'s own header.
|
|
53
|
+
*
|
|
54
|
+
* ⛔ `firewallPolicyId` IS OPTIONAL SINCE THE 2026-10-19 hf: CREATE LIFT. An adopt-only
|
|
55
|
+
* declaration (`declareFirewallPolicy`, every imported row) always carries the vendor id it was
|
|
56
|
+
* read from. A fresh `hf:` creatable declaration (`declareHfFirewallPolicy` below) does not have
|
|
57
|
+
* one yet — the vendor assigns it on `createFirewallPolicy`'s response — so `spec.fetchLive`
|
|
58
|
+
* (`firewall-policy.ts`) locates by `name` instead of `id` whenever this is absent (netbox's
|
|
59
|
+
* `locateOne` is the precedent for identity-by-natural-key over identity-by-vendor-id). */
|
|
53
60
|
export interface FirewallPolicyProps {
|
|
54
61
|
siteId: string;
|
|
55
|
-
firewallPolicyId
|
|
62
|
+
firewallPolicyId?: string | undefined;
|
|
56
63
|
action: firewall.FirewallPolicyAction;
|
|
57
64
|
/** Connection states this policy matches — compared as a SET, see the header. */
|
|
58
65
|
connectionStateFilter?: string[] | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firewall-policy-form.d.ts","sourceRoot":"","sources":["../../src/unifi/firewall-policy-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,OAAO,KAAK,KAAK,QAAQ,MAAM,yCAAyC,CAAC;AAIzE;;wBAEwB;AACxB,eAAO,MAAM,8BAA8B,UAClC,SAAS,MAAM,EAAE,GAAG,SAAS,KACnC,MAAM,EAAE,GAAG,SAA2D,CAAC;AAE1E;
|
|
1
|
+
{"version":3,"file":"firewall-policy-form.d.ts","sourceRoot":"","sources":["../../src/unifi/firewall-policy-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,OAAO,KAAK,KAAK,QAAQ,MAAM,yCAAyC,CAAC;AAIzE;;wBAEwB;AACxB,eAAO,MAAM,8BAA8B,UAClC,SAAS,MAAM,EAAE,GAAG,SAAS,KACnC,MAAM,EAAE,GAAG,SAA2D,CAAC;AAE1E;;;;;;;;6FAQ6F;AAC7F,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IACtC,iFAAiF;IACjF,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7C,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,sFAAsF;IACtF,WAAW,EAAE,QAAQ,CAAC,yBAAyB,CAAC;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,8FAA8F;IAC9F,eAAe,EAAE,QAAQ,CAAC,6BAA6B,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,oGAAoG;IACpG,QAAQ,CAAC,EAAE,QAAQ,CAAC,gBAAgB,GAAG,SAAS,CAAC;IACjD,iFAAiF;IACjF,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC;CACvC;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IACtC,qBAAqB,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC5C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,QAAQ,CAAC,yBAAyB,CAAC;IAChD,OAAO,EAAE,OAAO,CAAC;IACjB,oGAAoG;IACpG,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,QAAQ,CAAC,6BAA6B,CAAC;IACxD,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,cAAc,EAAE,OAAO,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAChD,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC;IACtC,6FAA6F;IAC7F,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,YAAY,SACjB,QAAQ,CAAC,cAAc,SACtB,mBAAmB,KACzB,wBAgBD,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,SAC1B,QAAQ,CAAC,cAAc,UACrB,MAAM,KACb,mBAcD,CAAC"}
|
|
@@ -53,7 +53,7 @@ export declare const handlers: {
|
|
|
53
53
|
session: {
|
|
54
54
|
note: (message: string) => Effect.Effect<void, never, never>;
|
|
55
55
|
};
|
|
56
|
-
}) => Effect.Effect<FirewallPolicyOrderingAttributes, import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | firewall.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
56
|
+
}) => Effect.Effect<FirewallPolicyOrderingAttributes, import("./policy.ts").UnifiCreateDidNotConverge | import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | firewall.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
57
57
|
delete: (args: {
|
|
58
58
|
olds: FirewallPolicyOrderingProps;
|
|
59
59
|
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, import("effect/http/HttpClient").HttpClient>;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `Unifi.FirewallPolicy` — one firewall policy on a UniFi Network site
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* `Unifi.FirewallPolicy` — one firewall policy on a UniFi Network site. READ/ADOPT-ONLY for every
|
|
3
|
+
* row (`policy.ts`), EXCEPT create for a row whose declared `name` starts `hf: `
|
|
4
|
+
* (`isHfPolicyName`, Tim 2026-10-19 ~16:20Z) — `update` and `delete` stay refused even for an
|
|
5
|
+
* `hf:` row. `Unifi.FirewallPolicyOrdering` (`firewall-policy-ordering.ts`) is the sibling
|
|
6
|
+
* resource for policy ORDER within a zone pair (T5) — one per `(sourceFirewallZoneId,
|
|
7
|
+
* destinationFirewallZoneId)`, still read-only; this lift does not touch it, so a created `hf:`
|
|
8
|
+
* row lands wherever the controller places a new policy, and placing it ahead of its zone pair's
|
|
9
|
+
* "Block All Traffic" (if the controller does not already do that) would need an ordering WRITE —
|
|
10
|
+
* a second, separate lift this PR does not make (see `docs/unifi-firewall-policy.md`).
|
|
7
11
|
*
|
|
8
12
|
* ★ T11 GATE, CHECKED 2026-09-26: the `Firewall` tag's 13 operations (5 already covered for
|
|
9
13
|
* `Unifi.FirewallZone` in `docs/unifi.md`, 8 new here for `FirewallPolicy` — `getFirewallPolicies`,
|
|
@@ -14,30 +18,59 @@
|
|
|
14
18
|
* that DID change elsewhere in the document. Corroborated independently by the A3 changeset's own
|
|
15
19
|
* broader 25-operation/139-schema re-check (`.changeset/unifi-network-a3-typed-discriminator-filters.md`).
|
|
16
20
|
* Full breakdown: `docs/unifi-firewall-policy.md`.
|
|
21
|
+
*
|
|
22
|
+
* ⛔ `fetchLive` LOCATES BY `name` WHEN `firewallPolicyId` IS ABSENT (an `hf:` creatable
|
|
23
|
+
* declaration never has the vendor id before its first successful create). A real site's policy
|
|
24
|
+
* count is in the hundreds (T9, 424 live policies), so this walks every page with `pageAll`
|
|
25
|
+
* rather than trusting any one page to hold the match — same reasoning netbox's `locateOne`
|
|
26
|
+
* gives for a server-side filter that cannot be trusted alone, except here there IS no
|
|
27
|
+
* server-side name filter at all, so every row is paged. More than one row sharing a name is a
|
|
28
|
+
* defect, not a reason to take the first: `policy.ts`'s naming convention is exactly `hf: <desc>`
|
|
29
|
+
* because a free-text description makes a collision astronomically unlikely, but if the console
|
|
30
|
+
* ever has one, this engine refuses to guess which row is "the" declared one.
|
|
17
31
|
*/
|
|
18
32
|
import { Resource } from 'alchemy';
|
|
19
33
|
import * as Provider from 'alchemy/Provider';
|
|
20
34
|
import * as firewall from '@distilled.cloud/unifi-network/firewall';
|
|
21
35
|
import * as Effect from 'effect/Effect';
|
|
22
36
|
import { type FirewallPolicyAttributes, type FirewallPolicyProps } from './firewall-policy-form.ts';
|
|
37
|
+
import { type UnifiPaginationInconsistent } from './paginate.ts';
|
|
23
38
|
import type { UnifiRequirements, UnifiSpec } from './resource.ts';
|
|
39
|
+
import type { UnifiWriteRefused } from './policy.ts';
|
|
24
40
|
export type { FirewallPolicyAttributes, FirewallPolicyProps } from './firewall-policy-form.ts';
|
|
25
41
|
export { declareFirewallPolicy } from './firewall-policy-form.ts';
|
|
26
42
|
export { driftOf } from './firewall-policy-drift.ts';
|
|
43
|
+
/** `checkHfCreateScope`'s typed refusal, union'd with the real create call's own error, same
|
|
44
|
+
* shape `network.ts`'s own `UpdateError` gives its write half. */
|
|
45
|
+
type CreateError = UnifiWriteRefused | firewall.CreateFirewallPolicyError;
|
|
27
46
|
export interface UnifiFirewallPolicy extends Resource<'Unifi.FirewallPolicy', FirewallPolicyProps, FirewallPolicyAttributes, never, UnifiRequirements> {
|
|
28
47
|
}
|
|
29
48
|
export declare const UnifiFirewallPolicy: import("alchemy").ResourceClass<UnifiFirewallPolicy>;
|
|
30
|
-
/** `firewallPolicy('block-iot-wan', props)` —
|
|
31
|
-
* `network.ts`. */
|
|
49
|
+
/** `firewallPolicy('block-iot-wan', props)` — for an IMPORTED, adopt-only row (always carries the
|
|
50
|
+
* vendor's `firewallPolicyId`). `adopt(true)` piped on by default (H5); see `network.ts`. */
|
|
32
51
|
export declare const firewallPolicy: (id: string, props: FirewallPolicyProps) => Effect.Effect<UnifiFirewallPolicy, never, import("effect/http/HttpClient").HttpClient>;
|
|
52
|
+
/**
|
|
53
|
+
* `hfFirewallPolicy('ct100-to-nas-https', props)` — for a NEW, kit-creatable row: `props` has no
|
|
54
|
+
* `firewallPolicyId` (the vendor assigns one), and `name` MUST start `hf: ` or this throws
|
|
55
|
+
* immediately, at declaration time, rather than waiting for `createReconcile`'s own typed refusal
|
|
56
|
+
* at deploy time. That typed refusal (`checkHfCreateScope`) is still the authoritative gate — this
|
|
57
|
+
* is a second, earlier check for the common case of a typo, not a replacement for it; a caller
|
|
58
|
+
* that builds `FirewallPolicyProps` by hand and calls `UnifiFirewallPolicy` directly still goes
|
|
59
|
+
* through `checkHfCreateScope` and is still refused the same way.
|
|
60
|
+
*
|
|
61
|
+
* ⛔ NO `adopt(true)` HERE. This is a create path, not an adoption: a cold read finding an
|
|
62
|
+
* existing row of this exact name is `Unowned`, same as any other family, and a plan that adopts
|
|
63
|
+
* one silently is not what "create" means for this lift.
|
|
64
|
+
*/
|
|
65
|
+
export declare const hfFirewallPolicy: (id: string, props: Omit<FirewallPolicyProps, 'firewallPolicyId'>) => Effect.Effect<UnifiFirewallPolicy, never, import("effect/http/HttpClient").HttpClient>;
|
|
33
66
|
/** ★ EXPORTED for direct testing against a fake `Credentials` layer — see `network.ts`'s own. */
|
|
34
|
-
export declare const spec: UnifiSpec<FirewallPolicyProps, firewall.FirewallPolicy, FirewallPolicyAttributes, firewall.GetFirewallPolicyError>;
|
|
67
|
+
export declare const spec: UnifiSpec<FirewallPolicyProps, firewall.FirewallPolicy, FirewallPolicyAttributes, firewall.GetFirewallPolicyError | firewall.GetFirewallPoliciesError | UnifiPaginationInconsistent, never, CreateError>;
|
|
35
68
|
export declare const handlers: {
|
|
36
69
|
list: () => Effect.Effect<never[], never, never>;
|
|
37
70
|
read: (args: {
|
|
38
71
|
olds: FirewallPolicyProps;
|
|
39
72
|
output: FirewallPolicyAttributes | undefined;
|
|
40
|
-
}) => Effect.Effect<FirewallPolicyAttributes | undefined, firewall.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
73
|
+
}) => Effect.Effect<FirewallPolicyAttributes | undefined, UnifiPaginationInconsistent | firewall.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
41
74
|
diff: (args: {
|
|
42
75
|
news: import("alchemy").Input<FirewallPolicyProps>;
|
|
43
76
|
output: FirewallPolicyAttributes | undefined;
|
|
@@ -45,7 +78,7 @@ export declare const handlers: {
|
|
|
45
78
|
readonly action: 'update';
|
|
46
79
|
} | {
|
|
47
80
|
readonly action: 'noop';
|
|
48
|
-
} | undefined, firewall.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
81
|
+
} | undefined, UnifiPaginationInconsistent | firewall.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
49
82
|
reconcile: (args: {
|
|
50
83
|
news: FirewallPolicyProps;
|
|
51
84
|
olds: FirewallPolicyProps | undefined;
|
|
@@ -53,10 +86,10 @@ export declare const handlers: {
|
|
|
53
86
|
session: {
|
|
54
87
|
note: (message: string) => Effect.Effect<void, never, never>;
|
|
55
88
|
};
|
|
56
|
-
}) => Effect.Effect<FirewallPolicyAttributes, import("./policy.ts").
|
|
89
|
+
}) => Effect.Effect<FirewallPolicyAttributes, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | import("@distilled.cloud/caddy").Conflict | import("@distilled.cloud/caddy").Forbidden | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/caddy").NotFound | import("@distilled.cloud/caddy").ServiceUnavailable | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("./policy.ts").UnifiCreateDidNotConverge | import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("@distilled.cloud/unifi-network/Errors").UnifiNetworkParseError | UnifiPaginationInconsistent | import("./policy.ts").UnifiUpdateDidNotConverge | UnifiWriteRefused | import("@distilled.cloud/unifi-network/Errors").UnknownUnifiNetworkError | import("@distilled.cloud/caddy").UnprocessableEntity, import("effect/http/HttpClient").HttpClient>;
|
|
57
90
|
delete: (args: {
|
|
58
91
|
olds: FirewallPolicyProps;
|
|
59
|
-
}) => Effect.Effect<never,
|
|
92
|
+
}) => Effect.Effect<never, UnifiWriteRefused, import("effect/http/HttpClient").HttpClient>;
|
|
60
93
|
};
|
|
61
94
|
export declare const UnifiFirewallPolicyProvider: () => import("effect/Layer").Layer<Provider.Provider<UnifiFirewallPolicy>, never, import("effect/http/HttpClient").HttpClient>;
|
|
62
95
|
//# sourceMappingURL=firewall-policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firewall-policy.d.ts","sourceRoot":"","sources":["../../src/unifi/firewall-policy.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"firewall-policy.d.ts","sourceRoot":"","sources":["../../src/unifi/firewall-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,QAAQ,MAAM,yCAAyC,CAAC;AACpE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAOxC,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EAEzB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,KAAK,2BAA2B,EAAW,MAAM,eAAe,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,YAAY,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAErD;mEACmE;AACnE,KAAK,WAAW,GAAG,iBAAiB,GAAG,QAAQ,CAAC,yBAAyB,CAAC;AAE1E,MAAM,WAAW,mBAAoB,SAAQ,QAAQ,CACnD,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,mBAAmB,sDAE9B,CAAC;AAEH;8FAC8F;AAC9F,eAAO,MAAM,cAAc,OAAQ,MAAM,SAAS,mBAAmB,2FACnB,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,OACvB,MAAM,SACH,IAAI,CAAC,mBAAmB,EAAE,kBAAkB,CAAC,2FAuBrD,CAAC;AAkBF,iGAAiG;AACjG,eAAO,MAAM,IAAI,EAAE,SAAS,CAC1B,mBAAmB,EACnB,QAAQ,CAAC,cAAc,EACvB,wBAAwB,EACxB,QAAQ,CAAC,sBAAsB,GAAG,QAAQ,CAAC,wBAAwB,GAAG,2BAA2B,EACjG,KAAK,EACL,WAAW,CAsBZ,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;CAAsB,CAAC;AAE5C,eAAO,MAAM,2BAA2B,gIACyD,CAAC"}
|
|
@@ -46,7 +46,7 @@ export declare const handlers: {
|
|
|
46
46
|
session: {
|
|
47
47
|
note: (message: string) => Effect.Effect<void, never, never>;
|
|
48
48
|
};
|
|
49
|
-
}) => Effect.Effect<FirewallZoneAttributes, import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | firewall.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
49
|
+
}) => Effect.Effect<FirewallZoneAttributes, import("./policy.ts").UnifiCreateDidNotConverge | import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | firewall.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
50
50
|
delete: (args: {
|
|
51
51
|
olds: FirewallZoneProps;
|
|
52
52
|
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, import("effect/http/HttpClient").HttpClient>;
|
package/dist/unifi/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export { UnifiDnsPolicy, UnifiDnsPolicyProvider, declareDnsPolicy, dnsPolicy, ty
|
|
|
16
16
|
export { UnifiAclRule, UnifiAclRuleProvider, aclRule, declareAclRule, type AclRuleAttributes, type AclRuleProps, driftOf as aclRuleDriftOf, } from './acl-rule.ts';
|
|
17
17
|
export { UnifiAclRuleOrdering, UnifiAclRuleOrderingProvider, aclRuleOrdering, declareAclRuleOrdering, type AclRuleOrderingAttributes, type AclRuleOrderingProps, driftOf as aclRuleOrderingDriftOf, } from './acl-rule-ordering.ts';
|
|
18
18
|
export { UnifiWifiBroadcast, UnifiWifiBroadcastProvider, declareWifiBroadcast, wifiBroadcast, type WifiBroadcastAttributes, type WifiBroadcastProps, driftOf as wifiBroadcastDriftOf, } from './wifi-broadcast.ts';
|
|
19
|
-
export { UnifiFirewallPolicy, UnifiFirewallPolicyProvider, declareFirewallPolicy, firewallPolicy, type FirewallPolicyAttributes, type FirewallPolicyProps, driftOf as firewallPolicyDriftOf, } from './firewall-policy.ts';
|
|
19
|
+
export { UnifiFirewallPolicy, UnifiFirewallPolicyProvider, declareFirewallPolicy, firewallPolicy, hfFirewallPolicy, type FirewallPolicyAttributes, type FirewallPolicyProps, driftOf as firewallPolicyDriftOf, } from './firewall-policy.ts';
|
|
20
20
|
export { UnifiFirewallPolicyOrdering, UnifiFirewallPolicyOrderingProvider, declareFirewallPolicyOrdering, firewallPolicyOrdering, type FirewallPolicyOrderingAttributes, type FirewallPolicyOrderingProps, driftOf as firewallPolicyOrderingDriftOf, } from './firewall-policy-ordering.ts';
|
|
21
21
|
export { UnifiTrafficMatchingList, UnifiTrafficMatchingListProvider, declareTrafficMatchingList, trafficMatchingList, type TrafficMatchingListAttributes, type TrafficMatchingListProps, driftOf as trafficMatchingListDriftOf, } from './traffic-matching-list.ts';
|
|
22
22
|
export { providers } from './providers.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/unifi/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,0BAA0B,EAC1B,2BAA2B,EAC3B,6BAA6B,EAC7B,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAIL,KAAK,YAAY,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AACvE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,OAAO,EACP,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAIjB,OAAO,IAAI,cAAc,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,EACnB,YAAY,EACZ,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,OAAO,IAAI,mBAAmB,GAC/B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,SAAS,EACT,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,OAAO,IAAI,gBAAgB,GAC5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,OAAO,EACP,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,OAAO,IAAI,cAAc,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,eAAe,EACf,sBAAsB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,OAAO,IAAI,sBAAsB,GAClC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,oBAAoB,EACpB,aAAa,EACb,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,OAAO,IAAI,oBAAoB,GAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,OAAO,IAAI,qBAAqB,GACjC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,2BAA2B,EAC3B,mCAAmC,EACnC,6BAA6B,EAC7B,sBAAsB,EACtB,KAAK,gCAAgC,EACrC,KAAK,2BAA2B,EAChC,OAAO,IAAI,6BAA6B,GACzC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,wBAAwB,EACxB,gCAAgC,EAChC,0BAA0B,EAC1B,mBAAmB,EACnB,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,OAAO,IAAI,0BAA0B,GACtC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/unifi/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,0BAA0B,EAC1B,2BAA2B,EAC3B,6BAA6B,EAC7B,yBAAyB,EACzB,sBAAsB,EACtB,iBAAiB,EACjB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAIL,KAAK,YAAY,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AACvE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,OAAO,EACP,KAAK,iBAAiB,EACtB,KAAK,YAAY,EAIjB,OAAO,IAAI,cAAc,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,EACnB,YAAY,EACZ,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,OAAO,IAAI,mBAAmB,GAC/B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,SAAS,EACT,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,OAAO,IAAI,gBAAgB,GAC5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,OAAO,EACP,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,OAAO,IAAI,cAAc,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,eAAe,EACf,sBAAsB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,OAAO,IAAI,sBAAsB,GAClC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,oBAAoB,EACpB,aAAa,EACb,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,OAAO,IAAI,oBAAoB,GAChC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,OAAO,IAAI,qBAAqB,GACjC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,2BAA2B,EAC3B,mCAAmC,EACnC,6BAA6B,EAC7B,sBAAsB,EACtB,KAAK,gCAAgC,EACrC,KAAK,2BAA2B,EAChC,OAAO,IAAI,6BAA6B,GACzC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,wBAAwB,EACxB,gCAAgC,EAChC,0BAA0B,EAC1B,mBAAmB,EACnB,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,OAAO,IAAI,0BAA0B,GACtC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/unifi/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// src/unifi/policy.ts
|
|
2
2
|
import * as Data from "effect/Data";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
|
-
var UNIFI_WRITE_POLICY = "UniFi is adopt-only except Unifi.Network update: one whole-object PUT built from the live " + "object plus the declared field changes, only on a row with prior Alchemy state, only through " +
|
|
4
|
+
var UNIFI_WRITE_POLICY = "UniFi is adopt-only except (1) Unifi.Network update: one whole-object PUT built from the live " + "object plus the declared field changes, only on a row with prior Alchemy state, only through " + 'deploy-approve; and (2) Unifi.FirewallPolicy create, only when the declared name starts "hf: ", ' + "one POST built from only the declared fields, read back and checked to match after; every " + "other create, every update of an hf: row, every delete and every other family stay refused -- " + "widening is a kit change (Tim 2026-10-10 lifts the 2026-09-24 rule for Network update, Tim " + "2026-10-19 lifts it again for hf:-named FirewallPolicy create)";
|
|
5
|
+
var isHfPolicyName = (name) => name.startsWith("hf: ");
|
|
5
6
|
var UNIFI_READ_ONLY_POLICY = UNIFI_WRITE_POLICY;
|
|
6
7
|
|
|
7
8
|
class UnifiWriteRefused extends Data.TaggedError("UnifiWriteRefused") {
|
|
@@ -30,6 +31,12 @@ class UnifiUpdateDidNotConverge extends Data.TaggedError("UnifiUpdateDidNotConve
|
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
class UnifiCreateDidNotConverge extends Data.TaggedError("UnifiCreateDidNotConverge") {
|
|
35
|
+
get message() {
|
|
36
|
+
return `${this.type} ${this.identity}: the POST created an object, but the read-back differs from ` + `the declaration on ${fieldList(this.fields)}. The object is on the console and is not ` + "rolled back; the next deploy will see this as drift. Recovery is a re-import of the live object.";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
class UnifiFieldNotRemovable extends Data.TaggedError("UnifiFieldNotRemovable") {
|
|
34
41
|
get message() {
|
|
35
42
|
return `${this.type} ${this.identity}: refusing to remove ${fieldList(this.fields)}; a whole-object ` + "PUT turns an omitted block off, and only the removable allowlist may be dropped.";
|
|
@@ -351,6 +358,28 @@ var updateReconcile = (spec, { news, olds, output, note }) => Effect5.gen(functi
|
|
|
351
358
|
fields: update.driftOf(written, news).map((d) => d.field)
|
|
352
359
|
});
|
|
353
360
|
});
|
|
361
|
+
var createReconcile = (spec, news, note) => Effect5.gen(function* () {
|
|
362
|
+
const identity = spec.describe(news);
|
|
363
|
+
const create = spec.create;
|
|
364
|
+
if (create === undefined)
|
|
365
|
+
return yield* refuseWrite(spec.type, identity, "create");
|
|
366
|
+
yield* create.checkScope(news);
|
|
367
|
+
yield* note(`${spec.type} ${identity}: POST create`);
|
|
368
|
+
const written = yield* create.write(news);
|
|
369
|
+
const withId = create.identify(news, written);
|
|
370
|
+
const confirmed = yield* spec.fetchLive(withId);
|
|
371
|
+
if (confirmed === undefined) {
|
|
372
|
+
return yield* Effect5.die(new Error(`${spec.type} ${identity}: the create POST answered, but the read-back found nothing ` + "at the id it assigned. The object may or may not exist on the console; re-import " + "before trying again rather than retrying this create blindly."));
|
|
373
|
+
}
|
|
374
|
+
const attrs = spec.attributes(confirmed, withId);
|
|
375
|
+
if (spec.matches(attrs, withId))
|
|
376
|
+
return attrs;
|
|
377
|
+
return yield* new UnifiCreateDidNotConverge({
|
|
378
|
+
type: spec.type,
|
|
379
|
+
identity,
|
|
380
|
+
fields: create.driftOf(confirmed, withId).map((d) => d.field)
|
|
381
|
+
});
|
|
382
|
+
});
|
|
354
383
|
|
|
355
384
|
// src/unifi/resource.ts
|
|
356
385
|
var unifiOperations = (spec) => {
|
|
@@ -371,12 +400,17 @@ var unifiOperations = (spec) => {
|
|
|
371
400
|
return { action: "update" };
|
|
372
401
|
return spec.matches(live, news) ? { action: "noop" } : { action: "update" };
|
|
373
402
|
}),
|
|
374
|
-
reconcile: (news, rest = {}) =>
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
403
|
+
reconcile: (news, rest = {}) => {
|
|
404
|
+
const note = rest.note ?? (() => Effect6.void);
|
|
405
|
+
const create = spec.create;
|
|
406
|
+
if (create !== undefined && rest.olds === undefined && rest.output === undefined) {
|
|
407
|
+
return Effect6.gen(function* () {
|
|
408
|
+
const live = yield* spec.fetchLive(news);
|
|
409
|
+
return live === undefined ? yield* createReconcile(spec, news, note) : yield* updateReconcile(spec, { news, olds: undefined, output: undefined, note });
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
return updateReconcile(spec, { news, olds: rest.olds, output: rest.output, note });
|
|
413
|
+
},
|
|
380
414
|
destroy: (olds) => refuseWrite(spec.type, spec.describe(olds), "delete")
|
|
381
415
|
};
|
|
382
416
|
};
|
|
@@ -394,7 +428,10 @@ var unifiHandlers = (spec) => {
|
|
|
394
428
|
olds: args.olds,
|
|
395
429
|
output: args.output,
|
|
396
430
|
note: (message) => args.session.note(message)
|
|
397
|
-
}),
|
|
431
|
+
}), [
|
|
432
|
+
...spec.update ? [spec.update.allowedWrite(args.news)] : [],
|
|
433
|
+
...spec.create && args.olds === undefined && args.output === undefined ? [spec.create.allowedWrite(args.news)] : []
|
|
434
|
+
]),
|
|
398
435
|
delete: (args) => withCredentials(ops.destroy(args.olds))
|
|
399
436
|
};
|
|
400
437
|
};
|
|
@@ -863,8 +900,38 @@ var UnifiWifiBroadcastProvider = () => Provider6.effect(UnifiWifiBroadcast, Effe
|
|
|
863
900
|
import { Resource as Resource7 } from "alchemy";
|
|
864
901
|
import { adopt as adopt7 } from "alchemy/AdoptPolicy";
|
|
865
902
|
import * as Provider7 from "alchemy/Provider";
|
|
903
|
+
import * as firewall3 from "@distilled.cloud/unifi-network/firewall";
|
|
904
|
+
import * as Effect15 from "effect/Effect";
|
|
905
|
+
|
|
906
|
+
// src/unifi/firewall-policy-create.ts
|
|
866
907
|
import * as firewall2 from "@distilled.cloud/unifi-network/firewall";
|
|
867
908
|
import * as Effect14 from "effect/Effect";
|
|
909
|
+
var firewallPolicyCreateAllowedWrite = (props) => ({
|
|
910
|
+
method: "POST",
|
|
911
|
+
tail: `/v1/sites/${props.siteId}/firewall/policies`
|
|
912
|
+
});
|
|
913
|
+
var describeByName = (props) => `sites/${props.siteId}/firewall/policies/by-name/${props.name}`;
|
|
914
|
+
var checkHfCreateScope = (props) => {
|
|
915
|
+
if (props.firewallPolicyId !== undefined) {
|
|
916
|
+
return refuseWrite("Unifi.FirewallPolicy", `sites/${props.siteId}/firewall/policies/${props.firewallPolicyId}`, "create");
|
|
917
|
+
}
|
|
918
|
+
return isHfPolicyName(props.name) ? Effect14.void : refuseWrite("Unifi.FirewallPolicy", describeByName(props), "create");
|
|
919
|
+
};
|
|
920
|
+
var firewallPolicyCreateBody = (props) => ({
|
|
921
|
+
siteId: props.siteId,
|
|
922
|
+
action: props.action,
|
|
923
|
+
...props.connectionStateFilter === undefined ? {} : { connectionStateFilter: props.connectionStateFilter },
|
|
924
|
+
...props.description === undefined ? {} : { description: props.description },
|
|
925
|
+
destination: props.destination,
|
|
926
|
+
enabled: props.enabled,
|
|
927
|
+
ipProtocolScope: props.ipProtocolScope,
|
|
928
|
+
...props.ipsecFilter === undefined ? {} : { ipsecFilter: props.ipsecFilter },
|
|
929
|
+
loggingEnabled: props.loggingEnabled,
|
|
930
|
+
name: props.name,
|
|
931
|
+
...props.schedule === undefined ? {} : { schedule: props.schedule },
|
|
932
|
+
source: props.source
|
|
933
|
+
});
|
|
934
|
+
var writeFirewallPolicyCreate = (props) => firewall2.createFirewallPolicy(firewallPolicyCreateBody(props));
|
|
868
935
|
|
|
869
936
|
// src/unifi/firewall-policy-form.ts
|
|
870
937
|
var sortedSet4 = (values) => [...new Set(values)].sort();
|
|
@@ -928,21 +995,44 @@ var UnifiFirewallPolicy = Resource7("Unifi.FirewallPolicy", {
|
|
|
928
995
|
defaultRemovalPolicy: "retain"
|
|
929
996
|
});
|
|
930
997
|
var firewallPolicy = (id, props) => UnifiFirewallPolicy(id, props).pipe(adopt7(true));
|
|
998
|
+
var hfFirewallPolicy = (id, props) => {
|
|
999
|
+
if ("firewallPolicyId" in props && props.firewallPolicyId !== undefined) {
|
|
1000
|
+
throw new Error(`hfFirewallPolicy(${id}): props already carries a firewallPolicyId -- this constructor is ` + "for a row that does not exist yet. A row with a vendor id is adopted or updated, never " + "created; use firewallPolicy() instead.");
|
|
1001
|
+
}
|
|
1002
|
+
if (!props.name.startsWith("hf: ")) {
|
|
1003
|
+
throw new Error(`hfFirewallPolicy(${id}): name "${props.name}" does not start "hf: " -- only hf:-named ` + "policies may be created by this kit (Tim 2026-10-19); rename the declaration.");
|
|
1004
|
+
}
|
|
1005
|
+
return UnifiFirewallPolicy(id, props);
|
|
1006
|
+
};
|
|
1007
|
+
var locateByName = (siteId, name) => pageAll(firewall3.getFirewallPolicies, { siteId }).pipe(Effect15.map((rows) => {
|
|
1008
|
+
const matching = rows.filter((row) => row.name === name);
|
|
1009
|
+
if (matching.length > 1) {
|
|
1010
|
+
throw new Error(`Unifi.FirewallPolicy: ${String(matching.length)} live policies are named "${name}" on ` + `site ${siteId}. A name this engine locates by must be unique; rename one on the console.`);
|
|
1011
|
+
}
|
|
1012
|
+
return matching[0];
|
|
1013
|
+
}));
|
|
931
1014
|
var spec7 = {
|
|
932
1015
|
type: "Unifi.FirewallPolicy",
|
|
933
|
-
describe: (props) => `sites/${props.siteId}/firewall/policies/${props.firewallPolicyId}`,
|
|
934
|
-
fetchLive: (props) =>
|
|
1016
|
+
describe: (props) => props.firewallPolicyId === undefined ? `sites/${props.siteId}/firewall/policies/by-name/${props.name}` : `sites/${props.siteId}/firewall/policies/${props.firewallPolicyId}`,
|
|
1017
|
+
fetchLive: (props) => props.firewallPolicyId === undefined ? locateByName(props.siteId, props.name) : firewall3.getFirewallPolicy({ siteId: props.siteId, firewallPolicyId: props.firewallPolicyId }).pipe(Effect15.catchTag("NotFound", () => Effect15.succeed(undefined))),
|
|
935
1018
|
attributes: attributesOf7,
|
|
936
|
-
matches: matches7
|
|
1019
|
+
matches: matches7,
|
|
1020
|
+
create: {
|
|
1021
|
+
allowedWrite: firewallPolicyCreateAllowedWrite,
|
|
1022
|
+
checkScope: checkHfCreateScope,
|
|
1023
|
+
write: writeFirewallPolicyCreate,
|
|
1024
|
+
identify: (props, written) => ({ ...props, firewallPolicyId: written.id }),
|
|
1025
|
+
driftOf: driftOf7
|
|
1026
|
+
}
|
|
937
1027
|
};
|
|
938
1028
|
var handlers7 = unifiHandlers(spec7);
|
|
939
|
-
var UnifiFirewallPolicyProvider = () => Provider7.effect(UnifiFirewallPolicy,
|
|
1029
|
+
var UnifiFirewallPolicyProvider = () => Provider7.effect(UnifiFirewallPolicy, Effect15.succeed(UnifiFirewallPolicy.Provider.of(handlers7)));
|
|
940
1030
|
// src/unifi/firewall-policy-ordering.ts
|
|
941
1031
|
import { Resource as Resource8 } from "alchemy";
|
|
942
1032
|
import { adopt as adopt8 } from "alchemy/AdoptPolicy";
|
|
943
1033
|
import * as Provider8 from "alchemy/Provider";
|
|
944
|
-
import * as
|
|
945
|
-
import * as
|
|
1034
|
+
import * as firewall4 from "@distilled.cloud/unifi-network/firewall";
|
|
1035
|
+
import * as Effect16 from "effect/Effect";
|
|
946
1036
|
|
|
947
1037
|
// src/unifi/firewall-policy-ordering-form.ts
|
|
948
1038
|
var attributesOf8 = (live, props) => ({
|
|
@@ -980,22 +1070,22 @@ var firewallPolicyOrdering = (id, props) => UnifiFirewallPolicyOrdering(id, prop
|
|
|
980
1070
|
var spec8 = {
|
|
981
1071
|
type: "Unifi.FirewallPolicyOrdering",
|
|
982
1072
|
describe: (props) => `sites/${props.siteId}/firewall/policies/ordering` + `?sourceFirewallZoneId=${props.sourceFirewallZoneId}` + `&destinationFirewallZoneId=${props.destinationFirewallZoneId}`,
|
|
983
|
-
fetchLive: (props) =>
|
|
1073
|
+
fetchLive: (props) => firewall4.getFirewallPolicyOrdering({
|
|
984
1074
|
siteId: props.siteId,
|
|
985
1075
|
sourceFirewallZoneId: props.sourceFirewallZoneId,
|
|
986
1076
|
destinationFirewallZoneId: props.destinationFirewallZoneId
|
|
987
|
-
}).pipe(
|
|
1077
|
+
}).pipe(Effect16.catchTag("NotFound", () => Effect16.succeed(undefined))),
|
|
988
1078
|
attributes: attributesOf8,
|
|
989
1079
|
matches: matches8
|
|
990
1080
|
};
|
|
991
1081
|
var handlers8 = unifiHandlers(spec8);
|
|
992
|
-
var UnifiFirewallPolicyOrderingProvider = () => Provider8.effect(UnifiFirewallPolicyOrdering,
|
|
1082
|
+
var UnifiFirewallPolicyOrderingProvider = () => Provider8.effect(UnifiFirewallPolicyOrdering, Effect16.succeed(UnifiFirewallPolicyOrdering.Provider.of(handlers8)));
|
|
993
1083
|
// src/unifi/traffic-matching-list.ts
|
|
994
1084
|
import { Resource as Resource9 } from "alchemy";
|
|
995
1085
|
import { adopt as adopt9 } from "alchemy/AdoptPolicy";
|
|
996
1086
|
import * as Provider9 from "alchemy/Provider";
|
|
997
1087
|
import * as trafficMatchingLists from "@distilled.cloud/unifi-network/traffic_matching_lists";
|
|
998
|
-
import * as
|
|
1088
|
+
import * as Effect17 from "effect/Effect";
|
|
999
1089
|
|
|
1000
1090
|
// src/unifi/traffic-matching-list-form.ts
|
|
1001
1091
|
var attributesOf9 = (live, props) => ({
|
|
@@ -1031,12 +1121,12 @@ var spec9 = {
|
|
|
1031
1121
|
fetchLive: (props) => trafficMatchingLists.getTrafficMatchingList({
|
|
1032
1122
|
siteId: props.siteId,
|
|
1033
1123
|
trafficMatchingListId: props.trafficMatchingListId
|
|
1034
|
-
}).pipe(
|
|
1124
|
+
}).pipe(Effect17.catchTag("NotFound", () => Effect17.succeed(undefined))),
|
|
1035
1125
|
attributes: attributesOf9,
|
|
1036
1126
|
matches: matches9
|
|
1037
1127
|
};
|
|
1038
1128
|
var handlers9 = unifiHandlers(spec9);
|
|
1039
|
-
var UnifiTrafficMatchingListProvider = () => Provider9.effect(UnifiTrafficMatchingList,
|
|
1129
|
+
var UnifiTrafficMatchingListProvider = () => Provider9.effect(UnifiTrafficMatchingList, Effect17.succeed(UnifiTrafficMatchingList.Provider.of(handlers9)));
|
|
1040
1130
|
// src/unifi/providers.ts
|
|
1041
1131
|
import * as Layer from "effect/Layer";
|
|
1042
1132
|
var providers = () => Layer.mergeAll(UnifiNetworkProvider(), UnifiFirewallZoneProvider(), UnifiDnsPolicyProvider(), UnifiAclRuleProvider(), UnifiAclRuleOrderingProvider(), UnifiWifiBroadcastProvider(), UnifiFirewallPolicyProvider(), UnifiFirewallPolicyOrderingProvider(), UnifiTrafficMatchingListProvider());
|
|
@@ -1095,6 +1185,7 @@ export {
|
|
|
1095
1185
|
firewallZone,
|
|
1096
1186
|
driftOf2 as firewallZoneDriftOf,
|
|
1097
1187
|
guardedHttpClient,
|
|
1188
|
+
hfFirewallPolicy,
|
|
1098
1189
|
network,
|
|
1099
1190
|
driftOf as networkDriftOf,
|
|
1100
1191
|
providers,
|
|
@@ -1104,5 +1195,5 @@ export {
|
|
|
1104
1195
|
driftOf6 as wifiBroadcastDriftOf
|
|
1105
1196
|
};
|
|
1106
1197
|
|
|
1107
|
-
//# debugId=
|
|
1198
|
+
//# debugId=F58E0BCA65985B3464756E2164756E21
|
|
1108
1199
|
//# sourceMappingURL=index.js.map
|