+ {filteredPackages.length} {filteredPackages.length === 1 ? 'Extension' : 'Extensions'} +
+ {hasActiveFilters && ( ++ Showing filtered results +
+ )} ++ No extensions found +
++ Try adjusting your filters or search query +
+ +dAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJw b z_^v8bbg` SAn{I*4bH$u(RZ6*x UhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=p C^ S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk( $?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU ^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvh CL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c 70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397* _cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111a H}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*I cmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU &68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-= A= yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v #ix45EVrcEhr>!NMhprl $InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~ &^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7< 4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}sc Zlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+ 9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2 `1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M =hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S( O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/apps/marketplace/src/app/globals.css b/apps/marketplace/src/app/globals.css new file mode 100644 index 0000000..36ff891 --- /dev/null +++ b/apps/marketplace/src/app/globals.css @@ -0,0 +1,130 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; +} + +/* Markdown styling for react-markdown */ +.prose { + color: var(--foreground); + max-width: 65ch; +} + +.prose h1 { + font-size: 2.25rem; + font-weight: 700; + margin-top: 0; + margin-bottom: 1rem; +} + +.prose h2 { + font-size: 1.875rem; + font-weight: 700; + margin-top: 2rem; + margin-bottom: 1rem; +} + +.prose h3 { + font-size: 1.5rem; + font-weight: 600; + margin-top: 1.5rem; + margin-bottom: 0.75rem; +} + +.prose p { + margin-top: 1rem; + margin-bottom: 1rem; + line-height: 1.75; +} + +.prose a { + color: #2563eb; + text-decoration: underline; +} + +.prose a:hover { + color: #1d4ed8; +} + +.prose ul, .prose ol { + margin-top: 1rem; + margin-bottom: 1rem; + padding-left: 1.5rem; +} + +.prose li { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.prose code { + background-color: #f3f4f6; + padding: 0.125rem 0.25rem; + border-radius: 0.25rem; + font-size: 0.875em; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +.prose pre { + background-color: #1f2937; + color: #f9fafb; + padding: 1rem; + border-radius: 0.5rem; + overflow-x: auto; + margin-top: 1rem; + margin-bottom: 1rem; +} + +.prose pre code { + background-color: transparent; + padding: 0; + color: inherit; + font-size: 0.875rem; +} + +.prose table { + width: 100%; + border-collapse: collapse; + margin-top: 1rem; + margin-bottom: 1rem; +} + +.prose th { + background-color: #f3f4f6; + padding: 0.5rem; + text-align: left; + font-weight: 600; + border: 1px solid #e5e7eb; +} + +.prose td { + padding: 0.5rem; + border: 1px solid #e5e7eb; +} + +.prose blockquote { + border-left: 4px solid #e5e7eb; + padding-left: 1rem; + margin-left: 0; + font-style: italic; + color: #6b7280; +} diff --git a/apps/marketplace/src/app/layout.tsx b/apps/marketplace/src/app/layout.tsx new file mode 100644 index 0000000..e76d9be --- /dev/null +++ b/apps/marketplace/src/app/layout.tsx @@ -0,0 +1,28 @@ +import type { Metadata } from "next"; +import "./globals.css"; + +export const metadata: Metadata = { + title: "ObjectStack Marketplace - Discover Drivers, Plugins & Modules", + description: "The official catalog for ObjectStack extensions. Find drivers for PostgreSQL, Redis, Excel, Salesforce, and more. Discover plugins and modules to extend your stack.", + keywords: ["ObjectStack", "marketplace", "drivers", "plugins", "modules", "database", "integrations"], + authors: [{ name: "ObjectStack Team" }], + openGraph: { + title: "ObjectStack Marketplace", + description: "Discover extensions for your ObjectStack", + type: "website", + }, +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/apps/marketplace/src/app/page.tsx b/apps/marketplace/src/app/page.tsx new file mode 100644 index 0000000..8302b29 --- /dev/null +++ b/apps/marketplace/src/app/page.tsx @@ -0,0 +1,173 @@ +import Link from 'next/link'; +import { Search, TrendingUp, Sparkles } from 'lucide-react'; +import { PluginCard } from '@/components/marketplace/plugin-card'; +import { mockPackages, getTrendingPackages, categories } from '@/lib/mock-registry'; + +export default function Home() { + const trendingPackages = getTrendingPackages(); + const featuredPackages = mockPackages.filter(pkg => pkg.isOfficial).slice(0, 3); + + return ( + + {/* Header */} ++ ); +} diff --git a/apps/marketplace/src/app/pkg/[packageId]/page.tsx b/apps/marketplace/src/app/pkg/[packageId]/page.tsx new file mode 100644 index 0000000..ece89d8 --- /dev/null +++ b/apps/marketplace/src/app/pkg/[packageId]/page.tsx @@ -0,0 +1,227 @@ +import { notFound } from 'next/navigation'; +import Link from 'next/link'; +import type { Metadata } from 'next'; +import { ArrowLeft, Github, Calendar, Scale, ExternalLink, BadgeCheck } from 'lucide-react'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { InstallButton } from '@/components/marketplace/install-button'; +import { CategoryPill } from '@/components/marketplace/category-pill'; +import { getPackageById, mockPackages } from '@/lib/mock-registry'; + +interface PageProps { + params: Promise<{ packageId: string }>; +} + +export async function generateStaticParams() { + return mockPackages.map((pkg) => ({ + packageId: pkg.id, + })); +} + +export async function generateMetadata({ params }: PageProps): Promise+ + + {/* Hero Section */} +++++++ ++ ObjectStack ++ Marketplace ++ + + {/* Featured Section */} ++++ Discover Extensions for your{' '} + + ObjectStack + +
++ Find drivers, plugins, and modules to connect your applications with databases, + SaaS platforms, and powerful integrations. +
+ + {/* Search Bar */} ++ ++ + {/* Quick Stats */} +++ ++ + +++++ {mockPackages.length} Extensions + +++ {mockPackages.filter(p => p.isOfficial).length} Official + + + + {/* Categories Grid */} +++++ +++Featured Extensions
+Official drivers trusted by thousands of developers
++ {featuredPackages.map((pkg) => ( +++ ))} + + + + {/* Trending Section */} + {trendingPackages.length > 0 && ( +++++ +Browse by Category
+Find the perfect integration for your use case
++ {categories.map((category) => ( + ++{category.icon}++ {category.name} +
+{category.description}
++ {category.tags.slice(0, 3).map((tag) => ( + + {tag} + + ))} ++ + ))} ++ + )} + + {/* Footer */} + +++++ ++ ++Trending This Week
+Popular extensions gaining traction
++ {trendingPackages.map((pkg) => ( +++ ))} + { + const { packageId } = await params; + const pkg = getPackageById(packageId); + + if (!pkg) { + return { + title: 'Package Not Found', + }; + } + + return { + title: `${pkg.name} - ObjectStack Marketplace`, + description: pkg.description, + keywords: [pkg.name, ...pkg.tags, 'ObjectStack', pkg.type], + openGraph: { + title: pkg.name, + description: pkg.description, + type: 'website', + }, + }; +} + +export default async function PackageDetailPage({ params }: PageProps) { + const { packageId } = await params; + const pkg = getPackageById(packageId); + + if (!pkg) { + notFound(); + } + + return ( + + {/* Header */} ++ ); +} diff --git a/apps/marketplace/src/app/search/page.tsx b/apps/marketplace/src/app/search/page.tsx new file mode 100644 index 0000000..52b339b --- /dev/null +++ b/apps/marketplace/src/app/search/page.tsx @@ -0,0 +1,227 @@ +'use client'; + +import { useState, useMemo } from 'react'; +import Link from 'next/link'; +import { Search, Filter, X } from 'lucide-react'; +import { PluginCard } from '@/components/marketplace/plugin-card'; +import { mockPackages, categories, type PackageType } from '@/lib/mock-registry'; + +export default function SearchPage() { + const [searchQuery, setSearchQuery] = useState(''); + const [selectedType, setSelectedType] = useState+ + ++++ +++ ObjectStack ++ Marketplace + + ++ Back to Search + + + {/* Package Header */} ++++ ++ {/* Icon */} +++ {pkg.icon || '📦'} ++ + {/* Title and Description */} +++++ +{pkg.name}
+ {pkg.isOfficial && ( +++ )} + + {pkg.type} + ++ Official + {pkg.description}
+ + {/* Tags */} ++ {pkg.tags.map((tag) => ( ++ + {/* Install Button */} ++ ))} + + + {/* Main Content */} ++++ + {/* Sidebar */} + +++Documentation
+ ++++ {pkg.readme} + +('all'); + const [selectedCategory, setSelectedCategory] = useState ('all'); + const [showOnlyOfficial, setShowOnlyOfficial] = useState(false); + + const filteredPackages = useMemo(() => { + return mockPackages.filter(pkg => { + // Search query filter + if (searchQuery) { + const query = searchQuery.toLowerCase(); + const matchesSearch = + pkg.name.toLowerCase().includes(query) || + pkg.description.toLowerCase().includes(query) || + pkg.tags.some(tag => tag.toLowerCase().includes(query)); + + if (!matchesSearch) return false; + } + + // Type filter + if (selectedType !== 'all' && pkg.type !== selectedType) { + return false; + } + + // Category filter + if (selectedCategory !== 'all') { + const category = categories.find(c => c.id === selectedCategory); + if (category) { + const matchesCategory = pkg.tags.some(tag => + category.tags.includes(tag) + ); + if (!matchesCategory) return false; + } + } + + // Official filter + if (showOnlyOfficial && !pkg.isOfficial) { + return false; + } + + return true; + }); + }, [searchQuery, selectedType, selectedCategory, showOnlyOfficial]); + + const clearFilters = () => { + setSearchQuery(''); + setSelectedType('all'); + setSelectedCategory('all'); + setShowOnlyOfficial(false); + }; + + const hasActiveFilters = + searchQuery !== '' || + selectedType !== 'all' || + selectedCategory !== 'all' || + showOnlyOfficial; + + return ( + + {/* Header */} ++ ); +} diff --git a/apps/marketplace/src/components/marketplace/category-pill.tsx b/apps/marketplace/src/components/marketplace/category-pill.tsx new file mode 100644 index 0000000..c254752 --- /dev/null +++ b/apps/marketplace/src/components/marketplace/category-pill.tsx @@ -0,0 +1,24 @@ +import Link from 'next/link'; + +interface CategoryPillProps { + tag: string; + href?: string; +} + +export function CategoryPill({ tag, href }: CategoryPillProps) { + const className = "inline-block px-3 py-1 text-sm font-medium text-blue-700 bg-blue-50 rounded-full hover:bg-blue-100 transition-colors"; + + if (href) { + return ( + + {tag} + + ); + } + + return ( + + {tag} + + ); +} diff --git a/apps/marketplace/src/components/marketplace/install-button.tsx b/apps/marketplace/src/components/marketplace/install-button.tsx new file mode 100644 index 0000000..6f38452 --- /dev/null +++ b/apps/marketplace/src/components/marketplace/install-button.tsx @@ -0,0 +1,48 @@ +'use client'; + +import { useState } from 'react'; +import { Copy, Check } from 'lucide-react'; + +interface InstallButtonProps { + packageName: string; +} + +export function InstallButton({ packageName }: InstallButtonProps) { + const [copied, setCopied] = useState(false); + const command = `ostack add ${packageName}`; + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(command); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error('Failed to copy:', err); + } + }; + + return ( ++ + ++++ +++ ObjectStack ++ Marketplace + ++ {/* Search Bar */} ++++ ++++ setSearchQuery(e.target.value)} + placeholder="Search for drivers, plugins, modules..." + className="w-full pl-12 pr-4 py-3 text-lg border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" + /> + + {/* Sidebar Filters */} + + + {/* Results */} +++ +++ + {filteredPackages.length > 0 ? ( ++ {filteredPackages.length} {filteredPackages.length === 1 ? 'Extension' : 'Extensions'} +
+ {hasActiveFilters && ( ++ Showing filtered results +
+ )} ++ {filteredPackages.map((pkg) => ( ++ ) : ( ++ ))} + ++ )} +🔍++ No extensions found +
++ Try adjusting your filters or search query +
+ +++ ); +} diff --git a/apps/marketplace/src/components/marketplace/plugin-card.tsx b/apps/marketplace/src/components/marketplace/plugin-card.tsx new file mode 100644 index 0000000..a817a28 --- /dev/null +++ b/apps/marketplace/src/components/marketplace/plugin-card.tsx @@ -0,0 +1,79 @@ +import Link from 'next/link'; +import { Star, Download, BadgeCheck } from 'lucide-react'; +import type { RegistryPackage } from '@/lib/mock-registry'; + +interface PluginCardProps { + package: RegistryPackage; +} + +export function PluginCard({ package: pkg }: PluginCardProps) { + const formatNumber = (num: number): string => { + if (num >= 1000000) { + return `${(num / 1000000).toFixed(1)}M`; + } + if (num >= 1000) { + return `${(num / 1000).toFixed(1)}K`; + } + return num.toString(); + }; + + return ( + ++ {command} ++ ++ {/* Icon */} ++ + ); +} diff --git a/apps/marketplace/src/lib/mock-registry.ts b/apps/marketplace/src/lib/mock-registry.ts new file mode 100644 index 0000000..308af16 --- /dev/null +++ b/apps/marketplace/src/lib/mock-registry.ts @@ -0,0 +1,559 @@ +/** + * Mock Registry Data Layer + * This provides sample data until the live Registry API is available + */ + +export type PackageType = 'driver' | 'plugin' | 'module'; + +export interface Author { + name: string; + avatar: string; + url: string; +} + +export interface PackageStats { + downloads: number; + stars: number; +} + +export interface RegistryPackage { + id: string; + name: string; + version: string; + description: string; + author: Author; + type: PackageType; + stats: PackageStats; + tags: string[]; + readme: string; + license?: string; + repository?: string; + lastUpdated: string; + isOfficial?: boolean; + isTrending?: boolean; + icon?: string; +} + +// Mock data for core drivers and plugins +export const mockPackages: RegistryPackage[] = [ + { + id: 'driver-postgres', + name: 'driver-postgres', + version: '1.2.0', + description: 'Official PostgreSQL driver for ObjectStack. Connect to Postgres databases with full SQL support and type safety.', + author: { + name: 'ObjectStack Team', + avatar: '/avatars/objectstack.png', + url: 'https://github.com/objectstack-ai' + }, + type: 'driver', + stats: { + downloads: 45230, + stars: 892 + }, + tags: ['database', 'sql', 'postgresql', 'relational'], + license: 'MIT', + repository: 'https://github.com/objectstack-ai/driver-postgres', + lastUpdated: '2024-01-15', + isOfficial: true, + icon: '🐘', + readme: `# PostgreSQL Driver for ObjectStack + +The official PostgreSQL driver provides seamless integration with PostgreSQL databases. + +## Features + +- ✅ Full SQL support with type safety +- ✅ Connection pooling +- ✅ Transaction management +- ✅ Query builder integration +- ✅ Migration support + +## Installation + +\`\`\`bash +ostack add driver-postgres +\`\`\` + +## Quick Start + +\`\`\`typescript +import { PostgresDriver } from '@objectstack/driver-postgres'; + +const db = new PostgresDriver({ + host: 'localhost', + port: 5432, + database: 'mydb', + user: 'postgres', + password: 'secret' +}); + +await db.connect(); +const users = await db.query('SELECT * FROM users'); +\`\`\` + +## Configuration + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| host | string | Yes | Database host | +| port | number | No | Port (default: 5432) | +| database | string | Yes | Database name | +| user | string | Yes | Username | +| password | string | Yes | Password | + +## Advanced Usage + +### Connection Pooling + +\`\`\`typescript +const db = new PostgresDriver({ + // ... connection options + pool: { + min: 2, + max: 10 + } +}); +\`\`\` + +### Transactions + +\`\`\`typescript +await db.transaction(async (tx) => { + await tx.query('INSERT INTO users (name) VALUES ($1)', ['Alice']); + await tx.query('INSERT INTO logs (action) VALUES ($1)', ['user_created']); +}); +\`\`\` + +## License + +MIT © ObjectStack Team +` + }, + { + id: 'driver-redis', + name: 'driver-redis', + version: '1.5.3', + description: 'High-performance Redis driver for ObjectStack. Perfect for caching, session storage, and real-time applications.', + author: { + name: 'ObjectStack Team', + avatar: '/avatars/objectstack.png', + url: 'https://github.com/objectstack-ai' + }, + type: 'driver', + stats: { + downloads: 38920, + stars: 745 + }, + tags: ['cache', 'redis', 'nosql', 'key-value', 'real-time'], + license: 'MIT', + repository: 'https://github.com/objectstack-ai/driver-redis', + lastUpdated: '2024-01-10', + isOfficial: true, + icon: '🔴', + readme: `# Redis Driver for ObjectStack + +Official Redis driver with support for caching, pub/sub, and advanced data structures. + +## Features + +- ✅ Full Redis command support +- ✅ Pub/Sub messaging +- ✅ Cluster support +- ✅ Redis Streams +- ✅ Automatic reconnection + +## Installation + +\`\`\`bash +ostack add driver-redis +\`\`\` + +## Quick Start + +\`\`\`typescript +import { RedisDriver } from '@objectstack/driver-redis'; + +const redis = new RedisDriver({ + host: 'localhost', + port: 6379 +}); + +await redis.connect(); +await redis.set('key', 'value'); +const value = await redis.get('key'); +\`\`\` + +## Caching Example + +\`\`\`typescript +// Simple cache with TTL +await redis.setex('session:123', 3600, JSON.stringify(sessionData)); + +// Get cached data +const cached = await redis.get('session:123'); +\`\`\` + +## Pub/Sub + +\`\`\`typescript +// Subscribe to channel +redis.subscribe('notifications', (message) => { + console.log('Received:', message); +}); + +// Publish message +await redis.publish('notifications', 'Hello World'); +\`\`\` + +## License + +MIT © ObjectStack Team +` + }, + { + id: 'driver-excel', + name: 'driver-excel', + version: '2.0.1', + description: 'Parse and generate Excel files with ease. Support for XLSX, XLS, and CSV formats with advanced formatting options.', + author: { + name: 'ObjectStack Team', + avatar: '/avatars/objectstack.png', + url: 'https://github.com/objectstack-ai' + }, + type: 'driver', + stats: { + downloads: 28450, + stars: 512 + }, + tags: ['excel', 'spreadsheet', 'csv', 'data-import', 'export'], + license: 'MIT', + repository: 'https://github.com/objectstack-ai/driver-excel', + lastUpdated: '2024-01-18', + isOfficial: true, + isTrending: true, + icon: '📊', + readme: `# Excel Driver for ObjectStack + +Read, write, and manipulate Excel files with a simple, intuitive API. + +## Features + +- ✅ Read/Write XLSX, XLS, CSV +- ✅ Cell formatting and styling +- ✅ Formula support +- ✅ Charts and images +- ✅ Large file streaming + +## Installation + +\`\`\`bash +ostack add driver-excel +\`\`\` + +## Quick Start + +\`\`\`typescript +import { ExcelDriver } from '@objectstack/driver-excel'; + +const excel = new ExcelDriver(); + +// Read Excel file +const workbook = await excel.read('data.xlsx'); +const sheet = workbook.getSheet('Sheet1'); +const data = sheet.getData(); + +// Write Excel file +const newWorkbook = excel.createWorkbook(); +const newSheet = newWorkbook.addSheet('Sales'); +newSheet.setData([ + ['Product', 'Price', 'Quantity'], + ['Widget', 29.99, 100], + ['Gadget', 49.99, 50] +]); + +await excel.write(newWorkbook, 'output.xlsx'); +\`\`\` + +## Advanced Features + +### Formatting + +\`\`\`typescript +sheet.getCell('A1').style = { + font: { bold: true, size: 14 }, + fill: { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFFF0000' } } +}; +\`\`\` + +### Formulas + +\`\`\`typescript +sheet.getCell('D2').value = { formula: '=B2*C2' }; +\`\`\` + +## License + +MIT © ObjectStack Team +` + }, + { + id: 'driver-salesforce', + name: 'driver-salesforce', + version: '1.8.0', + description: 'Connect to Salesforce CRM with OAuth support. Query, create, and update Salesforce objects seamlessly.', + author: { + name: 'ObjectStack Team', + avatar: '/avatars/objectstack.png', + url: 'https://github.com/objectstack-ai' + }, + type: 'driver', + stats: { + downloads: 15680, + stars: 423 + }, + tags: ['salesforce', 'crm', 'saas', 'oauth', 'enterprise'], + license: 'MIT', + repository: 'https://github.com/objectstack-ai/driver-salesforce', + lastUpdated: '2024-01-12', + isOfficial: true, + icon: '☁️', + readme: `# Salesforce Driver for ObjectStack + +Official Salesforce CRM integration with full API support. + +## Features + +- ✅ OAuth 2.0 authentication +- ✅ SOQL query support +- ✅ CRUD operations on all objects +- ✅ Bulk API support +- ✅ Metadata API access + +## Installation + +\`\`\`bash +ostack add driver-salesforce +\`\`\` + +## Quick Start + +\`\`\`typescript +import { SalesforceDriver } from '@objectstack/driver-salesforce'; + +const sf = new SalesforceDriver({ + clientId: process.env.SF_CLIENT_ID, + clientSecret: process.env.SF_CLIENT_SECRET, + redirectUri: 'http://localhost:3000/callback' +}); + +// Authenticate +await sf.authenticate(); + +// Query records +const accounts = await sf.query('SELECT Id, Name FROM Account LIMIT 10'); + +// Create record +const newContact = await sf.create('Contact', { + FirstName: 'John', + LastName: 'Doe', + Email: 'john@example.com' +}); + +// Update record +await sf.update('Contact', contactId, { + Phone: '555-1234' +}); +\`\`\` + +## Bulk Operations + +\`\`\`typescript +// Insert multiple records efficiently +const jobs = await sf.bulk.insert('Account', [ + { Name: 'Company A' }, + { Name: 'Company B' }, + { Name: 'Company C' } +]); +\`\`\` + +## License + +MIT © ObjectStack Team +` + }, + { + id: 'plugin-auth', + name: 'plugin-auth', + version: '3.1.0', + description: 'Complete authentication solution with JWT, OAuth, and multi-factor authentication support.', + author: { + name: 'Community Contributors', + avatar: '/avatars/community.png', + url: 'https://github.com/objectstack-community' + }, + type: 'plugin', + stats: { + downloads: 52100, + stars: 1203 + }, + tags: ['authentication', 'security', 'jwt', 'oauth', '2fa'], + license: 'MIT', + repository: 'https://github.com/objectstack-community/plugin-auth', + lastUpdated: '2024-01-14', + icon: '🔐', + readme: `# Authentication Plugin for ObjectStack + +Production-ready authentication with multiple strategies. + +## Features + +- ✅ JWT tokens +- ✅ OAuth 2.0 (Google, GitHub, etc.) +- ✅ Multi-factor authentication +- ✅ Session management +- ✅ Password hashing + +## Installation + +\`\`\`bash +ostack add plugin-auth +\`\`\` + +## License + +MIT +` + }, + { + id: 'module-ui-kit', + name: 'module-ui-kit', + version: '2.3.5', + description: 'Beautiful, accessible React components built on Tailwind CSS. Dark mode support included.', + author: { + name: 'UI Team', + avatar: '/avatars/ui-team.png', + url: 'https://github.com/objectstack-ui' + }, + type: 'module', + stats: { + downloads: 18920, + stars: 689 + }, + tags: ['ui', 'components', 'react', 'tailwind', 'design-system'], + license: 'MIT', + repository: 'https://github.com/objectstack-ui/module-ui-kit', + lastUpdated: '2024-01-16', + isTrending: true, + icon: '🎨', + readme: `# UI Kit Module for ObjectStack + +A comprehensive UI component library. + +## Features + +- ✅ 50+ components +- ✅ Dark mode support +- ✅ Fully accessible +- ✅ Tailwind CSS +- ✅ TypeScript + +## Installation + +\`\`\`bash +ostack add module-ui-kit +\`\`\` + +## License + +MIT +` + } +]; + +// Helper functions +export function getPackageById(id: string): RegistryPackage | undefined { + return mockPackages.find(pkg => pkg.id === id); +} + +export function getPackagesByType(type: PackageType): RegistryPackage[] { + return mockPackages.filter(pkg => pkg.type === type); +} + +export function getTrendingPackages(): RegistryPackage[] { + return mockPackages.filter(pkg => pkg.isTrending); +} + +export function getOfficialPackages(): RegistryPackage[] { + return mockPackages.filter(pkg => pkg.isOfficial); +} + +export function searchPackages(query: string): RegistryPackage[] { + const lowerQuery = query.toLowerCase(); + return mockPackages.filter(pkg => + pkg.name.toLowerCase().includes(lowerQuery) || + pkg.description.toLowerCase().includes(lowerQuery) || + pkg.tags.some(tag => tag.toLowerCase().includes(lowerQuery)) + ); +} + +export function getPackagesByTag(tag: string): RegistryPackage[] { + return mockPackages.filter(pkg => + pkg.tags.some(t => t.toLowerCase() === tag.toLowerCase()) + ); +} + +// Categories for the homepage +export const categories = [ + { + id: 'databases', + name: 'Databases', + description: 'SQL and NoSQL database drivers', + icon: '🗄️', + tags: ['database', 'sql', 'nosql'] + }, + { + id: 'saas', + name: 'SaaS Integrations', + description: 'Connect to popular SaaS platforms', + icon: '☁️', + tags: ['saas', 'crm', 'enterprise'] + }, + { + id: 'data', + name: 'Data & Analytics', + description: 'Data processing and analytics tools', + icon: '📊', + tags: ['data-import', 'export', 'analytics'] + }, + { + id: 'ui', + name: 'UI Components', + description: 'Pre-built UI components and design systems', + icon: '🎨', + tags: ['ui', 'components', 'design-system'] + }, + { + id: 'security', + name: 'Security & Auth', + description: 'Authentication and security plugins', + icon: '🔐', + tags: ['authentication', 'security', 'oauth'] + }, + { + id: 'cache', + name: 'Caching', + description: 'Cache and session storage solutions', + icon: '⚡', + tags: ['cache', 'redis', 'performance'] + } +]; + +export function getPackagesByCategory(categoryId: string): RegistryPackage[] { + const category = categories.find(c => c.id === categoryId); + if (!category) return []; + + return mockPackages.filter(pkg => + pkg.tags.some(tag => category.tags.includes(tag)) + ); +} diff --git a/apps/marketplace/tsconfig.json b/apps/marketplace/tsconfig.json new file mode 100644 index 0000000..cf9c65d --- /dev/null +++ b/apps/marketplace/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +}+ {pkg.icon || '📦'} ++ + {/* Content */} +++++ ++ {pkg.name} +
+ {pkg.isOfficial && ( ++ )} + {pkg.isTrending && ( + + Trending + + )} + + {pkg.description} +
+ + {/* Tags */} ++ {pkg.tags.slice(0, 3).map((tag) => ( + + {tag} + + ))} ++ + {/* Stats */} ++++++ {formatNumber(pkg.stats.downloads)} + ++ v{pkg.version} ++ {formatNumber(pkg.stats.stars)} +