@onexapis/cli 1.1.30 → 1.1.32

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.
@@ -20,27 +20,16 @@ import * as coreProducts from "@onexapis/core/products";
20
20
  import * as coreOrders from "@onexapis/core/orders";
21
21
  import * as coreBlog from "@onexapis/core/blog";
22
22
  import * as coreFinance from "@onexapis/core/finance";
23
+ import { initializeOnex } from "@onexapis/core";
24
+ import * as coreHooksAll from "@onexapis/core/hooks";
23
25
  import * as coreInternal from "@onexapis/core/internal";
24
26
  import * as coreTypes from "@onexapis/core/types";
25
27
  import { CartProvider, FlyToCartProvider } from "@onexapis/core/contexts";
26
28
  import { LocaleProvider } from "@onexapis/core/contexts";
27
29
 
28
- // Compose @onexapis/core/hooks from published subpaths
29
- // Only include use* hook functions spreading entire modules includes
30
- // Zustand stores and React providers that cause "selector is not a function" errors.
31
- const coreHooks = {
32
- ...coreCommerceHooks,
33
- };
34
- // Add context/state hooks individually (avoid spreading full modules)
35
- for (const mod of [coreContexts, coreAuth, coreOrders, coreCart] as any[]) {
36
- if (mod && typeof mod === "object") {
37
- for (const [k, v] of Object.entries(mod)) {
38
- if (typeof v === "function" && k.startsWith("use") && !(k in coreHooks)) {
39
- (coreHooks as any)[k] = v;
40
- }
41
- }
42
- }
43
- }
30
+ // @onexapis/core/hooks is the single source of truth — no manual composition.
31
+ // coreHooksAll re-exports all hooks (commerce, cart, auth, checkout, payment, etc.)
32
+ const coreHooks = coreHooksAll;
44
33
 
45
34
  // Set React globals
46
35
  (globalThis as any).__ONEX_REACT__ = React;
