Skip to content

Commit 274e607

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

File tree

3 files changed

+109
-19
lines changed

3 files changed

+109
-19
lines changed

src/lib/utils/authorship.ts

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,112 @@
11
import { redirect } from '@sveltejs/kit';
22

3-
import { TEMPORARY_REDIRECT } from '$lib/constants/http-response-status-codes';
3+
import { superValidate } from 'sveltekit-superforms/server';
4+
import { zod } from 'sveltekit-superforms/adapters';
5+
6+
import { TEMPORARY_REDIRECT, SEE_OTHER } from '$lib/constants/http-response-status-codes';
7+
import { HOME_PAGE } from '$lib/constants/navbar-links';
8+
import { authSchema } from '$lib/zod/schema';
49
import { Roles } from '$lib/types/user';
510

11+
/**
12+
* Initialize authentication form pages (login/signup)
13+
* Redirects to home page if already logged in,
14+
* otherwise initializes the authentication form for unauthenticated users
15+
*/
16+
export const initializeAuthForm = async (locals: App.Locals) => {
17+
const session = await locals.auth.validate();
18+
19+
if (session) {
20+
redirect(SEE_OTHER, HOME_PAGE);
21+
}
22+
23+
return await createAuthFormWithFallback();
24+
};
25+
26+
/**
27+
* Create authentication form with comprehensive fallback handling
28+
* Tries multiple strategies until one succeeds
29+
*/
30+
const createAuthFormWithFallback = async () => {
31+
for (const strategy of formCreationStrategies) {
32+
try {
33+
const result = await strategy.run();
34+
console.log(`Success: ${strategy.name}`);
35+
36+
return result;
37+
} catch (error) {
38+
console.warn(`Failed to ${strategy.name}`);
39+
40+
if (error instanceof Error) {
41+
console.warn('Error:', error.message);
42+
}
43+
}
44+
}
45+
46+
// This should never be reached due to manual creation strategy
47+
throw new Error('Failed to create form for authentication.');
48+
};
49+
50+
/**
51+
* Form creation strategies in order of preference
52+
* Each strategy attempts a different approach to create a valid form
53+
*
54+
* See:
55+
* https://superforms.rocks/concepts/client-validation
56+
* https://superforms.rocks/api#supervalidate-options
57+
*/
58+
const formCreationStrategies = [
59+
{
60+
name: '(Basic case) Use standard superValidate',
61+
async run() {
62+
const form = await superValidate(null, zod(authSchema));
63+
return { form: { ...form, message: '' } };
64+
},
65+
},
66+
{
67+
name: 'Use zod adapter explicitly',
68+
async run() {
69+
const zodAdapter = zod(authSchema);
70+
const form = await superValidate(null, zodAdapter);
71+
return { form: { ...form, message: '' } };
72+
},
73+
},
74+
{
75+
name: 'Create form by manually defining structure',
76+
async run() {
77+
const defaultForm = {
78+
id: 'fallback-form-' + Date.now(), // Note: Use only client-side validation
79+
valid: true,
80+
posted: false,
81+
data: { username: '', password: '' },
82+
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+
},
104+
};
105+
return { form: { ...defaultForm, message: '' } };
106+
},
107+
},
108+
];
109+
6110
export const ensureSessionOrRedirect = async (locals: App.Locals): Promise<void> => {
7111
const session = await locals.auth.validate();
8112

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

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { zod } from 'sveltekit-superforms/adapters';
66
import { fail, redirect } from '@sveltejs/kit';
77
import { LuciaError } from 'lucia';
88

9+
import { initializeAuthForm } from '$lib/utils/authorship';
910
import { authSchema } from '$lib/zod/schema';
1011
import { auth } from '$lib/server/auth';
1112

@@ -19,15 +20,7 @@ import { HOME_PAGE } from '$lib/constants/navbar-links';
1920
import type { Actions, PageServerLoad } from './$types';
2021

2122
export const load: PageServerLoad = async ({ locals }) => {
22-
const session = await locals.auth.validate();
23-
24-
if (session) {
25-
redirect(SEE_OTHER, HOME_PAGE);
26-
}
27-
28-
const form = await superValidate(null, zod(authSchema));
29-
30-
return { form: { ...form, message: '' } };
23+
return await initializeAuthForm(locals);
3124
};
3225

3326
export const actions: Actions = {

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

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { fail, redirect } from '@sveltejs/kit';
77
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
88
import { LuciaError } from 'lucia';
99

10+
import { initializeAuthForm } from '$lib/utils/authorship';
1011
import { authSchema } from '$lib/zod/schema';
1112
import { auth } from '$lib/server/auth';
1213

@@ -20,15 +21,7 @@ import { HOME_PAGE } from '$lib/constants/navbar-links';
2021
import type { Actions, PageServerLoad } from './$types';
2122

2223
export const load: PageServerLoad = async ({ locals }) => {
23-
const session = await locals.auth.validate();
24-
25-
if (session) {
26-
redirect(SEE_OTHER, HOME_PAGE);
27-
}
28-
29-
const form = await superValidate(null, zod(authSchema));
30-
31-
return { form: { ...form, message: '' } };
24+
return await initializeAuthForm(locals);
3225
};
3326

3427
// FIXME: エラー処理に共通部分があるため、リファクタリングをしましょう。

0 commit comments

Comments
 (0)