@ecomiq/storefront 1.0.0 → 1.0.2

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.
Files changed (45) hide show
  1. package/README.md +66 -19
  2. package/dist/chunk-PCLA4TZF.js +359 -0
  3. package/dist/chunk-PCLA4TZF.js.map +1 -0
  4. package/dist/chunk-Q762YXCQ.js +175 -0
  5. package/dist/chunk-Q762YXCQ.js.map +1 -0
  6. package/dist/chunk-TDQ53V3E.js +1598 -0
  7. package/dist/chunk-TDQ53V3E.js.map +1 -0
  8. package/dist/{index-C-xqLr1g.d.ts → chunks/client.d.ts} +6577 -4565
  9. package/dist/chunks/client.js +2046 -0
  10. package/dist/chunks/client.js.map +1 -0
  11. package/dist/chunks/config.js +3 -0
  12. package/dist/chunks/config.js.map +1 -0
  13. package/dist/chunks/server-nextjs.d.ts +33 -0
  14. package/dist/chunks/server-nextjs.js +30 -0
  15. package/dist/chunks/server-nextjs.js.map +1 -0
  16. package/dist/chunks/server-tanstack.d.ts +22 -0
  17. package/dist/chunks/server-tanstack.js +30 -0
  18. package/dist/chunks/server-tanstack.js.map +1 -0
  19. package/dist/ecomiq-shell-ui-SWIFLQVM.js +8 -0
  20. package/dist/ecomiq-shell-ui-SWIFLQVM.js.map +1 -0
  21. package/dist/format-currency-oJD9jGYW.d.ts +6751 -0
  22. package/dist/index.d.ts +20 -30
  23. package/dist/index.js +11 -103
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.server.d.ts +8 -0
  26. package/dist/index.server.js +7 -0
  27. package/dist/index.server.js.map +1 -0
  28. package/dist/nextjs/api.js +1 -174
  29. package/dist/nextjs/api.js.map +1 -1
  30. package/dist/nextjs.d.ts +13 -41
  31. package/dist/nextjs.js +11 -2964
  32. package/dist/nextjs.js.map +1 -1
  33. package/dist/nextjs.server.d.ts +1 -0
  34. package/dist/nextjs.server.js +1 -0
  35. package/dist/nextjs.server.js.map +1 -0
  36. package/dist/tanstack/api.js +3 -171
  37. package/dist/tanstack/api.js.map +1 -1
  38. package/dist/tanstack.d.ts +13 -30
  39. package/dist/tanstack.js +11 -2964
  40. package/dist/tanstack.js.map +1 -1
  41. package/dist/tanstack.server.d.ts +1 -0
  42. package/dist/tanstack.server.js +1 -0
  43. package/dist/tanstack.server.js.map +1 -0
  44. package/package.json +13 -5
  45. /package/dist/{..d.ts → chunks/config.d.ts} +0 -0
