@incodetech/core 2.4.1-agentic.2 → 2.4.1-agentic.4

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.
Files changed (75) hide show
  1. package/dist/{FaceDetectionProvider-D9PlON49.esm.js → FaceDetectionProvider-CuWUIxqF.esm.js} +4 -4
  2. package/dist/ae-signature.esm.js +1 -1
  3. package/dist/{apiError-gNfsp99I.esm.js → apiError-CUF7K_XZ.esm.js} +1 -1
  4. package/dist/authentication.esm.js +2 -2
  5. package/dist/{authenticationManager-BtN4CHOR.esm.js → authenticationManager-PlKb1nOo.esm.js} +3 -3
  6. package/dist/{authenticationStateMachine-DdcnWnBT.esm.js → authenticationStateMachine-BFR0oKdS.esm.js} +2 -2
  7. package/dist/certificate-issuance.esm.js +2 -2
  8. package/dist/{certificateIssuanceManager-C1nETQos.esm.js → certificateIssuanceManager-IRO_1Ug6.esm.js} +1 -1
  9. package/dist/{certificateIssuanceStateMachine-DThXMDqr.esm.js → certificateIssuanceStateMachine-CjxTBZuH.esm.js} +1 -1
  10. package/dist/consent.d.ts +1 -1
  11. package/dist/{consentManager-bjfVXO7p.d.ts → consentManager-D8rgr5Dv.d.ts} +7 -7
  12. package/dist/{deepsightLoader-Crsj_u0c.esm.js → deepsightLoader-DkADlKGZ.esm.js} +3 -3
  13. package/dist/document-capture.d.ts +1 -1
  14. package/dist/{documentCaptureManager-DDfviFg0.d.ts → documentCaptureManager-CD5iQiUI.d.ts} +1 -1
  15. package/dist/dynamic-forms.esm.js +2 -2
  16. package/dist/{dynamicFormsManager-CwqgwLa5.esm.js → dynamicFormsManager-CzEx6NHv.esm.js} +1 -1
  17. package/dist/{dynamicFormsStateMachine-CBdnpD95.esm.js → dynamicFormsStateMachine-uy2rO9_-.esm.js} +1 -1
  18. package/dist/ekyb.esm.js +2 -2
  19. package/dist/{ekybManager-BVTqTHd3.esm.js → ekybManager-DpeuX31I.esm.js} +1 -1
  20. package/dist/{ekybStateMachine-BmZHuZE1.esm.js → ekybStateMachine-CDBsMLvu.esm.js} +1 -1
  21. package/dist/electronic-signature.esm.js +1 -1
  22. package/dist/{electronicSignatureManager-Bt_tCIdI.esm.js → electronicSignatureManager-DM_e0gFI.esm.js} +2 -2
  23. package/dist/extensibility.d.ts +5 -5
  24. package/dist/extensibility.esm.js +13 -13
  25. package/dist/{externalFetch-CQY41QT1.esm.js → externalFetch-cUjJYyXj.esm.js} +1 -1
  26. package/dist/{faceCaptureManagerFactory-BWOB3ZeZ.esm.js → faceCaptureManagerFactory-D9bwl07b.esm.js} +1 -1
  27. package/dist/{faceCaptureSetup-D0w1UBlO.esm.js → faceCaptureSetup-DgPLnOKa.esm.js} +3 -3
  28. package/dist/{feature-management-DBh1nqp8.esm.js → feature-management-Yfp00gNu.esm.js} +1 -1
  29. package/dist/feature-management.esm.js +1 -1
  30. package/dist/flow.esm.js +3 -3
  31. package/dist/{flowCompletionService-Dv7zoVWe.esm.js → flowCompletionService-fJuLyBEb.esm.js} +1 -1
  32. package/dist/{flowServices-C5-oamoC.esm.js → flowServices-DORczGzA.esm.js} +4 -4
  33. package/dist/geolocation.esm.js +2 -2
  34. package/dist/{geolocationManager-BFTxgOZG.esm.js → geolocationManager-C6Gt3ePt.esm.js} +1 -1
  35. package/dist/{geolocationStateMachine-v729Lz_0.esm.js → geolocationStateMachine-Dw6D4uRM.esm.js} +1 -1
  36. package/dist/id-ocr.d.ts +1 -1
  37. package/dist/id-selfie-flow.esm.js +6 -6
  38. package/dist/id.esm.js +3 -3
  39. package/dist/{idCaptureManager-BVfqpIjM.esm.js → idCaptureManager-DymxK9jy.esm.js} +3 -3
  40. package/dist/{idCaptureServices-CwG9Xp_n.esm.js → idCaptureServices-TSSPG-WW.esm.js} +2 -2
  41. package/dist/{idCaptureStateMachine-DUm3a_Mt.esm.js → idCaptureStateMachine-BBF-qMAd.esm.js} +9 -9
  42. package/dist/{idOcrManager-BTdHlJ9D.d.ts → idOcrManager-DT3kPIB3.d.ts} +1 -1
  43. package/dist/identity-reuse.d.ts +1 -1
  44. package/dist/{identityReuseManager-ooAbp6Kc.d.ts → identityReuseManager-B1birg5X.d.ts} +4 -4
  45. package/dist/index.esm.js +1 -1
  46. package/dist/kyb-checks.esm.js +2 -2
  47. package/dist/{kybChecksManager-D2BEaUzK.esm.js → kybChecksManager-CDHqbT9w.esm.js} +1 -1
  48. package/dist/{kybChecksStateMachine-Cy-MsmKZ.esm.js → kybChecksStateMachine-CzkwMKKB.esm.js} +1 -1
  49. package/dist/mandatory-consent.d.ts +1 -1
  50. package/dist/{mandatoryConsentManager-B88Sm5nd.d.ts → mandatoryConsentManager-9PHQGri5.d.ts} +9 -9
  51. package/dist/qe-signature.esm.js +1 -1
  52. package/dist/redirect-to-mobile.esm.js +2 -2
  53. package/dist/{redirectToMobileManager-CgGaT2NP.esm.js → redirectToMobileManager-CCSNyi19.esm.js} +1 -1
  54. package/dist/{redirectToMobileStateMachine-CbhN8uVN.esm.js → redirectToMobileStateMachine-BKyOdAUe.esm.js} +1 -1
  55. package/dist/{runChildModule-BG46reQE.esm.js → runChildModule-WaHpne_n.esm.js} +1 -1
  56. package/dist/{sdkVersion-DJCo6uSb.esm.js → sdkVersion-D0wnG9m1.esm.js} +1 -1
  57. package/dist/selfie.esm.js +6 -6
  58. package/dist/{selfieErrorUtils-DNbVqz2B.esm.js → selfieErrorUtils-Sd36xaui.esm.js} +1 -1
  59. package/dist/{selfieManager-1_TQP4-x.esm.js → selfieManager-Q23b7zS_.esm.js} +3 -3
  60. package/dist/{selfieStateMachine-BUnZmMRY.esm.js → selfieStateMachine-BzSAxwIs.esm.js} +3 -3
  61. package/dist/{session-DdnAM9fZ.esm.js → session-XoKY3xHd.esm.js} +3 -3
  62. package/dist/session.esm.js +4 -4
  63. package/dist/{sessionInitializer-CwJACl13.esm.js → sessionInitializer-CN9C6ItC.esm.js} +4 -4
  64. package/dist/{setup-D26WdmvA.esm.js → setup-W-Tho5PO.esm.js} +5 -5
  65. package/dist/step-up.d.ts +384 -0
  66. package/dist/step-up.esm.js +648 -0
  67. package/dist/{tri-C9SvtzuQ.esm.js → tri-6laLcIfQ.esm.js} +1 -1
  68. package/dist/tri.esm.js +1 -1
  69. package/dist/{trueSightService-OZl8wQu7.esm.js → trueSightService-BPs_VmAj.esm.js} +1 -1
  70. package/dist/{urlUtils-eYiyWjh0.esm.js → urlUtils-B2wagB5v.esm.js} +14 -1
  71. package/dist/video-selfie.esm.js +3 -3
  72. package/dist/{videoSelfieCameraWarmer-Bhi70pSN.esm.js → videoSelfieCameraWarmer-DWGX1ByR.esm.js} +3 -3
  73. package/dist/wasm.esm.js +1 -1
  74. package/dist/workflow.esm.js +16 -16
  75. package/package.json +8 -3
