@farm.js/create-app 0.1.0-beta.103 → 0.1.0-beta.105

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/dist/index.mjs CHANGED
@@ -10,6 +10,174 @@ import { randomUUID } from "node:crypto";
10
10
  import { request } from "node:http";
11
11
  import { request as request$1 } from "node:https";
12
12
  import os from "node:os";
13
+ function shadcnComponentTemplate(component) {
14
+ switch (component) {
15
+ case "badge": return `import * as React from "react";
16
+ import { cva, type VariantProps } from "class-variance-authority";
17
+ import { cn } from "@/lib/utils";
18
+
19
+ const badgeVariants = cva(
20
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
21
+ {
22
+ variants: {
23
+ variant: {
24
+ default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
25
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
26
+ outline: "text-foreground",
27
+ },
28
+ },
29
+ defaultVariants: {
30
+ variant: "default",
31
+ },
32
+ },
33
+ );
34
+
35
+ export interface BadgeProps
36
+ extends React.HTMLAttributes<HTMLDivElement>,
37
+ VariantProps<typeof badgeVariants> {}
38
+
39
+ export function Badge({ className, variant, ...props }: BadgeProps) {
40
+ return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
41
+ }
42
+
43
+ export { badgeVariants };
44
+ `;
45
+ case "button": return `import * as React from "react";
46
+ import { cva, type VariantProps } from "class-variance-authority";
47
+ import { cn } from "@/lib/utils";
48
+
49
+ const buttonVariants = cva(
50
+ "inline-flex h-9 items-center justify-center whitespace-nowrap rounded-md px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
51
+ {
52
+ variants: {
53
+ variant: {
54
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
55
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
56
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
57
+ ghost: "hover:bg-accent hover:text-accent-foreground",
58
+ },
59
+ size: {
60
+ default: "h-9 px-4 py-2",
61
+ sm: "h-8 rounded-md px-3 text-xs",
62
+ lg: "h-10 rounded-md px-8",
63
+ },
64
+ },
65
+ defaultVariants: {
66
+ variant: "default",
67
+ size: "default",
68
+ },
69
+ },
70
+ );
71
+
72
+ export interface ButtonProps
73
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
74
+ VariantProps<typeof buttonVariants> {}
75
+
76
+ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
77
+ ({ className, variant, size, ...props }, ref) => {
78
+ return (
79
+ <button
80
+ className={cn(buttonVariants({ variant, size, className }))}
81
+ ref={ref}
82
+ {...props}
83
+ />
84
+ );
85
+ },
86
+ );
87
+ Button.displayName = "Button";
88
+
89
+ export { buttonVariants };
90
+ `;
91
+ case "card": return `import * as React from "react";
92
+ import { cn } from "@/lib/utils";
93
+
94
+ export const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
95
+ ({ className, ...props }, ref) => (
96
+ <div
97
+ ref={ref}
98
+ className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
99
+ {...props}
100
+ />
101
+ ),
102
+ );
103
+ Card.displayName = "Card";
104
+
105
+ export const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
106
+ ({ className, ...props }, ref) => (
107
+ <div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
108
+ ),
109
+ );
110
+ CardHeader.displayName = "CardHeader";
111
+
112
+ export const CardTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
113
+ ({ className, ...props }, ref) => (
114
+ <h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-normal", className)} {...props} />
115
+ ),
116
+ );
117
+ CardTitle.displayName = "CardTitle";
118
+
119
+ export const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
120
+ ({ className, ...props }, ref) => (
121
+ <p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
122
+ ),
123
+ );
124
+ CardDescription.displayName = "CardDescription";
125
+
126
+ export const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
127
+ ({ className, ...props }, ref) => (
128
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
129
+ ),
130
+ );
131
+ CardContent.displayName = "CardContent";
132
+
133
+ export const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
134
+ ({ className, ...props }, ref) => (
135
+ <div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
136
+ ),
137
+ );
138
+ CardFooter.displayName = "CardFooter";
139
+ `;
140
+ case "input": return `import * as React from "react";
141
+ import { cn } from "@/lib/utils";
142
+
143
+ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
144
+
145
+ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
146
+ ({ className, type, ...props }, ref) => (
147
+ <input
148
+ type={type}
149
+ className={cn(
150
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
151
+ className,
152
+ )}
153
+ ref={ref}
154
+ {...props}
155
+ />
156
+ ),
157
+ );
158
+ Input.displayName = "Input";
159
+ `;
160
+ case "label": return `import * as React from "react";
161
+ import { cn } from "@/lib/utils";
162
+
163
+ export const Label = React.forwardRef<HTMLLabelElement, React.LabelHTMLAttributes<HTMLLabelElement>>(
164
+ ({ className, ...props }, ref) => (
165
+ <label
166
+ ref={ref}
167
+ className={cn("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", className)}
168
+ {...props}
169
+ />
170
+ ),
171
+ );
172
+ Label.displayName = "Label";
173
+ `;
174
+ }
175
+ }
176
+ //#endregion
177
+ //#region ../farm-cli/src/ui-registry.ts
178
+ /** Value written into a generated `components.json` as `registries.farm.url`. */
179
+ const UI_REGISTRY_URL_TEMPLATE = "https://farmjs.dev/r/{name}.json";
180
+ //#endregion
13
181
  //#region ../farm-cli/src/ui-feature-registry.ts
