@@ -11,6 +11,13 @@ call typed methods like `peek.getProductService().getAllProducts()` or directly
1111via the top-level short-forms like ` peek.getAllProducts() ` and
1212` peek.getAllActivities() ` .
1313
14+ The package also ships a ** sibling accessor for the CNG backoffice** ,
15+ ` CngAccessService ` (REST, not GraphQL). It reuses this package's auth
16+ (` TokenManager ` ), retry/backoff loop, ` Logger ` , base error types, tooling, and
17+ the Odyssey UI — differing only in transport (REST vs GraphQL) and gateway
18+ routing (` cng_backoffice_api-v1 ` vs ` peek_backoffice_api-v1 ` ). See
19+ "CNG accessor" below.
20+
1421## Layers
1522
1623```
@@ -76,9 +83,14 @@ via the top-level short-forms like `peek.getAllProducts()` and
7683- Caches the token and re-mints it once it is within ` leewaySeconds ` of expiry.
7784
7885### 3. ` GraphQLClient ` — transport
79- ` src/internal/graphql-client.ts `
86+ ` src/internal/peek/ graphql-client.ts `
8087
81- The only place that touches the network. Responsibilities:
88+ The place that touches the network for Peek. The retry/backoff loop and 418/429
89+ mapping are ** shared** with the CNG transport in
90+ ` src/internal/http-transport.ts ` (` requestWithRetry ` ): both clients build their
91+ own ` url ` /` init ` , log their own "Making … request" line, and pass a per-response
92+ callback that handles the transport-specific success/error parsing.
93+ Responsibilities:
8294
8395- Builds the endpoint URL as ` ${baseUrl}/${appId}/${endpointName} ` , or
8496 ` ${baseUrl}/${appId}/${endpointPathPrefix}/${endpointName} ` when an
@@ -98,7 +110,11 @@ The only place that touches the network. Responsibilities:
98110 - other non-2xx → generic ` Error ` with the status.
99111
100112### 4. Per-resource services
101- ` src/internal/<resource>/ `
113+ ` src/internal/peek/<resource>/ `
114+
115+ Every Peek resource lives under ` src/internal/peek/ ` (mirrored by the CNG
116+ resources under ` src/internal/cng/ ` — see §5b); the shared plumbing
117+ (` token-manager.ts ` , ` http-transport.ts ` ) stays at ` src/internal/ ` .
102118
103119Each resource follows the same ** three-file triad** :
104120
@@ -110,15 +126,16 @@ Each resource follows the same **three-file triad**:
110126
111127Resources: ` products ` , ` account-users ` , ` resource-pools ` , ` timeslots ` ,
112128` resellers ` , ` promo-codes ` , ` daily-notes ` , ` availability ` , ` memberships ` ,
113- ` bookings ` , ` reviews ` . Clean data shapes live in ` src/models/ ` .
129+ ` bookings ` , ` reviews ` . Clean data shapes are split by brand: Peek models in
130+ ` src/models/peek/ ` , CNG models in ` src/models/cng/ ` .
114131
115132` ProductService ` exposes three top-level product filters in addition to the combined ` getAllProducts() ` :
116133- ` getAllActivities() ` — fetches only the ` activities ` connection (one request, no add-on pagination).
117134- ` getAllAddons() ` — fetches only the ` itemOptions ` connection, paginated.
118135
119136` waivers ` is a ** webhook-only resource** : it has no GraphQL reads (so no
120- queries/service/converter triad), just ` src/internal/waivers/waiver-webhook.ts `
121- and the ` src/models/waiver.ts ` model. See the webhook notes below.
137+ queries/service/converter triad), just ` src/internal/peek/ waivers/waiver-webhook.ts `
138+ and the ` src/models/peek/ waiver.ts ` model. See the webhook notes below.
122139
123140A resource may split into more than one triad when it carries a distinct
124141sub-domain. ` bookings ` does: alongside ` booking-queries ` /` booking-converter ` ,
@@ -223,6 +240,53 @@ internal — including the booking-webhook registration query
223240The webhook-related public exports are the two parsers ` parseBookingWebhook ` and
224241` parseWaiverWebhook ` (plus the ` Waiver ` model type; see the webhook notes above).
225242
243+ ### 5b. CNG accessor (REST)
244+ ` src/cng-access-service.ts ` , ` src/internal/cng/ ` , ` src/models/cng/product.ts `
245+
246+ A second, brand-parallel accessor for the ** CNG** backoffice — REST, not
247+ GraphQL. Deliberately low-churn: it sits alongside the Peek code and shares the
248+ plumbing rather than forking the package.
249+
250+ - ** ` CngAccessService ` ** — validates four config fields (` installId ` ,
251+ ` jwtSecret ` , ` issuer ` , ` appId ` ; ** no ` gatewayKey ` ** — the CNG gateway needs no
252+ ` pk-api-key ` ). Builds the shared ` TokenManager ` and a ` RestClient ` , defaults
253+ the base URL to the app-registry installations API, and exposes
254+ ` getProductService() ` + the short-form ` getAllActivities() ` .
255+ - ** ` RestClient ` ** (` src/internal/cng/rest-client.ts ` ) — the REST sibling of
256+ ` GraphQLClient ` . Builds ` ${baseUrl}/${appId}/${extendableSlug}/${path} ` with
257+ ` extendableSlug = cng_backoffice_api-v1 ` , GETs it with ` X-Peek-Auth: Bearer `
258+ (no ` pk-api-key ` , no ` {query,variables} ` body), and runs through the shared
259+ ` requestWithRetry ` loop. Parses the body as JSON, falling back to raw text
260+ when unparseable; non-2xx (other than 418/429) → ` CngApiError ` (status + body).
261+ - ** Products triad** (` src/internal/cng/products/ ` ) — same shape as every Peek
262+ resource: ` product-queries.ts ` (raw REST ` ProductNode ` /` ProductsResponse `
263+ interfaces, internal), ` product-converter.ts ` (pure ` fromProductNodes ` →
264+ ` Activity ` ), ` product-service.ts ` (` CngProductService.getAllActivities() ` ,
265+ tolerating a ` { products: [...] } ` envelope or a bare array). Endpoint segments
266+ live in ` src/internal/cng/endpoints.ts ` .
267+ - ** Model** ` src/models/cng/product.ts ` — ` Activity ` /` ActivityTicket ` , mirroring
268+ the Peek ` Product ` shape so both brands read uniformly.
269+ - ** Shared, not duplicated:** the config contract (` BaseAccessServiceConfig ` +
270+ the ` createTokenManager ` /` requireNonEmpty ` helpers and shared TTL/leeway/retry
271+ defaults, all in ` src/access-service-config.ts ` ), ` TokenManager ` ,
272+ ` Logger ` /` noopLogger ` , the ` AdminAccountRequiredError ` /` RateLimitError ` base
273+ errors, the ` requestWithRetry ` transport core, the build/test tooling, and the
274+ Odyssey UI. Each accessor's config just extends the base: ` PeekAccessServiceConfig `
275+ adds ` gatewayKey ` /` mode ` /` itemOptionsPageSize ` ; ` CngAccessServiceConfig ` adds
276+ nothing. So the only real per-accessor difference is the transport built and the
277+ services exposed.
278+ - ** Public exports:** ` CngAccessService ` + ` CngAccessServiceConfig ` ,
279+ ` CngProductService ` , the ` Activity ` /` ActivityTicket ` types, and ` CngApiError `
280+ (added to the errors export). REST paths and raw response interfaces stay
281+ internal.
282+
283+ > ⚠️ ** Guessed response shape.** The real ` commerce-config/products ` payload is
284+ > not yet confirmed. ` ProductNode ` , the converter mapping, and the ` Activity `
285+ > field set are best-guess placeholders (snake_case REST fields, defensive
286+ > defaults). Confirm against a live sample and adjust — touch only
287+ > ` cng/products/product-queries.ts ` , ` product-converter.ts ` , and
288+ > ` models/cng/product.ts ` .
289+
226290### 6. UI components — the ` ./ui ` subpath
227291` src/ui/ `
228292
0 commit comments