@usethrottle/auth 0.1.1 → 0.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.
- package/CLAUDE.md +25 -0
- package/README.md +307 -1
- package/dist/chunk-AIXBUDOT.js +50 -0
- package/dist/chunk-AIXBUDOT.js.map +1 -0
- package/dist/chunk-HGBAIM6B.js +140 -0
- package/dist/chunk-HGBAIM6B.js.map +1 -0
- package/dist/{chunk-OYNGTNOS.js → chunk-SZ427BQ3.js} +54 -6
- package/dist/chunk-SZ427BQ3.js.map +1 -0
- package/dist/index.cjs +9 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +377 -8
- package/dist/index.js.map +1 -1
- package/dist/react/components/index.cjs +4558 -0
- package/dist/react/components/index.cjs.map +1 -0
- package/dist/react/components/index.d.cts +735 -0
- package/dist/react/components/index.d.ts +735 -0
- package/dist/react/components/index.js +3918 -0
- package/dist/react/components/index.js.map +1 -0
- package/dist/react/forms/index.cjs +3 -0
- package/dist/react/forms/index.cjs.map +1 -1
- package/dist/react/forms/index.js +5 -46
- package/dist/react/forms/index.js.map +1 -1
- package/dist/react/index.cjs +22 -4
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +278 -3
- package/dist/react/index.d.ts +278 -3
- package/dist/react/index.js +17 -122
- package/dist/react/index.js.map +1 -1
- package/dist/styles.css +422 -0
- package/package.json +5 -2
- package/dist/chunk-IJ3DNMNG.js +0 -49
- package/dist/chunk-IJ3DNMNG.js.map +0 -1
- package/dist/chunk-OYNGTNOS.js.map +0 -1
- package/dist/chunk-ZMNCURDE.js +0 -1
- package/dist/chunk-ZMNCURDE.js.map +0 -1
package/dist/styles.css
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
|
|
2
|
+
.throttle-root {
|
|
3
|
+
--throttle-color-primary: #18181b;
|
|
4
|
+
--throttle-color-primary-foreground: #ffffff;
|
|
5
|
+
--throttle-color-background: #ffffff;
|
|
6
|
+
--throttle-color-surface: #f8fafc;
|
|
7
|
+
--throttle-color-foreground: #0e1116;
|
|
8
|
+
--throttle-color-muted: #eceef2;
|
|
9
|
+
--throttle-color-muted-foreground: #5b6472;
|
|
10
|
+
--throttle-color-border: #e2e8f0;
|
|
11
|
+
--throttle-color-danger: #c2321f;
|
|
12
|
+
--throttle-color-danger-surface: #fef2f2;
|
|
13
|
+
--throttle-color-danger-border: #fecaca;
|
|
14
|
+
--throttle-color-success: #127a4a;
|
|
15
|
+
--throttle-color-success-surface: #ecfdf5;
|
|
16
|
+
--throttle-color-success-border: #a7f3d0;
|
|
17
|
+
--throttle-color-warning: #92400e;
|
|
18
|
+
--throttle-color-warning-surface: #fffbeb;
|
|
19
|
+
--throttle-color-warning-border: #fde68a;
|
|
20
|
+
--throttle-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
21
|
+
--throttle-font-size: 16px;
|
|
22
|
+
--throttle-font-size-xs: 12px;
|
|
23
|
+
--throttle-font-size-sm: 14px;
|
|
24
|
+
--throttle-font-size-lg: 20px;
|
|
25
|
+
--throttle-font-size-xl: 24px;
|
|
26
|
+
--throttle-line-height: 1.5;
|
|
27
|
+
--throttle-line-height-tight: 1.25;
|
|
28
|
+
--throttle-radius: 12px;
|
|
29
|
+
--throttle-radius-sm: 8px;
|
|
30
|
+
--throttle-radius-full: 999px;
|
|
31
|
+
--throttle-border-width: 1px;
|
|
32
|
+
--throttle-spacing: 8px;
|
|
33
|
+
--throttle-space-1: 4px;
|
|
34
|
+
--throttle-space-2: var(--throttle-spacing);
|
|
35
|
+
--throttle-space-3: 12px;
|
|
36
|
+
--throttle-space-4: 16px;
|
|
37
|
+
--throttle-space-5: 24px;
|
|
38
|
+
--throttle-space-6: 32px;
|
|
39
|
+
--throttle-control-height: 48px;
|
|
40
|
+
--throttle-control-padding-x: 16px;
|
|
41
|
+
--throttle-shadow-card: 0 1px 3px rgba(15,23,42,.08), 0 1px 2px rgba(15,23,42,.04);
|
|
42
|
+
--throttle-shadow-popover: 0 10px 24px rgba(15,23,42,.12), 0 2px 6px rgba(15,23,42,.06);
|
|
43
|
+
--throttle-transition: 150ms cubic-bezier(.2,0,.2,1);
|
|
44
|
+
--throttle-z-modal: 2147483000;
|
|
45
|
+
--throttle-z-popover: 2147482000;
|
|
46
|
+
font-family: var(--throttle-font-family);
|
|
47
|
+
font-size: var(--throttle-font-size);
|
|
48
|
+
line-height: var(--throttle-line-height);
|
|
49
|
+
color: var(--throttle-color-foreground);
|
|
50
|
+
box-sizing: border-box;
|
|
51
|
+
}
|
|
52
|
+
.throttle-root *, .throttle-root *::before, .throttle-root *::after { box-sizing: inherit; }
|
|
53
|
+
|
|
54
|
+
.throttle-root[data-throttle-theme="dark"] {
|
|
55
|
+
--throttle-color-primary: #e6edf3;
|
|
56
|
+
--throttle-color-primary-foreground: #0d1117;
|
|
57
|
+
--throttle-color-background: #0d1117;
|
|
58
|
+
--throttle-color-surface: #1c2129;
|
|
59
|
+
--throttle-color-foreground: #e6edf3;
|
|
60
|
+
--throttle-color-muted: #21262d;
|
|
61
|
+
--throttle-color-muted-foreground: #9aa4b2;
|
|
62
|
+
--throttle-color-border: #30363d;
|
|
63
|
+
--throttle-color-danger: #ff6b58;
|
|
64
|
+
--throttle-color-danger-surface: #2a1513;
|
|
65
|
+
--throttle-color-danger-border: #5c2b25;
|
|
66
|
+
--throttle-color-success: #45c08a;
|
|
67
|
+
--throttle-color-success-surface: #0f2419;
|
|
68
|
+
--throttle-color-success-border: #1f4d36;
|
|
69
|
+
--throttle-color-warning: #e0a53a;
|
|
70
|
+
--throttle-color-warning-surface: #2a2113;
|
|
71
|
+
--throttle-color-warning-border: #5c4a25;
|
|
72
|
+
--throttle-shadow-card: 0 1px 3px rgba(0,0,0,.6), 0 1px 2px rgba(0,0,0,.4);
|
|
73
|
+
--throttle-shadow-popover: 0 10px 24px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.35);
|
|
74
|
+
}
|
|
75
|
+
@media (prefers-color-scheme: dark) {
|
|
76
|
+
.throttle-root[data-throttle-theme="auto"] {
|
|
77
|
+
--throttle-color-primary: #e6edf3;
|
|
78
|
+
--throttle-color-primary-foreground: #0d1117;
|
|
79
|
+
--throttle-color-background: #0d1117;
|
|
80
|
+
--throttle-color-surface: #1c2129;
|
|
81
|
+
--throttle-color-foreground: #e6edf3;
|
|
82
|
+
--throttle-color-muted: #21262d;
|
|
83
|
+
--throttle-color-muted-foreground: #9aa4b2;
|
|
84
|
+
--throttle-color-border: #30363d;
|
|
85
|
+
--throttle-color-danger: #ff6b58;
|
|
86
|
+
--throttle-color-danger-surface: #2a1513;
|
|
87
|
+
--throttle-color-danger-border: #5c2b25;
|
|
88
|
+
--throttle-color-success: #45c08a;
|
|
89
|
+
--throttle-color-success-surface: #0f2419;
|
|
90
|
+
--throttle-color-success-border: #1f4d36;
|
|
91
|
+
--throttle-color-warning: #e0a53a;
|
|
92
|
+
--throttle-color-warning-surface: #2a2113;
|
|
93
|
+
--throttle-color-warning-border: #5c4a25;
|
|
94
|
+
--throttle-shadow-card: 0 1px 3px rgba(0,0,0,.6), 0 1px 2px rgba(0,0,0,.4);
|
|
95
|
+
--throttle-shadow-popover: 0 10px 24px rgba(0,0,0,.5), 0 2px 6px rgba(0,0,0,.35);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.throttle-card {
|
|
100
|
+
background: var(--throttle-color-background);
|
|
101
|
+
border: var(--throttle-border-width) solid var(--throttle-color-border);
|
|
102
|
+
border-radius: var(--throttle-radius);
|
|
103
|
+
box-shadow: var(--throttle-shadow-card);
|
|
104
|
+
padding: var(--throttle-space-5);
|
|
105
|
+
width: 100%;
|
|
106
|
+
max-width: 400px;
|
|
107
|
+
margin-inline: auto;
|
|
108
|
+
}
|
|
109
|
+
/* Inside a Modal the dialog already provides the surface — background, radius
|
|
110
|
+
and elevation. A card that draws its own on top is the "box inside a box":
|
|
111
|
+
two shadowed rounded rectangles nested, with the card's max-width stranding
|
|
112
|
+
it in the middle of a much wider dialog. */
|
|
113
|
+
.throttle-card--bare { border: none; box-shadow: none; background: none; max-width: none; }
|
|
114
|
+
.throttle-card__header { margin-bottom: var(--throttle-space-4); }
|
|
115
|
+
.throttle-card__header--branded { text-align: center; }
|
|
116
|
+
.throttle-card__logo { display: flex; justify-content: center; margin-bottom: var(--throttle-space-3); }
|
|
117
|
+
.throttle-card__title { font-size: var(--throttle-font-size-xl); line-height: var(--throttle-line-height-tight); font-weight: 600; letter-spacing: -0.02em; margin: 0; }
|
|
118
|
+
.throttle-card__title--branded { font-size: var(--throttle-font-size-xl); }
|
|
119
|
+
.throttle-card__description { color: var(--throttle-color-muted-foreground); margin: var(--throttle-space-1) 0 0; }
|
|
120
|
+
.throttle-card__body { display: flex; flex-direction: column; gap: var(--throttle-space-3); }
|
|
121
|
+
.throttle-card__footer { margin-top: var(--throttle-space-4); text-align: center; }
|
|
122
|
+
.throttle-card__footer--ruled { border-top: var(--throttle-border-width) solid var(--throttle-color-border); padding-top: var(--throttle-space-4); }
|
|
123
|
+
|
|
124
|
+
.throttle-field { display: flex; flex-direction: column; gap: var(--throttle-space-1); }
|
|
125
|
+
.throttle-field__labelrow { display: flex; align-items: baseline; justify-content: space-between; gap: var(--throttle-space-2); }
|
|
126
|
+
.throttle-field__label { font-weight: 500; }
|
|
127
|
+
.throttle-field__input {
|
|
128
|
+
font: inherit;
|
|
129
|
+
font-size: var(--throttle-font-size);
|
|
130
|
+
color: var(--throttle-color-foreground);
|
|
131
|
+
background: var(--throttle-color-background);
|
|
132
|
+
border: var(--throttle-border-width) solid var(--throttle-color-border);
|
|
133
|
+
border-radius: var(--throttle-radius-sm);
|
|
134
|
+
/* padding-block stays 0 so min-height alone owns the height, exactly as
|
|
135
|
+
.throttle-button does. With 12px vertical padding the box computed to
|
|
136
|
+
50px against the button's 48px — a 2px mismatch, measured in a browser
|
|
137
|
+
across 19 controls. An input element centres its own text vertically. */
|
|
138
|
+
padding-block: 0;
|
|
139
|
+
padding-inline: var(--throttle-control-padding-x);
|
|
140
|
+
min-height: var(--throttle-control-height);
|
|
141
|
+
width: 100%;
|
|
142
|
+
transition: border-color var(--throttle-transition), box-shadow var(--throttle-transition);
|
|
143
|
+
}
|
|
144
|
+
.throttle-field__input:focus-visible {
|
|
145
|
+
outline: none;
|
|
146
|
+
border-color: var(--throttle-color-primary);
|
|
147
|
+
box-shadow: 0 0 0 2px var(--throttle-color-background), 0 0 0 4px var(--throttle-color-primary);
|
|
148
|
+
}
|
|
149
|
+
.throttle-field__input:disabled { background: var(--throttle-color-muted); color: var(--throttle-color-muted-foreground); cursor: not-allowed; }
|
|
150
|
+
.throttle-field__input::placeholder { color: var(--throttle-color-muted-foreground); opacity: 1; }
|
|
151
|
+
.throttle-field__input--grouped { border: none; border-radius: 0; padding-inline: 0; }
|
|
152
|
+
.throttle-field__hint { color: var(--throttle-color-muted-foreground); font-size: var(--throttle-font-size-xs); }
|
|
153
|
+
/* Always mounted, so it reserves its line whether or not there is an error:
|
|
154
|
+
an error appearing on submit must not push the submit button out from
|
|
155
|
+
under the buyer's pointer. */
|
|
156
|
+
.throttle-field__error { color: var(--throttle-color-danger); font-size: var(--throttle-font-size-xs); min-height: 1rem; line-height: 1rem; }
|
|
157
|
+
|
|
158
|
+
.throttle-field-group {
|
|
159
|
+
display: flex;
|
|
160
|
+
flex-direction: column;
|
|
161
|
+
border: var(--throttle-border-width) solid var(--throttle-color-border);
|
|
162
|
+
border-radius: var(--throttle-radius-sm);
|
|
163
|
+
overflow: hidden;
|
|
164
|
+
}
|
|
165
|
+
.throttle-field-group__item { padding: var(--throttle-space-3) var(--throttle-control-padding-x); }
|
|
166
|
+
.throttle-field-group__divider { height: var(--throttle-border-width); background: var(--throttle-color-border); }
|
|
167
|
+
.throttle-field-group__forgot { margin-left: auto; }
|
|
168
|
+
|
|
169
|
+
.throttle-button {
|
|
170
|
+
font: inherit;
|
|
171
|
+
font-weight: 500;
|
|
172
|
+
border-radius: var(--throttle-radius-sm);
|
|
173
|
+
border: var(--throttle-border-width) solid transparent;
|
|
174
|
+
padding-inline: var(--throttle-control-padding-x);
|
|
175
|
+
padding-block: 0;
|
|
176
|
+
min-height: var(--throttle-control-height);
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
justify-content: center;
|
|
180
|
+
cursor: pointer;
|
|
181
|
+
transition: opacity var(--throttle-transition), background-color var(--throttle-transition), border-color var(--throttle-transition), filter var(--throttle-transition), box-shadow var(--throttle-transition);
|
|
182
|
+
}
|
|
183
|
+
.throttle-button:disabled { opacity: .55; cursor: not-allowed; }
|
|
184
|
+
.throttle-button:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--throttle-color-background), 0 0 0 4px var(--throttle-color-primary); }
|
|
185
|
+
.throttle-button--primary { background: var(--throttle-color-primary); color: var(--throttle-color-primary-foreground); width: 100%; }
|
|
186
|
+
.throttle-button--primary:hover:not(:disabled) { filter: brightness(0.92); }
|
|
187
|
+
.throttle-button--secondary { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); border-color: var(--throttle-color-border); }
|
|
188
|
+
.throttle-button--secondary:hover:not(:disabled) { background: var(--throttle-color-muted); }
|
|
189
|
+
.throttle-button--danger { background: transparent; color: var(--throttle-color-danger); border-color: var(--throttle-color-border); }
|
|
190
|
+
.throttle-button--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--throttle-color-danger) 10%, transparent); }
|
|
191
|
+
/* Link-style actions are the rows panel default, and they carry Delete,
|
|
192
|
+
Remove, Cancel and Sign out. Zero padding with min-height 0 made those
|
|
193
|
+
roughly 20px tall and ~8px apart — a mis-tap on a phone lands on something
|
|
194
|
+
irreversible. The target is expanded with vertical padding and pulled back
|
|
195
|
+
with an equal negative margin, so the hit box clears 44px while the text
|
|
196
|
+
sits exactly where it did. Collapsing this back to zero padding shrinks the
|
|
197
|
+
target again. */
|
|
198
|
+
.throttle-button--link { background: none; border: none; color: var(--throttle-color-primary); padding-inline: 0; padding-block: var(--throttle-space-3); margin-block: calc(var(--throttle-space-3) * -1); min-height: 0; text-decoration: underline; width: auto; }
|
|
199
|
+
.throttle-button--link:hover:not(:disabled) { opacity: .8; }
|
|
200
|
+
.throttle-button--linkDanger { background: none; border: none; color: var(--throttle-color-danger); padding-inline: 0; padding-block: var(--throttle-space-3); margin-block: calc(var(--throttle-space-3) * -1); min-height: 0; text-decoration: underline; width: auto; }
|
|
201
|
+
.throttle-button--linkDanger:hover:not(:disabled) { opacity: .8; }
|
|
202
|
+
.throttle-button--xs { font-size: var(--throttle-font-size-xs); }
|
|
203
|
+
|
|
204
|
+
.throttle-alert { border-radius: var(--throttle-radius-sm); padding: var(--throttle-space-3); font-size: var(--throttle-font-size-sm); }
|
|
205
|
+
.throttle-alert--error { background: var(--throttle-color-danger-surface); border: var(--throttle-border-width) solid var(--throttle-color-danger-border); color: var(--throttle-color-danger); }
|
|
206
|
+
.throttle-alert--success { background: var(--throttle-color-success-surface); border: var(--throttle-border-width) solid var(--throttle-color-success-border); color: var(--throttle-color-success); }
|
|
207
|
+
.throttle-alert--info { background: var(--throttle-color-surface); color: var(--throttle-color-muted-foreground); }
|
|
208
|
+
|
|
209
|
+
.throttle-spinner {
|
|
210
|
+
display: inline-block; width: 1em; height: 1em;
|
|
211
|
+
border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
|
|
212
|
+
animation: throttle-spin 700ms linear infinite;
|
|
213
|
+
}
|
|
214
|
+
@keyframes throttle-spin { to { transform: rotate(360deg); } }
|
|
215
|
+
@media (prefers-reduced-motion: reduce) { .throttle-spinner { animation-duration: 2.4s; } }
|
|
216
|
+
|
|
217
|
+
.throttle-skeleton {
|
|
218
|
+
background: var(--throttle-color-muted);
|
|
219
|
+
border-radius: var(--throttle-radius-sm);
|
|
220
|
+
animation: throttle-pulse 1.4s ease-in-out infinite; height: 2.5em; }
|
|
221
|
+
@keyframes throttle-pulse { 50% { opacity: .5; } }
|
|
222
|
+
@media (prefers-reduced-motion: reduce) { .throttle-skeleton { animation: none; } }
|
|
223
|
+
|
|
224
|
+
.throttle-modal__overlay {
|
|
225
|
+
position: fixed; inset: 0; z-index: var(--throttle-z-modal);
|
|
226
|
+
background: rgba(14,17,22,.45);
|
|
227
|
+
display: flex; align-items: center; justify-content: center;
|
|
228
|
+
padding: var(--throttle-space-4);
|
|
229
|
+
overflow-y: auto;
|
|
230
|
+
}
|
|
231
|
+
.throttle-modal__container {
|
|
232
|
+
background: var(--throttle-color-background);
|
|
233
|
+
border-radius: var(--throttle-radius);
|
|
234
|
+
box-shadow: var(--throttle-shadow-popover);
|
|
235
|
+
max-width: 720px; width: 100%; max-height: 90vh; overflow-y: auto;
|
|
236
|
+
position: relative;
|
|
237
|
+
}
|
|
238
|
+
/* After the base rule on purpose: both are single-class, so equal specificity
|
|
239
|
+
means source order decides. Declared before it, this silently lost. */
|
|
240
|
+
.throttle-modal__container--compact { max-width: 440px; }
|
|
241
|
+
.throttle-modal__close {
|
|
242
|
+
position: absolute; top: var(--throttle-space-3); right: var(--throttle-space-3);
|
|
243
|
+
background: none; border: none; cursor: pointer; font-size: var(--throttle-font-size-lg);
|
|
244
|
+
color: var(--throttle-color-muted-foreground);
|
|
245
|
+
transition: color var(--throttle-transition);
|
|
246
|
+
}
|
|
247
|
+
.throttle-modal__close:hover { color: var(--throttle-color-foreground); }
|
|
248
|
+
.throttle-modal__header { padding: var(--throttle-space-4) var(--throttle-space-4) 0; }
|
|
249
|
+
.throttle-modal__title { font-size: var(--throttle-font-size-lg); line-height: var(--throttle-line-height-tight); font-weight: 600; margin: 0; }
|
|
250
|
+
.throttle-modal__body { padding: var(--throttle-space-4); }
|
|
251
|
+
|
|
252
|
+
.throttle-dropdown__trigger { background: none; border: none; cursor: pointer; padding: 0; border-radius: var(--throttle-radius-full); transition: background-color var(--throttle-transition); }
|
|
253
|
+
.throttle-dropdown__trigger:hover { background: var(--throttle-color-surface); }
|
|
254
|
+
.throttle-dropdown__trigger:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: 2px; }
|
|
255
|
+
.throttle-dropdown__menu {
|
|
256
|
+
position: absolute; right: 0; top: calc(100% + var(--throttle-space-2));
|
|
257
|
+
z-index: var(--throttle-z-popover);
|
|
258
|
+
min-width: 260px;
|
|
259
|
+
background: var(--throttle-color-background);
|
|
260
|
+
border: var(--throttle-border-width) solid var(--throttle-color-border);
|
|
261
|
+
border-radius: var(--throttle-radius);
|
|
262
|
+
box-shadow: var(--throttle-shadow-popover);
|
|
263
|
+
padding: var(--throttle-space-1);
|
|
264
|
+
}
|
|
265
|
+
.throttle-dropdown__item {
|
|
266
|
+
display: flex; align-items: center; gap: var(--throttle-space-2);
|
|
267
|
+
width: 100%; text-align: left; font: inherit; color: inherit;
|
|
268
|
+
background: none; border: none; cursor: pointer;
|
|
269
|
+
padding: var(--throttle-space-3);
|
|
270
|
+
border-radius: var(--throttle-radius-sm);
|
|
271
|
+
text-decoration: none;
|
|
272
|
+
transition: background-color var(--throttle-transition);
|
|
273
|
+
}
|
|
274
|
+
.throttle-dropdown__item:hover { background: var(--throttle-color-surface); }
|
|
275
|
+
.throttle-dropdown__item:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
|
|
276
|
+
.throttle-dropdown__separator { height: 1px; background: var(--throttle-color-border); margin: var(--throttle-space-1) 0; border: 0; }
|
|
277
|
+
|
|
278
|
+
.throttle-tabs__list {
|
|
279
|
+
display: flex; gap: var(--throttle-space-1);
|
|
280
|
+
border-bottom: var(--throttle-border-width) solid var(--throttle-color-border);
|
|
281
|
+
overflow-x: auto; scrollbar-width: thin;
|
|
282
|
+
}
|
|
283
|
+
.throttle-tabs__tab {
|
|
284
|
+
font: inherit; background: none; border: none; cursor: pointer; white-space: nowrap;
|
|
285
|
+
padding: var(--throttle-space-3);
|
|
286
|
+
color: var(--throttle-color-muted-foreground);
|
|
287
|
+
border-bottom: 2px solid transparent;
|
|
288
|
+
transition: color var(--throttle-transition), border-color var(--throttle-transition);
|
|
289
|
+
}
|
|
290
|
+
.throttle-tabs__tab:hover { color: var(--throttle-color-foreground); }
|
|
291
|
+
.throttle-tabs__tab--active { color: var(--throttle-color-foreground); border-bottom-color: var(--throttle-color-primary); }
|
|
292
|
+
.throttle-tabs__panel { padding-top: var(--throttle-space-4); }
|
|
293
|
+
|
|
294
|
+
/* Paints its own ground. A UserButton in a navbar should stay transparent, but
|
|
295
|
+
the dashboard is a large surface of themed text: in dark theme its foreground
|
|
296
|
+
is near-white, so on a light host page it was rendering white-on-white. */
|
|
297
|
+
.throttle-account-root { container-type: inline-size; background: var(--throttle-color-background); color: var(--throttle-color-foreground); }
|
|
298
|
+
.throttle-account { display: flex; gap: var(--throttle-space-5); align-items: flex-start; }
|
|
299
|
+
.throttle-account__content { flex: 1; min-width: 0; }
|
|
300
|
+
.throttle-tabs__rail { flex: none; width: 220px; display: flex; flex-direction: column; gap: 2px; }
|
|
301
|
+
.throttle-tabs__railitem {
|
|
302
|
+
font: inherit;
|
|
303
|
+
font-size: var(--throttle-font-size-sm);
|
|
304
|
+
display: flex;
|
|
305
|
+
align-items: center;
|
|
306
|
+
gap: var(--throttle-space-3);
|
|
307
|
+
width: 100%;
|
|
308
|
+
text-align: left;
|
|
309
|
+
background: none;
|
|
310
|
+
border: none;
|
|
311
|
+
border-radius: var(--throttle-radius-sm);
|
|
312
|
+
padding: 0 var(--throttle-space-3);
|
|
313
|
+
min-height: var(--throttle-control-height);
|
|
314
|
+
color: var(--throttle-color-muted-foreground);
|
|
315
|
+
cursor: pointer;
|
|
316
|
+
transition: background var(--throttle-transition), color var(--throttle-transition);
|
|
317
|
+
}
|
|
318
|
+
.throttle-tabs__railitem:hover { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); }
|
|
319
|
+
.throttle-tabs__railitem:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
|
|
320
|
+
.throttle-tabs__tab:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
|
|
321
|
+
.throttle-tabs__railitem--active { background: var(--throttle-color-surface); color: var(--throttle-color-foreground); font-weight: 500; }
|
|
322
|
+
.throttle-tabs__railicon { flex: none; opacity: .75; }
|
|
323
|
+
|
|
324
|
+
/* A CONTAINER query, not a viewport one: these components render inside a
|
|
325
|
+
merchant's page, so the viewport says nothing about how much room the
|
|
326
|
+
dashboard actually has — it could sit in a 400px column on a 1920px screen.
|
|
327
|
+
Below ~560px of container the rail becomes a full-width list above the
|
|
328
|
+
panel: all seven still visible, each still a 44px target. */
|
|
329
|
+
@container (max-width: 560px) {
|
|
330
|
+
/* align-items: flex-start is right in row direction and wrong in column —
|
|
331
|
+
it shrinks the rail to its content width, so width:100% alone cannot make
|
|
332
|
+
it span. Stretch it explicitly when stacked. */
|
|
333
|
+
.throttle-account { flex-direction: column; align-items: stretch; gap: var(--throttle-space-4); }
|
|
334
|
+
.throttle-tabs__rail { width: 100%; }
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.throttle-table { width: 100%; border-collapse: collapse; text-align: left; }
|
|
338
|
+
.throttle-table__head { color: var(--throttle-color-muted-foreground); font-weight: 500; font-size: var(--throttle-font-size-sm); }
|
|
339
|
+
.throttle-table__row { border-top: var(--throttle-border-width) solid var(--throttle-color-border); cursor: pointer; transition: background-color var(--throttle-transition); }
|
|
340
|
+
.throttle-table__row:hover { background: var(--throttle-color-surface); }
|
|
341
|
+
.throttle-table__row:focus-visible { outline: 2px solid var(--throttle-color-primary); outline-offset: -2px; }
|
|
342
|
+
.throttle-table__cell { padding: var(--throttle-space-3) var(--throttle-space-2); height: 56px; box-sizing: border-box; }
|
|
343
|
+
.throttle-table__scroll { overflow-x: auto; }
|
|
344
|
+
|
|
345
|
+
.throttle-empty { text-align: center; padding: var(--throttle-space-6) var(--throttle-space-4); }
|
|
346
|
+
.throttle-empty__title { font-weight: 600; margin: 0 0 var(--throttle-space-1); }
|
|
347
|
+
.throttle-empty__description { color: var(--throttle-color-muted-foreground); margin: 0; }
|
|
348
|
+
|
|
349
|
+
.throttle-avatar {
|
|
350
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
351
|
+
width: 32px; height: 32px; border-radius: var(--throttle-radius-full);
|
|
352
|
+
background: var(--throttle-color-primary); color: var(--throttle-color-primary-foreground);
|
|
353
|
+
font-size: var(--throttle-font-size-xs); font-weight: 600; text-transform: uppercase;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.throttle-badge {
|
|
357
|
+
display: inline-block; font-size: var(--throttle-font-size-xs); font-weight: 500;
|
|
358
|
+
padding: 2px var(--throttle-space-2); border-radius: var(--throttle-radius-full);
|
|
359
|
+
background: var(--throttle-color-muted); color: var(--throttle-color-muted-foreground);
|
|
360
|
+
}
|
|
361
|
+
.throttle-badge--muted { background: none; border: var(--throttle-border-width) solid var(--throttle-color-border); color: var(--throttle-color-muted-foreground); font-weight: 400; padding: 1px calc(var(--throttle-space-2) - 1px); }
|
|
362
|
+
.throttle-badge--success { background: color-mix(in srgb, var(--throttle-color-success) 15%, transparent); color: var(--throttle-color-success); }
|
|
363
|
+
.throttle-badge--warning { background: color-mix(in srgb, var(--throttle-color-warning) 15%, transparent); color: var(--throttle-color-warning); }
|
|
364
|
+
.throttle-badge--danger { background: color-mix(in srgb, var(--throttle-color-danger) 15%, transparent); color: var(--throttle-color-danger); }
|
|
365
|
+
|
|
366
|
+
.throttle-stack { display: flex; flex-direction: column; gap: var(--throttle-space-3); }
|
|
367
|
+
.throttle-row { display: flex; align-items: center; justify-content: space-between; gap: var(--throttle-space-2); flex-wrap: wrap; }
|
|
368
|
+
.throttle-muted { color: var(--throttle-color-muted-foreground); }
|
|
369
|
+
.throttle-banner {
|
|
370
|
+
display: flex; align-items: center; justify-content: space-between; gap: var(--throttle-space-2);
|
|
371
|
+
flex-wrap: wrap;
|
|
372
|
+
background: color-mix(in srgb, var(--throttle-color-warning) 12%, transparent);
|
|
373
|
+
color: var(--throttle-color-warning);
|
|
374
|
+
border-radius: var(--throttle-radius-sm);
|
|
375
|
+
padding: var(--throttle-space-3);
|
|
376
|
+
margin-bottom: var(--throttle-space-4);
|
|
377
|
+
}
|
|
378
|
+
.throttle-panel { display: flex; flex-direction: column; gap: var(--throttle-space-4); }
|
|
379
|
+
/* 100dvh, not 100vh: on mobile browsers 100vh includes the space behind the
|
|
380
|
+
dynamic address/toolbar, so the page would be taller than the visible
|
|
381
|
+
viewport and the card would sit off-centre until the toolbar collapsed. */
|
|
382
|
+
.throttle-page {
|
|
383
|
+
min-height: 100dvh;
|
|
384
|
+
width: 100%;
|
|
385
|
+
background: var(--throttle-color-surface);
|
|
386
|
+
display: flex;
|
|
387
|
+
flex-direction: column;
|
|
388
|
+
align-items: center;
|
|
389
|
+
justify-content: center;
|
|
390
|
+
gap: var(--throttle-space-5);
|
|
391
|
+
padding: var(--throttle-space-5);
|
|
392
|
+
}
|
|
393
|
+
.throttle-page__logo { display: flex; justify-content: center; }
|
|
394
|
+
.throttle-page__footer { color: var(--throttle-color-muted-foreground); text-align: center; font-size: var(--throttle-font-size-sm); }
|
|
395
|
+
.throttle-listitem {
|
|
396
|
+
display: flex; align-items: flex-start; justify-content: space-between; gap: var(--throttle-space-2);
|
|
397
|
+
flex-wrap: wrap;
|
|
398
|
+
border: var(--throttle-border-width) solid var(--throttle-color-border);
|
|
399
|
+
border-radius: var(--throttle-radius-sm);
|
|
400
|
+
padding: var(--throttle-space-3);
|
|
401
|
+
}
|
|
402
|
+
/* 'rows' variant: hairline rules between items instead of each item carrying
|
|
403
|
+
its own border+radius, so a panel does not read as a card inside a card. */
|
|
404
|
+
.throttle-rows { display: flex; flex-direction: column; }
|
|
405
|
+
.throttle-rowitem {
|
|
406
|
+
display: flex; align-items: flex-start; justify-content: space-between; gap: var(--throttle-space-2);
|
|
407
|
+
flex-wrap: wrap;
|
|
408
|
+
border-top: var(--throttle-border-width) solid var(--throttle-color-border);
|
|
409
|
+
padding: var(--throttle-space-3) 0;
|
|
410
|
+
}
|
|
411
|
+
.throttle-rowitem:first-child { border-top: none; padding-top: 0; }
|
|
412
|
+
.throttle-actions { display: flex; gap: var(--throttle-space-2); flex-wrap: wrap; }
|
|
413
|
+
.throttle-table__money { display: block; text-align: right; font-variant-numeric: tabular-nums; }
|
|
414
|
+
.throttle-lineitem { display: flex; align-items: center; gap: var(--throttle-space-3); padding: var(--throttle-space-2) 0; }
|
|
415
|
+
.throttle-lineitem__image { width: 48px; height: 48px; object-fit: cover; border-radius: var(--throttle-radius-sm); flex: none; }
|
|
416
|
+
.throttle-totals { display: flex; flex-direction: column; gap: var(--throttle-space-1); }
|
|
417
|
+
|
|
418
|
+
@media (prefers-reduced-motion: reduce) {
|
|
419
|
+
.throttle-button, .throttle-field__input, .throttle-dropdown__item, .throttle-dropdown__trigger, .throttle-tabs__tab, .throttle-table__row, .throttle-modal__close {
|
|
420
|
+
transition: none;
|
|
421
|
+
}
|
|
422
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usethrottle/auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Buyer authentication for Throttle storefronts — headless core, React hooks, and drop-in forms.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"exports": {
|
|
11
11
|
".": { "types": "./dist/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.cjs" },
|
|
12
12
|
"./react": { "types": "./dist/react/index.d.ts", "import": "./dist/react/index.js", "require": "./dist/react/index.cjs" },
|
|
13
|
-
"./forms": { "types": "./dist/react/forms/index.d.ts", "import": "./dist/react/forms/index.js", "require": "./dist/react/forms/index.cjs" }
|
|
13
|
+
"./forms": { "types": "./dist/react/forms/index.d.ts", "import": "./dist/react/forms/index.js", "require": "./dist/react/forms/index.cjs" },
|
|
14
|
+
"./components": { "types": "./dist/react/components/index.d.ts", "import": "./dist/react/components/index.js", "require": "./dist/react/components/index.cjs" },
|
|
15
|
+
"./styles.css": "./dist/styles.css"
|
|
14
16
|
},
|
|
15
17
|
"scripts": {
|
|
16
18
|
"build": "tsup",
|
|
@@ -23,6 +25,7 @@
|
|
|
23
25
|
"devDependencies": {
|
|
24
26
|
"@testing-library/react": "^16.1.0",
|
|
25
27
|
"@types/react": "^19.1.0",
|
|
28
|
+
"@types/react-dom": "^19.1.0",
|
|
26
29
|
"@vitejs/plugin-react": "^4.3.4",
|
|
27
30
|
"jsdom": "^25.0.1",
|
|
28
31
|
"react": "^19.1.0",
|
package/dist/chunk-IJ3DNMNG.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createAccount,
|
|
3
|
-
createThrottleAuth
|
|
4
|
-
} from "./chunk-OYNGTNOS.js";
|
|
5
|
-
|
|
6
|
-
// src/react/context.tsx
|
|
7
|
-
import { createContext, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
8
|
-
import { jsx } from "react/jsx-runtime";
|
|
9
|
-
var Ctx = createContext(null);
|
|
10
|
-
function ThrottleAuthProvider(props) {
|
|
11
|
-
const { auth: injected, publishableKey, baseUrl, storage, onStepUpRequired, children } = props;
|
|
12
|
-
const value = useMemo(() => {
|
|
13
|
-
const auth = injected ?? createThrottleAuth({ publishableKey, baseUrl, storage, onStepUpRequired });
|
|
14
|
-
return { auth, account: createAccount(auth) };
|
|
15
|
-
}, [injected, publishableKey, baseUrl]);
|
|
16
|
-
const restored = useRef(null);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (restored.current === value.auth) return;
|
|
19
|
-
restored.current = value.auth;
|
|
20
|
-
void value.auth.restore();
|
|
21
|
-
}, [value]);
|
|
22
|
-
return /* @__PURE__ */ jsx(Ctx.Provider, { value, children });
|
|
23
|
-
}
|
|
24
|
-
function useCtx() {
|
|
25
|
-
const ctx = useContext(Ctx);
|
|
26
|
-
if (!ctx) throw new Error("useAuth must be used inside a <ThrottleAuthProvider>");
|
|
27
|
-
return ctx;
|
|
28
|
-
}
|
|
29
|
-
function useAuthContext() {
|
|
30
|
-
return useCtx();
|
|
31
|
-
}
|
|
32
|
-
function useAuth() {
|
|
33
|
-
const { auth, account } = useCtx();
|
|
34
|
-
const snapshot = useSyncExternalStore(auth.subscribe, auth.getSnapshot, auth.getSnapshot);
|
|
35
|
-
return {
|
|
36
|
-
...snapshot,
|
|
37
|
-
isLoading: snapshot.status === "loading",
|
|
38
|
-
isAuthenticated: snapshot.status === "authenticated",
|
|
39
|
-
auth,
|
|
40
|
-
account
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export {
|
|
45
|
-
ThrottleAuthProvider,
|
|
46
|
-
useAuthContext,
|
|
47
|
-
useAuth
|
|
48
|
-
};
|
|
49
|
-
//# sourceMappingURL=chunk-IJ3DNMNG.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react/context.tsx"],"sourcesContent":["import { createContext, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport type { ReactNode } from 'react';\nimport { createThrottleAuth, type ThrottleAuth } from '../client';\nimport { createAccount, type ThrottleAccount } from '../account';\nimport type { AuthSnapshot } from '../session';\nimport type { ThrottleAuthConfig } from '../types';\n\nexport interface ThrottleAuthProviderProps extends Partial<ThrottleAuthConfig> {\n publishableKey: string;\n /** Inject a pre-built client. Tests use it; merchants sharing one client across roots may too. */\n auth?: ThrottleAuth;\n children: ReactNode;\n}\n\ninterface ContextValue { auth: ThrottleAuth; account: ThrottleAccount }\n\nconst Ctx = createContext<ContextValue | null>(null);\n\nexport function ThrottleAuthProvider(props: ThrottleAuthProviderProps) {\n const { auth: injected, publishableKey, baseUrl, storage, onStepUpRequired, children } = props;\n\n // `storage` and `onStepUpRequired` are deliberately NOT dependencies: they are\n // read once at mount. A merchant passing `storage={memoryStorage()}` inline —\n // which the README suggests — would otherwise get a new client on every parent\n // render, and with it a fresh empty SessionState that reads as signed out.\n const value = useMemo<ContextValue>(() => {\n const auth = injected ?? createThrottleAuth({ publishableKey, baseUrl, storage, onStepUpRequired });\n return { auth, account: createAccount(auth) };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [injected, publishableKey, baseUrl]);\n\n const restored = useRef<ThrottleAuth | null>(null);\n useEffect(() => {\n // Once per client. StrictMode runs effects twice in development, and a\n // second restore() would present the refresh token that the first one\n // just rotated away.\n if (restored.current === value.auth) return;\n restored.current = value.auth;\n void value.auth.restore();\n }, [value]);\n\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\nfunction useCtx(): ContextValue {\n const ctx = useContext(Ctx);\n if (!ctx) throw new Error('useAuth must be used inside a <ThrottleAuthProvider>');\n return ctx;\n}\n\nexport function useAuthContext(): ContextValue { return useCtx(); }\n\nexport type UseAuthResult = AuthSnapshot & {\n isLoading: boolean;\n isAuthenticated: boolean;\n auth: ThrottleAuth;\n account: ThrottleAccount;\n};\n\nexport function useAuth(): UseAuthResult {\n const { auth, account } = useCtx();\n const snapshot = useSyncExternalStore(auth.subscribe, auth.getSnapshot, auth.getSnapshot);\n return {\n ...snapshot,\n isLoading: snapshot.status === 'loading',\n isAuthenticated: snapshot.status === 'authenticated',\n auth,\n account,\n };\n}\n"],"mappings":";;;;;;AAAA,SAAS,eAAe,YAAY,WAAW,SAAS,QAAQ,4BAA4B;AAyCnF;AAzBT,IAAM,MAAM,cAAmC,IAAI;AAE5C,SAAS,qBAAqB,OAAkC;AACrE,QAAM,EAAE,MAAM,UAAU,gBAAgB,SAAS,SAAS,kBAAkB,SAAS,IAAI;AAMzF,QAAM,QAAQ,QAAsB,MAAM;AACxC,UAAM,OAAO,YAAY,mBAAmB,EAAE,gBAAgB,SAAS,SAAS,iBAAiB,CAAC;AAClG,WAAO,EAAE,MAAM,SAAS,cAAc,IAAI,EAAE;AAAA,EAE9C,GAAG,CAAC,UAAU,gBAAgB,OAAO,CAAC;AAEtC,QAAM,WAAW,OAA4B,IAAI;AACjD,YAAU,MAAM;AAId,QAAI,SAAS,YAAY,MAAM,KAAM;AACrC,aAAS,UAAU,MAAM;AACzB,SAAK,MAAM,KAAK,QAAQ;AAAA,EAC1B,GAAG,CAAC,KAAK,CAAC;AAEV,SAAO,oBAAC,IAAI,UAAJ,EAAa,OAAe,UAAS;AAC/C;AAEA,SAAS,SAAuB;AAC9B,QAAM,MAAM,WAAW,GAAG;AAC1B,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sDAAsD;AAChF,SAAO;AACT;AAEO,SAAS,iBAA+B;AAAE,SAAO,OAAO;AAAG;AAS3D,SAAS,UAAyB;AACvC,QAAM,EAAE,MAAM,QAAQ,IAAI,OAAO;AACjC,QAAM,WAAW,qBAAqB,KAAK,WAAW,KAAK,aAAa,KAAK,WAAW;AACxF,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW,SAAS,WAAW;AAAA,IAC/B,iBAAiB,SAAS,WAAW;AAAA,IACrC;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/request.ts","../src/session.ts","../src/client.ts","../src/account.ts"],"sourcesContent":["export class ThrottleAuthError extends Error {\n readonly code: string;\n readonly statusCode: number;\n readonly retryAfter?: number;\n readonly details?: unknown;\n\n constructor(args: { code: string; message: string; statusCode: number; retryAfter?: number; details?: unknown }) {\n super(args.message);\n this.name = 'ThrottleAuthError';\n this.code = args.code;\n this.statusCode = args.statusCode;\n this.retryAfter = args.retryAfter;\n this.details = args.details;\n }\n}\n","import type { AuthStorage } from './types';\n\nexport type { AuthStorage };\n\nexport function memoryStorage(): AuthStorage {\n const map = new Map<string, string>();\n return {\n get: (k) => map.get(k) ?? null,\n set: (k, v) => void map.set(k, v),\n remove: (k) => void map.delete(k),\n };\n}\n\n/**\n * localStorage when it works, memory when it does not. Safari private mode,\n * hardened browser settings and server-side rendering all make localStorage\n * either absent or throwing; none of them may take the storefront down.\n */\nexport function localStorageAdapter(): AuthStorage {\n const fallback = memoryStorage();\n const ls = (): Storage | null => {\n try {\n return typeof localStorage !== 'undefined' ? localStorage : null;\n } catch {\n return null;\n }\n };\n return {\n get(k) {\n try {\n const v = ls()?.getItem(k);\n if (v != null) return v;\n } catch { /* fall through */ }\n return fallback.get(k);\n },\n set(k, v) {\n fallback.set(k, v);\n try { ls()?.setItem(k, v); } catch { /* memory-only this session */ }\n },\n remove(k) {\n fallback.remove(k);\n try { ls()?.removeItem(k); } catch { /* already gone */ }\n },\n };\n}\n","import { ThrottleAuthError } from './errors';\n\nexport interface RequestInput {\n method: string;\n path: string;\n baseUrl: string;\n publishableKey: string;\n accessToken?: string;\n stepUpToken?: string;\n body?: unknown;\n}\n\n/** Returns the UNWRAPPED `data` from the `{ data: … }` envelope. Throws ThrottleAuthError otherwise. */\nexport async function request(input: RequestInput): Promise<any> {\n const headers: Record<string, string> = { 'X-API-Key': input.publishableKey };\n if (input.body !== undefined) headers['Content-Type'] = 'application/json';\n if (input.accessToken) headers.Authorization = `Bearer ${input.accessToken}`;\n if (input.stepUpToken) headers['X-Step-Up'] = input.stepUpToken;\n\n const res = await fetch(`${input.baseUrl}/v1/storefront${input.path}`, {\n method: input.method,\n headers,\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body — an HTML gateway page, say. Leave parsed empty so the\n // branch below still throws a typed error rather than a raw SyntaxError.\n }\n\n if (!res.ok) {\n const e = parsed?.error ?? {};\n // `Retry-After` is server-controlled but not trusted: Number('soon') is\n // NaN, and a NaN reaches the UI as \"wait NaN seconds\".\n const ra = Number(res.headers.get('retry-after'));\n throw new ThrottleAuthError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n retryAfter: Number.isFinite(ra) && ra >= 0 ? ra : undefined,\n details: e.details,\n });\n }\n\n // `'data' in parsed`, not `parsed?.data ?? parsed`: a legitimate\n // `{ \"data\": null }` is nullish, and `??` would fall through and hand the\n // caller the whole envelope instead of the null the API actually sent.\n return parsed && typeof parsed === 'object' && 'data' in parsed ? parsed.data : (parsed ?? {});\n}\n","import type { Customer, Session } from './types';\n\nexport type AuthStatus = 'loading' | 'authenticated' | 'unauthenticated';\n\nexport interface AuthSnapshot {\n status: AuthStatus;\n customer: Customer | null;\n /**\n * False here does NOT only mean \"we emailed them and they have not clicked\".\n * While it is false the API may also be withholding pre-existing account\n * data behind the claim cutoff — empty order lists and a 403 on `PATCH /me`\n * are expected, not broken. See the plan header.\n */\n emailVerified: boolean;\n}\n\ntype Listener = (snapshot: AuthSnapshot) => void;\n\nexport class SessionState {\n #snapshot: AuthSnapshot = { status: 'loading', customer: null, emailVerified: false };\n #accessToken: string | null = null;\n #expiresAt: number | null = null;\n #listeners = new Set<Listener>();\n\n get(): AuthSnapshot { return this.#snapshot; }\n accessToken(): string | null { return this.#accessToken; }\n expiresAt(): number | null { return this.#expiresAt; }\n\n setSession(session: Session): void {\n this.#accessToken = session.accessToken;\n this.#expiresAt = Date.parse(session.expiresAt);\n this.#publish({\n status: 'authenticated',\n customer: session.customer,\n emailVerified: Boolean(session.customer?.emailVerified),\n });\n }\n\n setStatus(status: AuthStatus): void {\n this.#publish({ ...this.#snapshot, status });\n }\n\n clear(): void {\n this.#accessToken = null;\n this.#expiresAt = null;\n this.#publish({ status: 'unauthenticated', customer: null, emailVerified: false });\n }\n\n subscribe(fn: Listener): () => void {\n this.#listeners.add(fn);\n return () => { this.#listeners.delete(fn); };\n }\n\n #publish(next: AuthSnapshot): void {\n // Identity matters: React's useSyncExternalStore re-renders on reference\n // change, and a token refresh every ten minutes must not re-render the\n // whole account area when nothing visible changed.\n const same =\n next.status === this.#snapshot.status &&\n next.emailVerified === this.#snapshot.emailVerified &&\n customerEquals(next.customer, this.#snapshot.customer);\n if (same) return;\n this.#snapshot = next;\n for (const fn of [...this.#listeners]) {\n // A merchant's subscriber throwing must not stop the rest from hearing.\n try { fn(next); } catch { /* merchant callback */ }\n }\n }\n}\n\nfunction customerEquals(a: Customer | null, b: Customer | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const keys = new Set([...Object.keys(a), ...Object.keys(b)]) as Set<keyof Customer>;\n for (const k of keys) if (a[k] !== b[k]) return false;\n return true;\n}\n","import { request } from './request';\nimport { ThrottleAuthError } from './errors';\nimport { SessionState, type AuthSnapshot } from './session';\nimport { localStorageAdapter, type AuthStorage } from './storage';\nimport type { Customer, Session, ThrottleAuthConfig } from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n/** Refresh this far before expiry so ordinary calls rarely see a 401 at all. */\nconst REFRESH_SKEW_MS = 30_000;\n\nexport interface DeviceSession {\n id: string;\n userAgent: string | null;\n ipPrefix: string | null;\n createdAt: string;\n lastUsedAt: string;\n current: boolean;\n}\n\nexport interface ThrottleAuth {\n register(email: string, password: string): Promise<{ status: 'active' | 'pending' }>;\n signIn(email: string, password: string): Promise<void>;\n signOut(): Promise<void>;\n signOutEverywhere(): Promise<void>;\n restore(): Promise<void>;\n getSnapshot(): AuthSnapshot;\n subscribe(fn: (s: AuthSnapshot) => void): () => void;\n stepUp(password: string): Promise<void>;\n call<T = any>(method: string, path: string, body?: unknown): Promise<T>;\n verifyEmail(token: string): Promise<void>;\n resendVerification(): Promise<void>;\n forgotPassword(email: string): Promise<void>;\n resetPassword(token: string, password: string): Promise<void>;\n changePassword(password: string): Promise<void>;\n listSessions(): Promise<DeviceSession[]>;\n revokeSession(id: string): Promise<void>;\n}\n\nexport function createThrottleAuth(config: ThrottleAuthConfig): ThrottleAuth {\n const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n const storage: AuthStorage = config.storage ?? localStorageAdapter();\n // Namespaced by key so two Throttle applications on one origin cannot read\n // each other's refresh token out of shared localStorage.\n const storageKey = `throttle_auth_rt:${config.publishableKey}`;\n const lockName = `throttle_auth_refresh:${config.publishableKey}`;\n\n const state = new SessionState();\n let refreshInFlight: Promise<void> | null = null;\n let stepUpToken: string | null = null;\n let stepUpExpiresAt = 0;\n\n const base = { baseUrl, publishableKey: config.publishableKey };\n\n async function persist(session: Session): Promise<void> {\n state.setSession(session);\n await storage.set(storageKey, session.refreshToken);\n }\n\n async function forget(): Promise<void> {\n stepUpToken = null;\n state.clear();\n await storage.remove(storageKey);\n }\n\n /**\n * Cross-tab single flight. Two tabs refreshing at once would each present the\n * same stored token; the second is a replay, and the server kills the family.\n * Web Locks serialises them; where it is unavailable the in-process guard\n * below still covers the common single-tab case.\n */\n function withLock<T>(fn: () => Promise<T>): Promise<T> {\n const locks = (globalThis as any).navigator?.locks;\n return locks?.request ? locks.request(lockName, fn) : fn();\n }\n\n async function doRefresh(): Promise<void> {\n await withLock(async () => {\n // Re-read INSIDE the lock: another tab may have rotated while we waited,\n // and presenting the token we read before the lock would be a replay.\n const refreshToken = await storage.get(storageKey);\n if (!refreshToken) {\n await forget();\n throw new ThrottleAuthError({ code: 'session_revoked', message: 'No session', statusCode: 401 });\n }\n try {\n const data = await request({ ...base, method: 'POST', path: '/auth/refresh', body: { refreshToken } });\n await persist(data as Session);\n } catch (err) {\n // Any refusal of a refresh token is terminal — there is nothing left to\n // retry with. Drop it rather than letting a dead token sit in storage.\n await forget();\n throw err;\n }\n });\n }\n\n function refresh(): Promise<void> {\n if (!refreshInFlight) {\n refreshInFlight = doRefresh().finally(() => { refreshInFlight = null; });\n }\n return refreshInFlight;\n }\n\n async function accessToken(): Promise<string | null> {\n const expires = state.expiresAt();\n if (state.accessToken() && expires && expires - Date.now() > REFRESH_SKEW_MS) {\n return state.accessToken();\n }\n if (await storage.get(storageKey)) {\n await refresh();\n return state.accessToken();\n }\n return state.accessToken();\n }\n\n async function ensureStepUp(): Promise<boolean> {\n if (stepUpToken && stepUpExpiresAt > Date.now()) return true;\n if (!config.onStepUpRequired) return false;\n const password = await config.onStepUpRequired();\n if (!password) return false;\n await stepUp(password);\n return true;\n }\n\n async function stepUp(password: string): Promise<void> {\n const data = await request({\n ...base, method: 'POST', path: '/auth/step-up',\n accessToken: await accessToken() ?? undefined, body: { password },\n });\n stepUpToken = data.stepUpToken;\n stepUpExpiresAt = Date.now() + (Number(data.expiresIn) || 300) * 1000 - 5_000;\n }\n\n async function call<T = any>(method: string, path: string, body?: unknown): Promise<T> {\n let retriedAuth = false;\n let retriedStepUp = false;\n\n for (;;) {\n const token = await accessToken();\n try {\n return await request({\n ...base, method, path, body,\n accessToken: token ?? undefined,\n stepUpToken: stepUpToken ?? undefined,\n });\n } catch (err) {\n const e = err as ThrottleAuthError;\n // `session_revoked` belongs here too. The server returns it for a\n // stale session EPOCH as well as for a genuinely dead session, and a\n // changed password bumps the epoch while deliberately sparing the\n // current session — so one refresh recovers it. When the session\n // really is dead the refresh fails and forget() runs, which is the\n // same outcome, one request later.\n const expired = e.statusCode === 401 &&\n (e.code === 'unauthorized' || e.code === 'token_expired' || e.code === 'session_revoked');\n if (expired && !retriedAuth) {\n retriedAuth = true;\n await refresh(); // shared — ten callers here produce ONE request\n continue;\n }\n if (e.code === 'step_up_required' && !retriedStepUp) {\n retriedStepUp = true;\n stepUpToken = null;\n if (await ensureStepUp()) continue;\n }\n throw err;\n }\n }\n }\n\n return {\n async register(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/register', body: { email, password } });\n if (data?.status === 'pending') return { status: 'pending' };\n await persist(data as Session);\n return { status: 'active' };\n },\n\n async signIn(email, password) {\n const data = await request({ ...base, method: 'POST', path: '/auth/login', body: { email, password } });\n await persist(data as Session);\n },\n\n async signOut() {\n try { await call('POST', '/auth/logout'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async signOutEverywhere() {\n try { await call('POST', '/auth/logout-all'); } catch { /* local state still goes */ }\n await forget();\n },\n\n async restore() {\n if (!(await storage.get(storageKey))) { state.setStatus('unauthenticated'); return; }\n try { await refresh(); } catch { /* forget() already ran */ }\n },\n\n getSnapshot: () => state.get(),\n subscribe: (fn) => state.subscribe(fn),\n stepUp,\n call,\n\n async verifyEmail(token) {\n await request({ ...base, method: 'POST', path: '/auth/verify-email', body: { token } });\n // `ver` is a claim baked into the access token, and the claim cutoff is\n // read from the session row. Without a new token the buyer stays gated —\n // and their own order history stays hidden — for the rest of this\n // token's ten minutes.\n if (await storage.get(storageKey)) {\n try { await refresh(); } catch { /* verification still succeeded */ }\n }\n },\n\n async resendVerification() {\n await call('POST', '/auth/verify-email/resend');\n },\n\n async forgotPassword(email) {\n // Always 202, whether or not the address has an account. Do not infer\n // anything from it and do not tell the buyer whether it existed.\n await request({ ...base, method: 'POST', path: '/auth/forgot-password', body: { email } });\n },\n\n async resetPassword(token, password) {\n await request({ ...base, method: 'POST', path: '/auth/reset-password', body: { token, password } });\n // The server revokes every session on reset, this one included.\n await forget();\n },\n\n async changePassword(password) {\n await call('POST', '/me/change-password', { password });\n // The change bumps the session epoch. This session was deliberately\n // spared, but the access token in hand now carries a stale epoch and the\n // next write would 401. Rotate now rather than on the buyer's next click\n // — and do not let a failed rotation report a change that already\n // succeeded server-side as a failure.\n try { await refresh(); } catch { /* the password change already succeeded */ }\n },\n\n async listSessions() {\n return call<DeviceSession[]>('GET', '/me/sessions');\n },\n\n async revokeSession(id) {\n const mine = await call<DeviceSession[]>('GET', '/me/sessions');\n const current = mine.find((s) => s.current);\n await call('DELETE', `/me/sessions/${encodeURIComponent(id)}`);\n if (current?.id === id) await forget();\n },\n };\n}\n\nexport type { AuthSnapshot, Customer, Session, ThrottleAuthConfig };\n","import type { ThrottleAuth } from './client';\nimport type {\n Address, AddressInput, Customer, Invoice, Order,\n PaymentMethod, ProfilePatch, Subscription,\n} from './types';\n\nexport interface Page { cursor?: string; limit?: number }\n\nexport interface ThrottleAccount {\n getProfile(): Promise<Customer>;\n updateProfile(patch: ProfilePatch): Promise<Customer>;\n listAddresses(): Promise<Address[]>;\n createAddress(input: AddressInput): Promise<Address>;\n updateAddress(id: string, patch: Partial<AddressInput>): Promise<Address>;\n deleteAddress(id: string): Promise<void>;\n listPaymentMethods(): Promise<PaymentMethod[]>;\n setDefaultPaymentMethod(id: string): Promise<PaymentMethod>;\n deletePaymentMethod(id: string): Promise<void>;\n listOrders(page?: Page): Promise<Order[]>;\n getOrder(id: string): Promise<Order>;\n listInvoices(page?: Page): Promise<Invoice[]>;\n getInvoice(id: string): Promise<Invoice>;\n listSubscriptions(page?: Page): Promise<Subscription[]>;\n getSubscription(id: string): Promise<Subscription>;\n cancelSubscription(id: string, opts?: { atPeriodEnd?: boolean }): Promise<Subscription>;\n pauseSubscription(id: string): Promise<Subscription>;\n resumeSubscription(id: string): Promise<Subscription>;\n claimCart(cartId: string): Promise<{ cartId: string; customerId: string }>;\n}\n\nconst seg = (v: string) => encodeURIComponent(v);\n\nfunction query(page?: Page): string {\n const params = new URLSearchParams();\n if (page?.cursor) params.set('cursor', page.cursor);\n if (page?.limit != null) params.set('limit', String(page.limit));\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\nexport function createAccount(auth: ThrottleAuth): ThrottleAccount {\n const { call } = auth;\n return {\n getProfile: () => call('GET', '/me'),\n updateProfile: (patch) => call('PATCH', '/me', patch),\n\n listAddresses: () => call('GET', '/me/addresses'),\n createAddress: (input) => call('POST', '/me/addresses', input),\n updateAddress: (id, patch) => call('PATCH', `/me/addresses/${seg(id)}`, patch),\n deleteAddress: (id) => call('DELETE', `/me/addresses/${seg(id)}`),\n\n listPaymentMethods: () => call('GET', '/me/payment-methods'),\n // The platform has no \"change this subscription's card\" capability, so\n // this is how a buyer changes what they are billed on: move the default.\n setDefaultPaymentMethod: (id) => call('PATCH', `/me/payment-methods/${seg(id)}`, { isDefault: true }),\n deletePaymentMethod: (id) => call('DELETE', `/me/payment-methods/${seg(id)}`),\n\n listOrders: (page) => call('GET', `/me/orders${query(page)}`),\n getOrder: (id) => call('GET', `/me/orders/${seg(id)}`),\n listInvoices: (page) => call('GET', `/me/invoices${query(page)}`),\n getInvoice: (id) => call('GET', `/me/invoices/${seg(id)}`),\n listSubscriptions: (page) => call('GET', `/me/subscriptions${query(page)}`),\n getSubscription: (id) => call('GET', `/me/subscriptions/${seg(id)}`),\n\n cancelSubscription: (id, opts) => call('POST', `/me/subscriptions/${seg(id)}/cancel`, opts),\n pauseSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/pause`),\n resumeSubscription: (id) => call('POST', `/me/subscriptions/${seg(id)}/resume`),\n\n claimCart: (cartId) => call('POST', `/me/carts/${seg(cartId)}/claim`),\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-ZMNCURDE.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=chunk-ZMNCURDE.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|