@d19n/youfibre-odin-sdk 2.0.103-beta.6 → 2.0.103-beta.7

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.
@@ -1,26 +1,33 @@
1
1
  /**
2
2
  * Wholesale provider — subpath barrel.
3
3
  *
4
- * Consumers import from `@d19n/youfibre-odin-sdk/wholesale-provider`:
4
+ * Consumers import from `@d19n/youfibre-odin-sdk/dist/wholesale-provider`:
5
5
  *
6
6
  * ```ts
7
7
  * import {
8
8
  * WholesaleProvider,
9
9
  * NetomniaProvider,
10
10
  * NetomniaProviderDeps,
11
- * } from '@d19n/youfibre-odin-sdk/wholesale-provider';
11
+ * } from '@d19n/youfibre-odin-sdk/dist/wholesale-provider';
12
12
  * ```
13
13
  *
14
- * This subpath exists so the wholesale provider abstraction does not collide
15
- * with the auto-generated SDK surface (entities-v2 / accessors-v2 / etc.)
16
- * that gets regenerated by `make regen`. Per `runner.ts:163-181`, the regen
17
- * does NOT touch this directory.
14
+ * **Architecture (Path B / pass-through delegation):**
15
+ * - `WholesaleProvider` is a strategy-dispatchable interface. Method names
16
+ * align with VMO2 spec where it makes sense; args match existing NET
17
+ * module method signatures.
18
+ * - `NetomniaProvider implements WholesaleProvider` — pass-through to deps
19
+ * wired by modules to their existing NestJS-injected NET methods. Zero
20
+ * transformation at the boundary.
21
+ * - VMO2-shaped types (`./types/vmo2`) and FibreCafe-shaped types
22
+ * (`./types/fibrecafe`) are exported as **reference types** for future
23
+ * use (Vmo2Provider implementation, module DTO consolidation). They are
24
+ * NOT on the WholesaleProvider public surface in Phase 0.
18
25
  *
19
- * Linked: T-20260508-009
26
+ * Linked: T-20260508-009 / T-20260508-010
20
27
  */
21
- export type { WholesaleProvider } from './interface';
28
+ export type { WholesaleProvider, CreateOrderOptions, CancellationReasonCode, ReserveAppointmentArgs, ListAppointmentsArgs, ScheduleServiceWorkOrderArgs, RescheduleServiceWorkOrderArgs, AmendAppointmentArgs, SyncWorkOrderToSupplierArgs, UpdateOrderExternalRefArgs, } from './interface';
22
29
  export type { WholesaleResult, WholesaleSyncResponse, WholesaleRejectionResponse, WholesaleRejectionMessage, WholesaleBUS, WholesaleSYS, ISO8601DateTime, ISODate, TransactionId, UPRN, UUID, SwitchOrderReference, } from './types/wholesale';
23
- export type { VMO2OrderId, VMO2RequestId, VMO2AppointmentKey, VMO2PremiseId, VMO2ParentServiceId, VMO2ChildServiceId, VMO2TechnicalProductId, VMO2InvoiceAccountNumber, VMO2NetworkType, VMO2DeploymentMethod, VMO2OrderIntent, VMO2OrderAction, VMO2OrderStatus, VMO2RequestType, VMO2RequestStatus, VMO2VisitType, VMO2AppointmentStatus, VMO2InstallFieldVisitRequired, VMO2SelfInstallAvailable, VMO2OrderServiceLevel, VMO2RequestServiceLevel, VMO2AdditionalWorkCode, VMO2SpecialRequestCode, VMO2CancellationReasonCode, VMO2IncidentReasonCode, VMO2ClosureCode, VMO2DelayCode, VMO2AddressFields, VMO2EndCustomerDetails, VMO2AddressLookupQuery, VMO2AddressLookupRequest, VMO2AddressLookupResult, VMO2DeploymentDetail, VMO2TechnicalProduct, VMO2NetworkCapability, VMO2PremiseRecord, VMO2JourneyType, VMO2AppointmentScope, VMO2AppointmentVisitFilter, VMO2ListAppointmentRequest, VMO2AvailableSlot, VMO2ListAppointmentResult, VMO2ReserveAppointmentRequest, VMO2ReservedSlot, VMO2ReserveAppointmentResult, VMO2AppointmentDetails, VMO2AmendAppointmentPatch, VMO2ConfirmAppointmentRequest, VMO2CreateOrderRequest, VMO2CreateOrderResult, VMO2AmendOrderPatch, VMO2OrderDetails, VMO2CreateIncidentRequest, VMO2CreateIncidentResult, VMO2AmendIncidentPatch, VMO2IncidentDetails, VMO2OrderNotificationType, VMO2SMRNotificationType, VMO2NotificationEnvelope, VMO2OrderNotification, VMO2SMRNotification, VMO2InboundNotification, } from './types/vmo2';
24
- export type { FibreCafeAddressType, FibreCafeAppointmentPurpose, FibreCafeAvailabilityConstraintCode, FibreCafeInstallationType, FibreCafePortStatus, FibreCafeUnavailabilityReasonCode, FibreCafeTimeslotClassification, FibreCafeServiceCharacteristic, FibreCafeServiceProblemCharacteristic, FibreCafeEngineerTask, FibreCafeProvideOrderType, FibreCafeCeaseOrderType, FibreCafeModifyOrderType, FibreCafeUnsolicitedCeaseOrderType, FibreCafeOrderStatus, FibreCafeUnsolicitedCeaseOrderStatus, FibreCafeUnsolicitedCeaseReason, FibreCafeKciReasonCode, FibreCafeKciProblemCode, FibreCafeResolutionCode, FibreCafeGatewayErrorCode, FibreCafeAddressIdentifier, FibreCafeAddressIdentifierWithAdditional, FibreCafeSubAddress, FibreCafeAddress, FibreCafeAddressAttribute, FibreCafeAddressTenant, FibreCafeContact, FibreCafeTimeslot, FibreCafeReserveTimeslot, FibreCafeServiceCharacteristicObject, FibreCafeServiceProblemCharacteristicObject, FibreCafeServiceCharacteristicAvailability, FibreCafeServiceSpecification, FibreCafeServiceSpecificationAvailability, FibreCafeServiceOrderItem, FibreCafeSupplierNote, FibreCafeResolutionDetails, FibreCafeGatewayError, FibreCafeAvailabilityConstraint, FibreCafeUnavailabilityReason, FibreCafeLineCharacteristic, FibreCafeOntPort, FibreCafeOnt, FibreCafeExistingLine, FibreCafeSiteInformation, FibreCafeServiceAvailabilityQuery, FibreCafeServiceAvailability200, FibreCafeAddressLookupQuery, FibreCafeAddressLookup200, FibreCafeAvailableAppointmentsQuery, FibreCafeAvailableAppointments200, FibreCafeReserveAppointmentDto, FibreCafeReserveAppointment201, FibreCafeServiceWorkOrderAddress, FibreCafeServiceWorkOrderTimeslot, FibreCafeScheduleServiceWorkOrderDto, FibreCafeRescheduleServiceWorkOrderDto, FibreCafeScheduleServiceWorkOrder201, FibreCafeRescheduleServiceWorkOrder200, FibreCafeProvideServiceOrderDto, FibreCafeCeaseServiceOrderDto, FibreCafeModifyServiceOrderDto, FibreCafeOrderCreateDto, FibreCafeOrderCreate202, FibreCafeServiceOrderCancellationDto, FibreCafeProvideServiceOrderAmendmentDto, FibreCafeOrderAmendAccept202, FibreCafeProvideServiceOrder, FibreCafeCeaseServiceOrder, FibreCafeModifyServiceOrder, FibreCafeUnsolicitedCeaseServiceOrder, FibreCafeKciDto, FibreCafeServiceProblemCreateDto, FibreCafeServiceProblemCreate202, FibreCafeServiceProblemAmendmentDto, FibreCafeServiceProblemCancellationDto, FibreCafeServiceProblemDetails, } from './types/fibrecafe';
25
- export { NetomniaProvider, AggregateNotificationError, NotImplementedError, } from './providers/netomnia-provider';
30
+ export { NetomniaProvider, AggregateNotificationError } from './providers/netomnia-provider';
26
31
  export type { NetomniaProviderDeps } from './providers/netomnia-provider';
