kaafil-react-uikit 0.1.0-beta.1
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.
- package/CHANGELOG.md +70 -0
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/OfflineFallbackScreen-B5dfaJBN.d.ts +37 -0
- package/dist/OfflineFallbackScreen-Ctm7nBa-.d.cts +37 -0
- package/dist/SessionExpiredScreen-CytQNuak.d.cts +50 -0
- package/dist/SessionExpiredScreen-D8GHyZc3.d.ts +50 -0
- package/dist/admin/index.cjs +23779 -0
- package/dist/admin/index.cjs.map +1 -0
- package/dist/admin/index.d.cts +1157 -0
- package/dist/admin/index.d.ts +1157 -0
- package/dist/admin/index.js +23724 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/chunk-6FPAGGFL.js +6028 -0
- package/dist/chunk-6FPAGGFL.js.map +1 -0
- package/dist/chunk-ACVU55PZ.cjs +294 -0
- package/dist/chunk-ACVU55PZ.cjs.map +1 -0
- package/dist/chunk-EM7JV3D7.cjs +6051 -0
- package/dist/chunk-EM7JV3D7.cjs.map +1 -0
- package/dist/chunk-IIPQ4JNC.js +287 -0
- package/dist/chunk-IIPQ4JNC.js.map +1 -0
- package/dist/chunk-LL243STJ.cjs +2195 -0
- package/dist/chunk-LL243STJ.cjs.map +1 -0
- package/dist/chunk-PJRZCOFF.cjs +958 -0
- package/dist/chunk-PJRZCOFF.cjs.map +1 -0
- package/dist/chunk-PTQ2CJKE.cjs +40 -0
- package/dist/chunk-PTQ2CJKE.cjs.map +1 -0
- package/dist/chunk-PZKXLQSU.js +10 -0
- package/dist/chunk-PZKXLQSU.js.map +1 -0
- package/dist/chunk-QHUDOVKW.cjs +111 -0
- package/dist/chunk-QHUDOVKW.cjs.map +1 -0
- package/dist/chunk-TOBZFADB.cjs +12 -0
- package/dist/chunk-TOBZFADB.cjs.map +1 -0
- package/dist/chunk-UUGMNP27.js +936 -0
- package/dist/chunk-UUGMNP27.js.map +1 -0
- package/dist/chunk-UYJ6LIWP.js +2154 -0
- package/dist/chunk-UYJ6LIWP.js.map +1 -0
- package/dist/chunk-VHTSTUCJ.cjs +346 -0
- package/dist/chunk-VHTSTUCJ.cjs.map +1 -0
- package/dist/chunk-WUAFL77X.js +340 -0
- package/dist/chunk-WUAFL77X.js.map +1 -0
- package/dist/chunk-XI7MK4Y7.js +38 -0
- package/dist/chunk-XI7MK4Y7.js.map +1 -0
- package/dist/chunk-YDGR7JSY.js +107 -0
- package/dist/chunk-YDGR7JSY.js.map +1 -0
- package/dist/core/index.cjs +1254 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +1267 -0
- package/dist/core/index.d.ts +1267 -0
- package/dist/core/index.js +995 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.css +1 -0
- package/dist/manager/index.cjs +13562 -0
- package/dist/manager/index.cjs.map +1 -0
- package/dist/manager/index.d.cts +1054 -0
- package/dist/manager/index.d.ts +1054 -0
- package/dist/manager/index.js +13518 -0
- package/dist/manager/index.js.map +1 -0
- package/dist/testing/index.cjs +221 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.cts +269 -0
- package/dist/testing/index.d.ts +269 -0
- package/dist/testing/index.js +208 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tokenStyle-CToG5oOS.d.cts +3 -0
- package/dist/tokenStyle-CToG5oOS.d.ts +3 -0
- package/dist/traveller/index.cjs +1799 -0
- package/dist/traveller/index.cjs.map +1 -0
- package/dist/traveller/index.d.cts +58 -0
- package/dist/traveller/index.d.ts +58 -0
- package/dist/traveller/index.js +1789 -0
- package/dist/traveller/index.js.map +1 -0
- package/dist/useAgencyTrip-BUYSIaC2.d.cts +454 -0
- package/dist/useAgencyTrip-BzyJB1D9.d.ts +454 -0
- package/dist/useNotifications-BZW1Z_ur.d.cts +356 -0
- package/dist/useNotifications-D262i5BQ.d.ts +356 -0
- package/dist/useVendors-DXOuZWhZ.d.cts +784 -0
- package/dist/useVendors-DXOuZWhZ.d.ts +784 -0
- package/package.json +95 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/** `11-engine-requirements.md`'s corrected `ActorKind` — four values, not two.
|
|
2
|
+
* An API-key write is the CRM acting; collapsing it into the admin case would
|
|
3
|
+
* name a person who did not act, which `ActorAttributionBadge` must never do. */
|
|
4
|
+
type MockActorKind = 'MANAGER' | 'AGENCY_ADMIN' | 'API_KEY' | 'SYSTEM';
|
|
5
|
+
interface MockActorAttributedRow {
|
|
6
|
+
readonly performedByKind: MockActorKind;
|
|
7
|
+
/** `null` for `SYSTEM`/`API_KEY` rows with no human name to show — the
|
|
8
|
+
* badge's `kind`-only state (`atomic/04-identity.md`'s `ActorBadge`
|
|
9
|
+
* States: "with `name`" · "`kind`-only"). */
|
|
10
|
+
readonly performedByName: string | null;
|
|
11
|
+
/** `meta.serverTime`-derived — never a device clock
|
|
12
|
+
* (`13-i18n-and-formatting.md`'s server-time rule, inherited here). */
|
|
13
|
+
readonly performedAt: string;
|
|
14
|
+
}
|
|
15
|
+
/** Produces the shape `ActorAttributionBadge` expects. Defaults to a named
|
|
16
|
+
* manager actor; override `performedByKind`/`performedByName` to exercise the
|
|
17
|
+
* other three kinds, including the `kind`-only (`performedByName: null`)
|
|
18
|
+
* state. */
|
|
19
|
+
declare function mockActorAttributedRow(overrides?: Partial<MockActorAttributedRow>): MockActorAttributedRow;
|
|
20
|
+
|
|
21
|
+
/** The exact row shape `journey.capabilities()` returns — a flat array, never
|
|
22
|
+
* a keyed map (`06-capability-and-personas.md §6` point 1). `capability` is an
|
|
23
|
+
* open string set (§6 point 5); this file never enumerates it exhaustively. */
|
|
24
|
+
interface MockCapabilityRow {
|
|
25
|
+
readonly capability: string;
|
|
26
|
+
readonly modeOk: boolean;
|
|
27
|
+
readonly dataOk: boolean;
|
|
28
|
+
readonly flagOk: boolean;
|
|
29
|
+
readonly enabled: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** All three gates true → lit (`06 §1`). */
|
|
32
|
+
declare function litCapability(key: string): MockCapabilityRow;
|
|
33
|
+
/** `modeOk: false`, others true, `enabled: false` — the "render nothing, no
|
|
34
|
+
* CTA" treatment (`06 §2`): this capability can never become available for
|
|
35
|
+
* this trip's mode. */
|
|
36
|
+
declare function modeDarkCapability(key: string): MockCapabilityRow;
|
|
37
|
+
/** `dataOk: false`, others true, `enabled: false` — the "connect this" empty
|
|
38
|
+
* state (`06 §2`): the module is entitled but the CRM hasn't pushed the rows
|
|
39
|
+
* it needs. */
|
|
40
|
+
declare function dataDarkCapability(key: string): MockCapabilityRow;
|
|
41
|
+
/** `flagOk: false`, others true, `enabled: false` — the upsell/locked
|
|
42
|
+
* treatment (`06 §2`), split further by `details.reason` (`06 §2.1`) at the
|
|
43
|
+
* component layer; this builder only produces the boolean triple. */
|
|
44
|
+
declare function flagDarkCapability(key: string): MockCapabilityRow;
|
|
45
|
+
/**
|
|
46
|
+
* Composes zero or more capability rows into the flat array shape the wire
|
|
47
|
+
* returns. Pass an array built from `litCapability`/`modeDarkCapability`/
|
|
48
|
+
* `dataDarkCapability`/`flagDarkCapability` — this is how a fixture reproduces,
|
|
49
|
+
* e.g., the frozen PERSONALIZED mode-dark set (`06 §3`, cited not restated
|
|
50
|
+
* here — enumerating those eleven module keys is that file's job, not this
|
|
51
|
+
* one's, to avoid a second, driftable copy of a frozen list) as an array of
|
|
52
|
+
* `modeDarkCapability(...)` calls.
|
|
53
|
+
*/
|
|
54
|
+
declare function mockCapabilities(overrides?: readonly MockCapabilityRow[]): MockCapabilityRow[];
|
|
55
|
+
|
|
56
|
+
/** The jittered, capped backoff ladder (`07-offline-and-sync.md §3`) — seven
|
|
57
|
+
* rungs, the last one held until connectivity or app resume. `0` means "not
|
|
58
|
+
* yet retried"; `6` means "held at 1h". */
|
|
59
|
+
type BackoffRung = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
60
|
+
declare const BACKOFF_LADDER_MS: readonly [number, number, number, number, number, number, number];
|
|
61
|
+
type MockOutboxEntryStatus = 'pending' | 'inflight' | 'parked' | 'done';
|
|
62
|
+
interface MockOutboxEntry {
|
|
63
|
+
readonly opId: string;
|
|
64
|
+
readonly tripRef: string;
|
|
65
|
+
readonly entity: {
|
|
66
|
+
readonly listName: string;
|
|
67
|
+
readonly id: string;
|
|
68
|
+
};
|
|
69
|
+
readonly status: MockOutboxEntryStatus;
|
|
70
|
+
readonly backoffRung: BackoffRung;
|
|
71
|
+
}
|
|
72
|
+
interface MockConflictEntry {
|
|
73
|
+
readonly opId: string;
|
|
74
|
+
readonly entity: {
|
|
75
|
+
readonly listName: string;
|
|
76
|
+
readonly id: string;
|
|
77
|
+
};
|
|
78
|
+
readonly queued: Record<string, unknown>;
|
|
79
|
+
readonly current: Record<string, unknown>;
|
|
80
|
+
}
|
|
81
|
+
interface MockOfflineEngineState {
|
|
82
|
+
readonly online: boolean;
|
|
83
|
+
readonly outbox: readonly MockOutboxEntry[];
|
|
84
|
+
readonly conflicts: readonly MockConflictEntry[];
|
|
85
|
+
}
|
|
86
|
+
interface MockOfflineEngine {
|
|
87
|
+
/** A snapshot of current state. Never mutated by the caller — mutate only
|
|
88
|
+
* through the methods below, mirroring the real engine's own
|
|
89
|
+
* read-through-methods-only shape. */
|
|
90
|
+
getState(): MockOfflineEngineState;
|
|
91
|
+
goOffline(): void;
|
|
92
|
+
goOnline(): void;
|
|
93
|
+
/** Adds a `pending` outbox entry at backoff rung 0. Returns the entry so a
|
|
94
|
+
* test can reference its `opId` in a later call. */
|
|
95
|
+
enqueuePendingEntry(entry: Partial<Omit<MockOutboxEntry, 'status' | 'backoffRung'>>): MockOutboxEntry;
|
|
96
|
+
/** Moves one rung up the ladder (`07 §3`) — capped at rung 6, never past
|
|
97
|
+
* it, mirroring the real ladder holding at 1h. Throws a named error if
|
|
98
|
+
* `opId` is not in the outbox, so a test typo fails loudly rather than
|
|
99
|
+
* silently no-op-ing. */
|
|
100
|
+
advanceBackoff(opId: string): BackoffRung;
|
|
101
|
+
/** Marks an entry `parked` — the terminal, non-retried state
|
|
102
|
+
* (`07 §10`). */
|
|
103
|
+
parkEntry(opId: string): void;
|
|
104
|
+
/** Marks an entry `done` and removes it from the outbox — the terminal
|
|
105
|
+
* success state, pruned on the next persist (mirrors `kaafil-js`'s own
|
|
106
|
+
* `OutboxStatus.Done`). */
|
|
107
|
+
completeEntry(opId: string): void;
|
|
108
|
+
/** Injects a conflict on a specific entity — the `409` reconciliation path
|
|
109
|
+
* (`07 §6`). */
|
|
110
|
+
forceConflict(entry: MockConflictEntry): void;
|
|
111
|
+
/** Removes a conflict once a test has driven its resolution. */
|
|
112
|
+
resolveConflict(opId: string): void;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Builds a controllable, deterministic offline-engine fixture. `initialState`
|
|
116
|
+
* seeds the outbox/conflicts/online flag; every mutator below is synchronous.
|
|
117
|
+
*/
|
|
118
|
+
declare function mockOfflineEngine(initialState?: Partial<MockOfflineEngineState>): MockOfflineEngine;
|
|
119
|
+
|
|
120
|
+
/** `04-customization-ladder.md §6` — the exact, build-time-fixed density scale. */
|
|
121
|
+
type DensityToken = 'compact' | 'default' | 'comfortable';
|
|
122
|
+
/** `03 §7` / `06-capability-and-personas.md §7` — `COORDINATOR` is read-only,
|
|
123
|
+
* `MANAGER` is full access. No third value; no relaxed/elevated variant. */
|
|
124
|
+
type ManagerRole = 'MANAGER' | 'COORDINATOR';
|
|
125
|
+
interface MockManagerSession {
|
|
126
|
+
readonly kind: 'managerSession';
|
|
127
|
+
readonly accessToken: string;
|
|
128
|
+
readonly refreshToken: string;
|
|
129
|
+
readonly expiresAt: string;
|
|
130
|
+
/** The mint response's internal `agencyId`, threaded through as `agencyRef`
|
|
131
|
+
* — see `kaafil-js`'s `OpenManagerSessionOptions.agencyRef` doc comment for
|
|
132
|
+
* why this is the internal id, not the external `AGENCY-###` ref. */
|
|
133
|
+
readonly agencyRef: string;
|
|
134
|
+
readonly role: ManagerRole;
|
|
135
|
+
/** Convenience only — see this file's header comment. Omitted overrides
|
|
136
|
+
* fall back to the family's own shell default, exactly as production does. */
|
|
137
|
+
readonly density?: DensityToken;
|
|
138
|
+
}
|
|
139
|
+
interface MockAgencyAdminSession {
|
|
140
|
+
readonly kind: 'agencyAdmin';
|
|
141
|
+
readonly accessToken: string;
|
|
142
|
+
readonly refreshToken: string;
|
|
143
|
+
readonly expiresAt: string;
|
|
144
|
+
/** Agency-scoped, one role, no read-only variant — `D-096`, `03 §7`. There is
|
|
145
|
+
* no `role` field: agencyAdmin has exactly one. */
|
|
146
|
+
readonly agencyRef: string;
|
|
147
|
+
readonly density?: DensityToken;
|
|
148
|
+
}
|
|
149
|
+
interface MockShareSession {
|
|
150
|
+
readonly kind: 'shareToken';
|
|
151
|
+
readonly token: string;
|
|
152
|
+
readonly tripRef: string;
|
|
153
|
+
/** `null` produces a family-token session (`briefing §26`) — no single
|
|
154
|
+
* traveller subject bound, the token covers the whole family/party. A
|
|
155
|
+
* non-null value is the default: a subject-bound share session. */
|
|
156
|
+
readonly travellerId: string | null;
|
|
157
|
+
readonly expiresAt: string;
|
|
158
|
+
readonly density?: DensityToken;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* A fake OPEN manager session satisfying the manager auth adapter
|
|
162
|
+
* (`06-capability-and-personas.md §7`, `U-003`). `role` defaults to `MANAGER`;
|
|
163
|
+
* pass `{ role: 'COORDINATOR' }` for the read-only variant.
|
|
164
|
+
*/
|
|
165
|
+
declare function mockManagerSession(overrides?: Partial<Omit<MockManagerSession, 'kind'>>): MockManagerSession;
|
|
166
|
+
/** A fake OPEN agencyAdmin session, agency-scoped. */
|
|
167
|
+
declare function mockAgencyAdminSession(overrides?: Partial<Omit<MockAgencyAdminSession, 'kind'>>): MockAgencyAdminSession;
|
|
168
|
+
/**
|
|
169
|
+
* A fake OPEN share-token session. `{ travellerId: null }` produces a
|
|
170
|
+
* family-token session; the default has a subject bound.
|
|
171
|
+
*/
|
|
172
|
+
declare function mockShareSession(overrides?: Partial<Omit<MockShareSession, 'kind'>>): MockShareSession;
|
|
173
|
+
|
|
174
|
+
/** The six kinds `14-testing-and-fixtures.md §1.3` names — no seventh, no
|
|
175
|
+
* collapsing two into one. */
|
|
176
|
+
type MockSyncEventKind = 'enqueued' | 'flushed' | 'conflict' | 'parked' | 'reconnected' | 'delta-applied';
|
|
177
|
+
/** Write-ahead, before any network call (`07 §2`). */
|
|
178
|
+
interface MockSyncEnqueuedEvent {
|
|
179
|
+
readonly kind: 'enqueued';
|
|
180
|
+
readonly opId: string;
|
|
181
|
+
readonly tripRef: string;
|
|
182
|
+
readonly entity: {
|
|
183
|
+
readonly listName: string;
|
|
184
|
+
readonly id: string;
|
|
185
|
+
};
|
|
186
|
+
readonly enqueuedAt: string;
|
|
187
|
+
/** The row painted immediately, ahead of any server confirmation. */
|
|
188
|
+
readonly optimistic: Record<string, unknown>;
|
|
189
|
+
}
|
|
190
|
+
/** An op reached a 2xx and drained (`07 §3`). */
|
|
191
|
+
interface MockSyncFlushedEvent {
|
|
192
|
+
readonly kind: 'flushed';
|
|
193
|
+
readonly opId: string;
|
|
194
|
+
readonly tripRef: string;
|
|
195
|
+
readonly entity: {
|
|
196
|
+
readonly listName: string;
|
|
197
|
+
readonly id: string;
|
|
198
|
+
};
|
|
199
|
+
/** The rebased canonical row, carrying the server `version`. */
|
|
200
|
+
readonly applied: Record<string, unknown>;
|
|
201
|
+
readonly appliedAt: string;
|
|
202
|
+
}
|
|
203
|
+
/** `07 §6` — two conflict shapes normalise into one component. Every versioned
|
|
204
|
+
* entity except the checklist-item toggle carries `currentVersion`; the
|
|
205
|
+
* toggle's optimistic concurrency uses `expectedStatus`/`currentStatus`
|
|
206
|
+
* instead (`07 §6`, `modules/checklists/FRD.md §4.3`). */
|
|
207
|
+
type MockConflictResolutionShape = {
|
|
208
|
+
readonly shape: 'version';
|
|
209
|
+
readonly currentVersion: number;
|
|
210
|
+
} | {
|
|
211
|
+
readonly shape: 'status';
|
|
212
|
+
readonly currentStatus: string;
|
|
213
|
+
};
|
|
214
|
+
/** Carries BOTH the queued and current values the shared resolver UI needs
|
|
215
|
+
* (`07 §6` steps 1–3) — never only one side. */
|
|
216
|
+
interface MockSyncConflictEvent {
|
|
217
|
+
readonly kind: 'conflict';
|
|
218
|
+
readonly opId: string;
|
|
219
|
+
readonly tripRef: string;
|
|
220
|
+
readonly entity: {
|
|
221
|
+
readonly listName: string;
|
|
222
|
+
readonly id: string;
|
|
223
|
+
};
|
|
224
|
+
/** The locally intended change, as originally enqueued. */
|
|
225
|
+
readonly queued: Record<string, unknown>;
|
|
226
|
+
/** The fresh server row, re-read at its current version (`07 §6`'s "why
|
|
227
|
+
* the entity is re-pulled first"). */
|
|
228
|
+
readonly current: Record<string, unknown>;
|
|
229
|
+
readonly resolution: MockConflictResolutionShape;
|
|
230
|
+
}
|
|
231
|
+
/** A permanently non-retryable op — `423`/`410`/`402`/`UnsatisfiableScheme`
|
|
232
|
+
* (`07 §10`). Never silently dropped; surfaced and left visibly stalled. */
|
|
233
|
+
interface MockSyncParkedEvent {
|
|
234
|
+
readonly kind: 'parked';
|
|
235
|
+
readonly opId: string;
|
|
236
|
+
readonly tripRef: string;
|
|
237
|
+
readonly status: number;
|
|
238
|
+
/** A message fit to show a manager (mirrors `SyncErrorEvent.userMessage`
|
|
239
|
+
* in `kaafil-js`'s own event map). */
|
|
240
|
+
readonly userMessage: string;
|
|
241
|
+
}
|
|
242
|
+
/** Connectivity restored — the drainer fires opportunistically (`07 §3`). */
|
|
243
|
+
interface MockSyncReconnectedEvent {
|
|
244
|
+
readonly kind: 'reconnected';
|
|
245
|
+
readonly at: string;
|
|
246
|
+
}
|
|
247
|
+
/** At-least-once redelivery (`07 §4`) — includes a row identical to one
|
|
248
|
+
* already applied, so a component's idempotence against duplicate delivery is
|
|
249
|
+
* directly testable rather than assumed. */
|
|
250
|
+
interface MockSyncDeltaAppliedEvent {
|
|
251
|
+
readonly kind: 'delta-applied';
|
|
252
|
+
readonly tripRef: string;
|
|
253
|
+
readonly list: string;
|
|
254
|
+
readonly rows: readonly Record<string, unknown>[];
|
|
255
|
+
/** True on the row(s) that are a byte-for-byte redelivery of a row already
|
|
256
|
+
* merged on a prior pull — never a change, by construction of this fixture. */
|
|
257
|
+
readonly redeliveredIds: readonly string[];
|
|
258
|
+
/** The new cursor, from `meta.serverTime` (`07 §4`). */
|
|
259
|
+
readonly serverTime: string;
|
|
260
|
+
}
|
|
261
|
+
type MockSyncEvent = MockSyncEnqueuedEvent | MockSyncFlushedEvent | MockSyncConflictEvent | MockSyncParkedEvent | MockSyncReconnectedEvent | MockSyncDeltaAppliedEvent;
|
|
262
|
+
declare function mockSyncEvent(kind: 'enqueued', overrides?: Partial<Omit<MockSyncEnqueuedEvent, 'kind'>>): MockSyncEnqueuedEvent;
|
|
263
|
+
declare function mockSyncEvent(kind: 'flushed', overrides?: Partial<Omit<MockSyncFlushedEvent, 'kind'>>): MockSyncFlushedEvent;
|
|
264
|
+
declare function mockSyncEvent(kind: 'conflict', overrides?: Partial<Omit<MockSyncConflictEvent, 'kind'>>): MockSyncConflictEvent;
|
|
265
|
+
declare function mockSyncEvent(kind: 'parked', overrides?: Partial<Omit<MockSyncParkedEvent, 'kind'>>): MockSyncParkedEvent;
|
|
266
|
+
declare function mockSyncEvent(kind: 'reconnected', overrides?: Partial<Omit<MockSyncReconnectedEvent, 'kind'>>): MockSyncReconnectedEvent;
|
|
267
|
+
declare function mockSyncEvent(kind: 'delta-applied', overrides?: Partial<Omit<MockSyncDeltaAppliedEvent, 'kind'>>): MockSyncDeltaAppliedEvent;
|
|
268
|
+
|
|
269
|
+
export { BACKOFF_LADDER_MS, type BackoffRung, type DensityToken, type ManagerRole, type MockActorAttributedRow, type MockActorKind, type MockAgencyAdminSession, type MockCapabilityRow, type MockConflictEntry, type MockConflictResolutionShape, type MockManagerSession, type MockOfflineEngine, type MockOfflineEngineState, type MockOutboxEntry, type MockOutboxEntryStatus, type MockShareSession, type MockSyncConflictEvent, type MockSyncDeltaAppliedEvent, type MockSyncEnqueuedEvent, type MockSyncEvent, type MockSyncEventKind, type MockSyncFlushedEvent, type MockSyncParkedEvent, type MockSyncReconnectedEvent, dataDarkCapability, flagDarkCapability, litCapability, mockActorAttributedRow, mockAgencyAdminSession, mockCapabilities, mockManagerSession, mockOfflineEngine, mockShareSession, mockSyncEvent, modeDarkCapability };
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// src/testing/actor-rows.ts
|
|
2
|
+
var DEFAULT_PERFORMED_AT = "2026-08-21T18:00:00.000Z";
|
|
3
|
+
function mockActorAttributedRow(overrides = {}) {
|
|
4
|
+
return {
|
|
5
|
+
performedByKind: overrides.performedByKind ?? "MANAGER",
|
|
6
|
+
performedByName: overrides.performedByName !== void 0 ? overrides.performedByName : "Asha Rao",
|
|
7
|
+
performedAt: overrides.performedAt ?? DEFAULT_PERFORMED_AT
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/testing/capabilities.ts
|
|
12
|
+
function litCapability(key) {
|
|
13
|
+
return { capability: key, modeOk: true, dataOk: true, flagOk: true, enabled: true };
|
|
14
|
+
}
|
|
15
|
+
function modeDarkCapability(key) {
|
|
16
|
+
return { capability: key, modeOk: false, dataOk: true, flagOk: true, enabled: false };
|
|
17
|
+
}
|
|
18
|
+
function dataDarkCapability(key) {
|
|
19
|
+
return { capability: key, modeOk: true, dataOk: false, flagOk: true, enabled: false };
|
|
20
|
+
}
|
|
21
|
+
function flagDarkCapability(key) {
|
|
22
|
+
return { capability: key, modeOk: true, dataOk: true, flagOk: false, enabled: false };
|
|
23
|
+
}
|
|
24
|
+
function mockCapabilities(overrides = []) {
|
|
25
|
+
return overrides.length > 0 ? [...overrides] : [litCapability("rooming")];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/testing/offline-engine.ts
|
|
29
|
+
var BACKOFF_LADDER_MS = [1e3, 5e3, 15e3, 6e4, 3e5, 9e5, 36e5];
|
|
30
|
+
var MockOfflineEngineOpNotFoundError = class extends Error {
|
|
31
|
+
constructor(opId) {
|
|
32
|
+
super(`mockOfflineEngine: no outbox entry with opId "${opId}"`);
|
|
33
|
+
this.name = "MockOfflineEngineOpNotFoundError";
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
function mockOfflineEngine(initialState = {}) {
|
|
37
|
+
let online = initialState.online === void 0 ? true : initialState.online;
|
|
38
|
+
let outbox = initialState.outbox ? [...initialState.outbox] : [];
|
|
39
|
+
let conflicts = initialState.conflicts ? [...initialState.conflicts] : [];
|
|
40
|
+
let opCounter = 0;
|
|
41
|
+
function requireEntry(opId) {
|
|
42
|
+
const found = outbox.find((entry) => entry.opId === opId);
|
|
43
|
+
if (found === void 0) {
|
|
44
|
+
throw new MockOfflineEngineOpNotFoundError(opId);
|
|
45
|
+
}
|
|
46
|
+
return found;
|
|
47
|
+
}
|
|
48
|
+
function replaceEntry(opId, next) {
|
|
49
|
+
outbox = outbox.map((entry) => entry.opId === opId ? next : entry);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
getState() {
|
|
53
|
+
return { online, outbox: [...outbox], conflicts: [...conflicts] };
|
|
54
|
+
},
|
|
55
|
+
goOffline() {
|
|
56
|
+
online = false;
|
|
57
|
+
},
|
|
58
|
+
goOnline() {
|
|
59
|
+
online = true;
|
|
60
|
+
},
|
|
61
|
+
enqueuePendingEntry(entry) {
|
|
62
|
+
opCounter += 1;
|
|
63
|
+
const created = {
|
|
64
|
+
opId: entry.opId ?? `mock-op-${String(opCounter)}`,
|
|
65
|
+
tripRef: entry.tripRef ?? "TRIP-001",
|
|
66
|
+
entity: entry.entity ?? { listName: "rooming", id: `entity-${String(opCounter)}` },
|
|
67
|
+
status: "pending",
|
|
68
|
+
backoffRung: 0
|
|
69
|
+
};
|
|
70
|
+
outbox = [...outbox, created];
|
|
71
|
+
return created;
|
|
72
|
+
},
|
|
73
|
+
advanceBackoff(opId) {
|
|
74
|
+
const entry = requireEntry(opId);
|
|
75
|
+
const nextRung = Math.min(entry.backoffRung + 1, 6);
|
|
76
|
+
replaceEntry(opId, { ...entry, backoffRung: nextRung });
|
|
77
|
+
return nextRung;
|
|
78
|
+
},
|
|
79
|
+
parkEntry(opId) {
|
|
80
|
+
const entry = requireEntry(opId);
|
|
81
|
+
replaceEntry(opId, { ...entry, status: "parked" });
|
|
82
|
+
},
|
|
83
|
+
completeEntry(opId) {
|
|
84
|
+
requireEntry(opId);
|
|
85
|
+
outbox = outbox.filter((entry) => entry.opId !== opId);
|
|
86
|
+
},
|
|
87
|
+
forceConflict(entry) {
|
|
88
|
+
conflicts = [...conflicts, entry];
|
|
89
|
+
},
|
|
90
|
+
resolveConflict(opId) {
|
|
91
|
+
conflicts = conflicts.filter((entry) => entry.opId !== opId);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/testing/sessions.ts
|
|
97
|
+
var sessionCounter = 0;
|
|
98
|
+
function nextSessionId(prefix) {
|
|
99
|
+
sessionCounter += 1;
|
|
100
|
+
return `${prefix}-${sessionCounter}`;
|
|
101
|
+
}
|
|
102
|
+
var DEFAULT_EXPIRES_AT = "2026-08-21T18:00:00.000Z";
|
|
103
|
+
function mockManagerSession(overrides = {}) {
|
|
104
|
+
return {
|
|
105
|
+
kind: "managerSession",
|
|
106
|
+
accessToken: overrides.accessToken ?? nextSessionId("mock-manager-access"),
|
|
107
|
+
refreshToken: overrides.refreshToken ?? nextSessionId("mock-manager-refresh"),
|
|
108
|
+
expiresAt: overrides.expiresAt ?? DEFAULT_EXPIRES_AT,
|
|
109
|
+
agencyRef: overrides.agencyRef ?? nextSessionId("mock-agency"),
|
|
110
|
+
role: overrides.role ?? "MANAGER",
|
|
111
|
+
...overrides.density !== void 0 ? { density: overrides.density } : {}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function mockAgencyAdminSession(overrides = {}) {
|
|
115
|
+
return {
|
|
116
|
+
kind: "agencyAdmin",
|
|
117
|
+
accessToken: overrides.accessToken ?? nextSessionId("mock-admin-access"),
|
|
118
|
+
refreshToken: overrides.refreshToken ?? nextSessionId("mock-admin-refresh"),
|
|
119
|
+
expiresAt: overrides.expiresAt ?? DEFAULT_EXPIRES_AT,
|
|
120
|
+
agencyRef: overrides.agencyRef ?? nextSessionId("mock-agency"),
|
|
121
|
+
...overrides.density !== void 0 ? { density: overrides.density } : {}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function mockShareSession(overrides = {}) {
|
|
125
|
+
return {
|
|
126
|
+
kind: "shareToken",
|
|
127
|
+
token: overrides.token ?? nextSessionId("mock-share-token"),
|
|
128
|
+
tripRef: overrides.tripRef ?? nextSessionId("mock-trip"),
|
|
129
|
+
travellerId: overrides.travellerId === void 0 ? nextSessionId("mock-traveller") : overrides.travellerId,
|
|
130
|
+
expiresAt: overrides.expiresAt ?? DEFAULT_EXPIRES_AT,
|
|
131
|
+
...overrides.density !== void 0 ? { density: overrides.density } : {}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// src/testing/sync-events.ts
|
|
136
|
+
var DEFAULT_SERVER_TIME = "2026-08-21T18:00:00.000Z";
|
|
137
|
+
function defaultEntity() {
|
|
138
|
+
return { listName: "rooming", id: "entity-1" };
|
|
139
|
+
}
|
|
140
|
+
function mockSyncEvent(kind, overrides = {}) {
|
|
141
|
+
switch (kind) {
|
|
142
|
+
case "enqueued":
|
|
143
|
+
return {
|
|
144
|
+
kind: "enqueued",
|
|
145
|
+
opId: "op-1",
|
|
146
|
+
tripRef: "TRIP-001",
|
|
147
|
+
entity: defaultEntity(),
|
|
148
|
+
enqueuedAt: DEFAULT_SERVER_TIME,
|
|
149
|
+
optimistic: {},
|
|
150
|
+
...overrides
|
|
151
|
+
};
|
|
152
|
+
case "flushed":
|
|
153
|
+
return {
|
|
154
|
+
kind: "flushed",
|
|
155
|
+
opId: "op-1",
|
|
156
|
+
tripRef: "TRIP-001",
|
|
157
|
+
entity: defaultEntity(),
|
|
158
|
+
applied: {},
|
|
159
|
+
appliedAt: DEFAULT_SERVER_TIME,
|
|
160
|
+
...overrides
|
|
161
|
+
};
|
|
162
|
+
case "conflict":
|
|
163
|
+
return {
|
|
164
|
+
kind: "conflict",
|
|
165
|
+
opId: "op-1",
|
|
166
|
+
tripRef: "TRIP-001",
|
|
167
|
+
entity: defaultEntity(),
|
|
168
|
+
queued: { roomId: "room-a" },
|
|
169
|
+
current: { roomId: "room-b" },
|
|
170
|
+
resolution: { shape: "version", currentVersion: 3 },
|
|
171
|
+
...overrides
|
|
172
|
+
};
|
|
173
|
+
case "parked":
|
|
174
|
+
return {
|
|
175
|
+
kind: "parked",
|
|
176
|
+
opId: "op-1",
|
|
177
|
+
tripRef: "TRIP-001",
|
|
178
|
+
status: 423,
|
|
179
|
+
userMessage: "This trip has been closed out and can no longer be edited.",
|
|
180
|
+
...overrides
|
|
181
|
+
};
|
|
182
|
+
case "reconnected":
|
|
183
|
+
return {
|
|
184
|
+
kind: "reconnected",
|
|
185
|
+
at: DEFAULT_SERVER_TIME,
|
|
186
|
+
...overrides
|
|
187
|
+
};
|
|
188
|
+
case "delta-applied":
|
|
189
|
+
return {
|
|
190
|
+
kind: "delta-applied",
|
|
191
|
+
tripRef: "TRIP-001",
|
|
192
|
+
list: "rooming",
|
|
193
|
+
rows: [
|
|
194
|
+
{ id: "row-1", roomId: "room-a" },
|
|
195
|
+
// A byte-for-byte redelivery of `row-1` — same id, same fields —
|
|
196
|
+
// exercising `07 §4`'s "applying the same row twice must be a no-op".
|
|
197
|
+
{ id: "row-1", roomId: "room-a" }
|
|
198
|
+
],
|
|
199
|
+
redeliveredIds: ["row-1"],
|
|
200
|
+
serverTime: DEFAULT_SERVER_TIME,
|
|
201
|
+
...overrides
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export { BACKOFF_LADDER_MS, dataDarkCapability, flagDarkCapability, litCapability, mockActorAttributedRow, mockAgencyAdminSession, mockCapabilities, mockManagerSession, mockOfflineEngine, mockShareSession, mockSyncEvent, modeDarkCapability };
|
|
207
|
+
//# sourceMappingURL=index.js.map
|
|
208
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/testing/actor-rows.ts","../../src/testing/capabilities.ts","../../src/testing/offline-engine.ts","../../src/testing/sessions.ts","../../src/testing/sync-events.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Bundled to the \"./testing\" export (02-architecture.md §1). A mock\n// actor-attributed row, the shape `ActorAttributionBadge` renders\n// (`14-testing-and-fixtures.md §1.5`).\n//\n// GAP-014/GAP-015 (`11-engine-requirements.md`) leave the actor pair absent on\n// `FormResponse` and `File` respectively — `FormResponse` has no schema column\n// at all, and `File` hand-rolls its own actor resolution instead of the shared\n// `write-actor.ts` resolver every sibling table uses, which is why it has none\n// today. Any `ActorAttributionBadge` usage built against a form-response or\n// file history/audit surface is therefore built against THIS MOCK ONLY — there\n// is no live data to fetch until those two gaps close. This is what makes\n// \"specified to full depth except `Backed by`\" (`10-doc-contract.md §6`) an\n// executable claim: the component can be built, screenshotted, and audited\n// against the DX bar (`14-testing-and-fixtures.md §3`) today, genuinely\n// blocked from shipping against real data until GAP-014/GAP-015 close.\n\n/** `11-engine-requirements.md`'s corrected `ActorKind` — four values, not two.\n * An API-key write is the CRM acting; collapsing it into the admin case would\n * name a person who did not act, which `ActorAttributionBadge` must never do. */\nexport type MockActorKind = 'MANAGER' | 'AGENCY_ADMIN' | 'API_KEY' | 'SYSTEM';\n\nexport interface MockActorAttributedRow {\n readonly performedByKind: MockActorKind;\n /** `null` for `SYSTEM`/`API_KEY` rows with no human name to show — the\n * badge's `kind`-only state (`atomic/04-identity.md`'s `ActorBadge`\n * States: \"with `name`\" · \"`kind`-only\"). */\n readonly performedByName: string | null;\n /** `meta.serverTime`-derived — never a device clock\n * (`13-i18n-and-formatting.md`'s server-time rule, inherited here). */\n readonly performedAt: string;\n}\n\nconst DEFAULT_PERFORMED_AT = '2026-08-21T18:00:00.000Z';\n\n/** Produces the shape `ActorAttributionBadge` expects. Defaults to a named\n * manager actor; override `performedByKind`/`performedByName` to exercise the\n * other three kinds, including the `kind`-only (`performedByName: null`)\n * state. */\nexport function mockActorAttributedRow(\n overrides: Partial<MockActorAttributedRow> = {},\n): MockActorAttributedRow {\n return {\n performedByKind: overrides.performedByKind ?? 'MANAGER',\n performedByName:\n overrides.performedByName !== undefined ? overrides.performedByName : 'Asha Rao',\n performedAt: overrides.performedAt ?? DEFAULT_PERFORMED_AT,\n };\n}\n","// Bundled to the \"./testing\" export (02-architecture.md §1). Fixture shapes for\n// the exact flat array `journey.capabilities()` returns\n// (`06-capability-and-personas.md §6`, cited not restated) plus one helper per\n// darkness reason (`§1.2` of `14-testing-and-fixtures.md`, cited not restated)\n// so a fixture author never hand-constructs the boolean triple.\n//\n// This file does not restate the lit/dark formula or the three darkness\n// treatments — `06-capability-and-personas.md §1`/`§2` is the one home. It only\n// produces data satisfying that shape.\n\n/** The exact row shape `journey.capabilities()` returns — a flat array, never\n * a keyed map (`06-capability-and-personas.md §6` point 1). `capability` is an\n * open string set (§6 point 5); this file never enumerates it exhaustively. */\nexport interface MockCapabilityRow {\n readonly capability: string;\n readonly modeOk: boolean;\n readonly dataOk: boolean;\n readonly flagOk: boolean;\n readonly enabled: boolean;\n}\n\n/** All three gates true → lit (`06 §1`). */\nexport function litCapability(key: string): MockCapabilityRow {\n return { capability: key, modeOk: true, dataOk: true, flagOk: true, enabled: true };\n}\n\n/** `modeOk: false`, others true, `enabled: false` — the \"render nothing, no\n * CTA\" treatment (`06 §2`): this capability can never become available for\n * this trip's mode. */\nexport function modeDarkCapability(key: string): MockCapabilityRow {\n return { capability: key, modeOk: false, dataOk: true, flagOk: true, enabled: false };\n}\n\n/** `dataOk: false`, others true, `enabled: false` — the \"connect this\" empty\n * state (`06 §2`): the module is entitled but the CRM hasn't pushed the rows\n * it needs. */\nexport function dataDarkCapability(key: string): MockCapabilityRow {\n return { capability: key, modeOk: true, dataOk: false, flagOk: true, enabled: false };\n}\n\n/** `flagOk: false`, others true, `enabled: false` — the upsell/locked\n * treatment (`06 §2`), split further by `details.reason` (`06 §2.1`) at the\n * component layer; this builder only produces the boolean triple. */\nexport function flagDarkCapability(key: string): MockCapabilityRow {\n return { capability: key, modeOk: true, dataOk: true, flagOk: false, enabled: false };\n}\n\n/**\n * Composes zero or more capability rows into the flat array shape the wire\n * returns. Pass an array built from `litCapability`/`modeDarkCapability`/\n * `dataDarkCapability`/`flagDarkCapability` — this is how a fixture reproduces,\n * e.g., the frozen PERSONALIZED mode-dark set (`06 §3`, cited not restated\n * here — enumerating those eleven module keys is that file's job, not this\n * one's, to avoid a second, driftable copy of a frozen list) as an array of\n * `modeDarkCapability(...)` calls.\n */\nexport function mockCapabilities(\n overrides: readonly MockCapabilityRow[] = [],\n): MockCapabilityRow[] {\n return overrides.length > 0 ? [...overrides] : [litCapability('rooming')];\n}\n","// Bundled to the \"./testing\" export (02-architecture.md §1). A mock\n// `OfflineEngine`-shaped fixture (`14-testing-and-fixtures.md §1.4`) — plain\n// data plus plain synchronous mutator functions, no timers, no real network,\n// no `kaafil-js` import (hard rule #1: this file lives outside `src/core/`, so\n// it never imports the real SDK — it produces a fixture satisfying the shape\n// `08-core-hooks.md` will define, not a copy of `kaafil-js`'s own\n// `OfflineEngine` type).\n//\n// Everything here is script-drivable and deterministic: force offline/online,\n// inject a pending outbox entry, advance the backoff ladder one step, force a\n// conflict on a specific entity — all synchronously, so a `smoke/dx-NN.sh`\n// script can assert against it without a timing-dependent wait\n// (`14-testing-and-fixtures.md §1.4`).\n\n/** The jittered, capped backoff ladder (`07-offline-and-sync.md §3`) — seven\n * rungs, the last one held until connectivity or app resume. `0` means \"not\n * yet retried\"; `6` means \"held at 1h\". */\nexport type BackoffRung = 0 | 1 | 2 | 3 | 4 | 5 | 6;\n\nexport const BACKOFF_LADDER_MS: readonly [number, number, number, number, number, number, number] =\n [1_000, 5_000, 15_000, 60_000, 300_000, 900_000, 3_600_000];\n\nexport type MockOutboxEntryStatus = 'pending' | 'inflight' | 'parked' | 'done';\n\nexport interface MockOutboxEntry {\n readonly opId: string;\n readonly tripRef: string;\n readonly entity: { readonly listName: string; readonly id: string };\n readonly status: MockOutboxEntryStatus;\n readonly backoffRung: BackoffRung;\n}\n\nexport interface MockConflictEntry {\n readonly opId: string;\n readonly entity: { readonly listName: string; readonly id: string };\n readonly queued: Record<string, unknown>;\n readonly current: Record<string, unknown>;\n}\n\nexport interface MockOfflineEngineState {\n readonly online: boolean;\n readonly outbox: readonly MockOutboxEntry[];\n readonly conflicts: readonly MockConflictEntry[];\n}\n\nexport interface MockOfflineEngine {\n /** A snapshot of current state. Never mutated by the caller — mutate only\n * through the methods below, mirroring the real engine's own\n * read-through-methods-only shape. */\n getState(): MockOfflineEngineState;\n goOffline(): void;\n goOnline(): void;\n /** Adds a `pending` outbox entry at backoff rung 0. Returns the entry so a\n * test can reference its `opId` in a later call. */\n enqueuePendingEntry(\n entry: Partial<Omit<MockOutboxEntry, 'status' | 'backoffRung'>>,\n ): MockOutboxEntry;\n /** Moves one rung up the ladder (`07 §3`) — capped at rung 6, never past\n * it, mirroring the real ladder holding at 1h. Throws a named error if\n * `opId` is not in the outbox, so a test typo fails loudly rather than\n * silently no-op-ing. */\n advanceBackoff(opId: string): BackoffRung;\n /** Marks an entry `parked` — the terminal, non-retried state\n * (`07 §10`). */\n parkEntry(opId: string): void;\n /** Marks an entry `done` and removes it from the outbox — the terminal\n * success state, pruned on the next persist (mirrors `kaafil-js`'s own\n * `OutboxStatus.Done`). */\n completeEntry(opId: string): void;\n /** Injects a conflict on a specific entity — the `409` reconciliation path\n * (`07 §6`). */\n forceConflict(entry: MockConflictEntry): void;\n /** Removes a conflict once a test has driven its resolution. */\n resolveConflict(opId: string): void;\n}\n\nclass MockOfflineEngineOpNotFoundError extends Error {\n constructor(opId: string) {\n super(`mockOfflineEngine: no outbox entry with opId \"${opId}\"`);\n this.name = 'MockOfflineEngineOpNotFoundError';\n }\n}\n\n/**\n * Builds a controllable, deterministic offline-engine fixture. `initialState`\n * seeds the outbox/conflicts/online flag; every mutator below is synchronous.\n */\nexport function mockOfflineEngine(\n initialState: Partial<MockOfflineEngineState> = {},\n): MockOfflineEngine {\n // Not `?? true` — `smoke/dx-03.sh` scans the whole package (`src/testing/**`\n // included) for exactly that shape, on purpose (hard rule #5's \"no second,\n // silent flag default\" is treated as the same anti-pattern in a mock\n // builder as in a real hook). An explicit `undefined` check reads the same\n // and satisfies the check.\n let online = initialState.online === undefined ? true : initialState.online;\n let outbox: MockOutboxEntry[] = initialState.outbox ? [...initialState.outbox] : [];\n let conflicts: MockConflictEntry[] = initialState.conflicts ? [...initialState.conflicts] : [];\n let opCounter = 0;\n\n function requireEntry(opId: string): MockOutboxEntry {\n const found = outbox.find((entry) => entry.opId === opId);\n if (found === undefined) {\n throw new MockOfflineEngineOpNotFoundError(opId);\n }\n return found;\n }\n\n function replaceEntry(opId: string, next: MockOutboxEntry): void {\n outbox = outbox.map((entry) => (entry.opId === opId ? next : entry));\n }\n\n return {\n getState() {\n return { online, outbox: [...outbox], conflicts: [...conflicts] };\n },\n goOffline() {\n online = false;\n },\n goOnline() {\n online = true;\n },\n enqueuePendingEntry(entry) {\n opCounter += 1;\n const created: MockOutboxEntry = {\n opId: entry.opId ?? `mock-op-${String(opCounter)}`,\n tripRef: entry.tripRef ?? 'TRIP-001',\n entity: entry.entity ?? { listName: 'rooming', id: `entity-${String(opCounter)}` },\n status: 'pending',\n backoffRung: 0,\n };\n outbox = [...outbox, created];\n return created;\n },\n advanceBackoff(opId) {\n const entry = requireEntry(opId);\n const nextRung = Math.min(entry.backoffRung + 1, 6) as BackoffRung;\n replaceEntry(opId, { ...entry, backoffRung: nextRung });\n return nextRung;\n },\n parkEntry(opId) {\n const entry = requireEntry(opId);\n replaceEntry(opId, { ...entry, status: 'parked' });\n },\n completeEntry(opId) {\n requireEntry(opId);\n outbox = outbox.filter((entry) => entry.opId !== opId);\n },\n forceConflict(entry) {\n conflicts = [...conflicts, entry];\n },\n resolveConflict(opId) {\n conflicts = conflicts.filter((entry) => entry.opId !== opId);\n },\n };\n}\n","// Bundled to the \"./testing\" export (02-architecture.md §1). Fake OPEN sessions,\n// one builder per persona — `14-testing-and-fixtures.md §1.1`. Plain factory\n// functions returning plain data; no test-framework dependency, no `kaafil-js`\n// import (hard rule #1 does not even need to be cited here — there is nothing\n// in this file an SDK type could usefully replace: a fixture only needs to look\n// like the token/claim shape the real auth adapters resolve, never the SDK's\n// own credential-resolver internals).\n//\n// U-013 (`00-decisions.md`) retired the `desk`/`field`/`public` `mode` axis, so\n// no builder below accepts a `mode` override — there is no second presentation\n// of a component for a fixture to select. `density` is the one presentational\n// knob that survives (`04-customization-ladder.md §6`); it is carried on the\n// fixture as a convenience field a Storybook decorator or smoke script can lift\n// onto the family's default density, not as anything the real session token\n// carries on the wire.\n\n/** `04-customization-ladder.md §6` — the exact, build-time-fixed density scale. */\nexport type DensityToken = 'compact' | 'default' | 'comfortable';\n\n/** `03 §7` / `06-capability-and-personas.md §7` — `COORDINATOR` is read-only,\n * `MANAGER` is full access. No third value; no relaxed/elevated variant. */\nexport type ManagerRole = 'MANAGER' | 'COORDINATOR';\n\nexport interface MockManagerSession {\n readonly kind: 'managerSession';\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly expiresAt: string;\n /** The mint response's internal `agencyId`, threaded through as `agencyRef`\n * — see `kaafil-js`'s `OpenManagerSessionOptions.agencyRef` doc comment for\n * why this is the internal id, not the external `AGENCY-###` ref. */\n readonly agencyRef: string;\n readonly role: ManagerRole;\n /** Convenience only — see this file's header comment. Omitted overrides\n * fall back to the family's own shell default, exactly as production does. */\n readonly density?: DensityToken;\n}\n\nexport interface MockAgencyAdminSession {\n readonly kind: 'agencyAdmin';\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly expiresAt: string;\n /** Agency-scoped, one role, no read-only variant — `D-096`, `03 §7`. There is\n * no `role` field: agencyAdmin has exactly one. */\n readonly agencyRef: string;\n readonly density?: DensityToken;\n}\n\nexport interface MockShareSession {\n readonly kind: 'shareToken';\n readonly token: string;\n readonly tripRef: string;\n /** `null` produces a family-token session (`briefing §26`) — no single\n * traveller subject bound, the token covers the whole family/party. A\n * non-null value is the default: a subject-bound share session. */\n readonly travellerId: string | null;\n readonly expiresAt: string;\n readonly density?: DensityToken;\n}\n\nlet sessionCounter = 0;\n\n/** Deterministic-but-distinct default ids — no `Date.now()`/`Math.random()`\n * (`07-offline-and-sync.md`'s determinism expectation for fixtures extends\n * here: a script asserting against a builder's default output must get the\n * same shape on every run within one process). */\nfunction nextSessionId(prefix: string): string {\n sessionCounter += 1;\n return `${prefix}-${sessionCounter}`;\n}\n\nconst DEFAULT_EXPIRES_AT = '2026-08-21T18:00:00.000Z';\n\n/**\n * A fake OPEN manager session satisfying the manager auth adapter\n * (`06-capability-and-personas.md §7`, `U-003`). `role` defaults to `MANAGER`;\n * pass `{ role: 'COORDINATOR' }` for the read-only variant.\n */\nexport function mockManagerSession(\n overrides: Partial<Omit<MockManagerSession, 'kind'>> = {},\n): MockManagerSession {\n return {\n kind: 'managerSession',\n accessToken: overrides.accessToken ?? nextSessionId('mock-manager-access'),\n refreshToken: overrides.refreshToken ?? nextSessionId('mock-manager-refresh'),\n expiresAt: overrides.expiresAt ?? DEFAULT_EXPIRES_AT,\n agencyRef: overrides.agencyRef ?? nextSessionId('mock-agency'),\n role: overrides.role ?? 'MANAGER',\n ...(overrides.density !== undefined ? { density: overrides.density } : {}),\n };\n}\n\n/** A fake OPEN agencyAdmin session, agency-scoped. */\nexport function mockAgencyAdminSession(\n overrides: Partial<Omit<MockAgencyAdminSession, 'kind'>> = {},\n): MockAgencyAdminSession {\n return {\n kind: 'agencyAdmin',\n accessToken: overrides.accessToken ?? nextSessionId('mock-admin-access'),\n refreshToken: overrides.refreshToken ?? nextSessionId('mock-admin-refresh'),\n expiresAt: overrides.expiresAt ?? DEFAULT_EXPIRES_AT,\n agencyRef: overrides.agencyRef ?? nextSessionId('mock-agency'),\n ...(overrides.density !== undefined ? { density: overrides.density } : {}),\n };\n}\n\n/**\n * A fake OPEN share-token session. `{ travellerId: null }` produces a\n * family-token session; the default has a subject bound.\n */\nexport function mockShareSession(\n overrides: Partial<Omit<MockShareSession, 'kind'>> = {},\n): MockShareSession {\n return {\n kind: 'shareToken',\n token: overrides.token ?? nextSessionId('mock-share-token'),\n tripRef: overrides.tripRef ?? nextSessionId('mock-trip'),\n travellerId:\n overrides.travellerId === undefined ? nextSessionId('mock-traveller') : overrides.travellerId,\n expiresAt: overrides.expiresAt ?? DEFAULT_EXPIRES_AT,\n ...(overrides.density !== undefined ? { density: overrides.density } : {}),\n };\n}\n","// Bundled to the \"./testing\" export (02-architecture.md §1). Synthetic sync\n// events — fixture data for the shapes a component's offline/sync UI must\n// render (`14-testing-and-fixtures.md §1.3`). `07-offline-and-sync.md` is the\n// one home for the mechanics these fixtures exercise; this file only produces\n// data satisfying it and never restates the outbox/backoff/conflict rules.\n\n/** The six kinds `14-testing-and-fixtures.md §1.3` names — no seventh, no\n * collapsing two into one. */\nexport type MockSyncEventKind =\n | 'enqueued'\n | 'flushed'\n | 'conflict'\n | 'parked'\n | 'reconnected'\n | 'delta-applied';\n\n/** Write-ahead, before any network call (`07 §2`). */\nexport interface MockSyncEnqueuedEvent {\n readonly kind: 'enqueued';\n readonly opId: string;\n readonly tripRef: string;\n readonly entity: { readonly listName: string; readonly id: string };\n readonly enqueuedAt: string;\n /** The row painted immediately, ahead of any server confirmation. */\n readonly optimistic: Record<string, unknown>;\n}\n\n/** An op reached a 2xx and drained (`07 §3`). */\nexport interface MockSyncFlushedEvent {\n readonly kind: 'flushed';\n readonly opId: string;\n readonly tripRef: string;\n readonly entity: { readonly listName: string; readonly id: string };\n /** The rebased canonical row, carrying the server `version`. */\n readonly applied: Record<string, unknown>;\n readonly appliedAt: string;\n}\n\n/** `07 §6` — two conflict shapes normalise into one component. Every versioned\n * entity except the checklist-item toggle carries `currentVersion`; the\n * toggle's optimistic concurrency uses `expectedStatus`/`currentStatus`\n * instead (`07 §6`, `modules/checklists/FRD.md §4.3`). */\nexport type MockConflictResolutionShape =\n | { readonly shape: 'version'; readonly currentVersion: number }\n | { readonly shape: 'status'; readonly currentStatus: string };\n\n/** Carries BOTH the queued and current values the shared resolver UI needs\n * (`07 §6` steps 1–3) — never only one side. */\nexport interface MockSyncConflictEvent {\n readonly kind: 'conflict';\n readonly opId: string;\n readonly tripRef: string;\n readonly entity: { readonly listName: string; readonly id: string };\n /** The locally intended change, as originally enqueued. */\n readonly queued: Record<string, unknown>;\n /** The fresh server row, re-read at its current version (`07 §6`'s \"why\n * the entity is re-pulled first\"). */\n readonly current: Record<string, unknown>;\n readonly resolution: MockConflictResolutionShape;\n}\n\n/** A permanently non-retryable op — `423`/`410`/`402`/`UnsatisfiableScheme`\n * (`07 §10`). Never silently dropped; surfaced and left visibly stalled. */\nexport interface MockSyncParkedEvent {\n readonly kind: 'parked';\n readonly opId: string;\n readonly tripRef: string;\n readonly status: number;\n /** A message fit to show a manager (mirrors `SyncErrorEvent.userMessage`\n * in `kaafil-js`'s own event map). */\n readonly userMessage: string;\n}\n\n/** Connectivity restored — the drainer fires opportunistically (`07 §3`). */\nexport interface MockSyncReconnectedEvent {\n readonly kind: 'reconnected';\n readonly at: string;\n}\n\n/** At-least-once redelivery (`07 §4`) — includes a row identical to one\n * already applied, so a component's idempotence against duplicate delivery is\n * directly testable rather than assumed. */\nexport interface MockSyncDeltaAppliedEvent {\n readonly kind: 'delta-applied';\n readonly tripRef: string;\n readonly list: string;\n readonly rows: readonly Record<string, unknown>[];\n /** True on the row(s) that are a byte-for-byte redelivery of a row already\n * merged on a prior pull — never a change, by construction of this fixture. */\n readonly redeliveredIds: readonly string[];\n /** The new cursor, from `meta.serverTime` (`07 §4`). */\n readonly serverTime: string;\n}\n\nexport type MockSyncEvent =\n | MockSyncEnqueuedEvent\n | MockSyncFlushedEvent\n | MockSyncConflictEvent\n | MockSyncParkedEvent\n | MockSyncReconnectedEvent\n | MockSyncDeltaAppliedEvent;\n\nconst DEFAULT_SERVER_TIME = '2026-08-21T18:00:00.000Z';\n\nfunction defaultEntity(): { listName: string; id: string } {\n return { listName: 'rooming', id: 'entity-1' };\n}\n\nexport function mockSyncEvent(\n kind: 'enqueued',\n overrides?: Partial<Omit<MockSyncEnqueuedEvent, 'kind'>>,\n): MockSyncEnqueuedEvent;\nexport function mockSyncEvent(\n kind: 'flushed',\n overrides?: Partial<Omit<MockSyncFlushedEvent, 'kind'>>,\n): MockSyncFlushedEvent;\nexport function mockSyncEvent(\n kind: 'conflict',\n overrides?: Partial<Omit<MockSyncConflictEvent, 'kind'>>,\n): MockSyncConflictEvent;\nexport function mockSyncEvent(\n kind: 'parked',\n overrides?: Partial<Omit<MockSyncParkedEvent, 'kind'>>,\n): MockSyncParkedEvent;\nexport function mockSyncEvent(\n kind: 'reconnected',\n overrides?: Partial<Omit<MockSyncReconnectedEvent, 'kind'>>,\n): MockSyncReconnectedEvent;\nexport function mockSyncEvent(\n kind: 'delta-applied',\n overrides?: Partial<Omit<MockSyncDeltaAppliedEvent, 'kind'>>,\n): MockSyncDeltaAppliedEvent;\nexport function mockSyncEvent(\n kind: MockSyncEventKind,\n overrides: Record<string, unknown> = {},\n): MockSyncEvent {\n switch (kind) {\n case 'enqueued':\n return {\n kind: 'enqueued',\n opId: 'op-1',\n tripRef: 'TRIP-001',\n entity: defaultEntity(),\n enqueuedAt: DEFAULT_SERVER_TIME,\n optimistic: {},\n ...overrides,\n } as MockSyncEnqueuedEvent;\n case 'flushed':\n return {\n kind: 'flushed',\n opId: 'op-1',\n tripRef: 'TRIP-001',\n entity: defaultEntity(),\n applied: {},\n appliedAt: DEFAULT_SERVER_TIME,\n ...overrides,\n } as MockSyncFlushedEvent;\n case 'conflict':\n return {\n kind: 'conflict',\n opId: 'op-1',\n tripRef: 'TRIP-001',\n entity: defaultEntity(),\n queued: { roomId: 'room-a' },\n current: { roomId: 'room-b' },\n resolution: { shape: 'version', currentVersion: 3 },\n ...overrides,\n } as MockSyncConflictEvent;\n case 'parked':\n return {\n kind: 'parked',\n opId: 'op-1',\n tripRef: 'TRIP-001',\n status: 423,\n userMessage: 'This trip has been closed out and can no longer be edited.',\n ...overrides,\n } as MockSyncParkedEvent;\n case 'reconnected':\n return {\n kind: 'reconnected',\n at: DEFAULT_SERVER_TIME,\n ...overrides,\n } as MockSyncReconnectedEvent;\n case 'delta-applied':\n return {\n kind: 'delta-applied',\n tripRef: 'TRIP-001',\n list: 'rooming',\n rows: [\n { id: 'row-1', roomId: 'room-a' },\n // A byte-for-byte redelivery of `row-1` — same id, same fields —\n // exercising `07 §4`'s \"applying the same row twice must be a no-op\".\n { id: 'row-1', roomId: 'room-a' },\n ],\n redeliveredIds: ['row-1'],\n serverTime: DEFAULT_SERVER_TIME,\n ...overrides,\n } as MockSyncDeltaAppliedEvent;\n }\n}\n"]}
|