@poly-x/next 0.1.0-alpha.2 → 0.1.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.cjs CHANGED
@@ -36,6 +36,8 @@ function buildServerAuthClient(config) {
36
36
  */
37
37
  const VERIFIER_COOKIE = "polyx_pkce";
38
38
  const VERIFIER_MAX_AGE = 600;
39
+ /** poly-auth's own floor (Joi: min 6, max 128) — reject early rather than round-trip. */
40
+ const MIN_PASSWORD_LENGTH = 6;
39
41
  function adapt(store) {
40
42
  return {
41
43
  get: (name) => store.get(name)?.value,
@@ -92,6 +94,7 @@ function createAuthHandlers(handlersConfig = {}) {
92
94
  async authenticate(request) {
93
95
  const config = resolveNextConfig(handlersConfig);
94
96
  const redirectUri = `${new URL(request.url).origin}${callbackPath}`;
97
+ const client = buildServerAuthClient(config);
95
98
  let payload;
96
99
  try {
97
100
  payload = await request.json();
@@ -103,36 +106,63 @@ function createAuthHandlers(handlersConfig = {}) {
103
106
  if (!email || !password) return Response.json({ status: "invalid_request" }, { status: 400 });
104
107
  const organizationCode = typeof payload.organizationCode === "string" ? payload.organizationCode : void 0;
105
108
  const tenantId = typeof payload.tenantId === "string" ? payload.tenantId : void 0;
106
- const { verifier, challenge } = await (0, _poly_x_core.generatePkce)();
107
- const state = (0, _poly_x_core.randomState)();
108
- const outcome = await buildServerAuthClient(config).authorizeWithPassword({
109
+ const newPassword = typeof payload.newPassword === "string" ? payload.newPassword : void 0;
110
+ const confirmPassword = typeof payload.confirmPassword === "string" ? payload.confirmPassword : void 0;
111
+ /** One authorize attempt, mapped to the wire response (sealing the session on success). */
112
+ async function attempt(withPassword) {
113
+ const { verifier, challenge } = await (0, _poly_x_core.generatePkce)();
114
+ const outcome = await client.authorizeWithPassword({
115
+ email,
116
+ password: withPassword,
117
+ redirectUri,
118
+ state: (0, _poly_x_core.randomState)(),
119
+ challenge,
120
+ organizationCode,
121
+ tenantId
122
+ });
123
+ switch (outcome.type) {
124
+ case "authorized":
125
+ await newSession(adapt(await (0, next_headers.cookies)()), config).establishFromCode(outcome.code, verifier, redirectUri);
126
+ return Response.json({
127
+ status: "success",
128
+ redirectTo: afterSignInPath
129
+ });
130
+ case "select_tenant": return Response.json({
131
+ status: "select_tenant",
132
+ tenants: outcome.tenants,
133
+ state: outcome.state
134
+ });
135
+ case "password_change_required": return Response.json({
136
+ status: "password_change_required",
137
+ userId: outcome.userId
138
+ }, { status: 403 });
139
+ case "no_access": return Response.json({ status: "no_access" }, { status: 403 });
140
+ default: return Response.json({ status: "invalid_credentials" }, { status: 401 });
141
+ }
142
+ }
143
+ if (newPassword === void 0) return attempt(password);
144
+ if (newPassword.length < MIN_PASSWORD_LENGTH) return Response.json({
145
+ status: "weak_password",
146
+ minLength: MIN_PASSWORD_LENGTH
147
+ }, { status: 400 });
148
+ if (newPassword !== confirmPassword) return Response.json({ status: "password_mismatch" }, { status: 400 });
149
+ const { challenge } = await (0, _poly_x_core.generatePkce)();
150
+ const proof = await client.authorizeWithPassword({
109
151
  email,
110
152
  password,
111
153
  redirectUri,
112
- state,
154
+ state: (0, _poly_x_core.randomState)(),
113
155
  challenge,
114
156
  organizationCode,
115
157
  tenantId
116
158
  });
117
- switch (outcome.type) {
118
- case "authorized":
119
- await newSession(adapt(await (0, next_headers.cookies)()), config).establishFromCode(outcome.code, verifier, redirectUri);
120
- return Response.json({
121
- status: "success",
122
- redirectTo: afterSignInPath
123
- });
124
- case "select_tenant": return Response.json({
125
- status: "select_tenant",
126
- tenants: outcome.tenants,
127
- state: outcome.state
128
- });
129
- case "password_change_required": return Response.json({
130
- status: "password_change_required",
131
- userId: outcome.userId
132
- }, { status: 403 });
133
- case "no_access": return Response.json({ status: "no_access" }, { status: 403 });
134
- default: return Response.json({ status: "invalid_credentials" }, { status: 401 });
135
- }
159
+ if (proof.type !== "password_change_required") return Response.json({ status: "invalid_credentials" }, { status: 401 });
160
+ await client.setPassword({
161
+ userId: proof.userId,
162
+ newPassword,
163
+ confirmPassword
164
+ });
165
+ return attempt(newPassword);
136
166
  },
137
167
  async callback(request) {
138
168
  const config = resolveNextConfig(handlersConfig);
package/dist/server.d.cts CHANGED
@@ -95,6 +95,11 @@ type AuthenticateResult = {
95
95
  } | {
96
96
  status: "password_change_required";
97
97
  userId: string;
98
+ } | {
99
+ status: "weak_password";
100
+ minLength: number;
101
+ } | {
102
+ status: "password_mismatch";
98
103
  } | {
99
104
  status: "no_access";
100
105
  } | {
package/dist/server.d.mts CHANGED
@@ -95,6 +95,11 @@ type AuthenticateResult = {
95
95
  } | {
96
96
  status: "password_change_required";
97
97
  userId: string;
98
+ } | {
99
+ status: "weak_password";
100
+ minLength: number;
101
+ } | {
102
+ status: "password_mismatch";
98
103
  } | {
99
104
  status: "no_access";
100
105
  } | {
package/dist/server.mjs CHANGED
@@ -35,6 +35,8 @@ function buildServerAuthClient(config) {
35
35
  */
36
36
  const VERIFIER_COOKIE = "polyx_pkce";
37
37
  const VERIFIER_MAX_AGE = 600;
38
+ /** poly-auth's own floor (Joi: min 6, max 128) — reject early rather than round-trip. */
39
+ const MIN_PASSWORD_LENGTH = 6;
38
40
  function adapt(store) {
39
41
  return {
40
42
  get: (name) => store.get(name)?.value,
@@ -91,6 +93,7 @@ function createAuthHandlers(handlersConfig = {}) {
91
93
  async authenticate(request) {
92
94
  const config = resolveNextConfig(handlersConfig);
93
95
  const redirectUri = `${new URL(request.url).origin}${callbackPath}`;
96
+ const client = buildServerAuthClient(config);
94
97
  let payload;
95
98
  try {
96
99
  payload = await request.json();
@@ -102,36 +105,63 @@ function createAuthHandlers(handlersConfig = {}) {
102
105
  if (!email || !password) return Response.json({ status: "invalid_request" }, { status: 400 });
103
106
  const organizationCode = typeof payload.organizationCode === "string" ? payload.organizationCode : void 0;
104
107
  const tenantId = typeof payload.tenantId === "string" ? payload.tenantId : void 0;
105
- const { verifier, challenge } = await generatePkce();
106
- const state = randomState();
107
- const outcome = await buildServerAuthClient(config).authorizeWithPassword({
108
+ const newPassword = typeof payload.newPassword === "string" ? payload.newPassword : void 0;
109
+ const confirmPassword = typeof payload.confirmPassword === "string" ? payload.confirmPassword : void 0;
110
+ /** One authorize attempt, mapped to the wire response (sealing the session on success). */
111
+ async function attempt(withPassword) {
112
+ const { verifier, challenge } = await generatePkce();
113
+ const outcome = await client.authorizeWithPassword({
114
+ email,
115
+ password: withPassword,
116
+ redirectUri,
117
+ state: randomState(),
118
+ challenge,
119
+ organizationCode,
120
+ tenantId
121
+ });
122
+ switch (outcome.type) {
123
+ case "authorized":
124
+ await newSession(adapt(await cookies()), config).establishFromCode(outcome.code, verifier, redirectUri);
125
+ return Response.json({
126
+ status: "success",
127
+ redirectTo: afterSignInPath
128
+ });
129
+ case "select_tenant": return Response.json({
130
+ status: "select_tenant",
131
+ tenants: outcome.tenants,
132
+ state: outcome.state
133
+ });
134
+ case "password_change_required": return Response.json({
135
+ status: "password_change_required",
136
+ userId: outcome.userId
137
+ }, { status: 403 });
138
+ case "no_access": return Response.json({ status: "no_access" }, { status: 403 });
139
+ default: return Response.json({ status: "invalid_credentials" }, { status: 401 });
140
+ }
141
+ }
142
+ if (newPassword === void 0) return attempt(password);
143
+ if (newPassword.length < MIN_PASSWORD_LENGTH) return Response.json({
144
+ status: "weak_password",
145
+ minLength: MIN_PASSWORD_LENGTH
146
+ }, { status: 400 });
147
+ if (newPassword !== confirmPassword) return Response.json({ status: "password_mismatch" }, { status: 400 });
148
+ const { challenge } = await generatePkce();
149
+ const proof = await client.authorizeWithPassword({
108
150
  email,
109
151
  password,
110
152
  redirectUri,
111
- state,
153
+ state: randomState(),
112
154
  challenge,
113
155
  organizationCode,
114
156
  tenantId
115
157
  });
116
- switch (outcome.type) {
117
- case "authorized":
118
- await newSession(adapt(await cookies()), config).establishFromCode(outcome.code, verifier, redirectUri);
119
- return Response.json({
120
- status: "success",
121
- redirectTo: afterSignInPath
122
- });
123
- case "select_tenant": return Response.json({
124
- status: "select_tenant",
125
- tenants: outcome.tenants,
126
- state: outcome.state
127
- });
128
- case "password_change_required": return Response.json({
129
- status: "password_change_required",
130
- userId: outcome.userId
131
- }, { status: 403 });
132
- case "no_access": return Response.json({ status: "no_access" }, { status: 403 });
133
- default: return Response.json({ status: "invalid_credentials" }, { status: 401 });
134
- }
158
+ if (proof.type !== "password_change_required") return Response.json({ status: "invalid_credentials" }, { status: 401 });
159
+ await client.setPassword({
160
+ userId: proof.userId,
161
+ newPassword,
162
+ confirmPassword
163
+ });
164
+ return attempt(newPassword);
135
165
  },
136
166
  async callback(request) {
137
167
  const config = resolveNextConfig(handlersConfig);
@@ -0,0 +1,379 @@
1
+ /**
2
+ * @poly-x/react — default component styles.
3
+ *
4
+ * Import once, near the root of your app:
5
+ * import "@poly-x/react/styles.css"; // or "@poly-x/next/styles.css"
6
+ *
7
+ * The SDK is a guest in your app: every rule is scoped under a `.polyx-*` class,
8
+ * there are no global element selectors, and nothing here leaks into your own
9
+ * markup. To theme it, override the custom properties below on any ancestor
10
+ * (`:root`, a wrapper div, …) — that is the supported theming surface. Dark mode
11
+ * follows the OS by default and defers to an explicit `.dark` / `[data-theme]`
12
+ * on a host ancestor when one is present.
13
+ */
14
+
15
+ .polyx-signin {
16
+ /* Colors */
17
+ --polyx-brand: #4f46e5;
18
+ --polyx-brand-hover: #4338ca;
19
+ --polyx-brand-fg: #ffffff;
20
+ --polyx-bg: #ffffff;
21
+ --polyx-fg: #18181b;
22
+ --polyx-muted-fg: #71717a;
23
+ --polyx-border: #e4e4e7;
24
+ --polyx-input-bg: #ffffff;
25
+ --polyx-ring: #a5b4fc;
26
+ --polyx-danger: #dc2626;
27
+ --polyx-danger-bg: #fef2f2;
28
+ --polyx-page-bg: #fafafa;
29
+
30
+ /* Shape + type */
31
+ --polyx-radius: 0.75rem;
32
+ --polyx-radius-sm: 0.5rem;
33
+ --polyx-font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
34
+ --polyx-shadow: 0 1px 3px rgb(0 0 0 / 0.06), 0 10px 30px rgb(0 0 0 / 0.07);
35
+
36
+ font-family: var(--polyx-font);
37
+ color: var(--polyx-fg);
38
+ -webkit-font-smoothing: antialiased;
39
+ }
40
+
41
+ /* Scoped box model — applies only inside our own subtree. */
42
+ .polyx-signin,
43
+ .polyx-signin *,
44
+ .polyx-signin *::before,
45
+ .polyx-signin *::after {
46
+ box-sizing: border-box;
47
+ }
48
+
49
+ /* Dark: follow the OS… */
50
+ @media (prefers-color-scheme: dark) {
51
+ .polyx-signin {
52
+ --polyx-brand: #6366f1;
53
+ --polyx-brand-hover: #818cf8;
54
+ --polyx-bg: #18181b;
55
+ --polyx-fg: #fafafa;
56
+ --polyx-muted-fg: #a1a1aa;
57
+ --polyx-border: #3f3f46;
58
+ --polyx-input-bg: #27272a;
59
+ --polyx-ring: #4f46e5;
60
+ --polyx-danger: #f87171;
61
+ --polyx-danger-bg: #2a1616;
62
+ --polyx-page-bg: #09090b;
63
+ --polyx-shadow: 0 1px 3px rgb(0 0 0 / 0.4), 0 10px 30px rgb(0 0 0 / 0.35);
64
+ }
65
+ }
66
+
67
+ /* …but an explicit host theme wins over the OS preference (`:where` keeps
68
+ specificity at 0 so these stay as easy to override as the base rule). */
69
+ :where(.dark, [data-theme="dark"]) .polyx-signin {
70
+ --polyx-brand: #6366f1;
71
+ --polyx-brand-hover: #818cf8;
72
+ --polyx-bg: #18181b;
73
+ --polyx-fg: #fafafa;
74
+ --polyx-muted-fg: #a1a1aa;
75
+ --polyx-border: #3f3f46;
76
+ --polyx-input-bg: #27272a;
77
+ --polyx-ring: #4f46e5;
78
+ --polyx-danger: #f87171;
79
+ --polyx-danger-bg: #2a1616;
80
+ --polyx-page-bg: #09090b;
81
+ --polyx-shadow: 0 1px 3px rgb(0 0 0 / 0.4), 0 10px 30px rgb(0 0 0 / 0.35);
82
+ }
83
+
84
+ :where(.light, [data-theme="light"]) .polyx-signin {
85
+ --polyx-brand: #4f46e5;
86
+ --polyx-brand-hover: #4338ca;
87
+ --polyx-bg: #ffffff;
88
+ --polyx-fg: #18181b;
89
+ --polyx-muted-fg: #71717a;
90
+ --polyx-border: #e4e4e7;
91
+ --polyx-input-bg: #ffffff;
92
+ --polyx-ring: #a5b4fc;
93
+ --polyx-danger: #dc2626;
94
+ --polyx-danger-bg: #fef2f2;
95
+ --polyx-page-bg: #fafafa;
96
+ --polyx-shadow: 0 1px 3px rgb(0 0 0 / 0.06), 0 10px 30px rgb(0 0 0 / 0.07);
97
+ }
98
+
99
+ /* --- Variants ------------------------------------------------------------ */
100
+
101
+ /* `card`: just the card — drop it anywhere in your own layout. */
102
+ .polyx-signin--card {
103
+ display: block;
104
+ }
105
+
106
+ .polyx-signin__card {
107
+ width: 100%;
108
+ max-width: 27rem;
109
+ margin-inline: auto;
110
+ padding: 2.25rem;
111
+ background: var(--polyx-bg);
112
+ border: 1px solid var(--polyx-border);
113
+ border-radius: var(--polyx-radius);
114
+ box-shadow: var(--polyx-shadow);
115
+ }
116
+
117
+ /**
118
+ * `page`: owns the viewport. With brand content (a `logo`, a `tagline`, or a
119
+ * custom `panel`) it becomes a split layout — brand panel beside the form on
120
+ * wide screens, a slim brand band above it on narrow ones. With no brand content
121
+ * there's nothing to put in a panel, so it stays a single centered column.
122
+ */
123
+ .polyx-signin--page {
124
+ display: grid;
125
+ grid-template-columns: 1fr;
126
+ min-height: 100dvh;
127
+ }
128
+
129
+ .polyx-signin--page.polyx-signin--split {
130
+ grid-template-rows: auto 1fr;
131
+ }
132
+
133
+ @media (min-width: 64rem) {
134
+ .polyx-signin--page.polyx-signin--split {
135
+ grid-template-columns: 1.1fr 1fr;
136
+ grid-template-rows: 1fr;
137
+ }
138
+ }
139
+
140
+ .polyx-signin__panel {
141
+ display: flex;
142
+ position: relative;
143
+ overflow: hidden;
144
+ align-items: center;
145
+ gap: 1rem;
146
+ padding: 1.25rem 1.5rem;
147
+ color: var(--polyx-brand-fg);
148
+ background: linear-gradient(145deg, var(--polyx-brand), color-mix(in srgb, var(--polyx-brand) 55%, #000));
149
+ }
150
+
151
+ @media (min-width: 64rem) {
152
+ .polyx-signin__panel {
153
+ flex-direction: column;
154
+ align-items: flex-start;
155
+ justify-content: space-between;
156
+ gap: 2rem;
157
+ padding: 3.5rem;
158
+ }
159
+ }
160
+
161
+ /* Soft light-bloom so a bare brand panel still reads as designed, not as a flat fill. */
162
+ .polyx-signin__panel::after {
163
+ content: "";
164
+ position: absolute;
165
+ inset: 0;
166
+ pointer-events: none;
167
+ background:
168
+ radial-gradient(circle at 82% 12%, rgb(255 255 255 / 0.18), transparent 45%),
169
+ radial-gradient(circle at 12% 88%, rgb(255 255 255 / 0.1), transparent 42%);
170
+ }
171
+
172
+ .polyx-signin__panel-logo,
173
+ .polyx-signin__tagline {
174
+ position: relative;
175
+ z-index: 1;
176
+ }
177
+
178
+ /* The tagline is the panel's headline — it only earns its space on wide screens. */
179
+ .polyx-signin__tagline {
180
+ display: none;
181
+ margin: 0;
182
+ max-width: 18ch;
183
+ font-size: 2rem;
184
+ font-weight: 600;
185
+ line-height: 1.2;
186
+ letter-spacing: -0.02em;
187
+ }
188
+
189
+ @media (min-width: 64rem) {
190
+ .polyx-signin__tagline {
191
+ display: block;
192
+ }
193
+ }
194
+
195
+ .polyx-signin__main {
196
+ display: grid;
197
+ place-items: center;
198
+ padding: 2.5rem 1.5rem;
199
+ background: var(--polyx-page-bg);
200
+ }
201
+
202
+ /* On the page variant the form isn't a floating card — it sits on the page itself. */
203
+ .polyx-signin--page .polyx-signin__card {
204
+ max-width: 25rem;
205
+ padding: 0;
206
+ background: transparent;
207
+ border: none;
208
+ box-shadow: none;
209
+ }
210
+
211
+
212
+ /* --- Header -------------------------------------------------------------- */
213
+
214
+ .polyx-signin__header {
215
+ display: flex;
216
+ flex-direction: column;
217
+ gap: 0.375rem;
218
+ margin-bottom: 1.75rem;
219
+ }
220
+
221
+ .polyx-signin__logo {
222
+ display: flex;
223
+ margin-bottom: 0.75rem;
224
+ }
225
+
226
+ .polyx-signin__heading {
227
+ margin: 0;
228
+ font-size: 1.375rem;
229
+ font-weight: 600;
230
+ letter-spacing: -0.01em;
231
+ color: var(--polyx-fg);
232
+ }
233
+
234
+ .polyx-signin__subheading {
235
+ margin: 0;
236
+ font-size: 0.875rem;
237
+ color: var(--polyx-muted-fg);
238
+ }
239
+
240
+ /* --- Form ---------------------------------------------------------------- */
241
+
242
+ .polyx-signin__form {
243
+ display: flex;
244
+ flex-direction: column;
245
+ gap: 1.125rem;
246
+ }
247
+
248
+ .polyx-signin__field {
249
+ display: flex;
250
+ flex-direction: column;
251
+ gap: 0.4375rem;
252
+ }
253
+
254
+ .polyx-signin__label {
255
+ font-size: 0.875rem;
256
+ font-weight: 500;
257
+ color: var(--polyx-fg);
258
+ }
259
+
260
+ .polyx-signin__input {
261
+ width: 100%;
262
+ height: 2.5rem;
263
+ padding: 0 0.75rem;
264
+ font: inherit;
265
+ font-size: 0.875rem;
266
+ color: var(--polyx-fg);
267
+ background: var(--polyx-input-bg);
268
+ border: 1px solid var(--polyx-border);
269
+ border-radius: var(--polyx-radius-sm);
270
+ outline: none;
271
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
272
+ }
273
+
274
+ .polyx-signin__input::placeholder {
275
+ color: var(--polyx-muted-fg);
276
+ }
277
+
278
+ .polyx-signin__input:focus-visible {
279
+ border-color: var(--polyx-brand);
280
+ box-shadow: 0 0 0 3px var(--polyx-ring);
281
+ }
282
+
283
+ .polyx-signin__input:disabled {
284
+ opacity: 0.6;
285
+ cursor: not-allowed;
286
+ }
287
+
288
+ /* --- Buttons ------------------------------------------------------------- */
289
+
290
+ .polyx-signin__submit,
291
+ .polyx-signin__tenant {
292
+ display: inline-flex;
293
+ align-items: center;
294
+ justify-content: center;
295
+ width: 100%;
296
+ min-height: 2.5rem;
297
+ padding: 0 1rem;
298
+ font: inherit;
299
+ font-size: 0.875rem;
300
+ font-weight: 500;
301
+ color: var(--polyx-brand-fg);
302
+ background: var(--polyx-brand);
303
+ border: 1px solid transparent;
304
+ border-radius: var(--polyx-radius-sm);
305
+ cursor: pointer;
306
+ transition: background-color 0.15s ease;
307
+ }
308
+
309
+ .polyx-signin__submit:hover:not(:disabled),
310
+ .polyx-signin__tenant:hover:not(:disabled) {
311
+ background: var(--polyx-brand-hover);
312
+ }
313
+
314
+ .polyx-signin__submit:focus-visible,
315
+ .polyx-signin__tenant:focus-visible {
316
+ outline: none;
317
+ box-shadow: 0 0 0 3px var(--polyx-ring);
318
+ }
319
+
320
+ .polyx-signin__submit:disabled,
321
+ .polyx-signin__tenant:disabled {
322
+ opacity: 0.6;
323
+ cursor: not-allowed;
324
+ }
325
+
326
+ /* --- Error --------------------------------------------------------------- */
327
+
328
+ .polyx-signin__error {
329
+ margin: 0;
330
+ padding: 0.625rem 0.75rem;
331
+ font-size: 0.8125rem;
332
+ color: var(--polyx-danger);
333
+ background: var(--polyx-danger-bg);
334
+ border: 1px solid color-mix(in srgb, var(--polyx-danger) 30%, transparent);
335
+ border-radius: var(--polyx-radius-sm);
336
+ }
337
+
338
+ /* --- Footer -------------------------------------------------------------- */
339
+
340
+ .polyx-signin__footer {
341
+ display: flex;
342
+ align-items: center;
343
+ justify-content: center;
344
+ gap: 0.375rem;
345
+ margin-top: 1.5rem;
346
+ font-size: 0.75rem;
347
+ color: var(--polyx-muted-fg);
348
+ }
349
+
350
+ .polyx-signin__shield {
351
+ flex: none;
352
+ width: 0.875rem;
353
+ height: 0.875rem;
354
+ opacity: 0.85;
355
+ }
356
+
357
+ /* --- Workspace picker ---------------------------------------------------- */
358
+
359
+ .polyx-signin__tenants {
360
+ display: flex;
361
+ flex-direction: column;
362
+ gap: 0.625rem;
363
+ margin: 0;
364
+ padding: 0;
365
+ list-style: none;
366
+ }
367
+
368
+ /* The picker's buttons read as choices, not as the primary CTA. */
369
+ .polyx-signin__tenant {
370
+ justify-content: flex-start;
371
+ color: var(--polyx-fg);
372
+ background: var(--polyx-input-bg);
373
+ border-color: var(--polyx-border);
374
+ }
375
+
376
+ .polyx-signin__tenant:hover:not(:disabled) {
377
+ background: var(--polyx-input-bg);
378
+ border-color: var(--polyx-brand);
379
+ }
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@poly-x/next",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "PolyX SDK for Next.js App Router - components plus server helpers",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "sideEffects": false,
7
+ "sideEffects": [
8
+ "**/*.css"
9
+ ],
8
10
  "files": [
9
11
  "dist"
10
12
  ],
@@ -38,14 +40,15 @@
38
40
  "types": "./dist/proxy.d.cts",
39
41
  "default": "./dist/proxy.cjs"
40
42
  }
41
- }
43
+ },
44
+ "./styles.css": "./dist/styles.css"
42
45
  },
43
46
  "main": "./dist/index.cjs",
44
47
  "module": "./dist/index.mjs",
45
48
  "types": "./dist/index.d.cts",
46
49
  "dependencies": {
47
- "@poly-x/core": "0.1.0-alpha.2",
48
- "@poly-x/react": "0.1.0-alpha.2"
50
+ "@poly-x/core": "0.1.0-alpha.4",
51
+ "@poly-x/react": "0.1.0-alpha.4"
49
52
  },
50
53
  "peerDependencies": {
51
54
  "next": ">=14",
@@ -68,7 +71,7 @@
68
71
  "react-dom": "^19.2.7"
69
72
  },
70
73
  "scripts": {
71
- "build": "tsdown",
74
+ "build": "tsdown && node scripts/copy-styles.mjs",
72
75
  "test": "vitest run",
73
76
  "lint": "eslint src"
74
77
  }