@finesoft/front 0.1.39 → 0.1.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/app-re7jUCuM.mjs +178 -0
  2. package/dist/app-re7jUCuM.mjs.map +1 -0
  3. package/dist/index.d.mts +1276 -0
  4. package/dist/index.d.mts.map +1 -0
  5. package/dist/index.mjs +2749 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/dist/locale-D2Bu7w47.mjs +27 -0
  8. package/dist/locale-D2Bu7w47.mjs.map +1 -0
  9. package/dist/rolldown-runtime-wcPFST8Q.mjs +13 -0
  10. package/dist/server-peer-modules-BSSsxBaF.d.mts +41 -0
  11. package/dist/server-peer-modules-BSSsxBaF.d.mts.map +1 -0
  12. package/package.json +21 -32
  13. package/README.md +0 -1136
  14. package/dist/app-XEMPAI6H.js +0 -10
  15. package/dist/app-XEMPAI6H.js.map +0 -1
  16. package/dist/browser.cjs +0 -1465
  17. package/dist/browser.cjs.map +0 -1
  18. package/dist/browser.d.cts +0 -904
  19. package/dist/browser.d.ts +0 -904
  20. package/dist/browser.js +0 -115
  21. package/dist/browser.js.map +0 -1
  22. package/dist/chunk-4PPCVAKZ.js +0 -463
  23. package/dist/chunk-4PPCVAKZ.js.map +0 -1
  24. package/dist/chunk-OYTIGVEG.js +0 -193
  25. package/dist/chunk-OYTIGVEG.js.map +0 -1
  26. package/dist/chunk-PSPVIVC2.js +0 -25
  27. package/dist/chunk-PSPVIVC2.js.map +0 -1
  28. package/dist/chunk-SDPWQT2T.js +0 -936
  29. package/dist/chunk-SDPWQT2T.js.map +0 -1
  30. package/dist/chunk-XQ3UWZOS.js +0 -187
  31. package/dist/chunk-XQ3UWZOS.js.map +0 -1
  32. package/dist/index.cjs +0 -3661
  33. package/dist/index.cjs.map +0 -1
  34. package/dist/index.d.cts +0 -582
  35. package/dist/index.d.ts +0 -582
  36. package/dist/index.js +0 -1648
  37. package/dist/index.js.map +0 -1
  38. package/dist/locale-CAZ4INCX.js +0 -7
  39. package/dist/locale-CAZ4INCX.js.map +0 -1
  40. package/dist/src-XNC7QNVI.js +0 -21
  41. package/dist/src-XNC7QNVI.js.map +0 -1
