@finesoft/front 0.1.77 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- import { $ as RenderMode, $t as Storage, A as IntersectionImpressionObserver, An as isCompoundAction, At as RouteAddOptions, B as buildUrl, Bt as RedirectResult, C as PluralRuleProvider, Cn as ActionDispatcher, Ct as Translator, D as WithFieldsRecorder, Dn as CompoundAction, Dt as ConsoleLoggerFactory, E as resolvePluralKey, En as Action, Et as ConsoleLogger, F as BrowserContextOptions, Ft as DenyResult, G as PWADisplayMode, Gt as rewrite, H as removeHost, Ht as deny, I as ServerContextOptions, It as MiddlewareResult, J as Optional, Jt as FeatureFlags, K as getPWADisplayMode, Kt as BasePage, L as createBrowserContext, Lt as NavigationContext, M as CompositeEventRecorder, Mn as isFlowAction, Mt as Router, N as runAfterLoadGuards, Nn as makeExternalUrlAction, Nt as AfterLoadGuard, O as VoidEventRecorder, On as ExternalUrlAction, Ot as CompositeLogger, P as runBeforeLoadGuards, Pn as makeFlowAction, Pt as BeforeLoadGuard, Q as DefineRoutesOptions, Qt as Net, R as createServerContext, Rt as NextResult, S as PluralCategory, Sn as Container, St as TextDirection, T as interpolate, Tn as ACTION_KINDS, Tt as shouldLog, U as removeQueryParams, Ut as next, V as getBaseUrl, Vt as RewriteResult, W as removeScheme, Wt as redirect, X as isSome, Xt as MakeDependenciesOptions, Y as isNone, Yt as FeatureFlagsProvider, Z as LruMap, Zt as MetricsRecorder, _ as getTextDirection, _n as Logger, _t as FrameworkConfig, a as BrowserAppConfig, an as resolveMessages, at as pipe, b as resolveLocaleFromUrl, bn as Intent, bt as LocaleAttributes, c as registerActionHandlers, cn as EventRecorder, ct as HttpClient, d as registerFlowActionHandler, dn as MetricsFieldsProvider, dt as RequestInterceptor, en as makeDependencies, et as RouteDefinition, f as ExternalUrlDependencies, fn as ReportCallback, ft as ResponseInterceptor, g as getLocaleAttributes, gn as BaseLogger, gt as Framework, h as SimpleTranslatorOptions, hn as ReportingLoggerOptions, ht as BaseShelf, i as History, in as resolveConfiguredMessages, it as mapEach, j as ConsoleEventRecorder, jn as isExternalUrlAction, jt as RouteMatch, k as ImpressionObserverOptions, kn as FlowAction, kt as CompositeLoggerFactory, l as FlowActionCallbacks, ln as ImpressionEntry, lt as HttpClientConfig, m as SimpleTranslator, mn as ReportingLoggerFactory, mt as BaseItem, n as deserializeServerData, nn as MessagesLoaderContext, nt as AsyncMapper, o as startBrowserApp, on as PlatformInfo, ot as pipeAsync, p as registerExternalUrlHandler, pn as ReportingLogger, pt as stableStringify, q as None, qt as DEP_KEYS, r as tryScroll, rn as TranslationMessages, rt as Mapper, s as ActionHandlerDependencies, sn as detectPlatform, st as BaseController, t as createPrefetchedIntentsFromDom, tn as MessagesLoader, tt as defineRoutes, u as FlowActionDependencies, un as ImpressionObserver, ut as HttpError, v as isRtl, vn as LoggerFactory, vt as PrefetchedIntent, w as englishPlural, wn as ActionHandler, wt as resetFilterCache, x as setHtmlLocaleAttributes, xn as IntentController, xt as LocaleInfo, y as makeLocaleInfo, yn as IntentDispatcher, yt as PrefetchedIntents, z as generateUuid, zt as PostLoadContext } from "./server-data-DGbiKzMS.mjs";
2
- export { ACTION_KINDS, Action, ActionDispatcher, ActionHandler, type ActionHandlerDependencies, AfterLoadGuard, AsyncMapper, BaseController, BaseItem, BaseLogger, BasePage, BaseShelf, BeforeLoadGuard, type BrowserAppConfig, BrowserContextOptions, CompositeEventRecorder, CompositeLogger, CompositeLoggerFactory, CompoundAction, ConsoleEventRecorder, ConsoleLogger, ConsoleLoggerFactory, Container, DEP_KEYS, DefineRoutesOptions, DenyResult, EventRecorder, ExternalUrlAction, type ExternalUrlDependencies, FeatureFlags, FeatureFlagsProvider, FlowAction, type FlowActionCallbacks, type FlowActionDependencies, Framework, FrameworkConfig, History, HttpClient, HttpClientConfig, HttpError, ImpressionEntry, ImpressionObserver, ImpressionObserverOptions, Intent, IntentController, IntentDispatcher, IntersectionImpressionObserver, LocaleAttributes, LocaleInfo, Logger, Logger as LoggerInterface, LoggerFactory, LruMap, MakeDependenciesOptions, Mapper, MessagesLoader, MessagesLoaderContext, MetricsFieldsProvider, MetricsRecorder, MiddlewareResult, NavigationContext, Net, NextResult, None, Optional, PWADisplayMode, PlatformInfo, PluralCategory, PluralRuleProvider, PostLoadContext, PrefetchedIntent, PrefetchedIntents, RedirectResult, RenderMode, ReportCallback, ReportingLogger, ReportingLoggerFactory, ReportingLoggerOptions, RequestInterceptor, ResponseInterceptor, RewriteResult, RouteAddOptions, RouteDefinition, RouteMatch, Router, ServerContextOptions, SimpleTranslator, SimpleTranslatorOptions, Storage, TextDirection, TranslationMessages, Translator, VoidEventRecorder, WithFieldsRecorder, buildUrl, createBrowserContext, createPrefetchedIntentsFromDom, createServerContext, defineRoutes, deny, deserializeServerData, detectPlatform, englishPlural, generateUuid, getBaseUrl, getLocaleAttributes, getPWADisplayMode, getTextDirection, interpolate, isCompoundAction, isExternalUrlAction, isFlowAction, isNone, isRtl, isSome, makeDependencies, makeExternalUrlAction, makeFlowAction, makeLocaleInfo, mapEach, next, pipe, pipeAsync, redirect, registerActionHandlers, registerExternalUrlHandler, registerFlowActionHandler, removeHost, removeQueryParams, removeScheme, resetFilterCache, resolveConfiguredMessages, resolveLocaleFromUrl, resolveMessages, resolvePluralKey, rewrite, runAfterLoadGuards, runBeforeLoadGuards, setHtmlLocaleAttributes, shouldLog, stableStringify, startBrowserApp, tryScroll };
1
+ import { $ as RenderMode, $n as BaseLogger, $t as RouteAddOptions, A as IntersectionImpressionObserver, An as DEP_KEYS, At as Translator, B as buildUrl, Bn as TranslationMessages, Bt as ParamsFor, C as PluralRuleProvider, Cn as BasePage, Ct as Framework, D as WithFieldsRecorder, Dn as markPublic, Dt as LocaleAttributes, E as resolvePluralKey, En as isPublicMarked, Et as PrefetchedIntents, F as BrowserContextOptions, Fn as Net, Ft as CompositeLogger, G as PWADisplayMode, Gn as EventRecorder, Gt as NumOptions, H as removeHost, Hn as resolveMessages, Ht as StripOptional, I as ServerContextOptions, In as Storage, It as CompositeLoggerFactory, J as Optional, Jn as MetricsFieldsProvider, Jt as int, K as getPWADisplayMode, Kn as ImpressionEntry, Kt as StrOptions, L as createBrowserContext, Ln as makeDependencies, Lt as ExtractParamNames, M as CompositeEventRecorder, Mn as FeatureFlagsProvider, Mt as shouldLog, N as runAfterLoadGuards, Nn as MakeDependenciesOptions, Nt as ConsoleLogger, O as VoidEventRecorder, On as RequestScopedKey, Ot as LocaleInfo, P as runBeforeLoadGuards, Pn as MetricsRecorder, Pt as ConsoleLoggerFactory, Q as DefineRoutesOptions, Qn as ReportingLoggerOptions, Qt as uuid, R as createServerContext, Rn as MessagesLoader, Rt as InferParams, S as PluralCategory, Sn as BASE_PAGE_FIELDS, St as safeErrorPage, T as interpolate, Tn as getPublicFields, Tt as PrefetchedIntent, U as removeQueryParams, Un as PlatformInfo, Ut as optional, V as getBaseUrl, Vn as resolveConfiguredMessages, Vt as QuerySchemaMap, W as removeScheme, Wn as detectPlatform, Wt as withDefault, X as isSome, Xn as ReportingLogger, Xt as oneOf, Y as isNone, Yn as ReportCallback, Yt as num, Z as LruMap, Zn as ReportingLoggerFactory, Zt as str, _ as getTextDirection, _n as RewriteResult, _r as isFlowAction, _t as ResponseInterceptor, a as BrowserAppConfig, an as StandardResult, ar as Intent, at as mapEach, b as resolveLocaleFromUrl, bn as redirect, bt as BaseShelf, c as registerActionHandlers, cn as runStandard, cr as ActionDispatcher, ct as BaseController, d as registerFlowActionHandler, dn as DenyResult, dr as Action, dt as classifyUrl, en as RouteMatch, er as Logger, et as RouteDefinition, f as ExternalUrlDependencies, fn as MiddlewareResult, fr as CompoundAction, ft as HostGuardError, g as getLocaleAttributes, gn as RedirectResult, gr as isExternalUrlAction, gt as RequestInterceptor, h as SimpleTranslatorOptions, hn as PostLoadContext, hr as isCompoundAction, ht as HttpError, i as History, in as StandardIssue, ir as IntentDispatcher, it as Mapper, j as ConsoleEventRecorder, jn as FeatureFlags, jt as resetFilterCache, k as ImpressionObserverOptions, kn as defineRequestScopedKey, kt as TextDirection, l as FlowActionCallbacks, ln as AfterLoadGuard, lr as ActionHandler, lt as HostCheckResult, m as SimpleTranslator, mn as NextResult, mr as FlowAction, mt as HttpClientConfig, n as deserializeServerData, nn as InferOutput, nr as SecureFetchOptions, nt as route, o as startBrowserApp, on as StandardSchemaV1, or as IntentController, ot as pipe, p as registerExternalUrlHandler, pn as NavigationContext, pr as ExternalUrlAction, pt as HttpClient, q as None, qn as ImpressionObserver, qt as bool, r as tryScroll, rn as ParamSchema, rr as secureFetch, rt as AsyncMapper, s as ActionHandlerDependencies, sn as makeSchema, sr as Container, st as pipeAsync, t as createPrefetchedIntentsFromDom, tn as Router, tr as LoggerFactory, tt as defineRoutes, u as FlowActionDependencies, un as BeforeLoadGuard, ur as ACTION_KINDS, ut as classifyHost, v as isRtl, vn as deny, vr as makeExternalUrlAction, vt as stableStringify, w as englishPlural, wn as FINESOFT_PUBLIC, wt as FrameworkConfig, x as setHtmlLocaleAttributes, xn as rewrite, xt as SafeErrorPageOptions, y as makeLocaleInfo, yn as next, yr as makeFlowAction, yt as BaseItem, z as generateUuid, zn as MessagesLoaderContext, zt as InferQuery } from "./server-data-DQzknR97.mjs";
2
+ export { ACTION_KINDS, Action, ActionDispatcher, ActionHandler, type ActionHandlerDependencies, AfterLoadGuard, AsyncMapper, BASE_PAGE_FIELDS, BaseController, BaseItem, BaseLogger, BasePage, BaseShelf, BeforeLoadGuard, type BrowserAppConfig, BrowserContextOptions, CompositeEventRecorder, CompositeLogger, CompositeLoggerFactory, CompoundAction, ConsoleEventRecorder, ConsoleLogger, ConsoleLoggerFactory, Container, DEP_KEYS, DefineRoutesOptions, DenyResult, EventRecorder, ExternalUrlAction, type ExternalUrlDependencies, ExtractParamNames, FINESOFT_PUBLIC, FeatureFlags, FeatureFlagsProvider, FlowAction, type FlowActionCallbacks, type FlowActionDependencies, Framework, FrameworkConfig, History, HostCheckResult, HostGuardError, HttpClient, HttpClientConfig, HttpError, ImpressionEntry, ImpressionObserver, ImpressionObserverOptions, InferOutput, InferParams, InferQuery, Intent, IntentController, IntentDispatcher, IntersectionImpressionObserver, LocaleAttributes, LocaleInfo, Logger, Logger as LoggerInterface, LoggerFactory, LruMap, MakeDependenciesOptions, Mapper, MessagesLoader, MessagesLoaderContext, MetricsFieldsProvider, MetricsRecorder, MiddlewareResult, NavigationContext, Net, NextResult, None, NumOptions, Optional, PWADisplayMode, ParamSchema, ParamsFor, PlatformInfo, PluralCategory, PluralRuleProvider, PostLoadContext, PrefetchedIntent, PrefetchedIntents, QuerySchemaMap, RedirectResult, RenderMode, ReportCallback, ReportingLogger, ReportingLoggerFactory, ReportingLoggerOptions, RequestInterceptor, RequestScopedKey, ResponseInterceptor, RewriteResult, RouteAddOptions, RouteDefinition, RouteMatch, Router, SafeErrorPageOptions, SecureFetchOptions, ServerContextOptions, SimpleTranslator, SimpleTranslatorOptions, StandardIssue, StandardResult, StandardSchemaV1, Storage, StrOptions, StripOptional, TextDirection, TranslationMessages, Translator, VoidEventRecorder, WithFieldsRecorder, bool, buildUrl, classifyHost, classifyUrl, createBrowserContext, createPrefetchedIntentsFromDom, createServerContext, defineRequestScopedKey, defineRoutes, deny, deserializeServerData, detectPlatform, englishPlural, generateUuid, getBaseUrl, getLocaleAttributes, getPWADisplayMode, getPublicFields, getTextDirection, int, interpolate, isCompoundAction, isExternalUrlAction, isFlowAction, isNone, isPublicMarked, isRtl, isSome, makeDependencies, makeExternalUrlAction, makeFlowAction, makeLocaleInfo, makeSchema, mapEach, markPublic, next, num, oneOf, optional, pipe, pipeAsync, redirect, registerActionHandlers, registerExternalUrlHandler, registerFlowActionHandler, removeHost, removeQueryParams, removeScheme, resetFilterCache, resolveConfiguredMessages, resolveLocaleFromUrl, resolveMessages, resolvePluralKey, rewrite, route, runAfterLoadGuards, runBeforeLoadGuards, runStandard, safeErrorPage, secureFetch, setHtmlLocaleAttributes, shouldLog, stableStringify, startBrowserApp, str, tryScroll, uuid, withDefault };
package/dist/browser.mjs CHANGED
@@ -1 +1 @@
1
- import{$ as e,A as t,B as n,C as r,D as i,E as a,F as o,G as s,H as c,I as l,J as u,K as d,L as f,M as p,N as m,O as h,P as g,Q as _,R as v,S as y,T as b,U as x,V as S,W as C,X as w,Y as T,Z as E,_ as D,_t as O,a as k,at as A,b as j,bt as M,c as N,ct as P,d as F,dt as I,et as L,f as R,ft as z,g as B,gt as V,h as H,ht as U,i as W,it as G,j as K,k as q,l as J,lt as Y,m as X,mt as Z,n as Q,nt as $,o as ee,ot as te,p as ne,pt as re,q as ie,r as ae,rt as oe,s as se,st as ce,t as le,tt as ue,u as de,ut as fe,v as pe,vt as me,w as he,x as ge,xt as _e,y as ve,yt as ye,z as be}from"./start-app-BdXBCcor.mjs";export{V as ACTION_KINDS,U as ActionDispatcher,m as BaseController,_ as BaseLogger,R as CompositeEventRecorder,e as CompositeLogger,L as CompositeLoggerFactory,s as ConsoleEventRecorder,u as ConsoleLogger,T as ConsoleLoggerFactory,re as Container,c as DEP_KEYS,l as Framework,ee as History,g as HttpClient,o as HttpError,Z as IntentDispatcher,F as IntersectionImpressionObserver,h as LruMap,f as PrefetchedIntents,d as ReportingLogger,ie as ReportingLoggerFactory,S as Router,ue as SimpleTranslator,de as VoidEventRecorder,J as WithFieldsRecorder,j as buildUrl,D as createBrowserContext,Q as createPrefetchedIntentsFromDom,pe as createServerContext,q as defineRoutes,ne as deny,ae as deserializeServerData,C as detectPlatform,$ as englishPlural,ve as generateUuid,ge as getBaseUrl,ce as getLocaleAttributes,b as getPWADisplayMode,P as getTextDirection,oe as interpolate,O as isCompoundAction,me as isExternalUrlAction,ye as isFlowAction,a as isNone,Y as isRtl,i as isSome,x as makeDependencies,M as makeExternalUrlAction,_e as makeFlowAction,fe as makeLocaleInfo,t as mapEach,X as next,K as pipe,p as pipeAsync,H as redirect,W as registerActionHandlers,N as registerExternalUrlHandler,k as registerFlowActionHandler,y as removeHost,r as removeQueryParams,he as removeScheme,w as resetFilterCache,A as resolveConfiguredMessages,I as resolveLocaleFromUrl,te as resolveMessages,G as resolvePluralKey,B as rewrite,be as runAfterLoadGuards,n as runBeforeLoadGuards,z as setHtmlLocaleAttributes,E as shouldLog,v as stableStringify,le as startBrowserApp,se as tryScroll};
1
+ import{$ as e,A as t,At as n,B as r,Bt as i,C as a,Ct as o,D as s,Dt as c,E as l,Et as u,F as d,Ft as f,G as p,H as m,Ht as h,I as g,It as _,J as v,K as y,L as b,Lt as x,M as S,Mt as C,N as w,Nt as T,O as E,Ot as D,P as O,Pt as k,Q as A,R as j,Rt as M,S as N,St as P,T as F,Tt as I,U as L,Ut as R,V as z,Vt as B,W as V,X as H,Y as U,Z as W,_ as G,_t as K,a as q,at as J,b as Y,bt as X,c as Z,ct as Q,d as $,dt as ee,et as te,f as ne,ft as re,g as ie,gt as ae,h as oe,ht as se,i as ce,it as le,j as ue,jt as de,k as fe,kt as pe,l as me,lt as he,m as ge,mt as _e,n as ve,nt as ye,o as be,ot as xe,p as Se,pt as Ce,q as we,r as Te,rt as Ee,s as De,st as Oe,t as ke,tt as Ae,u as je,ut as Me,v as Ne,vt as Pe,w as Fe,wt as Ie,x as Le,xt as Re,y as ze,yt as Be,z as Ve,zt as He}from"./start-app-S4DH-40i.mjs";export{M as ACTION_KINDS,x as ActionDispatcher,g as BASE_PAGE_FIELDS,O as BaseController,_e as BaseLogger,ne as CompositeEventRecorder,se as CompositeLogger,ae as CompositeLoggerFactory,Oe as ConsoleEventRecorder,Me as ConsoleLogger,ee as ConsoleLoggerFactory,f as Container,le as DEP_KEYS,b as FINESOFT_PUBLIC,z as Framework,be as History,n as HostGuardError,de as HttpClient,C as HttpError,_ as IntentDispatcher,$ as IntersectionImpressionObserver,E as LruMap,m as PrefetchedIntents,Q as ReportingLogger,he as ReportingLoggerFactory,te as Router,K as SimpleTranslator,je as VoidEventRecorder,me as WithFieldsRecorder,v as bool,Y as buildUrl,T as classifyHost,k as classifyUrl,G as createBrowserContext,ve as createPrefetchedIntentsFromDom,Ne as createServerContext,Ee as defineRequestScopedKey,fe as defineRoutes,Se as deny,Te as deserializeServerData,xe as detectPlatform,Pe as englishPlural,ze as generateUuid,Le as getBaseUrl,o as getLocaleAttributes,F as getPWADisplayMode,j as getPublicFields,Ie as getTextDirection,U as int,Be as interpolate,He as isCompoundAction,i as isExternalUrlAction,B as isFlowAction,l as isNone,Ve as isPublicMarked,I as isRtl,s as isSome,J as makeDependencies,h as makeExternalUrlAction,R as makeFlowAction,u as makeLocaleInfo,Ae as makeSchema,ue as mapEach,r as markPublic,ge as next,H as num,W as oneOf,y as optional,S as pipe,w as pipeAsync,oe as redirect,ce as registerActionHandlers,Z as registerExternalUrlHandler,q as registerFlowActionHandler,N as removeHost,a as removeQueryParams,Fe as removeScheme,re as resetFilterCache,Re as resolveConfiguredMessages,c as resolveLocaleFromUrl,P as resolveMessages,X as resolvePluralKey,ie as rewrite,t as route,V as runAfterLoadGuards,p as runBeforeLoadGuards,ye as runStandard,d as safeErrorPage,pe as secureFetch,D as setHtmlLocaleAttributes,Ce as shouldLog,L as stableStringify,ke as startBrowserApp,A as str,De as tryScroll,e as uuid,we as withDefault};
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as RenderMode, $t as Storage, A as IntersectionImpressionObserver, An as isCompoundAction, At as RouteAddOptions, B as buildUrl, Bt as RedirectResult, C as PluralRuleProvider, Cn as ActionDispatcher, Ct as Translator, D as WithFieldsRecorder, Dn as CompoundAction, Dt as ConsoleLoggerFactory, E as resolvePluralKey, En as Action, Et as ConsoleLogger, F as BrowserContextOptions, Ft as DenyResult, G as PWADisplayMode, Gt as rewrite, H as removeHost, Ht as deny, I as ServerContextOptions, It as MiddlewareResult, J as Optional, Jt as FeatureFlags, K as getPWADisplayMode, Kt as BasePage, L as createBrowserContext, Lt as NavigationContext, M as CompositeEventRecorder, Mn as isFlowAction, Mt as Router, N as runAfterLoadGuards, Nn as makeExternalUrlAction, Nt as AfterLoadGuard, O as VoidEventRecorder, On as ExternalUrlAction, Ot as CompositeLogger, P as runBeforeLoadGuards, Pn as makeFlowAction, Pt as BeforeLoadGuard, Q as DefineRoutesOptions, Qt as Net, R as createServerContext, Rt as NextResult, S as PluralCategory, Sn as Container, St as TextDirection, T as interpolate, Tn as ACTION_KINDS, Tt as shouldLog, U as removeQueryParams, Ut as next, V as getBaseUrl, Vt as RewriteResult, W as removeScheme, Wt as redirect, X as isSome, Xt as MakeDependenciesOptions, Y as isNone, Yt as FeatureFlagsProvider, Z as LruMap, Zt as MetricsRecorder, _ as getTextDirection, _n as Logger, _t as FrameworkConfig, a as BrowserAppConfig, an as resolveMessages, at as pipe, b as resolveLocaleFromUrl, bn as Intent, bt as LocaleAttributes, c as registerActionHandlers, cn as EventRecorder, ct as HttpClient, d as registerFlowActionHandler, dn as MetricsFieldsProvider, dt as RequestInterceptor, en as makeDependencies, et as RouteDefinition, f as ExternalUrlDependencies, fn as ReportCallback, ft as ResponseInterceptor, g as getLocaleAttributes, gn as BaseLogger, gt as Framework, h as SimpleTranslatorOptions, hn as ReportingLoggerOptions, ht as BaseShelf, i as History, in as resolveConfiguredMessages, it as mapEach, j as ConsoleEventRecorder, jn as isExternalUrlAction, jt as RouteMatch, k as ImpressionObserverOptions, kn as FlowAction, kt as CompositeLoggerFactory, l as FlowActionCallbacks, ln as ImpressionEntry, lt as HttpClientConfig, m as SimpleTranslator, mn as ReportingLoggerFactory, mt as BaseItem, n as deserializeServerData, nn as MessagesLoaderContext, nt as AsyncMapper, o as startBrowserApp, on as PlatformInfo, ot as pipeAsync, p as registerExternalUrlHandler, pn as ReportingLogger, pt as stableStringify, q as None, qt as DEP_KEYS, r as tryScroll, rn as TranslationMessages, rt as Mapper, s as ActionHandlerDependencies, sn as detectPlatform, st as BaseController, t as createPrefetchedIntentsFromDom, tn as MessagesLoader, tt as defineRoutes, u as FlowActionDependencies, un as ImpressionObserver, ut as HttpError, v as isRtl, vn as LoggerFactory, vt as PrefetchedIntent, w as englishPlural, wn as ActionHandler, wt as resetFilterCache, x as setHtmlLocaleAttributes, xn as IntentController, xt as LocaleInfo, y as makeLocaleInfo, yn as IntentDispatcher, yt as PrefetchedIntents, z as generateUuid, zt as PostLoadContext } from "./server-data-DGbiKzMS.mjs";
1
+ import { $ as RenderMode, $n as BaseLogger, $t as RouteAddOptions, A as IntersectionImpressionObserver, An as DEP_KEYS, At as Translator, B as buildUrl, Bn as TranslationMessages, Bt as ParamsFor, C as PluralRuleProvider, Cn as BasePage, Ct as Framework, D as WithFieldsRecorder, Dn as markPublic, Dt as LocaleAttributes, E as resolvePluralKey, En as isPublicMarked, Et as PrefetchedIntents, F as BrowserContextOptions, Fn as Net, Ft as CompositeLogger, G as PWADisplayMode, Gn as EventRecorder, Gt as NumOptions, H as removeHost, Hn as resolveMessages, Ht as StripOptional, I as ServerContextOptions, In as Storage, It as CompositeLoggerFactory, J as Optional, Jn as MetricsFieldsProvider, Jt as int, K as getPWADisplayMode, Kn as ImpressionEntry, Kt as StrOptions, L as createBrowserContext, Ln as makeDependencies, Lt as ExtractParamNames, M as CompositeEventRecorder, Mn as FeatureFlagsProvider, Mt as shouldLog, N as runAfterLoadGuards, Nn as MakeDependenciesOptions, Nt as ConsoleLogger, O as VoidEventRecorder, On as RequestScopedKey, Ot as LocaleInfo, P as runBeforeLoadGuards, Pn as MetricsRecorder, Pt as ConsoleLoggerFactory, Q as DefineRoutesOptions, Qn as ReportingLoggerOptions, Qt as uuid, R as createServerContext, Rn as MessagesLoader, Rt as InferParams, S as PluralCategory, Sn as BASE_PAGE_FIELDS, St as safeErrorPage, T as interpolate, Tn as getPublicFields, Tt as PrefetchedIntent, U as removeQueryParams, Un as PlatformInfo, Ut as optional, V as getBaseUrl, Vn as resolveConfiguredMessages, Vt as QuerySchemaMap, W as removeScheme, Wn as detectPlatform, Wt as withDefault, X as isSome, Xn as ReportingLogger, Xt as oneOf, Y as isNone, Yn as ReportCallback, Yt as num, Z as LruMap, Zn as ReportingLoggerFactory, Zt as str, _ as getTextDirection, _n as RewriteResult, _r as isFlowAction, _t as ResponseInterceptor, a as BrowserAppConfig, an as StandardResult, ar as Intent, at as mapEach, b as resolveLocaleFromUrl, bn as redirect, bt as BaseShelf, c as registerActionHandlers, cn as runStandard, cr as ActionDispatcher, ct as BaseController, d as registerFlowActionHandler, dn as DenyResult, dr as Action, dt as classifyUrl, en as RouteMatch, er as Logger, et as RouteDefinition, f as ExternalUrlDependencies, fn as MiddlewareResult, fr as CompoundAction, ft as HostGuardError, g as getLocaleAttributes, gn as RedirectResult, gr as isExternalUrlAction, gt as RequestInterceptor, h as SimpleTranslatorOptions, hn as PostLoadContext, hr as isCompoundAction, ht as HttpError, i as History, in as StandardIssue, ir as IntentDispatcher, it as Mapper, j as ConsoleEventRecorder, jn as FeatureFlags, jt as resetFilterCache, k as ImpressionObserverOptions, kn as defineRequestScopedKey, kt as TextDirection, l as FlowActionCallbacks, ln as AfterLoadGuard, lr as ActionHandler, lt as HostCheckResult, m as SimpleTranslator, mn as NextResult, mr as FlowAction, mt as HttpClientConfig, n as deserializeServerData, nn as InferOutput, nr as SecureFetchOptions, nt as route, o as startBrowserApp, on as StandardSchemaV1, or as IntentController, ot as pipe, p as registerExternalUrlHandler, pn as NavigationContext, pr as ExternalUrlAction, pt as HttpClient, q as None, qn as ImpressionObserver, qt as bool, r as tryScroll, rn as ParamSchema, rr as secureFetch, rt as AsyncMapper, s as ActionHandlerDependencies, sn as makeSchema, sr as Container, st as pipeAsync, t as createPrefetchedIntentsFromDom, tn as Router, tr as LoggerFactory, tt as defineRoutes, u as FlowActionDependencies, un as BeforeLoadGuard, ur as ACTION_KINDS, ut as classifyHost, v as isRtl, vn as deny, vr as makeExternalUrlAction, vt as stableStringify, w as englishPlural, wn as FINESOFT_PUBLIC, wt as FrameworkConfig, x as setHtmlLocaleAttributes, xn as rewrite, xt as SafeErrorPageOptions, y as makeLocaleInfo, yn as next, yr as makeFlowAction, yt as BaseItem, z as generateUuid, zn as MessagesLoaderContext, zt as InferQuery } from "./server-data-DQzknR97.mjs";
2
2
  import { Hono } from "hono";
3
3
  import * as _$node_fs0 from "node:fs";
4
4
  import { ViteDevServer } from "vite";
@@ -137,7 +137,18 @@ declare function injectCSRShell(template: string, locale?: {
137
137
  }): string;
138
138
  //#endregion
139
139
  //#region ../ssr/src/server-data.d.ts
140
- declare function serializeServerData(data: PrefetchedIntent[]): string;
140
+ interface SerializeServerDataOptions {
141
+ /**
142
+ * 没有 `markPublic` 标注的 page 怎么处理:
143
+ *
144
+ * - `"all"` (默认): 全字段序列化 + dev 启动后打印一次告警。向后兼容。
145
+ * - `"base-fields"`: 只保留 BasePage 标准字段 (id/pageType/title/description/url)。
146
+ * 下一个 major 会成为默认。
147
+ * - `"strict"`: 直接抛错。CI / 类型严格的场景用。
148
+ */
149
+ onUnmarkedPage?: "all" | "base-fields" | "strict";
150
+ }
151
+ declare function serializeServerData(data: PrefetchedIntent[], options?: SerializeServerDataOptions): string;
141
152
  //#endregion