@@ -0,0 +1,175 @@
1
+ // src/core/constants/api-paths.ts
2
+ var STOREFRONT_API_PATH_PREFIX = "v1";
3
+
4
+ // src/core/constants/storefront-ecomiq-oauth-cookie.ts
5
+ var STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME = "ecomiq_sf_oauth_at";
6
+ var STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH = "/api/store";
7
+
8
+ // src/core/server/store-proxy-handler.ts
9
+ var DEFAULT_API_BASE_URL = "https://storefront.ecomiq.pe";
10
+ function jsonError(status, message, error) {
11
+ return Response.json({ error, message, status }, { status });
12
+ }
13
+ async function ensureStoreContext(request) {
14
+ const xStoreId = request.headers.get("x-store-id")?.trim();
15
+ const xStoreDomain = request.headers.get("x-store-domain")?.trim();
16
+ if (xStoreDomain) {
17
+ return {
18
+ ok: true,
19
+ storeId: xStoreId || "",
20
+ storeDomain: xStoreDomain || null
21
+ };
22
+ }
23
+ if (!xStoreId) {
24
+ return {
25
+ ok: false,
26
+ response: jsonError(
27
+ 400,
28
+ "X-Store-Id is required",
29
+ "Store context required"
30
+ )
31
+ };
32
+ }
33
+ return {
34
+ ok: true,
35
+ storeId: xStoreId || "",
36
+ storeDomain: xStoreDomain || null
37
+ };
38
+ }
39
+ var SKIP_FORWARD_HEADERS = /* @__PURE__ */ new Set([
40
+ "host",
41
+ "connection",
42
+ "content-length",
43
+ "transfer-encoding",
44
+ "x-store-id",
45
+ "x-store-domain",
46
+ "authorization",
47
+ "cookie",
48
+ "cookie2",
49
+ "set-cookie",
50
+ "x-forwarded-for",
51
+ "x-forwarded-proto",
52
+ "x-forwarded-host",
53
+ "x-real-ip",
54
+ "forwarded",
55
+ "cf-connecting-ip",
56
+ "cf-connecting-ipv6",
57
+ "true-client-ip",
58
+ "cf-ipcountry",
59
+ "cf-ray",
60
+ "cf-visitor"
61
+ ]);
62
+ function getClientIp(request) {
63
+ const candidates = [
64
+ request.headers.get("cf-connecting-ip"),
65
+ request.headers.get("true-client-ip"),
66
+ request.headers.get("x-real-ip")
67
+ ];
68
+ for (const value of candidates) {
69
+ const trimmed = value?.trim();
70
+ if (trimmed) return trimmed;
71
+ }
72
+ const xff = request.headers.get("x-forwarded-for");
73
+ if (xff?.trim()) {
74
+ const first = xff.split(",")[0]?.trim();
75
+ if (first) return first;
76
+ }
77
+ return null;
78
+ }
79
+ function getCookieValue(request, name) {
80
+ const cookieHeader = request.headers.get("cookie");
81
+ if (!cookieHeader) return void 0;
82
+ for (const part of cookieHeader.split(";")) {
83
+ const [rawKey, ...rest] = part.trim().split("=");
84
+ if (rawKey?.trim() === name) {
85
+ return decodeURIComponent(rest.join("="));
86
+ }
87
+ }
88
+ return void 0;
89
+ }
90
+ function buildUpstreamRequest(request, pathSegments, config, storeId, storeDomain) {
91
+ const requestUrl = new URL(request.url);
92
+ const base = (config.apiBaseUrl ?? DEFAULT_API_BASE_URL).replace(/\/$/, "");
93
+ const userPath = pathSegments.length ? pathSegments.join("/") : "";
94
+ const path = userPath ? `${STOREFRONT_API_PATH_PREFIX}/${userPath}` : STOREFRONT_API_PATH_PREFIX;
95
+ const search = requestUrl.searchParams.toString();
96
+ const url = search ? `${base}/${path}?${search}` : `${base}/${path}`;
97
+ const headers = new Headers();
98
+ request.headers.forEach((value, key) => {
99
+ const lower = key.toLowerCase();
100
+ if (!SKIP_FORWARD_HEADERS.has(lower)) {
101
+ headers.set(key, value);
102
+ }
103
+ });
104
+ headers.set("X-Store-Id", storeId);
105
+ if (storeDomain) {
106
+ headers.set("X-Store-domain", storeDomain);
107
+ }
108
+ const clientIp = getClientIp(request);
109
+ if (clientIp) {
110
+ headers.set("X-Forwarded-For", clientIp);
111
+ }
112
+ const forwardedProto = request.headers.get("cf-visitor")?.match(/"scheme":"(https?)"/)?.[1] ?? requestUrl.protocol.replace(":", "") ?? "https";
113
+ headers.set("X-Forwarded-Proto", forwardedProto);
114
+ const forwardedHost = request.headers.get("host") ?? requestUrl.host;
115
+ if (forwardedHost) {
116
+ headers.set("X-Forwarded-Host", forwardedHost);
117
+ }
118
+ const incomingAuth = request.headers.get("authorization")?.trim();
119
+ const storefrontOAuth = getCookieValue(request, STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME)?.trim();
120
+ if (incomingAuth && /^Bearer\s+\S+/i.test(incomingAuth)) {
121
+ headers.set("Authorization", incomingAuth);
122
+ } else if (storefrontOAuth) {
123
+ headers.set("Authorization", `Bearer ${storefrontOAuth}`);
124
+ }
125
+ headers.set("Accept-Encoding", "identity");
126
+ const init = {
127
+ method: request.method,
128
+ headers
129
+ };
130
+ const bodyMethods = ["POST", "PUT", "PATCH"];
131
+ if (bodyMethods.includes(request.method) && request.body) {
132
+ init.body = request.body;
133
+ init.duplex = "half";
134
+ }
135
+ return { url, init };
136
+ }
137
+ function isResponseBodyForbidden(status) {
138
+ return status === 204 || status === 205 || status === 304;
139
+ }
140
+ async function handleStoreProxyRequest(request, pathSegments, config = {}) {
141
+ const storeContext = await ensureStoreContext(request);
142
+ if (!storeContext.ok) {
143
+ return storeContext.response;
144
+ }
145
+ const { storeId, storeDomain } = storeContext;
146
+ const { url, init } = buildUpstreamRequest(
147
+ request,
148
+ pathSegments,
149
+ config,
150
+ storeId,
151
+ storeDomain
152
+ );
153
+ const upstream = await fetch(url, init);
154
+ const responseHeaders = new Headers(upstream.headers);
155
+ const status = upstream.status;
156
+ if (isResponseBodyForbidden(status)) {
157
+ responseHeaders.delete("content-length");
158
+ responseHeaders.delete("content-type");
159
+ return new Response(null, {
160
+ status,
161
+ statusText: upstream.statusText,
162
+ headers: responseHeaders
163
+ });
164
+ }
165
+ const body = await upstream.text();
166
+ return new Response(body, {
167
+ status,
168
+ statusText: upstream.statusText,
169
+ headers: responseHeaders
170
+ });
171
+ }
172
+
173
+ export { STOREFRONT_API_PATH_PREFIX, STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME, STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH, handleStoreProxyRequest };
174
+ //# sourceMappingURL=chunk-Q762YXCQ.js.map
175
+ //# sourceMappingURL=chunk-Q762YXCQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/constants/api-paths.ts","../src/core/constants/storefront-ecomiq-oauth-cookie.ts","../src/core/server/store-proxy-handler.ts"],"names":[],"mappings":"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","file":"chunk-Q762YXCQ.js","sourcesContent":["/** Prefijo base de la API storefront (ej. v1/storefront). */\nexport const STOREFRONT_API_PATH_PREFIX = \"v1\";\n","/** Cookie HttpOnly con el bearer OAuth storefront; solo path `/api/store` (no navega a todo el sitio). */\nexport const STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME = \"ecomiq_sf_oauth_at\";\n\n/** Debe coincidir con el prefijo de la ruta proxy en Next (`app/api/store/...`). */\nexport const STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH = \"/api/store\";\n","import { STOREFRONT_API_PATH_PREFIX } from \"../constants/api-paths\";\nimport { STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME } from \"../constants/storefront-ecomiq-oauth-cookie\";\n\nconst DEFAULT_API_BASE_URL = \"https://storefront.ecomiq.pe\";\n\nexport type StoreProxyHandlerConfig = {\n /** Base URL del API de storefront. Default: `https://storefront.ecomiq.pe` */\n apiBaseUrl?: string;\n};\n\ntype StoreContextResult =\n | {\n ok: true;\n storeId: string;\n storeDomain: string | null;\n }\n | { ok: false; response: Response };\n\nfunction jsonError(status: number, message: string, error: string): Response {\n return Response.json({ error, message, status }, { status });\n}\n\nasync function ensureStoreContext(request: Request): Promise<StoreContextResult> {\n const xStoreId = request.headers.get(\"x-store-id\")?.trim();\n const xStoreDomain = request.headers.get(\"x-store-domain\")?.trim();\n if (xStoreDomain) {\n return {\n ok: true,\n storeId: xStoreId || \"\",\n storeDomain: xStoreDomain || null,\n };\n }\n if (!xStoreId) {\n return {\n ok: false,\n response: jsonError(\n 400,\n \"X-Store-Id is required\",\n \"Store context required\",\n ),\n };\n }\n\n return {\n ok: true,\n storeId: xStoreId || \"\",\n storeDomain: xStoreDomain || null,\n };\n}\n\nconst SKIP_FORWARD_HEADERS = new Set([\n \"host\",\n \"connection\",\n \"content-length\",\n \"transfer-encoding\",\n \"x-store-id\",\n \"x-store-domain\",\n \"authorization\",\n \"cookie\",\n \"cookie2\",\n \"set-cookie\",\n \"x-forwarded-for\",\n \"x-forwarded-proto\",\n \"x-forwarded-host\",\n \"x-real-ip\",\n \"forwarded\",\n \"cf-connecting-ip\",\n \"cf-connecting-ipv6\",\n \"true-client-ip\",\n \"cf-ipcountry\",\n \"cf-ray\",\n \"cf-visitor\",\n]);\n\nfunction getClientIp(request: Request): string | null {\n const candidates = [\n request.headers.get(\"cf-connecting-ip\"),\n request.headers.get(\"true-client-ip\"),\n request.headers.get(\"x-real-ip\"),\n ];\n for (const value of candidates) {\n const trimmed = value?.trim();\n if (trimmed) return trimmed;\n }\n const xff = request.headers.get(\"x-forwarded-for\");\n if (xff?.trim()) {\n const first = xff.split(\",\")[0]?.trim();\n if (first) return first;\n }\n return null;\n}\n\nfunction getCookieValue(request: Request, name: string): string | undefined {\n const cookieHeader = request.headers.get(\"cookie\");\n if (!cookieHeader) return undefined;\n for (const part of cookieHeader.split(\";\")) {\n const [rawKey, ...rest] = part.trim().split(\"=\");\n if (rawKey?.trim() === name) {\n return decodeURIComponent(rest.join(\"=\"));\n }\n }\n return undefined;\n}\n\nfunction buildUpstreamRequest(\n request: Request,\n pathSegments: string[],\n config: StoreProxyHandlerConfig,\n storeId: string,\n storeDomain: string | null,\n): { url: string; init: RequestInit & { duplex?: \"half\" } } {\n const requestUrl = new URL(request.url);\n const base = (config.apiBaseUrl ?? DEFAULT_API_BASE_URL).replace(/\\/$/, \"\");\n const userPath = pathSegments.length ? pathSegments.join(\"/\") : \"\";\n const path = userPath\n ? `${STOREFRONT_API_PATH_PREFIX}/${userPath}`\n : STOREFRONT_API_PATH_PREFIX;\n const search = requestUrl.searchParams.toString();\n const url = search ? `${base}/${path}?${search}` : `${base}/${path}`;\n\n const headers = new Headers();\n request.headers.forEach((value, key) => {\n const lower = key.toLowerCase();\n if (!SKIP_FORWARD_HEADERS.has(lower)) {\n headers.set(key, value);\n }\n });\n headers.set(\"X-Store-Id\", storeId);\n if (storeDomain) {\n headers.set(\"X-Store-domain\", storeDomain);\n }\n\n const clientIp = getClientIp(request);\n if (clientIp) {\n headers.set(\"X-Forwarded-For\", clientIp);\n }\n const forwardedProto =\n request.headers.get(\"cf-visitor\")?.match(/\"scheme\":\"(https?)\"/)?.[1] ??\n requestUrl.protocol.replace(\":\", \"\") ??\n \"https\";\n headers.set(\"X-Forwarded-Proto\", forwardedProto);\n const forwardedHost = request.headers.get(\"host\") ?? requestUrl.host;\n if (forwardedHost) {\n headers.set(\"X-Forwarded-Host\", forwardedHost);\n }\n\n const incomingAuth = request.headers.get(\"authorization\")?.trim();\n const storefrontOAuth = getCookieValue(request, STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME)?.trim();\n\n if (incomingAuth && /^Bearer\\s+\\S+/i.test(incomingAuth)) {\n headers.set(\"Authorization\", incomingAuth);\n } else if (storefrontOAuth) {\n headers.set(\"Authorization\", `Bearer ${storefrontOAuth}`);\n }\n headers.set(\"Accept-Encoding\", \"identity\");\n\n const init: RequestInit & { duplex?: \"half\" } = {\n method: request.method,\n headers,\n };\n\n const bodyMethods = [\"POST\", \"PUT\", \"PATCH\"];\n if (bodyMethods.includes(request.method) && request.body) {\n init.body = request.body;\n init.duplex = \"half\";\n }\n\n return { url, init };\n}\n\nfunction isResponseBodyForbidden(status: number): boolean {\n return status === 204 || status === 205 || status === 304;\n}\n\n/**\n * Proxy BFF `/api/store/*` → upstream Ecomiq. Usa Web Fetch API (`Request` / `Response`).\n */\nexport async function handleStoreProxyRequest(\n request: Request,\n pathSegments: string[],\n config: StoreProxyHandlerConfig = {},\n): Promise<Response> {\n const storeContext = await ensureStoreContext(request);\n if (!storeContext.ok) {\n return storeContext.response;\n }\n\n const { storeId, storeDomain } = storeContext;\n const { url, init } = buildUpstreamRequest(\n request,\n pathSegments,\n config,\n storeId,\n storeDomain,\n );\n\n const upstream = await fetch(url, init);\n const responseHeaders = new Headers(upstream.headers);\n const status = upstream.status;\n\n if (isResponseBodyForbidden(status)) {\n responseHeaders.delete(\"content-length\");\n responseHeaders.delete(\"content-type\");\n return new Response(null, {\n status,\n statusText: upstream.statusText,\n headers: responseHeaders,\n });\n }\n\n const body = await upstream.text();\n return new Response(body, {\n status,\n statusText: upstream.statusText,\n headers: responseHeaders,\n });\n}\n"]}