datakeen-session-react 1.1.190 → 1.1.192
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/DatakeenSession.js +7 -1
- package/dist/cjs/components/DatakeenSession.js.map +1 -1
- package/dist/cjs/components/document-collection/DocumentCollection.js +42 -29
- package/dist/cjs/components/document-collection/DocumentCollection.js.map +1 -1
- package/dist/cjs/components/session/EndFlow.js +3 -2
- package/dist/cjs/components/session/EndFlow.js.map +1 -1
- package/dist/cjs/components/session/MobileRedirect.js +4 -0
- package/dist/cjs/components/session/MobileRedirect.js.map +1 -1
- package/dist/cjs/components/session/PdfGeneration.js +25 -19
- package/dist/cjs/components/session/PdfGeneration.js.map +1 -1
- package/dist/cjs/components/session/StartSession.js +16 -5
- package/dist/cjs/components/session/StartSession.js.map +1 -1
- package/dist/cjs/components/session/UserInputForm/CustomFormFields.js +7 -14
- package/dist/cjs/components/session/UserInputForm/CustomFormFields.js.map +1 -1
- package/dist/cjs/components/session/UserInputForm.js +2 -2
- package/dist/cjs/components/session/UserInputForm.js.map +1 -1
- package/dist/cjs/components/signature-electronic/SignedDocumentPreview.js +12 -14
- package/dist/cjs/components/signature-electronic/SignedDocumentPreview.js.map +1 -1
- package/dist/cjs/components/template/ExternalVerificationNodeHandler.js +67 -4
- package/dist/cjs/components/template/ExternalVerificationNodeHandler.js.map +1 -1
- package/dist/cjs/hooks/useSession.js +3 -3
- package/dist/cjs/hooks/useSession.js.map +1 -1
- package/dist/cjs/hooks/useUserInputForm.js +13 -9
- package/dist/cjs/hooks/useUserInputForm.js.map +1 -1
- package/dist/cjs/i18n/en.json.js +4 -1
- package/dist/cjs/i18n/en.json.js.map +1 -1
- package/dist/cjs/i18n/fr.json.js +4 -1
- package/dist/cjs/i18n/fr.json.js.map +1 -1
- package/dist/cjs/services/api.js +5 -0
- package/dist/cjs/services/api.js.map +1 -1
- package/dist/cjs/services/auditTrailService.js +19 -9
- package/dist/cjs/services/auditTrailService.js.map +1 -1
- package/dist/cjs/services/integrationService.js +2 -0
- package/dist/cjs/services/integrationService.js.map +1 -1
- package/dist/cjs/services/sessionCredentials.js +24 -0
- package/dist/cjs/services/sessionCredentials.js.map +1 -0
- package/dist/cjs/types/session.js.map +1 -1
- package/dist/cjs/utils/customFieldIds.js +52 -0
- package/dist/cjs/utils/customFieldIds.js.map +1 -0
- package/dist/cjs/utils/jdiCodes.js +3 -1
- package/dist/cjs/utils/jdiCodes.js.map +1 -1
- package/dist/cjs/utils/navigation.js +21 -0
- package/dist/cjs/utils/navigation.js.map +1 -0
- package/dist/esm/components/DatakeenSession.js +8 -2
- package/dist/esm/components/DatakeenSession.js.map +1 -1
- package/dist/esm/components/document-collection/DocumentCollection.js +43 -30
- package/dist/esm/components/document-collection/DocumentCollection.js.map +1 -1
- package/dist/esm/components/session/EndFlow.js +3 -2
- package/dist/esm/components/session/EndFlow.js.map +1 -1
- package/dist/esm/components/session/MobileRedirect.js +4 -0
- package/dist/esm/components/session/MobileRedirect.js.map +1 -1
- package/dist/esm/components/session/PdfGeneration.js +25 -19
- package/dist/esm/components/session/PdfGeneration.js.map +1 -1
- package/dist/esm/components/session/StartSession.js +16 -5
- package/dist/esm/components/session/StartSession.js.map +1 -1
- package/dist/esm/components/session/UserInputForm/CustomFormFields.js +7 -14
- package/dist/esm/components/session/UserInputForm/CustomFormFields.js.map +1 -1
- package/dist/esm/components/session/UserInputForm.js +2 -2
- package/dist/esm/components/session/UserInputForm.js.map +1 -1
- package/dist/esm/components/signature-electronic/SignedDocumentPreview.js +12 -14
- package/dist/esm/components/signature-electronic/SignedDocumentPreview.js.map +1 -1
- package/dist/esm/components/template/ExternalVerificationNodeHandler.js +67 -4
- package/dist/esm/components/template/ExternalVerificationNodeHandler.js.map +1 -1
- package/dist/esm/hooks/useSession.js +3 -3
- package/dist/esm/hooks/useSession.js.map +1 -1
- package/dist/esm/hooks/useUserInputForm.js +13 -9
- package/dist/esm/hooks/useUserInputForm.js.map +1 -1
- package/dist/esm/i18n/en.json.js +4 -1
- package/dist/esm/i18n/en.json.js.map +1 -1
- package/dist/esm/i18n/fr.json.js +4 -1
- package/dist/esm/i18n/fr.json.js.map +1 -1
- package/dist/esm/services/api.js +5 -0
- package/dist/esm/services/api.js.map +1 -1
- package/dist/esm/services/auditTrailService.js +19 -9
- package/dist/esm/services/auditTrailService.js.map +1 -1
- package/dist/esm/services/integrationService.js +2 -0
- package/dist/esm/services/integrationService.js.map +1 -1
- package/dist/esm/services/sessionCredentials.js +20 -0
- package/dist/esm/services/sessionCredentials.js.map +1 -0
- package/dist/esm/types/session.js.map +1 -1
- package/dist/esm/utils/customFieldIds.js +50 -0
- package/dist/esm/utils/customFieldIds.js.map +1 -0
- package/dist/esm/utils/jdiCodes.js +3 -1
- package/dist/esm/utils/jdiCodes.js.map +1 -1
- package/dist/esm/utils/navigation.js +19 -0
- package/dist/esm/utils/navigation.js.map +1 -0
- package/dist/types/components/DatakeenSession.d.ts +1 -1
- package/dist/types/hooks/useSession.d.ts +1 -1
- package/dist/types/types/session.d.ts +3 -0
- package/docs/SESSION_TOKEN_MIGRATION.md +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Memory-only credentials, bound to a session and never sent to unrelated URLs. */
|
|
2
|
+
var credentials = new Map();
|
|
3
|
+
function setSessionCredential(sessionId, token) {
|
|
4
|
+
if (token)
|
|
5
|
+
credentials.set(sessionId, token);
|
|
6
|
+
else
|
|
7
|
+
credentials.delete(sessionId);
|
|
8
|
+
}
|
|
9
|
+
function getSessionCredential(sessionId) {
|
|
10
|
+
return credentials.get(sessionId);
|
|
11
|
+
}
|
|
12
|
+
function credentialForRequest(url) {
|
|
13
|
+
if (/^[a-z]+:/i.test(url) || url.startsWith('//'))
|
|
14
|
+
return undefined;
|
|
15
|
+
var match = url.match(/^\/?session\/sdk\/([^/?]+)/);
|
|
16
|
+
return match ? credentials.get(decodeURIComponent(match[1])) : undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { credentialForRequest, getSessionCredential, setSessionCredential };
|
|
20
|
+
//# 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,50 @@
|
|
|
1
|
+
import { __assign } from '../node_modules/tslib/tslib.es6.js';
|
|
2
|
+
|
|
3
|
+
/** Séparateur du suffixe ajouté à un identifiant en collision. */
|
|
4
|
+
var DUPLICATE_SUFFIX = "__dup";
|
|
5
|
+
/**
|
|
6
|
+
* Garantit l'unicité des identifiants de champs d'un formulaire personnalisé.
|
|
7
|
+
*
|
|
8
|
+
* Tout l'état du formulaire est indexé par `field.id` (valeur saisie, erreur,
|
|
9
|
+
* erreurs de cellule). Deux champs partageant un identifiant partagent donc
|
|
10
|
+
* leur valeur : taper dans l'un remplit l'autre, et un seul des deux est
|
|
11
|
+
* finalement transmis.
|
|
12
|
+
*
|
|
13
|
+
* Le builder a longtemps dérivé l'identifiant de `Date.now()` seul, sans
|
|
14
|
+
* composante aléatoire : deux champs enregistrés dans la même milliseconde
|
|
15
|
+
* (double clic, ajout programmatique, import) se retrouvent avec le même.
|
|
16
|
+
* Les parcours déjà publiés portent ces doublons, que le SDK doit servir sans
|
|
17
|
+
* attendre une ré-édition côté backoffice.
|
|
18
|
+
*
|
|
19
|
+
* La première occurrence garde son identifiant — les conditions de parcours et
|
|
20
|
+
* les sessions existantes qui le référencent continuent de fonctionner. Seules
|
|
21
|
+
* les suivantes reçoivent un suffixe déterministe, donc stable d'un rendu à
|
|
22
|
+
* l'autre. La clé métier envoyée à l'API (`userInputKey` ou le label
|
|
23
|
+
* normalisé) n'est pas touchée.
|
|
24
|
+
*/
|
|
25
|
+
function dedupeCustomFieldIds(fields) {
|
|
26
|
+
if (!fields || fields.length === 0)
|
|
27
|
+
return [];
|
|
28
|
+
var seen = new Set();
|
|
29
|
+
var hasDuplicate = false;
|
|
30
|
+
var deduped = fields.map(function (field, index) {
|
|
31
|
+
var id = field.id;
|
|
32
|
+
if (!seen.has(id)) {
|
|
33
|
+
seen.add(id);
|
|
34
|
+
return field;
|
|
35
|
+
}
|
|
36
|
+
hasDuplicate = true;
|
|
37
|
+
var candidate = "".concat(id).concat(DUPLICATE_SUFFIX).concat(index);
|
|
38
|
+
// Un parcours peut déjà contenir un identifiant homonyme du suffixe.
|
|
39
|
+
while (seen.has(candidate))
|
|
40
|
+
candidate = "".concat(candidate, "_");
|
|
41
|
+
seen.add(candidate);
|
|
42
|
+
return __assign(__assign({}, field), { id: candidate });
|
|
43
|
+
});
|
|
44
|
+
// Référence stable quand il n'y a rien à corriger : le cas normal ne doit pas
|
|
45
|
+
// invalider les mémos qui dépendent de la liste.
|
|
46
|
+
return hasDuplicate ? deduped : fields;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { dedupeCustomFieldIds };
|
|
50
|
+
//# 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":[],"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,OAAA,QAAA,CAAA,QAAA,CAAA,EAAA,EAAY,KAAK,CAAA,EAAA,EAAE,EAAE,EAAE,SAAS,EAAA,CAAA;AAClC,IAAA,CAAC,CAAC;;;IAIF,OAAO,YAAY,GAAG,OAAO,GAAG,MAAM;AACxC;;;;"}
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Miroir exact du catalogue frontend (app-frontend/src/utils/jdiCodes.ts) —
|
|
13
13
|
* les deux listes doivent rester synchronisées.
|
|
14
|
+
*
|
|
15
|
+
* 2026-09-03 : 7.1 retiré (décision produit, cf. scripts/remediate-controle-jdi-blocking-codes.ts
|
|
16
|
+
* côté app-backend pour le nettoyage des configs `blockingCodes` déjà sauvegardées).
|
|
14
17
|
*/
|
|
15
18
|
var DEFAULT_BLOCKING_CODES = [
|
|
16
19
|
'2.0',
|
|
@@ -22,7 +25,6 @@ var DEFAULT_BLOCKING_CODES = [
|
|
|
22
25
|
'2.6',
|
|
23
26
|
'3.0',
|
|
24
27
|
'7.0',
|
|
25
|
-
'7.1',
|
|
26
28
|
];
|
|
27
29
|
/**
|
|
28
30
|
* 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
|
|
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,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indirection minimale devant `window.location`.
|
|
3
|
+
*
|
|
4
|
+
* jsdom ≥ 22 déclare `window.location` non-configurable et `location.replace`
|
|
5
|
+
* non-inscriptible : `delete window.location` renvoie `false` et toute
|
|
6
|
+
* réassignation échoue en silence. Une redirection appelée directement sur
|
|
7
|
+
* `window.location` n'est donc plus observable en test — elle se contente
|
|
8
|
+
* d'émettre « Not implemented: navigation » dans la console jsdom, ce qui fait
|
|
9
|
+
* passer un test qui n'a en réalité rien vérifié.
|
|
10
|
+
*
|
|
11
|
+
* Ce module est interne : il n'est pas exporté par `src/index.ts` et ne fait
|
|
12
|
+
* pas partie du contrat public du paquet.
|
|
13
|
+
*/
|
|
14
|
+
function replaceLocation(url) {
|
|
15
|
+
window.location.replace(url);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { replaceLocation };
|
|
19
|
+
//# 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;;;;"}
|
|
@@ -20,6 +20,6 @@ import { DatakeenSessionProps } from '../types/session.js';
|
|
|
20
20
|
* @param {string} props.apiBaseUrl - Optional API base URL for dynamic environment configuration
|
|
21
21
|
* @returns {JSX.Element} A Paper-wrapped container with the appropriate step component based on current state
|
|
22
22
|
*/
|
|
23
|
-
declare const DatakeenSession: ({ sessionId, sessionConfig, apiBaseUrl, }: DatakeenSessionProps) => React.JSX.Element;
|
|
23
|
+
declare const DatakeenSession: ({ sessionId, sessionToken, sessionConfig, apiBaseUrl, }: DatakeenSessionProps) => React.JSX.Element;
|
|
24
24
|
|
|
25
25
|
export { DatakeenSession as default };
|
|
@@ -7,6 +7,6 @@ import { SessionConfig, UseSessionReturn } from '../types/session.js';
|
|
|
7
7
|
* @param {string} apiBaseUrl - Optional API base URL for dynamic environment configuration.
|
|
8
8
|
* @returns {UseSessionReturn} - An object containing the session component. The session component is a React element that can be rendered in your application.
|
|
9
9
|
*/
|
|
10
|
-
declare const useSession: (sessionId: string, sessionConfig?: SessionConfig, apiBaseUrl?: string) => UseSessionReturn;
|
|
10
|
+
declare const useSession: (sessionId: string, sessionConfig?: SessionConfig, apiBaseUrl?: string, sessionToken?: string) => UseSessionReturn;
|
|
11
11
|
|
|
12
12
|
export { useSession as default };
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
|
|
3
3
|
interface SessionConfig {
|
|
4
|
+
sessionToken?: string;
|
|
4
5
|
selfie?: boolean;
|
|
5
6
|
requireMobile?: boolean;
|
|
6
7
|
}
|
|
7
8
|
interface DatakeenSessionProps {
|
|
9
|
+
/** Session credential returned by server-side session creation. Required by secured backends. */
|
|
10
|
+
sessionToken?: string;
|
|
8
11
|
sessionId: string;
|
|
9
12
|
sessionConfig?: SessionConfig;
|
|
10
13
|
apiBaseUrl?: string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Migration vers les sessions authentifiées
|
|
2
|
+
|
|
3
|
+
Le backend protégé refuse l'identifiant de session seul. L'intégrateur récupère
|
|
4
|
+
`token` et `sessionURL` lors de la création côté serveur et remet uniquement le
|
|
5
|
+
credential de cette session à son utilisateur final.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
const { SessionComponent } = useSession(sessionId, config, apiBaseUrl, sessionToken);
|
|
9
|
+
// ou : <DatakeenSession sessionId={sessionId} sessionToken={sessionToken} />
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Le paramètre est additif côté TypeScript pour les anciens backends, mais requis
|
|
13
|
+
à l'exécution avec le backend durci. `SessionConfig.sessionToken` est aussi accepté.
|
|
14
|
+
Le lien généré par le backend contient `#sessionToken=...` : le fragment n'est pas
|
|
15
|
+
transmis au serveur HTTP ou dans le Referer. Le SDK le lit en mémoire et transmet
|
|
16
|
+
`X-Session-Token` uniquement aux routes de la session correspondante. Le QR de
|
|
17
|
+
transfert mobile conserve ce fragment. Ne pas enregistrer ce lien dans les logs.
|
|
18
|
+
|
|
19
|
+
Les appels directs doivent transmettre cet en-tête. Les médias générés/signés
|
|
20
|
+
sont chargés par Axios authentifié puis ouverts via une URL Blob locale, révoquée
|
|
21
|
+
au démontage. Aucun credential n'est ajouté à une URL de média.
|
|
22
|
+
|
|
23
|
+
## Fenêtre de compatibilité
|
|
24
|
+
|
|
25
|
+
Le backend expose `SESSION_TOKEN_ENFORCED`, **à `false` par défaut**. Pendant cette
|
|
26
|
+
période :
|
|
27
|
+
|
|
28
|
+
- les liens de session déjà envoyés par courriel ou SMS continuent de fonctionner ;
|
|
29
|
+
- une intégration restée sur une version antérieure du SDK continue de fonctionner ;
|
|
30
|
+
- le backend journalise un avertissement par session
|
|
31
|
+
(`accessed without X-Session-Token`), ce qui permet d'inventorier les liens
|
|
32
|
+
encore en circulation ;
|
|
33
|
+
- la tolérance ne s'applique qu'à l'utilisateur final, sur une route du parcours.
|
|
34
|
+
Les routes purement backoffice (selfie, vidéos et aperçus recto/verso, résumé,
|
|
35
|
+
chat, informations client) exigent un credential dès le premier jour.
|
|
36
|
+
|
|
37
|
+
Ordre de déploiement : livrer le backend, puis le backoffice, puis publier le SDK ;
|
|
38
|
+
renouveler les liens et faire migrer les intégrateurs qui construisent leurs liens
|
|
39
|
+
eux-mêmes ; enfin basculer `SESSION_TOKEN_ENFORCED=true` pour fermer la fenêtre.
|
|
40
|
+
Aucune publication de package n'est effectuée par cette migration de source.
|
|
41
|
+
|
|
42
|
+
## Écriture de l’audit trail
|
|
43
|
+
|
|
44
|
+
`POST /session/audit-trail` exige désormais un token de session, indépendamment
|
|
45
|
+
de SESSION_TOKEN_ENFORCED. Le SDK joint X-Session-Token depuis son stockage mémoire
|
|
46
|
+
pour l’analysisId (identifiant de session historique) de l’événement. Il n’envoie
|
|
47
|
+
jamais ce token dans une URL. Le serveur impose la cible et refuse toute attribution
|
|
48
|
+
à un opérateur fournie par le client. Les valeurs de userInputs et les métadonnées
|
|
49
|
+
libres ne sont plus conservées : l’audit ne garde que les noms des champs transmis.
|
|
50
|
+
Les lectures de timeline restent réservées au backoffice avec AUDIT_TRAIL:READ.
|
|
51
|
+
Livrer cette version du SDK et fournir sessionToken aux intégrations pour continuer
|
|
52
|
+
à enregistrer les événements du parcours, dont l’ouverture et la fin de signature.
|