142
153
  //#region ../server/src/proxy.d.ts
143
154
  /** 代理路由认证配置 */
@@ -537,5 +548,5 @@ declare function finesoftFrontViteConfig(options?: FinesoftFrontViteOptions): {
537
548
  closeBundle(): Promise<void>;
538
549
  };
539
550
  //#endregion
540
- export { ACTION_KINDS, Action, ActionDispatcher, ActionHandler, type ActionHandlerDependencies, type Adapter, type AdapterContext, AfterLoadGuard, AsyncMapper, BaseController, BaseItem, BaseLogger, BasePage, BaseShelf, BeforeLoadGuard, type BrowserAppConfig, BrowserContextOptions, CompositeEventRecorder, CompositeLogger, CompositeLoggerFactory, CompoundAction, ConsoleEventRecorder, ConsoleLogger, ConsoleLoggerFactory, Container, DEP_KEYS, DefineRoutesOptions, DenyResult, EventRecorder, ExternalUrlAction, type ExternalUrlDependencies, FeatureFlags, FeatureFlagsProvider, type FinesoftFrontViteOptions, FlowAction, type FlowActionCallbacks, type FlowActionDependencies, Framework, FrameworkConfig, History, HttpClient, HttpClientConfig, HttpError, ImpressionEntry, ImpressionObserver, ImpressionObserverOptions, type InjectSSROptions, Intent, IntentController, IntentDispatcher, IntersectionImpressionObserver, LocaleAttributes, LocaleInfo, Logger, Logger as LoggerInterface, LoggerFactory, LruMap, MakeDependenciesOptions, Mapper, MessagesLoader, MessagesLoaderContext, MetricsFieldsProvider, MetricsRecorder, MiddlewareResult, NavigationContext, Net, NextResult, None, Optional, PWADisplayMode, PlatformInfo, PluralCategory, PluralRuleProvider, PostLoadContext, PrefetchedIntent, PrefetchedIntents, type ProxyAuthConfig, type ProxyRouteConfig, RedirectResult, RenderMode, ReportCallback, ReportingLogger, ReportingLoggerFactory, ReportingLoggerOptions, RequestInterceptor, ResponseInterceptor, RewriteResult, RouteAddOptions, RouteDefinition, RouteMatch, Router, type RuntimeInfo, type SSRAppOptions, type SSRContext, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, type ServerConfig, ServerContextOptions, type ServerInstance, SimpleTranslator, SimpleTranslatorOptions, type StartServerOptions, Storage, TextDirection, TranslationMessages, Translator, VoidEventRecorder, WithFieldsRecorder, autoAdapter, buildUrl, cloudflareAdapter, createBrowserContext, createPrefetchedIntentsFromDom, createSSRApp, createSSRRender, createServer, createServerContext, defineRoutes, deny, deserializeServerData, detectPlatform, detectRuntime, englishPlural, finesoftFrontViteConfig, generateProxyCode, generateUuid, getBaseUrl, getLocaleAttributes, getPWADisplayMode, getTextDirection, injectCSRShell, injectSSRContent, interpolate, isCompoundAction, isExternalUrlAction, isFlowAction, isNone, isRtl, isSome, makeDependencies, makeExternalUrlAction, makeFlowAction, makeLocaleInfo, mapEach, netlifyAdapter, next, nodeAdapter, parseAcceptLanguage, pipe, pipeAsync, redirect, registerActionHandlers, registerExternalUrlHandler, registerFlowActionHandler, registerProxyRoutes, removeHost, removeQueryParams, removeScheme, resetFilterCache, resolveAdapter, resolveConfiguredMessages, resolveLocaleFromUrl, resolveMessages, resolvePluralKey, resolveRoot, rewrite, runAfterLoadGuards, runBeforeLoadGuards, serializeServerData, setHtmlLocaleAttributes, shouldLog, ssrRender, stableStringify, startBrowserApp, startServer, staticAdapter, tryScroll, vercelAdapter };
551
+ export { ACTION_KINDS, Action, ActionDispatcher, ActionHandler, type ActionHandlerDependencies, type Adapter, type AdapterContext, AfterLoadGuard, AsyncMapper, BASE_PAGE_FIELDS, BaseController, BaseItem, BaseLogger, BasePage, BaseShelf, BeforeLoadGuard, type BrowserAppConfig, BrowserContextOptions, CompositeEventRecorder, CompositeLogger, CompositeLoggerFactory, CompoundAction, ConsoleEventRecorder, ConsoleLogger, ConsoleLoggerFactory, Container, DEP_KEYS, DefineRoutesOptions, DenyResult, EventRecorder, ExternalUrlAction, type ExternalUrlDependencies, ExtractParamNames, FINESOFT_PUBLIC, FeatureFlags, FeatureFlagsProvider, type FinesoftFrontViteOptions, FlowAction, type FlowActionCallbacks, type FlowActionDependencies, Framework, FrameworkConfig, History, HostCheckResult, HostGuardError, HttpClient, HttpClientConfig, HttpError, ImpressionEntry, ImpressionObserver, ImpressionObserverOptions, InferOutput, InferParams, InferQuery, type InjectSSROptions, Intent, IntentController, IntentDispatcher, IntersectionImpressionObserver, LocaleAttributes, LocaleInfo, Logger, Logger as LoggerInterface, LoggerFactory, LruMap, MakeDependenciesOptions, Mapper, MessagesLoader, MessagesLoaderContext, MetricsFieldsProvider, MetricsRecorder, MiddlewareResult, NavigationContext, Net, NextResult, None, NumOptions, Optional, PWADisplayMode, ParamSchema, ParamsFor, PlatformInfo, PluralCategory, PluralRuleProvider, PostLoadContext, PrefetchedIntent, PrefetchedIntents, type ProxyAuthConfig, type ProxyRouteConfig, QuerySchemaMap, RedirectResult, RenderMode, ReportCallback, ReportingLogger, ReportingLoggerFactory, ReportingLoggerOptions, RequestInterceptor, RequestScopedKey, ResponseInterceptor, RewriteResult, RouteAddOptions, RouteDefinition, RouteMatch, Router, type RuntimeInfo, type SSRAppOptions, type SSRContext, type SSRModule, type SSRRenderConfig, type SSRRenderOptions, type SSRRenderResult, SSR_PLACEHOLDERS, SafeErrorPageOptions, SecureFetchOptions, type SerializeServerDataOptions, type ServerConfig, ServerContextOptions, type ServerInstance, SimpleTranslator, SimpleTranslatorOptions, StandardIssue, StandardResult, StandardSchemaV1, type StartServerOptions, Storage, StrOptions, StripOptional, TextDirection, TranslationMessages, Translator, VoidEventRecorder, WithFieldsRecorder, autoAdapter, bool, buildUrl, classifyHost, classifyUrl, cloudflareAdapter, createBrowserContext, createPrefetchedIntentsFromDom, createSSRApp, createSSRRender, createServer, createServerContext, defineRequestScopedKey, defineRoutes, deny, deserializeServerData, detectPlatform, detectRuntime, englishPlural, finesoftFrontViteConfig, generateProxyCode, generateUuid, getBaseUrl, getLocaleAttributes, getPWADisplayMode, getPublicFields, getTextDirection, injectCSRShell, injectSSRContent, int, interpolate, isCompoundAction, isExternalUrlAction, isFlowAction, isNone, isPublicMarked, isRtl, isSome, makeDependencies, makeExternalUrlAction, makeFlowAction, makeLocaleInfo, makeSchema, mapEach, markPublic, netlifyAdapter, next, nodeAdapter, num, oneOf, optional, parseAcceptLanguage, pipe, pipeAsync, redirect, registerActionHandlers, registerExternalUrlHandler, registerFlowActionHandler, registerProxyRoutes, removeHost, removeQueryParams, removeScheme, resetFilterCache, resolveAdapter, resolveConfiguredMessages, resolveLocaleFromUrl, resolveMessages, resolvePluralKey, resolveRoot, rewrite, route, runAfterLoadGuards, runBeforeLoadGuards, runStandard, safeErrorPage, secureFetch, serializeServerData, setHtmlLocaleAttributes, shouldLog, ssrRender, stableStringify, startBrowserApp, startServer, staticAdapter, str, tryScroll, uuid, vercelAdapter, withDefault };
541
552
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import{$ as e,A as t,B as n,C as r,D as i,E as a,F as o,G as s,H as c,I as l,J as u,K as d,L as f,M as p,N as m,O as h,P as g,Q as _,R as v,S as y,T as b,U as x,V as S,W as C,X as w,Y as T,Z as E,_ as D,_t as ee,a as te,at as ne,b as re,bt as ie,c as ae,ct as oe,d as se,dt as ce,et as le,f as ue,ft as de,g as fe,gt as pe,h as me,ht as he,i as ge,it as _e,j as ve,k as ye,l as be,lt as xe,m as Se,mt as Ce,n as we,nt as Te,o as Ee,ot as De,p as Oe,pt as ke,q as Ae,r as je,rt as Me,s as Ne,st as O,t as Pe,tt as Fe,u as Ie,ut as Le,v as k,vt as Re,w as ze,x as Be,xt as Ve,y as He,yt as Ue,z as We}from"./start-app-BdXBCcor.mjs";import{Hono as A}from"hono";async function Ge(e){return j(e,0)}async function j(e,t){if(t>=5)throw Error(`[SSR] Rewrite recursion depth exceeded (max 5) at "${e.url}"`);let{url:n,frameworkConfig:r,bootstrap:i,getErrorPage:a,renderApp:o,ssrContext:s,resolveLocale:u,loadMessages:d}=e,f=new URL(n,`http://localhost`),p=f.pathname+f.search,m=u?.(n,s?.request),h=m?{...r,locale:m.lang}:r,g=await ne({locale:h.locale,loadMessages:d,context:h.locale?{runtime:`server`,fetch:Ke(s?.fetch??h.fetch),url:p,request:s?.request}:void 0}),_={...h,fetch:s?.fetch??h.fetch},v=l.create({..._,_resolvedMessages:g});i(v);try{let n=v.routeUrl(p);if(n?.renderMode===`csr`)return{html:``,head:``,css:``,serverData:[],renderMode:`csr`};let r,i=[],l;if(n){let u=k({url:p,intent:n.intent,container:v.container,request:s?.request}),d=await v.runBeforeLoad(u,n.beforeGuards);if(d.kind===`rewrite`)return j({...e,url:d.url},t+1);if(d.kind!==`next`){let e=await M(d,a,o,v);if(e)return e}try{r=await v.dispatch(n.intent),i=[{intent:n.intent,data:r}]}catch(e){v.container.resolve(c.LOGGER).error(`[SSR] dispatch failed for intent "${n.intent.id}":`,e),r=a(500,`Internal error`)}let f={...u,page:r},m=await v.runAfterLoad(f,n.afterGuards);if(m.kind===`rewrite`)l=m.url;else if(m.kind!==`next`){let e=await M(m,a,o,v);if(e)return e}}else r=a(404,`Page not found`);let u=await o(r,v),d=m??v.getLocale();return{html:u.html,head:u.head,css:u.css,serverData:i,renderMode:n?.renderMode,slots:u.slots,locale:d,rewriteUrl:l}}finally{v.dispose()}}function Ke(e){return(e??globalThis.fetch?.bind(globalThis))||(()=>{throw Error(`[ssrRender] loadMessages requires a fetch implementation.`)})}async function M(e,t,n,r){switch(e.kind){case`next`:case`rewrite`:return null;case`redirect`:return{html:``,head:``,css:``,serverData:[],redirect:{url:e.url,status:e.status}};case`deny`:{let i=await n(t(e.status,e.message),r);return{html:i.html,head:i.head,css:i.css,serverData:[],slots:i.slots,status:e.status}}}}function qe(e){let{bootstrap:t,getErrorPage:n,renderApp:r,frameworkConfig:i,resolveLocale:a,loadMessages:o}=e;return(e,s)=>Ge({url:e,frameworkConfig:i??{},bootstrap:t,getErrorPage:n,renderApp:(e,t)=>r(e,t),ssrContext:s,resolveLocale:a,loadMessages:o})}const Je={HEAD:`<!--ssr-head-->`,BODY:`<!--ssr-body-->`,DATA:`<!--ssr-data-->`},N=/<!--ssr-([a-z][a-z0-9-]*)-->/g;function P(e){let{template:t,head:n,css:r,html:i,serializedData:a,slots:o,locale:s}=e,c={head:`${n}\n${r?`<style>${r}</style>`:``}`,body:i,data:`<script id="serialized-server-data" type="application/json">${a}<\/script>`,...o},l=t.replace(N,(e,t)=>c[t]??``);return s&&(l=Ze(l,s)),l}function F(e,t){let n=e.replace(N,()=>``);return t&&(n=Ze(n,t)),n}const Ye=/\s+lang=("[^"]*"|'[^']*'|[^\s>]+)/gi,Xe=/\s+dir=("[^"]*"|'[^']*'|[^\s>]+)/gi;function Ze(e,t){return e.replace(/(<html)([^>]*)(>)/i,(e,n,r,i)=>`${n}${r.replace(Ye,``).replace(Xe,``)} lang="${t.lang}" dir="${t.dir}"${i}`)}const Qe={"<":`\\u003C`,">":`\\u003E`,"/":`\\u002F`,"\u2028":`\\u2028`,"\u2029":`\\u2029`},$e=/[<>/\u2028\u2029]/g;function et(e){return JSON.stringify(e).replace($e,e=>Qe[e]??e)}const tt=new Map,nt=e=>import(e),rt=typeof process<`u`&&process.env?.FINESOFT_DEBUG===`1`;function it(e){rt&&console.debug(`[finesoft:dynamic-import] ${e}`)}async function I(e){let t=!e.startsWith(`file:`)&&!e.startsWith(`/`);if(t){let t=tt.get(e);if(t)return it(`cache hit → ${e}`),t}it(`importing → ${e}`);let n=await nt(e);return t&&tt.set(e,n),n}const L=10*1024*1024;function at(e){if(e.length>2048)return null;try{if(decodeURIComponent(e)!==e)return null}catch{return null}return e.startsWith(`//`)||!/^[/\w.\-~%:@!$&'()*+,;=]*$/.test(e)?null:e.startsWith(`/`)?e:`/${e}`}function ot(e){if(!e.prefix.startsWith(`/`))throw Error(`[proxy] prefix must start with "/": "${e.prefix}"`);let t=e.target.startsWith(`https://`),n=e.target.startsWith(`http://`);if(!t&&!n)throw Error(`[proxy] target must start with "https://" or "http://": "${e.target}"`);n&&console.warn(`[proxy] ⚠ target "${e.target}" uses plain HTTP — traffic will not be encrypted. Use HTTPS in production to prevent data interception.`)}function R(e,t){for(let n of t){ot(n);let t=n.methods??[`all`],r=`${n.prefix}/*`,i=async e=>{let t=at(e.req.path.replace(n.prefix,``));if(!t)return e.text(`Invalid path`,400);let r=new URL(t,n.target),i=new URL(n.target).origin;if(r.origin!==i)return e.text(`Invalid proxy target`,400);new URL(e.req.url).searchParams.forEach((e,t)=>r.searchParams.set(t,e));let a={...n.headers};if(n.auth){let e=process.env[n.auth.envKey];e?a.Authorization=n.auth.type===`bearer`?`Bearer ${e}`:`Basic ${e}`:console.warn(`[Proxy ${n.prefix}] Auth env var "${n.auth.envKey}" is not set`)}try{let t=await fetch(r.toString(),{headers:a,redirect:n.followRedirects?`follow`:`manual`}),i=t.headers.get(`Content-Length`);if(i&&parseInt(i,10)>L)return e.text(`Proxy response too large`,502);let o=await t.arrayBuffer();if(o.byteLength>L)return e.text(`Proxy response too large`,502);let s={"Content-Type":t.headers.get(`Content-Type`)??`application/json`};return n.cache&&(s[`Cache-Control`]=n.cache),e.newResponse(o,t.status,s)}catch(t){return console.error(`[Proxy ${n.prefix}]`,t),e.json({error:`Proxy request failed`},502)}};for(let n of t)e[n](r,i)}}function z(e){if(!e||e.length===0)return``;for(let t of e)ot(t);let t=[];t.push(`
1
+ import{$ as e,A as t,At as n,B as r,Bt as i,C as a,Ct as o,D as s,Dt as c,E as l,Et as u,F as d,Ft as f,G as p,H as m,Ht as h,I as g,It as _,J as v,K as y,L as b,Lt as x,M as S,Mt as C,N as ee,Nt as w,O as T,Ot as E,P as D,Pt as te,Q as ne,R as re,Rt as ie,S as ae,St as oe,T as se,Tt as ce,U as le,Ut as ue,V as de,Vt as fe,W as pe,X as me,Y as he,Z as ge,_ as _e,_t as ve,a as ye,at as be,b as xe,bt as Se,c as Ce,ct as we,d as Te,dt as Ee,et as De,f as Oe,ft as ke,g as Ae,gt as je,h as Me,ht as Ne,i as Pe,it as O,j as Fe,jt as Ie,k as Le,kt as Re,l as ze,lt as Be,m as Ve,mt as He,n as Ue,nt as We,o as Ge,ot as Ke,p as qe,pt as Je,q as Ye,r as Xe,rt as Ze,s as Qe,st as $e,t as et,tt,u as nt,ut as rt,v as k,vt as it,w as at,wt as ot,x as st,xt as A,y as ct,yt as lt,z as ut,zt as dt}from"./start-app-S4DH-40i.mjs";import{Hono as j}from"hono";async function M(e){return N(e,0)}async function N(e,t){if(t>=5)throw Error(`[SSR] Rewrite recursion depth exceeded (max 5) at "${e.url}"`);let{url:n,frameworkConfig:r,bootstrap:i,getErrorPage:a,renderApp:o,ssrContext:s,resolveLocale:c,loadMessages:l}=e,u=new URL(n,`http://localhost`),d=u.pathname+u.search,f=c?.(n,s?.request),p=f?{...r,locale:f.lang}:r,m=await A({locale:p.locale,loadMessages:l,context:p.locale?{runtime:`server`,fetch:ft(s?.fetch??p.fetch),url:d,request:s?.request}:void 0}),h={...p,fetch:s?.fetch??p.fetch},g=de.create({...h,_resolvedMessages:m});i(g);try{let n=await g.routeUrl(d);if(n?.renderMode===`csr`)return{html:``,head:``,css:``,serverData:[],renderMode:`csr`};let r,i=[],c;if(n){let l=k({url:d,intent:n.intent,container:g.container,request:s?.request}),u=await g.runBeforeLoad(l,n.beforeGuards);if(u.kind===`rewrite`)return N({...e,url:u.url},t+1);if(u.kind!==`next`){let e=await P(u,a,o,g);if(e)return e}try{r=await g.dispatch(n.intent),i=[{intent:n.intent,data:r}]}catch(e){g.container.resolve(O.LOGGER).error(`[SSR] dispatch failed for intent "${n.intent.id}":`,e),r=a(500,`Internal error`)}let f={...l,page:r},p=await g.runAfterLoad(f,n.afterGuards);if(p.kind===`rewrite`)c=p.url;else if(p.kind!==`next`){let e=await P(p,a,o,g);if(e)return e}}else r=a(404,`Page not found`);let l=await o(r,g),u=f??g.getLocale();return{html:l.html,head:l.head,css:l.css,serverData:i,renderMode:n?.renderMode,slots:l.slots,locale:u,rewriteUrl:c}}finally{g.dispose()}}function ft(e){return(e??globalThis.fetch?.bind(globalThis))||(()=>{throw Error(`[ssrRender] loadMessages requires a fetch implementation.`)})}async function P(e,t,n,r){switch(e.kind){case`next`:case`rewrite`:return null;case`redirect`:return{html:``,head:``,css:``,serverData:[],redirect:{url:e.url,status:e.status}};case`deny`:{let i=await n(t(e.status,e.message),r);return{html:i.html,head:i.head,css:i.css,serverData:[],slots:i.slots,status:e.status}}}}function pt(e){let{bootstrap:t,getErrorPage:n,renderApp:r,frameworkConfig:i,resolveLocale:a,loadMessages:o}=e;return(e,s)=>M({url:e,frameworkConfig:i??{},bootstrap:t,getErrorPage:n,renderApp:(e,t)=>r(e,t),ssrContext:s,resolveLocale:a,loadMessages:o})}const mt={HEAD:`<!--ssr-head-->`,BODY:`<!--ssr-body-->`,DATA:`<!--ssr-data-->`},F=/<!--ssr-([a-z][a-z0-9-]*)-->/g;function I(e){let{template:t,head:n,css:r,html:i,serializedData:a,slots:o,locale:s}=e,c={head:`${n}\n${r?`<style>${r}</style>`:``}`,body:i,data:`<script id="serialized-server-data" type="application/json">${a}<\/script>`,...o},l=t.replace(F,(e,t)=>c[t]??``);return s&&(l=R(l,s)),l}function L(e,t){let n=e.replace(F,()=>``);return t&&(n=R(n,t)),n}const ht=/\s+lang=("[^"]*"|'[^']*'|[^\s>]+)/gi,gt=/\s+dir=("[^"]*"|'[^']*'|[^\s>]+)/gi;function R(e,t){return e.replace(/(<html)([^>]*)(>)/i,(e,n,r,i)=>`${n}${r.replace(ht,``).replace(gt,``)} lang="${t.lang}" dir="${t.dir}"${i}`)}const _t={"<":`\\u003C`,">":`\\u003E`,"/":`\\u002F`,"\u2028":`\\u2028`,"\u2029":`\\u2029`},vt=RegExp(`[<>/\\u2028\\u2029]`,`g`);let z=!1;function yt(e,t={}){let n=t.onUnmarkedPage??`all`,r=e.map(e=>bt(e,n));return JSON.stringify(r).replace(vt,e=>_t[e]??e)}function bt(e,t){let n=e.data,r=re(n);if(r===!0)return e;if(Array.isArray(r))return{...e,data:B(n,r)};if(t===`strict`)throw Error(`[serializeServerData] page for intent "${e.intent.id}" has no markPublic marker; wrap it with markPublic(page, ["field", ...]) to declare client-visible fields.`);return t===`base-fields`?{...e,data:B(n,g)}:(xt(e.intent.id),e)}function B(e,t){let n={};for(let r of t)r in e&&(n[r]=e[r]);return n}function xt(e){z||(z=!0,St()&&console.warn(`[finesoft/ssr] Page for intent "${e}" was serialized without markPublic(); all fields (including any sensitive data) are exposed in the SSR HTML. Wrap your page with markPublic(page, ["field", ...]) to declare client-visible fields explicitly. The next major version will default to BasePage-only fields when no marker is present.`))}function St(){try{return globalThis.process?.env?.NODE_ENV!==`production`}catch{return!1}}const V=new Map,Ct=e=>import(e),wt=typeof process<`u`&&process.env?.FINESOFT_DEBUG===`1`;function Tt(e){wt&&console.debug(`[finesoft:dynamic-import] ${e}`)}async function H(e){let t=!e.startsWith(`file:`)&&!e.startsWith(`/`);if(t){let t=V.get(e);if(t)return Tt(`cache hit → ${e}`),t}Tt(`importing → ${e}`);let n=await Ct(e);return t&&V.set(e,n),n}const U=10*1024*1024;function Et(e){if(e.length>2048)return null;try{if(decodeURIComponent(e)!==e)return null}catch{return null}return e.startsWith(`//`)||!/^[/\w.\-~%:@!$&'()*+,;=]*$/.test(e)?null:e.startsWith(`/`)?e:`/${e}`}function Dt(e){if(!e.prefix.startsWith(`/`))throw Error(`[proxy] prefix must start with "/": "${e.prefix}"`);let t=e.target.startsWith(`https://`),n=e.target.startsWith(`http://`);if(!t&&!n)throw Error(`[proxy] target must start with "https://" or "http://": "${e.target}"`);n&&console.warn(`[proxy] ⚠ target "${e.target}" uses plain HTTP — traffic will not be encrypted. Use HTTPS in production to prevent data interception.`)}function W(e,t){for(let n of t){Dt(n);let t=n.methods??[`all`],r=`${n.prefix}/*`,i=async e=>{let t=Et(e.req.path.replace(n.prefix,``));if(!t)return e.text(`Invalid path`,400);let r=new URL(t,n.target),i=new URL(n.target).origin;if(r.origin!==i)return e.text(`Invalid proxy target`,400);new URL(e.req.url).searchParams.forEach((e,t)=>r.searchParams.set(t,e));let a={...n.headers};if(n.auth){let e=process.env[n.auth.envKey];e?a.Authorization=n.auth.type===`bearer`?`Bearer ${e}`:`Basic ${e}`:console.warn(`[Proxy ${n.prefix}] Auth env var "${n.auth.envKey}" is not set`)}try{let t=await fetch(r.toString(),{headers:a,redirect:n.followRedirects?`follow`:`manual`}),i=t.headers.get(`Content-Length`);if(i&&parseInt(i,10)>U)return e.text(`Proxy response too large`,502);let o=await t.arrayBuffer();if(o.byteLength>U)return e.text(`Proxy response too large`,502);let s={"Content-Type":t.headers.get(`Content-Type`)??`application/json`};return n.cache&&(s[`Cache-Control`]=n.cache),e.newResponse(o,t.status,s)}catch(t){return console.error(`[Proxy ${n.prefix}]`,t),e.json({error:`Proxy request failed`},502)}};for(let n of t)e[n](r,i)}}function Ot(e){if(!e||e.length===0)return``;for(let t of e)Dt(t);let t=[];t.push(`
2
2
  // ─── 框架声明式代理路由 ───
3
3
  function _sanitizeProxyPath(raw) {
4
4
  if (raw.length > 2048) return null;
@@ -21,12 +21,12 @@ function _sanitizeProxyPath(raw) {
21
21
  const _resp = await fetch(_target.toString(), { headers: _headers, redirect: ${o} });
22
22
  // Content-Length 快速拒绝,防止 serverless/edge 加载超大响应到内存
23
23
  const _cl = _resp.headers.get("Content-Length");
24
- if (_cl && parseInt(_cl, 10) > ${L}) {
24
+ if (_cl && parseInt(_cl, 10) > ${U}) {
25
25
  return c.text("Proxy response too large", 502);
26
26
  }
27
27
  // arrayBuffer 保留二进制完整性
28
28
  const _body = await _resp.arrayBuffer();
29
- if (_body.byteLength > ${L}) {
29
+ if (_body.byteLength > ${U}) {
30
30
  return c.text("Proxy response too large", 502);
31
31
  }
32
32
  const _rh = { "Content-Type": _resp.headers.get("Content-Type") || "application/json" };
@@ -37,7 +37,7 @@ function _sanitizeProxyPath(raw) {
37
37
  return c.json({ error: "Proxy request failed" }, 502);
38
38
  }
39
39
  }`;for(let n of e)t.push(`app.${n}(${r}, ${c});`)}return t.join(`
40
- `)}const st=[`vite`,`esbuild`,`rollup`,`fsevents`,`lightningcss`],B=[`node:async_hooks`,`node:buffer`,`node:crypto`,`node:fs`,`node:http`,`node:http2`,`node:module`,`node:net`,`node:os`,`node:path`,`node:stream`,`node:url`,`node:util`,`node:zlib`,`crypto`,`http`,`http2`,`stream`];function V(e,t){let n=e.setupPath?`import _setupDefault from "./${e.setupPath}";`:``,r=e.setupPath?`if (typeof _setupDefault === "function") await _setupDefault(app);`:``,i=JSON.stringify(e.renderModes??{}),a=t.platformCache?t.platformCache:`
40
+ `)}const kt=[`vite`,`esbuild`,`rollup`,`fsevents`,`lightningcss`],At=[`node:async_hooks`,`node:buffer`,`node:crypto`,`node:fs`,`node:http`,`node:http2`,`node:module`,`node:net`,`node:os`,`node:path`,`node:stream`,`node:url`,`node:util`,`node:zlib`,`crypto`,`http`,`http2`,`stream`];function G(e,t){let n=e.setupPath?`import _setupDefault from "./${e.setupPath}";`:``,r=e.setupPath?`if (typeof _setupDefault === "function") await _setupDefault(app);`:``,i=JSON.stringify(e.renderModes??{}),a=t.platformCache?t.platformCache:`
41
41
  const ISR_CACHE_MAX = 1000;
42
42
  const _isrMap = new Map();
43
43
  async function platformCacheGet(url) {
@@ -109,7 +109,7 @@ function matchRenderMode(url) {
109
109
  }
110
110
 
111
111
  const app = new Hono();
112
- ${z(e.proxies??[])}
112
+ ${Ot(e.proxies??[])}
113
113
  ${r}
114
114
  ${t.platformMiddleware??``}
115
115
 
@@ -173,8 +173,8 @@ app.get("*", async (c) => {
173
173
  });
174
174
 
175
175
  ${t.platformExport}
176
- `}async function H(e,t){await e.vite.build({root:e.root,build:{ssr:t.entry,outDir:t.outDir,emptyOutDir:t.emptyOutDir??!0,target:t.target??`node18`,rollupOptions:{output:{entryFileNames:t.fileName??`index.mjs`}}},ssr:{noExternal:t.noExternal!==!1,external:[...t.external??st,...B]},resolve:e.resolvedResolve,css:e.resolvedCss})}function U(e,t,n){let{fs:r,path:i}=e;r.cpSync(i.resolve(e.root,`dist/client`),t,{recursive:!0}),n?.excludeHtml!==!1&&r.rmSync(i.join(t,`index.html`),{force:!0})}async function W(e){let{fs:t,path:n,root:r,vite:i}=e,{pathToFileURL:a}=await I(`node:url`),o=e.bootstrapEntry??`src/lib/bootstrap.ts`,s=[];if(t.existsSync(n.resolve(r,o))){await i.build({root:r,build:{ssr:o,outDir:n.resolve(r,`dist/server`),emptyOutDir:!1,rollupOptions:{output:{entryFileNames:`_routes_prerender.mjs`}}},resolve:e.resolvedResolve});let c=a(n.resolve(r,`dist/server/_routes_prerender.mjs`)).href,l=await I(c);s=l.routes??l.default??[],t.rmSync(n.resolve(r,`dist/server/_routes_prerender.mjs`),{force:!0})}let c=new Set;for(let e of s)e.renderMode===`prerender`&&e.path&&!e.path.includes(`:`)&&c.add(e.path);if(e.renderModes)for(let[t,n]of Object.entries(e.renderModes))n===`prerender`&&!t.includes(`*`)&&!t.includes(`:`)&&c.add(t);if(e.locales?.length){let t=[...c];for(let n of e.locales)for(let e of t){let t=e===`/`?`/${n}`:`/${n}${e}`;c.add(t)}}if(c.size===0)return[];let l=a(n.resolve(r,`dist/server/ssr.js`)).href,u=await I(l),d=[];for(let t of c)try{let{html:n,head:r,css:i,serverData:a,locale:o}=await u.render(t),s=u.serializeServerData(a),c=e.templateHtml.replace(/<!--ssr-([a-z][a-z0-9-]*)-->/g,(e,t)=>({head:r+`
177
- <style>`+i+`</style>`,body:n,data:`<script id="serialized-server-data" type="application/json">`+s+`<\/script>`})[t]??``);if(o){let{getLocaleAttributes:e}=await I(`@finesoft/core`),t=e(o);c=c.replace(/<html([^>]*)>/i,(e,n)=>`<html${n.replace(/\s+lang=("[^"]*"|'[^']*'|[^\s>]+)/gi,``).replace(/\s+dir=("[^"]*"|'[^']*'|[^\s>]+)/gi,``)} lang="${t.lang}" dir="${t.dir}">`)}d.push({url:t,html:c})}catch(e){console.warn(` [prerender] Failed to render ${t}:`,e)}return d.length>0&&console.log(` Pre-rendered ${d.length} pages (${c.size} routes)\n`),d}function G(){return{name:`cloudflare`,async build(e){let{fs:t,path:n,root:r}=e,i=n.resolve(r,`dist/cloudflare`);t.rmSync(i,{recursive:!0,force:!0});let a=V(e,{platformImport:``,platformExport:`export default app;`,platformCache:`
176
+ `}async function K(e,t){await e.vite.build({root:e.root,build:{ssr:t.entry,outDir:t.outDir,emptyOutDir:t.emptyOutDir??!0,target:t.target??`node18`,rollupOptions:{output:{entryFileNames:t.fileName??`index.mjs`}}},ssr:{noExternal:t.noExternal!==!1,external:[...t.external??kt,...At]},resolve:e.resolvedResolve,css:e.resolvedCss})}function q(e,t,n){let{fs:r,path:i}=e;r.cpSync(i.resolve(e.root,`dist/client`),t,{recursive:!0}),n?.excludeHtml!==!1&&r.rmSync(i.join(t,`index.html`),{force:!0})}async function J(e){let{fs:t,path:n,root:r,vite:i}=e,{pathToFileURL:a}=await H(`node:url`),o=e.bootstrapEntry??`src/lib/bootstrap.ts`,s=[];if(t.existsSync(n.resolve(r,o))){await i.build({root:r,build:{ssr:o,outDir:n.resolve(r,`dist/server`),emptyOutDir:!1,rollupOptions:{output:{entryFileNames:`_routes_prerender.mjs`}}},resolve:e.resolvedResolve});let c=a(n.resolve(r,`dist/server/_routes_prerender.mjs`)).href,l=await H(c);s=l.routes??l.default??[],t.rmSync(n.resolve(r,`dist/server/_routes_prerender.mjs`),{force:!0})}let c=new Set;for(let e of s)e.renderMode===`prerender`&&e.path&&!e.path.includes(`:`)&&c.add(e.path);if(e.renderModes)for(let[t,n]of Object.entries(e.renderModes))n===`prerender`&&!t.includes(`*`)&&!t.includes(`:`)&&c.add(t);if(e.locales?.length){let t=[...c];for(let n of e.locales)for(let e of t){let t=e===`/`?`/${n}`:`/${n}${e}`;c.add(t)}}if(c.size===0)return[];let l=a(n.resolve(r,`dist/server/ssr.js`)).href,u=await H(l),d=[];for(let t of c)try{let{html:n,head:r,css:i,serverData:a,locale:o}=await u.render(t),s=u.serializeServerData(a),c=e.templateHtml.replace(/<!--ssr-([a-z][a-z0-9-]*)-->/g,(e,t)=>({head:r+`
177
+ <style>`+i+`</style>`,body:n,data:`<script id="serialized-server-data" type="application/json">`+s+`<\/script>`})[t]??``);if(o){let{getLocaleAttributes:e}=await H(`@finesoft/core`),t=e(o);c=c.replace(/<html([^>]*)>/i,(e,n)=>`<html${n.replace(/\s+lang=("[^"]*"|'[^']*'|[^\s>]+)/gi,``).replace(/\s+dir=("[^"]*"|'[^']*'|[^\s>]+)/gi,``)} lang="${t.lang}" dir="${t.dir}">`)}d.push({url:t,html:c})}catch(e){console.warn(` [prerender] Failed to render ${t}:`,e)}return d.length>0&&console.log(` Pre-rendered ${d.length} pages (${c.size} routes)\n`),d}function jt(){return{name:`cloudflare`,async build(e){let{fs:t,path:n,root:r}=e,i=n.resolve(r,`dist/cloudflare`);t.rmSync(i,{recursive:!0,force:!0});let a=G(e,{platformImport:``,platformExport:`export default app;`,platformCache:`
178
178
  const ISR_CACHE_TTL = 3600; // 1 hour
179
179
  async function platformCacheGet(url) {
180
180
  try {
@@ -194,8 +194,8 @@ async function platformCacheSet(url, html) {
194
194
  });
195
195
  await cache.put(cacheKey, resp);
196
196
  } catch {}
197
- }`}),o=n.resolve(r,`.cf-entry.tmp.mjs`);t.writeFileSync(o,a);try{await H(e,{entry:`.cf-entry.tmp.mjs`,outDir:i,target:`es2022`,fileName:`_worker.js`}),U(e,n.resolve(i,`assets`));let r=await W(e);for(let{url:e,html:a}of r){let r=e===`/`?n.join(i,`assets`,`index.html`):n.join(i,`assets`,e,`index.html`);t.mkdirSync(n.resolve(r,`..`),{recursive:!0}),t.writeFileSync(r,a)}}finally{t.rmSync(o,{force:!0})}console.log(` Cloudflare output → dist/cloudflare/
198
- `)}}}function K(){return{name:`netlify`,async build(e){let{fs:t,path:n,root:r}=e,i=n.resolve(r,`.netlify/functions-internal/ssr`);t.rmSync(n.resolve(r,`.netlify`),{recursive:!0,force:!0});let a=V(e,{platformImport:`import { handle } from "hono/netlify";`,platformExport:`export default handle(app);
197
+ }`}),o=n.resolve(r,`.cf-entry.tmp.mjs`);t.writeFileSync(o,a);try{await K(e,{entry:`.cf-entry.tmp.mjs`,outDir:i,target:`es2022`,fileName:`_worker.js`}),q(e,n.resolve(i,`assets`));let r=await J(e);for(let{url:e,html:a}of r){let r=e===`/`?n.join(i,`assets`,`index.html`):n.join(i,`assets`,e,`index.html`);t.mkdirSync(n.resolve(r,`..`),{recursive:!0}),t.writeFileSync(r,a)}}finally{t.rmSync(o,{force:!0})}console.log(` Cloudflare output → dist/cloudflare/
198
+ `)}}}function Mt(){return{name:`netlify`,async build(e){let{fs:t,path:n,root:r}=e,i=n.resolve(r,`.netlify/functions-internal/ssr`);t.rmSync(n.resolve(r,`.netlify`),{recursive:!0,force:!0});let a=G(e,{platformImport:`import { handle } from "hono/netlify";`,platformExport:`export default handle(app);
199
199
  export const config = { path: "/*", preferStatic: true };`,platformCache:`
200
200
  const ISR_SWR_TTL = 3600;
201
201
  const ISR_CACHE_MAX = 1000;
@@ -210,10 +210,10 @@ async function platformCacheSet(url, html) {
210
210
  }
211
211
  _isrMap.set(url, html);
212
212
  }`,platformPrerenderResponseHook:`c.header("Cache-Control", "public, max-age=0, must-revalidate");
213
- c.header("Netlify-CDN-Cache-Control", "public, max-age=" + ISR_SWR_TTL + ", stale-while-revalidate=" + ISR_SWR_TTL + ", durable");`}),o=n.resolve(r,`.netlify-entry.tmp.mjs`);t.writeFileSync(o,a);try{await H(e,{entry:`.netlify-entry.tmp.mjs`,outDir:i,target:`node18`})}finally{t.rmSync(o,{force:!0})}t.writeFileSync(n.resolve(r,`dist/client/_redirects`),`/* /.netlify/functions/ssr 200
214
- `);let s=await W(e),c=n.resolve(r,`dist/client`);for(let{url:e,html:r}of s){let i=e===`/`?n.join(c,`index.html`):n.join(c,e,`index.html`);t.mkdirSync(n.resolve(i,`..`),{recursive:!0}),t.writeFileSync(i,r)}console.log(` Netlify output → .netlify/functions-internal/ssr/
213
+ c.header("Netlify-CDN-Cache-Control", "public, max-age=" + ISR_SWR_TTL + ", stale-while-revalidate=" + ISR_SWR_TTL + ", durable");`}),o=n.resolve(r,`.netlify-entry.tmp.mjs`);t.writeFileSync(o,a);try{await K(e,{entry:`.netlify-entry.tmp.mjs`,outDir:i,target:`node18`})}finally{t.rmSync(o,{force:!0})}t.writeFileSync(n.resolve(r,`dist/client/_redirects`),`/* /.netlify/functions/ssr 200
214
+ `);let s=await J(e),c=n.resolve(r,`dist/client`);for(let{url:e,html:r}of s){let i=e===`/`?n.join(c,`index.html`):n.join(c,e,`index.html`);t.mkdirSync(n.resolve(i,`..`),{recursive:!0}),t.writeFileSync(i,r)}console.log(` Netlify output → .netlify/functions-internal/ssr/
215
215
  Publish dir: dist/client/
216
- `)}}}function q(){return{name:`node`,async build(e){let{fs:t,path:n,root:r}=e,i=V(e,{platformImport:`import { serve } from "@hono/node-server";
216
+ `)}}}function Nt(){return{name:`node`,async build(e){let{fs:t,path:n,root:r}=e,i=G(e,{platformImport:`import { serve } from "@hono/node-server";
217
217
  import { readFileSync, existsSync } from "node:fs";
218
218
  import { resolve, dirname } from "node:path";
219
219
  import { fileURLToPath } from "node:url";`,platformMiddleware:`
@@ -241,14 +241,14 @@ const port = +(process.env.PORT || 3000);
241
241
  serve({ fetch: app.fetch, port }, (info) => {
242
242
  console.log(\`Server running at http://localhost:\${info.port}\`);
243
243
  });
244
- `}),a=n.resolve(r,`.node-entry.tmp.mjs`);t.writeFileSync(a,i);try{await H(e,{entry:`.node-entry.tmp.mjs`,outDir:n.resolve(r,`dist/server`),target:`node18`,emptyOutDir:!1})}finally{t.rmSync(a,{force:!0})}let o=await W(e);if(o.length>0){let e=n.resolve(r,`dist/prerender`);t.mkdirSync(e,{recursive:!0});for(let{url:r,html:i}of o){let a=r===`/`?n.join(e,`index.html`):n.join(e,r,`index.html`);t.mkdirSync(n.resolve(a,`..`),{recursive:!0}),t.writeFileSync(a,i)}}console.log(` Node output → dist/server/index.mjs
244
+ `}),a=n.resolve(r,`.node-entry.tmp.mjs`);t.writeFileSync(a,i);try{await K(e,{entry:`.node-entry.tmp.mjs`,outDir:n.resolve(r,`dist/server`),target:`node18`,emptyOutDir:!1})}finally{t.rmSync(a,{force:!0})}let o=await J(e);if(o.length>0){let e=n.resolve(r,`dist/prerender`);t.mkdirSync(e,{recursive:!0});for(let{url:r,html:i}of o){let a=r===`/`?n.join(e,`index.html`):n.join(e,r,`index.html`);t.mkdirSync(n.resolve(a,`..`),{recursive:!0}),t.writeFileSync(a,i)}}console.log(` Node output → dist/server/index.mjs
245
245
  Run: node dist/server/index.mjs
246
- `)}}}function J(e={}){return{name:`static`,async build(t){let{fs:n,path:r,root:i}=t,a=r.resolve(i,`dist/static`);n.rmSync(a,{recursive:!0,force:!0}),n.mkdirSync(a,{recursive:!0});let{pathToFileURL:o}=await I(`node:url`),s=o(r.resolve(i,`dist/server/ssr.js`)).href,c=await I(s);t.copyStaticAssets(a,{excludeHtml:!0});let{paths:l,defs:u}=await ct(t,e);console.log(` Pre-rendering ${l.length} pages...\n`);for(let e of l)try{let i=dt(e,u.find(t=>t.path===e)?.renderMode,t.renderModes),o;if(i===`csr`)o=ut(t.templateHtml);else{let{html:n,head:r,css:i,serverData:a}=await c.render(e),s=c.serializeServerData(a);o=lt(t.templateHtml,r,i,n,s)}let s=e===`/`?r.join(a,`index.html`):r.join(a,e,`index.html`);n.mkdirSync(r.resolve(s,`..`),{recursive:!0}),n.writeFileSync(s,o)}catch(t){console.warn(` [static] Failed to render ${e}:`,t)}console.log(` Static output → dist/static/
247
- `)}}}async function ct(e,t){let n=t.routesExport??`src/lib/bootstrap.ts`,r=[],i=[];try{let{pathToFileURL:t}=await I(`node:url`);await e.vite.build({root:e.root,build:{ssr:n,outDir:e.path.resolve(e.root,`dist/server`),emptyOutDir:!1,rollupOptions:{output:{entryFileNames:`_routes.mjs`}}},resolve:e.resolvedResolve});let a=t(e.path.resolve(e.root,`dist/server/_routes.mjs`)).href,o=await I(a),s=o.routes??o.default;if(Array.isArray(s))for(let e of s)e.path&&!e.path.includes(`:`)&&(r.push(e.path),i.push({path:e.path,renderMode:e.renderMode}));e.fs.rmSync(e.path.resolve(e.root,`dist/server/_routes.mjs`),{force:!0})}catch(e){console.warn(` [static] Could not load routes from "${n}". Using "/" only.`,e),r.length===0&&r.push(`/`)}if(t.dynamicRoutes)for(let e of t.dynamicRoutes)r.includes(e)||r.push(e);return r.length===0&&r.push(`/`),{paths:r,defs:i}}function lt(e,t,n,r,i){let a=/<!--ssr-([a-z][a-z0-9-]*)-->/g,o={head:t+`
248
- <style>`+n+`</style>`,body:r,data:`<script id="serialized-server-data" type="application/json">`+i+`<\/script>`};return e.replace(a,(e,t)=>o[t]??``)}function ut(e){return e.replace(/<!--ssr-([a-z][a-z0-9-]*)-->/g,()=>``)}function dt(e,t,n){if(n){if(n[e])return n[e];for(let[t,r]of Object.entries(n))if(t.includes(`*`)){let n=t.replace(/[.+?^${}()|[\]\\]/g,`\\$&`);if(RegExp(`^`+n.replace(/\*/g,`.*`)+`$`).test(e))return r}}return t??`ssr`}function ft(){return{name:`vercel`,async build(e){let{fs:t,path:n,root:r}=e,i=n.resolve(r,`.vercel/output`);t.rmSync(i,{recursive:!0,force:!0});let a=V(e,{platformImport:`import { getRequestListener } from "@hono/node-server";`,platformExport:[`const _listener = getRequestListener(app.fetch);`,`export default (req, res) => {`,` const m = req.headers["x-now-route-matches"];`,` if (typeof m === "string") {`,` try {`,` const p = new URLSearchParams(m);`,` const c = p.get("1");`,` if (c != null) {`,` const qi = (req.url || "").indexOf("?");`,` const qs = qi !== -1 ? req.url.slice(qi) : "";`,` req.url = "/" + decodeURIComponent(c) + qs;`,` }`,` } catch {}`,` }`,` return _listener(req, res);`,`};`].join(`
249
- `)}),o=n.resolve(r,`.vercel-entry.tmp.mjs`);t.writeFileSync(o,a);try{let i=n.resolve(r,`.vercel/output/functions/ssr.func`);await H(e,{entry:`.vercel-entry.tmp.mjs`,outDir:i,target:`node18`}),t.writeFileSync(n.resolve(i,`.vc-config.json`),JSON.stringify({runtime:`nodejs20.x`,handler:`index.mjs`,launcherType:`Nodejs`},null,2)),U(e,n.resolve(r,`.vercel/output/static`)),t.writeFileSync(n.resolve(r,`.vercel/output/config.json`),JSON.stringify({version:3,routes:[{handle:`filesystem`},{src:`/(.*)`,dest:`/ssr`}]},null,2))}finally{t.rmSync(o,{force:!0})}let s=await W(e),c=n.resolve(r,`.vercel/output/static`);for(let{url:e,html:r}of s){let i=e===`/`?n.join(c,`index.html`):n.join(c,e,`index.html`);t.mkdirSync(n.resolve(i,`..`),{recursive:!0}),t.writeFileSync(i,r)}if(s.length>0){let e=n.resolve(r,`.vercel/output/config.json`),i=JSON.parse(t.readFileSync(e,`utf-8`));i.overrides=i.overrides??{};for(let{url:e}of s){let t=e===`/`?`index.html`:`${e.replace(/^\//,``)}/index.html`;i.overrides[t]={path:e===`/`?`/`:e,contentType:`text/html; charset=utf-8`}}t.writeFileSync(e,JSON.stringify(i,null,2))}console.log(` Vercel output → .vercel/output/
250
- `)}}}function Y(e){if(typeof e!=`string`)return e;switch(e){case`vercel`:return ft();case`cloudflare`:return G();case`netlify`:return K();case`node`:return q();case`static`:return J();case`auto`:return pt();default:throw Error(`[finesoft] Unknown adapter: "${e}". Available: vercel, cloudflare, netlify, node, static, auto`)}}function pt(){return{name:`auto`,async build(e){let t=mt();return console.log(` [auto] Detected platform: ${t}\n`),Y(t).build(e)}}}function mt(){return process.env.VERCEL?`vercel`:process.env.CF_PAGES?`cloudflare`:process.env.NETLIFY?`netlify`:`node`}function ht(e,t=1){return((n,r)=>{if(typeof n==`string`&&n.startsWith(`/`)){let i=new Request(`http://localhost${n}`,r);return i.headers.set(`x-ssr-depth`,String(t)),Promise.resolve(e(i))}return globalThis.fetch(n,r)})}function gt(e,t){if(!t)return null;let n=e.split(`?`)[0];if(t[n])return t[n];for(let[e,r]of Object.entries(t))if(e.includes(`*`)){let t=e.replace(/[.+?^${}()|[\]\\]/g,`\\$&`);if(RegExp(`^`+t.replace(/\*/g,`.*`)+`$`).test(n))return r}return null}function X(e){let{root:t,vite:n,isProduction:r,ssrEntryPath:i=`/src/ssr.ts`,ssrProductionModule:a,parentFetch:o,renderModes:s,defaultLocale:c}=e,l=new A,u=new h(1e3),d;async function f(e){if(!r&&n){let{readFileSync:r}=await I(`node:fs`),i=r((await I(`node:path`)).resolve(t,`index.html`),`utf-8`);return n.transformIndexHtml(e,i)}if(d)return d;if(globalThis.Deno!==void 0){let e=import.meta.url;return d=globalThis.Deno.readTextFileSync(new URL(`../dist/client/index.html`,e)),d}let{readFileSync:i}=await I(`node:fs`);return d=i((await I(`node:path`)).resolve(t,`dist/client/index.html`),`utf-8`),d}async function p(){if(!r&&n)return await n.ssrLoadModule(i);if(a)return I(a);let e=await I(`node:path`),{pathToFileURL:o}=await I(`node:url`),s=o(e.resolve(t,`dist/server/ssr.js`)).href;return I(s)}return l.get(`*`,async e=>{let t=parseInt(e.req.header(`x-ssr-depth`)??`0`,10);if(t>=5)return e.text(`SSR recursion loop detected`,508);let i=e.req.path+(e.req.url.includes(`?`)?`?`+e.req.url.split(`?`)[1]:``);try{let n=await f(i),r=await p();if(typeof r.render!=`function`||typeof r.serializeServerData!=`function`)throw Error(`[SSR] Module missing required exports: render, serializeServerData`);let{render:a,serializeServerData:l}=r,d=gt(i,s);if(d===`csr`)return e.html(F(n,c?O(c):void 0));let m=u.get(i);if(m)return e.html(m);let h=o?ht(o,t+1):void 0,g={request:e.req.raw};h&&(g.fetch=h);let{html:_,head:v,css:y,serverData:b,renderMode:x,redirect:S,slots:C,locale:w,status:T,rewriteUrl:E}=await a(i,g);if(S)return e.redirect(S.url,S.status);if(x===`csr`)return e.html(F(n,w));let D=P({template:n,head:v,css:y,html:_,serializedData:l(b),slots:C,locale:w});return(x===`prerender`||d===`prerender`)&&!T&&!E&&u.set(i,D),E&&e.header(`Content-Location`,E),T&&T>=400?e.html(D,T):e.html(D)}catch(t){return!r&&n&&n.ssrFixStacktrace(t),console.error(`[SSR Error]`,t),e.text(`Internal Server Error`,500)}}),l}function Z(){return{isDeno:globalThis.Deno!==void 0,isBun:globalThis.Bun!==void 0,isVercel:!!process.env.VERCEL,isProduction:process.env.NODE_ENV===`production`}}async function _t(e,t=0){if(globalThis.Deno!==void 0){let n=new URL(e);for(let e=0;e<t;e++)n=new URL(`..`,n);return n.pathname}let n=await I(`node:path`),{fileURLToPath:r}=await I(`node:url`),i=n.normalize(n.dirname(r(e)));for(let e=0;e<t;e++)i=n.resolve(i,`..`);return i}async function vt(e){let{app:t,root:n,port:r=3e3,isProduction:i,vite:a,routes:o,ssrEntryPath:s}=e,{isDeno:c,isBun:l,isVercel:u}=e.runtime??Z();function d(){let e=[`\n Server running at http://localhost:${r}\n`];if(o&&o.length>0){e.push(` Routes:`);for(let t of o)e.push(` ${t}`);e.push(``)}s&&e.push(` SSR Entry: ${s}`),s&&e.push(``),console.log(e.join(`
251
- `))}if(u)return{vite:a};if(!i){let e=a;if(!e){let{createServer:t}=await I(`vite`);e=await t({root:n,server:{middlewareMode:!0},appType:`custom`})}let{getRequestListener:i}=await I(`@hono/node-server`),{createServer:o}=await I(`node:http`),s=i(t.fetch);return o((t,n)=>{e.middlewares(t,n,()=>s(t,n))}).listen(r,()=>{d()}),{vite:e}}if(c)globalThis.Deno.serve({port:r},t.fetch);else if(!l){let{serveStatic:e}=await I(`@hono/node-server/serve-static`),i=await I(`node:path`),a=new A,o=i.resolve(n,`dist/client`);a.use(`/*`,e({root:o,rewriteRequestPath:e=>e.endsWith(`/`)?`/__nosuchfile__`:e})),a.route(`/`,t);let{serve:s}=await I(`@hono/node-server`);s({fetch:a.fetch,port:r},()=>{d()})}return{vite:a}}async function yt(e={}){let{root:t,port:n=Number(process.env.PORT)||3e3,setup:r,proxies:i,ssr:a}=e,o=t??process.cwd(),{existsSync:s}=await I(`node:fs`),c=(await I(`node:path`)).resolve(o,`.env`);if(s(c))try{let{config:e}=await I(`dotenv`);e({path:c})}catch(e){console.warn(`[Server] Failed to load .env: ${e.message}`)}let l=Z(),u;if(!l.isProduction&&!l.isVercel){let{createServer:e}=await I(`vite`);u=await e({root:o,server:{middlewareMode:!0},appType:`custom`})}let d=new A;i?.length&&R(d,i),r&&await r(d);let f=X({root:o,vite:u,isProduction:l.isProduction,parentFetch:d.fetch.bind(d),...a});return d.route(`/`,f),await vt({app:d,root:o,port:n,isProduction:l.isProduction,vite:u,runtime:l,ssrEntryPath:a?.ssrEntryPath}),{app:d,vite:u,runtime:l}}function bt(e,t,n){let r=t??[`zh`,`en`],i=n??r[0]??`en`;if(!e||e.length>1024)return i;let a=e.split(`,`);if(a.length>50)return i;let o=a.map(e=>{let[t,n]=e.trim().split(`;q=`),r=n?parseFloat(n):1;return{lang:t.trim().toLowerCase(),q:Number.isFinite(r)&&r>=0&&r<=1?r:0}}).filter(e=>e.q>0).sort((e,t)=>t.q-e.q);for(let{lang:e}of o){let t=e.split(`-`)[0];if(r.includes(t))return t}return i}const Q=`virtual:finesoft-front/i18n-loader`,$=`\0${Q}`;function xt(e){return typeof e.default==`function`?e.default:typeof e.setup==`function`?e.setup:Object.values(e).find(e=>typeof e==`function`)??null}function St(e,t){if(!t)return null;let n=e.split(`?`)[0];if(t[n])return t[n];for(let[e,r]of Object.entries(t))if(e.includes(`*`)){let t=e.replace(/[.+?^${}()|[\]\\]/g,`\\$&`);if(RegExp(`^`+t.replace(/\*/g,`.*`)+`$`).test(n))return r}return null}function Ct(e){return e.replace(/\\/g,`/`)}async function wt(e,t){let{existsSync:n}=await I(`node:fs`),r=await I(`node:path`),i=r.isAbsolute(t)?t:r.resolve(e,t);if(!n(i))throw Error(`[finesoftFrontViteConfig] i18n.messagesDir not found: ${t}`);let a=Ct(r.relative(e,i));if(!a||a.startsWith(`..`))throw Error(`[finesoftFrontViteConfig] i18n.messagesDir must stay inside the project root: ${t}`);return a.replace(/^\.\/+/,``)}function Tt(e={}){let t=e.ssr?.entry??`src/ssr.ts`,n=process.cwd(),r,i,a,o=/\.(css|scss|less|sass|styl|stylus|pcss|postcss)($|\?)/;return{name:`finesoft-front`,config(t){let n={appType:`custom`,define:{__FINESOFT_I18N_LOADER_SPECIFIER__:e.i18n?.messagesDir?JSON.stringify(Q):`undefined`}};return process.env.__FINESOFT_SUB_BUILD__||(n.build={outDir:t.build?.outDir??`dist/client`}),n},configResolved(e){r=e.command,i=e.resolve,a=e.css,n=e.root},resolveId(t){return t===Q&&e.i18n?.messagesDir?$:null},async load(t){if(t!==$||!e.i18n?.messagesDir)return null;let r=`/${await wt(n,e.i18n.messagesDir)}`,i=`${r}/*.json`,a=`${r}/`;return`
246
+ `)}}}function Pt(e={}){return{name:`static`,async build(t){let{fs:n,path:r,root:i}=t,a=r.resolve(i,`dist/static`);n.rmSync(a,{recursive:!0,force:!0}),n.mkdirSync(a,{recursive:!0});let{pathToFileURL:o}=await H(`node:url`),s=o(r.resolve(i,`dist/server/ssr.js`)).href,c=await H(s);t.copyStaticAssets(a,{excludeHtml:!0});let{paths:l,defs:u}=await Ft(t,e);console.log(` Pre-rendering ${l.length} pages...\n`);for(let e of l)try{let i=Rt(e,u.find(t=>t.path===e)?.renderMode,t.renderModes),o;if(i===`csr`)o=Lt(t.templateHtml);else{let{html:n,head:r,css:i,serverData:a}=await c.render(e),s=c.serializeServerData(a);o=It(t.templateHtml,r,i,n,s)}let s=e===`/`?r.join(a,`index.html`):r.join(a,e,`index.html`);n.mkdirSync(r.resolve(s,`..`),{recursive:!0}),n.writeFileSync(s,o)}catch(t){console.warn(` [static] Failed to render ${e}:`,t)}console.log(` Static output → dist/static/
247
+ `)}}}async function Ft(e,t){let n=t.routesExport??`src/lib/bootstrap.ts`,r=[],i=[];try{let{pathToFileURL:t}=await H(`node:url`);await e.vite.build({root:e.root,build:{ssr:n,outDir:e.path.resolve(e.root,`dist/server`),emptyOutDir:!1,rollupOptions:{output:{entryFileNames:`_routes.mjs`}}},resolve:e.resolvedResolve});let a=t(e.path.resolve(e.root,`dist/server/_routes.mjs`)).href,o=await H(a),s=o.routes??o.default;if(Array.isArray(s))for(let e of s)e.path&&!e.path.includes(`:`)&&(r.push(e.path),i.push({path:e.path,renderMode:e.renderMode}));e.fs.rmSync(e.path.resolve(e.root,`dist/server/_routes.mjs`),{force:!0})}catch(e){console.warn(` [static] Could not load routes from "${n}". Using "/" only.`,e),r.length===0&&r.push(`/`)}if(t.dynamicRoutes)for(let e of t.dynamicRoutes)r.includes(e)||r.push(e);return r.length===0&&r.push(`/`),{paths:r,defs:i}}function It(e,t,n,r,i){let a=/<!--ssr-([a-z][a-z0-9-]*)-->/g,o={head:t+`
248
+ <style>`+n+`</style>`,body:r,data:`<script id="serialized-server-data" type="application/json">`+i+`<\/script>`};return e.replace(a,(e,t)=>o[t]??``)}function Lt(e){return e.replace(/<!--ssr-([a-z][a-z0-9-]*)-->/g,()=>``)}function Rt(e,t,n){if(n){if(n[e])return n[e];for(let[t,r]of Object.entries(n))if(t.includes(`*`)){let n=t.replace(/[.+?^${}()|[\]\\]/g,`\\$&`);if(RegExp(`^`+n.replace(/\*/g,`.*`)+`$`).test(e))return r}}return t??`ssr`}function zt(){return{name:`vercel`,async build(e){let{fs:t,path:n,root:r}=e,i=n.resolve(r,`.vercel/output`);t.rmSync(i,{recursive:!0,force:!0});let a=G(e,{platformImport:`import { getRequestListener } from "@hono/node-server";`,platformExport:[`const _listener = getRequestListener(app.fetch);`,`export default (req, res) => {`,` const m = req.headers["x-now-route-matches"];`,` if (typeof m === "string") {`,` try {`,` const p = new URLSearchParams(m);`,` const c = p.get("1");`,` if (c != null) {`,` const qi = (req.url || "").indexOf("?");`,` const qs = qi !== -1 ? req.url.slice(qi) : "";`,` req.url = "/" + decodeURIComponent(c) + qs;`,` }`,` } catch {}`,` }`,` return _listener(req, res);`,`};`].join(`
249
+ `)}),o=n.resolve(r,`.vercel-entry.tmp.mjs`);t.writeFileSync(o,a);try{let i=n.resolve(r,`.vercel/output/functions/ssr.func`);await K(e,{entry:`.vercel-entry.tmp.mjs`,outDir:i,target:`node18`}),t.writeFileSync(n.resolve(i,`.vc-config.json`),JSON.stringify({runtime:`nodejs20.x`,handler:`index.mjs`,launcherType:`Nodejs`},null,2)),q(e,n.resolve(r,`.vercel/output/static`)),t.writeFileSync(n.resolve(r,`.vercel/output/config.json`),JSON.stringify({version:3,routes:[{handle:`filesystem`},{src:`/(.*)`,dest:`/ssr`}]},null,2))}finally{t.rmSync(o,{force:!0})}let s=await J(e),c=n.resolve(r,`.vercel/output/static`);for(let{url:e,html:r}of s){let i=e===`/`?n.join(c,`index.html`):n.join(c,e,`index.html`);t.mkdirSync(n.resolve(i,`..`),{recursive:!0}),t.writeFileSync(i,r)}if(s.length>0){let e=n.resolve(r,`.vercel/output/config.json`),i=JSON.parse(t.readFileSync(e,`utf-8`));i.overrides=i.overrides??{};for(let{url:e}of s){let t=e===`/`?`index.html`:`${e.replace(/^\//,``)}/index.html`;i.overrides[t]={path:e===`/`?`/`:e,contentType:`text/html; charset=utf-8`}}t.writeFileSync(e,JSON.stringify(i,null,2))}console.log(` Vercel output → .vercel/output/
250
+ `)}}}function Y(e){if(typeof e!=`string`)return e;switch(e){case`vercel`:return zt();case`cloudflare`:return jt();case`netlify`:return Mt();case`node`:return Nt();case`static`:return Pt();case`auto`:return Bt();default:throw Error(`[finesoft] Unknown adapter: "${e}". Available: vercel, cloudflare, netlify, node, static, auto`)}}function Bt(){return{name:`auto`,async build(e){let t=Vt();return console.log(` [auto] Detected platform: ${t}\n`),Y(t).build(e)}}}function Vt(){return process.env.VERCEL?`vercel`:process.env.CF_PAGES?`cloudflare`:process.env.NETLIFY?`netlify`:`node`}function Ht(e,t=1){return((n,r)=>{if(typeof n==`string`&&n.startsWith(`/`)){let i=new Request(`http://localhost${n}`,r);return i.headers.set(`x-ssr-depth`,String(t)),Promise.resolve(e(i))}return globalThis.fetch(n,r)})}function Ut(e,t){if(!t)return null;let n=e.split(`?`)[0];if(t[n])return t[n];for(let[e,r]of Object.entries(t))if(e.includes(`*`)){let t=e.replace(/[.+?^${}()|[\]\\]/g,`\\$&`);if(RegExp(`^`+t.replace(/\*/g,`.*`)+`$`).test(n))return r}return null}function X(e){let{root:t,vite:n,isProduction:r,ssrEntryPath:i=`/src/ssr.ts`,ssrProductionModule:a,parentFetch:s,renderModes:c,defaultLocale:l}=e,u=new j,d=new T(1e3),f;async function p(e){if(!r&&n){let{readFileSync:r}=await H(`node:fs`),i=r((await H(`node:path`)).resolve(t,`index.html`),`utf-8`);return n.transformIndexHtml(e,i)}if(f)return f;if(globalThis.Deno!==void 0){let e=import.meta.url;return f=globalThis.Deno.readTextFileSync(new URL(`../dist/client/index.html`,e)),f}let{readFileSync:i}=await H(`node:fs`);return f=i((await H(`node:path`)).resolve(t,`dist/client/index.html`),`utf-8`),f}async function m(){if(!r&&n)return await n.ssrLoadModule(i);if(a)return H(a);let e=await H(`node:path`),{pathToFileURL:o}=await H(`node:url`),s=o(e.resolve(t,`dist/server/ssr.js`)).href;return H(s)}return u.get(`*`,async e=>{let t=parseInt(e.req.header(`x-ssr-depth`)??`0`,10);if(t>=5)return e.text(`SSR recursion loop detected`,508);let i=e.req.path+(e.req.url.includes(`?`)?`?`+e.req.url.split(`?`)[1]:``);try{let n=await p(i),r=await m();if(typeof r.render!=`function`||typeof r.serializeServerData!=`function`)throw Error(`[SSR] Module missing required exports: render, serializeServerData`);let{render:a,serializeServerData:u}=r,f=Ut(i,c);if(f===`csr`)return e.html(L(n,l?o(l):void 0));let h=d.get(i);if(h)return e.html(h);let g=s?Ht(s,t+1):void 0,_={request:e.req.raw};g&&(_.fetch=g);let{html:v,head:y,css:b,serverData:x,renderMode:S,redirect:C,slots:ee,locale:w,status:T,rewriteUrl:E}=await a(i,_);if(C)return e.redirect(C.url,C.status);if(S===`csr`)return e.html(L(n,w));let D=I({template:n,head:y,css:b,html:v,serializedData:u(x),slots:ee,locale:w});return(S===`prerender`||f===`prerender`)&&!T&&!E&&d.set(i,D),E&&e.header(`Content-Location`,E),T&&T>=400?e.html(D,T):e.html(D)}catch(t){return!r&&n&&n.ssrFixStacktrace(t),console.error(`[SSR Error]`,t),e.text(`Internal Server Error`,500)}}),u}function Z(){return{isDeno:globalThis.Deno!==void 0,isBun:globalThis.Bun!==void 0,isVercel:!!process.env.VERCEL,isProduction:process.env.NODE_ENV===`production`}}async function Wt(e,t=0){if(globalThis.Deno!==void 0){let n=new URL(e);for(let e=0;e<t;e++)n=new URL(`..`,n);return n.pathname}let n=await H(`node:path`),{fileURLToPath:r}=await H(`node:url`),i=n.normalize(n.dirname(r(e)));for(let e=0;e<t;e++)i=n.resolve(i,`..`);return i}async function Gt(e){let{app:t,root:n,port:r=3e3,isProduction:i,vite:a,routes:o,ssrEntryPath:s}=e,{isDeno:c,isBun:l,isVercel:u}=e.runtime??Z();function d(){let e=[`\n Server running at http://localhost:${r}\n`];if(o&&o.length>0){e.push(` Routes:`);for(let t of o)e.push(` ${t}`);e.push(``)}s&&e.push(` SSR Entry: ${s}`),s&&e.push(``),console.log(e.join(`
251
+ `))}if(u)return{vite:a};if(!i){let e=a;if(!e){let{createServer:t}=await H(`vite`);e=await t({root:n,server:{middlewareMode:!0},appType:`custom`})}let{getRequestListener:i}=await H(`@hono/node-server`),{createServer:o}=await H(`node:http`),s=i(t.fetch);return o((t,n)=>{e.middlewares(t,n,()=>s(t,n))}).listen(r,()=>{d()}),{vite:e}}if(c)globalThis.Deno.serve({port:r},t.fetch);else if(!l){let{serveStatic:e}=await H(`@hono/node-server/serve-static`),i=await H(`node:path`),a=new j,o=i.resolve(n,`dist/client`);a.use(`/*`,e({root:o,rewriteRequestPath:e=>e.endsWith(`/`)?`/__nosuchfile__`:e})),a.route(`/`,t);let{serve:s}=await H(`@hono/node-server`);s({fetch:a.fetch,port:r},()=>{d()})}return{vite:a}}async function Kt(e={}){let{root:t,port:n=Number(process.env.PORT)||3e3,setup:r,proxies:i,ssr:a}=e,o=t??process.cwd(),{existsSync:s}=await H(`node:fs`),c=(await H(`node:path`)).resolve(o,`.env`);if(s(c))try{let{config:e}=await H(`dotenv`);e({path:c})}catch(e){console.warn(`[Server] Failed to load .env: ${e.message}`)}let l=Z(),u;if(!l.isProduction&&!l.isVercel){let{createServer:e}=await H(`vite`);u=await e({root:o,server:{middlewareMode:!0},appType:`custom`})}let d=new j;i?.length&&W(d,i),r&&await r(d);let f=X({root:o,vite:u,isProduction:l.isProduction,parentFetch:d.fetch.bind(d),...a});return d.route(`/`,f),await Gt({app:d,root:o,port:n,isProduction:l.isProduction,vite:u,runtime:l,ssrEntryPath:a?.ssrEntryPath}),{app:d,vite:u,runtime:l}}function qt(e,t,n){let r=t??[`zh`,`en`],i=n??r[0]??`en`;if(!e||e.length>1024)return i;let a=e.split(`,`);if(a.length>50)return i;let o=a.map(e=>{let[t,n]=e.trim().split(`;q=`),r=n?parseFloat(n):1;return{lang:t.trim().toLowerCase(),q:Number.isFinite(r)&&r>=0&&r<=1?r:0}}).filter(e=>e.q>0).sort((e,t)=>t.q-e.q);for(let{lang:e}of o){let t=e.split(`-`)[0];if(r.includes(t))return t}return i}const Q=`virtual:finesoft-front/i18n-loader`,Jt=`\0${Q}`;function Yt(e){return typeof e.default==`function`?e.default:typeof e.setup==`function`?e.setup:Object.values(e).find(e=>typeof e==`function`)??null}function Xt(e,t){if(!t)return null;let n=e.split(`?`)[0];if(t[n])return t[n];for(let[e,r]of Object.entries(t))if(e.includes(`*`)){let t=e.replace(/[.+?^${}()|[\]\\]/g,`\\$&`);if(RegExp(`^`+t.replace(/\*/g,`.*`)+`$`).test(n))return r}return null}function Zt(e){return e.replace(/\\/g,`/`)}let $=!1;function Qt(){$||($=!0,console.log(`\n${(e=>`\x1b[33m${e}\x1b[0m`)(`[finesoft]`)} dev server is exposing source via /src/* and /@fs/* (HMR).\n`+(e=>`\x1b[2m${e}\x1b[0m`)(" Do NOT expose this server to untrusted networks; run `vp build && vp preview` for production.\n")))}async function $t(e,t){let{existsSync:n}=await H(`node:fs`),r=await H(`node:path`),i=r.isAbsolute(t)?t:r.resolve(e,t);if(!n(i))throw Error(`[finesoftFrontViteConfig] i18n.messagesDir not found: ${t}`);let a=Zt(r.relative(e,i));if(!a||a.startsWith(`..`))throw Error(`[finesoftFrontViteConfig] i18n.messagesDir must stay inside the project root: ${t}`);return a.replace(/^\.\/+/,``)}function en(e={}){let t=e.ssr?.entry??`src/ssr.ts`,n=process.cwd(),r,i,a,s=/\.(css|scss|less|sass|styl|stylus|pcss|postcss)($|\?)/;return{name:`finesoft-front`,config(t){let n={appType:`custom`,define:{__FINESOFT_I18N_LOADER_SPECIFIER__:e.i18n?.messagesDir?JSON.stringify(Q):`undefined`}};return process.env.__FINESOFT_SUB_BUILD__||(n.build={outDir:t.build?.outDir??`dist/client`}),n},configResolved(e){r=e.command,i=e.resolve,a=e.css,n=e.root},resolveId(t){return t===Q&&e.i18n?.messagesDir?Jt:null},async load(t){if(t!==Jt||!e.i18n?.messagesDir)return null;let r=`/${await $t(n,e.i18n.messagesDir)}`,i=`${r}/*.json`,a=`${r}/`;return`
252
252
  const localeModules = import.meta.glob(${JSON.stringify(i)}, { import: "default" });
253
253
 
254
254
  export async function loadMessages(locale) {
@@ -257,8 +257,8 @@ export async function loadMessages(locale) {
257
257
  const messages = await loader();
258
258
  return messages ?? undefined;
259
259
  }
260
- `},transformIndexHtml:{order:`pre`,async handler(e,t){let n=t.server;if(!n)return;let r=(t.originalUrl||t.path||``).split(`?`)[0];if(/\.\w+$/.test(r)&&!r.endsWith(`.html`))return;let i=[...e.matchAll(/<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/g)].find(e=>!e[1].startsWith(`/@`));if(!i)return;let a=i[1];try{await n.transformRequest(a)}catch{return}let s=[],c=new Set;function l(e,t=0){if(!(t>100)&&!(!e?.url||c.has(e.url))&&(c.add(e.url),o.test(e.url)&&!e.url.includes(`.svelte`)&&s.push(e.url),e.importedModules))for(let n of e.importedModules)l(n,t+1)}let u=await n.moduleGraph.getModuleByUrl(a);if(u&&l(u),s.length===0)return;let d=[];for(let e of s)try{let t=(await n.ssrLoadModule(e))?.default;typeof t==`string`&&t.length>0&&d.push({tag:`style`,attrs:{"data-vite-dev-id":e},children:t,injectTo:`head`})}catch{}return d}},configureServer(r){return async()=>{let{Hono:i}=await I(`hono`),{getRequestListener:a}=await I(`@hono/node-server`),o=new i;if(e.proxies?.length&&R(o,e.proxies),typeof e.setup==`function`)await e.setup(o);else if(typeof e.setup==`string`){let t=xt(await r.ssrLoadModule(`/`+e.setup));t&&await t(o)}let s=X({root:n,vite:r,isProduction:!1,ssrEntryPath:`/`+t,parentFetch:o.fetch.bind(o),renderModes:e.renderModes,defaultLocale:e.defaultLocale});o.route(`/`,s);let c=a(o.fetch);r.middlewares.use((e,t)=>{c(e,t)})}},configurePreviewServer(t){return async()=>{let{readFileSync:r}=await I(`node:fs`),i=await I(`node:path`),{pathToFileURL:a}=await I(`node:url`),{Hono:o}=await I(`hono`),{getRequestListener:s}=await I(`@hono/node-server`),c=new o,l=new h(1e3);if(e.proxies?.length&&R(c,e.proxies),typeof e.setup==`function`)await e.setup(c);else if(typeof e.setup==`string`)try{let e=a(i.resolve(n,`dist/server/setup.mjs`)).href,t=xt(await I(e));t&&await t(c)}catch{console.warn(`[finesoft] Could not load setup module for preview. API routes disabled.`)}let u=r(i.resolve(n,`dist/client/index.html`),`utf-8`),d=a(i.resolve(n,`dist/server/ssr.js`)).href,f=await I(d);c.get(`*`,async t=>{let n=parseInt(t.req.header(`x-ssr-depth`)??`0`,10);if(n>=5)return t.text(`SSR recursion loop detected`,508);let r=t.req.path+(t.req.url.includes(`?`)?`?`+t.req.url.split(`?`)[1]:``);try{let i=St(r,e.renderModes);if(i===`csr`)return t.html(F(u,e.defaultLocale?O(e.defaultLocale):void 0));let a=l.get(r);if(a)return t.html(a);let{html:o,head:s,css:d,serverData:p,renderMode:m,redirect:h,slots:g,locale:_,status:v,rewriteUrl:y}=await f.render(r,{fetch:ht(c.fetch.bind(c),n+1)});if(h)return t.redirect(h.url,h.status);if(m===`csr`)return t.html(F(u,_));let b=P({template:u,head:s,css:d,html:o,serializedData:f.serializeServerData(p),slots:g,locale:_});return(m===`prerender`||i===`prerender`)&&!v&&!y&&l.set(r,b),y&&t.header(`Content-Location`,y),v&&v>=400?t.html(b,v):t.html(b)}catch(e){return console.error(`[SSR Preview Error]`,e),t.text(`Internal Server Error`,500)}});let p=s(c.fetch);t.middlewares.use((e,t)=>{p(e,t)})}},async closeBundle(){if(!process.env.__FINESOFT_SUB_BUILD__&&r===`build`){process.env.__FINESOFT_SUB_BUILD__=`1`;try{let r=await I(`vite`),o=await I(`node:fs`),s=await I(`node:path`);if(console.log(`
260
+ `},transformIndexHtml:{order:`pre`,async handler(e,t){let n=t.server;if(!n)return;let r=(t.originalUrl||t.path||``).split(`?`)[0];if(/\.\w+$/.test(r)&&!r.endsWith(`.html`))return;let i=[...e.matchAll(/<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/g)].find(e=>!e[1].startsWith(`/@`));if(!i)return;let a=i[1];try{await n.transformRequest(a)}catch{return}let o=[],c=new Set;function l(e,t=0){if(!(t>100)&&!(!e?.url||c.has(e.url))&&(c.add(e.url),s.test(e.url)&&!e.url.includes(`.svelte`)&&o.push(e.url),e.importedModules))for(let n of e.importedModules)l(n,t+1)}let u=await n.moduleGraph.getModuleByUrl(a);if(u&&l(u),o.length===0)return;let d=[];for(let e of o)try{let t=(await n.ssrLoadModule(e))?.default;typeof t==`string`&&t.length>0&&d.push({tag:`style`,attrs:{"data-vite-dev-id":e},children:t,injectTo:`head`})}catch{}return d}},configureServer(r){return async()=>{let{Hono:i}=await H(`hono`),{getRequestListener:a}=await H(`@hono/node-server`);Qt();let o=new i;if(e.proxies?.length&&W(o,e.proxies),typeof e.setup==`function`)await e.setup(o);else if(typeof e.setup==`string`){let t=Yt(await r.ssrLoadModule(`/`+e.setup));t&&await t(o)}let s=X({root:n,vite:r,isProduction:!1,ssrEntryPath:`/`+t,parentFetch:o.fetch.bind(o),renderModes:e.renderModes,defaultLocale:e.defaultLocale});o.route(`/`,s);let c=a(o.fetch);r.middlewares.use((e,t)=>{c(e,t)})}},configurePreviewServer(t){return async()=>{let{readFileSync:r}=await H(`node:fs`),i=await H(`node:path`),{pathToFileURL:a}=await H(`node:url`),{Hono:s}=await H(`hono`),{getRequestListener:c}=await H(`@hono/node-server`),l=new s,u=new T(1e3);if(e.proxies?.length&&W(l,e.proxies),typeof e.setup==`function`)await e.setup(l);else if(typeof e.setup==`string`)try{let e=a(i.resolve(n,`dist/server/setup.mjs`)).href,t=Yt(await H(e));t&&await t(l)}catch{console.warn(`[finesoft] Could not load setup module for preview. API routes disabled.`)}let d=r(i.resolve(n,`dist/client/index.html`),`utf-8`),f=a(i.resolve(n,`dist/server/ssr.js`)).href,p=await H(f);l.get(`*`,async t=>{let n=parseInt(t.req.header(`x-ssr-depth`)??`0`,10);if(n>=5)return t.text(`SSR recursion loop detected`,508);let r=t.req.path+(t.req.url.includes(`?`)?`?`+t.req.url.split(`?`)[1]:``);try{let i=Xt(r,e.renderModes);if(i===`csr`)return t.html(L(d,e.defaultLocale?o(e.defaultLocale):void 0));let a=u.get(r);if(a)return t.html(a);let{html:s,head:c,css:f,serverData:m,renderMode:h,redirect:g,slots:_,locale:v,status:y,rewriteUrl:b}=await p.render(r,{fetch:Ht(l.fetch.bind(l),n+1)});if(g)return t.redirect(g.url,g.status);if(h===`csr`)return t.html(L(d,v));let x=I({template:d,head:c,css:f,html:s,serializedData:p.serializeServerData(m),slots:_,locale:v});return(h===`prerender`||i===`prerender`)&&!y&&!b&&u.set(r,x),b&&t.header(`Content-Location`,b),y&&y>=400?t.html(x,y):t.html(x)}catch(e){return console.error(`[SSR Preview Error]`,e),t.text(`Internal Server Error`,500)}});let m=c(l.fetch);t.middlewares.use((e,t)=>{m(e,t)})}},async closeBundle(){if(!process.env.__FINESOFT_SUB_BUILD__&&r===`build`){process.env.__FINESOFT_SUB_BUILD__=`1`;try{let r=await H(`vite`),o=await H(`node:fs`),s=await H(`node:path`);if(console.log(`
261
261
  Building SSR bundle...
262
- `),await r.build({root:n,build:{ssr:t,outDir:`dist/server`},ssr:{external:B},resolve:i,css:a}),typeof e.setup==`string`&&(console.log(` Building setup module...
263
- `),await r.build({root:n,build:{ssr:e.setup,outDir:`dist/server`,emptyOutDir:!1,rollupOptions:{output:{entryFileNames:`setup.mjs`}}},resolve:i})),e.adapter){let c=Y(e.adapter),l=o.readFileSync(s.resolve(n,`dist/client/index.html`),`utf-8`),u={root:n,ssrEntry:t,setupPath:typeof e.setup==`string`?e.setup:void 0,bootstrapEntry:e.bootstrapEntry,templateHtml:l,renderModes:e.renderModes,proxies:e.proxies,locales:e.locales,defaultLocale:e.defaultLocale,resolvedResolve:i,resolvedCss:a,vite:r,fs:o,path:s,generateSSREntry(e){return V(u,e)},buildBundle(e){return H(u,e)},copyStaticAssets(e,t){return U(u,e,t)}};console.log(` Running adapter: ${c.name}...\n`),await c.build(u)}}finally{delete process.env.__FINESOFT_SUB_BUILD__}}}}}export{pe as ACTION_KINDS,he as ActionDispatcher,m as BaseController,_ as BaseLogger,ue as CompositeEventRecorder,e as CompositeLogger,le as CompositeLoggerFactory,s as ConsoleEventRecorder,u as ConsoleLogger,T as ConsoleLoggerFactory,ke as Container,c as DEP_KEYS,l as Framework,Ee as History,g as HttpClient,o as HttpError,Ce as IntentDispatcher,se as IntersectionImpressionObserver,h as LruMap,f as PrefetchedIntents,d as ReportingLogger,Ae as ReportingLoggerFactory,S as Router,Je as SSR_PLACEHOLDERS,Fe as SimpleTranslator,Ie as VoidEventRecorder,be as WithFieldsRecorder,pt as autoAdapter,re as buildUrl,G as cloudflareAdapter,D as createBrowserContext,we as createPrefetchedIntentsFromDom,X as createSSRApp,qe as createSSRRender,yt as createServer,k as createServerContext,ye as defineRoutes,Oe as deny,je as deserializeServerData,C as detectPlatform,Z as detectRuntime,Te as englishPlural,Tt as finesoftFrontViteConfig,z as generateProxyCode,He as generateUuid,Be as getBaseUrl,O as getLocaleAttributes,b as getPWADisplayMode,oe as getTextDirection,F as injectCSRShell,P as injectSSRContent,Me as interpolate,ee as isCompoundAction,Re as isExternalUrlAction,Ue as isFlowAction,a as isNone,xe as isRtl,i as isSome,x as makeDependencies,ie as makeExternalUrlAction,Ve as makeFlowAction,Le as makeLocaleInfo,t as mapEach,K as netlifyAdapter,Se as next,q as nodeAdapter,bt as parseAcceptLanguage,ve as pipe,p as pipeAsync,me as redirect,ge as registerActionHandlers,ae as registerExternalUrlHandler,te as registerFlowActionHandler,R as registerProxyRoutes,y as removeHost,r as removeQueryParams,ze as removeScheme,w as resetFilterCache,Y as resolveAdapter,ne as resolveConfiguredMessages,ce as resolveLocaleFromUrl,De as resolveMessages,_e as resolvePluralKey,_t as resolveRoot,fe as rewrite,We as runAfterLoadGuards,n as runBeforeLoadGuards,et as serializeServerData,de as setHtmlLocaleAttributes,E as shouldLog,Ge as ssrRender,v as stableStringify,Pe as startBrowserApp,vt as startServer,J as staticAdapter,Ne as tryScroll,ft as vercelAdapter};
262
+ `),await r.build({root:n,build:{ssr:t,outDir:`dist/server`},ssr:{external:At},resolve:i,css:a}),typeof e.setup==`string`&&(console.log(` Building setup module...
263
+ `),await r.build({root:n,build:{ssr:e.setup,outDir:`dist/server`,emptyOutDir:!1,rollupOptions:{output:{entryFileNames:`setup.mjs`}}},resolve:i})),e.adapter){let c=Y(e.adapter),l=o.readFileSync(s.resolve(n,`dist/client/index.html`),`utf-8`),u={root:n,ssrEntry:t,setupPath:typeof e.setup==`string`?e.setup:void 0,bootstrapEntry:e.bootstrapEntry,templateHtml:l,renderModes:e.renderModes,proxies:e.proxies,locales:e.locales,defaultLocale:e.defaultLocale,resolvedResolve:i,resolvedCss:a,vite:r,fs:o,path:s,generateSSREntry(e){return G(u,e)},buildBundle(e){return K(u,e)},copyStaticAssets(e,t){return q(u,e,t)}};console.log(` Running adapter: ${c.name}...\n`),await c.build(u)}}finally{delete process.env.__FINESOFT_SUB_BUILD__}}}}}export{ie as ACTION_KINDS,x as ActionDispatcher,g as BASE_PAGE_FIELDS,D as BaseController,He as BaseLogger,Oe as CompositeEventRecorder,Ne as CompositeLogger,je as CompositeLoggerFactory,$e as ConsoleEventRecorder,rt as ConsoleLogger,Ee as ConsoleLoggerFactory,f as Container,O as DEP_KEYS,b as FINESOFT_PUBLIC,de as Framework,Ge as History,n as HostGuardError,Ie as HttpClient,C as HttpError,_ as IntentDispatcher,Te as IntersectionImpressionObserver,T as LruMap,m as PrefetchedIntents,we as ReportingLogger,Be as ReportingLoggerFactory,De as Router,mt as SSR_PLACEHOLDERS,ve as SimpleTranslator,nt as VoidEventRecorder,ze as WithFieldsRecorder,Bt as autoAdapter,v as bool,xe as buildUrl,w as classifyHost,te as classifyUrl,jt as cloudflareAdapter,_e as createBrowserContext,Ue as createPrefetchedIntentsFromDom,X as createSSRApp,pt as createSSRRender,Kt as createServer,k as createServerContext,Ze as defineRequestScopedKey,Le as defineRoutes,qe as deny,Xe as deserializeServerData,Ke as detectPlatform,Z as detectRuntime,it as englishPlural,en as finesoftFrontViteConfig,Ot as generateProxyCode,ct as generateUuid,st as getBaseUrl,o as getLocaleAttributes,se as getPWADisplayMode,re as getPublicFields,ot as getTextDirection,L as injectCSRShell,I as injectSSRContent,he as int,lt as interpolate,dt as isCompoundAction,i as isExternalUrlAction,fe as isFlowAction,l as isNone,ut as isPublicMarked,ce as isRtl,s as isSome,be as makeDependencies,h as makeExternalUrlAction,ue as makeFlowAction,u as makeLocaleInfo,tt as makeSchema,Fe as mapEach,r as markPublic,Mt as netlifyAdapter,Ve as next,Nt as nodeAdapter,me as num,ge as oneOf,y as optional,qt as parseAcceptLanguage,S as pipe,ee as pipeAsync,Me as redirect,Pe as registerActionHandlers,Ce as registerExternalUrlHandler,ye as registerFlowActionHandler,W as registerProxyRoutes,ae as removeHost,a as removeQueryParams,at as removeScheme,ke as resetFilterCache,Y as resolveAdapter,A as resolveConfiguredMessages,c as resolveLocaleFromUrl,oe as resolveMessages,Se as resolvePluralKey,Wt as resolveRoot,Ae as rewrite,t as route,pe as runAfterLoadGuards,p as runBeforeLoadGuards,We as runStandard,d as safeErrorPage,Re as secureFetch,yt as serializeServerData,E as setHtmlLocaleAttributes,Je as shouldLog,M as ssrRender,le as stableStringify,et as startBrowserApp,Gt as startServer,Pt as staticAdapter,ne as str,Qe as tryScroll,e as uuid,zt as vercelAdapter,Ye as withDefault};
264
264
  //# sourceMappingURL=index.mjs.map
@@ -72,6 +72,16 @@ declare class Container {
72
72
  resolve<T>(key: string): T;
73
73
  /** 检查是否已注册(含 parent) */
74
74
  has(key: string): boolean;
75
+ /**
76
+ * 移除当前容器的注册(不影响 parent)。
77
+ *
78
+ * 用途:在 scope 内显式撤销之前覆写的依赖,避免用 `register(() => null)` 这种
79
+ * 反语义的写法。被移除的 key 之后再 resolve 会回退到 parent 容器。
80
+ *
81
+ * 返回 true 表示当前层确实存在过这个注册并被移除,false 表示未注册(含「只在
82
+ * parent 注册」的情况,本方法不向上递归删除 —— scope 不应能影响 parent 状态)。
83
+ */
84
+ unregister(key: string): boolean;
75
85
  /**
76
86
  * 创建子容器(请求级 scope)
77
87
  *
@@ -95,8 +105,8 @@ declare class Container {
95
105
  interface Intent<T = unknown> {
96
106
  /** Intent 标识符(用于匹配 Controller) */
97
107
  id: string;
98
- /** 意图参数 */
99
- params?: Record<string, string>;
108
+ /** 意图参数(path/query 经 codec 转换后可能是 number/boolean 等) */
109
+ params?: Record<string, unknown>;
100
110
  /** 预期返回的数据(仅用于类型推断) */
101
111
  _returnType?: T;
102
112
  }
@@ -119,6 +129,45 @@ declare class IntentDispatcher {
119
129
  has(intentId: string): boolean;
120
130
  }
121
131
  //#endregion
132
+ //#region ../core/src/http/secure-fetch.d.ts
133
+ /**
134
+ * secureFetch — `fetch` with SSRF defense baked in.
135
+ *
136
+ * Wraps an existing fetch implementation and refuses targets that resolve to
137
+ * private / loopback / reserved ranges. Use when your controller fetches a
138
+ * URL that the user can influence (image proxies, link previews, callbacks).
139
+ *
140
+ * Defaults to checking IP literals (sync) AND resolving hostnames via DNS
141
+ * (Node only; silently skipped in browsers). Pass `allowInternalHosts: true`
142
+ * to opt out — for example, when you have a legitimate server-to-server call
143
+ * to 10.x or 127.0.0.1 and you trust the URL source.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * import { secureFetch, DEP_KEYS } from "@finesoft/front";
148
+ *
149
+ * class ShareController extends BaseController<{ next?: string }, Page> {
150
+ * async execute(params, container) {
151
+ * const baseFetch = container.resolve<typeof globalThis.fetch>(DEP_KEYS.FETCH);
152
+ * const fetch = secureFetch(baseFetch);
153
+ * const response = await fetch(params.next ?? "https://example.com");
154
+ * ...
155
+ * }
156
+ * }
157
+ * ```
158
+ */
159
+ interface SecureFetchOptions {
160
+ /** Opt out of SSRF defense entirely (default false). */
161
+ allowInternalHosts?: boolean;
162
+ /** DNS-resolve hostnames and check each resolved IP (default true, Node only). */
163
+ validateDns?: boolean;
164
+ }
165
+ /**
166
+ * Return a `fetch`-shaped function that refuses requests to private hosts
167
+ * before calling through to `baseFetch`.
168
+ */
169
+ declare function secureFetch(baseFetch: typeof globalThis.fetch, options?: SecureFetchOptions): typeof globalThis.fetch;
170
+ //#endregion
122
171
  //#region ../core/src/logger/types.d.ts
123
172
  /** 日志级别 */
124
173
  type Level = "debug" | "info" | "warn" | "error";
@@ -323,6 +372,12 @@ declare const DEP_KEYS: {
323
372
  readonly FEATURE_FLAGS: "featureFlags";
324
373
  readonly METRICS: "metrics";
325
374
  readonly FETCH: "fetch";
375
+ /**
376
+ * `fetch` 包了 SSRF 防护(拒绝 private / loopback / 保留 IP + DNS resolve 后逐 IP 校验)。
377
+ * 当 controller 用用户可控的 URL 发起请求(图片代理、链接预览、回调等),
378
+ * 优先 resolve 这个 key 而不是 `FETCH`。要 opt-out 可手动调 `secureFetch(baseFetch, { allowInternalHosts: true })`。
379
+ */
380
+ readonly SAFE_FETCH: "safeFetch";
326
381
  readonly EVENT_RECORDER: "eventRecorder";
327
382
  readonly LOCALE: "locale";
328
383
  readonly PLATFORM: "platform";
@@ -341,14 +396,51 @@ interface MakeDependenciesOptions {
341
396
  locale?: string;
342
397
  /** 自定义 PlatformInfo(默认通过 UA 自动检测) */
343
398
  platform?: PlatformInfo;
399
+ /**
400
+ * 覆盖 `DEP_KEYS.SAFE_FETCH` 默认行为。比如服务正常需要打内网,可以传
401
+ * `{ allowInternalHosts: true }` 整体放行;或自定义 DNS 校验策略。
402
+ */
403
+ safeFetch?: SecureFetchOptions;
344
404
  }
345
405
  declare function makeDependencies(container: Container, options?: MakeDependenciesOptions): void;
346
406
  //#endregion
407
+ //#region ../core/src/dependencies/request-scoped-key.d.ts
408
+ /** Anything that exposes the per-request DI container — covers NavigationContext, PostLoadContext, and raw Container. */
409
+ type ContainerHolder = Container | {
410
+ container: Container;
411
+ };
412
+ interface RequestScopedKey<T> {
413
+ /** The underlying string key registered with the Container. */
414
+ readonly key: string;
415
+ /** Write the current request's value. Overwrites any prior registration. */
416
+ set(target: ContainerHolder, value: T): void;
417
+ /** Read the current request's value, or `undefined` if not set. */
418
+ get(target: ContainerHolder): T | undefined;
419
+ /**
420
+ * Remove the registration from this request's container. Subsequent `get`
421
+ * calls return `undefined`. Returns true if the key existed.
422
+ */
423
+ clear(target: ContainerHolder): boolean;
424
+ }
425
+ /**
426
+ * Define a typed, request-scoped DI key.
427
+ *
428
+ * The returned object's `set`/`get`/`clear` work against the container belonging
429
+ * to the request you hand in — typically `ctx.container` from a middleware or
430
+ * the `container` argument inside `BaseController.execute`.
431
+ */
432
+ declare function defineRequestScopedKey<T>(key: string): RequestScopedKey<T>;
433
+ //#endregion
347
434
  //#region ../core/src/models/page.d.ts
348
435
  /**
349
436
  * BasePage — 所有页面共享的基础属性
350
437
  *
351
438
  * 具体页面类型由应用层定义并扩展此接口。
439
+ *
440
+ * SSR prefetch 数据序列化时的可见性由 `FINESOFT_PUBLIC` symbol 控制 —— 见
441
+ * `markPublic` / `isPublicMarked`。没有 marker 时 `serializeServerData` 仍按全字段
442
+ * 序列化(向后兼容),但会在 dev 启动后打印一次告警,下一个 major 会改为只序列化
443
+ * `BasePage` 的标准字段(id/pageType/title/description/url)。
352
444
  */
353
445
  interface BasePage {
354
446
  id: string;
@@ -357,6 +449,41 @@ interface BasePage {
357
449
  description?: string;
358
450
  url?: string;
359
451
  }
452
+ /**
453
+ * 显式声明 page 对象里哪些字段可以跨 SSR/CSR 边界进入 HTML。未列出的字段在
454
+ * `serializeServerData` 时会被剥除,杜绝整 page 对象(含 apiToken、内部备注等)
455
+ * 被无意中 hydrate 到客户端的失误。
456
+ *
457
+ * 用 symbol 而非普通字段名:避免和应用自己的字段冲突,且 `JSON.stringify` 自动
458
+ * 忽略 symbol key,所以 marker 永远不会出现在序列化输出里。
459
+ */
460
+ declare const FINESOFT_PUBLIC: unique symbol;
461
+ /** 标准 BasePage 字段 —— marker 缺失但显式标注时使用。 */
462
+ declare const BASE_PAGE_FIELDS: readonly ["id", "pageType", "title", "description", "url"];
463
+ /**
464
+ * 把 `publicFields` 列表写到 page 上,供 `serializeServerData` 读取。
465
+ * 推荐做法是不要直接渲染原始 page 对象,而是经过这个函数显式声明 contract:
466
+ *
467
+ * ```ts
468
+ * return markPublic(
469
+ * {
470
+ * id: "profile",
471
+ * pageType: "profile",
472
+ * title: user.name,
473
+ * email: user.email,
474
+ * apiToken: user.apiToken, // 仍在对象上,给服务端别处用
475
+ * },
476
+ * ["id", "pageType", "title", "email"], // 但 prefetch 只 serialize 这些
477
+ * );
478
+ * ```
479
+ *
480
+ * 也可以传 `true` 表示「所有字段都安全」—— 当作 opt-out,等价于不调用本函数。
481
+ */
482
+ declare function markPublic<P extends BasePage>(page: P, publicFields: readonly (keyof P)[] | true): P;
483
+ /** True 如果 page 用 `markPublic` 显式标过。 */
484
+ declare function isPublicMarked(page: unknown): boolean;
485
+ /** 取出 `markPublic` 写入的字段白名单;`true` 表示全开放,`null` 表示未标注。 */
486
+ declare function getPublicFields(page: unknown): readonly string[] | true | null;
360
487
  //#endregion
361
488
  //#region ../core/src/middleware/types.d.ts
362
489
  /** 导航上下文(beforeLoad 阶段可用) */
@@ -365,8 +492,8 @@ interface NavigationContext {
365
492
  readonly url: string;
366
493
  /** 仅路径部分 */
367
494
  readonly path: string;
368
- /** 路由参数 + 查询参数 */
369
- readonly params: Record<string, string>;
495
+ /** 路由参数 + 查询参数(codec 转换后可能是 number/boolean 等) */
496
+ readonly params: Record<string, unknown>;
370
497
  /** 匹配的 Intent */
371
498
  readonly intent: Intent;
372
499
  /** 是否在服务端运行 */
@@ -418,6 +545,50 @@ type BeforeLoadGuard = (ctx: NavigationContext) => MiddlewareResult | Promise<Mi
418
545
  /** afterLoad 守卫:数据加载后、渲染前 */
419
546
  type AfterLoadGuard = (ctx: PostLoadContext) => MiddlewareResult | Promise<MiddlewareResult>;
420
547
  //#endregion
548
+ //#region ../core/src/router/params/standard.d.ts
549
+ /**
550
+ * Standard Schema v1 接口的最小本地声明 + 运行助手。
551
+ * 纯 type-level 规范 + 运行时鸭子类型,不依赖 @standard-schema/spec 运行时包。
552
+ * https://standardschema.dev
553
+ */
554
+ interface StandardSchemaV1<Input = unknown, Output = Input> {
555
+ readonly "~standard": {
556
+ readonly version: 1;
557
+ readonly vendor: string;
558
+ readonly validate: (value: unknown) => StandardResult<Output> | Promise<StandardResult<Output>>;
559
+ readonly types?: {
560
+ readonly input: Input;
561
+ readonly output: Output;
562
+ };
563
+ };
564
+ }
565
+ type StandardResult<Output> = {
566
+ readonly value: Output;
567
+ readonly issues?: undefined;
568
+ } | {
569
+ readonly issues: ReadonlyArray<StandardIssue>;
570
+ };
571
+ interface StandardIssue {
572
+ readonly message: string;
573
+ readonly path?: ReadonlyArray<PropertyKey | {
574
+ readonly key: PropertyKey;
575
+ }>;
576
+ }
577
+ /** 提取 Standard Schema 的输出类型 */
578
+ type InferOutput<S extends StandardSchemaV1> = NonNullable<S["~standard"]["types"]>["output"];
579
+ /** 路由参数 codec:输入恒为 string(缺失时 undefined),输出为目标类型 T */
580
+ type ParamSchema<T = unknown> = StandardSchemaV1<string, T>;
581
+ /** 工厂:从一个 validate 函数构造实现了 ~standard 的 codec */
582
+ declare function makeSchema<T>(validate: (value: unknown) => StandardResult<T> | Promise<StandardResult<T>>): ParamSchema<T>;
583
+ /** 统一执行任意 Standard Schema 的校验,吸收同步/异步差异 */
584
+ declare function runStandard(schema: StandardSchemaV1, raw: string | undefined): Promise<{
585
+ ok: true;
586
+ value: unknown;
587
+ } | {
588
+ ok: false;
589
+ issues: readonly StandardIssue[];
590
+ }>;
591
+ //#endregion
421
592
  //#region ../core/src/router/router.d.ts
422
593
  /** 路由匹配结果 */
423
594
  interface RouteMatch {
@@ -434,18 +605,58 @@ interface RouteAddOptions {
434
605
  renderMode?: string;
435
606
  beforeGuards?: BeforeLoadGuard[];
436
607
  afterGuards?: AfterLoadGuard[];
608
+ paramCodecs?: Record<string, ParamSchema>;
609
+ queryCodecs?: Record<string, StandardSchemaV1<string, unknown>>;
437
610
  }
438
611
  declare class Router {
612
+ private readonly debug?;
439
613
  private routes;
614
+ constructor(debug?: ((message: string) => void) | undefined);
440
615
  /** 添加路由规则 */
441
616
  add(pattern: string, intentId: string, renderModeOrOptions?: string | RouteAddOptions): this;
442
- /** 解析 URL → RouteMatch */
443
- resolve(urlOrPath: string): RouteMatch | null;
617
+ /** 解析 URL → RouteMatch(含参数校验;校验失败则 fall-through 到下一条路由) */
618
+ resolve(urlOrPath: string): Promise<RouteMatch | null>;
444
619
  /** 获取所有已注册的路由 */
445
620
  getRoutes(): string[];
446
621
  private parseUrl;
447
622
  }
448
623
  //#endregion
624
+ //#region ../core/src/router/params/primitives.d.ts
625
+ interface StrOptions {
626
+ minLength?: number;
627
+ maxLength?: number;
628
+ pattern?: RegExp;
629
+ }
630
+ declare function str(opts?: StrOptions): ParamSchema<string>;
631
+ interface NumOptions {
632
+ min?: number;
633
+ max?: number;
634
+ }
635
+ declare function int(opts?: NumOptions): ParamSchema<number>;
636
+ declare function num(opts?: NumOptions): ParamSchema<number>;
637
+ declare function bool(): ParamSchema<boolean>;
638
+ declare function oneOf<const T extends readonly string[]>(values: T): ParamSchema<T[number]>;
639
+ declare function uuid(): ParamSchema<string>;
640
+ //#endregion
641
+ //#region ../core/src/router/params/modifiers.d.ts
642
+ /** 输入缺失(undefined)时跳过校验、产出 undefined;否则委托内部 codec(同步或异步均可)。 */
643
+ declare function optional<T>(codec: ParamSchema<T>): ParamSchema<T | undefined>;
644
+ /** 输入缺失时用 fallback;否则委托内部 codec。 */
645
+ declare function withDefault<T>(codec: ParamSchema<T>, fallback: T): ParamSchema<T>;
646
+ //#endregion
647
+ //#region ../core/src/router/params/infer.d.ts
648
+ /** 剥离可选参数尾随的 "?" */
649
+ type StripOptional<S extends string> = S extends `${infer N}?` ? N : S;
650
+ /** 从 path pattern 字面量提取参数名联合(处理 :param 与 :param?) */
651
+ type ExtractParamNames<Path extends string> = Path extends `${infer _Head}:${infer Rest}` ? Rest extends `${infer Name}/${infer Tail}` ? StripOptional<Name> | ExtractParamNames<`/${Tail}`> : StripOptional<Rest> : never;
652
+ /** path 参数 codec map 的形状:key 只能是 path 中出现的参数名(均可选声明) */
653
+ type ParamsFor<Path extends string> = { [K in ExtractParamNames<Path>]?: ParamSchema };
654
+ /** query 参数 codec map:key 自由开放 */
655
+ type QuerySchemaMap = Record<string, StandardSchemaV1<string, unknown>>;
656
+ /** 从 codec map 推导运行期参数类型 */
657
+ type InferParams<P extends Record<string, ParamSchema>> = { [K in keyof P]: InferOutput<P[K]> };
658
+ type InferQuery<Q extends QuerySchemaMap> = { [K in keyof Q]: InferOutput<Q[K]> };
659
+ //#endregion
449
660
  //#region ../core/src/logger/composite.d.ts
450
661
  declare class CompositeLoggerFactory implements LoggerFactory {
451
662
  private readonly factories;
@@ -570,7 +781,7 @@ declare class Framework {
570
781
  /** 执行 Action — 处理用户交互 */
571
782
  perform(action: Action): Promise<void>;
572
783
  /** 路由 URL — 将 URL 解析为 Intent + Action */
573
- routeUrl(url: string): RouteMatch | null;
784
+ routeUrl(url: string): Promise<RouteMatch | null>;
574
785
  /** 记录页面访问事件 */
575
786
  didEnterPage(page: BasePage): void;
576
787
  /** 获取 locale 信息(如果已配置) */
@@ -595,6 +806,32 @@ declare class Framework {
595
806
  dispose(): void;
596
807
  }
597
808
  //#endregion
809
+ //#region ../core/src/models/safe-error-page.d.ts
810
+ interface SafeErrorPageOptions {
811
+ /** HTTP-style status code used in the page title (e.g. 404, 500). */
812
+ status: number;
813
+ /**
814
+ * The message users / client code may safely see in production. Should
815
+ * contain no stack, file paths, hostnames, or secrets.
816
+ */
817
+ publicMessage: string;
818
+ /**
819
+ * Optional error / debug payload to surface only in non-production. In
820
+ * production this is dropped entirely; only `publicMessage` is exposed.
821
+ */
822
+ devError?: unknown;
823
+ /**
824
+ * Override the production detection. Pass `true` to force the prod-safe
825
+ * variant (drops devError). Defaults to detecting `process.env.NODE_ENV`.
826
+ */
827
+ isProduction?: boolean;
828
+ }
829
+ /**
830
+ * Build a BasePage for an error condition. In production, only `publicMessage`
831
+ * makes it into the page. In dev, `devError` (if provided) is appended.
832
+ */
833
+ declare function safeErrorPage(options: SafeErrorPageOptions): BasePage;
834
+ //#endregion
598
835
  //#region ../core/src/models/shelf.d.ts
599
836
  interface BaseShelf {
600
837
  id: string;
@@ -624,6 +861,9 @@ declare function stableStringify(obj: unknown): string;
624
861
  *
625
862
  * 为 API Client 提供标准化的 HTTP 请求能力。
626
863
  * 子类继承后只需关注业务端点定义,不需要重复实现 fetch / JSON 解析 / 错误处理。
864
+ *
865
+ * 默认安全:拒绝向内网 / loopback / 保留地址发请求(SSRF 防御)。应用层
866
+ * 显式 opt-out 用 `allowInternalHosts: true`。详见 host-guard.ts。
627
867
  */
628
868
  /** HTTP 请求错误 */
629
869
  declare class HttpError extends Error {
@@ -632,6 +872,15 @@ declare class HttpError extends Error {
632
872
  readonly body?: string | undefined;
633
873
  constructor(status: number, statusText: string, body?: string | undefined);
634
874
  }
875
+ /**
876
+ * SSRF 防护拦截到不安全的目标地址时抛出。应用层可以 catch 它来给出业务友好的错误,
877
+ * 不需要靠 message 字符串匹配。
878
+ */
879
+ declare class HostGuardError extends Error {
880
+ readonly url: string;
881
+ readonly reason: string;
882
+ constructor(url: string, reason: string);
883
+ }
635
884
  /** 请求拦截器 — 在发送前修改请求 */
636
885
  interface RequestInterceptor {
637
886
  (url: string, init: RequestInit): RequestInit | Promise<RequestInit>;
@@ -652,6 +901,22 @@ interface HttpClientConfig {
652
901
  requestInterceptors?: RequestInterceptor[];
653
902
  /** 响应拦截器(按注册顺序执行) */
654
903
  responseInterceptors?: ResponseInterceptor[];
904
+ /**
905
+ * 是否允许向私有 / loopback / 保留 IP 段发请求。
906
+ *
907
+ * **默认 false** —— 阻止内网穿透(SSRF)。如果你的服务正常需要打内网(如
908
+ * 微服务对内 API、127.0.0.1 上的开发依赖),把它设为 true 显式 opt-out,并
909
+ * 自己做来源校验。
910
+ */
911
+ allowInternalHosts?: boolean;
912
+ /**
913
+ * 是否在请求前 DNS 解析 hostname 并对解析结果做 IP 段校验。
914
+ *
915
+ * **默认 true**(仅 Node 环境有效;浏览器静默跳过)。配合 `allowInternalHosts`
916
+ * 防御 DNS rebinding:如果 hostname 不是 IP 字面量,框架会 resolve 它的 A/AAAA
917
+ * 记录并按 IP 段校验。`false` 关闭只剩 IP 字面量同步校验。
918
+ */
919
+ validateDns?: boolean;
655
920
  }
656
921
  /**
657
922
  * 通用 HTTP 客户端基类
@@ -673,6 +938,8 @@ declare abstract class HttpClient {
673
938
  protected readonly fetchFn: typeof globalThis.fetch;
674
939
  private readonly requestInterceptors;
675
940
  private readonly responseInterceptors;
941
+ private readonly allowInternalHosts;
942
+ private readonly validateDns;
676
943
  constructor(config: HttpClientConfig);
677
944
  /** 动态添加请求拦截器 */
678
945
  useRequestInterceptor(interceptor: RequestInterceptor): this;
@@ -691,6 +958,7 @@ declare abstract class HttpClient {
691
958
  *
692
959
  * 自动处理:
693
960
  * - URL 拼接 (baseUrl + path + params)
961
+ * - SSRF 防护(IP 字面量同步校验 + 可选 DNS 解析校验)
694
962
  * - 默认 headers 合并
695
963
  * - JSON body 序列化
696
964
  * - 响应 JSON 解析
@@ -703,8 +971,58 @@ declare abstract class HttpClient {
703
971
  }): Promise<T>;
704
972
  /** 构建完整 URL — 子类可覆写以自定义 URL 拼接逻辑 */
705
973
  protected buildUrl(path: string, params?: Record<string, string>): string;
974
+ private enforceHostGuard;
706
975
  }
707
976
  //#endregion
977
+ //#region ../core/src/http/host-guard.d.ts
978
+ /**
979
+ * host-guard — refuse outbound requests to private / loopback / reserved hosts.
980
+ *
981
+ * Why this exists: `HttpClient` and the raw `fetch` injected via DI both used
982
+ * to accept any URL the application built. SSR controllers that take a
983
+ * user-controlled URL (image proxy, link preview, OAuth callback) could be
984
+ * tricked into fetching `http://127.0.0.1/admin` and embedding the response in
985
+ * the SSR HTML. Round 1 of the adversarial drill walked straight through this;
986
+ * round 2's hand-rolled IPv4 regex was bypassed in five minutes with
987
+ * `[::ffff:7f00:1]`. This module centralises the check so applications can stop
988
+ * re-implementing it (badly).
989
+ *
990
+ * Coverage (synchronous, IP-literal forms):
991
+ * - IPv4 dotted-decimal: `127.0.0.1`, `10.0.0.5`, `192.168.1.1`, `172.16.0.1`
992
+ * - IPv4 zero / link-local / multicast / reserved: `0.0.0.0`, `169.254.0.1`,
993
+ * `224.0.0.1`, `240.0.0.0/4`
994
+ * - IPv4 non-dotted forms accepted by some parsers: decimal `2130706433`,
995
+ * hex `0x7f000001`, octal `0177.0.0.1`
996
+ * - IPv6 loopback `::1`, link-local `fe80::/10`, ULA `fc00::/7`, unspecified `::`
997
+ * - IPv4-mapped IPv6: `::ffff:7f00:1`, `::ffff:127.0.0.1`
998
+ * - Names: `localhost`, `*.localhost`
999
+ *
1000
+ * NOT covered here (callers can layer on top):
1001
+ * - DNS resolution of arbitrary hostnames — see `validateUrlWithDns` in
1002
+ * server-only callers. DNS rebinding is impossible to fix at this layer
1003
+ * alone; the right pattern is "resolve once, then fetch by IP".
1004
+ * - IDN / homograph attacks — Node's URL parser punycode-encodes hostnames
1005
+ * already, so the hostname this code sees is the ASCII form.
1006
+ */
1007
+ type HostCheckResult = {
1008
+ ok: true;
1009
+ } | {
1010
+ ok: false;
1011
+ reason: string;
1012
+ };
1013
+ /**
1014
+ * Inspect a hostname string (the `URL.hostname` value, without brackets, port,
1015
+ * or userinfo). Returns `{ok: false}` for anything in a private/loopback/
1016
+ * reserved range; `{ok: true}` if the literal looks like a public address or a
1017
+ * non-IP name (the caller may then DNS-resolve and re-check).
1018
+ */
1019
+ declare function classifyHost(rawHost: string): HostCheckResult;
1020
+ /**
1021
+ * Convenience wrapper for callers holding a full URL string. Also rejects
1022
+ * non-http(s) schemes (gopher, file, data, …).
1023
+ */
1024
+ declare function classifyUrl(rawUrl: string): HostCheckResult;
1025
+ //#endregion
708
1026
  //#region ../core/src/intents/base-controller.d.ts
709
1027
  /**
710
1028
  * 抽象 Controller 基类
@@ -730,7 +1048,7 @@ declare abstract class HttpClient {
730
1048
  * }
731
1049
  * ```
732
1050
  */
733
- declare abstract class BaseController<TParams extends Record<string, string | undefined> = Record<string, string>, TResult = unknown> implements IntentController<TResult> {
1051
+ declare abstract class BaseController<TParams extends Record<string, unknown> = Record<string, unknown>, TResult = unknown> implements IntentController<TResult> {
734
1052
  /** Controller 对应的 Intent ID */
735
1053
  abstract readonly intentId: string;
736
1054
  /**
@@ -803,34 +1121,40 @@ declare function mapEach<TInput, TOutput>(mapper: Mapper<TInput, TOutput>): Mapp
803
1121
  /** 渲染模式 */
804
1122
  type RenderMode = "ssr" | "csr" | "prerender";
805
1123
  /** 单条路由定义 */
806
- interface RouteDefinition {
807
- /** URL pattern (如 "/product/:productId") */
808
- path: string;
809
- /** Intent ID (如 "product-page") */
1124
+ interface RouteDefinition<Path extends string = string, P extends ParamsFor<Path> = ParamsFor<Path>, Q extends QuerySchemaMap = QuerySchemaMap> {
1125
+ /** URL pattern (如 "/product/:id") */
1126
+ path: Path;
1127
+ /** Intent ID */
810
1128
  intentId: string;
811
- /**
812
- * Controller 实例(可选)。
813
- * 同一个 intentId 的多条路由只需在第一条提供 controller。
814
- */
1129
+ /** Controller 实例(可选)。同一 intentId 的多条路由只需在第一条提供。 */
815
1130
  controller?: IntentController;
816
- /**
817
- * 渲染模式(可选,默认 "ssr")。
818
- * - "ssr": 服务端渲染(默认)
819
- * - "csr": 客户端渲染(返回空壳 HTML,由客户端 JS 渲染)
820
- * - "prerender": 预渲染(构建时生成静态 HTML + ISR 缓存)
821
- */
1131
+ /** path 参数 codec;key 必须是 path 中出现的 :param 名 */
1132
+ params?: P;
1133
+ /** query 参数 codec;key 自由 */
1134
+ query?: Q;
1135
+ /** 渲染模式(可选,默认 "ssr" */
822
1136
  renderMode?: RenderMode;
823
- /**
824
- * 路由级 beforeLoad 守卫(可选)。
825
- * 在全局守卫之后执行,仅对匹配此路由的请求生效。
826
- */
1137
+ /** 路由级 beforeLoad 守卫 */
827
1138
  beforeLoad?: BeforeLoadGuard[];
828
- /**
829
- * 路由级 afterLoad 守卫(可选)。
830
- * 在全局守卫之后执行,仅对匹配此路由的请求生效。
831
- */
1139
+ /** 路由级 afterLoad 守卫 */
832
1140
  afterLoad?: AfterLoadGuard[];
833
1141
  }
1142
+ /**
1143
+ * 构造一条强类型路由定义。
1144
+ * `params` 的 key 受 `path` 字面量约束——写入 path 中不存在的参数名会编译期报错。
1145
+ *
1146
+ * @example
1147
+ * route("/product/:id", { intentId: "product", controller, params: { id: int() } })
1148
+ */
1149
+ declare function route<const Path extends string, P extends ParamsFor<Path> = ParamsFor<Path>, Q extends QuerySchemaMap = QuerySchemaMap>(path: Path, def: {
1150
+ intentId: string;
1151
+ controller?: IntentController;
1152
+ params?: P;
1153
+ query?: Q;
1154
+ renderMode?: RenderMode;
1155
+ beforeLoad?: BeforeLoadGuard[];
1156
+ afterLoad?: AfterLoadGuard[];
1157
+ }): RouteDefinition;
834
1158
  /** defineRoutes 选项 */
835
1159
  interface DefineRoutesOptions {
836
1160
  /**
@@ -1245,5 +1569,5 @@ declare function deserializeServerData(): PrefetchedIntent[] | undefined;
1245
1569
  */
1246
1570
  declare function createPrefetchedIntentsFromDom(): PrefetchedIntents;
1247
1571
  //#endregion
1248
- export { RenderMode as $, Storage as $t, IntersectionImpressionObserver as A, isCompoundAction as An, RouteAddOptions as At, buildUrl as B, RedirectResult as Bt, PluralRuleProvider as C, ActionDispatcher as Cn, Translator as Ct, WithFieldsRecorder as D, CompoundAction as Dn, ConsoleLoggerFactory as Dt, resolvePluralKey as E, Action as En, ConsoleLogger as Et, BrowserContextOptions as F, DenyResult as Ft, PWADisplayMode as G, rewrite as Gt, removeHost as H, deny as Ht, ServerContextOptions as I, MiddlewareResult as It, Optional as J, FeatureFlags as Jt, getPWADisplayMode as K, BasePage as Kt, createBrowserContext as L, NavigationContext as Lt, CompositeEventRecorder as M, isFlowAction as Mn, Router as Mt, runAfterLoadGuards as N, makeExternalUrlAction as Nn, AfterLoadGuard as Nt, VoidEventRecorder as O, ExternalUrlAction as On, CompositeLogger as Ot, runBeforeLoadGuards as P, makeFlowAction as Pn, BeforeLoadGuard as Pt, DefineRoutesOptions as Q, Net as Qt, createServerContext as R, NextResult as Rt, PluralCategory as S, Container as Sn, TextDirection as St, interpolate as T, ACTION_KINDS as Tn, shouldLog as Tt, removeQueryParams as U, next as Ut, getBaseUrl as V, RewriteResult as Vt, removeScheme as W, redirect as Wt, isSome as X, MakeDependenciesOptions as Xt, isNone as Y, FeatureFlagsProvider as Yt, LruMap as Z, MetricsRecorder as Zt, getTextDirection as _, Logger as _n, FrameworkConfig as _t, BrowserAppConfig as a, resolveMessages as an, pipe as at, resolveLocaleFromUrl as b, Intent as bn, LocaleAttributes as bt, registerActionHandlers as c, EventRecorder as cn, HttpClient as ct, registerFlowActionHandler as d, MetricsFieldsProvider as dn, RequestInterceptor as dt, makeDependencies as en, RouteDefinition as et, ExternalUrlDependencies as f, ReportCallback as fn, ResponseInterceptor as ft, getLocaleAttributes as g, BaseLogger as gn, Framework as gt, SimpleTranslatorOptions as h, ReportingLoggerOptions as hn, BaseShelf as ht, History as i, resolveConfiguredMessages as in, mapEach as it, ConsoleEventRecorder as j, isExternalUrlAction as jn, RouteMatch as jt, ImpressionObserverOptions as k, FlowAction as kn, CompositeLoggerFactory as kt, FlowActionCallbacks as l, ImpressionEntry as ln, HttpClientConfig as lt, SimpleTranslator as m, ReportingLoggerFactory as mn, BaseItem as mt, deserializeServerData as n, MessagesLoaderContext as nn, AsyncMapper as nt, startBrowserApp as o, PlatformInfo as on, pipeAsync as ot, registerExternalUrlHandler as p, ReportingLogger as pn, stableStringify as pt, None as q, DEP_KEYS as qt, tryScroll as r, TranslationMessages as rn, Mapper as rt, ActionHandlerDependencies as s, detectPlatform as sn, BaseController as st, createPrefetchedIntentsFromDom as t, MessagesLoader as tn, defineRoutes as tt, FlowActionDependencies as u, ImpressionObserver as un, HttpError as ut, isRtl as v, LoggerFactory as vn, PrefetchedIntent as vt, englishPlural as w, ActionHandler as wn, resetFilterCache as wt, setHtmlLocaleAttributes as x, IntentController as xn, LocaleInfo as xt, makeLocaleInfo as y, IntentDispatcher as yn, PrefetchedIntents as yt, generateUuid as z, PostLoadContext as zt };
1249
- //# sourceMappingURL=server-data-DGbiKzMS.d.mts.map
1572
+ export { RenderMode as $, BaseLogger as $n, RouteAddOptions as $t, IntersectionImpressionObserver as A, DEP_KEYS as An, Translator as At, buildUrl as B, TranslationMessages as Bn, ParamsFor as Bt, PluralRuleProvider as C, BasePage as Cn, Framework as Ct, WithFieldsRecorder as D, markPublic as Dn, LocaleAttributes as Dt, resolvePluralKey as E, isPublicMarked as En, PrefetchedIntents as Et, BrowserContextOptions as F, Net as Fn, CompositeLogger as Ft, PWADisplayMode as G, EventRecorder as Gn, NumOptions as Gt, removeHost as H, resolveMessages as Hn, StripOptional as Ht, ServerContextOptions as I, Storage as In, CompositeLoggerFactory as It, Optional as J, MetricsFieldsProvider as Jn, int as Jt, getPWADisplayMode as K, ImpressionEntry as Kn, StrOptions as Kt, createBrowserContext as L, makeDependencies as Ln, ExtractParamNames as Lt, CompositeEventRecorder as M, FeatureFlagsProvider as Mn, shouldLog as Mt, runAfterLoadGuards as N, MakeDependenciesOptions as Nn, ConsoleLogger as Nt, VoidEventRecorder as O, RequestScopedKey as On, LocaleInfo as Ot, runBeforeLoadGuards as P, MetricsRecorder as Pn, ConsoleLoggerFactory as Pt, DefineRoutesOptions as Q, ReportingLoggerOptions as Qn, uuid as Qt, createServerContext as R, MessagesLoader as Rn, InferParams as Rt, PluralCategory as S, BASE_PAGE_FIELDS as Sn, safeErrorPage as St, interpolate as T, getPublicFields as Tn, PrefetchedIntent as Tt, removeQueryParams as U, PlatformInfo as Un, optional as Ut, getBaseUrl as V, resolveConfiguredMessages as Vn, QuerySchemaMap as Vt, removeScheme as W, detectPlatform as Wn, withDefault as Wt, isSome as X, ReportingLogger as Xn, oneOf as Xt, isNone as Y, ReportCallback as Yn, num as Yt, LruMap as Z, ReportingLoggerFactory as Zn, str as Zt, getTextDirection as _, RewriteResult as _n, isFlowAction as _r, ResponseInterceptor as _t, BrowserAppConfig as a, StandardResult as an, Intent as ar, mapEach as at, resolveLocaleFromUrl as b, redirect as bn, BaseShelf as bt, registerActionHandlers as c, runStandard as cn, ActionDispatcher as cr, BaseController as ct, registerFlowActionHandler as d, DenyResult as dn, Action as dr, classifyUrl as dt, RouteMatch as en, Logger as er, RouteDefinition as et, ExternalUrlDependencies as f, MiddlewareResult as fn, CompoundAction as fr, HostGuardError as ft, getLocaleAttributes as g, RedirectResult as gn, isExternalUrlAction as gr, RequestInterceptor as gt, SimpleTranslatorOptions as h, PostLoadContext as hn, isCompoundAction as hr, HttpError as ht, History as i, StandardIssue as in, IntentDispatcher as ir, Mapper as it, ConsoleEventRecorder as j, FeatureFlags as jn, resetFilterCache as jt, ImpressionObserverOptions as k, defineRequestScopedKey as kn, TextDirection as kt, FlowActionCallbacks as l, AfterLoadGuard as ln, ActionHandler as lr, HostCheckResult as lt, SimpleTranslator as m, NextResult as mn, FlowAction as mr, HttpClientConfig as mt, deserializeServerData as n, InferOutput as nn, SecureFetchOptions as nr, route as nt, startBrowserApp as o, StandardSchemaV1 as on, IntentController as or, pipe as ot, registerExternalUrlHandler as p, NavigationContext as pn, ExternalUrlAction as pr, HttpClient as pt, None as q, ImpressionObserver as qn, bool as qt, tryScroll as r, ParamSchema as rn, secureFetch as rr, AsyncMapper as rt, ActionHandlerDependencies as s, makeSchema as sn, Container as sr, pipeAsync as st, createPrefetchedIntentsFromDom as t, Router as tn, LoggerFactory as tr, defineRoutes as tt, FlowActionDependencies as u, BeforeLoadGuard as un, ACTION_KINDS as ur, classifyHost as ut, isRtl as v, deny as vn, makeExternalUrlAction as vr, stableStringify as vt, englishPlural as w, FINESOFT_PUBLIC as wn, FrameworkConfig as wt, setHtmlLocaleAttributes as x, rewrite as xn, SafeErrorPageOptions as xt, makeLocaleInfo as y, next as yn, makeFlowAction as yr, BaseItem as yt, generateUuid as z, MessagesLoaderContext as zn, InferQuery as zt };
1573
+ //# sourceMappingURL=server-data-DQzknR97.d.mts.map
@@ -0,0 +1,2 @@
1
+ const e={FLOW:`flow`,EXTERNAL_URL:`externalUrl`,COMPOUND:`compound`};function t(t){return t.kind===e.FLOW}function n(t){return t.kind===e.EXTERNAL_URL}function r(t){return t.kind===e.COMPOUND}function i(t,n){return{kind:e.FLOW,url:t,presentationContext:n}}function a(t){return{kind:e.EXTERNAL_URL,url:t}}var o=class{handlers=new Map;onAction(e,t){if(this.handlers.has(e)){console.warn(`[ActionDispatcher] kind="${e}" already registered, skipping`);return}this.handlers.set(e,t)}removeAction(e){return this.handlers.delete(e)}async perform(e,t=0){if(r(e)){if(t>=32)throw Error(`[ActionDispatcher] CompoundAction recursion depth exceeded (max 32)`);for(let n of e.actions)await this.perform(n,t+1);return}let n=this.handlers.get(e.kind);if(!n){console.warn(`[ActionDispatcher] No handler for kind="${e.kind}"`);return}await n(e)}},s=class{controllers=new Map;register(e){this.controllers.set(e.intentId,e)}async dispatch(e,t){let n=this.controllers.get(e.id);if(!n)throw Error(`[IntentDispatcher] No controller for "${e.id}". Registered: [${Array.from(this.controllers.keys()).join(`, `)}]`);return n.perform(e,t)}has(e){return this.controllers.has(e)}},c=class e{registrations=new Map;resolutionStack=new Set;parent;children=new Set;register(e,t,n=!0){return this.registrations.set(e,{factory:t,singleton:n}),this}resolve(e){let t=this.registrations.get(e);if(!t){if(this.parent)return this.parent.resolve(e);throw Error(`[Container] No registration for key: "${e}"`)}if(t.singleton){if(t.instance===void 0){if(this.resolutionStack.has(e))throw Error(`[Container] Circular dependency detected: ${[...this.resolutionStack,e].join(` → `)}`);this.resolutionStack.add(e);try{t.instance=t.factory()}finally{this.resolutionStack.delete(e)}}return t.instance}return t.factory()}has(e){return this.registrations.has(e)||(this.parent?.has(e)??!1)}unregister(e){return this.registrations.delete(e)}createScope(){let t=new e;return t.parent=this,this.children.add(t),t}dispose(){let e=Array.from(this.children);for(let t of e)t.dispose();this.children.clear();for(let e of this.registrations.values())e.instance=void 0;this.registrations.clear(),this.parent&&=(this.parent.children.delete(this),void 0)}};function l(e){if(!e)return{ok:!1,reason:`empty host`};let t=e.toLowerCase(),n=t.startsWith(`[`)&&t.endsWith(`]`)?t.slice(1,-1):t;if(n===`localhost`||n.endsWith(`.localhost`))return{ok:!1,reason:`loopback host forbidden`};if(n.includes(`:`))return m(n);let r=d(n);return r?p(r):{ok:!0}}function u(e){let t;try{t=new URL(e)}catch{return{ok:!1,reason:`invalid URL`}}return t.protocol!==`http:`&&t.protocol!==`https:`?{ok:!1,reason:`scheme "${t.protocol}" not allowed (only http/https)`}:l(t.hostname)}function d(e){if(e.length===0)return null;if(!e.includes(`.`)){let t=f(e);return t===null||t<0||t>4294967295?null:[t>>>24&255,t>>>16&255,t>>>8&255,t&255]}let t=e.split(`.`);if(t.length!==4)return null;let n=[];for(let e of t){let t=f(e);if(t===null||t<0||t>255)return null;n.push(t)}return n}function f(e){if(e.length===0)return null;if(e.startsWith(`0x`)||e.startsWith(`0X`)){let t=e.slice(2);return/^[0-9a-f]+$/i.test(t)?parseInt(t,16):null}return e.length>1&&e.startsWith(`0`)?/^[0-7]+$/.test(e)?parseInt(e,8):null:/^[0-9]+$/.test(e)?parseInt(e,10):null}function p(e){let[t,n]=e;return t===0?{ok:!1,reason:`unspecified IPv4 forbidden`}:t===127?{ok:!1,reason:`loopback IPv4 forbidden`}:t===10?{ok:!1,reason:`private IPv4 (10/8) forbidden`}:t===172&&n>=16&&n<=31?{ok:!1,reason:`private IPv4 (172.16/12) forbidden`}:t===192&&n===168?{ok:!1,reason:`private IPv4 (192.168/16) forbidden`}:t===169&&n===254?{ok:!1,reason:`link-local IPv4 forbidden`}:t===100&&n>=64&&n<=127?{ok:!1,reason:`CGNAT IPv4 forbidden`}:t>=224&&t<=239?{ok:!1,reason:`multicast IPv4 forbidden`}:t>=240?{ok:!1,reason:`reserved IPv4 forbidden`}:{ok:!0}}function m(e){let t=e.toLowerCase();if(t===`::`||t===`::0`)return{ok:!1,reason:`unspecified IPv6 forbidden`};if(t===`::1`)return{ok:!1,reason:`loopback IPv6 forbidden`};if(t.startsWith(`fe8`)||t.startsWith(`fe9`)||t.startsWith(`fea`)||t.startsWith(`feb`))return{ok:!1,reason:`link-local IPv6 forbidden`};if(t.startsWith(`fc`)||t.startsWith(`fd`))return{ok:!1,reason:`ULA IPv6 forbidden`};if(t.startsWith(`ff`))return{ok:!1,reason:`multicast IPv6 forbidden`};let n=h(t);if(n){let e=p(n);if(!e.ok)return{ok:!1,reason:`IPv4-in-IPv6 ${e.reason}`}}return{ok:!0}}function h(e){let t=e.match(/(?:^|:)((?:\d{1,3}\.){3}\d{1,3})$/);if(t){let e=d(t[1]);if(e)return e}let n=e.match(/(?:^|:)([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);if(n&&(e.startsWith(`::ffff:`)||e.includes(`:ffff:`))){let e=parseInt(n[1],16),t=parseInt(n[2],16);return[e>>>8&255,e&255,t>>>8&255,t&255]}return null}var g=class extends Error{constructor(e,t,n){super(`HTTP ${e}: ${t}`),this.status=e,this.statusText=t,this.body=n,this.name=`HttpError`}},_=class extends Error{constructor(e,t){super(`Refused to fetch ${e}: ${t}`),this.url=e,this.reason=t,this.name=`HostGuardError`}},v=class{baseUrl;defaultHeaders;fetchFn;requestInterceptors;responseInterceptors;allowInternalHosts;validateDns;constructor(e){this.baseUrl=e.baseUrl,this.defaultHeaders=e.defaultHeaders??{},this.fetchFn=e.fetch??globalThis.fetch.bind(globalThis),this.requestInterceptors=[...e.requestInterceptors??[]],this.responseInterceptors=[...e.responseInterceptors??[]],this.allowInternalHosts=e.allowInternalHosts??!1,this.validateDns=e.validateDns??!0}useRequestInterceptor(e){return this.requestInterceptors.push(e),this}useResponseInterceptor(e){return this.responseInterceptors.push(e),this}async get(e,t){return this.request(`GET`,e,{params:t})}async post(e,t,n){return this.request(`POST`,e,{body:t,params:n})}async put(e,t,n){return this.request(`PUT`,e,{body:t,params:n})}async del(e,t){return this.request(`DELETE`,e,{params:t})}async request(e,t,n){let r=this.buildUrl(t,n?.params);this.allowInternalHosts||await this.enforceHostGuard(r);let i={...this.defaultHeaders,...n?.headers},a={method:e,headers:i};n?.body!==void 0&&(Object.keys(i).some(e=>e.toLowerCase()===`content-type`)||(i[`Content-Type`]=`application/json`),a.body=JSON.stringify(n.body));for(let e of this.requestInterceptors)a=await e(r,a);let o=await this.fetchFn(r,a);for(let e of this.responseInterceptors)o=await e(o,r);if(!o.ok){let e=await o.text().catch(()=>void 0);throw new g(o.status,o.statusText,e)}try{return await o.json()}catch(e){throw e instanceof SyntaxError?new g(o.status,`Invalid JSON response`,await o.text().catch(()=>void 0)):e}}buildUrl(e,t){let n=this.baseUrl.endsWith(`/`)?this.baseUrl.slice(0,-1):this.baseUrl,r=e.startsWith(`/`)?e:`/${e}`,i=new URL(`${n}${r}`,`http://placeholder`);if(t)for(let[e,n]of Object.entries(t))i.searchParams.set(e,n);return this.baseUrl.startsWith(`http`)?i.toString():`${i.pathname}${i.search}`}async enforceHostGuard(e){let t;try{t=new URL(e)}catch{return}let n=u(e);if(!n.ok)throw new _(e,n.reason);if(this.validateDns&&t.hostname&&!ee(t.hostname)){let n=await te(t.hostname);if(!n.ok)throw new _(e,n.reason)}}};function ee(e){return e.includes(`:`)?!0:/^[0-9.]+$/.test(e)||/^0x[0-9a-f]+$/i.test(e)}async function te(e){let t;try{let e=await import(`node:dns/promises`);t=e.default??e}catch{return{ok:!0}}let n=await Promise.allSettled([t.resolve4(e),t.resolve6(e)]),r=[];for(let e of n)e.status===`fulfilled`&&r.push(...e.value);if(r.length===0)return{ok:!0};for(let e of r){let t=u(`http://${e.includes(`:`)?`[${e}]`:e}/`);if(!t.ok)return{ok:!1,reason:`host resolves to ${e} (${t.reason})`}}return{ok:!0}}function y(e,t={}){let n=t.allowInternalHosts??!1,r=t.validateDns??!0;return async function(t,i){if(!n){let e=re(t);if(e&&ne(e)){let t=u(e);if(!t.ok)throw new _(e,t.reason);if(r){let t=new URL(e);if(t.hostname&&!ie(t.hostname)){let n=await ae(t.hostname);if(!n.ok)throw new _(e,n.reason)}}}}return e(t,i)}}function ne(e){try{return new URL(e),!0}catch{return!1}}function re(e){return typeof e==`string`?e:e instanceof URL?e.toString():typeof Request<`u`&&e instanceof Request?e.url:null}function ie(e){return e.includes(`:`)?!0:/^[0-9.]+$/.test(e)||/^0x[0-9a-f]+$/i.test(e)}async function ae(e){let t;try{let e=await import(`node:dns/promises`);t=e.default??e}catch{return{ok:!0}}let n;try{n=await t.lookup(e,{all:!0})}catch{return{ok:!0}}if(n.length===0)return{ok:!0};for(let{address:e}of n){let t=u(`http://${e.includes(`:`)?`[${e}]`:e}/`);if(!t.ok)return{ok:!1,reason:`host resolves to ${e} (${t.reason})`}}return{ok:!0}}const oe=new Set([`ar`,`arc`,`dv`,`fa`,`ha`,`he`,`khw`,`ks`,`ku`,`ps`,`ur`,`yi`]);function b(e){let t=e.split(`-`)[0].toLowerCase();return oe.has(t)}function x(e){return b(e)?`rtl`:`ltr`}function se(e){return{lang:e,dir:x(e)}}function ce(e,t){return{language:e,region:t,bcp47:t?`${e}-${t}`:e,dir:x(e)}}function le(e){document.documentElement.lang=e.lang,document.documentElement.dir=e.dir}function ue(e,t){let n=e.split(`?`)[0].match(/^\/([^/]+)(\/.*)?$/);if(!n)return null;let r=n[1],i=t.find(e=>e.toLowerCase()===r.toLowerCase());return i?{locale:i,strippedUrl:n[2]||`/`}:null}async function de(e,t){if(!e||!t)return;let n=await fe();if(n)return n(e,t)}async function fe(){if(typeof globalThis.__FINESOFT_I18N_LOADER__==`function`)return globalThis.__FINESOFT_I18N_LOADER__;if(typeof __FINESOFT_I18N_LOADER_SPECIFIER__==`string`)try{let e=await import(__FINESOFT_I18N_LOADER_SPECIFIER__),t=typeof e==`function`?e:typeof e?.loadMessages==`function`?e.loadMessages:void 0;return t?(globalThis.__FINESOFT_I18N_LOADER__=t,t):void 0}catch{return}}async function pe(e){let{locale:t,loadMessages:n,context:r}=e;if(!(!t||!r))return n?n(t,r):de(t,r)}function me(e,t){let n=Object.entries(e);if(n.length===0)return;if(typeof n[0][1]==`string`)return e;let r=e[t];if(!r)return;let i={};for(let[e,t]of Object.entries(r)){if(typeof t==`string`){i[e]=t;continue}for(let[n,r]of Object.entries(t))i[`${e}.${n}`]=r}return i}function he(e,t){return t?e.replace(/\{(\w+)\}/g,(e,n)=>{let r=t[n];return r===void 0?`{${n}}`:String(r)}):e}function ge(e){return e===1?`one`:`other`}function _e(e,t){return`${e}.${t}`}var ve=class{locale;messages;pluralRule;fallback;constructor(e){this.locale=e.locale,this.messages=e.messages,this.pluralRule=e.pluralRule??ge,this.fallback=e.fallback??(e=>e)}t(e,t){let n=this.messages[e];return n===void 0?this.fallback(e):he(n,t)}plural(e,t,n){let r=_e(e,this.pluralRule(t)),i={count:t,...n};return this.t(r,i)}},S=class{constructor(e){this.factories=e}loggerFor(e){return new C(this.factories.map(t=>t.loggerFor(e)))}},C=class{constructor(e){this.loggers=e}debug(...e){return this.callAll(`debug`,e)}info(...e){return this.callAll(`info`,e)}warn(...e){return this.callAll(`warn`,e)}error(...e){return this.callAll(`error`,e)}callAll(e,t){for(let n of this.loggers)n[e](...t);return``}},w=class{category;constructor(e){this.category=e}};const T={"*":4,debug:4,info:3,warn:2,error:1,off:0,"":0};let E,D;function ye(){if(globalThis.localStorage===void 0)return{};let e;try{e=globalThis.localStorage.getItem(`onyxLog`)}catch{return{}}if(!e)return{};if(e===D&&E)return E;D=e;let t={},n=e.split(`,`);for(let e of n){let[n,r]=e.trim().split(`=`);if(!n||r===void 0)continue;let i=T[r.toLowerCase()]??void 0;i!==void 0&&(n===`*`?t.defaultLevel=i:(t.named??={},t.named[n]=i))}return E=t,t}function O(e,t){let n=ye();if(n.defaultLevel===void 0&&!n.named)return!0;let r=T[t]??4;return n.named?.[e]===void 0?n.defaultLevel===void 0?!0:r<=n.defaultLevel:r<=n.named[e]}function be(){E=void 0,D=void 0}var k=class extends w{debug(...e){return O(this.category,`debug`)&&console.debug(`[${this.category}]`,...e),``}info(...e){return O(this.category,`info`)&&console.info(`[${this.category}]`,...e),``}warn(...e){return O(this.category,`warn`)&&console.warn(`[${this.category}]`,...e),``}error(...e){return console.error(`[${this.category}]`,...e),``}},A=class{loggerFor(e){return new k(e)}};const j={debug:0,info:1,warn:2,error:3};var M=class extends w{minPriority;report;constructor(e,t){super(e),this.minPriority=j[t.minLevel??`warn`],this.report=t.report}debug(...e){return this.maybeReport(`debug`,e),``}info(...e){return this.maybeReport(`info`,e),``}warn(...e){return this.maybeReport(`warn`,e),``}error(...e){return this.maybeReport(`error`,e),``}maybeReport(e,t){if(!(j[e]<this.minPriority))try{this.report(e,this.category,t)}catch(e){console.error(`[ReportingLogger] report callback threw:`,e)}}},N=class{options;constructor(e){this.options=e}loggerFor(e){return new M(e,this.options)}},P=class{prefix;constructor(e=`Metrics`){this.prefix=e}record(e,t){console.info(`[${this.prefix}:${e}]`,t??``)}async flush(){}destroy(){}};function F(e){let t=e??(typeof navigator<`u`?navigator.userAgent:``),n=t.toLowerCase();return{os:xe(n),browser:Se(n),engine:Ce(n),isMobile:/mobile|android|iphone|ipad|ipod/i.test(t),isTouch:typeof navigator<`u`&&`maxTouchPoints`in navigator?navigator.maxTouchPoints>0:!1}}function xe(e){return/iphone|ipad|ipod/.test(e)?`ios`:/android/.test(e)?`android`:/macintosh|mac os x/.test(e)?`macos`:/windows/.test(e)?`windows`:/linux/.test(e)?`linux`:`unknown`}function Se(e){return/edg\//.test(e)?`edge`:/opr\/|opera/.test(e)?`opera`:/samsungbrowser/.test(e)?`samsung`:/chrome|crios/.test(e)&&!/edg\//.test(e)?`chrome`:/firefox|fxios/.test(e)?`firefox`:/safari/.test(e)&&!/chrome/.test(e)?`safari`:`unknown`}function Ce(e){return/applewebkit/.test(e)&&!/chrome/.test(e)?`webkit`:/applewebkit/.test(e)&&/chrome/.test(e)?`blink`:/gecko\//.test(e)?`gecko`:`unknown`}const I={LOGGER:`logger`,LOGGER_FACTORY:`loggerFactory`,NET:`net`,STORAGE:`storage`,FEATURE_FLAGS:`featureFlags`,METRICS:`metrics`,FETCH:`fetch`,SAFE_FETCH:`safeFetch`,EVENT_RECORDER:`eventRecorder`,LOCALE:`locale`,PLATFORM:`platform`,TRANSLATOR:`translator`};var we=class{store=new Map;get(e){return this.store.get(e)}set(e,t){this.store.set(e,t)}delete(e){this.store.delete(e)}},Te=class{flags;providers=[];constructor(e={}){this.flags=e}addProvider(e){this.providers.push(e)}isEnabled(e){for(let t=this.providers.length-1;t>=0;t--)if(this.providers[t].isEnabled(e))return!0;return this.flags[e]===!0}getString(e){for(let t=this.providers.length-1;t>=0;t--){let n=this.providers[t].getString?.(e);if(n!==void 0)return n}let t=this.flags[e];return typeof t==`string`?t:void 0}getNumber(e){for(let t=this.providers.length-1;t>=0;t--){let n=this.providers[t].getNumber?.(e);if(n!==void 0)return n}let t=this.flags[e];return typeof t==`number`?t:void 0}},Ee=class{record(e,t){console.info(`[Metrics:${e}]`,t??``)}recordPageView(e,t){this.record(`PageView`,{page:e,...t})}recordEvent(e,t){this.record(`Event`,{name:e,...t})}};function L(e,t={}){let{_resolvedMessages:n}=t,{fetch:r=globalThis.fetch?.bind(globalThis),featureFlags:i={},featureFlagsProviders:a=[],reportCallback:o,eventRecorder:s,locale:c,platform:l}=t,u=new A,d=o?new S([u,new N({report:o})]):u;e.register(I.LOGGER_FACTORY,()=>d),e.register(I.LOGGER,()=>d.loggerFor(`framework`)),e.register(I.NET,()=>({fetch:(e,t)=>r(e,t)})),e.register(I.STORAGE,()=>new we);let f=new Te(i);for(let e of a)f.addProvider(e);if(e.register(I.FEATURE_FLAGS,()=>f),e.register(I.METRICS,()=>new Ee),e.register(I.EVENT_RECORDER,()=>s??new P),c&&e.register(I.LOCALE,()=>se(c)),c&&n){let t=me(n,c);t&&e.register(I.TRANSLATOR,()=>new ve({locale:c,messages:t}))}e.register(I.PLATFORM,()=>l??F(typeof navigator<`u`?navigator.userAgent:void 0)),e.register(I.FETCH,()=>r);let p=t.safeFetch??{};e.register(I.SAFE_FETCH,()=>y(r,p))}function R(e){return`container`in e?e.container:e}function De(e){return{key:e,set(t,n){R(t).register(e,()=>n)},get(t){let n=R(t);return n.has(e)?n.resolve(e):void 0},clear(t){return R(t).unregister(e)}}}function z(e){return{"~standard":{version:1,vendor:`finesoft`,validate:e}}}async function B(e,t){let n=await e[`~standard`].validate(t);return n.issues?{ok:!1,issues:n.issues}:{ok:!0,value:n.value}}function V(e){return Object.assign(Object.create(null),e)}var H=class{routes=[];constructor(e){this.debug=e}add(e,t,n){let r=typeof n==`string`?{renderMode:n}:n??{},i=[],a=e.split(/(\/:[\w]+\??)/).map(t=>{let n=t.match(/^\/:(\w+)(\?)?$/);if(n){if(i.includes(n[1]))throw Error(`[Router] Duplicate parameter name ":${n[1]}" in pattern "${e}"`);return i.push(n[1]),n[2]?`(?:/([^/]+))?`:`/([^/]+)`}return t.replace(/[.+?^${}()|[\]\\]/g,`\\$&`)}).join(``);return this.routes.push({pattern:e,intentId:t,regex:RegExp(`^${a}/?$`),paramNames:i,renderMode:r.renderMode,beforeGuards:r.beforeGuards,afterGuards:r.afterGuards,paramCodecs:r.paramCodecs,queryCodecs:r.queryCodecs}),this}async resolve(e){let{path:t,queryParams:n}=this.parseUrl(e);for(let r of this.routes){let a=t.match(r.regex);if(!a)continue;let o=V(),s=!0;for(let e=0;e<r.paramNames.length;e++){let t=r.paramNames[e],n=a[e+1],i=r.paramCodecs?.[t];if(i){let e=await B(i,n);if(!e.ok){this.debug?.(`[Router] route "${r.pattern}" skipped: path param "${t}" failed validation: ${e.issues[0]?.message??`invalid`}`),s=!1;break}e.value!==void 0&&(o[t]=e.value)}else n&&(o[t]=n)}if(s){if(r.queryCodecs)for(let e of Object.keys(r.queryCodecs)){let t=await B(r.queryCodecs[e],n[e]);if(!t.ok){this.debug?.(`[Router] route "${r.pattern}" skipped: query param "${e}" failed validation: ${t.issues[0]?.message??`invalid`}`),s=!1;break}t.value!==void 0&&(o[e]=t.value)}if(s){for(let e of Object.keys(n))!(e in o)&&!r.queryCodecs?.[e]&&(o[e]=n[e]);return{intent:{id:r.intentId,params:o},action:i(e),renderMode:r.renderMode,beforeGuards:r.beforeGuards,afterGuards:r.afterGuards}}}}return null}getRoutes(){return this.routes.map(e=>`${e.pattern} → ${e.intentId}`)}parseUrl(e){try{let t=new URL(e,`http://localhost`),n=V(Object.fromEntries(t.searchParams));return{path:t.pathname,queryParams:n}}catch{return{path:e.split(`?`)[0].split(`#`)[0],queryParams:V()}}}};const U=e=>({issues:[{message:e}]});function Oe(e={}){return z(t=>typeof t==`string`?e.minLength!==void 0&&t.length<e.minLength?U(`must be at least ${e.minLength} characters`):e.maxLength!==void 0&&t.length>e.maxLength?U(`must be at most ${e.maxLength} characters`):e.pattern&&!e.pattern.test(t)?U(`must match ${e.pattern}`):{value:t}:U(`expected a string`))}function ke(e,t){return t.min!==void 0&&e<t.min?U(`must be >= ${t.min}`):t.max!==void 0&&e>t.max?U(`must be <= ${t.max}`):null}function Ae(e={}){return z(t=>{if(typeof t!=`string`||!/^-?\d+$/.test(t))return U(`"${String(t)}" is not an integer`);let n=Number(t);return ke(n,e)??{value:n}})}function je(e={}){return z(t=>{if(typeof t!=`string`||!/^-?\d+(\.\d+)?$/.test(t))return U(`"${String(t)}" is not a number`);let n=Number(t);return ke(n,e)??{value:n}})}const Me=new Set([`true`,`1`]),Ne=new Set([`false`,`0`]);function Pe(){return z(e=>typeof e==`string`&&Me.has(e)?{value:!0}:typeof e==`string`&&Ne.has(e)?{value:!1}:U(`"${String(e)}" is not a boolean (true/false/1/0)`))}function Fe(e){return z(t=>typeof t==`string`&&e.includes(t)?{value:t}:U(`"${String(t)}" is not one of: ${e.join(`, `)}`))}const Ie=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function Le(){return z(e=>typeof e==`string`&&Ie.test(e)?{value:e}:U(`"${String(e)}" is not a valid UUID`))}function Re(e){return z(t=>t===void 0?{value:void 0}:e[`~standard`].validate(t))}function ze(e,t){return z(n=>n===void 0?{value:t}:e[`~standard`].validate(n))}async function Be(e,t){for(let n of e){let e=await n(t);if(e.kind!==`next`)return e}return{kind:`next`}}async function Ve(e,t){for(let n of e){let e=await n(t);if(e.kind!==`next`)return e}return{kind:`next`}}const He=new WeakMap;function W(e){return G(e,new Set,0)}function G(e,t,n){if(e==null)return String(e);if(typeof e!=`object`)return JSON.stringify(e);let r=He.get(e);if(r!==void 0)return r;if(n>50)return`"[Max Depth]"`;if(t.has(e))return`"[Circular]"`;t.add(e);try{let r;if(Array.isArray(e))r=`[`+e.map(e=>G(e,t,n+1)).join(`,`)+`]`;else{let i=Object.keys(e).sort(),a=[];for(let r of i){let i=e[r];i!==void 0&&a.push(JSON.stringify(r)+`:`+G(i,t,n+1))}r=`{`+a.join(`,`)+`}`}return He.set(e,r),r}finally{t.delete(e)}}var K=class e{intents;constructor(e){this.intents=e}static fromArray(t){let n=new Map;for(let e of t)if(e.intent&&e.data!==void 0){let t=W(e.intent);n.set(t,e.data)}return new e(n)}static empty(){return new e(new Map)}get(e){let t=W(e),n=this.intents.get(t);if(n!==void 0)return this.intents.delete(t),n}has(e){return this.intents.has(W(e))}get size(){return this.intents.size}},Ue=class e{container;intentDispatcher;actionDispatcher;router;prefetchedIntents;beforeGuards=[];afterGuards=[];_logger;constructor(e,t){this.container=e,this.intentDispatcher=new s,this.actionDispatcher=new o,this.router=new H(e=>this.getLogger().debug(e)),this.prefetchedIntents=t}static create(t={}){let n=new c;L(n,t);let r=new e(n,t.prefetchedIntents??K.empty());return t.setupRoutes?.(r.router),r}getLogger(){return this._logger??=this.container.resolve(I.LOGGER)}async dispatch(e){let t=this.getLogger(),n=this.prefetchedIntents.get(e);return n===void 0?(t.debug(`[Framework] dispatch intent: ${e.id}`,e.params),this.intentDispatcher.dispatch(e,this.container)):(t.debug(`[Framework] re-using prefetched intent response for: ${e.id}`,e.params),n)}async perform(e){return this.getLogger().debug(`[Framework] perform action: ${e.kind}`),this.actionDispatcher.perform(e)}async routeUrl(e){return this.router.resolve(e)}didEnterPage(e){this.container.resolve(I.METRICS).recordPageView(e.pageType,{pageId:e.id,title:e.title})}getLocale(){return this.container.has(I.LOCALE)?this.container.resolve(I.LOCALE):void 0}getTranslator(){return this.container.has(I.TRANSLATOR)?this.container.resolve(I.TRANSLATOR):void 0}getPlatform(){return this.container.resolve(I.PLATFORM)}onAction(e,t){this.actionDispatcher.onAction(e,t)}registerIntent(e){this.intentDispatcher.register(e)}beforeLoad(e){this.beforeGuards.push(e)}afterLoad(e){this.afterGuards.push(e)}runBeforeLoad(e,t){return Be(t?.length?[...this.beforeGuards,...t]:this.beforeGuards,e)}runAfterLoad(e,t){return Ve(t?.length?[...this.afterGuards,...t]:this.afterGuards,e)}dispose(){this.container.dispose()}};const q=Symbol.for(`finesoft.publicFields`),We=[`id`,`pageType`,`title`,`description`,`url`];function Ge(e,t){return t===!0?(Object.defineProperty(e,q,{value:!0,enumerable:!1,configurable:!0,writable:!1}),e):(Object.defineProperty(e,q,{value:Object.freeze([...t]),enumerable:!1,configurable:!0,writable:!1}),e)}function Ke(e){return typeof e==`object`&&!!e&&e[q]!==void 0}function qe(e){if(typeof e!=`object`||!e)return null;let t=e[q];return t===void 0?null:t===!0?!0:Array.isArray(t)?t:null}function Je(){try{let e=globalThis.process?.env?.NODE_ENV;return e===void 0?!0:e===`production`}catch{return!0}}function Ye(e){if(e instanceof Error)return e.stack??`${e.name}: ${e.message}`;if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return String(e)}}function Xe(e){let{status:t,publicMessage:n,devError:r,isProduction:i=Je()}=e,a=!i&&r!==void 0;return{id:`error`,pageType:`error`,title:`Error ${t}`,description:a?`${n}\n\n[dev only]\n${Ye(r)}`:n}}var Ze=class{fallback(e,t){throw t}async perform(e,t){let n=e.params??{};try{return await this.execute(n,t)}catch(e){return this.fallback(n,e instanceof Error?e:Error(String(e)))}}};function Qe(...e){return t=>e.reduce((e,t)=>t(e),t)}function $e(...e){return async t=>{let n=t;for(let t of e)n=await t(n);return n}}function et(e){return t=>t.map(e)}function tt(e,t){return{path:e,...t}}function nt(e,t,n){let r=new Set;for(let i of t){i.controller&&!r.has(i.intentId)&&(e.registerIntent(i.controller),r.add(i.intentId));let t={renderMode:i.renderMode,beforeGuards:i.beforeLoad,afterGuards:i.afterLoad,paramCodecs:i.params,queryCodecs:i.query};if(e.router.add(i.path,i.intentId,t),n?.locales?.length){let n=i.path===`/`?`/:locale`:`/:locale${i.path}`;e.router.add(n,i.intentId,t)}}}var rt=class{map=new Map;capacity;constructor(e){if(e<1)throw Error(`[LruMap] capacity must be >= 1, got ${e}`);this.capacity=e}get(e){if(!this.map.has(e))return;let t=this.map.get(e);return this.map.delete(e),this.map.set(e,t),t}set(e,t){if(this.map.has(e))this.map.delete(e);else if(this.map.size>=this.capacity){let e=this.map.keys().next().value;e!==void 0&&this.map.delete(e)}this.map.set(e,t)}has(e){return this.map.has(e)}delete(e){return this.map.delete(e)}get size(){return this.map.size}clear(){this.map.clear()}};function it(e){return e!=null}function at(e){return e==null}function ot(){return typeof window>`u`?`browser`:document.referrer.startsWith(`android-app://`)?`twa`:window.matchMedia(`(display-mode: standalone)`).matches||`standalone`in window.navigator&&window.navigator.standalone===!0?`standalone`:`browser`}function st(e){return e.replace(/^https?:\/\//,``)}function ct(e){try{let t=new URL(e);return t.pathname+t.search+t.hash}catch{return e}}function lt(e){return e.split(`?`)[0]}function ut(e){return e.split(`?`)[0].split(`#`)[0]}function dt(e,t){if(!t)return e;let n=new URLSearchParams;for(let[e,r]of Object.entries(t))r!==void 0&&n.set(e,r);let r=n.toString();return r?`${e}?${r}`:e}function J(){return typeof crypto<`u`&&crypto.randomUUID?crypto.randomUUID():`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}function ft(e){let t=new Map;if(!e)return t;for(let n of e.split(`;`)){let e=n.indexOf(`=`);if(e===-1)continue;let r=n.slice(0,e).trim(),i=n.slice(e+1).trim();r&&t.set(r,i)}return t}function pt(e){let{url:t,intent:n,container:r,request:i}=e,a=new URL(t,`http://localhost`),o=ft(i?.headers.get(`cookie`)??``);return{url:t,path:a.pathname,params:n.params??{},intent:n,isServer:!0,container:r,getCookie:e=>o.get(e),getHeader:e=>i?.headers.get(e)??void 0}}function Y(e){let{url:t,intent:n,container:r}=e;return{url:t,path:new URL(t,window.location.origin).pathname,params:n.params??{},intent:n,isServer:!1,container:r,getCookie:e=>ft(document.cookie).get(e),getHeader:()=>void 0}}function mt(){return{kind:`next`}}function ht(e,t=302){return{kind:`redirect`,url:e,status:t}}function gt(e){return{kind:`rewrite`,url:e}}function _t(e=403,t=`Forbidden`){return{kind:`deny`,status:e,message:t}}var vt=class{recorders;constructor(e){this.recorders=e}record(e,t){for(let n of this.recorders)n.record(e,t)}async flush(){let e=[];for(let t of this.recorders)t.flush&&e.push(t.flush());await Promise.all(e)}destroy(){for(let e of this.recorders)e.destroy?.()}},yt=class{observer;tracked=new Map;captured=[];minDuration;constructor(e={}){this.minDuration=e.minVisibleDuration??1e3,this.observer=new IntersectionObserver(e=>{let t=Date.now();for(let n of e){let e=this.tracked.get(n.target);e&&(n.isIntersecting?e.visibleSince===null&&(e.visibleSince=t):e.visibleSince!==null&&(t-e.visibleSince>=this.minDuration&&this.captured.push({id:e.id,timestamp:e.visibleSince,metadata:e.metadata}),e.visibleSince=null))}},{threshold:e.threshold??.5})}observe(e,t,n){this.tracked.set(e,{id:t,metadata:n,visibleSince:null}),this.observer.observe(e)}unobserve(e){this.observer.unobserve(e),this.tracked.delete(e)}consume(){let e=Date.now();for(let[,t]of this.tracked)t.visibleSince!==null&&e-t.visibleSince>=this.minDuration&&(this.captured.push({id:t.id,timestamp:t.visibleSince,metadata:t.metadata}),t.visibleSince=e);return this.captured.splice(0)}destroy(){this.observer.disconnect(),this.tracked.clear(),this.captured.length=0}},bt=class{record(){}async flush(){}destroy(){}},xt=class{constructor(e,t){this.inner=e,this.providers=t}record(e,t){let n={};for(let e of this.providers)Object.assign(n,e.getFields());t&&Object.assign(n,t),this.inner.record(e,n)}async flush(){return this.inner.flush?.()}destroy(){this.inner.destroy?.()}};function St(t){let{framework:n,log:r}=t;n.onAction(e.EXTERNAL_URL,e=>{r.debug(`ExternalUrlAction → ${e.url}`),window.open(e.url,`_blank`,`noopener,noreferrer`)})}const X=5e3;let Z=null;function Q(){Z?.()}function Ct(e,t,n){Q();let r=Math.max(0,n),i=Date.now(),a=!1,o=null,s=null,c=null,l=null;Z=p,f(),document.addEventListener(`load`,u,!0),s=setInterval(u,100),c=setTimeout(u,X),u();function u(){a||o!==null||(o=requestAnimationFrame(()=>{o=null,d()}))}function d(){if(a)return;let n=Date.now()-i,o=t();if(!o){n>=X&&(e.warn(`tryScroll: timed out waiting for the scrollable element`,{target:r,elapsedMs:n}),p());return}o.scrollTop=r;let s=o.scrollTop;if(s>=r-2){e.info(`scroll restored`,{target:r,actual:s,elapsedMs:n}),p();return}n>=X&&(e.warn(`tryScroll: timed out before reaching the target`,{target:r,actual:s,elapsedMs:n,scrollHeight:o.scrollHeight,clientHeight:o.clientHeight}),p())}function f(){if(typeof MutationObserver>`u`)return;let e=document.body??document.documentElement;e&&(l=new MutationObserver(()=>{u()}),l.observe(e,{childList:!0,subtree:!0}))}function p(){a||(a=!0,Z===p&&(Z=null),o!==null&&(cancelAnimationFrame(o),o=null),s!==null&&(clearInterval(s),s=null),c!==null&&(clearTimeout(c),c=null),l?.disconnect(),l=null,document.removeEventListener(`load`,u,!0))}}var wt=class{entries;log;getScrollablePageElement;currentStateId;constructor(e,t,n=10){this.entries=new rt(n),this.log=e,this.getScrollablePageElement=t.getScrollablePageElement}replaceState(e,t){Q();let n=J();window.history.replaceState({id:n},``,t),this.currentStateId=n,this.entries.set(n,{state:e,scrollY:0}),this.scrollTop=0,this.log.info(`replaceState`,e,t,n)}pushState(e,t){Q();let n=J();window.history.pushState({id:n},``,t),this.currentStateId=n,this.entries.set(n,{state:e,scrollY:0}),this.scrollTop=0,this.log.info(`pushState`,e,t,n)}beforeTransition(){Q();let{state:e}=window.history;if(!e)return;let t=this.entries.get(e.id);if(!t){this.log.info(`current history state evicted from LRU, not saving scroll position`);return}let{scrollTop:n}=this;this.entries.set(e.id,{...t,scrollY:n}),this.log.info(`saving scroll position`,n)}onPopState(e){window.addEventListener(`popstate`,t=>{Q(),this.currentStateId=t.state?.id,this.currentStateId||this.log.warn(`encountered a null event.state.id in onPopState event:`,window.location.href),this.log.info(`popstate`,this.entries,this.currentStateId);let n=this.currentStateId?this.entries.get(this.currentStateId):void 0;if(Promise.resolve(e(window.location.href,n?.state)).catch(e=>{this.log.error(`onPopState listener error:`,e)}),!n)return;let{scrollY:r}=n;this.log.info(`restoring scroll to`,r),Ct(this.log,()=>this.getScrollablePageElement(),r)})}pushUrl(e){Q();let t=J();window.history.pushState({id:t},``,e),this.currentStateId=t,this.scrollTop=0,this.log.info(`pushUrl (no state)`,e,t)}replaceUrl(e){Q();let t=J();window.history.replaceState({id:t},``,e),this.currentStateId=t,this.scrollTop=0,this.log.info(`replaceUrl (no state)`,e,t)}updateState(e){if(!this.currentStateId){this.log.warn(`failed: encountered a null currentStateId inside updateState`);return}let t=this.entries.get(this.currentStateId),n=e(t?.state);this.log.info(`updateState`,n,this.currentStateId),this.entries.set(this.currentStateId,{scrollY:t?.scrollY??0,state:n})}get scrollTop(){return this.getScrollablePageElement()?.scrollTop||0}set scrollTop(e){let t=this.getScrollablePageElement();t&&(t.scrollTop=e)}};function Tt(t){let{framework:n,log:r,callbacks:i,updateApp:a}=t,o=!0,s=0,c=new wt(r,{getScrollablePageElement:t.getScrollablePageElement??(()=>document.getElementById(`scrollable-page-override`)||document.getElementById(`scrollable-page`)||document.documentElement)});async function l(e,t,d){if(t>=5){r.error(`Navigation redirect loop detected (5 redirects), stopping at: ${e}`);return}let f=o||e===window.location.pathname+window.location.search,p=await n.routeUrl(e);if(!p){r.warn(`FlowAction: no route for ${e}`);return}let m=Y({url:e,intent:p.intent,container:n.container}),h=await n.runBeforeLoad(m,p.beforeGuards);if(h.kind===`redirect`){r.debug(`beforeLoad → redirect to ${h.url}`),await l(h.url,t+1,d);return}if(h.kind===`deny`){r.warn(`beforeLoad → denied (${h.status}): ${h.message}`);return}if(h.kind===`rewrite`){r.debug(`beforeLoad → rewrite to ${h.url}`),await l(h.url,t+1,d);return}let g=n.dispatch(p.intent);if(await Promise.race([g,new Promise(e=>setTimeout(e,500))]).catch(()=>{}),d!==s){r.info(`FlowAction superseded by newer navigation`,e);return}c.beforeTransition(),a({page:g.then(async a=>{if(d!==s)return r.info(`FlowAction commit superseded`,e),a;let o={...m,page:a},h=await n.runAfterLoad(o,p.afterGuards);if(h.kind===`redirect`)return r.debug(`afterLoad → redirect to ${h.url}`),l(h.url,t+1,d),a;let g=e;return h.kind===`rewrite`&&(g=h.url,r.debug(`afterLoad → rewrite URL to ${g}`)),h.kind===`deny`?(r.warn(`afterLoad → denied (${h.status})`),a):(f?c.replaceState({page:a},g):c.pushState({page:a},g),i.onNavigate(new URL(g,window.location.origin).pathname),u(a),a)},t=>{if(d===s){let t=e;f?c.replaceUrl(t):c.pushUrl(t),i.onNavigate(new URL(t,window.location.origin).pathname)}throw t}),isFirstPage:o}),o=!1}n.onAction(e.FLOW,async e=>{let t=e,a=t.url;if(r.debug(`FlowAction → ${a}`),t.presentationContext===`modal`){let e=await n.routeUrl(a);if(e){let t=await n.dispatch(e.intent);i.onModal(t)}return}await l(a,0,++s)}),c.onPopState(async(e,t)=>{if(r.debug(`popstate → ${e}, cached=${!!t}`),i.onNavigate(new URL(e).pathname),t){let{page:e}=t;u(e),a({page:e,isFirstPage:o});return}let c=new URL(e),d=await n.routeUrl(c.pathname+c.search);if(!d){r.error(`received popstate without data, but URL was unroutable:`,e),u(null),a({page:Promise.reject(Error(`404`)),isFirstPage:o});return}let f=Y({url:c.pathname+c.search,intent:d.intent,container:n.container}),p=await n.runBeforeLoad(f,d.beforeGuards);if(p.kind===`redirect`){r.debug(`popstate beforeLoad → redirect to ${p.url}`);let e=++s;await l(p.url,0,e);return}if(p.kind===`deny`||p.kind===`rewrite`){if(p.kind===`deny`)r.warn(`popstate beforeLoad → denied`);else{let e=++s;await l(p.url,0,e)}return}let m=n.dispatch(d.intent);await Promise.race([m,new Promise(e=>setTimeout(e,500))]).catch(()=>{}),a({page:m.then(async e=>{let t={...f,page:e},a=await n.runAfterLoad(t,d.afterGuards);if(a.kind===`redirect`){r.debug(`popstate afterLoad → redirect to ${a.url}`);let t=++s;return l(a.url,0,t),e}if(a.kind===`rewrite`){r.debug(`popstate afterLoad → rewrite URL to ${a.url}`);let t=window.history.state?.id;return window.history.replaceState({id:t},``,a.url),i.onNavigate(new URL(a.url,window.location.origin).pathname),u(e),e}return a.kind===`deny`?(r.warn(`popstate afterLoad → denied (${a.status})`),e):(u(e),e)}),isFirstPage:o})});function u(e){(async()=>{try{e&&n.didEnterPage(e)}catch(e){r.error(`didEnterPage error:`,e)}})()}}function $(e){let{framework:t,log:n,callbacks:r,updateApp:i}=e;Tt({framework:t,log:n,callbacks:r,updateApp:i,getScrollablePageElement:e.getScrollablePageElement}),St({framework:t,log:n})}function Et(){let e=document.getElementById(`serialized-server-data`);if(e?.textContent){e.parentNode?.removeChild(e);try{return JSON.parse(e.textContent)}catch{return}}}function Dt(){let e=Et();return!e||!Array.isArray(e)?K.empty():K.fromArray(e)}async function Ot(e){let{bootstrap:t,mountId:n=`app`,mount:r,callbacks:i,onBeforeStart:a,onAfterStart:o,frameworkConfig:s={},loadMessages:c}=e,l=Dt(),u=window.location.pathname+window.location.search,d=kt(s.locale),f=await pe({locale:d,loadMessages:c,context:d?{runtime:`browser`,fetch:At(s.fetch),url:u}:void 0}),p=Ue.create({...s,locale:d,_resolvedMessages:f,prefetchedIntents:l});t(p);let m=p.container.resolve(I.LOGGER_FACTORY).loggerFor(`browser`),h=p.getLocale();h&&(le(h),m.debug(`[startBrowserApp] Applied locale attributes:`,h)),await a?.(p);let g=await p.routeUrl(u),_=document.getElementById(n);if(!_)throw Error(`[startBrowserApp] Mount target not found: #${n}. Ensure your HTML has <div id="${n}"></div>.`);let v=r(_,{framework:p});$({framework:p,log:m,callbacks:i,updateApp:v,getScrollablePageElement:e.getScrollablePageElement}),g?await p.perform(g.action):v({page:Promise.reject(Error(`404`)),isFirstPage:!0}),await o?.(p)}function kt(e){return e||document.documentElement.lang.trim()||void 0}function At(e){return(e??globalThis.fetch?.bind(globalThis))||(()=>{throw Error(`[startBrowserApp] loadMessages requires a fetch implementation.`)})}export{Le as $,tt as A,_ as At,Ge as B,n as Bt,lt as C,se as Ct,it as D,ue as Dt,at as E,ce as Et,Xe as F,c as Ft,Be as G,K as H,a as Ht,We as I,s as It,Pe as J,Re as K,q as L,o as Lt,Qe as M,g as Mt,$e as N,l as Nt,rt as O,le as Ot,Ze as P,u as Pt,Oe as Q,qe as R,e as Rt,ct as S,me as St,ot as T,b as Tt,W as U,i as Ut,Ue as V,t as Vt,Ve as W,je as X,Ae as Y,Fe as Z,Y as _,ve as _t,Tt as a,L as at,dt as b,_e as bt,St as c,M as ct,yt as d,A as dt,H as et,vt as f,be as ft,gt as g,S as gt,ht as h,C as ht,$ as i,I as it,et as j,v as jt,nt as k,y as kt,xt as l,N as lt,mt as m,w as mt,Dt as n,B as nt,wt as o,F as ot,_t as p,O as pt,ze as q,Et as r,De as rt,Ct as s,P as st,Ot as t,z as tt,bt as u,k as ut,pt as v,ge as vt,st as w,x as wt,ut as x,pe as xt,J as y,he as yt,Ke as z,r as zt};
2
+ //# sourceMappingURL=start-app-S4DH-40i.mjs.map
@@ -191,6 +191,12 @@ const match = framework.router.resolve("/products/42");
191
191
 
192
192
  `router.resolve()` returns `null` for unmatched URLs — handle this in your server-side 404 logic.
193
193
 
194
+ ## Try it
195
+
196
+ A live `Router` instance is registered with the sample routes below. Type a URL on the left and watch `Router.resolve()` produce a `RouteMatch` on the right — the same code path the framework uses at runtime.
197
+
198
+ <Ch02RouteResolver />
199
+
194
200
  ## Next
195
201
 
196
202
  - [Middleware](./03-middleware.md) — gating navigation, redirects, denies
@@ -208,6 +208,12 @@ async function rateLimitGuard(ctx: NavigationContext) {
208
208
  - **`deny()` in `afterLoad` discards the produced page.** The controller already ran; deny only blocks the response. If `execute()` had side effects (writes), they already happened.
209
209
  - **Browser-side guards do not have access to request headers.** `getHeader()` returns `null` on the client. Cookies still work.
210
210
 
211
+ ## Try it
212
+
213
+ Build a `beforeLoad` chain of three guards, pick the result each one returns, then run it through the **real** `runBeforeLoadGuards()` from `@finesoft/core`. The pipeline below shows where the chain short-circuits and what the final `MiddlewareResult` looks like.
214
+
215
+ <Ch03MiddlewarePlayground />
216
+
211
217
  ## Next
212
218
 
213
219
  - [Rendering & hydration](./04-rendering-and-hydration.md) — what happens between `afterLoad` and HTML output
@@ -191,6 +191,12 @@ const match = framework.router.resolve("/products/42");
191
191
 
192
192
  未命中返回 `null` —— 在服务端 404 逻辑里处理它。
193
193
 
194
+ ## 实时演示
195
+
196
+ 下方注册了一个真实的 `Router` 实例(包含示例路由)。在左侧输入 URL,右侧实时显示 `Router.resolve()` 的 `RouteMatch` —— 与框架运行时走的是同一段代码。
197
+
198
+ <Ch02RouteResolver />
199
+
194
200
  ## 下一步
195
201
 
196
202
  - [中间件](./03-middleware.md) —— 守卫导航、重定向、拒绝
@@ -208,6 +208,12 @@ async function rateLimitGuard(ctx: NavigationContext) {
208
208
  - **`afterLoad` 里的 `deny()` 会丢弃已产出的页面。** Controller 已经跑过了;deny 只阻断响应。如果 `execute()` 有副作用(写操作),副作用已经发生。
209
209
  - **浏览器端守卫拿不到请求头。** `getHeader()` 在客户端返回 `null`。cookie 仍然可用。
210
210
 
211
+ ## 实时演示
212
+
213
+ 构造一条三守卫的 `beforeLoad` 链,逐个选定每个 guard 的返回值,然后交给 `@finesoft/core` 里**真实**的 `runBeforeLoadGuards()` 跑一次。下方流水线会显示链路在哪一步短路、最终的 `MiddlewareResult` 是什么。
214
+
215
+ <Ch03MiddlewarePlayground />
216
+
211
217
  ## 下一步
212
218
 
213
219
  - [渲染与 Hydration](./04-rendering-and-hydration.md) —— `afterLoad` 到 HTML 输出之间发生什么
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finesoft/front",
3
- "version": "0.1.77",
3
+ "version": "0.2.0",
4
4
  "description": "Full-stack framework: router, DI, actions, SSR, and server — all in one package",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -8,8 +8,8 @@
8
8
  "dist/browser.mjs",
9
9
  "dist/index.d.mts",
10
10
  "dist/index.mjs",
11
- "dist/server-data-DGbiKzMS.d.mts",
12
- "dist/start-app-BdXBCcor.mjs",
11
+ "dist/server-data-DQzknR97.d.mts",
12
+ "dist/start-app-S4DH-40i.mjs",
13
13
  "docs",
14
14
  "README.md"
15
15
  ],
@@ -1,2 +0,0 @@
1
- const e={FLOW:`flow`,EXTERNAL_URL:`externalUrl`,COMPOUND:`compound`};function t(t){return t.kind===e.FLOW}function n(t){return t.kind===e.EXTERNAL_URL}function r(t){return t.kind===e.COMPOUND}function i(t,n){return{kind:e.FLOW,url:t,presentationContext:n}}function a(t){return{kind:e.EXTERNAL_URL,url:t}}var o=class{handlers=new Map;onAction(e,t){if(this.handlers.has(e)){console.warn(`[ActionDispatcher] kind="${e}" already registered, skipping`);return}this.handlers.set(e,t)}removeAction(e){return this.handlers.delete(e)}async perform(e,t=0){if(r(e)){if(t>=32)throw Error(`[ActionDispatcher] CompoundAction recursion depth exceeded (max 32)`);for(let n of e.actions)await this.perform(n,t+1);return}let n=this.handlers.get(e.kind);if(!n){console.warn(`[ActionDispatcher] No handler for kind="${e.kind}"`);return}await n(e)}},s=class{controllers=new Map;register(e){this.controllers.set(e.intentId,e)}async dispatch(e,t){let n=this.controllers.get(e.id);if(!n)throw Error(`[IntentDispatcher] No controller for "${e.id}". Registered: [${Array.from(this.controllers.keys()).join(`, `)}]`);return n.perform(e,t)}has(e){return this.controllers.has(e)}},c=class e{registrations=new Map;resolutionStack=new Set;parent;children=new Set;register(e,t,n=!0){return this.registrations.set(e,{factory:t,singleton:n}),this}resolve(e){let t=this.registrations.get(e);if(!t){if(this.parent)return this.parent.resolve(e);throw Error(`[Container] No registration for key: "${e}"`)}if(t.singleton){if(t.instance===void 0){if(this.resolutionStack.has(e))throw Error(`[Container] Circular dependency detected: ${[...this.resolutionStack,e].join(` → `)}`);this.resolutionStack.add(e);try{t.instance=t.factory()}finally{this.resolutionStack.delete(e)}}return t.instance}return t.factory()}has(e){return this.registrations.has(e)||(this.parent?.has(e)??!1)}createScope(){let t=new e;return t.parent=this,this.children.add(t),t}dispose(){let e=Array.from(this.children);for(let t of e)t.dispose();this.children.clear();for(let e of this.registrations.values())e.instance=void 0;this.registrations.clear(),this.parent&&=(this.parent.children.delete(this),void 0)}};const l=new Set([`ar`,`arc`,`dv`,`fa`,`ha`,`he`,`khw`,`ks`,`ku`,`ps`,`ur`,`yi`]);function u(e){let t=e.split(`-`)[0].toLowerCase();return l.has(t)}function d(e){return u(e)?`rtl`:`ltr`}function f(e){return{lang:e,dir:d(e)}}function p(e,t){return{language:e,region:t,bcp47:t?`${e}-${t}`:e,dir:d(e)}}function m(e){document.documentElement.lang=e.lang,document.documentElement.dir=e.dir}function h(e,t){let n=e.split(`?`)[0].match(/^\/([^/]+)(\/.*)?$/);if(!n)return null;let r=n[1],i=t.find(e=>e.toLowerCase()===r.toLowerCase());return i?{locale:i,strippedUrl:n[2]||`/`}:null}async function g(e,t){if(!e||!t)return;let n=await _();if(n)return n(e,t)}async function _(){if(typeof globalThis.__FINESOFT_I18N_LOADER__==`function`)return globalThis.__FINESOFT_I18N_LOADER__;if(typeof __FINESOFT_I18N_LOADER_SPECIFIER__==`string`)try{let e=await import(__FINESOFT_I18N_LOADER_SPECIFIER__),t=typeof e==`function`?e:typeof e?.loadMessages==`function`?e.loadMessages:void 0;return t?(globalThis.__FINESOFT_I18N_LOADER__=t,t):void 0}catch{return}}async function v(e){let{locale:t,loadMessages:n,context:r}=e;if(!(!t||!r))return n?n(t,r):g(t,r)}function y(e,t){let n=Object.entries(e);if(n.length===0)return;if(typeof n[0][1]==`string`)return e;let r=e[t];if(!r)return;let i={};for(let[e,t]of Object.entries(r)){if(typeof t==`string`){i[e]=t;continue}for(let[n,r]of Object.entries(t))i[`${e}.${n}`]=r}return i}function b(e,t){return t?e.replace(/\{(\w+)\}/g,(e,n)=>{let r=t[n];return r===void 0?`{${n}}`:String(r)}):e}function x(e){return e===1?`one`:`other`}function S(e,t){return`${e}.${t}`}var C=class{locale;messages;pluralRule;fallback;constructor(e){this.locale=e.locale,this.messages=e.messages,this.pluralRule=e.pluralRule??x,this.fallback=e.fallback??(e=>e)}t(e,t){let n=this.messages[e];return n===void 0?this.fallback(e):b(n,t)}plural(e,t,n){let r=S(e,this.pluralRule(t)),i={count:t,...n};return this.t(r,i)}},w=class{constructor(e){this.factories=e}loggerFor(e){return new T(this.factories.map(t=>t.loggerFor(e)))}},T=class{constructor(e){this.loggers=e}debug(...e){return this.callAll(`debug`,e)}info(...e){return this.callAll(`info`,e)}warn(...e){return this.callAll(`warn`,e)}error(...e){return this.callAll(`error`,e)}callAll(e,t){for(let n of this.loggers)n[e](...t);return``}},E=class{category;constructor(e){this.category=e}};const ee={"*":4,debug:4,info:3,warn:2,error:1,off:0,"":0};let D,O;function te(){if(globalThis.localStorage===void 0)return{};let e;try{e=globalThis.localStorage.getItem(`onyxLog`)}catch{return{}}if(!e)return{};if(e===O&&D)return D;O=e;let t={},n=e.split(`,`);for(let e of n){let[n,r]=e.trim().split(`=`);if(!n||r===void 0)continue;let i=ee[r.toLowerCase()]??void 0;i!==void 0&&(n===`*`?t.defaultLevel=i:(t.named??={},t.named[n]=i))}return D=t,t}function k(e,t){let n=te();if(n.defaultLevel===void 0&&!n.named)return!0;let r=ee[t]??4;return n.named?.[e]===void 0?n.defaultLevel===void 0?!0:r<=n.defaultLevel:r<=n.named[e]}function ne(){D=void 0,O=void 0}var A=class extends E{debug(...e){return k(this.category,`debug`)&&console.debug(`[${this.category}]`,...e),``}info(...e){return k(this.category,`info`)&&console.info(`[${this.category}]`,...e),``}warn(...e){return k(this.category,`warn`)&&console.warn(`[${this.category}]`,...e),``}error(...e){return console.error(`[${this.category}]`,...e),``}},re=class{loggerFor(e){return new A(e)}};const j={debug:0,info:1,warn:2,error:3};var M=class extends E{minPriority;report;constructor(e,t){super(e),this.minPriority=j[t.minLevel??`warn`],this.report=t.report}debug(...e){return this.maybeReport(`debug`,e),``}info(...e){return this.maybeReport(`info`,e),``}warn(...e){return this.maybeReport(`warn`,e),``}error(...e){return this.maybeReport(`error`,e),``}maybeReport(e,t){if(!(j[e]<this.minPriority))try{this.report(e,this.category,t)}catch(e){console.error(`[ReportingLogger] report callback threw:`,e)}}},N=class{options;constructor(e){this.options=e}loggerFor(e){return new M(e,this.options)}},P=class{prefix;constructor(e=`Metrics`){this.prefix=e}record(e,t){console.info(`[${this.prefix}:${e}]`,t??``)}async flush(){}destroy(){}};function F(e){let t=e??(typeof navigator<`u`?navigator.userAgent:``),n=t.toLowerCase();return{os:ie(n),browser:ae(n),engine:oe(n),isMobile:/mobile|android|iphone|ipad|ipod/i.test(t),isTouch:typeof navigator<`u`&&`maxTouchPoints`in navigator?navigator.maxTouchPoints>0:!1}}function ie(e){return/iphone|ipad|ipod/.test(e)?`ios`:/android/.test(e)?`android`:/macintosh|mac os x/.test(e)?`macos`:/windows/.test(e)?`windows`:/linux/.test(e)?`linux`:`unknown`}function ae(e){return/edg\//.test(e)?`edge`:/opr\/|opera/.test(e)?`opera`:/samsungbrowser/.test(e)?`samsung`:/chrome|crios/.test(e)&&!/edg\//.test(e)?`chrome`:/firefox|fxios/.test(e)?`firefox`:/safari/.test(e)&&!/chrome/.test(e)?`safari`:`unknown`}function oe(e){return/applewebkit/.test(e)&&!/chrome/.test(e)?`webkit`:/applewebkit/.test(e)&&/chrome/.test(e)?`blink`:/gecko\//.test(e)?`gecko`:`unknown`}const I={LOGGER:`logger`,LOGGER_FACTORY:`loggerFactory`,NET:`net`,STORAGE:`storage`,FEATURE_FLAGS:`featureFlags`,METRICS:`metrics`,FETCH:`fetch`,EVENT_RECORDER:`eventRecorder`,LOCALE:`locale`,PLATFORM:`platform`,TRANSLATOR:`translator`};var se=class{store=new Map;get(e){return this.store.get(e)}set(e,t){this.store.set(e,t)}delete(e){this.store.delete(e)}},ce=class{flags;providers=[];constructor(e={}){this.flags=e}addProvider(e){this.providers.push(e)}isEnabled(e){for(let t=this.providers.length-1;t>=0;t--)if(this.providers[t].isEnabled(e))return!0;return this.flags[e]===!0}getString(e){for(let t=this.providers.length-1;t>=0;t--){let n=this.providers[t].getString?.(e);if(n!==void 0)return n}let t=this.flags[e];return typeof t==`string`?t:void 0}getNumber(e){for(let t=this.providers.length-1;t>=0;t--){let n=this.providers[t].getNumber?.(e);if(n!==void 0)return n}let t=this.flags[e];return typeof t==`number`?t:void 0}},le=class{record(e,t){console.info(`[Metrics:${e}]`,t??``)}recordPageView(e,t){this.record(`PageView`,{page:e,...t})}recordEvent(e,t){this.record(`Event`,{name:e,...t})}};function L(e,t={}){let{_resolvedMessages:n}=t,{fetch:r=globalThis.fetch?.bind(globalThis),featureFlags:i={},featureFlagsProviders:a=[],reportCallback:o,eventRecorder:s,locale:c,platform:l}=t,u=new re,d=o?new w([u,new N({report:o})]):u;e.register(I.LOGGER_FACTORY,()=>d),e.register(I.LOGGER,()=>d.loggerFor(`framework`)),e.register(I.NET,()=>({fetch:(e,t)=>r(e,t)})),e.register(I.STORAGE,()=>new se);let p=new ce(i);for(let e of a)p.addProvider(e);if(e.register(I.FEATURE_FLAGS,()=>p),e.register(I.METRICS,()=>new le),e.register(I.EVENT_RECORDER,()=>s??new P),c&&e.register(I.LOCALE,()=>f(c)),c&&n){let t=y(n,c);t&&e.register(I.TRANSLATOR,()=>new C({locale:c,messages:t}))}e.register(I.PLATFORM,()=>l??F(typeof navigator<`u`?navigator.userAgent:void 0)),e.register(I.FETCH,()=>r)}function R(e){return Object.assign(Object.create(null),e)}var z=class{routes=[];add(e,t,n){let r=typeof n==`string`?{renderMode:n}:n??{},i=[],a=e.split(/(\/:[\w]+\??)/).map(t=>{let n=t.match(/^\/:(\w+)(\?)?$/);if(n){if(i.includes(n[1]))throw Error(`[Router] Duplicate parameter name ":${n[1]}" in pattern "${e}"`);return i.push(n[1]),n[2]?`(?:/([^/]+))?`:`/([^/]+)`}return t.replace(/[.+?^${}()|[\]\\]/g,`\\$&`)}).join(``);return this.routes.push({pattern:e,intentId:t,regex:RegExp(`^${a}/?$`),paramNames:i,renderMode:r.renderMode,beforeGuards:r.beforeGuards,afterGuards:r.afterGuards}),this}resolve(e){let{path:t,queryParams:n}=this.parseUrl(e);for(let r of this.routes){let a=t.match(r.regex);if(a){let t=R(n);return r.paramNames.forEach((e,n)=>{let r=a[n+1];r&&(t[e]=r)}),{intent:{id:r.intentId,params:t},action:i(e),renderMode:r.renderMode,beforeGuards:r.beforeGuards,afterGuards:r.afterGuards}}}return null}getRoutes(){return this.routes.map(e=>`${e.pattern} → ${e.intentId}`)}parseUrl(e){try{let t=new URL(e,`http://localhost`),n=R(Object.fromEntries(t.searchParams));return{path:t.pathname,queryParams:n}}catch{return{path:e.split(`?`)[0].split(`#`)[0],queryParams:R()}}}};async function B(e,t){for(let n of e){let e=await n(t);if(e.kind!==`next`)return e}return{kind:`next`}}async function V(e,t){for(let n of e){let e=await n(t);if(e.kind!==`next`)return e}return{kind:`next`}}const H=new WeakMap;function U(e){return W(e,new Set,0)}function W(e,t,n){if(e==null)return String(e);if(typeof e!=`object`)return JSON.stringify(e);let r=H.get(e);if(r!==void 0)return r;if(n>50)return`"[Max Depth]"`;if(t.has(e))return`"[Circular]"`;t.add(e);try{let r;if(Array.isArray(e))r=`[`+e.map(e=>W(e,t,n+1)).join(`,`)+`]`;else{let i=Object.keys(e).sort(),a=[];for(let r of i){let i=e[r];i!==void 0&&a.push(JSON.stringify(r)+`:`+W(i,t,n+1))}r=`{`+a.join(`,`)+`}`}return H.set(e,r),r}finally{t.delete(e)}}var G=class e{intents;constructor(e){this.intents=e}static fromArray(t){let n=new Map;for(let e of t)if(e.intent&&e.data!==void 0){let t=U(e.intent);n.set(t,e.data)}return new e(n)}static empty(){return new e(new Map)}get(e){let t=U(e),n=this.intents.get(t);if(n!==void 0)return this.intents.delete(t),n}has(e){return this.intents.has(U(e))}get size(){return this.intents.size}},K=class e{container;intentDispatcher;actionDispatcher;router;prefetchedIntents;beforeGuards=[];afterGuards=[];_logger;constructor(e,t){this.container=e,this.intentDispatcher=new s,this.actionDispatcher=new o,this.router=new z,this.prefetchedIntents=t}static create(t={}){let n=new c;L(n,t);let r=new e(n,t.prefetchedIntents??G.empty());return t.setupRoutes?.(r.router),r}getLogger(){return this._logger??=this.container.resolve(I.LOGGER)}async dispatch(e){let t=this.getLogger(),n=this.prefetchedIntents.get(e);return n===void 0?(t.debug(`[Framework] dispatch intent: ${e.id}`,e.params),this.intentDispatcher.dispatch(e,this.container)):(t.debug(`[Framework] re-using prefetched intent response for: ${e.id}`,e.params),n)}async perform(e){return this.getLogger().debug(`[Framework] perform action: ${e.kind}`),this.actionDispatcher.perform(e)}routeUrl(e){return this.router.resolve(e)}didEnterPage(e){this.container.resolve(I.METRICS).recordPageView(e.pageType,{pageId:e.id,title:e.title})}getLocale(){return this.container.has(I.LOCALE)?this.container.resolve(I.LOCALE):void 0}getTranslator(){return this.container.has(I.TRANSLATOR)?this.container.resolve(I.TRANSLATOR):void 0}getPlatform(){return this.container.resolve(I.PLATFORM)}onAction(e,t){this.actionDispatcher.onAction(e,t)}registerIntent(e){this.intentDispatcher.register(e)}beforeLoad(e){this.beforeGuards.push(e)}afterLoad(e){this.afterGuards.push(e)}runBeforeLoad(e,t){return B(t?.length?[...this.beforeGuards,...t]:this.beforeGuards,e)}runAfterLoad(e,t){return V(t?.length?[...this.afterGuards,...t]:this.afterGuards,e)}dispose(){this.container.dispose()}},q=class extends Error{constructor(e,t,n){super(`HTTP ${e}: ${t}`),this.status=e,this.statusText=t,this.body=n,this.name=`HttpError`}},ue=class{baseUrl;defaultHeaders;fetchFn;requestInterceptors;responseInterceptors;constructor(e){this.baseUrl=e.baseUrl,this.defaultHeaders=e.defaultHeaders??{},this.fetchFn=e.fetch??globalThis.fetch.bind(globalThis),this.requestInterceptors=[...e.requestInterceptors??[]],this.responseInterceptors=[...e.responseInterceptors??[]]}useRequestInterceptor(e){return this.requestInterceptors.push(e),this}useResponseInterceptor(e){return this.responseInterceptors.push(e),this}async get(e,t){return this.request(`GET`,e,{params:t})}async post(e,t,n){return this.request(`POST`,e,{body:t,params:n})}async put(e,t,n){return this.request(`PUT`,e,{body:t,params:n})}async del(e,t){return this.request(`DELETE`,e,{params:t})}async request(e,t,n){let r=this.buildUrl(t,n?.params),i={...this.defaultHeaders,...n?.headers},a={method:e,headers:i};n?.body!==void 0&&(Object.keys(i).some(e=>e.toLowerCase()===`content-type`)||(i[`Content-Type`]=`application/json`),a.body=JSON.stringify(n.body));for(let e of this.requestInterceptors)a=await e(r,a);let o=await this.fetchFn(r,a);for(let e of this.responseInterceptors)o=await e(o,r);if(!o.ok){let e=await o.text().catch(()=>void 0);throw new q(o.status,o.statusText,e)}try{return await o.json()}catch(e){throw e instanceof SyntaxError?new q(o.status,`Invalid JSON response`,await o.text().catch(()=>void 0)):e}}buildUrl(e,t){let n=this.baseUrl.endsWith(`/`)?this.baseUrl.slice(0,-1):this.baseUrl,r=e.startsWith(`/`)?e:`/${e}`,i=new URL(`${n}${r}`,`http://placeholder`);if(t)for(let[e,n]of Object.entries(t))i.searchParams.set(e,n);return this.baseUrl.startsWith(`http`)?i.toString():`${i.pathname}${i.search}`}},de=class{fallback(e,t){throw t}async perform(e,t){let n=e.params??{};try{return await this.execute(n,t)}catch(e){return this.fallback(n,e instanceof Error?e:Error(String(e)))}}};function fe(...e){return t=>e.reduce((e,t)=>t(e),t)}function pe(...e){return async t=>{let n=t;for(let t of e)n=await t(n);return n}}function me(e){return t=>t.map(e)}function he(e,t,n){let r=new Set;for(let i of t){i.controller&&!r.has(i.intentId)&&(e.registerIntent(i.controller),r.add(i.intentId));let t={renderMode:i.renderMode,beforeGuards:i.beforeLoad,afterGuards:i.afterLoad};if(e.router.add(i.path,i.intentId,t),n?.locales?.length){let n=i.path===`/`?`/:locale`:`/:locale${i.path}`;e.router.add(n,i.intentId,t)}}}var ge=class{map=new Map;capacity;constructor(e){if(e<1)throw Error(`[LruMap] capacity must be >= 1, got ${e}`);this.capacity=e}get(e){if(!this.map.has(e))return;let t=this.map.get(e);return this.map.delete(e),this.map.set(e,t),t}set(e,t){if(this.map.has(e))this.map.delete(e);else if(this.map.size>=this.capacity){let e=this.map.keys().next().value;e!==void 0&&this.map.delete(e)}this.map.set(e,t)}has(e){return this.map.has(e)}delete(e){return this.map.delete(e)}get size(){return this.map.size}clear(){this.map.clear()}};function _e(e){return e!=null}function ve(e){return e==null}function ye(){return typeof window>`u`?`browser`:document.referrer.startsWith(`android-app://`)?`twa`:window.matchMedia(`(display-mode: standalone)`).matches||`standalone`in window.navigator&&window.navigator.standalone===!0?`standalone`:`browser`}function be(e){return e.replace(/^https?:\/\//,``)}function xe(e){try{let t=new URL(e);return t.pathname+t.search+t.hash}catch{return e}}function Se(e){return e.split(`?`)[0]}function Ce(e){return e.split(`?`)[0].split(`#`)[0]}function we(e,t){if(!t)return e;let n=new URLSearchParams;for(let[e,r]of Object.entries(t))r!==void 0&&n.set(e,r);let r=n.toString();return r?`${e}?${r}`:e}function J(){return typeof crypto<`u`&&crypto.randomUUID?crypto.randomUUID():`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e===`x`?t:t&3|8).toString(16)})}function Y(e){let t=new Map;if(!e)return t;for(let n of e.split(`;`)){let e=n.indexOf(`=`);if(e===-1)continue;let r=n.slice(0,e).trim(),i=n.slice(e+1).trim();r&&t.set(r,i)}return t}function Te(e){let{url:t,intent:n,container:r,request:i}=e,a=new URL(t,`http://localhost`),o=Y(i?.headers.get(`cookie`)??``);return{url:t,path:a.pathname,params:n.params??{},intent:n,isServer:!0,container:r,getCookie:e=>o.get(e),getHeader:e=>i?.headers.get(e)??void 0}}function X(e){let{url:t,intent:n,container:r}=e;return{url:t,path:new URL(t,window.location.origin).pathname,params:n.params??{},intent:n,isServer:!1,container:r,getCookie:e=>Y(document.cookie).get(e),getHeader:()=>void 0}}function Ee(){return{kind:`next`}}function De(e,t=302){return{kind:`redirect`,url:e,status:t}}function Oe(e){return{kind:`rewrite`,url:e}}function ke(e=403,t=`Forbidden`){return{kind:`deny`,status:e,message:t}}var Ae=class{recorders;constructor(e){this.recorders=e}record(e,t){for(let n of this.recorders)n.record(e,t)}async flush(){let e=[];for(let t of this.recorders)t.flush&&e.push(t.flush());await Promise.all(e)}destroy(){for(let e of this.recorders)e.destroy?.()}},je=class{observer;tracked=new Map;captured=[];minDuration;constructor(e={}){this.minDuration=e.minVisibleDuration??1e3,this.observer=new IntersectionObserver(e=>{let t=Date.now();for(let n of e){let e=this.tracked.get(n.target);e&&(n.isIntersecting?e.visibleSince===null&&(e.visibleSince=t):e.visibleSince!==null&&(t-e.visibleSince>=this.minDuration&&this.captured.push({id:e.id,timestamp:e.visibleSince,metadata:e.metadata}),e.visibleSince=null))}},{threshold:e.threshold??.5})}observe(e,t,n){this.tracked.set(e,{id:t,metadata:n,visibleSince:null}),this.observer.observe(e)}unobserve(e){this.observer.unobserve(e),this.tracked.delete(e)}consume(){let e=Date.now();for(let[,t]of this.tracked)t.visibleSince!==null&&e-t.visibleSince>=this.minDuration&&(this.captured.push({id:t.id,timestamp:t.visibleSince,metadata:t.metadata}),t.visibleSince=e);return this.captured.splice(0)}destroy(){this.observer.disconnect(),this.tracked.clear(),this.captured.length=0}},Me=class{record(){}async flush(){}destroy(){}},Ne=class{constructor(e,t){this.inner=e,this.providers=t}record(e,t){let n={};for(let e of this.providers)Object.assign(n,e.getFields());t&&Object.assign(n,t),this.inner.record(e,n)}async flush(){return this.inner.flush?.()}destroy(){this.inner.destroy?.()}};function Pe(t){let{framework:n,log:r}=t;n.onAction(e.EXTERNAL_URL,e=>{r.debug(`ExternalUrlAction → ${e.url}`),window.open(e.url,`_blank`,`noopener,noreferrer`)})}const Z=5e3;let Q=null;function $(){Q?.()}function Fe(e,t,n){$();let r=Math.max(0,n),i=Date.now(),a=!1,o=null,s=null,c=null,l=null;Q=p,f(),document.addEventListener(`load`,u,!0),s=setInterval(u,100),c=setTimeout(u,Z),u();function u(){a||o!==null||(o=requestAnimationFrame(()=>{o=null,d()}))}function d(){if(a)return;let n=Date.now()-i,o=t();if(!o){n>=Z&&(e.warn(`tryScroll: timed out waiting for the scrollable element`,{target:r,elapsedMs:n}),p());return}o.scrollTop=r;let s=o.scrollTop;if(s>=r-2){e.info(`scroll restored`,{target:r,actual:s,elapsedMs:n}),p();return}n>=Z&&(e.warn(`tryScroll: timed out before reaching the target`,{target:r,actual:s,elapsedMs:n,scrollHeight:o.scrollHeight,clientHeight:o.clientHeight}),p())}function f(){if(typeof MutationObserver>`u`)return;let e=document.body??document.documentElement;e&&(l=new MutationObserver(()=>{u()}),l.observe(e,{childList:!0,subtree:!0}))}function p(){a||(a=!0,Q===p&&(Q=null),o!==null&&(cancelAnimationFrame(o),o=null),s!==null&&(clearInterval(s),s=null),c!==null&&(clearTimeout(c),c=null),l?.disconnect(),l=null,document.removeEventListener(`load`,u,!0))}}var Ie=class{entries;log;getScrollablePageElement;currentStateId;constructor(e,t,n=10){this.entries=new ge(n),this.log=e,this.getScrollablePageElement=t.getScrollablePageElement}replaceState(e,t){$();let n=J();window.history.replaceState({id:n},``,t),this.currentStateId=n,this.entries.set(n,{state:e,scrollY:0}),this.scrollTop=0,this.log.info(`replaceState`,e,t,n)}pushState(e,t){$();let n=J();window.history.pushState({id:n},``,t),this.currentStateId=n,this.entries.set(n,{state:e,scrollY:0}),this.scrollTop=0,this.log.info(`pushState`,e,t,n)}beforeTransition(){$();let{state:e}=window.history;if(!e)return;let t=this.entries.get(e.id);if(!t){this.log.info(`current history state evicted from LRU, not saving scroll position`);return}let{scrollTop:n}=this;this.entries.set(e.id,{...t,scrollY:n}),this.log.info(`saving scroll position`,n)}onPopState(e){window.addEventListener(`popstate`,t=>{$(),this.currentStateId=t.state?.id,this.currentStateId||this.log.warn(`encountered a null event.state.id in onPopState event:`,window.location.href),this.log.info(`popstate`,this.entries,this.currentStateId);let n=this.currentStateId?this.entries.get(this.currentStateId):void 0;if(Promise.resolve(e(window.location.href,n?.state)).catch(e=>{this.log.error(`onPopState listener error:`,e)}),!n)return;let{scrollY:r}=n;this.log.info(`restoring scroll to`,r),Fe(this.log,()=>this.getScrollablePageElement(),r)})}pushUrl(e){$();let t=J();window.history.pushState({id:t},``,e),this.currentStateId=t,this.scrollTop=0,this.log.info(`pushUrl (no state)`,e,t)}replaceUrl(e){$();let t=J();window.history.replaceState({id:t},``,e),this.currentStateId=t,this.scrollTop=0,this.log.info(`replaceUrl (no state)`,e,t)}updateState(e){if(!this.currentStateId){this.log.warn(`failed: encountered a null currentStateId inside updateState`);return}let t=this.entries.get(this.currentStateId),n=e(t?.state);this.log.info(`updateState`,n,this.currentStateId),this.entries.set(this.currentStateId,{scrollY:t?.scrollY??0,state:n})}get scrollTop(){return this.getScrollablePageElement()?.scrollTop||0}set scrollTop(e){let t=this.getScrollablePageElement();t&&(t.scrollTop=e)}};function Le(t){let{framework:n,log:r,callbacks:i,updateApp:a}=t,o=!0,s=0,c=new Ie(r,{getScrollablePageElement:t.getScrollablePageElement??(()=>document.getElementById(`scrollable-page-override`)||document.getElementById(`scrollable-page`)||document.documentElement)});async function l(e,t,d){if(t>=5){r.error(`Navigation redirect loop detected (5 redirects), stopping at: ${e}`);return}let f=o||e===window.location.pathname+window.location.search,p=n.routeUrl(e);if(!p){r.warn(`FlowAction: no route for ${e}`);return}let m=X({url:e,intent:p.intent,container:n.container}),h=await n.runBeforeLoad(m,p.beforeGuards);if(h.kind===`redirect`){r.debug(`beforeLoad → redirect to ${h.url}`),await l(h.url,t+1,d);return}if(h.kind===`deny`){r.warn(`beforeLoad → denied (${h.status}): ${h.message}`);return}if(h.kind===`rewrite`){r.debug(`beforeLoad → rewrite to ${h.url}`),await l(h.url,t+1,d);return}let g=n.dispatch(p.intent);if(await Promise.race([g,new Promise(e=>setTimeout(e,500))]).catch(()=>{}),d!==s){r.info(`FlowAction superseded by newer navigation`,e);return}c.beforeTransition(),a({page:g.then(async a=>{if(d!==s)return r.info(`FlowAction commit superseded`,e),a;let o={...m,page:a},h=await n.runAfterLoad(o,p.afterGuards);if(h.kind===`redirect`)return r.debug(`afterLoad → redirect to ${h.url}`),l(h.url,t+1,d),a;let g=e;return h.kind===`rewrite`&&(g=h.url,r.debug(`afterLoad → rewrite URL to ${g}`)),h.kind===`deny`?(r.warn(`afterLoad → denied (${h.status})`),a):(f?c.replaceState({page:a},g):c.pushState({page:a},g),i.onNavigate(new URL(g,window.location.origin).pathname),u(a),a)},t=>{if(d===s){let t=e;f?c.replaceUrl(t):c.pushUrl(t),i.onNavigate(new URL(t,window.location.origin).pathname)}throw t}),isFirstPage:o}),o=!1}n.onAction(e.FLOW,async e=>{let t=e,a=t.url;if(r.debug(`FlowAction → ${a}`),t.presentationContext===`modal`){let e=n.routeUrl(a);if(e){let t=await n.dispatch(e.intent);i.onModal(t)}return}await l(a,0,++s)}),c.onPopState(async(e,t)=>{if(r.debug(`popstate → ${e}, cached=${!!t}`),i.onNavigate(new URL(e).pathname),t){let{page:e}=t;u(e),a({page:e,isFirstPage:o});return}let c=new URL(e),d=n.routeUrl(c.pathname+c.search);if(!d){r.error(`received popstate without data, but URL was unroutable:`,e),u(null),a({page:Promise.reject(Error(`404`)),isFirstPage:o});return}let f=X({url:c.pathname+c.search,intent:d.intent,container:n.container}),p=await n.runBeforeLoad(f,d.beforeGuards);if(p.kind===`redirect`){r.debug(`popstate beforeLoad → redirect to ${p.url}`);let e=++s;await l(p.url,0,e);return}if(p.kind===`deny`||p.kind===`rewrite`){if(p.kind===`deny`)r.warn(`popstate beforeLoad → denied`);else{let e=++s;await l(p.url,0,e)}return}let m=n.dispatch(d.intent);await Promise.race([m,new Promise(e=>setTimeout(e,500))]).catch(()=>{}),a({page:m.then(async e=>{let t={...f,page:e},a=await n.runAfterLoad(t,d.afterGuards);if(a.kind===`redirect`){r.debug(`popstate afterLoad → redirect to ${a.url}`);let t=++s;return l(a.url,0,t),e}if(a.kind===`rewrite`){r.debug(`popstate afterLoad → rewrite URL to ${a.url}`);let t=window.history.state?.id;return window.history.replaceState({id:t},``,a.url),i.onNavigate(new URL(a.url,window.location.origin).pathname),u(e),e}return a.kind===`deny`?(r.warn(`popstate afterLoad → denied (${a.status})`),e):(u(e),e)}),isFirstPage:o})});function u(e){(async()=>{try{e&&n.didEnterPage(e)}catch(e){r.error(`didEnterPage error:`,e)}})()}}function Re(e){let{framework:t,log:n,callbacks:r,updateApp:i}=e;Le({framework:t,log:n,callbacks:r,updateApp:i,getScrollablePageElement:e.getScrollablePageElement}),Pe({framework:t,log:n})}function ze(){let e=document.getElementById(`serialized-server-data`);if(e?.textContent){e.parentNode?.removeChild(e);try{return JSON.parse(e.textContent)}catch{return}}}function Be(){let e=ze();return!e||!Array.isArray(e)?G.empty():G.fromArray(e)}async function Ve(e){let{bootstrap:t,mountId:n=`app`,mount:r,callbacks:i,onBeforeStart:a,onAfterStart:o,frameworkConfig:s={},loadMessages:c}=e,l=Be(),u=window.location.pathname+window.location.search,d=He(s.locale),f=await v({locale:d,loadMessages:c,context:d?{runtime:`browser`,fetch:Ue(s.fetch),url:u}:void 0}),p=K.create({...s,locale:d,_resolvedMessages:f,prefetchedIntents:l});t(p);let h=p.container.resolve(I.LOGGER_FACTORY).loggerFor(`browser`),g=p.getLocale();g&&(m(g),h.debug(`[startBrowserApp] Applied locale attributes:`,g)),await a?.(p);let _=p.routeUrl(u),y=document.getElementById(n);if(!y)throw Error(`[startBrowserApp] Mount target not found: #${n}. Ensure your HTML has <div id="${n}"></div>.`);let b=r(y,{framework:p});Re({framework:p,log:h,callbacks:i,updateApp:b,getScrollablePageElement:e.getScrollablePageElement}),_?await p.perform(_.action):b({page:Promise.reject(Error(`404`)),isFirstPage:!0}),await o?.(p)}function He(e){return e||document.documentElement.lang.trim()||void 0}function Ue(e){return(e??globalThis.fetch?.bind(globalThis))||(()=>{throw Error(`[startBrowserApp] loadMessages requires a fetch implementation.`)})}export{T as $,me as A,B,Se as C,_e as D,ve as E,q as F,P as G,I as H,K as I,A as J,M as K,G as L,pe as M,de as N,ge as O,ue as P,E as Q,U as R,xe as S,ye as T,L as U,z as V,F as W,ne as X,re as Y,k as Z,X as _,r as _t,Le as a,v as at,we as b,a as bt,Pe as c,d as ct,je as d,h as dt,w as et,Ae as f,m as ft,Oe as g,e as gt,De as h,o as ht,Re as i,S as it,fe as j,he as k,Ne as l,u as lt,Ee as m,s as mt,Be as n,x as nt,Ie as o,y as ot,ke as p,c as pt,N as q,ze as r,b as rt,Fe as s,f as st,Ve as t,C as tt,Me as u,p as ut,Te as v,n as vt,be as w,Ce as x,i as xt,J as y,t as yt,V as z};
2
- //# sourceMappingURL=start-app-BdXBCcor.mjs.map