@float.js/core 2.0.1

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.
@@ -0,0 +1,399 @@
1
+ // src/ssg/index.ts
2
+ import { createHash } from "crypto";
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync } from "fs";
4
+ import { join, dirname } from "path";
5
+ var DEFAULT_CONFIG = {
6
+ outDir: ".float/static",
7
+ defaultRevalidate: 60,
8
+ staleWhileRevalidate: true,
9
+ maxMemoryCacheSize: 100,
10
+ compression: false
11
+ };
12
+ var SSGEngine = class {
13
+ config;
14
+ memoryCache = /* @__PURE__ */ new Map();
15
+ diskCachePath;
16
+ isrState = {
17
+ revalidating: /* @__PURE__ */ new Set(),
18
+ scheduled: /* @__PURE__ */ new Map(),
19
+ lastRevalidation: /* @__PURE__ */ new Map()
20
+ };
21
+ constructor(config = {}) {
22
+ this.config = { ...DEFAULT_CONFIG, ...config };
23
+ this.diskCachePath = join(process.cwd(), this.config.outDir);
24
+ this.ensureCacheDir();
25
+ }
26
+ ensureCacheDir() {
27
+ if (!existsSync(this.diskCachePath)) {
28
+ mkdirSync(this.diskCachePath, { recursive: true });
29
+ }
30
+ }
31
+ getCacheKey(path, locale) {
32
+ const normalized = path.replace(/^\/+|\/+$/g, "") || "index";
33
+ const key = locale ? `${locale}/${normalized}` : normalized;
34
+ return key.replace(/\//g, "__");
35
+ }
36
+ getEtag(content) {
37
+ return createHash("md5").update(content).digest("hex").slice(0, 16);
38
+ }
39
+ getCacheFilePath(cacheKey) {
40
+ return join(this.diskCachePath, `${cacheKey}.json`);
41
+ }
42
+ /**
43
+ * Generate static page and cache it
44
+ */
45
+ async generatePage(path, getStaticProps, render, params = {}, locale) {
46
+ const startTime = Date.now();
47
+ const cacheKey = this.getCacheKey(path, locale);
48
+ try {
49
+ const propsResult = await getStaticProps({
50
+ params,
51
+ locale,
52
+ preview: false
53
+ });
54
+ if (propsResult.redirect) {
55
+ return {
56
+ path,
57
+ success: true,
58
+ duration: Date.now() - startTime,
59
+ size: 0
60
+ };
61
+ }
62
+ if (propsResult.notFound) {
63
+ return {
64
+ path,
65
+ success: false,
66
+ duration: Date.now() - startTime,
67
+ size: 0,
68
+ error: new Error("Page not found")
69
+ };
70
+ }
71
+ const html = await render(propsResult.props);
72
+ const etag = this.getEtag(html);
73
+ const revalidate = propsResult.revalidate ?? this.config.defaultRevalidate;
74
+ const revalidateAfter = revalidate === false ? null : Date.now() + revalidate * 1e3;
75
+ const cachedPage = {
76
+ html,
77
+ props: propsResult.props,
78
+ generatedAt: Date.now(),
79
+ revalidateAfter,
80
+ etag,
81
+ headers: {
82
+ "Cache-Control": revalidate === false ? "public, max-age=31536000, immutable" : `public, s-maxage=${revalidate}, stale-while-revalidate=${revalidate * 2}`
83
+ }
84
+ };
85
+ this.setMemoryCache(cacheKey, cachedPage);
86
+ this.writeToDisk(cacheKey, cachedPage);
87
+ return {
88
+ path,
89
+ success: true,
90
+ duration: Date.now() - startTime,
91
+ size: html.length,
92
+ revalidateAt: revalidateAfter ? new Date(revalidateAfter) : void 0
93
+ };
94
+ } catch (error) {
95
+ return {
96
+ path,
97
+ success: false,
98
+ duration: Date.now() - startTime,
99
+ size: 0,
100
+ error: error instanceof Error ? error : new Error(String(error))
101
+ };
102
+ }
103
+ }
104
+ /**
105
+ * Get cached page or generate on-demand
106
+ */
107
+ async getPage(path, getStaticProps, render, params = {}, locale, fallback = false) {
108
+ const cacheKey = this.getCacheKey(path, locale);
109
+ let cached = this.memoryCache.get(cacheKey);
110
+ if (!cached) {
111
+ cached = this.readFromDisk(cacheKey) || void 0;
112
+ if (cached) {
113
+ this.setMemoryCache(cacheKey, cached);
114
+ }
115
+ }
116
+ if (cached) {
117
+ const isStale = cached.revalidateAfter !== null && Date.now() > cached.revalidateAfter;
118
+ if (isStale && this.config.staleWhileRevalidate) {
119
+ this.triggerRevalidation(path, getStaticProps, render, params, locale);
120
+ }
121
+ return { cached, stale: isStale };
122
+ }
123
+ if (fallback === false) {
124
+ return null;
125
+ }
126
+ if (fallback === "blocking") {
127
+ const result = await this.generatePage(path, getStaticProps, render, params, locale);
128
+ if (result.success) {
129
+ const newCached = this.memoryCache.get(cacheKey);
130
+ return newCached ? { cached: newCached, stale: false } : null;
131
+ }
132
+ }
133
+ return null;
134
+ }
135
+ /**
136
+ * Trigger ISR revalidation in background
137
+ */
138
+ async triggerRevalidation(path, getStaticProps, render, params = {}, locale) {
139
+ const cacheKey = this.getCacheKey(path, locale);
140
+ if (this.isrState.revalidating.has(cacheKey)) {
141
+ return;
142
+ }
143
+ this.isrState.revalidating.add(cacheKey);
144
+ try {
145
+ await this.generatePage(path, getStaticProps, render, params, locale);
146
+ this.isrState.lastRevalidation.set(cacheKey, Date.now());
147
+ } finally {
148
+ this.isrState.revalidating.delete(cacheKey);
149
+ }
150
+ }
151
+ /**
152
+ * Force revalidation of a path (On-Demand ISR)
153
+ */
154
+ async revalidate(path, getStaticProps, render, params = {}, locale) {
155
+ try {
156
+ const result = await this.generatePage(path, getStaticProps, render, params, locale);
157
+ return { revalidated: result.success, error: result.error?.message };
158
+ } catch (error) {
159
+ return {
160
+ revalidated: false,
161
+ error: error instanceof Error ? error.message : "Unknown error"
162
+ };
163
+ }
164
+ }
165
+ /**
166
+ * Batch generate multiple pages
167
+ */
168
+ async generatePaths(paths, getStaticProps, render, concurrency = 5) {
169
+ const results = [];
170
+ const queue = [...paths];
171
+ const worker = async () => {
172
+ while (queue.length > 0) {
173
+ const item = queue.shift();
174
+ if (!item) break;
175
+ const pathStr = this.buildPath(item.params);
176
+ const result = await this.generatePage(
177
+ pathStr,
178
+ getStaticProps,
179
+ render,
180
+ item.params,
181
+ item.locale
182
+ );
183
+ results.push(result);
184
+ }
185
+ };
186
+ const workers = Array(Math.min(concurrency, paths.length)).fill(null).map(() => worker());
187
+ await Promise.all(workers);
188
+ return results;
189
+ }
190
+ buildPath(params) {
191
+ return "/" + Object.values(params).map((v) => Array.isArray(v) ? v.join("/") : v).join("/");
192
+ }
193
+ /**
194
+ * Memory cache with LRU eviction
195
+ */
196
+ setMemoryCache(key, page) {
197
+ if (this.memoryCache.size >= this.config.maxMemoryCacheSize) {
198
+ const firstKey = this.memoryCache.keys().next().value;
199
+ if (firstKey) {
200
+ this.memoryCache.delete(firstKey);
201
+ }
202
+ }
203
+ this.memoryCache.delete(key);
204
+ this.memoryCache.set(key, page);
205
+ }
206
+ /**
207
+ * Write cached page to disk
208
+ */
209
+ writeToDisk(cacheKey, page) {
210
+ try {
211
+ const filePath = this.getCacheFilePath(cacheKey);
212
+ const dir = dirname(filePath);
213
+ if (!existsSync(dir)) {
214
+ mkdirSync(dir, { recursive: true });
215
+ }
216
+ writeFileSync(filePath, JSON.stringify(page));
217
+ } catch {
218
+ }
219
+ }
220
+ /**
221
+ * Read cached page from disk
222
+ */
223
+ readFromDisk(cacheKey) {
224
+ try {
225
+ const filePath = this.getCacheFilePath(cacheKey);
226
+ if (existsSync(filePath)) {
227
+ const content = readFileSync(filePath, "utf-8");
228
+ return JSON.parse(content);
229
+ }
230
+ } catch {
231
+ }
232
+ return null;
233
+ }
234
+ /**
235
+ * Clear all caches
236
+ */
237
+ clearCache() {
238
+ this.memoryCache.clear();
239
+ for (const timeout of this.isrState.scheduled.values()) {
240
+ clearTimeout(timeout);
241
+ }
242
+ this.isrState.scheduled.clear();
243
+ this.isrState.lastRevalidation.clear();
244
+ }
245
+ /**
246
+ * Get cache statistics
247
+ */
248
+ getStats() {
249
+ let diskPages = 0;
250
+ try {
251
+ if (existsSync(this.diskCachePath)) {
252
+ diskPages = readdirSync(this.diskCachePath).filter((f) => f.endsWith(".json")).length;
253
+ }
254
+ } catch {
255
+ }
256
+ return {
257
+ memoryPages: this.memoryCache.size,
258
+ diskPages,
259
+ revalidating: this.isrState.revalidating.size,
260
+ lastRevalidations: Object.fromEntries(
261
+ Array.from(this.isrState.lastRevalidation.entries()).map(([k, v]) => [k, new Date(v)])
262
+ )
263
+ };
264
+ }
265
+ /**
266
+ * Purge stale pages from disk
267
+ */
268
+ purgeStale() {
269
+ let purged = 0;
270
+ try {
271
+ if (!existsSync(this.diskCachePath)) return 0;
272
+ const files = readdirSync(this.diskCachePath).filter((f) => f.endsWith(".json"));
273
+ for (const file of files) {
274
+ const filePath = join(this.diskCachePath, file);
275
+ try {
276
+ const content = readFileSync(filePath, "utf-8");
277
+ const page = JSON.parse(content);
278
+ if (page.revalidateAfter !== null && Date.now() > page.revalidateAfter) {
279
+ unlinkSync(filePath);
280
+ purged++;
281
+ }
282
+ } catch {
283
+ unlinkSync(filePath);
284
+ purged++;
285
+ }
286
+ }
287
+ } catch {
288
+ }
289
+ return purged;
290
+ }
291
+ };
292
+ function defineStaticPaths(fn) {
293
+ return async () => {
294
+ const result = await fn();
295
+ return result;
296
+ };
297
+ }
298
+ function defineStaticProps(fn) {
299
+ return async (ctx) => {
300
+ const result = await fn(ctx);
301
+ return result;
302
+ };
303
+ }
304
+ var ssgEngine = null;
305
+ function getSSGEngine(config) {
306
+ if (!ssgEngine) {
307
+ ssgEngine = new SSGEngine(config);
308
+ }
309
+ return ssgEngine;
310
+ }
311
+ function configureSSG(config) {
312
+ ssgEngine = new SSGEngine(config);
313
+ return ssgEngine;
314
+ }
315
+ function createSSGHandler(options) {
316
+ const engine = getSSGEngine();
317
+ return async (req) => {
318
+ const url = new URL(req.url);
319
+ const path = url.pathname;
320
+ const params = {};
321
+ const result = await engine.getPage(
322
+ path,
323
+ options.getStaticProps,
324
+ options.render,
325
+ params,
326
+ void 0,
327
+ options.fallback ?? false
328
+ );
329
+ if (!result) {
330
+ return new Response("Not Found", { status: 404 });
331
+ }
332
+ const ifNoneMatch = req.headers.get("If-None-Match");
333
+ if (ifNoneMatch === result.cached.etag) {
334
+ return new Response(null, { status: 304 });
335
+ }
336
+ return new Response(result.cached.html, {
337
+ status: 200,
338
+ headers: {
339
+ "Content-Type": "text/html; charset=utf-8",
340
+ "ETag": result.cached.etag,
341
+ "X-Float-Generated": new Date(result.cached.generatedAt).toISOString(),
342
+ "X-Float-Stale": result.stale ? "true" : "false",
343
+ ...result.cached.headers
344
+ }
345
+ });
346
+ };
347
+ }
348
+ function createRevalidateHandler(options) {
349
+ const engine = getSSGEngine();
350
+ return async (req) => {
351
+ const url = new URL(req.url);
352
+ if (options.secret) {
353
+ const providedSecret = url.searchParams.get("secret");
354
+ if (providedSecret !== options.secret) {
355
+ return new Response(JSON.stringify({ error: "Invalid token" }), {
356
+ status: 401,
357
+ headers: { "Content-Type": "application/json" }
358
+ });
359
+ }
360
+ }
361
+ const pathToRevalidate = url.searchParams.get("path");
362
+ if (!pathToRevalidate) {
363
+ return new Response(JSON.stringify({ error: "Missing path parameter" }), {
364
+ status: 400,
365
+ headers: { "Content-Type": "application/json" }
366
+ });
367
+ }
368
+ const result = await engine.revalidate(
369
+ pathToRevalidate,
370
+ options.getStaticProps,
371
+ options.render
372
+ );
373
+ return new Response(JSON.stringify(result), {
374
+ status: result.revalidated ? 200 : 500,
375
+ headers: { "Content-Type": "application/json" }
376
+ });
377
+ };
378
+ }
379
+ var ssg = {
380
+ engine: getSSGEngine,
381
+ configure: configureSSG,
382
+ defineStaticPaths,
383
+ defineStaticProps,
384
+ createHandler: createSSGHandler,
385
+ createRevalidateHandler
386
+ };
387
+ var ssg_default = ssg;
388
+ export {
389
+ SSGEngine,
390
+ configureSSG,
391
+ createRevalidateHandler,
392
+ createSSGHandler,
393
+ ssg_default as default,
394
+ defineStaticPaths,
395
+ defineStaticProps,
396
+ getSSGEngine,
397
+ ssg
398
+ };
399
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ssg/index.ts"],"sourcesContent":["/**\n * Float.js - Static Site Generation (SSG) & Incremental Static Regeneration (ISR)\n * \n * Built-in static generation with automatic revalidation.\n * Simpler API than Next.js with more flexibility.\n */\n\nimport { createHash } from 'crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync } from 'fs';\nimport { join, dirname } from 'path';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\nexport interface StaticPath {\n params: Record<string, string | string[]>;\n locale?: string;\n}\n\nexport interface GetStaticPathsResult {\n paths: StaticPath[];\n fallback: boolean | 'blocking';\n}\n\nexport interface GetStaticPropsContext<P = Record<string, string>> {\n params: P;\n locale?: string;\n defaultLocale?: string;\n preview?: boolean;\n previewData?: unknown;\n}\n\nexport interface GetStaticPropsResult<T = unknown> {\n props: T;\n revalidate?: number | false;\n notFound?: boolean;\n redirect?: {\n destination: string;\n permanent?: boolean;\n statusCode?: 301 | 302 | 303 | 307 | 308;\n };\n}\n\nexport interface CachedPage {\n html: string;\n props: unknown;\n generatedAt: number;\n revalidateAfter: number | null;\n etag: string;\n headers?: Record<string, string>;\n}\n\nexport interface SSGConfig {\n /** Directory to store generated pages */\n outDir: string;\n /** Default revalidation time in seconds */\n defaultRevalidate: number;\n /** Enable stale-while-revalidate */\n staleWhileRevalidate: boolean;\n /** Max pages in memory cache */\n maxMemoryCacheSize: number;\n /** Custom render function */\n renderPage?: (component: unknown, props: unknown) => Promise<string>;\n /** Compression for stored pages */\n compression: boolean;\n}\n\nexport interface GenerateResult {\n path: string;\n success: boolean;\n error?: Error;\n duration: number;\n size: number;\n revalidateAt?: Date;\n}\n\nexport interface ISRState {\n revalidating: Set<string>;\n scheduled: Map<string, NodeJS.Timeout>;\n lastRevalidation: Map<string, number>;\n}\n\n// ============================================================================\n// DEFAULT CONFIG\n// ============================================================================\n\nconst DEFAULT_CONFIG: SSGConfig = {\n outDir: '.float/static',\n defaultRevalidate: 60,\n staleWhileRevalidate: true,\n maxMemoryCacheSize: 100,\n compression: false\n};\n\n// ============================================================================\n// SSG ENGINE\n// ============================================================================\n\nexport class SSGEngine {\n private config: SSGConfig;\n private memoryCache: Map<string, CachedPage> = new Map();\n private diskCachePath: string;\n private isrState: ISRState = {\n revalidating: new Set(),\n scheduled: new Map(),\n lastRevalidation: new Map()\n };\n\n constructor(config: Partial<SSGConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n this.diskCachePath = join(process.cwd(), this.config.outDir);\n this.ensureCacheDir();\n }\n\n private ensureCacheDir(): void {\n if (!existsSync(this.diskCachePath)) {\n mkdirSync(this.diskCachePath, { recursive: true });\n }\n }\n\n private getCacheKey(path: string, locale?: string): string {\n const normalized = path.replace(/^\\/+|\\/+$/g, '') || 'index';\n const key = locale ? `${locale}/${normalized}` : normalized;\n return key.replace(/\\//g, '__');\n }\n\n private getEtag(content: string): string {\n return createHash('md5').update(content).digest('hex').slice(0, 16);\n }\n\n private getCacheFilePath(cacheKey: string): string {\n return join(this.diskCachePath, `${cacheKey}.json`);\n }\n\n /**\n * Generate static page and cache it\n */\n async generatePage<P = unknown>(\n path: string,\n getStaticProps: (ctx: GetStaticPropsContext) => Promise<GetStaticPropsResult<P>>,\n render: (props: P) => Promise<string>,\n params: Record<string, string> = {},\n locale?: string\n ): Promise<GenerateResult> {\n const startTime = Date.now();\n const cacheKey = this.getCacheKey(path, locale);\n\n try {\n // Get props\n const propsResult = await getStaticProps({\n params,\n locale,\n preview: false\n });\n\n // Handle redirect/notFound\n if (propsResult.redirect) {\n return {\n path,\n success: true,\n duration: Date.now() - startTime,\n size: 0\n };\n }\n\n if (propsResult.notFound) {\n return {\n path,\n success: false,\n duration: Date.now() - startTime,\n size: 0,\n error: new Error('Page not found')\n };\n }\n\n // Render HTML\n const html = await render(propsResult.props);\n const etag = this.getEtag(html);\n\n // Calculate revalidation\n const revalidate = propsResult.revalidate ?? this.config.defaultRevalidate;\n const revalidateAfter = revalidate === false \n ? null \n : Date.now() + (revalidate * 1000);\n\n // Create cached page\n const cachedPage: CachedPage = {\n html,\n props: propsResult.props,\n generatedAt: Date.now(),\n revalidateAfter,\n etag,\n headers: {\n 'Cache-Control': revalidate === false\n ? 'public, max-age=31536000, immutable'\n : `public, s-maxage=${revalidate}, stale-while-revalidate=${revalidate * 2}`\n }\n };\n\n // Store in memory cache (with LRU eviction)\n this.setMemoryCache(cacheKey, cachedPage);\n\n // Store on disk\n this.writeToDisk(cacheKey, cachedPage);\n\n return {\n path,\n success: true,\n duration: Date.now() - startTime,\n size: html.length,\n revalidateAt: revalidateAfter ? new Date(revalidateAfter) : undefined\n };\n\n } catch (error) {\n return {\n path,\n success: false,\n duration: Date.now() - startTime,\n size: 0,\n error: error instanceof Error ? error : new Error(String(error))\n };\n }\n }\n\n /**\n * Get cached page or generate on-demand\n */\n async getPage<P = unknown>(\n path: string,\n getStaticProps: (ctx: GetStaticPropsContext) => Promise<GetStaticPropsResult<P>>,\n render: (props: P) => Promise<string>,\n params: Record<string, string> = {},\n locale?: string,\n fallback: boolean | 'blocking' = false\n ): Promise<{ cached: CachedPage; stale: boolean } | null> {\n const cacheKey = this.getCacheKey(path, locale);\n\n // Try memory cache first\n let cached = this.memoryCache.get(cacheKey);\n\n // Try disk cache if not in memory\n if (!cached) {\n cached = this.readFromDisk(cacheKey) || undefined;\n if (cached) {\n this.setMemoryCache(cacheKey, cached);\n }\n }\n\n // If we have a cached page\n if (cached) {\n const isStale = cached.revalidateAfter !== null && \n Date.now() > cached.revalidateAfter;\n\n // If stale and SWR enabled, trigger background revalidation\n if (isStale && this.config.staleWhileRevalidate) {\n this.triggerRevalidation(path, getStaticProps, render, params, locale);\n }\n\n return { cached, stale: isStale };\n }\n\n // No cached page - handle fallback\n if (fallback === false) {\n return null; // 404\n }\n\n // Generate on-demand (blocking or non-blocking)\n if (fallback === 'blocking') {\n const result = await this.generatePage(path, getStaticProps, render, params, locale);\n if (result.success) {\n const newCached = this.memoryCache.get(cacheKey);\n return newCached ? { cached: newCached, stale: false } : null;\n }\n }\n\n return null;\n }\n\n /**\n * Trigger ISR revalidation in background\n */\n private async triggerRevalidation<P = unknown>(\n path: string,\n getStaticProps: (ctx: GetStaticPropsContext) => Promise<GetStaticPropsResult<P>>,\n render: (props: P) => Promise<string>,\n params: Record<string, string> = {},\n locale?: string\n ): Promise<void> {\n const cacheKey = this.getCacheKey(path, locale);\n\n // Prevent concurrent revalidation\n if (this.isrState.revalidating.has(cacheKey)) {\n return;\n }\n\n this.isrState.revalidating.add(cacheKey);\n\n try {\n await this.generatePage(path, getStaticProps, render, params, locale);\n this.isrState.lastRevalidation.set(cacheKey, Date.now());\n } finally {\n this.isrState.revalidating.delete(cacheKey);\n }\n }\n\n /**\n * Force revalidation of a path (On-Demand ISR)\n */\n async revalidate<P = unknown>(\n path: string,\n getStaticProps: (ctx: GetStaticPropsContext) => Promise<GetStaticPropsResult<P>>,\n render: (props: P) => Promise<string>,\n params: Record<string, string> = {},\n locale?: string\n ): Promise<{ revalidated: boolean; error?: string }> {\n try {\n const result = await this.generatePage(path, getStaticProps, render, params, locale);\n return { revalidated: result.success, error: result.error?.message };\n } catch (error) {\n return { \n revalidated: false, \n error: error instanceof Error ? error.message : 'Unknown error' \n };\n }\n }\n\n /**\n * Batch generate multiple pages\n */\n async generatePaths<P = unknown>(\n paths: StaticPath[],\n getStaticProps: (ctx: GetStaticPropsContext) => Promise<GetStaticPropsResult<P>>,\n render: (props: P) => Promise<string>,\n concurrency: number = 5\n ): Promise<GenerateResult[]> {\n const results: GenerateResult[] = [];\n const queue = [...paths];\n\n const worker = async () => {\n while (queue.length > 0) {\n const item = queue.shift();\n if (!item) break;\n\n const pathStr = this.buildPath(item.params);\n const result = await this.generatePage(\n pathStr,\n getStaticProps,\n render,\n item.params as Record<string, string>,\n item.locale\n );\n results.push(result);\n }\n };\n\n // Run workers in parallel\n const workers = Array(Math.min(concurrency, paths.length))\n .fill(null)\n .map(() => worker());\n\n await Promise.all(workers);\n\n return results;\n }\n\n private buildPath(params: Record<string, string | string[]>): string {\n return '/' + Object.values(params)\n .map(v => Array.isArray(v) ? v.join('/') : v)\n .join('/');\n }\n\n /**\n * Memory cache with LRU eviction\n */\n private setMemoryCache(key: string, page: CachedPage): void {\n // Evict if at capacity\n if (this.memoryCache.size >= this.config.maxMemoryCacheSize) {\n const firstKey = this.memoryCache.keys().next().value;\n if (firstKey) {\n this.memoryCache.delete(firstKey);\n }\n }\n\n // Delete and re-add to maintain LRU order\n this.memoryCache.delete(key);\n this.memoryCache.set(key, page);\n }\n\n /**\n * Write cached page to disk\n */\n private writeToDisk(cacheKey: string, page: CachedPage): void {\n try {\n const filePath = this.getCacheFilePath(cacheKey);\n const dir = dirname(filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(filePath, JSON.stringify(page));\n } catch {\n // Silently fail disk writes\n }\n }\n\n /**\n * Read cached page from disk\n */\n private readFromDisk(cacheKey: string): CachedPage | null {\n try {\n const filePath = this.getCacheFilePath(cacheKey);\n if (existsSync(filePath)) {\n const content = readFileSync(filePath, 'utf-8');\n return JSON.parse(content);\n }\n } catch {\n // Silently fail disk reads\n }\n return null;\n }\n\n /**\n * Clear all caches\n */\n clearCache(): void {\n this.memoryCache.clear();\n \n // Clear scheduled revalidations\n for (const timeout of this.isrState.scheduled.values()) {\n clearTimeout(timeout);\n }\n this.isrState.scheduled.clear();\n this.isrState.lastRevalidation.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats(): {\n memoryPages: number;\n diskPages: number;\n revalidating: number;\n lastRevalidations: Record<string, Date>;\n } {\n let diskPages = 0;\n try {\n if (existsSync(this.diskCachePath)) {\n diskPages = readdirSync(this.diskCachePath)\n .filter(f => f.endsWith('.json')).length;\n }\n } catch {\n // Ignore\n }\n\n return {\n memoryPages: this.memoryCache.size,\n diskPages,\n revalidating: this.isrState.revalidating.size,\n lastRevalidations: Object.fromEntries(\n Array.from(this.isrState.lastRevalidation.entries())\n .map(([k, v]) => [k, new Date(v)])\n )\n };\n }\n\n /**\n * Purge stale pages from disk\n */\n purgeStale(): number {\n let purged = 0;\n\n try {\n if (!existsSync(this.diskCachePath)) return 0;\n\n const files = readdirSync(this.diskCachePath)\n .filter(f => f.endsWith('.json'));\n\n for (const file of files) {\n const filePath = join(this.diskCachePath, file);\n try {\n const content = readFileSync(filePath, 'utf-8');\n const page: CachedPage = JSON.parse(content);\n \n if (page.revalidateAfter !== null && Date.now() > page.revalidateAfter) {\n unlinkSync(filePath);\n purged++;\n }\n } catch {\n // Invalid file, remove it\n unlinkSync(filePath);\n purged++;\n }\n }\n } catch {\n // Ignore\n }\n\n return purged;\n }\n}\n\n// ============================================================================\n// HELPER FUNCTIONS (like getStaticPaths, getStaticProps)\n// ============================================================================\n\n/**\n * Define static paths for dynamic routes\n */\nexport function defineStaticPaths<_P extends StaticPath = StaticPath>(\n fn: () => Promise<GetStaticPathsResult> | GetStaticPathsResult\n): () => Promise<GetStaticPathsResult> {\n return async () => {\n const result = await fn();\n return result;\n };\n}\n\n/**\n * Define static props for a page\n */\nexport function defineStaticProps<P = unknown, Params = Record<string, string>>(\n fn: (ctx: GetStaticPropsContext<Params>) => Promise<GetStaticPropsResult<P>> | GetStaticPropsResult<P>\n): (ctx: GetStaticPropsContext<Params>) => Promise<GetStaticPropsResult<P>> {\n return async (ctx) => {\n const result = await fn(ctx);\n return result;\n };\n}\n\n// ============================================================================\n// SINGLETON & EXPORTS\n// ============================================================================\n\nlet ssgEngine: SSGEngine | null = null;\n\n/**\n * Get or create SSG engine instance\n */\nexport function getSSGEngine(config?: Partial<SSGConfig>): SSGEngine {\n if (!ssgEngine) {\n ssgEngine = new SSGEngine(config);\n }\n return ssgEngine;\n}\n\n/**\n * Configure SSG engine\n */\nexport function configureSSG(config: Partial<SSGConfig>): SSGEngine {\n ssgEngine = new SSGEngine(config);\n return ssgEngine;\n}\n\n// ============================================================================\n// REQUEST HANDLER\n// ============================================================================\n\nexport interface SSGHandlerOptions {\n getStaticPaths?: () => Promise<GetStaticPathsResult>;\n getStaticProps: (ctx: GetStaticPropsContext) => Promise<GetStaticPropsResult>;\n render: (props: unknown) => Promise<string>;\n fallback?: boolean | 'blocking';\n}\n\n/**\n * Create SSG request handler\n */\nexport function createSSGHandler(options: SSGHandlerOptions) {\n const engine = getSSGEngine();\n\n return async (req: Request): Promise<Response> => {\n const url = new URL(req.url);\n const path = url.pathname;\n \n // Extract params from path (simplified)\n const params: Record<string, string> = {};\n\n // Try to get cached page\n const result = await engine.getPage(\n path,\n options.getStaticProps as (ctx: GetStaticPropsContext) => Promise<GetStaticPropsResult<unknown>>,\n options.render,\n params,\n undefined,\n options.fallback ?? false\n );\n\n if (!result) {\n return new Response('Not Found', { status: 404 });\n }\n\n // Check ETag for conditional request\n const ifNoneMatch = req.headers.get('If-None-Match');\n if (ifNoneMatch === result.cached.etag) {\n return new Response(null, { status: 304 });\n }\n\n // Return cached HTML\n return new Response(result.cached.html, {\n status: 200,\n headers: {\n 'Content-Type': 'text/html; charset=utf-8',\n 'ETag': result.cached.etag,\n 'X-Float-Generated': new Date(result.cached.generatedAt).toISOString(),\n 'X-Float-Stale': result.stale ? 'true' : 'false',\n ...result.cached.headers\n }\n });\n };\n}\n\n// ============================================================================\n// REVALIDATION API\n// ============================================================================\n\nexport interface RevalidateAPIOptions {\n secret?: string;\n getStaticProps: (ctx: GetStaticPropsContext) => Promise<GetStaticPropsResult>;\n render: (props: unknown) => Promise<string>;\n}\n\n/**\n * Create on-demand revalidation API handler\n */\nexport function createRevalidateHandler(options: RevalidateAPIOptions) {\n const engine = getSSGEngine();\n\n return async (req: Request): Promise<Response> => {\n const url = new URL(req.url);\n \n // Validate secret if configured\n if (options.secret) {\n const providedSecret = url.searchParams.get('secret');\n if (providedSecret !== options.secret) {\n return new Response(JSON.stringify({ error: 'Invalid token' }), {\n status: 401,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n }\n\n // Get path to revalidate\n const pathToRevalidate = url.searchParams.get('path');\n if (!pathToRevalidate) {\n return new Response(JSON.stringify({ error: 'Missing path parameter' }), {\n status: 400,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n // Trigger revalidation\n const result = await engine.revalidate(\n pathToRevalidate,\n options.getStaticProps as (ctx: GetStaticPropsContext) => Promise<GetStaticPropsResult<unknown>>,\n options.render\n );\n\n return new Response(JSON.stringify(result), {\n status: result.revalidated ? 200 : 500,\n headers: { 'Content-Type': 'application/json' }\n });\n };\n}\n\n// ============================================================================\n// MAIN EXPORT\n// ============================================================================\n\nexport const ssg = {\n engine: getSSGEngine,\n configure: configureSSG,\n defineStaticPaths,\n defineStaticProps,\n createHandler: createSSGHandler,\n createRevalidateHandler\n};\n\nexport default ssg;\n"],"mappings":";AAOA,SAAS,kBAAkB;AAC3B,SAAS,YAAY,WAAW,cAAc,eAAe,aAAa,kBAAkB;AAC5F,SAAS,MAAM,eAAe;AA8E9B,IAAM,iBAA4B;AAAA,EAChC,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,aAAa;AACf;AAMO,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACA,cAAuC,oBAAI,IAAI;AAAA,EAC/C;AAAA,EACA,WAAqB;AAAA,IAC3B,cAAc,oBAAI,IAAI;AAAA,IACtB,WAAW,oBAAI,IAAI;AAAA,IACnB,kBAAkB,oBAAI,IAAI;AAAA,EAC5B;AAAA,EAEA,YAAY,SAA6B,CAAC,GAAG;AAC3C,SAAK,SAAS,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAC7C,SAAK,gBAAgB,KAAK,QAAQ,IAAI,GAAG,KAAK,OAAO,MAAM;AAC3D,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,iBAAuB;AAC7B,QAAI,CAAC,WAAW,KAAK,aAAa,GAAG;AACnC,gBAAU,KAAK,eAAe,EAAE,WAAW,KAAK,CAAC;AAAA,IACnD;AAAA,EACF;AAAA,EAEQ,YAAY,MAAc,QAAyB;AACzD,UAAM,aAAa,KAAK,QAAQ,cAAc,EAAE,KAAK;AACrD,UAAM,MAAM,SAAS,GAAG,MAAM,IAAI,UAAU,KAAK;AACjD,WAAO,IAAI,QAAQ,OAAO,IAAI;AAAA,EAChC;AAAA,EAEQ,QAAQ,SAAyB;AACvC,WAAO,WAAW,KAAK,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAAA,EACpE;AAAA,EAEQ,iBAAiB,UAA0B;AACjD,WAAO,KAAK,KAAK,eAAe,GAAG,QAAQ,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,MACA,gBACA,QACA,SAAiC,CAAC,GAClC,QACyB;AACzB,UAAM,YAAY,KAAK,IAAI;AAC3B,UAAM,WAAW,KAAK,YAAY,MAAM,MAAM;AAE9C,QAAI;AAEF,YAAM,cAAc,MAAM,eAAe;AAAA,QACvC;AAAA,QACA;AAAA,QACA,SAAS;AAAA,MACX,CAAC;AAGD,UAAI,YAAY,UAAU;AACxB,eAAO;AAAA,UACL;AAAA,UACA,SAAS;AAAA,UACT,UAAU,KAAK,IAAI,IAAI;AAAA,UACvB,MAAM;AAAA,QACR;AAAA,MACF;AAEA,UAAI,YAAY,UAAU;AACxB,eAAO;AAAA,UACL;AAAA,UACA,SAAS;AAAA,UACT,UAAU,KAAK,IAAI,IAAI;AAAA,UACvB,MAAM;AAAA,UACN,OAAO,IAAI,MAAM,gBAAgB;AAAA,QACnC;AAAA,MACF;AAGA,YAAM,OAAO,MAAM,OAAO,YAAY,KAAK;AAC3C,YAAM,OAAO,KAAK,QAAQ,IAAI;AAG9B,YAAM,aAAa,YAAY,cAAc,KAAK,OAAO;AACzD,YAAM,kBAAkB,eAAe,QACnC,OACA,KAAK,IAAI,IAAK,aAAa;AAG/B,YAAM,aAAyB;AAAA,QAC7B;AAAA,QACA,OAAO,YAAY;AAAA,QACnB,aAAa,KAAK,IAAI;AAAA,QACtB;AAAA,QACA;AAAA,QACA,SAAS;AAAA,UACP,iBAAiB,eAAe,QAC5B,wCACA,oBAAoB,UAAU,4BAA4B,aAAa,CAAC;AAAA,QAC9E;AAAA,MACF;AAGA,WAAK,eAAe,UAAU,UAAU;AAGxC,WAAK,YAAY,UAAU,UAAU;AAErC,aAAO;AAAA,QACL;AAAA,QACA,SAAS;AAAA,QACT,UAAU,KAAK,IAAI,IAAI;AAAA,QACvB,MAAM,KAAK;AAAA,QACX,cAAc,kBAAkB,IAAI,KAAK,eAAe,IAAI;AAAA,MAC9D;AAAA,IAEF,SAAS,OAAO;AACd,aAAO;AAAA,QACL;AAAA,QACA,SAAS;AAAA,QACT,UAAU,KAAK,IAAI,IAAI;AAAA,QACvB,MAAM;AAAA,QACN,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QACJ,MACA,gBACA,QACA,SAAiC,CAAC,GAClC,QACA,WAAiC,OACuB;AACxD,UAAM,WAAW,KAAK,YAAY,MAAM,MAAM;AAG9C,QAAI,SAAS,KAAK,YAAY,IAAI,QAAQ;AAG1C,QAAI,CAAC,QAAQ;AACX,eAAS,KAAK,aAAa,QAAQ,KAAK;AACxC,UAAI,QAAQ;AACV,aAAK,eAAe,UAAU,MAAM;AAAA,MACtC;AAAA,IACF;AAGA,QAAI,QAAQ;AACV,YAAM,UAAU,OAAO,oBAAoB,QAC3B,KAAK,IAAI,IAAI,OAAO;AAGpC,UAAI,WAAW,KAAK,OAAO,sBAAsB;AAC/C,aAAK,oBAAoB,MAAM,gBAAgB,QAAQ,QAAQ,MAAM;AAAA,MACvE;AAEA,aAAO,EAAE,QAAQ,OAAO,QAAQ;AAAA,IAClC;AAGA,QAAI,aAAa,OAAO;AACtB,aAAO;AAAA,IACT;AAGA,QAAI,aAAa,YAAY;AAC3B,YAAM,SAAS,MAAM,KAAK,aAAa,MAAM,gBAAgB,QAAQ,QAAQ,MAAM;AACnF,UAAI,OAAO,SAAS;AAClB,cAAM,YAAY,KAAK,YAAY,IAAI,QAAQ;AAC/C,eAAO,YAAY,EAAE,QAAQ,WAAW,OAAO,MAAM,IAAI;AAAA,MAC3D;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,oBACZ,MACA,gBACA,QACA,SAAiC,CAAC,GAClC,QACe;AACf,UAAM,WAAW,KAAK,YAAY,MAAM,MAAM;AAG9C,QAAI,KAAK,SAAS,aAAa,IAAI,QAAQ,GAAG;AAC5C;AAAA,IACF;AAEA,SAAK,SAAS,aAAa,IAAI,QAAQ;AAEvC,QAAI;AACF,YAAM,KAAK,aAAa,MAAM,gBAAgB,QAAQ,QAAQ,MAAM;AACpE,WAAK,SAAS,iBAAiB,IAAI,UAAU,KAAK,IAAI,CAAC;AAAA,IACzD,UAAE;AACA,WAAK,SAAS,aAAa,OAAO,QAAQ;AAAA,IAC5C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WACJ,MACA,gBACA,QACA,SAAiC,CAAC,GAClC,QACmD;AACnD,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,aAAa,MAAM,gBAAgB,QAAQ,QAAQ,MAAM;AACnF,aAAO,EAAE,aAAa,OAAO,SAAS,OAAO,OAAO,OAAO,QAAQ;AAAA,IACrE,SAAS,OAAO;AACd,aAAO;AAAA,QACL,aAAa;AAAA,QACb,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cACJ,OACA,gBACA,QACA,cAAsB,GACK;AAC3B,UAAM,UAA4B,CAAC;AACnC,UAAM,QAAQ,CAAC,GAAG,KAAK;AAEvB,UAAM,SAAS,YAAY;AACzB,aAAO,MAAM,SAAS,GAAG;AACvB,cAAM,OAAO,MAAM,MAAM;AACzB,YAAI,CAAC,KAAM;AAEX,cAAM,UAAU,KAAK,UAAU,KAAK,MAAM;AAC1C,cAAM,SAAS,MAAM,KAAK;AAAA,UACxB;AAAA,UACA;AAAA,UACA;AAAA,UACA,KAAK;AAAA,UACL,KAAK;AAAA,QACP;AACA,gBAAQ,KAAK,MAAM;AAAA,MACrB;AAAA,IACF;AAGA,UAAM,UAAU,MAAM,KAAK,IAAI,aAAa,MAAM,MAAM,CAAC,EACtD,KAAK,IAAI,EACT,IAAI,MAAM,OAAO,CAAC;AAErB,UAAM,QAAQ,IAAI,OAAO;AAEzB,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,QAAmD;AACnE,WAAO,MAAM,OAAO,OAAO,MAAM,EAC9B,IAAI,OAAK,MAAM,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC,EAC3C,KAAK,GAAG;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,KAAa,MAAwB;AAE1D,QAAI,KAAK,YAAY,QAAQ,KAAK,OAAO,oBAAoB;AAC3D,YAAM,WAAW,KAAK,YAAY,KAAK,EAAE,KAAK,EAAE;AAChD,UAAI,UAAU;AACZ,aAAK,YAAY,OAAO,QAAQ;AAAA,MAClC;AAAA,IACF;AAGA,SAAK,YAAY,OAAO,GAAG;AAC3B,SAAK,YAAY,IAAI,KAAK,IAAI;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,UAAkB,MAAwB;AAC5D,QAAI;AACF,YAAM,WAAW,KAAK,iBAAiB,QAAQ;AAC/C,YAAM,MAAM,QAAQ,QAAQ;AAC5B,UAAI,CAAC,WAAW,GAAG,GAAG;AACpB,kBAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,MACpC;AACA,oBAAc,UAAU,KAAK,UAAU,IAAI,CAAC;AAAA,IAC9C,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,UAAqC;AACxD,QAAI;AACF,YAAM,WAAW,KAAK,iBAAiB,QAAQ;AAC/C,UAAI,WAAW,QAAQ,GAAG;AACxB,cAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,eAAO,KAAK,MAAM,OAAO;AAAA,MAC3B;AAAA,IACF,QAAQ;AAAA,IAER;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,aAAmB;AACjB,SAAK,YAAY,MAAM;AAGvB,eAAW,WAAW,KAAK,SAAS,UAAU,OAAO,GAAG;AACtD,mBAAa,OAAO;AAAA,IACtB;AACA,SAAK,SAAS,UAAU,MAAM;AAC9B,SAAK,SAAS,iBAAiB,MAAM;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,WAKE;AACA,QAAI,YAAY;AAChB,QAAI;AACF,UAAI,WAAW,KAAK,aAAa,GAAG;AAClC,oBAAY,YAAY,KAAK,aAAa,EACvC,OAAO,OAAK,EAAE,SAAS,OAAO,CAAC,EAAE;AAAA,MACtC;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,WAAO;AAAA,MACL,aAAa,KAAK,YAAY;AAAA,MAC9B;AAAA,MACA,cAAc,KAAK,SAAS,aAAa;AAAA,MACzC,mBAAmB,OAAO;AAAA,QACxB,MAAM,KAAK,KAAK,SAAS,iBAAiB,QAAQ,CAAC,EAChD,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB;AACnB,QAAI,SAAS;AAEb,QAAI;AACF,UAAI,CAAC,WAAW,KAAK,aAAa,EAAG,QAAO;AAE5C,YAAM,QAAQ,YAAY,KAAK,aAAa,EACzC,OAAO,OAAK,EAAE,SAAS,OAAO,CAAC;AAElC,iBAAW,QAAQ,OAAO;AACxB,cAAM,WAAW,KAAK,KAAK,eAAe,IAAI;AAC9C,YAAI;AACF,gBAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,gBAAM,OAAmB,KAAK,MAAM,OAAO;AAE3C,cAAI,KAAK,oBAAoB,QAAQ,KAAK,IAAI,IAAI,KAAK,iBAAiB;AACtE,uBAAW,QAAQ;AACnB;AAAA,UACF;AAAA,QACF,QAAQ;AAEN,qBAAW,QAAQ;AACnB;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,WAAO;AAAA,EACT;AACF;AASO,SAAS,kBACd,IACqC;AACrC,SAAO,YAAY;AACjB,UAAM,SAAS,MAAM,GAAG;AACxB,WAAO;AAAA,EACT;AACF;AAKO,SAAS,kBACd,IAC0E;AAC1E,SAAO,OAAO,QAAQ;AACpB,UAAM,SAAS,MAAM,GAAG,GAAG;AAC3B,WAAO;AAAA,EACT;AACF;AAMA,IAAI,YAA8B;AAK3B,SAAS,aAAa,QAAwC;AACnE,MAAI,CAAC,WAAW;AACd,gBAAY,IAAI,UAAU,MAAM;AAAA,EAClC;AACA,SAAO;AACT;AAKO,SAAS,aAAa,QAAuC;AAClE,cAAY,IAAI,UAAU,MAAM;AAChC,SAAO;AACT;AAgBO,SAAS,iBAAiB,SAA4B;AAC3D,QAAM,SAAS,aAAa;AAE5B,SAAO,OAAO,QAAoC;AAChD,UAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,UAAM,OAAO,IAAI;AAGjB,UAAM,SAAiC,CAAC;AAGxC,UAAM,SAAS,MAAM,OAAO;AAAA,MAC1B;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,QAAQ,YAAY;AAAA,IACtB;AAEA,QAAI,CAAC,QAAQ;AACX,aAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,IAAI,CAAC;AAAA,IAClD;AAGA,UAAM,cAAc,IAAI,QAAQ,IAAI,eAAe;AACnD,QAAI,gBAAgB,OAAO,OAAO,MAAM;AACtC,aAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3C;AAGA,WAAO,IAAI,SAAS,OAAO,OAAO,MAAM;AAAA,MACtC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,QAAQ,OAAO,OAAO;AAAA,QACtB,qBAAqB,IAAI,KAAK,OAAO,OAAO,WAAW,EAAE,YAAY;AAAA,QACrE,iBAAiB,OAAO,QAAQ,SAAS;AAAA,QACzC,GAAG,OAAO,OAAO;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAeO,SAAS,wBAAwB,SAA+B;AACrE,QAAM,SAAS,aAAa;AAE5B,SAAO,OAAO,QAAoC;AAChD,UAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAG3B,QAAI,QAAQ,QAAQ;AAClB,YAAM,iBAAiB,IAAI,aAAa,IAAI,QAAQ;AACpD,UAAI,mBAAmB,QAAQ,QAAQ;AACrC,eAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,gBAAgB,CAAC,GAAG;AAAA,UAC9D,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAChD,CAAC;AAAA,MACH;AAAA,IACF;AAGA,UAAM,mBAAmB,IAAI,aAAa,IAAI,MAAM;AACpD,QAAI,CAAC,kBAAkB;AACrB,aAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,yBAAyB,CAAC,GAAG;AAAA,QACvE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAChD,CAAC;AAAA,IACH;AAGA,UAAM,SAAS,MAAM,OAAO;AAAA,MAC1B;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAEA,WAAO,IAAI,SAAS,KAAK,UAAU,MAAM,GAAG;AAAA,MAC1C,QAAQ,OAAO,cAAc,MAAM;AAAA,MACnC,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAChD,CAAC;AAAA,EACH;AACF;AAMO,IAAM,MAAM;AAAA,EACjB,QAAQ;AAAA,EACR,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AACF;AAEA,IAAO,cAAQ;","names":[]}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@float.js/core",
3
+ "version": "2.0.1",
4
+ "description": "Float.js Core - Ultra Modern Web Framework",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "float": "./dist/cli/index.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./server": {
17
+ "types": "./dist/server/index.d.ts",
18
+ "import": "./dist/server/index.js"
19
+ },
20
+ "./router": {
21
+ "types": "./dist/router/index.d.ts",
22
+ "import": "./dist/router/index.js"
23
+ }
24
+ },
25
+ "scripts": {
26
+ "dev": "tsup --watch",
27
+ "build": "tsup",
28
+ "test": "vitest",
29
+ "prepublishOnly": "pnpm build"
30
+ },
31
+ "dependencies": {
32
+ "cac": "^6.7.14",
33
+ "chokidar": "^3.5.3",
34
+ "esbuild": "^0.20.0",
35
+ "fast-glob": "^3.3.2",
36
+ "mime-types": "^2.1.35",
37
+ "picocolors": "^1.0.0",
38
+ "ws": "^8.19.0"
39
+ },
40
+ "peerDependencies": {
41
+ "react": "^18.2.0",
42
+ "react-dom": "^18.2.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/mime-types": "^2.1.4",
46
+ "@types/node": "^20.10.0",
47
+ "@types/react": "^18.2.0",
48
+ "@types/react-dom": "^18.2.0",
49
+ "@types/ws": "^8.18.1",
50
+ "react": "^18.2.0",
51
+ "react-dom": "^18.2.0",
52
+ "tsup": "^8.0.0",
53
+ "typescript": "^5.3.0",
54
+ "vitest": "^1.0.0"
55
+ },
56
+ "files": [
57
+ "dist"
58
+ ],
59
+ "keywords": [
60
+ "float",
61
+ "framework",
62
+ "ssr",
63
+ "ssg",
64
+ "react",
65
+ "modern",
66
+ "edge"
67
+ ],
68
+ "license": "MIT"
69
+ }