@homeflare/alchemy 0.49.0 → 0.51.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/kubernetes/helm-chart-bounded.d.ts +44 -0
- package/dist/kubernetes/helm-chart-bounded.d.ts.map +1 -0
- package/dist/kubernetes/index.d.ts +12 -0
- package/dist/kubernetes/index.d.ts.map +1 -0
- package/dist/kubernetes/index.js +585 -0
- package/dist/kubernetes/index.js.map +19 -0
- package/dist/kubernetes/ready-checks.d.ts +45 -0
- package/dist/kubernetes/ready-checks.d.ts.map +1 -0
- package/dist/kubernetes/ready-classify.d.ts +23 -0
- package/dist/kubernetes/ready-classify.d.ts.map +1 -0
- package/dist/kubernetes/ready-connect.d.ts +29 -0
- package/dist/kubernetes/ready-connect.d.ts.map +1 -0
- package/dist/kubernetes/ready-declare.d.ts +5 -0
- package/dist/kubernetes/ready-declare.d.ts.map +1 -0
- package/dist/kubernetes/ready-errors.d.ts +80 -0
- package/dist/kubernetes/ready-errors.d.ts.map +1 -0
- package/dist/kubernetes/ready-poll.d.ts +61 -0
- package/dist/kubernetes/ready-poll.d.ts.map +1 -0
- package/dist/kubernetes/ready.d.ts +74 -0
- package/dist/kubernetes/ready.d.ts.map +1 -0
- package/dist/talos/fake-apiserver.d.ts +11 -2
- package/dist/talos/fake-apiserver.d.ts.map +1 -1
- package/dist/unifi/acl-rule-ordering.d.ts +6 -1
- package/dist/unifi/acl-rule-ordering.d.ts.map +1 -1
- package/dist/unifi/acl-rule.d.ts +6 -1
- package/dist/unifi/acl-rule.d.ts.map +1 -1
- package/dist/unifi/dns-policy.d.ts +6 -1
- package/dist/unifi/dns-policy.d.ts.map +1 -1
- package/dist/unifi/fake-unifi.d.ts +4 -0
- package/dist/unifi/fake-unifi.d.ts.map +1 -1
- package/dist/unifi/firewall-policy-ordering.d.ts +6 -1
- package/dist/unifi/firewall-policy-ordering.d.ts.map +1 -1
- package/dist/unifi/firewall-policy.d.ts +6 -1
- package/dist/unifi/firewall-policy.d.ts.map +1 -1
- package/dist/unifi/firewall-zone.d.ts +6 -1
- package/dist/unifi/firewall-zone.d.ts.map +1 -1
- package/dist/unifi/index.d.ts +5 -3
- package/dist/unifi/index.d.ts.map +1 -1
- package/dist/unifi/index.js +339 -86
- package/dist/unifi/index.js.map +10 -6
- package/dist/unifi/network-scope.d.ts +31 -0
- package/dist/unifi/network-scope.d.ts.map +1 -0
- package/dist/unifi/network-update.d.ts +44 -0
- package/dist/unifi/network-update.d.ts.map +1 -0
- package/dist/unifi/network.d.ts +32 -12
- package/dist/unifi/network.d.ts.map +1 -1
- package/dist/unifi/policy.d.ts +89 -1
- package/dist/unifi/policy.d.ts.map +1 -1
- package/dist/unifi/resource.d.ts +17 -43
- package/dist/unifi/resource.d.ts.map +1 -1
- package/dist/unifi/traffic-matching-list.d.ts +6 -1
- package/dist/unifi/traffic-matching-list.d.ts.map +1 -1
- package/dist/unifi/update-reconcile.d.ts +31 -0
- package/dist/unifi/update-reconcile.d.ts.map +1 -0
- package/dist/unifi/wifi-broadcast.d.ts +6 -1
- package/dist/unifi/wifi-broadcast.d.ts.map +1 -1
- package/dist/unifi/wire-guard.d.ts +33 -0
- package/dist/unifi/wire-guard.d.ts.map +1 -0
- package/docs/kubernetes-ready.md +146 -0
- package/docs/peers.md +13 -0
- package/docs/provider-standard.md +7 -0
- package/docs/unifi-network-write.md +90 -0
- package/docs/unifi.md +36 -36
- package/package.json +7 -1
- package/src/kubernetes/helm-chart-bounded.test.ts +242 -0
- package/src/kubernetes/helm-chart-bounded.ts +110 -0
- package/src/kubernetes/index.ts +28 -0
- package/src/kubernetes/ready-backoff.test.ts +124 -0
- package/src/kubernetes/ready-checks.test.ts +106 -0
- package/src/kubernetes/ready-checks.ts +165 -0
- package/src/kubernetes/ready-classify.ts +76 -0
- package/src/kubernetes/ready-connect.ts +52 -0
- package/src/kubernetes/ready-declare.test.ts +85 -0
- package/src/kubernetes/ready-declare.ts +50 -0
- package/src/kubernetes/ready-errors.ts +102 -0
- package/src/kubernetes/ready-first-install.test.ts +126 -0
- package/src/kubernetes/ready-poll-states.test.ts +92 -0
- package/src/kubernetes/ready-poll.test.ts +194 -0
- package/src/kubernetes/ready-poll.ts +215 -0
- package/src/kubernetes/ready-rollout.test.ts +58 -0
- package/src/kubernetes/ready-secrecy.test.ts +131 -0
- package/src/kubernetes/ready.fixtures.ts +103 -0
- package/src/kubernetes/ready.test.ts +205 -0
- package/src/kubernetes/ready.ts +188 -0
- package/src/talos/fake-apiserver.ts +21 -3
- package/src/unifi/fake-unifi.ts +15 -2
- package/src/unifi/handler-allow-news.test.ts +80 -0
- package/src/unifi/index.ts +21 -5
- package/src/unifi/network-scope.test.ts +203 -0
- package/src/unifi/network-scope.ts +75 -0
- package/src/unifi/network-update.test.ts +250 -0
- package/src/unifi/network-update.ts +95 -0
- package/src/unifi/network.test.ts +27 -1
- package/src/unifi/network.ts +56 -12
- package/src/unifi/policy.ts +128 -10
- package/src/unifi/resource.test.ts +15 -3
- package/src/unifi/resource.ts +64 -105
- package/src/unifi/update-reconcile.ts +126 -0
- package/src/unifi/wire-guard-anchor.test.ts +198 -0
- package/src/unifi/wire-guard-round2.test.ts +120 -0
- package/src/unifi/wire-guard.test.ts +218 -0
- package/src/unifi/wire-guard.ts +192 -0
- package/src/unifi/write-op-reference.test.ts +38 -2
- package/src/unifi/get-only-guard.test.ts +0 -165
package/dist/unifi/policy.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as Effect from 'effect/Effect';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const UNIFI_WRITE_POLICY: string;
|
|
3
|
+
/** @deprecated renamed `UNIFI_WRITE_POLICY`; kept so existing consumer imports do not break. */
|
|
4
|
+
export declare const UNIFI_READ_ONLY_POLICY: string;
|
|
3
5
|
/** What kind of write was asked for and refused — for the message only, never acted on. */
|
|
4
6
|
export type UnifiWriteAction = 'create' | 'update' | 'delete';
|
|
5
7
|
declare const UnifiWriteRefused_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
@@ -16,5 +18,91 @@ export declare class UnifiWriteRefused extends UnifiWriteRefused_base<{
|
|
|
16
18
|
}
|
|
17
19
|
/** The only thing `reconcile`/`delete` ever return for a write this family cannot make. */
|
|
18
20
|
export declare const refuseWrite: (type: string, identity: string, action: UnifiWriteAction) => Effect.Effect<never, UnifiWriteRefused, never>;
|
|
21
|
+
declare const UnifiLiveDriftedSinceDeploy_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
22
|
+
readonly _tag: "UnifiLiveDriftedSinceDeploy";
|
|
23
|
+
} & Readonly<A>;
|
|
24
|
+
/** Live no longer equals what the last deploy wrote: a hand edit is never overwritten. */
|
|
25
|
+
export declare class UnifiLiveDriftedSinceDeploy extends UnifiLiveDriftedSinceDeploy_base<{
|
|
26
|
+
readonly type: string;
|
|
27
|
+
readonly identity: string;
|
|
28
|
+
readonly fields: ReadonlyArray<string>;
|
|
29
|
+
}> {
|
|
30
|
+
get message(): string;
|
|
31
|
+
}
|
|
32
|
+
declare const UnifiUpdateWouldBeNoop_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
33
|
+
readonly _tag: "UnifiUpdateWouldBeNoop";
|
|
34
|
+
} & Readonly<A>;
|
|
35
|
+
/** The merged body equals live: a PUT would change nothing (keeps a re-PUT loop impossible). */
|
|
36
|
+
export declare class UnifiUpdateWouldBeNoop extends UnifiUpdateWouldBeNoop_base<{
|
|
37
|
+
readonly type: string;
|
|
38
|
+
readonly identity: string;
|
|
39
|
+
}> {
|
|
40
|
+
get message(): string;
|
|
41
|
+
}
|
|
42
|
+
declare const UnifiUpdateDidNotConverge_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
43
|
+
readonly _tag: "UnifiUpdateDidNotConverge";
|
|
44
|
+
} & Readonly<A>;
|
|
45
|
+
/** The PUT answered, yet the response still differs from the declaration. */
|
|
46
|
+
export declare class UnifiUpdateDidNotConverge extends UnifiUpdateDidNotConverge_base<{
|
|
47
|
+
readonly type: string;
|
|
48
|
+
readonly identity: string;
|
|
49
|
+
readonly fields: ReadonlyArray<string>;
|
|
50
|
+
}> {
|
|
51
|
+
get message(): string;
|
|
52
|
+
}
|
|
53
|
+
declare const UnifiFieldNotRemovable_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
54
|
+
readonly _tag: "UnifiFieldNotRemovable";
|
|
55
|
+
} & Readonly<A>;
|
|
56
|
+
/** A patch would remove a key outside the removable allowlist (an omitted block is turned off). */
|
|
57
|
+
export declare class UnifiFieldNotRemovable extends UnifiFieldNotRemovable_base<{
|
|
58
|
+
readonly type: string;
|
|
59
|
+
readonly identity: string;
|
|
60
|
+
readonly fields: ReadonlyArray<string>;
|
|
61
|
+
}> {
|
|
62
|
+
get message(): string;
|
|
63
|
+
}
|
|
64
|
+
declare const UnifiFieldNotSettable_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
65
|
+
readonly _tag: "UnifiFieldNotSettable";
|
|
66
|
+
} & Readonly<A>;
|
|
67
|
+
/** A patch would SET a key outside the settable allowlist (only `ipv6Configuration` may be written). */
|
|
68
|
+
export declare class UnifiFieldNotSettable extends UnifiFieldNotSettable_base<{
|
|
69
|
+
readonly type: string;
|
|
70
|
+
readonly identity: string;
|
|
71
|
+
readonly fields: ReadonlyArray<string>;
|
|
72
|
+
}> {
|
|
73
|
+
get message(): string;
|
|
74
|
+
}
|
|
75
|
+
declare const UnifiManagementNetworkRefused_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
76
|
+
readonly _tag: "UnifiManagementNetworkRefused";
|
|
77
|
+
} & Readonly<A>;
|
|
78
|
+
/** The live object is the site's default (management) network: never written. */
|
|
79
|
+
export declare class UnifiManagementNetworkRefused extends UnifiManagementNetworkRefused_base<{
|
|
80
|
+
readonly type: string;
|
|
81
|
+
readonly identity: string;
|
|
82
|
+
}> {
|
|
83
|
+
get message(): string;
|
|
84
|
+
}
|
|
85
|
+
declare const UnifiImmutableFieldChanged_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
86
|
+
readonly _tag: "UnifiImmutableFieldChanged";
|
|
87
|
+
} & Readonly<A>;
|
|
88
|
+
/** A patch changes a field that decides where the network lives or what serves it. */
|
|
89
|
+
export declare class UnifiImmutableFieldChanged extends UnifiImmutableFieldChanged_base<{
|
|
90
|
+
readonly type: string;
|
|
91
|
+
readonly identity: string;
|
|
92
|
+
readonly fields: ReadonlyArray<string>;
|
|
93
|
+
}> {
|
|
94
|
+
get message(): string;
|
|
95
|
+
}
|
|
96
|
+
declare const UnifiIdentityChanged_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
97
|
+
readonly _tag: "UnifiIdentityChanged";
|
|
98
|
+
} & Readonly<A>;
|
|
99
|
+
/** `olds` and `news` name different objects: the prior state is no baseline for this write. */
|
|
100
|
+
export declare class UnifiIdentityChanged extends UnifiIdentityChanged_base<{
|
|
101
|
+
readonly type: string;
|
|
102
|
+
readonly identity: string;
|
|
103
|
+
readonly previous: string;
|
|
104
|
+
}> {
|
|
105
|
+
get message(): string;
|
|
106
|
+
}
|
|
19
107
|
export {};
|
|
20
108
|
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/unifi/policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/unifi/policy.ts"],"names":[],"mappings":"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"}
|
package/dist/unifi/resource.d.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { Input } from 'alchemy/Input';
|
|
2
2
|
import type { UnifiNetworkOpContext } from '@distilled.cloud/unifi-network/Protocol';
|
|
3
3
|
import * as Effect from 'effect/Effect';
|
|
4
|
-
import * as HttpClient from 'effect/http/HttpClient';
|
|
4
|
+
import type * as HttpClient from 'effect/http/HttpClient';
|
|
5
|
+
import { type ReconcileArgs, type UnifiUpdate } from './update-reconcile.ts';
|
|
5
6
|
/** What every handler needs from the caller's runtime once `CredentialsFromEnv` is baked in. */
|
|
6
7
|
export type UnifiRequirements = HttpClient.HttpClient;
|
|
7
8
|
/**
|
|
8
9
|
* One UniFi Network object's read. `Live` is whatever the SDK decodes (a `NetworkDetails`, a
|
|
9
10
|
* `FirewallZone`, …); `E` is left to each resource file to declare (its own `fetchLive`'s error
|
|
10
11
|
* union, with `NotFound` already folded away) so the precise per-operation error union flows
|
|
11
|
-
* through instead of being widened by hand here.
|
|
12
|
+
* through instead of being widened by hand here. `E2` is the error union of the optional write.
|
|
12
13
|
*/
|
|
13
|
-
export type UnifiSpec<Props extends object, Live, Attributes extends object, E> = {
|
|
14
|
+
export type UnifiSpec<Props extends object, Live, Attributes extends object, E, E2 = never> = {
|
|
14
15
|
/** The vendor type string, e.g. `Unifi.Network` — for the refusal message only. */
|
|
15
16
|
readonly type: string;
|
|
16
17
|
/** Already folds a genuine not-found to `undefined`; nothing else is folded (see header). */
|
|
@@ -19,8 +20,10 @@ export type UnifiSpec<Props extends object, Live, Attributes extends object, E>
|
|
|
19
20
|
readonly matches: (attributes: Attributes, props: Props) => boolean;
|
|
20
21
|
/** For the refusal message and read-back failures. */
|
|
21
22
|
readonly describe: (props: Props) => string;
|
|
23
|
+
/** Present only on a family allowed to write (`Unifi.Network`); see `update-reconcile.ts`. */
|
|
24
|
+
readonly update?: UnifiUpdate<Props, Live, E2>;
|
|
22
25
|
};
|
|
23
|
-
export declare const unifiOperations: <Props extends object, Live, Attributes extends object, E>(spec: UnifiSpec<Props, Live, Attributes, E>) => {
|
|
26
|
+
export declare const unifiOperations: <Props extends object, Live, Attributes extends object, E, E2>(spec: UnifiSpec<Props, Live, Attributes, E, E2>) => {
|
|
24
27
|
read: (props: Props) => Effect.Effect<Attributes | undefined, E, UnifiNetworkOpContext>;
|
|
25
28
|
readHandler: ({ olds, output }: {
|
|
26
29
|
olds: Props;
|
|
@@ -31,51 +34,17 @@ export declare const unifiOperations: <Props extends object, Live, Attributes ex
|
|
|
31
34
|
} | {
|
|
32
35
|
readonly action: 'noop';
|
|
33
36
|
} | undefined, E, UnifiNetworkOpContext>;
|
|
34
|
-
|
|
37
|
+
/** Pre-state callers (tests, adopt-only probes) pass just `news`: `olds`/`output` unset. */
|
|
38
|
+
reconcile: (news: Props, rest?: Partial<Omit<ReconcileArgs<Props>, 'news'>>) => Effect.Effect<Attributes, E | E2 | import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused, UnifiNetworkOpContext>;
|
|
35
39
|
destroy: (olds: Props) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, never>;
|
|
36
40
|
};
|
|
37
41
|
/**
|
|
38
|
-
*
|
|
39
|
-
* `reconcile`/`destroy` — every path this engine exposes already returns that refusal instead of
|
|
40
|
-
* calling an SDK write op. This guard stops the same thing one layer lower, AT THE WIRE: it wraps
|
|
41
|
-
* whatever `HttpClient` the caller already provides (the stack's `FetchHttpClient.layer` in
|
|
42
|
-
* production, `fakeUnifiLayer` in tests — `openbao/bao-http.ts`'s header names this same
|
|
43
|
-
* "wrap, don't replace" seam) so a future resource file that, by mistake, called an SDK write
|
|
44
|
-
* operation directly — bypassing `unifiOperations` entirely — would still never reach the vendor
|
|
45
|
-
* API. `write-op-reference.test.ts` proves no such call exists today; this is what stops one from
|
|
46
|
-
* ever taking effect if one is ever added.
|
|
47
|
-
*
|
|
48
|
-
* ⚠️ `Effect.die`, NOT A TYPED FAILURE — classified per distilled-doctrine's "classify every
|
|
49
|
-
* die/orDie". Every generated SDK operation (`networks.getNetworkDetails`, …) declares its OWN
|
|
50
|
-
* closed error union from the pinned OpenAPI spec, and generated files are never hand-edited to
|
|
51
|
-
* add a member to it. A typed `Effect.fail` here would be a failure mode absent from every op's
|
|
52
|
-
* declared type the moment it occurred — a type-system lie. A defect is the sound way to add a
|
|
53
|
-
* NEW "this must be structurally impossible" failure underneath types this package does not
|
|
54
|
-
* own, exactly like `openbao`'s `refuse()` helpers' `Effect.die(new Error(...))` for a
|
|
55
|
-
* configuration move that should never be reachable.
|
|
56
|
-
*/
|
|
57
|
-
export declare class UnifiNonGetRequest extends Error {
|
|
58
|
-
readonly method: string;
|
|
59
|
-
/** PATH ONLY, never the full URL — see `requestPath` below. */
|
|
60
|
-
readonly path: string;
|
|
61
|
-
constructor(method: string,
|
|
62
|
-
/** PATH ONLY, never the full URL — see `requestPath` below. */
|
|
63
|
-
path: string);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* A `Layer` that reads whatever `HttpClient` is already in the calling context and replaces it,
|
|
67
|
-
* for everything downstream, with `guardGetOnly`'s wrapped version (`HttpClient.layerMergedContext`
|
|
68
|
-
* — see the guard above). Exported so `get-only-guard.test.ts` can prove the mechanism directly,
|
|
69
|
-
* against a bare `HttpClient` service, without needing `CredentialsFromEnv` or any env var at all.
|
|
70
|
-
*/
|
|
71
|
-
export declare const GetOnlyHttpClient: import("effect/Layer").Layer<HttpClient.HttpClient, never, HttpClient.HttpClient>;
|
|
72
|
-
/**
|
|
73
|
-
* The four provider handlers for a spec'd read-only UniFi object, wired once.
|
|
42
|
+
* The four provider handlers for a spec'd UniFi object, wired once.
|
|
74
43
|
*
|
|
75
44
|
* ⛔ `list` ANSWERS EMPTY. `GET /v1/sites/{siteId}/networks` answers every network on the site;
|
|
76
45
|
* adoption stays explicit, the same reasoning `Proxmox.User`'s and NetBox's `list` give.
|
|
77
46
|
*/
|
|
78
|
-
export declare const unifiHandlers: <Props extends object, Live, Attributes extends object, E>(spec: UnifiSpec<Props, Live, Attributes, E>) => {
|
|
47
|
+
export declare const unifiHandlers: <Props extends object, Live, Attributes extends object, E, E2>(spec: UnifiSpec<Props, Live, Attributes, E, E2>) => {
|
|
79
48
|
list: () => Effect.Effect<never[], never, never>;
|
|
80
49
|
read: (args: {
|
|
81
50
|
olds: Props;
|
|
@@ -91,7 +60,12 @@ export declare const unifiHandlers: <Props extends object, Live, Attributes exte
|
|
|
91
60
|
} | undefined, E, HttpClient.HttpClient>;
|
|
92
61
|
reconcile: (args: {
|
|
93
62
|
news: Props;
|
|
94
|
-
|
|
63
|
+
olds: Props | undefined;
|
|
64
|
+
output: Attributes | undefined;
|
|
65
|
+
session: {
|
|
66
|
+
note: (message: string) => Effect.Effect<void, never, never>;
|
|
67
|
+
};
|
|
68
|
+
}) => Effect.Effect<Attributes, E | E2 | import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused, HttpClient.HttpClient>;
|
|
95
69
|
delete: (args: {
|
|
96
70
|
olds: Props;
|
|
97
71
|
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, HttpClient.HttpClient>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/unifi/resource.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/unifi/resource.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,wBAAwB,CAAC;AAE1D,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAmB,MAAM,uBAAuB,CAAC;AAG9F,gGAAgG;AAChG,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,UAAU,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,SAAS,CAAC,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,UAAU,SAAS,MAAM,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,IAAI;IAC5F,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6FAA6F;IAC7F,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,GAAG,SAAS,EAAE,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAChG,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,UAAU,CAAC;IAC9D,QAAQ,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACpE,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;IAC5C,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;CAChD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,UAAU,SAAS,MAAM,EAAE,CAAC,EAAE,EAAE,QACpF,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;kBAE1B,KAAK;oCAQQ;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;iBAUlE,KAAK,CAAC,KAAK,CAAC,UAAU,UAAU,GAAG,SAAS;yBAIZ,QAAQ;;yBAElC,MAAM;;IAIzB,4FAA4F;sBAC1E,KAAK,SAAQ,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;oBAQ1D,KAAK;CAExB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,UAAU,SAAS,MAAM,EAAE,CAAC,EAAE,EAAE,QAClF,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;IAe7C,IAAI;IACJ,IAAI,SAAS;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;IAE5D,IAAI,SAAS;QAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAAA;KAAE;yBA5CtB,QAAQ;;yBAElC,MAAM;;IA6CzB,SAAS,SAAS;QAChB,IAAI,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;QACxB,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC;QAC/B,OAAO,EAAE;YAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;SAAE,CAAC;KAC3E;IAWD,MAAM,SAAS;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE;CAEjC,CAAC"}
|
|
@@ -42,7 +42,12 @@ export declare const handlers: {
|
|
|
42
42
|
} | undefined, trafficMatchingLists.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
43
43
|
reconcile: (args: {
|
|
44
44
|
news: TrafficMatchingListProps;
|
|
45
|
-
|
|
45
|
+
olds: TrafficMatchingListProps | undefined;
|
|
46
|
+
output: TrafficMatchingListAttributes | undefined;
|
|
47
|
+
session: {
|
|
48
|
+
note: (message: string) => Effect.Effect<void, never, never>;
|
|
49
|
+
};
|
|
50
|
+
}) => Effect.Effect<TrafficMatchingListAttributes, import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | trafficMatchingLists.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
46
51
|
delete: (args: {
|
|
47
52
|
olds: TrafficMatchingListProps;
|
|
48
53
|
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, import("effect/http/HttpClient").HttpClient>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traffic-matching-list.d.ts","sourceRoot":"","sources":["../../src/unifi/traffic-matching-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,uDAAuD,CAAC;AAC9F,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAE9B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlE,YAAY,EACV,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAE3D,MAAM,WAAW,wBAAyB,SAAQ,QAAQ,CACxD,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,EAC7B,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,wBAAwB,2DAGpC,CAAC;AAEF;oBACoB;AACpB,eAAO,MAAM,mBAAmB,OAAQ,MAAM,SAAS,wBAAwB,gGACxB,CAAC;AAExD,iGAAiG;AACjG,eAAO,MAAM,IAAI,EAAE,SAAS,CAC1B,wBAAwB,EACxB,oBAAoB,CAAC,mBAAmB,EACxC,6BAA6B,EAC7B,oBAAoB,CAAC,2BAA2B,CAcjD,CAAC;AAEF,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"traffic-matching-list.d.ts","sourceRoot":"","sources":["../../src/unifi/traffic-matching-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,uDAAuD,CAAC;AAC9F,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAE9B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlE,YAAY,EACV,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAE3D,MAAM,WAAW,wBAAyB,SAAQ,QAAQ,CACxD,2BAA2B,EAC3B,wBAAwB,EACxB,6BAA6B,EAC7B,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,wBAAwB,2DAGpC,CAAC;AAEF;oBACoB;AACpB,eAAO,MAAM,mBAAmB,OAAQ,MAAM,SAAS,wBAAwB,gGACxB,CAAC;AAExD,iGAAiG;AACjG,eAAO,MAAM,IAAI,EAAE,SAAS,CAC1B,wBAAwB,EACxB,oBAAoB,CAAC,mBAAmB,EACxC,6BAA6B,EAC7B,oBAAoB,CAAC,2BAA2B,CAcjD,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;CAAsB,CAAC;AAE5C,eAAO,MAAM,gCAAgC,qIAI1C,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { UnifiNetworkOpContext } from '@distilled.cloud/unifi-network/Protocol';
|
|
2
|
+
import * as Effect from 'effect/Effect';
|
|
3
|
+
import type { FieldDrift } from './drift.ts';
|
|
4
|
+
import { UnifiIdentityChanged, UnifiLiveDriftedSinceDeploy, UnifiUpdateDidNotConverge } from './policy.ts';
|
|
5
|
+
import type { UnifiSpec } from './resource.ts';
|
|
6
|
+
import type { AllowedWrite } from './wire-guard.ts';
|
|
7
|
+
export type Patch<Props> = {
|
|
8
|
+
set: Partial<Props>;
|
|
9
|
+
unset: ReadonlyArray<keyof Props>;
|
|
10
|
+
};
|
|
11
|
+
/** The write half of a spec: everything needed to PUT one object, and nothing to create/delete. */
|
|
12
|
+
export type UnifiUpdate<Props extends object, Live, E2> = {
|
|
13
|
+
/** The one PUT the wire guard may pass for this row (path pinned to the row's own ids). */
|
|
14
|
+
readonly allowedWrite: (props: Props) => AllowedWrite;
|
|
15
|
+
/** Declarable keys; never the identity keys. */
|
|
16
|
+
readonly patchKeys: ReadonlyArray<keyof Props>;
|
|
17
|
+
readonly driftOf: (live: Live, props: Props) => ReadonlyArray<FieldDrift>;
|
|
18
|
+
/** Refuses (typed, before any note or request) a patch outside this family's write scope. */
|
|
19
|
+
readonly checkScope: (live: Live, patch: Patch<Props>, props: Props) => Effect.Effect<void, E2>;
|
|
20
|
+
readonly write: (live: Live, patch: Patch<Props>, props: Props) => Effect.Effect<Live, E2, UnifiNetworkOpContext>;
|
|
21
|
+
};
|
|
22
|
+
/** Keys whose `news` value differs from `olds`: set when still declared, unset when dropped. */
|
|
23
|
+
export declare const computePatch: <Props extends object>(news: Props, olds: Props, patchKeys: ReadonlyArray<keyof Props>) => Patch<Props>;
|
|
24
|
+
export type ReconcileArgs<Props extends object> = {
|
|
25
|
+
readonly news: Props;
|
|
26
|
+
readonly olds: Props | undefined;
|
|
27
|
+
readonly output: unknown;
|
|
28
|
+
readonly note: (message: string) => Effect.Effect<void>;
|
|
29
|
+
};
|
|
30
|
+
export declare const updateReconcile: <Props extends object, Live, Attributes extends object, E, E2>(spec: UnifiSpec<Props, Live, Attributes, E, E2>, { news, olds, output, note }: ReconcileArgs<Props>) => Effect.Effect<Attributes, E | E2 | UnifiIdentityChanged | UnifiLiveDriftedSinceDeploy | UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused, UnifiNetworkOpContext>;
|
|
31
|
+
//# sourceMappingURL=update-reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-reconcile.d.ts","sourceRoot":"","sources":["../../src/unifi/update-reconcile.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EACL,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EAE1B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,MAAM,KAAK,CAAC,KAAK,IAAI;IAAE,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC,MAAM,KAAK,CAAC,CAAA;CAAE,CAAC;AAEtF,mGAAmG;AACnG,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI;IACxD,2FAA2F;IAC3F,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,YAAY,CAAC;IACtD,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,MAAM,KAAK,CAAC,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,aAAa,CAAC,UAAU,CAAC,CAAC;IAC1E,6FAA6F;IAC7F,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAChG,QAAQ,CAAC,KAAK,EAAE,CACd,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EACnB,KAAK,EAAE,KAAK,KACT,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,qBAAqB,CAAC,CAAC;CACrD,CAAC;AAEF,gGAAgG;AAChG,eAAO,MAAM,YAAY,GAAI,KAAK,SAAS,MAAM,QACzC,KAAK,QACL,KAAK,aACA,aAAa,CAAC,MAAM,KAAK,CAAC,KACpC,KAAK,CAAC,KAAK,CASb,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,KAAK,SAAS,MAAM,IAAI;IAChD,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;CACzD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,UAAU,SAAS,MAAM,EAAE,CAAC,EAAE,EAAE,QACpF,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC,gCACjB,aAAa,CAAC,KAAK,CAAC,wLA2ChD,CAAC"}
|
|
@@ -64,7 +64,12 @@ export declare const handlers: {
|
|
|
64
64
|
} | undefined, UnifiPaginationInconsistent | wifiBroadcasts.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
65
65
|
reconcile: (args: {
|
|
66
66
|
news: WifiBroadcastProps;
|
|
67
|
-
|
|
67
|
+
olds: WifiBroadcastProps | undefined;
|
|
68
|
+
output: WifiBroadcastAttributes | undefined;
|
|
69
|
+
session: {
|
|
70
|
+
note: (message: string) => Effect.Effect<void, never, never>;
|
|
71
|
+
};
|
|
72
|
+
}) => Effect.Effect<WifiBroadcastAttributes, import("./policy.ts").UnifiIdentityChanged | import("./policy.ts").UnifiLiveDriftedSinceDeploy | UnifiPaginationInconsistent | import("./policy.ts").UnifiUpdateDidNotConverge | import("./policy.ts").UnifiWriteRefused | wifiBroadcasts.UnifiNetworkOpError, import("effect/http/HttpClient").HttpClient>;
|
|
68
73
|
delete: (args: {
|
|
69
74
|
olds: WifiBroadcastProps;
|
|
70
75
|
}) => Effect.Effect<never, import("./policy.ts").UnifiWriteRefused, import("effect/http/HttpClient").HttpClient>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wifi-broadcast.d.ts","sourceRoot":"","sources":["../../src/unifi/wifi-broadcast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,cAAc,MAAM,gDAAgD,CAAC;AACjF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAExB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,2BAA2B,EAAW,MAAM,eAAe,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlE,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEpD,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAE7B,CAAC;AAEH,kGAAkG;AAClG,eAAO,MAAM,aAAa,OAAQ,MAAM,SAAS,kBAAkB,0FAClB,CAAC;AAElD,iGAAiG;AACjG,eAAO,MAAM,IAAI,EAAE,SAAS,CAC1B,kBAAkB,EAClB,cAAc,CAAC,qBAAqB,EACpC,uBAAuB,EACvB,cAAc,CAAC,yBAAyB,GAAG,2BAA2B,CAWvE,CAAC;AAEF,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"wifi-broadcast.d.ts","sourceRoot":"","sources":["../../src/unifi/wifi-broadcast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,cAAc,MAAM,gDAAgD,CAAC;AACjF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAExB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,2BAA2B,EAAW,MAAM,eAAe,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAGlE,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEpD,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAE7B,CAAC;AAEH,kGAAkG;AAClG,eAAO,MAAM,aAAa,OAAQ,MAAM,SAAS,kBAAkB,0FAClB,CAAC;AAElD,iGAAiG;AACjG,eAAO,MAAM,IAAI,EAAE,SAAS,CAC1B,kBAAkB,EAClB,cAAc,CAAC,qBAAqB,EACpC,uBAAuB,EACvB,cAAc,CAAC,yBAAyB,GAAG,2BAA2B,CAWvE,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;CAAsB,CAAC;AAE5C,eAAO,MAAM,0BAA0B,+HACwD,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as HttpClient from 'effect/http/HttpClient';
|
|
2
|
+
/**
|
|
3
|
+
* One non-GET request the guard may let through. `tail` is the exact route below the configured
|
|
4
|
+
* base URL (`/v1/sites/<site>/networks/<id>`): the guard compares the request's origin and path to
|
|
5
|
+
* `baseUrl + tail` and requires an empty query and fragment (`isAllowed`).
|
|
6
|
+
*/
|
|
7
|
+
export type AllowedWrite = {
|
|
8
|
+
readonly method: 'PUT';
|
|
9
|
+
readonly tail: string;
|
|
10
|
+
};
|
|
11
|
+
export declare class UnifiRefusedRequest extends Error {
|
|
12
|
+
readonly method: string;
|
|
13
|
+
/** PATH ONLY, never the full URL — see `requestPath` below. */
|
|
14
|
+
readonly path: string;
|
|
15
|
+
constructor(method: string,
|
|
16
|
+
/** PATH ONLY, never the full URL — see `requestPath` below. */
|
|
17
|
+
path: string, detail?: string);
|
|
18
|
+
}
|
|
19
|
+
/** @deprecated renamed `UnifiRefusedRequest`; kept so existing consumer imports do not break. */
|
|
20
|
+
export declare const UnifiNonGetRequest: typeof UnifiRefusedRequest;
|
|
21
|
+
export type UnifiNonGetRequest = UnifiRefusedRequest;
|
|
22
|
+
/**
|
|
23
|
+
* A `Layer` that reads whatever `HttpClient` is already in the calling context and replaces it,
|
|
24
|
+
* for everything downstream, with the guarded version. Exported so `wire-guard.test.ts` can prove
|
|
25
|
+
* the mechanism against a bare `HttpClient`, without `CredentialsFromEnv` or any env var.
|
|
26
|
+
*/
|
|
27
|
+
export declare const guardedHttpClient: (allow: ReadonlyArray<AllowedWrite>, baseUrl?: string) => import("effect/Layer").Layer<HttpClient.HttpClient, never, HttpClient.HttpClient>;
|
|
28
|
+
/**
|
|
29
|
+
* No write may pass; the posture of `read`/`diff`/`delete` and of every non-updatable family. A 3xx
|
|
30
|
+
* answer to a GET is refused here exactly as under the allow-list posture — see REDIRECTS above.
|
|
31
|
+
*/
|
|
32
|
+
export declare const GetOnlyHttpClient: import("effect/Layer").Layer<HttpClient.HttpClient, never, HttpClient.HttpClient>;
|
|
33
|
+
//# sourceMappingURL=wire-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire-guard.d.ts","sourceRoot":"","sources":["../../src/unifi/wire-guard.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,UAAU,MAAM,wBAAwB,CAAC;AAKrD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7E,qBAAa,mBAAoB,SAAQ,KAAK;IAE1C,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM;IAHvB,YACW,MAAM,EAAE,MAAM;IACvB,+DAA+D;IACtD,IAAI,EAAE,MAAM,EACrB,MAAM,SAAiC,EASxC;CACF;AAED,iGAAiG;AACjG,eAAO,MAAM,kBAAkB,4BAAsB,CAAC;AACtD,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AA0FrD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,UAAW,aAAa,CAAC,YAAY,CAAC,YAAY,MAAM,sFASrF,CAAC;AAkBF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,mFAAwB,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# HomeFlare.Kubernetes.Ready
|
|
2
|
+
|
|
3
|
+
`@homeflare/alchemy/kubernetes` — a readiness gate for a Kubernetes cluster reached through the
|
|
4
|
+
`talos-openbao` adapter. Walked against `alchemy@2.0.0-beta.81`. Proven offline only (fake `bao`,
|
|
5
|
+
fake apiserver); **not** exercised against a live cluster.
|
|
6
|
+
|
|
7
|
+
## Why it exists
|
|
8
|
+
|
|
9
|
+
Upstream ships no readiness resource. `Kubernetes.HelmChart` reconcile returns when server-side
|
|
10
|
+
apply returns, and Alchemy releases every downstream row at that moment. A row chained after the
|
|
11
|
+
Cilium chart would start before one Cilium pod runs. Declare a `Ready` row with
|
|
12
|
+
`after: [chart.objects]` and chain later rows on `ready.ready`.
|
|
13
|
+
|
|
14
|
+
## Declaring it
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
const cniReady =
|
|
18
|
+
yield *
|
|
19
|
+
KubernetesReady('cni-ready', {
|
|
20
|
+
connection: talosOpenBaoConnection(PINNED_UID), // a literal uid, never an Output
|
|
21
|
+
checks: [
|
|
22
|
+
{ kind: 'DaemonSet', namespace: 'kube-system', name: 'cilium', minReady: 2 },
|
|
23
|
+
{ kind: 'Deployment', namespace: 'kube-system', name: 'cilium-operator' },
|
|
24
|
+
{ kind: 'CustomResourceDefinition', name: 'ciliumloadbalancerippools.cilium.io' },
|
|
25
|
+
],
|
|
26
|
+
waitTimeout: '10m0s', // Go style h/m/s, default 10m0s
|
|
27
|
+
pollInterval: '10s', // default 10s
|
|
28
|
+
after: [cilium.objects],
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Providers: `Layer.mergeAll(Kubernetes.providers(), TalosOpenBaoAdapter(...), KubernetesReadyProvider())`.
|
|
33
|
+
The provider requires `ChildProcessSpawner` (the adapter shells to `bao`).
|
|
34
|
+
|
|
35
|
+
## What "ready" means
|
|
36
|
+
|
|
37
|
+
The rules are `kubectl rollout status` (kubectl v0.34.0 `rollout_status.go`), not "Available":
|
|
38
|
+
|
|
39
|
+
| kind | ready when |
|
|
40
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
41
|
+
| DaemonSet | `updateStrategy` is `RollingUpdate`, `observedGeneration >= generation`, `updatedNumberScheduled >= minReady`, `numberAvailable - (desiredNumberScheduled - updatedNumberScheduled) >= minReady` |
|
|
42
|
+
| Deployment | `observedGeneration >= generation`, `updatedReplicas >= spec.replicas`, `replicas == updatedReplicas`, `availableReplicas >= updatedReplicas` |
|
|
43
|
+
| CustomResourceDefinition | condition `Established` is `True` |
|
|
44
|
+
|
|
45
|
+
`minReady` defaults to `desiredNumberScheduled`, and a DaemonSet that wants zero pods is never ready.
|
|
46
|
+
`numberAvailable` also counts available pods of the OLD template, so it is never compared to
|
|
47
|
+
`minReady` alone: subtracting `desired - updated` (the most old pods there can be) leaves a lower
|
|
48
|
+
bound on pods that are both updated and available. Without it, minReady 2 of 4 with two crashlooping
|
|
49
|
+
new pods and two healthy old ones would pass.
|
|
50
|
+
A Deployment whose `Progressing` condition has reason `ProgressDeadlineExceeded` (for the
|
|
51
|
+
generation the controller has observed), and a DaemonSet whose `updateStrategy` is not
|
|
52
|
+
`RollingUpdate` (absent counts as `RollingUpdate`), are typed failures, `KubernetesRolloutFailed`
|
|
53
|
+
(`reason`), not pending.
|
|
54
|
+
|
|
55
|
+
## Refused at declaration
|
|
56
|
+
|
|
57
|
+
- `checks` must be non-empty; each needs a known `kind`, a non-empty `name`, a non-empty
|
|
58
|
+
`namespace` for DaemonSet and Deployment, and `minReady` (DaemonSet only) an integer >= 1:
|
|
59
|
+
`KubernetesReadyBadCheck { index, problem }`.
|
|
60
|
+
- `after` must hold lazy Outputs of the chart (`chart.objects`): `KubernetesReadyBadAfter { index }`
|
|
61
|
+
for a plain value, a bare `chart`, or a stable such as `chart.connection`. Those resolve at plan
|
|
62
|
+
time, so the row would plan `noop` before the chart's update and the gate would be skipped.
|
|
63
|
+
|
|
64
|
+
## Errors
|
|
65
|
+
|
|
66
|
+
| outcome | `read` / `diff` (one pass) | `reconcile` (poll) |
|
|
67
|
+
| -------------------------------------------------------------- | -------------------------- | --------------------------------------------------------- |
|
|
68
|
+
| 404 (`_tag` `KubernetesNotFound`, or `KubernetesApiError` 404) | pending | pending |
|
|
69
|
+
| per-GET timeout (5 s), 5xx, 429, upstream transport `Error` | read: throws; diff: update | pending, recorded as `lastTransient` (tag + status) |
|
|
70
|
+
| the same on the connect's identity GET (incl. its 5 s timeout) | propagates | connect retried every `pollInterval`, inside the deadline |
|
|
71
|
+
| 401, 403, other 4xx | propagates | propagates |
|
|
72
|
+
| uid mismatch, vault (`bao`) failure | propagates | propagates |
|
|
73
|
+
| any other untagged `Error` (a defect, e.g. an empty namespace) | propagates | propagates |
|
|
74
|
+
|
|
75
|
+
- "Upstream transport `Error`" is only the `Failed Kubernetes ...` wrapper (alchemy `client.ts`).
|
|
76
|
+
- `lastTransient` is the transient of the LAST pass; one that has cleared is not blamed.
|
|
77
|
+
- `read` with no prior output (Alchemy's deferred adoption read on a first create) returns
|
|
78
|
+
`undefined` without touching the cluster: the row owns nothing to adopt.
|
|
79
|
+
- Every error leaving a GET, the connect's identity GET included, is scrubbed of the response body.
|
|
80
|
+
|
|
81
|
+
- 404 is matched by tag string with no import, so it is proof against alchemy raising
|
|
82
|
+
`KubernetesNotFound` instead of `KubernetesApiError` (standing watch: drop the second shape once
|
|
83
|
+
upstream settles).
|
|
84
|
+
- A deadline fails with `KubernetesReadyTimeout { failing, seconds, lastTransient?, states? }`.
|
|
85
|
+
`failing` holds **check keys** (`DaemonSet/kube-system/cilium`) only; `states` records per key
|
|
86
|
+
what the last pass saw: `{ notFound: true }` (a 404 that never resolves) or `{ counts }`
|
|
87
|
+
(generation and rollout numbers), so a missing object reads differently from a slow rollout.
|
|
88
|
+
- A propagated apiserver refusal is rewrapped as `KubernetesReadyApiError { method, path,
|
|
89
|
+
statusCode }`: upstream's `KubernetesApiError` message quotes up to 1000 bytes of the response
|
|
90
|
+
body, which this row does not republish.
|
|
91
|
+
- Upstream `readObject` takes no signal, so a timed-out GET abandons its socket rather than
|
|
92
|
+
aborting it, and process exit is not a bound for a long apply. The poll therefore rations
|
|
93
|
+
timeouts: after one GET times out the rest of that pass is pending with no GET, and the pause
|
|
94
|
+
doubles per consecutive timeout (cap 30 s), resetting on a clean pass. Standing watch: drop this
|
|
95
|
+
when upstream's `readObject` takes an AbortSignal (alchemy PR 1948, per-attempt deadlines).
|
|
96
|
+
- Upstream also retries a transport error itself, for about 40 s (a `spaced` 5 s schedule capped
|
|
97
|
+
by `recurs(8)`), longer than the per-GET bound, so a dead socket surfaces as the GET timeout.
|
|
98
|
+
|
|
99
|
+
## Lifecycle
|
|
100
|
+
|
|
101
|
+
- `read`: one pass, `ready: false` when any check is pending. Never waits.
|
|
102
|
+
- `diff`: refuses a non-literal `connection` (`TalosUidNotLiteral`, also at declaration);
|
|
103
|
+
`undefined` when unresolved; `replace` when the connection differs (it is in `stables`, so
|
|
104
|
+
dependants would otherwise see the old one for a deploy; `delete` is a no-op, so nothing live
|
|
105
|
+
is touched); `noop` when the live pass is ready and the check keys are unchanged; otherwise
|
|
106
|
+
`update`. It is live on every plan, like `Talos.ClusterHealth`, but a transient (5xx, 429, a
|
|
107
|
+
timeout, on the connect or a GET) is `update`, never a failed plan: `reconcile` does the
|
|
108
|
+
waiting. 401/403 and vault failures still propagate.
|
|
109
|
+
- `reconcile`: polls until every check passes or fails typed.
|
|
110
|
+
- `delete` is a no-op and `list` is empty: the row owns nothing in the cluster. `connection` is
|
|
111
|
+
`stables`, and is copied onto the attributes so the row is `ClusterLike`.
|
|
112
|
+
|
|
113
|
+
## Secrecy
|
|
114
|
+
|
|
115
|
+
Attributes are `{ connection, ready, checks }`; the connection is `{ kind, uid }`. No kubeconfig,
|
|
116
|
+
PEM, token or response body reaches an attribute, an error, a log or a file
|
|
117
|
+
(`ready-secrecy.test.ts`).
|
|
118
|
+
|
|
119
|
+
## Bounded HelmChart (`boundedHelmChartProvider`, ledger row `t10-k1b`)
|
|
120
|
+
|
|
121
|
+
Upstream's `Kubernetes.HelmChart` has no deadline: alchemy 2.0.0-beta.81's `requestJson` sets no
|
|
122
|
+
timeout and no signal, so a hung apiserver blocks `reconcile`/`read` forever and would hold a 1h
|
|
123
|
+
platform token across a stuck deploy. `boundedHelmChartProvider()` wraps upstream's provider (never
|
|
124
|
+
re-implements it) in `Effect.timeoutOrElse`, failing typed `KubernetesReconcileTimeout { id,
|
|
125
|
+
operation, seconds }`: `HELM_RECONCILE_TIMEOUT = '5m0s'` (render, discovery, ~60 SSA PATCHes) and
|
|
126
|
+
`HELM_READ_TIMEOUT = '1m0s'`.
|
|
127
|
+
|
|
128
|
+
- Merge it beside `Kubernetes.providers()`: `Layer.mergeAll(Kubernetes.providers(),
|
|
129
|
+
boundedHelmChartProvider())`. A direct `Provider(HelmChart)` service beats the collection
|
|
130
|
+
(`Provider.ts` `tryFindProviderRegistrationByType`), so the wrapper is the one the engine resolves.
|
|
131
|
+
- `diff`, `delete`, `stables` and `aliases` are upstream's by spread: state identity and replace
|
|
132
|
+
semantics do not change.
|
|
133
|
+
- The interrupted fiber abandons its socket (upstream takes no signal); the run fails typed and the
|
|
134
|
+
process exits once main completes. Standing watch: upstream puts no `AbortSignal` on its
|
|
135
|
+
requests; drop the wrapper when it ships request deadlines (alchemy PR 1948).
|
|
136
|
+
- A malformed override dies at layer build rather than leaving the call unbounded, as
|
|
137
|
+
`KubernetesReadyBadDuration` naming `Kubernetes.HelmChart` and the field
|
|
138
|
+
(`reconcileTimeout`/`readTimeout`). The timeout message names the row and seconds only.
|
|
139
|
+
- Errors that pass through (an upstream `KubernetesApiError` quotes up to 1000 bytes of the
|
|
140
|
+
apiserver body, `client.ts:39-41`) are scrubbed to the body-free `KubernetesReadyApiError`.
|
|
141
|
+
- The read bound rarely fires with the `talos-openbao` adapter: upstream's read only connects, and
|
|
142
|
+
the kit already caps connect at 10 s and the uid read at 5 s. It is a backstop.
|
|
143
|
+
- `delete` stays unbounded on purpose: every platform row is `retain`, replace cleanup skips
|
|
144
|
+
deletion of retained rows (alchemy `Apply.ts:1111-1112`), and design §8 only drops state.
|
|
145
|
+
- `isBoundedHelmChartProvider(service)` is true for a service this layer built, so a consuming
|
|
146
|
+
stack can assert its real `providers` layer resolves the wrapper and not upstream's.
|
package/docs/peers.md
CHANGED
|
@@ -29,6 +29,8 @@ asserted against that field; change the field first, then copy its `overrides` b
|
|
|
29
29
|
"@effect/platform-node": "4.0.1",
|
|
30
30
|
"@effect/platform-node-shared": "4.0.1",
|
|
31
31
|
"@effect/platform-bun": "4.0.1",
|
|
32
|
+
"@effect/sql-d1": "4.0.1",
|
|
33
|
+
"@effect/sql-sqlite-do": "4.0.1",
|
|
32
34
|
"rolldown": "1.2.8",
|
|
33
35
|
"redis": "6.3.0"
|
|
34
36
|
}
|
|
@@ -50,6 +52,7 @@ next rc and breaks at import. Pin the whole set.
|
|
|
50
52
|
| `rolldown` → 1.2.9 via vite's `~1.2.6` (measured 2026-09-16) | `GET …/rolldown-1.2.9.tgz - 404` at `bun add` |
|
|
51
53
|
| no `mime` (measured 2026-09-17 against Alchemy 78) | `Cannot find package 'mime'` from cloudflare-runtime |
|
|
52
54
|
| `redis` → 6.3.0 via the `>=5.0.0 <7.0.0` peer of platform-node/sql-pg (measured 2026-09-30) | `No version matching "6.3.0" found for specifier "@redis/time-series" (but package exists)` at `bun add` |
|
|
55
|
+
| `@effect/sql-d1`/`-sqlite-do` → 4.0.2 via alchemy's `^4.0.0` (measured 2026-10-09) | unmet peer: 4.0.2 wants `effect ^4.0.2`, the contract pins 4.0.1 |
|
|
53
56
|
|
|
54
57
|
Alchemy 78 adapted to rc.115 — that first row is why we used to pin 112, not a reason to
|
|
55
58
|
stay there. The override is still the only thing that holds the set together.
|
|
@@ -62,6 +65,16 @@ failed outright for that window. The line completed the same hour — every `@re
|
|
|
62
65
|
sub-package published at 6.3.0 (measured 11:12:19Z) — and the pin moved onto that complete
|
|
63
66
|
set. The pin stays exact because the same class of gap can recur with any future redis minor.
|
|
64
67
|
|
|
68
|
+
⛔ **`@effect/sql-d1` and `@effect/sql-sqlite-do` (added 2026-10-09, ledger row
|
|
69
|
+
kit-contract-effect-402) are exact in `overrides` because alchemy beta.81 itself depends on
|
|
70
|
+
both at `^4.0.0`** (`npm view alchemy@2.0.0-beta.81 dependencies`). A fresh, lockless install
|
|
71
|
+
resolves `^4.0.0` to 4.0.2, whose peer is `effect ^4.0.2` — an **unmet peer against the
|
|
72
|
+
contract's effect 4.0.1 on every fresh consumer install**; only an already-locked tree hid it
|
|
73
|
+
(the kit's own `bun.lock` said 4.0.1 because it was locked earlier). 4.0.1 peers on
|
|
74
|
+
`effect ^4.0.1`, so the pin holds the D1 / Durable-Object SQL resources on the version the
|
|
75
|
+
kit is tested on — the runtime path is unverified, as no stack uses those resources yet. They
|
|
76
|
+
are overrides, not install-line pins: they arrive transitively through `alchemy`.
|
|
77
|
+
|
|
65
78
|
⚠️ `@effect/platform-node` is **required, not optional**: Alchemy's module graph reaches
|
|
66
79
|
`Cloudflare/Workers/WorkerBridge → @effect/platform-node/NodeServices` even when you only
|
|
67
80
|
import the Proxmox subpath.
|
|
@@ -98,6 +98,13 @@ already ships is a finding, even when it works.
|
|
|
98
98
|
`Redacted` values in plaintext, and only one state store encrypts them.
|
|
99
99
|
- **Bounded waits (S26).** A retry stops after at most 8 to 10 attempts and under about a
|
|
100
100
|
minute. A poll never runs past about 90 seconds.
|
|
101
|
+
- **The one exception: readiness gates** (`Talos.ClusterHealth`, `Kubernetes.Ready`). For these,
|
|
102
|
+
waiting IS the resource's job: a CNI or control-plane rollout takes minutes. So the wait may
|
|
103
|
+
run to a declared `waitTimeout` (default minutes, never unbounded) when ALL of these hold:
|
|
104
|
+
the timeout is a typed, declared prop; every single request inside it is bounded (a per-call
|
|
105
|
+
deadline); transient errors are pending, never a retry storm; and the timeout is a typed error
|
|
106
|
+
naming what was still pending. Any other resource keeps the 90-second rule. (Added
|
|
107
|
+
2026-10-10 with `Kubernetes.Ready`, PR 373; `Talos.ClusterHealth` already waited 20m.)
|
|
101
108
|
|
|
102
109
|
## The Bun line for this package (S42–S45)
|
|
103
110
|
|