14
182
  async function installUIFeature(input) {
15
183
  const feature = input.definition.ui;
@@ -252,6 +420,16 @@ function unkeyApiKeysUIFeature() {
252
420
  ]
253
421
  };
254
422
  }
423
+ function sanityContentUIFeature() {
424
+ return {
425
+ name: "sanity-content",
426
+ description: "Sanity content list UI",
427
+ components: ["badge", "card"],
428
+ needsApiClient: false,
429
+ notes: ["Open \"/integrations/sanity\" to check the Sanity connection."],
430
+ files: () => [componentFile("sanity-content-console.tsx", sanityContentTemplate()), integrationPageFile("sanity", "SanityContentConsole")]
431
+ };
432
+ }
255
433
  function billingUIFeature(input) {
256
434
  return {
257
435
  name: `${input.provider}-billing`,
@@ -554,7 +732,7 @@ function createComponentsJson() {
554
732
  lib: "@/lib",
555
733
  hooks: "@/hooks"
556
734
  },
557
- registries: { farm: { url: "https://farmjs.dev/r/{name}.json" } }
735
+ registries: { farm: { url: UI_REGISTRY_URL_TEMPLATE } }
558
736
  };
559
737
  }
560
738
  function mergeComponentsJson(current, defaults) {
@@ -597,169 +775,6 @@ import type { AppIntegrations } from "./integrations";
597
775
  export const { api, apiClient } = createIntegrations<AppIntegrations>();
598
776
  `;
599
777
  }
600
- function shadcnComponentTemplate(component) {
601
- switch (component) {
602
- case "badge": return `import * as React from "react";
603
- import { cva, type VariantProps } from "class-variance-authority";
604
- import { cn } from "@/lib/utils";
605
-
606
- const badgeVariants = cva(
607
- "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
608
- {
609
- variants: {
610
- variant: {
611
- default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
612
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
613
- outline: "text-foreground",
614
- },
615
- },
616
- defaultVariants: {
617
- variant: "default",
618
- },
619
- },
620
- );
621
-
622
- export interface BadgeProps
623
- extends React.HTMLAttributes<HTMLDivElement>,
624
- VariantProps<typeof badgeVariants> {}
625
-
626
- export function Badge({ className, variant, ...props }: BadgeProps) {
627
- return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
628
- }
629
-
630
- export { badgeVariants };
631
- `;
632
- case "button": return `import * as React from "react";
633
- import { cva, type VariantProps } from "class-variance-authority";
634
- import { cn } from "@/lib/utils";
635
-
636
- const buttonVariants = cva(
637
- "inline-flex h-9 items-center justify-center whitespace-nowrap rounded-md px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
638
- {
639
- variants: {
640
- variant: {
641
- default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
642
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
643
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
644
- ghost: "hover:bg-accent hover:text-accent-foreground",
645
- },
646
- size: {
647
- default: "h-9 px-4 py-2",
648
- sm: "h-8 rounded-md px-3 text-xs",
649
- lg: "h-10 rounded-md px-8",
650
- },
651
- },
652
- defaultVariants: {
653
- variant: "default",
654
- size: "default",
655
- },
656
- },
657
- );
658
-
659
- export interface ButtonProps
660
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
661
- VariantProps<typeof buttonVariants> {}
662
-
663
- export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
664
- ({ className, variant, size, ...props }, ref) => {
665
- return (
666
- <button
667
- className={cn(buttonVariants({ variant, size, className }))}
668
- ref={ref}
669
- {...props}
670
- />
671
- );
672
- },
673
- );
674
- Button.displayName = "Button";
675
-
676
- export { buttonVariants };
677
- `;
678
- case "card": return `import * as React from "react";
679
- import { cn } from "@/lib/utils";
680
-
681
- export const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
682
- ({ className, ...props }, ref) => (
683
- <div
684
- ref={ref}
685
- className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
686
- {...props}
687
- />
688
- ),
689
- );
690
- Card.displayName = "Card";
691
-
692
- export const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
693
- ({ className, ...props }, ref) => (
694
- <div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
695
- ),
696
- );
697
- CardHeader.displayName = "CardHeader";
698
-
699
- export const CardTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
700
- ({ className, ...props }, ref) => (
701
- <h3 ref={ref} className={cn("text-2xl font-semibold leading-none tracking-normal", className)} {...props} />
702
- ),
703
- );
704
- CardTitle.displayName = "CardTitle";
705
-
706
- export const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
707
- ({ className, ...props }, ref) => (
708
- <p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
709
- ),
710
- );
711
- CardDescription.displayName = "CardDescription";
712
-
713
- export const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
714
- ({ className, ...props }, ref) => (
715
- <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
716
- ),
717
- );
718
- CardContent.displayName = "CardContent";
719
-
720
- export const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
721
- ({ className, ...props }, ref) => (
722
- <div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
723
- ),
724
- );
725
- CardFooter.displayName = "CardFooter";
726
- `;
727
- case "input": return `import * as React from "react";
728
- import { cn } from "@/lib/utils";
729
-
730
- export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
731
-
732
- export const Input = React.forwardRef<HTMLInputElement, InputProps>(
733
- ({ className, type, ...props }, ref) => (
734
- <input
735
- type={type}
736
- className={cn(
737
- "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
738
- className,
739
- )}
740
- ref={ref}
741
- {...props}
742
- />
743
- ),
744
- );
745
- Input.displayName = "Input";
746
- `;
747
- case "label": return `import * as React from "react";
748
- import { cn } from "@/lib/utils";
749
-
750
- export const Label = React.forwardRef<HTMLLabelElement, React.LabelHTMLAttributes<HTMLLabelElement>>(
751
- ({ className, ...props }, ref) => (
752
- <label
753
- ref={ref}
754
- className={cn("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", className)}
755
- {...props}
756
- />
757
- ),
758
- );
759
- Label.displayName = "Label";
760
- `;
761
- }
762
- }
763
778
  function billingPricingTemplate(input) {
764
779
  return `"use client";
