@reventlessdev/reventless-spec 3.0.0-alpha.123 → 3.0.0-alpha.125
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 +56 -0
- package/package.json +5 -3
- package/run-certify-trait.mjs +2 -0
- package/run-graft-trait.mjs +2 -0
- package/run-trait-manifest.mjs +2 -0
- package/schema/platform-api.graphql +41 -3
- package/src/components/Aggregate.res +22 -0
- package/src/components/AutomationSlice.res +29 -3
- package/src/components/CapabilityManifest.res +52 -24
- package/src/components/CapabilityManifest.res.mjs +35 -11
- package/src/components/InboundTranslationSlice.res +14 -0
- package/src/components/OutboundTranslationSlice.res +24 -0
- package/src/components/Plugin.res +202 -422
- package/src/components/Plugin.res.mjs +65 -3
- package/src/components/StateChangeSlice.res +22 -0
- package/src/components/TraitCertificate.res +105 -0
- package/src/components/TraitCertificate.res.mjs +65 -0
- package/src/components/TraitManifest.res +90 -0
- package/src/components/TraitManifest.res.mjs +48 -0
- package/src/generator/CertifyTrait.res +190 -0
- package/src/generator/CertifyTrait.res.mjs +154 -0
- package/src/generator/GraftTrait.res +230 -0
- package/src/generator/GraftTrait.res.mjs +193 -0
- package/src/generator/PlatformCodegen.res +44 -30
- package/src/generator/PlatformCodegen.res.mjs +36 -17
- package/src/generator/TraitManifestCli.res +138 -0
- package/src/generator/TraitManifestCli.res.mjs +105 -0
- package/src/semantic/Capabilities.res +19 -3
- package/src/semantic/Capabilities.res.mjs +18 -2
- package/src/semantic/CapabilityNeed.res +81 -0
- package/src/semantic/CapabilityNeed.res.mjs +46 -0
- package/src/semantic/Currency.res +519 -502
- package/src/semantic/Currency.res.mjs +7 -655
- package/src/semantic/Messaging.res +127 -0
- package/src/semantic/Messaging.res.mjs +57 -0
- package/src/semantic/Money.res +123 -21
- package/src/semantic/Money.res.mjs +49 -2
- package/src/types/Trait.res +62 -0
- package/src/types/Trait.res.mjs +18 -0
- package/src/types/Transition.res +71 -0
- package/src/types/Transition.res.mjs +36 -0
- package/scripts/generate-currency.mjs +0 -215
- package/scripts/iso-4217-list-one.xml +0 -1956
|
@@ -6,16 +6,8 @@ type name = string
|
|
|
6
6
|
@schema
|
|
7
7
|
type version = string
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
plugin-lifecycle read model can segregate infrastructure/commercial/marketplace
|
|
12
|
-
plugins from domain plugins in the admin Plugins view. Absent (on definitions
|
|
13
|
-
persisted before this field existed) is read as `Domain`.
|
|
14
|
-
|
|
15
|
-
Canonical here in `reventless-spec` because `pluginDefinition` (a `@schema` type
|
|
16
|
-
nested in the lifecycle Message union) needs the sury schema; `ReventlessCore.Plugin_BuiltHook`
|
|
17
|
-
re-exports this same type for its deploy-time metadata registry.
|
|
18
|
-
*/
|
|
9
|
+
/** A plugin's business role, used by the admin Plugins view to segregate
|
|
10
|
+
infrastructure from domain plugins. Absent is read as `Domain`. */
|
|
19
11
|
@schema
|
|
20
12
|
type pluginKind =
|
|
21
13
|
| Domain
|
|
@@ -43,22 +35,13 @@ Included in the plugin's `pluginDefinition` for use by the host.
|
|
|
43
35
|
type extensionDefinition = {
|
|
44
36
|
name: string,
|
|
45
37
|
extensionPointName: string,
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
the `name` field of a peer plugin's `dcbEventLogDefinition` (i.e. `${peer}DcbEventLog`).
|
|
49
|
-
The admin uses these to provision cross-plugin SNS subscriptions from peer DCB
|
|
50
|
-
EventTopics → this plugin's EventCollector. `[]` for extensions that only consume
|
|
51
|
-
ExtensionPoint EventTopics.
|
|
52
|
-
*/
|
|
38
|
+
/** Peer `dcbEventLogDefinition.name`s this extension consumes (`${peer}DcbEventLog`),
|
|
39
|
+
which the admin turns into cross-plugin SNS subscriptions. */
|
|
53
40
|
dcbSources: array<string>,
|
|
54
41
|
}
|
|
55
42
|
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
Included in the plugin's `pluginDefinition` so the admin can provision SNS
|
|
59
|
-
subscriptions from this plugin's DCB EventTopic to any peer plugin whose
|
|
60
|
-
extension references the DCB log by name.
|
|
61
|
-
*/
|
|
43
|
+
/** A DCB EventLog exposed by a plugin, so the admin can subscribe peer plugins
|
|
44
|
+
that reference it by name. */
|
|
62
45
|
@schema
|
|
63
46
|
type dcbEventLogDefinition = {
|
|
64
47
|
/** Service name carried in event meta — convention: `${plugin.name}DcbEventLog`. */
|
|
@@ -67,15 +50,8 @@ type dcbEventLogDefinition = {
|
|
|
67
50
|
eventTopicArn: string,
|
|
68
51
|
}
|
|
69
52
|
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Carried in the `ConnectPlugin` handshake so the host can validate schema
|
|
74
|
-
compatibility before accepting the extension. Use `[]` when version
|
|
75
|
-
negotiation is not needed.
|
|
76
|
-
*/
|
|
77
|
-
// Protocol version declaration for a single extension point connection.
|
|
78
|
-
// Carried in the ConnectPlugin handshake so the host can validate compatibility.
|
|
53
|
+
/** Protocol versions for one extension point connection, carried in the
|
|
54
|
+
`ConnectPlugin` handshake. `[]` when negotiation is not needed. */
|
|
79
55
|
@schema
|
|
80
56
|
type extensionProtocol = {
|
|
81
57
|
extensionPointName: string,
|
|
@@ -92,22 +68,15 @@ Encoded as JSON for transport; protocol identifies the schema format (e.g. "grap
|
|
|
92
68
|
@schema
|
|
93
69
|
type apiSchemaFragment = {encoded: string, protocol: string}
|
|
94
70
|
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
application plugins) or the Platform API (platform-level plugins such as an inspector,
|
|
98
|
-
which contribute fields alongside the admin base). Serializes as the bare string
|
|
99
|
-
"Domain" / "Platform". Consumed by the schema-fragment registry to maintain one
|
|
100
|
-
cumulative schema per API.
|
|
101
|
-
*/
|
|
71
|
+
/** Which API a plugin's GraphQL fields are stitched into. Serializes as the bare
|
|
72
|
+
string "Domain" / "Platform". */
|
|
102
73
|
@schema
|
|
103
74
|
type apiTarget = Domain | Platform
|
|
104
75
|
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
// JSON-safe and passes jsonableValidation in all contexts.
|
|
76
|
+
// js_nullable (T | null) is the only optional that passes jsonableValidation inside
|
|
77
|
+
// union variant payloads; nullableAsOption adds `undefined` and fails it.
|
|
108
78
|
let apiSchemaFragmentOffloadSchema = Offload.optionSchema(~store="pluginApiFragments", apiSchemaFragmentSchema)
|
|
109
79
|
let dcbEventLogOptionSchema = dcbEventLogDefinitionSchema->S.nullAsOption
|
|
110
|
-
// js_nullable creates T | null which passes sury's jsonableValidation inside union variant payloads.
|
|
111
80
|
let stringOptionSchema = S.string->S.nullAsOption
|
|
112
81
|
let stringArrayOptionSchema = S.array(S.string)->S.nullAsOption
|
|
113
82
|
let boolOptionSchema = S.bool->S.nullAsOption
|
|
@@ -168,60 +137,19 @@ type commandDef = {
|
|
|
168
137
|
aggregateIdField: @s.matches(stringOptionSchema) option<string>,
|
|
169
138
|
mutationField: string,
|
|
170
139
|
references: array<fieldReference>,
|
|
171
|
-
/**
|
|
172
|
-
|
|
173
|
-
is always available (back-compat default). `Some([…])` lets AutoUI hide the
|
|
174
|
-
command on rows whose lifecycle field is not in the set — see
|
|
175
|
-
`queryableDef.lifecycleField` for how the row's state is located. `Some([])` is
|
|
176
|
-
the defensive "never show" form.
|
|
177
|
-
*/
|
|
140
|
+
/** The `@transition` *from* set — lifecycle states this command is meaningful in.
|
|
141
|
+
`None` means always available; `Some([])` means never show. */
|
|
178
142
|
allowedStates: @s.matches(stringArrayOptionSchema) option<array<string>>,
|
|
179
|
-
/**
|
|
180
|
-
|
|
181
|
-
state, sibling of `allowedStates`' *from* set. Source: the
|
|
182
|
-
target of the `@transition(([Placed]) => Shipped)` command-variant annotation.
|
|
183
|
-
`Some("Shipped")` lets a resolver move a row by a declared transition instead
|
|
184
|
-
of a guess. `None` means no target was declared — and note that this is two
|
|
185
|
-
different statements depending on `allowedStates`: with a from-set present it
|
|
186
|
-
is the command declaring it does not move the row, and with none it is simply
|
|
187
|
-
an unannotated command. js_nullable for JSON safety, same as `allowedStates`.
|
|
188
|
-
*/
|
|
143
|
+
/** The `@transition` *to* state this command's handler writes. `None` with a
|
|
144
|
+
from-set present means the command does not move the row. */
|
|
189
145
|
targetState: @s.matches(stringOptionSchema) option<string>,
|
|
190
|
-
/**
|
|
191
|
-
Whether this command variant is exposed in the generated API (a non-`@noApi`
|
|
192
|
-
variant of a non-`@noApi` command). Dev tooling badges API-exposed commands in
|
|
193
|
-
the event graph. js_nullable (T | null) so it stays JSON-safe inside the
|
|
194
|
-
persisted/lifecycle payloads; absent on defs written before this field existed
|
|
195
|
-
(read as None) — those stores must be reset. See [[sury-optional-field-absent-vs-null]].
|
|
196
|
-
*/
|
|
146
|
+
/** Whether the variant is exposed in the generated API (non-`@noApi`). */
|
|
197
147
|
apiExposed: @s.matches(boolOptionSchema) option<bool>,
|
|
198
|
-
/**
|
|
199
|
-
|
|
200
|
-
derived from the authorization rule the server already enforces. `None` (or `[]`)
|
|
201
|
-
means the rule asks for nothing a client can check.
|
|
202
|
-
|
|
203
|
-
A hint, never a boundary: the rule in the resolver is what refuses a call, and a
|
|
204
|
-
caller who edits this list gains nothing. It exists so a client stops advertising
|
|
205
|
-
what the server would refuse — an offered command that always fails is a worse
|
|
206
|
-
answer than no command at all. Derived rather than authored, so it cannot drift
|
|
207
|
-
from the rule it describes. js_nullable, so defs written before this field
|
|
208
|
-
existed decode as None.
|
|
209
|
-
*/
|
|
148
|
+
/** Access keys — any one of them — a caller needs to be *offered* this command.
|
|
149
|
+
A hint derived from the server's rule, never the refusal itself. */
|
|
210
150
|
requiredAccess: @s.matches(stringArrayOptionSchema) option<array<string>>,
|
|
211
|
-
/**
|
|
212
|
-
|
|
213
|
-
the command declares one (`@owner`). A client should omit it from a generated
|
|
214
|
-
form for a caller who is not elevated: whatever it collects there is discarded
|
|
215
|
-
and replaced, so offering the field asks a question whose answer is ignored.
|
|
216
|
-
|
|
217
|
-
Derived from the annotation, never authored, so it cannot disagree with what the
|
|
218
|
-
write path actually does. js_nullable for the same JSON-safety reason as
|
|
219
|
-
`requiredAccess`.
|
|
220
|
-
|
|
221
|
-
⚠️ A client cannot decide the *elevated* half from this alone — the manifest
|
|
222
|
-
states which field carries the owner, and the caller's own identity says whether
|
|
223
|
-
they are exempt. Both are needed, and neither is derivable from the other.
|
|
224
|
-
*/
|
|
151
|
+
/** The `@owner` command field the server stamps with the caller's identity; a
|
|
152
|
+
client omits it from a form, since whatever it collects is discarded. */
|
|
225
153
|
ownerField: @s.matches(stringOptionSchema) option<string>,
|
|
226
154
|
}
|
|
227
155
|
|
|
@@ -232,209 +160,53 @@ type queryableDef = {
|
|
|
232
160
|
schema: string,
|
|
233
161
|
consumedEventTypes: array<string>,
|
|
234
162
|
linkedWriteSide: array<string>,
|
|
235
|
-
/**
|
|
236
|
-
|
|
237
|
-
When the entity's state schema declares one or more `@displayName` annotations,
|
|
238
|
-
this resolves to `"displayName"` (the projected column). Otherwise it falls back
|
|
239
|
-
to the first non-`id` string property, or `"id"` as a last resort.
|
|
240
|
-
*/
|
|
163
|
+
/** The field carrying the human-readable label: `"displayName"` when the state
|
|
164
|
+
declares `@displayName`, else the first non-`id` string, else `"id"`. */
|
|
241
165
|
labelField: string,
|
|
242
|
-
/**
|
|
243
|
-
|
|
244
|
-
Mirrors `labelField` when the entity uses the fallback or single-field label.
|
|
245
|
-
For composite `@displayName` annotations, lists the *raw* underlying source
|
|
246
|
-
fields (so clients with substring indexes can target them directly).
|
|
247
|
-
*/
|
|
166
|
+
/** Fields for label-oriented text search — the *raw* source fields behind a
|
|
167
|
+
composite `@displayName`, else `labelField`. */
|
|
248
168
|
searchableFields: array<string>,
|
|
249
|
-
/**
|
|
250
|
-
|
|
251
|
-
rule of its own can tell a declaration from a guess before ranking the two:
|
|
252
|
-
|
|
253
|
-
- `"annotation"` — a `@displayName` spec. The author said which field names the
|
|
254
|
-
record; nothing a client infers locally outranks it.
|
|
255
|
-
- `"convention"` — a field literally named `name`/`title`/`label`/`displayName`.
|
|
256
|
-
A guess, and the one guess a client can independently arrive at.
|
|
257
|
-
- `"position"` — the first candidate in declaration order. A guess, and a fact
|
|
258
|
-
only this side knows; a client's own conventional-name rule is the better
|
|
259
|
-
answer where the two differ.
|
|
260
|
-
- `"fallback"` — no candidate at all, so `labelField` is `"id"`. The state
|
|
261
|
-
saying it has no human-readable field.
|
|
262
|
-
|
|
263
|
-
`None` means not stated — defs persisted before this field existed, and
|
|
264
|
-
hand-rolled defs that decline to say. Distinct from `Some("fallback")`, which
|
|
265
|
-
is this side stating that it looked. js_nullable for the same JSON-safety
|
|
266
|
-
reason as `lifecycleField`.
|
|
267
|
-
*/
|
|
169
|
+
/** Which rung produced `labelField`, so a consumer can rank it against its own
|
|
170
|
+
rule: `"annotation"` | `"convention"` | `"position"` | `"fallback"`. */
|
|
268
171
|
labelFieldSource: @s.matches(stringOptionSchema) option<string>,
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
AutoUI together with `commandDef.allowedStates` to filter the per-row command
|
|
272
|
-
menu. Resolution order (codegen): (1) field annotated `@lifecycle`; (2) a field
|
|
273
|
-
literally named `"lifecycle"` whose shape is an enum; (3) `None`. Spec authors
|
|
274
|
-
that hand-roll a `queryableDef` set this explicitly.
|
|
275
|
-
*/
|
|
172
|
+
/** The state field holding the row's lifecycle, paired with
|
|
173
|
+
`commandDef.allowedStates`. From `@lifecycle`, else an enum named `lifecycle`. */
|
|
276
174
|
lifecycleField: @s.matches(stringOptionSchema) option<string>,
|
|
277
|
-
/**
|
|
278
|
-
|
|
279
|
-
when the view declares one. Two consequences for a client: reads of this view
|
|
280
|
-
are narrowed server-side to a non-elevated caller's own rows, and the column is
|
|
281
|
-
constant for such a caller and so carries no information in a list.
|
|
282
|
-
|
|
283
|
-
Derived from the annotation. As on `commandDef.ownerField`, this states which
|
|
284
|
-
field carries the owner and not whether the current caller is exempt — that is
|
|
285
|
-
the caller's own identity to answer.
|
|
286
|
-
*/
|
|
175
|
+
/** The `@owner` state field. Reads of this view are narrowed server-side to a
|
|
176
|
+
non-elevated caller's own rows. */
|
|
287
177
|
ownerField: @s.matches(stringOptionSchema) option<string>,
|
|
288
|
-
/**
|
|
289
|
-
|
|
290
|
-
(`@retired`), when the view declares one. Reads of this view exclude rows whose
|
|
291
|
-
flag is true for callers outside `OwnerScope.elevatedGroups`, on the list door
|
|
292
|
-
and the single-entity door alike; an exempt caller reaches them by asking for
|
|
293
|
-
them.
|
|
294
|
-
|
|
295
|
-
Derived from the annotation and from nothing else — deliberately no fallback to
|
|
296
|
-
a conventionally-named boolean, unlike `lifecycleField`. A field named `archived`
|
|
297
|
-
that nobody annotated must not start hiding rows the day this ships, and the
|
|
298
|
-
cost of guessing wrong here is data disappearing rather than a menu filtering
|
|
299
|
-
oddly.
|
|
300
|
-
|
|
301
|
-
The label the flag reads as is not here. It travels on the state schema, which
|
|
302
|
-
every consumer of this def already holds, and a second copy is a second thing
|
|
303
|
-
to keep in step.
|
|
304
|
-
*/
|
|
178
|
+
/** The `@retired` state field withdrawing a row from ordinary reads. From the
|
|
179
|
+
annotation only — no fallback by name, since guessing hides data. */
|
|
305
180
|
retiredField: @s.matches(stringOptionSchema) option<string>,
|
|
306
|
-
/**
|
|
307
|
-
|
|
308
|
-
`@retired` — `Some(["Archived", "Discontinued"])` beside
|
|
309
|
-
`retiredField: Some("shelfStatus")`. `None` is the boolean form, where the
|
|
310
|
-
excluded value is always `true` and naming it would be a field that can only
|
|
311
|
-
hold one thing.
|
|
312
|
-
|
|
313
|
-
A set: a lifecycle may be withdrawn by more than one state, which exclude
|
|
314
|
-
identically and differ only in the way back. Retired iff the field's value is in
|
|
315
|
-
it, and one member is the ordinary case rather than a special one.
|
|
316
|
-
|
|
317
|
-
Published beside the field rather than left for a consumer to re-derive from the
|
|
318
|
-
state schema: a client holding this def holds the whole predicate, and two places
|
|
319
|
-
deriving one comparison is how they come to disagree about it.
|
|
320
|
-
|
|
321
|
-
The state form is also what lets a command's `@transition` answer applicability
|
|
322
|
-
when retired, with no annotation beyond the one — retirement expressed in the
|
|
323
|
-
vocabulary a command's stance is already written in.
|
|
324
|
-
*/
|
|
181
|
+
/** The states a row is retired *in* (state form of `@retired`); `None` is the
|
|
182
|
+
boolean form, where the excluded value is always `true`. */
|
|
325
183
|
retiredValues: @s.matches(stringArrayOptionSchema) option<array<string>>,
|
|
326
|
-
/**
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
its `labelField` and the value of `retiredField`. Declared with
|
|
330
|
-
`@namedWhenRetired` on the state record.
|
|
331
|
-
|
|
332
|
-
Published so a client knows the door exists without probing for it — a query
|
|
333
|
-
against a field the schema does not have is a validation error, not an empty
|
|
334
|
-
answer, so "ask and see" is not a usable fallback here.
|
|
335
|
-
|
|
336
|
-
Nullable rather than a bare required bool, which is the rule this schema's own
|
|
337
|
-
tripwire enforces: a definition stored before the field existed would otherwise
|
|
338
|
-
decode with an invented value and a runtime warning. Absent and `false` mean the
|
|
339
|
-
same thing to every reader — the archive stays shut — but only one of them is
|
|
340
|
-
something the platform actually said.
|
|
341
|
-
|
|
342
|
-
It is never `true` without `retiredField`: the PPX refuses the annotation on a
|
|
343
|
-
record with no retirement.
|
|
344
|
-
*/
|
|
184
|
+
/** Whether the view publishes the by-ids reference door that names a retired row
|
|
185
|
+
to any caller holding a pointer (`@namedWhenRetired`). Never true without
|
|
186
|
+
`retiredField`. */
|
|
345
187
|
namedWhenRetired: @s.matches(boolOptionSchema) option<bool>,
|
|
346
|
-
/**
|
|
347
|
-
|
|
348
|
-
ReadModel / StateViewSlice that the deployed AutoUI hides from its menu, drill-down
|
|
349
|
-
pages, web event graph and cross-plugin edges. `None` (absent) means Public. Internal
|
|
350
|
-
components are still CARRIED in `pluginStructure` (tagged here) so developer tools — the
|
|
351
|
-
`reventless-gwt` / VSCode domain graph and dead-code analysis — can see them, per
|
|
352
|
-
Visibility.res. Optional for back-compat: definitions persisted before this field
|
|
353
|
-
existed decode as `None` (Public).
|
|
354
|
-
*/
|
|
188
|
+
/** `@@reventless.visibility`. `Some("Internal")` hides the component from AutoUI;
|
|
189
|
+
it is still carried here for developer tooling. `None` means Public. */
|
|
355
190
|
visibility: @s.matches(stringOptionSchema) option<string>,
|
|
356
|
-
/**
|
|
357
|
-
|
|
358
|
-
build time from its source folder by the plugin generator: the first path segment
|
|
359
|
-
under the plugin's `src/` that is not a recognised kind-folder
|
|
360
|
-
(`src/<Chapter>/…/<Component>.res` → `Some("<Chapter>")`; a component directly under a
|
|
361
|
-
kind-folder → `None`). Lets a consumer that renders the event graph from the
|
|
362
|
-
*deployed* plugin structure group components into chapter sub-containers identically
|
|
363
|
-
to the authoring tooling, with no workspace/disk access — the renderer already
|
|
364
|
-
supports the bands (`DomainGraphD2 ~chapters`); only this datum was missing on the
|
|
365
|
-
deployed side. `None` (absent) renders flat. js_nullable (T | null) keeps it JSON-safe
|
|
366
|
-
inside the lifecycle Message union; always written (None → null), so defs persisted
|
|
367
|
-
before this field existed must be reset/re-emitted. See [[deployed-chapter-grouping]].
|
|
368
|
-
*/
|
|
191
|
+
/** Intra-plugin grouping band, the first non-kind path segment under `src/`.
|
|
192
|
+
`None` renders flat. */
|
|
369
193
|
chapter: @s.matches(stringOptionSchema) option<string>,
|
|
370
|
-
/**
|
|
371
|
-
|
|
372
|
-
(`Plugin_Order(id: ID!)` beside the list field `Plugin_Orders`), and — because
|
|
373
|
-
`Api_Naming` returns the same string for both — the prefix of the queryable's
|
|
374
|
-
generated input types (`Plugin_OrderFilter`, `Plugin_OrderOrderBy`). One field
|
|
375
|
-
rather than two, so the two uses cannot drift apart.
|
|
376
|
-
|
|
377
|
-
Published because it is not derivable from `queryField` without re-implementing
|
|
378
|
-
`Api_Naming.singularize`: a consumer that strips a trailing `s` turns
|
|
379
|
-
`Plugin_Categories` into `Plugin_Categorie`, a name the schema does not serve,
|
|
380
|
-
and fails at query time against that one view. Sourced from the naming module
|
|
381
|
-
itself, never re-derived.
|
|
382
|
-
|
|
383
|
-
`None` means not stated — defs persisted before this field existed, and
|
|
384
|
-
hand-rolled defs that decline to say; a consumer falls back to its own
|
|
385
|
-
derivation there. js_nullable for the same JSON-safety reason as `lifecycleField`.
|
|
386
|
-
*/
|
|
194
|
+
/** The singular counterpart of `queryField` (`Plugin_Order`), also the prefix of
|
|
195
|
+
the generated input types. Not derivable without `Api_Naming.singularize`. */
|
|
387
196
|
singleQueryField: @s.matches(stringOptionSchema) option<string>,
|
|
388
|
-
/**
|
|
389
|
-
|
|
390
|
-
a reference to some other entity. `Products` carries `productId` and
|
|
391
|
-
`categoryId`; this says which of the two the row is about.
|
|
392
|
-
|
|
393
|
-
`None` means unresolved: a state with several `*Id` fields and no name match,
|
|
394
|
-
or with none at all. Such a component gets no key-derived filter or sort until
|
|
395
|
-
its spec declares `@id`. Also `None` on defs persisted before this field
|
|
396
|
-
existed. js_nullable for the same JSON-safety reason as `lifecycleField`.
|
|
397
|
-
*/
|
|
197
|
+
/** The state field identifying a row, as opposed to a reference to another entity.
|
|
198
|
+
`None` means unresolved — no key-derived filter or sort until `@id` is declared. */
|
|
398
199
|
idField: @s.matches(stringOptionSchema) option<string>,
|
|
399
|
-
/**
|
|
400
|
-
|
|
401
|
-
guess — the same reason `labelFieldSource` exists:
|
|
402
|
-
|
|
403
|
-
- `"annotation"` — the state declares `@id`. The author said which field keys
|
|
404
|
-
the row; nothing inferred outranks it.
|
|
405
|
-
- `"convention"` — a field named `<singular component name>Id` exists
|
|
406
|
-
(`Products` → `productId`). A guess, and the one guess a client can make for
|
|
407
|
-
itself.
|
|
408
|
-
- `"sole"` — the state has exactly one `*Id` field, so there is nothing else
|
|
409
|
-
the key could be (`AvailableProducts` → `productId`). A guess, and one that
|
|
410
|
-
needs the state's full field list to make.
|
|
411
|
-
|
|
412
|
-
`None` whenever `idField` is `None`, and on defs that predate the field.
|
|
413
|
-
*/
|
|
200
|
+
/** Which rung produced `idField`, as `labelFieldSource` does: `"annotation"` |
|
|
201
|
+
`"convention"` | `"sole"`. */
|
|
414
202
|
idFieldSource: @s.matches(stringOptionSchema) option<string>,
|
|
415
|
-
/**
|
|
416
|
-
|
|
417
|
-
derived from the component's module-level authorization rule. Same terms as
|
|
418
|
-
`commandDef.requiredAccess`: a hint that keeps a client from advertising a
|
|
419
|
-
surface the server would refuse, never the refusal itself.
|
|
420
|
-
|
|
421
|
-
Worth stating for reads in particular: a denied query does not error, it comes
|
|
422
|
-
back empty, so a client that offers a view it may not read renders a confident
|
|
423
|
-
blank table rather than a visible failure.
|
|
424
|
-
*/
|
|
203
|
+
/** Access keys — any one of them — a caller needs to be *offered* this view. A
|
|
204
|
+
denied read comes back empty rather than erroring, hence the hint. */
|
|
425
205
|
requiredAccess: @s.matches(stringArrayOptionSchema) option<array<string>>,
|
|
426
206
|
}
|
|
427
207
|
|
|
428
|
-
/**
|
|
429
|
-
|
|
430
|
-
but for the past-tense facts a write side produces: `name` is the event variant
|
|
431
|
-
name (e.g. `OrderPlaced`), `schema` is the JSON Schema of that variant's payload
|
|
432
|
-
(same serialization as `commandDef.schema`: `SuryToJsonSchema.deriveObjectSchema`,
|
|
433
|
-
so field-level `x-reventless-*` extensions are carried and the variant's `TAG`
|
|
434
|
-
discriminator is not — the constructor name is already `name`), `references` its
|
|
435
|
-
cross-entity field links.
|
|
436
|
-
Carried so developer tools (the `reventless-dev` / VSCode domain graph) can show
|
|
437
|
-
event field rows — AutoUI ignores it. */
|
|
208
|
+
/** One emitted event of a write side. `name` is the variant name, `schema` its
|
|
209
|
+
payload's JSON Schema (no `TAG` — the constructor name is already `name`). */
|
|
438
210
|
@schema
|
|
439
211
|
type eventDef = {
|
|
440
212
|
name: string,
|
|
@@ -442,17 +214,8 @@ type eventDef = {
|
|
|
442
214
|
references: array<fieldReference>,
|
|
443
215
|
}
|
|
444
216
|
|
|
445
|
-
/**
|
|
446
|
-
|
|
447
|
-
derivation as `eventDef` — a refusal is a variant of `Spec.errorSchema` exactly as
|
|
448
|
-
an emitted fact is a variant of `Spec.eventSchema`, so a consumer reading an
|
|
449
|
-
error's payload walks it with the code path it already uses for an event. `name`
|
|
450
|
-
is the variant name (e.g. `CategoryNotFound`) — the same string the runtime puts
|
|
451
|
-
on `errorCode` when a decision is rejected (see `CommandTopic_Helpers`), so a
|
|
452
|
-
caller can match what it reads here against what it receives. Payload-less
|
|
453
|
-
variants (the common case for errors) carry an empty `schema` object and no
|
|
454
|
-
references.
|
|
455
|
-
*/
|
|
217
|
+
/** One declared error of a write side, derived exactly as `eventDef`. `name` is the
|
|
218
|
+
string the runtime puts on `errorCode`; payload-less variants carry `{}`. */
|
|
456
219
|
@schema
|
|
457
220
|
type errorDef = {
|
|
458
221
|
name: string,
|
|
@@ -468,23 +231,11 @@ type writableDef = {
|
|
|
468
231
|
consumedEventTypes: array<string>,
|
|
469
232
|
linkedViews: array<string>,
|
|
470
233
|
consistencyRead: @s.matches(stringOptionSchema) option<string>,
|
|
471
|
-
/** Emitted-event field schemas
|
|
472
|
-
when there are none. */
|
|
234
|
+
/** Emitted-event field schemas; `[]` when there are none. */
|
|
473
235
|
events: array<eventDef>,
|
|
474
|
-
/** Declared-error field schemas
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
serving path reads it as raw JSON), so `[]` honestly means "declares no errors"
|
|
478
|
-
rather than "an older deploy could not say".
|
|
479
|
-
|
|
480
|
-
Being required does NOT make it safe to add such a field without a read-path
|
|
481
|
-
shim. A structure is re-derived on every build, but it is only RE-REGISTERED
|
|
482
|
-
when a plugin re-runs the connect handshake — which a plugin whose version never
|
|
483
|
-
changes may not do for a long time. Until then the serving path reads a
|
|
484
|
-
persisted structure that has no key for the new field, and against a `[T!]!` SDL
|
|
485
|
-
field that null propagates to the root and answers the whole query with `data:
|
|
486
|
-
null`. Both admin resolvers therefore heal absent required lists to `[]` on
|
|
487
|
-
read; a new one has to be added to that list too. */
|
|
236
|
+
/** Declared-error field schemas. Required, but a persisted structure predating a
|
|
237
|
+
required list still lacks the key — both admin resolvers heal absent ones to
|
|
238
|
+
`[]` on read, and a new one must be added there too. */
|
|
488
239
|
errors: array<errorDef>,
|
|
489
240
|
/** Chapter grouping band — see `queryableDef.chapter`. */
|
|
490
241
|
chapter: @s.matches(stringOptionSchema) option<string>,
|
|
@@ -523,37 +274,72 @@ type inboundTranslationSliceDef = {
|
|
|
523
274
|
chapter: @s.matches(stringOptionSchema) option<string>,
|
|
524
275
|
}
|
|
525
276
|
|
|
277
|
+
/** A published event of an extension point and the internal events producing it.
|
|
278
|
+
`name` is EP-qualified, `fromEventTypes` plugin-qualified; `[]` means published
|
|
279
|
+
from a path the declaration cannot name. */
|
|
280
|
+
@schema
|
|
281
|
+
type publishedEventDef = {
|
|
282
|
+
name: string,
|
|
283
|
+
fromEventTypes: array<string>,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let publishedEventDefArrayOptionSchema = S.array(publishedEventDefSchema)->S.nullAsOption
|
|
287
|
+
|
|
288
|
+
/** The command direction's producer half: a command an extension point takes and
|
|
289
|
+
the delegate commands it routes to. `name` is EP-qualified, `toCommandTypes`
|
|
290
|
+
plugin-qualified. */
|
|
291
|
+
@schema
|
|
292
|
+
type acceptedCommandDef = {
|
|
293
|
+
name: string,
|
|
294
|
+
toCommandTypes: array<string>,
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let acceptedCommandDefArrayOptionSchema = S.array(acceptedCommandDefSchema)->S.nullAsOption
|
|
298
|
+
|
|
299
|
+
/** The subscriber's half: a published event and the commands it routes to. A
|
|
300
|
+
delegate command is plugin-qualified, one sent back to the EP is EP-qualified. */
|
|
301
|
+
@schema
|
|
302
|
+
type handledEventDef = {
|
|
303
|
+
name: string,
|
|
304
|
+
toCommandTypes: array<string>,
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let handledEventDefArrayOptionSchema = S.array(handledEventDefSchema)->S.nullAsOption
|
|
308
|
+
|
|
309
|
+
/** The command direction's subscriber half: a command sent back to the port and
|
|
310
|
+
the internal events producing it. `name` is EP-qualified, `fromEventTypes`
|
|
311
|
+
plugin-qualified. */
|
|
312
|
+
@schema
|
|
313
|
+
type issuedCommandDef = {
|
|
314
|
+
name: string,
|
|
315
|
+
fromEventTypes: array<string>,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
let issuedCommandDefArrayOptionSchema = S.array(issuedCommandDefSchema)->S.nullAsOption
|
|
319
|
+
|
|
526
320
|
@schema
|
|
527
321
|
type extensionDef = {
|
|
528
322
|
name: string,
|
|
529
323
|
delegateNames: array<string>,
|
|
530
324
|
eventTypes: array<string>,
|
|
531
325
|
commandTypes: array<string>,
|
|
326
|
+
/** Which published event routes to which commands. js_nullable like
|
|
327
|
+
`extensionPointDef.commandTypes`; re-emit definitions persisted before it. */
|
|
328
|
+
handledEvents: @s.matches(handledEventDefArrayOptionSchema) option<array<handledEventDef>>,
|
|
329
|
+
/** Which internal event sends which command back to the port. `None` means a
|
|
330
|
+
definition persisted before the field, NOT an extension that issues nothing —
|
|
331
|
+
a reader joining the two halves must keep them apart. */
|
|
332
|
+
issuedCommands: @s.matches(issuedCommandDefArrayOptionSchema) option<array<issuedCommandDef>>,
|
|
532
333
|
}
|
|
533
334
|
|
|
534
335
|
/**
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
`sourceEventTypes` are the
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
`commandTypes` are the EP's *inbound* command protocol (the variants of its
|
|
544
|
-
`command` type). It is empty (None, read as []) when the EP declares
|
|
545
|
-
`command = unit` — a notification-only, events-out boundary that accepts nothing
|
|
546
|
-
inward. The event graph uses this to decide whether the EP routes any command (an
|
|
547
|
-
empty list means no `routesTo` edge: there is nothing for the EP to route).
|
|
548
|
-
|
|
549
|
-
Optional (None for a `command = unit` EP). Uses the `js_nullable` pattern (T | null).
|
|
550
|
-
A sury field cannot be BOTH absent-tolerant on decode AND JSON-encodable (proven:
|
|
551
|
-
S.option = `T|undefined`, nullableAsOption = `T|undefined|null` both decode an absent
|
|
552
|
-
key but fail jsonableValidation; js_nullable = `T|null` is the only JSON-safe form but
|
|
553
|
-
rejects an absent key). This def is nested in the JSON-encoded lifecycle Message union
|
|
554
|
-
(Connect/Heartbeat), so jsonability wins → js_nullable. It always writes the field
|
|
555
|
-
(None → null), so it is present-required on decode; a plugin definition persisted
|
|
556
|
-
before this field existed must be reset/re-emitted. Read with `->Option.getOr([])`.
|
|
336
|
+
An extension point owned by a plugin, from the producer side.
|
|
337
|
+
|
|
338
|
+
`sourceEventTypes` are the `Delegate`'s events feeding the published protocol,
|
|
339
|
+
plugin-qualified to match `writableDef.producedEventTypes`. `commandTypes` is the
|
|
340
|
+
EP's inbound protocol — None (read as []) for a `command = unit` EP, which routes
|
|
341
|
+
nothing. js_nullable is the only JSON-safe optional here (this def is nested in the
|
|
342
|
+
lifecycle Message union); definitions persisted before a field must be re-emitted.
|
|
557
343
|
*/
|
|
558
344
|
@schema
|
|
559
345
|
type extensionPointDef = {
|
|
@@ -561,6 +347,13 @@ type extensionPointDef = {
|
|
|
561
347
|
delegateNames: array<string>,
|
|
562
348
|
sourceEventTypes: array<string>,
|
|
563
349
|
commandTypes: @s.matches(stringArrayOptionSchema) option<array<string>>,
|
|
350
|
+
/** Which internal event becomes which published event. */
|
|
351
|
+
publishedEvents: @s.matches(publishedEventDefArrayOptionSchema)
|
|
352
|
+
option<array<publishedEventDef>>,
|
|
353
|
+
/** Which arriving command becomes which delegate command. `None` means a
|
|
354
|
+
definition persisted before the field, NOT a port that accepts nothing. */
|
|
355
|
+
acceptedCommands: @s.matches(acceptedCommandDefArrayOptionSchema)
|
|
356
|
+
option<array<acceptedCommandDef>>,
|
|
564
357
|
}
|
|
565
358
|
|
|
566
359
|
// js_nullable creates `array | null` (not `| undefined`), which passes sury's
|
|
@@ -570,30 +363,11 @@ let extensionPointDefArrayOptionSchema = S.array(extensionPointDefSchema)->S.nul
|
|
|
570
363
|
/**
|
|
571
364
|
One field's store requirement, with its provenance.
|
|
572
365
|
|
|
573
|
-
`store` is the
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
`annotation` is the store exactly as the field spells it — bare for a store
|
|
579
|
-
the declaring plugin owns, qualified for a foreign one. It is recorded rather
|
|
580
|
-
than reconstructed: only here is the owning plugin unambiguous, so anything
|
|
581
|
-
downstream would have to infer it by comparing a registered plugin name with
|
|
582
|
-
whatever name a deploy manifest happened to use, and those were never required
|
|
583
|
-
to match.
|
|
584
|
-
|
|
585
|
-
Optional for the same reason `CapabilityManifest.provenance` and
|
|
586
|
-
`PlatformCodegen.provenance` — the two places this value travels onward to —
|
|
587
|
-
already declare it optional: an event stored before the field existed cannot
|
|
588
|
-
say what the source said, and a reader that cannot say omits the claim rather
|
|
589
|
-
than inventing one. Every definition emitted now carries it.
|
|
590
|
-
|
|
591
|
-
That is not a stylistic preference. It was first added here as a required
|
|
592
|
-
`string` while events written without it were already stored, and since the
|
|
593
|
-
lifecycle aggregate replays its own log before every decision, those events
|
|
594
|
-
stopped decoding and the plugin's registration froze for two days. `None` is
|
|
595
|
-
also the honest value: `""` would assert the author wrote an empty annotation.
|
|
596
|
-
See the schema-evolution note on `pluginStructure` below.
|
|
366
|
+
`store` is the qualified `{plugin}.{store}` string `requiredStores` carries;
|
|
367
|
+
`component` and `field` name the declaration site, so a diff can say which field
|
|
368
|
+
added or removed a store. `annotation` is the store as the field spells it —
|
|
369
|
+
recorded, not reconstructed, since only here is the owning plugin unambiguous.
|
|
370
|
+
Optional because an event stored before it cannot say (`""` would claim it did).
|
|
597
371
|
*/
|
|
598
372
|
@schema
|
|
599
373
|
type requiredStoreDeclaration = {
|
|
@@ -607,30 +381,53 @@ let requiredStoreDeclarationArrayOptionSchema =
|
|
|
607
381
|
S.array(requiredStoreDeclarationSchema)->S.nullAsOption
|
|
608
382
|
|
|
609
383
|
/**
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
384
|
+
One component's capability requirement, with its provenance.
|
|
385
|
+
|
|
386
|
+
`capability` is `CapabilityNeed.toString` — a string rather than an enum so a
|
|
387
|
+
plugin built against a newer framework still decodes here; `component` names the
|
|
388
|
+
slice that declared it, so a diff can say which component added or removed the
|
|
389
|
+
need. Unlike a store there is no field: what a `translate` reaches for is not
|
|
390
|
+
expressible as an annotation on one, which is why the need is declared.
|
|
391
|
+
*/
|
|
392
|
+
@schema
|
|
393
|
+
type requiredCapabilityDeclaration = {capability: string, component: string}
|
|
394
|
+
|
|
395
|
+
let requiredCapabilityDeclarationArrayOptionSchema =
|
|
396
|
+
S.array(requiredCapabilityDeclarationSchema)->S.nullAsOption
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
One graft's provenance: which trait, at which version, on which component.
|
|
400
|
+
|
|
401
|
+
Strings rather than the `Trait.t` variant for `posture`, on the same rule the
|
|
402
|
+
capability above follows — a plugin built against a newer framework, naming a
|
|
403
|
+
posture this one has never heard of, still decodes here rather than failing the
|
|
404
|
+
whole structure.
|
|
405
|
+
|
|
406
|
+
`component` is not declared by the trait or by the host: the structure fills it in
|
|
407
|
+
while it walks the components, because it is the only party that knows which one
|
|
408
|
+
carried the declaration. Nothing in this record is a string a developer typed.
|
|
409
|
+
|
|
410
|
+
It records ORIGIN, not behaviour — a grafted file is the host's to edit
|
|
411
|
+
afterwards. What answers "does it still behave like the trait" is the trait's own
|
|
412
|
+
conformance suite, which runs in the consumer's build and is not this field.
|
|
413
|
+
*/
|
|
414
|
+
@schema
|
|
415
|
+
type traitDeclaration = {
|
|
416
|
+
trait: string,
|
|
417
|
+
version: string,
|
|
418
|
+
posture: string,
|
|
419
|
+
component: string,
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
let traitDeclarationArrayOptionSchema = S.array(traitDeclarationSchema)->S.nullAsOption
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
Adding a field here? It must be a shape a stale event can be healed into — the
|
|
426
|
+
lifecycle aggregate replays its own log before every decision, so one event that
|
|
427
|
+
fails to decode freezes that plugin's registration. `Message.parseJsonTolerant`
|
|
428
|
+
heals `T | null`, arrays, enums and nested objects; a bare scalar is fabricated
|
|
429
|
+
and warned about. Prefer `js_nullable`. Regression suite:
|
|
430
|
+
`PluginLifecycleCorpusTest` — if it goes red, re-shape the field, not the fixtures.
|
|
634
431
|
*/
|
|
635
432
|
@schema
|
|
636
433
|
type pluginStructure = {
|
|
@@ -642,34 +439,29 @@ type pluginStructure = {
|
|
|
642
439
|
outboundTranslationSlices: array<outboundTranslationSliceDef>,
|
|
643
440
|
inboundTranslationSlices: array<inboundTranslationSliceDef>,
|
|
644
441
|
extensions: array<extensionDef>,
|
|
645
|
-
// Extension points owned by this plugin (producer side). Optional so
|
|
646
|
-
// definitions
|
|
647
|
-
// read as []). js_nullable keeps it JSON-safe inside union variant payloads.
|
|
442
|
+
// Extension points owned by this plugin (producer side). Optional so older
|
|
443
|
+
// definitions still decode (absent → None, read as []).
|
|
648
444
|
extensionPoints: @s.matches(extensionPointDefArrayOptionSchema)
|
|
649
445
|
option<array<extensionPointDef>>,
|
|
650
|
-
/**
|
|
651
|
-
|
|
652
|
-
fully qualified as `{plugin}.{store}`.
|
|
653
|
-
|
|
654
|
-
A field typed as a storage ref states a *requirement*: the deployment needs
|
|
655
|
-
that store to exist. Collecting the requirement here is what lets it be read
|
|
656
|
-
without re-walking every component's schema — the same reason
|
|
657
|
-
`producedEventTypes` is carried rather than recomputed.
|
|
658
|
-
|
|
659
|
-
Qualified even for the common same-plugin case, so one entry has one shape
|
|
660
|
-
and the string is directly the store's identity. Optional and js_nullable for
|
|
661
|
-
the same reason as `extensionPoints`: definitions persisted before this field
|
|
662
|
-
existed still decode (absent → None, read as []).
|
|
663
|
-
*/
|
|
446
|
+
/** The object stores this plugin's fields declare they need, deduplicated and
|
|
447
|
+
qualified as `{plugin}.{store}` even for the same-plugin case. */
|
|
664
448
|
requiredStores: @s.matches(stringArrayOptionSchema) option<array<string>>,
|
|
665
|
-
/**
|
|
666
|
-
|
|
667
|
-
site, with `store` matching the qualified key above. `requiredStores` is
|
|
668
|
-
derived from this list, so the two cannot disagree. Optional and js_nullable
|
|
669
|
-
for the same reason as `extensionPoints`.
|
|
670
|
-
*/
|
|
449
|
+
/** Provenance for `requiredStores`: one entry per declaring `(component, field)`.
|
|
450
|
+
`requiredStores` is derived from it, so the two cannot disagree. */
|
|
671
451
|
requiredStoreDeclarations: @s.matches(requiredStoreDeclarationArrayOptionSchema)
|
|
672
452
|
option<array<requiredStoreDeclaration>>,
|
|
453
|
+
/** The platform capabilities this plugin's components declare they need, one
|
|
454
|
+
entry per declaring component. Object stores are not here — a store need is
|
|
455
|
+
a field's, and travels as `requiredStores`. Absent → None, read as []. */
|
|
456
|
+
requiredCapabilities: @s.matches(requiredCapabilityDeclarationArrayOptionSchema)
|
|
457
|
+
option<array<requiredCapabilityDeclaration>>,
|
|
458
|
+
/** The domain traits grafted into this plugin, one entry per declaring
|
|
459
|
+
component. Absent → None, read as []. The only signal a graft leaves that
|
|
460
|
+
survives into a deployed plugin — every other one (the dependency, the
|
|
461
|
+
variant spread, the rules alias, the conformance binding) is source-side.
|
|
462
|
+
A claim about origin, never about behaviour: see `Trait`. */
|
|
463
|
+
traitDeclarations: @s.matches(traitDeclarationArrayOptionSchema)
|
|
464
|
+
option<array<traitDeclaration>>,
|
|
673
465
|
}
|
|
674
466
|
|
|
675
467
|
let pluginStructureOffloadSchema = Offload.optionSchema(~store="pluginStructures", pluginStructureSchema)
|
|
@@ -689,34 +481,22 @@ type pluginDefinition = {
|
|
|
689
481
|
extensionPoints: array<extensionPointDefinition>,
|
|
690
482
|
extensions: array<extensionDefinition>,
|
|
691
483
|
mutable eventCollector: string,
|
|
692
|
-
//
|
|
693
|
-
// Use [] when the plugin does not need version negotiation.
|
|
484
|
+
// [] when the plugin does not need version negotiation.
|
|
694
485
|
extensionProtocols: array<extensionProtocol>,
|
|
695
|
-
// GraphQL schema fragment contributed by this plugin (optional, set at build time).
|
|
696
486
|
// Offloadable: a large SDL fragment is content-addressed to the pluginApiFragments
|
|
697
|
-
// store
|
|
698
|
-
// wraps the untagged codec in js_nullable (T | null, not T | undefined | null) so it
|
|
699
|
-
// passes jsonableValidation inside the lifecycle Message union, and marks the store.
|
|
487
|
+
// store and carried by reference; a small one stays Inline.
|
|
700
488
|
apiSchemaFragment: @s.matches(apiSchemaFragmentOffloadSchema) option<Offload.payload<apiSchemaFragment>>,
|
|
701
|
-
//
|
|
702
|
-
//
|
|
703
|
-
// Some("Platform") → fragment goes to the PlatformApi; excluded from DomainApi runtime schema.
|
|
704
|
-
// Uses @s.matches(stringOptionSchema) — js_nullable creates string | null (not string | undefined),
|
|
705
|
-
// which passes sury's jsonableValidation inside union variant payloads.
|
|
489
|
+
// Schema routing in split-API mode: None/"Domain" → DomainApi, Some("Platform") →
|
|
490
|
+
// PlatformApi (and excluded from the DomainApi runtime schema).
|
|
706
491
|
apiTarget: @s.matches(stringOptionSchema) option<string>,
|
|
707
|
-
// Component graph metadata
|
|
708
|
-
//
|
|
709
|
-
// the client and carried by reference; a small one stays Inline (see apiSchemaFragment).
|
|
492
|
+
// Component graph metadata, offloadable like apiSchemaFragment. Absent for older
|
|
493
|
+
// protocol versions.
|
|
710
494
|
structure: @s.matches(pluginStructureOffloadSchema) option<Offload.payload<pluginStructure>>,
|
|
711
|
-
//
|
|
712
|
-
//
|
|
713
|
-
// subscriptions from this plugin's DCB topic → peer EventCollectors.
|
|
714
|
-
// None for plugins without a DCB EventLog.
|
|
495
|
+
// EventTopic ARN of a bundled DcbEventLog, so the admin can subscribe peer
|
|
496
|
+
// EventCollectors to it. None for plugins without one.
|
|
715
497
|
dcbEventLog: @s.matches(dcbEventLogOptionSchema) option<dcbEventLogDefinition>,
|
|
716
|
-
//
|
|
717
|
-
//
|
|
718
|
-
// plugins are segregated out of the admin Plugins list. Payload-less variant → serialises
|
|
719
|
-
// as a bare JSON string, so it is JSON-safe inside the lifecycle Message union without js_nullable.
|
|
498
|
+
// Mandatory; `Domain` is resolved as the default in Plugin_Builder. Payload-less
|
|
499
|
+
// variant → a bare JSON string, so JSON-safe without js_nullable.
|
|
720
500
|
kind: pluginKind,
|
|
721
501
|
}
|
|
722
502
|
|