@reventlessdev/reventless-spec 3.0.0-alpha.136 → 3.0.0-alpha.138
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 +24 -0
- package/package.json +1 -1
- package/src/components/DcbScopeInference.res +16 -2
- package/src/components/DcbTag.res +26 -13
- package/src/components/DcbTag.res.mjs +22 -17
- package/src/components/DcbValidation.res +51 -18
- package/src/components/DcbValidation.res.mjs +16 -0
- package/src/semantic/Semantic.res +44 -0
- package/src/semantic/Semantic.res.mjs +100 -12
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,30 @@
|
|
|
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.138 (2026-09-20)
|
|
7
|
+
|
|
8
|
+
* fix(spec)!: let an explicit partition tag declare an identity the name cannot ([01d82c0](https://github.com/ReventlessDev/reventless-core/commit/01d82c0714a31f31d55c466eae9100cf4dcc85f0))
|
|
9
|
+
|
|
10
|
+
### BREAKING CHANGES
|
|
11
|
+
|
|
12
|
+
* a @partitionTag on a field whose name is not *Id / *Ids was
|
|
13
|
+
inert and now takes effect, so such a slice's partition key — and with it its
|
|
14
|
+
storage key, fence and read scope — changes to the annotated field. Nothing else
|
|
15
|
+
moves: the annotation was doing nothing before, so no slice that resolved a key
|
|
16
|
+
resolves a different one.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# 3.0.0-alpha.137 (2026-09-20)
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* **spec:** resolve the drift guard's paths from the module, not the cwd ([7f4d8de](https://github.com/ReventlessDev/reventless-core/commit/7f4d8de896f37ce4a855ecc6a980788f01d0e1c7))
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* **spec:** enumerate the transparent-string semantics, and check the list against the sources ([995c31c](https://github.com/ReventlessDev/reventless-core/commit/995c31c5eb1a9bdb2ad88d39b2b9a8a5348be0da))
|
|
28
|
+
|
|
29
|
+
|
|
6
30
|
# 3.0.0-alpha.136 (2026-09-16)
|
|
7
31
|
|
|
8
32
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -34,8 +34,22 @@ The three rules (over the representation):
|
|
|
34
34
|
on `ProductAdded`) are payload ⇒ not indexed ⇒ the sibling-leak GSI write never
|
|
35
35
|
happens.
|
|
36
36
|
*/
|
|
37
|
-
/** A
|
|
38
|
-
|
|
37
|
+
/** A field the slice graph treats as an entity identity.
|
|
38
|
+
|
|
39
|
+
Normally that is a `*Id` / `*Ids`-shaped name — the convention is the signal,
|
|
40
|
+
and this module stays schema-agnostic by taking the name alone.
|
|
41
|
+
|
|
42
|
+
`byTag` marks the exception: an identity the *name* does not declare, which an
|
|
43
|
+
adapter recognised from an explicit `@partitionTag`. A domain's own identifier
|
|
44
|
+
is often not suffixed — `sku`, `isbn`, `vin` — and without this the annotation
|
|
45
|
+
naming one would be extracted as a hint and then dropped, because `seedOf`
|
|
46
|
+
only honours a hint already among the produced keys. So the escape hatch would
|
|
47
|
+
have worked for every field except the ones that need it.
|
|
48
|
+
|
|
49
|
+
It is carried rather than folded in because removing the annotation removes
|
|
50
|
+
the identity, which is exactly what the redundancy check has to know: a hint
|
|
51
|
+
inference cannot reach without it is never redundant. */
|
|
52
|
+
type idField = {name: string, isList: bool, byTag?: bool}
|
|
39
53
|
|
|
40
54
|
/** One variant arm: its constructor name and the `*Id` fields it carries. */
|
|
41
55
|
type eventShape = {eventType: string, idFields: array<idField>}
|
|
@@ -1084,29 +1084,42 @@ which is this module's half of the split.
|
|
|
1084
1084
|
*/
|
|
1085
1085
|
let idFieldsOfProperties = (properties: dict<S.t<unknown>>): array<DcbScopeInference.idField> => {
|
|
1086
1086
|
let isIdName = (name: string) => name->String.endsWith("Ids") || name->String.endsWith("Id")
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1087
|
+
// An explicit `@partitionTag` declares an identity the naming convention cannot
|
|
1088
|
+
// express — a domain's own identifier is often unsuffixed (`sku`, `isbn`).
|
|
1089
|
+
// Without this the hint is extracted and then ignored, since `seedOf` honours
|
|
1090
|
+
// only a hint already among the produced keys, so the escape hatch would work
|
|
1091
|
+
// for every field except the ones that need it. Flagged, not folded in: taking
|
|
1092
|
+
// the annotation away takes the identity with it, which is what the redundancy
|
|
1093
|
+
// check has to be able to tell.
|
|
1094
|
+
let identity = (name, fieldSchema, isList) =>
|
|
1090
1095
|
if isIdName(name) {
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
}
|
|
1095
|
-
[{DcbScopeInference.name, isList}]
|
|
1096
|
+
Some({DcbScopeInference.name, isList})
|
|
1097
|
+
} else if isPartitionTag(fieldSchema) {
|
|
1098
|
+
Some({DcbScopeInference.name, isList, byTag: true})
|
|
1096
1099
|
} else {
|
|
1100
|
+
None
|
|
1101
|
+
}
|
|
1102
|
+
properties
|
|
1103
|
+
->Dict.toArray
|
|
1104
|
+
->Array.flatMap(((name, fieldSchema)) => {
|
|
1105
|
+
let isList = switch fieldSchema {
|
|
1106
|
+
| Array(_) => true
|
|
1107
|
+
| _ => false
|
|
1108
|
+
}
|
|
1109
|
+
switch identity(name, fieldSchema, isList) {
|
|
1110
|
+
| Some(f) => [f]
|
|
1111
|
+
| None =>
|
|
1097
1112
|
switch nestedRecordProperties(fieldSchema) {
|
|
1098
1113
|
| Some((nested, nestedIsList)) =>
|
|
1099
1114
|
nested
|
|
1100
1115
|
->Dict.toArray
|
|
1101
|
-
->Array.filterMap(((nestedName,
|
|
1102
|
-
|
|
1103
|
-
? Some({DcbScopeInference.name: nestedName, isList: nestedIsList})
|
|
1104
|
-
: None
|
|
1116
|
+
->Array.filterMap(((nestedName, nestedSchema)) =>
|
|
1117
|
+
identity(nestedName, nestedSchema, nestedIsList)
|
|
1105
1118
|
)
|
|
1106
1119
|
| None => []
|
|
1107
1120
|
}
|
|
1108
1121
|
}
|
|
1109
|
-
)
|
|
1122
|
+
})
|
|
1110
1123
|
}
|
|
1111
1124
|
|
|
1112
1125
|
/**
|
|
@@ -703,31 +703,36 @@ function idFieldsOfProperties(properties) {
|
|
|
703
703
|
return name.endsWith("Id");
|
|
704
704
|
}
|
|
705
705
|
};
|
|
706
|
+
let identity = (name, fieldSchema, isList) => {
|
|
707
|
+
if (isIdName(name)) {
|
|
708
|
+
return {
|
|
709
|
+
name: name,
|
|
710
|
+
isList: isList
|
|
711
|
+
};
|
|
712
|
+
} else if (Stdlib_Option.isSome(Sury.$Metadata_get(fieldSchema, dcbPartitionTagId))) {
|
|
713
|
+
return {
|
|
714
|
+
name: name,
|
|
715
|
+
isList: isList,
|
|
716
|
+
byTag: true
|
|
717
|
+
};
|
|
718
|
+
} else {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
};
|
|
706
722
|
return Object.entries(properties).flatMap(param => {
|
|
707
723
|
let fieldSchema = param[1];
|
|
708
|
-
let
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
return [
|
|
713
|
-
name: name,
|
|
714
|
-
isList: isList
|
|
715
|
-
}];
|
|
724
|
+
let isList;
|
|
725
|
+
isList = fieldSchema.type === "array";
|
|
726
|
+
let f = identity(param[0], fieldSchema, isList);
|
|
727
|
+
if (f !== undefined) {
|
|
728
|
+
return [f];
|
|
716
729
|
}
|
|
717
730
|
let match = nestedRecordProperties(fieldSchema);
|
|
718
731
|
if (match === undefined) {
|
|
719
732
|
return [];
|
|
720
733
|
}
|
|
721
734
|
let nestedIsList = match[1];
|
|
722
|
-
return Stdlib_Array.filterMap(Object.entries(match[0]), param =>
|
|
723
|
-
let nestedName = param[0];
|
|
724
|
-
if (isIdName(nestedName)) {
|
|
725
|
-
return {
|
|
726
|
-
name: nestedName,
|
|
727
|
-
isList: nestedIsList
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
});
|
|
735
|
+
return Stdlib_Array.filterMap(Object.entries(match[0]), param => identity(param[0], param[1], nestedIsList));
|
|
731
736
|
});
|
|
732
737
|
}
|
|
733
738
|
|
|
@@ -501,29 +501,62 @@ let validatePartitionHintsVsInference = (
|
|
|
501
501
|
shapes->Array.forEach(s =>
|
|
502
502
|
switch s.partitionHint {
|
|
503
503
|
| Some(hint) if DcbScopeInference.producedKeys(s)->Array.includes(hint) =>
|
|
504
|
+
// "Without the annotation" has to mean without everything the annotation
|
|
505
|
+
// brought. A field that is an identity only because it carries the tag
|
|
506
|
+
// (`byTag`) stops being one when the tag goes, so dropping only the hint
|
|
507
|
+
// would ask whether inference reaches a key it can no longer see — and
|
|
508
|
+
// answer yes, reporting a load-bearing annotation as removable.
|
|
509
|
+
let withoutTag = (e: DcbScopeInference.eventShape) => {
|
|
510
|
+
...e,
|
|
511
|
+
idFields: e.idFields->Array.filter(f => f.byTag != Some(true)),
|
|
512
|
+
}
|
|
513
|
+
// A hint naming an identity that exists only because of the annotation is
|
|
514
|
+
// **necessary**, and fits neither verdict this check was built to give.
|
|
515
|
+
// Called redundant it would be removed and the key would vanish; called
|
|
516
|
+
// contradictory it would be "corrected" to whatever name-shaped field
|
|
517
|
+
// happens to sit beside it — which is the author's choice overruled, not a
|
|
518
|
+
// mistake found. Both readings come from assuming the field is an identity
|
|
519
|
+
// either way, which is true only when the name says so.
|
|
520
|
+
let declaredByTag =
|
|
521
|
+
s.produced->Array.some(e =>
|
|
522
|
+
e.idFields->Array.some(f => f.name == hint && f.byTag == Some(true))
|
|
523
|
+
)
|
|
504
524
|
let unaided = DcbScopeInference.resolvePartitions(
|
|
505
|
-
shapes->Array.map(o =>
|
|
525
|
+
shapes->Array.map(o =>
|
|
526
|
+
o.sliceName == s.sliceName
|
|
527
|
+
? {
|
|
528
|
+
...o,
|
|
529
|
+
partitionHint: None,
|
|
530
|
+
produced: o.produced->Array.map(withoutTag),
|
|
531
|
+
consumed: o.consumed->Array.map(withoutTag),
|
|
532
|
+
}
|
|
533
|
+
: o
|
|
534
|
+
),
|
|
506
535
|
)
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
})
|
|
518
|
-
| None =>
|
|
519
|
-
let candidates = unaided.candidatesBySlice->Dict.get(s.sliceName)->Option.getOr([])
|
|
520
|
-
if !(candidates->Array.includes(hint)) {
|
|
536
|
+
if declaredByTag {
|
|
537
|
+
()
|
|
538
|
+
} else {
|
|
539
|
+
switch unaided.partitionBySlice->Dict.get(s.sliceName) {
|
|
540
|
+
| Some(inferred) if inferred == hint =>
|
|
541
|
+
redundancies->Array.push({
|
|
542
|
+
sliceName: s.sliceName,
|
|
543
|
+
message: `@partitionTag ${hint} is what inference derives without it — the annotation is redundant and can be removed.`,
|
|
544
|
+
})
|
|
545
|
+
| Some(inferred) =>
|
|
521
546
|
contradictions->Array.push({
|
|
522
547
|
sliceName: s.sliceName,
|
|
523
|
-
message: `@partitionTag names ${hint},
|
|
524
|
-
", ",
|
|
525
|
-
)}). Move the annotation to the slice's own key.`,
|
|
548
|
+
message: `@partitionTag names ${hint}, but inference derives ${inferred} from the slice graph — ${hint} is read from another entity. Remove the annotation, or move it to ${inferred}.`,
|
|
526
549
|
})
|
|
550
|
+
| None =>
|
|
551
|
+
let candidates = unaided.candidatesBySlice->Dict.get(s.sliceName)->Option.getOr([])
|
|
552
|
+
if !(candidates->Array.includes(hint)) {
|
|
553
|
+
contradictions->Array.push({
|
|
554
|
+
sliceName: s.sliceName,
|
|
555
|
+
message: `@partitionTag names ${hint}, which this slice only reads as a reference to another entity (candidates: ${candidates->Array.join(
|
|
556
|
+
", ",
|
|
557
|
+
)}). Move the annotation to the slice's own key.`,
|
|
558
|
+
})
|
|
559
|
+
}
|
|
527
560
|
}
|
|
528
561
|
}
|
|
529
562
|
| _ => ()
|
|
@@ -437,14 +437,30 @@ function validatePartitionHintsVsInference(shapes) {
|
|
|
437
437
|
if (!DcbScopeInference$Reventless.producedKeys(s).includes(hint)) {
|
|
438
438
|
return;
|
|
439
439
|
}
|
|
440
|
+
let withoutTag = e => ({
|
|
441
|
+
eventType: e.eventType,
|
|
442
|
+
idFields: e.idFields.filter(f => Primitive_object.notequal(f.byTag, true))
|
|
443
|
+
});
|
|
444
|
+
let declaredByTag = s.produced.some(e => e.idFields.some(f => {
|
|
445
|
+
if (f.name === hint) {
|
|
446
|
+
return Primitive_object.equal(f.byTag, true);
|
|
447
|
+
} else {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
}));
|
|
440
451
|
let unaided = DcbScopeInference$Reventless.resolvePartitions(shapes.map(o => {
|
|
441
452
|
if (o.sliceName !== s.sliceName) {
|
|
442
453
|
return o;
|
|
443
454
|
}
|
|
444
455
|
let newrecord = {...o};
|
|
445
456
|
newrecord.partitionHint = undefined;
|
|
457
|
+
newrecord.produced = o.produced.map(withoutTag);
|
|
458
|
+
newrecord.consumed = o.consumed.map(withoutTag);
|
|
446
459
|
return newrecord;
|
|
447
460
|
}));
|
|
461
|
+
if (declaredByTag) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
448
464
|
let inferred = unaided.partitionBySlice[s.sliceName];
|
|
449
465
|
if (inferred !== undefined) {
|
|
450
466
|
if (inferred === hint) {
|
|
@@ -113,6 +113,50 @@ module Id = {
|
|
|
113
113
|
let geolocation = "geolocation"
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/** One transparent-string semantic: a `semantic/` module whose `type t` is a
|
|
117
|
+
bare `string`. */
|
|
118
|
+
type brandedString = {
|
|
119
|
+
moduleName: string,
|
|
120
|
+
/** The `Id` values of this module carry. Not derivable from the module name —
|
|
121
|
+
`CalendarDate` carries `date`. */
|
|
122
|
+
id: string,
|
|
123
|
+
/** Whether the module exposes the `let schema` that sury-ppx resolves `X.t`
|
|
124
|
+
to by convention. The `false`s build theirs from a function taking
|
|
125
|
+
arguments (`forStore` / `forField` / `forCollection`), so such a field
|
|
126
|
+
always carries an explicit `@s.matches` and there is no name a pass could
|
|
127
|
+
derive. Offering one as a plain type pick emits code that does not
|
|
128
|
+
compile. */
|
|
129
|
+
hasDerivableSchema: bool,
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Every transparent-string semantic, so a consumer can ask what they are
|
|
133
|
+
instead of transcribing the set.
|
|
134
|
+
|
|
135
|
+
A check written against the literal `string` keyword sees only the brand and
|
|
136
|
+
refuses the field, which would make declaring a semantic cost the field
|
|
137
|
+
whatever that check gates — so a pass that must leave these alone needs the
|
|
138
|
+
set, not a guess. It cannot be guessed from the names: `Money.t` is a record,
|
|
139
|
+
`Duration.t` an int, `Percent.t` and `Bytes.t` floats, and all four are
|
|
140
|
+
deliberately absent.
|
|
141
|
+
|
|
142
|
+
Both facts here are computable from the sources, and `SemanticBrandedStringsTest`
|
|
143
|
+
recomputes them rather than trusting this list — so adding a module without
|
|
144
|
+
adding it here fails, and so does the ppx's copy drifting from either. */
|
|
145
|
+
let brandedStrings: array<brandedString> = [
|
|
146
|
+
{moduleName: "DateTime", id: Id.dateTime, hasDerivableSchema: true},
|
|
147
|
+
{moduleName: "CalendarDate", id: Id.date, hasDerivableSchema: true},
|
|
148
|
+
{moduleName: "Email", id: Id.email, hasDerivableSchema: true},
|
|
149
|
+
{moduleName: "Phone", id: Id.phone, hasDerivableSchema: true},
|
|
150
|
+
{moduleName: "Url", id: Id.url, hasDerivableSchema: true},
|
|
151
|
+
{moduleName: "Color", id: Id.color, hasDerivableSchema: true},
|
|
152
|
+
{moduleName: "FileRef", id: Id.fileRef, hasDerivableSchema: true},
|
|
153
|
+
{moduleName: "ImageRef", id: Id.imageRef, hasDerivableSchema: true},
|
|
154
|
+
{moduleName: "MemberRef", id: Id.memberRef, hasDerivableSchema: false},
|
|
155
|
+
{moduleName: "StorageRef", id: Id.storageRef, hasDerivableSchema: false},
|
|
156
|
+
{moduleName: "UploadableFile", id: Id.uploadableFile, hasDerivableSchema: false},
|
|
157
|
+
{moduleName: "UploadableImage", id: Id.uploadableImage, hasDerivableSchema: false},
|
|
158
|
+
]
|
|
159
|
+
|
|
116
160
|
let semanticId: S.Metadata.Id.t<t> = S.Metadata.Id.make(~namespace="reventless", ~name="semantic")
|
|
117
161
|
|
|
118
162
|
/** Mark a schema as carrying a semantic. */
|
|
@@ -4,25 +4,49 @@ import * as S from "sury/src/S.res.mjs";
|
|
|
4
4
|
import * as Sury from "sury";
|
|
5
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
6
|
|
|
7
|
+
let dateTime = "dateTime";
|
|
8
|
+
|
|
9
|
+
let date = "date";
|
|
10
|
+
|
|
11
|
+
let storageRef = "storageRef";
|
|
12
|
+
|
|
13
|
+
let uploadableImage = "uploadableImage";
|
|
14
|
+
|
|
15
|
+
let uploadableFile = "uploadableFile";
|
|
16
|
+
|
|
17
|
+
let imageRef = "imageRef";
|
|
18
|
+
|
|
19
|
+
let fileRef = "fileRef";
|
|
20
|
+
|
|
21
|
+
let memberRef = "memberRef";
|
|
22
|
+
|
|
23
|
+
let email = "email";
|
|
24
|
+
|
|
25
|
+
let phone = "phone";
|
|
26
|
+
|
|
27
|
+
let url = "url";
|
|
28
|
+
|
|
29
|
+
let color = "color";
|
|
30
|
+
|
|
7
31
|
let Id = {
|
|
8
|
-
dateTime:
|
|
9
|
-
date:
|
|
32
|
+
dateTime: dateTime,
|
|
33
|
+
date: date,
|
|
10
34
|
reference: "reference",
|
|
11
|
-
storageRef:
|
|
35
|
+
storageRef: storageRef,
|
|
12
36
|
offload: "offload",
|
|
13
|
-
uploadableImage:
|
|
14
|
-
uploadableFile:
|
|
15
|
-
imageRef:
|
|
16
|
-
fileRef:
|
|
37
|
+
uploadableImage: uploadableImage,
|
|
38
|
+
uploadableFile: uploadableFile,
|
|
39
|
+
imageRef: imageRef,
|
|
40
|
+
fileRef: fileRef,
|
|
17
41
|
captionedImage: "captionedImage",
|
|
18
|
-
memberRef:
|
|
19
|
-
email:
|
|
20
|
-
phone:
|
|
21
|
-
url:
|
|
42
|
+
memberRef: memberRef,
|
|
43
|
+
email: email,
|
|
44
|
+
phone: phone,
|
|
45
|
+
url: url,
|
|
22
46
|
percent: "percent",
|
|
23
47
|
bytes: "bytes",
|
|
24
48
|
duration: "duration",
|
|
25
|
-
color:
|
|
49
|
+
color: color,
|
|
26
50
|
money: "money",
|
|
27
51
|
dateRange: "dateRange",
|
|
28
52
|
geoPoint: "geoPoint",
|
|
@@ -30,6 +54,69 @@ let Id = {
|
|
|
30
54
|
geolocation: "geolocation"
|
|
31
55
|
};
|
|
32
56
|
|
|
57
|
+
let brandedStrings = [
|
|
58
|
+
{
|
|
59
|
+
moduleName: "DateTime",
|
|
60
|
+
id: dateTime,
|
|
61
|
+
hasDerivableSchema: true
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
moduleName: "CalendarDate",
|
|
65
|
+
id: date,
|
|
66
|
+
hasDerivableSchema: true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
moduleName: "Email",
|
|
70
|
+
id: email,
|
|
71
|
+
hasDerivableSchema: true
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
moduleName: "Phone",
|
|
75
|
+
id: phone,
|
|
76
|
+
hasDerivableSchema: true
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
moduleName: "Url",
|
|
80
|
+
id: url,
|
|
81
|
+
hasDerivableSchema: true
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
moduleName: "Color",
|
|
85
|
+
id: color,
|
|
86
|
+
hasDerivableSchema: true
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
moduleName: "FileRef",
|
|
90
|
+
id: fileRef,
|
|
91
|
+
hasDerivableSchema: true
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
moduleName: "ImageRef",
|
|
95
|
+
id: imageRef,
|
|
96
|
+
hasDerivableSchema: true
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
moduleName: "MemberRef",
|
|
100
|
+
id: memberRef,
|
|
101
|
+
hasDerivableSchema: false
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
moduleName: "StorageRef",
|
|
105
|
+
id: storageRef,
|
|
106
|
+
hasDerivableSchema: false
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
moduleName: "UploadableFile",
|
|
110
|
+
id: uploadableFile,
|
|
111
|
+
hasDerivableSchema: false
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
moduleName: "UploadableImage",
|
|
115
|
+
id: uploadableImage,
|
|
116
|
+
hasDerivableSchema: false
|
|
117
|
+
}
|
|
118
|
+
];
|
|
119
|
+
|
|
33
120
|
let semanticId = Sury.$Metadata_Id_make("reventless", "semantic");
|
|
34
121
|
|
|
35
122
|
function mark(schema, id, payloadOpt) {
|
|
@@ -129,6 +216,7 @@ function has(fieldSchema, id) {
|
|
|
129
216
|
|
|
130
217
|
export {
|
|
131
218
|
Id,
|
|
219
|
+
brandedStrings,
|
|
132
220
|
semanticId,
|
|
133
221
|
mark,
|
|
134
222
|
refined,
|