@bridge-ai-dev/ecom-chat 1.0.10 → 1.0.12
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/README.md +53 -70
- package/bin/cli.js +53 -27
- package/package.json +1 -1
- package/templates/ChatWrapper.tsx.template +19 -0
- package/templates/route.ts.template +11 -10
package/README.md
CHANGED
|
@@ -1,93 +1,76 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Bridge E-commerce Chat widget integration for Next.js
|
|
2
2
|
|
|
3
|
+
A powerful chat widget integration specifically designed for Next.js and Litium storefronts. It handles complex authentication flows and automatic cart context initialization.
|
|
3
4
|
|
|
5
|
+
## Features
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
- **Automatic Cart Scaling**: Server-side cart creation using Litium's `CreateCart` mutation.
|
|
8
|
+
- **Secure Auth Bridge**: Handles HttpOnly cookies (`.AspNetCore.Identity.Application` and `cart-context`) safely.
|
|
9
|
+
- **Next.js Optimized**: Built for the App Router and React Server Components.
|
|
10
|
+
- **Zero Configuration**: Scaffolding tool to get you started in seconds.
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
## Quick Start
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
Run the following command in your Next.js project root:
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
|
14
|
-
- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
cd existing_repo
|
|
18
|
-
git remote add origin https://git.bridge-delivery.com/bridge-ai/ecom-chat-ui.git
|
|
19
|
-
git branch -M main
|
|
20
|
-
git push -uf origin main
|
|
16
|
+
```bash
|
|
17
|
+
npx @bridge-ai-dev/ecom-chat
|
|
21
18
|
```
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
## Collaborate with your team
|
|
28
|
-
|
|
29
|
-
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
|
30
|
-
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
|
31
|
-
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
|
32
|
-
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
|
33
|
-
- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
|
|
34
|
-
|
|
35
|
-
## Test and Deploy
|
|
36
|
-
|
|
37
|
-
Use the built-in continuous integration in GitLab.
|
|
38
|
-
|
|
39
|
-
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
|
|
40
|
-
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
|
41
|
-
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
|
42
|
-
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
|
43
|
-
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
|
20
|
+
This will:
|
|
21
|
+
1. Create `app/api/bridge-auth/route.ts` (The secure auth bridge)
|
|
22
|
+
2. Create `components/ChatWrapper.tsx` (A pre-configured component wrapper)
|
|
44
23
|
|
|
45
|
-
|
|
24
|
+
## Configuration
|
|
46
25
|
|
|
47
|
-
|
|
26
|
+
Add the following environment variables to your `.env.local`:
|
|
48
27
|
|
|
49
|
-
|
|
28
|
+
```env
|
|
29
|
+
# Required for the Widget
|
|
30
|
+
NEXT_PUBLIC_BRIDGE_TENANT_ID=your-tenant-uuid
|
|
31
|
+
NEXT_PUBLIC_BRIDGE_WIDGET_URL=https://...
|
|
50
32
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
## Name
|
|
56
|
-
Choose a self-explaining name for your project.
|
|
57
|
-
|
|
58
|
-
## Description
|
|
59
|
-
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
|
60
|
-
|
|
61
|
-
## Badges
|
|
62
|
-
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
|
63
|
-
|
|
64
|
-
## Visuals
|
|
65
|
-
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
|
66
|
-
|
|
67
|
-
## Installation
|
|
68
|
-
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
|
33
|
+
# Required for server-side cart initialization
|
|
34
|
+
RUNTIME_LITIUM_SERVER_URL=https://your-litium-backend.com
|
|
35
|
+
```
|
|
69
36
|
|
|
70
37
|
## Usage
|
|
71
|
-
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
|
72
38
|
|
|
73
|
-
|
|
74
|
-
|
|
39
|
+
In your `app/layout.tsx`:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import ChatWrapper from '@/components/ChatWrapper';
|
|
43
|
+
|
|
44
|
+
export default function RootLayout({ children }) {
|
|
45
|
+
return (
|
|
46
|
+
<html>
|
|
47
|
+
<body>
|
|
48
|
+
{children}
|
|
49
|
+
<ChatWrapper />
|
|
50
|
+
</body>
|
|
51
|
+
</html>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
75
55
|
|
|
76
|
-
##
|
|
77
|
-
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
|
56
|
+
## How it works
|
|
78
57
|
|
|
79
|
-
|
|
80
|
-
|
|
58
|
+
The library uses a "Bridge Auth" pattern:
|
|
59
|
+
1. The **ChatComponent** (client-side) calls the `/api/bridge-auth` route.
|
|
60
|
+
2. The **Auth Route** (server-side) reads the secure Litium cookies.
|
|
61
|
+
3. If the **cart-context** cookie is missing, the route automatically performs a `CreateCart` mutation to Litium's GraphQL API.
|
|
62
|
+
4. The route returns the auth data to the widget and forwards any new `Set-Cookie` headers to the browser.
|
|
81
63
|
|
|
82
|
-
|
|
64
|
+
## Customization
|
|
83
65
|
|
|
84
|
-
|
|
66
|
+
### `ChatComponent` Props
|
|
85
67
|
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
| Prop | Type | Default | Description |
|
|
69
|
+
|------|------|---------|-------------|
|
|
70
|
+
| `tenantId` | `string` | **Required** | Your Bridge AI tenant ID. |
|
|
71
|
+
| `widgetScriptUrl` | `string` | **Required** | URL of the chat widget script. |
|
|
72
|
+
| `cartCreationMode` | `'server' \| 'client' \| 'none'` | `'server'` | How to handle missing carts. |
|
|
88
73
|
|
|
89
74
|
## License
|
|
90
|
-
For open source projects, say how it is licensed.
|
|
91
75
|
|
|
92
|
-
|
|
93
|
-
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
|
76
|
+
MIT
|
package/bin/cli.js
CHANGED
|
@@ -21,14 +21,19 @@ const log = {
|
|
|
21
21
|
step: (msg) => console.log(`${colors.bold}🚀 ${msg}${colors.reset}`)
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
// Read the
|
|
25
|
-
const
|
|
26
|
-
|
|
24
|
+
// Read the template contents
|
|
25
|
+
const templatesDir = path.join(__dirname, '..', 'templates');
|
|
26
|
+
const routeTemplatePath = path.join(templatesDir, 'route.ts.template');
|
|
27
|
+
const wrapperTemplatePath = path.join(templatesDir, 'ChatWrapper.tsx.template');
|
|
28
|
+
|
|
29
|
+
let routeTemplateContent;
|
|
30
|
+
let wrapperTemplateContent;
|
|
27
31
|
|
|
28
32
|
try {
|
|
29
|
-
|
|
33
|
+
routeTemplateContent = fs.readFileSync(routeTemplatePath, 'utf8');
|
|
34
|
+
wrapperTemplateContent = fs.readFileSync(wrapperTemplatePath, 'utf8');
|
|
30
35
|
} catch (error) {
|
|
31
|
-
console.error(`${colors.red}❌ Error reading template
|
|
36
|
+
console.error(`${colors.red}❌ Error reading template files: ${error.message}${colors.reset}`);
|
|
32
37
|
process.exit(1);
|
|
33
38
|
}
|
|
34
39
|
|
|
@@ -39,9 +44,7 @@ function isNextJsProject(cwd) {
|
|
|
39
44
|
'next.config.mjs',
|
|
40
45
|
'next.config.ts',
|
|
41
46
|
path.join('app', 'layout.tsx'),
|
|
42
|
-
path.join('app', 'layout.
|
|
43
|
-
path.join('app', 'page.tsx'),
|
|
44
|
-
path.join('app', 'page.js')
|
|
47
|
+
path.join('app', 'app', 'layout.tsx')
|
|
45
48
|
];
|
|
46
49
|
|
|
47
50
|
for (const indicator of indicators) {
|
|
@@ -115,42 +118,65 @@ function setup() {
|
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
function createFiles(cwd) {
|
|
118
|
-
|
|
119
|
-
const
|
|
121
|
+
// 1. Create the API Route
|
|
122
|
+
const apiDir = path.join(cwd, 'app', 'api', 'bridge-auth');
|
|
123
|
+
const apiFile = path.join(apiDir, 'route.ts');
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
if (fs.existsSync(targetFile)) {
|
|
125
|
+
if (fs.existsSync(apiFile)) {
|
|
123
126
|
log.warning('API route already exists at: app/api/bridge-auth/route.ts');
|
|
124
|
-
log.info('Skipping file creation to avoid overwriting.');
|
|
125
127
|
} else {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
128
|
+
if (!fs.existsSync(apiDir)) {
|
|
129
|
+
fs.mkdirSync(apiDir, { recursive: true });
|
|
129
130
|
log.success('Created directory: app/api/bridge-auth/');
|
|
130
131
|
}
|
|
131
|
-
|
|
132
|
-
// Write the API route file
|
|
133
|
-
fs.writeFileSync(targetFile, apiRouteContent, 'utf8');
|
|
132
|
+
fs.writeFileSync(apiFile, routeTemplateContent, 'utf8');
|
|
134
133
|
log.success('Created API route: app/api/bridge-auth/route.ts');
|
|
135
134
|
}
|
|
136
135
|
|
|
136
|
+
// 2. Create the ChatWrapper Component
|
|
137
|
+
// Try to find components directory, fallback to cwd/components
|
|
138
|
+
let componentsDir = path.join(cwd, 'components');
|
|
139
|
+
if (!fs.existsSync(componentsDir)) {
|
|
140
|
+
// Check if there's an src/components
|
|
141
|
+
const srcComponents = path.join(cwd, 'src', 'components');
|
|
142
|
+
if (fs.existsSync(srcComponents)) {
|
|
143
|
+
componentsDir = srcComponents;
|
|
144
|
+
} else {
|
|
145
|
+
// Create a components directory if it doesn't exist
|
|
146
|
+
fs.mkdirSync(componentsDir, { recursive: true });
|
|
147
|
+
log.success('Created directory: components/');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const wrapperFile = path.join(componentsDir, 'ChatWrapper.tsx');
|
|
152
|
+
|
|
153
|
+
if (fs.existsSync(wrapperFile)) {
|
|
154
|
+
log.warning('ChatWrapper already exists at: components/ChatWrapper.tsx');
|
|
155
|
+
} else {
|
|
156
|
+
fs.writeFileSync(wrapperFile, wrapperTemplateContent, 'utf8');
|
|
157
|
+
log.success(`Created ChatWrapper: ${path.relative(cwd, wrapperFile)}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
137
160
|
console.log('\n');
|
|
138
161
|
log.step('Next Steps:');
|
|
139
162
|
console.log('\n');
|
|
140
163
|
console.log(' 1. Install the package (if not already):');
|
|
141
164
|
console.log(` ${colors.yellow}npm install @bridge-ai-dev/ecom-chat${colors.reset}`);
|
|
142
165
|
console.log('\n');
|
|
143
|
-
console.log(' 2. Add
|
|
144
|
-
console.log(` ${colors.yellow}
|
|
166
|
+
console.log(' 2. Add environment variables to your .env.local:');
|
|
167
|
+
console.log(` ${colors.yellow}NEXT_PUBLIC_BRIDGE_TENANT_ID=your-tenant-id${colors.reset}`);
|
|
168
|
+
console.log(` ${colors.yellow}NEXT_PUBLIC_BRIDGE_WIDGET_URL=https://...${colors.reset}`);
|
|
169
|
+
console.log(` ${colors.yellow}RUNTIME_LITIUM_SERVER_URL=https://your-litium-backend.com${colors.reset}`);
|
|
145
170
|
console.log('\n');
|
|
146
|
-
console.log(' 3.
|
|
147
|
-
console.log(` ${colors.yellow}
|
|
171
|
+
console.log(' 3. Use the ChatWrapper in your app/layout.tsx:');
|
|
172
|
+
console.log(` ${colors.yellow}import ChatWrapper from 'components/ChatWrapper';${colors.reset}`);
|
|
173
|
+
console.log(' ...');
|
|
174
|
+
console.log(` ${colors.yellow}<ChatWrapper />${colors.reset}`);
|
|
148
175
|
console.log('\n');
|
|
149
|
-
console.log(' 4.
|
|
150
|
-
console.log(
|
|
151
|
-
console.log(` It uses ${colors.yellow}RUNTIME_LITIUM_SERVER_URL${colors.reset} env var for the Litium backend URL.`);
|
|
176
|
+
console.log(' 4. Verify the bridge-auth route is accessible:');
|
|
177
|
+
console.log(` ${colors.blue}http://localhost:3000/api/bridge-auth${colors.reset}`);
|
|
152
178
|
console.log('\n');
|
|
153
|
-
log.success('
|
|
179
|
+
log.success('Scaffolding complete! 🎉');
|
|
154
180
|
console.log('\n');
|
|
155
181
|
}
|
|
156
182
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ChatComponent } from '@bridge-ai-dev/ecom-chat';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ChatWrapper Component
|
|
7
|
+
*
|
|
8
|
+
* This component wraps the Bridge ChatComponent. By default, it uses
|
|
9
|
+
* 'server' cart creation mode, which relies on the /api/bridge-auth route
|
|
10
|
+
* to initialize the Litium cart context.
|
|
11
|
+
*/
|
|
12
|
+
export default function ChatWrapper() {
|
|
13
|
+
return (
|
|
14
|
+
<ChatComponent
|
|
15
|
+
tenantId={process.env.NEXT_PUBLIC_BRIDGE_TENANT_ID || ""}
|
|
16
|
+
widgetScriptUrl={process.env.NEXT_PUBLIC_BRIDGE_WIDGET_URL}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -12,19 +12,20 @@ const CREATE_CART_MUTATION = `
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Constructs the GraphQL endpoint URL.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* Note: The incoming request.url may show https:// (from the proxy),
|
|
19
|
-
* but the Next.js dev server runs on http://. We force http:// for
|
|
20
|
-
* localhost to avoid SSL errors.
|
|
15
|
+
* In some strict Next.js setups, local HTTP fetch is rejected ("host contains HTTP protocol").
|
|
16
|
+
* To avoid this, we prioritize the RUNTIME_LITIUM_SERVER_URL if available,
|
|
17
|
+
* falling back to the incoming request origin.
|
|
21
18
|
*/
|
|
22
19
|
function getGraphQLUrl(request: NextRequest): string {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
const litiumServerUrl = process.env.RUNTIME_LITIUM_SERVER_URL;
|
|
21
|
+
if (litiumServerUrl) {
|
|
22
|
+
// Ensure no trailing slash
|
|
23
|
+
const baseUrl = litiumServerUrl.endsWith('/') ? litiumServerUrl.slice(0, -1) : litiumServerUrl;
|
|
24
|
+
return `${baseUrl}/storefront.graphql`;
|
|
27
25
|
}
|
|
26
|
+
|
|
27
|
+
// Fallback if env var is missing
|
|
28
|
+
const url = new URL(request.url);
|
|
28
29
|
return `${url.origin}/storefront.graphql`;
|
|
29
30
|
}
|
|
30
31
|
|