@homeflare/site 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,11 +5,12 @@ hosts) and **pinned values**; `derive()` builds every hostname, address, zone su
5
5
  Access URL, OIDC redirect and vault mount name from it, the same way in every repo.
6
6
 
7
7
  ```sh
8
- bun add @homeflare/site effect@4.0.0-rc.115
8
+ bun add @homeflare/site effect@4.0.1
9
9
  ```
10
10
 
11
- ⛔ **`effect` is a pinned peer.** Effect release candidates are not compatible with each
12
- other, and the override behaviour this package relies on is measured per version.
11
+ ⛔ **`effect` is a pinned peer.** The override behaviour this package relies on is measured
12
+ per Effect version (stable 4.0.1 today), and the `@effect/*` packages are only compatible with
13
+ the exact `effect` they were released with.
13
14
 
14
15
  ★ **Why.** A public stack that types a hostname cannot be run by anyone else, and a stack
15
16
  that re-derives a name its own way drifts from its siblings. Swapping the apex here
@@ -61,18 +62,18 @@ every derived hostname.
61
62
 
62
63
  ## Derived names
63
64
 
64
- | call | example value |
65
- | --------------------------------------- | ----------------------------------------------- |
66
- | `d.vault.host` / `d.vault.apiHost` | `v.example.com` / `api.v.example.com` |
67
- | `d.vault.meshAddr` / `d.vault.lanAddr` | `https://198.18.0.2:8200` / `http://hub.mgmt…` |
68
- | `d.vault.oidcRedirects` | UI callback on the vault host, then `localhost` |
69
- | `d.mgmtZone`, `d.zone('lab')` | `mgmt.example.com`, `lab.example.com` |
70
- | `d.host('n2')`, `d.address('n2','lab')` | `n2.mgmt.example.com`, `198.51.100.12` |
71
- | `d.access.teamDomain` | `https://example-team.cloudflareaccess.com` |
72
- | `d.productHost('wiki')`, `d.productUrl` | `kb.example.com` (label), own `domain` if set |
73
- | `d.serviceUrl('grafana')` | `http://hub.mgmt.example.com:3000` |
74
- | `d.clusterMembers('c1')` | member FQDNs |
75
- | `d.cloudflareMount('main','dns')` | `cloudflare-main-dns` (all: `cloudflareMounts`) |
65
+ | call | example value |
66
+ | ----------------------------------------------- | ----------------------------------------------- |
67
+ | `d.vault.host` / `d.vault.apiHost` | `v.example.com` / `api.v.example.com` |
68
+ | `d.vault.meshAddr` / `d.vault.lanAddr` | `https://198.18.0.2:8200` / `http://hub.mgmt…` |
69
+ | `d.vault.oidcRedirects` | UI callback on the vault host, then `localhost` |
70
+ | `d.mgmtZone`, `d.zone('lab')` | `mgmt.example.com`, `lab.example.com` |
71
+ | `d.host('node-b')`, `d.address('node-b','lab')` | `node-b.mgmt.example.com`, `198.51.100.12` |
72
+ | `d.access.teamDomain` | `https://example-team.cloudflareaccess.com` |
73
+ | `d.productHost('wiki')`, `d.productUrl` | `kb.example.com` (label), own `domain` if set |
74
+ | `d.serviceUrl('grafana')` | `http://hub.mgmt.example.com:3000` |
75
+ | `d.clusterMembers('c1')` | member FQDNs |
76
+ | `d.cloudflareMount('main','dns')` | `cloudflare-main-dns` (all: `cloudflareMounts`) |
76
77
 
77
78
  ⛔ **Unknown keys throw** (`SiteError` code `unknown-key`, listing what is declared). There
78
79
  is no fallback host: a plausible default is how a typo becomes a DNS record.
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "0.1.0";
2
+ var VERSION = "0.1.2";
3
3
 
4
4
  // src/errors.ts
