@finesoft/front 0.1.12 → 0.1.14

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.cjs CHANGED
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
8
11
  var __export = (target, all) => {
9
12
  for (var name in all)
10
13
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -27,75 +30,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
30
  ));
28
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
32
 
30
- // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- ACTION_KINDS: () => ACTION_KINDS,
34
- ActionDispatcher: () => ActionDispatcher,
35
- BaseController: () => BaseController,
36
- BaseLogger: () => BaseLogger,
37
- CompositeLogger: () => CompositeLogger,
38
- CompositeLoggerFactory: () => CompositeLoggerFactory,
39
- ConsoleLogger: () => ConsoleLogger,
40
- ConsoleLoggerFactory: () => ConsoleLoggerFactory,
41
- Container: () => Container,
42
- DEP_KEYS: () => DEP_KEYS,
43
- Framework: () => Framework,
44
- History: () => History,
45
- HttpClient: () => HttpClient,
46
- HttpError: () => HttpError,
47
- IntentDispatcher: () => IntentDispatcher,
48
- LruMap: () => LruMap,
49
- PrefetchedIntents: () => PrefetchedIntents,
50
- Router: () => Router,
51
- SSR_PLACEHOLDERS: () => SSR_PLACEHOLDERS,
52
- buildUrl: () => buildUrl,
53
- createPrefetchedIntentsFromDom: () => createPrefetchedIntentsFromDom,
54
- createSSRApp: () => createSSRApp,
55
- createSSRRender: () => createSSRRender,
56
- createServer: () => createServer,
57
- defineRoutes: () => defineRoutes,
58
- deserializeServerData: () => deserializeServerData,
59
- detectRuntime: () => detectRuntime,
60
- generateUuid: () => generateUuid,
61
- getBaseUrl: () => getBaseUrl,
62
- injectSSRContent: () => injectSSRContent,
63
- isCompoundAction: () => isCompoundAction,
64
- isExternalUrlAction: () => isExternalUrlAction,
65
- isFlowAction: () => isFlowAction,
66
- isNone: () => isNone,
67
- isSome: () => isSome,
68
- makeDependencies: () => makeDependencies,
69
- makeExternalUrlAction: () => makeExternalUrlAction,
70
- makeFlowAction: () => makeFlowAction,
71
- mapEach: () => mapEach,
72
- parseAcceptLanguage: () => parseAcceptLanguage,
73
- pipe: () => pipe,
74
- pipeAsync: () => pipeAsync,
75
- registerActionHandlers: () => registerActionHandlers,
76
- registerExternalUrlHandler: () => registerExternalUrlHandler,
77
- registerFlowActionHandler: () => registerFlowActionHandler,
78
- removeHost: () => removeHost,
79
- removeQueryParams: () => removeQueryParams,
80
- removeScheme: () => removeScheme,
81
- resetFilterCache: () => resetFilterCache,
82
- resolveRoot: () => resolveRoot,
83
- serializeServerData: () => serializeServerData,
84
- shouldLog: () => shouldLog,
85
- ssrRender: () => ssrRender,
86
- stableStringify: () => stableStringify,
87
- startBrowserApp: () => startBrowserApp,
88
- startServer: () => startServer,
89
- tryScroll: () => tryScroll
90
- });
91
- module.exports = __toCommonJS(index_exports);
92
-
93
33
  // ../core/src/actions/types.ts
94
- var ACTION_KINDS = {
95
- FLOW: "flow",
96
- EXTERNAL_URL: "externalUrl",
97
- COMPOUND: "compound"
98
- };
99
34
  function isFlowAction(action) {
100
35
  return action.kind === ACTION_KINDS.FLOW;
101
36
  }