@@ -0,0 +1,648 @@
1
+ import { i as assign, s as createActor, t as setup, u as createManager } from "./xstate.esm-DSajW7CJ.esm.js";
2
+ import { n as getHttpUrlOrigin } from "./urlUtils-B2wagB5v.esm.js";
3
+ //#region src/modules/step-up/stepUpStateMachine.ts
4
+ /** The SDK draws the permissions, before the face check. */
5
+ function permissionsFirst(context) {
6
+ return context.permissionsUi === "sdk" && context.permissionsOrder === "beforeFaceCheck";
7
+ }
8
+ function allScopeNames(permissions) {
9
+ return permissions.scopes.map((scope) => scope.name);
10
+ }
11
+ /** `auto-select` scopes are always approved: the person cannot untick them. */
12
+ function isLockedScope(scope) {
13
+ return scope.selectionMode === "auto-select";
14
+ }
15
+ /** The scopes that start ticked (`autoSelect` or locked), in class order. */
16
+ function preselectedScopes(permissions) {
17
+ return permissions.scopes.filter((scope) => scope.autoSelect === true || isLockedScope(scope)).map((scope) => scope.name);
18
+ }
19
+ /** The agent class's scopes that `approvedScopes` names, in the class order. */
20
+ function knownScopes(permissions, approvedScopes) {
21
+ return allScopeNames(permissions).filter((name) => approvedScopes.includes(name));
22
+ }
23
+ /**
24
+ * Step-up flow: verify the person (workflow or hosted page, driven by the UI)
25
+ * and let them approve the agent class's scopes, in the order
26
+ * `permissionsOrder` sets: `afterFaceCheck` (verifying → permissions →
27
+ * finished) or `beforeFaceCheck` (permissions → verifying → finished, so Deny
28
+ * ends the step-up before any face check).
29
+ *
30
+ * - `verifying` — the workflow or iframe is on screen. In hosted mode the
31
+ * hosted page draws the permissions itself and reports the result
32
+ * (`HOSTED_FINISHED` / `HOSTED_DENIED`).
33
+ * - `permissions` — the scope checkboxes; the ones Incode marked `autoSelect`
34
+ * start checked (from the agent class's scope selection modes), the rest
35
+ * unchecked, and Approve needs at least one. `auto-select` scopes are
36
+ * always approved, so they stay checked and cannot be toggled.
37
+ * - `savingPermissions` — only when the host saves the choice: waits for it,
38
+ * then runs the face check (`beforeFaceCheck`) or finishes. A failed save
39
+ * is `error` (`VERIFICATION_ERROR`, `detail.code` `PERMISSIONS_NOT_SAVED`).
40
+ * - `finished` (final) — approved; `selectedScopes` is the result.
41
+ * - `closed` (final) — the user pressed Deny.
42
+ * - `error` — verification failed, is still pending, or could not finish
43
+ * (`errorCode`; not final, per the module convention).
44
+ *
45
+ * With `permissionsUi: 'none'` the machine skips `permissions` and finishes
46
+ * with no scopes (contract v0.2 §6.3): the bank draws its own screen and its
47
+ * server sends its own choice to `/scopes`.
48
+ */
49
+ const stepUpMachine = setup({
50
+ types: {
51
+ context: {},
52
+ events: {},
53
+ input: {}
54
+ },
55
+ guards: {
56
+ bankDrawsPermissions: ({ context }) => context.permissionsUi === "none",
57
+ permissionsFirst: ({ context }) => permissionsFirst(context),
58
+ hasSelection: ({ context }) => context.selectedScopes.length > 0,
59
+ approvedForHostSave: ({ context }) => context.hostSavesPermissions && context.selectedScopes.length > 0,
60
+ approvedBeforeFaceCheck: ({ context }) => permissionsFirst(context) && context.selectedScopes.length > 0,
61
+ hostedApprovalUsable: ({ context, event }) => {
62
+ const { approvedScopes } = event;
63
+ return knownScopes(context.permissions, approvedScopes).length > 0;
64
+ }
65
+ },
66
+ actions: {
67
+ toggleScope: assign(({ context, event }) => {
68
+ const { name } = event;
69
+ const known = allScopeNames(context.permissions);
70
+ const scope = context.permissions.scopes.find((candidate) => candidate.name === name);
71
+ if (!scope || isLockedScope(scope)) return {};
72
+ const selected = new Set(context.selectedScopes);
73
+ if (selected.has(name)) selected.delete(name);
74
+ else selected.add(name);
75
+ return { selectedScopes: known.filter((scope) => selected.has(scope)) };
76
+ }),
77
+ setHostedSelection: assign(({ context, event }) => {
78
+ const { approvedScopes } = event;
79
+ return { selectedScopes: knownScopes(context.permissions, approvedScopes) };
80
+ }),
81
+ clearSelection: assign({ selectedScopes: () => [] }),
82
+ setError: assign(({ event }) => {
83
+ const failed = event;
84
+ return {
85
+ error: failed.error,
86
+ errorCode: failed.code,
87
+ errorDetail: failed.detail
88
+ };
89
+ }),
90
+ setSaveError: assign(({ event }) => {
91
+ const { error } = event;
92
+ return {
93
+ error: "The approved permissions could not be saved",
94
+ errorCode: "VERIFICATION_ERROR",
95
+ errorDetail: {
96
+ code: "PERMISSIONS_NOT_SAVED",
97
+ message: error
98
+ }
99
+ };
100
+ }),
101
+ setNoKnownScopesError: assign(({ event }) => {
102
+ const { approvedScopes } = event;
103
+ return {
104
+ error: "The hosted page approved none of the agent class's scopes",
105
+ errorCode: "VERIFICATION_ERROR",
106
+ errorDetail: {
107
+ code: "NO_KNOWN_SCOPES",
108
+ message: `approvedScopes: ${JSON.stringify(approvedScopes)}`
109
+ }
110
+ };
111
+ })
112
+ }
113
+ }).createMachine({
114
+ id: "stepUp",
115
+ initial: "starting",
116
+ context: ({ input }) => ({
117
+ stepUpId: input.stepUpId,
118
+ permissions: input.permissions,
119
+ permissionsUi: input.permissionsUi,
120
+ permissionsOrder: input.permissionsOrder,
121
+ hostSavesPermissions: input.hostSavesPermissions,
122
+ selectedScopes: preselectedScopes(input.permissions),
123
+ error: void 0,
124
+ errorCode: void 0,
125
+ errorDetail: void 0
126
+ }),
127
+ states: {
128
+ starting: { always: [{
129
+ guard: "permissionsFirst",
130
+ target: "permissions"
131
+ }, { target: "verifying" }] },
132
+ verifying: { on: {
133
+ VERIFICATION_FINISHED: [
134
+ {
135
+ guard: "bankDrawsPermissions",
136
+ target: "finished",
137
+ actions: "clearSelection"
138
+ },
139
+ {
140
+ guard: "permissionsFirst",
141
+ target: "finished"
142
+ },
143
+ { target: "permissions" }
144
+ ],
145
+ VERIFICATION_FAILED: {
146
+ target: "error",
147
+ actions: "setError"
148
+ },
149
+ HOSTED_FINISHED: [
150
+ {
151
+ guard: "bankDrawsPermissions",
152
+ target: "finished",
153
+ actions: "clearSelection"
154
+ },
155
+ {
156
+ guard: "hostedApprovalUsable",
157
+ target: "finished",
158
+ actions: "setHostedSelection"
159
+ },
160
+ {
161
+ target: "error",
162
+ actions: "setNoKnownScopesError"
163
+ }
164
+ ],
165
+ HOSTED_DENIED: { target: "closed" }
166
+ } },
167
+ permissions: { on: {
168
+ TOGGLE_SCOPE: { actions: "toggleScope" },
169
+ APPROVE: [
170
+ {
171
+ guard: "approvedForHostSave",
172
+ target: "savingPermissions"
173
+ },
174
+ {
175
+ guard: "approvedBeforeFaceCheck",
176
+ target: "verifying"
177
+ },
178
+ {
179
+ guard: "hasSelection",
180
+ target: "finished"
181
+ }
182
+ ],
183
+ DENY: { target: "closed" }
184
+ } },
185
+ savingPermissions: { on: {
186
+ PERMISSIONS_SAVED: [{
187
+ guard: "permissionsFirst",
188
+ target: "verifying"
189
+ }, { target: "finished" }],
190
+ PERMISSIONS_SAVE_FAILED: {
191
+ target: "error",
192
+ actions: "setSaveError"
193
+ }
194
+ } },
195
+ finished: { type: "final" },
196
+ closed: { type: "final" },
197
+ error: {}
198
+ }
199
+ });
200
+ //#endregion
201
+ //#region src/modules/step-up/stepUpActor.ts
202
+ function createStepUpActor(options) {
203
+ return createActor(stepUpMachine, { input: options }).start();
204
+ }
205
+ //#endregion
206
+ //#region src/modules/step-up/stepUpError.ts
207
+ /**
208
+ * Error passed to the step-up `onError` callback (and thrown for invalid
209
+ * options). `code` is stable; `message` is for developers, not end users.
210
+ * `detail` is what the workflow or hosted page reported underneath (set for
211
+ * `VERIFICATION_ERROR`, and for `FACE_CHECK_FAILED` from the workflow).
212
+ */
213
+ var StepUpError = class extends Error {
214
+ constructor(code, message, stepUpId, detail) {
215
+ super(message);
216
+ this.name = "StepUpError";
217
+ this.code = code;
218
+ this.stepUpId = stepUpId;
219
+ if (detail) this.detail = detail;
220
+ }
221
+ };
222
+ function isStepUpError(error) {
223
+ return error instanceof StepUpError;
224
+ }
225
+ //#endregion
226
+ //#region src/modules/step-up/stepUpOptions.ts
227
+ const MODES = ["embedded", "hosted"];
228
+ const PERMISSIONS_UIS = ["sdk", "none"];
229
+ const PERMISSIONS_ORDERS = ["afterFaceCheck", "beforeFaceCheck"];
230
+ /** Largest delay setTimeout honours; above it browsers fire after ~1 ms. */
231
+ const MAX_TIMER_DELAY_MS = 2147483647;
232
+ function isRecord$1(value) {
233
+ return typeof value === "object" && value !== null && !Array.isArray(value);
234
+ }
235
+ function isNonEmptyString(value) {
236
+ return typeof value === "string" && value.trim().length > 0;
237
+ }
238
+ function optionalString(value) {
239
+ return isNonEmptyString(value) ? value : void 0;
240
+ }
241
+ /** The query part of a URL (between `?` and `#`), or `''`. */
242
+ function queryOf(url) {
243
+ const withoutFragment = url.split("#", 1)[0];
244
+ const index = withoutFragment.indexOf("?");
245
+ return index === -1 ? "" : withoutFragment.slice(index + 1);
246
+ }
247
+ function invalid(message, stepUpId) {
248
+ throw new StepUpError("INVALID_OPTIONS", message, stepUpId);
249
+ }
250
+ function normalizeScope(raw, index) {
251
+ if (!isRecord$1(raw) || !isNonEmptyString(raw.name)) invalid(`permissions.scopes[${index}].name must be a non-empty string`);
252
+ const displayName = optionalString(raw.displayName);
253
+ const description = optionalString(raw.description);
254
+ const autoSelect = typeof raw.autoSelect === "boolean" ? raw.autoSelect : void 0;
255
+ const selectionMode = raw.selectionMode === "auto-select" || raw.selectionMode === "dynamic-select" ? raw.selectionMode : void 0;
256
+ return {
257
+ name: raw.name,
258
+ displayName: displayName ?? raw.name,
259
+ ...description ? { description } : {},
260
+ ...autoSelect !== void 0 ? { autoSelect } : {},
261
+ ...selectionMode ? { selectionMode } : {}
262
+ };
263
+ }
264
+ function nonNegativeNumber(value) {
265
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
266
+ }
267
+ function normalizeLimit(raw) {
268
+ if (!isRecord$1(raw)) return void 0;
269
+ const budgetLimit = nonNegativeNumber(raw.budgetLimit);
270
+ const usageLimit = nonNegativeNumber(raw.usageLimit);
271
+ const usageLimitLabel = optionalString(raw.usageLimitLabel);
272
+ if (budgetLimit === void 0 && usageLimit === void 0) return void 0;
273
+ return {
274
+ ...budgetLimit !== void 0 ? { budgetLimit } : {},
275
+ ...usageLimit !== void 0 ? { usageLimit } : {},
276
+ ...usageLimit !== void 0 && usageLimitLabel ? { usageLimitLabel } : {}
277
+ };
278
+ }
279
+ /** Display only: a malformed limit is dropped rather than failing the call. */
280
+ function normalizeLimits(raw, scopeNames) {
281
+ if (!isRecord$1(raw)) return void 0;
282
+ const limits = {};
283
+ for (const [name, value] of Object.entries(raw)) {
284
+ const limit = scopeNames.has(name) ? normalizeLimit(value) : void 0;
285
+ if (limit) limits[name] = limit;
286
+ }
287
+ return Object.keys(limits).length > 0 ? limits : void 0;
288
+ }
289
+ /**
290
+ * Checks the permissions block from the start response and fills in
291
+ * `displayName` (falls back to the scope name). Keeps each scope's boolean
292
+ * `autoSelect` and known `selectionMode`, and the valid limits of known
293
+ * scopes. A missing or `null` `durationSeconds` is left out. Throws
294
+ * `INVALID_OPTIONS` when a required field is missing, or `durationSeconds` is
295
+ * given but not a positive number.
296
+ */
297
+ function normalizeStepUpPermissions(raw) {
298
+ if (!isRecord$1(raw)) invalid("permissions is required");
299
+ const scopesRaw = raw.scopes;
300
+ if (!Array.isArray(scopesRaw) || scopesRaw.length === 0) invalid("permissions.scopes must list at least one scope");
301
+ const scopes = scopesRaw.map(normalizeScope);
302
+ const names = new Set(scopes.map((scope) => scope.name));
303
+ if (names.size !== scopes.length) invalid("permissions.scopes must not repeat a scope name");
304
+ const { durationSeconds } = raw;
305
+ if (durationSeconds !== void 0 && durationSeconds !== null && (typeof durationSeconds !== "number" || !Number.isFinite(durationSeconds) || durationSeconds <= 0)) invalid("permissions.durationSeconds must be a positive number when given");
306
+ const agentClassId = optionalString(raw.agentClassId);
307
+ const purpose = optionalString(raw.purpose);
308
+ const limits = normalizeLimits(raw.limits, names);
309
+ return {
310
+ ...agentClassId ? { agentClassId } : {},
311
+ ...purpose ? { purpose } : {},
312
+ scopes,
313
+ ...limits ? { limits } : {},
314
+ ...typeof durationSeconds === "number" ? { durationSeconds } : {}
315
+ };
316
+ }
317
+ /**
318
+ * Exact origin (`scheme://host[:port]`) of an absolute http(s) URL, or
319
+ * `undefined` when the URL is not one.
320
+ */
321
+ function getUrlOrigin(url) {
322
+ return getHttpUrlOrigin(url);
323
+ }
324
+ /**
325
+ * Validates the `renderStepUp` options (everything except the DOM container,
326
+ * which the UI layer checks) and fills in defaults:
327
+ * `mode: 'embedded'`, `permissionsUi: 'sdk'`,
328
+ * `permissionsOrder: 'beforeFaceCheck'` (`'afterFaceCheck'` in hosted mode),
329
+ * `readyTimeoutMs: DEFAULT_STEP_UP_READY_TIMEOUT_MS` (15 s),
330
+ * `disableDashboardTheme: false`.
331
+ *
332
+ * Throws {@link StepUpError} with code `INVALID_OPTIONS`.
333
+ */
334
+ function validateStepUpOptions(raw) {
335
+ if (!isRecord$1(raw)) invalid("options are required");
336
+ const { stepUpId } = raw;
337
+ if (!isNonEmptyString(stepUpId)) invalid("stepUpId must be a non-empty string");
338
+ if (typeof raw.onFinish !== "function") invalid("onFinish must be a function", stepUpId);
339
+ if (raw.onError !== void 0 && typeof raw.onError !== "function") invalid("onError must be a function when given", stepUpId);
340
+ if (raw.onPermissionsApproved !== void 0 && typeof raw.onPermissionsApproved !== "function") invalid("onPermissionsApproved must be a function when given", stepUpId);
341
+ const mode = raw.mode ?? "embedded";
342
+ if (!MODES.includes(mode)) invalid(`mode must be one of ${MODES.join(", ")}`, stepUpId);
343
+ const permissionsUi = raw.permissionsUi ?? "sdk";
344
+ if (!PERMISSIONS_UIS.includes(permissionsUi)) invalid(`permissionsUi must be one of ${PERMISSIONS_UIS.join(", ")}`, stepUpId);
345
+ const permissionsOrder = raw.permissionsOrder ?? (mode === "hosted" ? "afterFaceCheck" : "beforeFaceCheck");
346
+ if (!PERMISSIONS_ORDERS.includes(permissionsOrder)) invalid(`permissionsOrder must be one of ${PERMISSIONS_ORDERS.join(", ")}`, stepUpId);
347
+ if (mode === "hosted" && permissionsOrder === "beforeFaceCheck") invalid("permissionsOrder 'beforeFaceCheck' needs embedded mode", stepUpId);
348
+ const sessionToken = optionalString(raw.sessionToken);
349
+ if (mode === "embedded" && !sessionToken) invalid("sessionToken is required in embedded mode", stepUpId);
350
+ if (raw.interviewId !== void 0 && !isNonEmptyString(raw.interviewId)) invalid("interviewId must be a non-empty string when given", stepUpId);
351
+ let onboardingUrl;
352
+ let onboardingOrigin;
353
+ if (mode === "hosted") {
354
+ if (!isNonEmptyString(raw.onboardingUrl)) invalid("onboardingUrl is required in hosted mode", stepUpId);
355
+ onboardingOrigin = getUrlOrigin(raw.onboardingUrl);
356
+ if (!onboardingOrigin) invalid("onboardingUrl must be an absolute http(s) URL", stepUpId);
357
+ if (sessionToken && queryOf(raw.onboardingUrl).includes(sessionToken)) invalid("onboardingUrl must carry the session token in the fragment, not the query", stepUpId);
358
+ onboardingUrl = raw.onboardingUrl;
359
+ }
360
+ const readyTimeoutMs = raw.readyTimeoutMs ?? 15e3;
361
+ if (typeof readyTimeoutMs !== "number" || !Number.isFinite(readyTimeoutMs) || readyTimeoutMs <= 0 || readyTimeoutMs > MAX_TIMER_DELAY_MS) invalid(`readyTimeoutMs must be a positive number up to ${MAX_TIMER_DELAY_MS} when given`, stepUpId);
362
+ if (raw.lang !== void 0 && !isNonEmptyString(raw.lang)) invalid("lang must be a non-empty string when given", stepUpId);
363
+ if (raw.disableDashboardTheme !== void 0 && typeof raw.disableDashboardTheme !== "boolean") invalid("disableDashboardTheme must be a boolean when given", stepUpId);
364
+ let permissions;
365
+ try {
366
+ permissions = normalizeStepUpPermissions(raw.permissions);
367
+ } catch (error) {
368
+ invalid(error instanceof Error ? error.message : String(error), stepUpId);
369
+ }
370
+ return {
371
+ stepUpId,
372
+ sessionToken: sessionToken ?? "",
373
+ ...isNonEmptyString(raw.interviewId) ? { interviewId: raw.interviewId } : {},
374
+ permissions,
375
+ mode,
376
+ ...onboardingUrl ? {
377
+ onboardingUrl,
378
+ onboardingOrigin
379
+ } : {},
380
+ permissionsUi,
381
+ permissionsOrder,
382
+ readyTimeoutMs,
383
+ ...isNonEmptyString(raw.lang) ? { lang: raw.lang } : {},
384
+ disableDashboardTheme: raw.disableDashboardTheme === true,
385
+ ...raw.onPermissionsApproved ? { onPermissionsApproved: raw.onPermissionsApproved } : {},
386
+ onFinish: raw.onFinish,
387
+ ...raw.onError ? { onError: raw.onError } : {}
388
+ };
389
+ }
390
+ const UNITS = [
391
+ {
392
+ unit: "days",
393
+ seconds: 86400
394
+ },
395
+ {
396
+ unit: "hours",
397
+ seconds: 3600
398
+ },
399
+ {
400
+ unit: "minutes",
401
+ seconds: 60
402
+ }
403
+ ];
404
+ /**
405
+ * Picks the largest unit that divides the duration exactly, so
406
+ * 36000 s → 10 hours, 5400 s → 90 minutes. A duration that is not a whole
407
+ * number of minutes is rounded up to minutes (never shown shorter than it is),
408
+ * and anything under a minute stays in seconds. No duration gives `undefined`.
409
+ */
410
+ function describeStepUpDuration(durationSeconds) {
411
+ if (durationSeconds === void 0) return void 0;
412
+ const seconds = Math.ceil(durationSeconds);
413
+ for (const { unit, seconds: size } of UNITS) if (seconds >= size && seconds % size === 0) return {
414
+ unit,
415
+ count: seconds / size
416
+ };
417
+ if (seconds >= 60) return {
418
+ unit: "minutes",
419
+ count: Math.ceil(seconds / 60)
420
+ };
421
+ return {
422
+ unit: "seconds",
423
+ count: seconds
424
+ };
425
+ }
426
+ //#endregion
427
+ //#region src/modules/step-up/stepUpManager.ts
428
+ /**
429
+ * What the workflow's `onFinish` result means for the step-up:
430
+ * - `failed` — `action: 'rejected'`, or score `FAIL` / `MANUAL_FAIL`.
431
+ * - `pending` — no result (the workflow went to async resolution).
432
+ * - `passed` — anything else, `WARN` and `UNKNOWN` included. That is only a
433
+ * UI hint: Incode passes the face check only on `OK` / `MANUAL_OK`, so
434
+ * `onFinish` is never an approval.
435
+ */
436
+ function readWorkflowResult(result) {
437
+ if (!result) return "pending";
438
+ if (result.action === "rejected" || result.scoreStatus === "FAIL" || result.scoreStatus === "MANUAL_FAIL") return "failed";
439
+ return "passed";
440
+ }
441
+ function toPermissionsState(context, saving) {
442
+ const { permissions, selectedScopes } = context;
443
+ const duration = describeStepUpDuration(permissions.durationSeconds);
444
+ return {
445
+ status: "permissions",
446
+ ...permissions.purpose ? { purpose: permissions.purpose } : {},
447
+ scopes: permissions.scopes.map((scope) => {
448
+ const limit = permissions.limits?.[scope.name];
449
+ return {
450
+ ...scope,
451
+ checked: selectedScopes.includes(scope.name),
452
+ locked: isLockedScope(scope),
453
+ ...limit ? { limit } : {}
454
+ };
455
+ }),
456
+ ...duration ? { duration } : {},
457
+ canApprove: selectedScopes.length > 0,
458
+ saving
459
+ };
460
+ }
461
+ function mapState(snapshot) {
462
+ const typed = snapshot;
463
+ const { context } = typed;
464
+ if (typed.matches("permissions")) return toPermissionsState(context, false);
465
+ if (typed.matches("savingPermissions")) return toPermissionsState(context, true);
466
+ if (typed.matches("finished")) return {
467
+ status: "finished",
468
+ result: {
469
+ stepUpId: context.stepUpId,
470
+ approvedScopes: [...context.selectedScopes]
471
+ }
472
+ };
473
+ if (typed.matches("closed")) return { status: "closed" };
474
+ if (typed.matches("error")) return {
475
+ status: "error",
476
+ code: context.errorCode ?? "VERIFICATION_ERROR",
477
+ error: context.error ?? "Verification failed",
478
+ ...context.errorDetail ? { detail: context.errorDetail } : {}
479
+ };
480
+ return { status: "verifying" };
481
+ }
482
+ function createApi({ actor }) {
483
+ return {
484
+ /** Verification passed: show the permissions (or finish, `'none'`). */
485
+ verificationFinished() {
486
+ actor.send({ type: "VERIFICATION_FINISHED" });
487
+ },
488
+ /**
489
+ * The workflow's `onFinish`, with its result. A failed face check goes to
490
+ * `error` (`FACE_CHECK_FAILED`) and no result to `error`
491
+ * (`VERIFICATION_PENDING`); neither shows the permissions.
492
+ */
493
+ workflowFinished(result) {
494
+ const outcome = readWorkflowResult(result);
495
+ if (outcome === "passed") actor.send({ type: "VERIFICATION_FINISHED" });
496
+ else if (outcome === "failed" && result) actor.send({
497
+ type: "VERIFICATION_FAILED",
498
+ code: "FACE_CHECK_FAILED",
499
+ error: `The face check failed (action ${result.action}, score ${result.scoreStatus})`,
500
+ detail: { code: result.scoreStatus }
501
+ });
502
+ else actor.send({
503
+ type: "VERIFICATION_FAILED",
504
+ code: "VERIFICATION_PENDING",
505
+ error: "The verification has no result yet"
506
+ });
507
+ },
508
+ /**
509
+ * The workflow or hosted page ended without a passed check. `code`
510
+ * defaults to `VERIFICATION_ERROR` (could not finish); pass
511
+ * `FACE_CHECK_FAILED` only for a check that ran and failed.
512
+ */
513
+ verificationFailed(error, code = "VERIFICATION_ERROR", detail) {
514
+ actor.send({
515
+ type: "VERIFICATION_FAILED",
516
+ code,
517
+ error,
518
+ ...detail ? { detail } : {}
519
+ });
520
+ },
521
+ /** Hosted mode: the hosted page finished, with the scopes it approved. */
522
+ hostedFinished(approvedScopes) {
523
+ actor.send({
524
+ type: "HOSTED_FINISHED",
525
+ approvedScopes
526
+ });
527
+ },
528
+ /** Hosted mode: the user denied on the hosted page. */
529
+ hostedDenied() {
530
+ actor.send({ type: "HOSTED_DENIED" });
531
+ },
532
+ toggleScope(name) {
533
+ actor.send({
534
+ type: "TOGGLE_SCOPE",
535
+ name
536
+ });
537
+ },
538
+ /** Ignored while no scope is checked. */
539
+ approve() {
540
+ actor.send({ type: "APPROVE" });
541
+ },
542
+ deny() {
543
+ actor.send({ type: "DENY" });
544
+ },
545
+ /** The host saved the approved scopes: go on to the face check or finish. */
546
+ permissionsSaved() {
547
+ actor.send({ type: "PERMISSIONS_SAVED" });
548
+ },
549
+ /** The host could not save them: the step-up ends in error. */
550
+ permissionsSaveFailed(error) {
551
+ actor.send({
552
+ type: "PERMISSIONS_SAVE_FAILED",
553
+ error
554
+ });
555
+ }
556
+ };
557
+ }
558
+ /**
559
+ * Creates the step-up manager. Pass permissions already normalized by
560
+ * `validateStepUpOptions` / `normalizeStepUpPermissions`.
561
+ */
562
+ function createStepUpManager(options) {
563
+ return createManager({
564
+ actor: createStepUpActor({
565
+ stepUpId: options.stepUpId,
566
+ permissions: options.permissions,
567
+ permissionsUi: options.permissionsUi ?? "sdk",
568
+ permissionsOrder: options.permissionsOrder ?? "beforeFaceCheck",
569
+ hostSavesPermissions: options.hostSavesPermissions ?? false
570
+ }),
571
+ mapState,
572
+ createApi
573
+ });
574
+ }
575
+ //#endregion
576
+ //#region src/modules/step-up/stepUpHostedMessage.ts
577
+ /**
578
+ * Tag on every step-up `postMessage`, both directions. Matches the local
579
+ * step-up mock's hosted page (contract P4.7 §6.6 reference implementation).
580
+ */
581
+ const STEP_UP_MESSAGE_SOURCE = "incode-step-up";
582
+ /**
583
+ * True only when the message came from exactly `expectedOrigin`
584
+ * (scheme, host and port). No suffix or prefix matching:
585
+ * `https://onboarding.incode.com.evil.com` does not match
586
+ * `https://onboarding.incode.com`.
587
+ */
588
+ function isTrustedHostedOrigin(origin, expectedOrigin) {
589
+ return typeof origin === "string" && origin.length > 0 && origin !== "null" && origin === expectedOrigin;
590
+ }
591
+ function isRecord(value) {
592
+ return typeof value === "object" && value !== null && !Array.isArray(value);
593
+ }
594
+ /**
595
+ * Reads a `postMessage` from the hosted step-up page.
596
+ *
597
+ * Pass only messages whose `source` is the step-up iframe's own window. The
598
+ * page tags messages `{ source: 'incode-step-up', type, ... }`:
599
+ * - `ready` — loaded; the parent answers with {@link createStepUpInitMessage}.
600
+ * - `finish` — `{ stepUpId, approvedScopes }`; the hosted page ran the face
601
+ * check and its own permissions screen. Read only when `stepUpId` is
602
+ * exactly `expectedStepUpId`; otherwise (missing or another step-up's)
603
+ * it is ignored, not relayed.
604
+ * - `error` — `{ code, message? }`, e.g. `USER_DENIED` or
605
+ * `FACE_CHECK_FAILED`. Ignored when it names another `stepUpId`.
606
+ *
607
+ * A tagged message from another origin returns `originMismatch` (the frame
608
+ * left the onboarding origin, for example after a redirect). Untagged
609
+ * messages are ignored.
610
+ */
611
+ function readHostedPageMessage(event, expectedOrigin, expectedStepUpId) {
612
+ const { data } = event;
613
+ if (!isRecord(data) || data.source !== "incode-step-up") return { kind: "ignored" };
614
+ if (!isTrustedHostedOrigin(event.origin, expectedOrigin)) return { kind: "originMismatch" };
615
+ if (data.type === "ready") return { kind: "ready" };
616
+ if (data.type === "finish") {
617
+ if (data.stepUpId !== expectedStepUpId) return { kind: "ignored" };
618
+ return {
619
+ kind: "finished",
620
+ approvedScopes: Array.isArray(data.approvedScopes) ? data.approvedScopes.filter((scope) => typeof scope === "string") : []
621
+ };
622
+ }
623
+ if (data.type === "error") {
624
+ if (data.stepUpId !== void 0 && data.stepUpId !== expectedStepUpId) return { kind: "ignored" };
625
+ return {
626
+ kind: "error",
627
+ code: typeof data.code === "string" && data.code ? data.code : "UNKNOWN",
628
+ ...typeof data.message === "string" && data.message ? { message: data.message } : {}
629
+ };
630
+ }
631
+ return { kind: "ignored" };
632
+ }
633
+ /**
634
+ * Builds the `init` message that hands the hosted page the permissions and
635
+ * the parent's origin (contract v0.2 §6.6; the page checks `parentOrigin`
636
+ * against its `returnOrigin`). The caller posts it with `targetOrigin` set to
637
+ * the exact onboarding origin, never `'*'`.
638
+ */
639
+ function createStepUpInitMessage(permissions, parentOrigin) {
640
+ return {
641
+ source: STEP_UP_MESSAGE_SOURCE,
642
+ type: "init",
643
+ permissions,
644
+ parentOrigin
645
+ };
646
+ }
647
+ //#endregion
648
+ export { STEP_UP_MESSAGE_SOURCE, StepUpError, createStepUpInitMessage, createStepUpManager, describeStepUpDuration, getUrlOrigin, isStepUpError, isTrustedHostedOrigin, normalizeStepUpPermissions, readHostedPageMessage, readWorkflowResult, validateStepUpOptions };
@@ -1,4 +1,4 @@
1
- import { t as SDK_VERSION } from "./sdkVersion-DJCo6uSb.esm.js";
1
+ import { t as SDK_VERSION } from "./sdkVersion-D0wnG9m1.esm.js";
2
2
  import { t as BrowserTimerProvider } from "./BrowserTimerProvider-BXhgNxLt.esm.js";
