datakeen-session-react 1.1.140-rc.97 → 1.1.140-rc.99

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 (83) hide show
  1. package/dist/cjs/components/DatakeenSession.js +7 -1
  2. package/dist/cjs/components/DatakeenSession.js.map +1 -1
  3. package/dist/cjs/components/session/EndFlow.js +3 -2
  4. package/dist/cjs/components/session/EndFlow.js.map +1 -1
  5. package/dist/cjs/components/session/MobileRedirect.js +4 -0
  6. package/dist/cjs/components/session/MobileRedirect.js.map +1 -1
  7. package/dist/cjs/components/session/PdfGeneration.js +25 -19
  8. package/dist/cjs/components/session/PdfGeneration.js.map +1 -1
  9. package/dist/cjs/components/session/UserInputForm/CustomFormFields.js +0 -10
  10. package/dist/cjs/components/session/UserInputForm/CustomFormFields.js.map +1 -1
  11. package/dist/cjs/components/session/UserInputForm.js +2 -2
  12. package/dist/cjs/components/session/UserInputForm.js.map +1 -1
  13. package/dist/cjs/components/signature-electronic/SignedDocumentPreview.js +12 -14
  14. package/dist/cjs/components/signature-electronic/SignedDocumentPreview.js.map +1 -1
  15. package/dist/cjs/components/template/ExternalVerificationNodeHandler.js +67 -4
  16. package/dist/cjs/components/template/ExternalVerificationNodeHandler.js.map +1 -1
  17. package/dist/cjs/hooks/useSession.js +3 -3
  18. package/dist/cjs/hooks/useSession.js.map +1 -1
  19. package/dist/cjs/hooks/useUserInputForm.js +13 -9
  20. package/dist/cjs/hooks/useUserInputForm.js.map +1 -1
  21. package/dist/cjs/i18n/en.json.js +4 -1
  22. package/dist/cjs/i18n/en.json.js.map +1 -1
  23. package/dist/cjs/i18n/fr.json.js +4 -1
  24. package/dist/cjs/i18n/fr.json.js.map +1 -1
  25. package/dist/cjs/services/api.js +5 -0
  26. package/dist/cjs/services/api.js.map +1 -1
  27. package/dist/cjs/services/auditTrailService.js +19 -9
  28. package/dist/cjs/services/auditTrailService.js.map +1 -1
  29. package/dist/cjs/services/integrationService.js +2 -0
  30. package/dist/cjs/services/integrationService.js.map +1 -1
  31. package/dist/cjs/services/sessionCredentials.js +24 -0
  32. package/dist/cjs/services/sessionCredentials.js.map +1 -0
  33. package/dist/cjs/types/session.js.map +1 -1
  34. package/dist/cjs/utils/customFieldIds.js +52 -0
  35. package/dist/cjs/utils/customFieldIds.js.map +1 -0
  36. package/dist/cjs/utils/jdiCodes.js +3 -1
  37. package/dist/cjs/utils/jdiCodes.js.map +1 -1
  38. package/dist/cjs/utils/navigation.js +21 -0
  39. package/dist/cjs/utils/navigation.js.map +1 -0
  40. package/dist/esm/components/DatakeenSession.js +8 -2
  41. package/dist/esm/components/DatakeenSession.js.map +1 -1
  42. package/dist/esm/components/session/EndFlow.js +3 -2
  43. package/dist/esm/components/session/EndFlow.js.map +1 -1
  44. package/dist/esm/components/session/MobileRedirect.js +4 -0
  45. package/dist/esm/components/session/MobileRedirect.js.map +1 -1
  46. package/dist/esm/components/session/PdfGeneration.js +25 -19
  47. package/dist/esm/components/session/PdfGeneration.js.map +1 -1
  48. package/dist/esm/components/session/UserInputForm/CustomFormFields.js +0 -10
  49. package/dist/esm/components/session/UserInputForm/CustomFormFields.js.map +1 -1
  50. package/dist/esm/components/session/UserInputForm.js +2 -2
  51. package/dist/esm/components/session/UserInputForm.js.map +1 -1
  52. package/dist/esm/components/signature-electronic/SignedDocumentPreview.js +12 -14
  53. package/dist/esm/components/signature-electronic/SignedDocumentPreview.js.map +1 -1
  54. package/dist/esm/components/template/ExternalVerificationNodeHandler.js +67 -4
  55. package/dist/esm/components/template/ExternalVerificationNodeHandler.js.map +1 -1
  56. package/dist/esm/hooks/useSession.js +3 -3
  57. package/dist/esm/hooks/useSession.js.map +1 -1
  58. package/dist/esm/hooks/useUserInputForm.js +13 -9
  59. package/dist/esm/hooks/useUserInputForm.js.map +1 -1
  60. package/dist/esm/i18n/en.json.js +4 -1
  61. package/dist/esm/i18n/en.json.js.map +1 -1
  62. package/dist/esm/i18n/fr.json.js +4 -1
  63. package/dist/esm/i18n/fr.json.js.map +1 -1
  64. package/dist/esm/services/api.js +5 -0
  65. package/dist/esm/services/api.js.map +1 -1
  66. package/dist/esm/services/auditTrailService.js +19 -9
  67. package/dist/esm/services/auditTrailService.js.map +1 -1
  68. package/dist/esm/services/integrationService.js +2 -0
  69. package/dist/esm/services/integrationService.js.map +1 -1
  70. package/dist/esm/services/sessionCredentials.js +20 -0
  71. package/dist/esm/services/sessionCredentials.js.map +1 -0
  72. package/dist/esm/types/session.js.map +1 -1
  73. package/dist/esm/utils/customFieldIds.js +50 -0
  74. package/dist/esm/utils/customFieldIds.js.map +1 -0
  75. package/dist/esm/utils/jdiCodes.js +3 -1
  76. package/dist/esm/utils/jdiCodes.js.map +1 -1
  77. package/dist/esm/utils/navigation.js +19 -0
  78. package/dist/esm/utils/navigation.js.map +1 -0
  79. package/dist/types/components/DatakeenSession.d.ts +1 -1
  80. package/dist/types/hooks/useSession.d.ts +1 -1
  81. package/dist/types/types/session.d.ts +3 -0
  82. package/docs/SESSION_TOKEN_MIGRATION.md +52 -0
  83. package/package.json +1 -1
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ /** Memory-only credentials, bound to a session and never sent to unrelated URLs. */
4
+ var credentials = new Map();
5
+ function setSessionCredential(sessionId, token) {
6
+ if (token)
7
+ credentials.set(sessionId, token);
8
+ else
9
+ credentials.delete(sessionId);
10
+ }
11
+ function getSessionCredential(sessionId) {
12
+ return credentials.get(sessionId);
13
+ }
14
+ function credentialForRequest(url) {
15
+ if (/^[a-z]+:/i.test(url) || url.startsWith('//'))
16
+ return undefined;
17
+ var match = url.match(/^\/?session\/sdk\/([^/?]+)/);
18
+ return match ? credentials.get(decodeURIComponent(match[1])) : undefined;
19
+ }
20
+
21
+ exports.credentialForRequest = credentialForRequest;
22
+ exports.getSessionCredential = getSessionCredential;
23
+ exports.setSessionCredential = setSessionCredential;
24
+ //# sourceMappingURL=sessionCredentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionCredentials.js","sources":["../../../../src/services/sessionCredentials.ts"],"sourcesContent":["/** Memory-only credentials, bound to a session and never sent to unrelated URLs. */\nconst credentials = new Map<string, string>();\nexport function setSessionCredential(sessionId: string, token?: string): void {\n if (token) credentials.set(sessionId, token);\n else credentials.delete(sessionId);\n}\nexport function getSessionCredential(sessionId: string): string | undefined {\n return credentials.get(sessionId);\n}\nexport function credentialForRequest(url: string): string | undefined {\n if (/^[a-z]+:/i.test(url) || url.startsWith('//')) return undefined;\n const match = url.match(/^\\/?session\\/sdk\\/([^/?]+)/);\n return match ? credentials.get(decodeURIComponent(match[1])) : undefined;\n}\n"],"names":[],"mappings":";;AAAA;AACA,IAAM,WAAW,GAAG,IAAI,GAAG,EAAkB;AACvC,SAAU,oBAAoB,CAAC,SAAiB,EAAE,KAAc,EAAA;AACpE,IAAA,IAAI,KAAK;AAAE,QAAA,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC;;AACvC,QAAA,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC;AACpC;AACM,SAAU,oBAAoB,CAAC,SAAiB,EAAA;AACpD,IAAA,OAAO,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC;AACnC;AACM,SAAU,oBAAoB,CAAC,GAAW,EAAA;AAC9C,IAAA,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;AAAE,QAAA,OAAO,SAAS;IACnE,IAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,4BAA4B,CAAC;IACrD,OAAO,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS;AAC1E;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sources":["../../../../src/types/session.ts"],"sourcesContent":["import type React from \"react\";\n\nexport interface SessionConfig {\n selfie?: boolean;\n requireMobile?: boolean;\n}\n\nexport interface DatakeenSessionProps {\n sessionId: string;\n sessionConfig?: SessionConfig;\n apiBaseUrl?: string; // Optional API base URL for dynamic environment configuration\n}\n\nexport interface UseSessionReturn {\n SessionComponent: React.ReactElement;\n}\n\nexport type stepObject = {\n setStep: (step: number, skipHistory?: boolean) => void;\n goBack: () => void;\n /** Retour arrière vers un nœud précis (ex: \"Corriger ma saisie\") :\n * tronque l'historique jusqu'à `targetStep` au lieu de l'empiler. */\n goBackToStep: (targetStep: number) => void;\n goToNextStep: (\n currentNodeId: string,\n template: SessionTemplate,\n handle?: string,\n ) => void;\n /** Avance via le graphe et tronque l'historique au nœud d'arrivée. */\n goToNextStepCommitted: (\n currentNodeId: string,\n template: SessionTemplate,\n handle?: string,\n ) => void;\n step: number;\n canGoBack: boolean;\n};\n\nexport interface ProcessingStep {\n title: string;\n subtitle: string;\n hasError?: boolean;\n}\n\nexport type ConditionTokenType = \"variable\" | \"control\" | \"operator\" | \"input\";\n\nexport interface ConditionToken {\n type: ConditionTokenType;\n value: string;\n label?: string;\n sourceNodeId?: string;\n}\n\n/**\n * Type for custom field value types\n */\nexport type CustomFieldValueType =\n | \"text\"\n | \"enum\"\n | \"number\"\n | \"boolean\"\n | \"date\"\n | \"email\"\n | \"address\"\n | \"list\";\n\n/**\n * Display formats supported for date fields (top-level and list columns).\n */\nexport type DateDisplayFormat = \"dd/mm/yyyy\" | \"mm/dd/yyyy\" | \"yyyy-mm-dd\";\n\nexport const DEFAULT_DATE_DISPLAY_FORMAT: DateDisplayFormat = \"dd/mm/yyyy\";\n\n/**\n * Column definition for a list-type custom field\n */\nexport interface ListColumn {\n label: string;\n type: \"text\" | \"enum\" | \"date\" | \"email\";\n options?: string[]; // required when type === 'enum'\n placeholder?: string; // text/email/date — shown to the end user\n regex?: string; // text — JS regex source (without slashes)\n regexErrorMessage?: string; // text — message displayed when regex fails\n dateFormat?: DateDisplayFormat; // date — display format expected\n}\n\n/**\n * Interface for custom field definition\n */\nexport interface CustomField {\n id: string;\n label: string;\n placeholder?: string;\n description?: string;\n valueType: CustomFieldValueType;\n enumOptions?: string[];\n listColumns?: ListColumn[]; // for list type: column definitions\n minRows?: number; // for list type: minimum rows expected\n required?: boolean;\n regex?: string; // text — JS regex source (without slashes)\n regexErrorMessage?: string; // text — message displayed when regex fails\n dateFormat?: DateDisplayFormat; // date — display format expected\n lockedFromApi?: boolean;\n editableIfMissing?: boolean; // soft lock — éditable si la valeur API est absente\n userInputKey?: string;\n}\n\n/**\n * Interface for session template node\n */\nexport interface SessionTemplateNode {\n id: string;\n type: string;\n title: string;\n description: string;\n informationType?:\n | \"identity\"\n | \"identity-legal\"\n | \"contact\"\n | \"address\"\n | \"nationality\"\n | \"custom\";\n position: {\n x: number;\n y: number;\n };\n options: unknown[];\n selectedOptions: string[];\n requiredDocumentType?: string;\n isRequired: boolean;\n order: number;\n optionalFields?: string[];\n requiredFields?: string[];\n pageTitle?: string;\n pageDescription?: string;\n // Properties for document-collection node type\n allowedDocumentTypes?: Array<{\n id: string;\n name: string;\n /**\n * Optional side information coming from the template.\n * When provided and equals to two (\"two\", 2, \"double\", \"recto-verso\"),\n * consumers should allow uploading two sides (front/back).\n */\n side?: string | number;\n }>;\n allowedAddingMethods?: string[];\n introductionPage?: {\n title?: string;\n description?: string;\n };\n documentSelection?: {\n title?: string;\n description?: string;\n };\n // Start node specific properties\n welcomeTitle?: string;\n welcomeSubtitle?: string;\n welcomeDescription?: string;\n welcomeImage?: string;\n qrCodeTitle?: string;\n qrCodeDescription?: string;\n showLegacyCGU?: boolean; // default: true — rétrocompatibilité\n\n // Legal consent node specific properties\n consentDescription?: string;\n consentDescription2?: string;\n cguUrl?: string;\n privacyPolicyUrl?: string;\n cguLinkLabel?: string;\n privacyLinkLabel?: string;\n checkboxText?: string;\n // Identity control specific properties\n automaticPhotoCapture?: boolean;\n nfcEnabled?: boolean;\n // \"nfcAndApi\" reste accepté pour rétrocompat (parcours legacy) ; à normaliser à la lecture.\n nfcMode?: \"nfcOnly\" | \"nfcOrPhoto\" | \"nfcAndApi\";\n acceptedCountries?: AcceptedCountry[];\n // End node specific properties\n callbackURL?: string | null;\n // Résultat forcé par le nœud `end` (configuré dans le builder). Sert au SDK à\n // afficher la variante « erreur conservée » de l'écran de fin quand la branche\n // fausse d'une condition pointe directement vers un end non_compliant.\n expectedResult?: string;\n // Condition node specific properties\n conditionExpression?: string;\n conditionTokens?: ConditionToken[];\n conditionFalseErrorMessage?: string;\n conditionMaxRetries?: number;\n conditionMaxRetryAction?: \"end-journey\" | \"force-true\";\n // Undefined is treated as \"loop\" by the SDK for legacy templates.\n conditionFalseMode?: \"retry\" | \"loop\";\n\n // External verification specific properties\n targetApi?: \"INSEE\" | \"INPI_RBE\";\n /** INPI_RBE only. Opt-in: retry against the RNA (associations) when the RNE finds nothing. */\n rnaFallbackEnabled?: boolean;\n referenceNodeId?: string;\n referenceNodeType?:\n | \"information-input\"\n | \"document-collection\"\n | \"identity-control\";\n referenceField?: \"siren\" | \"siret\";\n referenceVariable?: string;\n // Custom form fields (for information-input with type 'custom')\n customFields?: CustomField[];\n\n // Electronic signature specific properties\n templateId?: string;\n external_id?: string;\n /** Écran affiché après la signature. Valeurs vides : textes standard traduits. */\n signedScreenTitle?: string;\n signedScreenDescription?: string;\n signedScreenDownloadHint?: string;\n fieldMappings?: Array<{\n sourceFieldId: string;\n label: string;\n docusealType: string;\n readonly: boolean;\n role?: string;\n sourceNodeId?: string;\n }>;\n /** If set, the generated PDF from this upstream pdf-generation node will be used as the document to sign */\n sourcePdfNodeId?: string;\n\n // PDF generation node specific properties\n // Note: htmlTemplate is intentionally NOT included — it is server-side only and never sent to the client\n pdfMode?: \"upload\" | \"html-template\";\n sourceNodeIds?: string[];\n\n // retry properties\n allowResubmission: boolean;\n maxResubmissionAttempts?: number;\n\n /**\n * Paramétrable par nœud depuis le journey builder : autorise ou non le\n * bouton \"Poursuivre quand même\" en cas d'échec. S'applique aux nœuds\n * identity-control, document-collection, biometric-capture (selfie/video)\n * et external-verification. Absent/true = comportement actuel inchangé\n * (bouton proposé), false = masqué.\n *\n * Déprécié pour controle-jdi/identity-control quand une sortie \"false\" est\n * câblée : dans ce cas le routage automatique (blockingCodes +\n * maxResubmissionAction) prend le relais et ce bouton disparaît. Reste lu\n * tel quel pour les nœuds legacy sans sortie \"false\" (rétrocompat).\n */\n allowContinueOnFailure?: boolean;\n\n /**\n * Codes JDI exacts considérés comme bloquants pour ce nœud controle-jdi\n * (déclenchent la boucle de resoumission). Absent = DEFAULT_BLOCKING_CODES\n * (utils/jdiCodes.ts) — comportement legacy inchangé. Le code 8.0 est\n * toujours bloquant indépendamment de cette liste.\n */\n blockingCodes?: string[];\n\n /**\n * Action suivie quand les resoumissions sont épuisées sur un code bloquant\n * ET qu'une sortie \"false\" est câblée sur ce nœud controle-jdi (miroir de\n * conditionMaxRetryAction sur le nœud condition). Défaut \"end-journey\".\n */\n maxResubmissionAction?: \"end-journey\" | \"force-true\";\n}\n\n/**\n * Interface for accepted countries\n */\nexport interface AcceptedCountry {\n code: string;\n documents: {\n passport: string[];\n idCard: string[];\n driverLicense: string[];\n residencePermit: string[];\n pinkDriverLicense: string[];\n };\n}\n\n/**\n * Interface for session template edge\n */\nexport interface SessionTemplateEdge {\n id: string;\n source: string;\n target: string;\n sourceHandle?: string;\n targetHandle?: string;\n conditionValue?: string;\n}\n\n/**\n * Interface for platform information\n */\nexport interface PlatformInfo {\n mobile: boolean;\n desktop: boolean;\n backoffice: boolean;\n}\n\n/**\n * Interface for session template\n */\n/** Clé du catalogue d'icônes des écrans de fin (mappée vers lucide-react au rendu). */\nexport type EndScreenIcon =\n | \"thanks\"\n | \"send\"\n | \"mail\"\n | \"check\"\n | \"clock\"\n | \"shield\"\n | \"user\"\n | \"info\";\n\nexport interface EndScreenItem {\n icon: EndScreenIcon;\n text: string;\n}\n\nexport interface EndScreenCase {\n title?: string;\n items: EndScreenItem[];\n}\n\n/** Textes de fin personnalisés (config globale du journey). Vide → fallback standard. */\nexport interface EndScreenConfig {\n compliant?: EndScreenCase;\n nonCompliant?: EndScreenCase;\n}\n\nexport interface SessionTemplate {\n id: string;\n name: string;\n description: string;\n version: string;\n languages: string[];\n nodes: SessionTemplateNode[];\n edges: SessionTemplateEdge[];\n groupId: string;\n userId: string | null;\n created_at: string;\n updated_at: string;\n platforms?: PlatformInfo;\n logo?: string;\n showQRCode?: boolean;\n buttonBgColor?: string;\n buttonTextColor?: string;\n endScreenConfig?: EndScreenConfig;\n}\n\n/**\n * Interface for session data\n */\nexport interface SessionData {\n id: string;\n userId: string | null;\n token: string;\n templateId: string;\n templateKey: string;\n expireTime: number;\n status: string;\n result: Record<string, unknown>;\n landingPage: unknown;\n withSelfie: boolean | null;\n groupId: string | null;\n userInput: Record<string, unknown>;\n contactInfo?: {\n email: string;\n phoneNumber: string;\n };\n callbackURL?: string | null;\n webhookURL: string;\n analysisTemplateId: string | null;\n userAgent: unknown[];\n mobile: boolean;\n analysisId: string | null;\n currentStep?: number;\n nfcStatus?: \"pending\" | \"opened\" | \"completed\" | \"skipped\" | \"failed\";\n createdAt: string;\n updatedAt: string;\n auditTrail: unknown[];\n user: unknown | null;\n analysis: unknown[];\n documents: unknown[];\n template: SessionTemplate;\n retryCounts?: Record<string, number>; // nodeId -> retry count\n}\n\nexport interface ClientInfo {\n ip?: string;\n location?: string;\n device: string;\n browser: string;\n os: string;\n}\n"],"names":[],"mappings":";;AAuEO,IAAM,2BAA2B,GAAsB;;;;"}
1
+ {"version":3,"file":"session.js","sources":["../../../../src/types/session.ts"],"sourcesContent":["import type React from \"react\";\n\nexport interface SessionConfig {\n sessionToken?: string;\n selfie?: boolean;\n requireMobile?: boolean;\n}\n\nexport interface DatakeenSessionProps {\n /** Session credential returned by server-side session creation. Required by secured backends. */\n sessionToken?: string;\n sessionId: string;\n sessionConfig?: SessionConfig;\n apiBaseUrl?: string; // Optional API base URL for dynamic environment configuration\n}\n\nexport interface UseSessionReturn {\n SessionComponent: React.ReactElement;\n}\n\nexport type stepObject = {\n setStep: (step: number, skipHistory?: boolean) => void;\n goBack: () => void;\n /** Retour arrière vers un nœud précis (ex: \"Corriger ma saisie\") :\n * tronque l'historique jusqu'à `targetStep` au lieu de l'empiler. */\n goBackToStep: (targetStep: number) => void;\n goToNextStep: (\n currentNodeId: string,\n template: SessionTemplate,\n handle?: string,\n ) => void;\n /** Avance via le graphe et tronque l'historique au nœud d'arrivée. */\n goToNextStepCommitted: (\n currentNodeId: string,\n template: SessionTemplate,\n handle?: string,\n ) => void;\n step: number;\n canGoBack: boolean;\n};\n\nexport interface ProcessingStep {\n title: string;\n subtitle: string;\n hasError?: boolean;\n}\n\nexport type ConditionTokenType = \"variable\" | \"control\" | \"operator\" | \"input\";\n\nexport interface ConditionToken {\n type: ConditionTokenType;\n value: string;\n label?: string;\n sourceNodeId?: string;\n}\n\n/**\n * Type for custom field value types\n */\nexport type CustomFieldValueType =\n | \"text\"\n | \"enum\"\n | \"number\"\n | \"boolean\"\n | \"date\"\n | \"email\"\n | \"address\"\n | \"list\";\n\n/**\n * Display formats supported for date fields (top-level and list columns).\n */\nexport type DateDisplayFormat = \"dd/mm/yyyy\" | \"mm/dd/yyyy\" | \"yyyy-mm-dd\";\n\nexport const DEFAULT_DATE_DISPLAY_FORMAT: DateDisplayFormat = \"dd/mm/yyyy\";\n\n/**\n * Column definition for a list-type custom field\n */\nexport interface ListColumn {\n label: string;\n type: \"text\" | \"enum\" | \"date\" | \"email\";\n options?: string[]; // required when type === 'enum'\n placeholder?: string; // text/email/date — shown to the end user\n regex?: string; // text — JS regex source (without slashes)\n regexErrorMessage?: string; // text — message displayed when regex fails\n dateFormat?: DateDisplayFormat; // date — display format expected\n}\n\n/**\n * Interface for custom field definition\n */\nexport interface CustomField {\n id: string;\n label: string;\n placeholder?: string;\n description?: string;\n valueType: CustomFieldValueType;\n enumOptions?: string[];\n listColumns?: ListColumn[]; // for list type: column definitions\n minRows?: number; // for list type: minimum rows expected\n required?: boolean;\n regex?: string; // text — JS regex source (without slashes)\n regexErrorMessage?: string; // text — message displayed when regex fails\n dateFormat?: DateDisplayFormat; // date — display format expected\n lockedFromApi?: boolean;\n editableIfMissing?: boolean; // soft lock — éditable si la valeur API est absente\n userInputKey?: string;\n}\n\n/**\n * Interface for session template node\n */\nexport interface SessionTemplateNode {\n id: string;\n type: string;\n title: string;\n description: string;\n informationType?:\n | \"identity\"\n | \"identity-legal\"\n | \"contact\"\n | \"address\"\n | \"nationality\"\n | \"custom\";\n position: {\n x: number;\n y: number;\n };\n options: unknown[];\n selectedOptions: string[];\n requiredDocumentType?: string;\n isRequired: boolean;\n order: number;\n optionalFields?: string[];\n requiredFields?: string[];\n pageTitle?: string;\n pageDescription?: string;\n // Properties for document-collection node type\n allowedDocumentTypes?: Array<{\n id: string;\n name: string;\n /**\n * Optional side information coming from the template.\n * When provided and equals to two (\"two\", 2, \"double\", \"recto-verso\"),\n * consumers should allow uploading two sides (front/back).\n */\n side?: string | number;\n }>;\n allowedAddingMethods?: string[];\n introductionPage?: {\n title?: string;\n description?: string;\n };\n documentSelection?: {\n title?: string;\n description?: string;\n };\n // Start node specific properties\n welcomeTitle?: string;\n welcomeSubtitle?: string;\n welcomeDescription?: string;\n welcomeImage?: string;\n qrCodeTitle?: string;\n qrCodeDescription?: string;\n showLegacyCGU?: boolean; // default: true — rétrocompatibilité\n\n // Legal consent node specific properties\n consentDescription?: string;\n consentDescription2?: string;\n cguUrl?: string;\n privacyPolicyUrl?: string;\n cguLinkLabel?: string;\n privacyLinkLabel?: string;\n checkboxText?: string;\n // Identity control specific properties\n automaticPhotoCapture?: boolean;\n nfcEnabled?: boolean;\n // \"nfcAndApi\" reste accepté pour rétrocompat (parcours legacy) ; à normaliser à la lecture.\n nfcMode?: \"nfcOnly\" | \"nfcOrPhoto\" | \"nfcAndApi\";\n acceptedCountries?: AcceptedCountry[];\n // End node specific properties\n callbackURL?: string | null;\n // Résultat forcé par le nœud `end` (configuré dans le builder). Sert au SDK à\n // afficher la variante « erreur conservée » de l'écran de fin quand la branche\n // fausse d'une condition pointe directement vers un end non_compliant.\n expectedResult?: string;\n // Condition node specific properties\n conditionExpression?: string;\n conditionTokens?: ConditionToken[];\n conditionFalseErrorMessage?: string;\n conditionMaxRetries?: number;\n conditionMaxRetryAction?: \"end-journey\" | \"force-true\";\n // Undefined is treated as \"loop\" by the SDK for legacy templates.\n conditionFalseMode?: \"retry\" | \"loop\";\n\n // External verification specific properties\n targetApi?: \"INSEE\" | \"INPI_RBE\";\n /** INPI_RBE only. Opt-in: retry against the RNA (associations) when the RNE finds nothing. */\n rnaFallbackEnabled?: boolean;\n referenceNodeId?: string;\n referenceNodeType?:\n | \"information-input\"\n | \"document-collection\"\n | \"identity-control\";\n referenceField?: \"siren\" | \"siret\";\n referenceVariable?: string;\n // Custom form fields (for information-input with type 'custom')\n customFields?: CustomField[];\n\n // Electronic signature specific properties\n templateId?: string;\n external_id?: string;\n /** Écran affiché après la signature. Valeurs vides : textes standard traduits. */\n signedScreenTitle?: string;\n signedScreenDescription?: string;\n signedScreenDownloadHint?: string;\n fieldMappings?: Array<{\n sourceFieldId: string;\n label: string;\n docusealType: string;\n readonly: boolean;\n role?: string;\n sourceNodeId?: string;\n }>;\n /** If set, the generated PDF from this upstream pdf-generation node will be used as the document to sign */\n sourcePdfNodeId?: string;\n\n // PDF generation node specific properties\n // Note: htmlTemplate is intentionally NOT included — it is server-side only and never sent to the client\n pdfMode?: \"upload\" | \"html-template\";\n sourceNodeIds?: string[];\n\n // retry properties\n allowResubmission: boolean;\n maxResubmissionAttempts?: number;\n\n /**\n * Paramétrable par nœud depuis le journey builder : autorise ou non le\n * bouton \"Poursuivre quand même\" en cas d'échec. S'applique aux nœuds\n * identity-control, document-collection, biometric-capture (selfie/video)\n * et external-verification. Absent/true = comportement actuel inchangé\n * (bouton proposé), false = masqué.\n *\n * Déprécié pour controle-jdi/identity-control quand une sortie \"false\" est\n * câblée : dans ce cas le routage automatique (blockingCodes +\n * maxResubmissionAction) prend le relais et ce bouton disparaît. Reste lu\n * tel quel pour les nœuds legacy sans sortie \"false\" (rétrocompat).\n */\n allowContinueOnFailure?: boolean;\n\n /**\n * Codes JDI exacts considérés comme bloquants pour ce nœud controle-jdi\n * (déclenchent la boucle de resoumission). Absent = DEFAULT_BLOCKING_CODES\n * (utils/jdiCodes.ts) — comportement legacy inchangé. Le code 8.0 est\n * toujours bloquant indépendamment de cette liste.\n */\n blockingCodes?: string[];\n\n /**\n * Action suivie quand les resoumissions sont épuisées sur un code bloquant\n * ET qu'une sortie \"false\" est câblée sur ce nœud controle-jdi (miroir de\n * conditionMaxRetryAction sur le nœud condition). Défaut \"end-journey\".\n */\n maxResubmissionAction?: \"end-journey\" | \"force-true\";\n}\n\n/**\n * Interface for accepted countries\n */\nexport interface AcceptedCountry {\n code: string;\n documents: {\n passport: string[];\n idCard: string[];\n driverLicense: string[];\n residencePermit: string[];\n pinkDriverLicense: string[];\n };\n}\n\n/**\n * Interface for session template edge\n */\nexport interface SessionTemplateEdge {\n id: string;\n source: string;\n target: string;\n sourceHandle?: string;\n targetHandle?: string;\n conditionValue?: string;\n}\n\n/**\n * Interface for platform information\n */\nexport interface PlatformInfo {\n mobile: boolean;\n desktop: boolean;\n backoffice: boolean;\n}\n\n/**\n * Interface for session template\n */\n/** Clé du catalogue d'icônes des écrans de fin (mappée vers lucide-react au rendu). */\nexport type EndScreenIcon =\n | \"thanks\"\n | \"send\"\n | \"mail\"\n | \"check\"\n | \"clock\"\n | \"shield\"\n | \"user\"\n | \"info\";\n\nexport interface EndScreenItem {\n icon: EndScreenIcon;\n text: string;\n}\n\nexport interface EndScreenCase {\n title?: string;\n items: EndScreenItem[];\n}\n\n/** Textes de fin personnalisés (config globale du journey). Vide → fallback standard. */\nexport interface EndScreenConfig {\n compliant?: EndScreenCase;\n nonCompliant?: EndScreenCase;\n}\n\nexport interface SessionTemplate {\n id: string;\n name: string;\n description: string;\n version: string;\n languages: string[];\n nodes: SessionTemplateNode[];\n edges: SessionTemplateEdge[];\n groupId: string;\n userId: string | null;\n created_at: string;\n updated_at: string;\n platforms?: PlatformInfo;\n logo?: string;\n showQRCode?: boolean;\n buttonBgColor?: string;\n buttonTextColor?: string;\n endScreenConfig?: EndScreenConfig;\n}\n\n/**\n * Interface for session data\n */\nexport interface SessionData {\n id: string;\n userId: string | null;\n token: string;\n templateId: string;\n templateKey: string;\n expireTime: number;\n status: string;\n result: Record<string, unknown>;\n landingPage: unknown;\n withSelfie: boolean | null;\n groupId: string | null;\n userInput: Record<string, unknown>;\n contactInfo?: {\n email: string;\n phoneNumber: string;\n };\n callbackURL?: string | null;\n webhookURL: string;\n analysisTemplateId: string | null;\n userAgent: unknown[];\n mobile: boolean;\n analysisId: string | null;\n currentStep?: number;\n nfcStatus?: \"pending\" | \"opened\" | \"completed\" | \"skipped\" | \"failed\";\n createdAt: string;\n updatedAt: string;\n auditTrail: unknown[];\n user: unknown | null;\n analysis: unknown[];\n documents: unknown[];\n template: SessionTemplate;\n retryCounts?: Record<string, number>; // nodeId -> retry count\n}\n\nexport interface ClientInfo {\n ip?: string;\n location?: string;\n device: string;\n browser: string;\n os: string;\n}\n"],"names":[],"mappings":";;AA0EO,IAAM,2BAA2B,GAAsB;;;;"}
@@ -0,0 +1,52 @@
1
+ 'use strict';
2
+
3
+ var tslib_es6 = require('../node_modules/tslib/tslib.es6.js');
4
+
5
+ /** Séparateur du suffixe ajouté à un identifiant en collision. */
6
+ var DUPLICATE_SUFFIX = "__dup";
7
+ /**
8
+ * Garantit l'unicité des identifiants de champs d'un formulaire personnalisé.
9
+ *
10
+ * Tout l'état du formulaire est indexé par `field.id` (valeur saisie, erreur,
11
+ * erreurs de cellule). Deux champs partageant un identifiant partagent donc
12
+ * leur valeur : taper dans l'un remplit l'autre, et un seul des deux est
13
+ * finalement transmis.
14
+ *
15
+ * Le builder a longtemps dérivé l'identifiant de `Date.now()` seul, sans
16
+ * composante aléatoire : deux champs enregistrés dans la même milliseconde
17
+ * (double clic, ajout programmatique, import) se retrouvent avec le même.
18
+ * Les parcours déjà publiés portent ces doublons, que le SDK doit servir sans
19
+ * attendre une ré-édition côté backoffice.
20
+ *
21
+ * La première occurrence garde son identifiant — les conditions de parcours et
22
+ * les sessions existantes qui le référencent continuent de fonctionner. Seules
23
+ * les suivantes reçoivent un suffixe déterministe, donc stable d'un rendu à
24
+ * l'autre. La clé métier envoyée à l'API (`userInputKey` ou le label
25
+ * normalisé) n'est pas touchée.
26
+ */
27
+ function dedupeCustomFieldIds(fields) {
28
+ if (!fields || fields.length === 0)
29
+ return [];
30
+ var seen = new Set();
31
+ var hasDuplicate = false;
32
+ var deduped = fields.map(function (field, index) {
33
+ var id = field.id;
34
+ if (!seen.has(id)) {
35
+ seen.add(id);
36
+ return field;
37
+ }
38
+ hasDuplicate = true;
39
+ var candidate = "".concat(id).concat(DUPLICATE_SUFFIX).concat(index);
40
+ // Un parcours peut déjà contenir un identifiant homonyme du suffixe.
41
+ while (seen.has(candidate))
42
+ candidate = "".concat(candidate, "_");
43
+ seen.add(candidate);
44
+ return tslib_es6.__assign(tslib_es6.__assign({}, field), { id: candidate });
45
+ });
46
+ // Référence stable quand il n'y a rien à corriger : le cas normal ne doit pas
47
+ // invalider les mémos qui dépendent de la liste.
48
+ return hasDuplicate ? deduped : fields;
49
+ }
50
+
51
+ exports.dedupeCustomFieldIds = dedupeCustomFieldIds;
52
+ //# sourceMappingURL=customFieldIds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customFieldIds.js","sources":["../../../../src/utils/customFieldIds.ts"],"sourcesContent":["import type { CustomField } from \"../types/session\";\n\n/** Séparateur du suffixe ajouté à un identifiant en collision. */\nconst DUPLICATE_SUFFIX = \"__dup\";\n\n/**\n * Garantit l'unicité des identifiants de champs d'un formulaire personnalisé.\n *\n * Tout l'état du formulaire est indexé par `field.id` (valeur saisie, erreur,\n * erreurs de cellule). Deux champs partageant un identifiant partagent donc\n * leur valeur : taper dans l'un remplit l'autre, et un seul des deux est\n * finalement transmis.\n *\n * Le builder a longtemps dérivé l'identifiant de `Date.now()` seul, sans\n * composante aléatoire : deux champs enregistrés dans la même milliseconde\n * (double clic, ajout programmatique, import) se retrouvent avec le même.\n * Les parcours déjà publiés portent ces doublons, que le SDK doit servir sans\n * attendre une ré-édition côté backoffice.\n *\n * La première occurrence garde son identifiant — les conditions de parcours et\n * les sessions existantes qui le référencent continuent de fonctionner. Seules\n * les suivantes reçoivent un suffixe déterministe, donc stable d'un rendu à\n * l'autre. La clé métier envoyée à l'API (`userInputKey` ou le label\n * normalisé) n'est pas touchée.\n */\nexport function dedupeCustomFieldIds(\n fields: CustomField[] | undefined,\n): CustomField[] {\n if (!fields || fields.length === 0) return [];\n\n const seen = new Set<string>();\n let hasDuplicate = false;\n\n const deduped = fields.map((field, index) => {\n const id = field.id;\n if (!seen.has(id)) {\n seen.add(id);\n return field;\n }\n\n hasDuplicate = true;\n let candidate = `${id}${DUPLICATE_SUFFIX}${index}`;\n // Un parcours peut déjà contenir un identifiant homonyme du suffixe.\n while (seen.has(candidate)) candidate = `${candidate}_`;\n seen.add(candidate);\n return { ...field, id: candidate };\n });\n\n // Référence stable quand il n'y a rien à corriger : le cas normal ne doit pas\n // invalider les mémos qui dépendent de la liste.\n return hasDuplicate ? deduped : fields;\n}\n"],"names":["__assign"],"mappings":";;;;AAEA;AACA,IAAM,gBAAgB,GAAG,OAAO;AAEhC;;;;;;;;;;;;;;;;;;;AAmBG;AACG,SAAU,oBAAoB,CAClC,MAAiC,EAAA;AAEjC,IAAA,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,EAAE;AAE7C,IAAA,IAAM,IAAI,GAAG,IAAI,GAAG,EAAU;IAC9B,IAAI,YAAY,GAAG,KAAK;IAExB,IAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,UAAC,KAAK,EAAE,KAAK,EAAA;AACtC,QAAA,IAAM,EAAE,GAAG,KAAK,CAAC,EAAE;QACnB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;AACjB,YAAA,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACZ,YAAA,OAAO,KAAK;QACd;QAEA,YAAY,GAAG,IAAI;QACnB,IAAI,SAAS,GAAG,EAAA,CAAA,MAAA,CAAG,EAAE,SAAG,gBAAgB,CAAA,CAAA,MAAA,CAAG,KAAK,CAAE;;AAElD,QAAA,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;AAAE,YAAA,SAAS,GAAG,EAAA,CAAA,MAAA,CAAG,SAAS,EAAA,GAAA,CAAG;AACvD,QAAA,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC;AACnB,QAAA,OAAAA,kBAAA,CAAAA,kBAAA,CAAA,EAAA,EAAY,KAAK,CAAA,EAAA,EAAE,EAAE,EAAE,SAAS,EAAA,CAAA;AAClC,IAAA,CAAC,CAAC;;;IAIF,OAAO,YAAY,GAAG,OAAO,GAAG,MAAM;AACxC;;;;"}
@@ -13,6 +13,9 @@
13
13
  *
