@cloudwerk/vite-plugin 0.6.3 → 0.6.5
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 +433 -7
- package/package.json +1 -1
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 = ''`;
|
|
@@ -471,7 +509,7 @@ function registerRoute(app, pattern, routeModule, middlewareModules) {
|
|
|
471
509
|
for (const method of HTTP_METHODS) {
|
|
472
510
|
const handler = routeModule[method]
|
|
473
511
|
if (handler && typeof handler === 'function') {
|
|
474
|
-
const h =
|
|
512
|
+
const h = createHandlerAdapter(handler)
|
|
475
513
|
switch (method) {
|
|
476
514
|
case 'GET': app.get(pattern, h); break
|
|
477
515
|
case 'POST': app.post(pattern, h); break
|
|
@@ -497,7 +535,10 @@ const app = new Hono({ strict: false })
|
|
|
497
535
|
|
|
498
536
|
// Add context middleware
|
|
499
537
|
app.use('*', contextMiddleware())
|
|
500
|
-
${
|
|
538
|
+
${rootMiddlewareVarName ? `
|
|
539
|
+
// Apply root middleware globally (for all routes including auth)
|
|
540
|
+
app.use('*', createMiddlewareAdapter(${rootMiddlewareVarName}))
|
|
541
|
+
` : ""}${options.isProduction ? `
|
|
501
542
|
// Serve static assets using Workers Static Assets binding (production only)
|
|
502
543
|
app.use('*', async (c, next) => {
|
|
503
544
|
// Check if ASSETS binding is available
|
|
@@ -518,9 +559,27 @@ app.use('*', async (c, next) => {
|
|
|
518
559
|
// Try to serve the request as a static asset
|
|
519
560
|
const response = await c.env.ASSETS.fetch(c.req.raw)
|
|
520
561
|
|
|
521
|
-
// If asset found (not 404), return it
|
|
562
|
+
// If asset found (not 404), return it with cache headers
|
|
522
563
|
if (response.status !== 404) {
|
|
523
|
-
|
|
564
|
+
const path = new URL(c.req.url).pathname
|
|
565
|
+
|
|
566
|
+
// Check if this is a hashed asset (Vite adds content hash to filename)
|
|
567
|
+
// Hashed assets are immutable and can be cached forever
|
|
568
|
+
const isHashedAsset = path.startsWith('/__cloudwerk/') ||
|
|
569
|
+
/-[a-zA-Z0-9]{8,}\\.(js|css|woff2?|ttf|eot|svg|png|jpg|jpeg|gif|webp|avif|ico)$/.test(path)
|
|
570
|
+
|
|
571
|
+
const cacheControl = isHashedAsset
|
|
572
|
+
? 'public, max-age=31536000, immutable'
|
|
573
|
+
: 'public, max-age=3600'
|
|
574
|
+
|
|
575
|
+
return new Response(response.body, {
|
|
576
|
+
status: response.status,
|
|
577
|
+
statusText: response.statusText,
|
|
578
|
+
headers: {
|
|
579
|
+
...Object.fromEntries(response.headers.entries()),
|
|
580
|
+
'Cache-Control': cacheControl,
|
|
581
|
+
},
|
|
582
|
+
})
|
|
524
583
|
}
|
|
525
584
|
|
|
526
585
|
// Asset not found, continue to routes
|
|
@@ -531,6 +590,9 @@ app.use('*', async (c, next) => {
|
|
|
531
590
|
${pageRegistrations.join("\n")}
|
|
532
591
|
${routeRegistrations.join("\n")}
|
|
533
592
|
|
|
593
|
+
// Register auth routes
|
|
594
|
+
${generateAuthRouteRegistrations(authManifest)}
|
|
595
|
+
|
|
534
596
|
// SSG routes endpoint - returns all static routes for build-time generation
|
|
535
597
|
app.get('/__ssg/routes', async (c) => {
|
|
536
598
|
const routes = []
|
|
@@ -766,6 +828,242 @@ function generateServiceRegistration(serviceManifest) {
|
|
|
766
828
|
}
|
|
767
829
|
return lines.join("\n");
|
|
768
830
|
}
|
|
831
|
+
function generateAuthRouteRegistrations(authManifest) {
|
|
832
|
+
if (!authManifest) {
|
|
833
|
+
return "";
|
|
834
|
+
}
|
|
835
|
+
const lines = [];
|
|
836
|
+
const imports = [];
|
|
837
|
+
const basePath = authManifest.config?.basePath || "/auth";
|
|
838
|
+
lines.push("");
|
|
839
|
+
lines.push("// ============================================================================");
|
|
840
|
+
lines.push("// Auth Route Registration");
|
|
841
|
+
lines.push("// ============================================================================");
|
|
842
|
+
lines.push("");
|
|
843
|
+
imports.push(`import {
|
|
844
|
+
handleSession,
|
|
845
|
+
handleProviders,
|
|
846
|
+
handleSignIn,
|
|
847
|
+
handleSignInProvider,
|
|
848
|
+
handleSignOutGet,
|
|
849
|
+
handleSignOutPost,
|
|
850
|
+
} from '@cloudwerk/auth/routes'`);
|
|
851
|
+
imports.push(`import { createSessionManager, createKVSessionAdapter } from '@cloudwerk/auth/session'`);
|
|
852
|
+
const passkeyProviders = authManifest.providers.filter((p) => p.type === "passkey" && !p.disabled);
|
|
853
|
+
if (passkeyProviders.length > 0) {
|
|
854
|
+
imports.push(`import {
|
|
855
|
+
handlePasskeyRegisterOptions,
|
|
856
|
+
handlePasskeyRegisterVerify,
|
|
857
|
+
handlePasskeyAuthenticateOptions,
|
|
858
|
+
handlePasskeyAuthenticateVerify,
|
|
859
|
+
} from '@cloudwerk/auth/routes'`);
|
|
860
|
+
imports.push(`import {
|
|
861
|
+
createKVChallengeStorage,
|
|
862
|
+
createD1CredentialStorage,
|
|
863
|
+
} from '@cloudwerk/auth/providers'`);
|
|
864
|
+
for (let i = 0; i < passkeyProviders.length; i++) {
|
|
865
|
+
const provider = passkeyProviders[i];
|
|
866
|
+
imports.push(`import passkeyProviderDef_${i} from '${provider.filePath}'`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
lines.push(imports.join("\n"));
|
|
870
|
+
lines.push("");
|
|
871
|
+
lines.push(`
|
|
872
|
+
/**
|
|
873
|
+
* Build auth context for standard auth routes.
|
|
874
|
+
*/
|
|
875
|
+
function buildAuthContext(c) {
|
|
876
|
+
const env = c.env || {}
|
|
877
|
+
|
|
878
|
+
// Get KV binding - fall back to common binding names
|
|
879
|
+
let kvBinding = undefined
|
|
880
|
+
for (const name of ['FLAGSHIP_AUTH_SESSIONS', 'AUTH_KV', 'AUTH_SESSIONS', 'KV']) {
|
|
881
|
+
const binding = env[name]
|
|
882
|
+
if (binding && typeof binding.get === 'function') {
|
|
883
|
+
kvBinding = binding
|
|
884
|
+
break
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Create session manager from KV
|
|
889
|
+
const sessionAdapter = kvBinding ? createKVSessionAdapter({ binding: kvBinding, enableUserIndex: true }) : undefined
|
|
890
|
+
const sessionManager = sessionAdapter ? createSessionManager({ adapter: sessionAdapter }) : undefined
|
|
891
|
+
|
|
892
|
+
// Build providers map
|
|
893
|
+
const providers = new Map()
|
|
894
|
+
|
|
895
|
+
return {
|
|
896
|
+
request: c.req.raw,
|
|
897
|
+
env,
|
|
898
|
+
config: { basePath: '${basePath}', session: { strategy: '${authManifest.config?.sessionStrategy || "database"}' } },
|
|
899
|
+
sessionManager,
|
|
900
|
+
providers,
|
|
901
|
+
user: c.get?.('user') ?? null,
|
|
902
|
+
session: c.get?.('session') ?? null,
|
|
903
|
+
url: new URL(c.req.url),
|
|
904
|
+
responseHeaders: new Headers(),
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
`);
|
|
908
|
+
lines.push(`
|
|
909
|
+
// Standard auth routes
|
|
910
|
+
app.get('${basePath}/session', async (c) => {
|
|
911
|
+
const ctx = buildAuthContext(c)
|
|
912
|
+
return handleSession(ctx)
|
|
913
|
+
})
|
|
914
|
+
|
|
915
|
+
app.get('${basePath}/providers', async (c) => {
|
|
916
|
+
const ctx = buildAuthContext(c)
|
|
917
|
+
return handleProviders(ctx)
|
|
918
|
+
})
|
|
919
|
+
|
|
920
|
+
app.get('${basePath}/signin', async (c) => {
|
|
921
|
+
const ctx = buildAuthContext(c)
|
|
922
|
+
return handleSignIn(ctx)
|
|
923
|
+
})
|
|
924
|
+
|
|
925
|
+
app.get('${basePath}/signin/:provider', async (c) => {
|
|
926
|
+
const ctx = buildAuthContext(c)
|
|
927
|
+
const providerId = c.req.param('provider')
|
|
928
|
+
return handleSignInProvider(ctx, providerId)
|
|
929
|
+
})
|
|
930
|
+
|
|
931
|
+
app.get('${basePath}/signout', async (c) => {
|
|
932
|
+
const ctx = buildAuthContext(c)
|
|
933
|
+
return handleSignOutGet(ctx)
|
|
934
|
+
})
|
|
935
|
+
|
|
936
|
+
app.post('${basePath}/signout', async (c) => {
|
|
937
|
+
const ctx = buildAuthContext(c)
|
|
938
|
+
return handleSignOutPost(ctx)
|
|
939
|
+
})
|
|
940
|
+
`);
|
|
941
|
+
if (passkeyProviders.length > 0) {
|
|
942
|
+
lines.push(`
|
|
943
|
+
/**
|
|
944
|
+
* Build auth context for passkey handlers.
|
|
945
|
+
*/
|
|
946
|
+
function buildPasskeyAuthContext(c, passkeyProvider) {
|
|
947
|
+
const env = c.env || {}
|
|
948
|
+
|
|
949
|
+
// Get KV binding for challenges - use provider config or fall back to common names
|
|
950
|
+
const kvBindingName = passkeyProvider.kvBinding
|
|
951
|
+
let kvBinding = kvBindingName ? env[kvBindingName] : undefined
|
|
952
|
+
if (!kvBinding) {
|
|
953
|
+
// Fall back to common binding names
|
|
954
|
+
for (const name of ['AUTH_KV', 'AUTH_SESSIONS', 'KV']) {
|
|
955
|
+
const binding = env[name]
|
|
956
|
+
if (binding && typeof binding.get === 'function') {
|
|
957
|
+
kvBinding = binding
|
|
958
|
+
break
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
const challengeStorage = kvBinding ? createKVChallengeStorage(kvBinding, 'auth:challenge:') : undefined
|
|
963
|
+
|
|
964
|
+
// Get D1 binding for credentials - use provider config or fall back to common names
|
|
965
|
+
const d1BindingName = passkeyProvider.d1Binding
|
|
966
|
+
const d1Binding = d1BindingName ? env[d1BindingName] : (env.DB || env.D1 || env.DATABASE)
|
|
967
|
+
const credentialStorage = d1Binding ? createD1CredentialStorage(d1Binding, 'webauthn_credentials') : undefined
|
|
968
|
+
|
|
969
|
+
// Create user adapter from D1
|
|
970
|
+
const userAdapter = d1Binding ? {
|
|
971
|
+
async getUserByEmail(email) {
|
|
972
|
+
const user = await d1Binding.prepare(
|
|
973
|
+
'SELECT id, email, email_verified, name, image, created_at, updated_at FROM users WHERE email = ?'
|
|
974
|
+
).bind(email).first()
|
|
975
|
+
if (!user) return null
|
|
976
|
+
return {
|
|
977
|
+
id: user.id,
|
|
978
|
+
email: user.email,
|
|
979
|
+
emailVerified: user.email_verified ? new Date(user.email_verified) : null,
|
|
980
|
+
name: user.name,
|
|
981
|
+
image: user.image,
|
|
982
|
+
createdAt: new Date(user.created_at),
|
|
983
|
+
updatedAt: new Date(user.updated_at),
|
|
984
|
+
}
|
|
985
|
+
},
|
|
986
|
+
async createUser(userData) {
|
|
987
|
+
const id = crypto.randomUUID()
|
|
988
|
+
const now = new Date().toISOString()
|
|
989
|
+
await d1Binding.prepare(
|
|
990
|
+
'INSERT INTO users (id, email, email_verified, name, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)'
|
|
991
|
+
).bind(id, userData.email, userData.emailVerified?.toISOString() ?? null, userData.name ?? null, now, now).run()
|
|
992
|
+
return { id, email: userData.email, emailVerified: userData.emailVerified, name: userData.name ?? null, image: null, createdAt: new Date(now), updatedAt: new Date(now) }
|
|
993
|
+
},
|
|
994
|
+
async getUser(id) {
|
|
995
|
+
const user = await d1Binding.prepare(
|
|
996
|
+
'SELECT id, email, email_verified, name, image, created_at, updated_at FROM users WHERE id = ?'
|
|
997
|
+
).bind(id).first()
|
|
998
|
+
if (!user) return null
|
|
999
|
+
return {
|
|
1000
|
+
id: user.id,
|
|
1001
|
+
email: user.email,
|
|
1002
|
+
emailVerified: user.email_verified ? new Date(user.email_verified) : null,
|
|
1003
|
+
name: user.name,
|
|
1004
|
+
image: user.image,
|
|
1005
|
+
createdAt: new Date(user.created_at),
|
|
1006
|
+
updatedAt: new Date(user.updated_at),
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
} : undefined
|
|
1010
|
+
|
|
1011
|
+
// Create session manager from KV
|
|
1012
|
+
const sessionAdapter = kvBinding ? createKVSessionAdapter({ binding: kvBinding, enableUserIndex: true }) : undefined
|
|
1013
|
+
const sessionManager = sessionAdapter ? createSessionManager({ adapter: sessionAdapter }) : undefined
|
|
1014
|
+
|
|
1015
|
+
// Build providers map
|
|
1016
|
+
const providers = new Map()
|
|
1017
|
+
providers.set(passkeyProvider.id, passkeyProvider)
|
|
1018
|
+
|
|
1019
|
+
return {
|
|
1020
|
+
request: c.req.raw,
|
|
1021
|
+
env,
|
|
1022
|
+
config: { basePath: '${basePath}', session: { strategy: '${authManifest.config?.sessionStrategy || "database"}' } },
|
|
1023
|
+
sessionManager,
|
|
1024
|
+
providers,
|
|
1025
|
+
user: null,
|
|
1026
|
+
session: null,
|
|
1027
|
+
url: new URL(c.req.url),
|
|
1028
|
+
responseHeaders: new Headers(),
|
|
1029
|
+
challengeStorage,
|
|
1030
|
+
credentialStorage,
|
|
1031
|
+
userAdapter,
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
`);
|
|
1035
|
+
for (let i = 0; i < passkeyProviders.length; i++) {
|
|
1036
|
+
const provider = passkeyProviders[i];
|
|
1037
|
+
lines.push(`
|
|
1038
|
+
// Get provider from definition
|
|
1039
|
+
const passkeyProvider_${i} = passkeyProviderDef_${i}.provider || passkeyProviderDef_${i}
|
|
1040
|
+
`);
|
|
1041
|
+
lines.push(`
|
|
1042
|
+
// Passkey registration routes
|
|
1043
|
+
app.post('${basePath}/passkey/register/options', async (c) => {
|
|
1044
|
+
const ctx = buildPasskeyAuthContext(c, passkeyProvider_${i})
|
|
1045
|
+
return handlePasskeyRegisterOptions(ctx, '${provider.id}')
|
|
1046
|
+
})
|
|
1047
|
+
|
|
1048
|
+
app.post('${basePath}/passkey/register/verify', async (c) => {
|
|
1049
|
+
const ctx = buildPasskeyAuthContext(c, passkeyProvider_${i})
|
|
1050
|
+
return handlePasskeyRegisterVerify(ctx, '${provider.id}')
|
|
1051
|
+
})
|
|
1052
|
+
|
|
1053
|
+
app.post('${basePath}/passkey/authenticate/options', async (c) => {
|
|
1054
|
+
const ctx = buildPasskeyAuthContext(c, passkeyProvider_${i})
|
|
1055
|
+
return handlePasskeyAuthenticateOptions(ctx, '${provider.id}')
|
|
1056
|
+
})
|
|
1057
|
+
|
|
1058
|
+
app.post('${basePath}/passkey/authenticate/verify', async (c) => {
|
|
1059
|
+
const ctx = buildPasskeyAuthContext(c, passkeyProvider_${i})
|
|
1060
|
+
return handlePasskeyAuthenticateVerify(ctx, '${provider.id}')
|
|
1061
|
+
})
|
|
1062
|
+
`);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
return lines.join("\n");
|
|
1066
|
+
}
|
|
769
1067
|
|
|
770
1068
|
// src/virtual-modules/client-entry.ts
|
|
771
1069
|
function collectCssImports(cssImports) {
|
|
@@ -1798,6 +2096,62 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1798
2096
|
console.log(`[cloudwerk] Found ${state.serviceManifest.services.length} service(s)`);
|
|
1799
2097
|
}
|
|
1800
2098
|
}
|
|
2099
|
+
async function buildImageManifestIfExists(root) {
|
|
2100
|
+
if (!state) {
|
|
2101
|
+
throw new Error("Plugin state not initialized");
|
|
2102
|
+
}
|
|
2103
|
+
const imagesPath = path3.resolve(root, state.options.appDir, IMAGES_DIR);
|
|
2104
|
+
try {
|
|
2105
|
+
await fs2.promises.access(imagesPath);
|
|
2106
|
+
} catch {
|
|
2107
|
+
state.imageScanResult = null;
|
|
2108
|
+
state.imageManifest = null;
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
state.imageScanResult = await scanImages(
|
|
2112
|
+
path3.resolve(root, state.options.appDir),
|
|
2113
|
+
{ extensions: state.options.config.extensions }
|
|
2114
|
+
);
|
|
2115
|
+
state.imageManifest = buildImageManifest(
|
|
2116
|
+
state.imageScanResult,
|
|
2117
|
+
root
|
|
2118
|
+
);
|
|
2119
|
+
if (state.options.verbose && state.imageManifest.images.length > 0) {
|
|
2120
|
+
console.log(`[cloudwerk] Found ${state.imageManifest.images.length} image(s)`);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
async function buildAuthManifestIfExists(root) {
|
|
2124
|
+
if (!state) {
|
|
2125
|
+
throw new Error("Plugin state not initialized");
|
|
2126
|
+
}
|
|
2127
|
+
const authPath = path3.resolve(root, state.options.appDir, AUTH_DIR);
|
|
2128
|
+
try {
|
|
2129
|
+
await fs2.promises.access(authPath);
|
|
2130
|
+
} catch {
|
|
2131
|
+
state.authScanResult = null;
|
|
2132
|
+
state.authManifest = null;
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
state.authScanResult = await scanAuth(
|
|
2136
|
+
path3.resolve(root, state.options.appDir),
|
|
2137
|
+
{ extensions: state.options.config.extensions }
|
|
2138
|
+
);
|
|
2139
|
+
state.authManifest = await buildAuthManifestWithModules(state.authScanResult);
|
|
2140
|
+
state.serverEntryCache = null;
|
|
2141
|
+
if (state.options.verbose && state.authManifest.providers.length > 0) {
|
|
2142
|
+
console.log(`[cloudwerk] Found ${state.authManifest.providers.length} auth provider(s)`);
|
|
2143
|
+
for (const provider of state.authManifest.providers) {
|
|
2144
|
+
console.log(`[cloudwerk] - ${provider.id} (${provider.type})`);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
function isAuthFile(filePath) {
|
|
2149
|
+
if (!state) return false;
|
|
2150
|
+
const authDir = path3.resolve(state.options.root, state.options.appDir, AUTH_DIR);
|
|
2151
|
+
if (!filePath.startsWith(authDir)) return false;
|
|
2152
|
+
const ext = path3.extname(filePath);
|
|
2153
|
+
return state.options.config.extensions.includes(ext);
|
|
2154
|
+
}
|
|
1801
2155
|
function isRouteFile(filePath) {
|
|
1802
2156
|
if (!state) return false;
|
|
1803
2157
|
const appDir = path3.resolve(state.options.root, state.options.appDir);
|
|
@@ -1825,6 +2179,15 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1825
2179
|
if (nameWithoutExt !== SERVICE_FILE_NAME) return false;
|
|
1826
2180
|
return state.options.config.extensions.includes(ext);
|
|
1827
2181
|
}
|
|
2182
|
+
function isImageFile(filePath) {
|
|
2183
|
+
if (!state) return false;
|
|
2184
|
+
const imagesDir = path3.resolve(state.options.root, state.options.appDir, IMAGES_DIR);
|
|
2185
|
+
if (!filePath.startsWith(imagesDir)) return false;
|
|
2186
|
+
const relativePath = path3.relative(imagesDir, filePath);
|
|
2187
|
+
if (relativePath.includes(path3.sep)) return false;
|
|
2188
|
+
const ext = path3.extname(filePath);
|
|
2189
|
+
return state.options.config.extensions.includes(ext);
|
|
2190
|
+
}
|
|
1828
2191
|
function invalidateVirtualModules() {
|
|
1829
2192
|
if (!server) return;
|
|
1830
2193
|
const idsToInvalidate = [
|
|
@@ -1919,6 +2282,10 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1919
2282
|
queueScanResult: null,
|
|
1920
2283
|
serviceManifest: null,
|
|
1921
2284
|
serviceScanResult: null,
|
|
2285
|
+
imageManifest: null,
|
|
2286
|
+
imageScanResult: null,
|
|
2287
|
+
authManifest: null,
|
|
2288
|
+
authScanResult: null,
|
|
1922
2289
|
clientComponents: /* @__PURE__ */ new Map(),
|
|
1923
2290
|
cssImports: /* @__PURE__ */ new Map(),
|
|
1924
2291
|
serverEntryCache: null,
|
|
@@ -1927,6 +2294,8 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1927
2294
|
await buildManifest(root);
|
|
1928
2295
|
await buildQueueManifestIfExists(root);
|
|
1929
2296
|
await buildServiceManifestIfExists(root);
|
|
2297
|
+
await buildImageManifestIfExists(root);
|
|
2298
|
+
await buildAuthManifestIfExists(root);
|
|
1930
2299
|
await scanClientComponents(root, state);
|
|
1931
2300
|
await scanCssImports(root, state);
|
|
1932
2301
|
},
|
|
@@ -1963,6 +2332,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1963
2332
|
await buildServiceManifestIfExists(state.options.root);
|
|
1964
2333
|
invalidateVirtualModules();
|
|
1965
2334
|
}
|
|
2335
|
+
if (isImageFile(filePath)) {
|
|
2336
|
+
const imagesDir = path3.resolve(root, state.options.appDir, IMAGES_DIR);
|
|
2337
|
+
if (state?.options.verbose) {
|
|
2338
|
+
console.log(`[cloudwerk] Image added: ${path3.relative(imagesDir, filePath)}`);
|
|
2339
|
+
}
|
|
2340
|
+
await buildImageManifestIfExists(state.options.root);
|
|
2341
|
+
invalidateVirtualModules();
|
|
2342
|
+
}
|
|
2343
|
+
if (isAuthFile(filePath)) {
|
|
2344
|
+
const authDir = path3.resolve(root, state.options.appDir, AUTH_DIR);
|
|
2345
|
+
if (state?.options.verbose) {
|
|
2346
|
+
console.log(`[cloudwerk] Auth file added: ${path3.relative(authDir, filePath)}`);
|
|
2347
|
+
}
|
|
2348
|
+
await buildAuthManifestIfExists(state.options.root);
|
|
2349
|
+
invalidateVirtualModules();
|
|
2350
|
+
}
|
|
1966
2351
|
});
|
|
1967
2352
|
devServer.watcher.on("unlink", async (filePath) => {
|
|
1968
2353
|
if (isRouteFile(filePath)) {
|
|
@@ -1988,6 +2373,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
1988
2373
|
await buildServiceManifestIfExists(state.options.root);
|
|
1989
2374
|
invalidateVirtualModules();
|
|
1990
2375
|
}
|
|
2376
|
+
if (isImageFile(filePath)) {
|
|
2377
|
+
const imagesDir = path3.resolve(root, state.options.appDir, IMAGES_DIR);
|
|
2378
|
+
if (state?.options.verbose) {
|
|
2379
|
+
console.log(`[cloudwerk] Image removed: ${path3.relative(imagesDir, filePath)}`);
|
|
2380
|
+
}
|
|
2381
|
+
await buildImageManifestIfExists(state.options.root);
|
|
2382
|
+
invalidateVirtualModules();
|
|
2383
|
+
}
|
|
2384
|
+
if (isAuthFile(filePath)) {
|
|
2385
|
+
const authDir = path3.resolve(root, state.options.appDir, AUTH_DIR);
|
|
2386
|
+
if (state?.options.verbose) {
|
|
2387
|
+
console.log(`[cloudwerk] Auth file removed: ${path3.relative(authDir, filePath)}`);
|
|
2388
|
+
}
|
|
2389
|
+
await buildAuthManifestIfExists(state.options.root);
|
|
2390
|
+
invalidateVirtualModules();
|
|
2391
|
+
}
|
|
1991
2392
|
});
|
|
1992
2393
|
devServer.watcher.on("change", async (filePath) => {
|
|
1993
2394
|
if (isRouteFile(filePath)) {
|
|
@@ -2013,6 +2414,22 @@ function cloudwerkPlugin(options = {}) {
|
|
|
2013
2414
|
await buildServiceManifestIfExists(state.options.root);
|
|
2014
2415
|
invalidateVirtualModules();
|
|
2015
2416
|
}
|
|
2417
|
+
if (isImageFile(filePath)) {
|
|
2418
|
+
const imagesDir = path3.resolve(root, state.options.appDir, IMAGES_DIR);
|
|
2419
|
+
if (state?.options.verbose) {
|
|
2420
|
+
console.log(`[cloudwerk] Image changed: ${path3.relative(imagesDir, filePath)}`);
|
|
2421
|
+
}
|
|
2422
|
+
await buildImageManifestIfExists(state.options.root);
|
|
2423
|
+
invalidateVirtualModules();
|
|
2424
|
+
}
|
|
2425
|
+
if (isAuthFile(filePath)) {
|
|
2426
|
+
const authDir = path3.resolve(root, state.options.appDir, AUTH_DIR);
|
|
2427
|
+
if (state?.options.verbose) {
|
|
2428
|
+
console.log(`[cloudwerk] Auth file changed: ${path3.relative(authDir, filePath)}`);
|
|
2429
|
+
}
|
|
2430
|
+
await buildAuthManifestIfExists(state.options.root);
|
|
2431
|
+
invalidateVirtualModules();
|
|
2432
|
+
}
|
|
2016
2433
|
const wranglerPath2 = findWranglerTomlPath(root);
|
|
2017
2434
|
if (wranglerPath2 && filePath === wranglerPath2) {
|
|
2018
2435
|
if (verbose) {
|
|
@@ -2060,7 +2477,9 @@ function cloudwerkPlugin(options = {}) {
|
|
|
2060
2477
|
state.options,
|
|
2061
2478
|
{
|
|
2062
2479
|
queueManifest: state.queueManifest,
|
|
2063
|
-
serviceManifest: state.serviceManifest
|
|
2480
|
+
serviceManifest: state.serviceManifest,
|
|
2481
|
+
authManifest: state.authManifest,
|
|
2482
|
+
cssImports: state.cssImports
|
|
2064
2483
|
}
|
|
2065
2484
|
);
|
|
2066
2485
|
}
|
|
@@ -2130,6 +2549,13 @@ function cloudwerkPlugin(options = {}) {
|
|
|
2130
2549
|
"getDurableObject",
|
|
2131
2550
|
"hasDurableObject",
|
|
2132
2551
|
"getDurableObjectNames",
|
|
2552
|
+
"images",
|
|
2553
|
+
"getImages",
|
|
2554
|
+
"hasImages",
|
|
2555
|
+
"getImagesNames",
|
|
2556
|
+
"registerLocalImages",
|
|
2557
|
+
"unregisterLocalImages",
|
|
2558
|
+
"clearLocalImages",
|
|
2133
2559
|
"createLazyBinding"
|
|
2134
2560
|
];
|
|
2135
2561
|
const runtimeImports = [];
|