@d19n/youfibre-odin-sdk 2.0.103-beta.8 → 2.0.103-beta.9

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,4 +1,37 @@
1
+ /**
2
+ * WholesaleProvider — strategy-dispatchable interface for wholesale supplier
3
+ * integrations (Netomnia today, VMO2 from July 2026).
4
+ *
5
+ * **Architecture (T-20260508-009/010 — confirmed with Frank 2026-05-09 as
6
+ * "Path B / first step"):**
7
+ *
8
+ * Methods take YF-context arguments (orderId, principal, etc.) — matching
9
+ * the existing NET module method signatures. Each provider implementation
10
+ * builds the supplier-specific wire request internally from this YF context.
11
+ *
12
+ * Rationale:
13
+ * - Existing NET module methods take YF context, not wire DTOs. The SDK
14
+ * provider abstracts the supplier dispatch layer, not the DTO-building.
15
+ * - Method names align with VMO2 spec where it makes sense (`createOrder`,
16
+ * `cancelOrder`, etc.) so call-site vocabulary doesn't change when the
17
+ * provider switches.
18
+ * - When VMO2 ships, Vmo2Provider implements the same method names with
19
+ * args that may differ slightly. Frank's framing: "we adjust function
20
+ * arguments, nothing beyond that — we map X to Y and get the same result."
21
+ *
22
+ * **Trade-off accepted:** the interface is shaped after NET reality, not
23
+ * VMO2. That keeps Phase 0 a strict drop-in replacement. We can standardize
24
+ * args further once VMO2 ships if a tighter cross-provider abstraction
25
+ * proves valuable.
26
+ *
27
+ * **Type pragmatics:** principal/options/return values use `unknown` /
28
+ * `Record<string, unknown>` so the SDK doesn't need to depend on
29
+ * NestJS-decorated entity types. Modules cast at the wiring boundary (where
30
+ * full types are available).
31
+ */
1
32
  import type { VMO2OrderNotification, VMO2SMRNotification } from './types/vmo2';
33
+ import type { FibreCafeServiceAvailability200, FibreCafeAvailableAppointments200, FibreCafeReserveAppointment201, FibreCafeScheduleServiceWorkOrder201, FibreCafeRescheduleServiceWorkOrder200, FibreCafeOrderCreate202, FibreCafeOrderAmendAccept202, FibreCafeKciDto } from './types/fibrecafe';
34
+ import type { OrderRecord } from '../records-v2/OrderRecord';
2
35
  /**
3
36
  * Reasons a cancellation request can be made (NET wire-level enum value
4
37
  * passed through to the FibreCafe gateway).
@@ -53,8 +86,8 @@ export interface AmendAppointmentArgs {
53
86
  yfWorkOrderId: string;
54
87
  supplierAppointmentReference: string;
55
88
  }
56
- export interface SyncWorkOrderToSupplierArgs {
57
- principal: unknown;
89
+ export interface SyncWorkOrderToSupplierArgs<TPrincipal = unknown> {
90
+ principal: TPrincipal;
58
91
  yfOrderId: string;
59
92
  yfWorkOrderId: string;
60
93
  yfChangeReason: {
@@ -86,26 +119,41 @@ export interface UpdateOrderExternalRefArgs {
86
119
  * Returns are typed loosely — modules know the return shape via the dep
87
120
  * function they wire in.
88
121
  */
