@labyrinthe/otp-widget 0.0.0-stage → 1.2.0

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.
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Protocole JSON entre le widget (navigateur) et le serveur de l'intégrateur
3
+ * (`@labyrinthe/otp-widget/server`). Le navigateur ne voit jamais la clé API,
4
+ * ni `otp_id`, ni le code envoyé : seulement ce qui suit.
5
+ *
6
+ * POST {sendUrl} { "phone": "+243812345678", "locale": "fr", "send_id": "…" }
7
+ * 200 → SendSuccessBody erreur → ErrorBody
8
+ * POST {verifyUrl} { "code": "482913" }
9
+ * 200 → VerifySuccessBody | VerifyFailureBody erreur → ErrorBody
10
+ */
11
+ /** Why a code was refused (same values as the Labyrinthe API `reason`). */
12
+ type OtpFailureReason = 'invalid_code' | 'expired' | 'already_used' | 'max_attempts_reached';
13
+ /** Error codes sent by the server helper (stable, lower case). */
14
+ type ServerErrorCode =
15
+ /** The phone number cannot be used (invalid, blacklisted or opted out). */
16
+ 'invalid_phone'
17
+ /** A code was sent to this number too recently: wait `retry_after_seconds`. */
18
+ | 'resend_too_soon'
19
+ /** Too many requests (per number, per account or per API key). */
20
+ | 'rate_limited'
21
+ /** No code is pending for this browser (cookie missing, expired or tampered with): send a new one. */
22
+ | 'no_pending_verification'
23
+ /** Refused by the integrator (`beforeSend`) or by the origin / content-type checks. */
24
+ | 'forbidden'
25
+ /** Malformed request (missing field, wrong content type…). */
26
+ | 'invalid_request'
27
+ /**
28
+ * OTP service unavailable (Labyrinthe unreachable, misconfiguration, no credits…), or temporarily shut down by the
29
+ * Labyrinthe platform (FEATURE_DISABLED / OTP_SERVICE_DISABLED, maintenance): then `admin_message`, `until` and
30
+ * `retry_after_seconds` may be present.
31
+ */
32
+ | 'service_unavailable'
33
+ /** The integrator's Labyrinthe account is suspended (ACCOUNT_SUSPENDED): nothing the user can do. */
34
+ | 'account_suspended'
35
+ /**
36
+ * The send failed unexpectedly on the Labyrinthe side and its outcome is unknown (IDEMPOTENCY_KEY_UNRESOLVED):
37
+ * a code may have been sent. Never retried automatically; the user may ask for a new code (sent with a new key).
38
+ */
39
+ | 'send_failed'
40
+ /** Unexpected server error. */
41
+ | 'server_error';
42
+ /** Every error code the widget can report: server codes, refusal reasons and client-side errors. */
43
+ type OtpWidgetErrorCode = ServerErrorCode | OtpFailureReason | 'network_error' | 'incomplete_code';
44
+ type CodeType = 'numeric' | 'alphanumeric';
45
+ interface SendSuccessBody {
46
+ sent: true;
47
+ /** Masked number, for display ("+243 81•••••78"). */
48
+ phone_masked: string;
49
+ /** ISO 8601. */
50
+ expires_at: string;
51
+ max_attempts: number;
52
+ /** Seconds before "Resend" is enabled (account's anti-resend delay, returned by the API). */
53
+ resend_after_seconds: number;
54
+ /** Code length / alphabet (account's OTP settings, returned by the API 1.2.0, or the server's override). */
55
+ code_length?: number;
56
+ code_type?: CodeType;
57
+ /** ONLY when the integrator enabled `exposeTestCode` in development, with a `lsk_test_` key. */
58
+ test_code?: string;
59
+ }
60
+ interface VerifySuccessBody {
61
+ verified: true;
62
+ /** Whatever the integrator's `onVerified` returned (e.g. `{ redirect: "/dashboard" }`). */
63
+ data?: unknown;
64
+ }
65
+ interface VerifyFailureBody {
66
+ verified: false;
67
+ reason: OtpFailureReason;
68
+ /** Attempts left for this code (`attempts_remaining` of the API; estimated with an older SDK); null when unknown. */
69
+ attempts_left: number | null;
70
+ }
71
+ interface ErrorBody {
72
+ error: {
73
+ code: ServerErrorCode;
74
+ message: string;
75
+ retry_after_seconds?: number;
76
+ /** Maintenance only: message written by the Labyrinthe platform administrator, meant for end users. */
77
+ admin_message?: string;
78
+ /** Maintenance only: scheduled end (ISO 8601). */
79
+ until?: string;
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Texts of the widget. Placeholders: `{phone}`, `{time}`, `{count}`, `{index}`,
85
+ * `{total}`, `{code}`. Override any of them with the `texts` property (or React prop).
86
+ */
87
+ interface OtpWidgetTexts {
88
+ title: string;
89
+ phoneLabel: string;
90
+ countryCodeLabel: string;
91
+ phonePlaceholder: string;
92
+ phoneHint: string;
93
+ send: string;
94
+ sending: string;
95
+ codeLabel: string;
96
+ codeSentTo: string;
97
+ /** Accessible name of each code box. */
98
+ cellLabel: string;
99
+ verify: string;
100
+ verifying: string;
101
+ changePhone: string;
102
+ resend: string;
103
+ resendIn: string;
104
+ resendAvailable: string;
105
+ attemptsLeftOne: string;
106
+ attemptsLeftOther: string;
107
+ success: string;
108
+ testMode: string;
109
+ /** Appended to `errors.service_unavailable` (or the maintenance message) when the end of a maintenance is known. */
110
+ retryIn: string;
111
+ errors: Record<OtpWidgetErrorCode, string>;
112
+ }
113
+ type OtpWidgetLocale = 'fr' | 'en';
114
+ /** Partial override: top-level keys and `errors` entries are merged with the locale defaults. */
115
+ type OtpWidgetTextsOverride = Partial<Omit<OtpWidgetTexts, 'errors'>> & {
116
+ errors?: Partial<OtpWidgetTexts['errors']>;
117
+ };
118
+ declare const LOCALES: Readonly<Record<OtpWidgetLocale, OtpWidgetTexts>>;
119
+ /** Default texts of a locale merged with an override. */
120
+ declare function resolveTexts(locale: string | null | undefined, override?: OtpWidgetTextsOverride | null): OtpWidgetTexts;
121
+
122
+ declare const TAG_NAME = "labyrinthe-otp";
123
+ declare const EVENT_SENT = "labyrinthe-otp:sent";
124
+ declare const EVENT_VERIFIED = "labyrinthe-otp:verified";
125
+ declare const EVENT_ERROR = "labyrinthe-otp:error";
126
+ type OtpWidgetStep = 'phone' | 'code' | 'success';
127
+ /** `labyrinthe-otp:sent` — a code was sent (first send or resend). */
128
+ interface OtpSentDetail {
129
+ /** Normalised number (international, no "+"). */
130
+ phone: string;
131
+ phoneMasked: string;
132
+ expiresAt: Date;
133
+ maxAttempts: number;
134
+ resendAfterSeconds: number;
135
+ /** Only when the server exposes it (test key, development). */
136
+ testCode?: string;
137
+ }
138
+ /** `labyrinthe-otp:verified` — the code was accepted by the server. */
139
+ interface OtpVerifiedDetail {
140
+ phone: string;
141
+ /** What the integrator's `onVerified` returned. */
142
+ data: unknown;
143
+ }
144
+ /** `labyrinthe-otp:error` — anything that went wrong; `message` is the localised text shown to the user. */
145
+ interface OtpErrorDetail {
146
+ code: OtpWidgetErrorCode;
147
+ message: string;
148
+ /** HTTP status of the server response; null for local and network errors. */
149
+ status: number | null;
150
+ retryAfterSeconds?: number;
151
+ attemptsLeft?: number | null;
152
+ /** `service_unavailable` during a Labyrinthe maintenance: the administrator's message (shown instead of the text). */
153
+ adminMessage?: string;
154
+ /** `service_unavailable` during a Labyrinthe maintenance: scheduled end, when known. */
155
+ until?: Date;
156
+ }
157
+ /** Public API of the `<labyrinthe-otp>` element. */
158
+ interface LabyrintheOtpElement extends HTMLElement {
159
+ /** Current step. */
160
+ readonly step: OtpWidgetStep;
161
+ /** Text overrides (merged with the locale defaults). */
162
+ texts: OtpWidgetTextsOverride | null;
163
+ /** Extra headers sent to your endpoints (e.g. a CSRF token). */
164
+ requestHeaders: Record<string, string>;
165
+ /** Back to the phone step, clearing the code. */
166
+ reset(): void;
167
+ }
168
+
169
+ /**
170
+ * Normalisation des numéros, identique à celle du SDK Node (`PhoneNumber.normalize`
171
+ * de `@labyrinthe/connect`) et de l'API. Copiée ici pour garder le widget sans
172
+ * dépendance ; un test vérifie la parité avec le SDK.
173
+ */
174
+ /** Default country code added to local numbers (DR Congo). */
175
+ declare const DEFAULT_COUNTRY_CODE = "243";
176
+ /**
177
+ * Normalises a phone number to the API format (international, digits only, no "+"),
178
+ * or returns null when it cannot be a valid number.
179
+ *
180
+ * - "+243 81 234 5678", "00243812345678", "243812345678" → "243812345678"
181
+ * - "0812345678", "812345678" → "243812345678" (default country code added)
182
+ * - "+33 6 12 34 56 78" → "33612345678" (other countries are never blocked)
183
+ * - "33612345678" → "33612345678" (no "+", 11 digits or more not starting with 0: already international)
184
+ */
185
+ declare function normalizePhone(raw: string, defaultCountryCode?: string): string | null;
186
+ /** Masks a number for display and logs: "243812345678" → "+243 81•••••78". */
187
+ declare function maskPhone(phone: string): string;
188
+
189
+ declare const VERSION = "1.2.0";
190
+
191
+ /**
192
+ * `<labyrinthe-otp>`: phone verification by SMS code, ready to use.
193
+ *
194
+ * <script type="module">import '@labyrinthe/otp-widget';</script>
195
+ * <labyrinthe-otp send-url="/api/otp/send" verify-url="/api/otp/verify"></labyrinthe-otp>
196
+ *
197
+ * The widget only talks to YOUR server (see `@labyrinthe/otp-widget/server`):
198
+ * no API key, no `otp_id` and no code ever reach the browser.
199
+ *
200
+ * Importing this module defines the element when `customElements` exists;
201
+ * on a server (SSR) it does nothing.
202
+ */
203
+
204
+ /**
205
+ * Defines `<labyrinthe-otp>` (or another tag name) and returns its class.
206
+ * Idempotent; returns undefined where custom elements do not exist (SSR).
207
+ */
208
+ declare function defineLabyrintheOtp(tagName?: string): CustomElementConstructor | undefined;
209
+ declare global {
210
+ interface HTMLElementTagNameMap {
211
+ 'labyrinthe-otp': LabyrintheOtpElement;
212
+ }
213
+ }
214
+
215
+ export { type CodeType, DEFAULT_COUNTRY_CODE, EVENT_ERROR, EVENT_SENT, EVENT_VERIFIED, type ErrorBody, LOCALES, type LabyrintheOtpElement, type OtpErrorDetail, type OtpFailureReason, type OtpSentDetail, type OtpVerifiedDetail, type OtpWidgetErrorCode, type OtpWidgetLocale, type OtpWidgetStep, type OtpWidgetTexts, type OtpWidgetTextsOverride, type SendSuccessBody, type ServerErrorCode, TAG_NAME, VERSION, type VerifyFailureBody, type VerifySuccessBody, defineLabyrintheOtp, maskPhone, normalizePhone, resolveTexts };
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Protocole JSON entre le widget (navigateur) et le serveur de l'intégrateur
3
+ * (`@labyrinthe/otp-widget/server`). Le navigateur ne voit jamais la clé API,
4
+ * ni `otp_id`, ni le code envoyé : seulement ce qui suit.
5
+ *
6
+ * POST {sendUrl} { "phone": "+243812345678", "locale": "fr", "send_id": "…" }
7
+ * 200 → SendSuccessBody erreur → ErrorBody
8
+ * POST {verifyUrl} { "code": "482913" }
9
+ * 200 → VerifySuccessBody | VerifyFailureBody erreur → ErrorBody
10
+ */
11
+ /** Why a code was refused (same values as the Labyrinthe API `reason`). */
12
+ type OtpFailureReason = 'invalid_code' | 'expired' | 'already_used' | 'max_attempts_reached';
13
+ /** Error codes sent by the server helper (stable, lower case). */
14
+ type ServerErrorCode =
15
+ /** The phone number cannot be used (invalid, blacklisted or opted out). */
16
+ 'invalid_phone'
17
+ /** A code was sent to this number too recently: wait `retry_after_seconds`. */
18
+ | 'resend_too_soon'
19
+ /** Too many requests (per number, per account or per API key). */
20
+ | 'rate_limited'
21
+ /** No code is pending for this browser (cookie missing, expired or tampered with): send a new one. */
22
+ | 'no_pending_verification'
23
+ /** Refused by the integrator (`beforeSend`) or by the origin / content-type checks. */
24
+ | 'forbidden'
25
+ /** Malformed request (missing field, wrong content type…). */
26
+ | 'invalid_request'
27
+ /**
28
+ * OTP service unavailable (Labyrinthe unreachable, misconfiguration, no credits…), or temporarily shut down by the
29
+ * Labyrinthe platform (FEATURE_DISABLED / OTP_SERVICE_DISABLED, maintenance): then `admin_message`, `until` and
30
+ * `retry_after_seconds` may be present.
31
+ */
32
+ | 'service_unavailable'
33
+ /** The integrator's Labyrinthe account is suspended (ACCOUNT_SUSPENDED): nothing the user can do. */
34
+ | 'account_suspended'
35
+ /**
36
+ * The send failed unexpectedly on the Labyrinthe side and its outcome is unknown (IDEMPOTENCY_KEY_UNRESOLVED):
37
+ * a code may have been sent. Never retried automatically; the user may ask for a new code (sent with a new key).
38
+ */
39
+ | 'send_failed'
40
+ /** Unexpected server error. */
41
+ | 'server_error';
42
+ /** Every error code the widget can report: server codes, refusal reasons and client-side errors. */
43
+ type OtpWidgetErrorCode = ServerErrorCode | OtpFailureReason | 'network_error' | 'incomplete_code';
44
+ type CodeType = 'numeric' | 'alphanumeric';
45
+ interface SendSuccessBody {
46
+ sent: true;
47
+ /** Masked number, for display ("+243 81•••••78"). */
48
+ phone_masked: string;
49
+ /** ISO 8601. */
50
+ expires_at: string;
51
+ max_attempts: number;
52
+ /** Seconds before "Resend" is enabled (account's anti-resend delay, returned by the API). */
53
+ resend_after_seconds: number;
54
+ /** Code length / alphabet (account's OTP settings, returned by the API 1.2.0, or the server's override). */
55
+ code_length?: number;
56
+ code_type?: CodeType;
57
+ /** ONLY when the integrator enabled `exposeTestCode` in development, with a `lsk_test_` key. */
58
+ test_code?: string;
59
+ }
60
+ interface VerifySuccessBody {
61
+ verified: true;
62
+ /** Whatever the integrator's `onVerified` returned (e.g. `{ redirect: "/dashboard" }`). */
63
+ data?: unknown;
64
+ }
65
+ interface VerifyFailureBody {
66
+ verified: false;
67
+ reason: OtpFailureReason;
68
+ /** Attempts left for this code (`attempts_remaining` of the API; estimated with an older SDK); null when unknown. */
69
+ attempts_left: number | null;
70
+ }
71
+ interface ErrorBody {
72
+ error: {
73
+ code: ServerErrorCode;
74
+ message: string;
75
+ retry_after_seconds?: number;
76
+ /** Maintenance only: message written by the Labyrinthe platform administrator, meant for end users. */
77
+ admin_message?: string;
78
+ /** Maintenance only: scheduled end (ISO 8601). */
79
+ until?: string;
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Texts of the widget. Placeholders: `{phone}`, `{time}`, `{count}`, `{index}`,
85
+ * `{total}`, `{code}`. Override any of them with the `texts` property (or React prop).
86
+ */
87
+ interface OtpWidgetTexts {
88
+ title: string;
89
+ phoneLabel: string;
90
+ countryCodeLabel: string;
91
+ phonePlaceholder: string;
92
+ phoneHint: string;
93
+ send: string;
94
+ sending: string;
95
+ codeLabel: string;
96
+ codeSentTo: string;
97
+ /** Accessible name of each code box. */
98
+ cellLabel: string;
99
+ verify: string;
100
+ verifying: string;
101
+ changePhone: string;
102
+ resend: string;
103
+ resendIn: string;
104
+ resendAvailable: string;
105
+ attemptsLeftOne: string;
106
+ attemptsLeftOther: string;
107
+ success: string;
108
+ testMode: string;
109
+ /** Appended to `errors.service_unavailable` (or the maintenance message) when the end of a maintenance is known. */
110
+ retryIn: string;
111
+ errors: Record<OtpWidgetErrorCode, string>;
112
+ }
113
+ type OtpWidgetLocale = 'fr' | 'en';
114
+ /** Partial override: top-level keys and `errors` entries are merged with the locale defaults. */
115
+ type OtpWidgetTextsOverride = Partial<Omit<OtpWidgetTexts, 'errors'>> & {
116
+ errors?: Partial<OtpWidgetTexts['errors']>;
117
+ };
118
+ declare const LOCALES: Readonly<Record<OtpWidgetLocale, OtpWidgetTexts>>;
119
+ /** Default texts of a locale merged with an override. */
120
+ declare function resolveTexts(locale: string | null | undefined, override?: OtpWidgetTextsOverride | null): OtpWidgetTexts;
121
+
122
+ declare const TAG_NAME = "labyrinthe-otp";
123
+ declare const EVENT_SENT = "labyrinthe-otp:sent";
124
+ declare const EVENT_VERIFIED = "labyrinthe-otp:verified";
125
+ declare const EVENT_ERROR = "labyrinthe-otp:error";
126
+ type OtpWidgetStep = 'phone' | 'code' | 'success';
127
+ /** `labyrinthe-otp:sent` — a code was sent (first send or resend). */
128
+ interface OtpSentDetail {
129
+ /** Normalised number (international, no "+"). */
130
+ phone: string;
131
+ phoneMasked: string;
132
+ expiresAt: Date;
133
+ maxAttempts: number;
134
+ resendAfterSeconds: number;
135
+ /** Only when the server exposes it (test key, development). */
136
+ testCode?: string;
137
+ }
138
+ /** `labyrinthe-otp:verified` — the code was accepted by the server. */
139
+ interface OtpVerifiedDetail {
140
+ phone: string;
141
+ /** What the integrator's `onVerified` returned. */
142
+ data: unknown;
143
+ }
144
+ /** `labyrinthe-otp:error` — anything that went wrong; `message` is the localised text shown to the user. */
145
+ interface OtpErrorDetail {
146
+ code: OtpWidgetErrorCode;
147
+ message: string;
148
+ /** HTTP status of the server response; null for local and network errors. */
149
+ status: number | null;
150
+ retryAfterSeconds?: number;
151
+ attemptsLeft?: number | null;
152
+ /** `service_unavailable` during a Labyrinthe maintenance: the administrator's message (shown instead of the text). */
153
+ adminMessage?: string;
154
+ /** `service_unavailable` during a Labyrinthe maintenance: scheduled end, when known. */
155
+ until?: Date;
156
+ }
157
+ /** Public API of the `<labyrinthe-otp>` element. */
158
+ interface LabyrintheOtpElement extends HTMLElement {
159
+ /** Current step. */
160
+ readonly step: OtpWidgetStep;
161
+ /** Text overrides (merged with the locale defaults). */
162
+ texts: OtpWidgetTextsOverride | null;
163
+ /** Extra headers sent to your endpoints (e.g. a CSRF token). */
164
+ requestHeaders: Record<string, string>;
165
+ /** Back to the phone step, clearing the code. */
166
+ reset(): void;
167
+ }
168
+
169
+ /**
170
+ * Normalisation des numéros, identique à celle du SDK Node (`PhoneNumber.normalize`
171
+ * de `@labyrinthe/connect`) et de l'API. Copiée ici pour garder le widget sans
172
+ * dépendance ; un test vérifie la parité avec le SDK.
173
+ */
174
+ /** Default country code added to local numbers (DR Congo). */
175
+ declare const DEFAULT_COUNTRY_CODE = "243";
176
+ /**
177
+ * Normalises a phone number to the API format (international, digits only, no "+"),
178
+ * or returns null when it cannot be a valid number.
179
+ *
180
+ * - "+243 81 234 5678", "00243812345678", "243812345678" → "243812345678"
181
+ * - "0812345678", "812345678" → "243812345678" (default country code added)
182
+ * - "+33 6 12 34 56 78" → "33612345678" (other countries are never blocked)
183
+ * - "33612345678" → "33612345678" (no "+", 11 digits or more not starting with 0: already international)
184
+ */
185
+ declare function normalizePhone(raw: string, defaultCountryCode?: string): string | null;
186
+ /** Masks a number for display and logs: "243812345678" → "+243 81•••••78". */
187
+ declare function maskPhone(phone: string): string;
188
+
189
+ declare const VERSION = "1.2.0";
190
+
191
+ /**
192
+ * `<labyrinthe-otp>`: phone verification by SMS code, ready to use.
193
+ *
194
+ * <script type="module">import '@labyrinthe/otp-widget';</script>
195
+ * <labyrinthe-otp send-url="/api/otp/send" verify-url="/api/otp/verify"></labyrinthe-otp>
196
+ *
197
+ * The widget only talks to YOUR server (see `@labyrinthe/otp-widget/server`):
198
+ * no API key, no `otp_id` and no code ever reach the browser.
199
+ *
200
+ * Importing this module defines the element when `customElements` exists;
201
+ * on a server (SSR) it does nothing.
202
+ */
203
+
204
+ /**
205
+ * Defines `<labyrinthe-otp>` (or another tag name) and returns its class.
206
+ * Idempotent; returns undefined where custom elements do not exist (SSR).
207
+ */
208
+ declare function defineLabyrintheOtp(tagName?: string): CustomElementConstructor | undefined;
209
+ declare global {
210
+ interface HTMLElementTagNameMap {
211
+ 'labyrinthe-otp': LabyrintheOtpElement;
212
+ }
213
+ }
214
+
215
+ export { type CodeType, DEFAULT_COUNTRY_CODE, EVENT_ERROR, EVENT_SENT, EVENT_VERIFIED, type ErrorBody, LOCALES, type LabyrintheOtpElement, type OtpErrorDetail, type OtpFailureReason, type OtpSentDetail, type OtpVerifiedDetail, type OtpWidgetErrorCode, type OtpWidgetLocale, type OtpWidgetStep, type OtpWidgetTexts, type OtpWidgetTextsOverride, type SendSuccessBody, type ServerErrorCode, TAG_NAME, VERSION, type VerifyFailureBody, type VerifySuccessBody, defineLabyrintheOtp, maskPhone, normalizePhone, resolveTexts };