@marktiderman/genesis-forms 0.4.0 → 0.4.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/README.md +12 -2
- package/dist/destinations.cjs.map +1 -1
- package/dist/destinations.js.map +1 -1
- package/dist/index.cjs +124 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +123 -21
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +121 -23
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +121 -23
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +121 -23
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +121 -23
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -64,6 +64,7 @@ function tokenize(source) {
|
|
|
64
64
|
i++;
|
|
65
65
|
while (isDigit(source[i] ?? "")) i++;
|
|
66
66
|
}
|
|
67
|
+
if (isIdStart(source[i] ?? "")) throw new FormulaError("Write a number in digits only, like 1000000.", at);
|
|
67
68
|
const v = Number(source.slice(at, i));
|
|
68
69
|
if (!Number.isFinite(v)) throw new FormulaError("This number is too large.", at);
|
|
69
70
|
tokens.push({ t: "num", v, at });
|
|
@@ -293,6 +294,27 @@ function numbersIn(values, out = []) {
|
|
|
293
294
|
}
|
|
294
295
|
return out;
|
|
295
296
|
}
|
|
297
|
+
var listTotals = /* @__PURE__ */ new WeakMap();
|
|
298
|
+
var ownLists = /* @__PURE__ */ new WeakSet();
|
|
299
|
+
function ownFormulaList(list) {
|
|
300
|
+
Object.freeze(list);
|
|
301
|
+
ownLists.add(list);
|
|
302
|
+
return list;
|
|
303
|
+
}
|
|
304
|
+
function totalsOf(list) {
|
|
305
|
+
const own2 = ownLists.has(list);
|
|
306
|
+
let t = own2 ? listTotals.get(list) : void 0;
|
|
307
|
+
if (t) return t;
|
|
308
|
+
const nums = numbersIn(list);
|
|
309
|
+
t = { sum: 0, count: nums.length, min: Infinity, max: -Infinity };
|
|
310
|
+
for (const n of nums) {
|
|
311
|
+
t.sum += n;
|
|
312
|
+
if (n < t.min) t.min = n;
|
|
313
|
+
if (n > t.max) t.max = n;
|
|
314
|
+
}
|
|
315
|
+
if (own2) listTotals.set(list, t);
|
|
316
|
+
return t;
|
|
317
|
+
}
|
|
296
318
|
function compare(op, a, b) {
|
|
297
319
|
const blank2 = (v) => v === null || Array.isArray(v) || typeof v === "string" && v.trim() === "";
|
|
298
320
|
if (blank2(a) || blank2(b)) return op === "!=";
|
|
@@ -317,7 +339,7 @@ function evaluateFormula(ast, read) {
|
|
|
317
339
|
case "text":
|
|
318
340
|
return node.value;
|
|
319
341
|
case "ref":
|
|
320
|
-
return read(node
|
|
342
|
+
return read(node);
|
|
321
343
|
case "unary": {
|
|
322
344
|
const n = formulaNumber(scalar(run(node.arg)));
|
|
323
345
|
return n === null ? null : -n;
|
|
@@ -325,12 +347,31 @@ function evaluateFormula(ast, read) {
|
|
|
325
347
|
case "if":
|
|
326
348
|
return truthy(run(node.test)) ? run(node.then) : run(node.else);
|
|
327
349
|
case "call": {
|
|
328
|
-
|
|
329
|
-
|
|
350
|
+
let total = 0;
|
|
351
|
+
let count = 0;
|
|
352
|
+
let min = Infinity;
|
|
353
|
+
let max = -Infinity;
|
|
354
|
+
for (const arg of node.args) {
|
|
355
|
+
const v = run(arg);
|
|
356
|
+
if (Array.isArray(v)) {
|
|
357
|
+
const t = totalsOf(v);
|
|
358
|
+
total += t.sum;
|
|
359
|
+
count += t.count;
|
|
360
|
+
if (t.min < min) min = t.min;
|
|
361
|
+
if (t.max > max) max = t.max;
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
const n = formulaNumber(v);
|
|
365
|
+
if (n === null) continue;
|
|
366
|
+
total += n;
|
|
367
|
+
count++;
|
|
368
|
+
if (n < min) min = n;
|
|
369
|
+
if (n > max) max = n;
|
|
370
|
+
}
|
|
330
371
|
if (node.fn === "sum") return finite(total);
|
|
331
|
-
if (!
|
|
332
|
-
if (node.fn === "avg") return finite(total /
|
|
333
|
-
return
|
|
372
|
+
if (!count) return null;
|
|
373
|
+
if (node.fn === "avg") return finite(total / count);
|
|
374
|
+
return node.fn === "min" ? min : max;
|
|
334
375
|
}
|
|
335
376
|
case "binary": {
|
|
336
377
|
const a = run(node.left);
|
|
@@ -358,6 +399,7 @@ function evaluateFormula(ast, read) {
|
|
|
358
399
|
// src/calculated.ts
|
|
359
400
|
var CALCULATED_TYPE = "calculated";
|
|
360
401
|
var COMPUTED_FIELD_TYPES = [CALCULATED_TYPE];
|
|
402
|
+
var MAX_FORM_FORMULA_LENGTH = 2e4;
|
|
361
403
|
var MAX_CALCULATED_DECIMALS = 10;
|
|
362
404
|
function calculatedProps(field2) {
|
|
363
405
|
const p = field2.props ?? {};
|
|
@@ -396,23 +438,45 @@ function formatCalculated(value, field2) {
|
|
|
396
438
|
return typeof value === "string" ? value : "";
|
|
397
439
|
}
|
|
398
440
|
var MAX_FORMULA_ITEMS = 1e4;
|
|
441
|
+
var MAX_SETTLE_ITEMS = 5e4;
|
|
442
|
+
var extract = (value, column) => column !== void 0 ? value.flatMap((row) => {
|
|
443
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) return [];
|
|
444
|
+
const cell = row[column];
|
|
445
|
+
return typeof cell === "string" || typeof cell === "number" ? [cell] : [];
|
|
446
|
+
}) : value.flatMap((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" ? [v] : []);
|
|
447
|
+
var extracted = /* @__PURE__ */ new WeakMap();
|
|
448
|
+
function cachedList(value, column) {
|
|
449
|
+
let byColumn = extracted.get(value);
|
|
450
|
+
if (!byColumn) extracted.set(value, byColumn = /* @__PURE__ */ new Map());
|
|
451
|
+
const key = column ?? "";
|
|
452
|
+
let list = byColumn.get(key);
|
|
453
|
+
if (!list) byColumn.set(key, list = ownFormulaList(extract(value, column)));
|
|
454
|
+
return list;
|
|
455
|
+
}
|
|
456
|
+
function readAnswer(value, column) {
|
|
457
|
+
if (Array.isArray(value)) return cachedList(value, column);
|
|
458
|
+
return column !== void 0 ? null : formulaInput(value);
|
|
459
|
+
}
|
|
399
460
|
function formulaInput(value, column) {
|
|
400
461
|
if (value === void 0 || value === null) return null;
|
|
401
462
|
if (Array.isArray(value) && value.length > MAX_FORMULA_ITEMS) return null;
|
|
402
463
|
if (column !== void 0) {
|
|
403
464
|
if (!Array.isArray(value)) return null;
|
|
404
|
-
return value
|
|
405
|
-
if (!row || typeof row !== "object" || Array.isArray(row)) return [];
|
|
406
|
-
const cell = row[column];
|
|
407
|
-
return typeof cell === "string" || typeof cell === "number" ? [cell] : [];
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
if (Array.isArray(value)) {
|
|
411
|
-
return value.flatMap((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" ? [v] : []);
|
|
465
|
+
return extract(value, column);
|
|
412
466
|
}
|
|
467
|
+
if (Array.isArray(value)) return extract(value, void 0);
|
|
413
468
|
return typeof value === "object" ? null : value;
|
|
414
469
|
}
|
|
470
|
+
function refNodes(node, out = []) {
|
|
471
|
+
if (node.kind === "ref") out.push(node);
|
|
472
|
+
else if (node.kind === "unary") refNodes(node.arg, out);
|
|
473
|
+
else if (node.kind === "binary") refNodes(node.left, out), refNodes(node.right, out);
|
|
474
|
+
else if (node.kind === "if") refNodes(node.test, out), refNodes(node.then, out), refNodes(node.else, out);
|
|
475
|
+
else if (node.kind === "call") for (const a of node.args) refNodes(a, out);
|
|
476
|
+
return out;
|
|
477
|
+
}
|
|
415
478
|
var UNREAD = /* @__PURE__ */ Symbol("unread");
|
|
479
|
+
var OVER = /* @__PURE__ */ Symbol("over");
|
|
416
480
|
function calculator(definition) {
|
|
417
481
|
const compiled = [];
|
|
418
482
|
const idByKey = /* @__PURE__ */ new Map();
|
|
@@ -423,14 +487,39 @@ function calculator(definition) {
|
|
|
423
487
|
for (const block of definition.blocks) {
|
|
424
488
|
if (block.kind !== "field" || !isCalculated(block)) continue;
|
|
425
489
|
const parsed = parseFormula(calculatedProps(block).formula);
|
|
426
|
-
const reads =
|
|
427
|
-
|
|
490
|
+
const reads = [];
|
|
491
|
+
const slots = /* @__PURE__ */ new Map();
|
|
492
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
493
|
+
for (const ref of parsed.ok ? refNodes(parsed.ast) : []) {
|
|
494
|
+
const id = idByKey.get(ref.key);
|
|
495
|
+
const key = `${id ?? ""}\0${ref.column ?? ""}`;
|
|
496
|
+
let slot = byKey.get(key);
|
|
497
|
+
if (slot === void 0) {
|
|
498
|
+
slot = reads.push({ id, column: ref.column, key }) - 1;
|
|
499
|
+
byKey.set(key, slot);
|
|
500
|
+
}
|
|
501
|
+
slots.set(ref, slot);
|
|
502
|
+
}
|
|
503
|
+
compiled.push({ field: block, ast: parsed.ok ? parsed.ast : null, reads, slots, last: null });
|
|
428
504
|
}
|
|
429
505
|
const withCalculated = (values, path) => {
|
|
430
506
|
if (!compiled.length) return values;
|
|
431
507
|
const visible = new Set(path);
|
|
432
508
|
let next = values;
|
|
433
|
-
|
|
509
|
+
let budget = MAX_SETTLE_ITEMS;
|
|
510
|
+
const within = /* @__PURE__ */ new Map();
|
|
511
|
+
const input = ({ id, key }) => {
|
|
512
|
+
if (id === void 0 || !visible.has(id) || !Object.prototype.hasOwnProperty.call(next, id)) return UNREAD;
|
|
513
|
+
const value = next[id];
|
|
514
|
+
if (!Array.isArray(value)) return value;
|
|
515
|
+
let ok = within.get(key);
|
|
516
|
+
if (ok === void 0) {
|
|
517
|
+
ok = value.length <= MAX_FORMULA_ITEMS && value.length <= budget;
|
|
518
|
+
if (ok) budget -= value.length;
|
|
519
|
+
within.set(key, ok);
|
|
520
|
+
}
|
|
521
|
+
return ok ? value : OVER;
|
|
522
|
+
};
|
|
434
523
|
for (const entry of compiled) {
|
|
435
524
|
const { field: field2, ast } = entry;
|
|
436
525
|
const inputs = entry.reads.map(input);
|
|
@@ -438,11 +527,18 @@ function calculator(definition) {
|
|
|
438
527
|
if (entry.last && entry.last.inputs.length === inputs.length && entry.last.inputs.every((v, i) => v === inputs[i])) {
|
|
439
528
|
value = entry.last.value;
|
|
440
529
|
} else {
|
|
530
|
+
const resolved = new Array(inputs.length);
|
|
441
531
|
const read = (ref) => {
|
|
442
|
-
const
|
|
443
|
-
|
|
532
|
+
const slot = entry.slots.get(ref);
|
|
533
|
+
if (slot === void 0) return null;
|
|
534
|
+
let v = resolved[slot];
|
|
535
|
+
if (v === void 0) {
|
|
536
|
+
const raw = inputs[slot];
|
|
537
|
+
v = resolved[slot] = raw === UNREAD ? null : readAnswer(raw, entry.reads[slot].column);
|
|
538
|
+
}
|
|
539
|
+
return v;
|
|
444
540
|
};
|
|
445
|
-
value = ast ? calculatedValue(evaluateFormula(ast, read), field2) : void 0;
|
|
541
|
+
value = ast && !inputs.includes(OVER) ? calculatedValue(evaluateFormula(ast, read), field2) : void 0;
|
|
446
542
|
entry.last = { inputs, value };
|
|
447
543
|
}
|
|
448
544
|
const current = Object.prototype.hasOwnProperty.call(next, field2.id) ? next[field2.id] : void 0;
|
|
@@ -1423,9 +1519,15 @@ function parseForm(input) {
|
|
|
1423
1519
|
issue(`${path}.kind`, "Unknown block kind.");
|
|
1424
1520
|
}
|
|
1425
1521
|
});
|
|
1522
|
+
let formulaChars = 0;
|
|
1426
1523
|
input.blocks.forEach((block, i) => {
|
|
1427
1524
|
if (!isObject2(block) || block.kind !== "field" || block.type !== CALCULATED_TYPE) return;
|
|
1428
1525
|
const path = `blocks[${i}].props.formula`;
|
|
1526
|
+
const source = isObject2(block.props) && typeof block.props.formula === "string" ? block.props.formula : "";
|
|
1527
|
+
formulaChars += source.length;
|
|
1528
|
+
if (formulaChars > MAX_FORM_FORMULA_LENGTH && formulaChars - source.length <= MAX_FORM_FORMULA_LENGTH) {
|
|
1529
|
+
issue(path, `The form's formulas together are over ${MAX_FORM_FORMULA_LENGTH} characters; shorten some, or work part out once in an earlier calculated field.`);
|
|
1530
|
+
}
|
|
1429
1531
|
if (block.required === true) {
|
|
1430
1532
|
warnings.push({ path: `blocks[${i}].required`, message: "A calculated field is worked out, not answered, so it is never required." });
|
|
1431
1533
|
}
|
|
@@ -2267,9 +2369,11 @@ exports.HIGHLIGHT_NOTE_MESSAGE = HIGHLIGHT_NOTE_MESSAGE;
|
|
|
2267
2369
|
exports.MAX_CALCULATED_DECIMALS = MAX_CALCULATED_DECIMALS;
|
|
2268
2370
|
exports.MAX_FORMULA_ITEMS = MAX_FORMULA_ITEMS;
|
|
2269
2371
|
exports.MAX_FORMULA_LENGTH = MAX_FORMULA_LENGTH;
|
|
2372
|
+
exports.MAX_FORM_FORMULA_LENGTH = MAX_FORM_FORMULA_LENGTH;
|
|
2270
2373
|
exports.MAX_HIDDEN_STRING_LENGTH = MAX_HIDDEN_STRING_LENGTH;
|
|
2271
2374
|
exports.MAX_HIGHLIGHT_NOTE_LENGTH = MAX_HIGHLIGHT_NOTE_LENGTH;
|
|
2272
2375
|
exports.MAX_HIGHLIGHT_QUOTE_LENGTH = MAX_HIGHLIGHT_QUOTE_LENGTH;
|
|
2376
|
+
exports.MAX_SETTLE_ITEMS = MAX_SETTLE_ITEMS;
|
|
2273
2377
|
exports.MAX_TABLE_CELL_LENGTH = MAX_TABLE_CELL_LENGTH;
|
|
2274
2378
|
exports.MAX_TABLE_COLUMNS = MAX_TABLE_COLUMNS;
|
|
2275
2379
|
exports.MAX_TIMESTAMP_SECONDS = MAX_TIMESTAMP_SECONDS;
|