@reventlessdev/reventless-spec 3.0.0-alpha.107 → 3.0.0-alpha.108

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/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.108 (2026-08-11)
7
+
8
+ ### Features
9
+
10
+ * **api:** infer a queryable's key field and publish its provenance ([c835a42](https://github.com/ReventlessDev/reventless-core/commit/c835a42a0da07cdc4a3f010212e1f340a4a0ca27))
11
+ * **plugin:** publish singleQueryField on queryableDef ([a724ab5](https://github.com/ReventlessDev/reventless-core/commit/a724ab573614792c0615d68b6486b94da14f9f82))
12
+
13
+
6
14
  # 3.0.0-alpha.107 (2026-08-10)
7
15
 
8
16
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-spec",
3
- "version": "3.0.0-alpha.107",
3
+ "version": "3.0.0-alpha.108",
4
4
  "description": "Specifications for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  "sury": "11.0.0-alpha.4",
22
22
  "sury-ppx": "11.0.0-alpha.2",
23
23
  "yaml": "^2.8.3",
24
- "@reventlessdev/rescript-node": "2.0.0-alpha.3"
24
+ "@reventlessdev/rescript-node": "2.0.0-alpha.4"
25
25
  },
26
26
  "devDependencies": {
27
27
  "rescript": "12.3.0",
@@ -269,6 +269,51 @@ type queryableDef = {
269
269
  before this field existed must be reset/re-emitted. See [[deployed-chapter-grouping]].
270
270
  */
271
271
  chapter: @s.matches(stringOptionSchema) option<string>,
272
+ /**
273
+ The singular counterpart of `queryField`: the generated single-entity query
274
+ (`Plugin_Order(id: ID!)` beside the list field `Plugin_Orders`), and — because
275
+ `Api_Naming` returns the same string for both — the prefix of the queryable's
276
+ generated input types (`Plugin_OrderFilter`, `Plugin_OrderOrderBy`). One field
277
+ rather than two, so the two uses cannot drift apart.
278
+
279
+ Published because it is not derivable from `queryField` without re-implementing
280
+ `Api_Naming.singularize`: a consumer that strips a trailing `s` turns
281
+ `Plugin_Categories` into `Plugin_Categorie`, a name the schema does not serve,
282
+ and fails at query time against that one view. Sourced from the naming module
283
+ itself, never re-derived.
284
+
285
+ `None` means not stated — defs persisted before this field existed, and
286
+ hand-rolled defs that decline to say; a consumer falls back to its own
287
+ derivation there. js_nullable for the same JSON-safety reason as `statusField`.
288
+ */
289
+ singleQueryField: @s.matches(stringOptionSchema) option<string>,
290
+ /**
291
+ The state field that identifies a row — the queryable's own key, as opposed to
292
+ a reference to some other entity. `Products` carries `productId` and
293
+ `categoryId`; this says which of the two the row is about.
294
+
295
+ `None` means unresolved: a state with several `*Id` fields and no name match,
296
+ or with none at all. Such a component gets no key-derived filter or sort until
297
+ its spec declares `@id`. Also `None` on defs persisted before this field
298
+ existed. js_nullable for the same JSON-safety reason as `statusField`.
299
+ */
300
+ idField: @s.matches(stringOptionSchema) option<string>,
301
+ /**
302
+ Which rung produced `idField`, so a consumer can tell a declaration from a
303
+ guess — the same reason `labelFieldSource` exists:
304
+
305
+ - `"annotation"` — the state declares `@id`. The author said which field keys
306
+ the row; nothing inferred outranks it.
307
+ - `"convention"` — a field named `<singular component name>Id` exists
308
+ (`Products` → `productId`). A guess, and the one guess a client can make for
309
+ itself.
310
+ - `"sole"` — the state has exactly one `*Id` field, so there is nothing else
311
+ the key could be (`AvailableProducts` → `productId`). A guess, and one that
312
+ needs the state's full field list to make.
313
+
314
+ `None` whenever `idField` is `None`, and on defs that predate the field.
315
+ */
316
+ idFieldSource: @s.matches(stringOptionSchema) option<string>,
272
317
  }
273
318
 
274
319
  /**
@@ -118,7 +118,10 @@ let queryableDefSchema = S.schema(s => ({
118
118
  labelFieldSource: s.m(stringOptionSchema),
119
119
  statusField: s.m(stringOptionSchema),
120
120
  visibility: s.m(stringOptionSchema),
121
- chapter: s.m(stringOptionSchema)
121
+ chapter: s.m(stringOptionSchema),
122
+ singleQueryField: s.m(stringOptionSchema),
123
+ idField: s.m(stringOptionSchema),
124
+ idFieldSource: s.m(stringOptionSchema)
122
125
  }));
123
126
 
124
127
  let eventDefSchema = S.schema(s => ({