89
- export interface WholesaleProvider {
90
- searchAddress(uprn: number): Promise<unknown>;
91
- listAppointments(args: ListAppointmentsArgs): Promise<unknown>;
92
- reserveAppointment(args: ReserveAppointmentArgs): Promise<unknown>;
93
- scheduleServiceWorkOrder(args: ScheduleServiceWorkOrderArgs): Promise<unknown>;
94
- rescheduleServiceWorkOrder(args: RescheduleServiceWorkOrderArgs): Promise<unknown>;
95
- amendAppointment(args: AmendAppointmentArgs): Promise<unknown>;
96
- createOrder(principal: unknown, orderId: string, options?: CreateOrderOptions): Promise<unknown>;
97
- /** Tier-change CoS — wraps `createModifyOrderForYfOrder` style args. */
98
- amendOrder(principal: unknown, yfOrderId: string): Promise<unknown>;
122
+ /**
123
+ * Generic in `TPrincipal` so call sites get full typing on the user/org
124
+ * principal that NestJS injects via the `@Principal()` decorator. Modules
125
+ * specialize at the wiring boundary:
126
+ *
127
+ * ```ts
128
+ * class WholesaleProviderFactory {
129
+ * constructor(@Inject(...) private netomnia: WholesaleProvider<OrganizationUserEntity>) {}
130
+ * get(...): WholesaleProvider<OrganizationUserEntity> { ... }
131
+ * }
132
+ * ```
133
+ *
134
+ * Default `TPrincipal = unknown` lets the SDK compile standalone without
135
+ * tenant-specific type imports.
136
+ */
137
+ export interface WholesaleProvider<TPrincipal = unknown> {
138
+ searchAddress(uprn: number): Promise<FibreCafeServiceAvailability200>;
139
+ listAppointments(args: ListAppointmentsArgs): Promise<FibreCafeAvailableAppointments200>;
140
+ reserveAppointment(args: ReserveAppointmentArgs): Promise<FibreCafeReserveAppointment201>;
141
+ scheduleServiceWorkOrder(args: ScheduleServiceWorkOrderArgs): Promise<FibreCafeScheduleServiceWorkOrder201>;
142
+ rescheduleServiceWorkOrder(args: RescheduleServiceWorkOrderArgs): Promise<FibreCafeRescheduleServiceWorkOrder200>;
143
+ amendAppointment(args: AmendAppointmentArgs): Promise<FibreCafeOrderAmendAccept202>;
144
+ createOrder(principal: TPrincipal, orderId: string, options?: CreateOrderOptions): Promise<FibreCafeOrderCreate202>;
145
+ /** Tier-change CoS — wraps `createModifyOrderForYfOrder` style args. Returns null when already at target SKU. */
146
+ amendOrder(principal: TPrincipal, yfOrderId: string): Promise<FibreCafeOrderCreate202 | null>;
99
147
  /** Pre-install cancel of an in-flight provide order. */
100
- cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<unknown>;
148
+ cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<FibreCafeOrderCreate202>;
101
149
  /** Active-customer cease (post-Active churn / OTS-losing). */
102
- ceaseOrder(yfOrderId: string, serviceId: string): Promise<unknown>;
150
+ ceaseOrder(yfOrderId: string, serviceId: string): Promise<FibreCafeOrderCreate202>;
103
151
  /** Inbound supplier KCI / notification handler. */
104
- receiveKci(principal: unknown, dto: unknown): Promise<void>;
152
+ receiveKci(principal: TPrincipal, dto: FibreCafeKciDto): Promise<void>;
105
153
  /** Outbound YF→supplier WO sync (cancel propagation, etc.). */
106
- syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs): Promise<unknown>;
154
+ syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs<TPrincipal>): Promise<void>;
107
155
  /** Inbound supplier→YF WO change application (post-receiveKci). */
108
- syncWorkOrderFromSupplier(order: unknown, dto: unknown): Promise<unknown>;
156
+ syncWorkOrderFromSupplier(order: OrderRecord, dto: FibreCafeKciDto): Promise<void>;
109
157
  retrieveServiceIdByUprn(uprn: number): Promise<string | null>;
110
158
  updateOrderExternalRef(args: UpdateOrderExternalRefArgs): Promise<void>;
111
159
  /**
@@ -29,6 +29,8 @@
29
29
  */
30
30
  import type { WholesaleProvider, CreateOrderOptions, CancellationReasonCode, ReserveAppointmentArgs, ListAppointmentsArgs, ScheduleServiceWorkOrderArgs, RescheduleServiceWorkOrderArgs, AmendAppointmentArgs, SyncWorkOrderToSupplierArgs, UpdateOrderExternalRefArgs } from '../interface';
31
31
  import type { VMO2OrderNotification, VMO2SMRNotification } from '../types/vmo2';
