@astralweb/nova-recently-viewed 0.0.1

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.
@@ -0,0 +1 @@
1
+ "use strict";const E=require("vue"),u=require("./web-DMlcd6E0.js"),l={GUEST:"recently_viewed_products_guest",CUSTOMER:"recently_viewed_products_customer"},v={MAX_ITEMS:20},w={JS_TO_SERVER_DIVISOR:1e3,SERVER_TO_JS_MULTIPLIER:1e3},R={maxItems:v.MAX_ITEMS,guestStorageKey:l.GUEST,customerStorageKey:l.CUSTOMER,enableServerSync:!0};function I(g={},n){const s={...R,...g},a=E.computed(()=>n.value?s.customerStorageKey:s.guestStorageKey),c=()=>typeof window<"u"&&typeof localStorage<"u",S=e=>Math.floor(e/w.JS_TO_SERVER_DIVISOR),_=e=>e*w.SERVER_TO_JS_MULTIPLIER,i=e=>{if(!c())return[];try{const r=localStorage.getItem(e),t=JSON.parse(r??"[]");return Array.isArray(t)?t:[]}catch{return[]}},d=(e,r)=>{c()&&localStorage.setItem(e,JSON.stringify(r))},m=e=>{const r=new Set;return e.filter(t=>!t?.sku||typeof t.sku!="string"||r.has(t.sku)?!1:(r.add(t.sku),!0)).sort((t,o)=>o.viewedAt-t.viewedAt).slice(0,s.maxItems)},y=e=>{const t=i(a.value).filter(o=>o.sku!==e);t.push({sku:e,viewedAt:Date.now()}),d(a.value,m(t))},f=e=>{const r=i(a.value);return m(r).map(o=>o.sku).filter(o=>o&&o!==e)};return{addRecentlyViewed:async e=>{if(!(!e||typeof e!="string"))if(n.value&&s.enableServerSync)try{await u.addRecentlyViewedWeb([{product_sku:e,viewed_at:S(Date.now())}])}catch(r){throw y(e),new Error(`Failed to sync to server: ${r instanceof Error?r.message:String(r)}`)}else y(e)},getRecentlyViewedSkus:async e=>{if(n.value&&s.enableServerSync)try{const r=await u.fetchRecentlyViewedWeb();if(r&&r.length>0){const t=r.map(o=>({sku:o.product_sku,viewedAt:_(o.viewed_at)}));return d(a.value,t),r.map(o=>o.product_sku).filter(o=>o&&o!==e).slice(0,s.maxItems)}}catch{return f(e)}return f(e)},clearAllRecentlyViewed:()=>{c()&&(localStorage.removeItem(l.GUEST),localStorage.removeItem(l.CUSTOMER))},mergeGuestToCustomer:async()=>{if(!n.value||!s.enableServerSync)return;const e=i(s.guestStorageKey);if(e.length===0)return;const r=e.filter(t=>t?.sku).slice(0,s.maxItems).map(t=>({product_sku:t.sku,viewed_at:S(t.viewedAt)}));if(c()&&localStorage.removeItem(s.guestStorageKey),r.length>0)try{await u.addRecentlyViewedWeb(r)}catch(t){throw new Error(`Failed to backfill guest data: ${t instanceof Error?t.message:String(t)}`)}},isLoggedIn:n}}exports.useRecentlyViewed=I;
@@ -0,0 +1,96 @@
1
+ import { computed as E } from "vue";
2
+ import { a as y, f as v } from "./web-WoqqfD6L.js";
3
+ const l = {
4
+ GUEST: "recently_viewed_products_guest",
5
+ CUSTOMER: "recently_viewed_products_customer"
6
+ }, R = {
7
+ MAX_ITEMS: 20
8
+ }, w = {
9
+ JS_TO_SERVER_DIVISOR: 1e3,
10
+ // JavaScript timestamp 轉 Unix timestamp
11
+ SERVER_TO_JS_MULTIPLIER: 1e3
12
+ // Unix timestamp 轉 JavaScript timestamp
13
+ }, p = {
14
+ maxItems: R.MAX_ITEMS,
15
+ guestStorageKey: l.GUEST,
16
+ customerStorageKey: l.CUSTOMER,
17
+ enableServerSync: !0
18
+ };
19
+ function M(g = {}, a) {
20
+ const s = { ...p, ...g }, n = E(() => a.value ? s.customerStorageKey : s.guestStorageKey), c = () => typeof window < "u" && typeof localStorage < "u", u = (e) => Math.floor(e / w.JS_TO_SERVER_DIVISOR), _ = (e) => e * w.SERVER_TO_JS_MULTIPLIER, i = (e) => {
21
+ if (!c()) return [];
22
+ try {
23
+ const r = localStorage.getItem(e), t = JSON.parse(r ?? "[]");
24
+ return Array.isArray(t) ? t : [];
25
+ } catch {
26
+ return [];
27
+ }
28
+ }, S = (e, r) => {
29
+ c() && localStorage.setItem(e, JSON.stringify(r));
30
+ }, d = (e) => {
31
+ const r = /* @__PURE__ */ new Set();
32
+ return e.filter((t) => !t?.sku || typeof t.sku != "string" || r.has(t.sku) ? !1 : (r.add(t.sku), !0)).sort((t, o) => o.viewedAt - t.viewedAt).slice(0, s.maxItems);
33
+ }, m = (e) => {
34
+ const t = i(n.value).filter((o) => o.sku !== e);
35
+ t.push({ sku: e, viewedAt: Date.now() }), S(n.value, d(t));
36
+ }, f = (e) => {
37
+ const r = i(n.value);
38
+ return d(r).map((o) => o.sku).filter((o) => o && o !== e);
39
+ };
40
+ return {
41
+ addRecentlyViewed: async (e) => {
42
+ if (!(!e || typeof e != "string"))
43
+ if (a.value && s.enableServerSync)
44
+ try {
45
+ await y([
46
+ {
47
+ product_sku: e,
48
+ viewed_at: u(Date.now())
49
+ }
50
+ ]);
51
+ } catch (r) {
52
+ throw m(e), new Error(`Failed to sync to server: ${r instanceof Error ? r.message : String(r)}`);
53
+ }
54
+ else
55
+ m(e);
56
+ },
57
+ getRecentlyViewedSkus: async (e) => {
58
+ if (a.value && s.enableServerSync)
59
+ try {
60
+ const r = await v();
61
+ if (r && r.length > 0) {
62
+ const t = r.map((o) => ({
63
+ sku: o.product_sku,
64
+ viewedAt: _(o.viewed_at)
65
+ }));
66
+ return S(n.value, t), r.map((o) => o.product_sku).filter((o) => o && o !== e).slice(0, s.maxItems);
67
+ }
68
+ } catch {
69
+ return f(e);
70
+ }
71
+ return f(e);
72
+ },
73
+ clearAllRecentlyViewed: () => {
74
+ c() && (localStorage.removeItem(l.GUEST), localStorage.removeItem(l.CUSTOMER));
75
+ },
76
+ mergeGuestToCustomer: async () => {
77
+ if (!a.value || !s.enableServerSync) return;
78
+ const e = i(s.guestStorageKey);
79
+ if (e.length === 0) return;
80
+ const r = e.filter((t) => t?.sku).slice(0, s.maxItems).map((t) => ({
81
+ product_sku: t.sku,
82
+ viewed_at: u(t.viewedAt)
83
+ }));
84
+ if (c() && localStorage.removeItem(s.guestStorageKey), r.length > 0)
85
+ try {
86
+ await y(r);
87
+ } catch (t) {
88
+ throw new Error(`Failed to backfill guest data: ${t instanceof Error ? t.message : String(t)}`);
89
+ }
90
+ },
91
+ isLoggedIn: a
92
+ };
93
+ }
94
+ export {
95
+ M as u
96
+ };
@@ -0,0 +1 @@
1
+ "use strict";let r=null;function c(e){r=e}function d(){if(!r)throw new Error("SDK not found. Please use setSdk() to set the SDK instance.");return r}const o=async()=>{const{data:e,errors:t}=await d().magento.recentlyViewedProducts({});if(t&&t.length>0)throw new Error(t.map(n=>n.message).join(", "));return e?.recentlyViewedProducts??[]},a=async e=>{const{data:t,errors:n}=await d().magento.addRecentlyViewedProducts({input:e});if(n&&n.length>0)throw new Error(n.map(s=>s.message).join(", "));return t?.addRecentlyViewedProducts??{success:!0}};exports.addRecentlyViewedWeb=a;exports.fetchRecentlyViewedWeb=o;exports.getSdk=d;exports.setSdk=c;
@@ -0,0 +1,26 @@
1
+ let r = null;
2
+ function o(e) {
3
+ r = e;
4
+ }
5
+ function s() {
6
+ if (!r)
7
+ throw new Error("SDK not found. Please use setSdk() to set the SDK instance.");
8
+ return r;
9
+ }
10
+ const c = async () => {
11
+ const { data: e, errors: t } = await s().magento.recentlyViewedProducts({});
12
+ if (t && t.length > 0)
13
+ throw new Error(t.map((n) => n.message).join(", "));
14
+ return e?.recentlyViewedProducts ?? [];
15
+ }, d = async (e) => {
16
+ const { data: t, errors: n } = await s().magento.addRecentlyViewedProducts({ input: e });
17
+ if (n && n.length > 0)
18
+ throw new Error(n.map((a) => a.message).join(", "));
19
+ return t?.addRecentlyViewedProducts ?? { success: !0 };
20
+ };
21
+ export {
22
+ d as a,
23
+ c as f,
24
+ s as g,
25
+ o as s
26
+ };
@@ -0,0 +1,2 @@
1
+ export * from './useRecentlyViewed';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composables/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { ComputedRef } from 'vue';
2
+ import { RecentlyViewedConfig, UseRecentlyViewedReturn } from '../types';
3
+ /**
4
+ * 管理最近瀏覽商品的本地儲存和後端同步
5
+ */
6
+ export declare function useRecentlyViewed(config: RecentlyViewedConfig | undefined, isLoggedIn: ComputedRef<boolean>): UseRecentlyViewedReturn;
7
+ //# sourceMappingURL=useRecentlyViewed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRecentlyViewed.d.ts","sourceRoot":"","sources":["../../src/composables/useRecentlyViewed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAEjD,OAAO,KAAK,EAEV,oBAAoB,EACpB,uBAAuB,EAExB,MAAM,UAAU,CAAC;AA2BlB;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,oBAAoB,YAAK,EACjC,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,GAC/B,uBAAuB,CAgMzB"}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/useRecentlyViewed-BMqCM4G9.js");exports.useRecentlyViewed=e.useRecentlyViewed;
@@ -0,0 +1,4 @@
1
+ import { u as r } from "./chunks/useRecentlyViewed-DmIwibiG.js";
2
+ export {
3
+ r as useRecentlyViewed
4
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./chunks/useRecentlyViewed-BMqCM4G9.js"),e=require("./api.cjs"),d=require("./chunks/web-DMlcd6E0.js");exports.useRecentlyViewed=t.useRecentlyViewed;exports.addRecentlyViewedProducts=e.addRecentlyViewedProducts;exports.addRecentlyViewedProductsFields=e.addRecentlyViewedProductsFields;exports.recentlyViewedProducts=e.recentlyViewedProducts;exports.recentlyViewedProductsFields=e.recentlyViewedProductsFields;exports.addRecentlyViewedWeb=d.addRecentlyViewedWeb;exports.fetchRecentlyViewedWeb=d.fetchRecentlyViewedWeb;exports.getSdk=d.getSdk;exports.setSdk=d.setSdk;
@@ -0,0 +1,4 @@
1
+ export * from './types';
2
+ export * from './composables';
3
+ export * from './api';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,OAAO,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import { u as t } from "./chunks/useRecentlyViewed-DmIwibiG.js";
2
+ import { addRecentlyViewedProducts as c, addRecentlyViewedProductsFields as r, recentlyViewedProducts as o, recentlyViewedProductsFields as a } from "./api.js";
3
+ import { a as l, f as n, g as w, s as y } from "./chunks/web-WoqqfD6L.js";
4
+ export {
5
+ c as addRecentlyViewedProducts,
6
+ r as addRecentlyViewedProductsFields,
7
+ l as addRecentlyViewedWeb,
8
+ n as fetchRecentlyViewedWeb,
9
+ w as getSdk,
10
+ o as recentlyViewedProducts,
11
+ a as recentlyViewedProductsFields,
12
+ y as setSdk,
13
+ t as useRecentlyViewed
14
+ };
@@ -0,0 +1,9 @@
1
+ export interface AddRecentlyViewedInputItem {
2
+ product_sku: string;
3
+ viewed_at: number;
4
+ }
5
+ export interface AddRecentlyViewedResponse {
6
+ success: boolean;
7
+ message?: string;
8
+ }
9
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/types/api.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
@@ -0,0 +1,19 @@
1
+ import { ComputedRef } from 'vue';
2
+ export interface RecentlyViewedItem {
3
+ sku: string;
4
+ viewedAt: number;
5
+ }
6
+ export interface RecentlyViewedConfig {
7
+ maxItems?: number;
8
+ guestStorageKey?: string;
9
+ customerStorageKey?: string;
10
+ enableServerSync?: boolean;
11
+ }
12
+ export interface UseRecentlyViewedReturn {
13
+ addRecentlyViewed: (sku: string) => Promise<void>;
14
+ getRecentlyViewedSkus: (excludeSku?: string) => Promise<string[]>;
15
+ clearAllRecentlyViewed: () => void;
16
+ mergeGuestToCustomer: () => Promise<void>;
17
+ isLoggedIn: ComputedRef<boolean>;
18
+ }
19
+ //# sourceMappingURL=composables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composables.d.ts","sourceRoot":"","sources":["../../src/types/composables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAEvC,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,qBAAqB,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAClE,sBAAsB,EAAE,MAAM,IAAI,CAAC;IACnC,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CAClC"}
@@ -0,0 +1,3 @@
1
+ export * from './composables';
2
+ export * from './api';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,OAAO,CAAC"}
package/dist/types.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@astralweb/nova-recently-viewed",
3
+ "version": "0.0.1",
4
+ "description": "Recently Viewed module for Nova e-commerce platform - (Internal Use Only)",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./api": {
16
+ "types": "./dist/api/index.d.ts",
17
+ "import": "./dist/api.js",
18
+ "require": "./dist/api.cjs"
19
+ },
20
+ "./types": {
21
+ "types": "./dist/types/index.d.ts",
22
+ "import": "./dist/types.js",
23
+ "require": "./dist/types.cjs"
24
+ },
25
+ "./composables": {
26
+ "types": "./dist/composables/index.d.ts",
27
+ "import": "./dist/composables.js",
28
+ "require": "./dist/composables.cjs"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "peerDependencies": {
35
+ "vue": "^3.5.18",
36
+ "nuxt": "^3.12.4",
37
+ "tailwindcss": "^3.4.17"
38
+ },
39
+ "devDependencies": {
40
+ "@astralweb/eslint-config": "^1.0.0",
41
+ "@astralweb/nova-magento-types": "^1.3.1",
42
+ "@types/node": "^22.13.10",
43
+ "@vitejs/plugin-vue": "^6.0.1",
44
+ "@vitest/coverage-v8": "^3.2.4",
45
+ "@vue/test-utils": "^2.4.6",
46
+ "eslint": "^9.22.0",
47
+ "happy-dom": "^20.0.2",
48
+ "sass-embedded": "^1.90.0",
49
+ "stylelint": "^16.23.1",
50
+ "terser": "^5.43.1",
51
+ "tsup": "^8.0.0",
52
+ "typescript": "^5.8.2",
53
+ "vite": "^7.1.1",
54
+ "vite-plugin-css-injected-by-js": "^3.5.2",
55
+ "vite-plugin-dts": "^4.3.0",
56
+ "vitest": "^3.0.9",
57
+ "vue-tsc": "^2.2.12"
58
+ },
59
+ "publishConfig": {
60
+ "access": "public"
61
+ },
62
+ "repository": {
63
+ "type": "git",
64
+ "url": "https://github.com/AstralWebTW/nova-packages",
65
+ "directory": "packages/recently-viewed"
66
+ },
67
+ "keywords": [
68
+ "recently-viewed",
69
+ "nova",
70
+ "vue",
71
+ "nuxt"
72
+ ],
73
+ "author": "Astral Web",
74
+ "license": "Proprietary",
75
+ "homepage": "https://github.com/AstralWebTW/nova-packages/tree/main/packages/recently-viewed",
76
+ "engines": {
77
+ "node": ">=20.0.0",
78
+ "pnpm": ">=10.0.0"
79
+ },
80
+ "scripts": {
81
+ "build": "vite build",
82
+ "dev": "vite build --watch",
83
+ "typecheck": "tsc --noEmit",
84
+ "typecheck:watch": "tsc --noEmit --watch",
85
+ "lint": "eslint src/",
86
+ "lint:fix": "eslint src/ --fix",
87
+ "lint:types": "tsc --noEmit && eslint .",
88
+ "test": "vitest run",
89
+ "test:watch": "vitest",
90
+ "test:ui": "vitest --ui",
91
+ "test:coverage": "vitest run --coverage",
92
+ "test:typecheck": "vitest typecheck",
93
+ "ci": "pnpm typecheck && pnpm lint && pnpm test && pnpm build"
94
+ }
95
+ }