32
+ export type { VMO2OrderId, VMO2RequestId, VMO2AppointmentKey, VMO2PremiseId, VMO2OrderNotificationType, VMO2SMRNotificationType, VMO2NotificationEnvelope, VMO2OrderNotification, VMO2SMRNotification, VMO2InboundNotification, } from './types/vmo2';
33
+ export * from './types/fibrecafe';
@@ -2,27 +2,58 @@
2
2
  /**
3
3
  * Wholesale provider — subpath barrel.
4
4
  *
5
- * Consumers import from `@d19n/youfibre-odin-sdk/wholesale-provider`:
5
+ * Consumers import from `@d19n/youfibre-odin-sdk/dist/wholesale-provider`:
6
6
  *
7
7
  * ```ts
8
8
  * import {
9
9
  * WholesaleProvider,
10
10
  * NetomniaProvider,
11
11
  * NetomniaProviderDeps,
12
- * } from '@d19n/youfibre-odin-sdk/wholesale-provider';
12
+ * } from '@d19n/youfibre-odin-sdk/dist/wholesale-provider';
13
13
  * ```
14
14
  *
15
- * This subpath exists so the wholesale provider abstraction does not collide
16
- * with the auto-generated SDK surface (entities-v2 / accessors-v2 / etc.)
17
- * that gets regenerated by `make regen`. Per `runner.ts:163-181`, the regen
18
- * does NOT touch this directory.
15
+ * **Architecture (Path B / pass-through delegation):**
16
+ * - `WholesaleProvider` is a strategy-dispatchable interface. Method names
17
+ * align with VMO2 spec where it makes sense; args match existing NET
18
+ * module method signatures.
19
+ * - `NetomniaProvider implements WholesaleProvider` — pass-through to deps
20
+ * wired by modules to their existing NestJS-injected NET methods. Zero
21
+ * transformation at the boundary.
22
+ * - VMO2-shaped types (`./types/vmo2`) and FibreCafe-shaped types
23
+ * (`./types/fibrecafe`) are exported as **reference types** for future
24
+ * use (Vmo2Provider implementation, module DTO consolidation). They are
25
+ * NOT on the WholesaleProvider public surface in Phase 0.
19
26
  *
20
- * Linked: T-20260508-009
27
+ * Linked: T-20260508-009 / T-20260508-010
21
28
  */
29
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
33
+ desc = { enumerable: true, get: function() { return m[k]; } };
34
+ }
35
+ Object.defineProperty(o, k2, desc);
36
+ }) : (function(o, m, k, k2) {
37
+ if (k2 === undefined) k2 = k;
38
+ o[k2] = m[k];
39
+ }));
40
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
41
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
42
+ };
22
43
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.NotImplementedError = exports.AggregateNotificationError = exports.NetomniaProvider = void 0;
24
- // Provider implementations
44
+ exports.AggregateNotificationError = exports.NetomniaProvider = void 0;
45
+ // ── Provider implementation ───────────────────────────────────────────
25
46
  var netomnia_provider_1 = require("./providers/netomnia-provider");
26
47
  Object.defineProperty(exports, "NetomniaProvider", { enumerable: true, get: function () { return netomnia_provider_1.NetomniaProvider; } });
27
48
  Object.defineProperty(exports, "AggregateNotificationError", { enumerable: true, get: function () { return netomnia_provider_1.AggregateNotificationError; } });
28
- Object.defineProperty(exports, "NotImplementedError", { enumerable: true, get: function () { return netomnia_provider_1.NotImplementedError; } });
49
+ // ── Reference types (re-exported — not on WholesaleProvider surface) ──
50
+ //
51
+ // FibreCafe wire-format types — useful for module-level DTO consolidation
52
+ // or building the VMO2 wire request inside a future Vmo2Provider. These
53
+ // are NOT used by NetomniaProviderDeps in Path B; the deps shape is YF-
54
+ // context-shaped.
55
+ //
56
+ // Re-exported with `export *` because TS 4.8 doesn't support `export type *`.
57
+ // All declarations in fibrecafe.ts are types/interfaces with no runtime
58
+ // exports, so `export *` re-exports them as types only (no JS code emit).
59
+ __exportStar(require("./types/fibrecafe"), exports);
@@ -1,65 +1,112 @@
1
+ import type { VMO2OrderNotification, VMO2SMRNotification } from './types/vmo2';
1
2
  /**
2
- * WholesaleProvider — the typed contract YouFibre code uses to consume any
3
- * wholesale gateway (FibreCafe-fronted suppliers like Netomnia / Brsk /
4
- * Trooli today, VMO2 / Nutmeg from July 2026, others in future).
5
- *
6
- * Capability-shaped, not surface-shaped — callers ask "reserve an
7
- * appointment", not "POST /ge/v1/appointments/reservations". Implementations
8
- * map capability calls onto the per-supplier wire format.
9
- *
10
- * Lifted under T-20260508-009 from
11
- * agent-os/knowledge/integrations/vmo2-nutmeg/vm-provider-interface.ts
12
- *
13
- * Every method returns a `WholesaleResult<T>` discriminated by `status`.
14
- * Errors (4xx/5xx) materialise as `WholesaleRejectionResponse`. Transport /
15
- * OAuth / idempotency / retry / rate-limiting are encapsulated by the
16
- * implementation.
3
+ * Reasons a cancellation request can be made (NET wire-level enum value
4
+ * passed through to the FibreCafe gateway).
5
+ */
6
+ export declare type CancellationReasonCode = string;
7
+ export interface CreateOrderOptions {
8
+ doNotCreateServiceAppointment?: boolean;
9
+ externalRefBrskFno?: string;
10
+ externalRefBrskIsp?: string;
11
+ /** Provider-specific extras — populated as needed per provider. */
12
+ [extra: string]: unknown;
13
+ }
14
+ export interface ReserveAppointmentArgs {
15
+ yfAppointmentId: string;
16
+ uprn: number;
17
+ startDateTime: string;
18
+ endDateTime: string;
19
+ /** 'PROVIDE' / 'REPAIR' / 'MODIFY' or supplier-specific. */
20
+ purpose: string;
21
+ yfOrderId: string;
22
+ woProperties: {
23
+ ServiceCallReason?: string;
24
+ WorkOrderExternalReference?: string;
25
+ };
26
+ }
27
+ export interface ListAppointmentsArgs {
28
+ uprn: number;
29
+ /** ISO date the appointment search starts from. */
30
+ start: string;
31
+ appointmentPurpose: 'PROVIDE' | 'REPAIR';
32
+ }
33
+ export interface ScheduleServiceWorkOrderArgs {
34
+ yfWorkOrderId: string;
35
+ uprn: number;
36
+ startDateTime: string;
37
+ endDateTime: string;
38
+ yfOrderId: string;
39
+ properties: {
40
+ ServiceCallReason?: string;
41
+ };
42
+ }
43
+ export interface RescheduleServiceWorkOrderArgs {
44
+ yfWorkOrderId: string;
45
+ uprn: number;
46
+ startDateTime: string;
47
+ endDateTime: string;
48
+ yfOrderId: string;
49
+ }
50
+ export interface AmendAppointmentArgs {
51
+ yfOrderId: string;
52
+ yfWorkOrderId: string;
53
+ supplierAppointmentReference: string;
54
+ }
55
+ export interface SyncWorkOrderToSupplierArgs {
56
+ principal: unknown;
57
+ yfOrderId: string;
58
+ yfWorkOrderId: string;
59
+ yfChangeReason: {
60
+ id?: string;
61
+ externalId: string;
62
+ entity: string;
63
+ type: unknown;
64
+ properties: unknown;
65
+ };
66
+ }
67
+ export interface UpdateOrderExternalRefArgs {
68
+ oldYfId: string;
69
+ newYfId: string;
70
+ }
71
+ /**
72
+ * Capability-shaped interface to a wholesale supplier. Method NAMES align
73
+ * with VMO2 spec where it makes sense; ARGS match existing NET module
74
+ * method signatures. Each provider implementation translates these args
75
+ * into its supplier wire format internally.
17
76
  *
18
- * Method names are supplier-agnostic. Argument/result types are currently
19
- * VMO2-shaped (since VMO2 is the first concrete shape modelled). Per-supplier
20
- * implementations adapt their wire format to/from these shapes — see
21
- * `comparison-fibrecafe-vs-vmo2.md` for the field-level mapping.
77
+ * Returns are typed loosely — modules know the return shape via the dep
78
+ * function they wire in.
22
79
  */