5
5
  class SiteError extends Error {
@@ -284,5 +284,5 @@ function decodeSite(input, options = {}) {
284
284
 
285
285
  export { VERSION, SiteError, unknownKey, lookup, SITE_FORMAT, SiteSchema, checkDeriveVersion, assertStage, addressOn, APEX_ZONE, joinPath, formatIssues, decodeStrict, validateSite, decodeSite };
286
286
 
287
- //# debugId=F23FD3C4E59FE45C64756E2164756E21
288
- //# sourceMappingURL=index-dvzn0279.js.map
287
+ //# debugId=B55825B45C9EA1CC64756E2164756E21
288
+ //# sourceMappingURL=index-j6tpnkxv.js.map
@@ -2,16 +2,16 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts", "../src/errors.ts", "../src/schema.ts", "../src/primitives.ts", "../src/guards.ts", "../src/decode.ts", "../src/net.ts", "../src/references.ts"],
4
4
  "sourcesContent": [
5
- "// ⛔ GENERATED by scripts/sync-versions.ts — do not edit. Source: package.json.\nexport const VERSION: string = '0.1.0';\n",
5
+ "// ⛔ GENERATED by scripts/sync-versions.ts — do not edit. Source: package.json.\nexport const VERSION: string = '0.1.2';\n",
6
6
  "/**\n * The one error this package throws.\n *\n * ★ ONE CLASS, A `code` TO SWITCH ON. Callers mostly print the message and stop; the few\n * that branch (a scaffold harness skipping `derive-version` in a dry run, say) branch on\n * `code`, which is stable, rather than on message text, which is not.\n *\n * ⛔ Messages name paths, keys and the fix — never a value from a live file beyond the\n * one being complained about. A site file is private, and an error string ends up in CI\n * logs.\n */\n\nexport type SiteErrorCode =\n /** The file does not match the schema. `issues` lists `path: problem`. */\n | 'decode'\n /** A value points at something the file does not declare (a zone, host, network). */\n | 'reference'\n /** A consumer asked for a product, service, zone, host or pin nobody declared. */\n | 'unknown-key'\n /** The file was reviewed against a different `@homeflare/site` version. */\n | 'derive-version'\n /** A non-live site was asked to plan stage `live`. */\n | 'stage'\n /** The observed vault or account is not the one the site describes. */\n | 'identity'\n /** The file could not be located, read or parsed. */\n | 'load'\n /** The file is not committed on `main` and `siteDev` was not set. */\n | 'checkout'\n /** An environment override was refused. */\n | 'override';\n\nexport class SiteError extends Error {\n override readonly name = 'SiteError';\n readonly code: SiteErrorCode;\n /** One line per problem, each naming its path. Empty when the message says it all. */\n readonly issues: readonly string[];\n\n constructor(code: SiteErrorCode, message: string, issues: readonly string[] = []) {\n super(issues.length === 0 ? message : `${message}\\n ${issues.join('\\n ')}`);\n this.code = code;\n this.issues = issues;\n }\n}\n\n/** Throw `unknown-key`, listing what IS declared so the fix is one glance away. */\nexport function unknownKey(kind: string, key: string, declared: Iterable<string>): never {\n const known = [...declared].sort();\n const list = known.length === 0 ? 'none are declared' : `declared: ${known.join(', ')}`;\n throw new SiteError('unknown-key', `unknown ${kind} \"${key}\" (${list})`);\n}\n\n/**\n * Look a key up in a record, or throw `unknown-key`.\n * ⛔ No fallback, ever. A plausible default host is how a typo becomes a live DNS record.\n */\nexport function lookup<V>(kind: string, record: Readonly<Record<string, V>>, key: string): V {\n if (!Object.hasOwn(record, key)) unknownKey(kind, key, Object.keys(record));\n return record[key] as V;\n}\n",
7
7
  "/**\n * The site file: BASE VALUES and PINNED values, nothing derived.\n *\n * ★ BASE VALUES are the few things every name is relative to — the apex, a handful of\n * labels, networks and hosts. `derive()` builds hostnames and addresses from them, so\n * swapping the apex changes every derived name by substitution and nothing else.\n *\n * ⛔ PINNED VALUES ARE NEVER DERIVED. Physical names (Workers, D1, KV, R2, Durable Objects)\n * follow no rule, and deriving one would REPLACE it: a derived D1 name deletes the data\n * behind the old one. Certificate hostnames on adopted certificates reissue and revoke\n * the certificate a host is still serving if they change. Adopted ids name objects that\n * already exist. Policy and SSH principal lists are explicit so a break-glass principal\n * can never vanish because a network leg was renamed.\n *\n * ⛔ UNKNOWN KEYS ARE REFUSED (see decode.ts). A misspelt optional field would otherwise\n * decode as \"absent\" and the file would look valid while saying less than its author\n * meant.\n */\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\nimport {\n AbsolutePath,\n AccountId,\n CertificateName,\n GithubOwner,\n HostNumber,\n Hostname,\n Ipv4,\n Ipv4Cidr,\n Label,\n ListEntry,\n NonBlank,\n PhysicalName,\n PinKey,\n Port,\n SemVer,\n} from './primitives.ts';\n\n/** The site file format this package reads. A new format is a new literal, never a reuse. */\nexport const SITE_FORMAT = 1;\n\nconst emptyRecord = <S extends Schema.Top>(schema: S) =>\n schema.pipe(Schema.withDecodingDefaultKey(Effect.succeed({})));\n\nconst Scheme = Schema.Literals(['http', 'https']);\n\n/**\n * A machine. Its FQDN is `<key>.<zone>`; its address on a network is that network's base\n * plus `legs[network]`.\n * ★ `zone: \"apex\"` places the host directly under the apex (`docs.<apex>`).\n */\nconst Host = Schema.Struct({\n zone: Schema.optionalKey(Label),\n aliases: Schema.optionalKey(Schema.Array(Label)),\n legs: Schema.optionalKey(Schema.Record(Label, HostNumber)),\n});\n\n/** A public product: `<label ?? key>.<apex>`, or its own `domain`. Never both. */\nconst Product = Schema.Struct({\n label: Schema.optionalKey(Label),\n domain: Schema.optionalKey(Hostname),\n});\n\n/** An internal endpoint on a declared host. ⛔ The scheme is explicit, never guessed. */\nconst Service = Schema.Struct({ host: Label, port: Port, scheme: Scheme });\n\nconst Account = Schema.Struct({\n id: AccountId,\n zones: Schema.Array(Hostname),\n /** Each surface becomes an OpenBao mount `cloudflare-<alias>-<surface>`. */\n surfaces: Schema.Array(Label),\n});\n\nconst Vault = Schema.Struct({\n /** `<vault-host>` = `<label>.<apex>`. */\n label: Label,\n /**\n * `<vault-api-host>` = `<apiLabel>.<vault-host>`.\n * ⚠️ NOT `vaultApi` beside a `vault` label. Measured 2026-09-21 on effect 4.0.0-rc.115:\n * constant-cased env names nest by prefix, so `HF_SITE_VAULT_API` made the provider\n * read `vault` as a record and the whole decode failed \"Expected string\". No field\n * name here may be another's name plus `_…`; tests/overrides.test.ts asserts it.\n */\n apiLabel: Label,\n /**\n * ⛔ PINNED IDENTITY: the `cluster_name` the vault reports on `sys/health`. Set at init,\n * never derived; plans compare it before touching anything (see identity.ts).\n */\n clusterName: NonBlank,\n namespace: Schema.String.pipe(Schema.withDecodingDefaultKey(Effect.succeed(''))),\n port: Port.pipe(Schema.withDecodingDefaultKey(Effect.succeed(8200))),\n /** The vault's address on the private Mesh path. Machines use it; strict TLS applies. */\n meshAddress: Ipv4,\n /** The LAN pass-through proxy: a declared host, its port, and its scheme. */\n lan: Schema.Struct({ host: Label, port: Port, scheme: Scheme }),\n oidcMount: Label.pipe(Schema.withDecodingDefaultKey(Effect.succeed('oidc'))),\n /** The port `bao login -method=oidc` listens on for its localhost callback. */\n cliCallbackPort: Port.pipe(Schema.withDecodingDefaultKey(Effect.succeed(8250))),\n});\n\nconst Pinned = Schema.Struct({\n names: emptyRecord(Schema.Record(PinKey, PhysicalName)),\n certificates: emptyRecord(Schema.Record(PinKey, Schema.NonEmptyArray(CertificateName))),\n adopted: emptyRecord(Schema.Record(PinKey, NonBlank)),\n policies: emptyRecord(Schema.Record(PinKey, Schema.Array(ListEntry))),\n sshPrincipals: emptyRecord(Schema.Record(PinKey, Schema.Array(ListEntry))),\n});\n\nexport const SiteSchema = Schema.Struct({\n /** The file format. Only {@link SITE_FORMAT} decodes. */\n version: Schema.Literal(SITE_FORMAT),\n /**\n * The `@homeflare/site` version whose derive rules this file was reviewed against.\n * ⛔ A repo refuses to load when its installed package differs (see guards.ts), so a\n * derive change can never rename live objects without someone bumping this line.\n */\n deriveVersion: SemVer,\n /** ⛔ Only a `live` site may plan stage `live` (see guards.ts). */\n kind: Schema.Literals(['live', 'testing', 'example']),\n apex: Hostname,\n /**\n * Zone suffixes under the apex: `{ \"mgmt\": \"mgmt\" }` gives `mgmt.<apex>`.\n * ⛔ `mgmt` is required (it is `<mgmt-zone>`), and the key `apex` is reserved.\n */\n zones: Schema.StructWithRest(Schema.Struct({ mgmt: Label }), [Schema.Record(Label, Label)]),\n networks: Schema.Record(Label, Ipv4Cidr),\n hosts: Schema.Record(Label, Host),\n clusters: emptyRecord(Schema.Record(Label, Schema.Struct({ members: Schema.Array(Label) }))),\n products: emptyRecord(Schema.Record(Label, Product)),\n services: emptyRecord(Schema.Record(Label, Service)),\n cloudflare: Schema.Struct({\n accounts: Schema.Record(Label, Account),\n access: Schema.Struct({ team: Label }),\n }),\n github: Schema.Struct({ owner: GithubOwner }),\n paths: Schema.Struct({ estateRoot: AbsolutePath }),\n vault: Vault,\n pinned: Pinned.pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))),\n});\n\n/** A decoded site: defaults applied, every value checked. */\nexport type Site = typeof SiteSchema.Type;\n/** What a site file may contain before defaults are applied. */\nexport type SiteInput = typeof SiteSchema.Encoded;\n",
8
- "/**\n * The value shapes a site file is made of. Each one refuses at decode time, naming its path,\n * so a typo fails when the file is read rather than when a plan renames something.\n *\n * ⛔ LOWERCASE ONLY for DNS names. Cloudflare, OpenBao and ssh compare names as strings in\n * places, and `Mgmt.Example.com` is not `mgmt.example.com` to a principal list.\n */\nimport * as Schema from 'effect/Schema';\n\n/** One DNS label: `v`, `mgmt`, `n1`. Also the shape of every key a consumer looks up. */\nexport const Label = Schema.String.check(\n Schema.isPattern(/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/, {\n message: 'Expected a lowercase DNS label (a-z, 0-9, inner hyphens, at most 63)',\n }),\n);\n\n/** A fully qualified name with at least two labels: `example.com`, `shop.example.net`. */\nexport const Hostname = Schema.String.check(\n Schema.isPattern(\n /^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]([a-z0-9-]{0,61}[a-z0-9])?$/,\n { message: 'Expected a lowercase fully qualified hostname such as example.com' },\n ),\n);\n\n/** A certificate name: a hostname, or one leading wildcard label. */\nexport const CertificateName = Schema.String.check(\n Schema.isPattern(\n /^(\\*\\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]([a-z0-9-]{0,61}[a-z0-9])?$/,\n { message: 'Expected a hostname, optionally with one leading *. wildcard' },\n ),\n);\n\nconst OCTET = '(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])';\n\n/** A dotted-quad IPv4 address, every octet 0–255. */\nexport const Ipv4 = Schema.String.check(\n Schema.isPattern(new RegExp(`^(${OCTET}\\\\.){3}${OCTET}$`), {\n message: 'Expected an IPv4 address such as 192.0.2.10',\n }),\n);\n\n/** Parse `a.b.c.d/p` into a 32-bit base and a prefix length. `undefined` when malformed. */\nexport function parseCidr(cidr: string): { base: number; prefix: number } | undefined {\n const match = new RegExp(`^((${OCTET}\\\\.){3}${OCTET})/([0-9]{1,2})$`).exec(cidr);\n if (match === null) return undefined;\n const prefix = Number(match[match.length - 1]);\n const base = (match[1] ?? '').split('.').reduce((acc, octet) => acc * 256 + Number(octet), 0);\n return { base, prefix };\n}\n\n/**\n * An IPv4 network, `/8` to `/30`, written at its network address.\n *\n * ⚠️ `192.0.2.5/24` is refused, not normalised. A host address with a prefix usually means\n * someone pasted an interface address where a network belongs; normalising it would hide\n * the mistake and derive every leg address from the wrong idea of the network.\n */\nexport const Ipv4Cidr = Schema.String.check(\n Schema.makeFilter(\n (value: string) => {\n const parsed = parseCidr(value);\n if (parsed === undefined) return 'Expected an IPv4 network such as 192.0.2.0/24';\n if (parsed.prefix < 8 || parsed.prefix > 30) return 'Expected a prefix between /8 and /30';\n const hostBits = 2 ** (32 - parsed.prefix);\n if (parsed.base % hostBits !== 0) return 'Expected the network address (host bits zero)';\n return undefined;\n },\n { expected: 'an IPv4 network' },\n ),\n);\n\n/** A host's number on a network: `.11` on a /24 is 11. Checked against the prefix later. */\nexport const HostNumber = Schema.Number.check(\n Schema.isInt(),\n Schema.isBetween({ minimum: 1, maximum: 16_777_214 }),\n);\n\nexport const Port = Schema.Number.check(\n Schema.isInt(),\n Schema.isBetween({ minimum: 1, maximum: 65_535 }),\n);\n\n/** A Cloudflare account id: 32 lowercase hex characters. */\nexport const AccountId = Schema.String.check(\n Schema.isPattern(/^[0-9a-f]{32}$/, {\n message: 'Expected a 32-character lowercase hex account id',\n }),\n);\n\n/** A GitHub user or organisation login. GitHub itself allows uppercase here. */\nexport const GithubOwner = Schema.String.check(\n Schema.isPattern(/^[A-Za-z0-9]([A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/, {\n message: 'Expected a GitHub login (letters, digits, inner hyphens)',\n }),\n);\n\n/** An absolute POSIX path with no trailing slash: `/opt/example`. */\nexport const AbsolutePath = Schema.String.check(\n Schema.isPattern(/^(\\/[^/\\0]+)+$/, {\n message: 'Expected an absolute path without a trailing slash',\n }),\n);\n\n/** A semver version, as npm writes one: `0.1.0`, `1.0.0-rc.1`. */\nexport const SemVer = Schema.String.check(\n Schema.isPattern(/^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.-]+)?$/, { message: 'Expected a semver version' }),\n);\n\n/** A key into a `pinned` table: `alerts.d1`, `ssh-host.host`. Dotted, lowercase. */\nexport const PinKey = Schema.String.check(\n Schema.isPattern(/^[a-z0-9]+([._-][a-z0-9]+)*$/, {\n message: 'Expected a lowercase dotted key such as alerts.d1',\n }),\n);\n\n/**\n * A physical resource name: Worker, D1, KV, R2, Durable Object class.\n * ★ Case is allowed because Durable Object bindings name PascalCase classes.\n */\nexport const PhysicalName = Schema.String.check(\n Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, {\n message: 'Expected a resource name (letters, digits, dot, dash, underscore)',\n }),\n);\n\n/**\n * One SSH principal or policy entry.\n * ⛔ No commas and no whitespace. OpenBao stores principal lists comma-joined, so an entry\n * holding a comma silently becomes two principals, and a stray space becomes a name no\n * host will ever present.\n */\nexport const ListEntry = Schema.String.check(\n Schema.isPattern(/^[^\\s,]+$/, { message: 'Expected one entry with no commas or whitespace' }),\n);\n\n/** A free-form identifier that must not be blank: cluster names, adopted ids, CNs. */\nexport const NonBlank = Schema.String.check(\n Schema.isPattern(/^\\S(.*\\S)?$/, { message: 'Expected a non-blank value without edge spaces' }),\n);\n",
8
+ "/**\n * The value shapes a site file is made of. Each one refuses at decode time, naming its path,\n * so a typo fails when the file is read rather than when a plan renames something.\n *\n * ⛔ LOWERCASE ONLY for DNS names. Cloudflare, OpenBao and ssh compare names as strings in\n * places, and `Mgmt.Example.com` is not `mgmt.example.com` to a principal list.\n */\nimport * as Schema from 'effect/Schema';\n\n/** One DNS label: `v`, `mgmt`, `node-a`. Also the shape of every key a consumer looks up. */\nexport const Label = Schema.String.check(\n Schema.isPattern(/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/, {\n message: 'Expected a lowercase DNS label (a-z, 0-9, inner hyphens, at most 63)',\n }),\n);\n\n/** A fully qualified name with at least two labels: `example.com`, `shop.example.net`. */\nexport const Hostname = Schema.String.check(\n Schema.isPattern(\n /^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]([a-z0-9-]{0,61}[a-z0-9])?$/,\n { message: 'Expected a lowercase fully qualified hostname such as example.com' },\n ),\n);\n\n/** A certificate name: a hostname, or one leading wildcard label. */\nexport const CertificateName = Schema.String.check(\n Schema.isPattern(\n /^(\\*\\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]([a-z0-9-]{0,61}[a-z0-9])?$/,\n { message: 'Expected a hostname, optionally with one leading *. wildcard' },\n ),\n);\n\nconst OCTET = '(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])';\n\n/** A dotted-quad IPv4 address, every octet 0–255. */\nexport const Ipv4 = Schema.String.check(\n Schema.isPattern(new RegExp(`^(${OCTET}\\\\.){3}${OCTET}$`), {\n message: 'Expected an IPv4 address such as 192.0.2.10',\n }),\n);\n\n/** Parse `a.b.c.d/p` into a 32-bit base and a prefix length. `undefined` when malformed. */\nexport function parseCidr(cidr: string): { base: number; prefix: number } | undefined {\n const match = new RegExp(`^((${OCTET}\\\\.){3}${OCTET})/([0-9]{1,2})$`).exec(cidr);\n if (match === null) return undefined;\n const prefix = Number(match[match.length - 1]);\n const base = (match[1] ?? '').split('.').reduce((acc, octet) => acc * 256 + Number(octet), 0);\n return { base, prefix };\n}\n\n/**\n * An IPv4 network, `/8` to `/30`, written at its network address.\n *\n * ⚠️ `192.0.2.5/24` is refused, not normalised. A host address with a prefix usually means\n * someone pasted an interface address where a network belongs; normalising it would hide\n * the mistake and derive every leg address from the wrong idea of the network.\n */\nexport const Ipv4Cidr = Schema.String.check(\n Schema.makeFilter(\n (value: string) => {\n const parsed = parseCidr(value);\n if (parsed === undefined) return 'Expected an IPv4 network such as 192.0.2.0/24';\n if (parsed.prefix < 8 || parsed.prefix > 30) return 'Expected a prefix between /8 and /30';\n const hostBits = 2 ** (32 - parsed.prefix);\n if (parsed.base % hostBits !== 0) return 'Expected the network address (host bits zero)';\n return undefined;\n },\n { expected: 'an IPv4 network' },\n ),\n);\n\n/** A host's number on a network: `.11` on a /24 is 11. Checked against the prefix later. */\nexport const HostNumber = Schema.Number.check(\n Schema.isInt(),\n Schema.isBetween({ minimum: 1, maximum: 16_777_214 }),\n);\n\nexport const Port = Schema.Number.check(\n Schema.isInt(),\n Schema.isBetween({ minimum: 1, maximum: 65_535 }),\n);\n\n/** A Cloudflare account id: 32 lowercase hex characters. */\nexport const AccountId = Schema.String.check(\n Schema.isPattern(/^[0-9a-f]{32}$/, {\n message: 'Expected a 32-character lowercase hex account id',\n }),\n);\n\n/** A GitHub user or organisation login. GitHub itself allows uppercase here. */\nexport const GithubOwner = Schema.String.check(\n Schema.isPattern(/^[A-Za-z0-9]([A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/, {\n message: 'Expected a GitHub login (letters, digits, inner hyphens)',\n }),\n);\n\n/** An absolute POSIX path with no trailing slash: `/opt/example`. */\nexport const AbsolutePath = Schema.String.check(\n Schema.isPattern(/^(\\/[^/\\0]+)+$/, {\n message: 'Expected an absolute path without a trailing slash',\n }),\n);\n\n/** A semver version, as npm writes one: `0.1.0`, `1.0.0-rc.1`. */\nexport const SemVer = Schema.String.check(\n Schema.isPattern(/^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z.-]+)?$/, { message: 'Expected a semver version' }),\n);\n\n/** A key into a `pinned` table: `alerts.d1`, `ssh-host.host`. Dotted, lowercase. */\nexport const PinKey = Schema.String.check(\n Schema.isPattern(/^[a-z0-9]+([._-][a-z0-9]+)*$/, {\n message: 'Expected a lowercase dotted key such as alerts.d1',\n }),\n);\n\n/**\n * A physical resource name: Worker, D1, KV, R2, Durable Object class.\n * ★ Case is allowed because Durable Object bindings name PascalCase classes.\n */\nexport const PhysicalName = Schema.String.check(\n Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, {\n message: 'Expected a resource name (letters, digits, dot, dash, underscore)',\n }),\n);\n\n/**\n * One SSH principal or policy entry.\n * ⛔ No commas and no whitespace. OpenBao stores principal lists comma-joined, so an entry\n * holding a comma silently becomes two principals, and a stray space becomes a name no\n * host will ever present.\n */\nexport const ListEntry = Schema.String.check(\n Schema.isPattern(/^[^\\s,]+$/, { message: 'Expected one entry with no commas or whitespace' }),\n);\n\n/** A free-form identifier that must not be blank: cluster names, adopted ids, CNs. */\nexport const NonBlank = Schema.String.check(\n Schema.isPattern(/^\\S(.*\\S)?$/, { message: 'Expected a non-blank value without edge spaces' }),\n);\n",
9
9
  "/**\n * Guards that refuse a plan before it can touch the wrong thing.\n *\n * ⛔ EACH ONE FAILS CLOSED. A guard that warns is a guard nobody reads in a CI log.\n */\nimport { SiteError } from './errors.ts';\nimport type { Site } from './schema.ts';\n\n/**\n * Refuse a site reviewed against a different `@homeflare/site` than the one installed.\n *\n * ★ WHY EXACT EQUALITY. Derive rules are this package's behaviour. If a new version builds\n * one hostname differently, every repo on it would plan a rename — a REPLACE — without\n * anyone having looked. Holding `deriveVersion` equal to the installed version makes that\n * a deliberate, reviewed one-line bump in the site file instead.\n */\nexport function checkDeriveVersion(site: Site, installed: string): void {\n if (site.deriveVersion === installed) return;\n throw new SiteError(\n 'derive-version',\n `the site file was reviewed against @homeflare/site ${site.deriveVersion}, ` +\n `but ${installed} is installed. Read the CHANGELOG between them for derive changes, ` +\n `then set \"deriveVersion\": \"${installed}\" — or install ${site.deriveVersion}.`,\n );\n}\n\n/** Stages as a deploy tool names them. Only `live` is special here. */\nexport type Stage = string;\n\n/**\n * ⛔ STAGE `live` ONLY WITH A LIVE SITE. An example or testing site can never plan\n * against live state — its account ids and cluster name are placeholders, and a plan\n * against real state would read every live object as \"not declared: delete\".\n */\nexport function assertStage(site: Site, stage: Stage): void {\n if (stage !== 'live' || site.kind === 'live') return;\n throw new SiteError(\n 'stage',\n `stage \"live\" needs a live site; this one is kind \"${site.kind}\". ` +\n `Point HF_SITE_FILE at the live site file, or plan a non-live stage.`,\n );\n}\n",
10
10
  "/**\n * Decode a site from plain data. Runtime-neutral: no filesystem, no environment.\n *\n * ★ Workers and scripts that already hold the JSON (a binding, a fetched file) call\n * `decodeSite`. Scripts that read `HF_SITE_FILE` call `loadSite` from\n * `@homeflare/site/load`, which ends up here too.\n */\nimport * as Cause from 'effect/Cause';\nimport * as Exit from 'effect/Exit';\nimport * as Schema from 'effect/Schema';\nimport * as SchemaIssue from 'effect/SchemaIssue';\nimport { SiteError } from './errors.ts';\nimport { checkDeriveVersion } from './guards.ts';\nimport { referenceIssues } from './references.ts';\nimport { type Site, SiteSchema } from './schema.ts';\nimport { VERSION } from './version.ts';\n\n/**\n * ⛔ `onExcessProperty: 'error'` IS LOAD-BEARING, twice over. Measured 2026-09-21 on\n * effect 4.0.0-rc.115: with the default (`ignore`), a misspelt field is dropped, AND a\n * record entry whose key fails the key schema (`\"Mgmt\"` in `networks`) is dropped too —\n * the file decodes, one network short, with no error at all.\n * ⚠️ With it on, that bad record key reports as \"excess property\", which reads oddly.\n * `formatIssues` rewrites it to say what it actually means.\n */\nconst strict = Schema.decodeUnknownExit(SiteSchema, { onExcessProperty: 'error', errors: 'all' });\nconst standard = SchemaIssue.makeFormatterStandardSchemaV1();\n\nconst EXCESS = 'Expected no excess property';\n\n/**\n * `pinned.certificates[\"vault.origin\"].0` — a key that itself holds a dot is bracketed,\n * or the path would read as two levels that do not exist.\n */\nexport function joinPath(segments: readonly string[]): string {\n return segments\n .map((segment, i) => {\n if (segment.includes('.')) return `[${JSON.stringify(segment)}]`;\n return i === 0 ? segment : `.${segment}`;\n })\n .join('');\n}\n\n/** Render a schema issue as `path: problem` lines, dropping a leading `skip` segment. */\nexport function formatIssues(issue: SchemaIssue.Issue, skip?: string): readonly string[] {\n return standard(issue).issues.map((entry) => {\n const segments = (entry.path ?? []).map((part) =>\n typeof part === 'object' && part !== null ? String(part.key) : String(part),\n );\n if (skip !== undefined && segments[0] === skip) segments.shift();\n const path = segments.length === 0 ? '(root)' : joinPath(segments);\n const message =\n entry.message === EXCESS\n ? 'unknown key (a misspelt field, or a record key that is not a lowercase label)'\n : entry.message;\n return `${path}: ${message}`;\n });\n}\n\n/** Schema-only decode. Throws `decode` listing every problem, each with its path. */\nexport function decodeStrict(input: unknown): Site {\n const exit = strict(input);\n if (Exit.isSuccess(exit)) return exit.value;\n const error = Cause.squash(exit.cause);\n if (Schema.isSchemaError(error)) {\n throw new SiteError(\n 'decode',\n 'the site file does not match the schema',\n formatIssues(error.issue),\n );\n }\n throw error;\n}\n\nexport interface ValidateOptions {\n /**\n * The `@homeflare/site` version to hold `deriveVersion` against. Defaults to the\n * installed one — which is the point; override only to test the refusal.\n */\n readonly installed?: string;\n}\n\n/** Cross-field references, then the derive-version guard. Returns the same site. */\nexport function validateSite(site: Site, options: ValidateOptions = {}): Site {\n const issues = referenceIssues(site);\n if (issues.length > 0) {\n throw new SiteError('reference', 'the site file names things it does not declare', issues);\n }\n checkDeriveVersion(site, options.installed ?? VERSION);\n return site;\n}\n\n/**\n * Decode and validate a site from plain data (already-parsed JSON).\n * Throws {@link SiteError}: `decode`, `reference` or `derive-version`.\n */\nexport function decodeSite(input: unknown, options: ValidateOptions = {}): Site {\n return validateSite(decodeStrict(input), options);\n}\n",
11
11
  "/**\n * IPv4 arithmetic for leg addresses: a network's base plus a host number.\n *\n * ★ WHY A HOST NUMBER AND NOT A LAST OCTET. On a /24 they are the same thing, and that is\n * the common case. On a /23 or a /16 a \"last octet\" is ambiguous, while \"the 11th\n * address after the network address\" is not. The /24 reading stays exact.\n */\nimport { parseCidr } from './primitives.ts';\n\nfunction dotted(value: number): string {\n return [24, 16, 8, 0].map((shift) => Math.floor(value / 2 ** shift) % 256).join('.');\n}\n\n/** The largest usable host number on a network (the broadcast address is excluded). */\nexport function lastHostNumber(cidr: string): number {\n const parsed = parseCidr(cidr);\n if (parsed === undefined) throw new Error(`not an IPv4 network: ${cidr}`);\n return 2 ** (32 - parsed.prefix) - 2;\n}\n\n/** `addressOn('192.0.2.0/24', 11)` is `192.0.2.11`. */\nexport function addressOn(cidr: string, hostNumber: number): string {\n const parsed = parseCidr(cidr);\n if (parsed === undefined) throw new Error(`not an IPv4 network: ${cidr}`);\n if (hostNumber < 1 || hostNumber > lastHostNumber(cidr)) {\n throw new Error(`host number ${hostNumber} does not fit ${cidr}`);\n }\n return dotted(parsed.base + hostNumber);\n}\n",
12
12
  "/**\n * Checks that span fields: every zone, host and network a value names is declared.\n *\n * ★ WHY NOT IN THE SCHEMA. A struct-level filter reports one issue at the struct's own\n * path; these checks want to name the exact field (`services.grafana.host`), and they\n * want to report every broken reference at once rather than the first.\n */\nimport { lastHostNumber } from './net.ts';\nimport type { Site } from './schema.ts';\n\n/** The zone key that means the apex itself. Reserved: a site may not declare it. */\nexport const APEX_ZONE = 'apex';\n\nfunction hostHasZone(site: Site, host: string): boolean {\n return Object.hasOwn(site.hosts, host) && site.hosts[host]?.zone !== undefined;\n}\n\nfunction checkHosts(site: Site, issues: string[]): void {\n for (const [key, host] of Object.entries(site.hosts)) {\n const zone = host.zone;\n if (zone !== undefined && zone !== APEX_ZONE && !Object.hasOwn(site.zones, zone)) {\n issues.push(`hosts.${key}.zone: \"${zone}\" is not a declared zone or \"${APEX_ZONE}\"`);\n }\n for (const [network, number] of Object.entries(host.legs ?? {})) {\n const cidr = site.networks[network];\n if (cidr === undefined) {\n issues.push(`hosts.${key}.legs.${network}: \"${network}\" is not a declared network`);\n } else if (number > lastHostNumber(cidr)) {\n issues.push(`hosts.${key}.legs.${network}: host number ${number} does not fit ${network}`);\n }\n }\n }\n}\n\nfunction checkZonedHost(site: Site, path: string, host: string, issues: string[]): void {\n if (!Object.hasOwn(site.hosts, host)) {\n issues.push(`${path}: \"${host}\" is not a declared host`);\n } else if (!hostHasZone(site, host)) {\n issues.push(`${path}: host \"${host}\" has no zone, so it has no hostname`);\n }\n}\n\nfunction checkAccounts(site: Site, issues: string[]): void {\n for (const [alias, account] of Object.entries(site.cloudflare.accounts)) {\n const seen = new Set<string>();\n for (const surface of account.surfaces) {\n // ⚠️ A repeated surface derives the same mount name twice; the second declaration\n // would silently be the same mount, so a typo'd duplicate is refused here.\n if (seen.has(surface)) {\n issues.push(`cloudflare.accounts.${alias}.surfaces: \"${surface}\" is listed twice`);\n }\n seen.add(surface);\n }\n }\n}\n\n/** Every broken reference in `site`, each naming its path. Empty when the site is sound. */\nexport function referenceIssues(site: Site): readonly string[] {\n const issues: string[] = [];\n\n if (Object.hasOwn(site.zones, APEX_ZONE)) {\n issues.push(`zones.${APEX_ZONE}: reserved — a host with zone \"${APEX_ZONE}\" sits on the apex`);\n }\n checkHosts(site, issues);\n for (const [key, cluster] of Object.entries(site.clusters)) {\n for (const [i, member] of cluster.members.entries()) {\n checkZonedHost(site, `clusters.${key}.members.${i}`, member, issues);\n }\n }\n for (const [key, product] of Object.entries(site.products)) {\n if (product.label !== undefined && product.domain !== undefined) {\n issues.push(`products.${key}: set label OR domain, not both`);\n }\n }\n for (const [key, service] of Object.entries(site.services)) {\n checkZonedHost(site, `services.${key}.host`, service.host, issues);\n }\n checkZonedHost(site, 'vault.lan.host', site.vault.lan.host, issues);\n checkAccounts(site, issues);\n\n return issues;\n}\n"
13
13
  ],
14
14
  "mappings": "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",
15
- "debugId": "F23FD3C4E59FE45C64756E2164756E21",
15
+ "debugId": "B55825B45C9EA1CC64756E2164756E21",
16
16
  "names": []
17
17
  }
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  lookup,
13
13
  unknownKey,
14
14
  validateSite
15
- } from "./index-dvzn0279.js";
15
+ } from "./index-j6tpnkxv.js";
16
16
  // src/derive.ts