@@ -111,119 +46,144 @@ function makeFlowAction(url, presentationContext) {
111
46
  function makeExternalUrlAction(url) {
112
47
  return { kind: ACTION_KINDS.EXTERNAL_URL, url };
113
48
  }
49
+ var ACTION_KINDS;
50
+ var init_types = __esm({
51
+ "../core/src/actions/types.ts"() {
52
+ "use strict";
53
+ ACTION_KINDS = {
54
+ FLOW: "flow",
55
+ EXTERNAL_URL: "externalUrl",
56
+ COMPOUND: "compound"
57
+ };
58
+ }
59
+ });
114
60
 
115
61
  // ../core/src/actions/dispatcher.ts
116
- var ActionDispatcher = class {
117
- handlers = /* @__PURE__ */ new Map();
118
- wiredActions = /* @__PURE__ */ new Set();
119
- /** 注册指定 kind 的 handler(防止重复注册) */
120
- onAction(kind, handler) {
121
- if (this.wiredActions.has(kind)) {
122
- console.warn(
123
- `[ActionDispatcher] kind="${kind}" already registered, skipping`
124
- );
125
- return;
126
- }
127
- this.wiredActions.add(kind);
128
- this.handlers.set(kind, handler);
129
- }
130
- /** 执行一个 Action(CompoundAction 递归展开) */
131
- async perform(action) {
132
- if (isCompoundAction(action)) {
133
- for (const subAction of action.actions) {
134
- await this.perform(subAction);
62
+ var ActionDispatcher;
63
+ var init_dispatcher = __esm({
64
+ "../core/src/actions/dispatcher.ts"() {
65
+ "use strict";
66
+ init_types();
67
+ ActionDispatcher = class {
68
+ handlers = /* @__PURE__ */ new Map();
69
+ wiredActions = /* @__PURE__ */ new Set();
70
+ /** 注册指定 kind 的 handler(防止重复注册) */
71
+ onAction(kind, handler) {
72
+ if (this.wiredActions.has(kind)) {
73
+ console.warn(
74
+ `[ActionDispatcher] kind="${kind}" already registered, skipping`
75
+ );
76
+ return;
77
+ }
78
+ this.wiredActions.add(kind);
79
+ this.handlers.set(kind, handler);
135
80
  }
136
- return;
137
- }
138
- const handler = this.handlers.get(action.kind);
139
- if (!handler) {
140
- console.warn(
141
- `[ActionDispatcher] No handler for kind="${action.kind}"`
142
- );
143
- return;
144
- }
145
- await handler(action);
81
+ /** 执行一个 Action(CompoundAction 递归展开) */
82
+ async perform(action) {
83
+ if (isCompoundAction(action)) {
84
+ for (const subAction of action.actions) {
85
+ await this.perform(subAction);
86
+ }
87
+ return;
88
+ }
89
+ const handler = this.handlers.get(action.kind);
90
+ if (!handler) {
91
+ console.warn(
92
+ `[ActionDispatcher] No handler for kind="${action.kind}"`
93
+ );
94
+ return;
95
+ }
96
+ await handler(action);
97
+ }
98
+ };
146
99
  }
147
- };
100
+ });
148
101
 
149
102
  // ../core/src/intents/dispatcher.ts
150
- var IntentDispatcher = class {
151
- controllers = /* @__PURE__ */ new Map();
152
- /** 注册一个 IntentController */
153
- register(controller) {
154
- this.controllers.set(controller.intentId, controller);
155
- }
156
- /** 分发 Intent 到对应 Controller */
157
- async dispatch(intent, container) {
158
- const controller = this.controllers.get(intent.id);
159
- if (!controller) {
160
- throw new Error(
161
- `[IntentDispatcher] No controller for "${intent.id}". Registered: [${Array.from(this.controllers.keys()).join(", ")}]`
162
- );
163
- }
164
- return controller.perform(intent, container);
165
- }
166
- /** 检查是否已注册某个 Intent */
167
- has(intentId) {
168
- return this.controllers.has(intentId);
103
+ var IntentDispatcher;
104
+ var init_dispatcher2 = __esm({
105
+ "../core/src/intents/dispatcher.ts"() {
106
+ "use strict";
107
+ IntentDispatcher = class {
108
+ controllers = /* @__PURE__ */ new Map();
109
+ /** 注册一个 IntentController */
110
+ register(controller) {
111
+ this.controllers.set(controller.intentId, controller);
112
+ }
113
+ /** 分发 Intent 到对应 Controller */
114
+ async dispatch(intent, container) {
115
+ const controller = this.controllers.get(intent.id);
116
+ if (!controller) {
117
+ throw new Error(
118
+ `[IntentDispatcher] No controller for "${intent.id}". Registered: [${Array.from(this.controllers.keys()).join(", ")}]`
119
+ );
120
+ }
121
+ return controller.perform(intent, container);
122
+ }
123
+ /** 检查是否已注册某个 Intent */
124
+ has(intentId) {
125
+ return this.controllers.has(intentId);
126
+ }
127
+ };
169
128
  }
170
- };
129
+ });
171
130
 
172
131
  // ../core/src/dependencies/container.ts
173
- var Container = class {
174
- registrations = /* @__PURE__ */ new Map();
175
- /** 注册依赖(默认单例) */
176
- register(key, factory, singleton = true) {
177
- this.registrations.set(key, { factory, singleton });
178
- return this;
179
- }
180
- /** 解析依赖 */
181
- resolve(key) {
182
- const reg = this.registrations.get(key);
183
- if (!reg) {
184
- throw new Error(`[Container] No registration for key: "${key}"`);
185
- }
186
- if (reg.singleton) {
187
- if (reg.instance === void 0) {
188
- reg.instance = reg.factory();
132
+ var Container;
133
+ var init_container = __esm({
134
+ "../core/src/dependencies/container.ts"() {
135
+ "use strict";
136
+ Container = class {
137
+ registrations = /* @__PURE__ */ new Map();
138
+ /** 注册依赖(默认单例) */
139
+ register(key, factory, singleton = true) {
140
+ this.registrations.set(key, { factory, singleton });
141
+ return this;
189
142
  }
190
- return reg.instance;
191
- }
192
- return reg.factory();
193
- }
194
- /** 检查是否已注册 */
195
- has(key) {
196
- return this.registrations.has(key);
197
- }
198
- /** 销毁容器,清除所有缓存 */
199
- dispose() {
200
- for (const reg of this.registrations.values()) {
201
- reg.instance = void 0;
202
- }
203
- this.registrations.clear();
143
+ /** 解析依赖 */
144
+ resolve(key) {
145
+ const reg = this.registrations.get(key);
146
+ if (!reg) {
147
+ throw new Error(`[Container] No registration for key: "${key}"`);
148
+ }
149
+ if (reg.singleton) {
150
+ if (reg.instance === void 0) {
151
+ reg.instance = reg.factory();
152
+ }
153
+ return reg.instance;
154
+ }
155
+ return reg.factory();
156
+ }
157
+ /** 检查是否已注册 */
158
+ has(key) {
159
+ return this.registrations.has(key);
160
+ }
161
+ /** 销毁容器,清除所有缓存 */
162
+ dispose() {
163
+ for (const reg of this.registrations.values()) {
164
+ reg.instance = void 0;
165
+ }
166
+ this.registrations.clear();
167
+ }
168
+ };
204
169
  }
205
- };
170
+ });
206
171
 
207
172
  // ../core/src/logger/base.ts
208
- var BaseLogger = class {
209
- category;
210
- constructor(category) {
211
- this.category = category;
173
+ var BaseLogger;
174
+ var init_base = __esm({
175
+ "../core/src/logger/base.ts"() {
176
+ "use strict";
177
+ BaseLogger = class {
178
+ category;
179
+ constructor(category) {
180
+ this.category = category;
181
+ }
182
+ };
212
183
  }
213
- };
184
+ });
214
185
 
215
186
  // ../core/src/logger/local-storage-filter.ts
216
- var LEVEL_TO_NUM = {
217
- "*": 4,
218
- debug: 4,
219
- info: 3,
220
- warn: 2,
221
- error: 1,
222
- off: 0,
223
- "": 0
224
- };
225
- var cachedRules;
226
- var cachedRaw;
227
187
  function parseRules() {
228
188
  if (typeof globalThis.localStorage === "undefined") {
229
189
  return {};
@@ -272,94 +232,62 @@ function resetFilterCache() {
272
232
  cachedRules = void 0;
273
233
  cachedRaw = void 0;
274
234
  }
235
+ var LEVEL_TO_NUM, cachedRules, cachedRaw;
236
+ var init_local_storage_filter = __esm({
237
+ "../core/src/logger/local-storage-filter.ts"() {
238
+ "use strict";
239
+ LEVEL_TO_NUM = {
240
+ "*": 4,
241
+ debug: 4,
242
+ info: 3,
243
+ warn: 2,
244
+ error: 1,
245
+ off: 0,
246
+ "": 0
247
+ };
248
+ }
249
+ });
275
250
 
276
251
  // ../core/src/logger/console.ts
277
- var ConsoleLogger = class extends BaseLogger {
278
- debug(...args) {
279
- if (shouldLog(this.category, "debug")) {
280
- console.debug(`[${this.category}]`, ...args);
281
- }
282
- return "";
283
- }
284
- info(...args) {
285
- if (shouldLog(this.category, "info")) {
286
- console.info(`[${this.category}]`, ...args);
287
- }
288
- return "";
289
- }
290
- warn(...args) {
291
- if (shouldLog(this.category, "warn")) {
292
- console.warn(`[${this.category}]`, ...args);
293
- }
294
- return "";
295
- }
296
- error(...args) {
297
- console.error(`[${this.category}]`, ...args);
298
- return "";
299
- }
300
- };
301
- var ConsoleLoggerFactory = class {
302
- loggerFor(category) {
303
- return new ConsoleLogger(category);
252
+ var ConsoleLogger, ConsoleLoggerFactory;
253
+ var init_console = __esm({
254
+ "../core/src/logger/console.ts"() {
255
+ "use strict";
256
+ init_base();
257
+ init_local_storage_filter();
258
+ ConsoleLogger = class extends BaseLogger {
259
+ debug(...args) {
260
+ if (shouldLog(this.category, "debug")) {
261
+ console.debug(`[${this.category}]`, ...args);
262
+ }
263
+ return "";
264
+ }
265
+ info(...args) {
266
+ if (shouldLog(this.category, "info")) {
267
+ console.info(`[${this.category}]`, ...args);
268
+ }
269
+ return "";
270
+ }
271
+ warn(...args) {
272
+ if (shouldLog(this.category, "warn")) {
273
+ console.warn(`[${this.category}]`, ...args);
274
+ }
275
+ return "";
276
+ }
277
+ error(...args) {
278
+ console.error(`[${this.category}]`, ...args);
279
+ return "";
280
+ }
281
+ };
282
+ ConsoleLoggerFactory = class {
283
+ loggerFor(category) {
284
+ return new ConsoleLogger(category);
285
+ }
286
+ };
304
287
  }
305
- };
288
+ });
306
289
 
307
290
  // ../core/src/dependencies/make-dependencies.ts
308
- var DEP_KEYS = {
309
- LOGGER: "logger",
310
- LOGGER_FACTORY: "loggerFactory",
311
- NET: "net",
312
- LOCALE: "locale",
313
- STORAGE: "storage",
314
- FEATURE_FLAGS: "featureFlags",
315
- METRICS: "metrics",
316
- FETCH: "fetch"
317
- };
318
- var DefaultLocale = class {
319
- language = "en";
320
- storefront = "us";
321
- setActiveLocale(language, storefront) {
322
- this.language = language;
323
- this.storefront = storefront;
324
- }
325
- };
326
- var MemoryStorage = class {
327
- store = /* @__PURE__ */ new Map();
328
- get(key) {
329
- return this.store.get(key);
330
- }
331
- set(key, value) {
332
- this.store.set(key, value);
333
- }
334
- delete(key) {
335
- this.store.delete(key);
336
- }
337
- };
338
- var DefaultFeatureFlags = class {
339
- flags;
340
- constructor(flags = {}) {
341
- this.flags = flags;
342
- }
343
- isEnabled(key) {
344
- return this.flags[key] === true;
345
- }
346
- getString(key) {
347
- const v = this.flags[key];
348
- return typeof v === "string" ? v : void 0;
349
- }
350
- getNumber(key) {
351
- const v = this.flags[key];
352
- return typeof v === "number" ? v : void 0;
353
- }
354
- };
355
- var ConsoleMetrics = class {
356
- recordPageView(page, fields) {
357
- console.info(`[Metrics:PageView] ${page}`, fields ?? "");
358
- }
359
- recordEvent(name, fields) {
360
- console.info(`[Metrics:Event] ${name}`, fields ?? "");
361
- }
362
- };
363
291
  function makeDependencies(container, options = {}) {
364
292
  const {
365
293
  fetch: fetchFn = globalThis.fetch?.bind(globalThis),
@@ -395,479 +323,983 @@ function makeDependencies(container, options = {}) {
395
323
  );
396
324
  container.register(DEP_KEYS.FETCH, () => fetchFn);
397
325
  }
326
+ var DEP_KEYS, DefaultLocale, MemoryStorage, DefaultFeatureFlags, ConsoleMetrics;
327
+ var init_make_dependencies = __esm({
328
+ "../core/src/dependencies/make-dependencies.ts"() {
329
+ "use strict";
330
+ init_console();
331
+ DEP_KEYS = {
332
+ LOGGER: "logger",
333
+ LOGGER_FACTORY: "loggerFactory",
334
+ NET: "net",
335
+ LOCALE: "locale",
336
+ STORAGE: "storage",
337
+ FEATURE_FLAGS: "featureFlags",
338
+ METRICS: "metrics",
339
+ FETCH: "fetch"
340
+ };
341
+ DefaultLocale = class {
342
+ language = "en";
343
+ storefront = "us";
344
+ setActiveLocale(language, storefront) {
345
+ this.language = language;
346
+ this.storefront = storefront;
347
+ }
348
+ };
349
+ MemoryStorage = class {
350
+ store = /* @__PURE__ */ new Map();
351
+ get(key) {
352
+ return this.store.get(key);
353
+ }
354
+ set(key, value) {
355
+ this.store.set(key, value);
356
+ }
357
+ delete(key) {
358
+ this.store.delete(key);
359
+ }
360
+ };
361
+ DefaultFeatureFlags = class {
362
+ flags;
363
+ constructor(flags = {}) {
364
+ this.flags = flags;
365
+ }
366
+ isEnabled(key) {
367
+ return this.flags[key] === true;
368
+ }
369
+ getString(key) {
370
+ const v = this.flags[key];
371
+ return typeof v === "string" ? v : void 0;
372
+ }
373
+ getNumber(key) {
374
+ const v = this.flags[key];
375
+ return typeof v === "number" ? v : void 0;
376
+ }
377
+ };
378
+ ConsoleMetrics = class {
379
+ recordPageView(page, fields) {
380
+ console.info(`[Metrics:PageView] ${page}`, fields ?? "");
381
+ }
382
+ recordEvent(name, fields) {
383
+ console.info(`[Metrics:Event] ${name}`, fields ?? "");
384
+ }
385
+ };
386
+ }
387
+ });
398
388
 
399
389
  // ../core/src/router/router.ts
400
- var Router = class {
401
- routes = [];
402
- /** 添加路由规则 */
403
- add(pattern, intentId) {
404
- const paramNames = [];
405
- const regexStr = pattern.replace(
406
- /\/:(\w+)(\?)?/g,
407
- (_, name, optional) => {
408
- paramNames.push(name);
409
- return optional ? "(?:/([^/]+))?" : "/([^/]+)";
410
- }
411
- );
412
- this.routes.push({
413
- pattern,
414
- intentId,
415
- regex: new RegExp(`^${regexStr}/?$`),
416
- paramNames
417
- });
418
- return this;
419
- }
420
- /** 解析 URL → RouteMatch */
421
- resolve(urlOrPath) {
422
- const path = this.extractPath(urlOrPath);
423
- const queryParams = this.extractQueryParams(urlOrPath);
424
- for (const route of this.routes) {
425
- const match = path.match(route.regex);
426
- if (match) {
427
- const params = {};
428
- route.paramNames.forEach((name, index) => {
429
- const value = match[index + 1];
430
- if (value) params[name] = value;
390
+ var Router;
391
+ var init_router = __esm({
392
+ "../core/src/router/router.ts"() {
393
+ "use strict";
394
+ init_types();
395
+ Router = class {
396
+ routes = [];
397
+ /** 添加路由规则 */
398
+ add(pattern, intentId) {
399
+ const paramNames = [];
400
+ const regexStr = pattern.replace(
401
+ /\/:(\w+)(\?)?/g,
402
+ (_, name, optional) => {
403
+ paramNames.push(name);
404
+ return optional ? "(?:/([^/]+))?" : "/([^/]+)";
405
+ }
406
+ );
407
+ this.routes.push({
408
+ pattern,
409
+ intentId,
410
+ regex: new RegExp(`^${regexStr}/?$`),
411
+ paramNames
431
412
  });
432
- for (const [k, v] of Object.entries(queryParams)) {
433
- if (!(k in params)) params[k] = v;
413
+ return this;
414
+ }
415
+ /** 解析 URL → RouteMatch */
416
+ resolve(urlOrPath) {
417
+ const path = this.extractPath(urlOrPath);
418
+ const queryParams = this.extractQueryParams(urlOrPath);
419
+ for (const route of this.routes) {
420
+ const match = path.match(route.regex);
421
+ if (match) {
422
+ const params = {};
423
+ route.paramNames.forEach((name, index) => {
424
+ const value = match[index + 1];
425
+ if (value) params[name] = value;
426
+ });
427
+ for (const [k, v] of Object.entries(queryParams)) {
428
+ if (!(k in params)) params[k] = v;
429
+ }
430
+ return {
431
+ intent: { id: route.intentId, params },
432
+ action: makeFlowAction(urlOrPath)
433
+ };
434
+ }
435
+ }
436
+ return null;
437
+ }
438
+ /** 获取所有已注册的路由 */
439
+ getRoutes() {
440
+ return this.routes.map((r) => `${r.pattern} \u2192 ${r.intentId}`);
441
+ }
442
+ extractPath(url) {
443
+ try {
444
+ const parsed = new URL(url, "http://localhost");
445
+ return parsed.pathname;
446
+ } catch {
447
+ return url.split("?")[0].split("#")[0];
448
+ }
449
+ }
450
+ extractQueryParams(url) {
451
+ try {
452
+ const parsed = new URL(url, "http://localhost");
453
+ const params = {};
454
+ parsed.searchParams.forEach((v, k) => {
455
+ params[k] = v;
456
+ });
457
+ return params;
458
+ } catch {
459
+ return {};
460
+ }
461
+ }
462
+ };
463
+ }
464
+ });
465
+
466
+ // ../core/src/logger/composite.ts
467
+ var CompositeLoggerFactory, CompositeLogger;
468
+ var init_composite = __esm({
469
+ "../core/src/logger/composite.ts"() {
470
+ "use strict";
471
+ CompositeLoggerFactory = class {
472
+ constructor(factories) {
473
+ this.factories = factories;
474
+ }
475
+ loggerFor(name) {
476
+ return new CompositeLogger(
477
+ this.factories.map((f) => f.loggerFor(name))
478
+ );
479
+ }
480
+ };
481
+ CompositeLogger = class {
482
+ constructor(loggers) {
483
+ this.loggers = loggers;
484
+ }
485
+ debug(...args) {
486
+ return this.callAll("debug", args);
487
+ }
488
+ info(...args) {
489
+ return this.callAll("info", args);
490
+ }
491
+ warn(...args) {
492
+ return this.callAll("warn", args);
493
+ }
494
+ error(...args) {
495
+ return this.callAll("error", args);
496
+ }
497
+ callAll(method, args) {
498
+ for (const logger of this.loggers) {
499
+ logger[method](...args);
500
+ }
501
+ return "";
502
+ }
503
+ };
504
+ }
505
+ });
506
+
507
+ // ../core/src/prefetched-intents/stable-stringify.ts
508
+ function stableStringify(obj) {
509
+ if (obj === null || obj === void 0) return String(obj);
510
+ if (typeof obj !== "object") return JSON.stringify(obj);
511
+ if (Array.isArray(obj)) {
512
+ return "[" + obj.map(stableStringify).join(",") + "]";
513
+ }
514
+ const keys = Object.keys(obj).sort();
515
+ const parts = keys.filter((k) => obj[k] !== void 0).map(
516
+ (k) => JSON.stringify(k) + ":" + stableStringify(obj[k])
517
+ );
518
+ return "{" + parts.join(",") + "}";
519
+ }
520
+ var init_stable_stringify = __esm({
521
+ "../core/src/prefetched-intents/stable-stringify.ts"() {
522
+ "use strict";
523
+ }
524
+ });
525
+
526
+ // ../core/src/prefetched-intents/prefetched-intents.ts
527
+ var PrefetchedIntents;
528
+ var init_prefetched_intents = __esm({
529
+ "../core/src/prefetched-intents/prefetched-intents.ts"() {
530
+ "use strict";
531
+ init_stable_stringify();
532
+ PrefetchedIntents = class _PrefetchedIntents {
533
+ intents;
534
+ constructor(intents) {
535
+ this.intents = intents;
536
+ }
537
+ /** 从 PrefetchedIntent 数组创建缓存实例 */
538
+ static fromArray(items) {
539
+ const map = /* @__PURE__ */ new Map();
540
+ for (const item of items) {
541
+ if (item.intent && item.data !== void 0) {
542
+ const key = stableStringify(item.intent);
543
+ map.set(key, item.data);
544
+ }
545
+ }
546
+ return new _PrefetchedIntents(map);
547
+ }
548
+ /** 创建空缓存实例 */
549
+ static empty() {
550
+ return new _PrefetchedIntents(/* @__PURE__ */ new Map());
551
+ }
552
+ /**
553
+ * 获取缓存的 Intent 结果(一次性使用)。
554
+ * 命中后从缓存中删除。
555
+ */
556
+ get(intent) {
557
+ const key = stableStringify(intent);
558
+ const data = this.intents.get(key);
559
+ if (data !== void 0) {
560
+ this.intents.delete(key);
561
+ return data;
562
+ }
563
+ return void 0;
564
+ }
565
+ /** 检查缓存中是否有某个 Intent 的数据 */
566
+ has(intent) {
567
+ return this.intents.has(stableStringify(intent));
568
+ }
569
+ /** 缓存中的条目数 */
570
+ get size() {
571
+ return this.intents.size;
572
+ }
573
+ };
574
+ }
575
+ });
576
+
577
+ // ../core/src/framework.ts
578
+ var Framework;
579
+ var init_framework = __esm({
580
+ "../core/src/framework.ts"() {
581
+ "use strict";
582
+ init_dispatcher();
583
+ init_container();
584
+ init_make_dependencies();
585
+ init_dispatcher2();
586
+ init_prefetched_intents();
587
+ init_router();
588
+ Framework = class _Framework {
589
+ container;
590
+ intentDispatcher;
591
+ actionDispatcher;
592
+ router;
593
+ prefetchedIntents;
594
+ constructor(container, prefetchedIntents) {
595
+ this.container = container;
596
+ this.intentDispatcher = new IntentDispatcher();
597
+ this.actionDispatcher = new ActionDispatcher();
598
+ this.router = new Router();
599
+ this.prefetchedIntents = prefetchedIntents;
600
+ }
601
+ /** 创建并初始化 Framework 实例 */
602
+ static create(config = {}) {
603
+ const container = new Container();
604
+ makeDependencies(container, config);
605
+ const fw = new _Framework(
606
+ container,
607
+ config.prefetchedIntents ?? PrefetchedIntents.empty()
608
+ );
609
+ config.setupRoutes?.(fw.router);
610
+ return fw;
611
+ }
612
+ /** 分发 Intent — 获取页面数据 */
613
+ async dispatch(intent) {
614
+ const logger = this.container.resolve(DEP_KEYS.LOGGER);
615
+ const cached = this.prefetchedIntents.get(intent);
616
+ if (cached !== void 0) {
617
+ logger.debug(
618
+ `[Framework] re-using prefetched intent response for: ${intent.id}`,
619
+ intent.params
620
+ );
621
+ return cached;
434
622
  }
435
- return {
436
- intent: { id: route.intentId, params },
437
- action: makeFlowAction(urlOrPath)
623
+ logger.debug(
624
+ `[Framework] dispatch intent: ${intent.id}`,
625
+ intent.params
626
+ );
627
+ return this.intentDispatcher.dispatch(intent, this.container);
628
+ }
629
+ /** 执行 Action — 处理用户交互 */
630
+ async perform(action) {
631
+ const logger = this.container.resolve(DEP_KEYS.LOGGER);
632
+ logger.debug(`[Framework] perform action: ${action.kind}`);
633
+ return this.actionDispatcher.perform(action);
634
+ }
635
+ /** 路由 URL — 将 URL 解析为 Intent + Action */
636
+ routeUrl(url) {
637
+ return this.router.resolve(url);
638
+ }
639
+ /** 记录页面访问事件 */
640
+ didEnterPage(page) {
641
+ const metrics = this.container.resolve(
642
+ DEP_KEYS.METRICS
643
+ );
644
+ metrics.recordPageView(page.pageType, {
645
+ pageId: page.id,
646
+ title: page.title
647
+ });
648
+ }
649
+ /** 注册 Action 处理器 */
650
+ onAction(kind, handler) {
651
+ this.actionDispatcher.onAction(kind, handler);
652
+ }
653
+ /** 注册 Intent Controller */
654
+ registerIntent(controller) {
655
+ this.intentDispatcher.register(controller);
656
+ }
657
+ /** 销毁 Framework 实例 */
658
+ dispose() {
659
+ this.container.dispose();
660
+ }
661
+ };
662
+ }
663
+ });
664
+
665
+ // ../core/src/http/client.ts
666
+ var HttpError, HttpClient;
667
+ var init_client = __esm({
668
+ "../core/src/http/client.ts"() {
669
+ "use strict";
670
+ HttpError = class extends Error {
671
+ constructor(status, statusText, body) {
672
+ super(`HTTP ${status}: ${statusText}`);
673
+ this.status = status;
674
+ this.statusText = statusText;
675
+ this.body = body;
676
+ this.name = "HttpError";
677
+ }
678
+ };
679
+ HttpClient = class {
680
+ baseUrl;
681
+ defaultHeaders;
682
+ fetchFn;
683
+ constructor(config) {
684
+ this.baseUrl = config.baseUrl;
685
+ this.defaultHeaders = config.defaultHeaders ?? {};
686
+ this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
687
+ }
688
+ /** GET 请求,返回解析后的 JSON */
689
+ async get(path, params) {
690
+ return this.request("GET", path, { params });
691
+ }
692
+ /** POST 请求,自动序列化 body 为 JSON */
693
+ async post(path, body, params) {
694
+ return this.request("POST", path, { body, params });
695
+ }
696
+ /** PUT 请求 */
697
+ async put(path, body, params) {
698
+ return this.request("PUT", path, { body, params });
699
+ }
700
+ /** DELETE 请求 */
701
+ async del(path, params) {
702
+ return this.request("DELETE", path, { params });
703
+ }
704
+ /**
705
+ * 底层请求方法 — 子类可覆写以自定义行为
706
+ *
707
+ * 自动处理:
708
+ * - URL 拼接 (baseUrl + path + params)
709
+ * - 默认 headers 合并
710
+ * - JSON body 序列化
711
+ * - 响应 JSON 解析
712
+ * - 非 2xx 状态码抛出 HttpError
713
+ */
714
+ async request(method, path, options) {
715
+ const url = this.buildUrl(path, options?.params);
716
+ const headers = {
717
+ ...this.defaultHeaders,
718
+ ...options?.headers
438
719
  };
720
+ const init = { method, headers };
721
+ if (options?.body !== void 0) {
722
+ headers["Content-Type"] = headers["Content-Type"] ?? "application/json";
723
+ init.body = JSON.stringify(options.body);
724
+ }
725
+ const response = await this.fetchFn(url, init);
726
+ if (!response.ok) {
727
+ const body = await response.text().catch(() => void 0);
728
+ throw new HttpError(response.status, response.statusText, body);
729
+ }
730
+ return response.json();
439
731
  }
440
- }
441
- return null;
732
+ /** 构建完整 URL — 子类可覆写以自定义 URL 拼接逻辑 */
733
+ buildUrl(path, params) {
734
+ const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
735
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
736
+ const url = new URL(`${base}${normalizedPath}`, "http://placeholder");
737
+ if (params) {
738
+ for (const [k, v] of Object.entries(params)) {
739
+ url.searchParams.set(k, v);
740
+ }
741
+ }
742
+ if (this.baseUrl.startsWith("http")) {
743
+ return url.toString();
744
+ }
745
+ return `${url.pathname}${url.search}`;
746
+ }
747
+ };
442
748
  }
443
- /** 获取所有已注册的路由 */
444
- getRoutes() {
445
- return this.routes.map((r) => `${r.pattern} \u2192 ${r.intentId}`);
749
+ });
750
+
751
+ // ../core/src/intents/base-controller.ts
752
+ var BaseController;
753
+ var init_base_controller = __esm({
754
+ "../core/src/intents/base-controller.ts"() {
755
+ "use strict";
756
+ BaseController = class {
757
+ /**
758
+ * 错误回退 — 子类可选覆写
759
+ *
760
+ * 当 execute() 抛出异常时调用。
761
+ * 默认行为: 重新抛出原始错误。
762
+ *
763
+ * @param params - Intent 参数
764
+ * @param error - execute() 抛出的错误
765
+ * @returns 回退数据
766
+ */
767
+ fallback(params, error) {
768
+ throw error;
769
+ }
770
+ /**
771
+ * IntentController.perform() 实现
772
+ *
773
+ * 自动 try/catch → fallback 模式。
774
+ */
775
+ async perform(intent, container) {
776
+ const params = intent.params ?? {};
777
+ try {
778
+ return await this.execute(params, container);
779
+ } catch (e) {
780
+ return this.fallback(
781
+ params,
782
+ e instanceof Error ? e : new Error(String(e))
783
+ );
784
+ }
785
+ }
786
+ };
446
787
  }
447
- extractPath(url) {
448
- try {
449
- const parsed = new URL(url, "http://localhost");
450
- return parsed.pathname;
451
- } catch {
452
- return url.split("?")[0].split("#")[0];
788
+ });
789
+
790
+ // ../core/src/data/mapper.ts
791
+ function pipe(...mappers) {
792
+ return (input) => mappers.reduce((acc, mapper) => mapper(acc), input);
793
+ }
794
+ function pipeAsync(...mappers) {
795
+ return async (input) => {
796
+ let acc = input;
797
+ for (const mapper of mappers) {
798
+ acc = await mapper(acc);
453
799
  }
800
+ return acc;
801
+ };
802
+ }
803
+ function mapEach(mapper) {
804
+ return (items) => items.map(mapper);
805
+ }
806
+ var init_mapper = __esm({
807
+ "../core/src/data/mapper.ts"() {
808
+ "use strict";
454
809
  }
455
- extractQueryParams(url) {
456
- try {
457
- const parsed = new URL(url, "http://localhost");
458
- const params = {};
459
- parsed.searchParams.forEach((v, k) => {
460
- params[k] = v;
461
- });
462
- return params;
463
- } catch {
464
- return {};
810
+ });
811
+
812
+ // ../core/src/bootstrap/define-routes.ts
813
+ function defineRoutes(framework, definitions) {
814
+ const registeredIntents = /* @__PURE__ */ new Set();
815
+ for (const def of definitions) {
816
+ if (def.controller && !registeredIntents.has(def.intentId)) {
817
+ framework.registerIntent(def.controller);
818
+ registeredIntents.add(def.intentId);
465
819
  }
820
+ framework.router.add(def.path, def.intentId);
466
821
  }
467
- };
822
+ }
823
+ var init_define_routes = __esm({
824
+ "../core/src/bootstrap/define-routes.ts"() {
825
+ "use strict";
826
+ }
827
+ });
468
828
 
469
- // ../core/src/logger/composite.ts
470
- var CompositeLoggerFactory = class {
471
- constructor(factories) {
472
- this.factories = factories;
829
+ // ../core/src/utils/lru-map.ts
830
+ var LruMap;
831
+ var init_lru_map = __esm({
832
+ "../core/src/utils/lru-map.ts"() {
833
+ "use strict";
834
+ LruMap = class {
835
+ map = /* @__PURE__ */ new Map();
836
+ capacity;
837
+ constructor(capacity) {
838
+ this.capacity = capacity;
839
+ }
840
+ get(key) {
841
+ const value = this.map.get(key);
842
+ if (value !== void 0) {
843
+ this.map.delete(key);
844
+ this.map.set(key, value);
845
+ }
846
+ return value;
847
+ }
848
+ set(key, value) {
849
+ if (this.map.has(key)) {
850
+ this.map.delete(key);
851
+ } else if (this.map.size >= this.capacity) {
852
+ const oldest = this.map.keys().next().value;
853
+ if (oldest !== void 0) {
854
+ this.map.delete(oldest);
855
+ }
856
+ }
857
+ this.map.set(key, value);
858
+ }
859
+ has(key) {
860
+ return this.map.has(key);
861
+ }
862
+ delete(key) {
863
+ return this.map.delete(key);
864
+ }
865
+ get size() {
866
+ return this.map.size;
867
+ }
868
+ clear() {
869
+ this.map.clear();
870
+ }
871
+ };
473
872
  }
474
- loggerFor(name) {
475
- return new CompositeLogger(
476
- this.factories.map((f) => f.loggerFor(name))
477
- );
873
+ });
874
+
875
+ // ../core/src/utils/optional.ts
876
+ function isSome(value) {
877
+ return value !== null && value !== void 0;
878
+ }
879
+ function isNone(value) {
880
+ return value === null || value === void 0;
881
+ }
882
+ var init_optional = __esm({
883
+ "../core/src/utils/optional.ts"() {
884
+ "use strict";
478
885
  }
479
- };
480
- var CompositeLogger = class {
481
- constructor(loggers) {
482
- this.loggers = loggers;
886
+ });
887
+
888
+ // ../core/src/utils/url.ts
889
+ function removeScheme(url) {
890
+ return url.replace(/^https?:\/\//, "");
891
+ }
892
+ function removeHost(url) {
893
+ try {
894
+ const parsed = new URL(url);
895
+ return parsed.pathname + parsed.search + parsed.hash;
896
+ } catch {
897
+ return url;
898
+ }
899
+ }
900
+ function removeQueryParams(url) {
901
+ return url.split("?")[0];
902
+ }
903
+ function getBaseUrl(url) {
904
+ return url.split("?")[0].split("#")[0];
905
+ }
906
+ function buildUrl(path, params) {
907
+ if (!params) return path;
908
+ const searchParams = new URLSearchParams();
909
+ for (const [key, value] of Object.entries(params)) {
910
+ if (value !== void 0) {
911
+ searchParams.set(key, value);
912
+ }
483
913
  }
484
- debug(...args) {
485
- return this.callAll("debug", args);
914
+ const qs = searchParams.toString();
915
+ return qs ? `${path}?${qs}` : path;
916
+ }
917
+ var init_url = __esm({
918
+ "../core/src/utils/url.ts"() {
919
+ "use strict";
486
920
  }
487
- info(...args) {
488
- return this.callAll("info", args);
921
+ });
922
+
923
+ // ../core/src/utils/uuid.ts
924
+ function generateUuid() {
925
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
926
+ return crypto.randomUUID();
489
927
  }
490
- warn(...args) {
491
- return this.callAll("warn", args);
928
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
929
+ const r = Math.random() * 16 | 0;
930
+ const v = c === "x" ? r : r & 3 | 8;
931
+ return v.toString(16);
932
+ });
933
+ }
934
+ var init_uuid = __esm({
935
+ "../core/src/utils/uuid.ts"() {
936
+ "use strict";
492
937
  }
493
- error(...args) {
494
- return this.callAll("error", args);
938
+ });
939
+
940
+ // ../core/src/index.ts
941
+ var init_src = __esm({
942
+ "../core/src/index.ts"() {
943
+ "use strict";
944
+ init_dispatcher();
945
+ init_types();
946
+ init_dispatcher2();
947
+ init_container();
948
+ init_make_dependencies();
949
+ init_router();
950
+ init_base();
951
+ init_composite();
952
+ init_console();
953
+ init_local_storage_filter();
954
+ init_framework();
955
+ init_prefetched_intents();
956
+ init_stable_stringify();
957
+ init_client();
958
+ init_base_controller();
959
+ init_mapper();
960
+ init_define_routes();
961
+ init_lru_map();
962
+ init_optional();
963
+ init_url();
964
+ init_uuid();
495
965
  }
496
- callAll(method, args) {
497
- for (const logger of this.loggers) {
498
- logger[method](...args);
966
+ });
967
+
968
+ // ../ssr/src/render.ts
969
+ async function ssrRender(options) {
970
+ const { url, frameworkConfig, bootstrap, getErrorPage, renderApp } = options;
971
+ const framework = Framework.create(frameworkConfig);
972
+ bootstrap(framework);
973
+ const parsed = new URL(url, "http://localhost");
974
+ const fullPath = parsed.pathname + parsed.search;
975
+ const match = framework.routeUrl(fullPath);
976
+ let page;
977
+ let serverData = [];
978
+ if (match) {
979
+ try {
980
+ page = await framework.dispatch(match.intent);
981
+ serverData = [{ intent: match.intent, data: page }];
982
+ } catch {
983
+ page = getErrorPage(500, "Internal error");
499
984
  }
500
- return "";
985
+ } else {
986
+ page = getErrorPage(404, "Page not found");
501
987
  }
502
- };
988
+ const result = renderApp(page, framework);
989
+ framework.dispose();
990
+ return {
991
+ html: result.html,
992
+ head: result.head,
993
+ css: result.css,
994
+ serverData
995
+ };
996
+ }
997
+ var init_render = __esm({
998
+ "../ssr/src/render.ts"() {
999
+ "use strict";
1000
+ init_src();
1001
+ }
1002
+ });
503
1003
 
504
- // ../core/src/prefetched-intents/stable-stringify.ts
505
- function stableStringify(obj) {
506
- if (obj === null || obj === void 0) return String(obj);
507
- if (typeof obj !== "object") return JSON.stringify(obj);
508
- if (Array.isArray(obj)) {
509
- return "[" + obj.map(stableStringify).join(",") + "]";
1004
+ // ../ssr/src/create-render.ts
1005
+ function createSSRRender(config) {
1006
+ const { bootstrap, getErrorPage, renderApp, frameworkConfig } = config;
1007
+ return (url, locale) => ssrRender({
1008
+ url,
1009
+ frameworkConfig: frameworkConfig ?? {},
1010
+ bootstrap,
1011
+ getErrorPage,
1012
+ renderApp: (page) => renderApp(page, locale)
1013
+ });
1014
+ }
1015
+ var init_create_render = __esm({
1016
+ "../ssr/src/create-render.ts"() {
1017
+ "use strict";
1018
+ init_render();
510
1019
  }
511
- const keys = Object.keys(obj).sort();
512
- const parts = keys.filter((k) => obj[k] !== void 0).map(
513
- (k) => JSON.stringify(k) + ":" + stableStringify(obj[k])
1020
+ });
1021
+
1022
+ // ../ssr/src/inject.ts
1023
+ function injectSSRContent(options) {
1024
+ const { template, locale, head, css, html, serializedData } = options;
1025
+ const cssTag = css ? `<style>${css}</style>` : "";
1026
+ return template.replace(SSR_PLACEHOLDERS.LANG, locale).replace(SSR_PLACEHOLDERS.HEAD, `${head}
1027
+ ${cssTag}`).replace(SSR_PLACEHOLDERS.BODY, html).replace(
1028
+ SSR_PLACEHOLDERS.DATA,
1029
+ `<script id="serialized-server-data" type="application/json">${serializedData}</script>`
514
1030
  );
515
- return "{" + parts.join(",") + "}";
516
1031
  }
1032
+ var SSR_PLACEHOLDERS;
1033
+ var init_inject = __esm({
1034
+ "../ssr/src/inject.ts"() {
1035
+ "use strict";
1036
+ SSR_PLACEHOLDERS = {
1037
+ LANG: "<!--ssr-lang-->",
1038
+ HEAD: "<!--ssr-head-->",
1039
+ BODY: "<!--ssr-body-->",
1040
+ DATA: "<!--ssr-data-->"
1041
+ };
1042
+ }
1043
+ });
517
1044
 
518
- // ../core/src/prefetched-intents/prefetched-intents.ts
519
- var PrefetchedIntents = class _PrefetchedIntents {
520
- intents;
521
- constructor(intents) {
522
- this.intents = intents;
523
- }
524
- /** 从 PrefetchedIntent 数组创建缓存实例 */
525
- static fromArray(items) {
526
- const map = /* @__PURE__ */ new Map();
527
- for (const item of items) {
528
- if (item.intent && item.data !== void 0) {
529
- const key = stableStringify(item.intent);
530
- map.set(key, item.data);
531
- }
1045
+ // ../ssr/src/server-data.ts
1046
+ function serializeServerData(data) {
1047
+ const json = JSON.stringify(data);
1048
+ return json.replace(
1049
+ HTML_ESCAPE_PATTERN,
1050
+ (match) => HTML_REPLACEMENTS[match] ?? match
1051
+ );
1052
+ }
1053
+ var HTML_REPLACEMENTS, HTML_ESCAPE_PATTERN;
1054
+ var init_server_data = __esm({
1055
+ "../ssr/src/server-data.ts"() {
1056
+ "use strict";
1057
+ HTML_REPLACEMENTS = {
1058
+ "<": "\\u003C",
1059
+ ">": "\\u003E",
1060
+ "/": "\\u002F",
1061
+ "\u2028": "\\u2028",
1062
+ "\u2029": "\\u2029"
1063
+ };
1064
+ HTML_ESCAPE_PATTERN = /[<>/\u2028\u2029]/g;
1065
+ }
1066
+ });
1067
+
1068
+ // ../ssr/src/index.ts
1069
+ var src_exports = {};
1070
+ __export(src_exports, {
1071
+ Framework: () => Framework,
1072
+ SSR_PLACEHOLDERS: () => SSR_PLACEHOLDERS,
1073
+ createSSRRender: () => createSSRRender,
1074
+ injectSSRContent: () => injectSSRContent,
1075
+ serializeServerData: () => serializeServerData,
1076
+ ssrRender: () => ssrRender
1077
+ });
1078
+ var init_src2 = __esm({
1079
+ "../ssr/src/index.ts"() {
1080
+ "use strict";
1081
+ init_create_render();
1082
+ init_inject();
1083
+ init_render();
1084
+ init_server_data();
1085
+ init_src();
1086
+ }
1087
+ });
1088
+
1089
+ // ../server/src/locale.ts
1090
+ var locale_exports = {};
1091
+ __export(locale_exports, {
1092
+ parseAcceptLanguage: () => parseAcceptLanguage
1093
+ });
1094
+ function parseAcceptLanguage(header, supported, fallback) {
1095
+ const effectiveSupported = supported ?? ["zh", "en"];
1096
+ const effectiveFallback = fallback ?? effectiveSupported[0] ?? "en";
1097
+ if (!header) return effectiveFallback;
1098
+ const langs = header.split(",").map((part) => {
1099
+ const [lang, q] = part.trim().split(";q=");
1100
+ return {
1101
+ lang: lang.trim().toLowerCase(),
1102
+ q: q ? parseFloat(q) : 1
1103
+ };
1104
+ }).sort((a, b) => b.q - a.q);
1105
+ for (const { lang } of langs) {
1106
+ const prefix = lang.split("-")[0];
1107
+ if (effectiveSupported.includes(prefix)) {
1108
+ return prefix;
532
1109
  }
533
- return new _PrefetchedIntents(map);
534
- }
535
- /** 创建空缓存实例 */
536
- static empty() {
537
- return new _PrefetchedIntents(/* @__PURE__ */ new Map());
538
- }
539
- /**
540
- * 获取缓存的 Intent 结果(一次性使用)。
541
- * 命中后从缓存中删除。
542
- */
543
- get(intent) {
544
- const key = stableStringify(intent);
545
- const data = this.intents.get(key);
546
- if (data !== void 0) {
547
- this.intents.delete(key);
548
- return data;
1110
+ }
1111
+ return effectiveFallback;
1112
+ }
1113
+ var init_locale = __esm({
1114
+ "../server/src/locale.ts"() {
1115
+ "use strict";
1116
+ }
1117
+ });
1118
+
1119
+ // ../server/src/app.ts
1120
+ var app_exports = {};
1121
+ __export(app_exports, {
1122
+ createSSRApp: () => createSSRApp
1123
+ });
1124
+ function createSSRApp(options) {
1125
+ const {
1126
+ root,
1127
+ vite,
1128
+ isProduction,
1129
+ ssrEntryPath = "/src/ssr.ts",
1130
+ ssrProductionModule,
1131
+ supportedLocales,
1132
+ defaultLocale
1133
+ } = options;
1134
+ const app = new import_hono.Hono();
1135
+ async function readTemplate(url) {
1136
+ if (!isProduction && vite) {
1137
+ const { readFileSync: readFileSync2 } = await import(
1138
+ /* @vite-ignore */
1139
+ "fs"
1140
+ );
1141
+ const { resolve: resolve2 } = await import(
1142
+ /* @vite-ignore */
1143
+ "path"
1144
+ );
1145
+ const raw = readFileSync2(resolve2(root, "index.html"), "utf-8");
1146
+ return vite.transformIndexHtml(url, raw);
549
1147
  }
550
- return void 0;
551
- }
552
- /** 检查缓存中是否有某个 Intent 的数据 */
553
- has(intent) {
554
- return this.intents.has(stableStringify(intent));
555
- }
556
- /** 缓存中的条目数 */
557
- get size() {
558
- return this.intents.size;
559
- }
560
- };
561
-
562
- // ../core/src/framework.ts
563
- var Framework = class _Framework {
564
- container;
565
- intentDispatcher;
566
- actionDispatcher;
567
- router;
568
- prefetchedIntents;
569
- constructor(container, prefetchedIntents) {
570
- this.container = container;
571
- this.intentDispatcher = new IntentDispatcher();
572
- this.actionDispatcher = new ActionDispatcher();
573
- this.router = new Router();
574
- this.prefetchedIntents = prefetchedIntents;
575
- }
576
- /** 创建并初始化 Framework 实例 */
577
- static create(config = {}) {
578
- const container = new Container();
579
- makeDependencies(container, config);
580
- const fw = new _Framework(
581
- container,
582
- config.prefetchedIntents ?? PrefetchedIntents.empty()
583
- );
584
- config.setupRoutes?.(fw.router);
585
- return fw;
586
- }
587
- /** 分发 Intent — 获取页面数据 */
588
- async dispatch(intent) {
589
- const logger = this.container.resolve(DEP_KEYS.LOGGER);
590
- const cached = this.prefetchedIntents.get(intent);
591
- if (cached !== void 0) {
592
- logger.debug(
593
- `[Framework] re-using prefetched intent response for: ${intent.id}`,
594
- intent.params
1148
+ const isDeno = typeof globalThis.Deno !== "undefined";
1149
+ if (isDeno) {
1150
+ return globalThis.Deno.readTextFileSync(
1151
+ new URL("../dist/client/index.html", import_meta.url)
595
1152
  );
596
- return cached;
597
1153
  }
598
- logger.debug(
599
- `[Framework] dispatch intent: ${intent.id}`,
600
- intent.params
1154
+ const { readFileSync } = await import(
1155
+ /* @vite-ignore */
1156
+ "fs"
601
1157
  );
602
- return this.intentDispatcher.dispatch(intent, this.container);
603
- }
604
- /** 执行 Action — 处理用户交互 */
605
- async perform(action) {
606
- const logger = this.container.resolve(DEP_KEYS.LOGGER);
607
- logger.debug(`[Framework] perform action: ${action.kind}`);
608
- return this.actionDispatcher.perform(action);
609
- }
610
- /** 路由 URL — 将 URL 解析为 Intent + Action */
611
- routeUrl(url) {
612
- return this.router.resolve(url);
613
- }
614
- /** 记录页面访问事件 */
615
- didEnterPage(page) {
616
- const metrics = this.container.resolve(
617
- DEP_KEYS.METRICS
1158
+ const { resolve } = await import(
1159
+ /* @vite-ignore */
1160
+ "path"
618
1161
  );
619
- metrics.recordPageView(page.pageType, {
620
- pageId: page.id,
621
- title: page.title
622
- });
623
- }
624
- /** 注册 Action 处理器 */
625
- onAction(kind, handler) {
626
- this.actionDispatcher.onAction(kind, handler);
627
- }
628
- /** 注册 Intent Controller */
629
- registerIntent(controller) {
630
- this.intentDispatcher.register(controller);
631
- }
632
- /** 销毁 Framework 实例 */
633
- dispose() {
634
- this.container.dispose();
635
- }
636
- };
637
-
638
- // ../core/src/http/client.ts
639
- var HttpError = class extends Error {
640
- constructor(status, statusText, body) {
641
- super(`HTTP ${status}: ${statusText}`);
642
- this.status = status;
643
- this.statusText = statusText;
644
- this.body = body;
645
- this.name = "HttpError";
646
- }
647
- };
648
- var HttpClient = class {
649
- baseUrl;
650
- defaultHeaders;
651
- fetchFn;
652
- constructor(config) {
653
- this.baseUrl = config.baseUrl;
654
- this.defaultHeaders = config.defaultHeaders ?? {};
655
- this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
656
- }
657
- /** GET 请求,返回解析后的 JSON */
658
- async get(path, params) {
659
- return this.request("GET", path, { params });
660
- }
661
- /** POST 请求,自动序列化 body 为 JSON */
662
- async post(path, body, params) {
663
- return this.request("POST", path, { body, params });
664
- }
665
- /** PUT 请求 */
666
- async put(path, body, params) {
667
- return this.request("PUT", path, { body, params });
668
- }
669
- /** DELETE 请求 */
670
- async del(path, params) {
671
- return this.request("DELETE", path, { params });
672
- }
673
- /**
674
- * 底层请求方法 — 子类可覆写以自定义行为
675
- *
676
- * 自动处理:
677
- * - URL 拼接 (baseUrl + path + params)
678
- * - 默认 headers 合并
679
- * - JSON body 序列化
680
- * - 响应 JSON 解析
681
- * - 非 2xx 状态码抛出 HttpError
682
- */
683
- async request(method, path, options) {
684
- const url = this.buildUrl(path, options?.params);
685
- const headers = {
686
- ...this.defaultHeaders,
687
- ...options?.headers
688
- };
689
- const init = { method, headers };
690
- if (options?.body !== void 0) {
691
- headers["Content-Type"] = headers["Content-Type"] ?? "application/json";
692
- init.body = JSON.stringify(options.body);
693
- }
694
- const response = await this.fetchFn(url, init);
695
- if (!response.ok) {
696
- const body = await response.text().catch(() => void 0);
697
- throw new HttpError(response.status, response.statusText, body);
698
- }
699
- return response.json();
1162
+ return readFileSync(resolve(root, "dist/client/index.html"), "utf-8");
700
1163
  }
701
- /** 构建完整 URL — 子类可覆写以自定义 URL 拼接逻辑 */
702
- buildUrl(path, params) {
703
- const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
704
- const normalizedPath = path.startsWith("/") ? path : `/${path}`;
705
- const url = new URL(`${base}${normalizedPath}`, "http://placeholder");
706
- if (params) {
707
- for (const [k, v] of Object.entries(params)) {
708
- url.searchParams.set(k, v);
709
- }
1164
+ async function loadSSRModule() {
1165
+ if (!isProduction && vite) {
1166
+ return await vite.ssrLoadModule(ssrEntryPath);
710
1167
  }
711
- if (this.baseUrl.startsWith("http")) {
712
- return url.toString();
1168
+ if (ssrProductionModule) {
1169
+ return import(ssrProductionModule);
713
1170
  }
714
- return `${url.pathname}${url.search}`;
1171
+ const { resolve } = await import(
1172
+ /* @vite-ignore */
1173
+ "path"
1174
+ );
1175
+ const { pathToFileURL } = await import(
1176
+ /* @vite-ignore */
1177
+ "url"
1178
+ );
1179
+ const absPath = pathToFileURL(resolve(root, "dist/server/ssr.js")).href;
1180
+ return import(absPath);
715
1181
  }
716
- };
717
-
718
- // ../core/src/intents/base-controller.ts
719
- var BaseController = class {
720
- /**
721
- * 错误回退 — 子类可选覆写
722
- *
723
- * 当 execute() 抛出异常时调用。
724
- * 默认行为: 重新抛出原始错误。
725
- *
726
- * @param params - Intent 参数
727
- * @param error - execute() 抛出的错误
728
- * @returns 回退数据
729
- */
730
- fallback(params, error) {
731
- throw error;
732
- }
733
- /**
734
- * IntentController.perform() 实现
735
- *
736
- * 自动 try/catch → fallback 模式。
737
- */
738
- async perform(intent, container) {
739
- const params = intent.params ?? {};
1182
+ app.get("*", async (c) => {
1183
+ const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
740
1184
  try {
741
- return await this.execute(params, container);
742
- } catch (e) {
743
- return this.fallback(
744
- params,
745
- e instanceof Error ? e : new Error(String(e))
1185
+ const template = await readTemplate(url);
1186
+ const { render, serializeServerData: serializeServerData2 } = await loadSSRModule();
1187
+ const locale = parseAcceptLanguage(
1188
+ c.req.header("accept-language"),
1189
+ supportedLocales,
1190
+ defaultLocale
746
1191
  );
747
- }
748
- }
749
- };
750
-
751
- // ../core/src/data/mapper.ts
752
- function pipe(...mappers) {
753
- return (input) => mappers.reduce((acc, mapper) => mapper(acc), input);
754
- }
755
- function pipeAsync(...mappers) {
756
- return async (input) => {
757
- let acc = input;
758
- for (const mapper of mappers) {
759
- acc = await mapper(acc);
760
- }
761
- return acc;
762
- };
763
- }
764
- function mapEach(mapper) {
765
- return (items) => items.map(mapper);
766
- }
767
-
768
- // ../core/src/bootstrap/define-routes.ts
769
- function defineRoutes(framework, definitions) {
770
- const registeredIntents = /* @__PURE__ */ new Set();
771
- for (const def of definitions) {
772
- if (def.controller && !registeredIntents.has(def.intentId)) {
773
- framework.registerIntent(def.controller);
774
- registeredIntents.add(def.intentId);
775
- }
776
- framework.router.add(def.path, def.intentId);
777
- }
778
- }
779
-
780
- // ../core/src/utils/lru-map.ts
781
- var LruMap = class {
782
- map = /* @__PURE__ */ new Map();
783
- capacity;
784
- constructor(capacity) {
785
- this.capacity = capacity;
786
- }
787
- get(key) {
788
- const value = this.map.get(key);
789
- if (value !== void 0) {
790
- this.map.delete(key);
791
- this.map.set(key, value);
792
- }
793
- return value;
794
- }
795
- set(key, value) {
796
- if (this.map.has(key)) {
797
- this.map.delete(key);
798
- } else if (this.map.size >= this.capacity) {
799
- const oldest = this.map.keys().next().value;
800
- if (oldest !== void 0) {
801
- this.map.delete(oldest);
1192
+ const {
1193
+ html: appHtml,
1194
+ head,
1195
+ css,
1196
+ serverData
1197
+ } = await render(url, locale);
1198
+ const serializedData = serializeServerData2(serverData);
1199
+ const finalHtml = injectSSRContent({
1200
+ template,
1201
+ locale,
1202
+ head,
1203
+ css,
1204
+ html: appHtml,
1205
+ serializedData
1206
+ });
1207
+ return c.html(finalHtml);
1208
+ } catch (e) {
1209
+ if (!isProduction && vite) {
1210
+ vite.ssrFixStacktrace(e);
802
1211
  }
1212
+ console.error("[SSR Error]", e);
1213
+ return c.text("Internal Server Error", 500);
803
1214
  }
804
- this.map.set(key, value);
805
- }
806
- has(key) {
807
- return this.map.has(key);
808
- }
809
- delete(key) {
810
- return this.map.delete(key);
811
- }
812
- get size() {
813
- return this.map.size;
814
- }
815
- clear() {
816
- this.map.clear();
817
- }
818
- };
819
-
820
- // ../core/src/utils/optional.ts
821
- function isSome(value) {
822
- return value !== null && value !== void 0;
823
- }
824
- function isNone(value) {
825
- return value === null || value === void 0;
826
- }
827
-
828
- // ../core/src/utils/url.ts
829
- function removeScheme(url) {
830
- return url.replace(/^https?:\/\//, "");
831
- }
832
- function removeHost(url) {
833
- try {
834
- const parsed = new URL(url);
835
- return parsed.pathname + parsed.search + parsed.hash;
836
- } catch {
837
- return url;
838
- }
839
- }
840
- function removeQueryParams(url) {
841
- return url.split("?")[0];
842
- }
843
- function getBaseUrl(url) {
844
- return url.split("?")[0].split("#")[0];
1215
+ });
1216
+ return app;
845
1217
  }
846
- function buildUrl(path, params) {
847
- if (!params) return path;
848
- const searchParams = new URLSearchParams();
849
- for (const [key, value] of Object.entries(params)) {
850
- if (value !== void 0) {
851
- searchParams.set(key, value);
852
- }
1218
+ var import_hono, import_meta;
1219
+ var init_app = __esm({
1220
+ "../server/src/app.ts"() {
1221
+ "use strict";
1222
+ init_src2();
1223
+ import_hono = require("hono");
1224
+ init_locale();
1225
+ import_meta = {};
853
1226
  }
854
- const qs = searchParams.toString();
855
- return qs ? `${path}?${qs}` : path;
856
- }
1227
+ });
857
1228
 
858
- // ../core/src/utils/uuid.ts
859
- function generateUuid() {
860
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
861
- return crypto.randomUUID();
862
- }
863
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
864
- const r = Math.random() * 16 | 0;
865
- const v = c === "x" ? r : r & 3 | 8;
866
- return v.toString(16);
867
- });
868
- }
1229
+ // src/index.ts
1230
+ var index_exports = {};
1231
+ __export(index_exports, {
1232
+ ACTION_KINDS: () => ACTION_KINDS,
1233
+ ActionDispatcher: () => ActionDispatcher,
1234
+ BaseController: () => BaseController,
1235
+ BaseLogger: () => BaseLogger,
1236
+ CompositeLogger: () => CompositeLogger,
1237
+ CompositeLoggerFactory: () => CompositeLoggerFactory,
1238
+ ConsoleLogger: () => ConsoleLogger,
1239
+ ConsoleLoggerFactory: () => ConsoleLoggerFactory,
1240
+ Container: () => Container,
1241
+ DEP_KEYS: () => DEP_KEYS,
1242
+ Framework: () => Framework,
1243
+ History: () => History,
1244
+ HttpClient: () => HttpClient,
1245
+ HttpError: () => HttpError,
1246
+ IntentDispatcher: () => IntentDispatcher,
1247
+ LruMap: () => LruMap,
1248
+ PrefetchedIntents: () => PrefetchedIntents,
1249
+ Router: () => Router,
1250
+ SSR_PLACEHOLDERS: () => SSR_PLACEHOLDERS,
1251
+ autoAdapter: () => autoAdapter,
1252
+ buildUrl: () => buildUrl,
1253
+ cloudflareAdapter: () => cloudflareAdapter,
1254
+ createPrefetchedIntentsFromDom: () => createPrefetchedIntentsFromDom,
1255
+ createSSRApp: () => createSSRApp,
1256
+ createSSRRender: () => createSSRRender,
1257
+ createServer: () => createServer,
1258
+ defineRoutes: () => defineRoutes,
1259
+ deserializeServerData: () => deserializeServerData,
1260
+ detectRuntime: () => detectRuntime,
1261
+ finesoftFrontViteConfig: () => finesoftFrontViteConfig,
1262
+ generateUuid: () => generateUuid,
1263
+ getBaseUrl: () => getBaseUrl,
1264
+ injectSSRContent: () => injectSSRContent,
1265
+ isCompoundAction: () => isCompoundAction,
1266
+ isExternalUrlAction: () => isExternalUrlAction,
1267
+ isFlowAction: () => isFlowAction,
1268
+ isNone: () => isNone,
1269
+ isSome: () => isSome,
1270
+ makeDependencies: () => makeDependencies,
1271
+ makeExternalUrlAction: () => makeExternalUrlAction,
1272
+ makeFlowAction: () => makeFlowAction,
1273
+ mapEach: () => mapEach,
1274
+ netlifyAdapter: () => netlifyAdapter,
1275
+ nodeAdapter: () => nodeAdapter,
1276
+ parseAcceptLanguage: () => parseAcceptLanguage,
1277
+ pipe: () => pipe,
1278
+ pipeAsync: () => pipeAsync,
1279
+ registerActionHandlers: () => registerActionHandlers,
1280
+ registerExternalUrlHandler: () => registerExternalUrlHandler,
1281
+ registerFlowActionHandler: () => registerFlowActionHandler,
1282
+ removeHost: () => removeHost,
1283
+ removeQueryParams: () => removeQueryParams,
1284
+ removeScheme: () => removeScheme,
1285
+ resetFilterCache: () => resetFilterCache,
1286
+ resolveAdapter: () => resolveAdapter,
1287
+ resolveRoot: () => resolveRoot,
1288
+ serializeServerData: () => serializeServerData,
1289
+ shouldLog: () => shouldLog,
1290
+ ssrRender: () => ssrRender,
1291
+ stableStringify: () => stableStringify,
1292
+ startBrowserApp: () => startBrowserApp,
1293
+ startServer: () => startServer,
1294
+ staticAdapter: () => staticAdapter,
1295
+ tryScroll: () => tryScroll,
1296
+ vercelAdapter: () => vercelAdapter
1297
+ });
1298
+ module.exports = __toCommonJS(index_exports);
1299
+ init_src();
869
1300
 
870
1301
  // ../browser/src/action-handlers/external-url-action.ts
1302
+ init_src();
871
1303
  function registerExternalUrlHandler(deps) {
872
1304
  const { framework, log } = deps;
873
1305
  framework.onAction(
@@ -879,6 +1311,12 @@ function registerExternalUrlHandler(deps) {
879
1311
  );
880
1312
  }
881
1313
 
1314
+ // ../browser/src/action-handlers/flow-action.ts
1315
+ init_src();
1316
+
1317
+ // ../browser/src/utils/history.ts
1318
+ init_src();
1319
+
882
1320
  // ../browser/src/utils/try-scroll.ts
883
1321
  var MAX_TRIES = 100;
884
1322
  var FUDGE = 16;
@@ -1126,7 +1564,11 @@ function registerActionHandlers(deps) {
1126
1564
  registerExternalUrlHandler({ framework, log });
1127
1565
  }
1128
1566
 
1567
+ // ../browser/src/start-app.ts
1568
+ init_src();
1569
+
1129
1570
  // ../browser/src/server-data.ts
1571
+ init_src();
1130
1572
  var SERVER_DATA_ID = "serialized-server-data";
1131
1573
  function deserializeServerData() {
1132
1574
  const script = document.getElementById(SERVER_DATA_ID);
@@ -1184,194 +1626,459 @@ async function startBrowserApp(config) {
1184
1626
  }
1185
1627
  }
1186
1628
 
1187
- // ../ssr/src/render.ts
1188
- async function ssrRender(options) {
1189
- const { url, frameworkConfig, bootstrap, getErrorPage, renderApp } = options;
1190
- const framework = Framework.create(frameworkConfig);
1191
- bootstrap(framework);
1192
- const parsed = new URL(url, "http://localhost");
1193
- const fullPath = parsed.pathname + parsed.search;
1194
- const match = framework.routeUrl(fullPath);
1195
- let page;
1196
- let serverData = [];
1197
- if (match) {
1198
- try {
1199
- page = await framework.dispatch(match.intent);
1200
- serverData = [{ intent: match.intent, data: page }];
1201
- } catch {
1202
- page = getErrorPage(500, "Internal error");
1203
- }
1204
- } else {
1205
- page = getErrorPage(404, "Page not found");
1206
- }
1207
- const result = renderApp(page, framework);
1208
- framework.dispose();
1209
- return {
1210
- html: result.html,
1211
- head: result.head,
1212
- css: result.css,
1213
- serverData
1214
- };
1629
+ // ../browser/src/index.ts
1630
+ init_src();
1631
+
1632
+ // src/index.ts
1633
+ init_src2();
1634
+
1635
+ // ../server/src/adapters/shared.ts
1636
+ var BUILD_TOOL_EXTERNALS = [
1637
+ "vite",
1638
+ "esbuild",
1639
+ "rollup",
1640
+ "fsevents",
1641
+ "lightningcss"
1642
+ ];
1643
+ function generateSSREntry(ctx, opts) {
1644
+ const setupImport = ctx.setupPath ? `import _setupDefault from "./${ctx.setupPath}";` : ``;
1645
+ const setupCall = ctx.setupPath ? `if (typeof _setupDefault === "function") await _setupDefault(app);` : ``;
1646
+ const locales = JSON.stringify(ctx.locales);
1647
+ const defaultLocale = JSON.stringify(ctx.defaultLocale);
1648
+ return `
1649
+ import { Hono } from "hono";
1650
+ ${opts.platformImport}
1651
+ import { render, serializeServerData } from "./${ctx.ssrEntry}";
1652
+ ${setupImport}
1653
+
1654
+ const TEMPLATE = ${JSON.stringify(ctx.templateHtml)};
1655
+ const LOCALES = ${locales};
1656
+ const DEFAULT_LOCALE = ${defaultLocale};
1657
+
1658
+ function parseAcceptLanguage(header) {
1659
+ if (!header) return DEFAULT_LOCALE;
1660
+ const langs = header.split(",").map(p => {
1661
+ const [l, q] = p.trim().split(";q=");
1662
+ return { l: l.trim().toLowerCase(), q: q ? +q : 1 };
1663
+ }).sort((a, b) => b.q - a.q);
1664
+ for (const { l } of langs) {
1665
+ const prefix = l.split("-")[0];
1666
+ if (LOCALES.includes(prefix)) return prefix;
1667
+ }
1668
+ return DEFAULT_LOCALE;
1669
+ }
1670
+
1671
+ function injectSSR(t, locale, head, css, html, data) {
1672
+ return t
1673
+ .replace("<!--ssr-lang-->", locale)
1674
+ .replace("<!--ssr-head-->", head + "\\n<style>" + css + "</style>")
1675
+ .replace("<!--ssr-body-->", html)
1676
+ .replace("<!--ssr-data-->", '<script id="serialized-server-data" type="application/json">' + data + "</script>");
1215
1677
  }
1216
1678
 
1217
- // ../ssr/src/create-render.ts
1218
- function createSSRRender(config) {
1219
- const { bootstrap, getErrorPage, renderApp, frameworkConfig } = config;
1220
- return (url, locale) => ssrRender({
1221
- url,
1222
- frameworkConfig: frameworkConfig ?? {},
1223
- bootstrap,
1224
- getErrorPage,
1225
- renderApp: (page) => renderApp(page, locale)
1679
+ const app = new Hono();
1680
+ ${setupCall}
1681
+
1682
+ app.get("*", async (c) => {
1683
+ const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
1684
+ try {
1685
+ const locale = parseAcceptLanguage(c.req.header("accept-language"));
1686
+ const { html: appHtml, head, css, serverData } = await render(url, locale);
1687
+ const serializedData = serializeServerData(serverData);
1688
+ return c.html(injectSSR(TEMPLATE, locale, head, css, appHtml, serializedData));
1689
+ } catch (e) {
1690
+ console.error("[SSR Error]", e);
1691
+ return c.text("Internal Server Error", 500);
1692
+ }
1693
+ });
1694
+
1695
+ ${opts.platformExport}
1696
+ `;
1697
+ }
1698
+ async function buildBundle(ctx, opts) {
1699
+ await ctx.vite.build({
1700
+ root: ctx.root,
1701
+ build: {
1702
+ ssr: opts.entry,
1703
+ outDir: opts.outDir,
1704
+ emptyOutDir: true,
1705
+ target: opts.target ?? "node18",
1706
+ rollupOptions: {
1707
+ output: { entryFileNames: opts.fileName ?? "index.mjs" }
1708
+ }
1709
+ },
1710
+ ssr: {
1711
+ noExternal: opts.noExternal !== false,
1712
+ external: opts.external ?? BUILD_TOOL_EXTERNALS
1713
+ },
1714
+ resolve: ctx.resolvedResolve,
1715
+ css: ctx.resolvedCss
1226
1716
  });
1227
1717
  }
1228
-
1229
- // ../ssr/src/inject.ts
1230
- var SSR_PLACEHOLDERS = {
1231
- LANG: "<!--ssr-lang-->",
1232
- HEAD: "<!--ssr-head-->",
1233
- BODY: "<!--ssr-body-->",
1234
- DATA: "<!--ssr-data-->"
1235
- };
1236
- function injectSSRContent(options) {
1237
- const { template, locale, head, css, html, serializedData } = options;
1238
- const cssTag = css ? `<style>${css}</style>` : "";
1239
- return template.replace(SSR_PLACEHOLDERS.LANG, locale).replace(SSR_PLACEHOLDERS.HEAD, `${head}
1240
- ${cssTag}`).replace(SSR_PLACEHOLDERS.BODY, html).replace(
1241
- SSR_PLACEHOLDERS.DATA,
1242
- `<script id="serialized-server-data" type="application/json">${serializedData}</script>`
1243
- );
1718
+ function copyStaticAssets(ctx, destDir, opts) {
1719
+ const { fs, path } = ctx;
1720
+ fs.cpSync(path.resolve(ctx.root, "dist/client"), destDir, {
1721
+ recursive: true
1722
+ });
1723
+ if (opts?.excludeHtml !== false) {
1724
+ fs.rmSync(path.join(destDir, "index.html"), { force: true });
1725
+ }
1244
1726
  }
1245
1727
 
1246
- // ../ssr/src/server-data.ts
1247
- var HTML_REPLACEMENTS = {
1248
- "<": "\\u003C",
1249
- ">": "\\u003E",
1250
- "/": "\\u002F",
1251
- "\u2028": "\\u2028",
1252
- "\u2029": "\\u2029"
1253
- };
1254
- var HTML_ESCAPE_PATTERN = /[<>/\u2028\u2029]/g;
1255
- function serializeServerData(data) {
1256
- const json = JSON.stringify(data);
1257
- return json.replace(
1258
- HTML_ESCAPE_PATTERN,
1259
- (match) => HTML_REPLACEMENTS[match] ?? match
1260
- );
1728
+ // ../server/src/adapters/cloudflare.ts
1729
+ function cloudflareAdapter() {
1730
+ return {
1731
+ name: "cloudflare",
1732
+ async build(ctx) {
1733
+ const { fs, path, root } = ctx;
1734
+ const outputDir = path.resolve(root, "dist/cloudflare");
1735
+ fs.rmSync(outputDir, { recursive: true, force: true });
1736
+ const entrySource = generateSSREntry(ctx, {
1737
+ platformImport: ``,
1738
+ platformExport: `export default app;`
1739
+ });
1740
+ const tempEntry = path.resolve(root, ".cf-entry.tmp.mjs");
1741
+ fs.writeFileSync(tempEntry, entrySource);
1742
+ try {
1743
+ await buildBundle(ctx, {
1744
+ entry: ".cf-entry.tmp.mjs",
1745
+ outDir: outputDir,
1746
+ target: "es2022",
1747
+ fileName: "_worker.js",
1748
+ external: []
1749
+ // CF Workers 需全部打包
1750
+ });
1751
+ copyStaticAssets(ctx, path.resolve(outputDir, "assets"));
1752
+ } finally {
1753
+ fs.rmSync(tempEntry, { force: true });
1754
+ }
1755
+ console.log(" Cloudflare output \u2192 dist/cloudflare/\n");
1756
+ }
1757
+ };
1261
1758
  }
1262
1759
 
1263
- // ../server/src/app.ts
1264
- var import_hono = require("hono");
1760
+ // ../server/src/adapters/netlify.ts
1761
+ function netlifyAdapter() {
1762
+ return {
1763
+ name: "netlify",
1764
+ async build(ctx) {
1765
+ const { fs, path, root } = ctx;
1766
+ const funcDir = path.resolve(
1767
+ root,
1768
+ ".netlify/functions-internal/ssr"
1769
+ );
1770
+ fs.rmSync(path.resolve(root, ".netlify"), {
1771
+ recursive: true,
1772
+ force: true
1773
+ });
1774
+ const entrySource = generateSSREntry(ctx, {
1775
+ platformImport: `import { handle } from "hono/netlify";`,
1776
+ platformExport: `export default handle(app);`
1777
+ });
1778
+ const tempEntry = path.resolve(root, ".netlify-entry.tmp.mjs");
1779
+ fs.writeFileSync(tempEntry, entrySource);
1780
+ try {
1781
+ await buildBundle(ctx, {
1782
+ entry: ".netlify-entry.tmp.mjs",
1783
+ outDir: funcDir,
1784
+ target: "node18"
1785
+ });
1786
+ } finally {
1787
+ fs.rmSync(tempEntry, { force: true });
1788
+ }
1789
+ const redirects = `/* /.netlify/functions/ssr 200
1790
+ `;
1791
+ fs.writeFileSync(
1792
+ path.resolve(root, "dist/client/_redirects"),
1793
+ redirects
1794
+ );
1795
+ console.log(
1796
+ " Netlify output \u2192 .netlify/functions-internal/ssr/\n Publish dir: dist/client/\n"
1797
+ );
1798
+ }
1799
+ };
1800
+ }
1265
1801
 
1266
- // ../server/src/locale.ts
1267
- function parseAcceptLanguage(header, supported, fallback) {
1268
- const effectiveSupported = supported ?? ["zh", "en"];
1269
- const effectiveFallback = fallback ?? effectiveSupported[0] ?? "en";
1270
- if (!header) return effectiveFallback;
1271
- const langs = header.split(",").map((part) => {
1272
- const [lang, q] = part.trim().split(";q=");
1273
- return {
1274
- lang: lang.trim().toLowerCase(),
1275
- q: q ? parseFloat(q) : 1
1276
- };
1277
- }).sort((a, b) => b.q - a.q);
1278
- for (const { lang } of langs) {
1279
- const prefix = lang.split("-")[0];
1280
- if (effectiveSupported.includes(prefix)) {
1281
- return prefix;
1802
+ // ../server/src/adapters/node.ts
1803
+ function nodeAdapter() {
1804
+ return {
1805
+ name: "node",
1806
+ async build(ctx) {
1807
+ const { fs, path, root } = ctx;
1808
+ const entrySource = generateSSREntry(ctx, {
1809
+ platformImport: `import { serve } from "@hono/node-server";`,
1810
+ platformExport: `
1811
+ const port = +(process.env.PORT || 3000);
1812
+ serve({ fetch: app.fetch, port }, (info) => {
1813
+ console.log(\`Server running at http://localhost:\${info.port}\`);
1814
+ });
1815
+ `
1816
+ });
1817
+ const tempEntry = path.resolve(root, ".node-entry.tmp.mjs");
1818
+ fs.writeFileSync(tempEntry, entrySource);
1819
+ try {
1820
+ await buildBundle(ctx, {
1821
+ entry: ".node-entry.tmp.mjs",
1822
+ outDir: path.resolve(root, "dist/server"),
1823
+ target: "node18"
1824
+ });
1825
+ } finally {
1826
+ fs.rmSync(tempEntry, { force: true });
1827
+ }
1828
+ console.log(
1829
+ " Node output \u2192 dist/server/index.mjs\n Run: node dist/server/index.mjs\n"
1830
+ );
1282
1831
  }
1283
- }
1284
- return effectiveFallback;
1832
+ };
1285
1833
  }
1286
1834
 
1287
- // ../server/src/app.ts
1288
- var import_meta = {};
1289
- function createSSRApp(options) {
1290
- const {
1291
- root,
1292
- vite,
1293
- isProduction,
1294
- ssrEntryPath = "/src/ssr.ts",
1295
- ssrProductionModule,
1296
- supportedLocales,
1297
- defaultLocale
1298
- } = options;
1299
- const app = new import_hono.Hono();
1300
- async function readTemplate(url) {
1301
- if (!isProduction && vite) {
1302
- const { readFileSync: readFileSync2 } = await import(
1835
+ // ../server/src/adapters/static.ts
1836
+ function staticAdapter(opts = {}) {
1837
+ return {
1838
+ name: "static",
1839
+ async build(ctx) {
1840
+ const { fs, path, root, vite } = ctx;
1841
+ const outputDir = path.resolve(root, "dist/static");
1842
+ fs.rmSync(outputDir, { recursive: true, force: true });
1843
+ fs.mkdirSync(outputDir, { recursive: true });
1844
+ const { pathToFileURL } = await import(
1303
1845
  /* @vite-ignore */
1304
- "fs"
1846
+ "url"
1305
1847
  );
1306
- const { resolve: resolve2 } = await import(
1848
+ const ssrPath = pathToFileURL(
1849
+ path.resolve(root, "dist/server/ssr.js")
1850
+ ).href;
1851
+ const ssrModule = await import(
1307
1852
  /* @vite-ignore */
1308
- "path"
1853
+ ssrPath
1309
1854
  );
1310
- const raw = readFileSync2(resolve2(root, "index.html"), "utf-8");
1311
- return vite.transformIndexHtml(url, raw);
1312
- }
1313
- const isDeno = typeof globalThis.Deno !== "undefined";
1314
- if (isDeno) {
1315
- return globalThis.Deno.readTextFileSync(
1316
- new URL("../dist/client/index.html", import_meta.url)
1855
+ const routePaths = await extractRoutes(ctx, opts);
1856
+ const allUrls = [];
1857
+ for (const routePath of routePaths) {
1858
+ for (const locale of ctx.locales) {
1859
+ const url = locale === ctx.defaultLocale ? routePath : `/${locale}${routePath === "/" ? "" : routePath}`;
1860
+ allUrls.push(url);
1861
+ }
1862
+ }
1863
+ console.log(
1864
+ ` Pre-rendering ${allUrls.length} pages (${routePaths.length} routes \xD7 ${ctx.locales.length} locales)...
1865
+ `
1317
1866
  );
1867
+ for (const url of allUrls) {
1868
+ try {
1869
+ const locale = inferLocale(
1870
+ url,
1871
+ ctx.locales,
1872
+ ctx.defaultLocale
1873
+ );
1874
+ const {
1875
+ html: appHtml,
1876
+ head,
1877
+ css,
1878
+ serverData
1879
+ } = await ssrModule.render(url, locale);
1880
+ const serializedData = ssrModule.serializeServerData(serverData);
1881
+ const finalHtml = injectSSRForStatic(
1882
+ ctx.templateHtml,
1883
+ locale,
1884
+ head,
1885
+ css,
1886
+ appHtml,
1887
+ serializedData
1888
+ );
1889
+ const filePath = url === "/" ? path.join(outputDir, "index.html") : path.join(outputDir, url, "index.html");
1890
+ fs.mkdirSync(path.resolve(filePath, ".."), {
1891
+ recursive: true
1892
+ });
1893
+ fs.writeFileSync(filePath, finalHtml);
1894
+ } catch (e) {
1895
+ console.warn(` [static] Failed to render ${url}:`, e);
1896
+ }
1897
+ }
1898
+ ctx.copyStaticAssets(outputDir, { excludeHtml: true });
1899
+ console.log(` Static output \u2192 dist/static/
1900
+ `);
1318
1901
  }
1319
- const { readFileSync } = await import(
1902
+ };
1903
+ }
1904
+ async function extractRoutes(ctx, opts) {
1905
+ const routesFile = opts.routesExport ?? "src/lib/bootstrap.ts";
1906
+ const paths = [];
1907
+ try {
1908
+ const { pathToFileURL } = await import(
1320
1909
  /* @vite-ignore */
1321
- "fs"
1910
+ "url"
1322
1911
  );
1323
- const { resolve } = await import(
1912
+ await ctx.vite.build({
1913
+ root: ctx.root,
1914
+ build: {
1915
+ ssr: routesFile,
1916
+ outDir: ctx.path.resolve(ctx.root, "dist/server"),
1917
+ emptyOutDir: false,
1918
+ rollupOptions: {
1919
+ output: { entryFileNames: "_routes.mjs" }
1920
+ }
1921
+ },
1922
+ resolve: ctx.resolvedResolve
1923
+ });
1924
+ const routesPath = pathToFileURL(
1925
+ ctx.path.resolve(ctx.root, "dist/server/_routes.mjs")
1926
+ ).href;
1927
+ const routesMod = await import(
1324
1928
  /* @vite-ignore */
1325
- "path"
1929
+ routesPath
1326
1930
  );
1327
- return readFileSync(resolve(root, "dist/client/index.html"), "utf-8");
1931
+ const routes = routesMod.routes ?? routesMod.default;
1932
+ if (Array.isArray(routes)) {
1933
+ for (const r of routes) {
1934
+ if (r.path && !r.path.includes(":")) {
1935
+ paths.push(r.path);
1936
+ }
1937
+ }
1938
+ }
1939
+ ctx.fs.rmSync(ctx.path.resolve(ctx.root, "dist/server/_routes.mjs"), {
1940
+ force: true
1941
+ });
1942
+ } catch (e) {
1943
+ console.warn(
1944
+ ` [static] Could not load routes from "${routesFile}". Using "/" only.`,
1945
+ e
1946
+ );
1947
+ if (paths.length === 0) paths.push("/");
1328
1948
  }
1329
- async function loadSSRModule() {
1330
- if (!isProduction && vite) {
1331
- return await vite.ssrLoadModule(ssrEntryPath);
1949
+ if (opts.dynamicRoutes) {
1950
+ for (const r of opts.dynamicRoutes) {
1951
+ if (!paths.includes(r)) paths.push(r);
1332
1952
  }
1333
- const modulePath = ssrProductionModule ?? "../dist/server/ssr.js";
1334
- return import(modulePath);
1335
1953
  }
1336
- app.get("*", async (c) => {
1337
- const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
1338
- try {
1339
- const template = await readTemplate(url);
1340
- const { render, serializeServerData: serializeServerData2 } = await loadSSRModule();
1341
- const locale = parseAcceptLanguage(
1342
- c.req.header("accept-language"),
1343
- supportedLocales,
1344
- defaultLocale
1345
- );
1346
- const {
1347
- html: appHtml,
1348
- head,
1349
- css,
1350
- serverData
1351
- } = await render(url, locale);
1352
- const serializedData = serializeServerData2(serverData);
1353
- const finalHtml = injectSSRContent({
1354
- template,
1355
- locale,
1356
- head,
1357
- css,
1358
- html: appHtml,
1359
- serializedData
1954
+ if (paths.length === 0) paths.push("/");
1955
+ return paths;
1956
+ }
1957
+ function inferLocale(url, locales, defaultLocale) {
1958
+ const segments = url.split("/").filter(Boolean);
1959
+ if (segments.length > 0 && locales.includes(segments[0])) {
1960
+ return segments[0];
1961
+ }
1962
+ return defaultLocale;
1963
+ }
1964
+ function injectSSRForStatic(template, locale, head, css, html, serializedData) {
1965
+ return template.replace("<!--ssr-lang-->", locale).replace("<!--ssr-head-->", head + "\n<style>" + css + "</style>").replace("<!--ssr-body-->", html).replace(
1966
+ "<!--ssr-data-->",
1967
+ '<script id="serialized-server-data" type="application/json">' + serializedData + "</script>"
1968
+ );
1969
+ }
1970
+
1971
+ // ../server/src/adapters/vercel.ts
1972
+ function vercelAdapter() {
1973
+ return {
1974
+ name: "vercel",
1975
+ async build(ctx) {
1976
+ const { fs, path, root } = ctx;
1977
+ const outputDir = path.resolve(root, ".vercel/output");
1978
+ fs.rmSync(outputDir, { recursive: true, force: true });
1979
+ const entrySource = generateSSREntry(ctx, {
1980
+ platformImport: `import { handle } from "hono/vercel";`,
1981
+ platformExport: `export default handle(app);`
1360
1982
  });
1361
- return c.html(finalHtml);
1362
- } catch (e) {
1363
- if (!isProduction && vite) {
1364
- vite.ssrFixStacktrace(e);
1983
+ const tempEntry = path.resolve(root, ".vercel-entry.tmp.mjs");
1984
+ fs.writeFileSync(tempEntry, entrySource);
1985
+ try {
1986
+ const funcDir = path.resolve(
1987
+ root,
1988
+ ".vercel/output/functions/ssr.func"
1989
+ );
1990
+ await buildBundle(ctx, {
1991
+ entry: ".vercel-entry.tmp.mjs",
1992
+ outDir: funcDir,
1993
+ target: "node18"
1994
+ });
1995
+ fs.writeFileSync(
1996
+ path.resolve(funcDir, ".vc-config.json"),
1997
+ JSON.stringify(
1998
+ {
1999
+ runtime: "nodejs20.x",
2000
+ handler: "index.mjs",
2001
+ launcherType: "Nodejs"
2002
+ },
2003
+ null,
2004
+ 2
2005
+ )
2006
+ );
2007
+ copyStaticAssets(
2008
+ ctx,
2009
+ path.resolve(root, ".vercel/output/static")
2010
+ );
2011
+ fs.writeFileSync(
2012
+ path.resolve(root, ".vercel/output/config.json"),
2013
+ JSON.stringify(
2014
+ {
2015
+ version: 3,
2016
+ routes: [
2017
+ { handle: "filesystem" },
2018
+ { src: "/(.*)", dest: "/ssr" }
2019
+ ]
2020
+ },
2021
+ null,
2022
+ 2
2023
+ )
2024
+ );
2025
+ } finally {
2026
+ fs.rmSync(tempEntry, { force: true });
1365
2027
  }
1366
- console.error("[SSR Error]", e);
1367
- return c.text("Internal Server Error", 500);
2028
+ console.log(" Vercel output \u2192 .vercel/output/\n");
1368
2029
  }
1369
- });
1370
- return app;
2030
+ };
2031
+ }
2032
+
2033
+ // ../server/src/adapters/resolve.ts
2034
+ function resolveAdapter(value) {
2035
+ if (typeof value !== "string") return value;
2036
+ switch (value) {
2037
+ case "vercel":
2038
+ return vercelAdapter();
2039
+ case "cloudflare":
2040
+ return cloudflareAdapter();
2041
+ case "netlify":
2042
+ return netlifyAdapter();
2043
+ case "node":
2044
+ return nodeAdapter();
2045
+ case "static":
2046
+ return staticAdapter();
2047
+ case "auto":
2048
+ return autoAdapter();
2049
+ default:
2050
+ throw new Error(
2051
+ `[finesoft] Unknown adapter: "${value}". Available: vercel, cloudflare, netlify, node, static, auto`
2052
+ );
2053
+ }
1371
2054
  }
1372
2055
 
2056
+ // ../server/src/adapters/auto.ts
2057
+ function autoAdapter() {
2058
+ return {
2059
+ name: "auto",
2060
+ async build(ctx) {
2061
+ const detected = detectPlatform();
2062
+ console.log(` [auto] Detected platform: ${detected}
2063
+ `);
2064
+ const adapter = resolveAdapter(detected);
2065
+ return adapter.build(ctx);
2066
+ }
2067
+ };
2068
+ }
2069
+ function detectPlatform() {
2070
+ if (process.env.VERCEL) return "vercel";
2071
+ if (process.env.CF_PAGES) return "cloudflare";
2072
+ if (process.env.NETLIFY) return "netlify";
2073
+ return "node";
2074
+ }
2075
+
2076
+ // ../server/src/index.ts
2077
+ init_app();
2078
+
1373
2079
  // ../server/src/create-server.ts
1374
2080
  var import_hono3 = require("hono");
2081
+ init_app();
1375
2082
 
1376
2083
  // ../server/src/runtime.ts
1377
2084
  function detectRuntime() {
@@ -1490,7 +2197,15 @@ async function startServer(options) {
1490
2197
  "path"
1491
2198
  );
1492
2199
  const prodApp = new import_hono2.Hono();
1493
- prodApp.use("/*", serveStatic({ root: resolve(root, "dist/client") }));
2200
+ const clientDir = resolve(root, "dist/client");
2201
+ prodApp.use(
2202
+ "/*",
2203
+ serveStatic({
2204
+ root: clientDir,
2205
+ // 禁止目录路径自动提供 index.html,让其 fall through 到 SSR
2206
+ rewriteRequestPath: (path) => path.endsWith("/") ? "/__nosuchfile__" : path
2207
+ })
2208
+ );
1494
2209
  prodApp.route("/", app);
1495
2210
  const { serve } = await import(
1496
2211
  /* @vite-ignore */
@@ -1571,6 +2286,251 @@ async function createServer(config = {}) {
1571
2286
  });
1572
2287
  return { app, vite, runtime };
1573
2288
  }
2289
+
2290
+ // ../server/src/index.ts
2291
+ init_locale();
2292
+
2293
+ // ../server/src/vite-plugin.ts
2294
+ function resolveSetupFn(mod) {
2295
+ if (typeof mod.default === "function") return mod.default;
2296
+ if (typeof mod.setup === "function") return mod.setup;
2297
+ const first = Object.values(mod).find((v) => typeof v === "function");
2298
+ return first ?? null;
2299
+ }
2300
+ function finesoftFrontViteConfig(options = {}) {
2301
+ const ssrEntry = options.ssr?.entry ?? "src/ssr.ts";
2302
+ let root = process.cwd();
2303
+ let resolvedCommand;
2304
+ let resolvedResolve;
2305
+ let resolvedCss;
2306
+ return {
2307
+ name: "finesoft-front",
2308
+ config(userConfig, env) {
2309
+ const overrides = {
2310
+ appType: "custom"
2311
+ };
2312
+ if (env.command === "build" && !process.env.__FINESOFT_SUB_BUILD__) {
2313
+ overrides.build = {
2314
+ outDir: userConfig.build?.outDir ?? "dist/client"
2315
+ };
2316
+ }
2317
+ return overrides;
2318
+ },
2319
+ configResolved(config) {
2320
+ resolvedCommand = config.command;
2321
+ resolvedResolve = config.resolve;
2322
+ resolvedCss = config.css;
2323
+ root = config.root;
2324
+ },
2325
+ // ─── Dev ───────────────────────────────────────────────
2326
+ configureServer(server) {
2327
+ return async () => {
2328
+ const { Hono: HonoClass } = await import(
2329
+ /* @vite-ignore */
2330
+ "hono"
2331
+ );
2332
+ const { createSSRApp: createSSRApp2 } = await Promise.resolve().then(() => (init_app(), app_exports));
2333
+ const { getRequestListener } = await import(
2334
+ /* @vite-ignore */
2335
+ "@hono/node-server"
2336
+ );
2337
+ const app = new HonoClass();
2338
+ if (typeof options.setup === "function") {
2339
+ await options.setup(app);
2340
+ } else if (typeof options.setup === "string") {
2341
+ const mod = await server.ssrLoadModule("/" + options.setup);
2342
+ const fn = resolveSetupFn(mod);
2343
+ if (fn) await fn(app);
2344
+ }
2345
+ const ssrApp = createSSRApp2({
2346
+ root,
2347
+ vite: server,
2348
+ isProduction: false,
2349
+ ssrEntryPath: "/" + ssrEntry,
2350
+ supportedLocales: options.locales,
2351
+ defaultLocale: options.defaultLocale
2352
+ });
2353
+ app.route("/", ssrApp);
2354
+ const listener = getRequestListener(app.fetch);
2355
+ server.middlewares.use((req, res) => {
2356
+ listener(req, res);
2357
+ });
2358
+ };
2359
+ },
2360
+ // ─── Preview ───────────────────────────────────────────
2361
+ configurePreviewServer(server) {
2362
+ return async () => {
2363
+ const { readFileSync } = await import(
2364
+ /* @vite-ignore */
2365
+ "fs"
2366
+ );
2367
+ const { resolve } = await import(
2368
+ /* @vite-ignore */
2369
+ "path"
2370
+ );
2371
+ const { pathToFileURL } = await import(
2372
+ /* @vite-ignore */
2373
+ "url"
2374
+ );
2375
+ const { Hono: HonoClass } = await import(
2376
+ /* @vite-ignore */
2377
+ "hono"
2378
+ );
2379
+ const { injectSSRContent: injectSSRContent2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
2380
+ const { parseAcceptLanguage: parseAcceptLanguage2 } = await Promise.resolve().then(() => (init_locale(), locale_exports));
2381
+ const { getRequestListener } = await import(
2382
+ /* @vite-ignore */
2383
+ "@hono/node-server"
2384
+ );
2385
+ const app = new HonoClass();
2386
+ if (typeof options.setup === "function") {
2387
+ await options.setup(app);
2388
+ } else if (typeof options.setup === "string") {
2389
+ try {
2390
+ const setupPath = pathToFileURL(
2391
+ resolve(root, "dist/server/setup.mjs")
2392
+ ).href;
2393
+ const mod = await import(
2394
+ /* @vite-ignore */
2395
+ setupPath
2396
+ );
2397
+ const fn = resolveSetupFn(
2398
+ mod
2399
+ );
2400
+ if (fn) await fn(app);
2401
+ } catch {
2402
+ console.warn(
2403
+ "[finesoft] Could not load setup module for preview. API routes disabled."
2404
+ );
2405
+ }
2406
+ }
2407
+ const templatePath = resolve(root, "dist/client/index.html");
2408
+ const template = readFileSync(templatePath, "utf-8");
2409
+ const ssrPath = pathToFileURL(
2410
+ resolve(root, "dist/server/ssr.js")
2411
+ ).href;
2412
+ const ssrModule = await import(
2413
+ /* @vite-ignore */
2414
+ ssrPath
2415
+ );
2416
+ app.get("*", async (c) => {
2417
+ const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
2418
+ try {
2419
+ const locale = parseAcceptLanguage2(
2420
+ c.req.header("accept-language"),
2421
+ options.locales,
2422
+ options.defaultLocale
2423
+ );
2424
+ const {
2425
+ html: appHtml,
2426
+ head,
2427
+ css,
2428
+ serverData
2429
+ } = await ssrModule.render(url, locale);
2430
+ const serializedData = ssrModule.serializeServerData(serverData);
2431
+ const finalHtml = injectSSRContent2({
2432
+ template,
2433
+ locale,
2434
+ head,
2435
+ css,
2436
+ html: appHtml,
2437
+ serializedData
2438
+ });
2439
+ return c.html(finalHtml);
2440
+ } catch (e) {
2441
+ console.error("[SSR Preview Error]", e);
2442
+ return c.text("Internal Server Error", 500);
2443
+ }
2444
+ });
2445
+ const listener = getRequestListener(app.fetch);
2446
+ server.middlewares.use((req, res) => {
2447
+ listener(req, res);
2448
+ });
2449
+ };
2450
+ },
2451
+ // ─── Build ─────────────────────────────────────────────
2452
+ async closeBundle() {
2453
+ if (process.env.__FINESOFT_SUB_BUILD__) return;
2454
+ if (resolvedCommand !== "build") return;
2455
+ process.env.__FINESOFT_SUB_BUILD__ = "1";
2456
+ try {
2457
+ const vite = await import(
2458
+ /* @vite-ignore */
2459
+ "vite"
2460
+ );
2461
+ const fs = await import(
2462
+ /* @vite-ignore */
2463
+ "fs"
2464
+ );
2465
+ const path = await import(
2466
+ /* @vite-ignore */
2467
+ "path"
2468
+ );
2469
+ console.log("\n Building SSR bundle...\n");
2470
+ await vite.build({
2471
+ root,
2472
+ build: {
2473
+ ssr: ssrEntry,
2474
+ outDir: "dist/server"
2475
+ },
2476
+ resolve: resolvedResolve,
2477
+ css: resolvedCss
2478
+ });
2479
+ if (typeof options.setup === "string") {
2480
+ console.log(" Building setup module...\n");
2481
+ await vite.build({
2482
+ root,
2483
+ build: {
2484
+ ssr: options.setup,
2485
+ outDir: "dist/server",
2486
+ emptyOutDir: false,
2487
+ rollupOptions: {
2488
+ output: { entryFileNames: "setup.mjs" }
2489
+ }
2490
+ },
2491
+ resolve: resolvedResolve
2492
+ });
2493
+ }
2494
+ if (options.adapter) {
2495
+ const adapter = resolveAdapter(options.adapter);
2496
+ const locales = options.locales ?? ["zh", "en"];
2497
+ const defaultLocale = options.defaultLocale ?? locales[0] ?? "en";
2498
+ const templateHtml = fs.readFileSync(
2499
+ path.resolve(root, "dist/client/index.html"),
2500
+ "utf-8"
2501
+ );
2502
+ const ctx = {
2503
+ root,
2504
+ ssrEntry,
2505
+ setupPath: typeof options.setup === "string" ? options.setup : void 0,
2506
+ locales,
2507
+ defaultLocale,
2508
+ templateHtml,
2509
+ resolvedResolve,
2510
+ resolvedCss,
2511
+ vite,
2512
+ fs,
2513
+ path,
2514
+ generateSSREntry(opts) {
2515
+ return generateSSREntry(ctx, opts);
2516
+ },
2517
+ buildBundle(opts) {
2518
+ return buildBundle(ctx, opts);
2519
+ },
2520
+ copyStaticAssets(destDir, opts) {
2521
+ return copyStaticAssets(ctx, destDir, opts);
2522
+ }
2523
+ };
2524
+ console.log(` Running adapter: ${adapter.name}...
2525
+ `);
2526
+ await adapter.build(ctx);
2527
+ }
2528
+ } finally {
2529
+ delete process.env.__FINESOFT_SUB_BUILD__;
2530
+ }
2531
+ }
2532
+ };
2533
+ }
1574
2534
  // Annotate the CommonJS export names for ESM import in node:
1575
2535
  0 && (module.exports = {
1576
2536
  ACTION_KINDS,
@@ -1592,7 +2552,9 @@ async function createServer(config = {}) {
1592
2552
  PrefetchedIntents,
1593
2553
  Router,
1594
2554
  SSR_PLACEHOLDERS,
2555
+ autoAdapter,
1595
2556
  buildUrl,
2557
+ cloudflareAdapter,
1596
2558
  createPrefetchedIntentsFromDom,
1597
2559
  createSSRApp,
1598
2560
  createSSRRender,
@@ -1600,6 +2562,7 @@ async function createServer(config = {}) {
1600
2562
  defineRoutes,
1601
2563
  deserializeServerData,
1602
2564
  detectRuntime,
2565
+ finesoftFrontViteConfig,
1603
2566
  generateUuid,
1604
2567
  getBaseUrl,
1605
2568
  injectSSRContent,
@@ -1612,6 +2575,8 @@ async function createServer(config = {}) {
1612
2575
  makeExternalUrlAction,
1613
2576
  makeFlowAction,
1614
2577
  mapEach,
2578
+ netlifyAdapter,
2579
+ nodeAdapter,
1615
2580
  parseAcceptLanguage,
1616
2581
  pipe,
1617
2582
  pipeAsync,
@@ -1622,6 +2587,7 @@ async function createServer(config = {}) {
1622
2587
  removeQueryParams,
1623
2588
  removeScheme,
1624
2589
  resetFilterCache,
2590
+ resolveAdapter,
1625
2591
  resolveRoot,
1626
2592
  serializeServerData,
1627
2593
  shouldLog,
@@ -1629,6 +2595,8 @@ async function createServer(config = {}) {
1629
2595
  stableStringify,
1630
2596
  startBrowserApp,
1631
2597
  startServer,
1632
- tryScroll
2598
+ staticAdapter,
2599
+ tryScroll,
2600
+ vercelAdapter
1633
2601
  });
1634
2602
  //# sourceMappingURL=index.cjs.map