23
- import type { WholesaleResult } from './types/wholesale';
24
- import type { VMO2AddressLookupRequest, VMO2AddressLookupResult, VMO2ListAppointmentRequest, VMO2ListAppointmentResult, VMO2ReserveAppointmentRequest, VMO2ReserveAppointmentResult, VMO2ConfirmAppointmentRequest, VMO2AppointmentDetails, VMO2AppointmentKey, VMO2AmendAppointmentPatch, VMO2CreateOrderRequest, VMO2CreateOrderResult, VMO2AmendOrderPatch, VMO2OrderDetails, VMO2OrderId, VMO2CancellationReasonCode, VMO2PremiseId, VMO2CreateIncidentRequest, VMO2CreateIncidentResult, VMO2AmendIncidentPatch, VMO2IncidentDetails, VMO2RequestId, VMO2OrderNotification, VMO2SMRNotification } from './types/vmo2';
25
- import type { ISODate, SwitchOrderReference } from './types/wholesale';
26
80
  export interface WholesaleProvider {
27
- searchAddress(req: VMO2AddressLookupRequest): Promise<WholesaleResult<VMO2AddressLookupResult>>;
28
- listAppointments(req: VMO2ListAppointmentRequest): Promise<WholesaleResult<VMO2ListAppointmentResult>>;
29
- reserveAppointment(req: VMO2ReserveAppointmentRequest): Promise<WholesaleResult<VMO2ReserveAppointmentResult>>;
30
- confirmAppointment(req: VMO2ConfirmAppointmentRequest): Promise<WholesaleResult<{
31
- appointmentKey: VMO2AppointmentKey;
32
- }>>;
33
- getAppointment(key: VMO2AppointmentKey): Promise<WholesaleResult<VMO2AppointmentDetails>>;
34
- amendAppointment(key: VMO2AppointmentKey, patch: VMO2AmendAppointmentPatch): Promise<WholesaleResult<{
35
- appointmentKey: VMO2AppointmentKey;
36
- }>>;
37
- cancelAppointment(key: VMO2AppointmentKey): Promise<WholesaleResult<{
38
- appointmentKey: VMO2AppointmentKey;
39
- }>>;
40
- createOrder(req: VMO2CreateOrderRequest): Promise<WholesaleResult<VMO2CreateOrderResult>>;
41
- amendOrder(orderId: VMO2OrderId, patch: VMO2AmendOrderPatch): Promise<WholesaleResult<{
42
- orderId: VMO2OrderId;
43
- }>>;
44
- cancelOrder(orderId: VMO2OrderId, reason?: VMO2CancellationReasonCode): Promise<WholesaleResult<{
45
- orderId: VMO2OrderId;
46
- }>>;
47
- getOrder(orderId: VMO2OrderId, opts?: {
48
- displaySummary?: boolean;
49
- }): Promise<WholesaleResult<VMO2OrderDetails>>;
50
- unsolicitedCease(req: {
51
- premiseId: VMO2PremiseId;
52
- switchOrderReference?: SwitchOrderReference;
53
- requestedOrderCompletionDate: ISODate;
54
- }): Promise<WholesaleResult<VMO2CreateOrderResult>>;
55
- createIncident(req: VMO2CreateIncidentRequest): Promise<WholesaleResult<VMO2CreateIncidentResult>>;
56
- amendIncident(requestId: VMO2RequestId, patch: VMO2AmendIncidentPatch): Promise<WholesaleResult<{
57
- requestId: VMO2RequestId;
58
- }>>;
59
- cancelIncident(requestId: VMO2RequestId): Promise<WholesaleResult<{
60
- requestId: VMO2RequestId;
61
- }>>;
62
- getIncident(requestId: VMO2RequestId): Promise<WholesaleResult<VMO2IncidentDetails>>;
81
+ searchAddress(uprn: number): Promise<unknown>;
82
+ listAppointments(args: ListAppointmentsArgs): Promise<unknown>;
83
+ reserveAppointment(args: ReserveAppointmentArgs): Promise<unknown>;
84
+ scheduleServiceWorkOrder(args: ScheduleServiceWorkOrderArgs): Promise<unknown>;
85
+ rescheduleServiceWorkOrder(args: RescheduleServiceWorkOrderArgs): Promise<unknown>;
86
+ amendAppointment(args: AmendAppointmentArgs): Promise<unknown>;
87
+ createOrder(principal: unknown, orderId: string, options?: CreateOrderOptions): Promise<unknown>;
88
+ /** Tier-change CoS — wraps `createModifyOrderForYfOrder` style args. */
89
+ amendOrder(principal: unknown, yfOrderId: string): Promise<unknown>;
90
+ /** Pre-install cancel of an in-flight provide order. */
91
+ cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<unknown>;
92
+ /** Active-customer cease (post-Active churn / OTS-losing). */
93
+ ceaseOrder(yfOrderId: string, serviceId: string): Promise<unknown>;
94
+ /** Inbound supplier KCI / notification handler. */
95
+ receiveKci(principal: unknown, dto: unknown): Promise<void>;
96
+ /** Outbound YF→supplier WO sync (cancel propagation, etc.). */
97
+ syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs): Promise<unknown>;
98
+ /** Inbound supplier→YF WO change application (post-receiveKci). */
99
+ syncWorkOrderFromSupplier(order: unknown, dto: unknown): Promise<unknown>;
100
+ retrieveServiceIdByUprn(uprn: number): Promise<string | null>;
101
+ updateOrderExternalRef(args: UpdateOrderExternalRefArgs): Promise<void>;
102
+ /**
103
+ * Register a typed handler for fanout of supplier order notifications.
104
+ * The webhook controller invokes `dispatchOrderNotification` on the
105
+ * provider after envelope-mapping; registered handlers are called in
106
+ * order with the normalized `VMO2OrderNotification` envelope (the
107
+ * SDK uses VMO2 envelope types as the cross-supplier normalization
108
+ * shape — easier to reason about + matches the upcoming VMO2 wire).
109
+ */
63
110
  onOrderNotification(handler: (n: VMO2OrderNotification) => Promise<void>): void;
64
111
  onSMRNotification(handler: (n: VMO2SMRNotification) => Promise<void>): void;
65
112
  }
@@ -1,155 +1,99 @@
1
1
  /**
2
- * NetomniaProvider — concrete `WholesaleProvider` implementation that
3
- * routes capability calls to the Netomnia wholesale platform. Netomnia
4
- * exposes its tenant API via the FibreCafe gateway, so this provider's
5
- * dependency function-bag is **FibreCafe-shaped** (matches the wire +
6
- * existing NET module function signatures).
2
+ * NetomniaProvider — concrete `WholesaleProvider` for the Netomnia
3
+ * wholesale platform (which exposes its tenant API via the FibreCafe
4
+ * gateway). Same instance handles all three Netomnia-routed `OrderProvider`
5
+ * values (NETOMNIA / NETOMNIA_LEGACY / BRSK).
7
6
  *
8
- * **Architecture (T-20260508-009 — confirmed with Frank 2026-05-08):**
7
+ * **Architecture (T-20260508-009/010 — Path B):**
9
8
  *
10
- * 1. `WholesaleProvider` interface is **VMO2-shaped**. It's the single
11
- * abstraction call sites speak — same vocabulary regardless of supplier.
12
- * 2. `NetomniaProvider` implements that interface. Its **public methods
13
- * accept VMO2-shaped requests** (per the abstraction contract).
14
- * 3. `NetomniaProviderDeps` is **FibreCafe-shaped**. Modules wire their
15
- * existing NestJS-injected NET clients in directly — zero
16
- * transformation at the dep boundary.
17
- * 4. **Mapping VMO2 ↔ FibreCafe happens INSIDE each NetomniaProvider
18
- * method.** VMO2 in → map to FibreCafe → call dep → FibreCafe response
19
- * → map to VMO2 → return. The single place supplier shape differences
20
- * are reconciled.
9
+ * NetomniaProvider is a **strategy-dispatch pass-through**. Its public
10
+ * methods take YF-context args (matching existing NET module method
11
+ * signatures) and delegate 1:1 to deps. The deps are wired to the
12
+ * existing module-level `NetomniaProvider` / `NetomniaService` methods
13
+ * — zero behaviour change at the call site.
21
14
  *
22
- * Phase 0 scope: mapping bodies are **stubs** (throw NotImplementedError
23
- * for non-trivial mappings). T-20260508-010 will fill in the real mapping
24
- * logic alongside the call-site refactor — at that point the call sites
25
- * actually exercise these methods, so mapping correctness can be
26
- * validated with live behavior.
27
- *
28
- * The same instance handles all three Netomnia-routed `OrderProvider`
29
- * values (NETOMNIA / NETOMNIA_LEGACY / BRSK) — they share the same NET
30
- * wholesale platform.
15
+ * Why a thin pass-through is enough:
16
+ * - The "abstraction value" provided is strategy dispatch — call sites
17
+ * do `factory.get(provider).createOrder(...)` instead of
18
+ * `if (isNetomnia) netomniaProvider.createNetomniaOrder(...)`.
19
+ * - DTO-building / HTTP / PE-log emit semantics already live in the
20
+ * existing NET module methods. Lifting them into the SDK (via deps
21
+ * that take FibreCafe DTOs and a built-in HTTP transport) was
22
+ * considered (Path A/C) but rejected for Phase 0 — that's a follow-up
23
+ * standardization step we can take if/when VMO2 alignment requires it.
31
24
  *
32
25
  * Inbound notification handling: provider stores registered handlers
33
- * in-memory. A future webhook controller (T-20260508-010) invokes
34
- * `dispatchOrderNotification` / `dispatchSMRNotification` to fan out.
26
+ * in-memory. The wholesale webhook controller (T-20260508-010 Step 1d)
27
+ * invokes `dispatchOrderNotification` / `dispatchSMRNotification` after
28
+ * envelope-mapping; registered handlers are fanned out.
35
29
  */
