@finesoft/front 0.1.52 → 0.1.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +253 -0
- package/dist/index.d.mts +392 -13
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +583 -95
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,6 +89,259 @@ function authGuard(ctx: NavigationContext) {
|
|
|
89
89
|
|
|
90
90
|
Results: `next()`, `redirect(url, status?)`, `rewrite(url)`, `deny(status?, message?)`.
|
|
91
91
|
|
|
92
|
+
## Lifecycle Hooks
|
|
93
|
+
|
|
94
|
+
`startBrowserApp` provides hooks for initialization:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { startBrowserApp } from "@finesoft/front/browser";
|
|
98
|
+
|
|
99
|
+
startBrowserApp({
|
|
100
|
+
bootstrap,
|
|
101
|
+
// Pass framework config so locale/reporting/etc. are wired into Framework
|
|
102
|
+
frameworkConfig: {
|
|
103
|
+
locale: "zh-Hans",
|
|
104
|
+
},
|
|
105
|
+
mount: (target, { framework }) => {
|
|
106
|
+
/* ... */
|
|
107
|
+
},
|
|
108
|
+
callbacks: { onNavigate, onExternalUrl },
|
|
109
|
+
onBeforeStart(framework) {
|
|
110
|
+
// Runs after Framework creation, before mount.
|
|
111
|
+
// Good for: error monitoring, analytics SDK, i18n init.
|
|
112
|
+
},
|
|
113
|
+
onAfterStart(framework) {
|
|
114
|
+
// Runs after initial page trigger.
|
|
115
|
+
// Good for: service worker registration, performance marks.
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Locale (i18n)
|
|
121
|
+
|
|
122
|
+
Pass `locale` in `FrameworkConfig` to enable automatic locale handling:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
const framework = Framework.create({
|
|
126
|
+
locale: "zh-Hans", // or "en-US", "ar-SA", etc.
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// SSR: automatically injects <html lang="zh-Hans" dir="ltr">
|
|
130
|
+
// Browser: automatically sets document.documentElement.lang/dir on startup
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
For SSR, locale is resolved in this order:
|
|
134
|
+
|
|
135
|
+
1. Explicit `resolveLocale` callback (if provided)
|
|
136
|
+
2. `locale` from `FrameworkConfig` (via DI container)
|
|
137
|
+
|
|
138
|
+
Access at runtime:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const locale = framework.getLocale();
|
|
142
|
+
// { lang: "zh-Hans", dir: "ltr" }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Translator
|
|
146
|
+
|
|
147
|
+
For full i18n translation, use `SimpleTranslator`:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { SimpleTranslator } from "@finesoft/front";
|
|
151
|
+
|
|
152
|
+
const t = new SimpleTranslator({
|
|
153
|
+
locale: "zh-Hans",
|
|
154
|
+
messages: {
|
|
155
|
+
hello: "你好",
|
|
156
|
+
"items.one": "{count} 个项目",
|
|
157
|
+
"items.other": "{count} 个项目",
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
t.t("hello"); // "你好"
|
|
162
|
+
t.t("hello", { name: "World" }); // "你好"
|
|
163
|
+
t.plural("items", 5); // "5 个项目"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### RTL Support
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { isRtl, getTextDirection, getLocaleAttributes } from "@finesoft/front";
|
|
170
|
+
|
|
171
|
+
isRtl("ar"); // true
|
|
172
|
+
getTextDirection("he"); // "rtl"
|
|
173
|
+
getLocaleAttributes("ar-SA"); // { lang: "ar-SA", dir: "rtl" }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Metrics & Event Recording
|
|
177
|
+
|
|
178
|
+
### EventRecorder (recommended)
|
|
179
|
+
|
|
180
|
+
New pipeline for structured event recording. Default: `ConsoleEventRecorder` (logs to console).
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { Framework, type EventRecorder } from "@finesoft/front";
|
|
184
|
+
|
|
185
|
+
// Custom recorder
|
|
186
|
+
const framework = Framework.create({
|
|
187
|
+
eventRecorder: myAnalyticsRecorder,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Framework automatically records PageView events via didEnterPage()
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Compose multiple recorders:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { CompositeEventRecorder, ConsoleEventRecorder } from "@finesoft/front";
|
|
197
|
+
|
|
198
|
+
const recorder = new CompositeEventRecorder([new ConsoleEventRecorder(), myProductionRecorder]);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Inject common fields into every event:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import { WithFieldsRecorder } from "@finesoft/front";
|
|
205
|
+
|
|
206
|
+
const recorder = new WithFieldsRecorder(baseRecorder, [
|
|
207
|
+
{ getFields: () => ({ app: "myApp", version: "1.0" }) },
|
|
208
|
+
]);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Impression Tracking
|
|
212
|
+
|
|
213
|
+
Track element visibility using `IntersectionObserver`:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { IntersectionImpressionObserver } from "@finesoft/front";
|
|
217
|
+
|
|
218
|
+
const observer = new IntersectionImpressionObserver((entries) => {
|
|
219
|
+
for (const entry of entries) {
|
|
220
|
+
analytics.track("impression", { id: entry.id, ...entry.metadata });
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
observer.observe(element, "product-card-123", { category: "featured" });
|
|
225
|
+
// Later:
|
|
226
|
+
observer.unobserve(element);
|
|
227
|
+
observer.destroy();
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Error Reporting
|
|
231
|
+
|
|
232
|
+
Send `warn`/`error` logs to an external monitoring service (Sentry, Datadog, etc.):
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
import { Framework, type ReportCallback } from "@finesoft/front";
|
|
236
|
+
|
|
237
|
+
const framework = Framework.create({
|
|
238
|
+
reportCallback(level, category, args) {
|
|
239
|
+
sentry.captureMessage(`[${category}] ${args.join(" ")}`, level);
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Automatically composes with ConsoleLogger — console output is preserved.
|
|
244
|
+
// All framework.getLogger().warn(...) and .error(...) calls are forwarded.
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## HTTP Client
|
|
248
|
+
|
|
249
|
+
Subclass `HttpClient` to create typed API clients:
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
import { HttpClient } from "@finesoft/front";
|
|
253
|
+
|
|
254
|
+
class MyApi extends HttpClient {
|
|
255
|
+
async getUser(id: string) {
|
|
256
|
+
return this.get<User>(`/users/${id}`);
|
|
257
|
+
}
|
|
258
|
+
async createUser(data: NewUser) {
|
|
259
|
+
return this.post<User>("/users", data);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Interceptors
|
|
265
|
+
|
|
266
|
+
Add request/response interceptors for auth, logging, retries, etc.:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
const api = new MyApi({
|
|
270
|
+
baseUrl: "/api",
|
|
271
|
+
requestInterceptors: [
|
|
272
|
+
(url, init) => {
|
|
273
|
+
init.headers = {
|
|
274
|
+
...init.headers,
|
|
275
|
+
Authorization: `Bearer ${token}`,
|
|
276
|
+
};
|
|
277
|
+
return init;
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
responseInterceptors: [
|
|
281
|
+
(response, url) => {
|
|
282
|
+
if (response.status === 401) refreshToken();
|
|
283
|
+
return response;
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Or add dynamically:
|
|
289
|
+
api.useRequestInterceptor((url, init) => {
|
|
290
|
+
/* ... */ return init;
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Platform Detection
|
|
295
|
+
|
|
296
|
+
Automatically detected from User-Agent and available via DI:
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
const platform = framework.getPlatform();
|
|
300
|
+
// { os: "ios", browser: "safari", engine: "webkit", isMobile: true, isTouch: true }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Standalone usage:
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
import { detectPlatform } from "@finesoft/front";
|
|
307
|
+
const info = detectPlatform(); // auto-reads navigator.userAgent
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## PWA Detection
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
import { getPWADisplayMode } from "@finesoft/front";
|
|
314
|
+
|
|
315
|
+
const mode = getPWADisplayMode();
|
|
316
|
+
// "standalone" | "twa" | "browser"
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Feature Flags
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
const framework = Framework.create({
|
|
323
|
+
featureFlags: { darkMode: true, maxRetries: 3 },
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Add remote providers (last registered wins):
|
|
327
|
+
import { type FeatureFlagsProvider } from "@finesoft/front";
|
|
328
|
+
|
|
329
|
+
const framework = Framework.create({
|
|
330
|
+
featureFlags: { darkMode: false },
|
|
331
|
+
featureFlagsProviders: [remoteConfigProvider],
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## DI Container
|
|
336
|
+
|
|
337
|
+
Scoped containers for request isolation (SSR):
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
const requestScope = framework.container.createScope();
|
|
341
|
+
requestScope.register("user", () => currentUser);
|
|
342
|
+
// Falls back to parent container if key not found
|
|
343
|
+
```
|
|
344
|
+
|
|
92
345
|
## SSR
|
|
93
346
|
|
|
94
347
|
```ts
|