32
+ import type { FibreCafeServiceAvailability200, FibreCafeAvailableAppointments200, FibreCafeReserveAppointment201, FibreCafeScheduleServiceWorkOrder201, FibreCafeRescheduleServiceWorkOrder200, FibreCafeOrderCreate202, FibreCafeOrderAmendAccept202, FibreCafeKciDto } from '../types/fibrecafe';
33
+ import type { OrderRecord } from '../../records-v2/OrderRecord';
32
34
  /**
33
35
  * Function-bag of dependencies the NetomniaProvider delegates to.
34
36
  *
@@ -48,43 +50,43 @@ import type { VMO2OrderNotification, VMO2SMRNotification } from '../types/vmo2';
48
50
  * }
49
51
  * ```
50
52
  */
51
- export interface NetomniaProviderDeps {
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>;
53
+ export interface NetomniaProviderDeps<TPrincipal = unknown> {
54
+ searchAddress: (uprn: number) => Promise<FibreCafeServiceAvailability200>;
55
+ listAppointments: (args: ListAppointmentsArgs) => Promise<FibreCafeAvailableAppointments200>;
56
+ reserveAppointment: (args: ReserveAppointmentArgs) => Promise<FibreCafeReserveAppointment201>;
57
+ scheduleServiceWorkOrder: (args: ScheduleServiceWorkOrderArgs) => Promise<FibreCafeScheduleServiceWorkOrder201>;
58
+ rescheduleServiceWorkOrder: (args: RescheduleServiceWorkOrderArgs) => Promise<FibreCafeRescheduleServiceWorkOrder200>;
59
+ amendAppointment: (args: AmendAppointmentArgs) => Promise<FibreCafeOrderAmendAccept202>;
60
+ createOrder: (principal: TPrincipal, orderId: string, options?: CreateOrderOptions) => Promise<FibreCafeOrderCreate202>;
61
+ amendOrder: (principal: TPrincipal, yfOrderId: string) => Promise<FibreCafeOrderCreate202 | null>;
62
+ cancelOrder: (yfOrderId: string, reasonCode: CancellationReasonCode) => Promise<FibreCafeOrderCreate202>;
63
+ ceaseOrder: (yfOrderId: string, serviceId: string) => Promise<FibreCafeOrderCreate202>;
64
+ receiveKci: (principal: TPrincipal, dto: FibreCafeKciDto) => Promise<void>;
65
+ syncWorkOrderToSupplier: (args: SyncWorkOrderToSupplierArgs<TPrincipal>) => Promise<void>;
66
+ syncWorkOrderFromSupplier: (order: OrderRecord, dto: FibreCafeKciDto) => Promise<void>;
65
67
  retrieveServiceIdByUprn: (uprn: number) => Promise<string | null>;
66
68
  updateOrderExternalRef: (args: UpdateOrderExternalRefArgs) => Promise<void>;
67
69
  }
68
70
  declare type OrderNotificationHandler = (n: VMO2OrderNotification) => Promise<void>;
69
71
  declare type SMRNotificationHandler = (n: VMO2SMRNotification) => Promise<void>;
70
- export declare class NetomniaProvider implements WholesaleProvider {
72
+ export declare class NetomniaProvider<TPrincipal = unknown> implements WholesaleProvider<TPrincipal> {
71
73
  private readonly deps;
72
74
  private readonly orderHandlers;
73
75
  private readonly smrHandlers;
74
- constructor(deps: NetomniaProviderDeps);
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>;
76
+ constructor(deps: NetomniaProviderDeps<TPrincipal>);
77
+ searchAddress(uprn: number): Promise<FibreCafeServiceAvailability200>;
78
+ listAppointments(args: ListAppointmentsArgs): Promise<FibreCafeAvailableAppointments200>;
79
+ reserveAppointment(args: ReserveAppointmentArgs): Promise<FibreCafeReserveAppointment201>;
80
+ scheduleServiceWorkOrder(args: ScheduleServiceWorkOrderArgs): Promise<FibreCafeScheduleServiceWorkOrder201>;
81
+ rescheduleServiceWorkOrder(args: RescheduleServiceWorkOrderArgs): Promise<FibreCafeRescheduleServiceWorkOrder200>;
82
+ amendAppointment(args: AmendAppointmentArgs): Promise<FibreCafeOrderAmendAccept202>;
83
+ createOrder(principal: TPrincipal, orderId: string, options?: CreateOrderOptions): Promise<FibreCafeOrderCreate202>;
84
+ amendOrder(principal: TPrincipal, yfOrderId: string): Promise<FibreCafeOrderCreate202>;
85
+ cancelOrder(yfOrderId: string, reasonCode: CancellationReasonCode): Promise<FibreCafeOrderCreate202>;
86
+ ceaseOrder(yfOrderId: string, serviceId: string): Promise<FibreCafeOrderCreate202>;
87
+ receiveKci(principal: TPrincipal, dto: FibreCafeKciDto): Promise<void>;
88
+ syncWorkOrderToSupplier(args: SyncWorkOrderToSupplierArgs<TPrincipal>): Promise<void>;
89
+ syncWorkOrderFromSupplier(order: OrderRecord, dto: FibreCafeKciDto): Promise<void>;
88
90
  retrieveServiceIdByUprn(uprn: number): Promise<string>;
89
91
  updateOrderExternalRef(args: UpdateOrderExternalRefArgs): Promise<void>;
90
92
  onOrderNotification(handler: OrderNotificationHandler): void;
@@ -43,69 +43,127 @@ function brand(s) {
43
43
  return s;
44
44
  }
45
45
  /**
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.
46
+ * Stubs each dep with a typed canned return so the pass-through pattern
47
+ * can be tested via identity equality (`result === stubs.searchAddress`)
48
+ * without resorting to `as unknown` casts.
49
49
  */
50
50
  function buildDeps() {
51
51
  const log = [];
52
- const sentinel = Symbol('NET-RETURN');
53
- const record = (method) => (...args) => {
54
- log.push({ method, args });
55
- return Promise.resolve(sentinel);
56
- };
57
- const recordVoid = (method) => (...args) => {
58
- log.push({ method, args });
59
- return Promise.resolve(undefined);
52
+ const stubs = {
53
+ searchAddress: {
54
+ address: { identifier: { id: 'UPRN-1', type: 'UPRN' } },
55
+ serviceSpecifications: [],
56
+ },
57
+ listAppointments: { availableTimeslots: [] },
58
+ reserveAppointment: {
59
+ id: 'A1',
60
+ supplierReference: 'NET-A1',
61
+ address: { id: 'X', type: 'UPRN' },
62
+ serviceSpecification: { id: 'fttp' },
63
+ timeslot: {
64
+ timeslotStartDateTime: '2026-06-01T08:00:00Z',
65
+ timeslotEndDateTime: '2026-06-01T13:00:00Z',
66
+ },
67
+ },
68
+ scheduleServiceWorkOrder: { workOrderId: 'WO-1', appointmentId: 'APT-1' },
69
+ rescheduleServiceWorkOrder: { workOrderId: 'WO-1', appointmentId: 'APT-2' },
70
+ amendAppointment: {},
71
+ createOrder: {},
72
+ amendOrder: {},
73
+ cancelOrder: {},
74
+ ceaseOrder: {},
60
75
  };
61
76
  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'),
77
+ searchAddress: (uprn) => {
78
+ log.push({ method: 'searchAddress', args: [uprn] });
79
+ return Promise.resolve(stubs.searchAddress);
80
+ },
81
+ listAppointments: (args) => {
82
+ log.push({ method: 'listAppointments', args: [args] });
83
+ return Promise.resolve(stubs.listAppointments);
84
+ },
85
+ reserveAppointment: (args) => {
86
+ log.push({ method: 'reserveAppointment', args: [args] });
87
+ return Promise.resolve(stubs.reserveAppointment);
88
+ },
89
+ scheduleServiceWorkOrder: (args) => {
90
+ log.push({ method: 'scheduleServiceWorkOrder', args: [args] });
91
+ return Promise.resolve(stubs.scheduleServiceWorkOrder);
92
+ },
93
+ rescheduleServiceWorkOrder: (args) => {
94
+ log.push({ method: 'rescheduleServiceWorkOrder', args: [args] });
95
+ return Promise.resolve(stubs.rescheduleServiceWorkOrder);
96
+ },
97
+ amendAppointment: (args) => {
98
+ log.push({ method: 'amendAppointment', args: [args] });
99
+ return Promise.resolve(stubs.amendAppointment);
100
+ },
101
+ createOrder: (principal, orderId, options) => {
102
+ log.push({ method: 'createOrder', args: [principal, orderId, options] });
103
+ return Promise.resolve(stubs.createOrder);
104
+ },
105
+ amendOrder: (principal, yfOrderId) => {
106
+ log.push({ method: 'amendOrder', args: [principal, yfOrderId] });
107
+ return Promise.resolve(stubs.amendOrder);
108
+ },
109
+ cancelOrder: (yfOrderId, reasonCode) => {
110
+ log.push({ method: 'cancelOrder', args: [yfOrderId, reasonCode] });
111
+ return Promise.resolve(stubs.cancelOrder);
112
+ },
113
+ ceaseOrder: (yfOrderId, serviceId) => {
114
+ log.push({ method: 'ceaseOrder', args: [yfOrderId, serviceId] });
115
+ return Promise.resolve(stubs.ceaseOrder);
116
+ },
117
+ receiveKci: (principal, dto) => {
118
+ log.push({ method: 'receiveKci', args: [principal, dto] });
119
+ return Promise.resolve();
120
+ },
121
+ syncWorkOrderToSupplier: (args) => {
122
+ log.push({ method: 'syncWorkOrderToSupplier', args: [args] });
123
+ return Promise.resolve();
124
+ },
125
+ syncWorkOrderFromSupplier: (order, dto) => {
126
+ log.push({ method: 'syncWorkOrderFromSupplier', args: [order, dto] });
127
+ return Promise.resolve();
128
+ },
75
129
  retrieveServiceIdByUprn: (uprn) => {
76
130
  log.push({ method: 'retrieveServiceIdByUprn', args: [uprn] });
77
131
  return Promise.resolve('NET-SVC-1');
78
132
  },
79
- updateOrderExternalRef: recordVoid('updateOrderExternalRef'),
133
+ updateOrderExternalRef: (args) => {
134
+ log.push({ method: 'updateOrderExternalRef', args: [args] });
135
+ return Promise.resolve();
136
+ },
80
137
  };
81
- return { deps, log, sentinel };
138
+ return { deps, log, stubs };
82
139
  }
83
140
  function run() {
84
141
  return __awaiter(this, void 0, void 0, function* () {
85
142
  console.log('NetomniaProvider — pass-through delegation tests\n');
86
143
  console.log('Test: searchAddress delegates to deps');
87
144
  {
88
- const { deps, log, sentinel } = buildDeps();
145
+ const { deps, log, stubs } = buildDeps();
89
146
  const p = new netomnia_provider_1.NetomniaProvider(deps);
90
147
  const result = yield p.searchAddress(123);
91
148
  assert('called once', log.length === 1);
92
149
  assert('correct method', log[0].method === 'searchAddress');
93
150
  assert('args forwarded', JSON.stringify(log[0].args) === JSON.stringify([123]));
94
- assert('return forwarded', result === sentinel);
151
+ assert('return forwarded', result === stubs.searchAddress);
95
152
  }
96
153
  console.log('Test: listAppointments delegates to deps');
97
154
  {
98
- const { deps, log } = buildDeps();
155
+ const { deps, log, stubs } = buildDeps();
99
156
  const p = new netomnia_provider_1.NetomniaProvider(deps);
100
157
  const args = { uprn: 123, start: '2026-06-01', appointmentPurpose: 'PROVIDE' };
101
- yield p.listAppointments(args);
158
+ const result = yield p.listAppointments(args);
102
159
  assert('called once', log.length === 1);
103
160
  assert('correct method', log[0].method === 'listAppointments');
104
161
  assert('args forwarded', log[0].args[0] === args);
162
+ assert('return forwarded', result === stubs.listAppointments);
105
163
  }
106
164
  console.log('Test: reserveAppointment delegates to deps');
107
165
  {
108
- const { deps, log } = buildDeps();
166
+ const { deps, log, stubs } = buildDeps();
109
167
  const p = new netomnia_provider_1.NetomniaProvider(deps);
110
168
  const args = {
111
169
  yfAppointmentId: 'APT-1',
@@ -117,10 +175,11 @@ function run() {
117
175
  woProperties: {},
118
176
  flow: 'FIRST_TIME_BOOKING',
119
177
  };
120
- yield p.reserveAppointment(args);
178
+ const result = yield p.reserveAppointment(args);
121
179
  assert('called once', log.length === 1);
122
180
  assert('correct method', log[0].method === 'reserveAppointment');
123
181
  assert('args forwarded', log[0].args[0] === args);
182
+ assert('return forwarded', result === stubs.reserveAppointment);
124
183
  }
125
184
  console.log('Test: scheduleServiceWorkOrder delegates to deps');
126
185
  {
@@ -164,7 +223,7 @@ function run() {
164
223
  }
165
224
  console.log('Test: createOrder delegates to deps with all args (principal, orderId, options)');
166
225
  {
167
- const { deps, log, sentinel } = buildDeps();
226
+ const { deps, log, stubs } = buildDeps();
168
227
  const p = new netomnia_provider_1.NetomniaProvider(deps);
169
228
  const principal = { id: 'user-1' };
170
229
  const opts = { doNotCreateServiceAppointment: true };
@@ -174,7 +233,7 @@ function run() {
174
233
  assert('principal forwarded', log[0].args[0] === principal);
175
234
  assert('orderId forwarded', log[0].args[1] === 'ORD-1');
176
235
  assert('options forwarded', log[0].args[2] === opts);
177
- assert('return forwarded', result === sentinel);
236
+ assert('return forwarded', result === stubs.createOrder);
178
237
  }
179
238
  console.log('Test: createOrder forwards undefined options correctly');
180
239
  {
@@ -218,7 +277,13 @@ function run() {
218
277
  const { deps, log } = buildDeps();
219
278
  const p = new netomnia_provider_1.NetomniaProvider(deps);
220
279
  const principal = { id: 'user-1' };
221
- const dto = { id: 'kci-1', sequenceNumber: 1 };
280
+ // Minimal valid FibreCafeKciDto fixture.
281
+ const dto = {
282
+ id: 'kci-1',
283
+ sequenceNumber: 1,
284
+ issuedOn: '2026-05-09T00:00:00Z',
285
+ reasonCode: 'ACKNOWLEDGED',
286
+ };
222
287
  yield p.receiveKci(principal, dto);
223
288
  assert('called once', log.length === 1);
224
289
  assert('correct method', log[0].method === 'receiveKci');
@@ -243,11 +308,22 @@ function run() {
243
308
  {
244
309
  const { deps, log } = buildDeps();
245
310
  const p = new netomnia_provider_1.NetomniaProvider(deps);
311
+ // Test fixtures: pass-through tests treat these values as opaque; we
312
+ // assert reference equality, not structural equality. Casting a minimal
313
+ // shape to the expected types is acceptable here — the alternative is
314
+ // constructing 80+ OrderRecord properties for no semantic value.
246
315
  const order = { id: 'ORD-1' };
247
- const dto = { id: 'kci-1' };
316
+ const dto = {
317
+ id: 'kci-1',
318
+ sequenceNumber: 1,
319
+ issuedOn: '2026-05-09T00:00:00Z',
320
+ reasonCode: 'UPDATE',
321
+ };
248
322
  yield p.syncWorkOrderFromSupplier(order, dto);
249
323
  assert('called once', log.length === 1);
250
324
  assert('correct method', log[0].method === 'syncWorkOrderFromSupplier');
325
+ assert('order reference forwarded', log[0].args[0] === order);
326
+ assert('dto reference forwarded', log[0].args[1] === dto);
251
327
  }
252
328
  console.log('Test: retrieveServiceIdByUprn delegates to deps');
253
329
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d19n/youfibre-odin-sdk",
3
- "version": "2.0.103-beta.8",
3
+ "version": "2.0.103-beta.9",
4
4
  "description": "",
5
5
  "author": "@d19n",
6
6
  "license": "UNLICENSED",