14
14
  * Miroir exact du catalogue frontend (app-frontend/src/utils/jdiCodes.ts) —
15
15
  * les deux listes doivent rester synchronisées.
16
+ *
17
+ * 2026-09-03 : 7.1 retiré (décision produit, cf. scripts/remediate-controle-jdi-blocking-codes.ts
18
+ * côté app-backend pour le nettoyage des configs `blockingCodes` déjà sauvegardées).
16
19
  */
17
20
  var DEFAULT_BLOCKING_CODES = [
18
21
  '2.0',
@@ -24,7 +27,6 @@ var DEFAULT_BLOCKING_CODES = [
24
27
  '2.6',
25
28
  '3.0',
26
29
  '7.0',
27
- '7.1',
28
30
  ];
29
31
  /**
30
32
  * Codes toujours bloquants, quelle que soit la configuration du nœud.
@@ -1 +1 @@
1
- {"version":3,"file":"jdiCodes.js","sources":["../../../../src/utils/jdiCodes.ts"],"sourcesContent":["/**\n * Codes JDI (Justificatif D'Identité) considérés comme \"bloquants\" pour le\n * nœud controle-jdi, c'est-à-dire déclenchant la boucle de resoumission\n * (écran d'erreur + éventuel réessai) plutôt qu'un passage silencieux.\n *\n * Historiquement cette liste était hardcodée en dur (inversée) dans\n * JDIProcessing.tsx / DocumentVideoProcessing.tsx / DocumentProcessing.tsx :\n * `isSuccess = [\"1.\", \"4.\", \"5.\", \"6.\", \"9.\"].some(prefix => code.startsWith(prefix))`.\n * Elle est désormais configurable par nœud via `node.blockingCodes`, avec\n * cette liste comme valeur par défaut pour préserver le comportement legacy.\n *\n * Miroir exact du catalogue frontend (app-frontend/src/utils/jdiCodes.ts) —\n * les deux listes doivent rester synchronisées.\n */\nexport const DEFAULT_BLOCKING_CODES: string[] = [\n '2.0',\n '2.1',\n '2.2',\n '2.3',\n '2.4',\n '2.5',\n '2.6',\n '3.0',\n '7.0',\n '7.1',\n];\n\n/**\n * Codes toujours bloquants, quelle que soit la configuration du nœud.\n * 8.0 (document trouvé dans la base de spécimens frauduleux) ne doit jamais\n * pouvoir être désactivé — cf. DocumentCheck.tsx `isRetryAllowed` historique\n * (`errorCode?.startsWith(\"8\")` bloque déjà totalement le retry).\n */\nexport const ALWAYS_BLOCKING_CODES: string[] = ['8.0'];\n\n/**\n * Détermine si un code JDI doit être traité comme bloquant pour un nœud\n * controle-jdi donné.\n *\n * - `configured` absent (nœud legacy, jamais reconfiguré) → applique\n * DEFAULT_BLOCKING_CODES (comportement inchangé).\n * - `configured` présent (même vide) → correspondance exacte uniquement\n * (pas de préfixe : \"4.0\" dans la liste ne bloque pas \"4.1\"/\"4.2\").\n * - Les codes de ALWAYS_BLOCKING_CODES bloquent toujours, indépendamment\n * de `configured`.\n */\nexport function isBlockingCode(code: string, configured?: string[]): boolean {\n if (ALWAYS_BLOCKING_CODES.includes(code)) {\n return true;\n }\n const list = configured ?? DEFAULT_BLOCKING_CODES;\n return list.includes(code);\n}\n"],"names":[],"mappings":";;AAAA;;;;;;;;;;;;;AAaG;AACI,IAAM,sBAAsB,GAAa;IAC9C,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;;AAGP;;;;;AAKG;AACI,IAAM,qBAAqB,GAAa,CAAC,KAAK;AAErD;;;;;;;;;;AAUG;AACG,SAAU,cAAc,CAAC,IAAY,EAAE,UAAqB,EAAA;AAChE,IAAA,IAAI,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AACxC,QAAA,OAAO,IAAI;IACb;IACA,IAAM,IAAI,GAAG,UAAU,KAAA,IAAA,IAAV,UAAU,KAAA,MAAA,GAAV,UAAU,GAAI,sBAAsB;AACjD,IAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;AAC5B;;;;;;"}
1
+ {"version":3,"file":"jdiCodes.js","sources":["../../../../src/utils/jdiCodes.ts"],"sourcesContent":["/**\n * Codes JDI (Justificatif D'Identité) considérés comme \"bloquants\" pour le\n * nœud controle-jdi, c'est-à-dire déclenchant la boucle de resoumission\n * (écran d'erreur + éventuel réessai) plutôt qu'un passage silencieux.\n *\n * Historiquement cette liste était hardcodée en dur (inversée) dans\n * JDIProcessing.tsx / DocumentVideoProcessing.tsx / DocumentProcessing.tsx :\n * `isSuccess = [\"1.\", \"4.\", \"5.\", \"6.\", \"9.\"].some(prefix => code.startsWith(prefix))`.\n * Elle est désormais configurable par nœud via `node.blockingCodes`, avec\n * cette liste comme valeur par défaut pour préserver le comportement legacy.\n *\n * Miroir exact du catalogue frontend (app-frontend/src/utils/jdiCodes.ts) —\n * les deux listes doivent rester synchronisées.\n *\n * 2026-09-03 : 7.1 retiré (décision produit, cf. scripts/remediate-controle-jdi-blocking-codes.ts\n * côté app-backend pour le nettoyage des configs `blockingCodes` déjà sauvegardées).\n */\nexport const DEFAULT_BLOCKING_CODES: string[] = [\n '2.0',\n '2.1',\n '2.2',\n '2.3',\n '2.4',\n '2.5',\n '2.6',\n '3.0',\n '7.0',\n];\n\n/**\n * Codes toujours bloquants, quelle que soit la configuration du nœud.\n * 8.0 (document trouvé dans la base de spécimens frauduleux) ne doit jamais\n * pouvoir être désactivé — cf. DocumentCheck.tsx `isRetryAllowed` historique\n * (`errorCode?.startsWith(\"8\")` bloque déjà totalement le retry).\n */\nexport const ALWAYS_BLOCKING_CODES: string[] = ['8.0'];\n\n/**\n * Détermine si un code JDI doit être traité comme bloquant pour un nœud\n * controle-jdi donné.\n *\n * - `configured` absent (nœud legacy, jamais reconfiguré) → applique\n * DEFAULT_BLOCKING_CODES (comportement inchangé).\n * - `configured` présent (même vide) → correspondance exacte uniquement\n * (pas de préfixe : \"4.0\" dans la liste ne bloque pas \"4.1\"/\"4.2\").\n * - Les codes de ALWAYS_BLOCKING_CODES bloquent toujours, indépendamment\n * de `configured`.\n */\nexport function isBlockingCode(code: string, configured?: string[]): boolean {\n if (ALWAYS_BLOCKING_CODES.includes(code)) {\n return true;\n }\n const list = configured ?? DEFAULT_BLOCKING_CODES;\n return list.includes(code);\n}\n"],"names":[],"mappings":";;AAAA;;;;;;;;;;;;;;;;AAgBG;AACI,IAAM,sBAAsB,GAAa;IAC9C,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK;;AAGP;;;;;AAKG;AACI,IAAM,qBAAqB,GAAa,CAAC,KAAK;AAErD;;;;;;;;;;AAUG;AACG,SAAU,cAAc,CAAC,IAAY,EAAE,UAAqB,EAAA;AAChE,IAAA,IAAI,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE;AACxC,QAAA,OAAO,IAAI;IACb;IACA,IAAM,IAAI,GAAG,UAAU,KAAA,IAAA,IAAV,UAAU,KAAA,MAAA,GAAV,UAAU,GAAI,sBAAsB;AACjD,IAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;AAC5B;;;;;;"}
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Indirection minimale devant `window.location`.
5
+ *
6
+ * jsdom ≥ 22 déclare `window.location` non-configurable et `location.replace`
7
+ * non-inscriptible : `delete window.location` renvoie `false` et toute
8
+ * réassignation échoue en silence. Une redirection appelée directement sur
9
+ * `window.location` n'est donc plus observable en test — elle se contente
10
+ * d'émettre « Not implemented: navigation » dans la console jsdom, ce qui fait
11
+ * passer un test qui n'a en réalité rien vérifié.
12
+ *
13
+ * Ce module est interne : il n'est pas exporté par `src/index.ts` et ne fait
14
+ * pas partie du contrat public du paquet.
15
+ */
16
+ function replaceLocation(url) {
17
+ window.location.replace(url);
18
+ }
19
+
20
+ exports.replaceLocation = replaceLocation;
21
+ //# sourceMappingURL=navigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.js","sources":["../../../../src/utils/navigation.ts"],"sourcesContent":["/**\n * Indirection minimale devant `window.location`.\n *\n * jsdom ≥ 22 déclare `window.location` non-configurable et `location.replace`\n * non-inscriptible : `delete window.location` renvoie `false` et toute\n * réassignation échoue en silence. Une redirection appelée directement sur\n * `window.location` n'est donc plus observable en test — elle se contente\n * d'émettre « Not implemented: navigation » dans la console jsdom, ce qui fait\n * passer un test qui n'a en réalité rien vérifié.\n *\n * Ce module est interne : il n'est pas exporté par `src/index.ts` et ne fait\n * pas partie du contrat public du paquet.\n */\nexport function replaceLocation(url: string): void {\n window.location.replace(url);\n}\n"],"names":[],"mappings":";;AAAA;;;;;;;;;;;;AAYG;AACG,SAAU,eAAe,CAAC,GAAW,EAAA;AACzC,IAAA,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;AAC9B;;;;"}
@@ -1,6 +1,7 @@
1
1
  import { __assign } from '../node_modules/tslib/tslib.es6.js';
2
2
  import { jsx, jsxs } from 'react/jsx-runtime';
3
- import { useEffect, useRef, useState, useLayoutEffect, useMemo } from 'react';
3
+ import { setSessionCredential } from '../services/sessionCredentials.js';
4
+ import { useLayoutEffect, useEffect, useRef, useState, useMemo } from 'react';
4
5
  import Paper from './ui/Paper.js';
5
6
  import useIsMobile from '../hooks/useIsMobile.js';
6
7
  import PoweredBy from './ui/PoweredBy.js';
@@ -43,7 +44,12 @@ import SessionContent from './session/SessionContent.js';
43
44
  var DatakeenSession = function (_a) {
44
45
  var _b, _c, _d;
45
46
  var _e, _f, _g, _h, _j, _k, _l, _m, _o;
46
- var sessionId = _a.sessionId, sessionConfig = _a.sessionConfig, apiBaseUrl = _a.apiBaseUrl;
47
+ var sessionId = _a.sessionId, sessionToken = _a.sessionToken, sessionConfig = _a.sessionConfig, apiBaseUrl = _a.apiBaseUrl;
48
+ useLayoutEffect(function () {
49
+ var fragmentToken = typeof window !== 'undefined' ? new URLSearchParams(window.location.hash.slice(1)).get('sessionToken') : null;
50
+ setSessionCredential(sessionId, sessionToken || (sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.sessionToken) || fragmentToken || undefined);
51
+ return function () { return setSessionCredential(sessionId); };
52
+ }, [sessionId, sessionToken, sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.sessionToken]);
47
53
  var isMobile = useIsMobile();
48
54
  var _p = useClientInfo(), clientInfo = _p.info, clientInfoLoaded = _p.isLoaded;
49
55
  var currentLanguage = useI18n().currentLanguage;
@@ -1 +1 @@
1
- {"version":3,"file":"DatakeenSession.js","sources":["../../../../src/components/DatakeenSession.tsx"],"sourcesContent":["import { useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { CSSProperties } from \"react\";\nimport type { DatakeenSessionProps } from \"../types/session\";\nimport Paper from \"./ui/Paper\";\nimport useIsMobile from \"../hooks/useIsMobile\";\nimport PoweredBy from \"./ui/PoweredBy\";\nimport SessionExpired from \"./session/SessionExpired\";\nimport { useRouteCSS } from \"../hooks/useRouteCSS\";\nimport { DocumentProvider } from \"../context/DocumentContext\";\nimport { ConfigProvider } from \"../context/ConfigContext\";\nimport { setSessionStore } from \"../context/SessionContext\";\n\n// Hooks\nimport { useSessionData } from \"../hooks/useSessionData\";\nimport { useStepNavigation } from \"../hooks/useStepNavigation\";\nimport { useStepCSS } from \"../hooks/useStepCSS\";\nimport { useTemplateLoader } from \"../hooks/useTemplateLoader\";\nimport { useClientInfo } from \"../hooks/useClientInfo\";\nimport { useI18n } from \"../hooks/useI18n\";\nimport { getOrderedJourneySteps } from \"../services/sessionService\";\n\n// Components\nimport NoSessionIdState from \"./states/NoSessionIdState\";\nimport LoadingState from \"./states/LoadingState\";\nimport ErrorState from \"./states/ErrorState\";\nimport SessionContent from \"./session/SessionContent\";\n\n/**\n * DatakeenSession Component\n *\n * The main component of the Datakeen SDK that manages the multi-step verification flow.\n * This component handles the different steps of the session process, including:\n * - Initial welcome screen\n * - User information collection\n * - Country selection for JDI verification\n * - Mobile redirect when selfie is required\n *\n * The component maintains internal state for the current step and user input data,\n * progressing through the verification journey as the user completes each step.\n *\n * @param {DatakeenSessionProps} props - Component props\n * @param {string} props.sessionId - Unique identifier for the verification session\n * @param {SessionConfig} props.sessionConfig - Configuration for the session (e.g., selfie: true)\n * @param {string} props.apiBaseUrl - Optional API base URL for dynamic environment configuration\n * @returns {JSX.Element} A Paper-wrapped container with the appropriate step component based on current state\n */\nconst DatakeenSession = ({\n sessionId,\n sessionConfig,\n apiBaseUrl,\n}: DatakeenSessionProps) => {\n const isMobile = useIsMobile();\n const { info: clientInfo, isLoaded: clientInfoLoaded } = useClientInfo();\n const { currentLanguage } = useI18n();\n\n // Load base CSS and UI components CSS that are always needed\n useRouteCSS(\"ui-components\");\n\n // Session data management\n const {\n session,\n setSession,\n loading,\n setLoading,\n error,\n isExpired,\n userInput,\n setUserInput,\n contactInfo,\n setContactInfo,\n loadSession,\n handleRetrySession,\n } = useSessionData(sessionId, clientInfo, clientInfoLoaded);\n\n // Step navigation management\n const { step, stepObject, history } = useStepNavigation(\n sessionId,\n session?.status,\n session?.currentStep,\n session?.template,\n );\n\n // Load route-specific CSS based on current step\n useStepCSS(step, session);\n\n // Template loading logic\n useTemplateLoader(step, sessionId, session, loading, setSession, setLoading);\n\n // Load session data on component mount\n useEffect(() => {\n loadSession();\n }, [sessionId, sessionConfig]);\n\n // Update global session store whenever session data changes\n useEffect(() => {\n const sanitizedSessionForGlobalStore = session\n ? {\n ...session,\n token: \"\",\n userInput: {},\n contactInfo: undefined,\n }\n : null;\n\n setSessionStore({\n session: sanitizedSessionForGlobalStore,\n loading,\n error,\n isExpired,\n userInput: {},\n contactInfo: { email: \"\", phoneNumber: \"\" },\n });\n }, [session, loading, error, isExpired]);\n\n // Handle initial step setting when session loads.\n // Quand la session est déjà terminée (ex: rechargement), on positionne le step\n // sur le nœud `end`. On indexe sur getOrderedJourneySteps (start filtré + tri par\n // `order`) — MÊME référentiel que TemplateNodeRenderer — sinon l'index calculé\n // pointe sur un autre nœud que celui rendu (ex: external-verification, qui se\n // ré-exécute et part en boucle).\n // Idempotent : on ne re-navigue que si on n'est pas déjà sur le bon step, et on\n // resserre les dépendances pour ne pas se re-déclencher à chaque mutation de session\n // (userInput, retryCounts, polling…) — ce qui relançait la boucle.\n useEffect(() => {\n if (session?.status !== \"ended\" || !session.template) {\n return;\n }\n const orderedSteps = getOrderedJourneySteps(session.template);\n const endNodeIndex = orderedSteps.findIndex(\n (node) => node.type === \"end\",\n );\n const targetStep =\n endNodeIndex !== -1 ? 1 + endNodeIndex : 1 + orderedSteps.length;\n\n if (stepObject.step !== targetStep) {\n // skipHistory: repositionnement technique, pas une étape utilisateur\n // (évite aussi que goBack() ne ramène sur un nœud auto-exécutant).\n stepObject.setStep(targetStep, true);\n }\n // Dépendances volontairement resserrées à status + template.id (cf. commentaire\n // ci-dessus) : ne pas inclure `session`/`stepObject` qui relanceraient la boucle.\n }, [session?.status, session?.template?.id]);\n\n if (!sessionId) {\n return <NoSessionIdState />;\n }\n\n // PoweredBy de DatakeenSession : uniquement pour les états spéciaux (loading/error/expired)\n // Les pages normales utilisent MobilePageLayout qui gère son propre PoweredBy\n const showMobilePoweredBy = isMobile && (loading || !!error || isExpired);\n const showDesktopPoweredBy = !isMobile && !loading && !error && !isExpired;\n const poweredByRef = useRef<HTMLDivElement | null>(null);\n const [poweredByHeight, setPoweredByHeight] = useState(0);\n\n useLayoutEffect(() => {\n if (typeof window === \"undefined\" || !showMobilePoweredBy) {\n setPoweredByHeight(0);\n return;\n }\n\n const updateHeight = () => {\n if (poweredByRef.current) {\n setPoweredByHeight(poweredByRef.current.offsetHeight);\n }\n };\n\n updateHeight();\n window.addEventListener(\"resize\", updateHeight);\n\n return () => {\n window.removeEventListener(\"resize\", updateHeight);\n };\n }, [showMobilePoweredBy]);\n\n const mobileRootStyle: CSSProperties = {\n [\"--dk-mobile-footer-offset\" as any]: `${poweredByHeight}px`,\n };\n\n const renderSessionContent = () => {\n if (loading) {\n return <LoadingState />;\n }\n\n if (error) {\n return <ErrorState error={error} />;\n }\n\n if (isExpired) {\n return <SessionExpired onRetry={handleRetrySession} />;\n }\n\n return (\n <SessionContent\n step={step}\n loading={loading}\n session={session}\n sessionId={sessionId}\n stepObject={stepObject}\n stepHistory={history}\n userInput={userInput}\n setUserInput={setUserInput}\n contactInfo={contactInfo}\n setContactInfo={setContactInfo}\n />\n );\n };\n\n const mobileScrollPadding = showMobilePoweredBy\n ? `calc(${poweredByHeight}px + env(safe-area-inset-bottom, 0px))`\n : \"env(safe-area-inset-bottom, 0px)\";\n\n const currentTemplateNode = useMemo(() => {\n if (!session?.template || step < 1) {\n return null;\n }\n\n const templateIndex = Math.floor(step - 1);\n const orderedSteps = getOrderedJourneySteps(session.template);\n return orderedSteps[templateIndex] || null;\n }, [session?.template, step]);\n\n const shouldUseWideDesktopLayout =\n currentTemplateNode?.type === \"information-input\" &&\n currentTemplateNode.informationType === \"custom\" &&\n (currentTemplateNode.customFields || []).some(\n (field) => field.valueType === \"list\",\n );\n\n const [isRecordingSelfie, setIsRecordingSelfie] = useState(false);\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const update = () =>\n setIsRecordingSelfie(\n document.body.classList.contains(\"recording-selfie\"),\n );\n update();\n const observer = new MutationObserver(update);\n observer.observe(document.body, {\n attributes: true,\n attributeFilter: [\"class\"],\n });\n return () => observer.disconnect();\n }, []);\n\n const isCaptureStep =\n currentTemplateNode?.type === \"biometric-capture\" ||\n currentTemplateNode?.type === \"selfie-capture\" ||\n currentTemplateNode?.type === \"document-collection\" || // Could be upload, but safer to allow full height\n currentTemplateNode?.type === \"controle-jdi\";\n\n const btnBg = session?.template?.buttonBgColor ?? \"#11E5C5\";\n const btnText = session?.template?.buttonTextColor ?? \"#3C3C40\";\n\n return (\n <ConfigProvider apiBaseUrl={apiBaseUrl}>\n <DocumentProvider>\n <div\n className=\"sdk-session notranslate flex-1 flex flex-col\"\n lang={currentLanguage}\n translate=\"no\"\n style={\n {\n [\"--dk-btn-bg\" as string]: btnBg,\n [\"--dk-btn-text\" as string]: btnText,\n ...(isMobile\n ? {\n [\"--dk-mobile-scroll-padding\" as string]:\n mobileScrollPadding,\n }\n : undefined),\n } as CSSProperties\n }\n >\n {isMobile ? (\n <div\n className=\"flex h-full w-full flex-col bg-white\"\n style={mobileRootStyle}\n >\n <div\n className={`flex flex-1 min-h-0 flex-col ${isCaptureStep ? \"\" : \"overflow-y-auto\"}`}\n style={{\n paddingBottom: isCaptureStep\n ? \"0\"\n : \"var(--dk-mobile-scroll-padding, env(safe-area-inset-bottom, 0px))\",\n }}\n >\n <div className=\"flex flex-1 flex-col min-h-0\">\n {renderSessionContent()}\n </div>\n </div>\n {showMobilePoweredBy && (\n <div\n className=\"bg-white px-4 pt-3 pb-4 sm:px-6 sm:pt-6 sm:pb-6\"\n style={{\n paddingBottom:\n \"calc(env(safe-area-inset-bottom, 0px) + 0.5rem)\",\n }}\n ref={poweredByRef}\n >\n <PoweredBy\n logo={session?.template?.logo}\n text={session?.template?.logo ? \"par\" : undefined}\n />\n </div>\n )}\n </div>\n ) : (\n <div className=\"flex items-center justify-center w-full min-h-screen bg-gray-100 py-12\">\n <div className=\"flex flex-col items-center justify-center\">\n <Paper\n className={`dk-session-paper flex flex-col h-[800px] px-8 py-10 bg-white rounded-2xl shadow-xl z-10 ${isRecordingSelfie\n ? \"\"\n : shouldUseWideDesktopLayout\n ? \"w-[98vw] max-w-[1500px]\"\n : \"w-[600px]\"\n }`}\n style={\n isRecordingSelfie\n ? { width: \"min(66vw, 942px)\" }\n : undefined\n }\n >\n <div className=\"flex-1 w-full h-full overflow-y-auto\">\n {renderSessionContent()}\n </div>\n </Paper>\n {showDesktopPoweredBy && (\n <div className=\"pt-6 text-center\">\n <PoweredBy\n logo={session?.template?.logo}\n text={session?.template?.logo ? \"par\" : undefined}\n />\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n </DocumentProvider>\n </ConfigProvider>\n );\n};\n\nexport default DatakeenSession;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"DatakeenSession.js","sources":["../../../../src/components/DatakeenSession.tsx"],"sourcesContent":["import { setSessionCredential } from '../services/sessionCredentials';\nimport { useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { CSSProperties } from \"react\";\nimport type { DatakeenSessionProps } from \"../types/session\";\nimport Paper from \"./ui/Paper\";\nimport useIsMobile from \"../hooks/useIsMobile\";\nimport PoweredBy from \"./ui/PoweredBy\";\nimport SessionExpired from \"./session/SessionExpired\";\nimport { useRouteCSS } from \"../hooks/useRouteCSS\";\nimport { DocumentProvider } from \"../context/DocumentContext\";\nimport { ConfigProvider } from \"../context/ConfigContext\";\nimport { setSessionStore } from \"../context/SessionContext\";\n\n// Hooks\nimport { useSessionData } from \"../hooks/useSessionData\";\nimport { useStepNavigation } from \"../hooks/useStepNavigation\";\nimport { useStepCSS } from \"../hooks/useStepCSS\";\nimport { useTemplateLoader } from \"../hooks/useTemplateLoader\";\nimport { useClientInfo } from \"../hooks/useClientInfo\";\nimport { useI18n } from \"../hooks/useI18n\";\nimport { getOrderedJourneySteps } from \"../services/sessionService\";\n\n// Components\nimport NoSessionIdState from \"./states/NoSessionIdState\";\nimport LoadingState from \"./states/LoadingState\";\nimport ErrorState from \"./states/ErrorState\";\nimport SessionContent from \"./session/SessionContent\";\n\n/**\n * DatakeenSession Component\n *\n * The main component of the Datakeen SDK that manages the multi-step verification flow.\n * This component handles the different steps of the session process, including:\n * - Initial welcome screen\n * - User information collection\n * - Country selection for JDI verification\n * - Mobile redirect when selfie is required\n *\n * The component maintains internal state for the current step and user input data,\n * progressing through the verification journey as the user completes each step.\n *\n * @param {DatakeenSessionProps} props - Component props\n * @param {string} props.sessionId - Unique identifier for the verification session\n * @param {SessionConfig} props.sessionConfig - Configuration for the session (e.g., selfie: true)\n * @param {string} props.apiBaseUrl - Optional API base URL for dynamic environment configuration\n * @returns {JSX.Element} A Paper-wrapped container with the appropriate step component based on current state\n */\nconst DatakeenSession = ({\n sessionId,\n sessionToken,\n sessionConfig,\n apiBaseUrl,\n}: DatakeenSessionProps) => {\n useLayoutEffect(() => {\n const fragmentToken = typeof window !== 'undefined' ? new URLSearchParams(window.location.hash.slice(1)).get('sessionToken') : null;\n setSessionCredential(sessionId, sessionToken || sessionConfig?.sessionToken || fragmentToken || undefined);\n return () => setSessionCredential(sessionId);\n }, [sessionId, sessionToken, sessionConfig?.sessionToken]);\n const isMobile = useIsMobile();\n const { info: clientInfo, isLoaded: clientInfoLoaded } = useClientInfo();\n const { currentLanguage } = useI18n();\n\n // Load base CSS and UI components CSS that are always needed\n useRouteCSS(\"ui-components\");\n\n // Session data management\n const {\n session,\n setSession,\n loading,\n setLoading,\n error,\n isExpired,\n userInput,\n setUserInput,\n contactInfo,\n setContactInfo,\n loadSession,\n handleRetrySession,\n } = useSessionData(sessionId, clientInfo, clientInfoLoaded);\n\n // Step navigation management\n const { step, stepObject, history } = useStepNavigation(\n sessionId,\n session?.status,\n session?.currentStep,\n session?.template,\n );\n\n // Load route-specific CSS based on current step\n useStepCSS(step, session);\n\n // Template loading logic\n useTemplateLoader(step, sessionId, session, loading, setSession, setLoading);\n\n // Load session data on component mount\n useEffect(() => {\n loadSession();\n }, [sessionId, sessionConfig]);\n\n // Update global session store whenever session data changes\n useEffect(() => {\n const sanitizedSessionForGlobalStore = session\n ? {\n ...session,\n token: \"\",\n userInput: {},\n contactInfo: undefined,\n }\n : null;\n\n setSessionStore({\n session: sanitizedSessionForGlobalStore,\n loading,\n error,\n isExpired,\n userInput: {},\n contactInfo: { email: \"\", phoneNumber: \"\" },\n });\n }, [session, loading, error, isExpired]);\n\n // Handle initial step setting when session loads.\n // Quand la session est déjà terminée (ex: rechargement), on positionne le step\n // sur le nœud `end`. On indexe sur getOrderedJourneySteps (start filtré + tri par\n // `order`) — MÊME référentiel que TemplateNodeRenderer — sinon l'index calculé\n // pointe sur un autre nœud que celui rendu (ex: external-verification, qui se\n // ré-exécute et part en boucle).\n // Idempotent : on ne re-navigue que si on n'est pas déjà sur le bon step, et on\n // resserre les dépendances pour ne pas se re-déclencher à chaque mutation de session\n // (userInput, retryCounts, polling…) — ce qui relançait la boucle.\n useEffect(() => {\n if (session?.status !== \"ended\" || !session.template) {\n return;\n }\n const orderedSteps = getOrderedJourneySteps(session.template);\n const endNodeIndex = orderedSteps.findIndex(\n (node) => node.type === \"end\",\n );\n const targetStep =\n endNodeIndex !== -1 ? 1 + endNodeIndex : 1 + orderedSteps.length;\n\n if (stepObject.step !== targetStep) {\n // skipHistory: repositionnement technique, pas une étape utilisateur\n // (évite aussi que goBack() ne ramène sur un nœud auto-exécutant).\n stepObject.setStep(targetStep, true);\n }\n // Dépendances volontairement resserrées à status + template.id (cf. commentaire\n // ci-dessus) : ne pas inclure `session`/`stepObject` qui relanceraient la boucle.\n }, [session?.status, session?.template?.id]);\n\n if (!sessionId) {\n return <NoSessionIdState />;\n }\n\n // PoweredBy de DatakeenSession : uniquement pour les états spéciaux (loading/error/expired)\n // Les pages normales utilisent MobilePageLayout qui gère son propre PoweredBy\n const showMobilePoweredBy = isMobile && (loading || !!error || isExpired);\n const showDesktopPoweredBy = !isMobile && !loading && !error && !isExpired;\n const poweredByRef = useRef<HTMLDivElement | null>(null);\n const [poweredByHeight, setPoweredByHeight] = useState(0);\n\n useLayoutEffect(() => {\n if (typeof window === \"undefined\" || !showMobilePoweredBy) {\n setPoweredByHeight(0);\n return;\n }\n\n const updateHeight = () => {\n if (poweredByRef.current) {\n setPoweredByHeight(poweredByRef.current.offsetHeight);\n }\n };\n\n updateHeight();\n window.addEventListener(\"resize\", updateHeight);\n\n return () => {\n window.removeEventListener(\"resize\", updateHeight);\n };\n }, [showMobilePoweredBy]);\n\n const mobileRootStyle: CSSProperties = {\n [\"--dk-mobile-footer-offset\" as any]: `${poweredByHeight}px`,\n };\n\n const renderSessionContent = () => {\n if (loading) {\n return <LoadingState />;\n }\n\n if (error) {\n return <ErrorState error={error} />;\n }\n\n if (isExpired) {\n return <SessionExpired onRetry={handleRetrySession} />;\n }\n\n return (\n <SessionContent\n step={step}\n loading={loading}\n session={session}\n sessionId={sessionId}\n stepObject={stepObject}\n stepHistory={history}\n userInput={userInput}\n setUserInput={setUserInput}\n contactInfo={contactInfo}\n setContactInfo={setContactInfo}\n />\n );\n };\n\n const mobileScrollPadding = showMobilePoweredBy\n ? `calc(${poweredByHeight}px + env(safe-area-inset-bottom, 0px))`\n : \"env(safe-area-inset-bottom, 0px)\";\n\n const currentTemplateNode = useMemo(() => {\n if (!session?.template || step < 1) {\n return null;\n }\n\n const templateIndex = Math.floor(step - 1);\n const orderedSteps = getOrderedJourneySteps(session.template);\n return orderedSteps[templateIndex] || null;\n }, [session?.template, step]);\n\n const shouldUseWideDesktopLayout =\n currentTemplateNode?.type === \"information-input\" &&\n currentTemplateNode.informationType === \"custom\" &&\n (currentTemplateNode.customFields || []).some(\n (field) => field.valueType === \"list\",\n );\n\n const [isRecordingSelfie, setIsRecordingSelfie] = useState(false);\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n const update = () =>\n setIsRecordingSelfie(\n document.body.classList.contains(\"recording-selfie\"),\n );\n update();\n const observer = new MutationObserver(update);\n observer.observe(document.body, {\n attributes: true,\n attributeFilter: [\"class\"],\n });\n return () => observer.disconnect();\n }, []);\n\n const isCaptureStep =\n currentTemplateNode?.type === \"biometric-capture\" ||\n currentTemplateNode?.type === \"selfie-capture\" ||\n currentTemplateNode?.type === \"document-collection\" || // Could be upload, but safer to allow full height\n currentTemplateNode?.type === \"controle-jdi\";\n\n const btnBg = session?.template?.buttonBgColor ?? \"#11E5C5\";\n const btnText = session?.template?.buttonTextColor ?? \"#3C3C40\";\n\n return (\n <ConfigProvider apiBaseUrl={apiBaseUrl}>\n <DocumentProvider>\n <div\n className=\"sdk-session notranslate flex-1 flex flex-col\"\n lang={currentLanguage}\n translate=\"no\"\n style={\n {\n [\"--dk-btn-bg\" as string]: btnBg,\n [\"--dk-btn-text\" as string]: btnText,\n ...(isMobile\n ? {\n [\"--dk-mobile-scroll-padding\" as string]:\n mobileScrollPadding,\n }\n : undefined),\n } as CSSProperties\n }\n >\n {isMobile ? (\n <div\n className=\"flex h-full w-full flex-col bg-white\"\n style={mobileRootStyle}\n >\n <div\n className={`flex flex-1 min-h-0 flex-col ${isCaptureStep ? \"\" : \"overflow-y-auto\"}`}\n style={{\n paddingBottom: isCaptureStep\n ? \"0\"\n : \"var(--dk-mobile-scroll-padding, env(safe-area-inset-bottom, 0px))\",\n }}\n >\n <div className=\"flex flex-1 flex-col min-h-0\">\n {renderSessionContent()}\n </div>\n </div>\n {showMobilePoweredBy && (\n <div\n className=\"bg-white px-4 pt-3 pb-4 sm:px-6 sm:pt-6 sm:pb-6\"\n style={{\n paddingBottom:\n \"calc(env(safe-area-inset-bottom, 0px) + 0.5rem)\",\n }}\n ref={poweredByRef}\n >\n <PoweredBy\n logo={session?.template?.logo}\n text={session?.template?.logo ? \"par\" : undefined}\n />\n </div>\n )}\n </div>\n ) : (\n <div className=\"flex items-center justify-center w-full min-h-screen bg-gray-100 py-12\">\n <div className=\"flex flex-col items-center justify-center\">\n <Paper\n className={`dk-session-paper flex flex-col h-[800px] px-8 py-10 bg-white rounded-2xl shadow-xl z-10 ${isRecordingSelfie\n ? \"\"\n : shouldUseWideDesktopLayout\n ? \"w-[98vw] max-w-[1500px]\"\n : \"w-[600px]\"\n }`}\n style={\n isRecordingSelfie\n ? { width: \"min(66vw, 942px)\" }\n : undefined\n }\n >\n <div className=\"flex-1 w-full h-full overflow-y-auto\">\n {renderSessionContent()}\n </div>\n </Paper>\n {showDesktopPoweredBy && (\n <div className=\"pt-6 text-center\">\n <PoweredBy\n logo={session?.template?.logo}\n text={session?.template?.logo ? \"par\" : undefined}\n />\n </div>\n )}\n </div>\n </div>\n )}\n </div>\n </DocumentProvider>\n </ConfigProvider>\n );\n};\n\nexport default DatakeenSession;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
@@ -14,6 +14,7 @@ import MobilePageLayout from '../ui/MobilePageLayout.js';
14
14
  import { useI18n } from '../../hooks/useI18n.js';
15
15
  import useIsMobile from '../../hooks/useIsMobile.js';
16
16
  import { resolveEndScreenIcon } from './endScreenIcons.js';
17
+ import { replaceLocation } from '../../utils/navigation.js';
17
18
 
18
19
  // Couleur des icônes = couleur du bouton choisie pour le journey (buttonBgColor).
19
20
  var ICON_COLOR = "var(--dk-btn-bg, #11e5c5)";
@@ -113,7 +114,7 @@ var EndFlow = function (_a) {
113
114
  }); };
114
115
  var redirectToCallback = function (url) {
115
116
  try {
116
- window.location.replace(url);
117
+ replaceLocation(url);
117
118
  }
118
119
  catch (e) {
119
120
  console.error("Redirection vers le callback échouée:", e);
@@ -201,7 +202,7 @@ var EndFlow = function (_a) {
201
202
  // Créer une URL de données avec un script de fermeture automatique
202
203
  var closeScript = "\n <html>\n <head><title>Session termin\u00E9e</title></head>\n <body style=\"margin:0;padding:0;background:#f0f0f0;font-family:Arial,sans-serif;\">\n <div style=\"display:flex;flex-direction:column;justify-content:center;align-items:center;height:100vh;text-align:center;\">\n <h1 style=\"color:#4ade80;margin-bottom:20px;\">\u2705 Session termin\u00E9e avec succ\u00E8s</h1>\n <p style=\"color:#666;margin-bottom:30px;\">Cette fen\u00EAtre va se fermer automatiquement.</p>\n <button onclick=\"attemptClose()\" style=\"background:#3b82f6;color:white;border:none;padding:12px 24px;border-radius:6px;cursor:pointer;font-size:16px;\">\n Fermer maintenant\n </button>\n </div>\n <script>\n function attemptClose() {\n try {\n window.close();\n self.close();\n if (window.opener) {\n window.opener.focus();\n window.close();\n }\n if (window.parent && window.parent !== window) {\n window.parent.postMessage({action:'closeWindow'}, '*');\n }\n } catch(e) {\n console.log('Fermeture impossible:', e);\n document.body.innerHTML = '<div style=\"text-align:center;padding:50px;\"><h2>Veuillez fermer cette fen\u00EAtre manuellement</h2><p>Appuyez sur Alt+F4 ou cliquez sur le X</p></div>';\n }\n }\n // Essayer de fermer automatiquement apr\u00E8s 1 seconde\n setTimeout(attemptClose, 1000);\n </script>\n </body>\n </html>\n ";
203
204
  var dataUrl = "data:text/html;charset=utf-8," + encodeURIComponent(closeScript);
204
- window.location.replace(dataUrl);
205
+ replaceLocation(dataUrl);
205
206
  console.log("Redirection vers page de fermeture personnalisée");
206
207
  }
207
208
  catch (e) {
@@ -1 +1 @@
1
- {"version":3,"file":"EndFlow.js","sources":["../../../../../src/components/session/EndFlow.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\nimport { HeartHandshake, Send, Mail } from \"lucide-react\";\nimport checkIcon from \"../../assets/check.svg\";\nimport LoadingState from \"../states/LoadingState\";\nimport Button from \"../ui/Button\";\nimport PageActions from \"../ui/PageActions\";\nimport Title from \"../ui/Title\";\nimport Subtitle from \"../ui/Subtitle\";\nimport {\n clearSessionSensitiveData,\n updateSessionStatus,\n} from \"../../services/sessionService\";\nimport { logSessionEnded } from \"../../services/auditTrailService\";\nimport MobilePageLayout from \"../ui/MobilePageLayout\";\nimport { useI18n } from \"../../hooks/useI18n\";\nimport useIsMobile from \"../../hooks/useIsMobile\";\nimport type { EndScreenCase } from \"../../types/session\";\nimport { resolveEndScreenIcon } from \"./endScreenIcons\";\n\n// Couleur des icônes = couleur du bouton choisie pour le journey (buttonBgColor).\nconst ICON_COLOR = \"var(--dk-btn-bg, #11e5c5)\";\n\ninterface EndFlowProps {\n sessionId: string;\n sessionStatus?: string; // Ajout du statut de session optionnel\n callbackURL?: string | null;\n endNodeId?: string; // Id du nœud `end` atteint, transmis au backend pour forcer le bon statut\n expectedResult?: string; // Résultat forcé du nœud end atteint (ex: 'non_compliant')\n /** Textes de fin personnalisés (config globale du journey). Vide → fallback standard. */\n compliantCase?: EndScreenCase;\n nonCompliantCase?: EndScreenCase;\n}\n\n/**\n * EndFlow Component\n *\n * Composant affiché à la fin du flux de vérification lorsque toutes les étapes ont été complétées.\n * Affiche un message de confirmation avec une icône de succès.\n *\n * @param {EndFlowProps} props - Propriétés du composant\n * @param {string} props.sessionId - L'identifiant de la session\n * @param {string} props.sessionStatus - Le statut actuel de la session\n * @returns {JSX.Element} Le composant EndFlow\n */\nconst EndFlow: React.FC<EndFlowProps> = ({\n sessionId,\n sessionStatus,\n callbackURL,\n endNodeId,\n expectedResult,\n compliantCase,\n nonCompliantCase,\n}) => {\n const [isEnding, setIsEnding] = useState(false);\n const [isRedirecting, setIsRedirecting] = useState(false);\n const [shouldRedirect, setShouldRedirect] = useState(false);\n // Statut consolidé renvoyé par le backend après updateSessionStatus —\n // sert à détecter un échec « poursuivre quand même » non porté par expectedResult.\n const [backendStatus, setBackendStatus] = useState<string | undefined>(\n undefined,\n );\n // Le verdict backend arrive de façon asynchrone (~1-2s). On le marque résolu\n // pour éviter de flasher l'écran succès puis basculer sur l'écran erreur.\n const [statusResolved, setStatusResolved] = useState(false);\n const { t } = useI18n();\n const isMobile = useIsMobile(768);\n // Décalage horizontal du bloc d'items réservé au desktop. On le pilote en JS\n // (useIsMobile, cohérent avec le composant Button) plutôt que via `md:` Tailwind :\n // les variantes responsive `md:translate-*` ne sont pas fiables dans le bundle SDK.\n // Utilisé uniquement par l'écran d'erreur générique hardcodé ci-dessous.\n const desktopTranslateX = isMobile ? \"\" : \"translate-x-10\";\n // Même décalage, mais via padding plutôt que transform — réservé aux écrans à\n // message personnalisé (renderCustomCase) : un `transform: translateX(...)` ne fait\n // déborder aucune boîte visuellement, mais contribue quand même au \"scrollable\n // overflow\" du conteneur `overflow-y-auto` ancêtre (DatakeenSession), ce qui\n // affichait une scrollbar horizontale fantôme sur desktop. `padding-left` reste\n // contenu dans la largeur déclarée (box-sizing: border-box global du SDK), donc ne\n // peut pas produire ce débordement invisible.\n const desktopOffsetX = isMobile ? \"\" : \"pl-10\";\n\n // Le nœud end peut déjà forcer un résultat non conforme : dans ce cas on\n // connaît l'issue sans attendre le backend.\n const expectedError =\n expectedResult === \"non_compliant\" || expectedResult === \"error\";\n\n // L'écran « erreur conservée » s'affiche si le nœud end force un résultat\n // non conforme, OU si le backend a consolidé un statut d'échec/vérification.\n const isError =\n expectedError ||\n backendStatus === \"ai_rejected\" ||\n backendStatus === \"verify\";\n\n // Tant que le verdict n'est pas connu (ni forcé par le nœud, ni renvoyé par\n // le backend), on affiche un état neutre plutôt que de présumer le succès.\n const isResolving = !expectedError && !statusResolved;\n\n // Trigger session end as soon as the user reaches this screen,\n // regardless of whether they click the button (e.g. tab close)\n useEffect(() => {\n updateSessionStatus(sessionId, \"ended\", endNodeId)\n .then((updatedSession) => {\n if (updatedSession?.status) {\n setBackendStatus(updatedSession.status);\n }\n if (updatedSession && updatedSession.analysisId) {\n logSessionEnded(sessionId).catch((err) => {\n console.error(\"Failed to log session end in audit trail:\", err);\n });\n }\n })\n .catch((error) => {\n console.error(\"Error updating session status on EndFlow mount:\", error);\n })\n .finally(() => {\n // Verdict connu (ou échec d'appel) : on peut afficher l'écran final.\n setStatusResolved(true);\n });\n }, []);\n\n const handleEndSession = async () => {\n setIsEnding(true);\n try {\n clearSessionSensitiveData(sessionId);\n } catch (error) {\n console.error(\"Erreur lors de la finalisation de la session:\", error);\n } finally {\n setIsEnding(false);\n if (callbackURL) {\n setIsRedirecting(true);\n setShouldRedirect(true);\n } else {\n // Forcer la fermeture immédiatement lorsqu'aucune redirection n'est attendue\n closeWindow();\n }\n }\n };\n\n const redirectToCallback = (url: string) => {\n try {\n window.location.replace(url);\n } catch (e) {\n console.error(\"Redirection vers le callback échouée:\", e);\n setIsRedirecting(false);\n closeWindow();\n }\n };\n\n useEffect(() => {\n if (sessionStatus === \"ended\" && callbackURL && !shouldRedirect) {\n setIsRedirecting(true);\n setShouldRedirect(true);\n }\n }, [sessionStatus, callbackURL, shouldRedirect]);\n\n useEffect(() => {\n if (!shouldRedirect || !callbackURL) {\n return;\n }\n\n const REDIRECT_DELAY_MS = 2000;\n const timer = window.setTimeout(\n () => redirectToCallback(callbackURL),\n REDIRECT_DELAY_MS,\n );\n return () => window.clearTimeout(timer);\n }, [shouldRedirect, callbackURL]);\n\n const closeWindow = () => {\n console.log(\"Tentative de fermeture de la fenêtre...\");\n\n // Méthode 1: Si dans un iframe, communiquer avec le parent IMMÉDIATEMENT\n if (window.parent && window.parent !== window) {\n try {\n window.parent.postMessage(\n {\n action: \"closeSession\",\n sessionId,\n status: \"ended\",\n message: \"Session terminée, veuillez fermer cette fenêtre\",\n },\n \"*\",\n );\n console.log(\"Message envoyé au parent pour fermeture\");\n } catch (e) {\n console.log(\"postMessage échoué:\", e);\n }\n }\n\n // Méthode 2: Essayer window.close() avec différentes approches\n try {\n // Essayer close direct\n window.close();\n console.log(\"window.close() direct appelé\");\n } catch (e) {\n console.log(\"window.close() direct échoué:\", e);\n }\n\n // Méthode 3: Si on a un opener, essayer de se fermer via l'opener\n setTimeout(() => {\n try {\n if (window.opener) {\n window.opener.focus();\n window.close();\n console.log(\"Fermeture via opener\");\n }\n } catch (e) {\n console.log(\"Fermeture via opener échouée:\", e);\n }\n }, 100);\n\n // Méthode 4: Forcer avec self.close()\n setTimeout(() => {\n try {\n self.close();\n console.log(\"self.close() appelé\");\n } catch (e) {\n console.log(\"self.close() échoué:\", e);\n }\n }, 200);\n\n // Méthode 5: Essayer de manipuler l'historique pour forcer la fermeture\n setTimeout(() => {\n try {\n window.history.back();\n window.close();\n console.log(\"history.back() + close\");\n } catch (e) {\n console.log(\"history.back() + close échoué:\", e);\n }\n }, 300);\n\n // Méthode 6: Rediriger vers une page de fermeture personnalisée\n setTimeout(() => {\n try {\n // Créer une URL de données avec un script de fermeture automatique\n const closeScript = `\n <html>\n <head><title>Session terminée</title></head>\n <body style=\"margin:0;padding:0;background:#f0f0f0;font-family:Arial,sans-serif;\">\n <div style=\"display:flex;flex-direction:column;justify-content:center;align-items:center;height:100vh;text-align:center;\">\n <h1 style=\"color:#4ade80;margin-bottom:20px;\">✅ Session terminée avec succès</h1>\n <p style=\"color:#666;margin-bottom:30px;\">Cette fenêtre va se fermer automatiquement.</p>\n <button onclick=\"attemptClose()\" style=\"background:#3b82f6;color:white;border:none;padding:12px 24px;border-radius:6px;cursor:pointer;font-size:16px;\">\n Fermer maintenant\n </button>\n </div>\n <script>\n function attemptClose() {\n try {\n window.close();\n self.close();\n if (window.opener) {\n window.opener.focus();\n window.close();\n }\n if (window.parent && window.parent !== window) {\n window.parent.postMessage({action:'closeWindow'}, '*');\n }\n } catch(e) {\n console.log('Fermeture impossible:', e);\n document.body.innerHTML = '<div style=\"text-align:center;padding:50px;\"><h2>Veuillez fermer cette fenêtre manuellement</h2><p>Appuyez sur Alt+F4 ou cliquez sur le X</p></div>';\n }\n }\n // Essayer de fermer automatiquement après 1 seconde\n setTimeout(attemptClose, 1000);\n </script>\n </body>\n </html>\n `;\n const dataUrl =\n \"data:text/html;charset=utf-8,\" + encodeURIComponent(closeScript);\n window.location.replace(dataUrl);\n console.log(\"Redirection vers page de fermeture personnalisée\");\n } catch (e) {\n console.log(\"Redirection vers page personnalisée échouée:\", e);\n }\n }, 500);\n };\n\n // Rendu d'un jeu de textes de fin personnalisé (titre ancré en haut,\n // items centrés verticalement, icônes à la couleur du bouton du journey).\n const renderCustomCase = (config: EndScreenCase, fallbackTitle: string) => (\n <div className=\"flex flex-1 flex-col items-center h-full px-4 pt-10 pb-8 md:px-8\">\n <Title className=\"text-2xl md:text-3xl text-center\">\n {config.title?.trim() || fallbackTitle}\n </Title>\n <div className=\"flex flex-1 items-center w-full max-w-md mx-auto\">\n {/* Padding appliqué uniquement sur desktop (md+) ; sur mobile il\n décalait le texte hors écran et le tronquait. */}\n <div\n className={`flex w-full flex-col items-start gap-7 -translate-y-20 ${desktopOffsetX}`}\n >\n {config.items.map((item, index) => {\n const Icon = resolveEndScreenIcon(item.icon);\n return (\n <div\n key={index}\n className=\"flex w-full items-center gap-4 text-left\"\n >\n <Icon\n className=\"w-7 h-7 shrink-0\"\n style={{ color: ICON_COLOR }}\n />\n <span className=\"flex-1 text-base md:text-lg text-[#3C3C40]\">\n {item.text}\n </span>\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n\n const hasCustomError = !!nonCompliantCase?.items?.length;\n const hasCustomSuccess = !!compliantCase?.items?.length;\n\n // En attente du verdict backend : éviter le flash de l'écran succès.\n if (isResolving) {\n return (\n <LoadingState\n message={t(\"session.end.finalizing\", \"Finalisation du parcours...\")}\n subtitle=\"\"\n />\n );\n }\n\n return (\n <MobilePageLayout\n footer={\n <PageActions\n primary={\n <Button\n onClick={handleEndSession}\n disabled={isEnding || isRedirecting}\n >\n {isRedirecting\n ? t(\"session.end.button_redirecting\", \"Redirection...\")\n : isEnding\n ? t(\"session.end.button_finalizing\", \"Finalisation...\")\n : t(\"session.end.button_end\", \"Terminer\")}\n </Button>\n }\n />\n }\n >\n {isError ? (\n hasCustomError ? (\n renderCustomCase(\n nonCompliantCase as EndScreenCase,\n t(\"session.end.error_title\", \"Les prochaines étapes\"),\n )\n ) : (\n <div className=\"flex flex-1 flex-col items-center h-full px-4 pt-10 pb-8 md:px-8\">\n {/* Titre ancré en haut */}\n <Title className=\"text-2xl md:text-3xl text-center\">\n {t(\"session.end.error_title\", \"Les prochaines étapes\")}\n </Title>\n\n {/* Liste centrée verticalement dans l'espace restant */}\n <div className=\"flex flex-1 items-center w-full max-w-md mx-auto\">\n {/* Translate desktop-only : sur mobile il tronquait le texte. */}\n <div\n className={`flex w-full flex-col items-start gap-7 -translate-y-20 ${desktopTranslateX}`}\n >\n {[\n {\n Icon: HeartHandshake,\n key: \"session.end.error_step_thanks\",\n fallback: \"Nous vous remercions pour ces informations\",\n },\n {\n Icon: Send,\n key: \"session.end.error_step_transmitted\",\n fallback: \"Votre dossier a été transmis à notre équipe\",\n },\n {\n Icon: Mail,\n key: \"session.end.error_step_contact\",\n fallback: \"Nous vous recontacterons si nécessaire\",\n },\n ].map(({ Icon, key, fallback }) => (\n <div\n key={key}\n className=\"flex w-full items-center gap-4 text-left\"\n >\n <Icon\n className=\"w-7 h-7 shrink-0\"\n style={{ color: ICON_COLOR }}\n />\n <span className=\"flex-1 text-base md:text-lg text-[#3C3C40]\">\n {t(key, fallback)}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n ) : hasCustomSuccess ? (\n renderCustomCase(\n compliantCase as EndScreenCase,\n t(\"session.end.title\", \"C'est tout bon !\"),\n )\n ) : (\n <div className=\"flex flex-1 flex-col items-center justify-center h-full px-4 py-8 md:px-8\">\n <div className=\"w-full max-w-md mx-auto text-center space-y-6\">\n {/* Icon - shown on top for both mobile and desktop */}\n <div className=\"flex justify-center\">\n <img\n src={checkIcon}\n alt={t(\"session.end.image_alt\", \"Vérification terminée\")}\n className=\"w-48 h-48\"\n />\n </div>\n\n {/* Text content */}\n <div className=\"space-y-4\">\n <Title className=\"text-xl md:text-2xl lg:text-3xl\">\n {t(\"session.end.title\", \"C'est tout bon !\")}\n </Title>\n <Subtitle className=\"text-sm md:text-base text-gray-600 leading-relaxed\">\n {t(\n \"session.end.subtitle\",\n \"Merci d'avoir terminé ce parcours, vous pouvez quitter cette fenêtre.\",\n )}\n </Subtitle>\n </div>\n </div>\n </div>\n )}\n </MobilePageLayout>\n );\n};\n\nexport default EndFlow;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
1
+ {"version":3,"file":"EndFlow.js","sources":["../../../../../src/components/session/EndFlow.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\nimport { HeartHandshake, Send, Mail } from \"lucide-react\";\nimport checkIcon from \"../../assets/check.svg\";\nimport LoadingState from \"../states/LoadingState\";\nimport Button from \"../ui/Button\";\nimport PageActions from \"../ui/PageActions\";\nimport Title from \"../ui/Title\";\nimport Subtitle from \"../ui/Subtitle\";\nimport {\n clearSessionSensitiveData,\n updateSessionStatus,\n} from \"../../services/sessionService\";\nimport { logSessionEnded } from \"../../services/auditTrailService\";\nimport MobilePageLayout from \"../ui/MobilePageLayout\";\nimport { useI18n } from \"../../hooks/useI18n\";\nimport useIsMobile from \"../../hooks/useIsMobile\";\nimport type { EndScreenCase } from \"../../types/session\";\nimport { resolveEndScreenIcon } from \"./endScreenIcons\";\nimport { replaceLocation } from \"../../utils/navigation\";\n\n// Couleur des icônes = couleur du bouton choisie pour le journey (buttonBgColor).\nconst ICON_COLOR = \"var(--dk-btn-bg, #11e5c5)\";\n\ninterface EndFlowProps {\n sessionId: string;\n sessionStatus?: string; // Ajout du statut de session optionnel\n callbackURL?: string | null;\n endNodeId?: string; // Id du nœud `end` atteint, transmis au backend pour forcer le bon statut\n expectedResult?: string; // Résultat forcé du nœud end atteint (ex: 'non_compliant')\n /** Textes de fin personnalisés (config globale du journey). Vide → fallback standard. */\n compliantCase?: EndScreenCase;\n nonCompliantCase?: EndScreenCase;\n}\n\n/**\n * EndFlow Component\n *\n * Composant affiché à la fin du flux de vérification lorsque toutes les étapes ont été complétées.\n * Affiche un message de confirmation avec une icône de succès.\n *\n * @param {EndFlowProps} props - Propriétés du composant\n * @param {string} props.sessionId - L'identifiant de la session\n * @param {string} props.sessionStatus - Le statut actuel de la session\n * @returns {JSX.Element} Le composant EndFlow\n */\nconst EndFlow: React.FC<EndFlowProps> = ({\n sessionId,\n sessionStatus,\n callbackURL,\n endNodeId,\n expectedResult,\n compliantCase,\n nonCompliantCase,\n}) => {\n const [isEnding, setIsEnding] = useState(false);\n const [isRedirecting, setIsRedirecting] = useState(false);\n const [shouldRedirect, setShouldRedirect] = useState(false);\n // Statut consolidé renvoyé par le backend après updateSessionStatus —\n // sert à détecter un échec « poursuivre quand même » non porté par expectedResult.\n const [backendStatus, setBackendStatus] = useState<string | undefined>(\n undefined,\n );\n // Le verdict backend arrive de façon asynchrone (~1-2s). On le marque résolu\n // pour éviter de flasher l'écran succès puis basculer sur l'écran erreur.\n const [statusResolved, setStatusResolved] = useState(false);\n const { t } = useI18n();\n const isMobile = useIsMobile(768);\n // Décalage horizontal du bloc d'items réservé au desktop. On le pilote en JS\n // (useIsMobile, cohérent avec le composant Button) plutôt que via `md:` Tailwind :\n // les variantes responsive `md:translate-*` ne sont pas fiables dans le bundle SDK.\n // Utilisé uniquement par l'écran d'erreur générique hardcodé ci-dessous.\n const desktopTranslateX = isMobile ? \"\" : \"translate-x-10\";\n // Même décalage, mais via padding plutôt que transform — réservé aux écrans à\n // message personnalisé (renderCustomCase) : un `transform: translateX(...)` ne fait\n // déborder aucune boîte visuellement, mais contribue quand même au \"scrollable\n // overflow\" du conteneur `overflow-y-auto` ancêtre (DatakeenSession), ce qui\n // affichait une scrollbar horizontale fantôme sur desktop. `padding-left` reste\n // contenu dans la largeur déclarée (box-sizing: border-box global du SDK), donc ne\n // peut pas produire ce débordement invisible.\n const desktopOffsetX = isMobile ? \"\" : \"pl-10\";\n\n // Le nœud end peut déjà forcer un résultat non conforme : dans ce cas on\n // connaît l'issue sans attendre le backend.\n const expectedError =\n expectedResult === \"non_compliant\" || expectedResult === \"error\";\n\n // L'écran « erreur conservée » s'affiche si le nœud end force un résultat\n // non conforme, OU si le backend a consolidé un statut d'échec/vérification.\n const isError =\n expectedError ||\n backendStatus === \"ai_rejected\" ||\n backendStatus === \"verify\";\n\n // Tant que le verdict n'est pas connu (ni forcé par le nœud, ni renvoyé par\n // le backend), on affiche un état neutre plutôt que de présumer le succès.\n const isResolving = !expectedError && !statusResolved;\n\n // Trigger session end as soon as the user reaches this screen,\n // regardless of whether they click the button (e.g. tab close)\n useEffect(() => {\n updateSessionStatus(sessionId, \"ended\", endNodeId)\n .then((updatedSession) => {\n if (updatedSession?.status) {\n setBackendStatus(updatedSession.status);\n }\n if (updatedSession && updatedSession.analysisId) {\n logSessionEnded(sessionId).catch((err) => {\n console.error(\"Failed to log session end in audit trail:\", err);\n });\n }\n })\n .catch((error) => {\n console.error(\"Error updating session status on EndFlow mount:\", error);\n })\n .finally(() => {\n // Verdict connu (ou échec d'appel) : on peut afficher l'écran final.\n setStatusResolved(true);\n });\n }, []);\n\n const handleEndSession = async () => {\n setIsEnding(true);\n try {\n clearSessionSensitiveData(sessionId);\n } catch (error) {\n console.error(\"Erreur lors de la finalisation de la session:\", error);\n } finally {\n setIsEnding(false);\n if (callbackURL) {\n setIsRedirecting(true);\n setShouldRedirect(true);\n } else {\n // Forcer la fermeture immédiatement lorsqu'aucune redirection n'est attendue\n closeWindow();\n }\n }\n };\n\n const redirectToCallback = (url: string) => {\n try {\n replaceLocation(url);\n } catch (e) {\n console.error(\"Redirection vers le callback échouée:\", e);\n setIsRedirecting(false);\n closeWindow();\n }\n };\n\n useEffect(() => {\n if (sessionStatus === \"ended\" && callbackURL && !shouldRedirect) {\n setIsRedirecting(true);\n setShouldRedirect(true);\n }\n }, [sessionStatus, callbackURL, shouldRedirect]);\n\n useEffect(() => {\n if (!shouldRedirect || !callbackURL) {\n return;\n }\n\n const REDIRECT_DELAY_MS = 2000;\n const timer = window.setTimeout(\n () => redirectToCallback(callbackURL),\n REDIRECT_DELAY_MS,\n );\n return () => window.clearTimeout(timer);\n }, [shouldRedirect, callbackURL]);\n\n const closeWindow = () => {\n console.log(\"Tentative de fermeture de la fenêtre...\");\n\n // Méthode 1: Si dans un iframe, communiquer avec le parent IMMÉDIATEMENT\n if (window.parent && window.parent !== window) {\n try {\n window.parent.postMessage(\n {\n action: \"closeSession\",\n sessionId,\n status: \"ended\",\n message: \"Session terminée, veuillez fermer cette fenêtre\",\n },\n \"*\",\n );\n console.log(\"Message envoyé au parent pour fermeture\");\n } catch (e) {\n console.log(\"postMessage échoué:\", e);\n }\n }\n\n // Méthode 2: Essayer window.close() avec différentes approches\n try {\n // Essayer close direct\n window.close();\n console.log(\"window.close() direct appelé\");\n } catch (e) {\n console.log(\"window.close() direct échoué:\", e);\n }\n\n // Méthode 3: Si on a un opener, essayer de se fermer via l'opener\n setTimeout(() => {\n try {\n if (window.opener) {\n window.opener.focus();\n window.close();\n console.log(\"Fermeture via opener\");\n }\n } catch (e) {\n console.log(\"Fermeture via opener échouée:\", e);\n }\n }, 100);\n\n // Méthode 4: Forcer avec self.close()\n setTimeout(() => {\n try {\n self.close();\n console.log(\"self.close() appelé\");\n } catch (e) {\n console.log(\"self.close() échoué:\", e);\n }\n }, 200);\n\n // Méthode 5: Essayer de manipuler l'historique pour forcer la fermeture\n setTimeout(() => {\n try {\n window.history.back();\n window.close();\n console.log(\"history.back() + close\");\n } catch (e) {\n console.log(\"history.back() + close échoué:\", e);\n }\n }, 300);\n\n // Méthode 6: Rediriger vers une page de fermeture personnalisée\n setTimeout(() => {\n try {\n // Créer une URL de données avec un script de fermeture automatique\n const closeScript = `\n <html>\n <head><title>Session terminée</title></head>\n <body style=\"margin:0;padding:0;background:#f0f0f0;font-family:Arial,sans-serif;\">\n <div style=\"display:flex;flex-direction:column;justify-content:center;align-items:center;height:100vh;text-align:center;\">\n <h1 style=\"color:#4ade80;margin-bottom:20px;\">✅ Session terminée avec succès</h1>\n <p style=\"color:#666;margin-bottom:30px;\">Cette fenêtre va se fermer automatiquement.</p>\n <button onclick=\"attemptClose()\" style=\"background:#3b82f6;color:white;border:none;padding:12px 24px;border-radius:6px;cursor:pointer;font-size:16px;\">\n Fermer maintenant\n </button>\n </div>\n <script>\n function attemptClose() {\n try {\n window.close();\n self.close();\n if (window.opener) {\n window.opener.focus();\n window.close();\n }\n if (window.parent && window.parent !== window) {\n window.parent.postMessage({action:'closeWindow'}, '*');\n }\n } catch(e) {\n console.log('Fermeture impossible:', e);\n document.body.innerHTML = '<div style=\"text-align:center;padding:50px;\"><h2>Veuillez fermer cette fenêtre manuellement</h2><p>Appuyez sur Alt+F4 ou cliquez sur le X</p></div>';\n }\n }\n // Essayer de fermer automatiquement après 1 seconde\n setTimeout(attemptClose, 1000);\n </script>\n </body>\n </html>\n `;\n const dataUrl =\n \"data:text/html;charset=utf-8,\" + encodeURIComponent(closeScript);\n replaceLocation(dataUrl);\n console.log(\"Redirection vers page de fermeture personnalisée\");\n } catch (e) {\n console.log(\"Redirection vers page personnalisée échouée:\", e);\n }\n }, 500);\n };\n\n // Rendu d'un jeu de textes de fin personnalisé (titre ancré en haut,\n // items centrés verticalement, icônes à la couleur du bouton du journey).\n const renderCustomCase = (config: EndScreenCase, fallbackTitle: string) => (\n <div className=\"flex flex-1 flex-col items-center h-full px-4 pt-10 pb-8 md:px-8\">\n <Title className=\"text-2xl md:text-3xl text-center\">\n {config.title?.trim() || fallbackTitle}\n </Title>\n <div className=\"flex flex-1 items-center w-full max-w-md mx-auto\">\n {/* Padding appliqué uniquement sur desktop (md+) ; sur mobile il\n décalait le texte hors écran et le tronquait. */}\n <div\n className={`flex w-full flex-col items-start gap-7 -translate-y-20 ${desktopOffsetX}`}\n >\n {config.items.map((item, index) => {\n const Icon = resolveEndScreenIcon(item.icon);\n return (\n <div\n key={index}\n className=\"flex w-full items-center gap-4 text-left\"\n >\n <Icon\n className=\"w-7 h-7 shrink-0\"\n style={{ color: ICON_COLOR }}\n />\n <span className=\"flex-1 text-base md:text-lg text-[#3C3C40]\">\n {item.text}\n </span>\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n\n const hasCustomError = !!nonCompliantCase?.items?.length;\n const hasCustomSuccess = !!compliantCase?.items?.length;\n\n // En attente du verdict backend : éviter le flash de l'écran succès.\n if (isResolving) {\n return (\n <LoadingState\n message={t(\"session.end.finalizing\", \"Finalisation du parcours...\")}\n subtitle=\"\"\n />\n );\n }\n\n return (\n <MobilePageLayout\n footer={\n <PageActions\n primary={\n <Button\n onClick={handleEndSession}\n disabled={isEnding || isRedirecting}\n >\n {isRedirecting\n ? t(\"session.end.button_redirecting\", \"Redirection...\")\n : isEnding\n ? t(\"session.end.button_finalizing\", \"Finalisation...\")\n : t(\"session.end.button_end\", \"Terminer\")}\n </Button>\n }\n />\n }\n >\n {isError ? (\n hasCustomError ? (\n renderCustomCase(\n nonCompliantCase as EndScreenCase,\n t(\"session.end.error_title\", \"Les prochaines étapes\"),\n )\n ) : (\n <div className=\"flex flex-1 flex-col items-center h-full px-4 pt-10 pb-8 md:px-8\">\n {/* Titre ancré en haut */}\n <Title className=\"text-2xl md:text-3xl text-center\">\n {t(\"session.end.error_title\", \"Les prochaines étapes\")}\n </Title>\n\n {/* Liste centrée verticalement dans l'espace restant */}\n <div className=\"flex flex-1 items-center w-full max-w-md mx-auto\">\n {/* Translate desktop-only : sur mobile il tronquait le texte. */}\n <div\n className={`flex w-full flex-col items-start gap-7 -translate-y-20 ${desktopTranslateX}`}\n >\n {[\n {\n Icon: HeartHandshake,\n key: \"session.end.error_step_thanks\",\n fallback: \"Nous vous remercions pour ces informations\",\n },\n {\n Icon: Send,\n key: \"session.end.error_step_transmitted\",\n fallback: \"Votre dossier a été transmis à notre équipe\",\n },\n {\n Icon: Mail,\n key: \"session.end.error_step_contact\",\n fallback: \"Nous vous recontacterons si nécessaire\",\n },\n ].map(({ Icon, key, fallback }) => (\n <div\n key={key}\n className=\"flex w-full items-center gap-4 text-left\"\n >\n <Icon\n className=\"w-7 h-7 shrink-0\"\n style={{ color: ICON_COLOR }}\n />\n <span className=\"flex-1 text-base md:text-lg text-[#3C3C40]\">\n {t(key, fallback)}\n </span>\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n ) : hasCustomSuccess ? (\n renderCustomCase(\n compliantCase as EndScreenCase,\n t(\"session.end.title\", \"C'est tout bon !\"),\n )\n ) : (\n <div className=\"flex flex-1 flex-col items-center justify-center h-full px-4 py-8 md:px-8\">\n <div className=\"w-full max-w-md mx-auto text-center space-y-6\">\n {/* Icon - shown on top for both mobile and desktop */}\n <div className=\"flex justify-center\">\n <img\n src={checkIcon}\n alt={t(\"session.end.image_alt\", \"Vérification terminée\")}\n className=\"w-48 h-48\"\n />\n </div>\n\n {/* Text content */}\n <div className=\"space-y-4\">\n <Title className=\"text-xl md:text-2xl lg:text-3xl\">\n {t(\"session.end.title\", \"C'est tout bon !\")}\n </Title>\n <Subtitle className=\"text-sm md:text-base text-gray-600 leading-relaxed\">\n {t(\n \"session.end.subtitle\",\n \"Merci d'avoir terminé ce parcours, vous pouvez quitter cette fenêtre.\",\n )}\n </Subtitle>\n </div>\n </div>\n </div>\n )}\n </MobilePageLayout>\n );\n};\n\nexport default EndFlow;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
+ import { getSessionCredential } from '../../services/sessionCredentials.js';
2
3
  import { useState, useEffect } from 'react';
3
4
  import useIsMobile from '../../hooks/useIsMobile.js';
4
5
  import QRCodeDisplay from '../ui/QRCodeDisplay.js';
@@ -16,6 +17,9 @@ var MobileRedirect = function (_a) {
16
17
  var url = new URL(baseUrl);
17
18
  url.searchParams.set("sessionId", sessionId);
18
19
  url.searchParams.set("mobile", "true");
20
+ var token = getSessionCredential(sessionId);
21
+ if (token)
22
+ url.hash = new URLSearchParams({ sessionToken: token }).toString();
19
23
  var mobileUrl = url.toString();
20
24
  setCurrentUrl(mobileUrl);
21
25
  }, [sessionId]);
@@ -1 +1 @@
1
- {"version":3,"file":"MobileRedirect.js","sources":["../../../../../src/components/session/MobileRedirect.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport useIsMobile from \"../../hooks/useIsMobile\";\nimport QRCodeDisplay from \"../ui/QRCodeDisplay\";\n\ninterface MobileRedirectProps {\n sessionId: string;\n children?: React.ReactNode;\n onBack?: () => void;\n onContinueOnPC?: () => void;\n title?: string;\n subtitle?: string;\n isDesktopAllowed?: boolean;\n}\n\nconst MobileRedirect = ({\n sessionId,\n children,\n onBack,\n onContinueOnPC,\n isDesktopAllowed,\n title,\n subtitle,\n}: MobileRedirectProps) => {\n const isMobile = useIsMobile();\n const [currentUrl, setCurrentUrl] = useState(\"\");\n\n useEffect(() => {\n // Only generate URL if sessionId is available\n if (!sessionId) return;\n\n // Generate the mobile URL with the session ID\n const baseUrl = window.location.origin + window.location.pathname;\n const url = new URL(baseUrl);\n url.searchParams.set(\"sessionId\", sessionId);\n url.searchParams.set(\"mobile\", \"true\");\n const mobileUrl = url.toString();\n\n setCurrentUrl(mobileUrl);\n }, [sessionId]);\n\n // If the user is on mobile, automatically continue to next step\n useEffect(() => {\n if (isMobile) {\n if (onContinueOnPC) {\n onContinueOnPC();\n } else {\n // Even if onContinueOnPC is not defined, we should not show the QR code on mobile\n console.warn(\n \"User is on mobile but onContinueOnPC callback is not provided. Cannot proceed automatically.\"\n );\n }\n }\n }, [isMobile, onContinueOnPC]);\n\n // If the user is on mobile, show the normal session flow (or will redirect automatically)\n if (isMobile) {\n return <>{children || null}</>;\n }\n\n // If the user is on desktop, show the QR code redirect\n return (\n <QRCodeDisplay\n url={currentUrl}\n title={title || \"Continuer sur mobile\"}\n subtitle={subtitle || \"Scannez ce QR code pour continuer la vérification d'identité sur votre smartphone.\"}\n onBack={onBack}\n onContinueOnPC={onContinueOnPC}\n isDesktopAllowed={isDesktopAllowed}\n />\n );\n};\n\nexport default MobileRedirect;\n"],"names":["_jsx","_Fragment"],"mappings":";;;;;AAcA,IAAM,cAAc,GAAG,UAAC,EAQF,EAAA;AAPpB,IAAA,IAAA,SAAS,eAAA,EACT,QAAQ,cAAA,EACR,MAAM,YAAA,EACN,cAAc,GAAA,EAAA,CAAA,cAAA,EACd,gBAAgB,GAAA,EAAA,CAAA,gBAAA,EAChB,KAAK,GAAA,EAAA,CAAA,KAAA,EACL,QAAQ,GAAA,EAAA,CAAA,QAAA;AAER,IAAA,IAAM,QAAQ,GAAG,WAAW,EAAE;IACxB,IAAA,EAAA,GAA8B,QAAQ,CAAC,EAAE,CAAC,EAAzC,UAAU,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,aAAa,GAAA,EAAA,CAAA,CAAA,CAAgB;AAEhD,IAAA,SAAS,CAAC,YAAA;;AAER,QAAA,IAAI,CAAC,SAAS;YAAE;;AAGhB,QAAA,IAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ;AACjE,QAAA,IAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC;QAC5B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC;QAC5C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC;AACtC,QAAA,IAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,EAAE;QAEhC,aAAa,CAAC,SAAS,CAAC;AAC1B,IAAA,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;;AAGf,IAAA,SAAS,CAAC,YAAA;QACR,IAAI,QAAQ,EAAE;YACZ,IAAI,cAAc,EAAE;AAClB,gBAAA,cAAc,EAAE;YAClB;iBAAO;;AAEL,gBAAA,OAAO,CAAC,IAAI,CACV,8FAA8F,CAC/F;YACH;QACF;AACF,IAAA,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;;IAG9B,IAAI,QAAQ,EAAE;AACZ,QAAA,OAAOA,GAAA,CAAAC,QAAA,EAAA,EAAA,QAAA,EAAG,QAAQ,IAAI,IAAI,GAAI;IAChC;;AAGA,IAAA,QACED,GAAA,CAAC,aAAa,EAAA,EACZ,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,KAAK,IAAI,sBAAsB,EACtC,QAAQ,EAAE,QAAQ,IAAI,oFAAoF,EAC1G,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,cAAc,EAC9B,gBAAgB,EAAE,gBAAgB,EAAA,CAClC;AAEN;;;;"}
1
+ {"version":3,"file":"MobileRedirect.js","sources":["../../../../../src/components/session/MobileRedirect.tsx"],"sourcesContent":["import { getSessionCredential } from '../../services/sessionCredentials';\nimport { useEffect, useState } from \"react\";\nimport useIsMobile from \"../../hooks/useIsMobile\";\nimport QRCodeDisplay from \"../ui/QRCodeDisplay\";\n\ninterface MobileRedirectProps {\n sessionId: string;\n children?: React.ReactNode;\n onBack?: () => void;\n onContinueOnPC?: () => void;\n title?: string;\n subtitle?: string;\n isDesktopAllowed?: boolean;\n}\n\nconst MobileRedirect = ({\n sessionId,\n children,\n onBack,\n onContinueOnPC,\n isDesktopAllowed,\n title,\n subtitle,\n}: MobileRedirectProps) => {\n const isMobile = useIsMobile();\n const [currentUrl, setCurrentUrl] = useState(\"\");\n\n useEffect(() => {\n // Only generate URL if sessionId is available\n if (!sessionId) return;\n\n // Generate the mobile URL with the session ID\n const baseUrl = window.location.origin + window.location.pathname;\n const url = new URL(baseUrl);\n url.searchParams.set(\"sessionId\", sessionId);\n url.searchParams.set(\"mobile\", \"true\");\n const token = getSessionCredential(sessionId);\n if (token) url.hash = new URLSearchParams({ sessionToken: token }).toString();\n const mobileUrl = url.toString();\n\n setCurrentUrl(mobileUrl);\n }, [sessionId]);\n\n // If the user is on mobile, automatically continue to next step\n useEffect(() => {\n if (isMobile) {\n if (onContinueOnPC) {\n onContinueOnPC();\n } else {\n // Even if onContinueOnPC is not defined, we should not show the QR code on mobile\n console.warn(\n \"User is on mobile but onContinueOnPC callback is not provided. Cannot proceed automatically.\"\n );\n }\n }\n }, [isMobile, onContinueOnPC]);\n\n // If the user is on mobile, show the normal session flow (or will redirect automatically)\n if (isMobile) {\n return <>{children || null}</>;\n }\n\n // If the user is on desktop, show the QR code redirect\n return (\n <QRCodeDisplay\n url={currentUrl}\n title={title || \"Continuer sur mobile\"}\n subtitle={subtitle || \"Scannez ce QR code pour continuer la vérification d'identité sur votre smartphone.\"}\n onBack={onBack}\n onContinueOnPC={onContinueOnPC}\n isDesktopAllowed={isDesktopAllowed}\n />\n );\n};\n\nexport default MobileRedirect;\n"],"names":["_jsx","_Fragment"],"mappings":";;;;;;AAeA,IAAM,cAAc,GAAG,UAAC,EAQF,EAAA;AAPpB,IAAA,IAAA,SAAS,eAAA,EACT,QAAQ,cAAA,EACR,MAAM,YAAA,EACN,cAAc,GAAA,EAAA,CAAA,cAAA,EACd,gBAAgB,GAAA,EAAA,CAAA,gBAAA,EAChB,KAAK,GAAA,EAAA,CAAA,KAAA,EACL,QAAQ,GAAA,EAAA,CAAA,QAAA;AAER,IAAA,IAAM,QAAQ,GAAG,WAAW,EAAE;IACxB,IAAA,EAAA,GAA8B,QAAQ,CAAC,EAAE,CAAC,EAAzC,UAAU,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,aAAa,GAAA,EAAA,CAAA,CAAA,CAAgB;AAEhD,IAAA,SAAS,CAAC,YAAA;;AAER,QAAA,IAAI,CAAC,SAAS;YAAE;;AAGhB,QAAA,IAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ;AACjE,QAAA,IAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC;QAC5B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC;QAC5C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC;AACtC,QAAA,IAAM,KAAK,GAAG,oBAAoB,CAAC,SAAS,CAAC;AAC7C,QAAA,IAAI,KAAK;AAAE,YAAA,GAAG,CAAC,IAAI,GAAG,IAAI,eAAe,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,EAAE;AAC7E,QAAA,IAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,EAAE;QAEhC,aAAa,CAAC,SAAS,CAAC;AAC1B,IAAA,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;;AAGf,IAAA,SAAS,CAAC,YAAA;QACR,IAAI,QAAQ,EAAE;YACZ,IAAI,cAAc,EAAE;AAClB,gBAAA,cAAc,EAAE;YAClB;iBAAO;;AAEL,gBAAA,OAAO,CAAC,IAAI,CACV,8FAA8F,CAC/F;YACH;QACF;AACF,IAAA,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;;IAG9B,IAAI,QAAQ,EAAE;AACZ,QAAA,OAAOA,GAAA,CAAAC,QAAA,EAAA,EAAA,QAAA,EAAG,QAAQ,IAAI,IAAI,GAAI;IAChC;;AAGA,IAAA,QACED,GAAA,CAAC,aAAa,EAAA,EACZ,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,KAAK,IAAI,sBAAsB,EACtC,QAAQ,EAAE,QAAQ,IAAI,oFAAoF,EAC1G,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,cAAc,EAC9B,gBAAgB,EAAE,gBAAgB,EAAA,CAClC;AAEN;;;;"}