765
780
 
@@ -1659,6 +1674,40 @@ export function UnkeyApiKeysConsole() {
1659
1674
  }
1660
1675
  `;
1661
1676
  }
1677
+ function sanityContentTemplate() {
1678
+ return `import { Badge } from "@/components/ui/badge";
1679
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
1680
+
1681
+ export function SanityContentConsole() {
1682
+ return (
1683
+ <main className="min-h-screen bg-background px-6 py-12 text-foreground">
1684
+ <section className="mx-auto flex w-full max-w-3xl flex-col gap-6">
1685
+ <div className="space-y-3">
1686
+ <Badge variant="secondary">Sanity</Badge>
1687
+ <h1 className="text-3xl font-semibold tracking-normal">Content</h1>
1688
+ </div>
1689
+ <Card>
1690
+ <CardHeader>
1691
+ <CardTitle className="text-xl">Connected to Sanity</CardTitle>
1692
+ <CardDescription>
1693
+ Query documents with createServerQuery and invalidate them from the Sanity webhook.
1694
+ </CardDescription>
1695
+ </CardHeader>
1696
+ <CardContent className="space-y-3">
1697
+ <code className="block overflow-x-auto rounded-md border bg-muted/30 p-3 text-xs">
1698
+ cms.fetch(&apos;*[_type == &quot;post&quot;]{"{"} title {"}"}&apos;)
1699
+ </code>
1700
+ <p className="text-sm text-muted-foreground">
1701
+ Point a Sanity webhook at /api/sanity/webhook to keep cached pages fresh.
1702
+ </p>
1703
+ </CardContent>
1704
+ </Card>
1705
+ </section>
1706
+ </main>
1707
+ );
1708
+ }
1709
+ `;
1710
+ }
1662
1711
  function unkeyProtectedRouteTemplate() {
1663
1712
  return `export function GET() {
1664
1713
  return Response.json({
@@ -2201,6 +2250,43 @@ export const unkeyIntegration = unkey({
2201
2250
  console.log("[unkey]", event.phase, event.route?.path || "none");
2202
2251
  },
2203
2252
  });
2253
+ `
2254
+ },
2255
+ {
2256
+ provider: "sanity",
2257
+ aliases: ["cms", "sanity-cms"],
2258
+ defaultKey: "cms",
2259
+ packageName: "@farm.js/sanity",
2260
+ fileName: "sanity",
2261
+ exportName: "sanityIntegration",
2262
+ description: "Sanity content with server queries, CDN images, and webhook cache invalidation",
2263
+ env: [
2264
+ "SANITY_PROJECT_ID",
2265
+ "SANITY_DATASET",
2266
+ "SANITY_API_READ_TOKEN",
2267
+ "SANITY_WEBHOOK_SECRET"
2268
+ ],
2269
+ dependencies: { "@sanity/client": "^8.4.0" },
2270
+ ui: sanityContentUIFeature(),
2271
+ template: () => `import { sanity } from "@farm.js/sanity";
2272
+
2273
+ export const sanityIntegration = sanity({
2274
+ projectId: process.env.SANITY_PROJECT_ID,
2275
+ dataset: process.env.SANITY_DATASET,
2276
+ token: process.env.SANITY_API_READ_TOKEN,
2277
+ // The webhook fires before Sanity's CDN updates, so reads go through the API.
2278
+ useCdn: false,
2279
+ webhook: {
2280
+ secret: process.env.SANITY_WEBHOOK_SECRET,
2281
+ onChange(payload) {
2282
+ console.log("[sanity]", payload._type, payload._id);
2283
+ return undefined;
2284
+ },
2285
+ },
2286
+ log(event) {
2287
+ console.log("[sanity]", event.phase, event.route?.path || "none");
2288
+ },
2289
+ });
2204
2290
  `
2205
2291
  }
2206
2292
  ];
@@ -2540,6 +2626,7 @@ const FARM_TELEMETRY_TEMPLATES = [
2540
2626
  "jobs-trigger",
2541
2627
  "polar",
2542
2628
  "resend",
2629
+ "sanity",
2543
2630
  "stripe",
2544
2631
  "supabase",
2545
2632
  "unkey",