@@ -100,6 +89,12 @@ try {
100
89
  if (apiUrl && companyId && CommerceClient && setCommerceClient) {
101
90
  // Real API connection — preview will show actual products/blogs
102
91
  setCommerceClient(new CommerceClient({ apiUrl, companyId }));
92
+ // Initialize all services (orders, finance, auth, etc.) so checkout/payment hooks work
93
+ try {
94
+ initializeOnex({ apiUrl, companyId });
95
+ } catch {
96
+ // May fail if already initialized — safe to ignore
97
+ }
103
98
  } else if (setCommerceClient) {
104
99
  // No API config — use stub that returns empty data
105
100
  const emptyPagination = {
@@ -342,9 +337,12 @@ function discoverPageConfigs(
342
337
  }
343
338
 
344
339
  // Sort: "home" page first
340
+ // Sort: home page first (by handle, not type — multiple pages can have type "home")
345
341
  pages.sort((a, b) => {
346
- if (a.config.type === "home") return -1;
347
- if (b.config.type === "home") return 1;
342
+ const aIsHome = a.config.handle === "home" || a.key === "homePageConfig";
343
+ const bIsHome = b.config.handle === "home" || b.key === "homePageConfig";
344
+ if (aIsHome && !bIsHome) return -1;
345
+ if (bIsHome && !aIsHome) return 1;
348
346
  return a.label.localeCompare(b.label);
349
347
  });
350
348
 
@@ -384,7 +382,10 @@ const PREVIEW_SUPPORTED_LOCALES = ["vi", "en"];
384
382
  * "/vi/san-pham/slug" → { locale: "vi", rest: "/san-pham/slug" }
385
383
  * "/products/slug" → { locale: "en", rest: "/products/slug" }
386
384
  */
387
- function extractLocaleFromUrl(pathname: string): { locale: string; rest: string } {
385
+ function extractLocaleFromUrl(pathname: string): {
386
+ locale: string;
387
+ rest: string;
388
+ } {
388
389
  const parts = pathname.replace(/^\/+/, "").split("/");
389
390
  if (parts[0] && PREVIEW_SUPPORTED_LOCALES.includes(parts[0])) {
390
391
  return { locale: parts[0], rest: "/" + parts.slice(1).join("/") };
@@ -427,7 +428,9 @@ function getInitialPageFromURL(
427
428
  if (!effectivePath) continue;
428
429
 
429
430
  // Static locale path match (e.g., /gioi-thieu matches localizedPaths.vi: "/gioi-thieu")
430
- const normalizedEffective = effectivePath.replace(/^\/+|\/+$/g, "").toLowerCase();
431
+ const normalizedEffective = effectivePath
432
+ .replace(/^\/+|\/+$/g, "")
433
+ .toLowerCase();
431
434
  if (normalizedEffective === pathname) return i;
432
435
 
433
436
  // Dynamic locale path match (e.g., /san-pham/my-slug matches localizedPaths.vi: "/san-pham/[slug]")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onexapis/cli",
3
- "version": "1.1.30",
3
+ "version": "1.1.32",
4
4
  "description": "CLI tool for OneX theme development - scaffolds themes using @onexapis/core",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@aws-sdk/client-s3": "^3.470.0",
53
- "@onexapis/core": "^1.0.3",
53
+ "@onexapis/core": "^1.0.5",
54
54
  "@tanstack/react-query": "^5.90.16",
55
55
  "adm-zip": "^0.5.16",
56
56
  "archiver": "^7.0.1",
@@ -605,12 +605,15 @@ Dynamic pages use URL parameters like `/products/[slug]` to render detail views.
605
605
  // pages/product-detail.ts
606
606
  import type { PageConfig } from "@onexapis/core/types";
607
607
 
608
- export const productDetailPageConfig: Omit<PageConfig, "id" | "createdAt" | "updatedAt"> = {
608
+ export const productDetailPageConfig: Omit<
609
+ PageConfig,
610
+ "id" | "createdAt" | "updatedAt"
611
+ > = {
609
612
  title: "Product Detail",
610
613
  handle: "product-detail",
611
- path: "/products/[slug]", // Dynamic segment in brackets
612
- isDynamic: true, // Flag as dynamic
613
- dynamicSegments: ["slug"], // Parameter names
614
+ path: "/products/[slug]", // Dynamic segment in brackets
615
+ isDynamic: true, // Flag as dynamic
616
+ dynamicSegments: ["slug"], // Parameter names
614
617
  type: "product",
615
618
  renderMode: "sections",
616
619
  themeId: "my-theme",
@@ -633,6 +636,7 @@ export default productDetailPageConfig;
633
636
  ```
634
637
 
635
638
  Export in `bundle-entry.ts`:
639
+
636
640
  ```typescript
637
641
  export { default as productDetailPageConfig } from "./pages/product-detail";
638
642
  ```
@@ -642,9 +646,14 @@ export { default as productDetailPageConfig } from "./pages/product-detail";
642
646
  Sections receive `data.routeParams` with the extracted URL parameters:
643
647
 
644
648
  ```tsx
645
- export function ProductDetail({ section, schema, isEditing, data }: SectionComponentProps) {
649
+ export function ProductDetail({
650
+ section,
651
+ schema,
652
+ isEditing,
653
+ data,
654
+ }: SectionComponentProps) {
646
655
  const routeParams = (data?.routeParams || {}) as Record<string, string>;
647
- const slug = routeParams.slug || ""; // "blue-shirt" from /products/blue-shirt
656
+ const slug = routeParams.slug || ""; // "blue-shirt" from /products/blue-shirt
648
657
 
649
658
  const { data: product, isLoading } = useProductBySlug(slug, !!slug);
650
659
  // ...
@@ -658,12 +667,13 @@ Use `localizedPaths` to define different URL slugs per locale:
658
667
  ```typescript
659
668
  export const productDetailPageConfig = {
660
669
  handle: "product-detail",
661
- path: "/products/[slug]", // Default/fallback
670
+ path: "/products/[slug]", // Default/fallback
662
671
  isDynamic: true,
663
672
  dynamicSegments: ["slug"],
664
- localizedPaths: { // Locale-specific paths (typed by Locale enum)
665
- vi: "/san-pham/[slug]", // /vi/san-pham/blue-shirt
666
- en: "/products/[slug]", // /en/products/blue-shirt
673
+ localizedPaths: {
674
+ // Locale-specific paths (typed by Locale enum)
675
+ vi: "/san-pham/[slug]", // /vi/san-pham/blue-shirt
676
+ en: "/products/[slug]", // /en/products/blue-shirt
667
677
  },
668
678
  type: "product",
669
679
  // ...
@@ -673,8 +683,9 @@ export const productDetailPageConfig = {
673
683
  The `Locale` type (`"vi" | "en"`) is from `@onexapis/core/types` — gives autocomplete.
674
684
 
675
685
  Sections also receive `data.locale` to know the current locale:
686
+
676
687
  ```tsx
677
- const locale = data?.locale as string; // "vi" or "en"
688
+ const locale = data?.locale as string; // "vi" or "en"
678
689
  ```
679
690
 
680
691
  ### Key conventions
@@ -1010,7 +1021,15 @@ The cart system uses `useCart()` for state and a flexible fly-to-cart animation
1010
1021
  ```tsx
1011
1022
  import { useCart } from "@onexapis/core/hooks";
1012
1023
 
1013
- const { items, addItem, removeItem, updateQuantity, clearCart, itemCount, subtotal } = useCart();
1024
+ const {
1025
+ items,
1026
+ addItem,
1027
+ removeItem,
1028
+ updateQuantity,
1029
+ clearCart,
1030
+ itemCount,
1031
+ subtotal,
1032
+ } = useCart();
1014
1033
 
1015
1034
  // Add to cart
1016
1035
  addItem({
@@ -1033,11 +1052,11 @@ When a user clicks "Add to Cart", the product thumbnail flies from the button to
1033
1052
  <div data-fly-to-cart-target className="my-cart-icon">
1034
1053
  <ShoppingCartIcon />
1035
1054
  <span>{itemCount}</span>
1036
- </div>
1055
+ </div>;
1037
1056
 
1038
1057
  // Option B: Use CartIcon convenience component (auto-adds data-fly-to-cart-target)
1039
1058
  import { CartIcon } from "@onexapis/core/components";
1040
- <CartIcon count={itemCount} onClick={() => openCartDrawer()} />
1059
+ <CartIcon count={itemCount} onClick={() => openCartDrawer()} />;
1041
1060
  ```
1042
1061
 
1043
1062
  **Step 2: ProductCard triggers animation automatically** — just pass `onAddToCart`:
@@ -1045,7 +1064,14 @@ import { CartIcon } from "@onexapis/core/components";
1045
1064
  ```tsx
1046
1065
  <ProductCard
1047
1066
  product={product}
1048
- onAddToCart={(p) => addItem({ productId: p.id, name: p.title, image: p.image, price: p.salePrice })}
1067
+ onAddToCart={(p) =>
1068
+ addItem({
1069
+ productId: p.id,
1070
+ name: p.title,
1071
+ image: p.image,
1072
+ price: p.salePrice,
1073
+ })
1074
+ }
1049
1075
  />
1050
1076
  ```
1051
1077
 
@@ -1087,7 +1113,9 @@ import {
1087
1113
  } from "@onexapis/core/server";
1088
1114
 
1089
1115
  // Fetch products (server-side with ISR caching)
1090
- const { data: products, pagination } = await fetchProducts(companyId, { limit: 12 });
1116
+ const { data: products, pagination } = await fetchProducts(companyId, {
1117
+ limit: 12,
1118
+ });
1091
1119
 
1092
1120
  // Fetch website settings
1093
1121
  const settings = await fetchSettings(companyId);