create-nextly-app 0.0.2-alpha.65 → 0.0.2-alpha.67

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 (33) hide show
  1. package/README.md +29 -8
  2. package/dist/{chunk-I2U7KBH6.mjs → chunk-XCYSH46I.mjs} +36805 -10917
  3. package/dist/chunk-XCYSH46I.mjs.map +1 -0
  4. package/dist/cli.cjs +27693 -1892
  5. package/dist/cli.cjs.map +1 -1
  6. package/dist/cli.mjs +17 -106
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/index.cjs +36591 -10708
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +6 -0
  11. package/dist/index.d.ts +6 -0
  12. package/dist/index.mjs +1 -1
  13. package/package.json +18 -7
  14. package/skills/nextly-authentication/SKILL.md +177 -0
  15. package/skills/nextly-collections/SKILL.md +215 -0
  16. package/skills/nextly-deploy/SKILL.md +246 -0
  17. package/skills/nextly-install/SKILL.md +122 -0
  18. package/templates/base/.env.example +3 -5
  19. package/templates/base/AGENTS.md.template +83 -15
  20. package/templates/base/src/types/generated/nextly-types.ts +6 -4
  21. package/templates/blank/src/app/(frontend)/components/ThemeToggle.tsx +55 -24
  22. package/templates/blank/src/instrumentation.ts +27 -0
  23. package/templates/plugin/AGENTS.md.template +33 -4
  24. package/templates/plugin/README.md +4 -0
  25. package/templates/plugin/dev/.env.example +5 -2
  26. package/templates/plugin/dev/src/app/admin/[[...params]]/page.tsx +14 -2
  27. package/templates/plugin/dev/src/instrumentation.ts +20 -0
  28. package/templates/plugin/dev/src/seed-dev-user.ts +51 -0
  29. package/templates/plugin/gitignore +5 -2
  30. package/templates/plugin/src/plugin.test.ts +9 -2
  31. package/templates/plugin/vitest.config.ts +19 -0
  32. package/dist/chunk-I2U7KBH6.mjs.map +0 -1
  33. package/templates/plugin/dev/instrumentation.ts +0 -10
@@ -40,10 +40,10 @@ working on an app.
40
40
 
41
41
  - **Plugin APIs come from `@nextlyhq/plugin-sdk`; schema APIs come from
42
42
  `nextly/config`.** The SDK is the stability boundary for everything a plugin
43
- does AS a plugin: `definePlugin`, `registerComponents` and
44
- `registerKnownPlugin` from `@nextlyhq/plugin-sdk/admin`, `createTestNextly`
45
- from `@nextlyhq/plugin-sdk/testing`, field factories, `pluginField`,
46
- `NextlyError`. Collections and singles are defined the same way an app defines
43
+ does AS a plugin: `definePlugin`, field factories, `pluginField` and
44
+ `NextlyError` from `@nextlyhq/plugin-sdk`; `registerComponents` and
45
+ `registerKnownPlugin` from `@nextlyhq/plugin-sdk/admin`; `createTestNextly`
46
+ from `@nextlyhq/plugin-sdk/testing`. Collections and singles are defined the same way an app defines
47
47
  them, with `defineCollection` from `nextly/config` — which is what
48
48
  `src/collections/example.ts` does.
49
49
 
@@ -73,6 +73,8 @@ in-memory SQLite with your plugin registered, so a test exercises the same
73
73
  lifecycle a host would run:
74
74
 
75
75
  ```ts
76
+ import { createTestNextly } from "@nextlyhq/plugin-sdk/testing";
77
+
76
78
  const t = await createTestNextly({ plugins: [myPlugin()] });
77
79
  ```
78
80
 
@@ -84,6 +86,33 @@ those slugs a second time as code-owned entities and boot fails with
84
86
  Assert on behaviour through `t.nextly` rather than on the plugin object: what a
85
87
  host observes is the contract, and the object shape is not.
86
88
 
