@pylonsync/create-pylon 0.3.53 → 0.3.54

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.
Files changed (127) hide show
  1. package/bin/create-pylon.js +98 -42
  2. package/package.json +1 -1
  3. package/templates/backend/b2b/apps/api/functions/archiveProject.ts +15 -0
  4. package/templates/backend/b2b/apps/api/functions/createOrg.ts +43 -0
  5. package/templates/backend/b2b/apps/api/functions/createProject.ts +25 -0
  6. package/templates/backend/b2b/apps/api/functions/inviteMember.ts +49 -0
  7. package/templates/backend/b2b/apps/api/functions/myOrgs.ts +37 -0
  8. package/templates/backend/b2b/apps/api/functions/orgMembers.ts +13 -0
  9. package/templates/backend/b2b/apps/api/functions/orgProjects.ts +18 -0
  10. package/templates/backend/b2b/apps/api/functions/removeMember.ts +29 -0
  11. package/templates/backend/b2b/apps/api/functions/setMemberRole.ts +38 -0
  12. package/templates/backend/b2b/apps/api/package.json +20 -0
  13. package/templates/backend/b2b/apps/api/schema.ts +171 -0
  14. package/templates/backend/b2b/apps/api/tsconfig.json +13 -0
  15. package/templates/backend/chat/apps/api/functions/createRoom.ts +32 -0
  16. package/templates/backend/chat/apps/api/functions/listRooms.ts +15 -0
  17. package/templates/backend/chat/apps/api/functions/roomMessages.ts +20 -0
  18. package/templates/backend/chat/apps/api/functions/sendMessage.ts +37 -0
  19. package/templates/backend/chat/apps/api/package.json +20 -0
  20. package/templates/backend/chat/apps/api/schema.ts +93 -0
  21. package/templates/backend/chat/apps/api/tsconfig.json +13 -0
  22. package/templates/backend/consumer/apps/api/functions/createPost.ts +48 -0
  23. package/templates/backend/consumer/apps/api/functions/deletePost.ts +21 -0
  24. package/templates/backend/consumer/apps/api/functions/feed.ts +57 -0
  25. package/templates/backend/consumer/apps/api/functions/myProfile.ts +17 -0
  26. package/templates/backend/consumer/apps/api/functions/profilePosts.ts +17 -0
  27. package/templates/backend/consumer/apps/api/functions/toggleLike.ts +48 -0
  28. package/templates/backend/consumer/apps/api/functions/upsertProfile.ts +70 -0
  29. package/templates/backend/consumer/apps/api/package.json +20 -0
  30. package/templates/backend/consumer/apps/api/schema.ts +130 -0
  31. package/templates/backend/consumer/apps/api/tsconfig.json +13 -0
  32. package/templates/expo/chat/apps/expo/App.tsx +414 -0
  33. package/templates/expo/chat/apps/expo/app.json +25 -0
  34. package/templates/expo/chat/apps/expo/babel.config.js +6 -0
  35. package/templates/expo/chat/apps/expo/package.json +30 -0
  36. package/templates/expo/chat/apps/expo/tsconfig.json +16 -0
  37. package/templates/expo/consumer/apps/expo/App.tsx +360 -0
  38. package/templates/expo/consumer/apps/expo/app.json +25 -0
  39. package/templates/expo/consumer/apps/expo/babel.config.js +6 -0
  40. package/templates/expo/consumer/apps/expo/package.json +30 -0
  41. package/templates/expo/consumer/apps/expo/tsconfig.json +16 -0
  42. package/templates/ios/chat/apps/ios/Package.swift +34 -0
  43. package/templates/ios/chat/apps/ios/Sources/__APP_NAME_PASCAL__/ChatRootView.swift +120 -0
  44. package/templates/ios/chat/apps/ios/Sources/__APP_NAME_PASCAL__/Models.swift +26 -0
  45. package/templates/ios/chat/apps/ios/Sources/__APP_NAME_PASCAL__/RoomView.swift +137 -0
  46. package/templates/ios/chat/apps/ios/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +35 -0
  47. package/templates/ios/chat/apps/ios/project.yml +42 -0
  48. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/FeedView.swift +170 -0
  49. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/Models.swift +42 -0
  50. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/ProfileSetupView.swift +60 -0
  51. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/RootView.swift +30 -0
  52. package/templates/ios/consumer/apps/ios/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +29 -0
  53. package/templates/ios/consumer/apps/ios/project.yml +42 -0
  54. package/templates/ios/todo/apps/ios/Package.swift +23 -0
  55. package/templates/mac/b2b/apps/mac/Package.swift +22 -0
  56. package/templates/mac/b2b/apps/mac/Sources/__APP_NAME_PASCAL__/Models.swift +15 -0
  57. package/templates/mac/b2b/apps/mac/Sources/__APP_NAME_PASCAL__/OrgPickerView.swift +178 -0
  58. package/templates/mac/b2b/apps/mac/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +30 -0
  59. package/templates/mac/b2b/apps/mac/__APP_NAME_PASCAL__.entitlements +13 -0
  60. package/templates/mac/b2b/apps/mac/project.yml +34 -0
  61. package/templates/mac/barebones/apps/mac/Package.swift +33 -0
  62. package/templates/mac/barebones/apps/mac/Sources/__APP_NAME_PASCAL__/ContentView.swift +104 -0
  63. package/templates/mac/barebones/apps/mac/Sources/__APP_NAME_PASCAL__/Models.swift +17 -0
  64. package/templates/mac/barebones/apps/mac/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +30 -0
  65. package/templates/mac/barebones/apps/mac/__APP_NAME_PASCAL__.entitlements +13 -0
  66. package/templates/mac/barebones/apps/mac/project.yml +34 -0
  67. package/templates/mac/chat/apps/mac/Package.swift +33 -0
  68. package/templates/mac/chat/apps/mac/Sources/__APP_NAME_PASCAL__/ChatRootView.swift +140 -0
  69. package/templates/mac/chat/apps/mac/Sources/__APP_NAME_PASCAL__/Models.swift +26 -0
  70. package/templates/mac/chat/apps/mac/Sources/__APP_NAME_PASCAL__/RoomView.swift +137 -0
  71. package/templates/mac/chat/apps/mac/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +37 -0
  72. package/templates/mac/chat/apps/mac/__APP_NAME_PASCAL__.entitlements +13 -0
  73. package/templates/mac/chat/apps/mac/project.yml +34 -0
  74. package/templates/mac/consumer/apps/mac/Package.swift +33 -0
  75. package/templates/mac/consumer/apps/mac/Sources/__APP_NAME_PASCAL__/FeedView.swift +170 -0
  76. package/templates/mac/consumer/apps/mac/Sources/__APP_NAME_PASCAL__/Models.swift +42 -0
  77. package/templates/mac/consumer/apps/mac/Sources/__APP_NAME_PASCAL__/ProfileSetupView.swift +60 -0
  78. package/templates/mac/consumer/apps/mac/Sources/__APP_NAME_PASCAL__/RootView.swift +30 -0
  79. package/templates/mac/consumer/apps/mac/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +31 -0
  80. package/templates/mac/consumer/apps/mac/__APP_NAME_PASCAL__.entitlements +13 -0
  81. package/templates/mac/consumer/apps/mac/project.yml +34 -0
  82. package/templates/mac/todo/apps/mac/Package.swift +33 -0
  83. package/templates/mac/todo/apps/mac/Sources/__APP_NAME_PASCAL__/Models.swift +19 -0
  84. package/templates/mac/todo/apps/mac/Sources/__APP_NAME_PASCAL__/TodoListView.swift +244 -0
  85. package/templates/mac/todo/apps/mac/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +30 -0
  86. package/templates/mac/todo/apps/mac/__APP_NAME_PASCAL__.entitlements +13 -0
  87. package/templates/mac/todo/apps/mac/project.yml +34 -0
  88. package/templates/web/b2b/apps/web/next-env.d.ts +2 -0
  89. package/templates/web/b2b/apps/web/next.config.ts +24 -0
  90. package/templates/web/b2b/apps/web/package.json +29 -0
  91. package/templates/web/b2b/apps/web/postcss.config.mjs +3 -0
  92. package/templates/web/b2b/apps/web/src/app/components/OrgPicker.tsx +171 -0
  93. package/templates/web/b2b/apps/web/src/app/globals.css +6 -0
  94. package/templates/web/b2b/apps/web/src/app/layout.tsx +21 -0
  95. package/templates/web/b2b/apps/web/src/app/page.tsx +39 -0
  96. package/templates/web/b2b/apps/web/src/lib/pylon.ts +5 -0
  97. package/templates/web/b2b/apps/web/tsconfig.json +26 -0
  98. package/templates/web/chat/apps/web/next-env.d.ts +2 -0
  99. package/templates/web/chat/apps/web/next.config.ts +24 -0
  100. package/templates/web/chat/apps/web/package.json +29 -0
  101. package/templates/web/chat/apps/web/postcss.config.mjs +3 -0
  102. package/templates/web/chat/apps/web/src/app/components/ChatRoom.tsx +250 -0
  103. package/templates/web/chat/apps/web/src/app/globals.css +6 -0
  104. package/templates/web/chat/apps/web/src/app/layout.tsx +21 -0
  105. package/templates/web/chat/apps/web/src/app/page.tsx +51 -0
  106. package/templates/web/chat/apps/web/src/lib/pylon.ts +5 -0
  107. package/templates/web/chat/apps/web/tsconfig.json +26 -0
  108. package/templates/web/consumer/apps/web/next-env.d.ts +2 -0
  109. package/templates/web/consumer/apps/web/next.config.ts +24 -0
  110. package/templates/web/consumer/apps/web/package.json +29 -0
  111. package/templates/web/consumer/apps/web/postcss.config.mjs +3 -0
  112. package/templates/web/consumer/apps/web/src/app/components/Feed.tsx +295 -0
  113. package/templates/web/consumer/apps/web/src/app/globals.css +6 -0
  114. package/templates/web/consumer/apps/web/src/app/layout.tsx +21 -0
  115. package/templates/web/consumer/apps/web/src/app/page.tsx +55 -0
  116. package/templates/web/consumer/apps/web/src/lib/pylon.ts +5 -0
  117. package/templates/web/consumer/apps/web/tsconfig.json +26 -0
  118. /package/templates/{mobile/barebones/apps/mobile → ios/barebones/apps/ios}/Package.swift +0 -0
  119. /package/templates/{mobile/barebones/apps/mobile → ios/barebones/apps/ios}/Sources/__APP_NAME_PASCAL__/ContentView.swift +0 -0
  120. /package/templates/{mobile/barebones/apps/mobile → ios/barebones/apps/ios}/Sources/__APP_NAME_PASCAL__/Models.swift +0 -0
  121. /package/templates/{mobile/barebones/apps/mobile → ios/barebones/apps/ios}/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +0 -0
  122. /package/templates/{mobile/barebones/apps/mobile → ios/barebones/apps/ios}/project.yml +0 -0
  123. /package/templates/{mobile/todo/apps/mobile → ios/consumer/apps/ios}/Package.swift +0 -0
  124. /package/templates/{mobile/todo/apps/mobile → ios/todo/apps/ios}/Sources/__APP_NAME_PASCAL__/Models.swift +0 -0
  125. /package/templates/{mobile/todo/apps/mobile → ios/todo/apps/ios}/Sources/__APP_NAME_PASCAL__/TodoListView.swift +0 -0
  126. /package/templates/{mobile/todo/apps/mobile → ios/todo/apps/ios}/Sources/__APP_NAME_PASCAL__/__APP_NAME_PASCAL__App.swift +0 -0
  127. /package/templates/{mobile/todo/apps/mobile → ios/todo/apps/ios}/project.yml +0 -0
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@__APP_NAME_KEBAB__/web",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "next dev --port 3000",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "next lint"
11
+ },
12
+ "dependencies": {
13
+ "@__APP_NAME_KEBAB__/ui": "__WORKSPACE_DEP__",
14
+ "@pylonsync/sdk": "^__PYLON_VERSION__",
15
+ "@pylonsync/react": "^__PYLON_VERSION__",
16
+ "@pylonsync/next": "^__PYLON_VERSION__",
17
+ "next": "^16.0.0",
18
+ "react": "^19.0.0",
19
+ "react-dom": "^19.0.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.0.0",
23
+ "@types/react": "^19.0.0",
24
+ "@types/react-dom": "^19.0.0",
25
+ "@tailwindcss/postcss": "^4.0.0",
26
+ "tailwindcss": "^4.0.0",
27
+ "typescript": "^5.5.0"
28
+ }
29
+ }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ plugins: { "@tailwindcss/postcss": {} },
3
+ };
@@ -0,0 +1,250 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef, useState, useTransition } from "react";
4
+ import { Button, Input } from "@__APP_NAME_KEBAB__/ui";
5
+
6
+ type Room = {
7
+ id: string;
8
+ slug: string;
9
+ name: string;
10
+ createdAt: string;
11
+ };
12
+
13
+ type Message = {
14
+ id: string;
15
+ roomId: string;
16
+ authorId: string;
17
+ authorName: string;
18
+ body: string;
19
+ createdAt: string;
20
+ };
21
+
22
+ const NAME_KEY = "__APP_NAME_SNAKE___author_name";
23
+
24
+ export function ChatRoom({
25
+ initialRooms,
26
+ initialActiveRoom,
27
+ initialMessages,
28
+ }: {
29
+ initialRooms: Room[];
30
+ initialActiveRoom: Room | null;
31
+ initialMessages: Message[];
32
+ }) {
33
+ const [rooms, setRooms] = useState(initialRooms);
34
+ const [active, setActive] = useState(initialActiveRoom);
35
+ const [messages, setMessages] = useState(initialMessages);
36
+ const [body, setBody] = useState("");
37
+ const [authorName, setAuthorName] = useState("anonymous");
38
+ const [pending, startTransition] = useTransition();
39
+ const [pollIdx, setPollIdx] = useState(0);
40
+ const scrollerRef = useRef<HTMLDivElement>(null);
41
+
42
+ // Poll the active room every 1.5s. The framework supports a
43
+ // WebSocket subscription path (db.useQuery) — we use polling here
44
+ // so the scaffold has zero front-end SDK setup. Swap in
45
+ // db.useQuery("Message", { roomId }) when you wire up
46
+ // `@pylonsync/react`'s init() in your layout.
47
+ useEffect(() => {
48
+ if (!active) return;
49
+ const t = setInterval(() => setPollIdx((n) => n + 1), 1500);
50
+ return () => clearInterval(t);
51
+ }, [active]);
52
+
53
+ useEffect(() => {
54
+ if (!active) return;
55
+ void (async () => {
56
+ const res = await fetch("/api/fn/roomMessages", {
57
+ method: "POST",
58
+ headers: { "Content-Type": "application/json" },
59
+ body: JSON.stringify({ roomId: active.id }),
60
+ });
61
+ if (res.ok) setMessages(await res.json());
62
+ })();
63
+ }, [active, pollIdx]);
64
+
65
+ useEffect(() => {
66
+ const stored =
67
+ typeof window !== "undefined"
68
+ ? window.localStorage.getItem(NAME_KEY)
69
+ : null;
70
+ if (stored) setAuthorName(stored);
71
+ }, []);
72
+
73
+ useEffect(() => {
74
+ // Auto-scroll to bottom on new messages.
75
+ scrollerRef.current?.scrollTo({
76
+ top: scrollerRef.current.scrollHeight,
77
+ behavior: "smooth",
78
+ });
79
+ }, [messages]);
80
+
81
+ function persistName(next: string) {
82
+ setAuthorName(next);
83
+ window.localStorage.setItem(NAME_KEY, next);
84
+ }
85
+
86
+ async function send() {
87
+ const trimmed = body.trim();
88
+ if (!trimmed || !active) return;
89
+ setBody("");
90
+ startTransition(async () => {
91
+ const res = await fetch("/api/fn/sendMessage", {
92
+ method: "POST",
93
+ headers: { "Content-Type": "application/json" },
94
+ body: JSON.stringify({
95
+ roomId: active.id,
96
+ body: trimmed,
97
+ authorName,
98
+ }),
99
+ });
100
+ if (res.ok) {
101
+ const msg = (await res.json()) as Message;
102
+ setMessages((prev) => [...prev, msg]);
103
+ }
104
+ });
105
+ }
106
+
107
+ async function createRoom() {
108
+ const name = window.prompt("Room name?");
109
+ if (!name) return;
110
+ const slug = name
111
+ .toLowerCase()
112
+ .replace(/[^a-z0-9]+/g, "-")
113
+ .replace(/^-|-$/g, "");
114
+ startTransition(async () => {
115
+ const res = await fetch("/api/fn/createRoom", {
116
+ method: "POST",
117
+ headers: { "Content-Type": "application/json" },
118
+ body: JSON.stringify({ slug, name }),
119
+ });
120
+ if (res.ok) {
121
+ const room = (await res.json()) as Room;
122
+ setRooms((prev) => [...prev, room]);
123
+ setActive(room);
124
+ }
125
+ });
126
+ }
127
+
128
+ return (
129
+ <>
130
+ <aside className="w-64 border-r border-neutral-200 dark:border-neutral-800 flex flex-col">
131
+ <div className="p-4 border-b border-neutral-200 dark:border-neutral-800">
132
+ <div className="flex items-center justify-between mb-2">
133
+ <h2 className="text-sm font-medium">Rooms</h2>
134
+ <button
135
+ onClick={createRoom}
136
+ className="text-xs text-neutral-500 hover:text-neutral-800 dark:hover:text-neutral-200"
137
+ >
138
+ + New
139
+ </button>
140
+ </div>
141
+ <Input
142
+ value={authorName}
143
+ onChange={(e) => persistName(e.target.value)}
144
+ placeholder="Your name"
145
+ className="text-xs"
146
+ />
147
+ </div>
148
+ <ul className="flex-1 overflow-auto divide-y divide-neutral-200 dark:divide-neutral-800">
149
+ {rooms.map((r) => (
150
+ <li key={r.id}>
151
+ <button
152
+ onClick={() => setActive(r)}
153
+ className={`w-full text-left px-4 py-2.5 text-sm hover:bg-neutral-50 dark:hover:bg-neutral-900 ${
154
+ r.id === active?.id
155
+ ? "bg-neutral-100 dark:bg-neutral-800 font-medium"
156
+ : ""
157
+ }`}
158
+ >
159
+ <div>{r.name}</div>
160
+ <div className="text-xs text-neutral-400 font-mono">
161
+ #{r.slug}
162
+ </div>
163
+ </button>
164
+ </li>
165
+ ))}
166
+ {rooms.length === 0 && (
167
+ <li className="px-4 py-3 text-xs text-neutral-500">
168
+ No rooms yet. Create one above.
169
+ </li>
170
+ )}
171
+ </ul>
172
+ </aside>
173
+
174
+ <section className="flex-1 flex flex-col min-w-0">
175
+ {active ? (
176
+ <>
177
+ <header className="px-6 py-3 border-b border-neutral-200 dark:border-neutral-800">
178
+ <h1 className="text-sm font-medium">{active.name}</h1>
179
+ <p className="text-xs text-neutral-400 font-mono">
180
+ #{active.slug}
181
+ </p>
182
+ </header>
183
+
184
+ <div ref={scrollerRef} className="flex-1 overflow-auto px-6 py-4 space-y-3">
185
+ {messages.length === 0 ? (
186
+ <p className="text-sm text-neutral-500 text-center py-12">
187
+ No messages yet. Say hi.
188
+ </p>
189
+ ) : (
190
+ messages.map((m) => (
191
+ <div key={m.id} className="space-y-0.5">
192
+ <div className="flex items-baseline gap-2">
193
+ <span className="text-sm font-medium">
194
+ {m.authorName}
195
+ </span>
196
+ <span className="text-xs text-neutral-400">
197
+ {new Date(m.createdAt).toLocaleTimeString(undefined, {
198
+ hour: "numeric",
199
+ minute: "2-digit",
200
+ })}
201
+ </span>
202
+ </div>
203
+ <p className="text-sm whitespace-pre-wrap break-words">
204
+ {m.body}
205
+ </p>
206
+ </div>
207
+ ))
208
+ )}
209
+ </div>
210
+
211
+ <form
212
+ onSubmit={(e) => {
213
+ e.preventDefault();
214
+ send();
215
+ }}
216
+ className="px-6 py-3 border-t border-neutral-200 dark:border-neutral-800 flex gap-2"
217
+ >
218
+ <Input
219
+ value={body}
220
+ onChange={(e) => setBody(e.target.value)}
221
+ placeholder={`Message ${active.name}…`}
222
+ disabled={pending}
223
+ className="flex-1"
224
+ />
225
+ <Button
226
+ type="submit"
227
+ variant="primary"
228
+ disabled={pending || !body.trim()}
229
+ >
230
+ Send
231
+ </Button>
232
+ </form>
233
+ </>
234
+ ) : (
235
+ <div className="flex-1 flex items-center justify-center">
236
+ <div className="text-center space-y-2">
237
+ <p className="text-sm text-neutral-500">No room selected.</p>
238
+ <button
239
+ onClick={createRoom}
240
+ className="text-sm text-blue-600 hover:underline"
241
+ >
242
+ Create the first one
243
+ </button>
244
+ </div>
245
+ </div>
246
+ )}
247
+ </section>
248
+ </>
249
+ );
250
+ }
@@ -0,0 +1,6 @@
1
+ @import "tailwindcss";
2
+ @source "../../../../packages/ui/src/**/*.{ts,tsx}";
3
+
4
+ :root { color-scheme: light dark; }
5
+ html, body { height: 100%; }
6
+ body { font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; }
@@ -0,0 +1,21 @@
1
+ import type { Metadata } from "next";
2
+ import "./globals.css";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "__APP_NAME__",
6
+ description: "Multi-tenant SaaS scaffold powered by Pylon",
7
+ };
8
+
9
+ export default function RootLayout({
10
+ children,
11
+ }: {
12
+ children: React.ReactNode;
13
+ }) {
14
+ return (
15
+ <html lang="en">
16
+ <body className="antialiased min-h-screen bg-white dark:bg-neutral-950 text-neutral-900 dark:text-neutral-100">
17
+ {children}
18
+ </body>
19
+ </html>
20
+ );
21
+ }
@@ -0,0 +1,51 @@
1
+ import { pylon } from "@/lib/pylon";
2
+ import { ChatRoom } from "./components/ChatRoom";
3
+
4
+ export const dynamic = "force-dynamic";
5
+
6
+ type Room = {
7
+ id: string;
8
+ slug: string;
9
+ name: string;
10
+ createdAt: string;
11
+ };
12
+
13
+ type Message = {
14
+ id: string;
15
+ roomId: string;
16
+ authorId: string;
17
+ authorName: string;
18
+ body: string;
19
+ createdAt: string;
20
+ };
21
+
22
+ export default async function HomePage() {
23
+ const rooms = await pylon
24
+ .json<Room[]>("/api/fn/listRooms", {
25
+ method: "POST",
26
+ body: "{}",
27
+ headers: { "Content-Type": "application/json" },
28
+ })
29
+ .catch(() => [] as Room[]);
30
+
31
+ const initialRoom = rooms[0] ?? null;
32
+ const initialMessages = initialRoom
33
+ ? await pylon
34
+ .json<Message[]>("/api/fn/roomMessages", {
35
+ method: "POST",
36
+ body: JSON.stringify({ roomId: initialRoom.id }),
37
+ headers: { "Content-Type": "application/json" },
38
+ })
39
+ .catch(() => [] as Message[])
40
+ : [];
41
+
42
+ return (
43
+ <main className="h-screen flex">
44
+ <ChatRoom
45
+ initialRooms={rooms}
46
+ initialActiveRoom={initialRoom}
47
+ initialMessages={initialMessages}
48
+ />
49
+ </main>
50
+ );
51
+ }
@@ -0,0 +1,5 @@
1
+ import { createPylonServer } from "@pylonsync/next/server";
2
+
3
+ export const pylon = createPylonServer({
4
+ cookieName: "__APP_NAME_SNAKE___session",
5
+ });
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [{ "name": "next" }],
17
+ "paths": { "@/*": ["./src/*"] }
18
+ },
19
+ "include": [
20
+ "next-env.d.ts",
21
+ "src/**/*.ts",
22
+ "src/**/*.tsx",
23
+ ".next/types/**/*.ts"
24
+ ],
25
+ "exclude": ["node_modules"]
26
+ }
@@ -0,0 +1,2 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
@@ -0,0 +1,24 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const PYLON_API_URL = process.env.PYLON_API_URL ?? "http://localhost:4321";
4
+
5
+ const config: NextConfig = {
6
+ transpilePackages: [
7
+ "@__APP_NAME_KEBAB__/ui",
8
+ "@pylonsync/sdk",
9
+ "@pylonsync/react",
10
+ "@pylonsync/next",
11
+ "@pylonsync/functions",
12
+ "@pylonsync/sync",
13
+ ],
14
+ async rewrites() {
15
+ return [
16
+ { source: "/api/fn/:path*", destination: `${PYLON_API_URL}/api/fn/:path*` },
17
+ { source: "/api/auth/:path*", destination: `${PYLON_API_URL}/api/auth/:path*` },
18
+ { source: "/api/sync/:path*", destination: `${PYLON_API_URL}/api/sync/:path*` },
19
+ { source: "/api/:path*", destination: `${PYLON_API_URL}/api/:path*` },
20
+ ];
21
+ },
22
+ };
23
+
24
+ export default config;
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@__APP_NAME_KEBAB__/web",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "next dev --port 3000",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "next lint"
11
+ },
12
+ "dependencies": {
13
+ "@__APP_NAME_KEBAB__/ui": "__WORKSPACE_DEP__",
14
+ "@pylonsync/sdk": "^__PYLON_VERSION__",
15
+ "@pylonsync/react": "^__PYLON_VERSION__",
16
+ "@pylonsync/next": "^__PYLON_VERSION__",
17
+ "next": "^16.0.0",
18
+ "react": "^19.0.0",
19
+ "react-dom": "^19.0.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.0.0",
23
+ "@types/react": "^19.0.0",
24
+ "@types/react-dom": "^19.0.0",
25
+ "@tailwindcss/postcss": "^4.0.0",
26
+ "tailwindcss": "^4.0.0",
27
+ "typescript": "^5.5.0"
28
+ }
29
+ }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ plugins: { "@tailwindcss/postcss": {} },
3
+ };