@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 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
- const clientEntryPath = options.isProduction ? `${options.hydrationEndpoint}/client.js` : "/@id/__x00__virtual:cloudwerk/client-entry";
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 = handler.length === 2 ? createHandlerAdapter(handler) : handler
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
- ${options.isProduction ? `
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
- return response
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 = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudwerk/vite-plugin",
3
- "version": "0.6.3",
3
+ "version": "0.6.5",
4
4
  "description": "Vite plugin for Cloudwerk file-based routing with virtual entry generation",
5
5
  "repository": {
6
6
  "type": "git",