89
+ ## Which Nextly this is, and where its docs are
90
+
91
+ An agent working here will otherwise answer from whatever it remembers of some
92
+ other version. Ask the project instead:
93
+
94
+ ```bash
95
+ {{execCommand}} nextly --version # the running core version
96
+ ```
97
+
98
+ That is the same version the plugin resolver validates a plugin's `nextly`
99
+ range against, so a compatibility answer and a reported version cannot
100
+ disagree. The dependency range this project pins is in `package.json` under
101
+ `nextly`; the resolved version is what the command above prints.
102
+
103
+ **The authoritative documentation is <https://nextlyhq.com/docs>.** Two files
104
+ are published for machine readers and are the ones to fetch:
105
+
106
+ | file | what it is |
107
+ | ---- | ---------- |
108
+ | <https://nextlyhq.com/llms.txt> | the index — every documentation page, with a one-line summary |
109
+ | <https://nextlyhq.com/llms-full.txt> | the same documentation as one file |
110
+
111
+ Both are generated from the pages the site publishes, so they cannot describe a
112
+ page that does not exist. Neither is version-stamped: they describe the CURRENT
113
+ release, so check the version above before trusting a detail against an older
114
+ pin.
115
+
87
116
  ## Conventions worth keeping
88
117
 
89
118
  - Version `nextly` compatibility in the factory's `nextly` range, and widen it
@@ -13,6 +13,10 @@ Your plugin lives in `src/`. The `dev/` folder is a minimal Nextly app on SQLite
13
13
  that registers this plugin so you can exercise it in a real admin with hot-reload.
14
14
  Editing files under `src/` reloads the playground. **`dev/` is never published.**
15
15
 
16
+ The playground signs you in automatically as `dev@nextly.local` (password
17
+ `DevPassword123!`). That user is created on the first `pnpm dev`, and only
18
+ under `next dev`; production builds ignore the auto-login setting.
19
+
16
20
  ## Test
17
21
 
