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

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nextly-app",
3
- "version": "0.0.2-alpha.64",
3
+ "version": "0.0.2-alpha.66",
4
4
  "description": "CLI to scaffold Nextly in your Next.js project",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -42,10 +42,10 @@
42
42
  "typescript": "^5.9.3",
43
43
  "vite-tsconfig-paths": "^5.1.4",
44
44
  "vitest": "^4.1.0",
45
- "@nextlyhq/eslint-config": "0.0.2-alpha.64",
46
- "@nextlyhq/eslint-plugin": "0.0.2-alpha.64",
47
- "@nextlyhq/telemetry": "0.0.2-alpha.64",
48
- "@nextlyhq/tsconfig": "0.0.2-alpha.64"
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"
49
49
  },
50
50
  "engines": {
51
51
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
@@ -84,9 +84,9 @@ const slug = typeof doc.slug === "string" ? doc.slug : undefined;
84
84
 
85
85
  **`keyParts` is a security boundary, not a performance detail.** Two callers
86
86
  whose `keyParts` match share one cache entry. So if the read applies any
87
- per-caller rule — owner-only scoping, role-based visibility, an API key's
88
- narrowed scope — the caller's identity has to be IN the key, or one user's
89
- rows get served to another:
87
+ per-caller rule — a code-defined `access.read` that reads the caller's
88
+ roles or permissions, an API key's narrowed scope — the caller's identity
89
+ has to be IN the key, or one user's rows get served to another:
90
90
 
91
91
  **A shared cache entry is only safe if the read is public for EVERY reader.**
92
92
  Two independent things can make it not so, and each needs its own answer:
@@ -94,16 +94,26 @@ const slug = typeof doc.slug === "string" ? doc.slug : undefined;
94
94
  - **drafts** — `find()` defaults to `overrideAccess: true`, a trusted server
95
95
  read, so a slug filter alone can return an unpublished row. `status:
96
96
  "published"` is enforced even on a trusted read and fixes this one.
97
- - **per-row access rules** — owner-only scoping, role-based visibility, an API
98
- key's narrowed scope. `status` does NOT evaluate these; it is a
99
- draft/published lifecycle filter and nothing more. A published row that only
100
- its owner may read is still returned by the query above.
101
-
102
- So the decision is: **does this collection carry read-access rules?**
97
+ - **access rules** — a code-defined `access.read` on the collection that
98
+ decides on the caller (their roles, their permissions, whether they are
99
+ signed in at all), or an API key's narrowed scope. `status` does NOT
100
+ evaluate these; it is a draft/published lifecycle filter and nothing more.
101
+ A published row that only some readers may see is still returned by the
102
+ query above.
103
+
104
+ - **field-level read rules** — a field can carry its own `access.read` even
105
+ when the collection declares none. The shared branch below reads with the
106
+ Direct API's default `overrideAccess: true`, which bypasses field rules
107
+ too, so the cached row holds every field and the entry serves fields the
108
+ schema meant to redact. A collection with ANY field-level read rule takes
109
+ the per-caller branch — or selects only fields known to be public.
110
+
111
+ So the decision is: **does this collection declare a read rule, on the
112
+ collection or on any of its fields?**
103
113
 
104
114
  ```ts
105
- // NO access rules — the result is identical for every reader, so one shared
106
- // entry is correct and gets the full ISR benefit.
115
+ // NO read rule, on the collection OR its fields — the result is identical for
116
+ // every reader, so one shared entry is correct and gets the full ISR benefit.
107
117
  // `find()` always returns the list envelope `{ items, meta }`, so a detail route takes the
108
118
  // first item — and caches THAT, not the envelope around it.
109
119
  cachedFind(
@@ -119,8 +129,8 @@ const slug = typeof doc.slug === "string" ? doc.slug : undefined;
119
129
  { tags: nextlyTags("posts"), keyParts: ["posts", slug] }
120
130
  );
121
131
 
122
- // ACCESS RULES — evaluate them as the caller, and key by EVERY dimension the
123
- // rules read, not just who the caller is.
132
+ // A READ RULE — evaluate it as the caller, and key by EVERY dimension the
133
+ // rule reads, not just who the caller is.
124
134
  //
125
135
  // Both role fields are optional and Nextly reads BOTH: the many-to-many
126
136
  // `roles` unioned with the singular `role`. Deduped and sorted, so one role
@@ -144,8 +154,35 @@ const slug = typeof doc.slug === "string" ? doc.slug : undefined;
144
154
  on CONTENT changes, and a permission change is not one. Include whatever the
145
155
  rules actually read: the role set, the claim, the key's scope.
146
156
 
147
- If you are unsure whether a collection has read rules, do not cache it. A
148
- missing cache is slow; a shared one leaks.
157
+ If you are unsure whether a collection or any of its fields declares a read
158
+ rule, do not cache it. A missing cache is slow; a shared one leaks.
159
+
160
+ ## Which Nextly this is, and where its docs are
161
+
162
+ An agent working here will otherwise answer from whatever it remembers of some
163
+ other version. Ask the project instead:
164
+
165
+ ```bash
166
+ {{execCommand}} nextly --version # the running core version
167
+ ```
168
+
169
+ That is the same version the plugin resolver validates a plugin's `nextly`
170
+ range against, so a compatibility answer and a reported version cannot
171
+ disagree. The dependency range this project pins is in `package.json` under
172
+ `nextly`; the resolved version is what the command above prints.
173
+
174
+ **The authoritative documentation is <https://nextlyhq.com/docs>.** Two files
175
+ are published for machine readers and are the ones to fetch:
176
+
177
+ | file | what it is |
178
+ | ---- | ---------- |
179
+ | <https://nextlyhq.com/llms.txt> | the index — every documentation page, with a one-line summary |
180
+ | <https://nextlyhq.com/llms-full.txt> | the same documentation as one file |
181
+
182
+ Both are generated from the pages the site publishes, so they cannot describe a
183
+ page that does not exist. Neither is version-stamped: they describe the CURRENT
184
+ release, so check the version above before trusting a detail against an older
185
+ pin.
149
186
 
150
187
  ## Conventions worth keeping
151
188
 
@@ -84,6 +84,33 @@ those slugs a second time as code-owned entities and boot fails with
84
84
  Assert on behaviour through `t.nextly` rather than on the plugin object: what a
85
85
  host observes is the contract, and the object shape is not.
86
86
 
87
+ ## Which Nextly this is, and where its docs are
88
+
89
+ An agent working here will otherwise answer from whatever it remembers of some
90
+ other version. Ask the project instead:
91
+
92
+ ```bash
93
+ {{execCommand}} nextly --version # the running core version
94
+ ```
95
+
96
+ That is the same version the plugin resolver validates a plugin's `nextly`
97
+ range against, so a compatibility answer and a reported version cannot
98
+ disagree. The dependency range this project pins is in `package.json` under
99
+ `nextly`; the resolved version is what the command above prints.
100
+
101
+ **The authoritative documentation is <https://nextlyhq.com/docs>.** Two files
102
+ are published for machine readers and are the ones to fetch:
103
+
104
+ | file | what it is |
105
+ | ---- | ---------- |
106
+ | <https://nextlyhq.com/llms.txt> | the index — every documentation page, with a one-line summary |
107
+ | <https://nextlyhq.com/llms-full.txt> | the same documentation as one file |
108
+
109
+ Both are generated from the pages the site publishes, so they cannot describe a
110
+ page that does not exist. Neither is version-stamped: they describe the CURRENT
111
+ release, so check the version above before trusting a detail against an older
112
+ pin.
113
+
87
114
  ## Conventions worth keeping
88
115
 
89
116
  - Version `nextly` compatibility in the factory's `nextly` range, and widen it
@@ -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
  });