@finesoft/front 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,45 +17,1522 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
18
- var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
19
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
29
 
21
30
  // src/index.ts
22
31
  var index_exports = {};
23
32
  __export(index_exports, {
24
- History: () => import_browser.History,
25
- createPrefetchedIntentsFromDom: () => import_browser.createPrefetchedIntentsFromDom,
26
- createSSRRender: () => import_ssr.createSSRRender,
27
- deserializeServerData: () => import_browser.deserializeServerData,
28
- injectSSRContent: () => import_ssr.injectSSRContent,
29
- registerActionHandlers: () => import_browser.registerActionHandlers,
30
- registerExternalUrlHandler: () => import_browser.registerExternalUrlHandler,
31
- registerFlowActionHandler: () => import_browser.registerFlowActionHandler,
32
- serializeServerData: () => import_ssr.serializeServerData,
33
- ssrRender: () => import_ssr.ssrRender,
34
- startBrowserApp: () => import_browser.startBrowserApp,
35
- tryScroll: () => import_browser.tryScroll
33
+ ACTION_KINDS: () => ACTION_KINDS,
34
+ ActionDispatcher: () => ActionDispatcher,
35
+ BaseController: () => BaseController,
36
+ BaseLogger: () => BaseLogger,
37
+ CompositeLogger: () => CompositeLogger,
38
+ CompositeLoggerFactory: () => CompositeLoggerFactory,
39
+ ConsoleLogger: () => ConsoleLogger,
40
+ ConsoleLoggerFactory: () => ConsoleLoggerFactory,
41
+ Container: () => Container,
42
+ DEP_KEYS: () => DEP_KEYS,
43
+ Framework: () => Framework,
44
+ History: () => History,
45
+ HttpClient: () => HttpClient,
46
+ HttpError: () => HttpError,
47
+ IntentDispatcher: () => IntentDispatcher,
48
+ LruMap: () => LruMap,
49
+ PrefetchedIntents: () => PrefetchedIntents,
50
+ Router: () => Router,
51
+ buildUrl: () => buildUrl,
52
+ createPrefetchedIntentsFromDom: () => createPrefetchedIntentsFromDom,
53
+ createSSRApp: () => createSSRApp,
54
+ createSSRRender: () => createSSRRender,
55
+ createServer: () => createServer,
56
+ defineRoutes: () => defineRoutes,
57
+ deserializeServerData: () => deserializeServerData,
58
+ detectRuntime: () => detectRuntime,
59
+ generateUuid: () => generateUuid,
60
+ getBaseUrl: () => getBaseUrl,
61
+ injectSSRContent: () => injectSSRContent,
62
+ isCompoundAction: () => isCompoundAction,
63
+ isExternalUrlAction: () => isExternalUrlAction,
64
+ isFlowAction: () => isFlowAction,
65
+ isNone: () => isNone,
66
+ isSome: () => isSome,
67
+ makeDependencies: () => makeDependencies,
68
+ makeExternalUrlAction: () => makeExternalUrlAction,
69
+ makeFlowAction: () => makeFlowAction,
70
+ mapEach: () => mapEach,
71
+ parseAcceptLanguage: () => parseAcceptLanguage,
72
+ pipe: () => pipe,
73
+ pipeAsync: () => pipeAsync,
74
+ registerActionHandlers: () => registerActionHandlers,
75
+ registerExternalUrlHandler: () => registerExternalUrlHandler,
76
+ registerFlowActionHandler: () => registerFlowActionHandler,
77
+ removeHost: () => removeHost,
78
+ removeQueryParams: () => removeQueryParams,
79
+ removeScheme: () => removeScheme,
80
+ resetFilterCache: () => resetFilterCache,
81
+ resolveRoot: () => resolveRoot,
82
+ serializeServerData: () => serializeServerData,
83
+ shouldLog: () => shouldLog,
84
+ ssrRender: () => ssrRender,
85
+ stableStringify: () => stableStringify,
86
+ startBrowserApp: () => startBrowserApp,
87
+ startServer: () => startServer,
88
+ tryScroll: () => tryScroll
36
89
  });
37
90
  module.exports = __toCommonJS(index_exports);
