@reventlessdev/reventless-spec 3.0.0-alpha.126 → 3.0.0-alpha.128

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.
@@ -0,0 +1,545 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
7
+ import * as Bytes$Reventless from "./Bytes.res.mjs";
8
+ import * as Money$Reventless from "./Money.res.mjs";
9
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
10
+ import * as Percent$Reventless from "./Percent.res.mjs";
11
+ import * as Duration$Reventless from "./Duration.res.mjs";
12
+ import * as GeoPoint$Reventless from "./GeoPoint.res.mjs";
13
+ import * as Semantic$Reventless from "./Semantic.res.mjs";
14
+ import * as DateRange$Reventless from "./DateRange.res.mjs";
15
+ import * as Sensitive$Reventless from "../components/Sensitive.res.mjs";
16
+
17
+ let rawFormatter = "raw";
18
+
19
+ let formatters = [
20
+ rawFormatter,
21
+ Semantic$Reventless.Id.money,
22
+ Semantic$Reventless.Id.percent,
23
+ Semantic$Reventless.Id.bytes,
24
+ Semantic$Reventless.Id.duration,
25
+ Semantic$Reventless.Id.dateRange,
26
+ Semantic$Reventless.Id.geoPoint
27
+ ];
28
+
29
+ let segmentGrammar = /^[A-Za-z0-9_]+$/;
30
+
31
+ let whitespace = /\s+/g;
32
+
33
+ function parsePath(raw, insideEach) {
34
+ let text = raw.trim();
35
+ if (text === "") {
36
+ return {
37
+ TAG: "Error",
38
+ _0: "a tag with no path"
39
+ };
40
+ }
41
+ let relative = text.startsWith(".");
42
+ if (relative && !insideEach) {
43
+ return {
44
+ TAG: "Error",
45
+ _0: `"` + text + `" is item-relative, and there is no item outside an "each"`
46
+ };
47
+ }
48
+ let body = relative ? text.slice(1) : text;
49
+ let segments = body === "" ? [] : body.split(".");
50
+ if (segments.every(segment => segmentGrammar.test(segment))) {
51
+ return {
52
+ TAG: "Ok",
53
+ _0: {
54
+ text: text,
55
+ relative: relative,
56
+ segments: segments
57
+ }
58
+ };
59
+ } else {
60
+ return {
61
+ TAG: "Error",
62
+ _0: `"` + text + `" is not a path — segments are letters, digits and underscores, separated by dots`
63
+ };
64
+ }
65
+ }
66
+
67
+ function tokenize(source) {
68
+ let tokens = [];
69
+ let rest = source;
70
+ let failure;
71
+ let running = true;
72
+ while (running) {
73
+ let current = rest;
74
+ let start = current.indexOf("{{");
75
+ if (start !== -1) {
76
+ let before = current.slice(0, start);
77
+ if (before !== "") {
78
+ tokens.push({
79
+ TAG: "TextToken",
80
+ _0: before
81
+ });
82
+ }
83
+ let after = current.slice(start + 2 | 0);
84
+ let stop = after.indexOf("}}");
85
+ if (stop !== -1) {
86
+ tokens.push({
87
+ TAG: "TagToken",
88
+ _0: after.slice(0, stop).replace(whitespace, " ").trim()
89
+ });
90
+ rest = after.slice(stop + 2 | 0);
91
+ } else {
92
+ failure = `an opening "{{" with no closing "}}"`;
93
+ running = false;
94
+ }
95
+ } else {
96
+ if (current !== "") {
97
+ tokens.push({
98
+ TAG: "TextToken",
99
+ _0: current
100
+ });
101
+ }
102
+ running = false;
103
+ }
104
+ };
105
+ let message = failure;
106
+ if (message !== undefined) {
107
+ return {
108
+ TAG: "Error",
109
+ _0: message
110
+ };
111
+ } else {
112
+ return {
113
+ TAG: "Ok",
114
+ _0: tokens
115
+ };
116
+ }
117
+ }
118
+
119
+ function blockWord(kind) {
120
+ if (kind === "IfBlock") {
121
+ return "if";
122
+ } else {
123
+ return "each";
124
+ }
125
+ }
126
+
127
+ function parse(source) {
128
+ let message = tokenize(source);
129
+ if (message.TAG !== "Ok") {
130
+ return {
131
+ TAG: "Error",
132
+ _0: message._0
133
+ };
134
+ }
135
+ let root = [];
136
+ let stack = [];
137
+ let failure = {
138
+ contents: undefined
139
+ };
140
+ let fail = message => {
141
+ if (Stdlib_Option.isNone(failure.contents)) {
142
+ failure.contents = message;
143
+ return;
144
+ }
145
+ };
146
+ let body = () => {
147
+ let frame = stack.at(-1);
148
+ if (frame !== undefined) {
149
+ return frame.body;
150
+ } else {
151
+ return root;
152
+ }
153
+ };
154
+ let insideEach = () => stack.some(frame => frame.kind === "EachBlock");
155
+ let addField = (rawPath, formatter) => {
156
+ let message = parsePath(rawPath, insideEach());
157
+ if (message.TAG !== "Ok") {
158
+ return fail(message._0);
159
+ }
160
+ body().push({
161
+ TAG: "Field",
162
+ path: message._0,
163
+ formatter: formatter
164
+ });
165
+ };
166
+ let openBlock = (kind, rawPath) => {
167
+ if (kind === "EachBlock" && insideEach()) {
168
+ return fail(`an "each" inside an "each" — iteration is one level deep`);
169
+ }
170
+ let message = parsePath(rawPath, insideEach());
171
+ if (message.TAG !== "Ok") {
172
+ return fail(message._0);
173
+ }
174
+ stack.push({
175
+ kind: kind,
176
+ path: message._0,
177
+ body: []
178
+ });
179
+ };
180
+ message._0.forEach(token => {
181
+ if (!Stdlib_Option.isNone(failure.contents)) {
182
+ return;
183
+ }
184
+ if (token.TAG === "TextToken") {
185
+ body().push({
186
+ TAG: "Text",
187
+ _0: token._0
188
+ });
189
+ return;
190
+ }
191
+ let tag = token._0;
192
+ if (tag.startsWith("#")) {
193
+ let rest = tag.slice(1).trim();
194
+ let at = rest.indexOf(" ");
195
+ if (at === -1) {
196
+ return fail(`"{{# ` + rest + ` }}" opens a block with no path`);
197
+ }
198
+ let word = rest.slice(0, at);
199
+ switch (word) {
200
+ case "each" :
201
+ return openBlock("EachBlock", rest.slice(at + 1 | 0));
202
+ case "if" :
203
+ return openBlock("IfBlock", rest.slice(at + 1 | 0));
204
+ default:
205
+ return fail(`"` + word + `" is not a block — "if" and "each" are`);
206
+ }
207
+ } else {
208
+ if (tag.startsWith("/")) {
209
+ let word$1 = tag.slice(1).trim();
210
+ let frame = stack.pop();
211
+ if (frame === undefined) {
212
+ return fail(`a closing "{{/ ` + word$1 + ` }}" with nothing open`);
213
+ }
214
+ if (blockWord(frame.kind) !== word$1) {
215
+ return fail(`"{{/ ` + word$1 + ` }}" closes a "` + blockWord(frame.kind) + `"`);
216
+ }
217
+ let match = frame.kind;
218
+ let tmp;
219
+ tmp = match === "IfBlock" ? ({
220
+ TAG: "If",
221
+ path: frame.path,
222
+ body: frame.body
223
+ }) : ({
224
+ TAG: "Each",
225
+ path: frame.path,
226
+ body: frame.body
227
+ });
228
+ body().push(tmp);
229
+ return;
230
+ }
231
+ let match$1 = tag.split("|");
232
+ let len = match$1.length;
233
+ if (len >= 3) {
234
+ return fail(`"` + tag + `" names more than one formatter`);
235
+ }
236
+ switch (len) {
237
+ case 0 :
238
+ return fail(`"` + tag + `" names more than one formatter`);
239
+ case 1 :
240
+ let rawPath = match$1[0];
241
+ return addField(rawPath, undefined);
242
+ case 2 :
243
+ let rawPath$1 = match$1[0];
244
+ let name = match$1[1];
245
+ let formatter = name.trim();
246
+ if (formatters.includes(formatter)) {
247
+ return addField(rawPath$1, formatter);
248
+ } else {
249
+ return fail(`"` + formatter + `" is not a formatter — one of ` + formatters.join(", "));
250
+ }
251
+ }
252
+ }
253
+ });
254
+ let message$1 = failure.contents;
255
+ if (message$1 !== undefined) {
256
+ return {
257
+ TAG: "Error",
258
+ _0: message$1
259
+ };
260
+ }
261
+ let frame = stack.at(-1);
262
+ if (frame !== undefined) {
263
+ return {
264
+ TAG: "Error",
265
+ _0: `"{{# ` + blockWord(frame.kind) + ` ` + frame.path.text + ` }}" was never closed`
266
+ };
267
+ } else {
268
+ return {
269
+ TAG: "Ok",
270
+ _0: root
271
+ };
272
+ }
273
+ }
274
+
275
+ let nothing = {
276
+ value: undefined,
277
+ schema: undefined
278
+ };
279
+
280
+ function missing(path) {
281
+ return `[missing: ` + path.text + `]`;
282
+ }
283
+
284
+ function withheld(path) {
285
+ return `[withheld: ` + path.text + `]`;
286
+ }
287
+
288
+ function unwrap(schema) {
289
+ return Stdlib_Option.getOr(Semantic$Reventless.unwrapOptional(schema), schema);
290
+ }
291
+
292
+ function propertyOf(schema, name) {
293
+ let match = Stdlib_Option.getOr(Semantic$Reventless.unwrapOptional(schema), schema);
294
+ if (match.type === "object") {
295
+ return match.properties[name];
296
+ }
297
+ }
298
+
299
+ function elementOf(schema) {
300
+ let match = Stdlib_Option.getOr(Semantic$Reventless.unwrapOptional(schema), schema);
301
+ if (match.type !== "array") {
302
+ return;
303
+ }
304
+ let item = match.additionalItems;
305
+ if (item === "strip" || item === "strict") {
306
+ return;
307
+ } else {
308
+ return item;
309
+ }
310
+ }
311
+
312
+ function step(scope, segment) {
313
+ let match = scope.value;
314
+ let tmp;
315
+ tmp = typeof match === "object" && match !== null && !Array.isArray(match) ? match[segment] : undefined;
316
+ return {
317
+ value: tmp,
318
+ schema: Stdlib_Option.flatMap(scope.schema, __x => propertyOf(__x, segment))
319
+ };
320
+ }
321
+
322
+ function resolve(path, root, item) {
323
+ return Stdlib_Array.reduce(path.segments, path.relative ? Stdlib_Option.getOr(item, nothing) : root, step);
324
+ }
325
+
326
+ function lookup(payload, dotted) {
327
+ return Stdlib_Array.reduce(dotted.split("."), {
328
+ value: payload,
329
+ schema: undefined
330
+ }, step).value;
331
+ }
332
+
333
+ function isWithheld(schema) {
334
+ if (schema === undefined) {
335
+ return false;
336
+ }
337
+ if (Sensitive$Reventless.isFieldSensitive(schema)) {
338
+ return true;
339
+ }
340
+ let match = Semantic$Reventless.getFrom(schema);
341
+ if (match !== undefined) {
342
+ return Sensitive$Reventless.impliedBySemantic(match.id);
343
+ } else {
344
+ return false;
345
+ }
346
+ }
347
+
348
+ function parseSafely(json, schema) {
349
+ try {
350
+ return Primitive_option.some(S.parseOrThrow(json, schema));
351
+ } catch (exn) {
352
+ return;
353
+ }
354
+ }
355
+
356
+ function formatBySemantic(json, id) {
357
+ if (id === Semantic$Reventless.Id.money) {
358
+ return Stdlib_Option.map(parseSafely(json, Money$Reventless.schema), Money$Reventless.format);
359
+ } else if (id === Semantic$Reventless.Id.dateRange) {
360
+ return Stdlib_Option.map(parseSafely(json, DateRange$Reventless.schema), DateRange$Reventless.format);
361
+ } else if (id === Semantic$Reventless.Id.geoPoint) {
362
+ return Stdlib_Option.map(parseSafely(json, GeoPoint$Reventless.schema), GeoPoint$Reventless.format);
363
+ } else if (typeof json === "number") {
364
+ if (id === Semantic$Reventless.Id.percent) {
365
+ return Percent$Reventless.format(json);
366
+ } else if (id === Semantic$Reventless.Id.bytes) {
367
+ return Bytes$Reventless.format(json);
368
+ } else if (id === Semantic$Reventless.Id.duration) {
369
+ return Duration$Reventless.format(json | 0);
370
+ } else {
371
+ return;
372
+ }
373
+ } else {
374
+ return;
375
+ }
376
+ }
377
+
378
+ function scalar(value) {
379
+ if (value === null) {
380
+ return "";
381
+ }
382
+ if (Array.isArray(value)) {
383
+ return JSON.stringify(value);
384
+ }
385
+ switch (typeof value) {
386
+ case "boolean" :
387
+ if (value) {
388
+ return "true";
389
+ } else {
390
+ return "false";
391
+ }
392
+ case "string" :
393
+ return value;
394
+ case "number" :
395
+ return value.toString();
396
+ case "object" :
397
+ return JSON.stringify(value);
398
+ }
399
+ }
400
+
401
+ function truthy(value) {
402
+ if (value === undefined) {
403
+ return false;
404
+ }
405
+ if (value === null) {
406
+ return false;
407
+ }
408
+ if (Array.isArray(value)) {
409
+ return value.length !== 0;
410
+ }
411
+ switch (typeof value) {
412
+ case "boolean" :
413
+ return value;
414
+ case "string" :
415
+ return value !== "";
416
+ case "number" :
417
+ return value !== 0.0;
418
+ case "object" :
419
+ return true;
420
+ }
421
+ }
422
+
423
+ function renderField(path, formatter, root, item) {
424
+ let scope = resolve(path, root, item);
425
+ if (isWithheld(scope.schema)) {
426
+ return withheld(path);
427
+ }
428
+ let value = scope.value;
429
+ if (value === undefined) {
430
+ return missing(path);
431
+ }
432
+ if (value === null) {
433
+ return missing(path);
434
+ }
435
+ if (formatter !== undefined) {
436
+ if (formatter === rawFormatter) {
437
+ return scalar(value);
438
+ } else {
439
+ return Stdlib_Option.getOr(formatBySemantic(value, formatter), scalar(value));
440
+ }
441
+ }
442
+ let match = Stdlib_Option.flatMap(scope.schema, Semantic$Reventless.getFrom);
443
+ if (match !== undefined) {
444
+ return Stdlib_Option.getOr(formatBySemantic(value, match.id), scalar(value));
445
+ } else {
446
+ return scalar(value);
447
+ }
448
+ }
449
+
450
+ function renderNodes(nodes, root, item, out) {
451
+ nodes.forEach(node => {
452
+ switch (node.TAG) {
453
+ case "Text" :
454
+ out.push(node._0);
455
+ return;
456
+ case "Field" :
457
+ out.push(renderField(node.path, node.formatter, root, item));
458
+ return;
459
+ case "If" :
460
+ if (truthy(resolve(node.path, root, item).value)) {
461
+ return renderNodes(node.body, root, item, out);
462
+ } else {
463
+ return;
464
+ }
465
+ case "Each" :
466
+ let body = node.body;
467
+ let path = node.path;
468
+ let scope = resolve(path, root, item);
469
+ if (isWithheld(scope.schema)) {
470
+ out.push(withheld(path));
471
+ return;
472
+ }
473
+ let match = scope.value;
474
+ if (Array.isArray(match)) {
475
+ let element = Stdlib_Option.flatMap(scope.schema, elementOf);
476
+ let total = match.length;
477
+ let shown = total > 100 ? 100 : total;
478
+ for (let index = 0; index < shown; ++index) {
479
+ renderNodes(body, root, {
480
+ value: match[index],
481
+ schema: element
482
+ }, out);
483
+ }
484
+ if (total > shown) {
485
+ out.push(`[… ` + (total - shown | 0).toString() + ` more]`);
486
+ return;
487
+ } else {
488
+ return;
489
+ }
490
+ }
491
+ out.push(missing(path));
492
+ return;
493
+ break;
494
+ }
495
+ });
496
+ }
497
+
498
+ function render(template, payload, schema) {
499
+ let out = [];
500
+ renderNodes(template, {
501
+ value: payload,
502
+ schema: schema
503
+ }, undefined, out);
504
+ return out.join("");
505
+ }
506
+
507
+ function renderSource(source, payload, schema) {
508
+ return Stdlib_Result.map(parse(source), template => render(template, payload, schema));
509
+ }
510
+
511
+ let variantSchema = Semantic$Reventless.unionVariant;
512
+
513
+ let maxItems = 100;
514
+
515
+ export {
516
+ rawFormatter,
517
+ formatters,
518
+ segmentGrammar,
519
+ whitespace,
520
+ parsePath,
521
+ tokenize,
522
+ blockWord,
523
+ parse,
524
+ nothing,
525
+ missing,
526
+ withheld,
527
+ maxItems,
528
+ unwrap,
529
+ propertyOf,
530
+ elementOf,
531
+ step,
532
+ resolve,
533
+ lookup,
534
+ isWithheld,
535
+ parseSafely,
536
+ formatBySemantic,
537
+ scalar,
538
+ truthy,
539
+ renderField,
540
+ renderNodes,
541
+ render,
542
+ renderSource,
543
+ variantSchema,
544
+ }
545
+ /* S Not a pure module */