@reventlessdev/trait-attachments 1.0.0-alpha.1
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/LICENSE +202 -0
- package/README.md +62 -0
- package/package.json +40 -0
- package/rescript.json +27 -0
- package/src/Attachments.res +66 -0
- package/src/Attachments.res.mjs +19 -0
- package/src/Attachments_Conformance.res +88 -0
- package/src/Attachments_Conformance.res.mjs +49 -0
- package/src/Attachments_Rules.res +101 -0
- package/src/Attachments_Rules.res.mjs +167 -0
- package/src/Attachments_Scaffold.res +454 -0
- package/src/Attachments_Scaffold.res.mjs +354 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Sury from "sury";
|
|
4
|
+
import * as Belt_Array from "@rescript/runtime/lib/es6/Belt_Array.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
|
|
7
|
+
let configSchema = Sury.$schema(s => ({
|
|
8
|
+
entity: s.m(Sury.string),
|
|
9
|
+
entityId: s.m(Sury.string),
|
|
10
|
+
noun: s.m(Sury.string),
|
|
11
|
+
file: s.m(Sury.string),
|
|
12
|
+
created: s.m(Sury.string),
|
|
13
|
+
createdCarriesEntityId: s.m(Sury.$option(Sury.bool)),
|
|
14
|
+
view: s.m(Sury.string),
|
|
15
|
+
refType: s.m(Sury.$option(Sury.string)),
|
|
16
|
+
authorize: s.m(Sury.$option(Sury.string)),
|
|
17
|
+
transition: s.m(Sury.$option(Sury.array(Sury.string))),
|
|
18
|
+
refA: s.m(Sury.$option(Sury.string)),
|
|
19
|
+
refB: s.m(Sury.$option(Sury.string))
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
function namesOf(c) {
|
|
23
|
+
let subject = c.entity + c.noun;
|
|
24
|
+
return {
|
|
25
|
+
slice: subject + "s",
|
|
26
|
+
attachCmd: "Attach" + subject,
|
|
27
|
+
removeCmd: "Remove" + subject,
|
|
28
|
+
setPrimaryCmd: "SetPrimary" + subject,
|
|
29
|
+
setAltTextCmd: "Set" + subject + "AltText",
|
|
30
|
+
attached: subject + "Attached",
|
|
31
|
+
removed: subject + "Removed",
|
|
32
|
+
primarySet: c.entity + "Primary" + c.noun + "Set",
|
|
33
|
+
altTextSet: subject + "AltTextSet",
|
|
34
|
+
notFound: c.entity + "NotFound",
|
|
35
|
+
notAttached: subject + "NotAttached"
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function refTypeOf(c) {
|
|
40
|
+
return Stdlib_Option.getOr(c.refType, "Reventless.UploadableImage.t");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function commandAttributes(c) {
|
|
44
|
+
let a = c.authorize;
|
|
45
|
+
if (a !== undefined) {
|
|
46
|
+
return ` | @authorize(` + a + `)\n `;
|
|
47
|
+
} else {
|
|
48
|
+
return " | ";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function commandTransitionBinding(c) {
|
|
53
|
+
let n = namesOf(c);
|
|
54
|
+
let arms = [
|
|
55
|
+
n.attachCmd,
|
|
56
|
+
n.removeCmd,
|
|
57
|
+
n.setPrimaryCmd,
|
|
58
|
+
n.setAltTextCmd
|
|
59
|
+
].map(cmd => ` | ` + cmd + `(_)`).join("\n");
|
|
60
|
+
let states = c.transition;
|
|
61
|
+
if (states !== undefined) {
|
|
62
|
+
return [
|
|
63
|
+
`type lifecycleState = ` + c.view + `.<lifecycle> // TODO(graft): the enum's name`,
|
|
64
|
+
`let commandTransition = (command: command): Reventless.Transition.t<lifecycleState> => {`,
|
|
65
|
+
` open Reventless.Transition`,
|
|
66
|
+
` switch command {`,
|
|
67
|
+
arms + ` =>`,
|
|
68
|
+
` Guards([` + states.join(", ") + `])`,
|
|
69
|
+
` }`,
|
|
70
|
+
`}`,
|
|
71
|
+
``
|
|
72
|
+
];
|
|
73
|
+
} else {
|
|
74
|
+
return [
|
|
75
|
+
`// TODO(graft): the states this host allows its attachment set to change in,`,
|
|
76
|
+
`// as constructors of the linked view's lifecycle enum. Delete this binding`,
|
|
77
|
+
`// outright if the answer is "any state" — the framework's default says so.`,
|
|
78
|
+
`//`,
|
|
79
|
+
`// type lifecycleState = ` + c.view + `.<lifecycle>`,
|
|
80
|
+
`// let commandTransition = (command: command): Reventless.Transition.t<lifecycleState> => {`,
|
|
81
|
+
`// open Reventless.Transition`,
|
|
82
|
+
`// switch command {`,
|
|
83
|
+
arms.replaceAll(" | ", "// | ") + ` =>`,
|
|
84
|
+
`// Guards([` + c.view + `.<State>])`,
|
|
85
|
+
`// }`,
|
|
86
|
+
`// }`,
|
|
87
|
+
``
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function lines(ls) {
|
|
93
|
+
return ls.join("\n");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function sliceSpec(c) {
|
|
97
|
+
let n = namesOf(c);
|
|
98
|
+
let ref = Stdlib_Option.getOr(c.refType, "Reventless.UploadableImage.t");
|
|
99
|
+
let attrs = commandAttributes(c);
|
|
100
|
+
let createdArm = Stdlib_Option.getOr(c.createdCarriesEntityId, true) ? ` | ` + c.created + `({ ` + c.entityId + `: string})` : ` | ` + c.created;
|
|
101
|
+
return Belt_Array.concatMany([
|
|
102
|
+
[
|
|
103
|
+
`// ` + n.slice + ` StateChangeSlice: ` + c.entity + `'s attachment set — attach, remove,`,
|
|
104
|
+
`// choose the primary, caption. A graft of the Attachments trait; the set's rules`,
|
|
105
|
+
`// are the trait's and are asserted by its conformance suite, bound in the tests.`,
|
|
106
|
+
`//`,
|
|
107
|
+
`// Emitted by the trait. Everything below is this host's own vocabulary, so it is`,
|
|
108
|
+
`// ordinary source from here on — edit it freely.`,
|
|
109
|
+
``,
|
|
110
|
+
`@@reventless.spec`,
|
|
111
|
+
``,
|
|
112
|
+
`@schema`,
|
|
113
|
+
`type consumedEvent =`,
|
|
114
|
+
createdArm,
|
|
115
|
+
` | ` + n.attached + `({ ` + c.file + `: string})`,
|
|
116
|
+
` | ` + n.removed + `({ ` + c.file + `: string})`,
|
|
117
|
+
` | ` + n.primarySet + `({ ` + c.file + `: string})`,
|
|
118
|
+
` | ` + n.altTextSet + `({ ` + c.file + `: string, altText: string})`,
|
|
119
|
+
` // TODO(graft): add the events this host's own refusal turns on — whatever`,
|
|
120
|
+
` // moves it into a state where attachments may not be changed.`,
|
|
121
|
+
``,
|
|
122
|
+
`@schema`,
|
|
123
|
+
`type command =`,
|
|
124
|
+
attrs + n.attachCmd + `({ ` + c.entityId + `: string, ` + c.file + `: ` + ref + `, altText?: string})`,
|
|
125
|
+
attrs + n.removeCmd + `({ ` + c.entityId + `: string, ` + c.file + `: ` + ref + `})`,
|
|
126
|
+
attrs + n.setPrimaryCmd + `({ ` + c.entityId + `: string, ` + c.file + `: ` + ref + `})`,
|
|
127
|
+
attrs + n.setAltTextCmd + `({ ` + c.entityId + `: string, ` + c.file + `: ` + ref + `, altText: string})`,
|
|
128
|
+
``,
|
|
129
|
+
`@schema`,
|
|
130
|
+
`type error =`,
|
|
131
|
+
` | ` + n.notFound,
|
|
132
|
+
` | ` + n.notAttached,
|
|
133
|
+
` // TODO(graft): add this host's own refusal.`,
|
|
134
|
+
``,
|
|
135
|
+
`@schema`,
|
|
136
|
+
`type event =`,
|
|
137
|
+
` | ` + n.attached + `({ ` + c.entityId + `: string, ` + c.file + `: ` + ref + `, altText?: string})`,
|
|
138
|
+
` | ` + n.removed + `({ ` + c.entityId + `: string, ` + c.file + `: ` + ref + `})`,
|
|
139
|
+
` | ` + n.primarySet + `({ ` + c.entityId + `: string, ` + c.file + `: ` + ref + `})`,
|
|
140
|
+
` | ` + n.altTextSet + `({ ` + c.entityId + `: string, ` + c.file + `: ` + ref + `, altText: string})`,
|
|
141
|
+
``
|
|
142
|
+
],
|
|
143
|
+
commandTransitionBinding(c),
|
|
144
|
+
[
|
|
145
|
+
`// The graft's own record of itself. Nothing else survives into a deployed`,
|
|
146
|
+
`// plugin — the dependency and the rules alias are source-side — so without`,
|
|
147
|
+
`// this a running estate cannot say where this slice came from.`,
|
|
148
|
+
`let traits = [TraitAttachments.Attachments.declaration]`,
|
|
149
|
+
``
|
|
150
|
+
]
|
|
151
|
+
]).join("\n");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function sliceBehavior(c) {
|
|
155
|
+
let n = namesOf(c);
|
|
156
|
+
return [
|
|
157
|
+
`@@reventless.behavior`,
|
|
158
|
+
``,
|
|
159
|
+
`// The set's rules are the trait's. What is left here is this host's own refusal`,
|
|
160
|
+
`// and the mapping between its constructors and the trait's ops and facts.`,
|
|
161
|
+
`module Attachments = TraitAttachments.Attachments_Rules`,
|
|
162
|
+
``,
|
|
163
|
+
`type state = {exists: bool, attachments: Attachments.t}`,
|
|
164
|
+
``,
|
|
165
|
+
`let initialState = {exists: false, attachments: Attachments.empty}`,
|
|
166
|
+
``,
|
|
167
|
+
`let evolve = (state, event) => {`,
|
|
168
|
+
` let fold = fact => {...state, attachments: state.attachments->Attachments.evolve(fact)}`,
|
|
169
|
+
` switch event {`,
|
|
170
|
+
` | ` + c.created + `(_) => {...state, exists: true}`,
|
|
171
|
+
` | ` + n.attached + `({` + c.file + `}) => fold(Attached({ref: ` + c.file + `, altText: None}))`,
|
|
172
|
+
` | ` + n.removed + `({` + c.file + `}) => fold(Removed({ref: ` + c.file + `}))`,
|
|
173
|
+
` | ` + n.primarySet + `({` + c.file + `}) => fold(PrimarySet({ref: ` + c.file + `}))`,
|
|
174
|
+
` | ` + n.altTextSet + `({` + c.file + `, altText}) => fold(AltTextSet({ref: ` + c.file + `, altText}))`,
|
|
175
|
+
` // TODO(graft): fold this host's own events into its own state.`,
|
|
176
|
+
` }`,
|
|
177
|
+
`}`,
|
|
178
|
+
``,
|
|
179
|
+
`let toOp = command =>`,
|
|
180
|
+
` switch command {`,
|
|
181
|
+
` | ` + n.attachCmd + `({` + c.entityId + `, ` + c.file + `, altText: ?altText}) => (`,
|
|
182
|
+
` ` + c.entityId + `,`,
|
|
183
|
+
` Attachments.Attach({ref: ` + c.file + `, altText}),`,
|
|
184
|
+
` )`,
|
|
185
|
+
` | ` + n.removeCmd + `({` + c.entityId + `, ` + c.file + `}) => (`,
|
|
186
|
+
` ` + c.entityId + `,`,
|
|
187
|
+
` Attachments.Remove({ref: ` + c.file + `}),`,
|
|
188
|
+
` )`,
|
|
189
|
+
` | ` + n.setPrimaryCmd + `({` + c.entityId + `, ` + c.file + `}) => (`,
|
|
190
|
+
` ` + c.entityId + `,`,
|
|
191
|
+
` Attachments.SetPrimary({ref: ` + c.file + `}),`,
|
|
192
|
+
` )`,
|
|
193
|
+
` | ` + n.setAltTextCmd + `({` + c.entityId + `, ` + c.file + `, altText}) => (`,
|
|
194
|
+
` ` + c.entityId + `,`,
|
|
195
|
+
` Attachments.SetAltText({ref: ` + c.file + `, altText}),`,
|
|
196
|
+
` )`,
|
|
197
|
+
` }`,
|
|
198
|
+
``,
|
|
199
|
+
`let toEvent = (` + c.entityId + `, fact) =>`,
|
|
200
|
+
` switch fact {`,
|
|
201
|
+
` | Attachments.Attached({ref, altText}) =>`,
|
|
202
|
+
` ` + n.attached + `({` + c.entityId + `, ` + c.file + `: ref, altText: ?altText})`,
|
|
203
|
+
` | Attachments.Removed({ref}) => ` + n.removed + `({` + c.entityId + `, ` + c.file + `: ref})`,
|
|
204
|
+
` | Attachments.PrimarySet({ref}) => ` + n.primarySet + `({` + c.entityId + `, ` + c.file + `: ref})`,
|
|
205
|
+
` | Attachments.AltTextSet({ref, altText}) =>`,
|
|
206
|
+
` ` + n.altTextSet + `({` + c.entityId + `, ` + c.file + `: ref, altText})`,
|
|
207
|
+
` }`,
|
|
208
|
+
``,
|
|
209
|
+
`let decide = (state, command) =>`,
|
|
210
|
+
` if !state.exists {`,
|
|
211
|
+
` Error(` + n.notFound + `)`,
|
|
212
|
+
` } else {`,
|
|
213
|
+
` // TODO(graft): this host's own refusal goes here, ahead of the set's rules —`,
|
|
214
|
+
` // an \`else if\` returning the error added above. A graft with no extra refusal`,
|
|
215
|
+
` // is a complete graft, so leaving this is legitimate.`,
|
|
216
|
+
` let (` + c.entityId + `, op) = toOp(command)`,
|
|
217
|
+
` switch state.attachments->Attachments.decide(op) {`,
|
|
218
|
+
` | Error(#NotAttached) => Error(` + n.notAttached + `)`,
|
|
219
|
+
` | Ok(None) => Ok([])`,
|
|
220
|
+
` | Ok(Some(fact)) => Ok([toEvent(` + c.entityId + `, fact)])`,
|
|
221
|
+
` }`,
|
|
222
|
+
` }`,
|
|
223
|
+
``
|
|
224
|
+
].join("\n");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function conformanceBinding(c) {
|
|
228
|
+
let n = namesOf(c);
|
|
229
|
+
let id = "e1";
|
|
230
|
+
let refA = Stdlib_Option.getOr(c.refA, `/uploads/00000000-0000-4000-8000-000000000001/a`);
|
|
231
|
+
let refB = Stdlib_Option.getOr(c.refB, `/uploads/00000000-0000-4000-8000-000000000002/b`);
|
|
232
|
+
let createdValue = Stdlib_Option.getOr(c.createdCarriesEntityId, true) ? c.created + `({ ` + c.entityId + `: "` + id + `"})` : c.created;
|
|
233
|
+
return [
|
|
234
|
+
`// The Attachments trait's conformance suite, bound to \`` + n.slice + `\`.`,
|
|
235
|
+
`// Emitted whole: every name here is one the graft already declared.`,
|
|
236
|
+
``,
|
|
237
|
+
`module Binding = {`,
|
|
238
|
+
` type ref = string`,
|
|
239
|
+
` let refA = "` + refA + `"`,
|
|
240
|
+
` let refB = "` + refB + `"`,
|
|
241
|
+
``,
|
|
242
|
+
` module Spec = ` + n.slice,
|
|
243
|
+
` module Behavior = ` + n.slice + `_Behavior`,
|
|
244
|
+
``,
|
|
245
|
+
` // Annotated: the slice consumes and emits same-named constructors.`,
|
|
246
|
+
` let created: array<` + n.slice + `.consumedEvent> = [` + createdValue + `]`,
|
|
247
|
+
` let attachedC = (ref): ` + n.slice + `.consumedEvent => ` + n.attached + `({ ` + c.file + `: ref})`,
|
|
248
|
+
` let removedC = (ref): ` + n.slice + `.consumedEvent => ` + n.removed + `({ ` + c.file + `: ref})`,
|
|
249
|
+
` let primarySetC = (ref): ` + n.slice + `.consumedEvent => ` + n.primarySet + `({ ` + c.file + `: ref})`,
|
|
250
|
+
` let altTextSetC = (ref, altText): ` + n.slice + `.consumedEvent =>`,
|
|
251
|
+
` ` + n.altTextSet + `({ ` + c.file + `: ref, altText})`,
|
|
252
|
+
``,
|
|
253
|
+
` let attach = ref => ` + n.slice + `.` + n.attachCmd + `({ ` + c.entityId + `: "` + id + `", ` + c.file + `: ref})`,
|
|
254
|
+
` let remove = ref => ` + n.slice + `.` + n.removeCmd + `({ ` + c.entityId + `: "` + id + `", ` + c.file + `: ref})`,
|
|
255
|
+
` let setPrimary = ref =>`,
|
|
256
|
+
` ` + n.slice + `.` + n.setPrimaryCmd + `({ ` + c.entityId + `: "` + id + `", ` + c.file + `: ref})`,
|
|
257
|
+
` let setAltText = (ref, altText) =>`,
|
|
258
|
+
` ` + n.slice + `.` + n.setAltTextCmd + `({ ` + c.entityId + `: "` + id + `", ` + c.file + `: ref, altText})`,
|
|
259
|
+
``,
|
|
260
|
+
` let attached = ref => ` + n.slice + `.` + n.attached + `({ ` + c.entityId + `: "` + id + `", ` + c.file + `: ref})`,
|
|
261
|
+
` let removed = ref => ` + n.slice + `.` + n.removed + `({ ` + c.entityId + `: "` + id + `", ` + c.file + `: ref})`,
|
|
262
|
+
` let primarySet = ref =>`,
|
|
263
|
+
` ` + n.slice + `.` + n.primarySet + `({ ` + c.entityId + `: "` + id + `", ` + c.file + `: ref})`,
|
|
264
|
+
` let altTextSet = (ref, altText) =>`,
|
|
265
|
+
` ` + n.slice + `.` + n.altTextSet + `({ ` + c.entityId + `: "` + id + `", ` + c.file + `: ref, altText})`,
|
|
266
|
+
` let notAttached = ` + n.slice + `.` + n.notAttached,
|
|
267
|
+
`}`,
|
|
268
|
+
``,
|
|
269
|
+
`module Conformance = TraitAttachments.Attachments_Conformance.Make(Binding)`,
|
|
270
|
+
``,
|
|
271
|
+
`Conformance.register()`,
|
|
272
|
+
``
|
|
273
|
+
].join("\n");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function projectionPatch(c) {
|
|
277
|
+
let n = namesOf(c);
|
|
278
|
+
return {
|
|
279
|
+
into: `StateViewSliceStream/` + c.view + `_Projection.res`,
|
|
280
|
+
at: `the projection's \`switch\`, and two fields on \`` + c.view + `\`'s state`,
|
|
281
|
+
contents: [
|
|
282
|
+
`// On the view's state, two fields — the set, and its primary as one string.`,
|
|
283
|
+
`// The second is not redundancy: a card, a gallery tile and a reference cell`,
|
|
284
|
+
`// each read one image-semantic string per row, so without it every tile is blank.`,
|
|
285
|
+
`//`,
|
|
286
|
+
`// ` + c.file + `s: array<{` + c.file + `: string, altText?: string}>,`,
|
|
287
|
+
`// ` + c.file + `?: string,`,
|
|
288
|
+
``,
|
|
289
|
+
`| ` + n.attached + `({` + c.entityId + `, ` + c.file + `, altText: ?altText}) =>`,
|
|
290
|
+
` Update(` + c.entityId + `, state => {`,
|
|
291
|
+
` let ` + c.file + `s = Array.concat(state.` + c.file + `s, [{` + c.file + `: ` + c.file + `, altText: ?altText}])`,
|
|
292
|
+
` {...state, ` + c.file + `s, ` + c.file + `: ?withPrimary(` + c.file + `s, state.primaryChosen)}`,
|
|
293
|
+
` })`,
|
|
294
|
+
`| ` + n.removed + `({` + c.entityId + `, ` + c.file + `}) =>`,
|
|
295
|
+
` Update(` + c.entityId + `, state => {`,
|
|
296
|
+
` let ` + c.file + `s = state.` + c.file + `s->Array.filter(m => m.` + c.file + ` != ` + c.file + `)`,
|
|
297
|
+
` {...state, ` + c.file + `s, ` + c.file + `: ?withPrimary(` + c.file + `s, state.primaryChosen)}`,
|
|
298
|
+
` })`,
|
|
299
|
+
`| ` + n.primarySet + `({` + c.entityId + `, ` + c.file + `}) =>`,
|
|
300
|
+
` Update(` + c.entityId + `, state => {...state, ` + c.file + `: Some(` + c.file + `)})`,
|
|
301
|
+
`| ` + n.altTextSet + `({` + c.entityId + `, ` + c.file + `, altText}) =>`,
|
|
302
|
+
` Update(` + c.entityId + `, state => {`,
|
|
303
|
+
` ...state,`,
|
|
304
|
+
` ` + c.file + `s: state.` + c.file + `s->Array.map(m =>`,
|
|
305
|
+
` m.` + c.file + ` == ` + c.file + ` ? {...m, altText} : m`,
|
|
306
|
+
` ),`,
|
|
307
|
+
` })`,
|
|
308
|
+
``,
|
|
309
|
+
`// The primary a reader should show: the one chosen, else the first attached —`,
|
|
310
|
+
`// the same rule the trait applies, over the view's own rows.`,
|
|
311
|
+
`let withPrimary = (members, chosen) =>`,
|
|
312
|
+
` TraitAttachments.Attachments_Rules.primaryOf(`,
|
|
313
|
+
` ~chosen,`,
|
|
314
|
+
` ~attached=members->Array.map(m => m.` + c.file + `),`,
|
|
315
|
+
` )`
|
|
316
|
+
].join("\n")
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function emit(config, into, tests) {
|
|
321
|
+
let n = namesOf(config);
|
|
322
|
+
return {
|
|
323
|
+
files: [
|
|
324
|
+
{
|
|
325
|
+
path: into + `/StateChangeSlice/` + n.slice + `.res`,
|
|
326
|
+
contents: sliceSpec(config)
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
path: into + `/StateChangeSlice/` + n.slice + `_Behavior.res`,
|
|
330
|
+
contents: sliceBehavior(config)
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
path: tests + `/` + n.slice + `Conformance_GWT.res`,
|
|
334
|
+
contents: conformanceBinding(config)
|
|
335
|
+
}
|
|
336
|
+
],
|
|
337
|
+
patches: [projectionPatch(config)]
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export {
|
|
342
|
+
configSchema,
|
|
343
|
+
namesOf,
|
|
344
|
+
refTypeOf,
|
|
345
|
+
commandAttributes,
|
|
346
|
+
commandTransitionBinding,
|
|
347
|
+
lines,
|
|
348
|
+
sliceSpec,
|
|
349
|
+
sliceBehavior,
|
|
350
|
+
conformanceBinding,
|
|
351
|
+
projectionPatch,
|
|
352
|
+
emit,
|
|
353
|
+
}
|
|
354
|
+
/* configSchema Not a pure module */
|