17
17
  function zoneOf(site, key) {
18
18
  if (key === APEX_ZONE)
package/dist/load.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  decodeStrict,
5
5
  formatIssues,
6
6
  validateSite
7
- } from "./index-dvzn0279.js";
7
+ } from "./index-j6tpnkxv.js";
8
8
 
9
9
  // src/load.ts
10
10
  import { readFile } from "node:fs/promises";
@@ -6,7 +6,7 @@
6
6
  * places, and `Mgmt.Example.com` is not `mgmt.example.com` to a principal list.
7
7
  */
8
8
  import * as Schema from 'effect/Schema';
9
- /** One DNS label: `v`, `mgmt`, `n1`. Also the shape of every key a consumer looks up. */
9
+ /** One DNS label: `v`, `mgmt`, `node-a`. Also the shape of every key a consumer looks up. */
10
10
  export declare const Label: Schema.String;
11
11
  /** A fully qualified name with at least two labels: `example.com`, `shop.example.net`. */
12
12
  export declare const Hostname: Schema.String;
@@ -1 +1 @@
1
- {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,yFAAyF;AACzF,eAAO,MAAM,KAAK,eAIjB,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,QAAQ,eAKpB,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,eAAe,eAK3B,CAAC;AAIF,qDAAqD;AACrD,eAAO,MAAM,IAAI,eAIhB,CAAC;AAEF,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAMpF;AAED;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,eAYpB,CAAC;AAEF,4FAA4F;AAC5F,eAAO,MAAM,UAAU,eAGtB,CAAC;AAEF,eAAO,MAAM,IAAI,eAGhB,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,eAIrB,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,WAAW,eAIvB,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,YAAY,eAIxB,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,MAAM,eAElB,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,MAAM,eAIlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,eAIxB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,eAErB,CAAC;AAEF,sFAAsF;AACtF,eAAO,MAAM,QAAQ,eAEpB,CAAC"}
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,6FAA6F;AAC7F,eAAO,MAAM,KAAK,eAIjB,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,QAAQ,eAKpB,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,eAAe,eAK3B,CAAC;AAIF,qDAAqD;AACrD,eAAO,MAAM,IAAI,eAIhB,CAAC;AAEF,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAMpF;AAED;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,eAYpB,CAAC;AAEF,4FAA4F;AAC5F,eAAO,MAAM,UAAU,eAGtB,CAAC;AAEF,eAAO,MAAM,IAAI,eAGhB,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,eAIrB,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,WAAW,eAIvB,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,YAAY,eAIxB,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,MAAM,eAElB,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,MAAM,eAIlB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,eAIxB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,eAErB,CAAC;AAEF,sFAAsF;AACtF,eAAO,MAAM,QAAQ,eAEpB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homeflare/site",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "One typed site config: base values, pinned names, and every hostname and address derived from them.",
5
5
  "homepage": "https://github.com/taslabs-net/homeflare-kit/tree/main/packages/site#readme",
6
6
  "bugs": "https://github.com/taslabs-net/homeflare-kit/issues",
@@ -36,6 +36,6 @@
36
36
  "access": "public"
37
37
  },
