Skip to content

Commit fb7283b

Browse files
authored
Merge pull request #581 from chaibuilder/dev
PR into main [#577 #579 #580]
2 parents ce41b39 + 9bb5f71 commit fb7283b

File tree

130 files changed

+1114
-4788
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

130 files changed

+1114
-4788
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: Release Next.js Package to npm
2+
3+
on:
4+
push:
5+
tags:
6+
- "next-v*" # Trigger only on tags starting with next-v
7+
8+
jobs:
9+
release-nextjs:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write # Required for creating releases
13+
packages: write # Required for publishing packages
14+
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v4
18+
with:
19+
fetch-depth: 0 # Required for tag information
20+
21+
- name: Set up Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: 20
25+
registry-url: https://registry.npmjs.org/
26+
27+
- name: Install pnpm
28+
run: npm install -g pnpm
29+
30+
- name: Install dependencies (root)
31+
run: pnpm install --no-frozen-lockfile
32+
33+
- name: Build and publish Next.js package
34+
run: |
35+
cd frameworks/nextjs/package
36+
echo "Building Next.js package..."
37+
pnpm run build
38+
39+
echo "Getting version from tag..."
40+
VERSION="${{ github.ref_name }}"
41+
VERSION="${VERSION#next-v}" # Remove next-v prefix
42+
echo "Version: $VERSION"
43+
44+
echo "Updating package.json version..."
45+
npm version $VERSION --no-git-tag-version --allow-same-version
46+
47+
echo "Publishing to npm..."
48+
pnpm publish --no-git-checks --access public
49+
env:
50+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
51+
52+
- name: Generate Release Notes
53+
id: release_notes
54+
run: |
55+
# Get the previous next-v tag
56+
PREVIOUS_TAG=$(git describe --tags --match "next-v*" --abbrev=0 HEAD^ 2>/dev/null || echo "")
57+
58+
if [ -z "$PREVIOUS_TAG" ]; then
59+
# If no previous tag exists, get all PRs
60+
SINCE_DATE="1970-01-01"
61+
else
62+
# Get date of previous tag
63+
SINCE_DATE=$(git log -1 --format=%aI $PREVIOUS_TAG)
64+
fi
65+
66+
# Use GitHub API to get PRs merged since the last tag
67+
REPO_NAME="${GITHUB_REPOSITORY}"
68+
CHANGELOG=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
69+
"https://api.github.com/repos/${REPO_NAME}/pulls?state=closed&sort=updated&direction=desc&per_page=100" | \
70+
jq -r ".[] | select(.merged_at != null) | select(.merged_at > \"${SINCE_DATE}\") | \"* \(.title) ([#\(.number)](\(.html_url))) by @\(.user.login)\"")
71+
72+
# Save changelog to a file with proper escaping
73+
echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
74+
echo "$CHANGELOG" >> $GITHUB_OUTPUT
75+
echo "EOF" >> $GITHUB_OUTPUT
76+
77+
- name: Create GitHub Release
78+
uses: softprops/action-gh-release@v2
79+
with:
80+
tag_name: ${{ github.ref_name }}
81+
name: "Next.js Package ${{ github.ref_name }}"
82+
body: |
83+
## What's Changed
84+
${{ steps.release_notes.outputs.CHANGELOG }}
85+
86+
## Installation
87+
```bash
88+
pnpm add @chaibuilder/next@${{ steps.get_version.outputs.VERSION }}
89+
```
90+
91+
This release includes the latest @chaibuilder/sdk dependency and is ready for production use.
92+
draft: false
93+
prerelease: ${{ contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') || contains(github.ref_name, 'rc') }}
94+
make_latest: ${{ !contains(github.ref_name, 'beta') && !contains(github.ref_name, 'alpha') && !contains(github.ref_name, 'rc') }}
95+
env:
96+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ The core builder package that can be integrated as a standard React component in
4848
- 🎛️ **Custom Panels** - Add your own custom panels
4949
- 🔗 **Data Binding** - Built-in data-binding support
5050
- 🌍 **i18n Support** - Internationalization ready
51-
- 🤖 **AI Panel** - AI integration (no backend required)
51+
- 🤖 **AI Panel** - AI Chat Panel
5252
- 🧩 **Partial Blocks** - Reusable header, footer components
5353
- 🎨 **Theme System** - Customizable theming
5454
- 🎯 **Design Tokens** - Design system integration
@@ -71,14 +71,13 @@ A more powerful Next.js-specific implementation designed for blogs, marketing si
7171
- 🖼️ **Media Manager** - Built-in media manager with image editor
7272
- 🤖 **AI Assistant** - Create and edit UI components and content with AI
7373
- 🔍 **SEO & JSON-LD** - Advanced SEO features with structured data
74-
- 🌐 **Multilingual** - Full multilingual support
74+
- 🌐 **Multilingual** - Multilingual support
7575
- 👁️ **Preview Mode** - Live preview before publishing
7676
- 📝 **Revisions & Compare** - Version control with comparison tools
7777
- 📚 **Library Blocks** - Shared block library across projects
78-
- 👥 **Multi-User Support** - Collaborative editing with multiple users
7978
- 🔒 **Page Lock** - Prevent conflicts with page locking
80-
- **CMS Integration** - Connect to any headless CMS
81-
- 🔐 **BYO Stack** - Bring Your Own Auth, Database (Postgres), and Storage
79+
- 🔒 **CMS Integration** - Connect to any headless CMS
80+
- 🔐 **BYO Stack** - Bring Your Own Auth, Database (Postgres), and Storage
8281

8382
🚀 **Get Started:** [Next.js + Supabase Starter](https://github.com/chaibuilder/chaibuilder-next-supabase-starter) (Work in Progress)
8483

frameworks/nextjs/app/(public)/[[...slug]]/page.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string[
2727
ChaiBuilder.init(process.env.CHAIBUILDER_APP_KEY!, isEnabled);
2828
let page = null;
2929
try {
30-
page = await ChaiBuilder.getPageBySlug(slug);
30+
page = await ChaiBuilder.getPage(slug);
3131
if ("error" in page) {
3232
return notFound();
3333
}
@@ -36,10 +36,11 @@ export default async function Page({ params }: { params: Promise<{ slug: string[
3636
}
3737

3838
//NOTE: pageProps are received in your dataProvider functions for block and page
39+
const siteSettings = await ChaiBuilder.getSiteSettings();
3940
const pageProps: ChaiPageProps = {
4041
slug,
4142
pageType: page.pageType,
42-
fallbackLang: page.fallbackLang,
43+
fallbackLang: siteSettings.fallbackLang,
4344
pageLang: page.lang,
4445
};
4546
return (

frameworks/nextjs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@supabase/ssr": "^0.8.0",
1414
"@supabase/supabase-js": "^2.90.1",
1515
"date-fns": "^4.1.0",
16-
"drizzle-orm": "^0.45.1",
16+
"drizzle-orm": "0.45.1",
1717
"lodash": "^4.17.21",
1818
"next": "^16.1.2",
1919
"react": "^19.2.3",

frameworks/nextjs/package/ChaiBuilder.ts

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { applyChaiDataBinding } from "@chaibuilder/sdk/render";
2-
import { ChaiBlock, ChaiPageProps } from "@chaibuilder/sdk/runtime";
2+
import type { ChaiBlock, ChaiPage, ChaiPageProps, ChaiWebsiteSetting } from "@chaibuilder/sdk/types";
33
import { unstable_cache } from "next/cache";
4+
import { notFound } from "next/navigation";
45
import { cache } from "react";
5-
import type { ChaiBuilderPage } from "./lib";
66
import * as utils from "./lib";
7+
import { ChaiFullPage, ChaiPartialPage } from "./types";
78

89
class ChaiBuilder {
910
private static appId?: string;
@@ -51,53 +52,71 @@ class ChaiBuilder {
5152
return ChaiBuilder.fallbackLang;
5253
}
5354

54-
static async getSiteSettings(): Promise<{ fallbackLang?: string; [key: string]: unknown }> {
55+
static async getSiteSettings(): Promise<ChaiWebsiteSetting> {
5556
ChaiBuilder.verifyInit();
5657
return await unstable_cache(
5758
async () => await utils.getSiteSettings(ChaiBuilder.appId!, ChaiBuilder.draftMode),
5859
[`website-settings-${ChaiBuilder.getAppId()}`],
59-
{
60-
tags: [`website-settings`, `website-settings-${ChaiBuilder.getAppId()}`],
61-
},
60+
{ tags: [`website-settings`, `website-settings-${ChaiBuilder.getAppId()}`] },
6261
)();
6362
}
6463

65-
static async getPageBySlug(slug: string): Promise<ChaiBuilderPage | { error: string }> {
64+
static async getPageBySlug(slug: string): Promise<ChaiPartialPage> {
6665
ChaiBuilder.verifyInit();
67-
return await utils.getPageBySlug(slug, this.appId!, this.draftMode);
66+
try {
67+
return await utils.getPageBySlug(slug, this.appId!, this.draftMode);
68+
} catch (error) {
69+
if (error instanceof Error && error.message === "PAGE_NOT_FOUND") {
70+
throw notFound();
71+
}
72+
throw error;
73+
}
6874
}
6975

70-
static async getFullPage(pageId: string): Promise<Record<string, unknown>> {
76+
static async getFullPage(pageId: string): Promise<ChaiFullPage> {
7177
ChaiBuilder.verifyInit();
7278
return await utils.getFullPage(pageId, this.appId!, this.draftMode);
7379
}
7480

75-
static async getPartialPageBySlug(slug: string): Promise<Record<string, unknown>> {
81+
static async getPartialPageBySlug(
82+
slug: string,
83+
): Promise<
84+
Pick<
85+
ChaiPage,
86+
| "id"
87+
| "name"
88+
| "slug"
89+
| "lang"
90+
| "primaryPage"
91+
| "seo"
92+
| "currentEditor"
93+
| "pageType"
94+
| "lastSaved"
95+
| "dynamic"
96+
| "parent"
97+
| "blocks"
98+
>
99+
> {
76100
ChaiBuilder.verifyInit();
77101
// if the slug is of format /_partial/{langcode}/{uuid}, get the uuid. langcode is optional
78102
const uuid = slug.split("/").pop();
79103
if (!uuid) {
80-
throw new Error("Invalid slug format for partial page");
104+
throw new Error("PAGE_NOT_FOUND");
81105
}
82106

83107
// Fetch the partial page data
84108
const siteSettings = await ChaiBuilder.getSiteSettings();
85109
const data = await ChaiBuilder.getFullPage(uuid);
86-
const fallbackLang = siteSettings?.fallbackLang;
87-
const lang = slug.split("/").length > 3 ? slug.split("/")[2] : fallbackLang;
88-
return { ...data, fallbackLang, lang };
110+
const lang = slug.split("/").length > 3 ? slug.split("/")[2] : siteSettings?.fallbackLang;
111+
return { ...data, lang };
89112
}
90113

91-
static getPage = cache(async (slug: string): Promise<ChaiBuilderPage | Record<string, unknown>> => {
114+
static getPage = cache(async (slug: string): Promise<ChaiFullPage> => {
92115
ChaiBuilder.verifyInit();
93116
if (slug.startsWith("/_partial/")) {
94117
return await ChaiBuilder.getPartialPageBySlug(slug);
95118
}
96-
const page: ChaiBuilderPage = await ChaiBuilder.getPageBySlug(slug);
97-
if ("error" in page) {
98-
return page;
99-
}
100-
119+
const page = await ChaiBuilder.getPageBySlug(slug);
101120
const siteSettings = await ChaiBuilder.getSiteSettings();
102121
const tagPageId = page.id;
103122
const languagePageId = page.languagePageId || page.id;
@@ -129,16 +148,12 @@ class ChaiBuilder {
129148
};
130149
}
131150
const page = await ChaiBuilder.getPage(slug);
132-
if ("error" in page) {
133-
return {
134-
title: "Page Not Found",
135-
description: "The requested page could not be found.",
136-
robots: { index: false, follow: false },
137-
};
151+
if ("error" in page && page.error === "PAGE_NOT_FOUND") {
152+
throw notFound();
138153
}
139154

140155
// Type assertion after error check
141-
const pageData = page as Exclude<ChaiBuilderPage, { error: string }>;
156+
const pageData = page as ChaiPage;
142157

143158
const externalData = await ChaiBuilder.getPageExternalData({
144159
blocks: pageData.blocks,
@@ -194,7 +209,7 @@ class ChaiBuilder {
194209

195210
static async getPageData(args: {
196211
blocks: ChaiBlock[];
197-
pageProps: Record<string, unknown>;
212+
pageProps: ChaiPageProps;
198213
pageType: string;
199214
lang: string;
200215
}): Promise<Record<string, unknown>> {

frameworks/nextjs/package/lib/get-blocks-styles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getStylesForBlocks } from "@chaibuilder/sdk/render";
2-
import { ChaiBlock } from "@chaibuilder/sdk/runtime";
2+
import type { ChaiBlock } from "@chaibuilder/sdk/types";
33

44
export async function getBlocksStyles(blocks: ChaiBlock[]): Promise<string> {
55
return await getStylesForBlocks(blocks, false);

0 commit comments

Comments
 (0)