litestar-vite-plugin 0.24.1 → 0.26.0

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.
Files changed (35) hide show
  1. package/README.md +6 -6
  2. package/dist/js/astro.d.ts +6 -0
  3. package/dist/js/astro.js +14 -77
  4. package/dist/js/dev-server-index.html +2 -2
  5. package/dist/js/helpers/csrf.d.ts +2 -0
  6. package/dist/js/helpers/csrf.js +28 -1
  7. package/dist/js/helpers/htmx.js +105 -2
  8. package/dist/js/index.d.ts +6 -0
  9. package/dist/js/index.js +75 -128
  10. package/dist/js/inertia-helpers/index.d.ts +27 -9
  11. package/dist/js/install-hint.d.ts +17 -0
  12. package/dist/js/install-hint.js +35 -1
  13. package/dist/js/nuxt.d.ts +6 -0
  14. package/dist/js/nuxt.js +12 -72
  15. package/dist/js/shared/bridge-schema.d.ts +1 -0
  16. package/dist/js/shared/bridge-schema.js +41 -9
  17. package/dist/js/shared/constants.d.ts +4 -18
  18. package/dist/js/shared/constants.js +2 -6
  19. package/dist/js/shared/emit-page-props-types.d.ts +1 -1
  20. package/dist/js/shared/emit-page-props-types.js +51 -5
  21. package/dist/js/shared/emit-schemas-types.d.ts +1 -1
  22. package/dist/js/shared/emit-schemas-types.js +86 -15
  23. package/dist/js/shared/emit-static-props-types.d.ts +1 -1
  24. package/dist/js/shared/emit-static-props-types.js +2 -2
  25. package/dist/js/shared/typegen-cache.d.ts +10 -11
  26. package/dist/js/shared/typegen-cache.js +10 -1
  27. package/dist/js/shared/typegen-core.d.ts +24 -0
  28. package/dist/js/shared/typegen-core.js +101 -45
  29. package/dist/js/shared/typegen-plugin.d.ts +26 -0
  30. package/dist/js/shared/typegen-plugin.js +191 -129
  31. package/dist/js/shared/vite-compat.d.ts +23 -15
  32. package/dist/js/shared/vite-compat.js +12 -6
  33. package/dist/js/sveltekit.d.ts +6 -0
  34. package/dist/js/sveltekit.js +14 -77
  35. package/package.json +14 -8
@@ -29,6 +29,9 @@ const allowedLogLevels = /* @__PURE__ */ new Set(["quiet", "normal", "verbose"])
29
29
  function fail(message) {
30
30
  throw new Error(`litestar-vite-plugin: invalid .litestar.json - ${message}`);
31
31
  }
