@ubean/integrations 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.
package/dist/index.js ADDED
@@ -0,0 +1,148 @@
1
+ import { C as getFontshareUrl, D as normalizeFontName, E as normalizeFontFamily, O as parseFontFamilyCSS, S as getBunnyFontsUrl, T as googleFontMetrics, _ as generateFontCSSVariable, a as generateFontCSS, b as generatePreconnectLink, c as getFontHash, d as defaultFontDisplay, f as defaultFontStyles, g as fallbackFonts, h as extractFontUrlsFromCSS, i as defineLocalFont, k as weightsToString, l as getFontsConfig, m as defaultFontWeights, n as defineFontFamily, o as generateFontLinks, p as defaultFontSubsets, r as defineGoogleFont, s as getFontFamilies, t as configureFontsRuntime, u as createFontHash, v as generateFontFace, w as getGoogleFontsUrl, x as generatePreloadLink, y as generateFontFallbackCSS } from "./runtime-C_0lCtWE.js";
2
+ import { computed, onMounted, ref } from "vue";
3
+ //#region src/pwa/runtime.ts
4
+ let swRegistration = null;
5
+ async function registerSWFn(swUrl, opts = {}) {
6
+ if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) return null;
7
+ const { immediate = true, onNeedRefresh, onOfflineReady, onRegistered, onRegisterError, onUpdateFound } = opts;
8
+ if (!immediate) return null;
9
+ try {
10
+ const registration = await navigator.serviceWorker.register(swUrl, { scope: "/" });
11
+ swRegistration = registration;
12
+ onRegistered?.(swUrl);
13
+ registration.addEventListener("updatefound", () => {
14
+ const newWorker = registration.installing;
15
+ onUpdateFound?.();
16
+ if (newWorker) newWorker.addEventListener("statechange", () => {
17
+ if (newWorker.state === "installed" && navigator.serviceWorker.controller) onNeedRefresh?.();
18
+ });
19
+ });
20
+ if (!navigator.serviceWorker.controller) onOfflineReady?.();
21
+ return registration;
22
+ } catch (error) {
23
+ onRegisterError?.(error instanceof Error ? error : new Error(String(error)));
24
+ return null;
25
+ }
26
+ }
27
+ function registerSW(swUrl = "/sw.js", options = {}) {
28
+ return registerSWFn(swUrl, options);
29
+ }
30
+ function updateSW() {
31
+ if (swRegistration?.waiting) swRegistration.waiting.postMessage({ type: "SKIP_WAITING" });
32
+ }
33
+ function getSWRegistration() {
34
+ return swRegistration;
35
+ }
36
+ function usePwa(options = {}) {
37
+ const { immediate = true, swUrl = "/sw.js", onNeedRefresh, onOfflineReady, onRegistered, onRegisterError, onUpdateFound } = options;
38
+ const isInstalled = ref(false);
39
+ const isUpdateAvailable = ref(false);
40
+ const isOfflineReady = ref(false);
41
+ const needRefresh = ref(false);
42
+ const registration = ref(null);
43
+ const state = computed(() => ({
44
+ isInstalled: isInstalled.value,
45
+ isUpdateAvailable: isUpdateAvailable.value,
46
+ isOfflineReady: isOfflineReady.value,
47
+ needRefresh: needRefresh.value,
48
+ registration: registration.value
49
+ }));
50
+ async function doRegister() {
51
+ if (typeof window === "undefined" || !("serviceWorker" in navigator)) return;
52
+ try {
53
+ const reg = await registerSWFn(swUrl, {
54
+ immediate,
55
+ onNeedRefresh: () => {
56
+ needRefresh.value = true;
57
+ isUpdateAvailable.value = true;
58
+ onNeedRefresh?.();
59
+ },
60
+ onOfflineReady: () => {
61
+ isOfflineReady.value = true;
62
+ isInstalled.value = true;
63
+ onOfflineReady?.();
64
+ },
65
+ onRegistered: (url) => {
66
+ registration.value = swRegistration;
67
+ onRegistered?.(url);
68
+ },
69
+ onRegisterError: (err) => {
70
+ onRegisterError?.(err);
71
+ },
72
+ onUpdateFound: () => {
73
+ onUpdateFound?.();
74
+ }
75
+ });
76
+ if (reg) registration.value = reg;
77
+ } catch {}
78
+ }
79
+ function skipWaitingAndRefresh() {
80
+ updateSW();
81
+ }
82
+ function closeNeedRefresh() {
83
+ needRefresh.value = false;
84
+ isUpdateAvailable.value = false;
85
+ }
86
+ onMounted(() => {
87
+ if (immediate) doRegister();
88
+ });
89
+ return {
90
+ state,
91
+ isInstalled,
92
+ isUpdateAvailable,
93
+ isOfflineReady,
94
+ needRefresh,
95
+ registration,
96
+ register: doRegister,
97
+ updateServiceWorker: skipWaitingAndRefresh,
98
+ skipWaiting: skipWaitingAndRefresh,
99
+ closePrompt: closeNeedRefresh
100
+ };
101
+ }
102
+ //#endregion
103
+ //#region src/pinia/runtime.ts
104
+ function getPiniaFromApp(app) {
105
+ return app.config.globalProperties.$pinia ?? null;
106
+ }
107
+ /**
108
+ * SSR 序列化:从 Vue app 实例提取 Pinia 的 root state。
109
+ *
110
+ * 在服务端 `renderToString(app)` 完成后由 ubean SSR 渲染器调用。
111
+ * 返回的 `{ pinia }` 会被序列化到 HTML 的 `__UBEAN_STATE__` script 标签中。
112
+ *
113
+ * 若 app 上未检测到 `$pinia`(未安装 pinia 插件),返回空对象,
114
+ * 不会抛出错误(允许在部分页面不使用 pinia)。
115
+ *
116
+ * @param app Vue app 实例(SSR 渲染用的 app)
117
+ * @returns 序列化后的状态对象,包含 `pinia.state.value`
118
+ */
119
+ function serializePiniaState(app) {
120
+ const pinia = getPiniaFromApp(app);
121
+ if (!pinia) return {};
122
+ return { pinia: JSON.parse(JSON.stringify(pinia.state.value)) };
123
+ }
124
+ /**
125
+ * 客户端水合:将 SSR 序列化的 state 注入到 Pinia 实例。
126
+ *
127
+ * 在 `applyAppConfig`(注册 `createPinia()` 插件)之后、`app.mount()` 之前调用。
128
+ * 必须在 mount 前执行,否则 store 已用默认 state 初始化,水合无效。
129
+ *
130
+ * 若 `state` 为 `null` 或不含 `pinia` 字段,直接返回(no-op),
131
+ * 允许在 CSR 模式或无 SSR state 时安全调用。
132
+ *
133
+ * @param app Vue app 实例(客户端 app,已 install pinia 插件)
134
+ * @param state 从 `__UBEAN_STATE__` 反序列化的状态对象,或 null
135
+ */
136
+ function hydratePiniaState(app, state) {
137
+ if (!state) return;
138
+ const piniaState = state.pinia;
139
+ if (!piniaState || typeof piniaState !== "object") return;
140
+ const pinia = getPiniaFromApp(app);
141
+ if (!pinia) {
142
+ console.warn("[ubean/pinia] hydrateState 被调用但 app 上未检测到 $pinia。请确保 defineApp({ plugins: [createPinia()] }) 已配置。");
143
+ return;
144
+ }
145
+ pinia.state.value = piniaState;
146
+ }
147
+ //#endregion
148
+ export { configureFontsRuntime, createFontHash, defaultFontDisplay, defaultFontStyles, defaultFontSubsets, defaultFontWeights, defineFontFamily, defineGoogleFont, defineLocalFont, extractFontUrlsFromCSS, fallbackFonts, generateFontCSS, generateFontCSSVariable, generateFontFace, generateFontFallbackCSS, generateFontLinks, generatePreconnectLink, generatePreloadLink, getBunnyFontsUrl, getFontFamilies, getFontHash, getFontsConfig, getFontshareUrl, getGoogleFontsUrl, getSWRegistration, googleFontMetrics, hydratePiniaState, normalizeFontFamily, normalizeFontName, parseFontFamilyCSS, registerSW, serializePiniaState, updateSW, usePwa, weightsToString };
@@ -0,0 +1,16 @@
1
+ import { n as UbeanPiniaOptions, t as PiniaSerializedState } from "../types-Qd7x9Fbj.js";
2
+ import { Plugin } from "vite";
3
+ //#region src/pinia/index.d.ts
4
+ /**
5
+ * ubean Pinia Vite 插件
6
+ *
7
+ * @param options 配置选项(`pinia: true` 时传入 `{}`,`pinia: { ... }` 时传入该对象)
8
+ * @returns Vite 插件数组
9
+ */
10
+ declare function ubeanPiniaPlugin(options?: UbeanPiniaOptions): Plugin[];
11
+ /**
12
+ * 类型安全的 Pinia 配置定义辅助函数(透传)
13
+ */
14
+ declare function definePiniaConfig(options: UbeanPiniaOptions): UbeanPiniaOptions;
15
+ //#endregion
16
+ export { type PiniaSerializedState, type UbeanPiniaOptions, ubeanPiniaPlugin as default, ubeanPiniaPlugin, definePiniaConfig };
@@ -0,0 +1,30 @@
1
+ //#region src/pinia/index.ts
2
+ /**
3
+ * ubean Pinia Vite 插件
4
+ *
5
+ * @param options 配置选项(`pinia: true` 时传入 `{}`,`pinia: { ... }` 时传入该对象)
6
+ * @returns Vite 插件数组
7
+ */
8
+ function ubeanPiniaPlugin(options = {}) {
9
+ if (options.enabled === false) return [{
10
+ name: "ubean:pinia:noop",
11
+ enforce: "post"
12
+ }];
13
+ const optimizeDeps = options.optimizeDeps !== false;
14
+ return [{
15
+ name: "ubean:pinia",
16
+ enforce: "pre",
17
+ config() {
18
+ if (!optimizeDeps) return void 0;
19
+ return { optimizeDeps: { include: ["pinia"] } };
20
+ }
21
+ }];
22
+ }
23
+ /**
24
+ * 类型安全的 Pinia 配置定义辅助函数(透传)
25
+ */
26
+ function definePiniaConfig(options) {
27
+ return options;
28
+ }
29
+ //#endregion
30
+ export { ubeanPiniaPlugin as default, ubeanPiniaPlugin, definePiniaConfig };
@@ -0,0 +1,47 @@
1
+ import { a as ManifestScreenshot, c as RuntimeCachingRule, i as ManifestOrientation, l as UsePwaOptions, n as DisplayMode, o as PwaOptions, r as ManifestIcon, s as PwaState, t as CachingStrategy, u as WebAppManifest } from "../types-D94CeRo-.js";
2
+ import { VitePWAOptions } from "vite-plugin-pwa";
3
+ import { Plugin } from "vite";
4
+ //#region src/pwa/index.d.ts
5
+ /**
6
+ * 将 ubean PwaOptions 转换为 vite-plugin-pwa 的 VitePluginPWOptions。
7
+ *
8
+ * 映射关系:
9
+ * - manifest → manifest
10
+ * - registerType → registerType
11
+ * - injectRegister → injectRegister
12
+ * - devOptions → devOptions
13
+ * - skipWaiting → workbox.skipWaiting
14
+ * - clientsClaim → workbox.clientsClaim
15
+ * - cleanupOutdatedCaches → workbox.cleanupOutdatedCaches
16
+ * - strategies + runtimeCaching → workbox.runtimeCaching
17
+ * - injectManifest + swSrc → strategies: 'injectManifest' + injectManifest.swSrc
18
+ */
19
+ declare function toVitePwaOptions(options?: PwaOptions): VitePWAOptions;
20
+ /**
21
+ * ubean PWA Vite 插件(基于 vite-plugin-pwa)
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * // vite.config.ts
26
+ * import { ubeanPwaPlugin } from '@ubean/integrations/pwa';
27
+ *
28
+ * export default defineConfig({
29
+ * plugins: [
30
+ * ubeanPwaPlugin({
31
+ * manifest: { name: 'My App', theme_color: '#ff0000' },
32
+ * registerType: 'prompt'
33
+ * })
34
+ * ]
35
+ * });
36
+ * ```
37
+ *
38
+ * 通常由 ubean module 系统在 `ubean.config.ts` 中 `pwa: true | PwaOptions` 自动加载,
39
+ * 无需在 vite.config.ts 中手动调用。
40
+ */
41
+ declare function ubeanPwaPlugin(options?: PwaOptions): Plugin;
42
+ /**
43
+ * 类型安全的 PWA 配置定义辅助函数(透传)
44
+ */
45
+ declare function definePwaConfig(options: PwaOptions): PwaOptions;
46
+ //#endregion
47
+ export { type CachingStrategy, type DisplayMode, type ManifestIcon, type ManifestOrientation, type ManifestScreenshot, type PwaOptions, type PwaState, type RuntimeCachingRule, type UsePwaOptions, type WebAppManifest, ubeanPwaPlugin as default, ubeanPwaPlugin, definePwaConfig, toVitePwaOptions };
@@ -0,0 +1,159 @@
1
+ import { VitePWA } from "vite-plugin-pwa";
2
+ import { defu } from "defu";
3
+ //#region src/pwa/index.ts
4
+ const DEFAULT_MANIFEST = {
5
+ name: "Ubean App",
6
+ short_name: "Ubean",
7
+ start_url: "/",
8
+ scope: "/",
9
+ display: "standalone",
10
+ background_color: "#ffffff",
11
+ theme_color: "#000000",
12
+ icons: [],
13
+ lang: "en"
14
+ };
15
+ const DEFAULT_STRATEGIES = {
16
+ assets: "cache-first",
17
+ pages: "network-first",
18
+ images: "stale-while-revalidate",
19
+ fonts: "cache-first",
20
+ crossOrigin: "network-first"
21
+ };
22
+ /**
23
+ * 将 ubean 的 strategies(按资源类型)转换为 workbox runtimeCaching 规则。
24
+ * 这些规则作为默认值,放在用户自定义 runtimeCaching 之前。
25
+ */
26
+ function strategiesToRuntimeCaching(strategies) {
27
+ const rules = [];
28
+ if (strategies.assets) rules.push({
29
+ urlPattern: /\.(?:js|css|woff2?|ttf|eot)$/,
30
+ handler: strategies.assets,
31
+ options: { cacheName: "assets" }
32
+ });
33
+ if (strategies.pages) rules.push({
34
+ urlPattern: /\/$/,
35
+ handler: strategies.pages,
36
+ options: { cacheName: "pages" }
37
+ });
38
+ if (strategies.images) rules.push({
39
+ urlPattern: /\.(?:png|jpg|jpeg|gif|webp|svg|avif)$/,
40
+ handler: strategies.images,
41
+ options: {
42
+ cacheName: "images",
43
+ expiration: {
44
+ maxEntries: 60,
45
+ maxAgeSeconds: 2592e3
46
+ }
47
+ }
48
+ });
49
+ if (strategies.fonts) rules.push({
50
+ urlPattern: /\.(?:woff2?|ttf|eot|otf)$/,
51
+ handler: strategies.fonts,
52
+ options: {
53
+ cacheName: "fonts",
54
+ expiration: {
55
+ maxEntries: 10,
56
+ maxAgeSeconds: 31536e3
57
+ }
58
+ }
59
+ });
60
+ if (strategies.crossOrigin) rules.push({
61
+ urlPattern: /^https?:\/\/(?!localhost).*/,
62
+ handler: strategies.crossOrigin,
63
+ options: { cacheName: "cross-origin" }
64
+ });
65
+ return rules;
66
+ }
67
+ /**
68
+ * 将 ubean PwaOptions 转换为 vite-plugin-pwa 的 VitePluginPWOptions。
69
+ *
70
+ * 映射关系:
71
+ * - manifest → manifest
72
+ * - registerType → registerType
73
+ * - injectRegister → injectRegister
74
+ * - devOptions → devOptions
75
+ * - skipWaiting → workbox.skipWaiting
76
+ * - clientsClaim → workbox.clientsClaim
77
+ * - cleanupOutdatedCaches → workbox.cleanupOutdatedCaches
78
+ * - strategies + runtimeCaching → workbox.runtimeCaching
79
+ * - injectManifest + swSrc → strategies: 'injectManifest' + injectManifest.swSrc
80
+ */
81
+ function toVitePwaOptions(options = {}) {
82
+ const { enabled = true, manifest, registerType = "autoUpdate", injectRegister = "auto", devOptions, strategies, runtimeCaching = [], skipWaiting = true, clientsClaim = true, cleanupOutdatedCaches = true, injectManifest = false, swSrc } = options;
83
+ if (!enabled) return { disable: true };
84
+ const resolvedManifest = defu(manifest ?? {}, DEFAULT_MANIFEST);
85
+ const allRuntimeCaching = [...strategiesToRuntimeCaching(defu(strategies ?? {}, DEFAULT_STRATEGIES)), ...runtimeCaching];
86
+ if (injectManifest) return {
87
+ strategies: "injectManifest",
88
+ registerType,
89
+ injectRegister: injectRegister ?? void 0,
90
+ manifest: resolvedManifest,
91
+ devOptions,
92
+ injectManifest: {
93
+ swSrc: swSrc ?? "src/sw.ts",
94
+ skipWaiting,
95
+ clientsClaim,
96
+ cleanupOutdatedCaches,
97
+ runtimeCaching: allRuntimeCaching
98
+ }
99
+ };
100
+ return {
101
+ strategies: "generateSW",
102
+ registerType,
103
+ injectRegister: injectRegister ?? void 0,
104
+ manifest: resolvedManifest,
105
+ devOptions,
106
+ workbox: {
107
+ skipWaiting,
108
+ clientsClaim,
109
+ cleanupOutdatedCaches,
110
+ runtimeCaching: allRuntimeCaching
111
+ }
112
+ };
113
+ }
114
+ /**
115
+ * ubean PWA Vite 插件(基于 vite-plugin-pwa)
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * // vite.config.ts
120
+ * import { ubeanPwaPlugin } from '@ubean/integrations/pwa';
121
+ *
122
+ * export default defineConfig({
123
+ * plugins: [
124
+ * ubeanPwaPlugin({
125
+ * manifest: { name: 'My App', theme_color: '#ff0000' },
126
+ * registerType: 'prompt'
127
+ * })
128
+ * ]
129
+ * });
130
+ * ```
131
+ *
132
+ * 通常由 ubean module 系统在 `ubean.config.ts` 中 `pwa: true | PwaOptions` 自动加载,
133
+ * 无需在 vite.config.ts 中手动调用。
134
+ */
135
+ function ubeanPwaPlugin(options = {}) {
136
+ if (options.enabled === false) return {
137
+ name: "ubean:pwa:noop",
138
+ enforce: "post"
139
+ };
140
+ const vitePwaOptions = toVitePwaOptions(options);
141
+ const plugins = VitePWA(vitePwaOptions);
142
+ const plugin = Array.isArray(plugins) ? plugins[0] : plugins;
143
+ if (!plugin) return {
144
+ name: "ubean:pwa:noop",
145
+ enforce: "post"
146
+ };
147
+ return {
148
+ ...plugin,
149
+ name: `ubean:pwa(${plugin.name})`
150
+ };
151
+ }
152
+ /**
153
+ * 类型安全的 PWA 配置定义辅助函数(透传)
154
+ */
155
+ function definePwaConfig(options) {
156
+ return options;
157
+ }
158
+ //#endregion
159
+ export { ubeanPwaPlugin as default, ubeanPwaPlugin, definePwaConfig, toVitePwaOptions };