create-nextly-app 0.0.2-alpha.66 → 0.0.2-alpha.68

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.d.cts CHANGED
@@ -48,6 +48,12 @@ interface CreateNextlyOptions {
48
48
  localTemplatePath?: string;
49
49
  /** Git branch for template download from GitHub (defaults to "main") */
50
50
  branch?: string;
51
+ /**
52
+ * Whether to add the Nextly skills for coding agents to the project.
53
+ * False skips them without asking; otherwise the developer is asked, and
54
+ * defaults mode adds them.
55
+ */
56
+ skills?: boolean;
51
57
  }
52
58
 
53
59
  /**
package/dist/index.d.ts CHANGED
@@ -48,6 +48,12 @@ interface CreateNextlyOptions {
48
48
  localTemplatePath?: string;
49
49
  /** Git branch for template download from GitHub (defaults to "main") */
50
50
  branch?: string;
51
+ /**
52
+ * Whether to add the Nextly skills for coding agents to the project.
53
+ * False skips them without asking; otherwise the developer is asked, and
54
+ * defaults mode adds them.
55
+ */
56
+ skills?: boolean;
51
57
  }
52
58
 
53
59
  /**
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from 'module';
2
- export { createNextly } from './chunk-I2U7KBH6.mjs';
2
+ export { createNextly } from './chunk-XCYSH46I.mjs';
3
3
 
4
4
  createRequire(import.meta.url);
5
5
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nextly-app",
3
- "version": "0.0.2-alpha.66",
3
+ "version": "0.0.2-alpha.68",
4
4
  "description": "CLI to scaffold Nextly in your Next.js project",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,32 +20,43 @@
20
20
  "files": [
21
21
  "dist",
22
22
  "bin",
23
+ "skills",
23
24
  "templates"
24
25
  ],
25
26
  "dependencies": {
27
+ "@babel/parser": "^7.29.7",
26
28
  "@clack/prompts": "^1.1.0",
27
29
  "commander": "^14.0.2",
30
+ "dotenv-expand": "10.0.0",
28
31
  "execa": "^9.6.0",
29
32
  "fs-extra": "^11.3.0",
30
33
  "picocolors": "^1.1.1",
31
- "tar": "^7.5.19"
34
+ "picomatch": "^4.0.5",
35
+ "tar": "^7.5.19",
36
+ "yaml": "^2.8.3"
32
37
  },
33
38
  "devDependencies": {
39
+ "@babel/types": "^7.29.7",
40
+ "@nextlyhq/eslint-config": "0.0.2-alpha.68",
41
+ "@nextlyhq/eslint-plugin": "0.0.2-alpha.68",
42
+ "@nextlyhq/telemetry": "0.0.2-alpha.68",
43
+ "@nextlyhq/tsconfig": "0.0.2-alpha.68",
44
+ "@testing-library/react": "^16.3.0",
34
45
  "@types/fs-extra": "^11.0.4",
35
46
  "@types/node": "^20.19.17",
47
+ "@types/picomatch": "^4.0.3",
36
48
  "@types/tar": "^6.1.13",
37
49
  "@typescript-eslint/parser": "^8.59.0",
38
50
  "@vitest/coverage-v8": "^4.1.0",
39
51
  "@vitest/ui": "^4.1.0",
40
52
  "eslint": "^9.39.1",
53
+ "jsdom": "^27.1.0",
54
+ "react": "19.2.0",
55
+ "react-dom": "19.2.0",
41
56
  "tsup": "^8.5.0",
42
57
  "typescript": "^5.9.3",
43
58
  "vite-tsconfig-paths": "^5.1.4",
44
- "vitest": "^4.1.0",
45
- "@nextlyhq/eslint-config": "0.0.2-alpha.66",
46
- "@nextlyhq/eslint-plugin": "0.0.2-alpha.66",
47
- "@nextlyhq/telemetry": "0.0.2-alpha.66",
48
- "@nextlyhq/tsconfig": "0.0.2-alpha.66"
59
+ "vitest": "^4.1.0"
49
60
  },
50
61
  "engines": {
51
62
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: nextly-authentication
3
+ description: >-
4
+ Use when deciding who can sign in to a Nextly project and what they can do:
5
+ the first admin account, roles, access rules on collections, API keys for
6
+ other apps and scripts, checking the signed-in user in your own server
7
+ code, and extra fields on users. Also use when asked to add login or
8
+ sign-up to a Nextly site, so the answer matches what Nextly provides.
9
+ metadata:
10
+ nextly-version: 0.0.2-alpha.67
11
+ ---
12
+
13
+ # Authentication and access in Nextly
14
+
15
+ Checked against `nextly` 0.0.2-alpha.67 with Next.js 16.4. Run
16
+ `npx nextly --version`; if it prints a newer version, prefer
17
+ <https://nextlyhq.com/docs/guides/authentication> where it disagrees with this
18
+ skill.
19
+
20
+ ## What is built in, and what is not
21
+
22
+ Nextly has its own email and password sign-in for the **people who run the
23
+ site**: admins, editors and authors who use the admin panel at `/admin`. It is
24
+ not built on NextAuth or any OAuth library, and it needs no setup beyond
25
+ `NEXTLY_SECRET`.
26
+
27
+ Its session cookies are scoped to `/admin`. So in this release Nextly does
28
+ **not** provide sign-in for the visitors of your public site, nor social or
29
+ OAuth login. If the site needs visitor accounts, use an auth library for the
30
+ site and keep Nextly for content; do not try to reuse the admin sign-in for
31
+ visitors.
32
+
33
+ In particular, do not build a sign-in form for your own pages on the Direct
34
+ API's `nextly.login()`. It is for trusted server code, not for signing people
35
+ in, and this release has no ready path for members-only pages. Protect those
36
+ with the auth library the site uses.
37
+
38
+ ## The secret
39
+
40
+ `NEXTLY_SECRET` signs the session tokens. The installer generates one in
41
+ `.env`. In production it must be at least 32 characters; generate one with:
42
+
43
+ ```bash
44
+ openssl rand -base64 32
45
+ ```
46
+
47
+ Never commit it, and use a different one per environment.
48
+
49
+ ## The first admin
50
+
51
+ Open `/admin/setup` once, on an empty database, and create the first account.
52
+ It gets the **Super Admin** role, which passes every check. After that, setup
53
+ is closed, and people are added in the admin under **Users**.
54
+
55
+ Built-in roles: `super-admin`, `admin`, `editor`, `author`, `viewer`. Roles and
56
+ their permissions are managed in the admin under **Settings > Roles**.
57
+
58
+ ## Access rules on a collection
59
+
60
+ Each operation takes `true`, `false` or a function. A function receives
61
+ `{ user, roles, permissions, operation, collection }`, where `roles` are role
62
+ slugs such as `"editor"` and `user` is `null` for a request with no session.
63
+
64
+ ```ts
65
+ import { defineCollection, text } from "nextly/config";
66
+ import type { AccessControlFunction } from "nextly";
67
+
68
+ const signedIn: AccessControlFunction = ({ user }) => Boolean(user);
69
+ const editors: AccessControlFunction = ({ roles }) =>
70
+ roles.includes("admin") || roles.includes("editor");
71
+
72
+ export const Articles = defineCollection({
73
+ slug: "articles",
74
+ fields: [text({ name: "title", required: true })],
75
+ access: {
76
+ read: () => true,
77
+ create: signedIn,
78
+ update: editors,
79
+ delete: editors,
80
+ },
81
+ });
82
+ ```
83
+
84
+ - A Super Admin passes every rule; test rules with another role.
85
+ - These rules are applied to requests to the admin API and to Direct API
86
+ reads made with `overrideAccess: false`. A plain server-side
87
+ `nextly.find(...)` is a trusted read that skips them.
88
+ - `read: () => true` does not make the admin API public. Every request to
89
+ `/admin/api/...` needs a session or an API key, and is refused with 401
90
+ otherwise. Public pages read content on the server with `getNextly` (the
91
+ `nextly-collections` skill).
92
+
93
+ To apply the rules for a particular user in server code:
94
+
95
+ ```ts
96
+ const result = await nextly.find({
97
+ collection: "articles",
98
+ overrideAccess: false,
99
+ user: { id: userId },
100
+ });
101
+ ```
102
+
103
+ ## API keys, for other apps and scripts
104
+
105
+ Create keys in the admin under **Settings > API Keys**. The full key is shown
106
+ once. Send it as a bearer token:
107
+
108
+ ```bash
109
+ curl -H "Authorization: Bearer $NEXTLY_API_KEY" \
110
+ "https://example.com/admin/api/collections/posts/entries?limit=10"
111
+ ```
112
+
113
+ A **Read-only** key can read and is refused writes (403). **Full access**
114
+ carries its creator's permissions; **Role-based** carries the permissions of a
115
+ role you choose. Keys can expire and be revoked. Keep keys in environment
116
+ variables, never in client code.
117
+
118
+ ## The signed-in user in your own server code
119
+
120
+ ```ts
121
+ // src/app/admin/me/route.ts: the session cookie only reaches /admin paths
122
+ import { getNextly } from "nextly";
123
+ import { getSession, isSuperAdmin } from "nextly/auth";
124
+
125
+ import nextlyConfig from "../../../../nextly.config";
126
+
127
+ export async function GET(request: Request) {
128
+ // Start Nextly first: this route may be the first request a server handles.
129
+ await getNextly({ config: nextlyConfig });
130
+ const session = await getSession(request, process.env.NEXTLY_SECRET ?? "");
131
+ if (!session.authenticated) {
132
+ return Response.json({ error: "Not signed in" }, { status: 401 });
133
+ }
134
+ return Response.json({
135
+ email: session.user.email,
136
+ superAdmin: await isSuperAdmin(session.user.id),
137
+ });
138
+ }
139
+ ```
140
+
141
+ - The route must live under `/admin`, or the browser never sends the session
142
+ cookie to it.
143
+ - `session.user.roleIds` holds role **IDs**, not role slugs, so
144
+ `hasRole(user, "editor")` from `nextly/auth` does not match by slug in this
145
+ release. Use `isSuperAdmin(user.id)`, or put role decisions in collection
146
+ access rules, where `roles` are slugs.
147
+
148
+ ## Extra fields on users
149
+
150
+ ```ts
151
+ import { defineConfig, text, textarea } from "nextly/config";
152
+
153
+ export default defineConfig({
154
+ users: {
155
+ fields: [
156
+ textarea({ name: "bio", maxLength: 500 }),
157
+ text({ name: "avatarUrl" }),
158
+ ],
159
+ },
160
+ // collections, typescript, ...
161
+ });
162
+ ```
163
+
164
+ Read users with `nextly.users.find()`, not `find({ collection: "users" })`.
165
+
166
+ ## Password reset and verification emails
167
+
168
+ These need an email provider. Without one, tokens come back in the API
169
+ response as a development fallback. See
170
+ <https://nextlyhq.com/docs/guides/email>.
171
+
172
+ ## Check it worked
173
+
174
+ - `/admin/setup` creates the first account, and `/admin` signs in.
175
+ - A request to `/admin/api/collections/<slug>/entries` without a session or
176
+ key answers 401.
177
+ - A read-only API key reads entries and gets 403 on a write.
@@ -0,0 +1,215 @@
1
+ ---
2
+ name: nextly-collections
3
+ description: >-
4
+ Use when defining or changing content types in a Nextly project in code:
5
+ writing a collection with defineCollection, choosing field types, adding
6
+ draft and published status, registering it in nextly.config.ts, generating
7
+ TypeScript types, creating a migration, and reading entries from a Next.js
8
+ Server Component with getNextly and find. Also use when a collection does
9
+ not appear in the admin, a field change does not reach the database, or a
10
+ page reading content fails to build.
11
+ metadata:
12
+ nextly-version: 0.0.2-alpha.67
13
+ ---
14
+
15
+ # Define a collection in Nextly
16
+
17
+ A collection is a content type with many entries: posts, products, authors.
18
+ Each one becomes a database table, an admin list and edit screen, and a type
19
+ you can query. Collections can also be made in the admin's visual Schema
20
+ Builder; this skill covers the code-first way.
21
+
22
+ Checked against `nextly` 0.0.2-alpha.67 with Next.js 16.4. Run
23
+ `npx nextly --version`; if it prints a newer version, prefer
24
+ <https://nextlyhq.com/docs> where it disagrees with this skill. Nextly must
25
+ already be installed (the `nextly-install` skill), with `esbuild` among the
26
+ dev dependencies.
27
+
28
+ ## 1. Write the collection
29
+
30
+ `src/collections/posts.ts`:
31
+
32
+ ```ts
33
+ import {
34
+ defineCollection,
35
+ text,
36
+ textarea,
37
+ richText,
38
+ date,
39
+ select,
40
+ } from "nextly/config";
41
+
42
+ export const Posts = defineCollection({
43
+ slug: "posts",
44
+ labels: { singular: "Post", plural: "Posts" },
45
+ fields: [
46
+ text({ name: "title", required: true }),
47
+ text({ name: "slug", required: true, unique: true }),
48
+ textarea({ name: "excerpt" }),
49
+ richText({ name: "content" }),
50
+ date({ name: "publishedAt" }),
51
+ select({
52
+ name: "category",
53
+ options: [
54
+ { label: "News", value: "news" },
55
+ { label: "Guide", value: "guide" },
56
+ ],
57
+ }),
58
+ ],
59
+ // Draft and Published, with Save draft and Publish buttons in the admin.
60
+ status: true,
61
+ admin: { useAsTitle: "title" },
62
+ access: {
63
+ read: () => true,
64
+ create: ({ user }) => Boolean(user),
65
+ update: ({ user }) => Boolean(user),
66
+ delete: ({ roles }) => roles.includes("admin"),
67
+ },
68
+ });
69
+ ```
70
+
71
+ Rules that matter:
72
+
73
+ - Import from `nextly/config` in collection files and in `nextly.config.ts`.
74
+ - Use **relative imports** in these files, never the `@/` alias. The Nextly
75
+ CLI loads them with Node.js, which does not know Next.js aliases.
76
+ - `slug` is the table and API name: lowercase, stable. Renaming it later is a
77
+ migration, not an edit.
78
+ - `id`, `createdAt` and `updatedAt` are added for you; do not declare them.
79
+ Declare `title` and `slug` yourself, as above, or the admin shows no input
80
+ for them and the slug is not unique. The admin fills an empty `slug` from
81
+ the title.
82
+ - Field helpers in this release: `text`, `textarea`, `richText`, `email`,
83
+ `password`, `code`, `number`, `checkbox`, `date`, `select`, `radio`,
84
+ `chips`, `upload`, `relationship`, `repeater`, `array`, `group`, `json`.
85
+ `relationship({ name: "author", relationTo: "users" })` links entries;
86
+ add `hasMany: true` for several. `upload({ name: "image", relationTo: "media" })`
87
+ links a media file.
88
+ - Access rules are explained in the `nextly-authentication` skill.
89
+
90
+ ## 2. Register it
91
+
92
+ `nextly.config.ts`:
93
+
94
+ ```ts
95
+ import { defineConfig } from "nextly/config";
96
+
97
+ import { Posts } from "./src/collections/posts";
98
+
99
+ export default defineConfig({
100
+ collections: [Posts],
101
+ singles: [],
102
+ typescript: {
103
+ outputFile: "./src/types/generated/nextly-types.ts",
104
+ },
105
+ });
106
+ ```
107
+
108
+ ## 3. Apply it in development
109
+
110
+ Restart `next dev` after adding or changing a collection. The change is
111
+ applied to the development database on the first request after the restart:
112
+ the log says `Code-first sync completed`. On Next.js 16.4 the in-process
113
+ reload does not pick up a changed field without a restart.
114
+
115
+ Then open `/admin`: the collection is in the sidebar, with list and create
116
+ screens.
117
+
118
+ ## 4. Generate types
119
+
120
+ ```bash
121
+ npx nextly generate:types
122
+ ```
123
+
124
+ This writes the file named in `typescript.outputFile`, and Zod schemas under
125
+ `src/db/schemas/collections/zod/` (`db/schemas/collections/zod/` without a
126
+ `src/` folder). With them, `find({ collection: "posts" })` is typed; without
127
+ them, entries are loosely typed and fields read as `unknown`. Regenerate in
128
+ the same change as every schema edit.
129
+
130
+ The Zod schemas import `z` from `nextly/zod`, so the project needs no `zod`
131
+ dependency of its own. To combine them with your own Zod code, import `z`
132
+ from `nextly/zod` there too.
133
+
134
+ ## 5. Read entries in a page
135
+
136
+ Content is read on the server with the Direct API. Do not build an API route
137
+ to fetch your own content.
138
+
139
+ ```tsx
140
+ // src/app/posts/page.tsx
141
+ import { getNextly } from "nextly";
142
+ import { cachedFind, nextlyTags } from "nextly/runtime";
143
+
144
+ import nextlyConfig from "../../../nextly.config";
145
+
146
+ async function getPublishedPosts() {
147
+ return cachedFind(
148
+ async () => {
149
+ const nextly = await getNextly({ config: nextlyConfig });
150
+ const { items } = await nextly.find({
151
+ collection: "posts",
152
+ status: "published",
153
+ limit: 10,
154
+ });
155
+ return items;
156
+ },
157
+ { tags: nextlyTags("posts"), keyParts: ["posts", "list"] }
158
+ );
159
+ }
160
+
161
+ export default async function PostsPage() {
162
+ const posts = await getPublishedPosts();
163
+ return (
164
+ <ul>
165
+ {posts.map(post => (
166
+ <li key={String(post.id)}>{String(post.title)}</li>
167
+ ))}
168
+ </ul>
169
+ );
170
+ }
171
+ ```
172
+
173
+ - Without a `src/` folder the page is `app/posts/page.tsx`, importing
174
+ `"../../nextly.config"`.
175
+ - `find()` returns `{ items, meta }`. `meta` holds the totals and pages.
176
+ - `status: "published"` keeps drafts off the page. Without it a trusted
177
+ server read returns drafts too.
178
+ - `cachedFind` with `nextlyTags("posts")` caches the result and clears it when
179
+ a post is saved, so the page updates on publish. It also lets the page build
180
+ in an app with `cacheComponents: true`, where an uncached database read
181
+ outside `<Suspense>` fails `next build`.
182
+ - `keyParts` names the cache entry, and everyone whose read uses the same key
183
+ gets the same rows. Cache only reads that are the same for every reader:
184
+ published content of a collection with no read rule, on the collection or
185
+ on any of its fields. Do not cache a read that applies a reader's access
186
+ rules (`overrideAccess: false` with a `user`): the reader's roles and
187
+ permissions can change without any content changing, so no tag clears the
188
+ entry. Read it uncached inside `<Suspense>` instead.
189
+ - Rich text comes back as Lexical JSON. Pass `richTextFormat: "html"` to
190
+ `find()` to get HTML instead.
191
+ - Users are not a normal collection: read them with `nextly.users.find()`,
192
+ which supports pagination and `search` but not `where`.
193
+
194
+ ## 6. Make a migration before deploying
195
+
196
+ Development applies changes directly; production applies migration files.
197
+ When the change is ready to ship:
198
+
199
+ ```bash
200
+ npx nextly migrate:create add_posts
201
+ ```
202
+
203
+ This compares the config with the last migration's snapshot and writes
204
+ `src/db/migrations/<timestamp>_add_posts.sql` plus a snapshot under
205
+ `src/db/migrations/meta/`. Read the SQL, then commit both with the config
206
+ change. `npx nextly migrate` applies pending migrations; the `nextly-deploy`
207
+ skill runs it on every deploy. Once a migration is shared, never edit it: add
208
+ a new one.
209
+
210
+ ## Check it worked
211
+
212
+ - The admin lists the collection and saves an entry.
213
+ - `npx nextly generate:types` succeeds and `npx tsc --noEmit` is clean.
214
+ - The page shows a published entry, and a draft does not appear.
215
+ - `next build` succeeds.