32
+ function warn(message) {
33
+ console.warn(`litestar-vite-plugin: invalid .litestar.json - ${message}`);
34
+ }
32
35
  function assertObject(value, label) {
33
36
  if (!value || typeof value !== "object" || Array.isArray(value)) {
34
37
  fail(`${label} must be an object`);
@@ -122,6 +125,8 @@ function parseTypesConfig(value) {
122
125
  const generatePageProps = assertBoolean(obj, "generatePageProps");
123
126
  const generateSchemas = assertOptionalBoolean(obj, "generateSchemas", true);
124
127
  const globalRoute = assertBoolean(obj, "globalRoute");
128
+ const rawFailOnError = obj.failOnError;
129
+ const failOnError = rawFailOnError === void 0 || rawFailOnError === null ? void 0 : assertBoolean(obj, "failOnError");
125
130
  return {
126
131
  enabled,
127
132
  output,
@@ -135,7 +140,8 @@ function parseTypesConfig(value) {
135
140
  generateRoutes,
136
141
  generatePageProps,
137
142
  generateSchemas,
138
- globalRoute
143
+ globalRoute,
144
+ failOnError
139
145
  };
140
146
  }
141
147
  function parseLogging(value) {
@@ -162,7 +168,7 @@ function parseBridgeSchema(value) {
162
168
  const obj = assertObject(value, "root");
163
169
  for (const key of Object.keys(obj)) {
164
170
  if (!allowedTopLevelKeys.has(key)) {
165
- fail(`unknown top-level key "${key}"`);
171
+ warn(`unknown top-level key "${key}" ignored`);
166
172
  }
167
173
  }
168
174
  const assetUrl = assertString(obj, "assetUrl");
@@ -211,16 +217,42 @@ function parseBridgeSchema(value) {
211
217
  function readBridgeConfig(explicitPath) {
212
218
  const envPath = explicitPath ?? process.env.LITESTAR_VITE_CONFIG_PATH;
213
219
  if (envPath) {
214
- if (!fs.existsSync(envPath)) {
215
- return null;
216
- }
217
- return readBridgeConfigFile(envPath);
220
+ return readBridgeConfigFileCached(envPath);
218
221
  }
219
222
  const defaultPath = path.join(process.cwd(), ".litestar.json");
220
- if (fs.existsSync(defaultPath)) {
221
- return readBridgeConfigFile(defaultPath);
223
+ return readBridgeConfigFileCached(defaultPath);
224
+ }
225
+ const bridgeConfigCache = /* @__PURE__ */ new Map();
226
+ function readBridgeConfigFileCached(filePath) {
227
+ const resolvedPath = path.resolve(filePath);
228
+ if (!fs.existsSync(resolvedPath)) {
229
+ return null;
230
+ }
231
+ let mtimeMs = null;
232
+ try {
233
+ mtimeMs = fs.statSync(resolvedPath).mtimeMs;
234
+ } catch {
235
+ mtimeMs = null;
236
+ }
237
+ if (mtimeMs !== null) {
238
+ const cached = bridgeConfigCache.get(resolvedPath);
239
+ if (cached?.mtimeMs === mtimeMs) {
240
+ return cached.config;
241
+ }
242
+ }
243
+ try {
244
+ const config = readBridgeConfigFile(resolvedPath);
245
+ if (mtimeMs !== null) {
246
+ bridgeConfigCache.set(resolvedPath, { mtimeMs, config });
247
+ }
248
+ return config;
249
+ } catch (error) {
250
+ warn(error instanceof Error ? error.message : String(error));
251
+ if (mtimeMs !== null) {
252
+ bridgeConfigCache.set(resolvedPath, { mtimeMs, config: null });
253
+ }
254
+ return null;
222
255
  }
223
- return null;
224
256
  }
225
257
  function readBridgeConfigFile(filePath) {
226
258
  let raw;
@@ -11,23 +11,9 @@
11
11
  */
12
12
  export declare const DEBOUNCE_MS = 300;
13
13
  /**
14
- * Timeout in milliseconds for backend health checks.
14
+ * Pinned fallback package spec for @hey-api/openapi-ts.
15
15
  *
16
- * Used when checking if the Litestar backend is available
17
- * before attempting to fetch OpenAPI schema.
16
+ * Keep this in sync with CURRENT_NPM_VERSION_RANGES in the Python scaffold
17
+ * registry and the optional peer dependency range in package.json.
18
18
  */
19
- export declare const BACKEND_CHECK_TIMEOUT_MS = 2000;
20
- /**
21
- * Default asset URL path.
22
- *
23
- * Used as fallback when no explicit assetUrl is configured.
24
- * Should match the default in Python ViteConfig.
25
- */
26
- export declare const DEFAULT_ASSET_URL = "/static/";
27
- /**
28
- * Default output directory for generated TypeScript types.
29
- *
30
- * Used by framework integrations as fallback when no output
31
- * is specified in Python bridge config or plugin options.
32
- */
33
- export declare const DEFAULT_TYPES_OUTPUT = "src/generated";
19
+ export declare const HEY_API_PINNED_SPEC = "@hey-api/openapi-ts@0.98.2";
@@ -1,10 +1,6 @@
1
1
  const DEBOUNCE_MS = 300;
2
- const BACKEND_CHECK_TIMEOUT_MS = 2e3;
3
- const DEFAULT_ASSET_URL = "/static/";
4
- const DEFAULT_TYPES_OUTPUT = "src/generated";
2
+ const HEY_API_PINNED_SPEC = "@hey-api/openapi-ts@0.98.2";
5
3
  export {
6
- BACKEND_CHECK_TIMEOUT_MS,
7
4
  DEBOUNCE_MS,
8
- DEFAULT_ASSET_URL,
9
- DEFAULT_TYPES_OUTPUT
5
+ HEY_API_PINNED_SPEC
10
6
  };
@@ -3,4 +3,4 @@
3
3
  *
4
4
  * @returns true if file was changed, false if unchanged
5
5
  */
6
- export declare function emitPagePropsTypes(pagesPath: string, outputDir: string): Promise<boolean>;
6
+ export declare function emitPagePropsTypes(pagesPath: string, outputDir: string, projectRoot?: string): Promise<boolean>;
@@ -1,10 +1,44 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { writeIfChanged } from "./write-if-changed.js";
4
- async function emitPagePropsTypes(pagesPath, outputDir) {
4
+ const semanticAliases = {
5
+ UUID: { description: "UUID v4 string", base: "string" },
6
+ DateTime: { description: "RFC 3339 date-time string", base: "string" },
7
+ DateOnly: { description: "ISO 8601 date string (YYYY-MM-DD)", base: "string" },
8
+ TimeOnly: { description: "ISO 8601 time string", base: "string" },
9
+ Duration: { description: "ISO 8601 duration string", base: "string" },
10
+ Email: { description: "Email address string", base: "string" },
11
+ URI: { description: "URI/URL string", base: "string" },
12
+ IPv4: { description: "IPv4 address string", base: "string" },
13
+ IPv6: { description: "IPv6 address string", base: "string" }
14
+ };
15
+ function collectSemanticAliases(typeExpr) {
16
+ const aliases = /* @__PURE__ */ new Set();
17
+ for (const match of typeExpr.matchAll(/\b[A-Za-z_][A-Za-z0-9_]*\b/g)) {
18
+ const name = match[0];
19
+ if (name in semanticAliases) {
20
+ aliases.add(name);
21
+ }
22
+ }
23
+ return [...aliases].toSorted();
24
+ }
25
+ function renderSemanticAliases(aliases) {
26
+ if (aliases.size === 0) {
27
+ return "";
28
+ }
29
+ const lines = ["/** Semantic string aliases derived from OpenAPI `format`. */"];
30
+ for (const alias of [...aliases].toSorted()) {
31
+ const { description, base } = semanticAliases[alias];
32
+ lines.push(`/** ${description} */`, `export type ${alias} = ${base}`, "");
33
+ }
34
+ return `${lines.join("\n").trimEnd()}
35
+
36
+ `;
37
+ }
38
+ async function emitPagePropsTypes(pagesPath, outputDir, projectRoot = process.cwd()) {
5
39
  const contents = await fs.promises.readFile(pagesPath, "utf-8");
6
40
  const json = JSON.parse(contents);
7
- const outDir = path.resolve(process.cwd(), outputDir);
41
+ const outDir = path.resolve(projectRoot, outputDir);
8
42
  await fs.promises.mkdir(outDir, { recursive: true });
9
43
  const outFile = path.join(outDir, "page-props.ts");
10
44
  const { includeDefaultAuth, includeDefaultFlash } = json.typeGenConfig;
@@ -84,8 +118,15 @@ export interface FlashMessages {}
84
118
  return ` ${safeKey}${optional}: ${def.type}`;
85
119
  });
86
120
  const allCustomTypes = /* @__PURE__ */ new Set();
121
+ const usedSemanticAliases = /* @__PURE__ */ new Set();
87
122
  for (const data of Object.values(json.pages)) {
88
- if (data.tsType) {
123
+ const typeExpr = data.tsType ?? data.propsType;
124
+ if (typeExpr) {
125
+ for (const alias of collectSemanticAliases(typeExpr)) {
126
+ usedSemanticAliases.add(alias);
127
+ }
128
+ }
129
+ if (data.tsType && /^[A-Za-z_][A-Za-z0-9_]*$/.test(data.tsType)) {
89
130
  allCustomTypes.add(data.tsType);
90
131
  }
91
132
  for (const t of data.customTypes ?? []) {
@@ -131,9 +172,13 @@ export interface FlashMessages {}
131
172
  "RegExp",
132
173
  "User",
133
174
  "AuthData",
134
- "FlashMessages"
175
+ "FlashMessages",
176
+ ...Object.keys(semanticAliases)
135
177
  ]);
136
178
  for (const def of Object.values(generatedSharedProps)) {
179
+ for (const alias of collectSemanticAliases(def.type)) {
180
+ usedSemanticAliases.add(alias);
181
+ }
137
182
  for (const match of def.type.matchAll(/\b[A-Za-z_][A-Za-z0-9_]*\b/g)) {
138
183
  const name = match[0];
139
184
  if (!builtinTypes.has(name)) {
@@ -190,6 +235,7 @@ export interface FlashMessages {}
190
235
  if (importStatement) {
191
236
  importStatement += "\n";
192
237
  }
238
+ const semanticAliasTypes = renderSemanticAliases(usedSemanticAliases);
193
239
  const pageEntries = [];
194
240
  for (const [component, data] of Object.entries(json.pages)) {
195
241
  const rawType = data.tsType || data.propsType || defaultFallback;
@@ -206,7 +252,7 @@ export interface FlashMessages {}
206
252
  // Import user-defined type extensions (edit page-props.user.ts to customize)
207
253
  import type { UserExtensions, SharedPropsExtensions } from "./page-props.user"
208
254
 
209
- ${importStatement}${userTypes}${authTypes}${flashTypes}/**
255
+ ${importStatement}${semanticAliasTypes}${userTypes}${authTypes}${flashTypes}/**
210
256
  * Generated shared props (always present).
211
257
  * Includes built-in props + static config props.
212
258
  */
@@ -5,4 +5,4 @@
5
5
  * @param outputDir - Output directory (contains api/types.gen.ts)
6
6
  * @returns true if file was changed, false if unchanged
7
7
  */
8
- export declare function emitSchemasTypes(routesJsonPath: string, outputDir: string, schemasOutputPath?: string): Promise<boolean>;
8
+ export declare function emitSchemasTypes(routesJsonPath: string, outputDir: string, schemasOutputPath?: string, projectRoot?: string): Promise<boolean>;
@@ -10,21 +10,12 @@ function toImportPath(fromDir, targetFile) {
10
10
  return `./${withoutExtension}`;
11
11
  }
12
12
  function parseHeyApiTypes(content) {
13
- const dataTypes = /* @__PURE__ */ new Map();
14
13
  const responsesTypes = /* @__PURE__ */ new Set();
15
14
  const errorsTypes = /* @__PURE__ */ new Set();
16
15
  const urlToDataType = /* @__PURE__ */ new Map();
17
- const typeBlockRegex = /export\s+type\s+(\w+Data)\s*=\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/gs;
18
- for (const match of content.matchAll(typeBlockRegex)) {
19
- const typeName = match[1];
20
- const typeBody = match[2];
21
- const urlMatch = typeBody.match(/url:\s*['"]([^'"]+)['"]/);
16
+ for (const { typeName, body } of findExportedDataTypeBlocks(content)) {
17
+ const urlMatch = body.match(/url:\s*['"]([^'"]+)['"]/);
22
18
  const url = urlMatch ? urlMatch[1] : null;
23
- const hasBody = /\bbody\s*:/.test(typeBody) && !/\bbody\s*\?\s*:\s*never/.test(typeBody);
24
- const hasPath = /\bpath\s*:/.test(typeBody) && !/\bpath\s*\?\s*:\s*never/.test(typeBody);
25
- const hasQuery = /\bquery\s*:/.test(typeBody) && !/\bquery\s*\?\s*:\s*never/.test(typeBody);
26
- const info = { typeName, url, hasBody, hasPath, hasQuery };
27
- dataTypes.set(typeName, info);
28
19
  if (url) {
29
20
  urlToDataType.set(url, typeName);
30
21
  }
@@ -37,7 +28,87 @@ function parseHeyApiTypes(content) {
37
28
  for (const match of content.matchAll(errorsRegex)) {
38
29
  errorsTypes.add(match[1]);
39
30
  }
40
- return { dataTypes, responsesTypes, errorsTypes, urlToDataType };
31
+ return { responsesTypes, errorsTypes, urlToDataType };
32
+ }
33
+ function findExportedDataTypeBlocks(content) {
34
+ const blocks = [];
35
+ const declarationRegex = /export\s+type\s+(\w+Data)\s*=\s*/g;
36
+ for (const match of content.matchAll(declarationRegex)) {
37
+ const typeName = match[1];
38
+ const bodyStart = match.index + match[0].length;
39
+ const openIndex = content.indexOf("{", bodyStart);
40
+ const nextToken = content.slice(bodyStart, openIndex === -1 ? content.length : openIndex).trim();
41
+ if (openIndex === -1 || nextToken.length > 0) {
42
+ console.warn(`litestar-vite: Unable to parse hey-api Data type ${typeName}; expected object type`);
43
+ continue;
44
+ }
45
+ const closeIndex = findMatchingBrace(content, openIndex);
46
+ if (closeIndex === -1) {
47
+ console.warn(`litestar-vite: Unable to parse hey-api Data type ${typeName}; unbalanced braces`);
48
+ continue;
49
+ }
50
+ blocks.push({ typeName, body: content.slice(openIndex + 1, closeIndex) });
51
+ }
52
+ return blocks;
53
+ }
54
+ function findMatchingBrace(content, openIndex) {
55
+ let depth = 0;
56
+ let quote = null;
57
+ let escaped = false;
58
+ let lineComment = false;
59
+ let blockComment = false;
60
+ for (let i = openIndex; i < content.length; i += 1) {
61
+ const char = content[i];
62
+ const next = content[i + 1];
63
+ if (lineComment) {
64
+ if (char === "\n") {
65
+ lineComment = false;
66
+ }
67
+ continue;
68
+ }
69
+ if (blockComment) {
70
+ if (char === "*" && next === "/") {
71
+ blockComment = false;
72
+ i += 1;
73
+ }
74
+ continue;
75
+ }
76
+ if (quote) {
77
+ if (escaped) {
78
+ escaped = false;
79
+ } else if (char === "\\") {
80
+ escaped = true;
81
+ } else if (char === quote) {
82
+ quote = null;
83
+ }
84
+ continue;
85
+ }
86
+ if (char === "/" && next === "/") {
87
+ lineComment = true;
88
+ i += 1;
89
+ continue;
90
+ }
91
+ if (char === "/" && next === "*") {
92
+ blockComment = true;
93
+ i += 1;
94
+ continue;
95
+ }
96
+ if (char === '"' || char === "'" || char === "`") {
97
+ quote = char;
98
+ continue;
99
+ }
100
+ if (char === "{") {
101
+ depth += 1;
102
+ continue;
103
+ }
104
+ if (char === "}") {
105
+ depth -= 1;
106
+ if (depth === 0) {
107
+ return i;
108
+ }
109
+ }
110
+ }
111
+ return -1;
41
112
  }
42
113
  function normalizePath(routePath) {
43
114
  return routePath.replace(/\{([^:}]+):[^}]+\}/g, "{$1}");
@@ -312,9 +383,9 @@ export type HasPathParams<T extends OperationName> = PathParams<T> extends never
312
383
  export type HasQueryParams<T extends OperationName> = QueryParams<T> extends never ? false : true
313
384
  `;
314
385
  }
315
- async function emitSchemasTypes(routesJsonPath, outputDir, schemasOutputPath) {
316
- const outDir = path.resolve(process.cwd(), outputDir);
317
- const outFile = schemasOutputPath ? path.resolve(process.cwd(), schemasOutputPath) : path.join(outDir, "schemas.ts");
386
+ async function emitSchemasTypes(routesJsonPath, outputDir, schemasOutputPath, projectRoot = process.cwd()) {
387
+ const outDir = path.resolve(projectRoot, outputDir);
388
+ const outFile = schemasOutputPath ? path.resolve(projectRoot, schemasOutputPath) : path.join(outDir, "schemas.ts");
318
389
  const schemasDir = path.dirname(outFile);
319
390
  const apiTypesPath = path.join(outDir, "api", "types.gen.ts");
320
391
  const apiTypesImportPath = toImportPath(schemasDir, apiTypesPath);
@@ -8,4 +8,4 @@
8
8
  *
9
9
  * @returns true if file was changed, false if unchanged
10
10
  */
11
- export declare function emitStaticPropsTypes(outputDir: string): Promise<boolean>;
11
+ export declare function emitStaticPropsTypes(outputDir: string, projectRoot?: string): Promise<boolean>;
@@ -53,10 +53,10 @@ function toInterfaceName(key) {
53
53
  const pascal = key.replace(/[^a-zA-Z0-9_$]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").replace(/(^|_)([a-z])/g, (_, __, c) => c.toUpperCase());
54
54
  return pascal || "StaticProp";
55
55
  }
56
- async function emitStaticPropsTypes(outputDir) {
56
+ async function emitStaticPropsTypes(outputDir, projectRoot = process.cwd()) {
57
57
  const bridgeConfig = readBridgeConfig();
58
58
  const staticProps = bridgeConfig?.staticProps ?? {};
59
- const outDir = path.resolve(process.cwd(), outputDir);
59
+ const outDir = path.resolve(projectRoot, outputDir);
60
60
  await fs.promises.mkdir(outDir, { recursive: true });
61
61
  const outFile = path.join(outDir, "static-props.ts");
62
62
  if (Object.keys(staticProps).length === 0) {
@@ -1,3 +1,9 @@
1
+ interface OpenApiTsCacheOptions {
2
+ generateSdk: boolean;
3
+ generateZod: boolean;
4
+ plugins: string[];
5
+ outputPaths?: string[];
6
+ }
1
7
  /**
2
8
  * Check if openapi-ts should run based on input cache.
3
9
  *
@@ -6,11 +12,7 @@
6
12
  * @param options - Generator options
7
13
  * @returns true if generation should run, false if cached
8
14
  */
9
- export declare function shouldRunOpenApiTs(openapiPath: string, configPath: string | null, options: {
10
- generateSdk: boolean;
11
- generateZod: boolean;
12
- plugins: string[];
13
- }): Promise<boolean>;
15
+ export declare function shouldRunOpenApiTs(openapiPath: string, configPath: string | null, options: OpenApiTsCacheOptions): Promise<boolean>;
14
16
  /**
15
17
  * Update cache after successful openapi-ts run.
16
18
  *
@@ -18,11 +20,7 @@ export declare function shouldRunOpenApiTs(openapiPath: string, configPath: stri
18
20
  * @param configPath - Path to config file (or null)
19
21
  * @param options - Generator options
20
22
  */
21
- export declare function updateOpenApiTsCache(openapiPath: string, configPath: string | null, options: {
22
- generateSdk: boolean;
23
- generateZod: boolean;
24
- plugins: string[];
25
- }): Promise<void>;
23
+ export declare function updateOpenApiTsCache(openapiPath: string, configPath: string | null, options: OpenApiTsCacheOptions): Promise<void>;
26
24
  /**
27
25
  * Compute input hash for page props generation.
28
26
  *
@@ -36,10 +34,11 @@ export declare function computePagePropsHash(pagePropsPath: string): Promise<str
36
34
  * @param pagePropsPath - Path to inertia-pages.json
37
35
  * @returns true if generation should run, false if cached
38
36
  */
39
- export declare function shouldRegeneratePageProps(pagePropsPath: string): Promise<boolean>;
37
+ export declare function shouldRegeneratePageProps(pagePropsPath: string, outputPath?: string): Promise<boolean>;
40
38
  /**
41
39
  * Update cache after successful page props generation.
42
40
  *
43
41
  * @param pagePropsPath - Path to inertia-pages.json
44
42
  */
45
43
  export declare function updatePagePropsCache(pagePropsPath: string): Promise<void>;
44
+ export {};
@@ -45,11 +45,17 @@ function isOptionalMetadataMatch(a, b) {
45
45
  }
46
46
  return isMetadataMatch(a, b);
47
47
  }
48
+ function areExpectedOutputsPresent(outputPaths) {
49
+ return !outputPaths?.some((outputPath) => !fs.existsSync(outputPath));
50
+ }
48
51
  function hashObject(obj) {
49
52
  const sorted = JSON.stringify(obj, Object.keys(obj).toSorted());
50
53
  return crypto.createHash("sha256").update(sorted).digest("hex");
51
54
  }
52
55
  async function shouldRunOpenApiTs(openapiPath, configPath, options) {
56
+ if (!areExpectedOutputsPresent(options.outputPaths)) {
57
+ return true;
58
+ }
53
59
  const cache = await loadCache();
54
60
  const optionsHash = hashObject(options);
55
61
  const cacheKey = "openapi-ts";
@@ -81,7 +87,10 @@ async function updateOpenApiTsCache(openapiPath, configPath, options) {
81
87
  async function computePagePropsHash(pagePropsPath) {
82
88
  return hashFile(pagePropsPath);
83
89
  }
84
- async function shouldRegeneratePageProps(pagePropsPath) {
90
+ async function shouldRegeneratePageProps(pagePropsPath, outputPath) {
91
+ if (outputPath && !fs.existsSync(outputPath)) {
92
+ return true;
93
+ }
85
94
  const cache = await loadCache();
86
95
  const currentMetadata = await readFileMetadata(pagePropsPath);
87
96
  const cacheKey = "page-props";
@@ -53,12 +53,30 @@ export interface TypeGenLogger {
53
53
  warn(message: string): void;
54
54
  error(message: string): void;
55
55
  }
56
+ export interface TypeGenCacheHooks {
57
+ shouldRunOpenApiTs(openapiPath: string, configPath: string | null, options: {
58
+ generateSdk: boolean;
59
+ generateZod: boolean;
60
+ plugins: string[];
61
+ outputPaths?: string[];
62
+ }): Promise<boolean>;
63
+ updateOpenApiTsCache(openapiPath: string, configPath: string | null, options: {
64
+ generateSdk: boolean;
65
+ generateZod: boolean;
66
+ plugins: string[];
67
+ outputPaths?: string[];
68
+ }): Promise<void>;
69
+ shouldRegeneratePageProps(pagePropsPath: string, outputPath?: string): Promise<boolean>;
70
+ updatePagePropsCache(pagePropsPath: string): Promise<void>;
71
+ }
56
72
  /**
57
73
  * Options for running type generation.
58
74
  */
59
75
  export interface RunTypeGenOptions {
60
76
  /** Logger for output (optional - silent if not provided) */
61
77
  logger?: TypeGenLogger;
78
+ /** Optional cache hooks for Vite dev/build contexts */
79
+ cache?: TypeGenCacheHooks;
62
80
  }
63
81
  /**
64
82
  * Resolve the default hey-api client plugin for the current project mode.
@@ -79,6 +97,12 @@ export declare function buildHeyApiPlugins(config: {
79
97
  generateZod: boolean;
80
98
  sdkClientPlugin: string;
81
99
  }): string[];
100
+ /**
101
+ * Resolve the locally installed @hey-api/openapi-ts executable.
102
+ */
103
+ export declare function resolveHeyApiBin(projectRoot: string): {
104
+ binPath: string;
105
+ } | null;
82
106
  /**
83
107
  * Run @hey-api/openapi-ts to generate TypeScript types from OpenAPI spec.
84
108
  *