38
- __reExport(index_exports, require("@finesoft/core"), module.exports);
39
- var import_browser = require("@finesoft/browser");
40
- var import_ssr = require("@finesoft/ssr");
41
- __reExport(index_exports, require("@finesoft/server"), module.exports);
91
+
92
+ // ../core/src/actions/types.ts
93
+ var ACTION_KINDS = {
94
+ FLOW: "flow",
95
+ EXTERNAL_URL: "externalUrl",
96
+ COMPOUND: "compound"
97
+ };
98
+ function isFlowAction(action) {
99
+ return action.kind === ACTION_KINDS.FLOW;
100
+ }
101
+ function isExternalUrlAction(action) {
102
+ return action.kind === ACTION_KINDS.EXTERNAL_URL;
103
+ }
104
+ function isCompoundAction(action) {
105
+ return action.kind === ACTION_KINDS.COMPOUND;
106
+ }
107
+ function makeFlowAction(url, presentationContext) {
108
+ return { kind: ACTION_KINDS.FLOW, url, presentationContext };
109
+ }
110
+ function makeExternalUrlAction(url) {
111
+ return { kind: ACTION_KINDS.EXTERNAL_URL, url };
112
+ }
113
+
114
+ // ../core/src/actions/dispatcher.ts
115
+ var ActionDispatcher = class {
116
+ handlers = /* @__PURE__ */ new Map();
117
+ wiredActions = /* @__PURE__ */ new Set();
118
+ /** 注册指定 kind 的 handler(防止重复注册) */
119
+ onAction(kind, handler) {
120
+ if (this.wiredActions.has(kind)) {
121
+ console.warn(
122
+ `[ActionDispatcher] kind="${kind}" already registered, skipping`
123
+ );
124
+ return;
125
+ }
126
+ this.wiredActions.add(kind);
127
+ this.handlers.set(kind, handler);
128
+ }
129
+ /** 执行一个 Action(CompoundAction 递归展开) */
130
+ async perform(action) {
131
+ if (isCompoundAction(action)) {
132
+ for (const subAction of action.actions) {
133
+ await this.perform(subAction);
134
+ }
135
+ return;
136
+ }
137
+ const handler = this.handlers.get(action.kind);
138
+ if (!handler) {
139
+ console.warn(
140
+ `[ActionDispatcher] No handler for kind="${action.kind}"`
141
+ );
142
+ return;
143
+ }
144
+ await handler(action);
145
+ }
146
+ };
147
+
148
+ // ../core/src/intents/dispatcher.ts
149
+ var IntentDispatcher = class {
150
+ controllers = /* @__PURE__ */ new Map();
151
+ /** 注册一个 IntentController */
152
+ register(controller) {
153
+ this.controllers.set(controller.intentId, controller);
154
+ }
155
+ /** 分发 Intent 到对应 Controller */
156
+ async dispatch(intent, container) {
157
+ const controller = this.controllers.get(intent.id);
158
+ if (!controller) {
159
+ throw new Error(
160
+ `[IntentDispatcher] No controller for "${intent.id}". Registered: [${Array.from(this.controllers.keys()).join(", ")}]`
161
+ );
162
+ }
163
+ return controller.perform(intent, container);
164
+ }
165
+ /** 检查是否已注册某个 Intent */
166
+ has(intentId) {
167
+ return this.controllers.has(intentId);
168
+ }
169
+ };
170
+
171
+ // ../core/src/dependencies/container.ts
172
+ var Container = class {
173
+ registrations = /* @__PURE__ */ new Map();
174
+ /** 注册依赖(默认单例) */
175
+ register(key, factory, singleton = true) {
176
+ this.registrations.set(key, { factory, singleton });
177
+ return this;
178
+ }
179
+ /** 解析依赖 */
180
+ resolve(key) {
181
+ const reg = this.registrations.get(key);
182
+ if (!reg) {
183
+ throw new Error(`[Container] No registration for key: "${key}"`);
184
+ }
185
+ if (reg.singleton) {
186
+ if (reg.instance === void 0) {
187
+ reg.instance = reg.factory();
188
+ }
189
+ return reg.instance;
190
+ }
191
+ return reg.factory();
192
+ }
193
+ /** 检查是否已注册 */
194
+ has(key) {
195
+ return this.registrations.has(key);
196
+ }
197
+ /** 销毁容器,清除所有缓存 */
198
+ dispose() {
199
+ for (const reg of this.registrations.values()) {
200
+ reg.instance = void 0;
201
+ }
202
+ this.registrations.clear();
203
+ }
204
+ };
205
+
206
+ // ../core/src/logger/base.ts
207
+ var BaseLogger = class {
208
+ category;
209
+ constructor(category) {
210
+ this.category = category;
211
+ }
212
+ };
213
+
214
+ // ../core/src/logger/local-storage-filter.ts
215
+ var LEVEL_TO_NUM = {
216
+ "*": 4,
217
+ debug: 4,
218
+ info: 3,
219
+ warn: 2,
220
+ error: 1,
221
+ off: 0,
222
+ "": 0
223
+ };
224
+ var cachedRules;
225
+ var cachedRaw;
226
+ function parseRules() {
227
+ if (typeof globalThis.localStorage === "undefined") {
228
+ return {};
229
+ }
230
+ let raw;
231
+ try {
232
+ raw = globalThis.localStorage.getItem("onyxLog");
233
+ } catch {
234
+ return {};
235
+ }
236
+ if (!raw) return {};
237
+ if (raw === cachedRaw && cachedRules) return cachedRules;
238
+ cachedRaw = raw;
239
+ const rules = {};
240
+ const parts = raw.split(",");
241
+ for (const part of parts) {
242
+ const [name, level] = part.trim().split("=");
243
+ if (!name || level === void 0) continue;
244
+ const num = LEVEL_TO_NUM[level.toLowerCase()] ?? void 0;
245
+ if (num === void 0) continue;
246
+ if (name === "*") {
247
+ rules.defaultLevel = num;
248
+ } else {
249
+ rules.named ??= {};
250
+ rules.named[name] = num;
251
+ }
252
+ }
253
+ cachedRules = rules;
254
+ return rules;
255
+ }
256
+ function shouldLog(name, level) {
257
+ const rules = parseRules();
258
+ if (rules.defaultLevel === void 0 && !rules.named) {
259
+ return true;
260
+ }
261
+ const currentNum = LEVEL_TO_NUM[level] ?? 4;
262
+ if (rules.named?.[name] !== void 0) {
263
+ return currentNum <= rules.named[name];
264
+ }
265
+ if (rules.defaultLevel !== void 0) {
266
+ return currentNum <= rules.defaultLevel;
267
+ }
268
+ return true;
269
+ }
270
+ function resetFilterCache() {
271
+ cachedRules = void 0;
272
+ cachedRaw = void 0;
273
+ }
274
+
275
+ // ../core/src/logger/console.ts
276
+ var ConsoleLogger = class extends BaseLogger {
277
+ debug(...args) {
278
+ if (shouldLog(this.category, "debug")) {
279
+ console.debug(`[${this.category}]`, ...args);
280
+ }
281
+ return "";
282
+ }
283
+ info(...args) {
284
+ if (shouldLog(this.category, "info")) {
285
+ console.info(`[${this.category}]`, ...args);
286
+ }
287
+ return "";
288
+ }
289
+ warn(...args) {
290
+ if (shouldLog(this.category, "warn")) {
291
+ console.warn(`[${this.category}]`, ...args);
292
+ }
293
+ return "";
294
+ }
295
+ error(...args) {
296
+ console.error(`[${this.category}]`, ...args);
297
+ return "";
298
+ }
299
+ };
300
+ var ConsoleLoggerFactory = class {
301
+ loggerFor(category) {
302
+ return new ConsoleLogger(category);
303
+ }
304
+ };
305
+
306
+ // ../core/src/dependencies/make-dependencies.ts
307
+ var DEP_KEYS = {
308
+ LOGGER: "logger",
309
+ LOGGER_FACTORY: "loggerFactory",
310
+ NET: "net",
311
+ LOCALE: "locale",
312
+ STORAGE: "storage",
313
+ FEATURE_FLAGS: "featureFlags",
314
+ METRICS: "metrics",
315
+ FETCH: "fetch"
316
+ };
317
+ var DefaultLocale = class {
318
+ language = "en";
319
+ storefront = "us";
320
+ setActiveLocale(language, storefront) {
321
+ this.language = language;
322
+ this.storefront = storefront;
323
+ }
324
+ };
325
+ var MemoryStorage = class {
326
+ store = /* @__PURE__ */ new Map();
327
+ get(key) {
328
+ return this.store.get(key);
329
+ }
330
+ set(key, value) {
331
+ this.store.set(key, value);
332
+ }
333
+ delete(key) {
334
+ this.store.delete(key);
335
+ }
336
+ };
337
+ var DefaultFeatureFlags = class {
338
+ flags;
339
+ constructor(flags = {}) {
340
+ this.flags = flags;
341
+ }
342
+ isEnabled(key) {
343
+ return this.flags[key] === true;
344
+ }
345
+ getString(key) {
346
+ const v = this.flags[key];
347
+ return typeof v === "string" ? v : void 0;
348
+ }
349
+ getNumber(key) {
350
+ const v = this.flags[key];
351
+ return typeof v === "number" ? v : void 0;
352
+ }
353
+ };
354
+ var ConsoleMetrics = class {
355
+ recordPageView(page, fields) {
356
+ console.info(`[Metrics:PageView] ${page}`, fields ?? "");
357
+ }
358
+ recordEvent(name, fields) {
359
+ console.info(`[Metrics:Event] ${name}`, fields ?? "");
360
+ }
361
+ };
362
+ function makeDependencies(container, options = {}) {
363
+ const {
364
+ fetch: fetchFn = globalThis.fetch?.bind(globalThis),
365
+ language = "en",
366
+ storefront = "us",
367
+ featureFlags = {}
368
+ } = options;
369
+ const loggerFactory = new ConsoleLoggerFactory();
370
+ container.register(
371
+ DEP_KEYS.LOGGER_FACTORY,
372
+ () => loggerFactory
373
+ );
374
+ container.register(
375
+ DEP_KEYS.LOGGER,
376
+ () => loggerFactory.loggerFor("framework")
377
+ );
378
+ container.register(DEP_KEYS.NET, () => ({
379
+ fetch: (url, opts) => fetchFn(url, opts)
380
+ }));
381
+ container.register(DEP_KEYS.LOCALE, () => {
382
+ const locale = new DefaultLocale();
383
+ locale.setActiveLocale(language, storefront);
384
+ return locale;
385
+ });
386
+ container.register(DEP_KEYS.STORAGE, () => new MemoryStorage());
387
+ container.register(
388
+ DEP_KEYS.FEATURE_FLAGS,
389
+ () => new DefaultFeatureFlags(featureFlags)
390
+ );
391
+ container.register(
392
+ DEP_KEYS.METRICS,
393
+ () => new ConsoleMetrics()
394
+ );
395
+ container.register(DEP_KEYS.FETCH, () => fetchFn);
396
+ }
397
+
398
+ // ../core/src/router/router.ts
399
+ var Router = class {
400
+ routes = [];
401
+ /** 添加路由规则 */
402
+ add(pattern, intentId) {
403
+ const paramNames = [];
404
+ const regexStr = pattern.replace(
405
+ /\/:(\w+)(\?)?/g,
406
+ (_, name, optional) => {
407
+ paramNames.push(name);
408
+ return optional ? "(?:/([^/]+))?" : "/([^/]+)";
409
+ }
410
+ );
411
+ this.routes.push({
412
+ pattern,
413
+ intentId,
414
+ regex: new RegExp(`^${regexStr}/?$`),
415
+ paramNames
416
+ });
417
+ return this;
418
+ }
419
+ /** 解析 URL → RouteMatch */
420
+ resolve(urlOrPath) {
421
+ const path = this.extractPath(urlOrPath);
422
+ const queryParams = this.extractQueryParams(urlOrPath);
423
+ for (const route of this.routes) {
424
+ const match = path.match(route.regex);
425
+ if (match) {
426
+ const params = {};
427
+ route.paramNames.forEach((name, index) => {
428
+ const value = match[index + 1];
429
+ if (value) params[name] = value;
430
+ });
431
+ for (const [k, v] of Object.entries(queryParams)) {
432
+ if (!(k in params)) params[k] = v;
433
+ }
434
+ return {
435
+ intent: { id: route.intentId, params },
436
+ action: makeFlowAction(urlOrPath)
437
+ };
438
+ }
439
+ }
440
+ return null;
441
+ }
442
+ /** 获取所有已注册的路由 */
443
+ getRoutes() {
444
+ return this.routes.map((r) => `${r.pattern} \u2192 ${r.intentId}`);
445
+ }
446
+ extractPath(url) {
447
+ try {
448
+ const parsed = new URL(url, "http://localhost");
449
+ return parsed.pathname;
450
+ } catch {
451
+ return url.split("?")[0].split("#")[0];
452
+ }
453
+ }
454
+ extractQueryParams(url) {
455
+ try {
456
+ const parsed = new URL(url, "http://localhost");
457
+ const params = {};
458
+ parsed.searchParams.forEach((v, k) => {
459
+ params[k] = v;
460
+ });
461
+ return params;
462
+ } catch {
463
+ return {};
464
+ }
465
+ }
466
+ };
467
+
468
+ // ../core/src/logger/composite.ts
469
+ var CompositeLoggerFactory = class {
470
+ constructor(factories) {
471
+ this.factories = factories;
472
+ }
473
+ loggerFor(name) {
474
+ return new CompositeLogger(
475
+ this.factories.map((f) => f.loggerFor(name))
476
+ );
477
+ }
478
+ };
479
+ var CompositeLogger = class {
480
+ constructor(loggers) {
481
+ this.loggers = loggers;
482
+ }
483
+ debug(...args) {
484
+ return this.callAll("debug", args);
485
+ }
486
+ info(...args) {
487
+ return this.callAll("info", args);
488
+ }
489
+ warn(...args) {
490
+ return this.callAll("warn", args);
491
+ }
492
+ error(...args) {
493
+ return this.callAll("error", args);
494
+ }
495
+ callAll(method, args) {
496
+ for (const logger of this.loggers) {
497
+ logger[method](...args);
498
+ }
499
+ return "";
500
+ }
501
+ };
502
+
503
+ // ../core/src/prefetched-intents/stable-stringify.ts
504
+ function stableStringify(obj) {
505
+ if (obj === null || obj === void 0) return String(obj);
506
+ if (typeof obj !== "object") return JSON.stringify(obj);
507
+ if (Array.isArray(obj)) {
508
+ return "[" + obj.map(stableStringify).join(",") + "]";
509
+ }
510
+ const keys = Object.keys(obj).sort();
511
+ const parts = keys.filter((k) => obj[k] !== void 0).map(
512
+ (k) => JSON.stringify(k) + ":" + stableStringify(obj[k])
513
+ );
514
+ return "{" + parts.join(",") + "}";
515
+ }
516
+
517
+ // ../core/src/prefetched-intents/prefetched-intents.ts
518
+ var PrefetchedIntents = class _PrefetchedIntents {
519
+ intents;
520
+ constructor(intents) {
521
+ this.intents = intents;
522
+ }
523
+ /** 从 PrefetchedIntent 数组创建缓存实例 */
524
+ static fromArray(items) {
525
+ const map = /* @__PURE__ */ new Map();
526
+ for (const item of items) {
527
+ if (item.intent && item.data !== void 0) {
528
+ const key = stableStringify(item.intent);
529
+ map.set(key, item.data);
530
+ }
531
+ }
532
+ return new _PrefetchedIntents(map);
533
+ }
534
+ /** 创建空缓存实例 */
535
+ static empty() {
536
+ return new _PrefetchedIntents(/* @__PURE__ */ new Map());
537
+ }
538
+ /**
539
+ * 获取缓存的 Intent 结果(一次性使用)。
540
+ * 命中后从缓存中删除。
541
+ */
542
+ get(intent) {
543
+ const key = stableStringify(intent);
544
+ const data = this.intents.get(key);
545
+ if (data !== void 0) {
546
+ this.intents.delete(key);
547
+ return data;
548
+ }
549
+ return void 0;
550
+ }
551
+ /** 检查缓存中是否有某个 Intent 的数据 */
552
+ has(intent) {
553
+ return this.intents.has(stableStringify(intent));
554
+ }
555
+ /** 缓存中的条目数 */
556
+ get size() {
557
+ return this.intents.size;
558
+ }
559
+ };
560
+
561
+ // ../core/src/framework.ts
562
+ var Framework = class _Framework {
563
+ container;
564
+ intentDispatcher;
565
+ actionDispatcher;
566
+ router;
567
+ prefetchedIntents;
568
+ constructor(container, prefetchedIntents) {
569
+ this.container = container;
570
+ this.intentDispatcher = new IntentDispatcher();
571
+ this.actionDispatcher = new ActionDispatcher();
572
+ this.router = new Router();
573
+ this.prefetchedIntents = prefetchedIntents;
574
+ }
575
+ /** 创建并初始化 Framework 实例 */
576
+ static create(config = {}) {
577
+ const container = new Container();
578
+ makeDependencies(container, config);
579
+ const fw = new _Framework(
580
+ container,
581
+ config.prefetchedIntents ?? PrefetchedIntents.empty()
582
+ );
583
+ config.setupRoutes?.(fw.router);
584
+ return fw;
585
+ }
586
+ /** 分发 Intent — 获取页面数据 */
587
+ async dispatch(intent) {
588
+ const logger = this.container.resolve(DEP_KEYS.LOGGER);
589
+ const cached = this.prefetchedIntents.get(intent);
590
+ if (cached !== void 0) {
591
+ logger.debug(
592
+ `[Framework] re-using prefetched intent response for: ${intent.id}`,
593
+ intent.params
594
+ );
595
+ return cached;
596
+ }
597
+ logger.debug(
598
+ `[Framework] dispatch intent: ${intent.id}`,
599
+ intent.params
600
+ );
601
+ return this.intentDispatcher.dispatch(intent, this.container);
602
+ }
603
+ /** 执行 Action — 处理用户交互 */
604
+ async perform(action) {
605
+ const logger = this.container.resolve(DEP_KEYS.LOGGER);
606
+ logger.debug(`[Framework] perform action: ${action.kind}`);
607
+ return this.actionDispatcher.perform(action);
608
+ }
609
+ /** 路由 URL — 将 URL 解析为 Intent + Action */
610
+ routeUrl(url) {
611
+ return this.router.resolve(url);
612
+ }
613
+ /** 记录页面访问事件 */
614
+ didEnterPage(page) {
615
+ const metrics = this.container.resolve(
616
+ DEP_KEYS.METRICS
617
+ );
618
+ metrics.recordPageView(page.pageType, {
619
+ pageId: page.id,
620
+ title: page.title
621
+ });
622
+ }
623
+ /** 注册 Action 处理器 */
624
+ onAction(kind, handler) {
625
+ this.actionDispatcher.onAction(kind, handler);
626
+ }
627
+ /** 注册 Intent Controller */
628
+ registerIntent(controller) {
629
+ this.intentDispatcher.register(controller);
630
+ }
631
+ /** 销毁 Framework 实例 */
632
+ dispose() {
633
+ this.container.dispose();
634
+ }
635
+ };
636
+
637
+ // ../core/src/http/client.ts
638
+ var HttpError = class extends Error {
639
+ constructor(status, statusText, body) {
640
+ super(`HTTP ${status}: ${statusText}`);
641
+ this.status = status;
642
+ this.statusText = statusText;
643
+ this.body = body;
644
+ this.name = "HttpError";
645
+ }
646
+ };
647
+ var HttpClient = class {
648
+ baseUrl;
649
+ defaultHeaders;
650
+ fetchFn;
651
+ constructor(config) {
652
+ this.baseUrl = config.baseUrl;
653
+ this.defaultHeaders = config.defaultHeaders ?? {};
654
+ this.fetchFn = config.fetch ?? globalThis.fetch.bind(globalThis);
655
+ }
656
+ /** GET 请求,返回解析后的 JSON */
657
+ async get(path, params) {
658
+ return this.request("GET", path, { params });
659
+ }
660
+ /** POST 请求,自动序列化 body 为 JSON */
661
+ async post(path, body, params) {
662
+ return this.request("POST", path, { body, params });
663
+ }
664
+ /** PUT 请求 */
665
+ async put(path, body, params) {
666
+ return this.request("PUT", path, { body, params });
667
+ }
668
+ /** DELETE 请求 */
669
+ async del(path, params) {
670
+ return this.request("DELETE", path, { params });
671
+ }
672
+ /**
673
+ * 底层请求方法 — 子类可覆写以自定义行为
674
+ *
675
+ * 自动处理:
676
+ * - URL 拼接 (baseUrl + path + params)
677
+ * - 默认 headers 合并
678
+ * - JSON body 序列化
679
+ * - 响应 JSON 解析
680
+ * - 非 2xx 状态码抛出 HttpError
681
+ */
682
+ async request(method, path, options) {
683
+ const url = this.buildUrl(path, options?.params);
684
+ const headers = {
685
+ ...this.defaultHeaders,
686
+ ...options?.headers
687
+ };
688
+ const init = { method, headers };
689
+ if (options?.body !== void 0) {
690
+ headers["Content-Type"] = headers["Content-Type"] ?? "application/json";
691
+ init.body = JSON.stringify(options.body);
692
+ }
693
+ const response = await this.fetchFn(url, init);
694
+ if (!response.ok) {
695
+ const body = await response.text().catch(() => void 0);
696
+ throw new HttpError(response.status, response.statusText, body);
697
+ }
698
+ return response.json();
699
+ }
700
+ /** 构建完整 URL — 子类可覆写以自定义 URL 拼接逻辑 */
701
+ buildUrl(path, params) {
702
+ const base = this.baseUrl.endsWith("/") ? this.baseUrl.slice(0, -1) : this.baseUrl;
703
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
704
+ const url = new URL(`${base}${normalizedPath}`, "http://placeholder");
705
+ if (params) {
706
+ for (const [k, v] of Object.entries(params)) {
707
+ url.searchParams.set(k, v);
708
+ }
709
+ }
710
+ if (this.baseUrl.startsWith("http")) {
711
+ return url.toString();
712
+ }
713
+ return `${url.pathname}${url.search}`;
714
+ }
715
+ };
716
+
717
+ // ../core/src/intents/base-controller.ts
718
+ var BaseController = class {
719
+ /**
720
+ * 错误回退 — 子类可选覆写
721
+ *
722
+ * 当 execute() 抛出异常时调用。
723
+ * 默认行为: 重新抛出原始错误。
724
+ *
725
+ * @param params - Intent 参数
726
+ * @param error - execute() 抛出的错误
727
+ * @returns 回退数据
728
+ */
729
+ fallback(params, error) {
730
+ throw error;
731
+ }
732
+ /**
733
+ * IntentController.perform() 实现
734
+ *
735
+ * 自动 try/catch → fallback 模式。
736
+ */
737
+ async perform(intent, container) {
738
+ const params = intent.params ?? {};
739
+ try {
740
+ return await this.execute(params, container);
741
+ } catch (e) {
742
+ return this.fallback(
743
+ params,
744
+ e instanceof Error ? e : new Error(String(e))
745
+ );
746
+ }
747
+ }
748
+ };
749
+
750
+ // ../core/src/data/mapper.ts
751
+ function pipe(...mappers) {
752
+ return (input) => mappers.reduce((acc, mapper) => mapper(acc), input);
753
+ }
754
+ function pipeAsync(...mappers) {
755
+ return async (input) => {
756
+ let acc = input;
757
+ for (const mapper of mappers) {
758
+ acc = await mapper(acc);
759
+ }
760
+ return acc;
761
+ };
762
+ }
763
+ function mapEach(mapper) {
764
+ return (items) => items.map(mapper);
765
+ }
766
+
767
+ // ../core/src/bootstrap/define-routes.ts
768
+ function defineRoutes(framework, definitions) {
769
+ const registeredIntents = /* @__PURE__ */ new Set();
770
+ for (const def of definitions) {
771
+ if (def.controller && !registeredIntents.has(def.intentId)) {
772
+ framework.registerIntent(def.controller);
773
+ registeredIntents.add(def.intentId);
774
+ }
775
+ framework.router.add(def.path, def.intentId);
776
+ }
777
+ }
778
+
779
+ // ../core/src/utils/lru-map.ts
780
+ var LruMap = class {
781
+ map = /* @__PURE__ */ new Map();
782
+ capacity;
783
+ constructor(capacity) {
784
+ this.capacity = capacity;
785
+ }
786
+ get(key) {
787
+ const value = this.map.get(key);
788
+ if (value !== void 0) {
789
+ this.map.delete(key);
790
+ this.map.set(key, value);
791
+ }
792
+ return value;
793
+ }
794
+ set(key, value) {
795
+ if (this.map.has(key)) {
796
+ this.map.delete(key);
797
+ } else if (this.map.size >= this.capacity) {
798
+ const oldest = this.map.keys().next().value;
799
+ if (oldest !== void 0) {
800
+ this.map.delete(oldest);
801
+ }
802
+ }
803
+ this.map.set(key, value);
804
+ }
805
+ has(key) {
806
+ return this.map.has(key);
807
+ }
808
+ delete(key) {
809
+ return this.map.delete(key);
810
+ }
811
+ get size() {
812
+ return this.map.size;
813
+ }
814
+ clear() {
815
+ this.map.clear();
816
+ }
817
+ };
818
+
819
+ // ../core/src/utils/optional.ts
820
+ function isSome(value) {
821
+ return value !== null && value !== void 0;
822
+ }
823
+ function isNone(value) {
824
+ return value === null || value === void 0;
825
+ }
826
+
827
+ // ../core/src/utils/url.ts
828
+ function removeScheme(url) {
829
+ return url.replace(/^https?:\/\//, "");
830
+ }
831
+ function removeHost(url) {
832
+ try {
833
+ const parsed = new URL(url);
834
+ return parsed.pathname + parsed.search + parsed.hash;
835
+ } catch {
836
+ return url;
837
+ }
838
+ }
839
+ function removeQueryParams(url) {
840
+ return url.split("?")[0];
841
+ }
842
+ function getBaseUrl(url) {
843
+ return url.split("?")[0].split("#")[0];
844
+ }
845
+ function buildUrl(path, params) {
846
+ if (!params) return path;
847
+ const searchParams = new URLSearchParams();
848
+ for (const [key, value] of Object.entries(params)) {
849
+ if (value !== void 0) {
850
+ searchParams.set(key, value);
851
+ }
852
+ }
853
+ const qs = searchParams.toString();
854
+ return qs ? `${path}?${qs}` : path;
855
+ }
856
+
857
+ // ../core/src/utils/uuid.ts
858
+ function generateUuid() {
859
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
860
+ return crypto.randomUUID();
861
+ }
862
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
863
+ const r = Math.random() * 16 | 0;
864
+ const v = c === "x" ? r : r & 3 | 8;
865
+ return v.toString(16);
866
+ });
867
+ }
868
+
869
+ // ../browser/src/action-handlers/external-url-action.ts
870
+ function registerExternalUrlHandler(deps) {
871
+ const { framework, log } = deps;
872
+ framework.onAction(
873
+ ACTION_KINDS.EXTERNAL_URL,
874
+ (action) => {
875
+ log.debug(`ExternalUrlAction \u2192 ${action.url}`);
876
+ window.open(action.url, "_blank", "noopener,noreferrer");
877
+ }
878
+ );
879
+ }
880
+
881
+ // ../browser/src/utils/try-scroll.ts
882
+ var MAX_TRIES = 100;
883
+ var FUDGE = 16;
884
+ var pendingFrame = null;
885
+ function tryScroll(log, getScrollableElement, scrollY) {
886
+ if (pendingFrame !== null) {
887
+ cancelAnimationFrame(pendingFrame);
888
+ pendingFrame = null;
889
+ }
890
+ let tries = 0;
891
+ pendingFrame = requestAnimationFrame(function attempt() {
892
+ if (++tries >= MAX_TRIES) {
893
+ log.warn(
894
+ `tryScroll: gave up after ${MAX_TRIES} frames, target=${scrollY}`
895
+ );
896
+ pendingFrame = null;
897
+ return;
898
+ }
899
+ const el = getScrollableElement();
900
+ if (!el) {
901
+ log.warn(
902
+ "could not restore scroll: the scrollable element is missing"
903
+ );
904
+ return;
905
+ }
906
+ const { scrollHeight, offsetHeight } = el;
907
+ const canScroll = scrollY + offsetHeight <= scrollHeight + FUDGE;
908
+ if (!canScroll) {
909
+ log.info("page is not tall enough for scroll yet", {
910
+ scrollHeight,
911
+ offsetHeight
912
+ });
913
+ pendingFrame = requestAnimationFrame(attempt);
914
+ return;
915
+ }
916
+ el.scrollTop = scrollY;
917
+ log.info("scroll restored to", scrollY);
918
+ pendingFrame = null;
919
+ });
920
+ }
921
+
922
+ // ../browser/src/utils/history.ts
923
+ var HISTORY_SIZE_LIMIT = 10;
924
+ var History = class {
925
+ entries;
926
+ log;
927
+ getScrollablePageElement;
928
+ currentStateId;
929
+ constructor(log, options, sizeLimit = HISTORY_SIZE_LIMIT) {
930
+ this.entries = new LruMap(sizeLimit);
931
+ this.log = log;
932
+ this.getScrollablePageElement = options.getScrollablePageElement;
933
+ }
934
+ replaceState(state, url) {
935
+ const id = generateUuid();
936
+ window.history.replaceState({ id }, "", url);
937
+ this.currentStateId = id;
938
+ this.entries.set(id, { state, scrollY: 0 });
939
+ this.scrollTop = 0;
940
+ this.log.info("replaceState", state, url, id);
941
+ }
942
+ pushState(state, url) {
943
+ const id = generateUuid();
944
+ window.history.pushState({ id }, "", url);
945
+ this.currentStateId = id;
946
+ this.entries.set(id, { state, scrollY: 0 });
947
+ this.scrollTop = 0;
948
+ this.log.info("pushState", state, url, id);
949
+ }
950
+ beforeTransition() {
951
+ const { state } = window.history;
952
+ if (!state) return;
953
+ const oldEntry = this.entries.get(state.id);
954
+ if (!oldEntry) {
955
+ this.log.info(
956
+ "current history state evicted from LRU, not saving scroll position"
957
+ );
958
+ return;
959
+ }
960
+ const { scrollTop } = this;
961
+ this.entries.set(state.id, { ...oldEntry, scrollY: scrollTop });
962
+ this.log.info("saving scroll position", scrollTop);
963
+ }
964
+ onPopState(listener) {
965
+ window.addEventListener("popstate", (event) => {
966
+ this.currentStateId = event.state?.id;
967
+ if (!this.currentStateId) {
968
+ this.log.warn(
969
+ "encountered a null event.state.id in onPopState event:",
970
+ window.location.href
971
+ );
972
+ }
973
+ this.log.info("popstate", this.entries, this.currentStateId);
974
+ const entry = this.currentStateId ? this.entries.get(this.currentStateId) : void 0;
975
+ listener(window.location.href, entry?.state);
976
+ if (!entry) {
977
+ return;
978
+ }
979
+ const { scrollY } = entry;
980
+ this.log.info("restoring scroll to", scrollY);
981
+ tryScroll(this.log, () => this.getScrollablePageElement(), scrollY);
982
+ });
983
+ }
984
+ updateState(update) {
985
+ if (!this.currentStateId) {
986
+ this.log.warn(
987
+ "failed: encountered a null currentStateId inside updateState"
988
+ );
989
+ return;
990
+ }
991
+ const currentState = this.entries.get(this.currentStateId);
992
+ const newState = update(currentState?.state);
993
+ this.log.info("updateState", newState, this.currentStateId);
994
+ this.entries.set(this.currentStateId, {
995
+ ...currentState,
996
+ state: newState
997
+ });
998
+ }
999
+ get scrollTop() {
1000
+ return this.getScrollablePageElement()?.scrollTop || 0;
1001
+ }
1002
+ set scrollTop(scrollTop) {
1003
+ const element = this.getScrollablePageElement();
1004
+ if (element) {
1005
+ element.scrollTop = scrollTop;
1006
+ }
1007
+ }
1008
+ };
1009
+
1010
+ // ../browser/src/action-handlers/flow-action.ts
1011
+ function registerFlowActionHandler(deps) {
1012
+ const { framework, log, callbacks, updateApp } = deps;
1013
+ let isFirstPage = true;
1014
+ const history = new History(log, {
1015
+ getScrollablePageElement: () => document.getElementById("scrollable-page-override") || document.getElementById("scrollable-page") || document.documentElement
1016
+ });
1017
+ framework.onAction(ACTION_KINDS.FLOW, async (action) => {
1018
+ const flowAction = action;
1019
+ const url = flowAction.url;
1020
+ log.debug(`FlowAction \u2192 ${url}`);
1021
+ if (flowAction.presentationContext === "modal") {
1022
+ const match2 = framework.routeUrl(url);
1023
+ if (match2) {
1024
+ const page = await framework.dispatch(
1025
+ match2.intent
1026
+ );
1027
+ callbacks.onModal(page);
1028
+ }
1029
+ return;
1030
+ }
1031
+ const shouldReplace = isFirstPage;
1032
+ const match = framework.routeUrl(url);
1033
+ if (!match) {
1034
+ log.warn(`FlowAction: no route for ${url}`);
1035
+ return;
1036
+ }
1037
+ const pagePromise = framework.dispatch(
1038
+ match.intent
1039
+ );
1040
+ await Promise.race([
1041
+ pagePromise,
1042
+ new Promise((r) => setTimeout(r, 500))
1043
+ ]).catch(() => {
1044
+ });
1045
+ history.beforeTransition();
1046
+ updateApp({
1047
+ page: pagePromise.then((page) => {
1048
+ const canonicalURL = url;
1049
+ if (shouldReplace) {
1050
+ history.replaceState({ page }, canonicalURL);
1051
+ } else {
1052
+ history.pushState({ page }, canonicalURL);
1053
+ }
1054
+ callbacks.onNavigate(
1055
+ new URL(canonicalURL, window.location.origin).pathname
1056
+ );
1057
+ didEnterPage(page);
1058
+ return page;
1059
+ }),
1060
+ isFirstPage
1061
+ });
1062
+ isFirstPage = false;
1063
+ });
1064
+ history.onPopState(async (url, cachedState) => {
1065
+ log.debug(`popstate \u2192 ${url}, cached=${!!cachedState}`);
1066
+ callbacks.onNavigate(new URL(url).pathname);
1067
+ if (cachedState) {
1068
+ const { page } = cachedState;
1069
+ didEnterPage(page);
1070
+ updateApp({ page, isFirstPage });
1071
+ return;
1072
+ }
1073
+ const parsed = new URL(url);
1074
+ const routeMatch = framework.routeUrl(parsed.pathname + parsed.search);
1075
+ if (!routeMatch) {
1076
+ log.error(
1077
+ "received popstate without data, but URL was unroutable:",
1078
+ url
1079
+ );
1080
+ didEnterPage(null);
1081
+ updateApp({
1082
+ page: Promise.reject(new Error("404")),
1083
+ isFirstPage
1084
+ });
1085
+ return;
1086
+ }
1087
+ const pagePromise = framework.dispatch(
1088
+ routeMatch.intent
1089
+ );
1090
+ await Promise.race([
1091
+ pagePromise,
1092
+ new Promise((r) => setTimeout(r, 500))
1093
+ ]).catch(() => {
1094
+ });
1095
+ updateApp({
1096
+ page: pagePromise.then((page) => {
1097
+ didEnterPage(page);
1098
+ return page;
1099
+ }),
1100
+ isFirstPage
1101
+ });
1102
+ });
1103
+ function didEnterPage(page) {
1104
+ (async () => {
1105
+ try {
1106
+ if (page) {
1107
+ await framework.didEnterPage(page);
1108
+ }
1109
+ } catch (e) {
1110
+ log.error("didEnterPage error:", e);
1111
+ }
1112
+ })();
1113
+ }
1114
+ }
1115
+
1116
+ // ../browser/src/action-handlers/register.ts
1117
+ function registerActionHandlers(deps) {
1118
+ const { framework, log, callbacks, updateApp } = deps;
1119
+ registerFlowActionHandler({
1120
+ framework,
1121
+ log,
1122
+ callbacks,
1123
+ updateApp
1124
+ });
1125
+ registerExternalUrlHandler({ framework, log });
1126
+ }
1127
+
1128
+ // ../browser/src/server-data.ts
1129
+ var SERVER_DATA_ID = "serialized-server-data";
1130
+ function deserializeServerData() {
1131
+ const script = document.getElementById(SERVER_DATA_ID);
1132
+ if (!script?.textContent) return void 0;
1133
+ script.parentNode?.removeChild(script);
1134
+ try {
1135
+ return JSON.parse(script.textContent);
1136
+ } catch {
1137
+ return void 0;
1138
+ }
1139
+ }
1140
+ function createPrefetchedIntentsFromDom() {
1141
+ const data = deserializeServerData();
1142
+ if (!data || !Array.isArray(data)) {
1143
+ return PrefetchedIntents.empty();
1144
+ }
1145
+ return PrefetchedIntents.fromArray(data);
1146
+ }
1147
+
1148
+ // ../browser/src/start-app.ts
1149
+ async function startBrowserApp(config) {
1150
+ const { bootstrap, defaultLocale = "en", mount, callbacks } = config;
1151
+ const prefetchedIntents = createPrefetchedIntentsFromDom();
1152
+ const framework = Framework.create({ prefetchedIntents });
1153
+ bootstrap(framework);
1154
+ const loggerFactory = framework.container.resolve(
1155
+ DEP_KEYS.LOGGER_FACTORY
1156
+ );
1157
+ const log = loggerFactory.loggerFor("browser");
1158
+ const initialAction = framework.routeUrl(
1159
+ window.location.pathname + window.location.search
1160
+ );
1161
+ const locale = document.documentElement.lang || defaultLocale;
1162
+ const target = document.getElementById("app");
1163
+ const updateApp = mount(target, { framework, locale });
1164
+ registerActionHandlers({
1165
+ framework,
1166
+ log,
1167
+ callbacks,
1168
+ updateApp
1169
+ });
1170
+ if (initialAction) {
1171
+ await framework.perform(initialAction.action);
1172
+ } else {
1173
+ updateApp({
1174
+ page: Promise.reject(new Error("404")),
1175
+ isFirstPage: true
1176
+ });
1177
+ }
1178
+ }
1179
+
1180
+ // ../ssr/src/render.ts
1181
+ async function ssrRender(options) {
1182
+ const { url, frameworkConfig, bootstrap, getErrorPage, renderApp } = options;
1183
+ const framework = Framework.create(frameworkConfig);
1184
+ bootstrap(framework);
1185
+ const parsed = new URL(url, "http://localhost");
1186
+ const fullPath = parsed.pathname + parsed.search;
1187
+ const match = framework.routeUrl(fullPath);
1188
+ let page;
1189
+ let serverData = [];
1190
+ if (match) {
1191
+ try {
1192
+ page = await framework.dispatch(match.intent);
1193
+ serverData = [{ intent: match.intent, data: page }];
1194
+ } catch {
1195
+ page = getErrorPage(500, "Internal error");
1196
+ }
1197
+ } else {
1198
+ page = getErrorPage(404, "Page not found");
1199
+ }
1200
+ const result = renderApp(page, framework);
1201
+ framework.dispose();
1202
+ return {
1203
+ html: result.html,
1204
+ head: result.head,
1205
+ css: result.css,
1206
+ serverData
1207
+ };
1208
+ }
1209
+
1210
+ // ../ssr/src/create-render.ts
1211
+ function createSSRRender(config) {
1212
+ const { bootstrap, getErrorPage, renderApp, frameworkConfig } = config;
1213
+ return (url, locale) => ssrRender({
1214
+ url,
1215
+ frameworkConfig: frameworkConfig ?? {},
1216
+ bootstrap,
1217
+ getErrorPage,
1218
+ renderApp: (page) => renderApp(page, locale)
1219
+ });
1220
+ }
1221
+
1222
+ // ../ssr/src/inject.ts
1223
+ function injectSSRContent(options) {
1224
+ const { template, locale, head, css, html, serializedData } = options;
1225
+ const cssTag = css ? `<style>${css}</style>` : "";
1226
+ return template.replace("<!--ssr-lang-->", locale).replace("<!--ssr-head-->", `${head}
1227
+ ${cssTag}`).replace("<!--ssr-body-->", html).replace("<!--ssr-data-->", serializedData);
1228
+ }
1229
+
1230
+ // ../ssr/src/server-data.ts
1231
+ var HTML_REPLACEMENTS = {
1232
+ "<": "\\u003C",
1233
+ ">": "\\u003E",
1234
+ "/": "\\u002F",
1235
+ "\u2028": "\\u2028",
1236
+ "\u2029": "\\u2029"
1237
+ };
1238
+ var HTML_ESCAPE_PATTERN = /[<>/\u2028\u2029]/g;
1239
+ function serializeServerData(data) {
1240
+ const json = JSON.stringify(data);
1241
+ return json.replace(
1242
+ HTML_ESCAPE_PATTERN,
1243
+ (match) => HTML_REPLACEMENTS[match] ?? match
1244
+ );
1245
+ }
1246
+
1247
+ // ../server/src/app.ts
1248
+ var import_hono = require("hono");
1249
+
1250
+ // ../server/src/locale.ts
1251
+ var DEFAULT_SUPPORTED = ["zh", "en"];
1252
+ var DEFAULT_LOCALE = "en";
1253
+ function parseAcceptLanguage(header, supported = DEFAULT_SUPPORTED, fallback = DEFAULT_LOCALE) {
1254
+ if (!header) return fallback;
1255
+ const langs = header.split(",").map((part) => {
1256
+ const [lang, q] = part.trim().split(";q=");
1257
+ return {
1258
+ lang: lang.trim().toLowerCase(),
1259
+ q: q ? parseFloat(q) : 1
1260
+ };
1261
+ }).sort((a, b) => b.q - a.q);
1262
+ for (const { lang } of langs) {
1263
+ const prefix = lang.split("-")[0];
1264
+ if (supported.includes(prefix)) {
1265
+ return prefix;
1266
+ }
1267
+ }
1268
+ return fallback;
1269
+ }
1270
+
1271
+ // ../server/src/app.ts
1272
+ var import_meta = {};
1273
+ function createSSRApp(options) {
1274
+ const {
1275
+ root,
1276
+ vite,
1277
+ isProduction,
1278
+ ssrEntryPath = "/src/ssr.ts",
1279
+ ssrProductionModule,
1280
+ supportedLocales,
1281
+ defaultLocale
1282
+ } = options;
1283
+ const app = new import_hono.Hono();
1284
+ async function readTemplate(url) {
1285
+ if (!isProduction && vite) {
1286
+ const { readFileSync: readFileSync2 } = await import("fs");
1287
+ const { resolve: resolve2 } = await import("path");
1288
+ const raw = readFileSync2(resolve2(root, "index.html"), "utf-8");
1289
+ return vite.transformIndexHtml(url, raw);
1290
+ }
1291
+ const isDeno = typeof globalThis.Deno !== "undefined";
1292
+ if (isDeno) {
1293
+ return globalThis.Deno.readTextFileSync(
1294
+ new URL("../dist/client/index.html", import_meta.url)
1295
+ );
1296
+ }
1297
+ const { readFileSync } = await import("fs");
1298
+ const { resolve } = await import("path");
1299
+ return readFileSync(resolve(root, "dist/client/index.html"), "utf-8");
1300
+ }
1301
+ async function loadSSRModule() {
1302
+ if (!isProduction && vite) {
1303
+ return await vite.ssrLoadModule(ssrEntryPath);
1304
+ }
1305
+ const modulePath = ssrProductionModule ?? "../dist/server/ssr.js";
1306
+ return import(modulePath);
1307
+ }
1308
+ app.get("*", async (c) => {
1309
+ const url = c.req.path + (c.req.url.includes("?") ? "?" + c.req.url.split("?")[1] : "");
1310
+ try {
1311
+ const template = await readTemplate(url);
1312
+ const { render, serializeServerData: serializeServerData2 } = await loadSSRModule();
1313
+ const locale = parseAcceptLanguage(
1314
+ c.req.header("accept-language"),
1315
+ supportedLocales,
1316
+ defaultLocale
1317
+ );
1318
+ const {
1319
+ html: appHtml,
1320
+ head,
1321
+ css,
1322
+ serverData
1323
+ } = await render(url, locale);
1324
+ const serializedData = serializeServerData2(serverData);
1325
+ const finalHtml = injectSSRContent({
1326
+ template,
1327
+ locale,
1328
+ head,
1329
+ css,
1330
+ html: appHtml,
1331
+ serializedData
1332
+ });
1333
+ return c.html(finalHtml);
1334
+ } catch (e) {
1335
+ if (!isProduction && vite) {
1336
+ vite.ssrFixStacktrace(e);
1337
+ }
1338
+ console.error("[SSR Error]", e);
1339
+ return c.text("Internal Server Error", 500);
1340
+ }
1341
+ });
1342
+ return app;
1343
+ }
1344
+
1345
+ // ../server/src/create-server.ts
1346
+ var import_hono3 = require("hono");
1347
+
1348
+ // ../server/src/runtime.ts
1349
+ function detectRuntime() {
1350
+ return {
1351
+ isDeno: typeof globalThis.Deno !== "undefined",
1352
+ isBun: typeof globalThis.Bun !== "undefined",
1353
+ isVercel: !!process.env.VERCEL,
1354
+ isProduction: process.env.NODE_ENV === "production"
1355
+ };
1356
+ }
1357
+ async function resolveRoot(importMetaUrl, levelsUp = 0) {
1358
+ const isDeno = typeof globalThis.Deno !== "undefined";
1359
+ if (isDeno) {
1360
+ let url = new URL(importMetaUrl);
1361
+ for (let i = 0; i < levelsUp; i++) {
1362
+ url = new URL("..", url);
1363
+ }
1364
+ return url.pathname;
1365
+ }
1366
+ const { dirname, resolve, normalize } = await import("path");
1367
+ const { fileURLToPath } = await import("url");
1368
+ let dir = normalize(dirname(fileURLToPath(importMetaUrl)));
1369
+ for (let i = 0; i < levelsUp; i++) {
1370
+ dir = resolve(dir, "..");
1371
+ }
1372
+ return dir;
1373
+ }
1374
+
1375
+ // ../server/src/start.ts
1376
+ var import_hono2 = require("hono");
1377
+ async function startServer(options) {
1378
+ const { app, root, port = 3e3, isProduction, vite } = options;
1379
+ const { isDeno, isBun, isVercel } = options.runtime ?? detectRuntime();
1380
+ if (isVercel) {
1381
+ return { vite };
1382
+ }
1383
+ if (!isProduction) {
1384
+ let devVite = vite;
1385
+ if (!devVite) {
1386
+ const { createServer: createViteServer } = await import("vite");
1387
+ devVite = await createViteServer({
1388
+ root,
1389
+ server: { middlewareMode: true },
1390
+ appType: "custom"
1391
+ });
1392
+ }
1393
+ const { getRequestListener } = await import("@hono/node-server");
1394
+ const { createServer: createServer2 } = await import("http");
1395
+ const listener = getRequestListener(app.fetch);
1396
+ const server = createServer2((req, res) => {
1397
+ devVite.middlewares(req, res, () => listener(req, res));
1398
+ });
1399
+ server.listen(port, () => {
1400
+ console.log(`
1401
+ Server running at http://localhost:${port}
1402
+ `);
1403
+ });
1404
+ return { vite: devVite };
1405
+ }
1406
+ if (isDeno) {
1407
+ globalThis.Deno.serve({ port }, app.fetch);
1408
+ } else if (isBun) {
1409
+ } else {
1410
+ const { serveStatic } = await import("@hono/node-server/serve-static");
1411
+ const { resolve } = await import("path");
1412
+ const prodApp = new import_hono2.Hono();
1413
+ prodApp.use("/*", serveStatic({ root: resolve(root, "dist/client") }));
1414
+ prodApp.route("/", app);
1415
+ const { serve } = await import("@hono/node-server");
1416
+ serve({ fetch: prodApp.fetch, port }, () => {
1417
+ console.log(`
1418
+ Server running at http://localhost:${port}
1419
+ `);
1420
+ });
1421
+ }
1422
+ return { vite };
1423
+ }
1424
+
1425
+ // ../server/src/create-server.ts
1426
+ async function createServer(config = {}) {
1427
+ const {
1428
+ root: rootOverride,
1429
+ locales,
1430
+ defaultLocale,
1431
+ port = Number(process.env.PORT) || 3e3,
1432
+ setup,
1433
+ ssr
1434
+ } = config;
1435
+ const root = rootOverride ?? process.cwd();
1436
+ const { existsSync } = await import("fs");
1437
+ const { resolve } = await import("path");
1438
+ const envPath = resolve(root, ".env");
1439
+ if (existsSync(envPath)) {
1440
+ try {
1441
+ const { config: dotenvConfig } = await import("dotenv");
1442
+ dotenvConfig({ path: envPath });
1443
+ } catch {
1444
+ }
1445
+ }
1446
+ const runtime = detectRuntime();
1447
+ let vite;
1448
+ if (!runtime.isProduction && !runtime.isVercel) {
1449
+ const { createServer: createViteServer } = await import("vite");
1450
+ vite = await createViteServer({
1451
+ root,
1452
+ server: { middlewareMode: true },
1453
+ appType: "custom"
1454
+ });
1455
+ }
1456
+ const app = new import_hono3.Hono();
1457
+ if (setup) {
1458
+ await setup(app);
1459
+ }
1460
+ const ssrApp = createSSRApp({
1461
+ root,
1462
+ vite,
1463
+ isProduction: runtime.isProduction,
1464
+ supportedLocales: locales,
1465
+ defaultLocale,
1466
+ ...ssr
1467
+ });
1468
+ app.route("/", ssrApp);
1469
+ await startServer({
1470
+ app,
1471
+ root,
1472
+ port,
1473
+ isProduction: runtime.isProduction,
1474
+ vite,
1475
+ runtime
1476
+ });
1477
+ return { app, vite, runtime };
1478
+ }
42
1479
  // Annotate the CommonJS export names for ESM import in node:
