@reventlessdev/reventless-core 3.0.0-alpha.248 → 3.0.0-alpha.249

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,13 @@
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.249 (2026-08-23)
7
+
8
+ ### Features
9
+
10
+ * **querydb:** serve owner-scoped lists from an index instead of a filtered Scan ([ad26c28](https://github.com/ReventlessDev/reventless-core/commit/ad26c284b4b0650fa3f3b6109ed33bbfe8608f6d))
11
+
12
+
6
13
  # 3.0.0-alpha.248 (2026-08-23)
7
14
 
8
15
  * feat(aws)!: scope the active-role store to the identity provider ([08e287d](https://github.com/ReventlessDev/reventless-core/commit/08e287db9721977b818bad76671ec4c62be6f9af))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.248",
3
+ "version": "3.0.0-alpha.249",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -29,21 +29,21 @@
29
29
  "sury": "11.0.0-rc.2",
30
30
  "uuid": "^13.0.0",
31
31
  "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
32
- "@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
33
32
  "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.8",
33
+ "@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
34
34
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
35
35
  "@reventlessdev/rescript-node": "2.0.0-alpha.8",
36
36
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
37
- "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
38
37
  "@reventlessdev/rescript-ssh2": "2.0.0-alpha.8",
39
- "@reventlessdev/reventless-infra": "3.0.0-alpha.150",
38
+ "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
40
39
  "@reventlessdev/reventless-interop": "3.0.0-alpha.33",
41
- "@reventlessdev/reventless-spec": "3.0.0-alpha.122"
40
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.151",
41
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.123"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "12.3.0",
45
45
  "sury-ppx": "11.0.0-rc.2",
46
- "@reventlessdev/reventless-ppx": "1.0.0-alpha.72"
46
+ "@reventlessdev/reventless-ppx": "1.0.0-alpha.73"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "rescript": "12.3.0"
@@ -903,7 +903,12 @@ let generate = (
903
903
  switch entry.indexQueries {
904
904
  | Some(indexes) =>
905
905
  let connectionTypeName = entry.returnTypeName ++ "Connection"
906
- indexes->Array.forEach(indexConfig =>
906
+ // Derived indexes are skipped: they key a read the list resolver already
907
+ // makes on the caller's behalf, and a `<single>By<OwnerField>` door any
908
+ // caller may name is not the read they serve.
909
+ indexes
910
+ ->Array.filter(ic => !Reventless.ReadModel.isDerivedIndex(ic))
911
+ ->Array.forEach(indexConfig =>
907
912
  queries->Array.push(
908
913
  deriveIndexQueryField(
909
914
  ~singleFieldName=entry.singleFieldName,
@@ -5,6 +5,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
5
  import * as Stdlib_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
6
6
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
7
7
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
8
+ import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
8
9
  import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
9
10
  import * as Api_Naming$ReventlessCore from "./Api_Naming.res.mjs";
10
11
  import * as SchemaType$ReventlessCore from "./SchemaType.res.mjs";
@@ -580,7 +581,7 @@ function generate(mutationEntries, queryEntries) {
580
581
  let indexes = entry.indexQueries;
581
582
  if (indexes !== undefined) {
582
583
  let connectionTypeName = entry.returnTypeName + "Connection";
583
- indexes.forEach(indexConfig => {
584
+ indexes.filter(ic => !ReadModel$Reventless.isDerivedIndex(ic)).forEach(indexConfig => {
584
585
  queries.push(deriveIndexQueryField(entry.singleFieldName, indexConfig, connectionTypeName));
585
586
  });
586
587
  }
@@ -509,6 +509,42 @@ describe("the by-index door", () => {
509
509
  GraphQL_FragmentGenerator.indexQueryFieldName(~singleFieldName="X", ~index="by"),
510
510
  ))->toEqual(("XByOwner", "XByOwnerId", "XByBy"))
511
511
  )
512
+
513
+ // The regression that would otherwise land in silence: `@owner` provisions an
514
+ // index on every view that declares one, and if that index emitted a door, a
515
+ // `<single>By<OwnerField>` field any caller may name would appear in every
516
+ // published SDL — a schema change nobody asked for, on views that only wanted
517
+ // their own list read to stop scanning.
518
+ testSync("a derived index adds no query field", () => {
519
+ let entryWith = (indexes): ReventlessInfra.Api.querySchemaEntry => {
520
+ singleFieldName: "Ordering_Order",
521
+ listFieldName: "Ordering_Orders",
522
+ returnTypeName: "Ordering_Order",
523
+ stateSchema: S.unknown,
524
+ authorization: None,
525
+ indexQueries: indexes,
526
+ }
527
+ let derived: Reventless.ReadModel.indexConfig = {
528
+ index: "_owner",
529
+ type_: "S",
530
+ idField: "customerId",
531
+ subIdField: "id",
532
+ projectionType: ALL,
533
+ derived: true,
534
+ }
535
+ let authored: Reventless.ReadModel.indexConfig = {
536
+ index: "byCategory",
537
+ type_: "S",
538
+ idField: "categoryId",
539
+ projectionType: ALL,
540
+ }
541
+ let doorsOf = indexes =>
542
+ GraphQL_Stitcher.decode(
543
+ GraphQL_FragmentGenerator.generate(~mutationEntries=[], ~queryEntries=[entryWith(indexes)]),
544
+ ).queries->Array.filter(q => q->String.includes("Ordering_OrderBy"))
545
+ expect((doorsOf([derived])->Array.length, doorsOf([authored, derived])->Array.length))
546
+ ->toEqual((0, 1))
547
+ })
512
548
  })
513
549
 
514
550
  // ── @internal: a field that is not on the GraphQL surface ────────────────────
@@ -427,6 +427,40 @@ globalThis.describe("the by-index door", () => {
427
427
  "XByBy"
428
428
  ]);
429
429
  });
430
+ globalThis.test("a derived index adds no query field", () => {
431
+ let entryWith = indexes => ({
432
+ singleFieldName: "Ordering_Order",
433
+ listFieldName: "Ordering_Orders",
434
+ returnTypeName: "Ordering_Order",
435
+ stateSchema: Sury.unknown,
436
+ authorization: undefined,
437
+ indexQueries: indexes
438
+ });
439
+ let derived = {
440
+ index: "_owner",
441
+ type_: "S",
442
+ idField: "customerId",
443
+ subIdField: "id",
444
+ projectionType: "ALL",
445
+ derived: true
446
+ };
447
+ let doorsOf = indexes => GraphQL_Stitcher$ReventlessCore.decode(GraphQL_FragmentGenerator$ReventlessCore.generate([], [entryWith(indexes)])).queries.filter(q => q.includes("Ordering_OrderBy"));
448
+ globalThis.expect([
449
+ doorsOf([derived]).length,
450
+ doorsOf([
451
+ {
452
+ index: "byCategory",
453
+ type_: "S",
454
+ idField: "categoryId",
455
+ projectionType: "ALL"
456
+ },
457
+ derived
458
+ ]).length
459
+ ]).toEqual([
460
+ 0,
461
+ 1
462
+ ]);
463
+ });
430
464
  });
431
465
 
432
466
  globalThis.describe("@internal keeps a field off the generated type", () => {