36
- import type { WholesaleProvider } from '../interface';
37
- import type { WholesaleResult } from '../types/wholesale';
38
- import type { ISODate, SwitchOrderReference } from '../types/wholesale';
39
- import type { VMO2AddressLookupRequest, VMO2AddressLookupResult, VMO2ListAppointmentRequest, VMO2ListAppointmentResult, VMO2ReserveAppointmentRequest, VMO2ReserveAppointmentResult, VMO2ConfirmAppointmentRequest, VMO2AppointmentDetails, VMO2AppointmentKey, VMO2AmendAppointmentPatch, VMO2CreateOrderRequest, VMO2CreateOrderResult, VMO2AmendOrderPatch, VMO2OrderDetails, VMO2OrderId, VMO2CancellationReasonCode, VMO2PremiseId, VMO2CreateIncidentRequest, VMO2CreateIncidentResult, VMO2AmendIncidentPatch, VMO2IncidentDetails, VMO2RequestId, VMO2OrderNotification, VMO2SMRNotification } from '../types/vmo2';
40
- import type { FibreCafeServiceAvailabilityQuery, FibreCafeServiceAvailability200, FibreCafeAvailableAppointmentsQuery, FibreCafeAvailableAppointments200, FibreCafeReserveAppointmentDto, FibreCafeReserveAppointment201, FibreCafeOrderCreateDto, FibreCafeOrderCreate202, FibreCafeProvideServiceOrderAmendmentDto, FibreCafeOrderAmendAccept202, FibreCafeServiceOrderCancellationDto, FibreCafeServiceProblemCreateDto, FibreCafeServiceProblemCreate202, FibreCafeServiceProblemAmendmentDto, FibreCafeServiceProblemCancellationDto, FibreCafeServiceProblemDetails, FibreCafeProvideServiceOrder } from '../types/fibrecafe';
30
+ import type { WholesaleProvider, CreateOrderOptions, CancellationReasonCode, ReserveAppointmentArgs, ListAppointmentsArgs, ScheduleServiceWorkOrderArgs, RescheduleServiceWorkOrderArgs, AmendAppointmentArgs, SyncWorkOrderToSupplierArgs, UpdateOrderExternalRefArgs } from '../interface';
31
+ import type { VMO2OrderNotification, VMO2SMRNotification } from '../types/vmo2';
41
32
  /**
42
- * Function-bag of FibreCafe-shaped dependencies that NetomniaProvider
43
- * delegates to. Modules wire each property to their existing typed NET
44
- * client method — typically these come from NestJS-injected services
45
- * (`netomniaProvider.getServiceAvailability` / `.createProvideOrder` /
46
- * etc.). The adapter boundary is intentionally narrow so refactoring to
47
- * use NetomniaProvider doesn't change runtime behavior or PE-log emit
48
- * semantics — those still live in the wrapped function bodies.
33
+ * Function-bag of dependencies the NetomniaProvider delegates to.
34
+ *
35
+ * **Deps shape mirrors the WholesaleProvider interface 1:1.** Modules wire
36
+ * each property to their existing NestJS-injected NET service/provider
37
+ * method that already takes the equivalent YF-context args. The adapter
38
+ * boundary is intentionally narrow: zero transformation between dep input
39
+ * and the existing module method's input.
49
40
  *
50
- * Where a FibreCafe endpoint has no current YF caller (e.g. service-problems),
51
- * the dep is still typed; the wired function may be a NotImplementedError
52
- * thrower until that endpoint is built out.
41
+ * Example (in a module's WholesaleModule.useFactory):
42
+ * ```typescript
43
+ * {
44
+ * createOrder: (p, id, opts) => existingNetomniaProvider.createNetomniaOrder(p, id, opts),
45
+ * cancelOrder: (id, reason) => existingNetomniaProvider.cancelProvideOrder(id, reason),
46
+ * amendOrder: (p, id) => existingNetomniaService.createModifyOrderForYfOrder(p, id),
47
+ * ...
48
+ * }
49
+ * ```
53
50
  */
54
51
  export interface NetomniaProviderDeps {
55
- getServiceAvailability: (query: FibreCafeServiceAvailabilityQuery) => Promise<WholesaleResult<FibreCafeServiceAvailability200>>;
56
- getAvailableAppointments: (query: FibreCafeAvailableAppointmentsQuery) => Promise<WholesaleResult<FibreCafeAvailableAppointments200>>;
57
- reserveAppointment: (dto: FibreCafeReserveAppointmentDto) => Promise<WholesaleResult<FibreCafeReserveAppointment201>>;
58
- /**
59
- * FibreCafe doesn't expose a separate confirm endpoint — confirmation is
60
- * implicit at order placement. NetomniaProvider's confirmAppointment is a
61
- * no-op pass-through; this dep is provided for symmetry and future use.
62
- */
63
- confirmAppointment?: (appointmentId: string) => Promise<WholesaleResult<{
64
- appointmentId: string;
65
- }>>;
66
- /**
67
- * FibreCafe retrieves appointment via the parent order — this dep wraps
68
- * the existing module logic that reconstructs appointment state from the
69
- * locally-cached NET WorkOrder mirror.
70
- */
71
- getAppointment?: (appointmentId: string) => Promise<WholesaleResult<FibreCafeReserveAppointment201>>;
72
- /** Reappoint flow — same endpoint as reserve with rescheduleType discriminator. */
73
- amendAppointment?: (dto: FibreCafeReserveAppointmentDto) => Promise<WholesaleResult<FibreCafeReserveAppointment201>>;
74
- /** WO-level cancellation propagating to NET-side appointment. */
75
- cancelAppointment?: (appointmentId: string, reasonCode?: string) => Promise<WholesaleResult<{
76
- appointmentId: string;
77
- }>>;
78
- createOrder: (dto: FibreCafeOrderCreateDto) => Promise<WholesaleResult<FibreCafeOrderCreate202>>;
79
- amendOrder: (dto: FibreCafeProvideServiceOrderAmendmentDto) => Promise<WholesaleResult<FibreCafeOrderAmendAccept202>>;
80
- cancelOrder: (dto: FibreCafeServiceOrderCancellationDto) => Promise<WholesaleResult<{
81
- cancellationRequestId: string;
82
- }>>;
83
- getOrder: (orderId: string) => Promise<WholesaleResult<FibreCafeProvideServiceOrder>>;
84
- createIncident?: (dto: FibreCafeServiceProblemCreateDto) => Promise<WholesaleResult<FibreCafeServiceProblemCreate202>>;
85
- amendIncident?: (dto: FibreCafeServiceProblemAmendmentDto) => Promise<WholesaleResult<{
86
- amendmentId: string;
87
- }>>;
88
- cancelIncident?: (dto: FibreCafeServiceProblemCancellationDto) => Promise<WholesaleResult<{
89
- cancellationId: string;
90
- }>>;
91
- getIncident?: (serviceProblemId: string) => Promise<WholesaleResult<FibreCafeServiceProblemDetails>>;
52
+ searchAddress: (uprn: number) => Promise<unknown>;
53
+ listAppointments: (args: ListAppointmentsArgs) => Promise<unknown>;
54
+ reserveAppointment: (args: ReserveAppointmentArgs) => Promise<unknown>;
55
+ scheduleServiceWorkOrder: (args: ScheduleServiceWorkOrderArgs) => Promise<unknown>;
56
+ rescheduleServiceWorkOrder: (args: RescheduleServiceWorkOrderArgs) => Promise<unknown>;
57
+ amendAppointment: (args: AmendAppointmentArgs) => Promise<unknown>;
58
+ createOrder: (principal: unknown, orderId: string, options?: CreateOrderOptions) => Promise<unknown>;
59
+ amendOrder: (principal: unknown, yfOrderId: string) => Promise<unknown>;
60
+ cancelOrder: (yfOrderId: string, reasonCode: CancellationReasonCode) => Promise<unknown>;
61
+ ceaseOrder: (yfOrderId: string, serviceId: string) => Promise<unknown>;
62
+ receiveKci: (principal: unknown, dto: unknown) => Promise<void>;
63
+ syncWorkOrderToSupplier: (args: SyncWorkOrderToSupplierArgs) => Promise<unknown>;
64
+ syncWorkOrderFromSupplier: (order: unknown, dto: unknown) => Promise<unknown>;
65
+ retrieveServiceIdByUprn: (uprn: number) => Promise<string | null>;
66
+ updateOrderExternalRef: (args: UpdateOrderExternalRefArgs) => Promise<void>;
92
67
  }
93
68
  declare type OrderNotificationHandler = (n: VMO2OrderNotification) => Promise<void>;
94
69
  declare type SMRNotificationHandler = (n: VMO2SMRNotification) => Promise<void>;
