@reventlessdev/reventless-spec 3.0.0-alpha.100

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 (139) hide show
  1. package/CHANGELOG.md +931 -0
  2. package/LICENSE +202 -0
  3. package/README.md +109 -0
  4. package/package.json +49 -0
  5. package/rescript.json +32 -0
  6. package/run-generator.mjs +2 -0
  7. package/run-platform-generator.mjs +2 -0
  8. package/scripts/generate-currency.mjs +215 -0
  9. package/scripts/iso-4217-list-one.xml +1956 -0
  10. package/src/AnsiStyle.res +40 -0
  11. package/src/AnsiStyle.res.mjs +54 -0
  12. package/src/LogPrefix.res +192 -0
  13. package/src/LogPrefix.res.mjs +159 -0
  14. package/src/PackageVersion.res +67 -0
  15. package/src/PackageVersion.res.mjs +81 -0
  16. package/src/components/Aggregate.res +64 -0
  17. package/src/components/Aggregate.res.mjs +2 -0
  18. package/src/components/AutomationSlice.res +279 -0
  19. package/src/components/AutomationSlice.res.mjs +30 -0
  20. package/src/components/CapabilityManifest.res +74 -0
  21. package/src/components/CapabilityManifest.res.mjs +61 -0
  22. package/src/components/ComponentKind.res +99 -0
  23. package/src/components/ComponentKind.res.mjs +125 -0
  24. package/src/components/Counter.res +24 -0
  25. package/src/components/Counter.res.mjs +2 -0
  26. package/src/components/DcbDecode.res +118 -0
  27. package/src/components/DcbDecode.res.mjs +100 -0
  28. package/src/components/DcbScopeInference.res +244 -0
  29. package/src/components/DcbScopeInference.res.mjs +177 -0
  30. package/src/components/DcbTag.res +1335 -0
  31. package/src/components/DcbTag.res.mjs +898 -0
  32. package/src/components/DcbValidation.res +427 -0
  33. package/src/components/DcbValidation.res.mjs +423 -0
  34. package/src/components/DisplayName.res +40 -0
  35. package/src/components/DisplayName.res.mjs +26 -0
  36. package/src/components/ExtensionPoint.res +27 -0
  37. package/src/components/ExtensionPoint.res.mjs +2 -0
  38. package/src/components/InboundTranslationSlice.res +85 -0
  39. package/src/components/InboundTranslationSlice.res.mjs +2 -0
  40. package/src/components/OutboundTranslationSlice.res +153 -0
  41. package/src/components/OutboundTranslationSlice.res.mjs +2 -0
  42. package/src/components/Plugin.res +538 -0
  43. package/src/components/Plugin.res.mjs +264 -0
  44. package/src/components/PluginName.res +39 -0
  45. package/src/components/PluginName.res.mjs +45 -0
  46. package/src/components/ReadModel.res +199 -0
  47. package/src/components/ReadModel.res.mjs +18 -0
  48. package/src/components/Reference.res +55 -0
  49. package/src/components/Reference.res.mjs +50 -0
  50. package/src/components/Snapshot.res +26 -0
  51. package/src/components/Snapshot.res.mjs +2 -0
  52. package/src/components/StateAnnotations.res +97 -0
  53. package/src/components/StateAnnotations.res.mjs +15 -0
  54. package/src/components/StateChangeSlice.res +131 -0
  55. package/src/components/StateChangeSlice.res.mjs +2 -0
  56. package/src/components/StateViewSlice.res +123 -0
  57. package/src/components/StateViewSlice.res.mjs +2 -0
  58. package/src/components/Task.res +62 -0
  59. package/src/components/Task.res.mjs +2 -0
  60. package/src/generator/Codegen.res +842 -0
  61. package/src/generator/Codegen.res.mjs +565 -0
  62. package/src/generator/Config.res +106 -0
  63. package/src/generator/Config.res.mjs +69 -0
  64. package/src/generator/Discovery.res +230 -0
  65. package/src/generator/Discovery.res.mjs +198 -0
  66. package/src/generator/Generator_Node.res +14 -0
  67. package/src/generator/Generator_Node.res.mjs +18 -0
  68. package/src/generator/Pairing.res +460 -0
  69. package/src/generator/Pairing.res.mjs +415 -0
  70. package/src/generator/PlatformCodegen.res +207 -0
  71. package/src/generator/PlatformCodegen.res.mjs +154 -0
  72. package/src/generator/PlatformGenerator.res +126 -0
  73. package/src/generator/PlatformGenerator.res.mjs +114 -0
  74. package/src/generator/PlatformManifests.res +203 -0
  75. package/src/generator/PlatformManifests.res.mjs +212 -0
  76. package/src/generator/PluginGenerator.res +57 -0
  77. package/src/generator/PluginGenerator.res.mjs +73 -0
  78. package/src/semantic/Bytes.res +54 -0
  79. package/src/semantic/Bytes.res.mjs +38 -0
  80. package/src/semantic/Capabilities.res +43 -0
  81. package/src/semantic/Capabilities.res.mjs +17 -0
  82. package/src/semantic/Color.res +51 -0
  83. package/src/semantic/Color.res.mjs +29 -0
  84. package/src/semantic/Currency.res +598 -0
  85. package/src/semantic/Currency.res.mjs +743 -0
  86. package/src/semantic/DateRange.res +148 -0
  87. package/src/semantic/DateRange.res.mjs +74 -0
  88. package/src/semantic/Duration.res +53 -0
  89. package/src/semantic/Duration.res.mjs +26 -0
  90. package/src/semantic/Email.res +51 -0
  91. package/src/semantic/Email.res.mjs +31 -0
  92. package/src/semantic/GeoPoint.res +226 -0
  93. package/src/semantic/GeoPoint.res.mjs +190 -0
  94. package/src/semantic/Geocoding.res +127 -0
  95. package/src/semantic/Geocoding.res.mjs +36 -0
  96. package/src/semantic/Money.res +196 -0
  97. package/src/semantic/Money.res.mjs +138 -0
  98. package/src/semantic/Offload.res +294 -0
  99. package/src/semantic/Offload.res.mjs +191 -0
  100. package/src/semantic/Percent.res +53 -0
  101. package/src/semantic/Percent.res.mjs +33 -0
  102. package/src/semantic/Phone.res +55 -0
  103. package/src/semantic/Phone.res.mjs +29 -0
  104. package/src/semantic/Semantic.res +162 -0
  105. package/src/semantic/Semantic.res.mjs +95 -0
  106. package/src/semantic/StorageRef.res +164 -0
  107. package/src/semantic/StorageRef.res.mjs +111 -0
  108. package/src/semantic/Url.res +66 -0
  109. package/src/semantic/Url.res.mjs +48 -0
  110. package/src/types/Authorization.res +23 -0
  111. package/src/types/Authorization.res.mjs +33 -0
  112. package/src/types/Behavior.res +86 -0
  113. package/src/types/Behavior.res.mjs +2 -0
  114. package/src/types/DateTime.res +29 -0
  115. package/src/types/DateTime.res.mjs +16 -0
  116. package/src/types/EventMapping.res +100 -0
  117. package/src/types/EventMapping.res.mjs +15 -0
  118. package/src/types/Handler.res +30 -0
  119. package/src/types/Handler.res.mjs +2 -0
  120. package/src/types/Id.res +75 -0
  121. package/src/types/Id.res.mjs +37 -0
  122. package/src/types/Identity.res +46 -0
  123. package/src/types/Identity.res.mjs +51 -0
  124. package/src/types/Message.res +326 -0
  125. package/src/types/Message.res.mjs +186 -0
  126. package/src/types/Projection.res +220 -0
  127. package/src/types/Projection.res.mjs +44 -0
  128. package/src/types/QueryEngine.res +123 -0
  129. package/src/types/QueryEngine.res.mjs +12 -0
  130. package/src/types/ReadConsistency.res +38 -0
  131. package/src/types/ReadConsistency.res.mjs +29 -0
  132. package/src/types/Schedule.res +65 -0
  133. package/src/types/Schedule.res.mjs +68 -0
  134. package/src/types/SideEffect.res +45 -0
  135. package/src/types/SideEffect.res.mjs +2 -0
  136. package/src/types/StoredEvent.res +46 -0
  137. package/src/types/StoredEvent.res.mjs +32 -0
  138. package/src/types/Visibility.res +24 -0
  139. package/src/types/Visibility.res.mjs +25 -0