18
22
  ```bash
@@ -1,6 +1,9 @@
1
- # Dev playground env. Copy to dev/.env and run `pnpm dev`.
1
+ # Dev playground env. create-nextly-app writes dev/.env from this file;
2
+ # copy it again to start over.
2
3
  DB_DIALECT=sqlite
3
- DATABASE_URL=file:./nextly-dev.db
4
+ # Relative to the plugin root, where `pnpm dev` runs (`next dev dev` keeps
5
+ # that working directory), so the database lands in dev/.
6
+ DATABASE_URL=file:./dev/nextly-dev.db
4
7
 
5
8
  # Any non-empty secret works for local dev (sign/verify session cookies).
6
9
  NEXTLY_SECRET=dev-secret-change-me
@@ -1,9 +1,21 @@
1
1
  "use client";
2
2
 
3
3
  import "@nextlyhq/admin/style.css";
4
- import { RootLayout } from "@nextlyhq/admin";
4
+ import { ErrorBoundary, QueryProvider, RootLayout } from "@nextlyhq/admin";
5
5
 
6
6
  // Mounts the Nextly admin shell. Your plugin's menu/pages/settings appear here.
7
+ // The admin's data hooks read their client from QueryProvider, so RootLayout
8
+ // renders only inside it, as in an app scaffold.
7
9
  export default function AdminPage() {
8
- return <RootLayout />;
10
+ return (
11
+ <ErrorBoundary
12
+ onError={(error, errorInfo) => {
13
+ console.error("Admin error:", error, errorInfo);
14
+ }}
15
+ >
16
+ <QueryProvider>
17
+ <RootLayout />
18
+ </QueryProvider>
19
+ </ErrorBoundary>
20
+ );
9
21
  }
@@ -0,0 +1,20 @@
1
+ // Warm up Nextly once per worker at startup (Node.js runtime only) so admin/API
2
+ // handlers never run before init and schema boot-apply runs once, not per request.
3
+ // Imported dynamically + guarded so nextly stays out of the Edge bundle. It sits
4
+ // in src/ beside app/, where Next.js looks for it when an app lives under src/.
5
+ export async function register(): Promise<void> {
6
+ if (process.env.NEXT_RUNTIME !== "nodejs") return;
7
+
8
+ const { createRegister, getService } = await import("nextly");
9
+ const { default: config } = await import("../nextly.config");
10
+ await createRegister(config)();
11
+
12
+ // Create the user admin.devAutoLogin signs in as (see seed-dev-user.ts).
13
+ // After boot, because boot seeds every permission and the Super Admin role
14
+ // created here takes all that exist.
15
+ const { seedSuperAdmin } = await import("nextly/database/seeders");
16
+ const { seedDevUser } = await import("./seed-dev-user");
17
+ await seedDevUser(config.admin?.devAutoLogin, options =>
18
+ seedSuperAdmin(getService("adapter"), options)
19
+ );
20
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Create the account `admin.devAutoLogin` signs in as.
3
+ *
4
+ * Auto-login signs in a user that already exists and never creates one, so
5
+ * without this the first /admin visit lands on the setup wizard. The seeder is
6
+ * passed in by instrumentation.ts, which keeps this file free of imports and
7
+ * lets it run on its own.
8
+ */
9
+
10
+ /** The account `admin.devAutoLogin` names, as nextly.config.ts declares it. */
11
+ export type DevAutoLogin = false | { email: string; password?: string };
12
+
13
+ /** Creates a super-admin, skipping one that already exists. */
14
+ export type SeedSuperAdmin = (options: {
15
+ email: string;
16
+ password: string;
17
+ name: string;
18
+ silent: boolean;
19
+ }) => Promise<{ success: boolean; errorMessages?: string[] }>;
20
+
21
+ export async function seedDevUser(
22
+ login: DevAutoLogin | undefined,
23
+ seed: SeedSuperAdmin
24
+ ): Promise<void> {
25
+ // `next dev` only: no credentials in a production build, and tests bring
26
+ // their own fixtures.
27
+ if (process.env.NODE_ENV !== "development" || !login) return;
28
+
29
+ // Auto-login needs no password, but creating the account does.
30
+ if (!login.password) {
31
+ console.warn(
32
+ `[dev] admin.devAutoLogin names ${login.email} without a password, so that user is not created.`
33
+ );
34
+ return;
35
+ }
36
+
37
+ const result = await seed({
38
+ email: login.email,
39
+ password: login.password,
40
+ name: "Dev User",
41
+ silent: true,
42
+ });
43
+ if (!result.success) {
44
+ // Otherwise the only sign is the setup wizard, with nothing saying why.
45
+ console.warn(
46
+ `[dev] Could not create the auto-login user ${login.email}: ${
47
+ result.errorMessages?.join("; ") ?? "unknown error"
48
+ }`
49
+ );
50
+ }
51
+ }
@@ -4,7 +4,10 @@ dist
4
4
 
5
5
  # Embedded dev playground — never published, never committed build output
6
6
  dev/.next
7
- dev/*.db
8
- dev/*.db-journal
7
+ # SQLite files, and the journal and WAL files beside them, wherever they land.
8
+ *.db
9
+ *.db-journal
10
+ *.db-wal
11
+ *.db-shm
9
12
  .env
10
13
  .env.local
@@ -20,9 +20,16 @@ afterEach(async () => {
20
20
  it("boots and exposes the plugin's example collection (real table)", async () => {
21
21
  // Creating an entry proves the contributed collection got a real table and
22
22
  // the plugin's lifecycle ran. Replace with assertions for your own behavior.
23
- const created = await t.nextly.create("examples", { title: "First example" });
23
+ // The Direct API takes one argument object per call.
24
+ const created = await t.nextly.create({
25
+ collection: "examples",
26
+ data: { title: "First example" },
27
+ });
24
28
  expect(created.item.id).toBeDefined();
25
29
 
26
- const fetched = await t.nextly.findById("examples", created.item.id);
30
+ const fetched = await t.nextly.findByID({
31
+ collection: "examples",
32
+ id: String(created.item.id),
33
+ });
27
34
  expect(fetched?.title).toBe("First example");
28
35
  });
@@ -4,5 +4,24 @@ export default defineConfig({
4
4
  test: {
5
5
  environment: "node",
6
6
  include: ["src/**/*.test.ts"],
7
+ /*
8
+ * Sized for an instance boot, because that is what this suite does.
9
+ *
10
+ * `plugin.test.ts` calls `createTestNextly` in `beforeEach`, which builds a
11
+ * DI container, registers the plugin's schema and runs auto-sync against a
12
+ * real SQLite database. Vitest's defaults are sized for a unit test that
13
+ * touches none of that: 5s for a case and 10s for a hook. A boot is about a
14
+ * second and a half on a warm machine, and the first run of a freshly
15
+ * scaffolded project is the least warm moment there is - a cold install, no
16
+ * build cache, whatever the laptop or CI container is doing.
17
+ *
18
+ * This is the first command a new plugin author runs, so a timeout here
19
+ * reads as "the scaffold ships a broken test" rather than "the budget was
20
+ * tight". The monorepo solves the same problem by routing boots to a lane
21
+ * with a 30s budget; a single-package project has one lane and nothing to
22
+ * contend with, so it states the budget instead.
23
+ */
24
+ testTimeout: 30_000,
25
+ hookTimeout: 30_000,
7
26
  },
8
27
  });