This PR contains the latest updates from the generic Website template, which have been added while working on #loud-amsterdam. Reviewed-on: #1 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
5.4 KiB
5.4 KiB
Infrastructure
The shared Hummingbird toolkit the platform's services build on: declarative routing, hardened HTTP middlewares, pre-rendered localized responses, and page and asset scaffolding.
Overview
| Role | Types |
|---|---|
| Routing | RouterController, RouteCollectionBuilder, the addController extension on RouterMethods |
| Middlewares | SecurityHeadersMiddleware, HTTPSRedirectMiddleware, TrailingSlashRedirectMiddleware, VaryMiddleware, RateLimitMiddleware, LocalizationMiddleware (negotiating from a lang query parameter, then a leading path segment, then Accept-Language), NotFoundMiddleware |
| Pages and assets | Page, Asset, AssetExtension, FingerprintAssets |
| Link previews | SocialCard, its locale and the alternateLocales of its other language editions, its Image and Style, and the Tag meta tags it derives |
| Structured data | StructuredData, the Node, Property, and Value types of its schema.org graph, the open Name and Kind vocabularies, and the site-wide initializer building the Organization/WebSite pair |
| Analytics | Analytics, the Events a page reports (with tagging() to apply one to any attribute-bearing HTML or SVG tag), the tracker script Attributes it derives, the optional session recorder script paired with it, and the origin its preconnect hint targets |
| Responses | CachedHTMLResponse, and LocalizedHTMLCollectionResponse rendering one of them per catalog language |
| Contexts | LocalizedRequestContext |
| Constants | The HTTPField.Name header names, Int.RateLimit limits, and String.Security header values the middlewares default to |
Design rules
The package holds only what every service can reuse. Anything a service owns is injected, never referenced.
- No site-specific content. No page markup, no asset catalog, no
Bundle.modulelookups. What a type needs, it takes as a parameter: thebundle:whose String Catalog names the supported languages, thedocument:closure that builds a page for a locale, aPageconformer'smetadataand its optional head concerns (summary,canonicalURL,socialCard,structuredData,analytics). URLs arrive absolute and fully formed — composing them stays with the page. The same holds for request semantics: only the caller knows whether it negotiates the language or pins it by route, sovariesOnAcceptLanguage:declares whether the responses carryVary: Accept-Language. - Types own their format;
Pagerenders generically. Each head concern derives its own render-ready form —SocialCard.tags,StructuredData.payload,Analytics.attributes— whichPageapplies without knowing the vocabulary. A page'sscriptsand the tracker render asdeferred head tags, the tracker preceded by apreconnectto its cross-origin host. - Nodes are joined by
@id, not repetition. A node another page must point at gets its identifier from a helper rather than a hand-spelled fragment —organizationID(forSiteURL:)names the node the site-wide initializer builds, and that initializer'sfoundertakes such an identifier back. A service adds the helper for any node it owns, so neither side can drift. - Services fill the gaps once, via extensions. A service restores its convenient call sites retroactively — the Website's
*+Defaultsare the pattern. The open schema.org vocabularies work the same way: the package declares the sharedProperty.NameandNode.Kindconstants, a service adds its own. - Method structs. Single-operation types such as
FingerprintAssetstake lifetime-fixed configuration ininitand per-call inputs incallAsFunction.
Layout
Sources are split by visibility, then by kind, one type per file:
Sources/
├── Public/ public API
│ ├── Builders/ RouteCollectionBuilder
│ ├── Enumerations/ AssetExtension
│ ├── Extensions/ addController and Analytics.Event tagging, plus the default header names, rate limits, and header values
│ ├── Methods/ FingerprintAssets
│ ├── Middlewares/ the seven HTTP middlewares
│ ├── Protocols/ Asset, LocalizedRequestContext, Page, RouterController
│ ├── Responses/ CachedHTMLResponse, LocalizedHTMLCollectionResponse
│ └── Types/ Analytics, SocialCard, StructuredData — each nesting its own types in a folder of that name
└── Internal/
├── Extensions/ implementation details (the String separators)
└── Types/ implementation details (FNV1aHash)
Tests/
├── Cases/ the test suites, mirroring the Sources/ layout
├── Catalogs/ the String Catalog fixture, copied verbatim so it loads on Linux
└── Utils/ stubs (StubAsset, StubPage, …) and the suite Tag constants
Testing
Every suite carries a tag for the kind of API it exercises — .asset, .extension, .middleware, .protocol, .type — declared in Tests/Utils/Extensions/Tag+Constants.swift, so a run can be sliced by kind. A new suite takes the tag matching its subject, or adds one when none fits.
Requirements
- Swift 6.3 toolchain (
swift-tools-version:6.3). - macOS 15, matching the sibling packages. The services deploy to Linux containers; the packages declare no UI platforms.