@@ -0,0 +1,33 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+
5
+ let permissionSchema = S.union([
6
+ S.schema(s => ({
7
+ TAG: "AllowGroups",
8
+ _0: s.m(S.array(S.string))
9
+ })),
10
+ S.literal("AllowAuthenticated"),
11
+ S.literal("AllowAnonymous"),
12
+ S.literal("DenyAll")
13
+ ]);
14
+
15
+ function isAllowed(rule, identity) {
16
+ if (typeof rule === "object") {
17
+ return rule._0.some(group => identity.groups.includes(group));
18
+ }
19
+ switch (rule) {
20
+ case "AllowAuthenticated" :
21
+ return identity.userId !== "anonymous";
22
+ case "AllowAnonymous" :
23
+ return true;
24
+ case "DenyAll" :
25
+ return false;
26
+ }
27
+ }
28
+
29
+ export {
30
+ permissionSchema,
31
+ isAllowed,
32
+ }
33
+ /* permissionSchema Not a pure module */
@@ -0,0 +1,86 @@
1
+ // Behavior module type for aggregate business logic.
2
+ // Lives in reventless-spec so application domain code can define behaviors
3
+ // without importing the reventless implementation package.
4
+
5
+ /**
6
+ Module type that application code must implement to define aggregate business logic.
7
+
8
+ A `Behavior.T` module contains the `Spec` (command / event / error types), the
9
+ aggregate `state`, and two pure functions plus an initial state value:
10
+
11
+ - `initialState` — the starting state before any events (represents "not yet created")
12
+ - `evolve` — fold an event into the current state
13
+ - `decide` — handle a command on the current state, returning `Ok(events)` or `Error(error)`
14
+
15
+ @example
16
+ ```rescript
17
+ // CategoryBehavior.res
18
+ module Spec = Category
19
+
20
+ @schema
21
+ type state = NotCreated | Active({name: string}) | Archived
22
+
23
+ let initialState = NotCreated
24
+
25
+ let evolve = (state, event) => switch (state, event) {
26
+ | (NotCreated, CategoryAdded({name})) => Active({name})
27
+ | (Active(_), CategoryRenamed({name})) => Active({name})
28
+ | (Active(_), CategoryArchived(_)) => Archived
29
+ | _ => state
30
+ }
31
+
32
+ let decide = (state, cmd) => switch (state, cmd) {
33
+ | (NotCreated, AddCategory({categoryId, name})) => Ok([CategoryAdded({categoryId, name})])
34
+ | (NotCreated, _) => Error(CategoryNotFound)
35
+ | (Active(_), AddCategory(_)) => Error(CategoryAlreadyExists)
36
+ | (Active(_), RenameCategory({categoryId, name})) => Ok([CategoryRenamed({categoryId, name})])
37
+ | (Active(_), ArchiveCategory({categoryId})) => Ok([CategoryArchived({categoryId})])
38
+ | (Archived, _) => Error(CategoryAlreadyArchived)
39
+ }
40
+ ```
41
+ */
42
+ module type T = {
43
+ module Spec: {
44
+ /** The command type. Must carry a `@schema` attribute. */
45
+ @schema
46
+ type command
47
+
48
+ /** The event type. Must carry a `@schema` attribute. */
49
+ @schema
50
+ type event
51
+
52
+ /** The error type returned when business rules are violated. */
53
+ @schema
54
+ type error
55
+ }
56
+
57
+ /** The aggregate's internal state type. Not persisted; rebuilt by replaying events. */
58
+ type state
59
+
60
+ /** The starting state before any events have been applied. */
61
+ let initialState: state
62
+
63
+ /**
64
+ Fold one event into the current state. Called for every event during replay.
65
+ Must be a pure function — no side effects.
66
+ */
67
+ let evolve: (state, Spec.event) => state
68
+
69
+ /**
70
+ Handle a command on the current state.
71
+ Return `Ok(events)` to emit events, or `Error(error)` to reject the command.
72
+ */
73
+ let decide: (state, Spec.command) => result<array<Spec.event>, Spec.error>
74
+
75
+ /** Persisted-snapshot configuration (docs/plans/aggregate-snapshotting.md).
76
+ `None` — the default, auto-injected by `@@reventless.behavior` — keeps
77
+ full replay; `Some({interval, stateSchema})` writes a keep-one snapshot
78
+ every `interval` events and seeds cold replays from the latest one.
79
+ Enable with `@@reventless.snapshots(<interval>)` (requires
80
+ `@schema type state`) or a manual binding. */
81
+ let snapshot: option<Snapshot.config<state>>
82
+
83
+ /** File URL of this module (`import.meta.url`). Used by AWS builders to derive
84
+ the npm specifier for runtime dynamic imports. */
85
+ let moduleUrl: string
86
+ }
@@ -0,0 +1,2 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+ /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
@@ -0,0 +1,29 @@
1
+ /**
2
+ Marks a `string` state field as an ISO-8601 date-time.
3
+
4
+ Sury has no string-typed datetime format (`S.datetime` transforms to `Js.Date.t`,
5
+ changing the field's runtime type), so this mirrors the `DcbTag.string`
6
+ precedent: a `S.t<string>` carrying sury metadata that downstream schema walkers
7
+ detect. `SchemaType`/`SuryToJsonSchema` surface it as `format: "date-time"` on
8
+ the field's JSON Schema, which the AutoUI date heuristics key off (CalendarView,
9
+ TimelineView, date-axis charts).
10
+
11
+ Use on a producer/storage timestamp a projection writes into its state — most
12
+ commonly a `meta.time`-derived field such as `placedAt` / `shippedAt`:
13
+
14
+ @example
15
+ ```rescript
16
+ @schema
17
+ type state = {
18
+ orderId: string,
19
+ placedAt: @s.matches(Reventless.DateTime.string) string,
20
+ }
21
+ ```
22
+ */
23
+
24
+ /** A sury string schema annotated as an ISO-8601 date-time field.
25
+ Use with `@s.matches(Reventless.DateTime.string)`. */
26
+ let string: S.t<string> = S.string->Semantic.mark(~id=Semantic.Id.dateTime)
27
+
28
+ /** Whether a field schema carries the date-time marker. */
29
+ let isDateTime = (fieldSchema: S.t<unknown>) => fieldSchema->Semantic.has(~id=Semantic.Id.dateTime)
@@ -0,0 +1,16 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Semantic$Reventless from "../semantic/Semantic.res.mjs";
5
+
6
+ let string = Semantic$Reventless.mark(S.string, Semantic$Reventless.Id.dateTime, undefined);
7
+
8
+ function isDateTime(fieldSchema) {
9
+ return Semantic$Reventless.has(fieldSchema, Semantic$Reventless.Id.dateTime);
10
+ }
11
+
12
+ export {
13
+ string,
14
+ isDateTime,
15
+ }
16
+ /* string Not a pure module */
@@ -0,0 +1,100 @@
1
+ /**
2
+ The aggregate whose events will be mapped.
3
+ Identifies the event topic to subscribe to via `name` and `Id`.
4
+ */
5
+ module type Source = {
6
+ let name: string
7
+ module Id: Id.T
8
+ @schema
9
+ type event
10
+ }
11
+
12
+ /**
13
+ The aggregate whose commands will be produced.
14
+ Identifies the command topic to publish to via `name` and `Id`.
15
+ */
16
+ module type Target = {
17
+ let name: string
18
+ module Id: Id.T
19
+ @schema
20
+ type command
21
+ }
22
+
23
+ /**
24
+ The outcome of an event-to-command mapping.
25
+
26
+ A `map` function returns an array of `action` values describing what should
27
+ happen when a source event is observed.
28
+
29
+ - `Publish(id, cmd)` — immediately publish a command to the target aggregate
30
+ - `PublishDelayed(id, cmd, seconds)` — publish a command after a delay
31
+ - `PublishAsync(promise)` — resolve a promise and publish all resulting commands
32
+ - `AddToCounterTarget(target)` — register a counter threshold target
33
+ - `Count(id)` — increment a counter by 1
34
+ - `CountMulti(id, n)` — increment a counter by `n`
35
+
36
+ @example
37
+ ```rescript
38
+ // Route CategoryArchived to update the Products read model
39
+ let map = (sourceId, event, _queryEngine) => switch event {
40
+ | CategoryArchived({categoryId}) =>
41
+ [EventMapping.Publish(categoryId, ArchiveProductsInCategory({categoryId}))]
42
+ | _ => []
43
+ }
44
+ ```
45
+ */
46
+ type action<'id, 'command> =
47
+ /** Immediately publish a command to the target aggregate. */
48
+ | Publish('id, 'command)
49
+ /** Publish a command after `int` seconds. */
50
+ | PublishDelayed('id, 'command, int)
51
+ /** Resolve a promise and publish all resulting `(id, command)` pairs. */
52
+ | PublishAsync(promise<array<('id, 'command)>>)
53
+ /** Register a counter threshold target (used with the Counter component). */
54
+ | AddToCounterTarget(Counter.counterTarget)
55
+ /** Increment a named counter by 1. */
56
+ | Count(Counter.counterId)
57
+ /** Increment a named counter by `n`. */
58
+ | CountMulti(Counter.counterId, int)
59
+
60
+ /**
61
+ Module type for an event-to-command mapping.
62
+
63
+ Implement this to route events from one aggregate to commands on another.
64
+ The `map` function is pure — use `PublishAsync` for async lookups.
65
+
66
+ @example
67
+ ```rescript
68
+ module CategoryToProduct: EventMapping.T = {
69
+ module Source = Category
70
+ module Target = Product
71
+ let map = (sourceId, event, _q) => switch event {
72
+ | CategoryArchived({categoryId}) =>
73
+ [Publish(categoryId, ArchiveProductsInCategory({categoryId}))]
74
+ | _ => []
75
+ }
76
+ }
77
+ ```
78
+ */
79
+ module type T = {
80
+ module Source: Source
81
+ module Target: Target
82
+ /**
83
+ Map a source event to zero or more target actions.
84
+
85
+ - `Source.Id.t` — the ID of the aggregate that emitted the event
86
+ - `Source.event` — the domain event payload
87
+ - `QueryEngine.operations` — read-only query engine for async lookups
88
+ */
89
+ let map: (
90
+ Source.Id.t,
91
+ Source.event,
92
+ QueryEngine.operations,
93
+ ) => array<action<Target.Id.t, Target.command>>
94
+ }
95
+
96
+ module Mappings = {
97
+ module Make = (Target: Target) => {
98
+ module type Mapping = T with module Target := Target
99
+ }
100
+ }
@@ -0,0 +1,15 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function Make(Target) {
5
+ return {};
6
+ }
7
+
8
+ let Mappings = {
9
+ Make: Make
10
+ };
11
+
12
+ export {
13
+ Mappings,
14
+ }
15
+ /* No side effect */
@@ -0,0 +1,30 @@
1
+ // Handler types for application developers.
2
+ // These define the function signatures for handling commands, events, and errors.
3
+
4
+ /** A generic async handler for any message type `'msg`. */
5
+ type handler<'msg> = 'msg => promise<unit>
6
+
7
+ /**
8
+ Handler for a single typed command.
9
+
10
+ @example
11
+ ```rescript
12
+ let handleCategory: Handler.commandHandler<string, Category.command> = async msg => {
13
+ Console.log2("Handling command for", msg.id)
14
+ }
15
+ ```
16
+ */
17
+ type commandHandler<'id, 'command> = Message.command'<'id, 'command> => promise<unit>
18
+
19
+ /**
20
+ Handler for a batch of commands sharing the same aggregate ID.
21
+ Batches arrive when multiple commands are enqueued for the same ID.
22
+ */
23
+ type commandsHandler<'id, 'command> = ('id, array<Message.command'<'id, 'command>>) => promise<unit>
24
+
25
+ /**
26
+ Handler for a batch of events sharing the same aggregate ID.
27
+ Used by read model projections and extension points.
28
+ */
29
+ type eventsHandler<'id, 'event> = ('id, array<Message.event'<'id, 'event>>) => promise<unit>
30
+
@@ -0,0 +1,2 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+ /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
@@ -0,0 +1,75 @@
1
+ /**
2
+ Module type for aggregate and read model identifiers.
3
+
4
+ Every Reventless component declares its own `Id` module satisfying this type.
5
+ The abstract `type t` prevents accidentally mixing identifiers from different
6
+ aggregates at compile time.
7
+
8
+ @example
9
+ ```rescript
10
+ // Category.res
11
+ module Id = Id.String
12
+ ```
13
+ */
14
+ module type T = {
15
+ /** The opaque identity type. Sealed so IDs from different aggregates cannot be mixed. */
16
+ @schema
17
+ type t
18
+
19
+ /** The input form accepted by `make` (e.g. `string`, `int`, or a record). */
20
+ type input
21
+
22
+ /** Construct a typed ID from its input representation. */
23
+ let make: input => t
24
+
25
+ /** Construct a typed ID from a plain string (used by adapters and routing layers). */
26
+ let makeFromString: string => t
27
+
28
+ /** Convert a typed ID back to its string representation. */
29
+ let toString: t => string
30
+
31
+ /** Compare two IDs. Required for sorted data structures and range queries. */
32
+ let cmp: (t, t) => Ordering.t
33
+ }
34
+
35
+ /**
36
+ A transparent string-based `Id.T` implementation.
37
+
38
+ `StringPure.t = string`, so IDs can be written as plain string literals in
39
+ test code without casting. Use `Id.String` (the sealed version) in production
40
+ module specs.
41
+
42
+ @example
43
+ ```rescript
44
+ // In test specs where literal strings are convenient:
45
+ module TestCategorySpec = {
46
+ module Id = Id.StringPure
47
+ let name = "Category"
48
+ // Id.make("cat-1") == "cat-1"
49
+ }
50
+ ```
51
+ */
52
+ module StringPure = {
53
+ @schema
54
+ type t = string
55
+ type input = string
56
+ external make: t => t = "%identity"
57
+ external makeFromString: string => t = "%identity"
58
+ external toString: t => t = "%identity"
59
+ let cmp: (t, t) => Ordering.t = String.compare
60
+ }
61
+
62
+ /**
63
+ A sealed string-based `Id.T` implementation for use in production aggregate specs.
64
+
65
+ Unlike `StringPure`, the `t` type is abstract, preventing accidental cross-aggregate
66
+ ID mixing. Use `Id.StringPure` in tests when string literals are needed.
67
+
68
+ @example
69
+ ```rescript
70
+ // Category.res
71
+ module Id = Id.String
72
+ let name = "Category"
73
+ ```
74
+ */
75
+ module String: T = StringPure
@@ -0,0 +1,37 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
5
+
6
+ let cmp = Primitive_string.compare;
7
+
8
+ let StringPure = {
9
+ schema: S.string,
10
+ cmp: cmp
11
+ };
12
+
13
+ function String_make(prim) {
14
+ return prim;
15
+ }
16
+
17
+ function String_makeFromString(prim) {
18
+ return prim;
19
+ }
20
+
21
+ function String_toString(prim) {
22
+ return prim;
23
+ }
24
+
25
+ let $$String = {
26
+ schema: S.string,
27
+ make: String_make,
28
+ makeFromString: String_makeFromString,
29
+ toString: String_toString,
30
+ cmp: cmp
31
+ };
32
+
33
+ export {
34
+ StringPure,
35
+ $$String,
36
+ }
37
+ /* S Not a pure module */
@@ -0,0 +1,46 @@
1
+ S.enableJson()
2
+
3
+ /** Identity provider that authenticated the user. */
4
+ @schema
5
+ type provider = Cognito | InMemory | Custom(string)
6
+
7
+ /**
8
+ Per-request identity carried through RequestContext.
9
+
10
+ NOT persisted in events — only `userId` is stored as `meta.user`.
11
+ Full identity lives in-memory for the duration of a single request.
12
+ */
13
+ @schema
14
+ type t = {
15
+ userId: string,
16
+ username: string,
17
+ groups: array<string>,
18
+ claims?: dict<string>,
19
+ provider: provider,
20
+ }
21
+
22
+ let anonymous: t = {
23
+ userId: "anonymous",
24
+ username: "anonymous",
25
+ groups: [],
26
+ provider: InMemory,
27
+ }
28
+
29
+ let hasGroup = (identity: t, group: string): bool =>
30
+ identity.groups->Array.includes(group)
31
+
32
+ let getClaim = (identity: t, key: string): option<string> =>
33
+ switch identity.claims {
34
+ | Some(claims) => claims->Dict.get(key)
35
+ | None => None
36
+ }
37
+
38
+ /**
39
+ Outcome of a single `Auth_Adapter.Provider.authenticate` call.
40
+ `AuthError` carries a short human-readable reason (logged, not returned
41
+ verbatim to clients) — useful for diagnosing failed bearer-token validation.
42
+ */
43
+ type authResult =
44
+ | Authenticated(t)
45
+ | Anonymous
46
+ | AuthError(string)
@@ -0,0 +1,51 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+
5
+ S.enableJson();
6
+
7
+ let providerSchema = S.union([
8
+ S.literal("Cognito"),
9
+ S.literal("InMemory"),
10
+ S.schema(s => ({
11
+ TAG: "Custom",
12
+ _0: s.m(S.string)
13
+ }))
14
+ ]);
15
+
16
+ let schema = S.schema(s => ({
17
+ userId: s.m(S.string),
18
+ username: s.m(S.string),
19
+ groups: s.m(S.array(S.string)),
20
+ claims: s.m(S.option(S.dict(S.string))),
21
+ provider: s.m(providerSchema)
22
+ }));
23
+
24
+ let anonymous_groups = [];
25
+
26
+ let anonymous = {
27
+ userId: "anonymous",
28
+ username: "anonymous",
29
+ groups: anonymous_groups,
30
+ provider: "InMemory"
31
+ };
32
+
33
+ function hasGroup(identity, group) {
34
+ return identity.groups.includes(group);
35
+ }
36
+
37
+ function getClaim(identity, key) {
38
+ let claims = identity.claims;
39
+ if (claims !== undefined) {
40
+ return claims[key];
41
+ }
42
+ }
43
+
44
+ export {
45
+ providerSchema,
46
+ schema,
47
+ anonymous,
48
+ hasGroup,
49
+ getClaim,
50
+ }
51
+ /* Not a pure module */