43
1480
  0 && (module.exports = {
1481
+ ACTION_KINDS,
1482
+ ActionDispatcher,
1483
+ BaseController,
1484
+ BaseLogger,
1485
+ CompositeLogger,
1486
+ CompositeLoggerFactory,
1487
+ ConsoleLogger,
1488
+ ConsoleLoggerFactory,
1489
+ Container,
1490
+ DEP_KEYS,
1491
+ Framework,
44
1492
  History,
1493
+ HttpClient,
1494
+ HttpError,
1495
+ IntentDispatcher,
1496
+ LruMap,
1497
+ PrefetchedIntents,
1498
+ Router,
1499
+ buildUrl,
45
1500
  createPrefetchedIntentsFromDom,
1501
+ createSSRApp,
46
1502
  createSSRRender,
1503
+ createServer,
1504
+ defineRoutes,
47
1505
  deserializeServerData,
1506
+ detectRuntime,
1507
+ generateUuid,
1508
+ getBaseUrl,
48
1509
  injectSSRContent,
1510
+ isCompoundAction,
1511
+ isExternalUrlAction,
1512
+ isFlowAction,
1513
+ isNone,
1514
+ isSome,
1515
+ makeDependencies,
1516
+ makeExternalUrlAction,
1517
+ makeFlowAction,
1518
+ mapEach,
1519
+ parseAcceptLanguage,
1520
+ pipe,
1521
+ pipeAsync,
49
1522
  registerActionHandlers,
50
1523
  registerExternalUrlHandler,
51
1524
  registerFlowActionHandler,
1525
+ removeHost,
1526
+ removeQueryParams,
1527
+ removeScheme,
1528
+ resetFilterCache,
1529
+ resolveRoot,
52
1530
  serializeServerData,
1531
+ shouldLog,
53
1532
  ssrRender,
1533
+ stableStringify,
54
1534
  startBrowserApp,
55
- tryScroll,
56
- ...require("@finesoft/core"),
57
- ...require("@finesoft/server")
1535
+ startServer,
1536
+ tryScroll
58
1537
  });
59
1538
  //# sourceMappingURL=index.cjs.map