38
38
  "peerDependencies": {
39
- "effect": "4.0.0-rc.115"
39
+ "effect": "4.0.1"
40
40
  }
41
41
  }
package/site.example.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "deriveVersion": "0.1.0",
3
+ "deriveVersion": "0.1.2",
4
4
  "kind": "example",
5
5
  "apex": "example.com",
6
6
  "zones": { "mgmt": "mgmt", "lab": "lab" },
@@ -16,13 +16,13 @@
16
16
  "aliases": ["gateway"],
17
17
  "legs": { "mgmt": 250, "services": 250 }
18
18
  },
19
- "n1": { "zone": "mgmt", "legs": { "mgmt": 11, "lab": 11 } },
20
- "n2": { "zone": "mgmt", "legs": { "mgmt": 12, "lab": 12 } },
21
- "n3": { "zone": "mgmt", "legs": { "mgmt": 13, "lab": 13 } },
19
+ "node-a": { "zone": "mgmt", "legs": { "mgmt": 11, "lab": 11 } },
20
+ "node-b": { "zone": "mgmt", "legs": { "mgmt": 12, "lab": 12 } },
21
+ "node-c": { "zone": "mgmt", "legs": { "mgmt": 13, "lab": 13 } },
22
22
  "backup": { "zone": "lab", "legs": { "mgmt": 7, "storage": 7 } },
