@alchemy-unifi/network 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +92 -1
- package/dist/index.d.mts +929 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2372 -0
- package/dist/index.mjs.map +1 -0
- package/dist/rolldown-runtime-D8jQpDG7.mjs +12 -0
- package/package.json +74 -1
- package/src/AuthConfig.ts +23 -0
- package/src/AuthProvider.ts +469 -0
- package/src/Connection.ts +185 -0
- package/src/Credentials.ts +85 -0
- package/src/DnsPolicy.ts +181 -0
- package/src/FirewallPolicy.ts +456 -0
- package/src/FirewallPolicyOrdering.ts +286 -0
- package/src/FirewallZone.ts +247 -0
- package/src/HttpClient.ts +138 -0
- package/src/Lookup.ts +34 -0
- package/src/Network.ts +335 -0
- package/src/Providers.ts +59 -0
- package/src/ReadCache.ts +79 -0
- package/src/Site.ts +100 -0
- package/src/SiteManager.ts +68 -0
- package/src/State.ts +607 -0
- package/src/TrafficMatchingList.ts +250 -0
- package/src/WifiBroadcast.ts +318 -0
- package/src/index.ts +17 -0
- package/src/internal/Fields.ts +49 -0
- package/src/internal/Networks.ts +7 -0
- package/src/internal/Plan.ts +22 -0
- package/src/internal/Services.ts +19 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["UnifiHttpClient.isCertificateError","UnifiHttpClient.make","layer","UnifiHttpClient.normaliseFingerprint","UnifiHttpClient.inspectCertificate","UnifiHttpClient.make","SiteManager.listConsoles","ReadCache.key","ReadCache.cached","loadPolicies","policiesKey","ReadCache.key","ReadCache.cached","toAttributes","READ_ONLY","getPolicy","ReadCache.invalidating","ReadCache.key","ReadCache.cached","toAttributes","ReadCache.invalidating","ReadCache.key","ReadCache.cached","toAttributes","READ_ONLY","withDefaults","toBody","attributesOf","isUserDefined","ReadCache.invalidating","ReadCache.key","ReadCache.cached","toAttributes","READ_ONLY","ReadCache.invalidating","ReadCache.key","ReadCache.cached","toAttributes","READ_ONLY","toBody","ReadCache.invalidating","Connection.describeError","Connection.withConnection","Connection.layer","ReadCache.layer","Credentials.fromAuthProvider","toAttributes","ReadCache.key","ReadCache.cached","ReadCache.invalidating","Connection.layer","ReadCache.layer","Credentials.fromAuthProvider"],"sources":["../src/AuthConfig.ts","../src/Credentials.ts","../src/HttpClient.ts","../src/Connection.ts","../src/SiteManager.ts","../src/AuthProvider.ts","../src/internal/Fields.ts","../src/internal/Plan.ts","../src/internal/Services.ts","../src/ReadCache.ts","../src/Site.ts","../src/DnsPolicy.ts","../src/internal/Networks.ts","../src/Lookup.ts","../src/FirewallZone.ts","../src/Network.ts","../src/TrafficMatchingList.ts","../src/FirewallPolicy.ts","../src/State.ts","../src/FirewallPolicyOrdering.ts","../src/WifiBroadcast.ts","../src/Providers.ts"],"sourcesContent":["import * as Schema from \"effect/Schema\";\n\nexport const UNIFI_NETWORK_AUTH_PROVIDER_NAME = \"UnifiNetwork\";\n\n/** Values stored in a profile for the `UnifiNetwork` auth provider. */\nexport const UnifiNetworkAuthConfigSchema = Schema.Union([\n\tSchema.Struct({\n\t\tmethod: Schema.Literal(\"local\"),\n\t\turl: Schema.String,\n\t\tapiKey: Schema.String,\n\t\tcertFingerprint: Schema.optional(Schema.String),\n\t\tinsecure: Schema.optional(Schema.Boolean),\n\t\tsite: Schema.optional(Schema.String),\n\t}),\n\tSchema.Struct({\n\t\tmethod: Schema.Literal(\"remote\"),\n\t\tconsoleId: Schema.String,\n\t\tapiKey: Schema.String,\n\t\tsite: Schema.optional(Schema.String),\n\t}),\n]);\n\nexport type UnifiNetworkAuthConfig = typeof UnifiNetworkAuthConfigSchema.Type;\n","import { ConfigError } from \"@alchemy-unifi/network-sdk\";\nimport { deferUntilFirstUse, orDieCredentialsUnavailable, resolveProviderConfig } from \"alchemy/Auth/Resolve\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport type * as Redacted from \"effect/Redacted\";\nimport { UNIFI_NETWORK_AUTH_PROVIDER_NAME, type UnifiNetworkAuthConfig } from \"./AuthConfig.ts\";\n\n/** A console or self-hosted controller reached directly, e.g. `https://192.168.1.1`. */\nexport interface LocalConnection {\n\treadonly type: \"local\";\n\t/** Origin of the console, without the Integration API path. */\n\treadonly url: string;\n\treadonly apiKey: Redacted.Redacted<string>;\n\t/**\n\t * Accept the server only if a certificate in its chain has this SHA-256\n\t * fingerprint (`AB:CD:…`), whether or not Node trusts it.\n\t */\n\treadonly certFingerprint?: string | undefined;\n\t/** Skip TLS verification entirely. */\n\treadonly insecure: boolean;\n}\n\n/** A console reached through the `api.ui.com` cloud connector. */\nexport interface RemoteConnection {\n\treadonly type: \"remote\";\n\treadonly consoleId: string;\n\treadonly apiKey: Redacted.Redacted<string>;\n}\n\nexport type Connection = LocalConnection | RemoteConnection;\n\n/** Where credentials came from: a named Alchemy profile, or `UNIFI_*` environment variables. */\nexport type CredentialsSource = { readonly type: \"profile\"; readonly profile: string } | { readonly type: \"env\" };\n\nexport interface ResolvedCredentials {\n\treadonly connection: Connection;\n\t/**\n\t * Site used by resources that don't set `site`, by internal reference\n\t * (e.g. `default`) or name. Unset means the console's only site; it's only\n\t * needed on consoles hosting several.\n\t */\n\treadonly site?: string | undefined;\n\treadonly source: CredentialsSource;\n}\n\n/**\n * Lazily-resolved credentials for the active profile (or the environment).\n * The service value is an Effect so nothing resolves at Layer construction;\n * handlers run `yield* yield* UnifiNetworkCredentials`.\n */\nexport class UnifiNetworkCredentials extends Context.Service<\n\tUnifiNetworkCredentials,\n\tEffect.Effect<ResolvedCredentials>\n>()(\"UnifiNetwork::Credentials\") {\n\t/** Alchemy's tag for credentials services, which lets stack bodies require this one. */\n\tdeclare readonly kind: \"Credentials\";\n}\n\n/**\n * Resolves {@link UnifiNetworkCredentials} through the `UnifiNetwork` auth\n * provider: environment variables when they're all set, otherwise the\n * selected Alchemy profile. Resolution is deferred to first use and cached.\n */\nexport const fromAuthProvider = () =>\n\tLayer.effect(\n\t\tUnifiNetworkCredentials,\n\t\tEffect.gen(function* () {\n\t\t\tconst deferred = yield* resolveProviderConfig<UnifiNetworkAuthConfig, ResolvedCredentials>(\n\t\t\t\tUNIFI_NETWORK_AUTH_PROVIDER_NAME,\n\t\t\t).pipe(\n\t\t\t\tEffect.flatMap(({ profileName, resolve }) =>\n\t\t\t\t\tresolve.pipe(\n\t\t\t\t\t\tEffect.mapError((e: { readonly message: string }) =>\n\t\t\t\t\t\t\tConfigError.make({\n\t\t\t\t\t\t\t\tmessage: `Failed to resolve UniFi Network credentials from ${profileName === undefined ? \"the environment\" : `profile '${profileName}'`}: ${e.message}`,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tdeferUntilFirstUse,\n\t\t\t);\n\t\t\treturn yield* deferred.pipe(orDieCredentialsUnavailable(UNIFI_NETWORK_AUTH_PROVIDER_NAME), Effect.cached);\n\t\t}),\n\t);\n","import * as NodeHttpClient from \"@effect/platform-node/NodeHttpClient\";\nimport { buildConnector } from \"@effect/platform-node/Undici\";\nimport * as Data from \"effect/Data\";\nimport * as Effect from \"effect/Effect\";\nimport type * as HttpClient from \"effect/http/HttpClient\";\nimport type * as Scope from \"effect/Scope\";\nimport * as net from \"node:net\";\nimport * as tls from \"node:tls\";\n\n/** How the client verifies a console's TLS certificate. */\nexport interface TlsOptions {\n\t/** Accept the server only if a certificate in its chain has this SHA-256 fingerprint. */\n\treadonly certFingerprint?: string | undefined;\n\t/** Skip verification entirely. */\n\treadonly insecure?: boolean | undefined;\n}\n\n/** Normalises a SHA-256 fingerprint to Node's `AB:CD:…` form, or returns `undefined` if it isn't one. */\nexport const normaliseFingerprint = (value: string): string | undefined => {\n\tconst hex = value.replace(/[\\s:]/g, \"\").toUpperCase();\n\treturn /^[0-9A-F]{64}$/.test(hex) ? (hex.match(/../g) ?? []).join(\":\") : undefined;\n};\n\n/** SHA-256 fingerprints of a peer's certificate chain, leaf first. */\nconst chainFingerprints = (socket: tls.TLSSocket): ReadonlyArray<string> => {\n\tconst fingerprints: string[] = [];\n\tlet cert: tls.DetailedPeerCertificate | undefined = socket.getPeerCertificate(true);\n\twhile (cert?.fingerprint256 !== undefined && !fingerprints.includes(cert.fingerprint256)) {\n\t\tfingerprints.push(cert.fingerprint256);\n\t\tcert = cert.issuerCertificate;\n\t}\n\treturn fingerprints;\n};\n\nconst PINNING_ERROR_CODE = \"ERR_UNIFI_CERT_PINNING\";\n\n/**\n * Whether an error (or one of its causes) is a certificate verification or\n * pinning failure. These are deterministic, so they aren't worth retrying.\n */\nexport const isCertificateError = (error: unknown): boolean => {\n\tfor (let e: unknown = error, depth = 0; typeof e === \"object\" && e !== null && depth < 5; depth++) {\n\t\tconst code = \"code\" in e ? String(e.code) : \"\";\n\t\tif (code === PINNING_ERROR_CODE || /CERT|SELF_SIGNED|UNABLE_TO_VERIFY|UNABLE_TO_GET_ISSUER/.test(code))\n\t\t\treturn true;\n\t\te = \"cause\" in e ? e.cause : \"reason\" in e ? e.reason : undefined;\n\t}\n\treturn false;\n};\n\n/**\n * Pinning can't use `checkServerIdentity`: Node skips it when the chain\n * doesn't verify, which is always the case for a console's self-signed\n * certificate. So verification is off, and the connector checks the\n * presented chain itself once the handshake completes. Session resumption\n * is off too, because a resumed session presents no certificate to check.\n */\nconst pinnedConnector = (fingerprint: string): buildConnector.connector => {\n\tconst connect = buildConnector({ rejectUnauthorized: false, maxCachedSessions: 0 });\n\treturn (options, callback) =>\n\t\tconnect(options, (error, socket) => {\n\t\t\tif (error !== null) return callback(error, null);\n\t\t\tif (socket instanceof tls.TLSSocket && !chainFingerprints(socket).includes(fingerprint)) {\n\t\t\t\tsocket.destroy();\n\t\t\t\treturn callback(\n\t\t\t\t\tObject.assign(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`The certificate presented by ${options.hostname} doesn't match the pinned fingerprint ${fingerprint}.`,\n\t\t\t\t\t\t),\n\t\t\t\t\t\t{ code: PINNING_ERROR_CODE },\n\t\t\t\t\t),\n\t\t\t\t\tnull,\n\t\t\t\t);\n\t\t\t}\n\t\t\tcallback(null, socket);\n\t\t});\n};\n\n/** An undici-backed `HttpClient` applying {@link TlsOptions}. The dispatcher closes with the scope. */\nexport const make = (options: TlsOptions = {}): Effect.Effect<HttpClient.HttpClient, never, Scope.Scope> =>\n\tNodeHttpClient.makeDispatcherOptions(\n\t\toptions.certFingerprint !== undefined\n\t\t\t? { connect: pinnedConnector(options.certFingerprint) }\n\t\t\t: options.insecure\n\t\t\t\t? { connect: { rejectUnauthorized: false } }\n\t\t\t\t: undefined,\n\t).pipe(\n\t\tEffect.flatMap((dispatcher) =>\n\t\t\tNodeHttpClient.makeUndici.pipe(Effect.provideService(NodeHttpClient.Dispatcher, dispatcher)),\n\t\t),\n\t);\n\nexport class CertificateInspectionError extends Data.TaggedError(\"CertificateInspectionError\")<{\n\treadonly message: string;\n\treadonly cause?: unknown;\n}> {}\n\nexport interface PeerCertificate {\n\t/** Whether Node's default CAs trust the chain for this host. */\n\treadonly trusted: boolean;\n\t/** SHA-256 fingerprint of the leaf certificate (`AB:CD:…`). */\n\treadonly fingerprint: string;\n\treadonly subject: string;\n\treadonly validTo: string;\n}\n\n/**\n * Connects to an `https:` URL without verifying it and reports the\n * certificate it presents, so `configure` can offer to pin it.\n */\nexport const inspectCertificate = (url: string): Effect.Effect<PeerCertificate, CertificateInspectionError> =>\n\tEffect.callback<PeerCertificate, CertificateInspectionError>((resume) => {\n\t\tconst { hostname, port } = new URL(url);\n\t\tconst host = hostname.replace(/^\\[|\\]$/g, \"\");\n\t\tconst socket = tls.connect({\n\t\t\thost,\n\t\t\tport: Number(port || 443),\n\t\t\trejectUnauthorized: false,\n\t\t\t...(net.isIP(host) === 0 ? { servername: host } : {}),\n\t\t});\n\t\tsocket.setTimeout(10_000, () => socket.destroy(new Error(`Timed out connecting to ${url}.`)));\n\t\tsocket.once(\"secureConnect\", () => {\n\t\t\tconst cert = socket.getPeerCertificate();\n\t\t\tresume(\n\t\t\t\tEffect.succeed({\n\t\t\t\t\ttrusted: socket.authorized,\n\t\t\t\t\tfingerprint: cert.fingerprint256,\n\t\t\t\t\tsubject: String(cert.subject?.CN ?? cert.subjectaltname ?? \"unknown\"),\n\t\t\t\t\tvalidTo: cert.valid_to,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tsocket.end();\n\t\t});\n\t\tsocket.once(\"error\", (cause) =>\n\t\t\tresume(Effect.fail(new CertificateInspectionError({ message: cause.message, cause }))),\n\t\t);\n\t\treturn Effect.sync(() => socket.destroy());\n\t});\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as HttpClient from \"effect/http/HttpClient\";\nimport * as Layer from \"effect/Layer\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Schedule from \"effect/Schedule\";\nimport * as RateLimiter from \"effect/persistence/RateLimiter\";\nimport * as Scope from \"effect/Scope\";\nimport { type Connection, UnifiNetworkCredentials } from \"./Credentials.ts\";\nimport * as UnifiHttpClient from \"./HttpClient.ts\";\n\nexport const SITE_MANAGER_URL = \"https://api.ui.com\";\n\n/** Integration API base URL through the cloud connector. */\nexport const connectorBaseUrl = (consoleId: string) =>\n\t`${SITE_MANAGER_URL}/v1/connector/consoles/${encodeURIComponent(consoleId)}/proxy/network/integration`;\n\n/** Integration API base URLs to try for a connection: a UniFi OS console's, then a standalone controller's. */\nexport const candidateBaseUrls = (connection: Connection): ReadonlyArray<string> =>\n\tconnection.type === \"remote\"\n\t\t? [connectorBaseUrl(connection.consoleId)]\n\t\t: [`${connection.url}/proxy/network/integration`, `${connection.url}/integration`];\n\n/** The SDK's default retry policy, minus certificate failures, which retrying won't fix. */\nconst probeRetry: Sdk.Retry.Policy = (lastError) => {\n\tconst defaults = Sdk.Retry.makeDefault(lastError);\n\treturn {\n\t\t...defaults,\n\t\twhile: (error) => (defaults.while?.(error) ?? false) && !UnifiHttpClient.isCertificateError(error),\n\t};\n};\n\n/** The connector allows 100 requests/minute per console; stay under it. */\nconst REMOTE_REQUESTS_PER_MINUTE = 90;\n\n/** A connection whose base URL has been found and whose API key works. */\nexport interface Established {\n\treadonly baseUrl: string;\n\treadonly applicationVersion: string;\n\t/** What SDK operations need: credentials for {@link baseUrl} and a client with the connection's TLS settings. */\n\treadonly services: Context.Context<Sdk.Credentials | HttpClient.HttpClient>;\n}\n\n/** Joins an error's message with its causes' (e.g. a transport error and the TLS failure behind it). */\nexport const describeError = (error: unknown): string => {\n\tconst messages: string[] = [];\n\tfor (let e: unknown = error, depth = 0; e !== undefined && e !== null && depth < 5; depth++) {\n\t\tconst message = typeof e === \"object\" && \"message\" in e ? String(e.message) : String(e);\n\t\tif (message !== \"\" && !messages.some((m) => m.includes(message))) messages.push(message);\n\t\te = typeof e === \"object\" && \"cause\" in e ? e.cause : undefined;\n\t}\n\treturn messages.join(\": \") || \"unknown error\";\n};\n\nconst describeTarget = (connection: Connection) =>\n\tconnection.type === \"remote\" ? `console ${connection.consoleId} via ${SITE_MANAGER_URL}` : connection.url;\n\nconst rateLimited = (client: HttpClient.HttpClient, key: string) =>\n\tEffect.gen(function* () {\n\t\tconst limiter = yield* RateLimiter.make.pipe(Effect.provide(RateLimiter.layerStoreMemory));\n\t\tconst wait = RateLimiter.sleep(limiter, {\n\t\t\talgorithm: \"token-bucket\",\n\t\t\twindow: \"1 minute\",\n\t\t\tlimit: REMOTE_REQUESTS_PER_MINUTE,\n\t\t\tkey,\n\t\t}).pipe(Effect.orDie);\n\t\treturn HttpClient.transform(client, (response) => Effect.andThen(wait, response));\n\t});\n\n/**\n * Builds a client for the connection and finds the Integration API: local\n * connections try the console path, then the standalone controller path.\n * Fails with a `ConfigError` describing what went wrong. `options.client`\n * replaces the TLS-configured client (for tests).\n */\nexport const connect = (\n\tconnection: Connection,\n\toptions: { readonly client?: HttpClient.HttpClient } = {},\n): Effect.Effect<Established, Sdk.ConfigError, Scope.Scope> =>\n\tEffect.gen(function* () {\n\t\tconst base = options.client ?? (yield* UnifiHttpClient.make(connection.type === \"local\" ? connection : {}));\n\t\tconst client =\n\t\t\tconnection.type === \"remote\" ? yield* rateLimited(base, `connector:${connection.consoleId}`) : base;\n\t\tconst servicesFor = (baseUrl: string) =>\n\t\t\tContext.make(Sdk.Credentials, Effect.succeed({ baseUrl, apiKey: connection.apiKey })).pipe(\n\t\t\t\tContext.add(HttpClient.HttpClient, client),\n\t\t\t);\n\t\tconst candidates = candidateBaseUrls(connection);\n\t\tfor (const baseUrl of candidates) {\n\t\t\tconst services = servicesFor(baseUrl);\n\t\t\tconst info = yield* Sdk.getInfo({}).pipe(\n\t\t\t\tEffect.provide(services),\n\t\t\t\tSdk.Retry.policy(probeRetry),\n\t\t\t\tEffect.map(({ applicationVersion }) => ({ baseUrl, applicationVersion, services })),\n\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.undefined),\n\t\t\t\tEffect.mapError((error) =>\n\t\t\t\t\tSdk.ConfigError.make({\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\terror._tag === \"Unauthorized\" || error._tag === \"Forbidden\"\n\t\t\t\t\t\t\t\t? `${describeTarget(connection)} rejected the API key (${error._tag}).`\n\t\t\t\t\t\t\t\t: `Could not connect to ${describeTarget(connection)}: ${describeError(error)}`,\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (info !== undefined) return info;\n\t\t}\n\t\treturn yield* Sdk.ConfigError.make({\n\t\t\tmessage:\n\t\t\t\tconnection.type === \"remote\"\n\t\t\t\t\t? `The cloud connector found no UniFi Network application on console ${connection.consoleId}.`\n\t\t\t\t\t: `No UniFi Network Integration API at ${connection.url} (tried ${candidates.join(\", \")}). Use the console's root URL, e.g. https://192.168.1.1.`,\n\t\t});\n\t});\n\nconst majorMinor = (version: string) => version.split(\".\").slice(0, 2).join(\".\");\n\n/** Patch releases routinely ship without a published spec, so only major.minor drift is worth a warning. */\nexport const checkVersion = (applicationVersion: string) =>\n\tmajorMinor(applicationVersion) === majorMinor(Sdk.SPEC_VERSION)\n\t\t? Effect.void\n\t\t: Effect.logWarning(\n\t\t\t\t`UniFi Network ${applicationVersion} is newer or older than the API spec this provider was built from (${Sdk.SPEC_VERSION}). Resources may not behave as documented.`,\n\t\t\t);\n\n/**\n * The profile's established connection, created on first use and shared for\n * the rest of the run. Provider-private: its `HttpClient` and SDK\n * `Credentials` never enter the stack's context, so they can't replace other\n * providers' clients.\n */\nexport class UnifiNetworkConnection extends Context.Service<\n\tUnifiNetworkConnection,\n\tEffect.Effect<Established, Sdk.ConfigError>\n>()(\"UnifiNetwork::Connection\") {\n\t/** Alchemy's tag for environment services, which lets stack bodies (and so lookups) require this one. */\n\tdeclare readonly kind: \"Environment\";\n}\n\nexport const layer = Layer.effect(\n\tUnifiNetworkConnection,\n\tEffect.gen(function* () {\n\t\tconst credentials = yield* UnifiNetworkCredentials;\n\t\tconst scope = yield* Effect.scope;\n\t\treturn yield* Effect.cached(\n\t\t\tcredentials.pipe(\n\t\t\t\tEffect.flatMap(({ connection }) => connect(connection)),\n\t\t\t\tEffect.tap(({ applicationVersion }) => checkVersion(applicationVersion)),\n\t\t\t\tScope.provide(scope),\n\t\t\t),\n\t\t);\n\t}),\n);\n\n/**\n * The 422 \"Configured network does not exist\" the console answers requests\n * with, on any endpoint, for a moment while networks are created or deleted\n * anywhere on it (seen on the test controller under parallel tests, for\n * network, zone and policy requests alike). It's never a real error, so it's\n * retried; other 422s are.\n */\nconst isStaleNetworkState = (error: unknown) =>\n\tPredicate.hasProperty(error, \"_tag\") &&\n\terror._tag === \"UnprocessableEntity\" &&\n\tPredicate.hasProperty(error, \"message\") &&\n\ttypeof error.message === \"string\" &&\n\terror.message.includes(\"Configured network does not exist\");\n\n/** Runs SDK operations against the profile's console, retrying the console's transient stale-network 422. */\nexport const withConnection = <A, E, R>(\n\teffect: Effect.Effect<A, E, R>,\n): Effect.Effect<\n\tA,\n\tE | Sdk.ConfigError,\n\tExclude<R, Sdk.Credentials | HttpClient.HttpClient> | UnifiNetworkConnection\n> =>\n\tEffect.gen(function* () {\n\t\tconst { services } = yield* yield* UnifiNetworkConnection;\n\t\treturn yield* Effect.provide(effect, services).pipe(\n\t\t\tEffect.retry({\n\t\t\t\twhile: isStaleNetworkState,\n\t\t\t\tschedule: Schedule.spaced(\"500 millis\").pipe(Schedule.upTo({ times: 10 })),\n\t\t\t}),\n\t\t);\n\t});\n","import * as Effect from \"effect/Effect\";\nimport * as HttpClient from \"effect/http/HttpClient\";\nimport * as HttpClientRequest from \"effect/http/HttpClientRequest\";\nimport * as HttpClientResponse from \"effect/http/HttpClientResponse\";\nimport * as Redacted from \"effect/Redacted\";\nimport * as Schema from \"effect/Schema\";\nimport { SITE_MANAGER_URL } from \"./Connection.ts\";\n\n/**\n * Just enough of the Site Manager API (`GET /v1/hosts`) for `configure` to\n * list the consoles a key can reach. A full SDK belongs in its own package.\n */\nconst Host = Schema.Struct({\n\tid: Schema.String,\n\ttype: Schema.optional(Schema.NullOr(Schema.String)),\n\treportedState: Schema.optional(\n\t\tSchema.NullOr(\n\t\t\tSchema.Struct({\n\t\t\t\tname: Schema.optional(Schema.NullOr(Schema.String)),\n\t\t\t\thostname: Schema.optional(Schema.NullOr(Schema.String)),\n\t\t\t}),\n\t\t),\n\t),\n});\n\nconst HostsPage = Schema.Struct({\n\tdata: Schema.Array(Host),\n\tnextToken: Schema.optional(Schema.NullOr(Schema.String)),\n});\n\nexport interface Console {\n\t/** The host ID, which the cloud connector takes as the console ID. */\n\treadonly id: string;\n\treadonly name: string;\n\t/** Host type as reported by Site Manager, e.g. `console`. */\n\treadonly type: string | undefined;\n}\n\nexport const listConsoles = (\n\tapiKey: Redacted.Redacted<string>,\n): Effect.Effect<ReadonlyArray<Console>, unknown, HttpClient.HttpClient> =>\n\tEffect.gen(function* () {\n\t\tconst client = (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatusOk);\n\t\tconst consoles: Console[] = [];\n\t\tlet nextToken: string | undefined;\n\t\tdo {\n\t\t\tconst page = yield* client\n\t\t\t\t.execute(\n\t\t\t\t\tHttpClientRequest.get(`${SITE_MANAGER_URL}/v1/hosts`).pipe(\n\t\t\t\t\t\tHttpClientRequest.setHeaders({\n\t\t\t\t\t\t\t\"X-API-KEY\": Redacted.value(apiKey),\n\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tHttpClientRequest.setUrlParams(nextToken === undefined ? {} : { nextToken }),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.pipe(Effect.flatMap(HttpClientResponse.schemaBodyJson(HostsPage)));\n\t\t\tfor (const host of page.data) {\n\t\t\t\tconsoles.push({\n\t\t\t\t\tid: host.id,\n\t\t\t\t\tname: host.reportedState?.name ?? host.reportedState?.hostname ?? host.id,\n\t\t\t\t\ttype: host.type ?? undefined,\n\t\t\t\t});\n\t\t\t}\n\t\t\tnextToken = page.nextToken ?? undefined;\n\t\t} while (nextToken !== undefined);\n\t\treturn consoles;\n\t});\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport {\n\tAuthError,\n\tAuthProviderLayer,\n\ttype ConfigureField,\n\ttype ConfigureMethod,\n\ttype EnvironmentVariable,\n\ttype ProviderDetailLine,\n} from \"alchemy/Auth/AuthProvider\";\nimport { displayRedacted } from \"alchemy/Auth/Credentials\";\nimport { getEnv, getEnvRedactedRequired, mapPromptCancellation } from \"alchemy/Auth/Env\";\nimport { storedSecret, storedValueText, validateFieldValues } from \"alchemy/Auth/StoredAuthProvider\";\nimport * as Interaction from \"alchemy/Interaction\";\nimport * as Effect from \"effect/Effect\";\nimport * as HttpClient from \"effect/http/HttpClient\";\nimport * as Redacted from \"effect/Redacted\";\nimport {\n\tUNIFI_NETWORK_AUTH_PROVIDER_NAME,\n\ttype UnifiNetworkAuthConfig,\n\tUnifiNetworkAuthConfigSchema,\n} from \"./AuthConfig.ts\";\nimport { connect, describeError } from \"./Connection.ts\";\nimport type { Connection, ResolvedCredentials } from \"./Credentials.ts\";\nimport * as UnifiHttpClient from \"./HttpClient.ts\";\nimport * as SiteManager from \"./SiteManager.ts\";\n\nexport { UNIFI_NETWORK_AUTH_PROVIDER_NAME, type UnifiNetworkAuthConfig, UnifiNetworkAuthConfigSchema };\n\nconst NAME = UNIFI_NETWORK_AUTH_PROVIDER_NAME;\n\nconst authError = (message: string, cause?: unknown) =>\n\tAuthError.make({ message: `${NAME}: ${message}`, ...(cause === undefined ? {} : { cause }) });\n\n/** Normalises a console URL (no trailing slash), or returns `undefined` if it isn't `http(s):`. */\nexport const parseConsoleUrl = (value: string): string | undefined => {\n\ttry {\n\t\tconst url = new URL(value.trim());\n\t\tif (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n\t\treturn `${url.origin}${url.pathname.replace(/\\/+$/, \"\")}`;\n\t} catch {\n\t\treturn undefined;\n\t}\n};\n\nconst parseBoolean = (value: string): boolean | undefined =>\n\t/^(true|1|yes)$/i.test(value) ? true : /^(false|0|no|)$/i.test(value) ? false : undefined;\n\nconst validateUrl = (value: string) =>\n\tparseConsoleUrl(value) === undefined ? \"Expected an http(s) URL such as https://192.168.1.1\" : undefined;\nconst validateFingerprint = (value: string) =>\n\tUnifiHttpClient.normaliseFingerprint(value) === undefined\n\t\t? \"Expected a SHA-256 fingerprint: 64 hex digits, optionally colon-separated\"\n\t\t: undefined;\nconst validateBoolean = (value: string) => (parseBoolean(value) === undefined ? \"Expected true or false\" : undefined);\nconst required = (value: string) => (value.length === 0 ? \"Required\" : undefined);\n\ninterface Input {\n\treadonly method: \"local\" | \"remote\";\n\treadonly url?: string | undefined;\n\treadonly consoleId?: string | undefined;\n\treadonly apiKey: Redacted.Redacted<string>;\n\treadonly certFingerprint?: string | undefined;\n\treadonly insecure?: boolean | undefined;\n\treadonly site?: string | undefined;\n}\n\n/** Credentials minus their {@link ResolvedCredentials.source}: what profile and environment values both describe. */\ntype Settings = Omit<ResolvedCredentials, \"source\">;\n\n/** Validates stored, entered or environment values. `source` names them in errors. */\nconst resolve = (input: Input, source: string): Effect.Effect<Settings, AuthError> =>\n\tEffect.gen(function* () {\n\t\tconst site = input.site || undefined;\n\t\tif (input.method === \"remote\") {\n\t\t\tif (!input.consoleId) return yield* authError(`no console ID (${source}).`);\n\t\t\treturn { connection: { type: \"remote\", consoleId: input.consoleId, apiKey: input.apiKey }, site };\n\t\t}\n\t\tconst url = parseConsoleUrl(input.url ?? \"\");\n\t\tif (url === undefined) return yield* authError(`'${input.url ?? \"\"}' is not an http(s) URL (${source}).`);\n\t\tconst certFingerprint =\n\t\t\tinput.certFingerprint === undefined\n\t\t\t\t? undefined\n\t\t\t\t: UnifiHttpClient.normaliseFingerprint(input.certFingerprint);\n\t\tif (input.certFingerprint !== undefined && certFingerprint === undefined) {\n\t\t\treturn yield* authError(`'${input.certFingerprint}' is not a SHA-256 fingerprint (${source}).`);\n\t\t}\n\t\tif (certFingerprint !== undefined && input.insecure) {\n\t\t\treturn yield* authError(`set a certificate fingerprint or insecure, not both (${source}).`);\n\t\t}\n\t\tif (certFingerprint !== undefined && url.startsWith(\"http:\")) {\n\t\t\treturn yield* authError(`a certificate fingerprint needs an https: URL (${source}).`);\n\t\t}\n\t\treturn {\n\t\t\tconnection: {\n\t\t\t\ttype: \"local\",\n\t\t\t\turl,\n\t\t\t\tapiKey: input.apiKey,\n\t\t\t\tcertFingerprint,\n\t\t\t\tinsecure: input.insecure ?? false,\n\t\t\t},\n\t\t\tsite,\n\t\t};\n\t});\n\nconst fromConfig = (\n\tprofileName: string,\n\tconfig: UnifiNetworkAuthConfig,\n): Effect.Effect<ResolvedCredentials, AuthError> =>\n\tresolve({ ...config, apiKey: Redacted.make(config.apiKey) }, `profile '${profileName}'`).pipe(\n\t\tEffect.map((settings) => ({ ...settings, source: { type: \"profile\", profile: profileName } })),\n\t);\n\nconst toConfig = ({ connection, site }: Settings): UnifiNetworkAuthConfig =>\n\tconnection.type === \"remote\"\n\t\t? {\n\t\t\t\tmethod: \"remote\",\n\t\t\t\tconsoleId: connection.consoleId,\n\t\t\t\tapiKey: Redacted.value(connection.apiKey),\n\t\t\t\t...(site === undefined ? {} : { site }),\n\t\t\t}\n\t\t: {\n\t\t\t\tmethod: \"local\",\n\t\t\t\turl: connection.url,\n\t\t\t\tapiKey: Redacted.value(connection.apiKey),\n\t\t\t\t...(connection.certFingerprint === undefined ? {} : { certFingerprint: connection.certFingerprint }),\n\t\t\t\t...(connection.insecure ? { insecure: true } : {}),\n\t\t\t\t...(site === undefined ? {} : { site }),\n\t\t\t};\n\nconst siteField: ConfigureField = {\n\tname: \"site\",\n\tlabel: \"Default site\",\n\tdescription:\n\t\t\"Only for consoles hosting several sites: internal reference (e.g. default) or name of the site resources use when they don't set one.\",\n\toptional: true,\n};\n\nconst configureMethods: ReadonlyArray<ConfigureMethod> = [\n\t{\n\t\tmethod: \"local\",\n\t\tfields: [\n\t\t\t{ name: \"url\", label: \"Console URL\", placeholder: \"https://192.168.1.1\", validate: validateUrl },\n\t\t\t{ name: \"apiKey\", label: \"API key\", secret: true },\n\t\t\t{\n\t\t\t\tname: \"certFingerprint\",\n\t\t\t\tlabel: \"Certificate SHA-256 fingerprint\",\n\t\t\t\tdescription: \"Pin the console's certificate (or a CA in its chain) instead of verifying it.\",\n\t\t\t\toptional: true,\n\t\t\t\tvalidate: validateFingerprint,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: \"insecure\",\n\t\t\t\tlabel: \"Skip TLS verification\",\n\t\t\t\toptional: true,\n\t\t\t\tvalidate: validateBoolean,\n\t\t\t},\n\t\t\tsiteField,\n\t\t],\n\t},\n\t{\n\t\tmethod: \"remote\",\n\t\tfields: [\n\t\t\t{ name: \"consoleId\", label: \"Console ID\", description: \"The console's host ID in Site Manager.\" },\n\t\t\t{ name: \"apiKey\", label: \"Site Manager API key\", secret: true },\n\t\t\tsiteField,\n\t\t],\n\t},\n];\n\nconst environment: ReadonlyArray<EnvironmentVariable> = [\n\t{\n\t\tname: \"UNIFI_URL\",\n\t\trequired: true,\n\t\talternatives: [\"UNIFI_CONSOLE_ID\"],\n\t\tdescription:\n\t\t\t\"URL of the console or controller, e.g. https://192.168.1.1. To go through the ui.com cloud connector instead, set UNIFI_CONSOLE_ID to the console's host ID.\",\n\t},\n\t{\n\t\tname: \"UNIFI_API_KEY\",\n\t\trequired: true,\n\t\tsecret: true,\n\t\tdescription: \"A Network API key (local) or a Site Manager API key (remote).\",\n\t},\n\t{\n\t\tname: \"UNIFI_CERT_FINGERPRINT\",\n\t\trequired: false,\n\t\tdescription: \"Local only: SHA-256 fingerprint of the console's certificate (or a CA in its chain) to pin.\",\n\t},\n\t{ name: \"UNIFI_INSECURE\", required: false, description: \"Local only: true to skip TLS verification.\" },\n\t{\n\t\tname: \"UNIFI_SITE\",\n\t\trequired: false,\n\t\tdescription: \"Default site, for consoles hosting several. Unset means the console's only site.\",\n\t},\n];\n\nconst readEnvironmentSettings = Effect.gen(function* () {\n\tconst apiKey = yield* getEnvRedactedRequired(\"UNIFI_API_KEY\");\n\tconst url = yield* getEnv(\"UNIFI_URL\");\n\tconst consoleId = yield* getEnv(\"UNIFI_CONSOLE_ID\");\n\tconst site = yield* getEnv(\"UNIFI_SITE\");\n\tif (url && consoleId) return yield* authError(\"set UNIFI_URL or UNIFI_CONSOLE_ID, not both.\");\n\tif (consoleId)\n\t\treturn yield* resolve({ method: \"remote\", consoleId, apiKey, site }, \"UNIFI_* environment variables\");\n\tif (!url) return yield* authError(\"set UNIFI_URL (local) or UNIFI_CONSOLE_ID (remote) along with UNIFI_API_KEY.\");\n\tconst insecureValue = (yield* getEnv(\"UNIFI_INSECURE\")) ?? \"\";\n\tconst insecure = parseBoolean(insecureValue);\n\tif (insecure === undefined)\n\t\treturn yield* authError(`UNIFI_INSECURE must be true or false, not '${insecureValue}'.`);\n\tconst certFingerprint = (yield* getEnv(\"UNIFI_CERT_FINGERPRINT\")) || undefined;\n\treturn yield* resolve(\n\t\t{ method: \"local\", url, apiKey, certFingerprint, insecure, site },\n\t\t\"UNIFI_* environment variables\",\n\t);\n});\n\nconst readEnvironment: Effect.Effect<ResolvedCredentials, AuthError> = readEnvironmentSettings.pipe(\n\tEffect.map((settings) => ({ ...settings, source: { type: \"env\" } })),\n);\n\n/** Runs `f` against a freshly established connection, mapping connection failures to `AuthError`. */\nconst withEstablished = <A, E>(\n\tconnection: Connection,\n\tf: (established: Effect.Success<ReturnType<typeof connect>>) => Effect.Effect<A, E>,\n) =>\n\tEffect.scoped(Effect.flatMap(connect(connection), f)).pipe(\n\t\tEffect.mapError((error) => (error instanceof AuthError ? error : authError(describeError(error), error))),\n\t);\n\nconst configureWith = (\n\t_profileName: string,\n\tinput: { readonly method: string; readonly values: Record<string, string> },\n): Effect.Effect<UnifiNetworkAuthConfig, AuthError> =>\n\tEffect.gen(function* () {\n\t\tconst method = configureMethods.find((m) => m.method === input.method);\n\t\tif (method === undefined) {\n\t\t\treturn yield* authError(`unknown method '${input.method}'. Valid methods: local, remote.`);\n\t\t}\n\t\tconst values = yield* validateFieldValues(NAME, method.fields, input.values);\n\t\tconst insecure = storedValueText(values[\"insecure\"]);\n\t\tconst resolved = yield* resolve(\n\t\t\t{\n\t\t\t\tmethod: method.method === \"remote\" ? \"remote\" : \"local\",\n\t\t\t\turl: storedValueText(values[\"url\"]),\n\t\t\t\tconsoleId: storedValueText(values[\"consoleId\"]),\n\t\t\t\tapiKey: storedSecret(values[\"apiKey\"]) ?? Redacted.make(\"\"),\n\t\t\t\tcertFingerprint: storedValueText(values[\"certFingerprint\"]),\n\t\t\t\tinsecure: insecure === undefined ? undefined : parseBoolean(insecure),\n\t\t\t\tsite: storedValueText(values[\"site\"]),\n\t\t\t},\n\t\t\t\"--set values\",\n\t\t);\n\t\treturn toConfig(resolved);\n\t});\n\nconst details = (profileName: string, config: UnifiNetworkAuthConfig) =>\n\tEffect.gen(function* () {\n\t\tconst { connection, site } = yield* fromConfig(profileName, config);\n\t\tconst lines: ProviderDetailLine[] =\n\t\t\tconnection.type === \"remote\"\n\t\t\t\t? [\n\t\t\t\t\t\t{ key: \"method\", value: \"remote (ui.com cloud connector)\" },\n\t\t\t\t\t\t{ key: \"consoleId\", value: connection.consoleId },\n\t\t\t\t\t]\n\t\t\t\t: [\n\t\t\t\t\t\t{ key: \"method\", value: \"local\" },\n\t\t\t\t\t\t{ key: \"url\", value: connection.url },\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tkey: \"tls\",\n\t\t\t\t\t\t\tvalue: connection.url.startsWith(\"http:\")\n\t\t\t\t\t\t\t\t? \"none (http)\"\n\t\t\t\t\t\t\t\t: connection.certFingerprint !== undefined\n\t\t\t\t\t\t\t\t\t? `pinned ${connection.certFingerprint}`\n\t\t\t\t\t\t\t\t\t: connection.insecure\n\t\t\t\t\t\t\t\t\t\t? \"not verified\"\n\t\t\t\t\t\t\t\t\t\t: \"verified\",\n\t\t\t\t\t\t},\n\t\t\t\t\t];\n\t\tlines.push({ key: \"apiKey\", value: displayRedacted(connection.apiKey) });\n\t\tif (site !== undefined) lines.push({ key: \"site\", value: site });\n\t\tconst network = yield* withEstablished(connection, (established) =>\n\t\t\tEffect.succeed(`UniFi Network ${established.applicationVersion}`),\n\t\t).pipe(Effect.catch((error) => Effect.succeed(`unreachable (${error.message})`)));\n\t\tlines.push({ key: \"network\", value: network });\n\t\treturn { lines };\n\t});\n\nexport const UnifiNetworkAuth = AuthProviderLayer<UnifiNetworkAuthConfig, ResolvedCredentials>()(\n\tNAME,\n\tEffect.gen(function* () {\n\t\tconst interaction = Interaction.accessors;\n\n\t\t/**\n\t\t * Lists the console's sites and, if there are several, lets the user\n\t\t * pick the default (remembering `current`). A single site isn't stored.\n\t\t */\n\t\tconst pickSite = (connection: Connection, current: string | undefined) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst { applicationVersion, sites } = yield* withEstablished(connection, (established) =>\n\t\t\t\t\tSdk.getSiteOverviewPage({ limit: 200 }).pipe(\n\t\t\t\t\t\tEffect.provide(established.services),\n\t\t\t\t\t\tEffect.map((page) => ({\n\t\t\t\t\t\t\tapplicationVersion: established.applicationVersion,\n\t\t\t\t\t\t\tsites: page.data,\n\t\t\t\t\t\t})),\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tyield* interaction.output.info(`Connected to UniFi Network ${applicationVersion}.`);\n\t\t\t\tconst [only] = sites;\n\t\t\t\tif (only === undefined) return yield* authError(\"the console has no sites.\");\n\t\t\t\tif (sites.length === 1) {\n\t\t\t\t\tyield* interaction.output.info(`The console has one site, ${only.name}; resources will use it.`);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\treturn yield* interaction.prompt\n\t\t\t\t\t.select({\n\t\t\t\t\t\tmessage: \"Default site\",\n\t\t\t\t\t\tsearchable: true,\n\t\t\t\t\t\t...(current !== undefined && sites.some((s) => s.internalReference === current)\n\t\t\t\t\t\t\t? { initialValue: current }\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\toptions: sites.map((s) => ({\n\t\t\t\t\t\t\tvalue: s.internalReference,\n\t\t\t\t\t\t\tlabel: s.name,\n\t\t\t\t\t\t\tdescription: s.internalReference,\n\t\t\t\t\t\t})),\n\t\t\t\t\t})\n\t\t\t\t\t.pipe(mapPromptCancellation);\n\t\t\t});\n\n\t\tconst promptApiKey = (message: string, description: string) =>\n\t\t\tinteraction.prompt\n\t\t\t\t.password({ message, description, validate: required })\n\t\t\t\t.pipe(mapPromptCancellation, Effect.map(Redacted.make));\n\n\t\tconst configureLocal = (current: UnifiNetworkAuthConfig | undefined) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst url = yield* interaction.prompt\n\t\t\t\t\t.text({\n\t\t\t\t\t\tmessage: \"Console URL\",\n\t\t\t\t\t\tdescription: \"The console's or self-hosted controller's address on your network.\",\n\t\t\t\t\t\tplaceholder: \"https://192.168.1.1\",\n\t\t\t\t\t\t...(current?.method === \"local\" ? { defaultValue: current.url } : {}),\n\t\t\t\t\t\tvalidate: validateUrl,\n\t\t\t\t\t})\n\t\t\t\t\t.pipe(\n\t\t\t\t\t\tmapPromptCancellation,\n\t\t\t\t\t\tEffect.map((value) => parseConsoleUrl(value) ?? value),\n\t\t\t\t\t);\n\t\t\t\tconst apiKey = yield* promptApiKey(\n\t\t\t\t\t\"API key\",\n\t\t\t\t\t\"Create one in UniFi Network under Settings → Integrations.\",\n\t\t\t\t);\n\n\t\t\t\tlet tls: { certFingerprint?: string; insecure?: boolean } = {};\n\t\t\t\tif (url.startsWith(\"https:\")) {\n\t\t\t\t\tconst cert = yield* UnifiHttpClient.inspectCertificate(url).pipe(\n\t\t\t\t\t\tEffect.mapError((error) =>\n\t\t\t\t\t\t\tauthError(`could not connect to ${url}: ${describeError(error)}`, error),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tif (cert.trusted) {\n\t\t\t\t\t\tyield* interaction.output.info(\n\t\t\t\t\t\t\t\"The console's certificate is publicly trusted; it will be verified normally.\",\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst choice = yield* interaction.prompt\n\t\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\t\tmessage: `${url} presents an untrusted certificate (${cert.subject}, valid until ${cert.validTo})`,\n\t\t\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"pin\" as const,\n\t\t\t\t\t\t\t\t\t\tlabel: \"Pin this certificate\",\n\t\t\t\t\t\t\t\t\t\tdescription: `SHA-256 ${cert.fingerprint}. Check it matches the console's before choosing this.`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tvalue: \"insecure\" as const,\n\t\t\t\t\t\t\t\t\t\tlabel: \"Skip verification\",\n\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\"Accept any certificate. Anyone on the network path could intercept the API key.\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.pipe(mapPromptCancellation);\n\t\t\t\t\t\ttls = choice === \"pin\" ? { certFingerprint: cert.fingerprint } : { insecure: true };\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst resolved = yield* resolve({ method: \"local\", url, apiKey, ...tls }, \"entered values\");\n\t\t\t\tconst site = yield* pickSite(resolved.connection, current?.site);\n\t\t\t\treturn toConfig({ ...resolved, site });\n\t\t\t});\n\n\t\tconst configureRemote = (current: UnifiNetworkAuthConfig | undefined) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst apiKey = yield* promptApiKey(\"Site Manager API key\", \"Create one at unifi.ui.com under API.\");\n\t\t\t\tconst consoles = yield* Effect.scoped(\n\t\t\t\t\tUnifiHttpClient.make().pipe(\n\t\t\t\t\t\tEffect.flatMap((client) =>\n\t\t\t\t\t\t\tSiteManager.listConsoles(apiKey).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t).pipe(\n\t\t\t\t\tEffect.mapError((error) => authError(`could not list consoles: ${describeError(error)}`, error)),\n\t\t\t\t);\n\t\t\t\tconst [only] = consoles;\n\t\t\t\tif (only === undefined) return yield* authError(\"this API key can't see any consoles.\");\n\t\t\t\tconst consoleId =\n\t\t\t\t\tconsoles.length === 1\n\t\t\t\t\t\t? only.id\n\t\t\t\t\t\t: yield* interaction.prompt\n\t\t\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\t\t\tmessage: \"Console\",\n\t\t\t\t\t\t\t\t\tsearchable: true,\n\t\t\t\t\t\t\t\t\t...(current?.method === \"remote\" && consoles.some((c) => c.id === current.consoleId)\n\t\t\t\t\t\t\t\t\t\t? { initialValue: current.consoleId }\n\t\t\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t\t\t\toptions: consoles.map((c) => ({\n\t\t\t\t\t\t\t\t\t\tvalue: c.id,\n\t\t\t\t\t\t\t\t\t\tlabel: c.name,\n\t\t\t\t\t\t\t\t\t\tdescription: c.type === undefined ? c.id : `${c.type} · ${c.id}`,\n\t\t\t\t\t\t\t\t\t})),\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.pipe(mapPromptCancellation);\n\t\t\t\tif (consoles.length === 1) yield* interaction.output.info(`Using console ${only.name}.`);\n\t\t\t\tconst resolved = yield* resolve({ method: \"remote\", consoleId, apiKey }, \"entered values\");\n\t\t\t\tconst site = yield* pickSite(resolved.connection, current?.site);\n\t\t\t\treturn toConfig({ ...resolved, site });\n\t\t\t});\n\n\t\tconst configure = (_profileName: string, current?: UnifiNetworkAuthConfig) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst method = yield* interaction.prompt\n\t\t\t\t\t.select({\n\t\t\t\t\t\tmessage: \"How should Alchemy reach the console?\",\n\t\t\t\t\t\tinitialValue: current?.method ?? (\"local\" as const),\n\t\t\t\t\t\toptions: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvalue: \"local\" as const,\n\t\t\t\t\t\t\t\tlabel: \"Directly\",\n\t\t\t\t\t\t\t\tdescription: \"the console's or controller's URL on your network\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvalue: \"remote\" as const,\n\t\t\t\t\t\t\t\tlabel: \"Through ui.com\",\n\t\t\t\t\t\t\t\tdescription: \"the api.ui.com cloud connector, from anywhere\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t})\n\t\t\t\t\t.pipe(mapPromptCancellation);\n\t\t\t\tconst config = yield* method === \"local\" ? configureLocal(current) : configureRemote(current);\n\t\t\t\tyield* interaction.output.success(`${NAME}: credentials saved.`);\n\t\t\t\treturn config;\n\t\t\t});\n\n\t\treturn {\n\t\t\tconfigSchema: UnifiNetworkAuthConfigSchema,\n\t\t\tconfigure,\n\t\t\tconfigureWith,\n\t\t\tconfigureMethods,\n\t\t\tlogin: (_profileName: string, config: UnifiNetworkAuthConfig) => Effect.succeed(config),\n\t\t\tlogout: () => Effect.void,\n\t\t\tdetails,\n\t\t\tread: fromConfig,\n\t\t\treadEnvironment,\n\t\t\tenvironment,\n\t\t};\n\t}),\n);\n","import { deepEqual } from \"alchemy/Diff\";\nimport * as Redacted from \"effect/Redacted\";\n\n/**\n * Comparison of request bodies with what the console reports. Props are\n * authoritative: a resource's desired body is its props with explicit\n * defaults for anything unset, sent whole on every update, and the console's\n * object must equal it field for field.\n */\n\n/** `Omit` applied to each member of a union, keeping the discriminant. */\nexport type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\n/** `value` with every `Redacted` unwrapped, recursively, so passphrases compare by contents. */\nconst unwrap = (value: unknown): unknown => {\n\tif (Redacted.isRedacted(value)) return Redacted.value(value);\n\tif (Array.isArray(value)) return value.map(unwrap);\n\tif (typeof value === \"object\" && value !== null) {\n\t\treturn Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, unwrap(nested)]));\n\t}\n\treturn value;\n};\n\n/**\n * Whether the console's object is exactly the desired body: same fields,\n * same values, recursively (key order and `Redacted` wrappers aside; unset\n * fields must be absent on both sides). Leave read-only fields out of\n * `observed` first.\n */\nexport const isCurrent = (desired: unknown, observed: unknown) => deepEqual(unwrap(desired), unwrap(observed));\n\n/**\n * `value` with every array sorted, recursively, for fields whose arrays are\n * sets: the console keeps them in an order of its own (e.g. a firewall\n * policy's port list), so order mustn't count as a difference.\n */\nexport const sortArrays = (value: unknown): unknown => {\n\tif (Array.isArray(value)) {\n\t\treturn value.map(sortArrays).toSorted((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)));\n\t}\n\tif (typeof value === \"object\" && value !== null && !Redacted.isRedacted(value)) {\n\t\treturn Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, sortArrays(nested)]));\n\t}\n\treturn value;\n};\n\n/** `record` without `keys`, e.g. read-only fields the console rejects in request bodies. */\nexport const omitKeys = (record: object, keys: ReadonlyArray<string>): Record<string, unknown> =>\n\tObject.fromEntries(Object.entries(record).filter(([key]) => !keys.includes(key)));\n","import { isResolved } from \"alchemy/Diff\";\nimport * as Predicate from \"effect/Predicate\";\n\n/** A prop whose value depends on another resource's output, so isn't known while planning. */\nexport const UNKNOWN: unique symbol = Symbol(\"UnifiNetwork.unknown\");\n\n/**\n * A top-level prop of `diff`'s `news` (props that may still hold outputs of\n * resources not yet deployed): its value, `undefined` when unset, or\n * {@link UNKNOWN}.\n */\nexport const planned = (news: unknown, key: string): unknown => {\n\tif (typeof news !== \"object\" || news === null) return UNKNOWN;\n\tconst value = Predicate.hasProperty(news, key) ? news[key] : undefined;\n\treturn isResolved(value) ? value : UNKNOWN;\n};\n\n/** {@link planned} for string props such as `site`. */\nexport const plannedString = (news: unknown, key: string): string | undefined | typeof UNKNOWN => {\n\tconst value = planned(news, key);\n\treturn value === UNKNOWN || value === undefined || typeof value === \"string\" ? value : UNKNOWN;\n};\n","import * as Effect from \"effect/Effect\";\nimport type { UnifiNetworkConnection } from \"../Connection.ts\";\nimport type { UnifiNetworkCredentials } from \"../Credentials.ts\";\nimport type { UnifiNetworkReadCache } from \"../ReadCache.ts\";\n\n/** What resource lifecycle handlers and lookups need. `providers()` provides all of them. */\nexport type Services = UnifiNetworkCredentials | UnifiNetworkConnection | UnifiNetworkReadCache;\n\n/**\n * The provider's services, captured when the provider is built, as a\n * function that runs a handler with them. Handlers then don't depend on\n * what the stack's merged context happens to hold when they run.\n */\nexport const captureServices = Effect.map(\n\tEffect.context<Services>(),\n\t(services) =>\n\t\t<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, Exclude<R, Services>> =>\n\t\t\tEffect.provide(effect, services),\n);\n","import * as Context from \"effect/Context\";\nimport * as Deferred from \"effect/Deferred\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as Layer from \"effect/Layer\";\n\n/**\n * Per-run memo of API reads, so a plan touching many resources lists each\n * collection once instead of once per resource. One instance per\n * `providers()` build, i.e. per CLI run or test file. The value is only an\n * identity: entries live on each {@link Key}, keyed by it.\n */\nexport class UnifiNetworkReadCache extends Context.Service<UnifiNetworkReadCache, { readonly run: object }>()(\n\t\"UnifiNetwork::ReadCache\",\n) {\n\t/** Alchemy's tag for environment services, which lets stack bodies (and so lookups) require this one. */\n\tdeclare readonly kind: \"Environment\";\n}\n\nexport const layer = Layer.sync(UnifiNetworkReadCache, () => ({ run: {} }));\n\n/**\n * One kind of cached read, e.g. a resource type's list. `scope` arguments\n * separate its entries, e.g. by site ID.\n */\nexport interface Key<A, E> {\n\treadonly name: string;\n\treadonly entries: WeakMap<object, Map<string, Deferred.Deferred<A, E>>>;\n}\n\nexport const key = <A, E = never>(name: string): Key<A, E> => ({ name, entries: new WeakMap() });\n\nconst entriesFor = <A, E>(cacheKey: Key<A, E>, run: object) => {\n\tconst existing = cacheKey.entries.get(run);\n\tif (existing !== undefined) return existing;\n\tconst entries = new Map<string, Deferred.Deferred<A, E>>();\n\tcacheKey.entries.set(run, entries);\n\treturn entries;\n};\n\n/**\n * `load`'s result, shared by every caller in this run until invalidated.\n * Concurrent callers wait for one load. Failures aren't cached.\n */\nexport const cached = <A, E, R>(\n\tcacheKey: Key<A, E>,\n\tscope: string,\n\tload: Effect.Effect<A, E, R>,\n): Effect.Effect<A, E, R | UnifiNetworkReadCache> =>\n\tEffect.gen(function* () {\n\t\tconst { run } = yield* UnifiNetworkReadCache;\n\t\tconst entries = entriesFor(cacheKey, run);\n\t\tconst existing = entries.get(scope);\n\t\tif (existing !== undefined) return yield* Deferred.await(existing);\n\t\tconst deferred = Deferred.makeUnsafe<A, E>();\n\t\tentries.set(scope, deferred);\n\t\treturn yield* load.pipe(\n\t\t\tEffect.onExit((exit) => {\n\t\t\t\tif (Exit.isFailure(exit) && entries.get(scope) === deferred) entries.delete(scope);\n\t\t\t\treturn Deferred.done(deferred, exit);\n\t\t\t}),\n\t\t);\n\t});\n\n/** Drops an entry, so the next read loads afresh. */\nexport const invalidate = <A, E>(\n\tcacheKey: Key<A, E>,\n\tscope: string,\n): Effect.Effect<void, never, UnifiNetworkReadCache> =>\n\tEffect.gen(function* () {\n\t\tconst { run } = yield* UnifiNetworkReadCache;\n\t\tcacheKey.entries.get(run)?.delete(scope);\n\t});\n\n/** Runs a write, then invalidates the entry it affects, whether or not the write succeeded. */\nexport const invalidating =\n\t<A, E>(cacheKey: Key<A, E>, scope: string) =>\n\t<B, E2, R>(write: Effect.Effect<B, E2, R>): Effect.Effect<B, E2, R | UnifiNetworkReadCache> =>\n\t\tEffect.ensuring(write, invalidate(cacheKey, scope));\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport { UserFacingError } from \"alchemy/UserFacingError\";\nimport * as Data from \"effect/Data\";\nimport * as Effect from \"effect/Effect\";\nimport * as Stream from \"effect/Stream\";\nimport { withConnection } from \"./Connection.ts\";\nimport { UnifiNetworkCredentials } from \"./Credentials.ts\";\nimport * as ReadCache from \"./ReadCache.ts\";\n\n/** A UniFi site: a namespace within one console. */\nexport interface Site {\n\t/** The ID other Integration API calls take. */\n\treadonly id: string;\n\t/** Stable reference that survives renames: `default`, or a generated one like `x7k2m9q1`. */\n\treadonly internalReference: string;\n\treadonly name: string;\n}\n\nconst describeSites = (sites: ReadonlyArray<Site>) =>\n\tsites.map((site) => `${site.internalReference} (${site.name})`).join(\", \") || \"none\";\n\nexport class SiteNotFound extends Data.TaggedError(\"SiteNotFound\")<{\n\treadonly site: string;\n\treadonly available: ReadonlyArray<Site>;\n}> {\n\treadonly [UserFacingError]: true = true;\n\toverride get message() {\n\t\treturn `The console has no site \"${this.site}\". Its sites: ${describeSites(this.available)}. Point the profile's site, or the resource's site prop, at one of them.`;\n\t}\n}\n\nexport class SiteRequired extends Data.TaggedError(\"SiteRequired\")<{ readonly available: ReadonlyArray<Site> }> {\n\treadonly [UserFacingError]: true = true;\n\toverride get message() {\n\t\treturn `The console hosts several sites (${describeSites(this.available)}), so resources need one: set site in the profile (alchemy profile edit --reconfigure UnifiNetwork), or the resource's site prop.`;\n\t}\n}\n\nexport class AmbiguousSite extends Data.TaggedError(\"AmbiguousSite\")<{\n\treadonly site: string;\n\treadonly matches: ReadonlyArray<Site>;\n}> {\n\treadonly [UserFacingError]: true = true;\n\toverride get message() {\n\t\treturn `Several sites are named \"${this.site}\" (${describeSites(this.matches)}). Use the internal reference instead.`;\n\t}\n}\n\n/** A resource referenced another that lives in a different site. */\nexport class CrossSiteReference extends Data.TaggedError(\"CrossSiteReference\")<{\n\treadonly what: string;\n\treadonly referencedSiteId: string;\n\treadonly site: Site;\n}> {\n\treadonly [UserFacingError]: true = true;\n\toverride get message() {\n\t\treturn `The referenced ${this.what} is in another site (${this.referencedSiteId}), not ${this.site.internalReference} (${this.site.name}). UniFi objects can only reference objects in their own site.`;\n\t}\n}\n\n/** Fails if a reference carries a site ID (resources and lookups do) other than `site`'s. */\nexport const checkSameSite = (what: string, referencedSiteId: string | undefined, site: Site) =>\n\treferencedSiteId === undefined || referencedSiteId === site.id\n\t\t? Effect.void\n\t\t: Effect.fail(new CrossSiteReference({ what, referencedSiteId, site }));\n\nconst loadSites = withConnection(\n\tSdk.getSiteOverviewPage.items({}).pipe(\n\t\tStream.map(({ id, internalReference, name }): Site => ({ id, internalReference, name })),\n\t\tStream.runCollect,\n\t),\n);\n\nexport const sitesKey = ReadCache.key<ReadonlyArray<Site>, Effect.Error<typeof loadSites>>(\"sites\");\n\n/** The console's sites, listed once per run. */\nexport const listSites = ReadCache.cached(sitesKey, \"\", loadSites);\n\n/**\n * The site a resource belongs to: `site` (an ID, internal reference or\n * name), else the profile's site, else the console's only site. Fails,\n * listing the console's sites, when it doesn't exist or is ambiguous, so a\n * plan stops before changing anything.\n */\nexport const resolveSite = Effect.fn(\"UnifiNetwork.resolveSite\")(function* (site?: string) {\n\tconst requested = site ?? (yield* yield* UnifiNetworkCredentials).site;\n\tconst sites = yield* listSites;\n\tif (requested === undefined) {\n\t\tconst [only] = sites;\n\t\tif (only !== undefined && sites.length === 1) return only;\n\t\treturn yield* new SiteRequired({ available: sites });\n\t}\n\tconst byReference = sites.find((s) => s.id === requested || s.internalReference === requested);\n\tif (byReference !== undefined) return byReference;\n\tconst byName = sites.filter((s) => s.name === requested);\n\tconst [match] = byName;\n\tif (match !== undefined && byName.length === 1) return match;\n\tif (byName.length > 1) return yield* new AmbiguousSite({ site: requested, matches: byName });\n\treturn yield* new SiteNotFound({ site: requested, available: sites });\n});\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport { Unowned } from \"alchemy/AdoptPolicy\";\nimport type { Diff } from \"alchemy/Diff\";\nimport * as Provider from \"alchemy/Provider\";\nimport { Resource } from \"alchemy/Resource\";\nimport * as Effect from \"effect/Effect\";\nimport * as Stream from \"effect/Stream\";\nimport { withConnection } from \"./Connection.ts\";\nimport { type DistributiveOmit, isCurrent, omitKeys } from \"./internal/Fields.ts\";\nimport { plannedString, UNKNOWN } from \"./internal/Plan.ts\";\nimport { captureServices } from \"./internal/Services.ts\";\nimport type { Providers } from \"./Providers.ts\";\nimport * as ReadCache from \"./ReadCache.ts\";\nimport { resolveSite } from \"./Site.ts\";\n\n/**\n * Props are the Integration API's DNS policy body plus `site`. `type` picks\n * the kind: a record (`\"A_RECORD\"`, `\"AAAA_RECORD\"`, `\"CNAME_RECORD\"`,\n * `\"MX_RECORD\"`, `\"SRV_RECORD\"`, `\"TXT_RECORD\"`) or `\"FORWARD_DOMAIN\"`\n * (send a domain's queries to another DNS server).\n */\nexport type DnsPolicyProps = Sdk.CreateOrUpdateDNSPolicy & {\n\t/** Site to create the policy in. @default the profile's site, else the console's only site */\n\tsite?: string;\n};\n\nexport interface DnsPolicyAttributes {\n\tsiteId: string;\n\tdnsPolicyId: string;\n\ttype: DnsPolicyProps[\"type\"];\n\tdomain: string;\n\t/** The whole policy as the console reports it, so `alchemy drift` sees any change. */\n\tconfig: DistributiveOmit<Sdk.DNSPolicy, \"id\" | \"metadata\">;\n}\n\nexport interface DnsPolicy extends Resource<\n\t\"UnifiNetwork.DnsPolicy\",\n\tDnsPolicyProps,\n\tDnsPolicyAttributes,\n\tnever,\n\tProviders\n> {}\n\nconst loadPolicies = (siteId: string) => withConnection(Stream.runCollect(Sdk.getDnsPolicyPage.items({ siteId })));\nconst policiesKey = ReadCache.key<ReadonlyArray<Sdk.DNSPolicy>, Effect.Error<ReturnType<typeof loadPolicies>>>(\n\t\"dnsPolicies\",\n);\n\n/** A site's DNS policies, listed once per run. */\nexport const listDnsPolicies = (siteId: string) => ReadCache.cached(policiesKey, siteId, loadPolicies(siteId));\n\nconst toAttributes = (siteId: string, policy: Sdk.DNSPolicy): DnsPolicyAttributes => {\n\tconst { id, metadata: _metadata, ...config } = policy;\n\treturn { siteId, dnsPolicyId: id, type: policy.type, domain: policy.domain, config };\n};\n\n/** Fields the console reports but rejects in request bodies. */\nconst READ_ONLY = [\"id\", \"metadata\"];\n\n/** A policy by ID, from the run's cached list, which returns whole policies (one request for every policy in a plan). */\nconst getPolicy = (siteId: string, dnsPolicyId: string) =>\n\tlistDnsPolicies(siteId).pipe(Effect.map((policies) => policies.find((p) => p.id === dnsPolicyId)));\n\n/**\n * The existing policy `body` describes: the only one with its type and\n * domain, or, when a domain has several records of that type (e.g. two A\n * records), the only one of those whose other fields match too.\n */\nconst findExisting = (policies: ReadonlyArray<Sdk.DNSPolicy>, body: Sdk.CreateOrUpdateDNSPolicy) => {\n\tconst sameName = policies.filter((p) => p.type === body.type && p.domain === body.domain);\n\tconst candidates = sameName.length > 1 ? sameName.filter((p) => isCurrent(body, omitKeys(p, READ_ONLY))) : sameName;\n\tconst [policy] = candidates;\n\treturn candidates.length === 1 ? policy : undefined;\n};\n\n/**\n * A local DNS record or forwarding rule on the gateway. DNS policies have no\n * name, so existing ones are adopted with `--adopt` by type and domain (and,\n * when a domain has several records of that type, by their values).\n * Changing `type` updates the policy in place.\n *\n * @example\n * ```ts\n * import * as UnifiNetwork from \"@alchemy-unifi/network\";\n * import * as Alchemy from \"alchemy\";\n * import * as Effect from \"effect/Effect\";\n *\n * export default Alchemy.Stack(\n * \t\"HomeNetwork\",\n * \t{ providers: UnifiNetwork.providers(), state: UnifiNetwork.state() },\n * \tEffect.gen(function* () {\n * \t\tyield* UnifiNetwork.DnsPolicy(\"home-assistant\", {\n * \t\t\ttype: \"A_RECORD\",\n * \t\t\tenabled: true,\n * \t\t\tdomain: \"ha.home.arpa\",\n * \t\t\tipv4Address: \"192.168.2.20\",\n * \t\t\tttlSeconds: 300,\n * \t\t});\n * \t}),\n * );\n * ```\n */\nexport const DnsPolicy = Resource<DnsPolicy>(\"UnifiNetwork.DnsPolicy\");\n\nexport const DnsPolicyProvider = () =>\n\tProvider.effect(\n\t\tDnsPolicy,\n\t\tEffect.gen(function* () {\n\t\t\tconst run = yield* captureServices;\n\n\t\t\treturn DnsPolicy.Provider.of({\n\t\t\t\tlist: () =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite();\n\t\t\t\t\t\t\treturn (yield* listDnsPolicies(site.id)).map((policy) => toAttributes(site.id, policy));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tread: ({ olds, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (output !== undefined) {\n\t\t\t\t\t\t\t\tconst policy = yield* getPolicy(output.siteId, output.dnsPolicyId);\n\t\t\t\t\t\t\t\treturn policy === undefined ? undefined : toAttributes(output.siteId, policy);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst site = yield* resolveSite(olds.site);\n\t\t\t\t\t\t\tconst { site: _site, ...body } = olds;\n\t\t\t\t\t\t\tconst policy = findExisting(yield* listDnsPolicies(site.id), body);\n\t\t\t\t\t\t\treturn policy === undefined ? undefined : Unowned(toAttributes(site.id, policy));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdiff: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst siteProp = plannedString(news, \"site\");\n\t\t\t\t\t\t\tif (output === undefined || siteProp === UNKNOWN) return undefined;\n\t\t\t\t\t\t\tconst site = yield* resolveSite(siteProp);\n\t\t\t\t\t\t\tif (site.id !== output.siteId) return { action: \"replace\" } satisfies Diff;\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\treconcile: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite(news.site);\n\t\t\t\t\t\t\tconst { site: _site, ...body } = news;\n\t\t\t\t\t\t\tconst observed =\n\t\t\t\t\t\t\t\toutput !== undefined && output.siteId === site.id\n\t\t\t\t\t\t\t\t\t? yield* getPolicy(site.id, output.dnsPolicyId)\n\t\t\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\t\t\tconst write = ReadCache.invalidating(policiesKey, site.id);\n\t\t\t\t\t\t\tif (observed === undefined) {\n\t\t\t\t\t\t\t\tconst created = yield* withConnection(\n\t\t\t\t\t\t\t\t\tSdk.createDnsPolicy({ siteId: site.id, body }),\n\t\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\t\treturn toAttributes(site.id, created);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (isCurrent(body, omitKeys(observed, READ_ONLY))) return toAttributes(site.id, observed);\n\t\t\t\t\t\t\tconst updated = yield* withConnection(\n\t\t\t\t\t\t\t\tSdk.updateDnsPolicy({ siteId: site.id, dnsPolicyId: observed.id, body }),\n\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\treturn toAttributes(site.id, updated);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdelete: ({ output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\twithConnection(\n\t\t\t\t\t\t\tSdk.deleteDnsPolicy({ siteId: output.siteId, dnsPolicyId: output.dnsPolicyId }),\n\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.void),\n\t\t\t\t\t\t\tReadCache.invalidating(policiesKey, output.siteId),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t});\n\t\t}),\n\t);\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport * as Stream from \"effect/Stream\";\nimport { withConnection } from \"../Connection.ts\";\n\n/** A site's networks, uncached. */\nexport const loadNetworks = (siteId: string) =>\n\twithConnection(Stream.runCollect(Sdk.getNetworksOverviewPage.items({ siteId })));\n","import { UserFacingError } from \"alchemy/UserFacingError\";\nimport * as Data from \"effect/Data\";\nimport type { Site } from \"./Site.ts\";\n\n/** A `lookup` found nothing matching its query. */\nexport class LookupNotFound extends Data.TaggedError(\"LookupNotFound\")<{\n\treadonly resource: string;\n\treadonly query: string;\n\treadonly site: Site;\n}> {\n\treadonly [UserFacingError]: true = true;\n\toverride get message() {\n\t\treturn `No ${this.resource} matches ${this.query} in site ${this.site.internalReference} (${this.site.name}).`;\n\t}\n}\n\n/** A `lookup` matched several objects, e.g. networks sharing a name. */\nexport class LookupAmbiguous extends Data.TaggedError(\"LookupAmbiguous\")<{\n\treadonly resource: string;\n\treadonly query: string;\n\treadonly site: Site;\n\treadonly count: number;\n}> {\n\treadonly [UserFacingError]: true = true;\n\toverride get message() {\n\t\treturn `${this.count} ${this.resource}s match ${this.query} in site ${this.site.internalReference} (${this.site.name}). Use a more specific query.`;\n\t}\n}\n\nexport const describeQuery = (query: object) =>\n\tObject.entries(query)\n\t\t.filter(([key, value]) => key !== \"site\" && value !== undefined)\n\t\t.map(([key, value]) => `${key} ${JSON.stringify(value)}`)\n\t\t.join(\" and \");\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport { Unowned } from \"alchemy/AdoptPolicy\";\nimport type { Diff } from \"alchemy/Diff\";\nimport * as Provider from \"alchemy/Provider\";\nimport { Resource } from \"alchemy/Resource\";\nimport * as Effect from \"effect/Effect\";\nimport * as Stream from \"effect/Stream\";\nimport { withConnection } from \"./Connection.ts\";\nimport { loadNetworks } from \"./internal/Networks.ts\";\nimport { plannedString, UNKNOWN } from \"./internal/Plan.ts\";\nimport { captureServices } from \"./internal/Services.ts\";\nimport { describeQuery, LookupNotFound } from \"./Lookup.ts\";\nimport type { Providers } from \"./Providers.ts\";\nimport * as ReadCache from \"./ReadCache.ts\";\nimport { checkSameSite, resolveSite, type Site } from \"./Site.ts\";\n\n/** The zones every zone-based firewall has, by their API names. */\nexport type BuiltinFirewallZoneName = \"Internal\" | \"External\" | \"Gateway\" | \"Vpn\" | \"Hotspot\" | \"Dmz\";\n\n/** A built-in zone, resolved by name in whichever site the referencing resource is in. */\nexport interface BuiltinFirewallZone {\n\treadonly builtinFirewallZone: BuiltinFirewallZoneName;\n}\n\n/**\n * A zone: a `FirewallZone` resource, a {@link FirewallZone.lookup} result,\n * `{ firewallZoneId }`, or a built-in constant such as `FirewallZone.INTERNAL`.\n */\nexport type FirewallZoneReference =\n\t| { readonly firewallZoneId: string; readonly siteId?: string | undefined }\n\t| BuiltinFirewallZone;\n\nexport interface FirewallZoneProps {\n\t/** Site to create the zone in. @default the profile's site, else the console's only site */\n\tsite?: string;\n\tname: string;\n}\n\nexport interface FirewallZoneAttributes {\n\tsiteId: string;\n\tfirewallZoneId: string;\n\tname: string;\n}\n\nexport interface FirewallZone extends Resource<\n\t\"UnifiNetwork.FirewallZone\",\n\tFirewallZoneProps,\n\tFirewallZoneAttributes,\n\tnever,\n\tProviders\n> {}\n\nconst loadZones = (siteId: string) => withConnection(Stream.runCollect(Sdk.getFirewallZones.items({ siteId })));\nconst zonesKey = ReadCache.key<ReadonlyArray<Sdk.FirewallZone>, Effect.Error<ReturnType<typeof loadZones>>>(\n\t\"firewallZones\",\n);\n\n/** A site's zones, built-in ones included, listed once per run. */\nexport const listFirewallZones = (siteId: string) => ReadCache.cached(zonesKey, siteId, loadZones(siteId));\n\nconst toAttributes = (siteId: string, zone: Sdk.FirewallZone): FirewallZoneAttributes => ({\n\tsiteId,\n\tfirewallZoneId: zone.id,\n\tname: zone.name,\n});\n\nconst isBuiltin = (zone: Sdk.FirewallZone) => zone.metadata.origin === \"SYSTEM_DEFINED\";\n\n/** The zone ID a reference means in `site`. */\nexport const resolveFirewallZone = Effect.fn(\"UnifiNetwork.resolveFirewallZone\")(function* (\n\treference: FirewallZoneReference,\n\tsite: Site,\n) {\n\tif (\"firewallZoneId\" in reference) {\n\t\tyield* checkSameSite(\"firewall zone\", reference.siteId, site);\n\t\treturn reference.firewallZoneId;\n\t}\n\tconst zones = yield* listFirewallZones(site.id);\n\tconst zone = zones.find((z) => isBuiltin(z) && z.name === reference.builtinFirewallZone);\n\tif (zone === undefined) {\n\t\treturn yield* new LookupNotFound({\n\t\t\tresource: \"built-in firewall zone\",\n\t\t\tquery: `name \"${reference.builtinFirewallZone}\" (has the site been migrated to zone-based firewall?)`,\n\t\t\tsite,\n\t\t});\n\t}\n\treturn zone.id;\n});\n\nconst builtin = (name: BuiltinFirewallZoneName): BuiltinFirewallZone => ({ builtinFirewallZone: name });\n\n/**\n * A custom zone in the zone-based firewall. Networks join zones through\n * `Network`'s `zone` prop; the zone doesn't list its networks, so the two\n * resources never fight over membership.\n *\n * @example\n * ```ts\n * import * as UnifiNetwork from \"@alchemy-unifi/network\";\n * import * as Alchemy from \"alchemy\";\n * import * as Effect from \"effect/Effect\";\n *\n * export default Alchemy.Stack(\n * \t\"HomeNetwork\",\n * \t{ providers: UnifiNetwork.providers(), state: UnifiNetwork.state() },\n * \tEffect.gen(function* () {\n * \t\tconst cameras = yield* UnifiNetwork.FirewallZone(\"cameras\", { name: \"Cameras\" });\n * \t\t// Networks join zones from the network's side.\n * \t\tyield* UnifiNetwork.Network(\"cctv\", {\n * \t\t\tmanagement: \"GATEWAY\",\n * \t\t\tname: \"CCTV\",\n * \t\t\tvlanId: 30,\n * \t\t\tenabled: true,\n * \t\t\tcellularBackupEnabled: false,\n * \t\t\tinternetAccessEnabled: false,\n * \t\t\tisolationEnabled: true,\n * \t\t\tzone: cameras, // or a built-in zone, e.g. UnifiNetwork.FirewallZone.INTERNAL\n * \t\t\tipv4Configuration: { autoScaleEnabled: false, hostIpAddress: \"192.168.30.1\", prefixLength: 24 },\n * \t\t});\n * \t}),\n * );\n * ```\n */\nexport const FirewallZone = Resource<FirewallZone>(\"UnifiNetwork.FirewallZone\")({\n\tINTERNAL: builtin(\"Internal\"),\n\tEXTERNAL: builtin(\"External\"),\n\tGATEWAY: builtin(\"Gateway\"),\n\tVPN: builtin(\"Vpn\"),\n\tHOTSPOT: builtin(\"Hotspot\"),\n\tDMZ: builtin(\"Dmz\"),\n\t/**\n\t * Any zone in the site, built-in or custom, by name. Dies with a\n\t * user-facing `LookupNotFound` if there's none: stack bodies can't fail\n\t * with typed errors.\n\t */\n\tlookup: Effect.fn(\"UnifiNetwork.FirewallZone.lookup\")(function* (query: {\n\t\treadonly name: string;\n\t\treadonly site?: string;\n\t}) {\n\t\tconst site = yield* resolveSite(query.site);\n\t\tconst zone = (yield* listFirewallZones(site.id)).find((z) => z.name === query.name);\n\t\tif (zone === undefined) {\n\t\t\treturn yield* new LookupNotFound({ resource: \"firewall zone\", query: describeQuery(query), site });\n\t\t}\n\t\treturn toAttributes(site.id, zone);\n\t}, Effect.orDie),\n});\n\nconst getZone = (siteId: string, firewallZoneId: string) =>\n\twithConnection(Sdk.getFirewallZone({ siteId, firewallZoneId })).pipe(\n\t\tEffect.catchTag(\"NotFound\", () => Effect.undefined),\n\t);\n\n/**\n * The networks in a zone, from the networks' own `zoneId`s. Updates replace\n * a zone's member list, and the zone's `networkIds` lags a network joining\n * it by a fraction of a second, so sending those back could detach a\n * network that just joined.\n */\nconst currentMembers = (siteId: string, firewallZoneId: string) =>\n\tloadNetworks(siteId).pipe(\n\t\tEffect.map((networks) => networks.filter((n) => \"zoneId\" in n && n.zoneId === firewallZoneId).map((n) => n.id)),\n\t);\n\nexport const FirewallZoneProvider = () =>\n\tProvider.effect(\n\t\tFirewallZone,\n\t\tEffect.gen(function* () {\n\t\t\tconst run = yield* captureServices;\n\n\t\t\treturn FirewallZone.Provider.of({\n\t\t\t\tlist: () =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite();\n\t\t\t\t\t\t\tconst zones = yield* listFirewallZones(site.id);\n\t\t\t\t\t\t\treturn zones.filter((zone) => !isBuiltin(zone)).map((zone) => toAttributes(site.id, zone));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tread: ({ olds, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (output !== undefined) {\n\t\t\t\t\t\t\t\tconst zone = yield* getZone(output.siteId, output.firewallZoneId);\n\t\t\t\t\t\t\t\treturn zone === undefined ? undefined : toAttributes(output.siteId, zone);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst site = yield* resolveSite(olds.site);\n\t\t\t\t\t\t\tconst zone = (yield* listFirewallZones(site.id)).find(\n\t\t\t\t\t\t\t\t(z) => !isBuiltin(z) && z.name === olds.name,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn zone === undefined ? undefined : Unowned(toAttributes(site.id, zone));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdiff: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst siteProp = plannedString(news, \"site\");\n\t\t\t\t\t\t\tif (output === undefined || siteProp === UNKNOWN) return undefined;\n\t\t\t\t\t\t\tconst site = yield* resolveSite(siteProp);\n\t\t\t\t\t\t\tif (site.id !== output.siteId) return { action: \"replace\" } satisfies Diff;\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\treconcile: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite(news.site);\n\t\t\t\t\t\t\tconst observed =\n\t\t\t\t\t\t\t\toutput !== undefined && output.siteId === site.id\n\t\t\t\t\t\t\t\t\t? yield* getZone(site.id, output.firewallZoneId)\n\t\t\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\t\t\tconst write = ReadCache.invalidating(zonesKey, site.id);\n\t\t\t\t\t\t\tif (observed === undefined) {\n\t\t\t\t\t\t\t\tconst created = yield* withConnection(\n\t\t\t\t\t\t\t\t\tSdk.createFirewallZone({ siteId: site.id, name: news.name, networkIds: [] }),\n\t\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\t\treturn toAttributes(site.id, created);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (observed.name === news.name) return toAttributes(site.id, observed);\n\t\t\t\t\t\t\tconst updated = yield* withConnection(\n\t\t\t\t\t\t\t\tSdk.updateFirewallZone({\n\t\t\t\t\t\t\t\t\tsiteId: site.id,\n\t\t\t\t\t\t\t\t\tfirewallZoneId: observed.id,\n\t\t\t\t\t\t\t\t\tname: news.name,\n\t\t\t\t\t\t\t\t\tnetworkIds: yield* currentMembers(site.id, observed.id),\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\treturn toAttributes(site.id, updated);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdelete: ({ output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\twithConnection(\n\t\t\t\t\t\t\tSdk.deleteFirewallZone({ siteId: output.siteId, firewallZoneId: output.firewallZoneId }),\n\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.void),\n\t\t\t\t\t\t\tReadCache.invalidating(zonesKey, output.siteId),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t});\n\t\t}),\n\t);\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport { Unowned } from \"alchemy/AdoptPolicy\";\nimport type { Diff } from \"alchemy/Diff\";\nimport * as Provider from \"alchemy/Provider\";\nimport { Resource } from \"alchemy/Resource\";\nimport { UserFacingError } from \"alchemy/UserFacingError\";\nimport * as Data from \"effect/Data\";\nimport * as Effect from \"effect/Effect\";\nimport { withConnection } from \"./Connection.ts\";\nimport { type FirewallZoneReference, resolveFirewallZone } from \"./FirewallZone.ts\";\nimport { type DistributiveOmit, isCurrent, omitKeys } from \"./internal/Fields.ts\";\nimport { loadNetworks } from \"./internal/Networks.ts\";\nimport { planned, plannedString, UNKNOWN } from \"./internal/Plan.ts\";\nimport { captureServices } from \"./internal/Services.ts\";\nimport { describeQuery, LookupAmbiguous, LookupNotFound } from \"./Lookup.ts\";\nimport type { Providers } from \"./Providers.ts\";\nimport * as ReadCache from \"./ReadCache.ts\";\nimport { checkSameSite, resolveSite, type Site } from \"./Site.ts\";\n\ninterface SiteProp {\n\t/** Site to create the network in. @default the profile's site, else the console's only site */\n\tsite?: string;\n}\n\n/** A device, e.g. the switch routing a switch-managed network. */\nexport interface DeviceReference {\n\treadonly deviceId: string;\n}\n\n/** A network routed by the gateway: the usual kind, with DHCP, zones and internet access. */\nexport interface GatewayNetworkProps\n\textends Omit<Sdk.IntegrationGatewayManagedNetworkCreateUpdateDto, \"zoneId\">, SiteProp {\n\t/**\n\t * The network's firewall zone, e.g. `FirewallZone.INTERNAL` or a\n\t * `FirewallZone` resource. Required once the site uses the zone-based\n\t * firewall.\n\t */\n\tzone?: FirewallZoneReference;\n}\n\n/** A network routed by a layer-3 switch. */\nexport interface SwitchNetworkProps\n\textends Omit<Sdk.IntegrationSwitchManagedNetworkCreateUpdateDto, \"deviceId\">, SiteProp {\n\t/** The switch routing the network. */\n\tdevice: DeviceReference;\n}\n\n/** A VLAN that UniFi carries but doesn't route; something else on it does. */\nexport interface UnmanagedNetworkProps extends Sdk.IntegrationUnmanagedNetworkCreateUpdateDto, SiteProp {}\n\n/** Props are the Integration API's network body, plus `site` and references; `management` picks the kind. */\nexport type NetworkProps = GatewayNetworkProps | SwitchNetworkProps | UnmanagedNetworkProps;\n\n/** A network: a `Network` resource, a {@link Network.lookup} result, or `{ networkId }`. */\nexport interface NetworkReference {\n\treadonly networkId: string;\n\treadonly siteId?: string | undefined;\n\t/** Set by resources and lookups. Some objects must reference the default network differently. */\n\treadonly default?: boolean | undefined;\n}\n\n/** A change that would replace the site's default network, which can't be deleted. */\nexport class DefaultNetworkReplacement extends Data.TaggedError(\"DefaultNetworkReplacement\")<{\n\treadonly networkName: string;\n\treadonly reason: string;\n}> {\n\treadonly [UserFacingError]: true = true;\n\toverride get message() {\n\t\treturn `${this.networkName} is the site's default network, which can't be replaced (${this.reason}). It must stay gateway-managed in its site.`;\n\t}\n}\n\nexport interface NetworkAttributes {\n\tsiteId: string;\n\tnetworkId: string;\n\tname: string;\n\tvlanId: number;\n\tmanagement: NetworkProps[\"management\"];\n\t/** The network's firewall zone (gateway networks). */\n\tzoneId: string | undefined;\n\t/** Whether this is the site's default network, which is never deleted. */\n\tdefault: boolean;\n\t/** The whole network as the console reports it, so `alchemy drift` sees any change. */\n\tconfig: DistributiveOmit<Sdk.NetworkDetails, \"id\" | \"metadata\" | \"default\">;\n}\n\nexport interface Network extends Resource<\"UnifiNetwork.Network\", NetworkProps, NetworkAttributes, never, Providers> {}\n\nconst networksKey = ReadCache.key<ReadonlyArray<Sdk.NetworkOverview>, Effect.Error<ReturnType<typeof loadNetworks>>>(\n\t\"networks\",\n);\n\n/** A site's networks, listed once per run. */\nexport const listNetworks = (siteId: string) => ReadCache.cached(networksKey, siteId, loadNetworks(siteId));\n\nconst toAttributes = (siteId: string, network: Sdk.NetworkDetails): NetworkAttributes => {\n\tconst { id, metadata: _metadata, default: isDefault, ...config } = network;\n\treturn {\n\t\tsiteId,\n\t\tnetworkId: id,\n\t\tname: network.name,\n\t\tvlanId: network.vlanId,\n\t\tmanagement: network.management,\n\t\tzoneId: \"zoneId\" in network ? network.zoneId : undefined,\n\t\tdefault: isDefault,\n\t\tconfig,\n\t};\n};\n\n/** Fields the console reports but rejects in request bodies. */\nconst READ_ONLY = [\"id\", \"metadata\", \"default\"];\n\n/**\n * `body` with explicit values for the two unset fields that can't simply be\n * left out (both verified):\n * - `mdnsForwardingEnabled`: a `PUT` without it keeps the console's value\n * instead of resetting it, so the default (`true`, what the console sets on\n * create) is always sent;\n * - a DHCP server's `domainName`: the console reports `\"\"` when it's unset,\n * so without the default the body would never match and every deploy\n * would update it (and drift would report it) forever.\n */\nconst withDefaults = (body: Sdk.CreateOrUpdateNetwork): Sdk.CreateOrUpdateNetwork => {\n\tif (body.management !== \"GATEWAY\") return body;\n\tconst dhcp = body.ipv4Configuration.dhcpConfiguration;\n\treturn {\n\t\t...body,\n\t\tmdnsForwardingEnabled: body.mdnsForwardingEnabled ?? true,\n\t\tipv4Configuration: {\n\t\t\t...body.ipv4Configuration,\n\t\t\t...(dhcp?.mode === \"SERVER\" ? { dhcpConfiguration: { ...dhcp, domainName: dhcp.domainName ?? \"\" } } : {}),\n\t\t},\n\t};\n};\n\n/** The request body for `props`, with references resolved to IDs. */\nconst toBody = Effect.fn(function* (props: NetworkProps, site: Site) {\n\tswitch (props.management) {\n\t\tcase \"GATEWAY\": {\n\t\t\tconst { site: _site, zone, ...rest } = props;\n\t\t\tif (zone === undefined) return rest satisfies Sdk.CreateOrUpdateNetwork;\n\t\t\tconst zoneId = yield* resolveFirewallZone(zone, site);\n\t\t\treturn { ...rest, zoneId } satisfies Sdk.CreateOrUpdateNetwork;\n\t\t}\n\t\tcase \"SWITCH\": {\n\t\t\tconst { site: _site, device, ...rest } = props;\n\t\t\treturn { ...rest, deviceId: device.deviceId } satisfies Sdk.CreateOrUpdateNetwork;\n\t\t}\n\t\tcase \"UNMANAGED\": {\n\t\t\tconst { site: _site, ...rest } = props;\n\t\t\treturn rest satisfies Sdk.CreateOrUpdateNetwork;\n\t\t}\n\t}\n});\n\n/** The network ID a reference means in `site`. */\nexport const resolveNetwork = (reference: NetworkReference, site: Site) =>\n\tcheckSameSite(\"network\", reference.siteId, site).pipe(Effect.as(reference.networkId));\n\nconst getNetwork = (siteId: string, networkId: string) =>\n\twithConnection(Sdk.getNetworkDetails({ siteId, networkId })).pipe(\n\t\tEffect.catchTag(\"NotFound\", () => Effect.undefined),\n\t);\n\n/** The attributes of a network known to exist (e.g. from a list). */\nconst attributesOf = (siteId: string, networkId: string) =>\n\twithConnection(Sdk.getNetworkDetails({ siteId, networkId })).pipe(\n\t\tEffect.map((network) => toAttributes(siteId, network)),\n\t);\n\nconst isUserDefined = (network: Sdk.NetworkOverview) => network.metadata.origin === \"USER_DEFINED\";\n\n/**\n * A network (VLAN). `management` picks the kind: `\"GATEWAY\"` (routed by the\n * gateway), `\"SWITCH\"` (routed by a layer-3 switch) or `\"UNMANAGED\"`.\n * Changing it, or the site, replaces the network: the old one is deleted\n * first, since VLAN IDs must be unique.\n *\n * Props are authoritative: every update sends the whole network, with\n * explicit defaults for unset fields (`mdnsForwardingEnabled: true`, and a\n * DHCP server's `domainName: \"\"`), so changes made in the UI are put back.\n * The `config` attribute mirrors the console's object, so `alchemy drift`\n * sees them.\n * Existing networks are adopted by VLAN ID\n * with `--adopt` (and renamed to match), including the site's default\n * network (VLAN 1), which is never deleted or replaced.\n *\n * @example\n * ```ts\n * import * as UnifiNetwork from \"@alchemy-unifi/network\";\n * import * as Alchemy from \"alchemy\";\n * import * as Effect from \"effect/Effect\";\n *\n * export default Alchemy.Stack(\n * \t\"HomeNetwork\",\n * \t{ providers: UnifiNetwork.providers(), state: UnifiNetwork.state() },\n * \tEffect.gen(function* () {\n * \t\tyield* UnifiNetwork.Network(\"guest\", {\n * \t\t\tmanagement: \"GATEWAY\",\n * \t\t\tname: \"Guest\",\n * \t\t\tvlanId: 12,\n * \t\t\tenabled: true,\n * \t\t\tcellularBackupEnabled: false,\n * \t\t\tinternetAccessEnabled: true,\n * \t\t\tisolationEnabled: true,\n * \t\t\tzone: UnifiNetwork.FirewallZone.HOTSPOT,\n * \t\t\tipv4Configuration: { autoScaleEnabled: false, hostIpAddress: \"192.168.12.1\", prefixLength: 24 },\n * \t\t});\n * \t}),\n * );\n * ```\n */\nexport const Network = Resource<Network>(\"UnifiNetwork.Network\")({\n\t/**\n\t * A network in the site by name or VLAN ID. Dies with a user-facing\n\t * `LookupNotFound` or `LookupAmbiguous`: stack bodies can't fail with typed\n\t * errors.\n\t */\n\tlookup: Effect.fn(\"UnifiNetwork.Network.lookup\")(function* (\n\t\tquery: ({ readonly name: string } | { readonly vlanId: number }) & { readonly site?: string },\n\t) {\n\t\tconst site = yield* resolveSite(query.site);\n\t\tconst networks = (yield* listNetworks(site.id)).filter((n) =>\n\t\t\t\"vlanId\" in query ? n.vlanId === query.vlanId : n.name === query.name,\n\t\t);\n\t\tconst [network] = networks;\n\t\tif (network === undefined) {\n\t\t\treturn yield* new LookupNotFound({ resource: \"network\", query: describeQuery(query), site });\n\t\t}\n\t\tif (networks.length > 1) {\n\t\t\treturn yield* new LookupAmbiguous({\n\t\t\t\tresource: \"network\",\n\t\t\t\tquery: describeQuery(query),\n\t\t\t\tsite,\n\t\t\t\tcount: networks.length,\n\t\t\t});\n\t\t}\n\t\treturn yield* attributesOf(site.id, network.id);\n\t}, Effect.orDie),\n});\n\nexport const NetworkProvider = () =>\n\tProvider.effect(\n\t\tNetwork,\n\t\tEffect.gen(function* () {\n\t\t\tconst run = yield* captureServices;\n\n\t\t\treturn Network.Provider.of({\n\t\t\t\tlist: () =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite();\n\t\t\t\t\t\t\tconst networks = yield* listNetworks(site.id);\n\t\t\t\t\t\t\treturn yield* Effect.forEach(networks.filter(isUserDefined), (network) =>\n\t\t\t\t\t\t\t\tattributesOf(site.id, network.id),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tread: ({ olds, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (output !== undefined) {\n\t\t\t\t\t\t\t\tconst network = yield* getNetwork(output.siteId, output.networkId);\n\t\t\t\t\t\t\t\treturn network === undefined ? undefined : toAttributes(output.siteId, network);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst site = yield* resolveSite(olds.site);\n\t\t\t\t\t\t\t// VLAN IDs are unique per site, so the VLAN alone identifies the network, and adopting renames it.\n\t\t\t\t\t\t\tconst network = (yield* listNetworks(site.id)).find((n) => n.vlanId === olds.vlanId);\n\t\t\t\t\t\t\treturn network === undefined\n\t\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t\t: Unowned(yield* attributesOf(site.id, network.id));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdiff: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (output === undefined) return undefined;\n\t\t\t\t\t\t\tconst replace = (reason: string) =>\n\t\t\t\t\t\t\t\toutput.default\n\t\t\t\t\t\t\t\t\t? Effect.fail(new DefaultNetworkReplacement({ networkName: output.name, reason }))\n\t\t\t\t\t\t\t\t\t: Effect.succeed({ action: \"replace\", deleteFirst: true } satisfies Diff);\n\t\t\t\t\t\t\tconst management = planned(news, \"management\");\n\t\t\t\t\t\t\tif (management !== UNKNOWN && management !== output.management) {\n\t\t\t\t\t\t\t\treturn yield* replace(`its management would change to ${String(management)}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst siteProp = plannedString(news, \"site\");\n\t\t\t\t\t\t\tif (siteProp === UNKNOWN) return undefined;\n\t\t\t\t\t\t\tconst site = yield* resolveSite(siteProp);\n\t\t\t\t\t\t\tif (site.id !== output.siteId)\n\t\t\t\t\t\t\t\treturn yield* replace(`it would move to site ${site.internalReference}`);\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\treconcile: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite(news.site);\n\t\t\t\t\t\t\tconst body = withDefaults(yield* toBody(news, site));\n\t\t\t\t\t\t\tconst observed =\n\t\t\t\t\t\t\t\toutput !== undefined && output.siteId === site.id\n\t\t\t\t\t\t\t\t\t? yield* getNetwork(site.id, output.networkId)\n\t\t\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\t\t\tconst write = ReadCache.invalidating(networksKey, site.id);\n\t\t\t\t\t\t\tif (observed === undefined) {\n\t\t\t\t\t\t\t\tconst created = yield* withConnection(\n\t\t\t\t\t\t\t\t\tSdk.createNetwork({ siteId: site.id, body }),\n\t\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\t\treturn toAttributes(site.id, created);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (isCurrent(body, omitKeys(observed, READ_ONLY))) return toAttributes(site.id, observed);\n\t\t\t\t\t\t\tconst updated = yield* withConnection(\n\t\t\t\t\t\t\t\tSdk.updateNetwork({ siteId: site.id, networkId: observed.id, body }),\n\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\treturn toAttributes(site.id, updated);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdelete: ({ output }) =>\n\t\t\t\t\toutput.default\n\t\t\t\t\t\t? Effect.logInfo(`Leaving ${output.name}, the site's default network, in place.`)\n\t\t\t\t\t\t: run(\n\t\t\t\t\t\t\t\twithConnection(\n\t\t\t\t\t\t\t\t\tSdk.deleteNetwork({ siteId: output.siteId, networkId: output.networkId }),\n\t\t\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\t\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.void),\n\t\t\t\t\t\t\t\t\tReadCache.invalidating(networksKey, output.siteId),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t),\n\t\t\t});\n\t\t}),\n\t);\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport { Unowned } from \"alchemy/AdoptPolicy\";\nimport type { Diff } from \"alchemy/Diff\";\nimport * as Provider from \"alchemy/Provider\";\nimport { Resource } from \"alchemy/Resource\";\nimport { UserFacingError } from \"alchemy/UserFacingError\";\nimport * as Data from \"effect/Data\";\nimport * as Effect from \"effect/Effect\";\nimport * as Stream from \"effect/Stream\";\nimport { withConnection } from \"./Connection.ts\";\nimport { type DistributiveOmit, isCurrent, omitKeys } from \"./internal/Fields.ts\";\nimport { plannedString, UNKNOWN } from \"./internal/Plan.ts\";\nimport { captureServices } from \"./internal/Services.ts\";\nimport { describeQuery, LookupNotFound } from \"./Lookup.ts\";\nimport type { Providers } from \"./Providers.ts\";\nimport * as ReadCache from \"./ReadCache.ts\";\nimport { checkSameSite, resolveSite, type Site } from \"./Site.ts\";\n\n/**\n * Props are the Integration API's list body plus `site`. `type` picks what\n * it holds: `\"IPV4_ADDRESSES\"`, `\"IPV6_ADDRESSES\"` or `\"PORTS\"` (no MACs).\n */\nexport type TrafficMatchingListProps = Sdk.CreateOrUpdateTrafficMatchingList & {\n\t/** Site to create the list in. @default the profile's site, else the console's only site */\n\tsite?: string;\n};\n\n/** A list: a `TrafficMatchingList` resource, a {@link TrafficMatchingList.lookup} result, or `{ trafficMatchingListId }`. */\nexport interface TrafficMatchingListReference {\n\treadonly trafficMatchingListId: string;\n\treadonly siteId?: string | undefined;\n}\n\nexport interface TrafficMatchingListAttributes {\n\tsiteId: string;\n\ttrafficMatchingListId: string;\n\tname: string;\n\ttype: TrafficMatchingListProps[\"type\"];\n\t/** The whole list as the console reports it, so `alchemy drift` sees any change. */\n\tconfig: DistributiveOmit<Sdk.TrafficMatchingList, \"id\">;\n}\n\nexport interface TrafficMatchingList extends Resource<\n\t\"UnifiNetwork.TrafficMatchingList\",\n\tTrafficMatchingListProps,\n\tTrafficMatchingListAttributes,\n\tnever,\n\tProviders\n> {}\n\nconst loadLists = (siteId: string) => withConnection(Stream.runCollect(Sdk.getTrafficMatchingLists.items({ siteId })));\nconst listsKey = ReadCache.key<ReadonlyArray<Sdk.TrafficMatchingList>, Effect.Error<ReturnType<typeof loadLists>>>(\n\t\"trafficMatchingLists\",\n);\n\n/** A site's traffic-matching lists, listed once per run. */\nexport const listTrafficMatchingLists = (siteId: string) => ReadCache.cached(listsKey, siteId, loadLists(siteId));\n\nconst toAttributes = (siteId: string, list: Sdk.TrafficMatchingList): TrafficMatchingListAttributes => {\n\tconst { id, ...config } = list;\n\treturn { siteId, trafficMatchingListId: id, name: list.name, type: list.type, config };\n};\n\n/** Fields the console reports but rejects in request bodies. */\nconst READ_ONLY = [\"id\"];\n\n/** A list can't be deleted while something references it. */\nexport class TrafficMatchingListInUse extends Data.TaggedError(\"TrafficMatchingListInUse\")<{\n\treadonly listName: string;\n\t/** Names of the firewall policies referencing it, as far as they could be found. */\n\treadonly referencedBy: ReadonlyArray<string>;\n\treadonly detail: string;\n}> {\n\treadonly [UserFacingError]: true = true;\n\toverride get message() {\n\t\tconst by =\n\t\t\tthis.referencedBy.length > 0\n\t\t\t\t? `firewall policies ${this.referencedBy.map((name) => `\"${name}\"`).join(\", \")}`\n\t\t\t\t: `something the provider couldn't identify (${this.detail})`;\n\t\treturn `Can't delete traffic-matching list \"${this.listName}\": it's still referenced by ${by}. Those references aren't managed by this stack (e.g. made in the UI); remove them, then deploy again.`;\n\t}\n}\n\n/** User-defined firewall policies that mention `trafficMatchingListId` in a filter. */\nconst policiesReferencing = (siteId: string, trafficMatchingListId: string) =>\n\twithConnection(Stream.runCollect(Sdk.getFirewallPolicies.items({ siteId }))).pipe(\n\t\tEffect.map((policies) =>\n\t\t\tpolicies\n\t\t\t\t.filter(\n\t\t\t\t\t(p) =>\n\t\t\t\t\t\tp.metadata.origin === \"USER_DEFINED\" &&\n\t\t\t\t\t\tJSON.stringify([p.source, p.destination]).includes(trafficMatchingListId),\n\t\t\t\t)\n\t\t\t\t.map((p) => p.name),\n\t\t),\n\t\tEffect.orElseSucceed((): ReadonlyArray<string> => []),\n\t);\n\n/** The list ID a reference means in `site`. */\nexport const resolveTrafficMatchingList = (reference: TrafficMatchingListReference, site: Site) =>\n\tcheckSameSite(\"traffic-matching list\", reference.siteId, site).pipe(Effect.as(reference.trafficMatchingListId));\n\n/** A list by ID, from the run's cached lists, which come whole (one request for every list in a plan). */\nconst getList = (siteId: string, trafficMatchingListId: string) =>\n\tlistTrafficMatchingLists(siteId).pipe(Effect.map((lists) => lists.find((l) => l.id === trafficMatchingListId)));\n\n/**\n * A named list of IPv4 addresses, IPv6 addresses or ports, which firewall\n * policies reference instead of repeating values inline. Names are unique\n * per site, so existing lists are adopted by name with `--adopt`. Changing\n * `type` updates the list in place.\n *\n * @example\n * ```ts\n * import * as UnifiNetwork from \"@alchemy-unifi/network\";\n * import * as Alchemy from \"alchemy\";\n * import * as Effect from \"effect/Effect\";\n *\n * export default Alchemy.Stack(\n * \t\"HomeNetwork\",\n * \t{ providers: UnifiNetwork.providers(), state: UnifiNetwork.state() },\n * \tEffect.gen(function* () {\n * \t\tconst haHosts = yield* UnifiNetwork.TrafficMatchingList(\"ha-hosts\", {\n * \t\t\ttype: \"IPV4_ADDRESSES\",\n * \t\t\tname: \"Home Assistant hosts\",\n * \t\t\titems: [{ type: \"IP_ADDRESS\", value: \"192.168.2.20\" }],\n * \t\t});\n * \t\t// Reference it from a policy's filter: { type: \"TRAFFIC_MATCHING_LIST\", matchOpposite: false, trafficMatchingList: haHosts }\n * \t}),\n * );\n * ```\n */\nexport const TrafficMatchingList = Resource<TrafficMatchingList>(\"UnifiNetwork.TrafficMatchingList\")({\n\t/**\n\t * A list in the site by name. Dies with a user-facing `LookupNotFound`:\n\t * stack bodies can't fail with typed errors.\n\t */\n\tlookup: Effect.fn(\"UnifiNetwork.TrafficMatchingList.lookup\")(function* (query: {\n\t\treadonly name: string;\n\t\treadonly site?: string;\n\t}) {\n\t\tconst site = yield* resolveSite(query.site);\n\t\tconst list = (yield* listTrafficMatchingLists(site.id)).find((l) => l.name === query.name);\n\t\tif (list === undefined) {\n\t\t\treturn yield* new LookupNotFound({ resource: \"traffic-matching list\", query: describeQuery(query), site });\n\t\t}\n\t\treturn toAttributes(site.id, list);\n\t}, Effect.orDie),\n});\n\nexport const TrafficMatchingListProvider = () =>\n\tProvider.effect(\n\t\tTrafficMatchingList,\n\t\tEffect.gen(function* () {\n\t\t\tconst run = yield* captureServices;\n\n\t\t\treturn TrafficMatchingList.Provider.of({\n\t\t\t\tlist: () =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite();\n\t\t\t\t\t\t\treturn (yield* listTrafficMatchingLists(site.id)).map((list) =>\n\t\t\t\t\t\t\t\ttoAttributes(site.id, list),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tread: ({ olds, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (output !== undefined) {\n\t\t\t\t\t\t\t\tconst list = yield* getList(output.siteId, output.trafficMatchingListId);\n\t\t\t\t\t\t\t\treturn list === undefined ? undefined : toAttributes(output.siteId, list);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst site = yield* resolveSite(olds.site);\n\t\t\t\t\t\t\tconst list = (yield* listTrafficMatchingLists(site.id)).find((l) => l.name === olds.name);\n\t\t\t\t\t\t\treturn list === undefined ? undefined : Unowned(toAttributes(site.id, list));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdiff: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst siteProp = plannedString(news, \"site\");\n\t\t\t\t\t\t\tif (output === undefined || siteProp === UNKNOWN) return undefined;\n\t\t\t\t\t\t\tconst site = yield* resolveSite(siteProp);\n\t\t\t\t\t\t\tif (site.id !== output.siteId) return { action: \"replace\" } satisfies Diff;\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\treconcile: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite(news.site);\n\t\t\t\t\t\t\tconst { site: _site, ...body } = news;\n\t\t\t\t\t\t\tconst observed =\n\t\t\t\t\t\t\t\toutput !== undefined && output.siteId === site.id\n\t\t\t\t\t\t\t\t\t? yield* getList(site.id, output.trafficMatchingListId)\n\t\t\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\t\t\tconst write = ReadCache.invalidating(listsKey, site.id);\n\t\t\t\t\t\t\tif (observed === undefined) {\n\t\t\t\t\t\t\t\tconst created = yield* withConnection(\n\t\t\t\t\t\t\t\t\tSdk.createTrafficMatchingList({ siteId: site.id, body }),\n\t\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\t\treturn toAttributes(site.id, created);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (isCurrent(body, omitKeys(observed, READ_ONLY))) return toAttributes(site.id, observed);\n\t\t\t\t\t\t\tconst updated = yield* withConnection(\n\t\t\t\t\t\t\t\tSdk.updateTrafficMatchingList({\n\t\t\t\t\t\t\t\t\tsiteId: site.id,\n\t\t\t\t\t\t\t\t\ttrafficMatchingListId: observed.id,\n\t\t\t\t\t\t\t\t\tbody,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\treturn toAttributes(site.id, updated);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdelete: ({ output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\twithConnection(\n\t\t\t\t\t\t\tSdk.deleteTrafficMatchingList({\n\t\t\t\t\t\t\t\tsiteId: output.siteId,\n\t\t\t\t\t\t\t\ttrafficMatchingListId: output.trafficMatchingListId,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\t\t\tEffect.catchTags({\n\t\t\t\t\t\t\t\tNotFound: () => Effect.void,\n\t\t\t\t\t\t\t\t// 422 \"Selected traffic matching list is referenced by other resources\".\n\t\t\t\t\t\t\t\tUnprocessableEntity: (error) =>\n\t\t\t\t\t\t\t\t\tpoliciesReferencing(output.siteId, output.trafficMatchingListId).pipe(\n\t\t\t\t\t\t\t\t\t\tEffect.flatMap((referencedBy) =>\n\t\t\t\t\t\t\t\t\t\t\tEffect.fail(\n\t\t\t\t\t\t\t\t\t\t\t\tnew TrafficMatchingListInUse({\n\t\t\t\t\t\t\t\t\t\t\t\t\tlistName: output.name,\n\t\t\t\t\t\t\t\t\t\t\t\t\treferencedBy,\n\t\t\t\t\t\t\t\t\t\t\t\t\tdetail: error.message,\n\t\t\t\t\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tReadCache.invalidating(listsKey, output.siteId),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t});\n\t\t}),\n\t);\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport { Unowned } from \"alchemy/AdoptPolicy\";\nimport type { Diff } from \"alchemy/Diff\";\nimport * as Provider from \"alchemy/Provider\";\nimport { Resource } from \"alchemy/Resource\";\nimport * as Effect from \"effect/Effect\";\nimport * as Stream from \"effect/Stream\";\nimport { withConnection } from \"./Connection.ts\";\nimport { type FirewallZoneReference, resolveFirewallZone } from \"./FirewallZone.ts\";\nimport { isCurrent, omitKeys, sortArrays } from \"./internal/Fields.ts\";\nimport { plannedString, UNKNOWN } from \"./internal/Plan.ts\";\nimport { captureServices } from \"./internal/Services.ts\";\nimport { describeQuery, LookupAmbiguous, LookupNotFound } from \"./Lookup.ts\";\nimport { type NetworkReference, resolveNetwork } from \"./Network.ts\";\nimport type { Providers } from \"./Providers.ts\";\nimport * as ReadCache from \"./ReadCache.ts\";\nimport { resolveSite, type Site } from \"./Site.ts\";\nimport { resolveTrafficMatchingList, type TrafficMatchingListReference } from \"./TrafficMatchingList.ts\";\n\n/** Match the addresses or ports in a traffic-matching list (a `TrafficMatchingList` resource or reference). */\nexport interface TrafficMatchingListFilter {\n\treadonly type: \"TRAFFIC_MATCHING_LIST\";\n\t/** Match everything except the list's entries. */\n\treadonly matchOpposite: boolean;\n\treadonly trafficMatchingList: TrafficMatchingListReference;\n}\n\n/** IP addresses, ranges or subnets to match: inline, or from a list. */\nexport type IpAddressFilter = Sdk.IntegrationFirewallPolicySpecificIpAddressFilterDto | TrafficMatchingListFilter;\n\n/** Ports or port ranges to match: inline, or from a list. */\nexport type PortFilter = Sdk.IntegrationFirewallPolicyPortValueFilterDto | TrafficMatchingListFilter;\n\n/** Networks to match (`Network` resources or references). */\nexport interface NetworkFilter {\n\t/** Match every network except these. */\n\treadonly matchOpposite: boolean;\n\treadonly networks: ReadonlyArray<NetworkReference>;\n}\n\n/** An API traffic filter with its ID fields replaced by references. */\ntype WithReferences<F> = {\n\t[K in keyof F]: K extends \"ipAddressFilter\"\n\t\t? IpAddressFilter\n\t\t: K extends \"portFilter\"\n\t\t\t? PortFilter\n\t\t\t: K extends \"networkFilter\"\n\t\t\t\t? NetworkFilter\n\t\t\t\t: F[K];\n};\ntype Distribute<F> = F extends unknown ? WithReferences<F> : never;\n\n/** What a policy matches at its source, besides the zone. `type` picks the kind. */\nexport type FirewallPolicySourceFilter = Distribute<Sdk.FirewallPolicySourceTrafficFilter>;\n\n/** What a policy matches at its destination, besides the zone. `type` picks the kind. */\nexport type FirewallPolicyDestinationFilter = Distribute<Sdk.FirewallPolicyDestinationTrafficFilter>;\n\nexport interface FirewallPolicyEndpoint<Filter> {\n\t/** A `FirewallZone` resource or lookup, `{ firewallZoneId }`, or a built-in constant such as `FirewallZone.INTERNAL`. */\n\treadonly zone: FirewallZoneReference;\n\t/** Narrows the zone's traffic. Unset matches all of it. */\n\treadonly trafficFilter?: Filter;\n}\n\n/**\n * Props are the Integration API's policy body, with zones, networks and\n * traffic-matching lists as references, plus `site`.\n *\n * Props are authoritative, like every resource's: leaving out\n * `trafficFilter`, `connectionStateFilter`, `ipsecFilter`, `schedule` or\n * `description` means none, even if the policy has one in the console.\n */\nexport interface FirewallPolicyProps extends Omit<\n\tSdk.CreateFirewallPolicyRequest,\n\t\"siteId\" | \"source\" | \"destination\"\n> {\n\t/** Site to create the policy in. @default the profile's site, else the console's only site */\n\tsite?: string;\n\tsource: FirewallPolicyEndpoint<FirewallPolicySourceFilter>;\n\tdestination: FirewallPolicyEndpoint<FirewallPolicyDestinationFilter>;\n}\n\n/** A policy: a `FirewallPolicy` resource, a {@link FirewallPolicy.lookup} result, or `{ firewallPolicyId }`. */\nexport interface FirewallPolicyReference {\n\treadonly firewallPolicyId: string;\n\treadonly siteId?: string | undefined;\n}\n\nexport interface FirewallPolicyAttributes {\n\tsiteId: string;\n\tfirewallPolicyId: string;\n\tname: string;\n\tsourceFirewallZoneId: string;\n\tdestinationFirewallZoneId: string;\n\t/**\n\t * The whole policy as the console reports it, so `alchemy drift` sees any\n\t * change; without its `index`, which moves whenever its ordering changes.\n\t */\n\tconfig: Omit<Sdk.FirewallPolicy, \"id\" | \"metadata\" | \"index\">;\n}\n\nexport interface FirewallPolicy extends Resource<\n\t\"UnifiNetwork.FirewallPolicy\",\n\tFirewallPolicyProps,\n\tFirewallPolicyAttributes,\n\tnever,\n\tProviders\n> {}\n\nconst loadPolicies = (siteId: string) => withConnection(Stream.runCollect(Sdk.getFirewallPolicies.items({ siteId })));\nconst policiesKey = ReadCache.key<ReadonlyArray<Sdk.FirewallPolicy>, Effect.Error<ReturnType<typeof loadPolicies>>>(\n\t\"firewallPolicies\",\n);\n\n/**\n * A site's firewall policies, listed once per run: all of them, including\n * system-defined ones and ones UniFi derives from UI Objects.\n */\nexport const listFirewallPolicies = (siteId: string) => ReadCache.cached(policiesKey, siteId, loadPolicies(siteId));\n\n/** Policies the user made, as opposed to system-defined ones and ones UniFi derives (from UI Objects, say), which the provider never touches. */\nexport const isUserDefined = (policy: Sdk.FirewallPolicy) => policy.metadata.origin === \"USER_DEFINED\";\n\nconst toAttributes = (siteId: string, policy: Sdk.FirewallPolicy): FirewallPolicyAttributes => {\n\tconst { id, metadata: _metadata, index: _index, ...config } = policy;\n\treturn {\n\t\tsiteId,\n\t\tfirewallPolicyId: id,\n\t\tname: policy.name,\n\t\tsourceFirewallZoneId: policy.source.zoneId,\n\t\tdestinationFirewallZoneId: policy.destination.zoneId,\n\t\tconfig,\n\t};\n};\n\n/** Fields the console reports but rejects in request bodies, or that change without the policy being edited. */\nconst READ_ONLY = [\"id\", \"metadata\", \"index\"];\n\nconst resolvePortFilter = Effect.fn(function* (filter: PortFilter, site: Site) {\n\tif (filter.type === \"PORTS\") return filter satisfies Sdk.FirewallPolicyPortFilter;\n\tconst trafficMatchingListId = yield* resolveTrafficMatchingList(filter.trafficMatchingList, site);\n\treturn {\n\t\ttype: \"TRAFFIC_MATCHING_LIST\",\n\t\tmatchOpposite: filter.matchOpposite,\n\t\ttrafficMatchingListId,\n\t} satisfies Sdk.FirewallPolicyPortFilter;\n});\n\nconst resolveIpAddressFilter = Effect.fn(function* (filter: IpAddressFilter, site: Site) {\n\tif (filter.type === \"IP_ADDRESSES\") return filter satisfies Sdk.FirewallPolicyIPAddressFilter;\n\tconst trafficMatchingListId = yield* resolveTrafficMatchingList(filter.trafficMatchingList, site);\n\treturn {\n\t\ttype: \"TRAFFIC_MATCHING_LIST\",\n\t\tmatchOpposite: filter.matchOpposite,\n\t\ttrafficMatchingListId,\n\t} satisfies Sdk.FirewallPolicyIPAddressFilter;\n});\n\nconst resolveNetworkFilter = (filter: NetworkFilter, site: Site) =>\n\tEffect.forEach(filter.networks, (network) => resolveNetwork(network, site)).pipe(\n\t\tEffect.map((networkIds): Sdk.FirewallPolicyNetworkFilter => ({\n\t\t\tmatchOpposite: filter.matchOpposite,\n\t\t\tnetworkIds,\n\t\t})),\n\t);\n\n/** `filter` with its optional `portFilter` resolved (left out when unset). */\nconst withPortFilter = Effect.fn(function* <F extends { readonly portFilter?: PortFilter }>(filter: F, site: Site) {\n\tconst { portFilter, ...rest } = filter;\n\tif (portFilter === undefined) return rest;\n\treturn { ...rest, portFilter: yield* resolvePortFilter(portFilter, site) };\n});\n\nconst resolveSourceFilter = Effect.fn(function* (filter: FirewallPolicySourceFilter, site: Site) {\n\tswitch (filter.type) {\n\t\tcase \"IP_ADDRESS\": {\n\t\t\tconst { ipAddressFilter, ...rest } = yield* withPortFilter(filter, site);\n\t\t\treturn {\n\t\t\t\t...rest,\n\t\t\t\tipAddressFilter: yield* resolveIpAddressFilter(ipAddressFilter, site),\n\t\t\t} satisfies Sdk.FirewallPolicySourceTrafficFilter;\n\t\t}\n\t\tcase \"NETWORK\": {\n\t\t\tconst { networkFilter, ...rest } = yield* withPortFilter(filter, site);\n\t\t\treturn {\n\t\t\t\t...rest,\n\t\t\t\tnetworkFilter: yield* resolveNetworkFilter(networkFilter, site),\n\t\t\t} satisfies Sdk.FirewallPolicySourceTrafficFilter;\n\t\t}\n\t\tcase \"PORT\":\n\t\t\treturn {\n\t\t\t\t...filter,\n\t\t\t\tportFilter: yield* resolvePortFilter(filter.portFilter, site),\n\t\t\t} satisfies Sdk.FirewallPolicySourceTrafficFilter;\n\t\tcase \"IPV6_IID\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicySourceTrafficFilter;\n\t\tcase \"MAC_ADDRESS\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicySourceTrafficFilter;\n\t\tcase \"REGION\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicySourceTrafficFilter;\n\t\tcase \"SITE_TO_SITE_VPN_TUNNEL\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicySourceTrafficFilter;\n\t\tcase \"VPN_SERVER\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicySourceTrafficFilter;\n\t}\n});\n\nconst resolveDestinationFilter = Effect.fn(function* (filter: FirewallPolicyDestinationFilter, site: Site) {\n\tswitch (filter.type) {\n\t\tcase \"IP_ADDRESS\": {\n\t\t\tconst { ipAddressFilter, ...rest } = yield* withPortFilter(filter, site);\n\t\t\treturn {\n\t\t\t\t...rest,\n\t\t\t\tipAddressFilter: yield* resolveIpAddressFilter(ipAddressFilter, site),\n\t\t\t} satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t\t}\n\t\tcase \"NETWORK\": {\n\t\t\tconst { networkFilter, ...rest } = yield* withPortFilter(filter, site);\n\t\t\treturn {\n\t\t\t\t...rest,\n\t\t\t\tnetworkFilter: yield* resolveNetworkFilter(networkFilter, site),\n\t\t\t} satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t\t}\n\t\tcase \"PORT\":\n\t\t\treturn {\n\t\t\t\t...filter,\n\t\t\t\tportFilter: yield* resolvePortFilter(filter.portFilter, site),\n\t\t\t} satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t\tcase \"APPLICATION\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t\tcase \"APPLICATION_CATEGORY\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t\tcase \"DOMAIN\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t\tcase \"IPV6_IID\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t\tcase \"REGION\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t\tcase \"SITE_TO_SITE_VPN_TUNNEL\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t\tcase \"VPN_SERVER\":\n\t\t\treturn (yield* withPortFilter(filter, site)) satisfies Sdk.FirewallPolicyDestinationTrafficFilter;\n\t}\n});\n\n/** The request body for `props` (without `siteId`), with references resolved to IDs. */\nconst toBody = Effect.fn(function* (props: FirewallPolicyProps, site: Site) {\n\tconst { site: _site, source, destination, ...rest } = props;\n\tconst sourceFilter = source.trafficFilter;\n\tconst destinationFilter = destination.trafficFilter;\n\treturn {\n\t\t...rest,\n\t\tsource: {\n\t\t\tzoneId: yield* resolveFirewallZone(source.zone, site),\n\t\t\t...(sourceFilter === undefined ? {} : { trafficFilter: yield* resolveSourceFilter(sourceFilter, site) }),\n\t\t},\n\t\tdestination: {\n\t\t\tzoneId: yield* resolveFirewallZone(destination.zone, site),\n\t\t\t...(destinationFilter === undefined\n\t\t\t\t? {}\n\t\t\t\t: { trafficFilter: yield* resolveDestinationFilter(destinationFilter, site) }),\n\t\t},\n\t} satisfies Omit<Sdk.CreateFirewallPolicyRequest, \"siteId\">;\n});\n\n/**\n * A policy by ID, from the run's cached list: the list returns whole\n * policies, so a plan reading every policy costs one request, not one each\n * (this matters through the ui.com connector's rate limit).\n */\nconst getPolicy = (siteId: string, firewallPolicyId: string) =>\n\tlistFirewallPolicies(siteId).pipe(Effect.map((policies) => policies.find((p) => p.id === firewallPolicyId)));\n\n/** User-defined policies named `name` from `source` to `destination`. Names aren't unique, even within a zone pair. */\nconst findByName = Effect.fn(function* (\n\tsite: Site,\n\tname: string,\n\tsource: FirewallZoneReference | undefined,\n\tdestination: FirewallZoneReference | undefined,\n) {\n\tconst sourceId = source === undefined ? undefined : yield* resolveFirewallZone(source, site);\n\tconst destinationId = destination === undefined ? undefined : yield* resolveFirewallZone(destination, site);\n\treturn (yield* listFirewallPolicies(site.id)).filter(\n\t\t(p) =>\n\t\t\tisUserDefined(p) &&\n\t\t\tp.name === name &&\n\t\t\t(sourceId === undefined || p.source.zoneId === sourceId) &&\n\t\t\t(destinationId === undefined || p.destination.zoneId === destinationId),\n\t);\n});\n\n/**\n * A zone-based firewall policy: allow, block or reject traffic from one\n * zone to another, optionally narrowed by traffic filters. Changing its\n * zones updates it in place and moves it to the new zone pair's ordering\n * (see `FirewallPolicyOrdering`).\n *\n * Existing user-defined policies are adopted with `--adopt` by name and zone\n * pair (names aren't unique, so several matches adopt nothing). Policies\n * UniFi derives, e.g. from UI Objects, are invisible: never listed, adopted\n * or ordered.\n *\n * @example\n * ```ts\n * import * as UnifiNetwork from \"@alchemy-unifi/network\";\n * import * as Alchemy from \"alchemy\";\n * import * as Effect from \"effect/Effect\";\n *\n * export default Alchemy.Stack(\n * \t\"HomeNetwork\",\n * \t{ providers: UnifiNetwork.providers(), state: UnifiNetwork.state() },\n * \tEffect.gen(function* () {\n * \t\tconst iot = yield* UnifiNetwork.FirewallZone(\"iot-zone\", { name: \"IoT\" });\n * \t\tyield* UnifiNetwork.FirewallPolicy(\"block-iot-to-internal\", {\n * \t\t\tname: \"Block IoT to Internal\",\n * \t\t\tenabled: true,\n * \t\t\taction: { type: \"BLOCK\" },\n * \t\t\tsource: { zone: iot },\n * \t\t\tdestination: { zone: UnifiNetwork.FirewallZone.INTERNAL },\n * \t\t\tipProtocolScope: { ipVersion: \"IPV4_AND_IPV6\" },\n * \t\t\tloggingEnabled: false,\n * \t\t});\n * \t}),\n * );\n * ```\n */\nexport const FirewallPolicy = Resource<FirewallPolicy>(\"UnifiNetwork.FirewallPolicy\")({\n\t/**\n\t * A user-defined policy by name, optionally narrowed to a zone pair. Dies\n\t * with a user-facing `LookupNotFound` or `LookupAmbiguous`: stack bodies\n\t * can't fail with typed errors.\n\t */\n\tlookup: Effect.fn(\"UnifiNetwork.FirewallPolicy.lookup\")(function* (query: {\n\t\treadonly name: string;\n\t\treadonly source?: FirewallZoneReference;\n\t\treadonly destination?: FirewallZoneReference;\n\t\treadonly site?: string;\n\t}) {\n\t\tconst site = yield* resolveSite(query.site);\n\t\tconst policies = yield* findByName(site, query.name, query.source, query.destination);\n\t\tconst [policy] = policies;\n\t\tconst described = describeQuery({ name: query.name });\n\t\tif (policy === undefined) {\n\t\t\treturn yield* new LookupNotFound({ resource: \"firewall policy\", query: described, site });\n\t\t}\n\t\tif (policies.length > 1) {\n\t\t\treturn yield* new LookupAmbiguous({\n\t\t\t\tresource: \"firewall policy\",\n\t\t\t\tquery: described,\n\t\t\t\tsite,\n\t\t\t\tcount: policies.length,\n\t\t\t});\n\t\t}\n\t\treturn toAttributes(site.id, policy);\n\t}, Effect.orDie),\n});\n\nexport const FirewallPolicyProvider = () =>\n\tProvider.effect(\n\t\tFirewallPolicy,\n\t\tEffect.gen(function* () {\n\t\t\tconst run = yield* captureServices;\n\n\t\t\treturn FirewallPolicy.Provider.of({\n\t\t\t\tlist: () =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite();\n\t\t\t\t\t\t\tconst policies = yield* listFirewallPolicies(site.id);\n\t\t\t\t\t\t\treturn policies.filter(isUserDefined).map((policy) => toAttributes(site.id, policy));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tread: ({ olds, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (output !== undefined) {\n\t\t\t\t\t\t\t\tconst policy = yield* getPolicy(output.siteId, output.firewallPolicyId);\n\t\t\t\t\t\t\t\treturn policy === undefined || !isUserDefined(policy)\n\t\t\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t\t\t: toAttributes(output.siteId, policy);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst site = yield* resolveSite(olds.site);\n\t\t\t\t\t\t\tconst matching = yield* findByName(\n\t\t\t\t\t\t\t\tsite,\n\t\t\t\t\t\t\t\tolds.name,\n\t\t\t\t\t\t\t\tolds.source.zone,\n\t\t\t\t\t\t\t\tolds.destination.zone,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconst [policy] = matching;\n\t\t\t\t\t\t\treturn policy === undefined || matching.length > 1\n\t\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t\t: Unowned(toAttributes(site.id, policy));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdiff: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst siteProp = plannedString(news, \"site\");\n\t\t\t\t\t\t\tif (output === undefined || siteProp === UNKNOWN) return undefined;\n\t\t\t\t\t\t\tconst site = yield* resolveSite(siteProp);\n\t\t\t\t\t\t\tif (site.id !== output.siteId) return { action: \"replace\" } satisfies Diff;\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\treconcile: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite(news.site);\n\t\t\t\t\t\t\tconst body = yield* toBody(news, site);\n\t\t\t\t\t\t\tconst observed =\n\t\t\t\t\t\t\t\toutput !== undefined && output.siteId === site.id\n\t\t\t\t\t\t\t\t\t? yield* getPolicy(site.id, output.firewallPolicyId)\n\t\t\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\t\t\tconst write = ReadCache.invalidating(policiesKey, site.id);\n\t\t\t\t\t\t\tif (observed === undefined) {\n\t\t\t\t\t\t\t\tconst created = yield* withConnection(\n\t\t\t\t\t\t\t\t\tSdk.createFirewallPolicy({ siteId: site.id, ...body }),\n\t\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\t\treturn toAttributes(site.id, created);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Every list in a policy is a set (ports, addresses, networks, states), and the console reorders them.\n\t\t\t\t\t\t\tif (isCurrent(sortArrays(body), sortArrays(omitKeys(observed, READ_ONLY)))) {\n\t\t\t\t\t\t\t\treturn toAttributes(site.id, observed);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// A full PUT of the body: optional fields it leaves out are removed.\n\t\t\t\t\t\t\tconst updated = yield* withConnection(\n\t\t\t\t\t\t\t\tSdk.updateFirewallPolicy({\n\t\t\t\t\t\t\t\t\t...body,\n\t\t\t\t\t\t\t\t\tsiteId: site.id,\n\t\t\t\t\t\t\t\t\tfirewallPolicyId: observed.id,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\treturn toAttributes(site.id, updated);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdelete: ({ output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\twithConnection(\n\t\t\t\t\t\t\tSdk.deleteFirewallPolicy({\n\t\t\t\t\t\t\t\tsiteId: output.siteId,\n\t\t\t\t\t\t\t\tfirewallPolicyId: output.firewallPolicyId,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.void),\n\t\t\t\t\t\t\tReadCache.invalidating(policiesKey, output.siteId),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t});\n\t\t}),\n\t);\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport { STATE_STORE_VERSION } from \"alchemy/State/HttpStateApi\";\nimport type { ReplacedResourceState } from \"alchemy/State/ResourceState\";\nimport { type PersistedState, State, type StateService, StateStoreError } from \"alchemy/State/State\";\nimport { encodeState, reviveState } from \"alchemy/State/StateEncoding\";\nimport { recordStateStoreInit } from \"alchemy/Telemetry/Metrics\";\nimport * as Clock from \"effect/Clock\";\nimport * as Effect from \"effect/Effect\";\nimport type * as HttpClient from \"effect/http/HttpClient\";\nimport * as Layer from \"effect/Layer\";\nimport * as LogLevel from \"effect/LogLevel\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as References from \"effect/References\";\nimport * as Scope from \"effect/Scope\";\nimport * as Semaphore from \"effect/Semaphore\";\nimport * as Stream from \"effect/Stream\";\nimport * as zlib from \"node:zlib\";\nimport { UnifiNetworkAuth } from \"./AuthProvider.ts\";\nimport * as Connection from \"./Connection.ts\";\nimport * as Credentials from \"./Credentials.ts\";\nimport * as ReadCache from \"./ReadCache.ts\";\nimport { resolveSite } from \"./Site.ts\";\n\n/**\n * State store on the console itself (see `docs/development/state-store.md`). Each stack and\n * stage is one disabled Gateway → Gateway firewall policy whose description\n * holds the stage's state, and a traffic-matching list (whose names, unlike\n * policies', are unique) is its lock.\n */\n\n/** Name prefix of the policies holding state. Other code leaves these alone. */\nexport const STATE_POLICY_PREFIX = \"alchemy-state/\";\n/** Name prefix of the traffic-matching lists that lock a stage. */\nexport const STATE_LOCK_PREFIX = \"alchemy-lock/\";\n\n/** Prefix of a state policy's description, ahead of the base64 of the Brotli-compressed JSON. */\nconst FORMAT = \"alchemy-state:v1:\";\n\nexport const DEFAULT_WARN_AT = 4_000_000;\n\n/** How long a lock stays valid without a renewal. */\nconst LOCK_TTL_MS = 120_000;\nconst RENEW_EVERY_MS = 30_000;\n/** How long a passing check that the lock still exists is trusted. */\nconst LOCK_CHECK_TTL_MS = 5_000;\n\nexport interface UnifiStateOptions {\n\t/**\n\t * Warn once a stage's stored state passes this many bytes (compressed and\n\t * base64-encoded), as a prompt to move to a store without the console's\n\t * limits. A console stores up to about 16 MiB directly and 7.5 MiB through\n\t * ui.com.\n\t *\n\t * @default 4_000_000\n\t */\n\treadonly warnAt?: number;\n}\n\n/** A state policy as the store sees it. */\nexport interface StatePolicy {\n\treadonly id: string;\n\treadonly name: string;\n\treadonly description: string;\n}\n\n/**\n * The console operations the store needs. Every operation fails with\n * {@link StateStoreError}; deletes of missing objects succeed.\n */\nexport interface StateBackend {\n\t/** Every policy whose name starts with {@link STATE_POLICY_PREFIX}. */\n\treadonly listPolicies: Effect.Effect<ReadonlyArray<StatePolicy>, StateStoreError>;\n\treadonly createPolicy: (name: string, description: string) => Effect.Effect<string, StateStoreError>;\n\treadonly updatePolicy: (id: string, name: string, description: string) => Effect.Effect<void, StateStoreError>;\n\treadonly deletePolicy: (id: string) => Effect.Effect<void, StateStoreError>;\n\t/** Creates the lock list, returning its ID, or `undefined` when one with the name exists. */\n\treadonly createLock: (name: string) => Effect.Effect<string | undefined, StateStoreError>;\n\treadonly findLock: (name: string) => Effect.Effect<string | undefined, StateStoreError>;\n\treadonly lockExists: (id: string) => Effect.Effect<boolean, StateStoreError>;\n\treadonly deleteLock: (id: string) => Effect.Effect<void, StateStoreError>;\n}\n\ninterface LockRecord {\n\treadonly listId: string;\n\treadonly expiresAt: number;\n}\n\n/** What a state policy's description holds. Records are kept as `encodeState` output. */\ninterface Snapshot {\n\treadonly lock?: LockRecord | undefined;\n\treadonly resources: Readonly<Record<string, unknown>>;\n\treadonly output?: unknown;\n}\n\nexport const policyName = (stack: string, stage: string) => `${STATE_POLICY_PREFIX}${stack}/${stage}`;\nexport const lockName = (stack: string, stage: string) => `${STATE_LOCK_PREFIX}${stack}/${stage}`;\n\nconst parsePolicyName = (name: string) => {\n\tconst [stack, stage, ...rest] = name.slice(STATE_POLICY_PREFIX.length).split(\"/\");\n\treturn stack === undefined || stage === undefined || rest.length > 0 ? undefined : { stack, stage };\n};\n\nexport const encodeSnapshot = (snapshot: Snapshot): string =>\n\tFORMAT +\n\tzlib\n\t\t.brotliCompressSync(JSON.stringify(snapshot), {\n\t\t\t// Quality 11 is several times slower on a large state, for a few percent.\n\t\t\tparams: { [zlib.constants.BROTLI_PARAM_QUALITY]: 9 },\n\t\t})\n\t\t.toString(\"base64\");\n\nconst isLockRecord = (value: unknown): value is LockRecord =>\n\tPredicate.hasProperty(value, \"listId\") &&\n\ttypeof value.listId === \"string\" &&\n\tPredicate.hasProperty(value, \"expiresAt\") &&\n\ttypeof value.expiresAt === \"number\";\n\nexport const decodeSnapshot = (policy: StatePolicy): Effect.Effect<Snapshot, StateStoreError> =>\n\tEffect.try({\n\t\ttry: (): Snapshot => {\n\t\t\tif (!policy.description.startsWith(FORMAT)) throw new Error(\"its description isn't in a known format\");\n\t\t\tconst parsed: unknown = JSON.parse(\n\t\t\t\tzlib.brotliDecompressSync(Buffer.from(policy.description.slice(FORMAT.length), \"base64\")).toString(),\n\t\t\t);\n\t\t\tif (!Predicate.isObject(parsed) || !Predicate.isObject(parsed[\"resources\"])) {\n\t\t\t\tthrow new Error(\"it has no resources\");\n\t\t\t}\n\t\t\tconst lock = isLockRecord(parsed[\"lock\"]) ? parsed[\"lock\"] : undefined;\n\t\t\treturn { lock, resources: { ...parsed[\"resources\"] }, output: parsed[\"output\"] };\n\t\t},\n\t\tcatch: (cause) =>\n\t\t\tnew StateStoreError({\n\t\t\t\tmessage: `UniFi state policy '${policy.name}' can't be read: ${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t\t\t...(cause instanceof Error ? { cause } : {}),\n\t\t\t}),\n\t});\n\n/** A copy of a stored value, revived (e.g. `Redacted` values rebuilt). */\nconst revive = (encoded: unknown): unknown =>\n\tencoded === undefined ? undefined : JSON.parse(JSON.stringify(encoded), reviveState);\n\n// Records are only ever written by `set`, so a stored object is a record.\nconst isRecord = (value: unknown): value is PersistedState => Predicate.isObject(value);\nconst isReplaced = (value: unknown): value is ReplacedResourceState =>\n\tisRecord(value) && \"status\" in value && value.status === \"replaced\";\n\nconst heldError = (stack: string, stage: string, detail: string) =>\n\tnew StateStoreError({ message: `Another run holds the UniFi state lock on '${stack}/${stage}': ${detail}` });\n\n/** A stage this store has locked and loaded. */\ninterface OpenStage {\n\treadonly stack: string;\n\treadonly stage: string;\n\treadonly listId: string;\n\tpolicyId: string | undefined;\n\tresources: Record<string, unknown>;\n\toutput: unknown;\n\t/** Bumped by every change; `written` is the latest one on the console. */\n\tversion: number;\n\twritten: number;\n\tlockCheckedAt: number | undefined;\n\tlost: boolean;\n\tclosed: boolean;\n\t/** One write at a time. Changes that arrive during a write share the next one. */\n\treadonly writer: Semaphore.Semaphore;\n}\n\n/**\n * A {@link StateService} over `backend`. The first operation on a stage\n * locks it and loads its state, and the lock is released when `Scope`\n * closes.\n */\nexport const makeUnifiState = (backend: StateBackend, options: UnifiStateOptions = {}) =>\n\tEffect.gen(function* () {\n\t\tconst scope = yield* Effect.scope;\n\t\tconst warnAt = options.warnAt ?? DEFAULT_WARN_AT;\n\t\tlet warned = false;\n\n\t\tconst findPolicy = Effect.fn(function* (stack: string, stage: string) {\n\t\t\tconst name = policyName(stack, stage);\n\t\t\tconst found = (yield* backend.listPolicies).filter((p) => p.name === name);\n\t\t\tif (found.length > 1) {\n\t\t\t\treturn yield* new StateStoreError({\n\t\t\t\t\tmessage: `Several firewall policies are named '${name}'. Only one may hold this stage's state: delete the others (keep the one with the newest state).`,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn found[0];\n\t\t});\n\n\t\tconst takeLock = Effect.fn(function* (stack: string, stage: string) {\n\t\t\tconst name = lockName(stack, stage);\n\t\t\tfor (let attempt = 0; attempt < 2; attempt++) {\n\t\t\t\tconst created = yield* backend.createLock(name);\n\t\t\t\tif (created !== undefined) return created;\n\t\t\t\tconst holder = yield* backend.findLock(name);\n\t\t\t\t// Released between the create and the find: try again.\n\t\t\t\tif (holder === undefined) continue;\n\t\t\t\tconst policy = yield* findPolicy(stack, stage);\n\t\t\t\tconst record = policy === undefined ? undefined : (yield* decodeSnapshot(policy)).lock;\n\t\t\t\tif (record === undefined || record.listId !== holder) {\n\t\t\t\t\treturn yield* heldError(\n\t\t\t\t\t\tstack,\n\t\t\t\t\t\tstage,\n\t\t\t\t\t\t`the traffic-matching list '${name}' exists, but no run has recorded holding it. A run may be starting, or one crashed before recording it. If nothing is using this stage, delete the list in the UniFi UI.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst now = yield* Clock.currentTimeMillis;\n\t\t\t\tif (record.expiresAt > now) {\n\t\t\t\t\treturn yield* heldError(\n\t\t\t\t\t\tstack,\n\t\t\t\t\t\tstage,\n\t\t\t\t\t\t`it expires at ${new Date(record.expiresAt).toISOString()} unless renewed.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// The holder stopped renewing. Deleting by ID means only one run can break it:\n\t\t\t\t// another breaker's delete misses, or its create finds the new list.\n\t\t\t\tyield* backend.deleteLock(holder);\n\t\t\t}\n\t\t\treturn yield* heldError(stack, stage, `the traffic-matching list '${name}' keeps reappearing.`);\n\t\t});\n\n\t\tconst checkLock = (s: OpenStage) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst lostError = new StateStoreError({\n\t\t\t\t\tmessage: `The UniFi state lock on '${s.stack}/${s.stage}' was lost mid-run (its traffic-matching list was deleted); refusing to continue unlocked.`,\n\t\t\t\t});\n\t\t\t\tif (s.lost) return yield* lostError;\n\t\t\t\tconst now = yield* Clock.currentTimeMillis;\n\t\t\t\tif (s.lockCheckedAt !== undefined && now - s.lockCheckedAt < LOCK_CHECK_TTL_MS) return;\n\t\t\t\tif (!(yield* backend.lockExists(s.listId))) {\n\t\t\t\t\ts.lost = true;\n\t\t\t\t\treturn yield* lostError;\n\t\t\t\t}\n\t\t\t\ts.lockCheckedAt = now;\n\t\t\t});\n\n\t\t/**\n\t\t * Writes the stage unless `upTo` is already on the console. Without\n\t\t * `upTo` (a renewal) it writes only an existing policy.\n\t\t */\n\t\tconst flush = (s: OpenStage, upTo?: number) =>\n\t\t\tSemaphore.withPermits(\n\t\t\t\ts.writer,\n\t\t\t\t1,\n\t\t\t)(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tif (s.closed) {\n\t\t\t\t\t\treturn yield* new StateStoreError({\n\t\t\t\t\t\t\tmessage: `The UniFi state store for '${s.stack}/${s.stage}' is closed.`,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (upTo === undefined ? s.policyId === undefined : s.written >= upTo) return;\n\t\t\t\t\tyield* checkLock(s);\n\t\t\t\t\tconst version = s.version;\n\t\t\t\t\tconst expiresAt = (yield* Clock.currentTimeMillis) + LOCK_TTL_MS;\n\t\t\t\t\tconst description = encodeSnapshot({\n\t\t\t\t\t\tlock: { listId: s.listId, expiresAt },\n\t\t\t\t\t\tresources: s.resources,\n\t\t\t\t\t\toutput: s.output,\n\t\t\t\t\t});\n\t\t\t\t\tif (description.length > warnAt && !warned) {\n\t\t\t\t\t\twarned = true;\n\t\t\t\t\t\tyield* Effect.logWarning(\n\t\t\t\t\t\t\t`UniFi state for '${s.stack}/${s.stage}' is ${description.length} bytes, over the ${warnAt}-byte warning size. The console stores up to about 16 MiB directly and 7.5 MiB through ui.com: consider moving this stack to another state store.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tconst name = policyName(s.stack, s.stage);\n\t\t\t\t\tif (s.policyId === undefined) s.policyId = yield* backend.createPolicy(name, description);\n\t\t\t\t\telse yield* backend.updatePolicy(s.policyId, name, description);\n\t\t\t\t\ts.written = version;\n\t\t\t\t}),\n\t\t\t);\n\n\t\tconst openStage = Effect.fn(function* (stack: string, stage: string) {\n\t\t\tconst listId = yield* takeLock(stack, stage);\n\t\t\tconst s: OpenStage = {\n\t\t\t\tstack,\n\t\t\t\tstage,\n\t\t\t\tlistId,\n\t\t\t\tpolicyId: undefined,\n\t\t\t\tresources: {},\n\t\t\t\toutput: undefined,\n\t\t\t\tversion: 0,\n\t\t\t\twritten: 0,\n\t\t\t\tlockCheckedAt: yield* Clock.currentTimeMillis,\n\t\t\t\tlost: false,\n\t\t\t\tclosed: false,\n\t\t\t\twriter: Semaphore.makeUnsafe(1),\n\t\t\t};\n\t\t\t// Added before the renewal is forked, so it runs after the renewal is interrupted.\n\t\t\tyield* Scope.addFinalizer(\n\t\t\t\tscope,\n\t\t\t\tSemaphore.withPermits(\n\t\t\t\t\ts.writer,\n\t\t\t\t\t1,\n\t\t\t\t)(\n\t\t\t\t\tEffect.suspend(() => {\n\t\t\t\t\t\ts.closed = true;\n\t\t\t\t\t\treturn backend.deleteLock(listId);\n\t\t\t\t\t}).pipe(Effect.ignore),\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst policy = yield* findPolicy(stack, stage);\n\t\t\tif (policy !== undefined) {\n\t\t\t\tconst snapshot = yield* decodeSnapshot(policy);\n\t\t\t\ts.policyId = policy.id;\n\t\t\t\ts.resources = { ...snapshot.resources };\n\t\t\t\ts.output = snapshot.output;\n\t\t\t\t// Record the lock, so another run can tell when it goes stale.\n\t\t\t\tyield* flush(s);\n\t\t\t}\n\t\t\tyield* flush(s).pipe(\n\t\t\t\tEffect.ignore,\n\t\t\t\tEffect.delay(RENEW_EVERY_MS),\n\t\t\t\tEffect.forever,\n\t\t\t\tEffect.interruptible,\n\t\t\t\tEffect.forkIn(scope),\n\t\t\t);\n\t\t\treturn s;\n\t\t});\n\n\t\t// One open stage per (stack, stage), opened one at a time; a failed open isn't kept.\n\t\tconst stages = new Map<string, OpenStage>();\n\t\tconst opening = Semaphore.makeUnsafe(1);\n\t\tconst stageFor = (request: { readonly stack: string; readonly stage: string }) =>\n\t\t\tSemaphore.withPermits(\n\t\t\t\topening,\n\t\t\t\t1,\n\t\t\t)(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst key = `${request.stack}/${request.stage}`;\n\t\t\t\t\tconst existing = stages.get(key);\n\t\t\t\t\tif (existing !== undefined) return existing;\n\t\t\t\t\tconst opened = yield* openStage(request.stack, request.stage);\n\t\t\t\t\tstages.set(key, opened);\n\t\t\t\t\treturn opened;\n\t\t\t\t}),\n\t\t\t);\n\n\t\tconst change = (request: { readonly stack: string; readonly stage: string }, apply: (s: OpenStage) => void) =>\n\t\t\tstageFor(request).pipe(\n\t\t\t\tEffect.flatMap((s) => {\n\t\t\t\t\tapply(s);\n\t\t\t\t\ts.version++;\n\t\t\t\t\treturn flush(s, s.version);\n\t\t\t\t}),\n\t\t\t);\n\n\t\tconst deleteStage = (stack: string, stage: string) =>\n\t\t\tstageFor({ stack, stage }).pipe(\n\t\t\t\tEffect.flatMap((s) =>\n\t\t\t\t\tSemaphore.withPermits(\n\t\t\t\t\t\ts.writer,\n\t\t\t\t\t\t1,\n\t\t\t\t\t)(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tyield* checkLock(s);\n\t\t\t\t\t\t\tif (s.policyId !== undefined) yield* backend.deletePolicy(s.policyId);\n\t\t\t\t\t\t\ts.policyId = undefined;\n\t\t\t\t\t\t\ts.resources = {};\n\t\t\t\t\t\t\ts.output = undefined;\n\t\t\t\t\t\t\ts.written = s.version;\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t);\n\n\t\tconst stored = Effect.map(backend.listPolicies, (policies) =>\n\t\t\tpolicies.flatMap((p) => {\n\t\t\t\tconst parsed = parsePolicyName(p.name);\n\t\t\t\treturn parsed === undefined ? [] : [parsed];\n\t\t\t}),\n\t\t);\n\n\t\tconst listStages = (stack: string) =>\n\t\t\tEffect.map(stored, (all) =>\n\t\t\t\t[...new Set(all.filter((s) => s.stack === stack).map((s) => s.stage))].toSorted(),\n\t\t\t);\n\n\t\tconst service: StateService = {\n\t\t\tid: \"unifi-network\",\n\t\t\tgetVersion: () => Effect.succeed(STATE_STORE_VERSION),\n\t\t\tlistStacks: () => Effect.map(stored, (all) => [...new Set(all.map((s) => s.stack))].toSorted()),\n\t\t\tlistStages,\n\t\t\tget: (request) =>\n\t\t\t\tEffect.map(stageFor(request), (s) => {\n\t\t\t\t\tconst record = revive(s.resources[request.fqn]);\n\t\t\t\t\treturn isRecord(record) ? record : undefined;\n\t\t\t\t}),\n\t\t\tgetReplacedResources: (request) =>\n\t\t\t\tEffect.map(stageFor(request), (s) => Object.values(s.resources).map(revive).filter(isReplaced)),\n\t\t\tlist: (request) => Effect.map(stageFor(request), (s) => Object.keys(s.resources).toSorted()),\n\t\t\tset: <V extends PersistedState>(request: { stack: string; stage: string; fqn: string; value: V }) =>\n\t\t\t\tchange(request, (s) => {\n\t\t\t\t\ts.resources[request.fqn] = encodeState(request.value);\n\t\t\t\t}).pipe(Effect.as(request.value)),\n\t\t\tdelete: (request) =>\n\t\t\t\tchange(request, (s) => {\n\t\t\t\t\tdelete s.resources[request.fqn];\n\t\t\t\t}),\n\t\t\tdeleteStack: ({ stack, stage }) =>\n\t\t\t\tstage === undefined\n\t\t\t\t\t? Effect.flatMap(listStages(stack), (found) =>\n\t\t\t\t\t\t\tEffect.forEach(found, (each) => deleteStage(stack, each), { discard: true }),\n\t\t\t\t\t\t)\n\t\t\t\t\t: deleteStage(stack, stage),\n\t\t\tgetOutput: (request) => Effect.map(stageFor(request), (s) => revive(s.output)),\n\t\t\tsetOutput: (request) =>\n\t\t\t\tchange(request, (s) => {\n\t\t\t\t\ts.output = encodeState(request.value);\n\t\t\t\t}).pipe(Effect.as(request.value)),\n\t\t};\n\t\treturn service;\n\t});\n\nconst toStateStoreError = (what: string) => (cause: unknown) =>\n\tnew StateStoreError({\n\t\tmessage: `UniFi state store: ${what} failed: ${Connection.describeError(cause)}`,\n\t\t...(cause instanceof Error ? { cause } : {}),\n\t});\n\nconst isAlreadyExists = (error: unknown) =>\n\tPredicate.hasProperty(error, \"_tag\") &&\n\terror._tag === \"UnprocessableEntity\" &&\n\tPredicate.hasProperty(error, \"message\") &&\n\ttypeof error.message === \"string\" &&\n\terror.message.includes(\"already exist\");\n\nconst policyBody = (zoneId: string, name: string, description: string) => ({\n\tenabled: false,\n\tname,\n\tdescription,\n\taction: { type: \"BLOCK\" as const },\n\tsource: { zoneId },\n\tdestination: { zoneId },\n\tipProtocolScope: { ipVersion: \"IPV4_AND_IPV6\" as const },\n\tloggingEnabled: false,\n});\n\nconst describeWrite = (description: string) =>\n\t`writing ${description.length} bytes of state (a console stores up to about 16 MiB directly, 7.5 MiB through ui.com)`;\n\n/**\n * Runs with Debug and Trace records off: request bodies are the state,\n * which holds secrets (e.g. Wi-Fi passphrases). Floors already above Info\n * are left alone.\n */\nconst withoutDebugLogs = Effect.updateService(References.MinimumLogLevel, (level) =>\n\tLogLevel.isLessThan(level, \"Info\") ? \"Info\" : level,\n);\n\n/** The {@link StateBackend} of the profile's console and site. */\nconst consoleBackend = Effect.gen(function* () {\n\tconst { id: siteId } = yield* resolveSite();\n\t// Operations run later, from wherever Alchemy calls the store.\n\tconst context = yield* Effect.context<Connection.UnifiNetworkConnection>();\n\tconst run = <A, E>(what: string, effect: Effect.Effect<A, E, Sdk.Credentials | HttpClient.HttpClient>) =>\n\t\tConnection.withConnection(effect).pipe(\n\t\t\tEffect.mapError(toStateStoreError(what)),\n\t\t\tEffect.provideContext(context),\n\t\t\twithoutDebugLogs,\n\t\t);\n\n\tconst gatewayZoneId = yield* Effect.cached(\n\t\trun(\"finding the Gateway zone\", Stream.runCollect(Sdk.getFirewallZones.items({ siteId }))).pipe(\n\t\t\tEffect.flatMap((zones) => {\n\t\t\t\tconst zone = zones.find((z) => z.metadata.origin === \"SYSTEM_DEFINED\" && z.name === \"Gateway\");\n\t\t\t\treturn zone === undefined\n\t\t\t\t\t? Effect.fail(new StateStoreError({ message: \"UniFi state store: the site has no Gateway zone.\" }))\n\t\t\t\t\t: Effect.succeed(zone.id);\n\t\t\t}),\n\t\t),\n\t);\n\n\treturn {\n\t\tlistPolicies: run(\n\t\t\t\"listing state policies\",\n\t\t\tStream.runCollect(Sdk.getFirewallPolicies.items({ siteId })).pipe(\n\t\t\t\tEffect.map((policies) =>\n\t\t\t\t\t// User-defined first: some system-defined policies have no name, whatever the spec says.\n\t\t\t\t\tpolicies.flatMap((p) =>\n\t\t\t\t\t\tp.metadata.origin === \"USER_DEFINED\" && p.name.startsWith(STATE_POLICY_PREFIX)\n\t\t\t\t\t\t\t? [{ id: p.id, name: p.name, description: p.description ?? \"\" }]\n\t\t\t\t\t\t\t: [],\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t),\n\t\t),\n\t\tcreatePolicy: (name, description) =>\n\t\t\tEffect.flatMap(gatewayZoneId, (zoneId) =>\n\t\t\t\trun(\n\t\t\t\t\tdescribeWrite(description),\n\t\t\t\t\tSdk.createFirewallPolicy({ siteId, ...policyBody(zoneId, name, description) }),\n\t\t\t\t),\n\t\t\t).pipe(Effect.map((created) => created.id)),\n\t\tupdatePolicy: (id, name, description) =>\n\t\t\tEffect.flatMap(gatewayZoneId, (zoneId) =>\n\t\t\t\trun(\n\t\t\t\t\tdescribeWrite(description),\n\t\t\t\t\tSdk.updateFirewallPolicy({\n\t\t\t\t\t\tsiteId,\n\t\t\t\t\t\tfirewallPolicyId: id,\n\t\t\t\t\t\t...policyBody(zoneId, name, description),\n\t\t\t\t\t}),\n\t\t\t\t),\n\t\t\t).pipe(Effect.asVoid),\n\t\tdeletePolicy: (id) =>\n\t\t\trun(\n\t\t\t\t\"deleting a state policy\",\n\t\t\t\tSdk.deleteFirewallPolicy({ siteId, firewallPolicyId: id }).pipe(\n\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.void),\n\t\t\t\t),\n\t\t\t),\n\t\tcreateLock: (name) =>\n\t\t\trun(\n\t\t\t\t\"taking the lock\",\n\t\t\t\tSdk.createTrafficMatchingList({\n\t\t\t\t\tsiteId,\n\t\t\t\t\tbody: { type: \"PORTS\", name, items: [{ type: \"PORT_NUMBER\", value: 1 }] },\n\t\t\t\t}).pipe(\n\t\t\t\t\tEffect.map((created): string | undefined => created.id),\n\t\t\t\t\tEffect.catchIf(isAlreadyExists, () => Effect.undefined),\n\t\t\t\t),\n\t\t\t),\n\t\tfindLock: (name) =>\n\t\t\trun(\n\t\t\t\t\"finding the lock\",\n\t\t\t\tStream.runCollect(Sdk.getTrafficMatchingLists.items({ siteId })).pipe(\n\t\t\t\t\tEffect.map((lists) => lists.find((l) => l.name === name)?.id),\n\t\t\t\t),\n\t\t\t),\n\t\tlockExists: (id) =>\n\t\t\trun(\n\t\t\t\t\"checking the lock\",\n\t\t\t\tSdk.getTrafficMatchingList({ siteId, trafficMatchingListId: id }).pipe(\n\t\t\t\t\tEffect.as(true),\n\t\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.succeed(false)),\n\t\t\t\t),\n\t\t\t),\n\t\tdeleteLock: (id) =>\n\t\t\trun(\n\t\t\t\t\"releasing the lock\",\n\t\t\t\tSdk.deleteTrafficMatchingList({ siteId, trafficMatchingListId: id }).pipe(\n\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.void),\n\t\t\t\t),\n\t\t\t),\n\t} satisfies StateBackend;\n}).pipe(\n\tEffect.mapError((error) =>\n\t\terror instanceof StateStoreError ? error : toStateStoreError(\"finding the site\")(error),\n\t),\n);\n\n/**\n * State store on the console the profile points at: each stack and stage\n * is a disabled Gateway → Gateway firewall policy holding the stage's\n * compressed state, locked with a traffic-matching list while a run uses\n * it. Nothing connects until the first state operation.\n *\n * The state includes secrets in plaintext, as in every Alchemy store: a\n * UniFi stack's are already on the console, but another provider's\n * resources in the same stack would put theirs there too.\n *\n * @example\n * ```ts\n * import * as UnifiNetwork from \"@alchemy-unifi/network\";\n * import * as Alchemy from \"alchemy\";\n * import * as Effect from \"effect/Effect\";\n *\n * export default Alchemy.Stack(\n * \t\"HomeNetwork\",\n * \t{ providers: UnifiNetwork.providers(), state: UnifiNetwork.state() },\n * \tEffect.gen(function* () {\n * \t\tyield* UnifiNetwork.FirewallZone(\"iot-zone\", { name: \"IoT\" });\n * \t}),\n * );\n * ```\n */\nexport const state = (options: UnifiStateOptions = {}) =>\n\tLayer.effect(\n\t\tState,\n\t\tEffect.gen(function* () {\n\t\t\tconst scope = yield* Effect.scope;\n\t\t\tconst context = yield* Effect.context<\n\t\t\t\t| Connection.UnifiNetworkConnection\n\t\t\t\t| Credentials.UnifiNetworkCredentials\n\t\t\t\t| ReadCache.UnifiNetworkReadCache\n\t\t\t>();\n\t\t\tconst make = Effect.flatMap(consoleBackend, (backend) => makeUnifiState(backend, options)).pipe(\n\t\t\t\tScope.provide(scope),\n\t\t\t\trecordStateStoreInit,\n\t\t\t\tEffect.orDie,\n\t\t\t\tEffect.provideContext(context),\n\t\t\t);\n\t\t\treturn yield* Effect.cached(make);\n\t\t}),\n\t).pipe(\n\t\tLayer.provide(\n\t\t\tLayer.merge(Connection.layer, ReadCache.layer).pipe(\n\t\t\t\tLayer.provideMerge(Credentials.fromAuthProvider()),\n\t\t\t\tLayer.provideMerge(UnifiNetworkAuth),\n\t\t\t),\n\t\t),\n\t\tLayer.orDie,\n\t);\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport * as Provider from \"alchemy/Provider\";\nimport { Resource } from \"alchemy/Resource\";\nimport { UserFacingError } from \"alchemy/UserFacingError\";\nimport * as Data from \"effect/Data\";\nimport * as Effect from \"effect/Effect\";\nimport { withConnection } from \"./Connection.ts\";\nimport { type FirewallPolicyReference, isUserDefined, listFirewallPolicies } from \"./FirewallPolicy.ts\";\nimport { type FirewallZoneReference, resolveFirewallZone } from \"./FirewallZone.ts\";\nimport { captureServices } from \"./internal/Services.ts\";\nimport type { Providers } from \"./Providers.ts\";\nimport { checkSameSite, resolveSite, type Site } from \"./Site.ts\";\nimport { STATE_POLICY_PREFIX } from \"./State.ts\";\n\nexport interface FirewallPolicyOrderingProps {\n\t/** Site of the zones. @default the profile's site, else the console's only site */\n\tsite?: string;\n\tsource: FirewallZoneReference;\n\tdestination: FirewallZoneReference;\n\t/** Policies evaluated before the system-defined ones, first match wins. */\n\tbeforeSystemDefined?: ReadonlyArray<FirewallPolicyReference>;\n\t/** Policies evaluated after the system-defined ones. */\n\tafterSystemDefined?: ReadonlyArray<FirewallPolicyReference>;\n\t/**\n\t * Fail instead of keeping policies in the zone pair that aren't listed\n\t * (e.g. made in the UI). @default false\n\t */\n\texclusive?: boolean;\n}\n\nexport interface FirewallPolicyOrderingAttributes {\n\tsiteId: string;\n\tsourceFirewallZoneId: string;\n\tdestinationFirewallZoneId: string;\n\tbeforeSystemDefined: ReadonlyArray<string>;\n\tafterSystemDefined: ReadonlyArray<string>;\n\t/** The policies the props list, as opposed to unlisted ones kept in the order. */\n\tlistedFirewallPolicyIds: ReadonlyArray<string>;\n}\n\nexport interface FirewallPolicyOrdering extends Resource<\n\t\"UnifiNetwork.FirewallPolicyOrdering\",\n\tFirewallPolicyOrderingProps,\n\tFirewallPolicyOrderingAttributes,\n\tnever,\n\tProviders\n> {}\n\n/** An ordering that can't be applied as given. */\nexport class InvalidFirewallPolicyOrdering extends Data.TaggedError(\"InvalidFirewallPolicyOrdering\")<{\n\treadonly reason: string;\n}> {\n\treadonly [UserFacingError]: true = true;\n\toverride get message() {\n\t\treturn `Can't apply the firewall policy ordering: ${this.reason}`;\n\t}\n}\n\ntype Order = Sdk.OrderedFirewallPolicyIDs;\n\nconst sameOrder = (a: Order, b: Order) =>\n\ta.beforeSystemDefined.join() === b.beforeSystemDefined.join() &&\n\ta.afterSystemDefined.join() === b.afterSystemDefined.join();\n\nconst resolvePair = Effect.fn(function* (props: FirewallPolicyOrderingProps) {\n\tconst site = yield* resolveSite(props.site);\n\treturn {\n\t\tsite,\n\t\tsiteId: site.id,\n\t\tsourceFirewallZoneId: yield* resolveFirewallZone(props.source, site),\n\t\tdestinationFirewallZoneId: yield* resolveFirewallZone(props.destination, site),\n\t};\n});\n\nconst getOrder = ({ siteId, sourceFirewallZoneId, destinationFirewallZoneId }: Pair) =>\n\twithConnection(Sdk.getFirewallPolicyOrdering({ siteId, sourceFirewallZoneId, destinationFirewallZoneId })).pipe(\n\t\tEffect.map((ordering) => ordering.orderedFirewallPolicyIds),\n\t);\n\ninterface Pair {\n\treadonly siteId: string;\n\treadonly sourceFirewallZoneId: string;\n\treadonly destinationFirewallZoneId: string;\n}\n\nconst toAttributes = (\n\tpair: Pair,\n\torder: Order,\n\tlistedFirewallPolicyIds: ReadonlyArray<string>,\n): FirewallPolicyOrderingAttributes => ({\n\tsiteId: pair.siteId,\n\tsourceFirewallZoneId: pair.sourceFirewallZoneId,\n\tdestinationFirewallZoneId: pair.destinationFirewallZoneId,\n\tbeforeSystemDefined: order.beforeSystemDefined,\n\tafterSystemDefined: order.afterSystemDefined,\n\tlistedFirewallPolicyIds,\n});\n\nconst resolvePolicies = (references: ReadonlyArray<FirewallPolicyReference> | undefined, site: Site) =>\n\tEffect.forEach(references ?? [], (reference) =>\n\t\tcheckSameSite(\"firewall policy\", reference.siteId, site).pipe(Effect.as(reference.firewallPolicyId)),\n\t);\n\n/** Why a listed policy isn't in the zone pair's ordering. */\nconst explainMissing = Effect.fn(function* (site: Site, firewallPolicyId: string, pair: Pair) {\n\tconst policy = (yield* listFirewallPolicies(site.id)).find((p) => p.id === firewallPolicyId);\n\tif (policy === undefined) return `policy ${firewallPolicyId} doesn't exist`;\n\tif (!isUserDefined(policy)) return `\"${policy.name}\" isn't user-defined, so it can't be ordered`;\n\tif (\n\t\tpolicy.source.zoneId !== pair.sourceFirewallZoneId ||\n\t\tpolicy.destination.zoneId !== pair.destinationFirewallZoneId\n\t) {\n\t\treturn `\"${policy.name}\" belongs to another zone pair`;\n\t}\n\treturn `\"${policy.name}\" isn't in the zone pair's ordering`;\n});\n\n/**\n * The state store's policies (`UnifiNetwork.state()`) among `ids`. The store\n * may have created one since the run's policy list was read, so policies\n * missing from it are fetched.\n */\nconst statePolicies = Effect.fn(function* (site: Site, ids: ReadonlyArray<string>) {\n\tconst policies = yield* listFirewallPolicies(site.id);\n\tconst names = yield* Effect.forEach(ids, (id) => {\n\t\tconst listed = policies.find((p) => p.id === id);\n\t\treturn listed !== undefined\n\t\t\t? Effect.succeed(listed.name)\n\t\t\t: withConnection(Sdk.getFirewallPolicy({ siteId: site.id, firewallPolicyId: id })).pipe(\n\t\t\t\t\tEffect.map((policy): string | undefined => policy.name),\n\t\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.undefined),\n\t\t\t\t);\n\t});\n\treturn ids.filter((_, i) => names[i]?.startsWith(STATE_POLICY_PREFIX) === true);\n});\n\nconst describePolicies = Effect.fn(function* (site: Site, ids: ReadonlyArray<string>) {\n\tconst policies = yield* listFirewallPolicies(site.id);\n\treturn ids.map((id) => `\"${policies.find((p) => p.id === id)?.name ?? id}\"`).join(\", \");\n});\n\n/**\n * The evaluation order of user-defined policies from one zone to another.\n * Listing policies makes the ordering depend on them, so it's applied after\n * they're created or moved, and re-applied when the list changes.\n *\n * UniFi rejects an ordering that leaves out any of the pair's policies, so\n * unlisted ones (e.g. made in the UI) are kept after the listed ones in each\n * bucket, in their current relative order, with a warning; `exclusive: true`\n * fails instead. A policy the ordering listed before but no longer does is\n * kept quietly, even when exclusive: it's usually being deleted in the same\n * deploy, which runs after the ordering is applied. The state store's\n * policies (`UnifiNetwork.state()`, Gateway → Gateway) are kept quietly too.\n * System-defined policies sit between the two buckets and\n * can't be reordered; policies UniFi derives from UI Objects aren't ordered\n * at all.\n *\n * The zone pair is the identity: an existing ordering is adopted without\n * `--adopt`, and deleting the resource leaves the order as it is.\n *\n * @example\n * ```ts\n * import * as UnifiNetwork from \"@alchemy-unifi/network\";\n * import * as Alchemy from \"alchemy\";\n * import * as Effect from \"effect/Effect\";\n *\n * export default Alchemy.Stack(\n * \t\"HomeNetwork\",\n * \t{ providers: UnifiNetwork.providers(), state: UnifiNetwork.state() },\n * \tEffect.gen(function* () {\n * \t\tconst { INTERNAL } = UnifiNetwork.FirewallZone;\n * \t\tconst iot = yield* UnifiNetwork.FirewallZone(\"iot-zone\", { name: \"IoT\" });\n * \t\tconst policy = { enabled: true, source: { zone: INTERNAL }, destination: { zone: iot }, loggingEnabled: false } as const;\n * \t\tconst allowHomeAssistant = yield* UnifiNetwork.FirewallPolicy(\"allow-ha\", {\n * \t\t\t...policy,\n * \t\t\tname: \"Allow Home Assistant\",\n * \t\t\taction: { type: \"ALLOW\", allowReturnTraffic: true },\n * \t\t\tipProtocolScope: { ipVersion: \"IPV4_AND_IPV6\" },\n * \t\t});\n * \t\tconst blockTheRest = yield* UnifiNetwork.FirewallPolicy(\"block-rest\", {\n * \t\t\t...policy,\n * \t\t\tname: \"Block the rest\",\n * \t\t\taction: { type: \"BLOCK\" },\n * \t\t\tipProtocolScope: { ipVersion: \"IPV4_AND_IPV6\" },\n * \t\t});\n * \t\tyield* UnifiNetwork.FirewallPolicyOrdering(\"internal-to-iot\", {\n * \t\t\tsource: INTERNAL,\n * \t\t\tdestination: iot,\n * \t\t\tbeforeSystemDefined: [allowHomeAssistant, blockTheRest],\n * \t\t});\n * \t}),\n * );\n * ```\n */\nexport const FirewallPolicyOrdering = Resource<FirewallPolicyOrdering>(\"UnifiNetwork.FirewallPolicyOrdering\");\n\nexport const FirewallPolicyOrderingProvider = () =>\n\tProvider.effect(\n\t\tFirewallPolicyOrdering,\n\t\tEffect.gen(function* () {\n\t\t\tconst run = yield* captureServices;\n\n\t\t\treturn FirewallPolicyOrdering.Provider.of({\n\t\t\t\t// Orderings aren't objects of their own: there's nothing to list or nuke.\n\t\t\t\tlist: () => Effect.succeed([]),\n\n\t\t\t\tread: ({ olds, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (output !== undefined) {\n\t\t\t\t\t\t\t\treturn toAttributes(output, yield* getOrder(output), output.listedFirewallPolicyIds);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// One ordering per zone pair, so the existing one is this resource's.\n\t\t\t\t\t\t\tconst pair = yield* resolvePair(olds);\n\t\t\t\t\t\t\treturn toAttributes(pair, yield* getOrder(pair), []);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\treconcile: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst pair = yield* resolvePair(news);\n\t\t\t\t\t\t\tconst { site } = pair;\n\t\t\t\t\t\t\tconst before = yield* resolvePolicies(news.beforeSystemDefined, site);\n\t\t\t\t\t\t\tconst after = yield* resolvePolicies(news.afterSystemDefined, site);\n\t\t\t\t\t\t\tconst listed = [...before, ...after];\n\t\t\t\t\t\t\tconst duplicate = listed.find((id, i) => listed.indexOf(id) !== i);\n\t\t\t\t\t\t\tif (duplicate !== undefined) {\n\t\t\t\t\t\t\t\tconst names = yield* describePolicies(site, [duplicate]);\n\t\t\t\t\t\t\t\treturn yield* new InvalidFirewallPolicyOrdering({\n\t\t\t\t\t\t\t\t\treason: `${names} is listed twice.`,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst current = yield* getOrder(pair);\n\t\t\t\t\t\t\tconst inPair = new Set([...current.beforeSystemDefined, ...current.afterSystemDefined]);\n\t\t\t\t\t\t\tconst missing = listed.find((id) => !inPair.has(id));\n\t\t\t\t\t\t\tif (missing !== undefined) {\n\t\t\t\t\t\t\t\treturn yield* new InvalidFirewallPolicyOrdering({\n\t\t\t\t\t\t\t\t\treason: `${yield* explainMissing(site, missing, pair)}.`,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst unlisted = (ids: ReadonlyArray<string>) => ids.filter((id) => !listed.includes(id));\n\t\t\t\t\t\t\tconst formerlyListed = output?.listedFirewallPolicyIds ?? [];\n\t\t\t\t\t\t\tconst unlistedNow = [\n\t\t\t\t\t\t\t\t...unlisted(current.beforeSystemDefined),\n\t\t\t\t\t\t\t\t...unlisted(current.afterSystemDefined),\n\t\t\t\t\t\t\t].filter((id) => !formerlyListed.includes(id));\n\t\t\t\t\t\t\t// The state store's policies are disabled and kept where they are, without a word.\n\t\t\t\t\t\t\tconst stateIds = yield* statePolicies(site, unlistedNow);\n\t\t\t\t\t\t\tconst unmanaged = unlistedNow.filter((id) => !stateIds.includes(id));\n\t\t\t\t\t\t\tif (unmanaged.length > 0) {\n\t\t\t\t\t\t\t\tconst names = yield* describePolicies(site, unmanaged);\n\t\t\t\t\t\t\t\tif (news.exclusive === true) {\n\t\t\t\t\t\t\t\t\treturn yield* new InvalidFirewallPolicyOrdering({\n\t\t\t\t\t\t\t\t\t\treason: `the zone pair has policies the ordering doesn't list: ${names}. List them, delete them, or drop \\`exclusive\\`.`,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tyield* Effect.logWarning(\n\t\t\t\t\t\t\t\t\t`Firewall policy ordering: keeping unlisted policies after the listed ones: ${names}.`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst desired: Order = {\n\t\t\t\t\t\t\t\tbeforeSystemDefined: [...before, ...unlisted(current.beforeSystemDefined)],\n\t\t\t\t\t\t\t\tafterSystemDefined: [...after, ...unlisted(current.afterSystemDefined)],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (sameOrder(desired, current)) return toAttributes(pair, current, listed);\n\t\t\t\t\t\t\tconst updated = yield* withConnection(\n\t\t\t\t\t\t\t\tSdk.updateFirewallPolicyOrdering({\n\t\t\t\t\t\t\t\t\tsiteId: pair.siteId,\n\t\t\t\t\t\t\t\t\tsourceFirewallZoneId: pair.sourceFirewallZoneId,\n\t\t\t\t\t\t\t\t\tdestinationFirewallZoneId: pair.destinationFirewallZoneId,\n\t\t\t\t\t\t\t\t\torderedFirewallPolicyIds: desired,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn toAttributes(pair, updated.orderedFirewallPolicyIds, listed);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\t// The order stays as it is; the policies themselves are deleted by their own resources.\n\t\t\t\tdelete: () => Effect.void,\n\t\t\t});\n\t\t}),\n\t);\n","import * as Sdk from \"@alchemy-unifi/network-sdk\";\nimport { Unowned } from \"alchemy/AdoptPolicy\";\nimport type { Diff } from \"alchemy/Diff\";\nimport * as Provider from \"alchemy/Provider\";\nimport { Resource } from \"alchemy/Resource\";\nimport * as Effect from \"effect/Effect\";\nimport * as Stream from \"effect/Stream\";\nimport { withConnection } from \"./Connection.ts\";\nimport { type DistributiveOmit, isCurrent, omitKeys } from \"./internal/Fields.ts\";\nimport { planned, plannedString, UNKNOWN } from \"./internal/Plan.ts\";\nimport { captureServices } from \"./internal/Services.ts\";\nimport { describeQuery, LookupAmbiguous, LookupNotFound } from \"./Lookup.ts\";\nimport { type NetworkReference, resolveNetwork } from \"./Network.ts\";\nimport type { Providers } from \"./Providers.ts\";\nimport * as ReadCache from \"./ReadCache.ts\";\nimport { resolveSite, type Site } from \"./Site.ts\";\n\n/** The site's native (untagged) network. */\nexport interface NativeNetwork {\n\treadonly type: \"NATIVE\";\n}\n\ninterface CommonProps {\n\t/** Site to create the broadcast in. @default the profile's site, else the console's only site */\n\tsite?: string;\n\t/**\n\t * The network clients join: a network reference, or\n\t * `WifiBroadcast.NATIVE_NETWORK` for the site's default network (a\n\t * `Network` resource or lookup for the default network works too).\n\t */\n\tnetwork?: NetworkReference | NativeNetwork;\n}\n\n/** WPA security on a standard broadcast must set `fastRoamingEnabled` (the console rejects it otherwise). */\ntype WithFastRoaming<S> = S extends { fastRoamingEnabled?: boolean } ? S & { fastRoamingEnabled: boolean } : S;\n\nexport type StandardWifiSecurityConfiguration = WithFastRoaming<Sdk.WifiSecurityConfigurationDetailObject>;\n\n/**\n * An ordinary SSID. Pass passphrases as `Redacted` (e.g. from\n * `Config.Redacted`) to keep them out of logs.\n */\nexport interface StandardWifiBroadcastProps\n\textends\n\t\tOmit<Sdk.IntegrationStandardWifiBroadcastCreateUpdateDto, \"network\" | \"securityConfiguration\">,\n\t\tCommonProps {\n\tsecurityConfiguration: StandardWifiSecurityConfiguration;\n}\n\n/** An SSID tuned for IoT devices (2.4 GHz only, longer DTIM). */\nexport interface IotWifiBroadcastProps\n\textends Omit<Sdk.IntegrationIotOptimizedWifiBroadcastCreateUpdateDto, \"network\">, CommonProps {}\n\n/** Props are the Integration API's Wi-Fi body, plus `site` and references; `type` picks the kind. */\nexport type WifiBroadcastProps = StandardWifiBroadcastProps | IotWifiBroadcastProps;\n\nexport interface WifiBroadcastAttributes {\n\tsiteId: string;\n\twifiBroadcastId: string;\n\tname: string;\n\ttype: WifiBroadcastProps[\"type\"];\n\t/** The network clients join, when it isn't the native one. */\n\tnetworkId: string | undefined;\n\t/** The whole broadcast as the console reports it (passphrases `Redacted`), so `alchemy drift` sees any change. */\n\tconfig: DistributiveOmit<Sdk.WifiBroadcastDetails, \"id\" | \"metadata\">;\n}\n\nexport interface WifiBroadcast extends Resource<\n\t\"UnifiNetwork.WifiBroadcast\",\n\tWifiBroadcastProps,\n\tWifiBroadcastAttributes,\n\tnever,\n\tProviders\n> {}\n\nconst loadBroadcasts = (siteId: string) =>\n\twithConnection(Stream.runCollect(Sdk.getWifiBroadcastPage.items({ siteId })));\nconst broadcastsKey = ReadCache.key<\n\tReadonlyArray<Sdk.WifiBroadcastOverview>,\n\tEffect.Error<ReturnType<typeof loadBroadcasts>>\n>(\"wifiBroadcasts\");\n\n/** A site's Wi-Fi broadcasts, listed once per run. */\nexport const listWifiBroadcasts = (siteId: string) => ReadCache.cached(broadcastsKey, siteId, loadBroadcasts(siteId));\n\nconst toAttributes = (siteId: string, broadcast: Sdk.WifiBroadcastDetails): WifiBroadcastAttributes => {\n\tconst { id, metadata: _metadata, ...config } = broadcast;\n\treturn {\n\t\tsiteId,\n\t\twifiBroadcastId: id,\n\t\tname: broadcast.name,\n\t\ttype: broadcast.type,\n\t\tnetworkId: broadcast.network?.type === \"SPECIFIC\" ? broadcast.network.networkId : undefined,\n\t\tconfig,\n\t};\n};\n\nconst byFrequency = (bands: ReadonlyArray<number>) => bands.toSorted((a, b) => a - b);\n\n/**\n * `body` with its bands sorted (their order means nothing; see\n * {@link comparable}), and an explicit value for the one unset field that\n * can't simply be left out (verified): a standard broadcast's\n * `bandSteeringEnabled`, which the console reports as `false` when unset,\n * so without the default the body would never match and every deploy would\n * update it (and drift would report it) forever.\n */\nconst withDefaults = (body: Sdk.WifiBroadcastCreateOrUpdate): Sdk.WifiBroadcastCreateOrUpdate =>\n\tbody.type === \"STANDARD\"\n\t\t? {\n\t\t\t\t...body,\n\t\t\t\tbandSteeringEnabled: body.bandSteeringEnabled ?? false,\n\t\t\t\tbroadcastingFrequenciesGHz: byFrequency(body.broadcastingFrequenciesGHz),\n\t\t\t}\n\t\t: body;\n\n/** Fields the console reports but rejects in request bodies. */\nconst READ_ONLY = [\"id\", \"metadata\"];\n\nconst toBody = Effect.fn(function* (props: WifiBroadcastProps, site: Site) {\n\tconst { site: _site, network, ...rest } = props;\n\tif (network === undefined) return rest satisfies Sdk.WifiBroadcastCreateOrUpdate;\n\t// The console only accepts the default network as NATIVE; referencing it by ID is rejected.\n\tif (\"type\" in network || network.default === true) {\n\t\treturn { ...rest, network: { type: \"NATIVE\" } } satisfies Sdk.WifiBroadcastCreateOrUpdate;\n\t}\n\tconst networkId = yield* resolveNetwork(network, site);\n\treturn { ...rest, network: { type: \"SPECIFIC\", networkId } } satisfies Sdk.WifiBroadcastCreateOrUpdate;\n});\n\n/**\n * The console's broadcast in body form, with its bands sorted: consoles keep\n * them in an order of their own (not the one sent; the test controller\n * sorts them), so order mustn't count as a difference.\n */\nconst comparable = (observed: Sdk.WifiBroadcastDetails) => {\n\tconst body = omitKeys(observed, READ_ONLY);\n\treturn observed.type === \"STANDARD\"\n\t\t? { ...body, broadcastingFrequenciesGHz: byFrequency(observed.broadcastingFrequenciesGHz) }\n\t\t: body;\n};\n\nconst getBroadcast = (siteId: string, wifiBroadcastId: string) =>\n\twithConnection(Sdk.getWifiBroadcastDetails({ siteId, wifiBroadcastId })).pipe(\n\t\tEffect.catchTag(\"NotFound\", () => Effect.undefined),\n\t);\n\n/** The attributes of a broadcast known to exist (e.g. from a list). */\nconst attributesOf = (siteId: string, wifiBroadcastId: string) =>\n\twithConnection(Sdk.getWifiBroadcastDetails({ siteId, wifiBroadcastId })).pipe(\n\t\tEffect.map((broadcast) => toAttributes(siteId, broadcast)),\n\t);\n\nconst NATIVE_NETWORK: NativeNetwork = { type: \"NATIVE\" };\n\n/**\n * A Wi-Fi network (SSID). `type` picks a standard or an IoT-optimised\n * broadcast; changing it, or the site, replaces the broadcast.\n *\n * Props are authoritative: every update sends the whole broadcast, with\n * explicit defaults for unset fields (a standard broadcast's\n * `bandSteeringEnabled: false`), so changes made in the UI, passphrases\n * included, are put back. The `config` attribute mirrors the console's\n * object, so `alchemy drift` sees them. Existing broadcasts are adopted by name with\n * `--adopt`.\n *\n * @example\n * ```ts\n * import * as UnifiNetwork from \"@alchemy-unifi/network\";\n * import * as Alchemy from \"alchemy\";\n * import * as Config from \"effect/Config\";\n * import * as Effect from \"effect/Effect\";\n *\n * export default Alchemy.Stack(\n * \t\"HomeNetwork\",\n * \t{ providers: UnifiNetwork.providers(), state: UnifiNetwork.state() },\n * \tEffect.gen(function* () {\n * \t\tconst lan = yield* UnifiNetwork.Network.lookup({ vlanId: 1 });\n * \t\tyield* UnifiNetwork.WifiBroadcast(\"home\", {\n * \t\t\ttype: \"STANDARD\",\n * \t\t\tname: \"Home\",\n * \t\t\tenabled: true,\n * \t\t\tnetwork: lan,\n * \t\t\tsecurityConfiguration: {\n * \t\t\t\ttype: \"WPA2_PERSONAL\",\n * \t\t\t\tpassphrase: yield* Config.Redacted(\"WIFI_PASSPHRASE\"),\n * \t\t\t\tfastRoamingEnabled: false,\n * \t\t\t},\n * \t\t\tbroadcastingFrequenciesGHz: [2.4, 5],\n * \t\t\thideName: false,\n * \t\t\tclientIsolationEnabled: false,\n * \t\t\tmulticastToUnicastConversionEnabled: false,\n * \t\t\tuapsdEnabled: false,\n * \t\t\tchannel2gLockedTo6: false,\n * \t\t\tdtimPeriod2gLockedTo3: false,\n * \t\t\tadvertiseDeviceName: false,\n * \t\t\tarpProxyEnabled: false,\n * \t\t\tbssTransitionEnabled: true,\n * \t\t});\n * \t}),\n * );\n * ```\n */\nexport const WifiBroadcast = Resource<WifiBroadcast>(\"UnifiNetwork.WifiBroadcast\")({\n\tNATIVE_NETWORK,\n\t/**\n\t * A broadcast in the site by name. Dies with a user-facing\n\t * `LookupNotFound` or `LookupAmbiguous`: stack bodies can't fail with typed\n\t * errors.\n\t */\n\tlookup: Effect.fn(\"UnifiNetwork.WifiBroadcast.lookup\")(function* (query: {\n\t\treadonly name: string;\n\t\treadonly site?: string;\n\t}) {\n\t\tconst site = yield* resolveSite(query.site);\n\t\tconst broadcasts = (yield* listWifiBroadcasts(site.id)).filter((b) => b.name === query.name);\n\t\tconst [broadcast] = broadcasts;\n\t\tif (broadcast === undefined) {\n\t\t\treturn yield* new LookupNotFound({ resource: \"Wi-Fi broadcast\", query: describeQuery(query), site });\n\t\t}\n\t\tif (broadcasts.length > 1) {\n\t\t\treturn yield* new LookupAmbiguous({\n\t\t\t\tresource: \"Wi-Fi broadcast\",\n\t\t\t\tquery: describeQuery(query),\n\t\t\t\tsite,\n\t\t\t\tcount: broadcasts.length,\n\t\t\t});\n\t\t}\n\t\treturn yield* attributesOf(site.id, broadcast.id);\n\t}, Effect.orDie),\n});\n\nexport const WifiBroadcastProvider = () =>\n\tProvider.effect(\n\t\tWifiBroadcast,\n\t\tEffect.gen(function* () {\n\t\t\tconst run = yield* captureServices;\n\n\t\t\treturn WifiBroadcast.Provider.of({\n\t\t\t\tlist: () =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite();\n\t\t\t\t\t\t\tconst broadcasts = yield* listWifiBroadcasts(site.id);\n\t\t\t\t\t\t\treturn yield* Effect.forEach(\n\t\t\t\t\t\t\t\tbroadcasts.filter((b) => b.metadata.origin === \"USER_DEFINED\"),\n\t\t\t\t\t\t\t\t(b) => attributesOf(site.id, b.id),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tread: ({ olds, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (output !== undefined) {\n\t\t\t\t\t\t\t\tconst broadcast = yield* getBroadcast(output.siteId, output.wifiBroadcastId);\n\t\t\t\t\t\t\t\treturn broadcast === undefined ? undefined : toAttributes(output.siteId, broadcast);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst site = yield* resolveSite(olds.site);\n\t\t\t\t\t\t\tconst matching = (yield* listWifiBroadcasts(site.id)).filter((b) => b.name === olds.name);\n\t\t\t\t\t\t\tconst [broadcast] = matching;\n\t\t\t\t\t\t\treturn broadcast === undefined || matching.length > 1\n\t\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t\t: Unowned(yield* attributesOf(site.id, broadcast.id));\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdiff: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tif (output === undefined) return undefined;\n\t\t\t\t\t\t\tconst type = planned(news, \"type\");\n\t\t\t\t\t\t\tif (type !== UNKNOWN && type !== output.type) return { action: \"replace\" } satisfies Diff;\n\t\t\t\t\t\t\tconst siteProp = plannedString(news, \"site\");\n\t\t\t\t\t\t\tif (siteProp === UNKNOWN) return undefined;\n\t\t\t\t\t\t\tconst site = yield* resolveSite(siteProp);\n\t\t\t\t\t\t\tif (site.id !== output.siteId) return { action: \"replace\" } satisfies Diff;\n\t\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\treconcile: ({ news, output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\t\t\tconst site = yield* resolveSite(news.site);\n\t\t\t\t\t\t\tconst body = withDefaults(yield* toBody(news, site));\n\t\t\t\t\t\t\tconst observed =\n\t\t\t\t\t\t\t\toutput !== undefined && output.siteId === site.id\n\t\t\t\t\t\t\t\t\t? yield* getBroadcast(site.id, output.wifiBroadcastId)\n\t\t\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\t\t\tconst write = ReadCache.invalidating(broadcastsKey, site.id);\n\t\t\t\t\t\t\tif (observed === undefined) {\n\t\t\t\t\t\t\t\tconst created = yield* withConnection(\n\t\t\t\t\t\t\t\t\tSdk.createWifiBroadcast({ siteId: site.id, body }),\n\t\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\t\treturn toAttributes(site.id, created);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (isCurrent(body, comparable(observed))) return toAttributes(site.id, observed);\n\t\t\t\t\t\t\tconst updated = yield* withConnection(\n\t\t\t\t\t\t\t\tSdk.updateWifiBroadcast({ siteId: site.id, wifiBroadcastId: observed.id, body }),\n\t\t\t\t\t\t\t).pipe(write);\n\t\t\t\t\t\t\treturn toAttributes(site.id, updated);\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\n\t\t\t\tdelete: ({ output }) =>\n\t\t\t\t\trun(\n\t\t\t\t\t\twithConnection(\n\t\t\t\t\t\t\tSdk.deleteWifiBroadcast({ siteId: output.siteId, wifiBroadcastId: output.wifiBroadcastId }),\n\t\t\t\t\t\t).pipe(\n\t\t\t\t\t\t\tEffect.asVoid,\n\t\t\t\t\t\t\tEffect.catchTag(\"NotFound\", () => Effect.void),\n\t\t\t\t\t\t\tReadCache.invalidating(broadcastsKey, output.siteId),\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t});\n\t\t}),\n\t);\n","import * as Provider from \"alchemy/Provider\";\nimport * as Layer from \"effect/Layer\";\nimport { UnifiNetworkAuth } from \"./AuthProvider.ts\";\nimport * as Connection from \"./Connection.ts\";\nimport * as Credentials from \"./Credentials.ts\";\nimport { DnsPolicy, DnsPolicyProvider } from \"./DnsPolicy.ts\";\nimport { FirewallPolicy, FirewallPolicyProvider } from \"./FirewallPolicy.ts\";\nimport { FirewallPolicyOrdering, FirewallPolicyOrderingProvider } from \"./FirewallPolicyOrdering.ts\";\nimport { FirewallZone, FirewallZoneProvider } from \"./FirewallZone.ts\";\nimport { Network, NetworkProvider } from \"./Network.ts\";\nimport { TrafficMatchingList, TrafficMatchingListProvider } from \"./TrafficMatchingList.ts\";\nimport { WifiBroadcast, WifiBroadcastProvider } from \"./WifiBroadcast.ts\";\nimport * as ReadCache from \"./ReadCache.ts\";\n\nexport class Providers extends Provider.ProviderCollection<Providers>()(\"UnifiNetwork\") {}\n\n/**\n * Registers every UniFi Network resource provider, the `UnifiNetwork` auth\n * provider, the lazily-resolved credentials and connection, and the per-run\n * read cache. Merge\n * alongside other cloud `providers()` layers in a Stack.\n *\n * Its requirements (`AuthProviders`, `ProfileStore`, `FileSystem`, `Path`)\n * come from Alchemy's runtime; it provides only its own services.\n *\n * Unlike most providers this doesn't provide an `HttpClient`: ours carries\n * the console's TLS settings, and lifecycle operations run in the stack's\n * merged context, so it would replace other providers' clients. SDK calls\n * get it through `UnifiNetworkConnection` instead.\n */\nexport const providers = () =>\n\tLayer.effect(\n\t\tProviders,\n\t\tProvider.collection([\n\t\t\tFirewallZone,\n\t\t\tNetwork,\n\t\t\tWifiBroadcast,\n\t\t\tTrafficMatchingList,\n\t\t\tFirewallPolicy,\n\t\t\tFirewallPolicyOrdering,\n\t\t\tDnsPolicy,\n\t\t]),\n\t).pipe(\n\t\tLayer.provide(\n\t\t\tLayer.mergeAll(\n\t\t\t\tFirewallZoneProvider(),\n\t\t\t\tNetworkProvider(),\n\t\t\t\tWifiBroadcastProvider(),\n\t\t\t\tTrafficMatchingListProvider(),\n\t\t\t\tFirewallPolicyProvider(),\n\t\t\t\tFirewallPolicyOrderingProvider(),\n\t\t\t\tDnsPolicyProvider(),\n\t\t\t),\n\t\t),\n\t\tLayer.provideMerge(Layer.merge(Connection.layer, ReadCache.layer)),\n\t\tLayer.provideMerge(Credentials.fromAuthProvider()),\n\t\tLayer.provideMerge(UnifiNetworkAuth),\n\t\tLayer.orDie,\n\t);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// \0rolldown/runtime.js
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __exportAll = (all, no_symbols) => {
|
|
4
|
+
let target = {};
|
|
5
|
+
for (var name in all) __defProp(target, name, {
|
|
6
|
+
get: all[name],
|
|
7
|
+
enumerable: true
|
|
8
|
+
});
|
|
9
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
10
|
+
return target;
|
|
11
|
+
};
|
|
12
|
+
export { __exportAll as t };
|