@cloudwerk/vite-plugin 0.6.3 → 0.6.6
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/index.d.ts +5 -1
- package/dist/index.js +445 -15
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { CloudwerkConfig, QueueManifest, ServiceManifest, RouteManifest, ScanResult } from '@cloudwerk/core/build';
|
|
2
|
+
import { CloudwerkConfig, QueueManifest, ServiceManifest, AuthManifest, RouteManifest, ScanResult } from '@cloudwerk/core/build';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @cloudwerk/vite-plugin - Types
|
|
@@ -185,6 +185,10 @@ interface GenerateServerEntryOptions {
|
|
|
185
185
|
serviceManifest?: ServiceManifest | null;
|
|
186
186
|
/** Asset manifest from Vite build for CSS injection */
|
|
187
187
|
assetManifest?: AssetManifest | null;
|
|
188
|
+
/** Auth manifest if auth providers are configured */
|
|
189
|
+
authManifest?: AuthManifest | null;
|
|
190
|
+
/** CSS imports from layouts/pages (for dev mode injection) */
|
|
191
|
+
cssImports?: Map<string, CssImportInfo[]>;
|
|
188
192
|
}
|
|
189
193
|
/**
|
|
190
194
|
* Generate the server entry module code.
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,13 @@ import {
|
|
|
19
19
|
scanServices,
|
|
20
20
|
buildServiceManifest,
|
|
21
21
|
SERVICES_DIR,
|
|
22
|
-
SERVICE_FILE_NAME
|
|
22
|
+
SERVICE_FILE_NAME,
|
|
23
|
+
scanImages,
|
|
24
|
+
buildImageManifest,
|
|
25
|
+
IMAGES_DIR,
|
|
26
|
+
scanAuth,
|
|
27
|
+
buildAuthManifestWithModules,
|
|
28
|
+
AUTH_DIR
|
|
23
29
|
} from "@cloudwerk/core/build";
|
|
24
30
|
|
|
25
31
|
// src/types.ts
|
|
@@ -40,6 +46,7 @@ function generateServerEntry(manifest, scanResult, options, entryOptions) {
|
|
|
40
46
|
const queueManifest = entryOptions?.queueManifest;
|
|
41
47
|
const serviceManifest = entryOptions?.serviceManifest;
|
|
42
48
|
const assetManifest = entryOptions?.assetManifest;
|
|
49
|
+
const authManifest = entryOptions?.authManifest;
|
|
43
50
|
const imports = [];
|
|
44
51
|
const pageRegistrations = [];
|
|
45
52
|
const routeRegistrations = [];
|
|
@@ -58,6 +65,10 @@ function generateServerEntry(manifest, scanResult, options, entryOptions) {
|
|
|
58
65
|
let middlewareIndex = 0;
|
|
59
66
|
let errorIndex = 0;
|
|
60
67
|
let notFoundIndex = 0;
|
|
68
|
+
const rootMiddleware = scanResult.middleware.find(
|
|
69
|
+
(m) => m.relativePath === "middleware.ts" || m.relativePath === "middleware.tsx"
|
|
70
|
+
);
|
|
71
|
+
let rootMiddlewareVarName = null;
|
|
61
72
|
const ssgPageInfo = [];
|
|
62
73
|
for (const err of scanResult.errors) {
|
|
63
74
|
if (!importedModules.has(err.absolutePath)) {
|
|
@@ -89,6 +100,12 @@ function generateServerEntry(manifest, scanResult, options, entryOptions) {
|
|
|
89
100
|
const varName = notFoundModules.get(nf.absolutePath);
|
|
90
101
|
notFoundBoundaryMapEntries.push(` ['${normalizedDir}', ${varName}]`);
|
|
91
102
|
}
|
|
103
|
+
if (rootMiddleware) {
|
|
104
|
+
rootMiddlewareVarName = `middleware_${middlewareIndex++}`;
|
|
105
|
+
middlewareImports.push(`import { middleware as ${rootMiddlewareVarName} } from '${rootMiddleware.absolutePath}'`);
|
|
106
|
+
middlewareModules.set(rootMiddleware.absolutePath, rootMiddlewareVarName);
|
|
107
|
+
importedModules.add(rootMiddleware.absolutePath);
|
|
108
|
+
}
|
|
92
109
|
for (const route of manifest.routes) {
|
|
93
110
|
for (const middlewarePath of route.middleware) {
|
|
94
111
|
if (!importedModules.has(middlewarePath)) {
|
|
@@ -139,7 +156,17 @@ function generateServerEntry(manifest, scanResult, options, entryOptions) {
|
|
|
139
156
|
}
|
|
140
157
|
}
|
|
141
158
|
const rendererName = options.renderer;
|
|
142
|
-
|
|
159
|
+
let clientEntryPath = "/@id/__x00__virtual:cloudwerk/client-entry";
|
|
160
|
+
if (options.isProduction && assetManifest) {
|
|
161
|
+
const clientEntry = assetManifest["virtual:cloudwerk/client-entry"];
|
|
162
|
+
if (clientEntry?.file) {
|
|
163
|
+
clientEntryPath = `/${clientEntry.file}`;
|
|
164
|
+
} else {
|
|
165
|
+
clientEntryPath = `${options.hydrationEndpoint}/client.js`;
|
|
166
|
+
}
|
|
167
|
+
} else if (options.isProduction) {
|
|
168
|
+
clientEntryPath = `${options.hydrationEndpoint}/client.js`;
|
|
169
|
+
}
|
|
143
170
|
let cssLinksCode = "";
|
|
144
171
|
if (options.isProduction && assetManifest) {
|
|
145
172
|
const clientEntry = assetManifest["virtual:cloudwerk/client-entry"];
|
|
@@ -147,6 +174,17 @@ function generateServerEntry(manifest, scanResult, options, entryOptions) {
|
|
|
147
174
|
const cssLinks = clientEntry.css.map((css) => `<link rel="stylesheet" href="/${css}" />`).join("");
|
|
148
175
|
cssLinksCode = `const CSS_LINKS = '${cssLinks}'`;
|
|
149
176
|
}
|
|
177
|
+
} else if (!options.isProduction && entryOptions?.cssImports) {
|
|
178
|
+
const allCss = /* @__PURE__ */ new Set();
|
|
179
|
+
for (const imports2 of entryOptions.cssImports.values()) {
|
|
180
|
+
for (const info of imports2) {
|
|
181
|
+
allCss.add(info.absolutePath);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (allCss.size > 0) {
|
|
185
|
+
const cssLinks = Array.from(allCss).map((css) => `<link rel="stylesheet" href="/@fs${css}" />`).join("");
|
|
186
|
+
cssLinksCode = `const CSS_LINKS = '${cssLinks}'`;
|
|
187
|
+
}
|
|
150
188
|
}
|
|
151
189
|
if (!cssLinksCode) {
|
|
152
190
|
cssLinksCode = `const CSS_LINKS = ''`;
|
|
@@ -160,7 +198,7 @@ function generateServerEntry(manifest, scanResult, options, entryOptions) {
|
|
|
160
198
|
import { Hono } from 'hono'
|
|
161
199
|
import { ssgParams } from 'hono/ssg'
|
|
162
200
|
import { contextMiddleware, createHandlerAdapter, createMiddlewareAdapter, setRouteConfig, NotFoundError, RedirectError } from '@cloudwerk/core/runtime'
|
|
163
|
-
import { setActiveRenderer } from '@cloudwerk/ui'
|
|
201
|
+
import { setActiveRenderer${rendererName === "react" ? ", initReactRenderer" : ""} } from '@cloudwerk/ui'
|
|
164
202
|
|
|
165
203
|
// Page and Route Imports
|
|
166
204
|
${imports.join("\n")}
|
|
@@ -289,7 +327,7 @@ async function renderErrorPage(error, errorModule, layoutModules, layoutLoaderDa
|
|
|
289
327
|
element = await Promise.resolve(Layout(layoutProps))
|
|
290
328
|
}
|
|
291
329
|
|
|
292
|
-
return renderWithHydration(element, 500)
|
|
330
|
+
return await renderWithHydration(element, 500)
|
|
293
331
|
}
|
|
294
332
|
|
|
295
333
|
/**
|
|
@@ -316,7 +354,7 @@ async function renderNotFoundPage(notFoundModule, layoutModules, layoutLoaderDat
|
|
|
316
354
|
element = await Promise.resolve(Layout(layoutProps))
|
|
317
355
|
}
|
|
318
356
|
|
|
319
|
-
return renderWithHydration(element, 404)
|
|
357
|
+
return await renderWithHydration(element, 404)
|
|
320
358
|
}
|
|
321
359
|
|
|
322
360
|
// ============================================================================
|
|
@@ -390,7 +428,7 @@ function registerPage(app, pattern, pageModule, layoutModules, middlewareModules
|
|
|
390
428
|
}
|
|
391
429
|
|
|
392
430
|
// Render the page with hydration script injection
|
|
393
|
-
return renderWithHydration(element)
|
|
431
|
+
return await renderWithHydration(element)
|
|
394
432
|
} catch (error) {
|
|
395
433
|
// Handle NotFoundError (check both instanceof and name for module duplication)
|
|
396
434
|
if (error instanceof NotFoundError || error?.name === 'NotFoundError') {
|
|
@@ -422,9 +460,12 @@ function registerPage(app, pattern, pageModule, layoutModules, middlewareModules
|
|
|
422
460
|
* - CSS links are injected before </head>
|
|
423
461
|
* - Vite client (dev) and hydration script are injected before </body>
|
|
424
462
|
*/
|
|
425
|
-
function renderWithHydration(element, status = 200) {
|
|
426
|
-
//
|
|
427
|
-
|
|
463
|
+
async function renderWithHydration(element, status = 200) {
|
|
464
|
+
// Render element to HTML string using the active renderer
|
|
465
|
+
${rendererName === "react" ? `// React: use renderToString from react-dom/server
|
|
466
|
+
const { renderToString } = await import('react-dom/server')
|
|
467
|
+
let html = '<!DOCTYPE html>' + renderToString(element)` : `// Hono JSX elements have toString() for synchronous rendering
|
|
468
|
+
let html = '<!DOCTYPE html>' + String(element)`}
|
|
428
469
|
|
|
429
470
|
// Inject CSS links before </head> if present
|
|
430
471
|
if (CSS_LINKS) {
|
|
@@ -471,7 +512,7 @@ function registerRoute(app, pattern, routeModule, middlewareModules) {
|
|
|
471
512
|
for (const method of HTTP_METHODS) {
|
|
472
513
|
const handler = routeModule[method]
|
|
473
514
|
if (handler && typeof handler === 'function') {
|
|
474
|
-
const h =
|
|
515
|
+
const h = createHandlerAdapter(handler)
|
|
475
516
|
switch (method) {
|
|
476
517
|
case 'GET': app.get(pattern, h); break
|
|
477
518
|
case 'POST': app.post(pattern, h); break
|
|
@@ -490,14 +531,18 @@ function registerRoute(app, pattern, routeModule, middlewareModules) {
|
|
|
490
531
|
// ============================================================================
|
|
491
532
|
|
|
492
533
|
// Initialize renderer
|
|
493
|
-
|
|
534
|
+
${rendererName === "react" ? `await initReactRenderer()
|
|
535
|
+
` : ""}setActiveRenderer('${rendererName}')
|
|
494
536
|
|
|
495
537
|
// Create Hono app
|
|
496
538
|
const app = new Hono({ strict: false })
|
|
497
539
|
|
|
498
540
|
// Add context middleware
|
|
499
541
|
app.use('*', contextMiddleware())
|
|
500
|
-
${
|
|
542
|
+
${rootMiddlewareVarName ? `
|
|
543
|
+
// Apply root middleware globally (for all routes including auth)
|
|
544
|
+
app.use('*', createMiddlewareAdapter(${rootMiddlewareVarName}))
|
|
545
|
+
` : ""}${options.isProduction ? `
|
|
501
546
|
// Serve static assets using Workers Static Assets binding (production only)
|
|
502
547
|
app.use('*', async (c, next) => {
|
|
503
548
|
// Check if ASSETS binding is available
|
|
@@ -518,9 +563,27 @@ app.use('*', async (c, next) => {
|
|
|
518
563
|
// Try to serve the request as a static asset
|
|
519
564
|
const response = await c.env.ASSETS.fetch(c.req.raw)
|
|
520
565
|
|
|
521
|
-
// If asset found (not 404), return it
|
|
566
|
+
// If asset found (not 404), return it with cache headers
|
|
522
567
|
if (response.status !== 404) {
|
|
523
|
-
|
|
568
|
+
const path = new URL(c.req.url).pathname
|
|
569
|
+
|
|
570
|
+
// Check if this is a hashed asset (Vite adds content hash to filename)
|
|
571
|
+
// Hashed assets are immutable and can be cached forever
|
|
572
|
+
const isHashedAsset = path.startsWith('/__cloudwerk/') ||
|
|
573
|
+
/-[a-zA-Z0-9]{8,}\\.(js|css|woff2?|ttf|eot|svg|png|jpg|jpeg|gif|webp|avif|ico)$/.test(path)
|
|
574
|
+
|
|
575
|
+
const cacheControl = isHashedAsset
|
|
576
|
+
? 'public, max-age=31536000, immutable'
|
|
577
|
+
: 'public, max-age=3600'
|
|
578
|
+
|
|
579
|
+
return new Response(response.body, {
|
|
580
|
+
status: response.status,
|
|
581
|
+
statusText: response.statusText,
|
|
582
|
+
headers: {
|
|
583
|
+
...Object.fromEntries(response.headers.entries()),
|
|
584
|
+
'Cache-Control': cacheControl,
|
|
585
|
+
},
|
|
586
|
+
})
|
|
524
587
|
}
|
|
525
588
|
|
|
526
589
|
// Asset not found, continue to routes
|
|
@@ -531,6 +594,9 @@ app.use('*', async (c, next) => {
|
|
|
531
594
|
${pageRegistrations.join("\n")}
|
|
532
595
|
${routeRegistrations.join("\n")}
|
|
533
596
|
|
|
597
|
+
// Register auth routes
|
|
598
|
+
${generateAuthRouteRegistrations(authManifest)}
|
|
599
|
+
|
|
534
600
|
// SSG routes endpoint - returns all static routes for build-time generation
|
|
535
601
|
app.get('/__ssg/routes', async (c) => {
|
|
536
602
|
const routes = []
|
|
@@ -766,6 +832,242 @@ function generateServiceRegistration(serviceManifest) {
|
|
|
766
832
|
}
|
|
767
833
|
return lines.join("\n");
|
|
768
834
|
}
|
|
835
|
+
function generateAuthRouteRegistrations(authManifest) {
|
|
836
|
+
if (!authManifest) {
|
|
837
|
+
return "";
|
|
838
|
+
}
|
|
839
|
+
const lines = [];
|
|
840
|
+
const imports = [];
|
|
841
|
+
const basePath = authManifest.config?.basePath || "/auth";
|
|
842
|
+
lines.push("");
|
|
843
|
+
lines.push("// ============================================================================");
|
|
844
|
+
lines.push("// Auth Route Registration");
|
|
845
|
+
lines.push("// ============================================================================");
|
|
846
|
+
lines.push("");
|
|
847
|
+
imports.push(`import {
|
|
848
|
+
handleSession,
|
|
849
|
+
handleProviders,
|
|
850
|
+
handleSignIn,
|
|
851
|
+
handleSignInProvider,
|
|
852
|
+
handleSignOutGet,
|
|
853
|
+
handleSignOutPost,
|
|
854
|
+
} from '@cloudwerk/auth/routes'`);
|
|
855
|
+
imports.push(`import { createSessionManager, createKVSessionAdapter } from '@cloudwerk/auth/session'`);
|
|
856
|
+
const passkeyProviders = authManifest.providers.filter((p) => p.type === "passkey" && !p.disabled);
|
|
857
|
+
if (passkeyProviders.length > 0) {
|
|
858
|
+
imports.push(`import {
|
|
859
|
+
handlePasskeyRegisterOptions,
|
|
860
|
+
handlePasskeyRegisterVerify,
|
|
861
|
+
handlePasskeyAuthenticateOptions,
|
|
862
|
+
handlePasskeyAuthenticateVerify,
|
|
863
|
+
} from '@cloudwerk/auth/routes'`);
|
|
864
|
+
imports.push(`import {
|
|
865
|
+
createKVChallengeStorage,
|
|
866
|
+
createD1CredentialStorage,
|
|
867
|
+
} from '@cloudwerk/auth/providers'`);
|
|
868
|
+
for (let i = 0; i < passkeyProviders.length; i++) {
|
|
869
|
+
const provider = passkeyProviders[i];
|
|
870
|
+
imports.push(`import passkeyProviderDef_${i} from '${provider.filePath}'`);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
lines.push(imports.join("\n"));
|
|
874
|
+
lines.push("");
|
|
875
|
+
lines.push(`
|
|
876
|
+
/**
|
|
877
|
+
* Build auth context for standard auth routes.
|
|
878
|
+
*/
|
|
879
|
+
function buildAuthContext(c) {
|
|
880
|
+
const env = c.env || {}
|
|
881
|
+
|
|
882
|
+
// Get KV binding - fall back to common binding names
|
|
883
|
+
let kvBinding = undefined
|
|
884
|
+
for (const name of ['FLAGSHIP_AUTH_SESSIONS', 'AUTH_KV', 'AUTH_SESSIONS', 'KV']) {
|
|
885
|
+
const binding = env[name]
|
|
886
|
+
if (binding && typeof binding.get === 'function') {
|
|
887
|
+
kvBinding = binding
|
|
888
|
+
break
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// Create session manager from KV
|
|
893
|
+
const sessionAdapter = kvBinding ? createKVSessionAdapter({ binding: kvBinding, enableUserIndex: true }) : undefined
|
|
894
|
+
const sessionManager = sessionAdapter ? createSessionManager({ adapter: sessionAdapter }) : undefined
|
|
895
|
+
|
|
896
|
+
// Build providers map
|
|
897
|
+
const providers = new Map()
|
|
898
|
+
|
|
899
|
+
return {
|
|
900
|
+
request: c.req.raw,
|
|
901
|
+
env,
|
|
902
|
+
config: { basePath: '${basePath}', session: { strategy: '${authManifest.config?.sessionStrategy || "database"}' } },
|
|
903
|
+
sessionManager,
|
|
904
|
+
providers,
|
|
905
|
+
user: c.get?.('user') ?? null,
|
|
906
|
+
session: c.get?.('session') ?? null,
|
|
907
|
+
url: new URL(c.req.url),
|
|
908
|
+
responseHeaders: new Headers(),
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
`);
|
|
912
|
+
lines.push(`
|
|
913
|
+
// Standard auth routes
|
|
914
|
+
app.get('${basePath}/session', async (c) => {
|
|
915
|
+
const ctx = buildAuthContext(c)
|
|
916
|
+
return handleSession(ctx)
|
|
917
|
+
})
|
|
918
|
+
|
|
919
|
+
app.get('${basePath}/providers', async (c) => {
|
|
920
|
+
const ctx = buildAuthContext(c)
|
|
921
|
+
return handleProviders(ctx)
|
|
922
|
+
})
|
|
923
|
+
|
|
924
|
+
app.get('${basePath}/signin', async (c) => {
|
|
925
|
+
const ctx = buildAuthContext(c)
|
|
926
|
+
return handleSignIn(ctx)
|
|
927
|
+
})
|
|
928
|
+
|
|
929
|
+
app.get('${basePath}/signin/:provider', async (c) => {
|
|
930
|
+
const ctx = buildAuthContext(c)
|
|
931
|
+
const providerId = c.req.param('provider')
|
|
932
|
+
return handleSignInProvider(ctx, providerId)
|
|
933
|
+
})
|
|
934
|
+
|
|
935
|
+
app.get('${basePath}/signout', async (c) => {
|
|
936
|
+
const ctx = buildAuthContext(c)
|
|
937
|
+
return handleSignOutGet(ctx)
|
|
938
|
+
})
|
|
939
|
+
|
|
940
|
+
app.post('${basePath}/signout', async (c) => {
|
|
941
|
+
const ctx = buildAuthContext(c)
|
|
942
|
+
return handleSignOutPost(ctx)
|
|
943
|
+
})
|
|
944
|
+
`);
|
|
945
|
+
if (passkeyProviders.length > 0) {
|
|
946
|
+
lines.push(`
|
|
947
|
+
/**
|
|
948
|
+
* Build auth context for passkey handlers.
|
|
949
|
+
*/
|
|
950
|
+
function buildPasskeyAuthContext(c, passkeyProvider) {
|
|
951
|
+
const env = c.env || {}
|
|
952
|
+
|
|
953
|
+
// Get KV binding for challenges - use provider config or fall back to common names
|
|
954
|
+
const kvBindingName = passkeyProvider.kvBinding
|
|
955
|
+
let kvBinding = kvBindingName ? env[kvBindingName] : undefined
|
|
956
|
+
if (!kvBinding) {
|
|
957
|
+
// Fall back to common binding names
|
|
958
|
+
for (const name of ['AUTH_KV', 'AUTH_SESSIONS', 'KV']) {
|
|
959
|
+
const binding = env[name]
|
|
960
|
+
if (binding && typeof binding.get === 'function') {
|
|
961
|
+
kvBinding = binding
|
|
962
|
+
break
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
const challengeStorage = kvBinding ? createKVChallengeStorage(kvBinding, 'auth:challenge:') : undefined
|
|
967
|
+
|
|
968
|
+
// Get D1 binding for credentials - use provider config or fall back to common names
|
|
969
|
+
const d1BindingName = passkeyProvider.d1Binding
|
|
970
|
+
const d1Binding = d1BindingName ? env[d1BindingName] : (env.DB || env.D1 || env.DATABASE)
|
|
971
|
+
const credentialStorage = d1Binding ? createD1CredentialStorage(d1Binding, 'webauthn_credentials') : undefined
|
|
972
|
+
|
|
973
|
+
// Create user adapter from D1
|
|
974
|
+
const userAdapter = d1Binding ? {
|
|
975
|
+
async getUserByEmail(email) {
|
|
976
|
+
const user = await d1Binding.prepare(
|
|
977
|
+
'SELECT id, email, email_verified, name, image, created_at, updated_at FROM users WHERE email = ?'
|
|
978
|
+
).bind(email).first()
|
|
979
|
+
if (!user) return null
|
|
980
|
+
return {
|
|
981
|
+
id: user.id,
|
|
982
|
+
email: user.email,
|
|
983
|
+
emailVerified: user.email_verified ? new Date(user.email_verified) : null,
|
|
984
|
+
name: user.name,
|
|
985
|
+
image: user.image,
|
|
986
|
+
createdAt: new Date(user.created_at),
|
|
987
|
+
updatedAt: new Date(user.updated_at),
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
async createUser(userData) {
|
|
991
|
+
const id = crypto.randomUUID()
|
|
992
|
+
const now = new Date().toISOString()
|
|
993
|
+
await d1Binding.prepare(
|
|
994
|
+
'INSERT INTO users (id, email, email_verified, name, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)'
|
|
995
|
+
).bind(id, userData.email, userData.emailVerified?.toISOString() ?? null, userData.name ?? null, now, now).run()
|
|
996
|
+
return { id, email: userData.email, emailVerified: userData.emailVerified, name: userData.name ?? null, image: null, createdAt: new Date(now), updatedAt: new Date(now) }
|
|
997
|
+
},
|
|
998
|
+
async getUser(id) {
|
|
999
|
+
const user = await d1Binding.prepare(
|
|
1000
|
+
'SELECT id, email, email_verified, name, image, created_at, updated_at FROM users WHERE id = ?'
|
|
1001
|
+
).bind(id).first()
|
|
1002
|
+
if (!user) return null
|
|
1003
|
+
return {
|
|
1004
|
+
id: user.id,
|
|
1005
|
+
email: user.email,
|
|
1006
|
+
emailVerified: user.email_verified ? new Date(user.email_verified) : null,
|
|
1007
|
+
name: user.name,
|
|
1008
|
+
image: user.image,
|
|
1009
|
+
createdAt: new Date(user.created_at),
|
|
1010
|
+
updatedAt: new Date(user.updated_at),
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
} : undefined
|
|
1014
|
+
|
|
1015
|
+
// Create session manager from KV
|
|
1016
|
+
const sessionAdapter = kvBinding ? createKVSessionAdapter({ binding: kvBinding, enableUserIndex: true }) : undefined
|
|
1017
|
+
const sessionManager = sessionAdapter ? createSessionManager({ adapter: sessionAdapter }) : undefined
|
|
1018
|
+
|
|
1019
|
+
// Build providers map
|
|
1020
|
+
const providers = new Map()
|
|
1021
|
+
providers.set(passkeyProvider.id, passkeyProvider)
|
|
1022
|
+
|
|
1023
|
+
return {
|
|
1024
|
+
request: c.req.raw,
|
|
1025
|
+
env,
|
|
1026
|
+
config: { basePath: '${basePath}', session: { strategy: '${authManifest.config?.sessionStrategy || "database"}' } },
|
|
1027
|
+
sessionManager,
|
|
1028
|
+
providers,
|
|
1029
|
+
user: null,
|
|
1030
|
+
session: null,
|
|
1031
|
+
url: new URL(c.req.url),
|
|
1032
|
+
responseHeaders: new Headers(),
|
|
1033
|
+
challengeStorage,
|
|
1034
|
+
credentialStorage,
|
|
1035
|
+
userAdapter,
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
`);
|
|
1039
|
+
for (let i = 0; i < passkeyProviders.length; i++) {
|
|
1040
|
+
const provider = passkeyProviders[i];
|
|
1041
|
+
lines.push(`
|
|
1042
|
+
// Get provider from definition
|
|
1043
|
+
const passkeyProvider_${i} = passkeyProviderDef_${i}.provider || passkeyProviderDef_${i}
|
|
1044
|
+
`);
|
|
1045
|
+
lines.push(`
|
|
1046
|
+
// Passkey registration routes
|
|
1047
|
+
app.post('${basePath}/passkey/register/options', async (c) => {
|
|
1048
|
+
const ctx = buildPasskeyAuthContext(c, passkeyProvider_${i})
|
|
1049
|
+
return handlePasskeyRegisterOptions(ctx, '${provider.id}')
|
|
1050
|
+
})
|
|
1051
|
+
|
|
1052
|
+
app.post('${basePath}/passkey/register/verify', async (c) => {
|
|
1053
|
+
const ctx = buildPasskeyAuthContext(c, passkeyProvider_${i})
|
|
1054
|
+
return handlePasskeyRegisterVerify(ctx, '${provider.id}')
|
|
1055
|
+
})
|
|
1056
|
+
|
|
1057
|
+
app.post('${basePath}/passkey/authenticate/options', async (c) => {
|
|
1058
|
+
const ctx = buildPasskeyAuthContext(c, passkeyProvider_${i})
|
|
1059
|
+
return handlePasskeyAuthenticateOptions(ctx, '${provider.id}')
|
|
1060
|
+
})
|
|
1061
|
+
|
|
1062
|
+
app.post('${basePath}/passkey/authenticate/verify', async (c) => {
|
|
1063
|
+
const ctx = buildPasskeyAuthContext(c, passkeyProvider_${i})
|
|
1064
|
+
return handlePasskeyAuthenticateVerify(ctx, '${provider.id}')
|
|
1065
|
+
})
|
|
1066
|
+
`);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return lines.join("\n");
|
|
1070
|
+
}
|
|
769
1071
|
|
|
770
1072
|
// src/virtual-modules/client-entry.ts
|
|
771
1073
|
function collectCssImports(cssImports) {
|
|
@@ -1798,6 +2100,62 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1798
2100
|
console.log(`[cloudwerk] Found ${state.serviceManifest.services.length} service(s)`);
|
|
1799
2101
|
}
|
|
1800
2102
|
}
|
|
2103
|
+
async function buildImageManifestIfExists(root) {
|
|
2104
|
+
if (!state) {
|
|
2105
|
+
throw new Error("Plugin state not initialized");
|
|
2106
|
+
}
|
|
2107
|
+
const imagesPath = path3.resolve(root, state.options.appDir, IMAGES_DIR);
|
|
2108
|
+
try {
|
|
2109
|
+
await fs2.promises.access(imagesPath);
|
|
2110
|
+
} catch {
|
|
2111
|
+
state.imageScanResult = null;
|
|
2112
|
+
state.imageManifest = null;
|
|
2113
|
+
return;
|
|
2114
|
+
}
|
|
2115
|
+
state.imageScanResult = await scanImages(
|
|
2116
|
+
path3.resolve(root, state.options.appDir),
|
|
2117
|
+
{ extensions: state.options.config.extensions }
|
|
2118
|
+
);
|
|
2119
|
+
state.imageManifest = buildImageManifest(
|
|
2120
|
+
state.imageScanResult,
|
|
2121
|
+
root
|
|
2122
|
+
);
|
|
2123
|
+
if (state.options.verbose && state.imageManifest.images.length > 0) {
|
|
2124
|
+
console.log(`[cloudwerk] Found ${state.imageManifest.images.length} image(s)`);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
async function buildAuthManifestIfExists(root) {
|
|
2128
|
+
if (!state) {
|
|
2129
|
+
throw new Error("Plugin state not initialized");
|
|
2130
|
+
}
|
|
2131
|
+
const authPath = path3.resolve(root, state.options.appDir, AUTH_DIR);
|
|
2132
|
+
try {
|
|
2133
|
+
await fs2.promises.access(authPath);
|
|
2134
|
+
} catch {
|
|
2135
|
+
state.authScanResult = null;
|
|
2136
|
+
state.authManifest = null;
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
state.authScanResult = await scanAuth(
|
|
2140
|
+
path3.resolve(root, state.options.appDir),
|
|
2141
|
+
{ extensions: state.options.config.extensions }
|
|
2142
|
+
);
|
|
2143
|
+
state.authManifest = await buildAuthManifestWithModules(state.authScanResult);
|
|
2144
|
+
state.serverEntryCache = null;
|
|
2145
|
+
if (state.options.verbose && state.authManifest.providers.length > 0) {
|
|
2146
|
+
console.log(`[cloudwerk] Found ${state.authManifest.providers.length} auth provider(s)`);
|
|
2147
|
+
for (const provider of state.authManifest.providers) {
|
|
2148
|
+
console.log(`[cloudwerk] - ${provider.id} (${provider.type})`);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
function isAuthFile(filePath) {
|
|
2153
|
+
if (!state) return false;
|
|
2154
|
+
const authDir = path3.resolve(state.options.root, state.options.appDir, AUTH_DIR);
|
|
2155
|
+
if (!filePath.startsWith(authDir)) return false;
|
|
2156
|
+
const ext = path3.extname(filePath);
|
|
2157
|
+
return state.options.config.extensions.includes(ext);
|
|
2158
|
+
}
|
|
1801
2159
|
function isRouteFile(filePath) {
|
|
1802
2160
|
if (!state) return false;
|
|
1803
2161
|
const appDir = path3.resolve(state.options.root, state.options.appDir);
|
|
@@ -1825,6 +2183,15 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1825
2183
|
if (nameWithoutExt !== SERVICE_FILE_NAME) return false;
|
|
1826
2184
|
return state.options.config.extensions.includes(ext);
|
|
1827
2185
|
}
|
|
2186
|
+
function isImageFile(filePath) {
|
|
2187
|
+
if (!state) return false;
|
|
2188
|
+
const imagesDir = path3.resolve(state.options.root, state.options.appDir, IMAGES_DIR);
|
|
2189
|
+
if (!filePath.startsWith(imagesDir)) return false;
|
|
2190
|
+
const relativePath = path3.relative(imagesDir, filePath);
|
|
2191
|
+
if (relativePath.includes(path3.sep)) return false;
|
|
2192
|
+
const ext = path3.extname(filePath);
|
|
2193
|
+
return state.options.config.extensions.includes(ext);
|
|
2194
|
+
}
|
|
1828
2195
|
function invalidateVirtualModules() {
|
|
1829
2196
|
if (!server) return;
|
|
1830
2197
|
const idsToInvalidate = [
|
|
@@ -1919,6 +2286,10 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1919
2286
|
queueScanResult: null,
|
|
1920
2287
|
serviceManifest: null,
|
|
1921
2288
|
serviceScanResult: null,
|
|
2289
|
+
imageManifest: null,
|
|
2290
|
+
imageScanResult: null,
|
|
2291
|
+
authManifest: null,
|
|
2292
|
+
authScanResult: null,
|
|
1922
2293
|
clientComponents: /* @__PURE__ */ new Map(),
|
|
1923
2294
|
cssImports: /* @__PURE__ */ new Map(),
|
|
1924
2295
|
serverEntryCache: null,
|
|
@@ -1927,6 +2298,8 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1927
2298
|
await buildManifest(root);
|
|
1928
2299
|
await buildQueueManifestIfExists(root);
|
|
1929
2300
|
await buildServiceManifestIfExists(root);
|
|
2301
|
+
await buildImageManifestIfExists(root);
|
|
2302
|
+
await buildAuthManifestIfExists(root);
|
|
1930
2303
|
await scanClientComponents(root, state);
|
|
1931
2304
|
await scanCssImports(root, state);
|
|
1932
2305
|
},
|
|
@@ -1963,6 +2336,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1963
2336
|
await buildServiceManifestIfExists(state.options.root);
|
|
1964
2337
|
invalidateVirtualModules();
|
|
1965
2338
|
}
|
|
2339
|
+
if (isImageFile(filePath)) {
|
|
2340
|
+
const imagesDir = path3.resolve(root, state.options.appDir, IMAGES_DIR);
|
|
2341
|
+
if (state?.options.verbose) {
|
|
2342
|
+
console.log(`[cloudwerk] Image added: ${path3.relative(imagesDir, filePath)}`);
|
|
2343
|
+
}
|
|
2344
|
+
await buildImageManifestIfExists(state.options.root);
|
|
2345
|
+
invalidateVirtualModules();
|
|
2346
|
+
}
|
|
2347
|
+
if (isAuthFile(filePath)) {
|
|
2348
|
+
const authDir = path3.resolve(root, state.options.appDir, AUTH_DIR);
|
|
2349
|
+
if (state?.options.verbose) {
|
|
2350
|
+
console.log(`[cloudwerk] Auth file added: ${path3.relative(authDir, filePath)}`);
|
|
2351
|
+
}
|
|
2352
|
+
await buildAuthManifestIfExists(state.options.root);
|
|
2353
|
+
invalidateVirtualModules();
|
|
2354
|
+
}
|
|
1966
2355
|
});
|
|
1967
2356
|
devServer.watcher.on("unlink", async (filePath) => {
|
|
1968
2357
|
if (isRouteFile(filePath)) {
|
|
@@ -1988,6 +2377,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1988
2377
|
await buildServiceManifestIfExists(state.options.root);
|
|
1989
2378
|
invalidateVirtualModules();
|
|
1990
2379
|
}
|
|
2380
|
+
if (isImageFile(filePath)) {
|
|
2381
|
+
const imagesDir = path3.resolve(root, state.options.appDir, IMAGES_DIR);
|
|
2382
|
+
if (state?.options.verbose) {
|
|
2383
|
+
console.log(`[cloudwerk] Image removed: ${path3.relative(imagesDir, filePath)}`);
|
|
2384
|
+
}
|
|
2385
|
+
await buildImageManifestIfExists(state.options.root);
|
|
2386
|
+
invalidateVirtualModules();
|
|
2387
|
+
}
|
|
2388
|
+
if (isAuthFile(filePath)) {
|
|
2389
|
+
const authDir = path3.resolve(root, state.options.appDir, AUTH_DIR);
|
|
2390
|
+
if (state?.options.verbose) {
|
|
2391
|
+
console.log(`[cloudwerk] Auth file removed: ${path3.relative(authDir, filePath)}`);
|
|
2392
|
+
}
|
|
2393
|
+
await buildAuthManifestIfExists(state.options.root);
|
|
2394
|
+
invalidateVirtualModules();
|
|
2395
|
+
}
|
|
1991
2396
|
});
|
|
1992
2397
|
devServer.watcher.on("change", async (filePath) => {
|
|
1993
2398
|
if (isRouteFile(filePath)) {
|
|
@@ -2013,6 +2418,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
2013
2418
|
await buildServiceManifestIfExists(state.options.root);
|
|
2014
2419
|
invalidateVirtualModules();
|
|
2015
2420
|
}
|
|
2421
|
+
if (isImageFile(filePath)) {
|
|
2422
|
+
const imagesDir = path3.resolve(root, state.options.appDir, IMAGES_DIR);
|
|
2423
|
+
if (state?.options.verbose) {
|
|
2424
|
+
console.log(`[cloudwerk] Image changed: ${path3.relative(imagesDir, filePath)}`);
|
|
2425
|
+
}
|
|
2426
|
+
await buildImageManifestIfExists(state.options.root);
|
|
2427
|
+
invalidateVirtualModules();
|
|
2428
|
+
}
|
|
2429
|
+
if (isAuthFile(filePath)) {
|
|
2430
|
+
const authDir = path3.resolve(root, state.options.appDir, AUTH_DIR);
|
|
2431
|
+
if (state?.options.verbose) {
|
|
2432
|
+
console.log(`[cloudwerk] Auth file changed: ${path3.relative(authDir, filePath)}`);
|
|
2433
|
+
}
|
|
2434
|
+
await buildAuthManifestIfExists(state.options.root);
|
|
2435
|
+
invalidateVirtualModules();
|
|
2436
|
+
}
|
|
2016
2437
|
const wranglerPath2 = findWranglerTomlPath(root);
|
|
2017
2438
|
if (wranglerPath2 && filePath === wranglerPath2) {
|
|
2018
2439
|
if (verbose) {
|
|
@@ -2060,7 +2481,9 @@ function cloudwerkPlugin(options = {}) {
|
|
|
2060
2481
|
state.options,
|
|
2061
2482
|
{
|
|
2062
2483
|
queueManifest: state.queueManifest,
|
|
2063
|
-
serviceManifest: state.serviceManifest
|
|
2484
|
+
serviceManifest: state.serviceManifest,
|
|
2485
|
+
authManifest: state.authManifest,
|
|
2486
|
+
cssImports: state.cssImports
|
|
2064
2487
|
}
|
|
2065
2488
|
);
|
|
2066
2489
|
}
|
|
@@ -2130,6 +2553,13 @@ function cloudwerkPlugin(options = {}) {
|
|
|
2130
2553
|
"getDurableObject",
|
|
2131
2554
|
"hasDurableObject",
|
|
2132
2555
|
"getDurableObjectNames",
|
|
2556
|
+
"images",
|
|
2557
|
+
"getImages",
|
|
2558
|
+
"hasImages",
|
|
2559
|
+
"getImagesNames",
|
|
2560
|
+
"registerLocalImages",
|
|
2561
|
+
"unregisterLocalImages",
|
|
2562
|
+
"clearLocalImages",
|
|
2133
2563
|
"createLazyBinding"
|
|
2134
2564
|
];
|
|
2135
2565
|
const runtimeImports = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudwerk/vite-plugin",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"description": "Vite plugin for Cloudwerk file-based routing with virtual entry generation",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@swc/core": "^1.3.100",
|
|
22
|
-
"@cloudwerk/core": "^0.15.
|
|
23
|
-
"@cloudwerk/ui": "^0.15.
|
|
22
|
+
"@cloudwerk/core": "^0.15.3",
|
|
23
|
+
"@cloudwerk/ui": "^0.15.3"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"vite": "^5.0.0 || ^6.0.0",
|