23
23
  "docs": { "zone": "apex" }
24
24
  },
25
- "clusters": { "c1": { "members": ["n1", "n2", "n3"] } },
25
+ "clusters": { "c1": { "members": ["node-a", "node-b", "node-c"] } },
26
26
  "products": {
27
27
  "alerts": {},
28
28
  "wiki": { "label": "kb" },
@@ -70,9 +70,9 @@
70
70
  "hub",
71
71
  "gateway",
72
72
  "192.0.2.250",
73
- "n1",
74
- "n2",
75
- "n3",
73
+ "node-a",
74
+ "node-b",
75
+ "node-c",
76
76
  "192.0.2.11",
77
77
  "198.51.100.11"
78
78
  ]
package/src/primitives.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import * as Schema from 'effect/Schema';
9
9
 
10
- /** One DNS label: `v`, `mgmt`, `n1`. Also the shape of every key a consumer looks up. */
10
+ /** One DNS label: `v`, `mgmt`, `node-a`. Also the shape of every key a consumer looks up. */
11
11
  export const Label = Schema.String.check(
12
12
  Schema.isPattern(/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/, {
13
13
  message: 'Expected a lowercase DNS label (a-z, 0-9, inner hyphens, at most 63)',
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // ⛔ GENERATED by scripts/sync-versions.ts — do not edit. Source: package.json.
2
- export const VERSION: string = '0.1.0';
2
+ export const VERSION: string = '0.1.2';