@commercebuild/extension 0.0.13 → 0.0.15
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/config/host-libs.json +20 -0
- package/config/vite.config.mjs +133 -9
- package/package.json +3 -2
- package/schemas/config.schema.json +113 -0
- package/types/global.d.ts +522 -76
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "AUTO-GENERATED from packages/extension-host-libs — do not edit. Regenerate with `yarn generate-extension-host-types`.",
|
|
3
|
+
"external": [
|
|
4
|
+
"react",
|
|
5
|
+
"react-dom",
|
|
6
|
+
"react-dom/client",
|
|
7
|
+
"firebase/app",
|
|
8
|
+
"firebase/firestore"
|
|
9
|
+
],
|
|
10
|
+
"globals": {
|
|
11
|
+
"react": "cb.lib.React",
|
|
12
|
+
"react-dom": "cb.lib.ReactDOM",
|
|
13
|
+
"react-dom/client": "cb.lib.ReactDOM",
|
|
14
|
+
"firebase/app": "cb.lib.firebase.app",
|
|
15
|
+
"firebase/firestore": "cb.lib.firebase.firestore"
|
|
16
|
+
},
|
|
17
|
+
"forbidden": [
|
|
18
|
+
"firebase/firestore/lite"
|
|
19
|
+
]
|
|
20
|
+
}
|
package/config/vite.config.mjs
CHANGED
|
@@ -3,12 +3,134 @@ import ViteHMRNotifierPlugin from "../scripts/vite-plugin-hmr-notifier.mjs";
|
|
|
3
3
|
import react from "@vitejs/plugin-react";
|
|
4
4
|
import tailwindcss from "@tailwindcss/vite";
|
|
5
5
|
// import dts from "vite-plugin-dts";
|
|
6
|
-
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
7
|
import path from "path";
|
|
8
|
-
import { existsSync } from "fs";
|
|
8
|
+
import { existsSync, readFileSync } from "fs";
|
|
9
9
|
import chalk from "chalk";
|
|
10
|
-
|
|
11
|
-
//
|
|
10
|
+
|
|
11
|
+
// The host contract version this extension targets, declared in the manifest.
|
|
12
|
+
// Injected into the bundle so the storefront host can check compatibility
|
|
13
|
+
// before mounting the extension. See cb-store's docs/extension-host-api.
|
|
14
|
+
function readCbApiVersion() {
|
|
15
|
+
try {
|
|
16
|
+
const manifestPath = path.resolve(process.cwd(), "commercebuild.json");
|
|
17
|
+
if (existsSync(manifestPath)) {
|
|
18
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
19
|
+
return manifest.cbApiVersion;
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
// fall through — an extension with no declared version loads as legacy
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const cbApiVersion = readCbApiVersion();
|
|
28
|
+
const cbMetaBanner = cbApiVersion
|
|
29
|
+
? `;(function(){try{if(typeof window!=="undefined"){window.__CBExtensionMeta=Object.assign(window.__CBExtensionMeta||{},{cbApiVersion:${JSON.stringify(
|
|
30
|
+
cbApiVersion,
|
|
31
|
+
)}});}}catch(e){}})();`
|
|
32
|
+
: "";
|
|
33
|
+
|
|
34
|
+
// Host-provided libraries (import specifier → cb.* global), generated
|
|
35
|
+
// from the cb-store host-lib registry so this build externalizes
|
|
36
|
+
// exactly what the storefront provides. Regenerated by cb-store's
|
|
37
|
+
// `yarn generate-extension-host-types`; do not edit host-libs.json.
|
|
38
|
+
const hostLibs = JSON.parse(
|
|
39
|
+
readFileSync(
|
|
40
|
+
path.join(path.dirname(fileURLToPath(import.meta.url)), "host-libs.json"),
|
|
41
|
+
"utf8",
|
|
42
|
+
),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Emit dist/cb-manifest.json alongside the bundle: the host libs this
|
|
47
|
+
* build actually externalized (the storefront preloads the lazy ones
|
|
48
|
+
* BEFORE injecting index.js) plus the app's data-connection
|
|
49
|
+
* declarations copied from config.json. The manifest travels with the
|
|
50
|
+
* artifact — versioned by uploadId like index.js itself — so nothing
|
|
51
|
+
* app-side needs to be written to Firestore, and a store pinned to an
|
|
52
|
+
* old upload gets exactly that upload's declarations.
|
|
53
|
+
*/
|
|
54
|
+
function cbManifestPlugin() {
|
|
55
|
+
return {
|
|
56
|
+
name: "cb-emit-manifest",
|
|
57
|
+
generateBundle(_options, bundle) {
|
|
58
|
+
// The chunk's own `imports` lists exactly the modules rollup left
|
|
59
|
+
// external — the ground truth of what the artifact expects the
|
|
60
|
+
// host to provide (a resolveId hook can't see them: rollup
|
|
61
|
+
// matches the `external` option before plugins run).
|
|
62
|
+
const used = new Set();
|
|
63
|
+
for (const output of Object.values(bundle)) {
|
|
64
|
+
if (output.type !== "chunk") continue;
|
|
65
|
+
for (const spec of [...output.imports, ...output.dynamicImports]) {
|
|
66
|
+
used.add(spec);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
let connections = [];
|
|
70
|
+
let declaredHostLibs = [];
|
|
71
|
+
try {
|
|
72
|
+
const configPath = path.resolve(process.cwd(), "config.json");
|
|
73
|
+
if (existsSync(configPath)) {
|
|
74
|
+
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
75
|
+
if (Array.isArray(config.connections)) {
|
|
76
|
+
connections = config.connections.filter(
|
|
77
|
+
(c) => c && typeof c === "object" && typeof c.name === "string",
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const manifestPath = path.resolve(process.cwd(), "commercebuild.json");
|
|
82
|
+
if (existsSync(manifestPath)) {
|
|
83
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
84
|
+
// Optional explicit override for anything the resolve hook
|
|
85
|
+
// can't see (kept for parity with the editor compiler).
|
|
86
|
+
if (Array.isArray(manifest.hostLibs)) {
|
|
87
|
+
declaredHostLibs = manifest.hostLibs.filter(
|
|
88
|
+
(s) => typeof s === "string",
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
// A broken config fails the type-check/build on its own terms.
|
|
94
|
+
}
|
|
95
|
+
this.emitFile({
|
|
96
|
+
type: "asset",
|
|
97
|
+
fileName: "cb-manifest.json",
|
|
98
|
+
source:
|
|
99
|
+
JSON.stringify(
|
|
100
|
+
{
|
|
101
|
+
cbApiVersion,
|
|
102
|
+
hostLibs: [...new Set([...used, ...declaredHostLibs])].sort(),
|
|
103
|
+
connections,
|
|
104
|
+
},
|
|
105
|
+
null,
|
|
106
|
+
2,
|
|
107
|
+
) + "\n",
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Packages that must never be bundled OR externalized — each would
|
|
114
|
+
// silently conflict with a host lib at runtime (e.g. firestore lite
|
|
115
|
+
// registers the same 'firestore' component as the full SDK, and
|
|
116
|
+
// whichever loads first wins with no error).
|
|
117
|
+
function forbiddenImportsPlugin() {
|
|
118
|
+
const forbidden = new Set(hostLibs.forbidden ?? []);
|
|
119
|
+
return {
|
|
120
|
+
name: "cb-forbidden-imports",
|
|
121
|
+
enforce: "pre",
|
|
122
|
+
resolveId(source) {
|
|
123
|
+
if (forbidden.has(source)) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`"${source}" cannot be used in a commercebuild extension — ` +
|
|
126
|
+
`the host provides a conflicting implementation. ` +
|
|
127
|
+
`Import the host-provided module instead (see host-libs.json).`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
12
134
|
|
|
13
135
|
const jsEntry = path.resolve(process.cwd(), "src/index.js");
|
|
14
136
|
const tsEntry = path.resolve(process.cwd(), "src/index.ts");
|
|
@@ -25,12 +147,10 @@ const baseBuildConfig = {
|
|
|
25
147
|
minify: "esbuild",
|
|
26
148
|
sourcemap: false,
|
|
27
149
|
rollupOptions: {
|
|
28
|
-
external:
|
|
150
|
+
external: hostLibs.external,
|
|
29
151
|
output: {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"react-dom": "cb.ReactDom",
|
|
33
|
-
},
|
|
152
|
+
banner: cbMetaBanner,
|
|
153
|
+
globals: hostLibs.globals,
|
|
34
154
|
assetFileNames: (assetInfo) => {
|
|
35
155
|
if (assetInfo.name.endsWith(".css")) {
|
|
36
156
|
return assetInfo.originalFileName || assetInfo.name || "asset.css";
|
|
@@ -43,6 +163,8 @@ const baseBuildConfig = {
|
|
|
43
163
|
|
|
44
164
|
const prodConfig = {
|
|
45
165
|
plugins: [
|
|
166
|
+
forbiddenImportsPlugin(),
|
|
167
|
+
cbManifestPlugin(),
|
|
46
168
|
react({
|
|
47
169
|
jsxRuntime: "classic",
|
|
48
170
|
}),
|
|
@@ -82,6 +204,8 @@ export default defineConfig(({ mode, command }) => {
|
|
|
82
204
|
return {
|
|
83
205
|
root: ".",
|
|
84
206
|
plugins: [
|
|
207
|
+
forbiddenImportsPlugin(),
|
|
208
|
+
cbManifestPlugin(),
|
|
85
209
|
ViteHMRNotifierPlugin(),
|
|
86
210
|
react({
|
|
87
211
|
jsxRuntime: "classic",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercebuild/extension",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"types": "./types/index.d.ts",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@commercebuild/platform-api": "^0.*",
|
|
14
|
-
"@commercebuild/ui": "^0
|
|
14
|
+
"@commercebuild/ui": "^0.*",
|
|
15
15
|
"@headlessui/react": "^2.2.4",
|
|
16
16
|
"@tailwindcss/postcss": "^4.1.10",
|
|
17
17
|
"@tailwindcss/vite": "^4.1.11",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"@vitejs/plugin-react": "^4.5.2",
|
|
23
23
|
"chalk": "^5.4.1",
|
|
24
24
|
"esbuild": "^0.25.6",
|
|
25
|
+
"firebase": "^12.2.1",
|
|
25
26
|
"lucide-react": "^0.525.0",
|
|
26
27
|
"react": "^19.1.0",
|
|
27
28
|
"react-dom": "^19.1.0",
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "commercebuild extension configuration",
|
|
4
|
+
"description": "Data-connection declarations (config.json) or development overrides (config.development.json — never deployed, matched to declarations by name).",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"$schema": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"connections": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"description": "Named data connections. In config.json each entry declares a connection; in config.development.json each entry overrides the declaration with the same name (scratch names may be introduced for testing).",
|
|
13
|
+
"items": {
|
|
14
|
+
"$ref": "#/$defs/connection"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"$defs": {
|
|
20
|
+
"connection": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": [
|
|
23
|
+
"name"
|
|
24
|
+
],
|
|
25
|
+
"properties": {
|
|
26
|
+
"name": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"pattern": "^[A-Za-z][A-Za-z0-9_-]*$",
|
|
29
|
+
"description": "Connection name the app code passes to cb.utils.useFirebaseApp(name). With exactly one connection configured, useFirebaseApp() with no argument resolves it; otherwise the no-argument form looks for \"default\"."
|
|
30
|
+
},
|
|
31
|
+
"provider": {
|
|
32
|
+
"description": "Backing service. Absent means \"firebase\".",
|
|
33
|
+
"enum": [
|
|
34
|
+
"firebase"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"scope": {
|
|
38
|
+
"description": "\"store\" (the default): the merchant supplies the config per store under Installed Apps → Configure data — do not put a config here. \"app\": the config is fixed in this declaration (required below), owned by the app and not merchant-overridable.",
|
|
39
|
+
"enum": [
|
|
40
|
+
"store",
|
|
41
|
+
"app"
|
|
42
|
+
],
|
|
43
|
+
"default": "store"
|
|
44
|
+
},
|
|
45
|
+
"label": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "Shown as the connection's title in the merchant's config form."
|
|
48
|
+
},
|
|
49
|
+
"description": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"description": "Shown under the label in the merchant's config form."
|
|
52
|
+
},
|
|
53
|
+
"config": {
|
|
54
|
+
"$ref": "#/$defs/firebaseConfig"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"additionalProperties": false,
|
|
58
|
+
"allOf": [
|
|
59
|
+
{
|
|
60
|
+
"if": {
|
|
61
|
+
"properties": {
|
|
62
|
+
"scope": {
|
|
63
|
+
"const": "app"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"required": [
|
|
67
|
+
"scope"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
"then": {
|
|
71
|
+
"required": [
|
|
72
|
+
"config"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"firebaseConfig": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"description": "Firebase web config (the firebaseConfig snippet from the Firebase console). Public by design — access control lives in the project's Security Rules.",
|
|
81
|
+
"required": [
|
|
82
|
+
"apiKey",
|
|
83
|
+
"projectId"
|
|
84
|
+
],
|
|
85
|
+
"properties": {
|
|
86
|
+
"apiKey": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"minLength": 1
|
|
89
|
+
},
|
|
90
|
+
"projectId": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"minLength": 1
|
|
93
|
+
},
|
|
94
|
+
"authDomain": {
|
|
95
|
+
"type": "string"
|
|
96
|
+
},
|
|
97
|
+
"storageBucket": {
|
|
98
|
+
"type": "string"
|
|
99
|
+
},
|
|
100
|
+
"messagingSenderId": {
|
|
101
|
+
"type": "string"
|
|
102
|
+
},
|
|
103
|
+
"appId": {
|
|
104
|
+
"type": "string"
|
|
105
|
+
},
|
|
106
|
+
"measurementId": {
|
|
107
|
+
"type": "string"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"additionalProperties": true
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
package/types/global.d.ts
CHANGED
|
@@ -1,15 +1,392 @@
|
|
|
1
|
+
// AUTO-GENERATED — DO NOT EDIT.
|
|
2
|
+
// Generated by scripts/generate-extension-host-types.mjs from
|
|
3
|
+
// apps/storefront/src/lib/extension/cb-host-api.types.ts (the CbHostApi
|
|
4
|
+
// contract). Regenerate with `yarn generate-extension-host-types`.
|
|
5
|
+
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import React__default, { CSSProperties } from "react";
|
|
8
|
+
import * as ReactDOM from "react-dom";
|
|
9
|
+
import * as FirebaseAppNs from "firebase/app";
|
|
10
|
+
import * as FirebaseFirestoreNs from "firebase/firestore";
|
|
11
|
+
import { cart_v2, catalog_v1, Platform } from "@commercebuild/platform-api";
|
|
12
|
+
import * as icons from "lucide-react";
|
|
13
|
+
import * as uis from "@commercebuild/ui";
|
|
1
14
|
import {
|
|
2
15
|
Disclosure,
|
|
3
16
|
DisclosureButton,
|
|
4
17
|
DisclosurePanel,
|
|
5
18
|
} from "@headlessui/react";
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
19
|
+
import { UrlObject } from "url";
|
|
20
|
+
import { ProductTypeInterface } from "@commercebuild/platform-api/type/v1";
|
|
21
|
+
|
|
22
|
+
declare function useLanagueTranslater(): {
|
|
23
|
+
_t: (k: string, obj?: any) => string | undefined;
|
|
24
|
+
t: (k: string) => string;
|
|
25
|
+
tt: (k: string, obj?: any) => (kk: string) => string;
|
|
26
|
+
tmsg: (k: string) => any;
|
|
27
|
+
tlink: (uri: string) => string;
|
|
28
|
+
talink: (uri: string) => string;
|
|
29
|
+
trlink: (uri: string) => string;
|
|
30
|
+
lang: "en-US" | "fr-FR" | "zh";
|
|
31
|
+
locale: "en-US" | "fr-FR" | "zh";
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
declare const useTranslater: typeof useLanagueTranslater;
|
|
35
|
+
|
|
36
|
+
type Category = catalog_v1.VerifiedCategoryMapper;
|
|
37
|
+
type CartRequestItem = Omit<
|
|
38
|
+
cart_v2.CartUserCartItemInterface,
|
|
39
|
+
"configuratorCode" | "groupCode"
|
|
40
|
+
> & {
|
|
41
|
+
imageUrl?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
unitPrice?: number;
|
|
44
|
+
detailPageUrl?: string;
|
|
45
|
+
};
|
|
46
|
+
type PostItemsSearchItem = catalog_v1.PostItemsSearchItem;
|
|
47
|
+
type PostItemsSearchResponse = catalog_v1.PostItemsSearchResponse;
|
|
48
|
+
type GetItemDetailResponse = catalog_v1.GetItemDetailResponse;
|
|
49
|
+
|
|
50
|
+
interface AddToCartProps {
|
|
51
|
+
items: CartRequestItem[];
|
|
52
|
+
disabled?: boolean;
|
|
53
|
+
onSuccessCallback?: () => void;
|
|
54
|
+
className?: string;
|
|
55
|
+
/** Inline style forwarded to the button (e.g. Product Card button-scheme vars). */
|
|
56
|
+
style?: CSSProperties;
|
|
57
|
+
/**
|
|
58
|
+
* Width control (opt-in). Omit for the default responsive width; `true` = full
|
|
59
|
+
* width at all breakpoints; `false` = content width (auto).
|
|
60
|
+
*/
|
|
61
|
+
fullWidth?: boolean;
|
|
62
|
+
}
|
|
63
|
+
declare function AddToCart({
|
|
64
|
+
items,
|
|
65
|
+
disabled,
|
|
66
|
+
onSuccessCallback,
|
|
67
|
+
className,
|
|
68
|
+
style,
|
|
69
|
+
fullWidth,
|
|
70
|
+
}: AddToCartProps): React.JSX.Element;
|
|
71
|
+
|
|
72
|
+
declare function CategoryProduct({
|
|
73
|
+
product,
|
|
74
|
+
imagePriority,
|
|
75
|
+
}: {
|
|
76
|
+
product: PostItemsSearchItem;
|
|
77
|
+
imagePriority?: boolean;
|
|
78
|
+
}): React.JSX.Element;
|
|
79
|
+
|
|
80
|
+
declare function CategoryBreadcrumb({
|
|
81
|
+
category,
|
|
82
|
+
}: {
|
|
83
|
+
category: Category;
|
|
84
|
+
}): React.JSX.Element;
|
|
85
|
+
|
|
86
|
+
declare function CategoryTitle({
|
|
87
|
+
category,
|
|
88
|
+
}: {
|
|
89
|
+
category: Category;
|
|
90
|
+
}): React.JSX.Element;
|
|
91
|
+
|
|
92
|
+
declare function CategorySidebar({
|
|
93
|
+
category,
|
|
94
|
+
url,
|
|
95
|
+
aggregation,
|
|
96
|
+
currentFilters,
|
|
97
|
+
isDrawer,
|
|
98
|
+
}: {
|
|
99
|
+
category: Category;
|
|
100
|
+
url: string;
|
|
101
|
+
aggregation?: PostItemsSearchResponse["aggregation"];
|
|
102
|
+
currentFilters: Record<string, string[]>;
|
|
103
|
+
isDrawer?: boolean;
|
|
104
|
+
}): React.JSX.Element | null;
|
|
105
|
+
|
|
106
|
+
declare function CategorySort({
|
|
107
|
+
url,
|
|
108
|
+
options,
|
|
109
|
+
sort,
|
|
110
|
+
}: {
|
|
111
|
+
url: string;
|
|
112
|
+
options: {
|
|
113
|
+
name: string;
|
|
114
|
+
}[];
|
|
115
|
+
sort: number;
|
|
116
|
+
}): React.JSX.Element;
|
|
117
|
+
|
|
118
|
+
declare function CategoryProducts({
|
|
119
|
+
hasSiderbar,
|
|
120
|
+
items,
|
|
121
|
+
}: {
|
|
122
|
+
hasSiderbar?: boolean;
|
|
123
|
+
items: PostItemsSearchItem[];
|
|
124
|
+
}): React.JSX.Element;
|
|
125
|
+
|
|
126
|
+
declare function CategoryProductsPageSize({
|
|
127
|
+
url,
|
|
128
|
+
options,
|
|
129
|
+
size,
|
|
130
|
+
}: {
|
|
131
|
+
url: string;
|
|
132
|
+
options: {
|
|
133
|
+
name: string;
|
|
134
|
+
value: number;
|
|
135
|
+
}[];
|
|
136
|
+
size: number;
|
|
137
|
+
}): React.JSX.Element;
|
|
138
|
+
|
|
139
|
+
declare function ProductTitle({
|
|
140
|
+
product,
|
|
141
|
+
}: {
|
|
142
|
+
product: GetItemDetailResponse;
|
|
143
|
+
}): React.JSX.Element;
|
|
144
|
+
|
|
145
|
+
type ThumbnailPosition = "top" | "right" | "bottom" | "left";
|
|
146
|
+
declare const ProductImages: ({
|
|
147
|
+
product,
|
|
148
|
+
thumbnailPosition,
|
|
149
|
+
}: {
|
|
150
|
+
product: GetItemDetailResponse;
|
|
151
|
+
thumbnailPosition?: ThumbnailPosition;
|
|
152
|
+
}) => React.JSX.Element;
|
|
153
|
+
|
|
154
|
+
declare function ProductPrice({
|
|
155
|
+
product,
|
|
156
|
+
items,
|
|
157
|
+
}: {
|
|
158
|
+
product: GetItemDetailResponse | PostItemsSearchItem;
|
|
159
|
+
items?: {
|
|
160
|
+
price?: number;
|
|
161
|
+
}[];
|
|
162
|
+
}): React.JSX.Element | null;
|
|
163
|
+
|
|
164
|
+
declare const ProductStockStatus: ({
|
|
165
|
+
product,
|
|
166
|
+
displayStatusTitle,
|
|
167
|
+
}: {
|
|
168
|
+
product: GetItemDetailResponse | PostItemsSearchItem;
|
|
169
|
+
displayStatusTitle?: boolean;
|
|
170
|
+
}) => React.JSX.Element | null;
|
|
171
|
+
|
|
172
|
+
declare const ProductStockQuantity: ({
|
|
173
|
+
product,
|
|
174
|
+
}: {
|
|
175
|
+
product: GetItemDetailResponse;
|
|
176
|
+
}) => React.JSX.Element | null;
|
|
177
|
+
|
|
178
|
+
declare function ProductRating({
|
|
179
|
+
product,
|
|
180
|
+
}: {
|
|
181
|
+
product: GetItemDetailResponse;
|
|
182
|
+
}): React.JSX.Element;
|
|
183
|
+
|
|
184
|
+
declare function ProductDescription({
|
|
185
|
+
product,
|
|
186
|
+
}: {
|
|
187
|
+
product: GetItemDetailResponse;
|
|
188
|
+
}): React.JSX.Element;
|
|
189
|
+
|
|
190
|
+
interface ProductQuantityInputProps {
|
|
191
|
+
qty: number;
|
|
192
|
+
setQty: (qty: number) => void;
|
|
193
|
+
min?: number;
|
|
194
|
+
max?: number;
|
|
195
|
+
disabled?: boolean;
|
|
196
|
+
}
|
|
197
|
+
declare const ProductQuantityInput: React__default.FC<ProductQuantityInputProps>;
|
|
198
|
+
|
|
199
|
+
declare function StandardProductAddToCart({
|
|
200
|
+
product,
|
|
201
|
+
quantityRequested,
|
|
202
|
+
}: {
|
|
203
|
+
product: GetItemDetailResponse;
|
|
204
|
+
quantityRequested: number;
|
|
205
|
+
}): React.JSX.Element;
|
|
206
|
+
|
|
207
|
+
declare function ProductAddToFavorite({
|
|
208
|
+
product,
|
|
209
|
+
}: {
|
|
210
|
+
product: GetItemDetailResponse;
|
|
211
|
+
}): React.JSX.Element;
|
|
11
212
|
|
|
12
|
-
|
|
213
|
+
declare function ProductMakeOffer({
|
|
214
|
+
product,
|
|
215
|
+
qty,
|
|
216
|
+
}: {
|
|
217
|
+
product: GetItemDetailResponse;
|
|
218
|
+
qty: number;
|
|
219
|
+
}): React.JSX.Element | null;
|
|
220
|
+
|
|
221
|
+
declare function ProductRelatedProducts({
|
|
222
|
+
relatedProducts,
|
|
223
|
+
}: {
|
|
224
|
+
relatedProducts: PostItemsSearchItem[];
|
|
225
|
+
}): React.JSX.Element | null;
|
|
226
|
+
|
|
227
|
+
declare function VariantProductAddToCart({
|
|
228
|
+
items,
|
|
229
|
+
}: {
|
|
230
|
+
items: CartRequestItem[];
|
|
231
|
+
}): React.JSX.Element;
|
|
232
|
+
|
|
233
|
+
declare function ProductBreadcrumb({
|
|
234
|
+
product,
|
|
235
|
+
}: {
|
|
236
|
+
product: GetItemDetailResponse;
|
|
237
|
+
}): React.JSX.Element;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @deprecated Use convertProductToCartRequestItem instead
|
|
241
|
+
*/
|
|
242
|
+
|
|
243
|
+
type ProductProps = {
|
|
244
|
+
type: ProductTypeInterface;
|
|
245
|
+
itemCode: string;
|
|
246
|
+
variantCode?: string;
|
|
247
|
+
defaultUnitOfMeasure: {
|
|
248
|
+
unitCode: string;
|
|
249
|
+
conversion: number;
|
|
250
|
+
default: boolean;
|
|
251
|
+
} | null;
|
|
252
|
+
description?: string;
|
|
253
|
+
detailPageUrl?: string;
|
|
254
|
+
images: {
|
|
255
|
+
main: {
|
|
256
|
+
thumbnailSmall: string;
|
|
257
|
+
} | null;
|
|
258
|
+
} | null;
|
|
259
|
+
quantityRequested?: number;
|
|
260
|
+
};
|
|
261
|
+
declare const convertProductItemToCartRequestItem: (
|
|
262
|
+
product: ProductProps,
|
|
263
|
+
) => CartRequestItem;
|
|
264
|
+
|
|
265
|
+
interface Options extends Intl.NumberFormatOptions {
|
|
266
|
+
locale?: string;
|
|
267
|
+
forceDecimals?: number;
|
|
268
|
+
}
|
|
269
|
+
declare const displayMoney: (
|
|
270
|
+
amount: string | number,
|
|
271
|
+
options?: Options,
|
|
272
|
+
) => string;
|
|
273
|
+
|
|
274
|
+
declare function revalidateTag(
|
|
275
|
+
tag: string,
|
|
276
|
+
profile:
|
|
277
|
+
| string
|
|
278
|
+
| {
|
|
279
|
+
expire?: number;
|
|
280
|
+
},
|
|
281
|
+
): Promise<void>;
|
|
282
|
+
declare function revalidatePath(
|
|
283
|
+
path: string,
|
|
284
|
+
type?: "layout" | "page",
|
|
285
|
+
): Promise<void>;
|
|
286
|
+
declare function revalidate(): Promise<void>;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Per-extension render scope.
|
|
290
|
+
*
|
|
291
|
+
* Extension code has no runtime identity of its own — components are
|
|
292
|
+
* bare functions and `cb` is one page-global object, while a single
|
|
293
|
+
* React tree can hold components from several different extensions at
|
|
294
|
+
* once. This context supplies that identity lexically: the host wraps
|
|
295
|
+
* every extension render site (renderExtensionComponent's boundary and
|
|
296
|
+
* the extension page route) in a provider carrying the registration id
|
|
297
|
+
* and the extension's resolved per-store configuration. Contract hooks
|
|
298
|
+
* like cb.utils.useFirebaseApp() read it, so the same component gets
|
|
299
|
+
* its own extension's connections wherever it renders — including the
|
|
300
|
+
* synthetic "preview" / "local" registrations, which just carry their
|
|
301
|
+
* own scope value.
|
|
302
|
+
*/
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Merchant-supplied Firebase web config (the Firebase-console snippet
|
|
306
|
+
* subset). Not a secret — it ships to every browser by design; access
|
|
307
|
+
* control lives entirely in the merchant's Firestore Security Rules.
|
|
308
|
+
*/
|
|
309
|
+
interface CbFirebaseConfig {
|
|
310
|
+
apiKey: string;
|
|
311
|
+
projectId: string;
|
|
312
|
+
authDomain?: string;
|
|
313
|
+
storageBucket?: string;
|
|
314
|
+
messagingSenderId?: string;
|
|
315
|
+
appId?: string;
|
|
316
|
+
measurementId?: string;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* The extension host API contract (`window.cb`).
|
|
321
|
+
*
|
|
322
|
+
* This file is the SINGLE SOURCE OF TRUTH for the `cb` surface. The runtime
|
|
323
|
+
* factory `buildCbHostApi()` (./cb-host-api.ts) is typed to return `CbHostApi`,
|
|
324
|
+
* so `tsc` fails if the runtime values and this contract ever diverge.
|
|
325
|
+
*
|
|
326
|
+
* The author-facing type declaration (`global.d.ts`) is GENERATED from this
|
|
327
|
+
* file by `scripts/generate-extension-host-types.mjs` (`yarn
|
|
328
|
+
* generate-extension-host-types`). Never hand-edit the generated file.
|
|
329
|
+
*
|
|
330
|
+
* Boundary typing is hybrid: most members derive their type straight from the
|
|
331
|
+
* runtime symbol (`typeof ProductTitle`, `typeof displayMoney`); a member is
|
|
332
|
+
* only curated by hand when deriving would emit an import an extension author's
|
|
333
|
+
* project cannot resolve (e.g. `next/link`) or would leak internals.
|
|
334
|
+
*/
|
|
335
|
+
|
|
336
|
+
type TranslationHelpers = ReturnType<typeof useTranslater>;
|
|
337
|
+
/**
|
|
338
|
+
* The Next.js App Router surface exposed to extensions. Curated (not derived
|
|
339
|
+
* from `next`) so the generated declaration does not import a `next/*` path an
|
|
340
|
+
* extension author's project cannot resolve.
|
|
341
|
+
*/
|
|
342
|
+
interface CbRouter {
|
|
343
|
+
push(
|
|
344
|
+
href: string,
|
|
345
|
+
options?: {
|
|
346
|
+
scroll?: boolean;
|
|
347
|
+
},
|
|
348
|
+
): void;
|
|
349
|
+
replace(
|
|
350
|
+
href: string,
|
|
351
|
+
options?: {
|
|
352
|
+
scroll?: boolean;
|
|
353
|
+
},
|
|
354
|
+
): void;
|
|
355
|
+
back(): void;
|
|
356
|
+
forward(): void;
|
|
357
|
+
refresh(): void;
|
|
358
|
+
prefetch(href: string): void;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Dynamic values the host supplies to the API at build time (per-request React
|
|
362
|
+
* context values that cannot be static imports).
|
|
363
|
+
*/
|
|
364
|
+
interface CbHostDeps {
|
|
365
|
+
platformApi: Platform;
|
|
366
|
+
router: CbRouter;
|
|
367
|
+
tmsg: TranslationHelpers["tmsg"];
|
|
368
|
+
tlink: TranslationHelpers["tlink"];
|
|
369
|
+
}
|
|
370
|
+
type CbClassValue =
|
|
371
|
+
| string
|
|
372
|
+
| number
|
|
373
|
+
| bigint
|
|
374
|
+
| boolean
|
|
375
|
+
| null
|
|
376
|
+
| undefined
|
|
377
|
+
| Record<string, unknown>
|
|
378
|
+
| CbClassValue[];
|
|
379
|
+
type CbClassNameFn = (...inputs: CbClassValue[]) => string;
|
|
380
|
+
type CbContainerProps = React.PropsWithChildren<
|
|
381
|
+
React.HTMLAttributes<HTMLDivElement> & {
|
|
382
|
+
maxWidth?: "sm" | "md" | "lg" | "xl" | "2xl" | "full";
|
|
383
|
+
padding?: "default";
|
|
384
|
+
additionalMaxWidth?: "default";
|
|
385
|
+
}
|
|
386
|
+
>;
|
|
387
|
+
type CbContainer = React.ForwardRefExoticComponent<
|
|
388
|
+
CbContainerProps & React.RefAttributes<HTMLDivElement>
|
|
389
|
+
>;
|
|
13
390
|
type Url = string | UrlObject;
|
|
14
391
|
type OnNavigateEventHandler = (event: { preventDefault: () => void }) => void;
|
|
15
392
|
type InternalLinkProps = {
|
|
@@ -28,15 +405,13 @@ type InternalLinkProps = {
|
|
|
28
405
|
children?: React.ReactNode | undefined;
|
|
29
406
|
onNavigate?: OnNavigateEventHandler;
|
|
30
407
|
} & React.RefAttributes<HTMLAnchorElement>;
|
|
31
|
-
|
|
32
|
-
declare const NextLink: React.ForwardRefExoticComponent<
|
|
408
|
+
type CbLink = React.ForwardRefExoticComponent<
|
|
33
409
|
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof InternalLinkProps> &
|
|
34
410
|
InternalLinkProps & {
|
|
35
411
|
children?: React.ReactNode | undefined;
|
|
36
412
|
} & React.RefAttributes<HTMLAnchorElement>
|
|
37
413
|
>;
|
|
38
|
-
|
|
39
|
-
export interface StaticImageData {
|
|
414
|
+
interface StaticImageData {
|
|
40
415
|
src: string;
|
|
41
416
|
height: number;
|
|
42
417
|
width: number;
|
|
@@ -44,19 +419,19 @@ export interface StaticImageData {
|
|
|
44
419
|
blurWidth?: number;
|
|
45
420
|
blurHeight?: number;
|
|
46
421
|
}
|
|
47
|
-
|
|
422
|
+
interface StaticRequire {
|
|
48
423
|
default: StaticImageData;
|
|
49
424
|
}
|
|
50
|
-
|
|
51
|
-
|
|
425
|
+
type StaticImport = StaticRequire | StaticImageData;
|
|
426
|
+
type ImageLoaderProps = {
|
|
52
427
|
src: string;
|
|
53
428
|
width: number;
|
|
54
429
|
quality?: number;
|
|
55
430
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
431
|
+
type ImageLoader = (p: ImageLoaderProps) => string;
|
|
432
|
+
type PlaceholderValue = "blur" | "empty" | `data:image/${string}`;
|
|
433
|
+
type OnLoadingComplete = (img: HTMLImageElement) => void;
|
|
434
|
+
type CbImage = React.ForwardRefExoticComponent<
|
|
60
435
|
Omit<
|
|
61
436
|
React.DetailedHTMLProps<
|
|
62
437
|
React.ImgHTMLAttributes<HTMLImageElement>,
|
|
@@ -86,70 +461,141 @@ declare const NextImage: React.ForwardRefExoticComponent<
|
|
|
86
461
|
} & React.RefAttributes<HTMLImageElement | null>
|
|
87
462
|
>;
|
|
88
463
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
464
|
+
/**
|
|
465
|
+
* Import specifier → module namespace for every runtime host lib, so
|
|
466
|
+
* `cb.requireLib("firebase/firestore")` returns the fully typed module.
|
|
467
|
+
*/
|
|
468
|
+
interface CbHostLibs {
|
|
469
|
+
react: typeof React;
|
|
470
|
+
"react-dom": typeof ReactDOM;
|
|
471
|
+
"firebase/app": typeof FirebaseAppNs;
|
|
472
|
+
"firebase/firestore": typeof FirebaseFirestoreNs;
|
|
473
|
+
}
|
|
474
|
+
interface CbHostApi {
|
|
475
|
+
/** Host-contract semver — see docs/extension-host-api/versioning.md. */
|
|
476
|
+
version: string;
|
|
477
|
+
/**
|
|
478
|
+
* Shared libraries the host provides as build-time externals, so an extension
|
|
479
|
+
* shares the host's single instance instead of bundling its own (critical for
|
|
480
|
+
* React — two instances break hooks). The extension build maps
|
|
481
|
+
* `import ... from "react"` → `cb.lib.React` and `"react-dom"` →
|
|
482
|
+
* `cb.lib.ReactDOM`.
|
|
483
|
+
*/
|
|
484
|
+
lib: {
|
|
94
485
|
React: typeof React;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
486
|
+
ReactDOM: typeof ReactDOM;
|
|
487
|
+
/**
|
|
488
|
+
* Lazy host libs, bound by the extension build — do not read
|
|
489
|
+
* directly. The host import()s each one before injecting a bundle
|
|
490
|
+
* that declared it; until then a member read throws a descriptive
|
|
491
|
+
* error naming the missing lib. Use `cb.requireLib(...)` for an
|
|
492
|
+
* explicit, typed lookup.
|
|
493
|
+
*/
|
|
494
|
+
firebase: {
|
|
495
|
+
app: typeof FirebaseAppNs;
|
|
496
|
+
firestore: typeof FirebaseFirestoreNs;
|
|
106
497
|
};
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
498
|
+
};
|
|
499
|
+
/**
|
|
500
|
+
* The module namespace for a runtime host lib, throwing a descriptive
|
|
501
|
+
* error when it is not loaded (the extension didn't declare it).
|
|
502
|
+
*/
|
|
503
|
+
requireLib: <S extends keyof CbHostLibs>(spec: S) => CbHostLibs[S];
|
|
504
|
+
/** The platform (commerce backend) API SDK client. */
|
|
505
|
+
platform: Platform;
|
|
506
|
+
/**
|
|
507
|
+
* Feature-detect a member by dotted path, e.g.
|
|
508
|
+
* `cb.has("com.Product.ProductMakeOffer")`. Lets an extension degrade
|
|
509
|
+
* gracefully across host versions instead of hard-crashing.
|
|
510
|
+
*/
|
|
511
|
+
has: (path: string) => boolean;
|
|
512
|
+
utils: {
|
|
513
|
+
convertProductItemToCartRequestItem: typeof convertProductItemToCartRequestItem;
|
|
514
|
+
tlink: TranslationHelpers["tlink"];
|
|
515
|
+
tmsg: TranslationHelpers["tmsg"];
|
|
516
|
+
router: CbRouter;
|
|
517
|
+
displayMoney: typeof displayMoney;
|
|
518
|
+
cn: CbClassNameFn;
|
|
519
|
+
revalidate: typeof revalidate;
|
|
520
|
+
revalidateTag: typeof revalidateTag;
|
|
521
|
+
revalidatePath: typeof revalidatePath;
|
|
522
|
+
/**
|
|
523
|
+
* React hook: connection names the store configured for the current
|
|
524
|
+
* extension (via storeadmin → Installed Apps → Configure data).
|
|
525
|
+
* Empty outside an extension subtree.
|
|
526
|
+
*/
|
|
527
|
+
useFirebaseConnections: () => string[];
|
|
528
|
+
/**
|
|
529
|
+
* React hook: the host-initialized FirebaseApp for a named
|
|
530
|
+
* connection, or undefined when it is not configured — always
|
|
531
|
+
* render a fallback for that case. With no argument: the store's
|
|
532
|
+
* only connection when exactly one is configured, else the one
|
|
533
|
+
* named "default". Never call initializeApp yourself; pass this app
|
|
534
|
+
* to the product entry points (e.g. getFirestore(app)).
|
|
535
|
+
*/
|
|
536
|
+
useFirebaseApp: (
|
|
537
|
+
connection?: string,
|
|
538
|
+
) => FirebaseAppNs.FirebaseApp | undefined;
|
|
539
|
+
};
|
|
540
|
+
com: {
|
|
541
|
+
Cart: {
|
|
542
|
+
AddToCart: typeof AddToCart;
|
|
543
|
+
};
|
|
544
|
+
Category: {
|
|
545
|
+
CategoryProducts: typeof CategoryProducts;
|
|
546
|
+
CategoryProduct: typeof CategoryProduct;
|
|
547
|
+
CategoryBreadcrumb: typeof CategoryBreadcrumb;
|
|
548
|
+
CategoryTitle: typeof CategoryTitle;
|
|
549
|
+
CategorySidebar: typeof CategorySidebar;
|
|
550
|
+
CategorySort: typeof CategorySort;
|
|
551
|
+
CategoryProductsPageSize: typeof CategoryProductsPageSize;
|
|
552
|
+
};
|
|
553
|
+
Product: {
|
|
554
|
+
ProductTitle: typeof ProductTitle;
|
|
555
|
+
ProductImages: typeof ProductImages;
|
|
556
|
+
ProductPrice: typeof ProductPrice;
|
|
557
|
+
ProductStockStatus: typeof ProductStockStatus;
|
|
558
|
+
ProductStockQuantity: typeof ProductStockQuantity;
|
|
559
|
+
ProductRating: typeof ProductRating;
|
|
560
|
+
ProductDescription: typeof ProductDescription;
|
|
561
|
+
ProductQuantityInput: typeof ProductQuantityInput;
|
|
562
|
+
StandardProductAddToCart: typeof StandardProductAddToCart;
|
|
563
|
+
VariantProductAddToCart: typeof VariantProductAddToCart;
|
|
564
|
+
ProductAddToFavorite: typeof ProductAddToFavorite;
|
|
565
|
+
ProductMakeOffer: typeof ProductMakeOffer;
|
|
566
|
+
ProductRelatedProducts: typeof ProductRelatedProducts;
|
|
567
|
+
ProductBreadcrumb: typeof ProductBreadcrumb;
|
|
136
568
|
};
|
|
137
|
-
ui: {
|
|
138
|
-
icons: { [K in keyof typeof icons]: (typeof icons)[K] };
|
|
139
|
-
Link: typeof NextLink;
|
|
140
|
-
Image: typeof NextImage;
|
|
141
|
-
Container: React.FC<
|
|
142
|
-
React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>
|
|
143
|
-
>;
|
|
144
|
-
Disclosure: typeof Disclosure;
|
|
145
|
-
DisclosureButton: typeof DisclosureButton;
|
|
146
|
-
DisclosurePanel: typeof DisclosurePanel;
|
|
147
|
-
} & { [K in keyof typeof uis]: (typeof uis)[K] };
|
|
148
569
|
};
|
|
149
|
-
|
|
570
|
+
ui: {
|
|
571
|
+
icons: typeof icons;
|
|
572
|
+
Link: CbLink;
|
|
573
|
+
Image: CbImage;
|
|
574
|
+
Container: CbContainer;
|
|
575
|
+
Disclosure: typeof Disclosure;
|
|
576
|
+
DisclosureButton: typeof DisclosureButton;
|
|
577
|
+
DisclosurePanel: typeof DisclosurePanel;
|
|
578
|
+
} & typeof uis;
|
|
579
|
+
}
|
|
580
|
+
declare global {
|
|
581
|
+
const cb: CbHostApi;
|
|
150
582
|
interface Window {
|
|
151
|
-
cb:
|
|
583
|
+
cb: CbHostApi;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Storefront domain types made ambient for extension authors, so
|
|
587
|
+
* component/page code (and editor scaffolds) can reference them bare
|
|
588
|
+
* without importing from the SDK.
|
|
589
|
+
*/
|
|
590
|
+
type Category = catalog_v1.VerifiedCategoryMapper;
|
|
591
|
+
interface Product extends catalog_v1.CatalogProductSearchProduct {
|
|
592
|
+
image: Required<
|
|
593
|
+
Pick<catalog_v1.CatalogProductSearchProduct["details"]["images"], "main">
|
|
594
|
+
>["main"];
|
|
595
|
+
originalPrice?: catalog_v1.CatalogPriceItemDto;
|
|
596
|
+
formattedPrice: string;
|
|
597
|
+
href: string;
|
|
152
598
|
}
|
|
153
599
|
}
|
|
154
600
|
|
|
155
|
-
export {};
|
|
601
|
+
export type { CbFirebaseConfig, CbHostApi, CbHostDeps, CbHostLibs, CbRouter };
|