95
- /**
96
- * Sentinel error surfaced by Phase 0 stub mapping bodies. T-20260508-010
97
- * fills in real bodies; until then any call to these methods at runtime
98
- * throws this. Tests verify the *delegation* path works for methods whose
99
- * mapping is trivial enough to ship in Phase 0.
100
- */
101
- export declare class NotImplementedError extends Error {
102
- readonly method: string;
103
- constructor(method: string);
104
- }
105
70
  export declare class NetomniaProvider implements WholesaleProvider {
106
71
  private readonly deps;
107
72
  private readonly orderHandlers;
108
73
  private readonly smrHandlers;
109
74
  constructor(deps: NetomniaProviderDeps);
110
- searchAddress(_req: VMO2AddressLookupRequest): Promise<WholesaleResult<VMO2AddressLookupResult>>;
111
- listAppointments(_req: VMO2ListAppointmentRequest): Promise<WholesaleResult<VMO2ListAppointmentResult>>;
112
- reserveAppointment(_req: VMO2ReserveAppointmentRequest): Promise<WholesaleResult<VMO2ReserveAppointmentResult>>;
113
- confirmAppointment(_req: VMO2ConfirmAppointmentRequest): Promise<WholesaleResult<{
114
- appointmentKey: VMO2AppointmentKey;
115
- }>>;
116
- getAppointment(_key: VMO2AppointmentKey): Promise<WholesaleResult<VMO2AppointmentDetails>>;
117
- amendAppointment(_key: VMO2AppointmentKey, _patch: VMO2AmendAppointmentPatch): Promise<WholesaleResult<{
118
- appointmentKey: VMO2AppointmentKey;
119
- }>>;
120
- cancelAppointment(_key: VMO2AppointmentKey): Promise<WholesaleResult<{
121
- appointmentKey: VMO2AppointmentKey;
122
- }>>;
123
- createOrder(_req: VMO2CreateOrderRequest): Promise<WholesaleResult<VMO2CreateOrderResult>>;
124
- amendOrder(_orderId: VMO2OrderId, _patch: VMO2AmendOrderPatch): Promise<WholesaleResult<{
125
- orderId: VMO2OrderId;
126
- }>>;
127
- cancelOrder(_orderId: VMO2OrderId, _reason?: VMO2CancellationReasonCode): Promise<WholesaleResult<{
128
- orderId: VMO2OrderId;
129
- }>>;
130
- getOrder(_orderId: VMO2OrderId, _opts?: {
131
- displaySummary?: boolean;
132
- }): Promise<WholesaleResult<VMO2OrderDetails>>;
133
- unsolicitedCease(_req: {
134
- premiseId: VMO2PremiseId;
135
- switchOrderReference?: SwitchOrderReference;
136
- requestedOrderCompletionDate: ISODate;
137
- }): Promise<WholesaleResult<VMO2CreateOrderResult>>;
138
- createIncident(_req: VMO2CreateIncidentRequest): Promise<WholesaleResult<VMO2CreateIncidentResult>>;
139
- amendIncident(_requestId: VMO2RequestId, _patch: VMO2AmendIncidentPatch): Promise<WholesaleResult<{
140
- requestId: VMO2RequestId;
141
- }>>;
142
- cancelIncident(_requestId: VMO2RequestId): Promise<WholesaleResult<{
143
- requestId: VMO2RequestId;
144
- }>>;
145
- getIncident(_requestId: VMO2RequestId): Promise<WholesaleResult<VMO2IncidentDetails>>;
75
+ searchAddress(uprn: number): Promise<unknown>;
76
+ listAppointments(args: ListAppointmentsArgs): Promise<unknown>;
77
+ reserveAppointment(args: ReserveAppointmentArgs): Promise<unknown>;
78
+ scheduleServiceWorkOrder(args: ScheduleServiceWorkOrderArgs): Promise<unknown>;
79
+ rescheduleServiceWorkOrder(args: RescheduleServiceWorkOrderArgs): Promise<unknown>;
80
+ amendAppointment(args: AmendAppointmentArgs): Promise<unknown>;
81
+ createOrder(principal: unknown, orderId: string, options?: CreateOrderOptions): Promise<unknown>;
82
+ amendOrder(principal: unknown, yfOrderId: string): Promise<unknown>;
83
+ cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<unknown>;
84
+ ceaseOrder(yfOrderId: string, serviceId: string): Promise<unknown>;
85
+ receiveKci(principal: unknown, dto: unknown): Promise<void>;
86
+ syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs): Promise<unknown>;
87
+ syncWorkOrderFromSupplier(order: unknown, dto: unknown): Promise<unknown>;
88
+ retrieveServiceIdByUprn(uprn: number): Promise<string>;
89
+ updateOrderExternalRef(args: UpdateOrderExternalRefArgs): Promise<void>;
146
90
  onOrderNotification(handler: OrderNotificationHandler): void;
147
91
  onSMRNotification(handler: SMRNotificationHandler): void;
148
92
  /**
149
- * Internal dispatch — invoked by the webhook controller (T-20260508-010)
150
- * after mapping a FibreCafe KCI to a VMO2OrderNotification envelope. Fans
151
- * out to every registered handler in registration order. Handler errors
152
- * are aggregated and re-thrown after all handlers complete.
93
+ * Internal dispatch — invoked by the wholesale webhook controller after
94
+ * mapping a supplier KCI to a normalized `VMO2OrderNotification` envelope.
95
+ * Fans out to every registered handler in registration order. Handler
96
+ * errors are aggregated and re-thrown after all handlers complete.
153
97
  */
154
98
  dispatchOrderNotification(n: VMO2OrderNotification): Promise<void>;
155
99
  dispatchSMRNotification(n: VMO2SMRNotification): Promise<void>;