3
3
  import { t as BrowserLazyImportProvider } from "./BrowserLazyImportProvider-BhoA957O.esm.js";
4
4
  //#region ../infra/src/providers/browser/TransactionalRiskIntelligenceProvider.ts
package/dist/tri.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as isTRIStarted, d as startTRI, f as stopTRI, l as resetTRI, n as createTRISession, r as getTRIActivityId, u as setupTRI } from "./tri-C9SvtzuQ.esm.js";
1
+ import { a as isTRIStarted, d as startTRI, f as stopTRI, l as resetTRI, n as createTRISession, r as getTRIActivityId, u as setupTRI } from "./tri-6laLcIfQ.esm.js";
2
2
  export { createTRISession, getTRIActivityId, isTRIStarted, resetTRI, setupTRI, startTRI, stopTRI };
@@ -2,7 +2,7 @@ import { T as resolveWasmConfig, _ as emitWasmDiagnostic, a as setWebApiHttpTran
2
2
  import { n as getTimestamp } from "./IBrowserEnvironmentCapability-D7JG7ML3.esm.js";
3
3
  import { C as subscribeEvent, n as eventModuleNames } from "./events-ggpBBaZ8.esm.js";
4
4
  import { i as isApiConfigured, l as withTiming, n as getApi, o as setClient, r as getToken, t as api, u as endpoints } from "./api-CpP4PS4S.esm.js";
5
- import { t as SDK_VERSION } from "./sdkVersion-DJCo6uSb.esm.js";
5
+ import { t as SDK_VERSION } from "./sdkVersion-D0wnG9m1.esm.js";
6
6
  import { n as reportHttpError, t as HttpRequestError } from "./HttpRequestError-Bh0pNGSC.esm.js";
7
7
  /** Dirty-timer half of the interrupted-snapshot rebuild debounce (module boundaries + timer). */
8
8
  const SNAPSHOT_DIRTY_INTERVAL_MS = 15e3;
@@ -22,5 +22,18 @@ function stripUrlQueryAndFragment(url) {
22
22
  return url.split(/[?#]/, 1)[0];
23
23
  }
24
24
  }
25
+ /**
26
+ * Exact origin (`scheme://host[:port]`) of an absolute `http:` or `https:`
27
+ * URL, or `undefined` for anything else (relative, unparsable, other schemes).
28
+ */
29
+ function getHttpUrlOrigin(url) {
30
+ try {
31
+ const parsed = new URL(url);
32
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return;
33
+ return parsed.origin;
34
+ } catch {
35
+ return;
36
+ }
37
+ }
25
38
  //#endregion
26
- export { stripUrlQueryAndFragment as i, getUrlSearchParam as n, resolveAgainstPageOrigin as r, buildUrlWithSearchParams as t };
39
+ export { stripUrlQueryAndFragment as a, resolveAgainstPageOrigin as i, getHttpUrlOrigin as n, getUrlSearchParam as r, buildUrlWithSearchParams as t };