@reventlessdev/reventless-core 3.0.0-alpha.247 โ 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 +21 -0
- package/package.json +4 -4
- package/src/adapter/Auth/Auth_ActiveRole.res +15 -0
- package/src/components/Api/GraphQL_FragmentGenerator.res +6 -1
- package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +2 -1
- package/tests/api/GraphQL_FragmentGeneratorTest.res +36 -0
- package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +34 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,27 @@
|
|
|
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
|
+
|
|
13
|
+
# 3.0.0-alpha.248 (2026-08-23)
|
|
14
|
+
|
|
15
|
+
* feat(aws)!: scope the active-role store to the identity provider ([08e287d](https://github.com/ReventlessDev/reventless-core/commit/08e287db9721977b818bad76671ec4c62be6f9af))
|
|
16
|
+
|
|
17
|
+
### BREAKING CHANGES
|
|
18
|
+
|
|
19
|
+
* platform:cognitoUserPoolId is now
|
|
20
|
+
platform:identityProviderId (REVENTLESS_IDENTITY_PROVIDER_ID). The old
|
|
21
|
+
spelling is still read and warns โ do not drop it until the new secret
|
|
22
|
+
is in place, because an unset provider id is auto mode, and auto mode
|
|
23
|
+
creates a new user pool.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
6
27
|
# 3.0.0-alpha.247 (2026-08-22)
|
|
7
28
|
|
|
8
29
|
* feat(core)!: a slice that has given up says so ([f5559ab](https://github.com/ReventlessDev/reventless-core/commit/f5559abede3a9cf2c9a48aadc69a666c46cc75bf))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.249",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
37
37
|
"@reventlessdev/rescript-ssh2": "2.0.0-alpha.8",
|
|
38
38
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
39
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.150",
|
|
40
39
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.33",
|
|
41
|
-
"@reventlessdev/reventless-
|
|
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.
|
|
46
|
+
"@reventlessdev/reventless-ppx": "1.0.0-alpha.73"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"rescript": "12.3.0"
|
|
@@ -43,6 +43,21 @@ they picked stopped taking effect.
|
|
|
43
43
|
*/
|
|
44
44
|
let staleRoleClaim = "activeRoleStale"
|
|
45
45
|
|
|
46
|
+
// ๐จ **State that narrows an identity is scoped to the identity provider, not to
|
|
47
|
+
// the platform.**
|
|
48
|
+
//
|
|
49
|
+
// A provider has one minting point, and narrowing works by driving it. Keep the
|
|
50
|
+
// state a platform-scoped resource and two deployments sharing one provider each
|
|
51
|
+
// drive their own copy โ whichever holds the minting point reads state the other
|
|
52
|
+
// never writes, so a role switch reports success and changes nothing. Each half
|
|
53
|
+
// is individually correct, so nothing short of two deployments can observe it.
|
|
54
|
+
//
|
|
55
|
+
// On AWS that is a pool's single `PreTokenGeneration` slot and the
|
|
56
|
+
// `platform:activeRoleStore` that follows it; elsewhere the shape differs and the
|
|
57
|
+
// rule does not. It cannot be a `conformanceCases` entry โ those are
|
|
58
|
+
// `(membership, requested) โ expected`, and this is about *where two deployments
|
|
59
|
+
// keep state*. See [docs/plans/active-role-store-scoped-to-the-pool.md].
|
|
60
|
+
|
|
46
61
|
/**
|
|
47
62
|
The conformance table ยง6 of the plan requires: the same (membership, requested,
|
|
48
63
|
expected) cases run against both minting paths, so the two implementations cannot
|
|
@@ -903,7 +903,12 @@ let generate = (
|
|
|
903
903
|
switch entry.indexQueries {
|
|
904
904
|
| Some(indexes) =>
|
|
905
905
|
let connectionTypeName = entry.returnTypeName ++ "Connection"
|
|
906
|
-
indexes
|
|
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", () => {
|