package/dist/index.cjs DELETED
@@ -1,3661 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
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
- };
11
- var __export = (target, all) => {
12
- for (var name in all)
13
- __defProp(target, name, { get: all[name], enumerable: true });
14
- };
15
- var __copyProps = (to, from, except, desc) => {
16
- if (from && typeof from === "object" || typeof from === "function") {
17
- for (let key of __getOwnPropNames(from))
18
- if (!__hasOwnProp.call(to, key) && key !== except)
19
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
- }
21
- return to;
22
- };
23
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
- // If the importer is in node compatibility mode or this is not an ESM
25
- // file that has been converted to a CommonJS file using a Babel-
26
- // compatible transform (i.e. "__esModule" has not been set), then set
27
- // "default" to the CommonJS "module.exports" for node compatibility.
28
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
- mod
30
- ));
31
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
-
33
- // ../core/src/actions/types.ts
34
- function isFlowAction(action) {
35
- return action.kind === ACTION_KINDS.FLOW;
36
- }
37
- function isExternalUrlAction(action) {
38
- return action.kind === ACTION_KINDS.EXTERNAL_URL;
39
- }
40
- function isCompoundAction(action) {
41
- return action.kind === ACTION_KINDS.COMPOUND;
42
- }
43
- function makeFlowAction(url, presentationContext) {
44
- return { kind: ACTION_KINDS.FLOW, url, presentationContext };
45
- }
46
- function makeExternalUrlAction(url) {
47
- return { kind: ACTION_KINDS.EXTERNAL_URL, url };
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
- });
60
-
61
- // ../core/src/actions/dispatcher.ts
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);
80
- }
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
- };
99
- }
100
- });
101
-
102
- // ../core/src/intents/dispatcher.ts
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
- };
128
- }
129
- });
130
-
131
- // ../core/src/dependencies/container.ts
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;
142
- }
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
- };
169
- }
170
- });
171
-
172
- // ../core/src/logger/base.ts
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
- };
183
- }
184
- });
185
-
186
- // ../core/src/logger/local-storage-filter.ts
187
- function parseRules() {
188
- if (typeof globalThis.localStorage === "undefined") {
189
- return {};
190
- }
191
- let raw;
192
- try {
193
- raw = globalThis.localStorage.getItem("onyxLog");
194
- } catch {
195
- return {};
196
- }
197
- if (!raw) return {};
198
- if (raw === cachedRaw && cachedRules) return cachedRules;
199
- cachedRaw = raw;
200
- const rules = {};
201
- const parts = raw.split(",");
202
- for (const part of parts) {
203
- const [name, level] = part.trim().split("=");
204
- if (!name || level === void 0) continue;
205
- const num = LEVEL_TO_NUM[level.toLowerCase()] ?? void 0;
206
- if (num === void 0) continue;
207
- if (name === "*") {
208
- rules.defaultLevel = num;
209
- } else {
210
- rules.named ??= {};
211
- rules.named[name] = num;
212
- }
213
- }
214
- cachedRules = rules;
215
- return rules;
216
- }
217
- function shouldLog(name, level) {
218
- const rules = parseRules();
219
- if (rules.defaultLevel === void 0 && !rules.named) {
220
- return true;
221
- }
222
- const currentNum = LEVEL_TO_NUM[level] ?? 4;
223
- if (rules.named?.[name] !== void 0) {
224
- return currentNum <= rules.named[name];
225
- }
226
- if (rules.defaultLevel !== void 0) {
227
- return currentNum <= rules.defaultLevel;
228
- }
229
- return true;
230
- }
231
- function resetFilterCache() {
232
- cachedRules = void 0;
233
- cachedRaw = void 0;
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
- });
250
-
251
- // ../core/src/logger/console.ts
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
- };
287
- }
288
- });
289
-
290
- // ../core/src/dependencies/make-dependencies.ts
291
- function makeDependencies(container, options = {}) {
292
- const {
293
- fetch: fetchFn = globalThis.fetch?.bind(globalThis),
294
- language = "en",
295
- storefront = "us",
296
- featureFlags = {}
297
- } = options;
298
- const loggerFactory = new ConsoleLoggerFactory();
299
- container.register(
300
- DEP_KEYS.LOGGER_FACTORY,
301
- () => loggerFactory
302
- );
303
- container.register(
304
- DEP_KEYS.LOGGER,
305
- () => loggerFactory.loggerFor("framework")
306
- );
307
- container.register(DEP_KEYS.NET, () => ({
308
- fetch: (url, opts) => fetchFn(url, opts)
309
- }));
310
- container.register(DEP_KEYS.LOCALE, () => {
311
- const locale = new DefaultLocale();
312
- locale.setActiveLocale(language, storefront);
313
- return locale;
314
- });
315
- container.register(DEP_KEYS.STORAGE, () => new MemoryStorage());
316
- container.register(
317
- DEP_KEYS.FEATURE_FLAGS,
318
- () => new DefaultFeatureFlags(featureFlags)
319
- );
320
- container.register(
321
- DEP_KEYS.METRICS,
322
- () => new ConsoleMetrics()
323
- );
324
- container.register(DEP_KEYS.FETCH, () => fetchFn);
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
- });
388
-
389
- // ../core/src/router/router.ts
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, renderModeOrOptions) {
399
- const opts = typeof renderModeOrOptions === "string" ? { renderMode: renderModeOrOptions } : renderModeOrOptions ?? {};
400
- const paramNames = [];
401
- const regexStr = pattern.split(/(\/:[\w]+\??)/).map((segment) => {
402
- const paramMatch = segment.match(/^\/:(\w+)(\?)?$/);
403
- if (paramMatch) {
404
- paramNames.push(paramMatch[1]);
405
- return paramMatch[2] ? "(?:/([^/]+))?" : "/([^/]+)";
406
- }
407
- return segment.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
408
- }).join("");
409
- this.routes.push({
410
- pattern,
411
- intentId,
412
- regex: new RegExp(`^${regexStr}/?$`),
413
- paramNames,
414
- renderMode: opts.renderMode,
415
- beforeGuards: opts.beforeGuards,
416
- afterGuards: opts.afterGuards
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;
431
- });
432
- for (const [k, v] of Object.entries(queryParams)) {
433
- if (!(k in params)) params[k] = v;
434
- }
435
- return {
436
- intent: { id: route.intentId, params },
437
- action: makeFlowAction(urlOrPath),
438
- renderMode: route.renderMode,
439
- beforeGuards: route.beforeGuards,
440
- afterGuards: route.afterGuards
441
- };
442
- }
443
- }
444
- return null;
445
- }
446
- /** 获取所有已注册的路由 */
447
- getRoutes() {
448
- return this.routes.map((r) => `${r.pattern} \u2192 ${r.intentId}`);
449
- }
450
- extractPath(url) {
451
- try {
452
- const parsed = new URL(url, "http://localhost");
453
- return parsed.pathname;
454
- } catch {
455
- return url.split("?")[0].split("#")[0];
456
- }
457
- }
458
- extractQueryParams(url) {
459
- try {
460
- const parsed = new URL(url, "http://localhost");
461
- const params = {};
462
- parsed.searchParams.forEach((v, k) => {
463
- params[k] = v;
464
- });
465
- return params;
466
- } catch {
467
- return {};
468
- }
469
- }
470
- };
471
- }
472
- });
473
-
474
- // ../core/src/logger/composite.ts
475
- var CompositeLoggerFactory, CompositeLogger;
476
- var init_composite = __esm({
477
- "../core/src/logger/composite.ts"() {
478
- "use strict";
479
- CompositeLoggerFactory = class {
480
- constructor(factories) {
481
- this.factories = factories;
482
- }
483
- loggerFor(name) {
484
- return new CompositeLogger(
485
- this.factories.map((f) => f.loggerFor(name))
486
- );
487
- }
488
- };
489
- CompositeLogger = class {
490
- constructor(loggers) {
491
- this.loggers = loggers;
492
- }
493
- debug(...args) {
494
- return this.callAll("debug", args);
495
- }
496
- info(...args) {
497
- return this.callAll("info", args);
498
- }
499
- warn(...args) {
500
- return this.callAll("warn", args);
501
- }
502
- error(...args) {
503
- return this.callAll("error", args);
504
- }
505
- callAll(method, args) {
506
- for (const logger of this.loggers) {
507
- logger[method](...args);
508
- }
509
- return "";
510
- }
511
- };
512
- }
513
- });
514
-
515
- // ../core/src/middleware/pipeline.ts
516
- async function runBeforeLoadGuards(guards, ctx) {
517
- for (const guard of guards) {
518
- const result = await guard(ctx);
519
- if (result.kind !== "next") return result;
520
- }
521
- return { kind: "next" };
522
- }
523
- async function runAfterLoadGuards(guards, ctx) {
524
- for (const guard of guards) {
525
- const result = await guard(ctx);
526
- if (result.kind !== "next") return result;
527
- }
528
- return { kind: "next" };
529
- }
530
- var init_pipeline = __esm({
531
- "../core/src/middleware/pipeline.ts"() {
532
- "use strict";
533
- }
534
- });
535
-
536
- // ../core/src/prefetched-intents/stable-stringify.ts
537
- function stableStringify(obj, _seen) {
538
- if (obj === null || obj === void 0) return String(obj);
539
- if (typeof obj !== "object") return JSON.stringify(obj);
540
- const seen = _seen ?? /* @__PURE__ */ new Set();
541
- if (seen.has(obj)) return '"[Circular]"';
542
- seen.add(obj);
543
- if (Array.isArray(obj)) {
544
- return "[" + obj.map((v) => stableStringify(v, seen)).join(",") + "]";
545
- }
546
- const keys = Object.keys(obj).sort();
547
- const parts = keys.filter((k) => obj[k] !== void 0).map(
548
- (k) => JSON.stringify(k) + ":" + stableStringify(obj[k], seen)
549
- );
550
- return "{" + parts.join(",") + "}";
551
- }
552
- var init_stable_stringify = __esm({
553
- "../core/src/prefetched-intents/stable-stringify.ts"() {
554
- "use strict";
555
- }
556
- });
557
-
558
- // ../core/src/prefetched-intents/prefetched-intents.ts
559
- var PrefetchedIntents;
560
- var init_prefetched_intents = __esm({
561
- "../core/src/prefetched-intents/prefetched-intents.ts"() {
562
- "use strict";
563
- init_stable_stringify();
564
- PrefetchedIntents = class _PrefetchedIntents {
565
- intents;
566
- constructor(intents) {
567
- this.intents = intents;
568
- }
569
- /** 从 PrefetchedIntent 数组创建缓存实例 */
570
- static fromArray(items) {
571
- const map = /* @__PURE__ */ new Map();
572
- for (const item of items) {
573
- if (item.intent && item.data !== void 0) {
574
- const key = stableStringify(item.intent);
575
- map.set(key, item.data);
576
- }
577
- }
578
- return new _PrefetchedIntents(map);
579
- }
580
- /** 创建空缓存实例 */
581
- static empty() {
582
- return new _PrefetchedIntents(/* @__PURE__ */ new Map());
583
- }
584
- /**
585
- * 获取缓存的 Intent 结果(一次性使用)。
586
- * 命中后从缓存中删除。
587
- */
588
- get(intent) {
589
- const key = stableStringify(intent);
590
- const data = this.intents.get(key);
591
- if (data !== void 0) {
592
- this.intents.delete(key);
593
- return data;
594
- }
595
- return void 0;
596
- }
597
- /** 检查缓存中是否有某个 Intent 的数据 */
598
- has(intent) {
599
- return this.intents.has(stableStringify(intent));
600
- }
601
- /** 缓存中的条目数 */
602
- get size() {
603
- return this.intents.size;
604
- }
605
- };
606
- }
607
- });
608
-
609
- // ../core/src/framework.ts
610
- var Framework;
611
- var init_framework = __esm({
612
- "../core/src/framework.ts"() {
613
- "use strict";
614
- init_dispatcher();
615
- init_container();
616
- init_make_dependencies();
617
- init_dispatcher2();
618
- init_pipeline();
619
- init_prefetched_intents();
620
- init_router();
621
- Framework = class _Framework {
622
- container;
623
- intentDispatcher;
624
- actionDispatcher;
625
- router;
626
- prefetchedIntents;
627
- beforeGuards = [];
628
- afterGuards = [];
629
- constructor(container, prefetchedIntents) {
630
- this.container = container;
631
- this.intentDispatcher = new IntentDispatcher();
632
- this.actionDispatcher = new ActionDispatcher();
633
- this.router = new Router();
634
- this.prefetchedIntents = prefetchedIntents;
635
- }
636
- /** 创建并初始化 Framework 实例 */
637
- static create(config = {}) {
638
- const container = new Container();
639
- makeDependencies(container, config);
640
- const fw = new _Framework(
641
- container,
642
- config.prefetchedIntents ?? PrefetchedIntents.empty()
643
- );
644
- config.setupRoutes?.(fw.router);
645
- return fw;
646
- }
647
- /** 分发 Intent — 获取页面数据 */
648
- async dispatch(intent) {
649
- const logger = this.container.resolve(DEP_KEYS.LOGGER);
650
- const cached = this.prefetchedIntents.get(intent);
651
- if (cached !== void 0) {
652
- logger.debug(
653
- `[Framework] re-using prefetched intent response for: ${intent.id}`,
654
- intent.params
655
- );
656
- return cached;
657
- }
658
- logger.debug(
659
- `[Framework] dispatch intent: ${intent.id}`,
660
- intent.params
661
- );
662
- return this.intentDispatcher.dispatch(intent, this.container);
663
- }
664
- /** 执行 Action — 处理用户交互 */
665
- async perform(action) {
666
- const logger = this.container.resolve(DEP_KEYS.LOGGER);
667
- logger.debug(`[Framework] perform action: ${action.kind}`);
668
- return this.actionDispatcher.perform(action);
669
- }
670
- /** 路由 URL — 将 URL 解析为 Intent + Action */
671
- routeUrl(url) {
672
- return this.router.resolve(url);
673
- }
674
- /** 记录页面访问事件 */
675
- didEnterPage(page) {
676
- const metrics = this.container.resolve(
677
- DEP_KEYS.METRICS
678
- );
679
- metrics.recordPageView(page.pageType, {
680
- pageId: page.id,
681
- title: page.title
682
- });
683
- }
684
- /** 注册 Action 处理器 */
685
- onAction(kind, handler) {
686
- this.actionDispatcher.onAction(kind, handler);
687
- }
688
- /** 注册 Intent Controller */
689
- registerIntent(controller) {
690
- this.intentDispatcher.register(controller);
691
- }
692
- // ===== Navigation Middleware =====
693
- /** 注册 beforeLoad 守卫(路由匹配后、数据加载前) */
694
- beforeLoad(guard) {
695
- this.beforeGuards.push(guard);
696
- }
697
- /** 注册 afterLoad 守卫(数据加载后、渲染前) */
698
- afterLoad(guard) {
699
- this.afterGuards.push(guard);
700
- }
701
- /** 执行所有 beforeLoad 守卫(全局 → 路由级) */
702
- runBeforeLoad(ctx, routeGuards) {
703
- const guards = routeGuards?.length ? [...this.beforeGuards, ...routeGuards] : this.beforeGuards;
704
- return runBeforeLoadGuards(guards, ctx);
705
- }
706
- /** 执行所有 afterLoad 守卫(全局 → 路由级) */
707
- runAfterLoad(ctx, routeGuards) {
708
- const guards = routeGuards?.length ? [...this.afterGuards, ...routeGuards] : this.afterGuards;
709
- return runAfterLoadGuards(guards, ctx);
710
- }
711
- /** 销毁 Framework 实例 */
712
- dispose() {
713
- this.container.dispose();
714
- }
715
- };
716
- }
717
- });
718
-
719
- // ../core/src/http/client.ts
720
- var HttpError, HttpClient;
721
- var init_client = __esm({
722
- "../core/src/http/client.ts"() {
723
- "use strict";
724
- HttpError = class extends Error {
725
- constructor(status, statusText, body) {
726
- super(`HTTP ${status}: ${statusText}`);
727
- this.status = status;
728
- this.statusText = statusText;
729
- this.body = body;
730
- this.name = "HttpError";
731
- }
732
- };
733
- HttpClient = class {
734
- baseUrl;
735
- defaultHeaders;
736
- fetchFn;
737
- constructor(config) {
738
- this.baseUrl = config.baseUrl;
739
- this.defaultHeaders = config.defaultHeaders ?? {};
740
- this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
741
- }
742
- /** GET 请求,返回解析后的 JSON */
743
- async get(path, params) {
744
- return this.request("GET", path, { params });
745
- }
746
- /** POST 请求,自动序列化 body 为 JSON */
747
- async post(path, body, params) {
748
- return this.request("POST", path, { body, params });
749
- }
750
- /** PUT 请求 */
751
- async put(path, body, params) {
752
- return this.request("PUT", path, { body, params });
753
- }
754
- /** DELETE 请求 */
755
- async del(path, params) {
756
- return this.request("DELETE", path, { params });
757
- }
758
- /**
759
- * 底层请求方法 — 子类可覆写以自定义行为
760
- *
761
- * 自动处理:
762
- * - URL 拼接 (baseUrl + path + params)
763
- * - 默认 headers 合并
764
- * - JSON body 序列化
765
- * - 响应 JSON 解析
766
- * - 非 2xx 状态码抛出 HttpError
767
- */
768
- async request(method, path, options) {
769
- const url = this.buildUrl(path, options?.params);
770
- const headers = {
771
- ...this.defaultHeaders,
772
- ...options?.headers
773
- };
774
- const init = { method, headers };
775
- if (options?.body !== void 0) {
776
- headers["Content-Type"] = headers["Content-Type"] ?? "application/json";
777
- init.body = JSON.stringify(options.body);
778
- }
779
- const response = await this.fetchFn(url, init);
780
- if (!response.ok) {
781
- const body = await response.text().catch(() => void 0);
782
- throw new HttpError(response.status, response.statusText, body);
783
- }
784
- return response.json();
785
- }
786
- /** 构建完整 URL — 子类可覆写以自定义 URL 拼接逻辑 */
787
- buildUrl(path, params) {
788
- const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
789
- const normalizedPath = path.startsWith("/") ? path : `/${path}`;
790
- const url = new URL(`${base}${normalizedPath}`, "http://placeholder");
791
- if (params) {
792
- for (const [k, v] of Object.entries(params)) {
793
- url.searchParams.set(k, v);
794
- }
795
- }
796
- if (this.baseUrl.startsWith("http")) {
797
- return url.toString();
798
- }
799
- return `${url.pathname}${url.search}`;
800
- }
801
- };
802
- }
803
- });
804
-
805
- // ../core/src/intents/base-controller.ts
806
- var BaseController;
807
- var init_base_controller = __esm({
808
- "../core/src/intents/base-controller.ts"() {
809
- "use strict";
810
- BaseController = class {
811
- /**
812
- * 错误回退 — 子类可选覆写
813
- *
814
- * 当 execute() 抛出异常时调用。
815
- * 默认行为: 重新抛出原始错误。
816
- *
817
- * @param params - Intent 参数
818
- * @param error - execute() 抛出的错误
819
- * @returns 回退数据
820
- */
821
- fallback(params, error) {
822
- throw error;
823
- }
824
- /**
825
- * IntentController.perform() 实现
826
- *
827
- * 自动 try/catch → fallback 模式。
828
- */
829
- async perform(intent, container) {
830
- const params = intent.params ?? {};
831
- try {
832
- return await this.execute(params, container);
833
- } catch (e) {
834
- return this.fallback(
835
- params,
836
- e instanceof Error ? e : new Error(String(e))
837
- );
838
- }
839
- }
840
- };
841
- }
842
- });
843
-
844
- // ../core/src/data/mapper.ts
845
- function pipe(...mappers) {
846
- return (input) => mappers.reduce((acc, mapper) => mapper(acc), input);
847
- }
848
- function pipeAsync(...mappers) {
849
- return async (input) => {
850
- let acc = input;
851
- for (const mapper of mappers) {
852
- acc = await mapper(acc);
853
- }
854
- return acc;
855
- };
856
- }
857
- function mapEach(mapper) {
858
- return (items) => items.map(mapper);
859
- }
860
- var init_mapper = __esm({
861
- "../core/src/data/mapper.ts"() {
862
- "use strict";
863
- }
864
- });
865
-
866
- // ../core/src/bootstrap/define-routes.ts
867
- function defineRoutes(framework, definitions) {
868
- const registeredIntents = /* @__PURE__ */ new Set();
869
- for (const def of definitions) {
870
- if (def.controller && !registeredIntents.has(def.intentId)) {
871
- framework.registerIntent(def.controller);
872
- registeredIntents.add(def.intentId);
873
- }
874
- framework.router.add(def.path, def.intentId, {
875
- renderMode: def.renderMode,
876
- beforeGuards: def.beforeLoad,
877
- afterGuards: def.afterLoad
878
- });
879
- }
880
- }
881
- var init_define_routes = __esm({
882
- "../core/src/bootstrap/define-routes.ts"() {
883
- "use strict";
884
- }
885
- });
886
-
887
- // ../core/src/utils/lru-map.ts
888
- var LruMap;
889
- var init_lru_map = __esm({
890
- "../core/src/utils/lru-map.ts"() {
891
- "use strict";
892
- LruMap = class {
893
- map = /* @__PURE__ */ new Map();
894
- capacity;
895
- constructor(capacity) {
896
- this.capacity = capacity;
897
- }
898
- get(key) {
899
- const value = this.map.get(key);
900
- if (value !== void 0) {
901
- this.map.delete(key);
902
- this.map.set(key, value);
903
- }
904
- return value;
905
- }
906
- set(key, value) {
907
- if (this.map.has(key)) {
908
- this.map.delete(key);
909
- } else if (this.map.size >= this.capacity) {
910
- const oldest = this.map.keys().next().value;
911
- if (oldest !== void 0) {
912
- this.map.delete(oldest);
913
- }
914
- }
915
- this.map.set(key, value);
916
- }
917
- has(key) {
918
- return this.map.has(key);
919
- }
920
- delete(key) {
921
- return this.map.delete(key);
922
- }
923
- get size() {
924
- return this.map.size;
925
- }
926
- clear() {
927
- this.map.clear();
928
- }
929
- };
930
- }
931
- });
932
-
933
- // ../core/src/utils/optional.ts
934
- function isSome(value) {
935
- return value !== null && value !== void 0;
936
- }
937
- function isNone(value) {
938
- return value === null || value === void 0;
939
- }
940
- var init_optional = __esm({
941
- "../core/src/utils/optional.ts"() {
942
- "use strict";
943
- }
944
- });
945
-
946
- // ../core/src/utils/url.ts
947
- function removeScheme(url) {
948
- return url.replace(/^https?:\/\//, "");
949
- }
950
- function removeHost(url) {
951
- try {
952
- const parsed = new URL(url);
953
- return parsed.pathname + parsed.search + parsed.hash;
954
- } catch {
955
- return url;
956
- }
957
- }
958
- function removeQueryParams(url) {
959
- return url.split("?")[0];
960
- }
961
- function getBaseUrl(url) {
962
- return url.split("?")[0].split("#")[0];
963
- }
964
- function buildUrl(path, params) {
965
- if (!params) return path;
966
- const searchParams = new URLSearchParams();
967
- for (const [key, value] of Object.entries(params)) {
968
- if (value !== void 0) {
969
- searchParams.set(key, value);
970
- }
971
- }
972
- const qs = searchParams.toString();
973
- return qs ? `${path}?${qs}` : path;
974
- }
975
- var init_url = __esm({
976
- "../core/src/utils/url.ts"() {
977
- "use strict";
978
- }
979
- });
980
-
981
- // ../core/src/utils/uuid.ts
982
- function generateUuid() {
983
- if (typeof crypto !== "undefined" && crypto.randomUUID) {
984
- return crypto.randomUUID();
985
- }
986
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
987
- const r = Math.random() * 16 | 0;
988
- const v = c === "x" ? r : r & 3 | 8;
989
- return v.toString(16);
990
- });
991
- }
992
- var init_uuid = __esm({
993
- "../core/src/utils/uuid.ts"() {
994
- "use strict";
995
- }
996
- });
997
-
998
- // ../core/src/middleware/context.ts
999
- function parseCookieString(str) {
1000
- const map = /* @__PURE__ */ new Map();
1001
- if (!str) return map;
1002
- for (const pair of str.split(";")) {
1003
- const idx = pair.indexOf("=");
1004
- if (idx === -1) continue;
1005
- const key = pair.slice(0, idx).trim();
1006
- const val = pair.slice(idx + 1).trim();
1007
- if (key) map.set(key, val);
1008
- }
1009
- return map;
1010
- }
1011
- function createServerContext(options) {
1012
- const { url, intent, container, request } = options;
1013
- const parsed = new URL(url, "http://localhost");
1014
- const cookieHeader = request?.headers.get("cookie") ?? "";
1015
- const cookies = parseCookieString(cookieHeader);
1016
- return {
1017
- url,
1018
- path: parsed.pathname,
1019
- params: intent.params ?? {},
1020
- intent,
1021
- isServer: true,
1022
- container,
1023
- getCookie: (name) => cookies.get(name),
1024
- getHeader: (name) => request?.headers.get(name) ?? void 0
1025
- };
1026
- }
1027
- function createBrowserContext(options) {
1028
- const { url, intent, container } = options;
1029
- const parsed = new URL(url, window.location.origin);
1030
- return {
1031
- url,
1032
- path: parsed.pathname,
1033
- params: intent.params ?? {},
1034
- intent,
1035
- isServer: false,
1036
- container,
1037
- getCookie: (name) => parseCookieString(document.cookie).get(name),
1038
- getHeader: () => void 0
1039
- };
1040
- }
1041
- var init_context = __esm({
1042
- "../core/src/middleware/context.ts"() {
1043
- "use strict";
1044
- }
1045
- });
1046
-
1047
- // ../core/src/middleware/types.ts
1048
- function next() {
1049
- return { kind: "next" };
1050
- }
1051
- function redirect(url, status = 302) {
1052
- return { kind: "redirect", url, status };
1053
- }
1054
- function rewrite(url) {
1055
- return { kind: "rewrite", url };
1056
- }
1057
- function deny(status = 403, message = "Forbidden") {
1058
- return { kind: "deny", status, message };
1059
- }
1060
- var init_types2 = __esm({
1061
- "../core/src/middleware/types.ts"() {
1062
- "use strict";
1063
- }
1064
- });
1065
-
1066
- // ../core/src/index.ts
1067
- var init_src = __esm({
1068
- "../core/src/index.ts"() {
1069
- "use strict";
1070
- init_dispatcher();
1071
- init_types();
1072
- init_dispatcher2();
1073
- init_container();
1074
- init_make_dependencies();
1075
- init_router();
1076
- init_base();
1077
- init_composite();
1078
- init_console();
1079
- init_local_storage_filter();
1080
- init_framework();
1081
- init_prefetched_intents();
1082
- init_stable_stringify();
1083
- init_client();
1084
- init_base_controller();
1085
- init_mapper();
1086
- init_define_routes();
1087
- init_lru_map();
1088
- init_optional();
1089
- init_url();
1090
- init_uuid();
1091
- init_context();
1092
- init_pipeline();
1093
- init_types2();
1094
- }
1095
- });
1096
-
1097
- // ../ssr/src/render.ts
1098
- async function ssrRender(options) {
1099
- const {
1100
- url,
1101
- frameworkConfig,
1102
- bootstrap,
1103
- getErrorPage,
1104
- renderApp,
1105
- ssrContext
1106
- } = options;
1107
- const mergedConfig = ssrContext?.fetch ? { ...frameworkConfig, fetch: ssrContext.fetch } : frameworkConfig;
1108
- const framework = Framework.create(mergedConfig);
1109
- bootstrap(framework);
1110
- try {
1111
- const parsed = new URL(url, "http://localhost");
1112
- const fullPath = parsed.pathname + parsed.search;
1113
- const match = framework.routeUrl(fullPath);
1114
- if (match?.renderMode === "csr") {
1115
- return {
1116
- html: "",
1117
- head: "",
1118
- css: "",
1119
- serverData: [],
1120
- renderMode: "csr"
1121
- };
1122
- }
1123
- let page;
1124
- let serverData = [];
1125
- if (match) {
1126
- const navCtx = createServerContext({
1127
- url: fullPath,
1128
- intent: match.intent,
1129
- container: framework.container,
1130
- request: ssrContext?.request
1131
- });
1132
- const beforeResult = await framework.runBeforeLoad(
1133
- navCtx,
1134
- match.beforeGuards
1135
- );
1136
- if (beforeResult.kind !== "next") {
1137
- const earlyReturn = handleMiddlewareResult(
1138
- beforeResult,
1139
- getErrorPage,
1140
- renderApp,
1141
- framework
1142
- );
1143
- if (earlyReturn) return earlyReturn;
1144
- }
1145
- try {
1146
- page = await framework.dispatch(match.intent);
1147
- serverData = [{ intent: match.intent, data: page }];
1148
- } catch (e) {
1149
- console.error(
1150
- `[SSR] dispatch failed for intent "${match.intent.id}":`,
1151
- e
1152
- );
1153
- page = getErrorPage(500, "Internal error");
1154
- }
1155
- const postCtx = {
1156
- ...navCtx,
1157
- page
1158
- };
1159
- const afterResult = await framework.runAfterLoad(
1160
- postCtx,
1161
- match.afterGuards
1162
- );
1163
- if (afterResult.kind !== "next") {
1164
- const lateReturn = handleMiddlewareResult(
1165
- afterResult,
1166
- getErrorPage,
1167
- renderApp,
1168
- framework
1169
- );
1170
- if (lateReturn) return lateReturn;
1171
- }
1172
- } else {
1173
- page = getErrorPage(404, "Page not found");
1174
- }
1175
- const result = renderApp(page, framework);
1176
- return {
1177
- html: result.html,
1178
- head: result.head,
1179
- css: result.css,
1180
- serverData,
1181
- renderMode: match?.renderMode
1182
- };
1183
- } finally {
1184
- framework.dispose();
1185
- }
1186
- }
1187
- function handleMiddlewareResult(result, getErrorPage, renderApp, framework) {
1188
- switch (result.kind) {
1189
- case "next":
1190
- return null;
1191
- case "redirect":
1192
- return {
1193
- html: "",
1194
- head: "",
1195
- css: "",
1196
- serverData: [],
1197
- redirect: { url: result.url, status: result.status }
1198
- };
1199
- case "rewrite":
1200
- return {
1201
- html: "",
1202
- head: "",
1203
- css: "",
1204
- serverData: [],
1205
- redirect: { url: result.url, status: 301 }
1206
- };
1207
- case "deny": {
1208
- const errorPage = getErrorPage(result.status, result.message);
1209
- const rendered = renderApp(errorPage, framework);
1210
- return {
1211
- html: rendered.html,
1212
- head: rendered.head,
1213
- css: rendered.css,
1214
- serverData: []
1215
- };
1216
- }
1217
- }
1218
- }
1219
- var init_render = __esm({
1220
- "../ssr/src/render.ts"() {
1221
- "use strict";
1222
- init_src();
1223
- }
1224
- });
1225
-
1226
- // ../ssr/src/create-render.ts
1227
- function createSSRRender(config) {
1228
- const { bootstrap, getErrorPage, renderApp, frameworkConfig } = config;
1229
- return (url, locale, ssrContext) => ssrRender({
1230
- url,
1231
- frameworkConfig: frameworkConfig ?? {},
1232
- bootstrap,
1233
- getErrorPage,
1234
- renderApp: (page) => renderApp(page, locale),
1235
- ssrContext
1236
- });
1237
- }
1238
- var init_create_render = __esm({
1239
- "../ssr/src/create-render.ts"() {
1240
- "use strict";
1241
- init_render();
1242
- }
1243
- });
1244
-
1245
- // ../ssr/src/inject.ts
1246
- function injectSSRContent(options) {
1247
- const { template, locale, head, css, html, serializedData } = options;
1248
- const cssTag = css ? `<style>${css}</style>` : "";
1249
- return template.replace(SSR_PLACEHOLDERS.LANG, locale).replace(SSR_PLACEHOLDERS.HEAD, `${head}
1250
- ${cssTag}`).replace(SSR_PLACEHOLDERS.BODY, html).replace(
1251
- SSR_PLACEHOLDERS.DATA,
1252
- `<script id="serialized-server-data" type="application/json">${serializedData}</script>`
1253
- );
1254
- }
1255
- function injectCSRShell(template, locale) {
1256
- return template.replace(SSR_PLACEHOLDERS.LANG, locale).replace(SSR_PLACEHOLDERS.HEAD, "").replace(SSR_PLACEHOLDERS.BODY, "").replace(SSR_PLACEHOLDERS.DATA, "");
1257
- }
1258
- var SSR_PLACEHOLDERS;
1259
- var init_inject = __esm({
1260
- "../ssr/src/inject.ts"() {
1261
- "use strict";
1262
- SSR_PLACEHOLDERS = {
1263
- LANG: "<!--ssr-lang-->",
1264
- HEAD: "<!--ssr-head-->",
1265
- BODY: "<!--ssr-body-->",
1266
- DATA: "<!--ssr-data-->"
1267
- };
1268
- }
1269
- });
1270
-
1271
- // ../ssr/src/server-data.ts
1272
- function serializeServerData(data) {
1273
- const json = JSON.stringify(data);
1274
- return json.replace(
1275
- HTML_ESCAPE_PATTERN,
1276
- (match) => HTML_REPLACEMENTS[match] ?? match
1277
- );
1278
- }
1279
- var HTML_REPLACEMENTS, HTML_ESCAPE_PATTERN;
1280
- var init_server_data = __esm({
1281
- "../ssr/src/server-data.ts"() {
1282
- "use strict";
1283
- HTML_REPLACEMENTS = {
1284
- "<": "\\u003C",
1285
- ">": "\\u003E",
1286
- "/": "\\u002F",
1287
- "\u2028": "\\u2028",
1288
- "\u2029": "\\u2029"
1289
- };
1290
- HTML_ESCAPE_PATTERN = /[<>/\u2028\u2029]/g;
1291
- }
1292
- });
1293
-
1294
- // ../ssr/src/index.ts
1295
- var src_exports = {};
1296
- __export(src_exports, {
1297
- Framework: () => Framework,
1298
- SSR_PLACEHOLDERS: () => SSR_PLACEHOLDERS,
1299
- createSSRRender: () => createSSRRender,
1300
- injectCSRShell: () => injectCSRShell,
1301
- injectSSRContent: () => injectSSRContent,
1302
- serializeServerData: () => serializeServerData,
1303
- ssrRender: () => ssrRender
1304
- });
1305
- var init_src2 = __esm({
1306
- "../ssr/src/index.ts"() {
1307
- "use strict";
1308
- init_create_render();
1309
- init_inject();
1310
- init_render();
1311
- init_server_data();
1312
- init_src();
1313
- }
1314
- });
1315
-
1316
- // ../server/src/internal-fetch.ts
1317
- function createInternalFetch(appFetch, depth = 1) {
1318
- return ((input, init) => {
1319
- if (typeof input === "string" && input.startsWith("/")) {
1320
- const request = new Request(`http://localhost${input}`, init);
1321
- request.headers.set(SSR_DEPTH_HEADER, String(depth));
1322
- return Promise.resolve(appFetch(request));
1323
- }
1324
- return globalThis.fetch(input, init);
1325
- });
1326
- }
1327
- var SSR_DEPTH_HEADER, MAX_SSR_DEPTH;
1328
- var init_internal_fetch = __esm({
1329
- "../server/src/internal-fetch.ts"() {
1330
- "use strict";
1331
- SSR_DEPTH_HEADER = "x-ssr-depth";
1332
- MAX_SSR_DEPTH = 5;
1333
- }
1334
- });
1335
-
1336
- // ../server/src/locale.ts
1337
- var locale_exports = {};
1338
- __export(locale_exports, {
1339
- parseAcceptLanguage: () => parseAcceptLanguage
1340
- });
1341
- function parseAcceptLanguage(header, supported, fallback) {
1342
- const effectiveSupported = supported ?? ["zh", "en"];
1343
- const effectiveFallback = fallback ?? effectiveSupported[0] ?? "en";
1344
- if (!header) return effectiveFallback;
1345
- const langs = header.split(",").map((part) => {
1346
- const [lang, q] = part.trim().split(";q=");
1347
- return {
1348
- lang: lang.trim().toLowerCase(),
1349
- q: q ? parseFloat(q) || 0 : 1
1350
- };
1351
- }).sort((a, b) => b.q - a.q);
1352
- for (const { lang } of langs) {
1353
- const prefix = lang.split("-")[0];
1354
- if (effectiveSupported.includes(prefix)) {
1355
- return prefix;
1356
- }
1357
- }
1358
- return effectiveFallback;
1359
- }
1360
- var init_locale = __esm({
1361
- "../server/src/locale.ts"() {
1362
- "use strict";
1363
- }
1364
- });
1365
-
1366
- // ../server/src/app.ts
1367
- var app_exports = {};
1368
- __export(app_exports, {
1369
- createSSRApp: () => createSSRApp
1370
- });
1371
- function matchRenderModeOverride(url, renderModes) {
1372
- if (!renderModes) return null;
1373
- const path = url.split("?")[0];
1374
- if (renderModes[path]) return renderModes[path];
1375
- for (const [pattern, mode] of Object.entries(renderModes)) {
1376
- if (pattern.includes("*")) {
1377
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
1378
- const re = new RegExp("^" + escaped.replace(/\*/g, ".*") + "$");
1379
- if (re.test(path)) return mode;
1380
- }
1381
- }
1382
- return null;
1383
- }
1384
- function createSSRApp(options) {
1385
- const {
1386
- root,
1387
- vite,
1388
- isProduction,
1389
- ssrEntryPath = "/src/ssr.ts",
1390
- ssrProductionModule,
1391
- supportedLocales,
1392
- defaultLocale,
1393
- parentFetch,
1394
- renderModes
1395
- } = options;
1396
- const app = new import_hono.Hono();
1397
- const ISR_CACHE_MAX = 1e3;
1398
- const isrCache = /* @__PURE__ */ new Map();
1399
- function isrSet(key, val) {
1400
- if (isrCache.size >= ISR_CACHE_MAX) {
1401
- const first = isrCache.keys().next().value;
1402
- if (first !== void 0) isrCache.delete(first);
1403
- }
1404
- isrCache.set(key, val);
1405
- }
1406
- let templateCache;
1407
- async function readTemplate(url) {
1408
- if (!isProduction && vite) {
1409
- const { readFileSync: readFileSync2 } = await import(
1410
- /* @vite-ignore */
1411
- "fs"
1412
- );
1413
- const { resolve: resolve2 } = await import(
1414
- /* @vite-ignore */
1415
- "path"
1416
- );
1417
- const raw = readFileSync2(resolve2(root, "index.html"), "utf-8");
1418
- return vite.transformIndexHtml(url, raw);
1419
- }
1420
- if (templateCache) return templateCache;
1421
- const isDeno = typeof globalThis.Deno !== "undefined";
1422
- if (isDeno) {
1423
- templateCache = globalThis.Deno.readTextFileSync(
1424
- new URL("../dist/client/index.html", import_meta.url)
1425
- );
1426
- return templateCache;
1427
- }
1428
- const { readFileSync } = await import(
1429
- /* @vite-ignore */
1430
- "fs"
1431
- );
1432
- const { resolve } = await import(
1433
- /* @vite-ignore */
1434
- "path"
1435
- );
1436
- templateCache = readFileSync(
1437
- resolve(root, "dist/client/index.html"),
1438
- "utf-8"
1439
- );
1440
- return templateCache;
1441
- }
1442
- async function loadSSRModule() {
1443
- if (!isProduction && vite) {
1444
- return await vite.ssrLoadModule(ssrEntryPath);
1445
- }
1446
- if (ssrProductionModule) {
1447
- return import(
1448
- /* @vite-ignore */
1449
- ssrProductionModule
1450
- );
1451
- }
1452
- const { resolve } = await import(
1453
- /* @vite-ignore */
1454
- "path"
1455
- );
1456
- const { pathToFileURL } = await import(
1457
- /* @vite-ignore */
1458
- "url"
1459
- );
1460
- const absPath = pathToFileURL(resolve(root, "dist/server/ssr.js")).href;
1461
- return import(
1462
- /* @vite-ignore */
1463
- absPath
1464
- );
1465
- }
1466
- app.get("*", async (c) => {
1467
- const ssrDepth = parseInt(c.req.header(SSR_DEPTH_HEADER) ?? "0", 10);
1468
- if (ssrDepth >= MAX_SSR_DEPTH) {
1469
- return c.text("SSR recursion loop detected", 508);
1470
- }
1471
- const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
1472
- try {
1473
- const template = await readTemplate(url);
1474
- const { render, serializeServerData: serializeServerData2 } = await loadSSRModule();
1475
- const locale = parseAcceptLanguage(
1476
- c.req.header("accept-language"),
1477
- supportedLocales,
1478
- defaultLocale
1479
- );
1480
- const overrideMode = matchRenderModeOverride(url, renderModes);
1481
- if (overrideMode === "csr") {
1482
- return c.html(injectCSRShell(template, locale));
1483
- }
1484
- const cacheKey = `${locale}:${url}`;
1485
- const cached = isrCache.get(cacheKey);
1486
- if (cached) return c.html(cached);
1487
- const requestFetch = parentFetch ? createInternalFetch(parentFetch, ssrDepth + 1) : void 0;
1488
- const ssrContext = { request: c.req.raw };
1489
- if (requestFetch) ssrContext.fetch = requestFetch;
1490
- const {
1491
- html: appHtml,
1492
- head,
1493
- css,
1494
- serverData,
1495
- renderMode,
1496
- redirect: middlewareRedirect
1497
- } = await render(url, locale, ssrContext);
1498
- if (middlewareRedirect) {
1499
- return c.redirect(
1500
- middlewareRedirect.url,
1501
- middlewareRedirect.status
1502
- );
1503
- }
1504
- if (renderMode === "csr") {
1505
- return c.html(injectCSRShell(template, locale));
1506
- }
1507
- const serializedData = serializeServerData2(serverData);
1508
- const finalHtml = injectSSRContent({
1509
- template,
1510
- locale,
1511
- head,
1512
- css,
1513
- html: appHtml,
1514
- serializedData
1515
- });
1516
- if (renderMode === "prerender" || overrideMode === "prerender") {
1517
- isrSet(cacheKey, finalHtml);
1518
- }
1519
- return c.html(finalHtml);
1520
- } catch (e) {
1521
- if (!isProduction && vite) {
1522
- vite.ssrFixStacktrace(e);
1523
- }
1524
- console.error("[SSR Error]", e);
1525
- return c.text("Internal Server Error", 500);
1526
- }
1527
- });
1528
- return app;
1529
- }
1530
- var import_hono, import_meta;
1531
- var init_app = __esm({
1532
- "../server/src/app.ts"() {
1533
- "use strict";
1534
- init_src2();
1535
- import_hono = require("hono");
1536
- init_internal_fetch();
1537
- init_locale();
1538
- import_meta = {};
1539
- }
1540
- });
1541
-
1542
- // src/index.ts
1543
- var index_exports = {};
1544
- __export(index_exports, {
1545
- ACTION_KINDS: () => ACTION_KINDS,
1546
- ActionDispatcher: () => ActionDispatcher,
1547
- BaseController: () => BaseController,
1548
- BaseLogger: () => BaseLogger,
1549
- CompositeLogger: () => CompositeLogger,
1550
- CompositeLoggerFactory: () => CompositeLoggerFactory,
1551
- ConsoleLogger: () => ConsoleLogger,
1552
- ConsoleLoggerFactory: () => ConsoleLoggerFactory,
1553
- Container: () => Container,
1554
- DEP_KEYS: () => DEP_KEYS,
1555
- Framework: () => Framework,
1556
- History: () => History,
1557
- HttpClient: () => HttpClient,
1558
- HttpError: () => HttpError,
1559
- IntentDispatcher: () => IntentDispatcher,
1560
- LruMap: () => LruMap,
1561
- PrefetchedIntents: () => PrefetchedIntents,
1562
- Router: () => Router,
1563
- SSR_PLACEHOLDERS: () => SSR_PLACEHOLDERS,
1564
- autoAdapter: () => autoAdapter,
1565
- buildUrl: () => buildUrl,
1566
- cloudflareAdapter: () => cloudflareAdapter,
1567
- createBrowserContext: () => createBrowserContext,
1568
- createPrefetchedIntentsFromDom: () => createPrefetchedIntentsFromDom,
1569
- createSSRApp: () => createSSRApp,
1570
- createSSRRender: () => createSSRRender,
1571
- createServer: () => createServer,
1572
- createServerContext: () => createServerContext,
1573
- defineRoutes: () => defineRoutes,
1574
- deny: () => deny,
1575
- deserializeServerData: () => deserializeServerData,
1576
- detectRuntime: () => detectRuntime,
1577
- finesoftFrontViteConfig: () => finesoftFrontViteConfig,
1578
- generateProxyCode: () => generateProxyCode,
1579
- generateUuid: () => generateUuid,
1580
- getBaseUrl: () => getBaseUrl,
1581
- injectCSRShell: () => injectCSRShell,
1582
- injectSSRContent: () => injectSSRContent,
1583
- isCompoundAction: () => isCompoundAction,
1584
- isExternalUrlAction: () => isExternalUrlAction,
1585
- isFlowAction: () => isFlowAction,
1586
- isNone: () => isNone,
1587
- isSome: () => isSome,
1588
- makeDependencies: () => makeDependencies,
1589
- makeExternalUrlAction: () => makeExternalUrlAction,
1590
- makeFlowAction: () => makeFlowAction,
1591
- mapEach: () => mapEach,
1592
- netlifyAdapter: () => netlifyAdapter,
1593
- next: () => next,
1594
- nodeAdapter: () => nodeAdapter,
1595
- parseAcceptLanguage: () => parseAcceptLanguage,
1596
- pipe: () => pipe,
1597
- pipeAsync: () => pipeAsync,
1598
- redirect: () => redirect,
1599
- registerActionHandlers: () => registerActionHandlers,
1600
- registerExternalUrlHandler: () => registerExternalUrlHandler,
1601
- registerFlowActionHandler: () => registerFlowActionHandler,
1602
- registerProxyRoutes: () => registerProxyRoutes,
1603
- removeHost: () => removeHost,
1604
- removeQueryParams: () => removeQueryParams,
1605
- removeScheme: () => removeScheme,
1606
- resetFilterCache: () => resetFilterCache,
1607
- resolveAdapter: () => resolveAdapter,
1608
- resolveRoot: () => resolveRoot,
1609
- rewrite: () => rewrite,
1610
- runAfterLoadGuards: () => runAfterLoadGuards,
1611
- runBeforeLoadGuards: () => runBeforeLoadGuards,
1612
- serializeServerData: () => serializeServerData,
1613
- shouldLog: () => shouldLog,
1614
- ssrRender: () => ssrRender,
1615
- stableStringify: () => stableStringify,
1616
- startBrowserApp: () => startBrowserApp,
1617
- startServer: () => startServer,
1618
- staticAdapter: () => staticAdapter,
1619
- tryScroll: () => tryScroll,
1620
- vercelAdapter: () => vercelAdapter
1621
- });
1622
- module.exports = __toCommonJS(index_exports);
1623
- init_src();
1624
-
1625
- // ../browser/src/action-handlers/external-url-action.ts
1626
- init_src();
1627
- function registerExternalUrlHandler(deps) {
1628
- const { framework, log } = deps;
1629
- framework.onAction(
1630
- ACTION_KINDS.EXTERNAL_URL,
1631
- (action) => {
1632
- log.debug(`ExternalUrlAction \u2192 ${action.url}`);
1633
- window.open(action.url, "_blank", "noopener,noreferrer");
1634
- }
1635
- );
1636
- }
1637
-
1638
- // ../browser/src/action-handlers/flow-action.ts
1639
- init_src();
1640
-
1641
- // ../browser/src/utils/history.ts
1642
- init_src();
1643
-
1644
- // ../browser/src/utils/try-scroll.ts
1645
- var MAX_TRIES = 100;
1646
- var FUDGE = 16;
1647
- var pendingFrame = null;
1648
- function tryScroll(log, getScrollableElement, scrollY) {
1649
- if (pendingFrame !== null) {
1650
- cancelAnimationFrame(pendingFrame);
1651
- pendingFrame = null;
1652
- }
1653
- let tries = 0;
1654
- pendingFrame = requestAnimationFrame(function attempt() {
1655
- if (++tries >= MAX_TRIES) {
1656
- log.warn(
1657
- `tryScroll: gave up after ${MAX_TRIES} frames, target=${scrollY}`
1658
- );
1659
- pendingFrame = null;
1660
- return;
1661
- }
1662
- const el = getScrollableElement();
1663
- if (!el) {
1664
- log.warn(
1665
- "could not restore scroll: the scrollable element is missing"
1666
- );
1667
- return;
1668
- }
1669
- const { scrollHeight, offsetHeight } = el;
1670
- const canScroll = scrollY + offsetHeight <= scrollHeight + FUDGE;
1671
- if (!canScroll) {
1672
- log.info("page is not tall enough for scroll yet", {
1673
- scrollHeight,
1674
- offsetHeight
1675
- });
1676
- pendingFrame = requestAnimationFrame(attempt);
1677
- return;
1678
- }
1679
- el.scrollTop = scrollY;
1680
- log.info("scroll restored to", scrollY);
1681
- pendingFrame = null;
1682
- });
1683
- }
1684
-
1685
- // ../browser/src/utils/history.ts
1686
- var HISTORY_SIZE_LIMIT = 10;
1687
- var History = class {
1688
- entries;
1689
- log;
1690
- getScrollablePageElement;
1691
- currentStateId;
1692
- constructor(log, options, sizeLimit = HISTORY_SIZE_LIMIT) {
1693
- this.entries = new LruMap(sizeLimit);
1694
- this.log = log;
1695
- this.getScrollablePageElement = options.getScrollablePageElement;
1696
- }
1697
- replaceState(state, url) {
1698
- const id = generateUuid();
1699
- window.history.replaceState({ id }, "", url);
1700
- this.currentStateId = id;
1701
- this.entries.set(id, { state, scrollY: 0 });
1702
- this.scrollTop = 0;
1703
- this.log.info("replaceState", state, url, id);
1704
- }
1705
- pushState(state, url) {
1706
- const id = generateUuid();
1707
- window.history.pushState({ id }, "", url);
1708
- this.currentStateId = id;
1709
- this.entries.set(id, { state, scrollY: 0 });
1710
- this.scrollTop = 0;
1711
- this.log.info("pushState", state, url, id);
1712
- }
1713
- beforeTransition() {
1714
- const { state } = window.history;
1715
- if (!state) return;
1716
- const oldEntry = this.entries.get(state.id);
1717
- if (!oldEntry) {
1718
- this.log.info(
1719
- "current history state evicted from LRU, not saving scroll position"
1720
- );
1721
- return;
1722
- }
1723
- const { scrollTop } = this;
1724
- this.entries.set(state.id, { ...oldEntry, scrollY: scrollTop });
1725
- this.log.info("saving scroll position", scrollTop);
1726
- }
1727
- onPopState(listener) {
1728
- window.addEventListener("popstate", async (event) => {
1729
- this.currentStateId = event.state?.id;
1730
- if (!this.currentStateId) {
1731
- this.log.warn(
1732
- "encountered a null event.state.id in onPopState event:",
1733
- window.location.href
1734
- );
1735
- }
1736
- this.log.info("popstate", this.entries, this.currentStateId);
1737
- const entry = this.currentStateId ? this.entries.get(this.currentStateId) : void 0;
1738
- await listener(window.location.href, entry?.state);
1739
- if (!entry) {
1740
- return;
1741
- }
1742
- const { scrollY } = entry;
1743
- this.log.info("restoring scroll to", scrollY);
1744
- tryScroll(this.log, () => this.getScrollablePageElement(), scrollY);
1745
- });
1746
- }
1747
- /** 仅推入 URL,不缓存页面状态(用于页面加载失败场景) */
1748
- pushUrl(url) {
1749
- const id = generateUuid();
1750
- window.history.pushState({ id }, "", url);
1751
- this.currentStateId = id;
1752
- this.scrollTop = 0;
1753
- this.log.info("pushUrl (no state)", url, id);
1754
- }
1755
- /** 仅替换 URL,不缓存页面状态(用于页面加载失败场景) */
1756
- replaceUrl(url) {
1757
- const id = generateUuid();
1758
- window.history.replaceState({ id }, "", url);
1759
- this.currentStateId = id;
1760
- this.scrollTop = 0;
1761
- this.log.info("replaceUrl (no state)", url, id);
1762
- }
1763
- updateState(update) {
1764
- if (!this.currentStateId) {
1765
- this.log.warn(
1766
- "failed: encountered a null currentStateId inside updateState"
1767
- );
1768
- return;
1769
- }
1770
- const currentState = this.entries.get(this.currentStateId);
1771
- const newState = update(currentState?.state);
1772
- this.log.info("updateState", newState, this.currentStateId);
1773
- this.entries.set(this.currentStateId, {
1774
- ...currentState,
1775
- state: newState
1776
- });
1777
- }
1778
- get scrollTop() {
1779
- return this.getScrollablePageElement()?.scrollTop || 0;
1780
- }
1781
- set scrollTop(scrollTop) {
1782
- const element = this.getScrollablePageElement();
1783
- if (element) {
1784
- element.scrollTop = scrollTop;
1785
- }
1786
- }
1787
- };
1788
-
1789
- // ../browser/src/action-handlers/flow-action.ts
1790
- function registerFlowActionHandler(deps) {
1791
- const { framework, log, callbacks, updateApp } = deps;
1792
- let isFirstPage = true;
1793
- let navigationId = 0;
1794
- const MAX_REDIRECTS = 5;
1795
- const defaultGetScrollable = () => document.getElementById("scrollable-page-override") || document.getElementById("scrollable-page") || document.documentElement;
1796
- const history = new History(log, {
1797
- getScrollablePageElement: deps.getScrollablePageElement ?? defaultGetScrollable
1798
- });
1799
- async function navigateTo(url, redirectCount, thisNav) {
1800
- if (redirectCount >= MAX_REDIRECTS) {
1801
- log.error(
1802
- `Navigation redirect loop detected (${MAX_REDIRECTS} redirects), stopping at: ${url}`
1803
- );
1804
- return;
1805
- }
1806
- const shouldReplace = isFirstPage || url === window.location.pathname + window.location.search;
1807
- const match = framework.routeUrl(url);
1808
- if (!match) {
1809
- log.warn(`FlowAction: no route for ${url}`);
1810
- return;
1811
- }
1812
- const navCtx = createBrowserContext({
1813
- url,
1814
- intent: match.intent,
1815
- container: framework.container
1816
- });
1817
- const beforeResult = await framework.runBeforeLoad(
1818
- navCtx,
1819
- match.beforeGuards
1820
- );
1821
- if (beforeResult.kind === "redirect") {
1822
- log.debug(`beforeLoad \u2192 redirect to ${beforeResult.url}`);
1823
- await navigateTo(beforeResult.url, redirectCount + 1, thisNav);
1824
- return;
1825
- }
1826
- if (beforeResult.kind === "deny") {
1827
- log.warn(
1828
- `beforeLoad \u2192 denied (${beforeResult.status}): ${beforeResult.message}`
1829
- );
1830
- return;
1831
- }
1832
- if (beforeResult.kind === "rewrite") {
1833
- log.debug(`beforeLoad \u2192 rewrite to ${beforeResult.url}`);
1834
- await navigateTo(beforeResult.url, redirectCount + 1, thisNav);
1835
- return;
1836
- }
1837
- const pagePromise = framework.dispatch(
1838
- match.intent
1839
- );
1840
- await Promise.race([
1841
- pagePromise,
1842
- new Promise((r) => setTimeout(r, 500))
1843
- ]).catch(() => {
1844
- });
1845
- if (thisNav !== navigationId) {
1846
- log.info("FlowAction superseded by newer navigation", url);
1847
- return;
1848
- }
1849
- history.beforeTransition();
1850
- updateApp({
1851
- page: pagePromise.then(
1852
- async (page) => {
1853
- if (thisNav !== navigationId) {
1854
- log.info("FlowAction commit superseded", url);
1855
- return page;
1856
- }
1857
- const postCtx = {
1858
- ...navCtx,
1859
- page
1860
- };
1861
- const afterResult = await framework.runAfterLoad(
1862
- postCtx,
1863
- match.afterGuards
1864
- );
1865
- if (afterResult.kind === "redirect") {
1866
- log.debug(`afterLoad \u2192 redirect to ${afterResult.url}`);
1867
- navigateTo(afterResult.url, redirectCount + 1, thisNav);
1868
- return page;
1869
- }
1870
- let canonicalURL = url;
1871
- if (afterResult.kind === "rewrite") {
1872
- canonicalURL = afterResult.url;
1873
- log.debug(`afterLoad \u2192 rewrite URL to ${canonicalURL}`);
1874
- }
1875
- if (afterResult.kind === "deny") {
1876
- log.warn(`afterLoad \u2192 denied (${afterResult.status})`);
1877
- return page;
1878
- }
1879
- if (shouldReplace) {
1880
- history.replaceState({ page }, canonicalURL);
1881
- } else {
1882
- history.pushState({ page }, canonicalURL);
1883
- }
1884
- callbacks.onNavigate(
1885
- new URL(canonicalURL, window.location.origin).pathname
1886
- );
1887
- didEnterPage(page);
1888
- return page;
1889
- },
1890
- (error) => {
1891
- if (thisNav === navigationId) {
1892
- const canonicalURL = url;
1893
- if (shouldReplace) {
1894
- history.replaceUrl(canonicalURL);
1895
- } else {
1896
- history.pushUrl(canonicalURL);
1897
- }
1898
- callbacks.onNavigate(
1899
- new URL(canonicalURL, window.location.origin).pathname
1900
- );
1901
- }
1902
- throw error;
1903
- }
1904
- ),
1905
- isFirstPage
1906
- });
1907
- isFirstPage = false;
1908
- }
1909
- framework.onAction(ACTION_KINDS.FLOW, async (action) => {
1910
- const flowAction = action;
1911
- const url = flowAction.url;
1912
- log.debug(`FlowAction \u2192 ${url}`);
1913
- if (flowAction.presentationContext === "modal") {
1914
- const match = framework.routeUrl(url);
1915
- if (match) {
1916
- const page = await framework.dispatch(
1917
- match.intent
1918
- );
1919
- callbacks.onModal(page);
1920
- }
1921
- return;
1922
- }
1923
- const thisNav = ++navigationId;
1924
- await navigateTo(url, 0, thisNav);
1925
- });
1926
- history.onPopState(async (url, cachedState) => {
1927
- log.debug(`popstate \u2192 ${url}, cached=${!!cachedState}`);
1928
- callbacks.onNavigate(new URL(url).pathname);
1929
- if (cachedState) {
1930
- const { page } = cachedState;
1931
- didEnterPage(page);
1932
- updateApp({ page, isFirstPage });
1933
- return;
1934
- }
1935
- const parsed = new URL(url);
1936
- const routeMatch = framework.routeUrl(parsed.pathname + parsed.search);
1937
- if (!routeMatch) {
1938
- log.error(
1939
- "received popstate without data, but URL was unroutable:",
1940
- url
1941
- );
1942
- didEnterPage(null);
1943
- updateApp({
1944
- page: Promise.reject(new Error("404")),
1945
- isFirstPage
1946
- });
1947
- return;
1948
- }
1949
- const navCtx = createBrowserContext({
1950
- url: parsed.pathname + parsed.search,
1951
- intent: routeMatch.intent,
1952
- container: framework.container
1953
- });
1954
- const beforeResult = await framework.runBeforeLoad(
1955
- navCtx,
1956
- routeMatch.beforeGuards
1957
- );
1958
- if (beforeResult.kind === "redirect") {
1959
- log.debug(`popstate beforeLoad \u2192 redirect to ${beforeResult.url}`);
1960
- const thisNav = ++navigationId;
1961
- await navigateTo(beforeResult.url, 0, thisNav);
1962
- return;
1963
- }
1964
- if (beforeResult.kind === "deny" || beforeResult.kind === "rewrite") {
1965
- if (beforeResult.kind === "deny") {
1966
- log.warn(`popstate beforeLoad \u2192 denied`);
1967
- } else {
1968
- const thisNav = ++navigationId;
1969
- await navigateTo(beforeResult.url, 0, thisNav);
1970
- }
1971
- return;
1972
- }
1973
- const pagePromise = framework.dispatch(
1974
- routeMatch.intent
1975
- );
1976
- await Promise.race([
1977
- pagePromise,
1978
- new Promise((r) => setTimeout(r, 500))
1979
- ]).catch(() => {
1980
- });
1981
- updateApp({
1982
- page: pagePromise.then((page) => {
1983
- didEnterPage(page);
1984
- return page;
1985
- }),
1986
- isFirstPage
1987
- });
1988
- });
1989
- function didEnterPage(page) {
1990
- (async () => {
1991
- try {
1992
- if (page) {
1993
- await framework.didEnterPage(page);
1994
- }
1995
- } catch (e) {
1996
- log.error("didEnterPage error:", e);
1997
- }
1998
- })();
1999
- }
2000
- }
2001
-
2002
- // ../browser/src/action-handlers/register.ts
2003
- function registerActionHandlers(deps) {
2004
- const { framework, log, callbacks, updateApp } = deps;
2005
- registerFlowActionHandler({
2006
- framework,
2007
- log,
2008
- callbacks,
2009
- updateApp,
2010
- getScrollablePageElement: deps.getScrollablePageElement
2011
- });
2012
- registerExternalUrlHandler({ framework, log });
2013
- }
2014
-
2015
- // ../browser/src/start-app.ts
2016
- init_src();
2017
-
2018
- // ../browser/src/server-data.ts
2019
- init_src();
2020
- var SERVER_DATA_ID = "serialized-server-data";
2021
- function deserializeServerData() {
2022
- const script = document.getElementById(SERVER_DATA_ID);
2023
- if (!script?.textContent) return void 0;
2024
- script.parentNode?.removeChild(script);
2025
- try {
2026
- return JSON.parse(script.textContent);
2027
- } catch {
2028
- return void 0;
2029
- }
2030
- }
2031
- function createPrefetchedIntentsFromDom() {
2032
- const data = deserializeServerData();
2033
- if (!data || !Array.isArray(data)) {
2034
- return PrefetchedIntents.empty();
2035
- }
2036
- return PrefetchedIntents.fromArray(data);
2037
- }
2038
-
2039
- // ../browser/src/start-app.ts
2040
- async function startBrowserApp(config) {
2041
- const {
2042
- bootstrap,
2043
- defaultLocale = "en",
2044
- mountId = "app",
2045
- mount,
2046
- callbacks
2047
- } = config;
2048
- const prefetchedIntents = createPrefetchedIntentsFromDom();
2049
- const framework = Framework.create({ prefetchedIntents });
2050
- bootstrap(framework);
2051
- const loggerFactory = framework.container.resolve(
2052
- DEP_KEYS.LOGGER_FACTORY
2053
- );
2054
- const log = loggerFactory.loggerFor("browser");
2055
- const initialAction = framework.routeUrl(
2056
- window.location.pathname + window.location.search
2057
- );
2058
- const locale = document.documentElement.lang || defaultLocale;
2059
- const target = document.getElementById(mountId);
2060
- const updateApp = mount(target, { framework, locale });
2061
- registerActionHandlers({
2062
- framework,
2063
- log,
2064
- callbacks,
2065
- updateApp,
2066
- getScrollablePageElement: config.getScrollablePageElement
2067
- });
2068
- if (initialAction) {
2069
- await framework.perform(initialAction.action);
2070
- } else {
2071
- updateApp({
2072
- page: Promise.reject(new Error("404")),
2073
- isFirstPage: true
2074
- });
2075
- }
2076
- }
2077
-
2078
- // ../browser/src/index.ts
2079
- init_src();
2080
-
2081
- // src/index.ts
2082
- init_src2();
2083
-
2084
- // ../server/src/proxy.ts
2085
- function sanitizeProxyPath(raw) {
2086
- if (raw.startsWith("//")) return null;
2087
- return raw.startsWith("/") ? raw : `/${raw}`;
2088
- }
2089
- function validateConfig(config) {
2090
- if (!config.prefix.startsWith("/")) {
2091
- throw new Error(
2092
- `[proxy] prefix must start with "/": "${config.prefix}"`
2093
- );
2094
- }
2095
- if (!config.target.startsWith("https://")) {
2096
- throw new Error(`[proxy] target must use HTTPS: "${config.target}"`);
2097
- }
2098
- }
2099
- function registerProxyRoutes(app, configs) {
2100
- for (const config of configs) {
2101
- validateConfig(config);
2102
- const methods = config.methods ?? ["all"];
2103
- const pattern = `${config.prefix}/*`;
2104
- const handler = async (c) => {
2105
- const subPath = sanitizeProxyPath(
2106
- c.req.path.replace(config.prefix, "")
2107
- );
2108
- if (!subPath) return c.text("Invalid path", 400);
2109
- const targetUrl = new URL(subPath, config.target);
2110
- const reqUrl = new URL(c.req.url);
2111
- reqUrl.searchParams.forEach(
2112
- (v, k) => targetUrl.searchParams.set(k, v)
2113
- );
2114
- const headers = { ...config.headers };
2115
- if (config.auth) {
2116
- const token = process.env[config.auth.envKey] ?? "";
2117
- if (token) {
2118
- headers.Authorization = config.auth.type === "bearer" ? `Bearer ${token}` : `Basic ${token}`;
2119
- }
2120
- }
2121
- try {
2122
- const resp = await fetch(targetUrl.toString(), {
2123
- headers,
2124
- redirect: config.followRedirects ? "follow" : "manual"
2125
- });
2126
- const body = await resp.text();
2127
- const respHeaders = {
2128
- "Content-Type": resp.headers.get("Content-Type") ?? "application/json"
2129
- };
2130
- if (config.cache) {
2131
- respHeaders["Cache-Control"] = config.cache;
2132
- }
2133
- return c.newResponse(body, resp.status, respHeaders);
2134
- } catch (e) {
2135
- console.error(`[Proxy ${config.prefix}]`, e);
2136
- return c.json({ error: "Proxy request failed" }, 502);
2137
- }
2138
- };
2139
- for (const method of methods) {
2140
- app[method](pattern, handler);
2141
- }
2142
- }
2143
- }
2144
- function generateProxyCode(configs) {
2145
- if (!configs || configs.length === 0) return "";
2146
- for (const config of configs) {
2147
- validateConfig(config);
2148
- }
2149
- const blocks = [];
2150
- blocks.push(`
2151
- // \u2500\u2500\u2500 \u6846\u67B6\u58F0\u660E\u5F0F\u4EE3\u7406\u8DEF\u7531 \u2500\u2500\u2500
2152
- function _sanitizeProxyPath(raw) {
2153
- if (raw.startsWith("//")) return null;
2154
- return raw.startsWith("/") ? raw : "/" + raw;
2155
- }
2156
- `);
2157
- for (const config of configs) {
2158
- const methods = config.methods ?? ["all"];
2159
- const pattern = `"${config.prefix}/*"`;
2160
- const headersJson = JSON.stringify(config.headers ?? {});
2161
- const cacheStr = config.cache ? JSON.stringify(config.cache) : "null";
2162
- const redirect2 = config.followRedirects ? '"follow"' : '"manual"';
2163
- let authCode = "";
2164
- if (config.auth) {
2165
- const envKey = JSON.stringify(config.auth.envKey);
2166
- const prefix = config.auth.type === "bearer" ? "Bearer " : "Basic ";
2167
- authCode = `
2168
- const _token = (typeof process !== "undefined" && process.env && process.env[${envKey}]) || "";
2169
- if (_token) _headers.Authorization = "${prefix}" + _token;`;
2170
- }
2171
- const handlerCode = `async (c) => {
2172
- const _sub = _sanitizeProxyPath(c.req.path.replace(${JSON.stringify(
2173
- config.prefix
2174
- )}, ""));
2175
- if (!_sub) return c.text("Invalid path", 400);
2176
- const _target = new URL(_sub, ${JSON.stringify(config.target)});
2177
- const _reqUrl = new URL(c.req.url);
2178
- _reqUrl.searchParams.forEach((v, k) => _target.searchParams.set(k, v));
2179
- const _headers = ${headersJson};${authCode}
2180
- try {
2181
- const _resp = await fetch(_target.toString(), { headers: _headers, redirect: ${redirect2} });
2182
- const _body = await _resp.text();
2183
- const _rh = { "Content-Type": _resp.headers.get("Content-Type") || "application/json" };
2184
- if (${cacheStr}) _rh["Cache-Control"] = ${cacheStr};
2185
- return c.newResponse(_body, _resp.status, _rh);
2186
- } catch (_e) {
2187
- console.error("[Proxy ${config.prefix}]", _e);
2188
- return c.json({ error: "Proxy request failed" }, 502);
2189
- }
2190
- }`;
2191
- for (const method of methods) {
2192
- blocks.push(`app.${method}(${pattern}, ${handlerCode});`);
2193
- }
2194
- }
2195
- return blocks.join("\n");
2196
- }
2197
-
2198
- // ../server/src/adapters/shared.ts
2199
- var BUILD_TOOL_EXTERNALS = [
2200
- "vite",
2201
- "esbuild",
2202
- "rollup",
2203
- "fsevents",
2204
- "lightningcss"
2205
- ];
2206
- function generateSSREntry(ctx, opts) {
2207
- const setupImport = ctx.setupPath ? `import _setupDefault from "./${ctx.setupPath}";` : ``;
2208
- const setupCall = ctx.setupPath ? `if (typeof _setupDefault === "function") await _setupDefault(app);` : ``;
2209
- const locales = JSON.stringify(ctx.locales);
2210
- const defaultLocale = JSON.stringify(ctx.defaultLocale);
2211
- const renderModes = JSON.stringify(ctx.renderModes ?? {});
2212
- const cacheImpl = opts.platformCache ? opts.platformCache : `
2213
- const ISR_CACHE_MAX = 1000;
2214
- const _isrMap = new Map();
2215
- async function platformCacheGet(url) {
2216
- return _isrMap.get(url) ?? null;
2217
- }
2218
- async function platformCacheSet(url, html) {
2219
- if (_isrMap.size >= ISR_CACHE_MAX) {
2220
- const first = _isrMap.keys().next().value;
2221
- _isrMap.delete(first);
2222
- }
2223
- _isrMap.set(url, html);
2224
- }`;
2225
- return `
2226
- import { Hono } from "hono";
2227
- ${opts.platformImport}
2228
- import { render, serializeServerData } from "./${ctx.ssrEntry}";
2229
- ${setupImport}
2230
-
2231
- const TEMPLATE = ${JSON.stringify(ctx.templateHtml)};
2232
- const LOCALES = ${locales};
2233
- const DEFAULT_LOCALE = ${defaultLocale};
2234
- const RENDER_MODES = ${renderModes};
2235
- ${cacheImpl}
2236
-
2237
- function parseAcceptLanguage(header) {
2238
- if (!header) return DEFAULT_LOCALE;
2239
- const langs = header.split(",").map(p => {
2240
- const [l, q] = p.trim().split(";q=");
2241
- return { l: l.trim().toLowerCase(), q: q ? (+q || 0) : 1 };
2242
- }).sort((a, b) => b.q - a.q);
2243
- for (const { l } of langs) {
2244
- const prefix = l.split("-")[0];
2245
- if (LOCALES.includes(prefix)) return prefix;
2246
- }
2247
- return DEFAULT_LOCALE;
2248
- }
2249
-
2250
- function injectSSR(t, locale, head, css, html, data) {
2251
- return t
2252
- .replace("<!--ssr-lang-->", locale)
2253
- .replace("<!--ssr-head-->", head + "\\n<style>" + css + "</style>")
2254
- .replace("<!--ssr-body-->", html)
2255
- .replace("<!--ssr-data-->", '<script id="serialized-server-data" type="application/json">' + data + "</script>");
2256
- }
2257
-
2258
- function injectCSRShell(t, locale) {
2259
- return t
2260
- .replace("<!--ssr-lang-->", locale)
2261
- .replace("<!--ssr-head-->", "")
2262
- .replace("<!--ssr-body-->", "")
2263
- .replace("<!--ssr-data-->", "");
2264
- }
2265
-
2266
- function matchRenderMode(url) {
2267
- const path = url.split("?")[0];
2268
- if (RENDER_MODES[path]) return RENDER_MODES[path];
2269
- for (const [pattern, mode] of Object.entries(RENDER_MODES)) {
2270
- if (pattern.includes("*")) {
2271
- const escaped = pattern.replace(/[.+?^\${}()|[\\]\\\\]/g, "\\\\$&");
2272
- const re = new RegExp("^" + escaped.replace(/\\*/g, ".*") + "$");
2273
- if (re.test(path)) return mode;
2274
- }
2275
- }
2276
- return null;
2277
- }
2278
-
2279
- const app = new Hono();
2280
- ${generateProxyCode(ctx.proxies ?? [])}
2281
- ${setupCall}
2282
- ${opts.platformMiddleware ?? ""}
2283
-
2284
- // \u5185\u90E8 fetch \u56DE\u73AF\uFF1ASSR \u63A7\u5236\u5668\u7684 fetch \u8BF7\u6C42\u76F4\u63A5\u8D70 Hono \u5185\u5B58\u8DEF\u7531
2285
- // \u6DF1\u5EA6\u901A\u8FC7\u8BF7\u6C42\u5934\u4F20\u9012\uFF0C\u5E76\u53D1\u5B89\u5168\u4E14\u80FD\u8DE8\u6E32\u67D3\u6B63\u786E\u8FFD\u8E2A\u9012\u5F52
2286
- const _SSR_DEPTH_HEADER = "x-ssr-depth";
2287
- const _MAX_SSR_DEPTH = 5;
2288
-
2289
- function _createInternalFetch(depth) {
2290
- return function(input, init) {
2291
- if (typeof input === "string" && input.startsWith("/")) {
2292
- const req = new Request("http://localhost" + input, init);
2293
- req.headers.set(_SSR_DEPTH_HEADER, String(depth));
2294
- return app.fetch(req);
2295
- }
2296
- return globalThis.fetch(input, init);
2297
- };
2298
- }
2299
-
2300
- app.get("*", async (c) => {
2301
- // \u9012\u5F52\u6DF1\u5EA6\u4FDD\u62A4\uFF1A\u4ECE\u8BF7\u6C42\u5934\u8BFB\u53D6 SSR \u6DF1\u5EA6
2302
- const _ssrDepth = parseInt(c.req.header(_SSR_DEPTH_HEADER) || "0", 10);
2303
- if (_ssrDepth >= _MAX_SSR_DEPTH) {
2304
- return c.text("SSR recursion loop detected", 508);
2305
- }
2306
-
2307
- const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
2308
- try {
2309
- const locale = parseAcceptLanguage(c.req.header("accept-language"));
2310
-
2311
- // Vite \u914D\u7F6E\u7EA7\u522B\u8986\u76D6: CSR \u76F4\u63A5\u8FD4\u56DE\u7A7A\u58F3
2312
- const overrideMode = matchRenderMode(url);
2313
- if (overrideMode === "csr") {
2314
- return c.html(injectCSRShell(TEMPLATE, locale));
2315
- }
2316
-
2317
- // ISR \u7F13\u5B58\u547D\u4E2D\uFF08key \u542B locale\uFF0C\u907F\u514D\u8DE8\u8BED\u8A00\u7F13\u5B58\u6C61\u67D3\uFF09
2318
- const _cacheKey = locale + ":" + url;
2319
- const cached = await platformCacheGet(_cacheKey);
2320
- if (cached) return c.html(cached);
2321
-
2322
- const { html: appHtml, head, css, serverData, renderMode } = await render(url, locale, { fetch: _createInternalFetch(_ssrDepth + 1) });
2323
-
2324
- // \u8DEF\u7531\u7EA7 CSR
2325
- if (renderMode === "csr") {
2326
- return c.html(injectCSRShell(TEMPLATE, locale));
2327
- }
2328
-
2329
- const serializedData = serializeServerData(serverData);
2330
- const finalHtml = injectSSR(TEMPLATE, locale, head, css, appHtml, serializedData);
2331
-
2332
- // Prerender ISR \u7F13\u5B58\uFF08\u5305\u62EC Vite \u914D\u7F6E\u8986\u76D6\u548C\u8DEF\u7531\u7EA7\uFF09
2333
- if (renderMode === "prerender" || overrideMode === "prerender") {
2334
- await platformCacheSet(_cacheKey, finalHtml);
2335
- ${opts.platformPrerenderResponseHook ?? ""}
2336
- }
2337
-
2338
- return c.html(finalHtml);
2339
- } catch (e) {
2340
- console.error("[SSR Error]", e);
2341
- return c.text("Internal Server Error", 500);
2342
- }
2343
- });
2344
-
2345
- ${opts.platformExport}
2346
- `;
2347
- }
2348
- async function buildBundle(ctx, opts) {
2349
- await ctx.vite.build({
2350
- root: ctx.root,
2351
- build: {
2352
- ssr: opts.entry,
2353
- outDir: opts.outDir,
2354
- emptyOutDir: true,
2355
- target: opts.target ?? "node18",
2356
- rollupOptions: {
2357
- output: { entryFileNames: opts.fileName ?? "index.mjs" }
2358
- }
2359
- },
2360
- ssr: {
2361
- noExternal: opts.noExternal !== false,
2362
- external: opts.external ?? BUILD_TOOL_EXTERNALS
2363
- },
2364
- resolve: ctx.resolvedResolve,
2365
- css: ctx.resolvedCss
2366
- });
2367
- }
2368
- function copyStaticAssets(ctx, destDir, opts) {
2369
- const { fs, path } = ctx;
2370
- fs.cpSync(path.resolve(ctx.root, "dist/client"), destDir, {
2371
- recursive: true
2372
- });
2373
- if (opts?.excludeHtml !== false) {
2374
- fs.rmSync(path.join(destDir, "index.html"), { force: true });
2375
- }
2376
- }
2377
- async function prerenderRoutes(ctx) {
2378
- const { fs, path, root, vite } = ctx;
2379
- const { pathToFileURL } = await import(
2380
- /* @vite-ignore */
2381
- "url"
2382
- );
2383
- const routesExport = ctx.bootstrapEntry ?? "src/lib/bootstrap.ts";
2384
- let routes = [];
2385
- const routesFileExists = fs.existsSync(path.resolve(root, routesExport));
2386
- if (routesFileExists) {
2387
- await vite.build({
2388
- root,
2389
- build: {
2390
- ssr: routesExport,
2391
- outDir: path.resolve(root, "dist/server"),
2392
- emptyOutDir: false,
2393
- rollupOptions: {
2394
- output: { entryFileNames: "_routes_prerender.mjs" }
2395
- }
2396
- },
2397
- resolve: ctx.resolvedResolve
2398
- });
2399
- const routesPath = pathToFileURL(
2400
- path.resolve(root, "dist/server/_routes_prerender.mjs")
2401
- ).href;
2402
- const routesMod = await import(
2403
- /* @vite-ignore */
2404
- routesPath
2405
- );
2406
- routes = routesMod.routes ?? routesMod.default ?? [];
2407
- fs.rmSync(path.resolve(root, "dist/server/_routes_prerender.mjs"), {
2408
- force: true
2409
- });
2410
- }
2411
- const prerenderPaths = /* @__PURE__ */ new Set();
2412
- for (const r of routes) {
2413
- if (r.renderMode === "prerender" && r.path && !r.path.includes(":")) {
2414
- prerenderPaths.add(r.path);
2415
- }
2416
- }
2417
- if (ctx.renderModes) {
2418
- for (const [pattern, mode] of Object.entries(ctx.renderModes)) {
2419
- if (mode === "prerender" && !pattern.includes("*") && !pattern.includes(":")) {
2420
- prerenderPaths.add(pattern);
2421
- }
2422
- }
2423
- }
2424
- if (prerenderPaths.size === 0) return [];
2425
- const ssrPath = pathToFileURL(
2426
- path.resolve(root, "dist/server/ssr.js")
2427
- ).href;
2428
- const ssrModule = await import(
2429
- /* @vite-ignore */
2430
- ssrPath
2431
- );
2432
- const results = [];
2433
- for (const routePath of prerenderPaths) {
2434
- for (const locale of ctx.locales) {
2435
- const url = locale === ctx.defaultLocale ? routePath : `/${locale}${routePath === "/" ? "" : routePath}`;
2436
- try {
2437
- const {
2438
- html: appHtml,
2439
- head,
2440
- css,
2441
- serverData
2442
- } = await ssrModule.render(url, locale);
2443
- const serializedData = ssrModule.serializeServerData(serverData);
2444
- const finalHtml = ctx.templateHtml.replace("<!--ssr-lang-->", locale).replace(
2445
- "<!--ssr-head-->",
2446
- head + "\n<style>" + css + "</style>"
2447
- ).replace("<!--ssr-body-->", appHtml).replace(
2448
- "<!--ssr-data-->",
2449
- '<script id="serialized-server-data" type="application/json">' + serializedData + "</script>"
2450
- );
2451
- results.push({ url, html: finalHtml });
2452
- } catch (e) {
2453
- console.warn(` [prerender] Failed to render ${url}:`, e);
2454
- }
2455
- }
2456
- }
2457
- if (results.length > 0) {
2458
- console.log(
2459
- ` Pre-rendered ${results.length} pages (${prerenderPaths.size} routes \xD7 ${ctx.locales.length} locales)
2460
- `
2461
- );
2462
- }
2463
- return results;
2464
- }
2465
-
2466
- // ../server/src/adapters/cloudflare.ts
2467
- function cloudflareAdapter() {
2468
- return {
2469
- name: "cloudflare",
2470
- async build(ctx) {
2471
- const { fs, path, root } = ctx;
2472
- const outputDir = path.resolve(root, "dist/cloudflare");
2473
- fs.rmSync(outputDir, { recursive: true, force: true });
2474
- const entrySource = generateSSREntry(ctx, {
2475
- platformImport: ``,
2476
- platformExport: `export default app;`,
2477
- // Cloudflare Cache API — 持久化 ISR 缓存到 CDN 边缘节点
2478
- platformCache: `
2479
- const ISR_CACHE_TTL = 3600; // 1 hour
2480
- async function platformCacheGet(url) {
2481
- try {
2482
- const cache = caches.default;
2483
- const cacheKey = new Request("https://isr-cache/" + encodeURIComponent(url));
2484
- const resp = await cache.match(cacheKey);
2485
- if (resp) return await resp.text();
2486
- } catch {}
2487
- return null;
2488
- }
2489
- async function platformCacheSet(url, html) {
2490
- try {
2491
- const cache = caches.default;
2492
- const cacheKey = new Request("https://isr-cache/" + encodeURIComponent(url));
2493
- const resp = new Response(html, {
2494
- headers: { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "public, max-age=" + ISR_CACHE_TTL },
2495
- });
2496
- await cache.put(cacheKey, resp);
2497
- } catch {}
2498
- }`
2499
- });
2500
- const tempEntry = path.resolve(root, ".cf-entry.tmp.mjs");
2501
- fs.writeFileSync(tempEntry, entrySource);
2502
- try {
2503
- await buildBundle(ctx, {
2504
- entry: ".cf-entry.tmp.mjs",
2505
- outDir: outputDir,
2506
- target: "es2022",
2507
- fileName: "_worker.js"
2508
- // 使用默认 external(vite/esbuild/rollup/fsevents/lightningcss)
2509
- // 这些构建工具运行时不需要,且 fsevents 是 macOS .node 原生二进制无法打包
2510
- });
2511
- copyStaticAssets(ctx, path.resolve(outputDir, "assets"));
2512
- const prerendered = await prerenderRoutes(ctx);
2513
- for (const { url, html } of prerendered) {
2514
- const filePath = url === "/" ? path.join(outputDir, "assets", "index.html") : path.join(outputDir, "assets", url, "index.html");
2515
- fs.mkdirSync(path.resolve(filePath, ".."), {
2516
- recursive: true
2517
- });
2518
- fs.writeFileSync(filePath, html);
2519
- }
2520
- } finally {
2521
- fs.rmSync(tempEntry, { force: true });
2522
- }
2523
- console.log(" Cloudflare output \u2192 dist/cloudflare/\n");
2524
- }
2525
- };
2526
- }
2527
-
2528
- // ../server/src/adapters/netlify.ts
2529
- function netlifyAdapter() {
2530
- return {
2531
- name: "netlify",
2532
- async build(ctx) {
2533
- const { fs, path, root } = ctx;
2534
- const funcDir = path.resolve(
2535
- root,
2536
- ".netlify/functions-internal/ssr"
2537
- );
2538
- fs.rmSync(path.resolve(root, ".netlify"), {
2539
- recursive: true,
2540
- force: true
2541
- });
2542
- const entrySource = generateSSREntry(ctx, {
2543
- platformImport: `import { handle } from "hono/netlify";`,
2544
- platformExport: `export default handle(app);
2545
- export const config = { path: "/*", preferStatic: true };`,
2546
- // Netlify CDN 缓存 — 使用 Netlify-CDN-Cache-Control 头做 ISR
2547
- platformCache: `
2548
- const ISR_SWR_TTL = 3600;
2549
- const ISR_CACHE_MAX = 1000;
2550
- const _isrMap = new Map();
2551
- async function platformCacheGet(url) {
2552
- return _isrMap.get(url) ?? null;
2553
- }
2554
- async function platformCacheSet(url, html) {
2555
- if (_isrMap.size >= ISR_CACHE_MAX) {
2556
- const first = _isrMap.keys().next().value;
2557
- _isrMap.delete(first);
2558
- }
2559
- _isrMap.set(url, html);
2560
- }`,
2561
- platformPrerenderResponseHook: `c.header("Cache-Control", "public, max-age=0, must-revalidate");
2562
- c.header("Netlify-CDN-Cache-Control", "public, max-age=" + ISR_SWR_TTL + ", stale-while-revalidate=" + ISR_SWR_TTL + ", durable");`
2563
- });
2564
- const tempEntry = path.resolve(root, ".netlify-entry.tmp.mjs");
2565
- fs.writeFileSync(tempEntry, entrySource);
2566
- try {
2567
- await buildBundle(ctx, {
2568
- entry: ".netlify-entry.tmp.mjs",
2569
- outDir: funcDir,
2570
- target: "node18"
2571
- });
2572
- } finally {
2573
- fs.rmSync(tempEntry, { force: true });
2574
- }
2575
- const redirects = `/* /.netlify/functions/ssr 200
2576
- `;
2577
- fs.writeFileSync(
2578
- path.resolve(root, "dist/client/_redirects"),
2579
- redirects
2580
- );
2581
- const prerendered = await prerenderRoutes(ctx);
2582
- const clientDir = path.resolve(root, "dist/client");
2583
- for (const { url, html } of prerendered) {
2584
- const filePath = url === "/" ? path.join(clientDir, "index.html") : path.join(clientDir, url, "index.html");
2585
- fs.mkdirSync(path.resolve(filePath, ".."), { recursive: true });
2586
- fs.writeFileSync(filePath, html);
2587
- }
2588
- console.log(
2589
- " Netlify output \u2192 .netlify/functions-internal/ssr/\n Publish dir: dist/client/\n"
2590
- );
2591
- }
2592
- };
2593
- }
2594
-
2595
- // ../server/src/adapters/node.ts
2596
- function nodeAdapter() {
2597
- return {
2598
- name: "node",
2599
- async build(ctx) {
2600
- const { fs, path, root } = ctx;
2601
- const entrySource = generateSSREntry(ctx, {
2602
- platformImport: `import { serve } from "@hono/node-server";
2603
- import { readFileSync, existsSync } from "node:fs";
2604
- import { resolve, dirname } from "node:path";
2605
- import { fileURLToPath } from "node:url";`,
2606
- platformMiddleware: `
2607
- // \u9884\u6E32\u67D3\u6587\u4EF6\u4E2D\u95F4\u4EF6\uFF1A\u68C0\u67E5 dist/prerender/ \u4E0B\u662F\u5426\u6709\u5BF9\u5E94\u7684\u9759\u6001 HTML
2608
- const __entry_dirname = dirname(fileURLToPath(import.meta.url));
2609
- const prerenderDir = resolve(__entry_dirname, "../prerender");
2610
-
2611
- app.use("*", async (c, next) => {
2612
- const urlPath = c.req.path;
2613
- const candidates = [
2614
- resolve(prerenderDir, "." + urlPath, "index.html"),
2615
- resolve(prerenderDir, "." + urlPath + ".html"),
2616
- ];
2617
- if (urlPath === "/") candidates.unshift(resolve(prerenderDir, "index.html"));
2618
- for (const f of candidates) {
2619
- if (existsSync(f)) {
2620
- const html = readFileSync(f, "utf-8");
2621
- return c.html(html);
2622
- }
2623
- }
2624
- await next();
2625
- });
2626
- `,
2627
- platformExport: `
2628
- const port = +(process.env.PORT || 3000);
2629
- serve({ fetch: app.fetch, port }, (info) => {
2630
- console.log(\`Server running at http://localhost:\${info.port}\`);
2631
- });
2632
- `
2633
- });
2634
- const tempEntry = path.resolve(root, ".node-entry.tmp.mjs");
2635
- fs.writeFileSync(tempEntry, entrySource);
2636
- try {
2637
- await buildBundle(ctx, {
2638
- entry: ".node-entry.tmp.mjs",
2639
- outDir: path.resolve(root, "dist/server"),
2640
- target: "node18"
2641
- });
2642
- } finally {
2643
- fs.rmSync(tempEntry, { force: true });
2644
- }
2645
- const prerendered = await prerenderRoutes(ctx);
2646
- if (prerendered.length > 0) {
2647
- const prerenderDir = path.resolve(root, "dist/prerender");
2648
- fs.mkdirSync(prerenderDir, { recursive: true });
2649
- for (const { url, html } of prerendered) {
2650
- const filePath = url === "/" ? path.join(prerenderDir, "index.html") : path.join(prerenderDir, url, "index.html");
2651
- fs.mkdirSync(path.resolve(filePath, ".."), {
2652
- recursive: true
2653
- });
2654
- fs.writeFileSync(filePath, html);
2655
- }
2656
- }
2657
- console.log(
2658
- " Node output \u2192 dist/server/index.mjs\n Run: node dist/server/index.mjs\n"
2659
- );
2660
- }
2661
- };
2662
- }
2663
-
2664
- // ../server/src/adapters/static.ts
2665
- function staticAdapter(opts = {}) {
2666
- return {
2667
- name: "static",
2668
- async build(ctx) {
2669
- const { fs, path, root, vite } = ctx;
2670
- const outputDir = path.resolve(root, "dist/static");
2671
- fs.rmSync(outputDir, { recursive: true, force: true });
2672
- fs.mkdirSync(outputDir, { recursive: true });
2673
- const { pathToFileURL } = await import(
2674
- /* @vite-ignore */
2675
- "url"
2676
- );
2677
- const ssrPath = pathToFileURL(
2678
- path.resolve(root, "dist/server/ssr.js")
2679
- ).href;
2680
- const ssrModule = await import(
2681
- /* @vite-ignore */
2682
- ssrPath
2683
- );
2684
- ctx.copyStaticAssets(outputDir, { excludeHtml: true });
2685
- const { paths: routePaths, defs: routeDefs } = await extractRoutesWithModes(ctx, opts);
2686
- const allUrls = [];
2687
- for (const routePath of routePaths) {
2688
- for (const locale of ctx.locales) {
2689
- const url = locale === ctx.defaultLocale ? routePath : `/${locale}${routePath === "/" ? "" : routePath}`;
2690
- allUrls.push(url);
2691
- }
2692
- }
2693
- console.log(
2694
- ` Pre-rendering ${allUrls.length} pages (${routePaths.length} routes \xD7 ${ctx.locales.length} locales)...
2695
- `
2696
- );
2697
- for (const url of allUrls) {
2698
- try {
2699
- const locale = inferLocale(
2700
- url,
2701
- ctx.locales,
2702
- ctx.defaultLocale
2703
- );
2704
- const routeDef = routeDefs.find(
2705
- (r) => r.path === stripLocalePrefix(url, ctx.locales)
2706
- );
2707
- const mode = resolveRenderMode(
2708
- stripLocalePrefix(url, ctx.locales),
2709
- routeDef?.renderMode,
2710
- ctx.renderModes
2711
- );
2712
- let finalHtml;
2713
- if (mode === "csr") {
2714
- finalHtml = injectCSRShellForStatic(
2715
- ctx.templateHtml,
2716
- locale
2717
- );
2718
- } else {
2719
- const {
2720
- html: appHtml,
2721
- head,
2722
- css,
2723
- serverData
2724
- } = await ssrModule.render(url, locale);
2725
- const serializedData = ssrModule.serializeServerData(serverData);
2726
- finalHtml = injectSSRForStatic(
2727
- ctx.templateHtml,
2728
- locale,
2729
- head,
2730
- css,
2731
- appHtml,
2732
- serializedData
2733
- );
2734
- }
2735
- const filePath = url === "/" ? path.join(outputDir, "index.html") : path.join(outputDir, url, "index.html");
2736
- fs.mkdirSync(path.resolve(filePath, ".."), {
2737
- recursive: true
2738
- });
2739
- fs.writeFileSync(filePath, finalHtml);
2740
- } catch (e) {
2741
- console.warn(` [static] Failed to render ${url}:`, e);
2742
- }
2743
- }
2744
- console.log(` Static output \u2192 dist/static/
2745
- `);
2746
- }
2747
- };
2748
- }
2749
- async function extractRoutesWithModes(ctx, opts) {
2750
- const routesFile = opts.routesExport ?? "src/lib/bootstrap.ts";
2751
- const paths = [];
2752
- const defs = [];
2753
- try {
2754
- const { pathToFileURL } = await import(
2755
- /* @vite-ignore */
2756
- "url"
2757
- );
2758
- await ctx.vite.build({
2759
- root: ctx.root,
2760
- build: {
2761
- ssr: routesFile,
2762
- outDir: ctx.path.resolve(ctx.root, "dist/server"),
2763
- emptyOutDir: false,
2764
- rollupOptions: {
2765
- output: { entryFileNames: "_routes.mjs" }
2766
- }
2767
- },
2768
- resolve: ctx.resolvedResolve
2769
- });
2770
- const routesPath = pathToFileURL(
2771
- ctx.path.resolve(ctx.root, "dist/server/_routes.mjs")
2772
- ).href;
2773
- const routesMod = await import(
2774
- /* @vite-ignore */
2775
- routesPath
2776
- );
2777
- const routes = routesMod.routes ?? routesMod.default;
2778
- if (Array.isArray(routes)) {
2779
- for (const r of routes) {
2780
- if (r.path && !r.path.includes(":")) {
2781
- paths.push(r.path);
2782
- defs.push({ path: r.path, renderMode: r.renderMode });
2783
- }
2784
- }
2785
- }
2786
- ctx.fs.rmSync(ctx.path.resolve(ctx.root, "dist/server/_routes.mjs"), {
2787
- force: true
2788
- });
2789
- } catch (e) {
2790
- console.warn(
2791
- ` [static] Could not load routes from "${routesFile}". Using "/" only.`,
2792
- e
2793
- );
2794
- if (paths.length === 0) paths.push("/");
2795
- }
2796
- if (opts.dynamicRoutes) {
2797
- for (const r of opts.dynamicRoutes) {
2798
- if (!paths.includes(r)) paths.push(r);
2799
- }
2800
- }
2801
- if (paths.length === 0) paths.push("/");
2802
- return { paths, defs };
2803
- }
2804
- function inferLocale(url, locales, defaultLocale) {
2805
- const segments = url.split("/").filter(Boolean);
2806
- if (segments.length > 0 && locales.includes(segments[0])) {
2807
- return segments[0];
2808
- }
2809
- return defaultLocale;
2810
- }
2811
- function injectSSRForStatic(template, locale, head, css, html, serializedData) {
2812
- return template.replace("<!--ssr-lang-->", locale).replace("<!--ssr-head-->", head + "\n<style>" + css + "</style>").replace("<!--ssr-body-->", html).replace(
2813
- "<!--ssr-data-->",
2814
- '<script id="serialized-server-data" type="application/json">' + serializedData + "</script>"
2815
- );
2816
- }
2817
- function injectCSRShellForStatic(template, locale) {
2818
- return template.replace("<!--ssr-lang-->", locale).replace("<!--ssr-head-->", "").replace("<!--ssr-body-->", "").replace("<!--ssr-data-->", "");
2819
- }
2820
- function stripLocalePrefix(url, locales) {
2821
- const segments = url.split("/").filter(Boolean);
2822
- if (segments.length > 0 && locales.includes(segments[0])) {
2823
- const rest = segments.slice(1).join("/");
2824
- return rest ? `/${rest}` : "/";
2825
- }
2826
- return url;
2827
- }
2828
- function resolveRenderMode(routePath, routeRenderMode, renderModes) {
2829
- if (renderModes) {
2830
- if (renderModes[routePath]) return renderModes[routePath];
2831
- for (const [pattern, mode] of Object.entries(renderModes)) {
2832
- if (pattern.includes("*")) {
2833
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
2834
- const re = new RegExp("^" + escaped.replace(/\*/g, ".*") + "$");
2835
- if (re.test(routePath)) return mode;
2836
- }
2837
- }
2838
- }
2839
- return routeRenderMode ?? "ssr";
2840
- }
2841
-
2842
- // ../server/src/adapters/vercel.ts
2843
- function vercelAdapter() {
2844
- return {
2845
- name: "vercel",
2846
- async build(ctx) {
2847
- const { fs, path, root } = ctx;
2848
- const outputDir = path.resolve(root, ".vercel/output");
2849
- fs.rmSync(outputDir, { recursive: true, force: true });
2850
- const entrySource = generateSSREntry(ctx, {
2851
- platformImport: `import { getRequestListener } from "@hono/node-server";`,
2852
- platformExport: [
2853
- `const _listener = getRequestListener(app.fetch);`,
2854
- `export default (req, res) => {`,
2855
- ` const m = req.headers["x-now-route-matches"];`,
2856
- ` if (typeof m === "string") {`,
2857
- ` try {`,
2858
- ` const p = new URLSearchParams(m);`,
2859
- ` const c = p.get("1");`,
2860
- ` if (c != null) {`,
2861
- ` const qi = (req.url || "").indexOf("?");`,
2862
- ` const qs = qi !== -1 ? req.url.slice(qi) : "";`,
2863
- ` req.url = "/" + decodeURIComponent(c) + qs;`,
2864
- ` }`,
2865
- ` } catch {}`,
2866
- ` }`,
2867
- ` return _listener(req, res);`,
2868
- `};`
2869
- ].join("\n")
2870
- });
2871
- const tempEntry = path.resolve(root, ".vercel-entry.tmp.mjs");
2872
- fs.writeFileSync(tempEntry, entrySource);
2873
- try {
2874
- const funcDir = path.resolve(
2875
- root,
2876
- ".vercel/output/functions/ssr.func"
2877
- );
2878
- await buildBundle(ctx, {
2879
- entry: ".vercel-entry.tmp.mjs",
2880
- outDir: funcDir,
2881
- target: "node18"
2882
- });
2883
- fs.writeFileSync(
2884
- path.resolve(funcDir, ".vc-config.json"),
2885
- JSON.stringify(
2886
- {
2887
- runtime: "nodejs20.x",
2888
- handler: "index.mjs",
2889
- launcherType: "Nodejs"
2890
- },
2891
- null,
2892
- 2
2893
- )
2894
- );
2895
- copyStaticAssets(
2896
- ctx,
2897
- path.resolve(root, ".vercel/output/static")
2898
- );
2899
- fs.writeFileSync(
2900
- path.resolve(root, ".vercel/output/config.json"),
2901
- JSON.stringify(
2902
- {
2903
- version: 3,
2904
- routes: [
2905
- { handle: "filesystem" },
2906
- { src: "/(.*)", dest: "/ssr" }
2907
- ]
2908
- },
2909
- null,
2910
- 2
2911
- )
2912
- );
2913
- } finally {
2914
- fs.rmSync(tempEntry, { force: true });
2915
- }
2916
- const prerendered = await prerenderRoutes(ctx);
2917
- const staticDir = path.resolve(root, ".vercel/output/static");
2918
- for (const { url, html } of prerendered) {
2919
- const filePath = url === "/" ? path.join(staticDir, "index.html") : path.join(staticDir, url, "index.html");
2920
- fs.mkdirSync(path.resolve(filePath, ".."), { recursive: true });
2921
- fs.writeFileSync(filePath, html);
2922
- }
2923
- if (prerendered.length > 0) {
2924
- const configPath = path.resolve(
2925
- root,
2926
- ".vercel/output/config.json"
2927
- );
2928
- const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
2929
- config.overrides = config.overrides ?? {};
2930
- for (const { url } of prerendered) {
2931
- const key = url === "/" ? "index.html" : `${url.replace(/^\//, "")}/index.html`;
2932
- config.overrides[key] = {
2933
- path: url === "/" ? "/" : url,
2934
- contentType: "text/html; charset=utf-8"
2935
- };
2936
- }
2937
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
2938
- }
2939
- console.log(" Vercel output \u2192 .vercel/output/\n");
2940
- }
2941
- };
2942
- }
2943
-
2944
- // ../server/src/adapters/resolve.ts
2945
- function resolveAdapter(value) {
2946
- if (typeof value !== "string") return value;
2947
- switch (value) {
2948
- case "vercel":
2949
- return vercelAdapter();
2950
- case "cloudflare":
2951
- return cloudflareAdapter();
2952
- case "netlify":
2953
- return netlifyAdapter();
2954
- case "node":
2955
- return nodeAdapter();
2956
- case "static":
2957
- return staticAdapter();
2958
- case "auto":
2959
- return autoAdapter();
2960
- default:
2961
- throw new Error(
2962
- `[finesoft] Unknown adapter: "${value}". Available: vercel, cloudflare, netlify, node, static, auto`
2963
- );
2964
- }
2965
- }
2966
-
2967
- // ../server/src/adapters/auto.ts
2968
- function autoAdapter() {
2969
- return {
2970
- name: "auto",
2971
- async build(ctx) {
2972
- const detected = detectPlatform();
2973
- console.log(` [auto] Detected platform: ${detected}
2974
- `);
2975
- const adapter = resolveAdapter(detected);
2976
- return adapter.build(ctx);
2977
- }
2978
- };
2979
- }
2980
- function detectPlatform() {
2981
- if (process.env.VERCEL) return "vercel";
2982
- if (process.env.CF_PAGES) return "cloudflare";
2983
- if (process.env.NETLIFY) return "netlify";
2984
- return "node";
2985
- }
2986
-
2987
- // ../server/src/index.ts
2988
- init_app();
2989
-
2990
- // ../server/src/create-server.ts
2991
- var import_hono3 = require("hono");
2992
- init_app();
2993
-
2994
- // ../server/src/runtime.ts
2995
- function detectRuntime() {
2996
- return {
2997
- isDeno: typeof globalThis.Deno !== "undefined",
2998
- isBun: typeof globalThis.Bun !== "undefined",
2999
- isVercel: !!process.env.VERCEL,
3000
- isProduction: process.env.NODE_ENV === "production"
3001
- };
3002
- }
3003
- async function resolveRoot(importMetaUrl, levelsUp = 0) {
3004
- const isDeno = typeof globalThis.Deno !== "undefined";
3005
- if (isDeno) {
3006
- let url = new URL(importMetaUrl);
3007
- for (let i = 0; i < levelsUp; i++) {
3008
- url = new URL("..", url);
3009
- }
3010
- return url.pathname;
3011
- }
3012
- const { dirname, resolve, normalize } = await import(
3013
- /* @vite-ignore */
3014
- "path"
3015
- );
3016
- const { fileURLToPath } = await import(
3017
- /* @vite-ignore */
3018
- "url"
3019
- );
3020
- let dir = normalize(dirname(fileURLToPath(importMetaUrl)));
3021
- for (let i = 0; i < levelsUp; i++) {
3022
- dir = resolve(dir, "..");
3023
- }
3024
- return dir;
3025
- }
3026
-
3027
- // ../server/src/start.ts
3028
- var import_hono2 = require("hono");
3029
- async function startServer(options) {
3030
- const {
3031
- app,
3032
- root,
3033
- port = 3e3,
3034
- isProduction,
3035
- vite,
3036
- routes,
3037
- locales,
3038
- ssrEntryPath
3039
- } = options;
3040
- const { isDeno, isBun, isVercel } = options.runtime ?? detectRuntime();
3041
- function printStartupBanner() {
3042
- const lines = [
3043
- `
3044
- Server running at http://localhost:${port}
3045
- `
3046
- ];
3047
- if (routes && routes.length > 0) {
3048
- lines.push(" Routes:");
3049
- for (const r of routes) {
3050
- lines.push(` ${r}`);
3051
- }
3052
- lines.push("");
3053
- }
3054
- if (locales && locales.length > 0) {
3055
- lines.push(` Locales: ${locales.join(", ")}`);
3056
- }
3057
- if (ssrEntryPath) {
3058
- lines.push(` SSR Entry: ${ssrEntryPath}`);
3059
- }
3060
- if (locales?.length || ssrEntryPath) {
3061
- lines.push("");
3062
- }
3063
- console.log(lines.join("\n"));
3064
- }
3065
- if (isVercel) {
3066
- return { vite };
3067
- }
3068
- if (!isProduction) {
3069
- let devVite = vite;
3070
- if (!devVite) {
3071
- const { createServer: createViteServer } = await import(
3072
- /* @vite-ignore */
3073
- "vite"
3074
- );
3075
- devVite = await createViteServer({
3076
- root,
3077
- server: { middlewareMode: true },
3078
- appType: "custom"
3079
- });
3080
- }
3081
- const { getRequestListener } = await import(
3082
- /* @vite-ignore */
3083
- "@hono/node-server"
3084
- );
3085
- const { createServer: createServer2 } = await import(
3086
- /* @vite-ignore */
3087
- "http"
3088
- );
3089
- const listener = getRequestListener(app.fetch);
3090
- const server = createServer2((req, res) => {
3091
- devVite.middlewares(req, res, () => listener(req, res));
3092
- });
3093
- server.listen(port, () => {
3094
- printStartupBanner();
3095
- });
3096
- return { vite: devVite };
3097
- }
3098
- if (isDeno) {
3099
- globalThis.Deno.serve({ port }, app.fetch);
3100
- } else if (isBun) {
3101
- } else {
3102
- const { serveStatic } = await import(
3103
- /* @vite-ignore */
3104
- "@hono/node-server/serve-static"
3105
- );
3106
- const { resolve } = await import(
3107
- /* @vite-ignore */
3108
- "path"
3109
- );
3110
- const prodApp = new import_hono2.Hono();
3111
- const clientDir = resolve(root, "dist/client");
3112
- prodApp.use(
3113
- "/*",
3114
- serveStatic({
3115
- root: clientDir,
3116
- // 禁止目录路径自动提供 index.html,让其 fall through 到 SSR
3117
- rewriteRequestPath: (path) => path.endsWith("/") ? "/__nosuchfile__" : path
3118
- })
3119
- );
3120
- prodApp.route("/", app);
3121
- const { serve } = await import(
3122
- /* @vite-ignore */
3123
- "@hono/node-server"
3124
- );
3125
- serve({ fetch: prodApp.fetch, port }, () => {
3126
- printStartupBanner();
3127
- });
3128
- }
3129
- return { vite };
3130
- }
3131
-
3132
- // ../server/src/create-server.ts
3133
- async function createServer(config = {}) {
3134
- const {
3135
- root: rootOverride,
3136
- locales,
3137
- defaultLocale,
3138
- port = Number(process.env.PORT) || 3e3,
3139
- setup,
3140
- proxies,
3141
- ssr
3142
- } = config;
3143
- const root = rootOverride ?? process.cwd();
3144
- const { existsSync } = await import(
3145
- /* @vite-ignore */
3146
- "fs"
3147
- );
3148
- const { resolve } = await import(
3149
- /* @vite-ignore */
3150
- "path"
3151
- );
3152
- const envPath = resolve(root, ".env");
3153
- if (existsSync(envPath)) {
3154
- try {
3155
- const { config: dotenvConfig } = await import(
3156
- /* @vite-ignore */
3157
- "dotenv"
3158
- );
3159
- dotenvConfig({ path: envPath });
3160
- } catch {
3161
- }
3162
- }
3163
- const runtime = detectRuntime();
3164
- let vite;
3165
- if (!runtime.isProduction && !runtime.isVercel) {
3166
- const { createServer: createViteServer } = await import(
3167
- /* @vite-ignore */
3168
- "vite"
3169
- );
3170
- vite = await createViteServer({
3171
- root,
3172
- server: { middlewareMode: true },
3173
- appType: "custom"
3174
- });
3175
- }
3176
- const app = new import_hono3.Hono();
3177
- if (proxies?.length) {
3178
- registerProxyRoutes(app, proxies);
3179
- }
3180
- if (setup) {
3181
- await setup(app);
3182
- }
3183
- const ssrApp = createSSRApp({
3184
- root,
3185
- vite,
3186
- isProduction: runtime.isProduction,
3187
- supportedLocales: locales,
3188
- defaultLocale,
3189
- parentFetch: app.fetch.bind(app),
3190
- ...ssr
3191
- });
3192
- app.route("/", ssrApp);
3193
- await startServer({
3194
- app,
3195
- root,
3196
- port,
3197
- isProduction: runtime.isProduction,
3198
- vite,
3199
- runtime,
3200
- locales,
3201
- ssrEntryPath: ssr?.ssrEntryPath
3202
- });
3203
- return { app, vite, runtime };
3204
- }
3205
-
3206
- // ../server/src/index.ts
3207
- init_locale();
3208
-
3209
- // ../server/src/vite-plugin.ts
3210
- init_internal_fetch();
3211
- function resolveSetupFn(mod) {
3212
- if (typeof mod.default === "function") return mod.default;
3213
- if (typeof mod.setup === "function") return mod.setup;
3214
- const first = Object.values(mod).find((v) => typeof v === "function");
3215
- return first ?? null;
3216
- }
3217
- function matchRenderModeConfig(url, renderModes) {
3218
- if (!renderModes) return null;
3219
- const path = url.split("?")[0];
3220
- if (renderModes[path]) return renderModes[path];
3221
- for (const [pattern, mode] of Object.entries(renderModes)) {
3222
- if (pattern.includes("*")) {
3223
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
3224
- const re = new RegExp("^" + escaped.replace(/\*/g, ".*") + "$");
3225
- if (re.test(path)) return mode;
3226
- }
3227
- }
3228
- return null;
3229
- }
3230
- function finesoftFrontViteConfig(options = {}) {
3231
- const ssrEntry = options.ssr?.entry ?? "src/ssr.ts";
3232
- let root = process.cwd();
3233
- let resolvedCommand;
3234
- let resolvedResolve;
3235
- let resolvedCss;
3236
- const CSS_EXTENSIONS = /\.(css|scss|less|sass|styl|stylus|pcss|postcss)($|\?)/;
3237
- return {
3238
- name: "finesoft-front",
3239
- config(userConfig, env) {
3240
- const overrides = {
3241
- appType: "custom"
3242
- };
3243
- if (env.command === "build" && !process.env.__FINESOFT_SUB_BUILD__) {
3244
- overrides.build = {
3245
- outDir: userConfig.build?.outDir ?? "dist/client"
3246
- };
3247
- }
3248
- return overrides;
3249
- },
3250
- configResolved(config) {
3251
- resolvedCommand = config.command;
3252
- resolvedResolve = config.resolve;
3253
- resolvedCss = config.css;
3254
- root = config.root;
3255
- },
3256
- /**
3257
- * Dev 模式 CSS 内联 — 消除 SSR 首屏布局抖动
3258
- *
3259
- * Vite dev 模式下,global.scss 等非组件 CSS 通过 JS 模块系统异步加载,
3260
- * 导致 SSR HTML 初次渲染缺少布局关键样式(box-sizing、flex 布局、padding-top 等)。
3261
- *
3262
- * 此 hook 在 HTML 模板变换阶段(SSR 渲染之前):
3263
- * 1. 找到浏览器入口脚本(排除 /@vite/client 等内部脚本)
3264
- * 2. 编译入口脚本,填充 Vite 模块图
3265
- * 3. 遍历模块图收集所有 CSS 依赖(排除 .svelte 组件 CSS,由 SSR 渲染自行处理)
3266
- * 4. 通过 ssrLoadModule 获取编译后 CSS(SCSS→CSS)
3267
- * 5. 注入 <style data-vite-dev-id> 标签到 <head>
3268
- *
3269
- * data-vite-dev-id 确保 Vite HMR 客户端复用已有标签,避免重复注入。
3270
- */
3271
- transformIndexHtml: {
3272
- order: "pre",
3273
- async handler(html, ctx) {
3274
- const server = ctx.server;
3275
- if (!server) return;
3276
- const urlPath = (ctx.originalUrl || ctx.path || "").split(
3277
- "?"
3278
- )[0];
3279
- if (/\.\w+$/.test(urlPath) && !urlPath.endsWith(".html")) {
3280
- return;
3281
- }
3282
- const scripts = [
3283
- ...html.matchAll(
3284
- /<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/g
3285
- )
3286
- ];
3287
- const appEntry = scripts.find((m) => !m[1].startsWith("/@"));
3288
- if (!appEntry) return;
3289
- const browserEntry = appEntry[1];
3290
- try {
3291
- await server.transformRequest(browserEntry);
3292
- } catch {
3293
- return;
3294
- }
3295
- const cssUrls = [];
3296
- const visited = /* @__PURE__ */ new Set();
3297
- function walk(mod) {
3298
- if (!mod?.url || visited.has(mod.url)) return;
3299
- visited.add(mod.url);
3300
- if (CSS_EXTENSIONS.test(mod.url) && !mod.url.includes(".svelte")) {
3301
- cssUrls.push(mod.url);
3302
- }
3303
- if (mod.importedModules) {
3304
- for (const imported of mod.importedModules) {
3305
- walk(imported);
3306
- }
3307
- }
3308
- }
3309
- const mg = server.moduleGraph;
3310
- const browserMod = await mg.getModuleByUrl(browserEntry);
3311
- if (browserMod) walk(browserMod);
3312
- if (cssUrls.length === 0) return;
3313
- const tags = [];
3314
- for (const url of cssUrls) {
3315
- try {
3316
- const mod = await server.ssrLoadModule(url);
3317
- const css = mod?.default;
3318
- if (typeof css === "string" && css.length > 0) {
3319
- tags.push({
3320
- tag: "style",
3321
- attrs: { "data-vite-dev-id": url },
3322
- children: css,
3323
- injectTo: "head"
3324
- });
3325
- }
3326
- } catch {
3327
- }
3328
- }
3329
- return tags;
3330
- }
3331
- },
3332
- // ─── Dev ───────────────────────────────────────────────
3333
- configureServer(server) {
3334
- return async () => {
3335
- const { Hono: HonoClass } = await import(
3336
- /* @vite-ignore */
3337
- "hono"
3338
- );
3339
- const { createSSRApp: createSSRApp2 } = await Promise.resolve().then(() => (init_app(), app_exports));
3340
- const { getRequestListener } = await import(
3341
- /* @vite-ignore */
3342
- "@hono/node-server"
3343
- );
3344
- const app = new HonoClass();
3345
- if (options.proxies?.length) {
3346
- registerProxyRoutes(app, options.proxies);
3347
- }
3348
- if (typeof options.setup === "function") {
3349
- await options.setup(app);
3350
- } else if (typeof options.setup === "string") {
3351
- const mod = await server.ssrLoadModule("/" + options.setup);
3352
- const fn = resolveSetupFn(mod);
3353
- if (fn) await fn(app);
3354
- }
3355
- const ssrApp = createSSRApp2({
3356
- root,
3357
- vite: server,
3358
- isProduction: false,
3359
- ssrEntryPath: "/" + ssrEntry,
3360
- supportedLocales: options.locales,
3361
- defaultLocale: options.defaultLocale,
3362
- parentFetch: app.fetch.bind(app),
3363
- renderModes: options.renderModes
3364
- });
3365
- app.route("/", ssrApp);
3366
- const listener = getRequestListener(app.fetch);
3367
- server.middlewares.use((req, res) => {
3368
- listener(req, res);
3369
- });
3370
- };
3371
- },
3372
- // ─── Preview ───────────────────────────────────────────
3373
- configurePreviewServer(server) {
3374
- return async () => {
3375
- const { readFileSync } = await import(
3376
- /* @vite-ignore */
3377
- "fs"
3378
- );
3379
- const { resolve } = await import(
3380
- /* @vite-ignore */
3381
- "path"
3382
- );
3383
- const { pathToFileURL } = await import(
3384
- /* @vite-ignore */
3385
- "url"
3386
- );
3387
- const { Hono: HonoClass } = await import(
3388
- /* @vite-ignore */
3389
- "hono"
3390
- );
3391
- const { injectSSRContent: injectSSRContent2, injectCSRShell: injectCSRShell2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
3392
- const { parseAcceptLanguage: parseAcceptLanguage2 } = await Promise.resolve().then(() => (init_locale(), locale_exports));
3393
- const { getRequestListener } = await import(
3394
- /* @vite-ignore */
3395
- "@hono/node-server"
3396
- );
3397
- const app = new HonoClass();
3398
- const isrCache = /* @__PURE__ */ new Map();
3399
- if (options.proxies?.length) {
3400
- registerProxyRoutes(app, options.proxies);
3401
- }
3402
- if (typeof options.setup === "function") {
3403
- await options.setup(app);
3404
- } else if (typeof options.setup === "string") {
3405
- try {
3406
- const setupPath = pathToFileURL(
3407
- resolve(root, "dist/server/setup.mjs")
3408
- ).href;
3409
- const mod = await import(
3410
- /* @vite-ignore */
3411
- setupPath
3412
- );
3413
- const fn = resolveSetupFn(
3414
- mod
3415
- );
3416
- if (fn) await fn(app);
3417
- } catch {
3418
- console.warn(
3419
- "[finesoft] Could not load setup module for preview. API routes disabled."
3420
- );
3421
- }
3422
- }
3423
- const templatePath = resolve(root, "dist/client/index.html");
3424
- const template = readFileSync(templatePath, "utf-8");
3425
- const ssrPath = pathToFileURL(
3426
- resolve(root, "dist/server/ssr.js")
3427
- ).href;
3428
- const ssrModule = await import(
3429
- /* @vite-ignore */
3430
- ssrPath
3431
- );
3432
- app.get("*", async (c) => {
3433
- const ssrDepth = parseInt(
3434
- c.req.header(SSR_DEPTH_HEADER) ?? "0",
3435
- 10
3436
- );
3437
- if (ssrDepth >= MAX_SSR_DEPTH) {
3438
- return c.text("SSR recursion loop detected", 508);
3439
- }
3440
- const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
3441
- try {
3442
- const locale = parseAcceptLanguage2(
3443
- c.req.header("accept-language"),
3444
- options.locales,
3445
- options.defaultLocale
3446
- );
3447
- const overrideMode = matchRenderModeConfig(
3448
- url,
3449
- options.renderModes
3450
- );
3451
- if (overrideMode === "csr") {
3452
- return c.html(injectCSRShell2(template, locale));
3453
- }
3454
- const cacheKey = `${locale}:${url}`;
3455
- const cached = isrCache.get(cacheKey);
3456
- if (cached) return c.html(cached);
3457
- const {
3458
- html: appHtml,
3459
- head,
3460
- css,
3461
- serverData,
3462
- renderMode
3463
- } = await ssrModule.render(url, locale, {
3464
- fetch: createInternalFetch(
3465
- app.fetch.bind(app),
3466
- ssrDepth + 1
3467
- )
3468
- });
3469
- if (renderMode === "csr") {
3470
- return c.html(injectCSRShell2(template, locale));
3471
- }
3472
- const serializedData = ssrModule.serializeServerData(serverData);
3473
- const finalHtml = injectSSRContent2({
3474
- template,
3475
- locale,
3476
- head,
3477
- css,
3478
- html: appHtml,
3479
- serializedData
3480
- });
3481
- if (renderMode === "prerender" || overrideMode === "prerender") {
3482
- isrCache.set(cacheKey, finalHtml);
3483
- }
3484
- return c.html(finalHtml);
3485
- } catch (e) {
3486
- console.error("[SSR Preview Error]", e);
3487
- return c.text("Internal Server Error", 500);
3488
- }
3489
- });
3490
- const listener = getRequestListener(app.fetch);
3491
- server.middlewares.use((req, res) => {
3492
- listener(req, res);
3493
- });
3494
- };
3495
- },
3496
- // ─── Build ─────────────────────────────────────────────
3497
- async closeBundle() {
3498
- if (process.env.__FINESOFT_SUB_BUILD__) return;
3499
- if (resolvedCommand !== "build") return;
3500
- process.env.__FINESOFT_SUB_BUILD__ = "1";
3501
- try {
3502
- const vite = await import(
3503
- /* @vite-ignore */
3504
- "vite"
3505
- );
3506
- const fs = await import(
3507
- /* @vite-ignore */
3508
- "fs"
3509
- );
3510
- const path = await import(
3511
- /* @vite-ignore */
3512
- "path"
3513
- );
3514
- console.log("\n Building SSR bundle...\n");
3515
- await vite.build({
3516
- root,
3517
- build: {
3518
- ssr: ssrEntry,
3519
- outDir: "dist/server"
3520
- },
3521
- resolve: resolvedResolve,
3522
- css: resolvedCss
3523
- });
3524
- if (typeof options.setup === "string") {
3525
- console.log(" Building setup module...\n");
3526
- await vite.build({
3527
- root,
3528
- build: {
3529
- ssr: options.setup,
3530
- outDir: "dist/server",
3531
- emptyOutDir: false,
3532
- rollupOptions: {
3533
- output: { entryFileNames: "setup.mjs" }
3534
- }
3535
- },
3536
- resolve: resolvedResolve
3537
- });
3538
- }
3539
- if (options.adapter) {
3540
- const adapter = resolveAdapter(options.adapter);
3541
- const locales = options.locales ?? ["zh", "en"];
3542
- const defaultLocale = options.defaultLocale ?? locales[0] ?? "en";
3543
- const templateHtml = fs.readFileSync(
3544
- path.resolve(root, "dist/client/index.html"),
3545
- "utf-8"
3546
- );
3547
- const ctx = {
3548
- root,
3549
- ssrEntry,
3550
- setupPath: typeof options.setup === "string" ? options.setup : void 0,
3551
- bootstrapEntry: options.bootstrapEntry,
3552
- locales,
3553
- defaultLocale,
3554
- templateHtml,
3555
- renderModes: options.renderModes,
3556
- proxies: options.proxies,
3557
- resolvedResolve,
3558
- resolvedCss,
3559
- vite,
3560
- fs,
3561
- path,
3562
- generateSSREntry(opts) {
3563
- return generateSSREntry(ctx, opts);
3564
- },
3565
- buildBundle(opts) {
3566
- return buildBundle(ctx, opts);
3567
- },
3568
- copyStaticAssets(destDir, opts) {
3569
- return copyStaticAssets(ctx, destDir, opts);
3570
- }
3571
- };
3572
- console.log(` Running adapter: ${adapter.name}...
3573
- `);
3574
- await adapter.build(ctx);
3575
- }
3576
- } finally {
3577
- delete process.env.__FINESOFT_SUB_BUILD__;
3578
- }
3579
- }
3580
- };
3581
- }
3582
- // Annotate the CommonJS export names for ESM import in node:
3583
- 0 && (module.exports = {
3584
- ACTION_KINDS,
3585
- ActionDispatcher,
3586
- BaseController,
3587
- BaseLogger,
3588
- CompositeLogger,
3589
- CompositeLoggerFactory,
3590
- ConsoleLogger,
3591
- ConsoleLoggerFactory,
3592
- Container,
3593
- DEP_KEYS,
3594
- Framework,
3595
- History,
3596
- HttpClient,
3597
- HttpError,
3598
- IntentDispatcher,
3599
- LruMap,
3600
- PrefetchedIntents,
3601
- Router,
3602
- SSR_PLACEHOLDERS,
3603
- autoAdapter,
3604
- buildUrl,
3605
- cloudflareAdapter,
3606
- createBrowserContext,
3607
- createPrefetchedIntentsFromDom,
3608
- createSSRApp,
3609
- createSSRRender,
3610
- createServer,
3611
- createServerContext,
3612
- defineRoutes,
3613
- deny,
3614
- deserializeServerData,
3615
- detectRuntime,
3616
- finesoftFrontViteConfig,
3617
- generateProxyCode,
3618
- generateUuid,
3619
- getBaseUrl,
3620
- injectCSRShell,
3621
- injectSSRContent,
3622
- isCompoundAction,
3623
- isExternalUrlAction,
3624
- isFlowAction,
3625
- isNone,
3626
- isSome,
3627
- makeDependencies,
3628
- makeExternalUrlAction,
3629
- makeFlowAction,
3630
- mapEach,
3631
- netlifyAdapter,
3632
- next,
3633
- nodeAdapter,
3634
- parseAcceptLanguage,
3635
- pipe,
3636
- pipeAsync,
3637
- redirect,
3638
- registerActionHandlers,
3639
- registerExternalUrlHandler,
3640
- registerFlowActionHandler,
3641
- registerProxyRoutes,
3642
- removeHost,
3643
- removeQueryParams,
3644
- removeScheme,
3645
- resetFilterCache,
3646
- resolveAdapter,
3647
- resolveRoot,
3648
- rewrite,
3649
- runAfterLoadGuards,
3650
- runBeforeLoadGuards,
3651
- serializeServerData,
3652
- shouldLog,
3653
- ssrRender,
3654
- stableStringify,
3655
- startBrowserApp,
3656
- startServer,
3657
- staticAdapter,
3658
- tryScroll,
3659
- vercelAdapter
3660
- });
3661
- //# sourceMappingURL=index.cjs.map