@brandlab-digital/site-core 1.0.10 → 1.0.11

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.
@@ -86,6 +86,34 @@ export interface RegistryNonSuburbLocality {
86
86
  notes?: string;
87
87
  }
88
88
  export type RegistryOfficialLocality = RegistrySuburb | RegistryNonSuburbLocality;
89
+ export interface WebsiteLocationPolicyExclusionGroup {
90
+ reasonCode: string;
91
+ reason: string;
92
+ slugs: readonly string[];
93
+ }
94
+ export interface WebsiteLocationPolicyExcludedLocality {
95
+ entityType: RegistryNonSuburbEntityType;
96
+ officialUrbanity: 'R';
97
+ reason: string;
98
+ }
99
+ /**
100
+ * Website eligibility policy layered over factual geography.
101
+ *
102
+ * The registry continues to retain every official locality. The policy keeps
103
+ * owner-excluded places out of normal project location catalogs and records
104
+ * how existing public URLs must be retired.
105
+ */
106
+ export interface WebsiteLocationPolicy {
107
+ version: string;
108
+ generated: string;
109
+ registryVersion: string;
110
+ scope: string;
111
+ defaultCandidateRule: string;
112
+ projectOverrideRule: string;
113
+ sourceSnapshot: string;
114
+ defaultExcludedSuburbGroups: readonly WebsiteLocationPolicyExclusionGroup[];
115
+ excludedLocalities: Readonly<Record<string, WebsiteLocationPolicyExcludedLocality>>;
116
+ }
89
117
  export interface GeographyRegistry {
90
118
  version: string;
91
119
  generated: string;
@@ -1 +1 @@
1
- {"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../src/locations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../src/locations.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"locations.js","sourceRoot":"","sources":["../src/locations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Location publication and geography-registry contracts.\n *\n * Brandlab owns route correctness and shared geography. Projects own their\n * content, evidence and publication decisions.\n */\n\n/**\n * The preferred location publication contract. A project owner explicitly\n * decides whether a route is indexable after checking customer usefulness,\n * geographic correctness and available evidence.\n */\nexport interface BrandlabLocationRouteDecision {\n indexing: 'index' | 'noindex-follow'\n rationale: string\n reviewedAt?: string\n}\n\nexport function defineLocationRouteDecision<const T extends BrandlabLocationRouteDecision>(\n input: T,\n): T {\n if (input.indexing !== 'index' && input.indexing !== 'noindex-follow') {\n throw new Error('Location route indexing must be \"index\" or \"noindex-follow\".')\n }\n if (!input.rationale.trim()) {\n throw new Error('Location route decision requires a concise rationale.')\n }\n if (input.reviewedAt && Number.isNaN(Date.parse(input.reviewedAt))) {\n throw new Error('Location route decision reviewedAt must be a valid date.')\n }\n return input\n}\n\nexport function resolveLocationRouteIndexing(\n decision: BrandlabLocationRouteDecision | null | undefined,\n): 'index' | 'noindex-follow' {\n if (!decision) {\n throw new Error('Location route decision is required.')\n }\n return defineLocationRouteDecision(decision).indexing\n}\n\n// ---------------------------------------------------------------------------\n// Geography registries (standards/registry/*-geography.json)\n// ---------------------------------------------------------------------------\n\nexport interface RegistryRegion {\n slug: string\n name: string\n /** Preferred query-facing region label for titles and primary headings. */\n seoName: string\n /** Recognised search/display variants; these never create additional URLs. */\n alternateNames?: readonly string[]\n /** 'the' → copy reads \"the Eastern Suburbs\"; 'none' → \"Western Sydney\". */\n articleForm: 'the' | 'none'\n councilSlugs: readonly string[]\n}\n\nexport interface RegistrySubRegion {\n slug: string\n name: string\n parentRegionSlug: string\n /** Council-combo sub-region membership. */\n councilSlugs?: readonly string[]\n /** Explicit suburb membership for district-style sub-regions. */\n suburbSlugs?: readonly string[]\n /** Provenance for promoted project districts or audited groupings. */\n source?: string\n}\n\n/**\n * Internal service-coverage partition. Coverage groups have no public name,\n * route, metadata, schema, sitemap, breadcrumb, navigation, or page model.\n */\nexport interface RegistryCoverageGroup {\n slug: string\n /** Maintainer-facing label; never render this value as customer copy. */\n adminLabel: string\n regionSlug: string\n visibility: 'internal'\n}\n\nexport interface RegistryCouncil {\n slug: string\n name: string\n}\n\nexport interface RegistrySuburb {\n name: string\n slug: string\n regionSlug: string\n /** Internal service-coverage partition; never a page parent or URL segment. */\n coverageGroupSlug?: string\n councilSlug: string\n /** Secondary council memberships for split localities. */\n sharedCouncilSlugs?: readonly string[]\n /** Australia Post-matched postcode. */\n postcode: string\n /** Stewardship notes for boundary bindings and corrections. */\n notes?: string\n}\n\nexport type RegistryNonSuburbEntityType =\n | 'rural-locality'\n | 'natural-area'\n | 'protected-area'\n | 'waterbody'\n\n/**\n * Official NSW geography retained for spatial and council completeness but\n * excluded from the normal website suburb catalog.\n */\nexport interface RegistryNonSuburbLocality {\n name: string\n slug: string\n regionSlug: string\n councilSlug: string\n sharedCouncilSlugs?: readonly string[]\n postcode: null\n entityType: RegistryNonSuburbEntityType\n officialUrbanity: 'R'\n defaultWebsiteEligibility: false\n eligibilityReason: string\n notes?: string\n}\n\nexport type RegistryOfficialLocality = RegistrySuburb | RegistryNonSuburbLocality\n\nexport interface GeographyRegistry {\n version: string\n generated: string\n /** Provenance: where the registry data was derived from. */\n sources?: readonly string[]\n /** Stewardship notes for maintainers. */\n notes?: string\n regions: readonly RegistryRegion[]\n subRegions: readonly RegistrySubRegion[]\n /** Backend-only groupings used to select served suburbs. */\n coverageGroups: readonly RegistryCoverageGroup[]\n /** Legacy slug → canonical slug. */\n aliases: Readonly<Record<string, string>>\n councils: readonly RegistryCouncil[]\n /** Postcode-backed candidates only; this does not itself approve pages. */\n suburbs: readonly RegistrySuburb[]\n /** Official non-suburb geography excluded from default website locations. */\n nonSuburbLocalities: readonly RegistryNonSuburbLocality[]\n}\n\nexport type RegistryEntityKind = 'region' | 'subRegion' | 'council' | 'suburb'\n\n/** Canonical Brandlab slugify — identical to the benchmark implementation. */\nexport function toLocationSlug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/(^-|-$)/g, '')\n}\n\n/**\n * Resolve a possibly-legacy slug within an explicit registry entity kind.\n *\n * Entity context is required because canonical suburb slugs such as\n * `blacktown`, `hornsby`, `randwick`, and `willoughby` are also legacy council\n * aliases. A canonical slug for the requested kind always wins over an alias.\n */\nexport function resolveRegistryAlias(\n slug: string,\n registry: GeographyRegistry,\n kind: RegistryEntityKind | readonly RegistryEntityKind[],\n): string {\n const kinds = Array.isArray(kind) ? kind : [kind]\n const canonicalSets = kinds.map((entityKind) => getRegistrySlugSet(registry, entityKind))\n\n if (canonicalSets.some((slugSet) => slugSet.has(slug))) return slug\n\n const target = registry.aliases[slug]\n if (!target) return slug\n\n return canonicalSets.some((slugSet) => slugSet.has(target)) ? target : slug\n}\n\nexport interface CatalogRegistryIssue {\n kind:\n | 'unknown-region'\n | 'unknown-coverage-group'\n | 'unknown-council'\n | 'unknown-suburb'\n | 'excluded-website-locality'\n | 'suburb-region-mismatch'\n | 'suburb-coverage-group-mismatch'\n | 'suburb-council-mismatch'\n slug: string\n detail: string\n}\n\nexport interface CatalogForValidation {\n regionSlugs: readonly string[]\n /** Backend coverage selectors; these are never treated as region/page slugs. */\n coverageGroupSlugs?: readonly string[]\n suburbs: readonly {\n slug: string\n regionSlug: string\n coverageGroupSlug?: string\n councilSlug?: string\n }[]\n}\n\n/**\n * Expand internal coverage selectors to canonical suburb slugs.\n *\n * This helper only answers where a business serves. It does not approve pages,\n * static params, sitemap entries, schema nodes, or internal links.\n */\nexport function getCoverageGroupSuburbSlugs(\n coverageGroupSlugs: readonly string[],\n registry: GeographyRegistry,\n): string[] {\n const knownCoverageGroupSlugs = new Set(registry.coverageGroups.map((group) => group.slug))\n const unknownCoverageGroupSlug = coverageGroupSlugs.find(\n (slug) => !knownCoverageGroupSlugs.has(slug),\n )\n if (unknownCoverageGroupSlug) {\n throw new Error(`Unknown registry coverage group \"${unknownCoverageGroupSlug}\".`)\n }\n\n const selected = new Set(coverageGroupSlugs)\n return registry.suburbs\n .filter(\n (suburb) => suburb.coverageGroupSlug && selected.has(suburb.coverageGroupSlug),\n )\n .map((suburb) => suburb.slug)\n}\n\n/**\n * Validate a project catalog subset against the shared registry. Alias slugs\n * are resolved before comparison so migrated repos that kept live URLs can\n * still pass. Returns an empty array when the subset is clean.\n */\nexport function validateCatalogAgainstRegistry(\n catalog: CatalogForValidation,\n registry: GeographyRegistry,\n): CatalogRegistryIssue[] {\n const issues: CatalogRegistryIssue[] = []\n const regionSlugSet = new Set(registry.regions.map((region) => region.slug))\n const subRegionSlugSet = new Set(registry.subRegions.map((subRegion) => subRegion.slug))\n const coverageGroupSlugSet = new Set(registry.coverageGroups.map((group) => group.slug))\n const councilSlugSet = new Set(registry.councils.map((council) => council.slug))\n const suburbBySlug = new Map(registry.suburbs.map((suburb) => [suburb.slug, suburb]))\n const nonSuburbLocalityBySlug = new Map(\n registry.nonSuburbLocalities.map((locality) => [locality.slug, locality]),\n )\n\n for (const rawRegionSlug of catalog.regionSlugs) {\n const regionSlug = resolveRegistryAlias(rawRegionSlug, registry, ['region', 'subRegion'])\n if (!regionSlugSet.has(regionSlug) && !subRegionSlugSet.has(regionSlug)) {\n issues.push({\n kind: 'unknown-region',\n slug: rawRegionSlug,\n detail: `Region slug \"${rawRegionSlug}\" is not a registry region, sub-region, or alias.`,\n })\n }\n }\n\n for (const coverageGroupSlug of catalog.coverageGroupSlugs ?? []) {\n if (!coverageGroupSlugSet.has(coverageGroupSlug)) {\n issues.push({\n kind: 'unknown-coverage-group',\n slug: coverageGroupSlug,\n detail: `Coverage group slug \"${coverageGroupSlug}\" is not an internal registry coverage group.`,\n })\n }\n }\n\n for (const suburb of catalog.suburbs) {\n const resolvedSuburbSlug = suburbBySlug.has(suburb.slug)\n ? suburb.slug\n : resolveRegistryAlias(suburb.slug, registry, 'suburb')\n const registrySuburb = suburbBySlug.get(resolvedSuburbSlug)\n\n if (!registrySuburb) {\n const excludedLocality = nonSuburbLocalityBySlug.get(suburb.slug)\n if (excludedLocality) {\n issues.push({\n kind: 'excluded-website-locality',\n slug: suburb.slug,\n detail: `Official locality \"${suburb.slug}\" is classified as ${excludedLocality.entityType} and excluded from the normal website suburb catalog: ${excludedLocality.eligibilityReason}`,\n })\n continue\n }\n issues.push({\n kind: 'unknown-suburb',\n slug: suburb.slug,\n detail: `Suburb slug \"${suburb.slug}\" is not in the registry (not gazetted, or needs adding to the registry first).`,\n })\n continue\n }\n\n const regionSlug = resolveRegistryAlias(suburb.regionSlug, registry, ['region', 'subRegion'])\n const regionMatches =\n registrySuburb.regionSlug === regionSlug ||\n registry.subRegions.some(\n (subRegion) =>\n subRegion.slug === regionSlug &&\n subRegion.parentRegionSlug === registrySuburb.regionSlug &&\n (subRegion.suburbSlugs?.includes(registrySuburb.slug) ||\n subRegion.councilSlugs?.some(\n (councilSlug) =>\n councilSlug === registrySuburb.councilSlug ||\n registrySuburb.sharedCouncilSlugs?.includes(councilSlug),\n )),\n )\n\n if (!regionMatches) {\n issues.push({\n kind: 'suburb-region-mismatch',\n slug: suburb.slug,\n detail: `Suburb \"${suburb.slug}\" is assigned to \"${suburb.regionSlug}\" but the registry places it in \"${registrySuburb.regionSlug}\".`,\n })\n }\n\n if (suburb.coverageGroupSlug) {\n if (!coverageGroupSlugSet.has(suburb.coverageGroupSlug)) {\n issues.push({\n kind: 'unknown-coverage-group',\n slug: suburb.coverageGroupSlug,\n detail: `Coverage group slug \"${suburb.coverageGroupSlug}\" is not an internal registry coverage group.`,\n })\n } else if (registrySuburb.coverageGroupSlug !== suburb.coverageGroupSlug) {\n issues.push({\n kind: 'suburb-coverage-group-mismatch',\n slug: suburb.slug,\n detail: `Suburb \"${suburb.slug}\" is assigned to coverage group \"${suburb.coverageGroupSlug}\" but the registry places it under \"${registrySuburb.coverageGroupSlug ?? 'none'}\".`,\n })\n }\n }\n\n if (suburb.councilSlug) {\n const councilSlug = resolveRegistryAlias(suburb.councilSlug, registry, 'council')\n if (!councilSlugSet.has(councilSlug)) {\n issues.push({\n kind: 'unknown-council',\n slug: suburb.councilSlug,\n detail: `Council slug \"${suburb.councilSlug}\" is not a registry council or alias.`,\n })\n } else if (\n registrySuburb.councilSlug !== councilSlug &&\n !registrySuburb.sharedCouncilSlugs?.includes(councilSlug)\n ) {\n issues.push({\n kind: 'suburb-council-mismatch',\n slug: suburb.slug,\n detail: `Suburb \"${suburb.slug}\" is assigned to council \"${suburb.councilSlug}\" but the registry places it under \"${registrySuburb.councilSlug}\".`,\n })\n }\n }\n }\n\n return issues\n}\n\nfunction getRegistrySlugSet(\n registry: GeographyRegistry,\n kind: RegistryEntityKind,\n): ReadonlySet<string> {\n switch (kind) {\n case 'region':\n return new Set(registry.regions.map((region) => region.slug))\n case 'subRegion':\n return new Set(registry.subRegions.map((subRegion) => subRegion.slug))\n case 'council':\n return new Set(registry.councils.map((council) => council.slug))\n case 'suburb':\n return new Set(registry.suburbs.map((suburb) => suburb.slug))\n }\n}\n"]}
1
+ {"version":3,"file":"locations.js","sourceRoot":"","sources":["../src/locations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Location publication and geography-registry contracts.\n *\n * Brandlab owns route correctness and shared geography. Projects own their\n * content, evidence and publication decisions.\n */\n\n/**\n * The preferred location publication contract. A project owner explicitly\n * decides whether a route is indexable after checking customer usefulness,\n * geographic correctness and available evidence.\n */\nexport interface BrandlabLocationRouteDecision {\n indexing: 'index' | 'noindex-follow'\n rationale: string\n reviewedAt?: string\n}\n\nexport function defineLocationRouteDecision<const T extends BrandlabLocationRouteDecision>(\n input: T,\n): T {\n if (input.indexing !== 'index' && input.indexing !== 'noindex-follow') {\n throw new Error('Location route indexing must be \"index\" or \"noindex-follow\".')\n }\n if (!input.rationale.trim()) {\n throw new Error('Location route decision requires a concise rationale.')\n }\n if (input.reviewedAt && Number.isNaN(Date.parse(input.reviewedAt))) {\n throw new Error('Location route decision reviewedAt must be a valid date.')\n }\n return input\n}\n\nexport function resolveLocationRouteIndexing(\n decision: BrandlabLocationRouteDecision | null | undefined,\n): 'index' | 'noindex-follow' {\n if (!decision) {\n throw new Error('Location route decision is required.')\n }\n return defineLocationRouteDecision(decision).indexing\n}\n\n// ---------------------------------------------------------------------------\n// Geography registries (standards/registry/*-geography.json)\n// ---------------------------------------------------------------------------\n\nexport interface RegistryRegion {\n slug: string\n name: string\n /** Preferred query-facing region label for titles and primary headings. */\n seoName: string\n /** Recognised search/display variants; these never create additional URLs. */\n alternateNames?: readonly string[]\n /** 'the' → copy reads \"the Eastern Suburbs\"; 'none' → \"Western Sydney\". */\n articleForm: 'the' | 'none'\n councilSlugs: readonly string[]\n}\n\nexport interface RegistrySubRegion {\n slug: string\n name: string\n parentRegionSlug: string\n /** Council-combo sub-region membership. */\n councilSlugs?: readonly string[]\n /** Explicit suburb membership for district-style sub-regions. */\n suburbSlugs?: readonly string[]\n /** Provenance for promoted project districts or audited groupings. */\n source?: string\n}\n\n/**\n * Internal service-coverage partition. Coverage groups have no public name,\n * route, metadata, schema, sitemap, breadcrumb, navigation, or page model.\n */\nexport interface RegistryCoverageGroup {\n slug: string\n /** Maintainer-facing label; never render this value as customer copy. */\n adminLabel: string\n regionSlug: string\n visibility: 'internal'\n}\n\nexport interface RegistryCouncil {\n slug: string\n name: string\n}\n\nexport interface RegistrySuburb {\n name: string\n slug: string\n regionSlug: string\n /** Internal service-coverage partition; never a page parent or URL segment. */\n coverageGroupSlug?: string\n councilSlug: string\n /** Secondary council memberships for split localities. */\n sharedCouncilSlugs?: readonly string[]\n /** Australia Post-matched postcode. */\n postcode: string\n /** Stewardship notes for boundary bindings and corrections. */\n notes?: string\n}\n\nexport type RegistryNonSuburbEntityType =\n | 'rural-locality'\n | 'natural-area'\n | 'protected-area'\n | 'waterbody'\n\n/**\n * Official NSW geography retained for spatial and council completeness but\n * excluded from the normal website suburb catalog.\n */\nexport interface RegistryNonSuburbLocality {\n name: string\n slug: string\n regionSlug: string\n councilSlug: string\n sharedCouncilSlugs?: readonly string[]\n postcode: null\n entityType: RegistryNonSuburbEntityType\n officialUrbanity: 'R'\n defaultWebsiteEligibility: false\n eligibilityReason: string\n notes?: string\n}\n\nexport type RegistryOfficialLocality = RegistrySuburb | RegistryNonSuburbLocality\n\nexport interface WebsiteLocationPolicyExclusionGroup {\n reasonCode: string\n reason: string\n slugs: readonly string[]\n}\n\nexport interface WebsiteLocationPolicyExcludedLocality {\n entityType: RegistryNonSuburbEntityType\n officialUrbanity: 'R'\n reason: string\n}\n\n/**\n * Website eligibility policy layered over factual geography.\n *\n * The registry continues to retain every official locality. The policy keeps\n * owner-excluded places out of normal project location catalogs and records\n * how existing public URLs must be retired.\n */\nexport interface WebsiteLocationPolicy {\n version: string\n generated: string\n registryVersion: string\n scope: string\n defaultCandidateRule: string\n projectOverrideRule: string\n sourceSnapshot: string\n defaultExcludedSuburbGroups: readonly WebsiteLocationPolicyExclusionGroup[]\n excludedLocalities: Readonly<Record<string, WebsiteLocationPolicyExcludedLocality>>\n}\n\nexport interface GeographyRegistry {\n version: string\n generated: string\n /** Provenance: where the registry data was derived from. */\n sources?: readonly string[]\n /** Stewardship notes for maintainers. */\n notes?: string\n regions: readonly RegistryRegion[]\n subRegions: readonly RegistrySubRegion[]\n /** Backend-only groupings used to select served suburbs. */\n coverageGroups: readonly RegistryCoverageGroup[]\n /** Legacy slug → canonical slug. */\n aliases: Readonly<Record<string, string>>\n councils: readonly RegistryCouncil[]\n /** Postcode-backed candidates only; this does not itself approve pages. */\n suburbs: readonly RegistrySuburb[]\n /** Official non-suburb geography excluded from default website locations. */\n nonSuburbLocalities: readonly RegistryNonSuburbLocality[]\n}\n\nexport type RegistryEntityKind = 'region' | 'subRegion' | 'council' | 'suburb'\n\n/** Canonical Brandlab slugify — identical to the benchmark implementation. */\nexport function toLocationSlug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/(^-|-$)/g, '')\n}\n\n/**\n * Resolve a possibly-legacy slug within an explicit registry entity kind.\n *\n * Entity context is required because canonical suburb slugs such as\n * `blacktown`, `hornsby`, `randwick`, and `willoughby` are also legacy council\n * aliases. A canonical slug for the requested kind always wins over an alias.\n */\nexport function resolveRegistryAlias(\n slug: string,\n registry: GeographyRegistry,\n kind: RegistryEntityKind | readonly RegistryEntityKind[],\n): string {\n const kinds = Array.isArray(kind) ? kind : [kind]\n const canonicalSets = kinds.map((entityKind) => getRegistrySlugSet(registry, entityKind))\n\n if (canonicalSets.some((slugSet) => slugSet.has(slug))) return slug\n\n const target = registry.aliases[slug]\n if (!target) return slug\n\n return canonicalSets.some((slugSet) => slugSet.has(target)) ? target : slug\n}\n\nexport interface CatalogRegistryIssue {\n kind:\n | 'unknown-region'\n | 'unknown-coverage-group'\n | 'unknown-council'\n | 'unknown-suburb'\n | 'excluded-website-locality'\n | 'suburb-region-mismatch'\n | 'suburb-coverage-group-mismatch'\n | 'suburb-council-mismatch'\n slug: string\n detail: string\n}\n\nexport interface CatalogForValidation {\n regionSlugs: readonly string[]\n /** Backend coverage selectors; these are never treated as region/page slugs. */\n coverageGroupSlugs?: readonly string[]\n suburbs: readonly {\n slug: string\n regionSlug: string\n coverageGroupSlug?: string\n councilSlug?: string\n }[]\n}\n\n/**\n * Expand internal coverage selectors to canonical suburb slugs.\n *\n * This helper only answers where a business serves. It does not approve pages,\n * static params, sitemap entries, schema nodes, or internal links.\n */\nexport function getCoverageGroupSuburbSlugs(\n coverageGroupSlugs: readonly string[],\n registry: GeographyRegistry,\n): string[] {\n const knownCoverageGroupSlugs = new Set(registry.coverageGroups.map((group) => group.slug))\n const unknownCoverageGroupSlug = coverageGroupSlugs.find(\n (slug) => !knownCoverageGroupSlugs.has(slug),\n )\n if (unknownCoverageGroupSlug) {\n throw new Error(`Unknown registry coverage group \"${unknownCoverageGroupSlug}\".`)\n }\n\n const selected = new Set(coverageGroupSlugs)\n return registry.suburbs\n .filter(\n (suburb) => suburb.coverageGroupSlug && selected.has(suburb.coverageGroupSlug),\n )\n .map((suburb) => suburb.slug)\n}\n\n/**\n * Validate a project catalog subset against the shared registry. Alias slugs\n * are resolved before comparison so migrated repos that kept live URLs can\n * still pass. Returns an empty array when the subset is clean.\n */\nexport function validateCatalogAgainstRegistry(\n catalog: CatalogForValidation,\n registry: GeographyRegistry,\n): CatalogRegistryIssue[] {\n const issues: CatalogRegistryIssue[] = []\n const regionSlugSet = new Set(registry.regions.map((region) => region.slug))\n const subRegionSlugSet = new Set(registry.subRegions.map((subRegion) => subRegion.slug))\n const coverageGroupSlugSet = new Set(registry.coverageGroups.map((group) => group.slug))\n const councilSlugSet = new Set(registry.councils.map((council) => council.slug))\n const suburbBySlug = new Map(registry.suburbs.map((suburb) => [suburb.slug, suburb]))\n const nonSuburbLocalityBySlug = new Map(\n registry.nonSuburbLocalities.map((locality) => [locality.slug, locality]),\n )\n\n for (const rawRegionSlug of catalog.regionSlugs) {\n const regionSlug = resolveRegistryAlias(rawRegionSlug, registry, ['region', 'subRegion'])\n if (!regionSlugSet.has(regionSlug) && !subRegionSlugSet.has(regionSlug)) {\n issues.push({\n kind: 'unknown-region',\n slug: rawRegionSlug,\n detail: `Region slug \"${rawRegionSlug}\" is not a registry region, sub-region, or alias.`,\n })\n }\n }\n\n for (const coverageGroupSlug of catalog.coverageGroupSlugs ?? []) {\n if (!coverageGroupSlugSet.has(coverageGroupSlug)) {\n issues.push({\n kind: 'unknown-coverage-group',\n slug: coverageGroupSlug,\n detail: `Coverage group slug \"${coverageGroupSlug}\" is not an internal registry coverage group.`,\n })\n }\n }\n\n for (const suburb of catalog.suburbs) {\n const resolvedSuburbSlug = suburbBySlug.has(suburb.slug)\n ? suburb.slug\n : resolveRegistryAlias(suburb.slug, registry, 'suburb')\n const registrySuburb = suburbBySlug.get(resolvedSuburbSlug)\n\n if (!registrySuburb) {\n const excludedLocality = nonSuburbLocalityBySlug.get(suburb.slug)\n if (excludedLocality) {\n issues.push({\n kind: 'excluded-website-locality',\n slug: suburb.slug,\n detail: `Official locality \"${suburb.slug}\" is classified as ${excludedLocality.entityType} and excluded from the normal website suburb catalog: ${excludedLocality.eligibilityReason}`,\n })\n continue\n }\n issues.push({\n kind: 'unknown-suburb',\n slug: suburb.slug,\n detail: `Suburb slug \"${suburb.slug}\" is not in the registry (not gazetted, or needs adding to the registry first).`,\n })\n continue\n }\n\n const regionSlug = resolveRegistryAlias(suburb.regionSlug, registry, ['region', 'subRegion'])\n const regionMatches =\n registrySuburb.regionSlug === regionSlug ||\n registry.subRegions.some(\n (subRegion) =>\n subRegion.slug === regionSlug &&\n subRegion.parentRegionSlug === registrySuburb.regionSlug &&\n (subRegion.suburbSlugs?.includes(registrySuburb.slug) ||\n subRegion.councilSlugs?.some(\n (councilSlug) =>\n councilSlug === registrySuburb.councilSlug ||\n registrySuburb.sharedCouncilSlugs?.includes(councilSlug),\n )),\n )\n\n if (!regionMatches) {\n issues.push({\n kind: 'suburb-region-mismatch',\n slug: suburb.slug,\n detail: `Suburb \"${suburb.slug}\" is assigned to \"${suburb.regionSlug}\" but the registry places it in \"${registrySuburb.regionSlug}\".`,\n })\n }\n\n if (suburb.coverageGroupSlug) {\n if (!coverageGroupSlugSet.has(suburb.coverageGroupSlug)) {\n issues.push({\n kind: 'unknown-coverage-group',\n slug: suburb.coverageGroupSlug,\n detail: `Coverage group slug \"${suburb.coverageGroupSlug}\" is not an internal registry coverage group.`,\n })\n } else if (registrySuburb.coverageGroupSlug !== suburb.coverageGroupSlug) {\n issues.push({\n kind: 'suburb-coverage-group-mismatch',\n slug: suburb.slug,\n detail: `Suburb \"${suburb.slug}\" is assigned to coverage group \"${suburb.coverageGroupSlug}\" but the registry places it under \"${registrySuburb.coverageGroupSlug ?? 'none'}\".`,\n })\n }\n }\n\n if (suburb.councilSlug) {\n const councilSlug = resolveRegistryAlias(suburb.councilSlug, registry, 'council')\n if (!councilSlugSet.has(councilSlug)) {\n issues.push({\n kind: 'unknown-council',\n slug: suburb.councilSlug,\n detail: `Council slug \"${suburb.councilSlug}\" is not a registry council or alias.`,\n })\n } else if (\n registrySuburb.councilSlug !== councilSlug &&\n !registrySuburb.sharedCouncilSlugs?.includes(councilSlug)\n ) {\n issues.push({\n kind: 'suburb-council-mismatch',\n slug: suburb.slug,\n detail: `Suburb \"${suburb.slug}\" is assigned to council \"${suburb.councilSlug}\" but the registry places it under \"${registrySuburb.councilSlug}\".`,\n })\n }\n }\n }\n\n return issues\n}\n\nfunction getRegistrySlugSet(\n registry: GeographyRegistry,\n kind: RegistryEntityKind,\n): ReadonlySet<string> {\n switch (kind) {\n case 'region':\n return new Set(registry.regions.map((region) => region.slug))\n case 'subRegion':\n return new Set(registry.subRegions.map((subRegion) => subRegion.slug))\n case 'council':\n return new Set(registry.councils.map((council) => council.slug))\n case 'suburb':\n return new Set(registry.suburbs.map((suburb) => suburb.slug))\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brandlab-digital/site-core",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/RomieDandan/brandlab.git",