Skip to content

Commit 362c923

Browse files
committed
🐛 Try to fix for "SchemaError: No shape could be created for schema" (#2446)
1 parent 274e607 commit 362c923

File tree

2 files changed

+100
-28
lines changed

2 files changed

+100
-28
lines changed

src/lib/utils/authorship.ts

Lines changed: 98 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -75,38 +75,113 @@ const formCreationStrategies = [
7575
name: 'Create form by manually defining structure',
7676
async run() {
7777
const defaultForm = {
78-
id: 'fallback-form-' + Date.now(), // Note: Use only client-side validation
7978
valid: true,
8079
posted: false,
81-
data: { username: '', password: '' },
8280
errors: {},
83-
constraints: {
84-
username: { minlength: 3, maxlength: 24, required: true, pattern: '[\\w]*' },
85-
password: {
86-
minlength: 8,
87-
maxlength: 128,
88-
required: true,
89-
pattern: '(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\\d)[a-zA-Z\\d]{8,128}',
90-
},
91-
},
92-
// [Workaround] Critical fix for production environment schema shape error
93-
// SuperForms requires a 'shape' property for internal form structure validation
94-
// In production builds, Zod schema internals may be optimized away, causing
95-
// "SchemaError: No shape could be created for schema" errors
96-
//
97-
// See:
98-
// SuperForms source - schemaShape() function in adapters/zod.ts
99-
// https://github.com/ciscoheat/sveltekit-superforms/issues/594
100-
shape: {
101-
username: { type: 'string' },
102-
password: { type: 'string' },
103-
},
81+
message: '',
82+
...createBaseAuthForm(),
10483
};
84+
10585
return { form: { ...defaultForm, message: '' } };
10686
},
10787
},
10888
];
10989

90+
/**
91+
* Validate authentication form data with comprehensive fallback handling
92+
* Tries multiple strategies until one succeeds
93+
*
94+
* @param request - The incoming request containing form data
95+
* @returns Object containing success status and either form or errorResponse
96+
*/
97+
export const validateAuthFormWithFallback = async (request: Request) => {
98+
for (const strategy of formValidationStrategies) {
99+
try {
100+
const result = await strategy.run(request);
101+
console.log(`${strategy.name} successful`);
102+
103+
return result.form;
104+
} catch (error) {
105+
console.warn(`Failed to ${strategy.name}`);
106+
107+
if (error instanceof Error) {
108+
console.warn('Error:', error.message);
109+
}
110+
}
111+
}
112+
113+
// This should never be reached due to fallback strategy
114+
throw new Error('Failed to validate form for authentication.');
115+
};
116+
117+
/**
118+
* Form validation strategies for action handlers
119+
* Each strategy attempts a different approach to validate form data from requests
120+
*/
121+
const formValidationStrategies = [
122+
{
123+
name: '(Basic Case) Use standard superValidate with request',
124+
async run(request: Request) {
125+
const form = await superValidate(request, zod(authSchema));
126+
return { form: { ...form, message: '' } };
127+
},
128+
},
129+
{
130+
name: 'Use zod adapter explicitly with request',
131+
async run(request: Request) {
132+
const zodAdapter = zod(authSchema);
133+
const form = await superValidate(request, zodAdapter);
134+
return { form: { ...form, message: '' } };
135+
},
136+
},
137+
{
138+
name: 'Create fallback form manually',
139+
async run(request: Request) {
140+
// Create a fallback form with error state
141+
// This maintains consistency with other strategies by returning { form }
142+
const fallbackForm = {
143+
valid: false,
144+
posted: true,
145+
errors: { _form: ['ログインできませんでした。'] },
146+
message: 'サーバでエラーが発生しました。本サービスの開発・運営チームまでご連絡ください。',
147+
...createBaseAuthForm(),
148+
};
149+
150+
return { form: { ...fallbackForm, message: '' } };
151+
},
152+
},
153+
];
154+
155+
/**
156+
* Common form structure for authentication forms
157+
* Contains constraints and shape definitions used across different form strategies
158+
*/
159+
const createBaseAuthForm = () => ({
160+
id: 'error-fallback-form-' + Date.now(), // Note: Use only client-side validation
161+
data: { username: '', password: '' },
162+
constraints: {
163+
username: { minlength: 3, maxlength: 24, required: true, pattern: '[\\w]*' },
164+
password: {
165+
minlength: 8,
166+
maxlength: 128,
167+
required: true,
168+
pattern: '(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\\d)[a-zA-Z\\d]{8,128}',
169+
},
170+
},
171+
// [Workaround] Critical fix for production environment schema shape error
172+
// SuperForms requires a 'shape' property for internal form structure validation
173+
// In production builds, Zod schema internals may be optimized away, causing
174+
// "SchemaError: No shape could be created for schema" errors
175+
//
176+
// See:
177+
// SuperForms source - schemaShape() function in adapters/zod.ts
178+
// https://github.com/ciscoheat/sveltekit-superforms/issues/594
179+
shape: {
180+
username: { type: 'string' },
181+
password: { type: 'string' },
182+
},
183+
});
184+
110185
export const ensureSessionOrRedirect = async (locals: App.Locals): Promise<void> => {
111186
const session = await locals.auth.validate();
112187

src/routes/(auth)/login/+page.server.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
// See:
22
// https://lucia-auth.com/guidebook/sign-in-with-username-and-password/sveltekit/
33
// https://superforms.rocks/get-started
4-
import { superValidate } from 'sveltekit-superforms/server';
5-
import { zod } from 'sveltekit-superforms/adapters';
64
import { fail, redirect } from '@sveltejs/kit';
75
import { LuciaError } from 'lucia';
86

9-
import { initializeAuthForm } from '$lib/utils/authorship';
10-
import { authSchema } from '$lib/zod/schema';
7+
import { initializeAuthForm, validateAuthFormWithFallback } from '$lib/utils/authorship';
118
import { auth } from '$lib/server/auth';
129

1310
import {
@@ -25,7 +22,7 @@ export const load: PageServerLoad = async ({ locals }) => {
2522

2623
export const actions: Actions = {
2724
default: async ({ request, locals }) => {
28-
const form = await superValidate(request, zod(authSchema));
25+
const form = await validateAuthFormWithFallback(request);
2926

3027
if (!form.valid) {
3128
return fail(BAD_REQUEST, {

0 commit comments

Comments
 (0)