@@ -9,133 +9,60 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.AggregateNotificationError = exports.NetomniaProvider = exports.NotImplementedError = void 0;
13
- /**
14
- * Sentinel error surfaced by Phase 0 stub mapping bodies. T-20260508-010
15
- * fills in real bodies; until then any call to these methods at runtime
16
- * throws this. Tests verify the *delegation* path works for methods whose
17
- * mapping is trivial enough to ship in Phase 0.
18
- */
19
- class NotImplementedError extends Error {
20
- constructor(method) {
21
- super(`NetomniaProvider.${method} mapping not yet implemented (Phase 0 stub) — ` +
22
- `lands in T-20260508-010 alongside call-site refactor.`);
23
- this.method = method;
24
- this.name = 'NotImplementedError';
25
- }
26
- }
27
- exports.NotImplementedError = NotImplementedError;
12
+ exports.AggregateNotificationError = exports.NetomniaProvider = void 0;
28
13
  class NetomniaProvider {
29
14
  constructor(deps) {
30
15
  this.deps = deps;
31
16
  this.orderHandlers = [];
32
17
  this.smrHandlers = [];
33
18
  }
34
- // ──────────────────────────────────────────────────────────────────
35
- // Address / availability
36
- // ──────────────────────────────────────────────────────────────────
37
- searchAddress(_req) {
38
- return __awaiter(this, void 0, void 0, function* () {
39
- // Phase 0 stub: the VMO2→FibreCafe mapping for searchAddress is
40
- // non-trivial (VMO2 returns premise + capability detail; FibreCafe
41
- // returns address + service-spec). Real mapping lands in T-010 when
42
- // call sites convert. Until then this throws to surface accidental
43
- // call-site exercise.
44
- throw new NotImplementedError('searchAddress');
45
- });
19
+ // ── Pass-through delegation ────────────────────────────────────────
20
+ searchAddress(uprn) {
21
+ return this.deps.searchAddress(uprn);
46
22
  }
47
- // ──────────────────────────────────────────────────────────────────
48
- // Appointments
49
- // ──────────────────────────────────────────────────────────────────
50
- listAppointments(_req) {
51
- return __awaiter(this, void 0, void 0, function* () {
52
- throw new NotImplementedError('listAppointments');
53
- });
23
+ listAppointments(args) {
24
+ return this.deps.listAppointments(args);
54
25
  }
55
- reserveAppointment(_req) {
56
- return __awaiter(this, void 0, void 0, function* () {
57
- throw new NotImplementedError('reserveAppointment');
58
- });
26
+ reserveAppointment(args) {
27
+ return this.deps.reserveAppointment(args);
59
28
  }
60
- confirmAppointment(_req) {
61
- return __awaiter(this, void 0, void 0, function* () {
62
- // FibreCafe has no separate confirm step — would be a no-op for NET
63
- // path. Stubbed until T-010 validates the assumption end-to-end.
64
- throw new NotImplementedError('confirmAppointment');
65
- });
29
+ scheduleServiceWorkOrder(args) {
30
+ return this.deps.scheduleServiceWorkOrder(args);
66
31
  }
67
- getAppointment(_key) {
68
- return __awaiter(this, void 0, void 0, function* () {
69
- throw new NotImplementedError('getAppointment');
70
- });
32
+ rescheduleServiceWorkOrder(args) {
33
+ return this.deps.rescheduleServiceWorkOrder(args);
71
34
  }
72
- amendAppointment(_key, _patch) {
73
- return __awaiter(this, void 0, void 0, function* () {
74
- throw new NotImplementedError('amendAppointment');
75
- });
35
+ amendAppointment(args) {
36
+ return this.deps.amendAppointment(args);
76
37
  }
77
- cancelAppointment(_key) {
78
- return __awaiter(this, void 0, void 0, function* () {
79
- throw new NotImplementedError('cancelAppointment');
80
- });
38
+ createOrder(principal, orderId, options) {
39
+ return this.deps.createOrder(principal, orderId, options);
81
40
  }
82
- // ──────────────────────────────────────────────────────────────────
83
- // Orders
84
- // ──────────────────────────────────────────────────────────────────
85
- createOrder(_req) {
86
- return __awaiter(this, void 0, void 0, function* () {
87
- throw new NotImplementedError('createOrder');
88
- });
41
+ amendOrder(principal, yfOrderId) {
42
+ return this.deps.amendOrder(principal, yfOrderId);
89
43
  }
90
- amendOrder(_orderId, _patch) {
91
- return __awaiter(this, void 0, void 0, function* () {
92
- throw new NotImplementedError('amendOrder');
93
- });
44
+ cancelOrder(yfOrderId, reasonCode) {
45
+ return this.deps.cancelOrder(yfOrderId, reasonCode);
94
46
  }
95
- cancelOrder(_orderId, _reason) {
96
- return __awaiter(this, void 0, void 0, function* () {
97
- throw new NotImplementedError('cancelOrder');
98
- });
47
+ ceaseOrder(yfOrderId, serviceId) {
48
+ return this.deps.ceaseOrder(yfOrderId, serviceId);
99
49
  }
100
- getOrder(_orderId, _opts) {
101
- return __awaiter(this, void 0, void 0, function* () {
102
- throw new NotImplementedError('getOrder');
103
- });
104
- }
105
- unsolicitedCease(_req) {
106
- return __awaiter(this, void 0, void 0, function* () {
107
- // FibreCafe doesn't have an outbound "unsolicited cease" — it's an
108
- // inbound-only KCI on the Netomnia side. NetomniaProvider exposes the
109
- // method for interface symmetry; calling it always throws.
110
- throw new NotImplementedError('unsolicitedCease');
111
- });
50
+ receiveKci(principal, dto) {
51
+ return this.deps.receiveKci(principal, dto);
112
52
  }
113
- // ──────────────────────────────────────────────────────────────────
114
- // Service Management
115
- // ──────────────────────────────────────────────────────────────────
116
- createIncident(_req) {
117
- return __awaiter(this, void 0, void 0, function* () {
118
- throw new NotImplementedError('createIncident');
119
- });
53
+ syncWorkOrderToSupplier(args) {
54
+ return this.deps.syncWorkOrderToSupplier(args);
120
55
  }
121
- amendIncident(_requestId, _patch) {
122
- return __awaiter(this, void 0, void 0, function* () {
123
- throw new NotImplementedError('amendIncident');
124
- });
56
+ syncWorkOrderFromSupplier(order, dto) {
57
+ return this.deps.syncWorkOrderFromSupplier(order, dto);
125
58
  }
126
- cancelIncident(_requestId) {
127
- return __awaiter(this, void 0, void 0, function* () {
128
- throw new NotImplementedError('cancelIncident');
129
- });
59
+ retrieveServiceIdByUprn(uprn) {
60
+ return this.deps.retrieveServiceIdByUprn(uprn);
130
61
  }
131
- getIncident(_requestId) {
132
- return __awaiter(this, void 0, void 0, function* () {
133
- throw new NotImplementedError('getIncident');
134
- });
62
+ updateOrderExternalRef(args) {
63
+ return this.deps.updateOrderExternalRef(args);
135
64
  }
136
- // ──────────────────────────────────────────────────────────────────
137
- // Inbound notification subscription
138
- // ──────────────────────────────────────────────────────────────────
65
+ // ── Inbound notification subscription ─────────────────────────────
139
66
  onOrderNotification(handler) {
140
67
  this.orderHandlers.push(handler);
141
68
  }
@@ -143,10 +70,10 @@ class NetomniaProvider {
143
70
  this.smrHandlers.push(handler);
144
71
  }
145
72
  /**
146
- * Internal dispatch — invoked by the webhook controller (T-20260508-010)
147
- * after mapping a FibreCafe KCI to a VMO2OrderNotification envelope. Fans
148
- * out to every registered handler in registration order. Handler errors
149
- * are aggregated and re-thrown after all handlers complete.
73
+ * Internal dispatch — invoked by the wholesale webhook controller after
74
+ * mapping a supplier KCI to a normalized `VMO2OrderNotification` envelope.
75
+ * Fans out to every registered handler in registration order. Handler
76
+ * errors are aggregated and re-thrown after all handlers complete.
150
77
  */
151
78
  dispatchOrderNotification(n) {
152
79
  return __awaiter(this, void 0, void 0, function* () {
@@ -10,25 +10,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  /**
13
- * NetomniaProvider Phase 0 tests.
13
+ * NetomniaProvider tests — verify pass-through delegation pattern.
14
14
  *
15
- * **What we verify in Phase 0 (T-20260508-009):**
16
- * 1. NetomniaProvider is structurally assignable to WholesaleProvider
15
+ * Path B (T-20260508-009/010): NetomniaProvider's public methods delegate
16
+ * 1:1 to deps that match the WholesaleProvider interface signature. These
17
+ * tests assert:
18
+ * 1. Each public method calls the matching dep with identical args.
19
+ * 2. The dep's return value is forwarded unchanged.
20
+ * 3. NetomniaProvider is structurally assignable to WholesaleProvider
17
21
  * (compile-time check via the class declaration).
18
- * 2. NetomniaProviderDeps is FibreCafe-shaped — modules can wire their
19
- * existing typed NET functions in. Verified by constructing a
20
- * stub-deps object that passes type-check.
21
- * 3. Each outbound method throws `NotImplementedError` until T-010 fills
22
- * in the VMO2↔FibreCafe mapping bodies.
23
- * 4. Inbound handler registration + dispatch + error aggregation work
24
- * as documented (these are NOT mapping-dependent — they fan out to
25
- * registered handlers regardless of supplier).
26
- *
27
- * **What's deferred to T-20260508-010 tests:** end-to-end behaviour parity
28
- * — the real test is "running through NetomniaProvider produces identical
29
- * NET-side state vs running through legacy direct calls". That requires
30
- * call sites + real deps + recorded baseline; lives in T-013 acceptance
31
- * gate.
22
+ * 4. Inbound handler registration + dispatch + error aggregation work.
32
23
  *
33
24
  * Run: `npm run test:wholesale-provider`
34
25
  */
@@ -51,134 +42,238 @@ function assert(label, cond, hint) {
51
42
  function brand(s) {
52
43
  return s;
53
44
  }
54
- function ok(data) {
55
- return {
56
- status: 'accepted',
57
- eventDateTime: brand('2026-05-09T00:00:00Z'),
58
- data,
59
- };
60
- }
61
45
  /**
62
- * Build a stub deps object that returns canned `WholesaleResult` envelopes.
63
- * Every method returns a passthrough OK; no method is exercised in Phase 0
64
- * (provider methods all throw). Deps exist so the type-check passes and
65
- * future tests can add behaviour assertions.
46
+ * Build deps that record their call args + return a sentinel value. Each
47
+ * dep is identical-shape to the corresponding WholesaleProvider method so
48
+ * the pass-through pattern can be tested.
66
49
  */
67
50
  function buildDeps() {
68
- const stub = (data) => () => Promise.resolve(ok(data));
69
- return {
70
- getServiceAvailability: stub({
71
- address: { identifier: { id: 'X', type: 'UPRN' } },
72
- serviceSpecifications: [],
73
- }),
74
- getAvailableAppointments: stub({ availableTimeslots: [] }),
75
- reserveAppointment: stub({
76
- id: 'A1',
77
- supplierReference: 'NET-A1',
78
- address: { id: 'X', type: 'UPRN' },
79
- serviceSpecification: { id: 'fttp' },
80
- timeslot: {
81
- timeslotStartDateTime: '2026-05-09T08:00:00Z',
82
- timeslotEndDateTime: '2026-05-09T13:00:00Z',
83
- },
84
- }),
85
- createOrder: stub({}),
86
- amendOrder: stub({}),
87
- cancelOrder: stub({ cancellationRequestId: 'CXL-1' }),
88
- getOrder: stub({
89
- id: 'O1',
90
- tenant: 'YOUFIBRE',
91
- status: 'IN_PROGRESS',
92
- address: { id: 'X', type: 'UPRN' },
93
- serviceOrderItem: {
94
- serviceSpecification: { id: 'fttp' },
95
- serviceCharacteristics: [],
96
- },
97
- primaryContact: { name: 'Bob', phoneNumber: '01234' },
98
- }),
99
- // Optional deps left unset — provider stubs throw when called regardless
51
+ const log = [];
52
+ const sentinel = Symbol('NET-RETURN');
53
+ const record = (method) => (...args) => {
54
+ log.push({ method, args });
55
+ return Promise.resolve(sentinel);
100
56
  };
57
+ const recordVoid = (method) => (...args) => {
58
+ log.push({ method, args });
59
+ return Promise.resolve(undefined);
60
+ };
61
+ const deps = {
62
+ searchAddress: record('searchAddress'),
63
+ listAppointments: record('listAppointments'),
64
+ reserveAppointment: record('reserveAppointment'),
65
+ scheduleServiceWorkOrder: record('scheduleServiceWorkOrder'),
66
+ rescheduleServiceWorkOrder: record('rescheduleServiceWorkOrder'),
67
+ amendAppointment: record('amendAppointment'),
68
+ createOrder: record('createOrder'),
69
+ amendOrder: record('amendOrder'),
70
+ cancelOrder: record('cancelOrder'),
71
+ ceaseOrder: record('ceaseOrder'),
72
+ receiveKci: recordVoid('receiveKci'),
73
+ syncWorkOrderToSupplier: record('syncWorkOrderToSupplier'),
74
+ syncWorkOrderFromSupplier: record('syncWorkOrderFromSupplier'),
75
+ retrieveServiceIdByUprn: (uprn) => {
76
+ log.push({ method: 'retrieveServiceIdByUprn', args: [uprn] });
77
+ return Promise.resolve('NET-SVC-1');
78
+ },
79
+ updateOrderExternalRef: recordVoid('updateOrderExternalRef'),
80
+ };
81
+ return { deps, log, sentinel };
101
82
  }
102
83
  function run() {
103
84
  return __awaiter(this, void 0, void 0, function* () {
104
- console.log('NetomniaProvider — Phase 0 tests\n');
105
- // ── 1. Structural assignment (compile-time check) ─────────────────
106
- console.log('Test: NetomniaProvider implements WholesaleProvider (compile-time)');
85
+ console.log('NetomniaProvider — pass-through delegation tests\n');
86
+ console.log('Test: searchAddress delegates to deps');
107
87
  {
108
- const p = new netomnia_provider_1.NetomniaProvider(buildDeps());
109
- assert('class instantiates', p instanceof netomnia_provider_1.NetomniaProvider);
88
+ const { deps, log, sentinel } = buildDeps();
89
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
90
+ const result = yield p.searchAddress(123);
91
+ assert('called once', log.length === 1);
92
+ assert('correct method', log[0].method === 'searchAddress');
93
+ assert('args forwarded', JSON.stringify(log[0].args) === JSON.stringify([123]));
94
+ assert('return forwarded', result === sentinel);
95
+ }
96
+ console.log('Test: listAppointments delegates to deps');
97
+ {
98
+ const { deps, log } = buildDeps();
99
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
100
+ const args = { uprn: 123, start: '2026-06-01', appointmentPurpose: 'PROVIDE' };
101
+ yield p.listAppointments(args);
102
+ assert('called once', log.length === 1);
103
+ assert('correct method', log[0].method === 'listAppointments');
104
+ assert('args forwarded', log[0].args[0] === args);
105
+ }
106
+ console.log('Test: reserveAppointment delegates to deps');
107
+ {
108
+ const { deps, log } = buildDeps();
109
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
110
+ const args = {
111
+ yfAppointmentId: 'APT-1',
112
+ uprn: 123,
113
+ startDateTime: '2026-06-01T08:00:00Z',
114
+ endDateTime: '2026-06-01T13:00:00Z',
115
+ purpose: 'PROVIDE',
116
+ yfOrderId: 'ORD-1',
117
+ woProperties: {},
118
+ };
119
+ yield p.reserveAppointment(args);
120
+ assert('called once', log.length === 1);
121
+ assert('correct method', log[0].method === 'reserveAppointment');
122
+ assert('args forwarded', log[0].args[0] === args);
110
123
  }
111
- // ── 2. NetomniaProviderDeps is FibreCafe-shaped ───────────────────
112
- console.log('Test: NetomniaProviderDeps accepts FibreCafe-shaped function bag');
113
- {
114
- const deps = buildDeps();
115
- assert('createOrder typed dep exists', typeof deps.createOrder === 'function');
116
- assert('getServiceAvailability typed dep exists', typeof deps.getServiceAvailability === 'function');
117
- assert('cancelOrder typed dep exists', typeof deps.cancelOrder === 'function');
118
- }
119
- // ── 3. Phase 0 stubs — every outbound method throws ──────────────
120
- console.log('Test: every outbound method throws NotImplementedError (Phase 0)');
121
- const p = new netomnia_provider_1.NetomniaProvider(buildDeps());
122
- function expectNotImplemented(label, fn) {
123
- return __awaiter(this, void 0, void 0, function* () {
124
- try {
125
- yield fn();
126
- assert(`${label} threw`, false, 'expected NotImplementedError, got no throw');
127
- }
128
- catch (err) {
129
- assert(`${label} threw NotImplementedError`, err instanceof netomnia_provider_1.NotImplementedError);
130
- }
131
- });
132
- }
133
- yield expectNotImplemented('searchAddress', () => p.searchAddress({ query: { kind: 'byAddress', postCode: 'SW1A' } }));
134
- yield expectNotImplemented('listAppointments', () => p.listAppointments({
135
- scope: { orderId: brand('O1') },
136
- networkType: 'xgspon',
137
- journeyType: 'order',
138
- visit: [],
139
- }));
140
- yield expectNotImplemented('reserveAppointment', () => p.reserveAppointment({
141
- scope: { orderId: brand('O1') },
142
- networkType: 'xgspon',
143
- journeyType: 'order',
144
- appointmentSlots: [],
145
- }));
146
- yield expectNotImplemented('confirmAppointment', () => p.confirmAppointment({ appointmentKey: brand('AK1') }));
147
- yield expectNotImplemented('getAppointment', () => p.getAppointment(brand('AK1')));
148
- yield expectNotImplemented('amendAppointment', () => p.amendAppointment(brand('AK1'), { appointmentSlot: 'AM' }));
149
- yield expectNotImplemented('cancelAppointment', () => p.cancelAppointment(brand('AK1')));
150
- yield expectNotImplemented('createOrder', () => p.createOrder({
151
- invoiceAccountNumber: brand('IAN-1'),
152
- orderIntent: 'new provide',
153
- requestedOrderCompletionDate: brand('2026-06-01'),
154
- productsAndServices: {},
155
- }));
156
- yield expectNotImplemented('amendOrder', () => p.amendOrder(brand('O1'), {}));
157
- yield expectNotImplemented('cancelOrder', () => p.cancelOrder(brand('O1')));
158
- yield expectNotImplemented('getOrder', () => p.getOrder(brand('O1')));
159
- yield expectNotImplemented('unsolicitedCease', () => p.unsolicitedCease({
160
- premiseId: brand('P1'),
161
- requestedOrderCompletionDate: brand('2026-06-01'),
162
- }));
163
- yield expectNotImplemented('createIncident', () => p.createIncident({
164
- requestType: 'reactiveincident',
165
- reasonCode: brand('RNC-1'),
166
- }));
167
- yield expectNotImplemented('amendIncident', () => p.amendIncident(brand('R1'), {
168
- kind: 'resolutionAcceptance',
169
- closureAcceptance: true,
170
- }));
171
- yield expectNotImplemented('cancelIncident', () => p.cancelIncident(brand('R1')));
172
- yield expectNotImplemented('getIncident', () => p.getIncident(brand('R1')));
173
- // ── 4. Inbound handler registration + dispatch ────────────────────
124
+ console.log('Test: scheduleServiceWorkOrder delegates to deps');
125
+ {
126
+ const { deps, log } = buildDeps();
127
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
128
+ const args = {
129
+ yfWorkOrderId: 'WO-1',
130
+ uprn: 123,
131
+ startDateTime: '2026-06-01T08:00:00Z',
132
+ endDateTime: '2026-06-01T13:00:00Z',
133
+ yfOrderId: 'ORD-1',
134
+ properties: {},
135
+ };
136
+ yield p.scheduleServiceWorkOrder(args);
137
+ assert('called once', log.length === 1);
138
+ assert('correct method', log[0].method === 'scheduleServiceWorkOrder');
139
+ }
140
+ console.log('Test: rescheduleServiceWorkOrder delegates to deps');
141
+ {
142
+ const { deps, log } = buildDeps();
143
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
144
+ const args = {
145
+ yfWorkOrderId: 'WO-1',
146
+ uprn: 123,
147
+ startDateTime: '2026-06-01T08:00:00Z',
148
+ endDateTime: '2026-06-01T13:00:00Z',
149
+ yfOrderId: 'ORD-1',
150
+ };
151
+ yield p.rescheduleServiceWorkOrder(args);
152
+ assert('called once', log.length === 1);
153
+ assert('correct method', log[0].method === 'rescheduleServiceWorkOrder');
154
+ }
155
+ console.log('Test: amendAppointment delegates to deps');
156
+ {
157
+ const { deps, log } = buildDeps();
158
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
159
+ const args = { yfOrderId: 'ORD-1', yfWorkOrderId: 'WO-1', supplierAppointmentReference: 'NET-A1' };
160
+ yield p.amendAppointment(args);
161
+ assert('called once', log.length === 1);
162
+ assert('correct method', log[0].method === 'amendAppointment');
163
+ }
164
+ console.log('Test: createOrder delegates to deps with all args (principal, orderId, options)');
165
+ {
166
+ const { deps, log, sentinel } = buildDeps();
167
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
168
+ const principal = { id: 'user-1' };
169
+ const opts = { doNotCreateServiceAppointment: true };
170
+ const result = yield p.createOrder(principal, 'ORD-1', opts);
171
+ assert('called once', log.length === 1);
172
+ assert('correct method', log[0].method === 'createOrder');
173
+ assert('principal forwarded', log[0].args[0] === principal);
174
+ assert('orderId forwarded', log[0].args[1] === 'ORD-1');
175
+ assert('options forwarded', log[0].args[2] === opts);
176
+ assert('return forwarded', result === sentinel);
177
+ }
178
+ console.log('Test: createOrder forwards undefined options correctly');
179
+ {
180
+ const { deps, log } = buildDeps();
181
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
182
+ yield p.createOrder({ id: 'user-1' }, 'ORD-1');
183
+ assert('called once', log.length === 1);
184
+ assert('options arg is undefined', log[0].args[2] === undefined);
185
+ }
186
+ console.log('Test: amendOrder delegates to deps');
187
+ {
188
+ const { deps, log } = buildDeps();
189
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
190
+ const principal = { id: 'user-1' };
191
+ yield p.amendOrder(principal, 'ORD-1');
192
+ assert('called once', log.length === 1);
193
+ assert('correct method', log[0].method === 'amendOrder');
194
+ assert('principal forwarded', log[0].args[0] === principal);
195
+ assert('orderId forwarded', log[0].args[1] === 'ORD-1');
196
+ }
197
+ console.log('Test: cancelOrder delegates to deps');
198
+ {
199
+ const { deps, log } = buildDeps();
200
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
201
+ yield p.cancelOrder('ORD-1', 'CUSTOMER_CHANGED_MIND');
202
+ assert('called once', log.length === 1);
203
+ assert('correct method', log[0].method === 'cancelOrder');
204
+ assert('orderId forwarded', log[0].args[0] === 'ORD-1');
205
+ assert('reasonCode forwarded', log[0].args[1] === 'CUSTOMER_CHANGED_MIND');
206
+ }
207
+ console.log('Test: ceaseOrder delegates to deps');
208
+ {
209
+ const { deps, log } = buildDeps();
210
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
211
+ yield p.ceaseOrder('ORD-1', 'NET-SVC-1');
212
+ assert('called once', log.length === 1);
213
+ assert('correct method', log[0].method === 'ceaseOrder');
214
+ }
215
+ console.log('Test: receiveKci delegates to deps');
216
+ {
217
+ const { deps, log } = buildDeps();
218
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
219
+ const principal = { id: 'user-1' };
220
+ const dto = { id: 'kci-1', sequenceNumber: 1 };
221
+ yield p.receiveKci(principal, dto);
222
+ assert('called once', log.length === 1);
223
+ assert('correct method', log[0].method === 'receiveKci');
224
+ assert('args forwarded', log[0].args[0] === principal && log[0].args[1] === dto);
225
+ }
226
+ console.log('Test: syncWorkOrderToSupplier delegates to deps');
227
+ {
228
+ const { deps, log } = buildDeps();
229
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
230
+ const args = {
231
+ principal: { id: 'user-1' },
232
+ yfOrderId: 'ORD-1',
233
+ yfWorkOrderId: 'WO-1',
234
+ yfChangeReason: { externalId: 'CR-1', entity: 'ChangeReason', type: 'CANCEL', properties: {} },
235
+ };
236
+ yield p.syncWorkOrderToSupplier(args);
237
+ assert('called once', log.length === 1);
238
+ assert('correct method', log[0].method === 'syncWorkOrderToSupplier');
239
+ }
240
+ console.log('Test: syncWorkOrderFromSupplier delegates to deps');
241
+ {
242
+ const { deps, log } = buildDeps();
243
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
244
+ const order = { id: 'ORD-1' };
245
+ const dto = { id: 'kci-1' };
246
+ yield p.syncWorkOrderFromSupplier(order, dto);
247
+ assert('called once', log.length === 1);
248
+ assert('correct method', log[0].method === 'syncWorkOrderFromSupplier');
249
+ }
250
+ console.log('Test: retrieveServiceIdByUprn delegates to deps');
251
+ {
252
+ const { deps, log } = buildDeps();
253
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
254
+ const result = yield p.retrieveServiceIdByUprn(123);
255
+ assert('called once', log.length === 1);
256
+ assert('correct method', log[0].method === 'retrieveServiceIdByUprn');
257
+ assert('return forwarded', result === 'NET-SVC-1');
258
+ }
259
+ console.log('Test: updateOrderExternalRef delegates to deps');
260
+ {
261
+ const { deps, log } = buildDeps();
262
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
263
+ yield p.updateOrderExternalRef({ oldYfId: 'OLD', newYfId: 'NEW' });
264
+ assert('called once', log.length === 1);
265
+ assert('correct method', log[0].method === 'updateOrderExternalRef');
266
+ }
267
+ // ── Inbound handler registration + dispatch ────────────────────────
174
268
  console.log('Test: onOrderNotification registers handler; dispatchOrderNotification fans out');
175
269
  {
176
- const provider = new netomnia_provider_1.NetomniaProvider(buildDeps());
270
+ const { deps } = buildDeps();
271
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
177
272
  const received = [];
178
- provider.onOrderNotification((n) => __awaiter(this, void 0, void 0, function* () {
273
+ p.onOrderNotification((n) => __awaiter(this, void 0, void 0, function* () {
179
274
  received.push(n);
180
275
  }));
181
- provider.onOrderNotification((n) => __awaiter(this, void 0, void 0, function* () {
276
+ p.onOrderNotification((n) => __awaiter(this, void 0, void 0, function* () {
182
277
  received.push(n);
183
278
  }));
184
279
  const notif = {
@@ -188,15 +283,16 @@ function run() {
188
283
  orderId: brand('OID1'),
189
284
  payload: {},
190
285
  };
191
- yield provider.dispatchOrderNotification(notif);
286
+ yield p.dispatchOrderNotification(notif);
192
287
  assert('both handlers fired', received.length === 2);
193
288
  assert('handler 1 received correct notif', received[0].orderId === notif.orderId);
194
289
  }
195
290
  console.log('Test: onSMRNotification registers handler; dispatchSMRNotification fans out');
196
291
  {
197
- const provider = new netomnia_provider_1.NetomniaProvider(buildDeps());
292
+ const { deps } = buildDeps();
293
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
198
294
  const received = [];
199
- provider.onSMRNotification((n) => __awaiter(this, void 0, void 0, function* () {
295
+ p.onSMRNotification((n) => __awaiter(this, void 0, void 0, function* () {
200
296
  received.push(n);
201
297
  }));
202
298
  const notif = {
@@ -206,23 +302,24 @@ function run() {
206
302
  requestId: brand('RID1'),
207
303
  payload: {},
208
304
  };
209
- yield provider.dispatchSMRNotification(notif);
305
+ yield p.dispatchSMRNotification(notif);
210
306
  assert('handler fired', received.length === 1);
211
307
  }
212
308
  console.log('Test: dispatchOrderNotification aggregates handler errors');
213
309
  {
214
- const provider = new netomnia_provider_1.NetomniaProvider(buildDeps());
310
+ const { deps } = buildDeps();
311
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
215
312
  const callState = { secondCalled: false };
216
- provider.onOrderNotification(() => __awaiter(this, void 0, void 0, function* () {
313
+ p.onOrderNotification(() => __awaiter(this, void 0, void 0, function* () {
217
314
  throw new Error('handler 1 boom');
218
315
  }));
219
- provider.onOrderNotification(() => __awaiter(this, void 0, void 0, function* () {
316
+ p.onOrderNotification(() => __awaiter(this, void 0, void 0, function* () {
220
317
  callState.secondCalled = true;
221
318
  throw new Error('handler 2 boom');
222
319
  }));
223
320
  let caught;
224
321
  try {
225
- yield provider.dispatchOrderNotification({
322
+ yield p.dispatchOrderNotification({
226
323
  notificationType: 'ordercompleted',
227
324
  eventDateTime: brand('2026-05-09T00:00:00Z'),
228
325
  transactionId: brand('tx-3'),
@@ -237,6 +334,12 @@ function run() {
237
334
  assert('aggregate error thrown', caught instanceof netomnia_provider_1.AggregateNotificationError);
238
335
  assert('2 underlying errors', caught instanceof netomnia_provider_1.AggregateNotificationError && caught.errors.length === 2);
239
336
  }
337
+ console.log('Test: NetomniaProvider implements WholesaleProvider (compile-time)');
338
+ {
339
+ const { deps } = buildDeps();
340
+ const p = new netomnia_provider_1.NetomniaProvider(deps);
341
+ assert('class instantiates', p instanceof netomnia_provider_1.NetomniaProvider);
342
+ }
240
343
  console.log('\n────────────────────────────────────────');
241
344
  console.log(`Tests: ${passed} passed, ${failed} failed`);
242
345
  if (failed > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d19n/youfibre-odin-sdk",
3
- "version": "2.0.103-beta.6",
3
+ "version": "2.0.103-beta.7",
4
4
  "description": "",
5
5
  "author": "@d19n",
6
6
  "license": "UNLICENSED",