@usefillo/cli 0.10.0 → 0.12.0
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/dist/index.js
CHANGED
|
@@ -1298,79 +1298,122 @@ var CONTENT_KINDS = ["heading", "paragraph", "divider"];
|
|
|
1298
1298
|
function isField(block) {
|
|
1299
1299
|
return !CONTENT_KINDS.includes(block.kind);
|
|
1300
1300
|
}
|
|
1301
|
-
var
|
|
1301
|
+
var regionNames;
|
|
1302
|
+
function regionDisplayNames() {
|
|
1303
|
+
if (regionNames !== void 0) return regionNames ?? void 0;
|
|
1304
|
+
if (typeof Intl === "undefined" || typeof Intl.DisplayNames === "undefined") {
|
|
1305
|
+
regionNames = null;
|
|
1306
|
+
return void 0;
|
|
1307
|
+
}
|
|
1308
|
+
try {
|
|
1309
|
+
regionNames = new Intl.DisplayNames(void 0, { type: "region" });
|
|
1310
|
+
} catch {
|
|
1311
|
+
regionNames = null;
|
|
1312
|
+
}
|
|
1313
|
+
return regionNames ?? void 0;
|
|
1314
|
+
}
|
|
1315
|
+
function regionName(iso2) {
|
|
1316
|
+
try {
|
|
1317
|
+
return regionDisplayNames()?.of(iso2) ?? iso2;
|
|
1318
|
+
} catch {
|
|
1319
|
+
return iso2;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
var C = (iso2, dialCode, lengths = [], groups = [], example = "") => ({ iso2, name: regionName(iso2), dialCode, lengths, groups, example });
|
|
1323
|
+
var CURATED_COUNTRIES = [
|
|
1324
|
+
C("US", "1", [10], [3, 3, 4], "2015550123"),
|
|
1325
|
+
C("CA", "1", [10], [3, 3, 4], "5145550123"),
|
|
1326
|
+
C("GB", "44", [10], [4, 6], "7400123456"),
|
|
1327
|
+
C("AU", "61", [9], [3, 3, 3], "412345678"),
|
|
1328
|
+
C("DE", "49", [10, 11], [4, 7], "15123456789"),
|
|
1329
|
+
C("FR", "33", [9], [1, 2, 2, 2, 2], "612345678"),
|
|
1330
|
+
C("IN", "91", [10], [5, 5], "8123456789"),
|
|
1331
|
+
C("DK", "45", [8], [2, 2, 2, 2], "32123456"),
|
|
1332
|
+
C("SE", "46", [9], [2, 3, 4], "701234567"),
|
|
1333
|
+
C("NO", "47", [8], [3, 2, 3], "40612345"),
|
|
1334
|
+
C("FI", "358", [9, 10], [2, 3, 4], "412345678"),
|
|
1335
|
+
C("NL", "31", [9], [1, 4, 4], "612345678"),
|
|
1336
|
+
C("BE", "32", [9], [3, 2, 2, 2], "470123456"),
|
|
1337
|
+
C("ES", "34", [9], [3, 3, 3], "612345678"),
|
|
1338
|
+
C("IT", "39", [9, 10], [3, 3, 4], "3123456789"),
|
|
1339
|
+
C("PT", "351", [9], [3, 3, 3], "912345678"),
|
|
1340
|
+
C("IE", "353", [9], [2, 3, 4], "851234567"),
|
|
1341
|
+
C("CH", "41", [9], [2, 3, 2, 2], "781234567"),
|
|
1342
|
+
C("AT", "43", [10, 11], [3, 7], "6641234567"),
|
|
1343
|
+
C("PL", "48", [9], [3, 3, 3], "512345678"),
|
|
1344
|
+
C("CZ", "420", [9], [3, 3, 3], "601123456"),
|
|
1345
|
+
C("GR", "30", [10], [3, 3, 4], "6912345678"),
|
|
1346
|
+
C("RO", "40", [9], [3, 3, 3], "712345678"),
|
|
1347
|
+
C("HU", "36", [9], [2, 3, 4], "201234567"),
|
|
1348
|
+
C("UA", "380", [9], [2, 3, 2, 2], "501234567"),
|
|
1349
|
+
C("RU", "7", [10], [3, 3, 2, 2], "9123456789"),
|
|
1350
|
+
C("TR", "90", [10], [3, 3, 2, 2], "5012345678"),
|
|
1351
|
+
C("IL", "972", [9], [2, 3, 4], "501234567"),
|
|
1352
|
+
C("AE", "971", [9], [2, 3, 4], "501234567"),
|
|
1353
|
+
C("SA", "966", [9], [2, 3, 4], "512345678"),
|
|
1354
|
+
C("ZA", "27", [9], [2, 3, 4], "711234567"),
|
|
1355
|
+
C("NG", "234", [10], [3, 3, 4], "8021234567"),
|
|
1356
|
+
C("KE", "254", [9], [3, 3, 3], "712123456"),
|
|
1357
|
+
C("EG", "20", [10], [3, 3, 4], "1001234567"),
|
|
1358
|
+
C("BR", "55", [10, 11], [2, 5, 4], "11961234567"),
|
|
1359
|
+
C("MX", "52", [10], [3, 3, 4], "2221234567"),
|
|
1360
|
+
C("AR", "54", [10], [4, 6], "1123456789"),
|
|
1361
|
+
C("CL", "56", [9], [1, 4, 4], "221234567"),
|
|
1362
|
+
C("CO", "57", [10], [3, 3, 4], "3211234567"),
|
|
1363
|
+
C("PE", "51", [9], [3, 3, 3], "912345678"),
|
|
1364
|
+
C("CN", "86", [11], [3, 4, 4], "13123456789"),
|
|
1365
|
+
C("JP", "81", [10], [2, 4, 4], "9012345678"),
|
|
1366
|
+
C("KR", "82", [9, 10], [2, 4, 4], "1023456789"),
|
|
1367
|
+
C("SG", "65", [8], [4, 4], "81234567"),
|
|
1368
|
+
C("HK", "852", [8], [4, 4], "51234567"),
|
|
1369
|
+
C("TW", "886", [9], [3, 3, 3], "912345678"),
|
|
1370
|
+
C("MY", "60", [9, 10], [2, 3, 4], "123456789"),
|
|
1371
|
+
C("ID", "62", [10, 11], [3, 4, 4], "81234567890"),
|
|
1372
|
+
C("TH", "66", [9], [2, 3, 4], "812345678"),
|
|
1373
|
+
C("PH", "63", [10], [3, 3, 4], "9171234567"),
|
|
1374
|
+
C("VN", "84", [9], [3, 3, 3], "912345678"),
|
|
1375
|
+
C("PK", "92", [10], [3, 3, 4], "3012345678"),
|
|
1376
|
+
C("BD", "880", [10], [2, 4, 4], "1812345678"),
|
|
1377
|
+
C("NZ", "64", [9, 10], [2, 3, 4], "211234567"),
|
|
1378
|
+
C("SK", "421", [9], [3, 3, 3], "912123456"),
|
|
1379
|
+
C("BG", "359", [9], [2, 3, 4], "481234567"),
|
|
1380
|
+
C("HR", "385", [8, 9], [2, 3, 4], "921234567"),
|
|
1381
|
+
C("RS", "381", [8, 9], [2, 3, 4], "601234567"),
|
|
1382
|
+
C("SI", "386", [8], [2, 3, 3], "31234567"),
|
|
1383
|
+
C("LT", "370", [8], [3, 2, 3], "61234567"),
|
|
1384
|
+
C("LV", "371", [8], [2, 3, 3], "21234567"),
|
|
1385
|
+
C("EE", "372", [7, 8], [3, 4], "51234567"),
|
|
1386
|
+
C("IS", "354", [7], [3, 4], "6111234"),
|
|
1387
|
+
C("LU", "352", [9], [3, 3, 3], "628123456"),
|
|
1388
|
+
C("MA", "212", [9], [3, 3, 3], "650123456"),
|
|
1389
|
+
C("CR", "506", [8], [4, 4], "83123456"),
|
|
1390
|
+
C("EC", "593", [9], [2, 3, 4], "991234567"),
|
|
1391
|
+
C("UY", "598", [8], [1, 3, 4], "94231234"),
|
|
1392
|
+
C("QA", "974", [8], [4, 4], "33123456"),
|
|
1393
|
+
C("KW", "965", [8], [4, 4], "50012345")
|
|
1394
|
+
];
|
|
1395
|
+
var PACKED_DIAL_CODES = "AD376 AF93 AG1 AI1 AL355 AM374 AO244 AS1 AW297 AX358 AZ994 BA387 BB1 BF226 BH973 BI257 BJ229 BL590 BM1 BN673 BO591 BQ599 BS1 BT975 BV47 BW267 BY375 BZ501 CC61 CD243 CF236 CG242 CI225 CK682 CM237 CU53 CV238 CW599 CX61 CY357 DJ253 DM1 DO1 DZ213 EH212 ER291 ET251 FJ679 FK500 FM691 FO298 GA241 GD1 GE995 GF594 GG44 GH233 GI350 GL299 GM220 GN224 GP590 GQ240 GS500 GT502 GU1 GW245 GY592 HN504 HT509 IM44 IO246 IQ964 IR98 JE44 JM1 JO962 KG996 KH855 KI686 KM269 KN1 KP850 KY1 KZ7 LA856 LB961 LC1 LI423 LK94 LR231 LS266 LY218 MC377 MD373 ME382 MF590 MG261 MH692 MK389 ML223 MM95 MN976 MO853 MP1 MQ596 MR222 MS1 MT356 MU230 MV960 MW265 MZ258 NA264 NC687 NE227 NF672 NI505 NP977 NR674 NU683 OM968 PA507 PF689 PG675 PM508 PN64 PR1 PS970 PW680 PY595 RE262 RW250 SB677 SC248 SD249 SH290 SJ47 SL232 SM378 SN221 SO252 SR597 SS211 ST239 SV503 SX1 SY963 SZ268 TC1 TD235 TF262 TG228 TJ992 TK690 TL670 TM993 TN216 TO676 TT1 TV688 TZ255 UG256 UZ998 VA379 VC1 VE58 VG1 VI1 VU678 WF681 WS685 XK383 YE967 YT262 ZM260 ZW263";
|
|
1396
|
+
function parsePackedCountries(packed) {
|
|
1397
|
+
const out = [];
|
|
1398
|
+
for (const token of packed.trim().split(/\s+/)) {
|
|
1399
|
+
const iso2 = token.slice(0, 2);
|
|
1400
|
+
const dialCode = token.slice(2);
|
|
1401
|
+
out.push({ iso2, name: regionName(iso2), dialCode, lengths: [], groups: [], example: "" });
|
|
1402
|
+
}
|
|
1403
|
+
return out;
|
|
1404
|
+
}
|
|
1302
1405
|
var PHONE_COUNTRIES = [
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
C("GB", "United Kingdom", "44", [10], [4, 6], "7400123456"),
|
|
1306
|
-
C("AU", "Australia", "61", [9], [3, 3, 3], "412345678"),
|
|
1307
|
-
C("DE", "Germany", "49", [10, 11], [4, 7], "15123456789"),
|
|
1308
|
-
C("FR", "France", "33", [9], [1, 2, 2, 2, 2], "612345678"),
|
|
1309
|
-
C("IN", "India", "91", [10], [5, 5], "8123456789"),
|
|
1310
|
-
C("DK", "Denmark", "45", [8], [2, 2, 2, 2], "32123456"),
|
|
1311
|
-
C("SE", "Sweden", "46", [9], [2, 3, 4], "701234567"),
|
|
1312
|
-
C("NO", "Norway", "47", [8], [3, 2, 3], "40612345"),
|
|
1313
|
-
C("FI", "Finland", "358", [9, 10], [2, 3, 4], "412345678"),
|
|
1314
|
-
C("NL", "Netherlands", "31", [9], [1, 4, 4], "612345678"),
|
|
1315
|
-
C("BE", "Belgium", "32", [9], [3, 2, 2, 2], "470123456"),
|
|
1316
|
-
C("ES", "Spain", "34", [9], [3, 3, 3], "612345678"),
|
|
1317
|
-
C("IT", "Italy", "39", [9, 10], [3, 3, 4], "3123456789"),
|
|
1318
|
-
C("PT", "Portugal", "351", [9], [3, 3, 3], "912345678"),
|
|
1319
|
-
C("IE", "Ireland", "353", [9], [2, 3, 4], "851234567"),
|
|
1320
|
-
C("CH", "Switzerland", "41", [9], [2, 3, 2, 2], "781234567"),
|
|
1321
|
-
C("AT", "Austria", "43", [10, 11], [3, 7], "6641234567"),
|
|
1322
|
-
C("PL", "Poland", "48", [9], [3, 3, 3], "512345678"),
|
|
1323
|
-
C("CZ", "Czechia", "420", [9], [3, 3, 3], "601123456"),
|
|
1324
|
-
C("GR", "Greece", "30", [10], [3, 3, 4], "6912345678"),
|
|
1325
|
-
C("RO", "Romania", "40", [9], [3, 3, 3], "712345678"),
|
|
1326
|
-
C("HU", "Hungary", "36", [9], [2, 3, 4], "201234567"),
|
|
1327
|
-
C("UA", "Ukraine", "380", [9], [2, 3, 2, 2], "501234567"),
|
|
1328
|
-
C("RU", "Russia", "7", [10], [3, 3, 2, 2], "9123456789"),
|
|
1329
|
-
C("TR", "Turkey", "90", [10], [3, 3, 2, 2], "5012345678"),
|
|
1330
|
-
C("IL", "Israel", "972", [9], [2, 3, 4], "501234567"),
|
|
1331
|
-
C("AE", "United Arab Emirates", "971", [9], [2, 3, 4], "501234567"),
|
|
1332
|
-
C("SA", "Saudi Arabia", "966", [9], [2, 3, 4], "512345678"),
|
|
1333
|
-
C("ZA", "South Africa", "27", [9], [2, 3, 4], "711234567"),
|
|
1334
|
-
C("NG", "Nigeria", "234", [10], [3, 3, 4], "8021234567"),
|
|
1335
|
-
C("KE", "Kenya", "254", [9], [3, 3, 3], "712123456"),
|
|
1336
|
-
C("EG", "Egypt", "20", [10], [3, 3, 4], "1001234567"),
|
|
1337
|
-
C("BR", "Brazil", "55", [10, 11], [2, 5, 4], "11961234567"),
|
|
1338
|
-
C("MX", "Mexico", "52", [10], [3, 3, 4], "2221234567"),
|
|
1339
|
-
C("AR", "Argentina", "54", [10], [4, 6], "1123456789"),
|
|
1340
|
-
C("CL", "Chile", "56", [9], [1, 4, 4], "221234567"),
|
|
1341
|
-
C("CO", "Colombia", "57", [10], [3, 3, 4], "3211234567"),
|
|
1342
|
-
C("PE", "Peru", "51", [9], [3, 3, 3], "912345678"),
|
|
1343
|
-
C("CN", "China", "86", [11], [3, 4, 4], "13123456789"),
|
|
1344
|
-
C("JP", "Japan", "81", [10], [2, 4, 4], "9012345678"),
|
|
1345
|
-
C("KR", "South Korea", "82", [9, 10], [2, 4, 4], "1023456789"),
|
|
1346
|
-
C("SG", "Singapore", "65", [8], [4, 4], "81234567"),
|
|
1347
|
-
C("HK", "Hong Kong", "852", [8], [4, 4], "51234567"),
|
|
1348
|
-
C("TW", "Taiwan", "886", [9], [3, 3, 3], "912345678"),
|
|
1349
|
-
C("MY", "Malaysia", "60", [9, 10], [2, 3, 4], "123456789"),
|
|
1350
|
-
C("ID", "Indonesia", "62", [10, 11], [3, 4, 4], "81234567890"),
|
|
1351
|
-
C("TH", "Thailand", "66", [9], [2, 3, 4], "812345678"),
|
|
1352
|
-
C("PH", "Philippines", "63", [10], [3, 3, 4], "9171234567"),
|
|
1353
|
-
C("VN", "Vietnam", "84", [9], [3, 3, 3], "912345678"),
|
|
1354
|
-
C("PK", "Pakistan", "92", [10], [3, 3, 4], "3012345678"),
|
|
1355
|
-
C("BD", "Bangladesh", "880", [10], [2, 4, 4], "1812345678"),
|
|
1356
|
-
C("NZ", "New Zealand", "64", [9, 10], [2, 3, 4], "211234567"),
|
|
1357
|
-
C("SK", "Slovakia", "421", [9], [3, 3, 3], "912123456"),
|
|
1358
|
-
C("BG", "Bulgaria", "359", [9], [2, 3, 4], "481234567"),
|
|
1359
|
-
C("HR", "Croatia", "385", [8, 9], [2, 3, 4], "921234567"),
|
|
1360
|
-
C("RS", "Serbia", "381", [8, 9], [2, 3, 4], "601234567"),
|
|
1361
|
-
C("SI", "Slovenia", "386", [8], [2, 3, 3], "31234567"),
|
|
1362
|
-
C("LT", "Lithuania", "370", [8], [3, 2, 3], "61234567"),
|
|
1363
|
-
C("LV", "Latvia", "371", [8], [2, 3, 3], "21234567"),
|
|
1364
|
-
C("EE", "Estonia", "372", [7, 8], [3, 4], "51234567"),
|
|
1365
|
-
C("IS", "Iceland", "354", [7], [3, 4], "6111234"),
|
|
1366
|
-
C("LU", "Luxembourg", "352", [9], [3, 3, 3], "628123456"),
|
|
1367
|
-
C("MA", "Morocco", "212", [9], [3, 3, 3], "650123456"),
|
|
1368
|
-
C("CR", "Costa Rica", "506", [8], [4, 4], "83123456"),
|
|
1369
|
-
C("EC", "Ecuador", "593", [9], [2, 3, 4], "991234567"),
|
|
1370
|
-
C("UY", "Uruguay", "598", [8], [1, 3, 4], "94231234"),
|
|
1371
|
-
C("QA", "Qatar", "974", [8], [4, 4], "33123456"),
|
|
1372
|
-
C("KW", "Kuwait", "965", [8], [4, 4], "50012345")
|
|
1406
|
+
...CURATED_COUNTRIES,
|
|
1407
|
+
...parsePackedCountries(PACKED_DIAL_CODES)
|
|
1373
1408
|
];
|
|
1409
|
+
var PHONE_PICKER_COUNTRIES = sortForPicker(PHONE_COUNTRIES);
|
|
1410
|
+
function sortForPicker(countries) {
|
|
1411
|
+
if (typeof Intl === "undefined" || typeof Intl.Collator === "undefined") {
|
|
1412
|
+
return [...countries].sort((a, b) => a.iso2 < b.iso2 ? -1 : a.iso2 > b.iso2 ? 1 : 0);
|
|
1413
|
+
}
|
|
1414
|
+
const collator = new Intl.Collator(void 0, { sensitivity: "base" });
|
|
1415
|
+
return [...countries].sort((a, b) => collator.compare(a.name, b.name));
|
|
1416
|
+
}
|
|
1374
1417
|
var byIso = new Map(PHONE_COUNTRIES.map((c) => [c.iso2, c]));
|
|
1375
1418
|
var fileValueSchema = object({
|
|
1376
1419
|
fileId: string2().check(_minLength(1)),
|
|
@@ -1398,6 +1441,7 @@ var FIELD_KINDS = [
|
|
|
1398
1441
|
"date",
|
|
1399
1442
|
"file_upload",
|
|
1400
1443
|
"hidden",
|
|
1444
|
+
"calculated",
|
|
1401
1445
|
"custom"
|
|
1402
1446
|
];
|
|
1403
1447
|
var CONTENT_KINDS2 = ["heading", "paragraph", "divider"];
|
|
@@ -1434,7 +1478,7 @@ var schemaShape = object({
|
|
|
1434
1478
|
});
|
|
1435
1479
|
var MAX_SCHEMA_VERSION = 1;
|
|
1436
1480
|
var FILLO_SCHEMA_VERSION = 1;
|
|
1437
|
-
var FILLO_SDK_VERSION = true ? "0.
|
|
1481
|
+
var FILLO_SDK_VERSION = true ? "0.12.0" : "0.0.0-dev";
|
|
1438
1482
|
function str(value, max, fallback = "") {
|
|
1439
1483
|
return typeof value === "string" ? value.trim().slice(0, max) : fallback;
|
|
1440
1484
|
}
|
|
@@ -1442,6 +1486,11 @@ function optionalStr(value, max) {
|
|
|
1442
1486
|
const s = str(value, max);
|
|
1443
1487
|
return s === "" ? void 0 : s;
|
|
1444
1488
|
}
|
|
1489
|
+
function optionalAffix(value, max) {
|
|
1490
|
+
if (typeof value !== "string") return void 0;
|
|
1491
|
+
const s = value.slice(0, max);
|
|
1492
|
+
return s.trim() === "" ? void 0 : s;
|
|
1493
|
+
}
|
|
1445
1494
|
function bool(value) {
|
|
1446
1495
|
return typeof value === "boolean" ? value : void 0;
|
|
1447
1496
|
}
|
|
@@ -1489,7 +1538,17 @@ function conditions(value) {
|
|
|
1489
1538
|
});
|
|
1490
1539
|
return normalized.length ? normalized : void 0;
|
|
1491
1540
|
}
|
|
1492
|
-
function
|
|
1541
|
+
function jumpDepsAllowed(fieldId, calcRefs, allowed, seen) {
|
|
1542
|
+
const refs = calcRefs.get(fieldId);
|
|
1543
|
+
if (refs === void 0) return allowed.has(fieldId);
|
|
1544
|
+
if (seen.has(fieldId)) return true;
|
|
1545
|
+
seen.add(fieldId);
|
|
1546
|
+
for (const ref of refs) {
|
|
1547
|
+
if (!jumpDepsAllowed(ref, calcRefs, allowed, seen)) return false;
|
|
1548
|
+
}
|
|
1549
|
+
return true;
|
|
1550
|
+
}
|
|
1551
|
+
function jumpRules(value, pageIds, sourceId, allowedFieldIds, calcRefs) {
|
|
1493
1552
|
if (!Array.isArray(value)) return void 0;
|
|
1494
1553
|
const rules = value.slice(0, 50).flatMap((item) => {
|
|
1495
1554
|
if (!item || typeof item !== "object") return [];
|
|
@@ -1501,11 +1560,187 @@ function jumpRules(value, pageIds, sourceId, allowedFieldIds) {
|
|
|
1501
1560
|
if (rawWhen !== void 0 && !Array.isArray(rawWhen)) return [];
|
|
1502
1561
|
const when2 = conditions(rawWhen);
|
|
1503
1562
|
if (Array.isArray(rawWhen) && rawWhen.length > 0 && when2 === void 0) return [];
|
|
1504
|
-
if (when2 && when2.some((c) => !
|
|
1563
|
+
if (when2 && when2.some((c) => !jumpDepsAllowed(c.fieldId, calcRefs, allowedFieldIds, /* @__PURE__ */ new Set())))
|
|
1564
|
+
return [];
|
|
1505
1565
|
return [{ when: when2 ?? [], to }];
|
|
1506
1566
|
});
|
|
1507
1567
|
return rules.length ? rules : void 0;
|
|
1508
1568
|
}
|
|
1569
|
+
var CALC_OPERAND_KINDS = ["number", "rating", "linear_scale", "calculated"];
|
|
1570
|
+
function normalizeCalc(value, depth = 0) {
|
|
1571
|
+
if (depth > 32) return null;
|
|
1572
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
1573
|
+
const rec = value;
|
|
1574
|
+
switch (rec.op) {
|
|
1575
|
+
case "value": {
|
|
1576
|
+
const fieldId = str(rec.fieldId, 128);
|
|
1577
|
+
return fieldId ? { op: "value", fieldId } : null;
|
|
1578
|
+
}
|
|
1579
|
+
case "const": {
|
|
1580
|
+
const n = finite(rec.value);
|
|
1581
|
+
return n === void 0 ? null : { op: "const", value: n };
|
|
1582
|
+
}
|
|
1583
|
+
case "add":
|
|
1584
|
+
case "mul":
|
|
1585
|
+
case "min":
|
|
1586
|
+
case "max": {
|
|
1587
|
+
if (!Array.isArray(rec.args) || rec.args.length < 1 || rec.args.length > 50) return null;
|
|
1588
|
+
const args = [];
|
|
1589
|
+
for (const raw of rec.args) {
|
|
1590
|
+
const arg = normalizeCalc(raw, depth + 1);
|
|
1591
|
+
if (!arg) return null;
|
|
1592
|
+
args.push(arg);
|
|
1593
|
+
}
|
|
1594
|
+
return { op: rec.op, args };
|
|
1595
|
+
}
|
|
1596
|
+
case "sub":
|
|
1597
|
+
case "div": {
|
|
1598
|
+
const left = normalizeCalc(rec.left, depth + 1);
|
|
1599
|
+
const right = normalizeCalc(rec.right, depth + 1);
|
|
1600
|
+
return left && right ? { op: rec.op, left, right } : null;
|
|
1601
|
+
}
|
|
1602
|
+
case "round": {
|
|
1603
|
+
const arg = normalizeCalc(rec.arg, depth + 1);
|
|
1604
|
+
if (!arg) return null;
|
|
1605
|
+
return cleanObject({
|
|
1606
|
+
op: "round",
|
|
1607
|
+
arg,
|
|
1608
|
+
decimals: finite(rec.decimals) === void 0 ? void 0 : int(rec.decimals, 0, 6, 0)
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
case "if": {
|
|
1612
|
+
if (!Array.isArray(rec.when)) return null;
|
|
1613
|
+
const when2 = conditions(rec.when) ?? [];
|
|
1614
|
+
if (when2.length !== rec.when.length) return null;
|
|
1615
|
+
const then = normalizeCalc(rec.then, depth + 1);
|
|
1616
|
+
const els = normalizeCalc(rec.else, depth + 1);
|
|
1617
|
+
return then && els ? { op: "if", when: when2, then, else: els } : null;
|
|
1618
|
+
}
|
|
1619
|
+
default:
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
function collectCalcRefs(expr, valueRefs, whenRefs) {
|
|
1624
|
+
switch (expr.op) {
|
|
1625
|
+
case "value":
|
|
1626
|
+
valueRefs.add(expr.fieldId);
|
|
1627
|
+
return;
|
|
1628
|
+
case "const":
|
|
1629
|
+
return;
|
|
1630
|
+
case "add":
|
|
1631
|
+
case "mul":
|
|
1632
|
+
case "min":
|
|
1633
|
+
case "max":
|
|
1634
|
+
for (const arg of expr.args) collectCalcRefs(arg, valueRefs, whenRefs);
|
|
1635
|
+
return;
|
|
1636
|
+
case "sub":
|
|
1637
|
+
case "div":
|
|
1638
|
+
collectCalcRefs(expr.left, valueRefs, whenRefs);
|
|
1639
|
+
collectCalcRefs(expr.right, valueRefs, whenRefs);
|
|
1640
|
+
return;
|
|
1641
|
+
case "round":
|
|
1642
|
+
collectCalcRefs(expr.arg, valueRefs, whenRefs);
|
|
1643
|
+
return;
|
|
1644
|
+
case "if":
|
|
1645
|
+
for (const cond of expr.when) whenRefs.add(cond.fieldId);
|
|
1646
|
+
collectCalcRefs(expr.then, valueRefs, whenRefs);
|
|
1647
|
+
collectCalcRefs(expr.else, valueRefs, whenRefs);
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
function validateCalculatedFields(pages) {
|
|
1652
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
1653
|
+
for (const page of pages) {
|
|
1654
|
+
for (const block of page.blocks) if (isField(block)) kinds.set(block.id, block.kind);
|
|
1655
|
+
}
|
|
1656
|
+
const deps = /* @__PURE__ */ new Map();
|
|
1657
|
+
for (const page of pages) {
|
|
1658
|
+
for (const block of page.blocks) {
|
|
1659
|
+
if (block.kind !== "calculated") continue;
|
|
1660
|
+
const calc = normalizeCalc(block.calc);
|
|
1661
|
+
if (!calc) {
|
|
1662
|
+
return `Field ${block.id} has a malformed calc expression \u2014 build it from value/const/add/sub/mul/div/min/max/round/if nodes`;
|
|
1663
|
+
}
|
|
1664
|
+
block.calc = calc;
|
|
1665
|
+
const valueRefs = /* @__PURE__ */ new Set();
|
|
1666
|
+
const whenRefs = /* @__PURE__ */ new Set();
|
|
1667
|
+
collectCalcRefs(calc, valueRefs, whenRefs);
|
|
1668
|
+
for (const ref of valueRefs) {
|
|
1669
|
+
const kind = kinds.get(ref);
|
|
1670
|
+
if (kind === void 0) {
|
|
1671
|
+
return `Calculated field ${block.id} references a missing field: ${ref} \u2014 remove the reference or restore that field`;
|
|
1672
|
+
}
|
|
1673
|
+
if (!CALC_OPERAND_KINDS.includes(kind)) {
|
|
1674
|
+
return `Calculated field ${block.id} references non-numeric field ${ref} (${kind}) \u2014 calculations can read number, rating, linear_scale, and calculated fields only`;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
deps.set(
|
|
1678
|
+
block.id,
|
|
1679
|
+
[...valueRefs, ...whenRefs].filter((ref) => kinds.get(ref) === "calculated")
|
|
1680
|
+
);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
const state = /* @__PURE__ */ new Map();
|
|
1684
|
+
const visit = (id, path) => {
|
|
1685
|
+
const mark = state.get(id);
|
|
1686
|
+
if (mark === "done") return null;
|
|
1687
|
+
if (mark === "visiting") {
|
|
1688
|
+
const chain = [...path.slice(path.indexOf(id)), id].join(" \u2192 ");
|
|
1689
|
+
return `Calculated field ${id} depends on its own result (${chain}) \u2014 break the cycle so the value can be computed`;
|
|
1690
|
+
}
|
|
1691
|
+
state.set(id, "visiting");
|
|
1692
|
+
for (const dep of deps.get(id) ?? []) {
|
|
1693
|
+
const error = visit(dep, [...path, id]);
|
|
1694
|
+
if (error) return error;
|
|
1695
|
+
}
|
|
1696
|
+
state.set(id, "done");
|
|
1697
|
+
return null;
|
|
1698
|
+
};
|
|
1699
|
+
for (const id of deps.keys()) {
|
|
1700
|
+
const error = visit(id, []);
|
|
1701
|
+
if (error) return error;
|
|
1702
|
+
}
|
|
1703
|
+
return null;
|
|
1704
|
+
}
|
|
1705
|
+
var MAX_CALC_FIELDS = 100;
|
|
1706
|
+
var MAX_CALC_TOTAL_NODES = 5e3;
|
|
1707
|
+
function countCalcNodes(expr) {
|
|
1708
|
+
switch (expr.op) {
|
|
1709
|
+
case "value":
|
|
1710
|
+
case "const":
|
|
1711
|
+
return 1;
|
|
1712
|
+
case "add":
|
|
1713
|
+
case "mul":
|
|
1714
|
+
case "min":
|
|
1715
|
+
case "max":
|
|
1716
|
+
return 1 + expr.args.reduce((n, arg) => n + countCalcNodes(arg), 0);
|
|
1717
|
+
case "sub":
|
|
1718
|
+
case "div":
|
|
1719
|
+
return 1 + countCalcNodes(expr.left) + countCalcNodes(expr.right);
|
|
1720
|
+
case "round":
|
|
1721
|
+
return 1 + countCalcNodes(expr.arg);
|
|
1722
|
+
case "if":
|
|
1723
|
+
return 1 + countCalcNodes(expr.then) + countCalcNodes(expr.else);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
function calcComplexityError(pages) {
|
|
1727
|
+
let calcFields = 0;
|
|
1728
|
+
let totalNodes = 0;
|
|
1729
|
+
for (const page of pages) {
|
|
1730
|
+
for (const block of page.blocks) {
|
|
1731
|
+
if (block.kind !== "calculated") continue;
|
|
1732
|
+
calcFields += 1;
|
|
1733
|
+
totalNodes += countCalcNodes(block.calc);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
if (calcFields > MAX_CALC_FIELDS) {
|
|
1737
|
+
return `Form has ${calcFields} calculated fields \u2014 the maximum is ${MAX_CALC_FIELDS}; remove some or consolidate them into fewer expressions`;
|
|
1738
|
+
}
|
|
1739
|
+
if (totalNodes > MAX_CALC_TOTAL_NODES) {
|
|
1740
|
+
return `Calculated expressions total ${totalNodes} operations \u2014 the maximum is ${MAX_CALC_TOTAL_NODES} across the form; simplify or split up the calculations`;
|
|
1741
|
+
}
|
|
1742
|
+
return null;
|
|
1743
|
+
}
|
|
1509
1744
|
function baseBlock(rec) {
|
|
1510
1745
|
return {
|
|
1511
1746
|
id: str(rec.id, 128),
|
|
@@ -1617,7 +1852,20 @@ function normalizeBlock(input) {
|
|
|
1617
1852
|
maxLength: finite(rec.maxLength) === void 0 ? void 0 : int(rec.maxLength, 1, 2e4, 2e4)
|
|
1618
1853
|
});
|
|
1619
1854
|
case "number":
|
|
1620
|
-
return cleanObject({
|
|
1855
|
+
return cleanObject({
|
|
1856
|
+
...baseField(rec, kind),
|
|
1857
|
+
min: finite(rec.min),
|
|
1858
|
+
max: finite(rec.max),
|
|
1859
|
+
// Display-only (decision 1 in the number-formatting contract) —
|
|
1860
|
+
// mirrors the calculated field's decimals/prefix/suffix carry-through
|
|
1861
|
+
// exactly, plus notation which calculated doesn't have.
|
|
1862
|
+
decimals: finite(rec.decimals) === void 0 ? void 0 : int(rec.decimals, 0, 6, 0),
|
|
1863
|
+
prefix: optionalAffix(rec.prefix, 100),
|
|
1864
|
+
suffix: optionalAffix(rec.suffix, 100),
|
|
1865
|
+
// The only supported values; anything else drops silently rather than
|
|
1866
|
+
// erroring (an old/garbage value is just "no grouping").
|
|
1867
|
+
notation: rec.notation === "grouped" ? "grouped" : rec.notation === "grouped-comma" ? "grouped-comma" : rec.notation === "grouped-dot" ? "grouped-dot" : void 0
|
|
1868
|
+
});
|
|
1621
1869
|
case "select":
|
|
1622
1870
|
case "multi_select":
|
|
1623
1871
|
case "dropdown": {
|
|
@@ -1681,6 +1929,21 @@ function normalizeBlock(input) {
|
|
|
1681
1929
|
paramName: optionalStr(rec.paramName, 128),
|
|
1682
1930
|
defaultValue: optionalStr(rec.defaultValue, 1e3)
|
|
1683
1931
|
});
|
|
1932
|
+
case "calculated":
|
|
1933
|
+
return cleanObject({
|
|
1934
|
+
...baseField(rec, kind),
|
|
1935
|
+
// A derived value isn't answerable, so it can't be required (the
|
|
1936
|
+
// hidden-field precedent) — a required one could never be satisfied.
|
|
1937
|
+
required: false,
|
|
1938
|
+
// Kept raw here on purpose: the calc AST is normalized and structurally
|
|
1939
|
+
// validated in normalizeFormSchema once the full field universe is
|
|
1940
|
+
// known (missing refs, operand kinds, cycles are HARD errors there — a
|
|
1941
|
+
// calculated field must never be silently dropped or degraded).
|
|
1942
|
+
calc: rec.calc,
|
|
1943
|
+
decimals: finite(rec.decimals) === void 0 ? void 0 : int(rec.decimals, 0, 6, 0),
|
|
1944
|
+
prefix: optionalAffix(rec.prefix, 100),
|
|
1945
|
+
suffix: optionalAffix(rec.suffix, 100)
|
|
1946
|
+
});
|
|
1684
1947
|
case "file_upload":
|
|
1685
1948
|
return cleanObject({
|
|
1686
1949
|
...baseField(rec, kind),
|
|
@@ -1719,6 +1982,22 @@ function normalizeFormSchema(input) {
|
|
|
1719
1982
|
return { id: str(page.id, 128), title: optionalStr(page.title, 500), blocks, next: page.next };
|
|
1720
1983
|
});
|
|
1721
1984
|
const targetIds = new Set(rebuilt.map((p) => p.id));
|
|
1985
|
+
const calcRefs = /* @__PURE__ */ new Map();
|
|
1986
|
+
for (const page of rebuilt) {
|
|
1987
|
+
for (const block of page.blocks) {
|
|
1988
|
+
if (block.kind !== "calculated") continue;
|
|
1989
|
+
const refs = /* @__PURE__ */ new Set();
|
|
1990
|
+
const calc = normalizeCalc(block.calc);
|
|
1991
|
+
if (calc) {
|
|
1992
|
+
const valueRefs = /* @__PURE__ */ new Set();
|
|
1993
|
+
const whenRefs = /* @__PURE__ */ new Set();
|
|
1994
|
+
collectCalcRefs(calc, valueRefs, whenRefs);
|
|
1995
|
+
for (const ref of valueRefs) refs.add(ref);
|
|
1996
|
+
for (const ref of whenRefs) refs.add(ref);
|
|
1997
|
+
}
|
|
1998
|
+
calcRefs.set(block.id, refs);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
1722
2001
|
const priorFieldIds = /* @__PURE__ */ new Set();
|
|
1723
2002
|
const pages = rebuilt.map((page) => {
|
|
1724
2003
|
for (const block of page.blocks) {
|
|
@@ -1728,7 +2007,7 @@ function normalizeFormSchema(input) {
|
|
|
1728
2007
|
id: page.id,
|
|
1729
2008
|
title: page.title,
|
|
1730
2009
|
blocks: page.blocks,
|
|
1731
|
-
next: jumpRules(page.next, targetIds, page.id, new Set(priorFieldIds))
|
|
2010
|
+
next: jumpRules(page.next, targetIds, page.id, new Set(priorFieldIds), calcRefs)
|
|
1732
2011
|
});
|
|
1733
2012
|
});
|
|
1734
2013
|
const pageIds = /* @__PURE__ */ new Set();
|
|
@@ -1763,6 +2042,10 @@ function normalizeFormSchema(input) {
|
|
|
1763
2042
|
}
|
|
1764
2043
|
}
|
|
1765
2044
|
}
|
|
2045
|
+
const calcError = validateCalculatedFields(pages);
|
|
2046
|
+
if (calcError) return { ok: false, error: calcError };
|
|
2047
|
+
const calcComplexity = calcComplexityError(pages);
|
|
2048
|
+
if (calcComplexity) return { ok: false, error: calcComplexity };
|
|
1766
2049
|
if (pages.every((page) => page.blocks.length === 0)) {
|
|
1767
2050
|
return { ok: false, error: "Schema must contain at least one valid block" };
|
|
1768
2051
|
}
|
|
@@ -1788,6 +2071,7 @@ var MASK = 256 - 256 % ALPHABET.length;
|
|
|
1788
2071
|
var S3_PART_TIMEOUT_MS = 5 * 6e4;
|
|
1789
2072
|
var UPLOAD_CONTROL_TIMEOUT_MS = 10 * 6e4;
|
|
1790
2073
|
var CLIENT_HEADER = { "X-Fillo-Client": `@usefillo/core@${FILLO_SDK_VERSION}` };
|
|
2074
|
+
var DARK_LUMINANCE_THRESHOLD = Math.sqrt(1.05 * 0.05) - 0.05;
|
|
1791
2075
|
var FilloJsxError = class extends Error {
|
|
1792
2076
|
constructor(code, message) {
|
|
1793
2077
|
super(`[fillo] ${message} \u2014 https://fillo.so/docs/troubleshooting#jsx-${code.toLowerCase()}`);
|
|
@@ -1818,7 +2102,7 @@ var JSX_BLOCK_SPECS = [
|
|
|
1818
2102
|
field("Email", "email", ["maxLength"]),
|
|
1819
2103
|
field("Url", "url", ["maxLength"]),
|
|
1820
2104
|
field("Phone", "phone", ["defaultCountry"]),
|
|
1821
|
-
field("Number", "number", ["min", "max"]),
|
|
2105
|
+
field("Number", "number", ["min", "max", "decimals", "prefix", "suffix", "notation"]),
|
|
1822
2106
|
field("Select", "select", CHOICE_EXTRA, { optionChildren: true }),
|
|
1823
2107
|
field("MultiSelect", "multi_select", CHOICE_EXTRA, { optionChildren: true }),
|
|
1824
2108
|
field("Dropdown", "dropdown", CHOICE_EXTRA, { optionChildren: true }),
|
|
@@ -1831,6 +2115,7 @@ var JSX_BLOCK_SPECS = [
|
|
|
1831
2115
|
field("Date", "date", []),
|
|
1832
2116
|
field("FileUpload", "file_upload", ["maxFiles", "maxFileSizeMb", "accept"]),
|
|
1833
2117
|
field("Hidden", "hidden", ["paramName", "defaultValue"]),
|
|
2118
|
+
field("Calculated", "calculated", ["calc", "decimals", "prefix", "suffix"]),
|
|
1834
2119
|
field("Custom", "custom", ["component", "config"]),
|
|
1835
2120
|
content("Heading", "heading", true),
|
|
1836
2121
|
content("Paragraph", "paragraph", true),
|
|
@@ -2024,7 +2309,8 @@ Login failed (${r.status}).`);
|
|
|
2024
2309
|
return;
|
|
2025
2310
|
}
|
|
2026
2311
|
if (body.error === "slow_down") interval += 2e3;
|
|
2027
|
-
else if (body.error && body.error !== "authorization_pending")
|
|
2312
|
+
else if (body.error && body.error !== "authorization_pending")
|
|
2313
|
+
die(`
|
|
2028
2314
|
Login failed: ${body.error}`);
|
|
2029
2315
|
}
|
|
2030
2316
|
die("\nLogin timed out \u2014 run `fillo login` again.");
|
|
@@ -2087,9 +2373,7 @@ async function publish(handle, flags) {
|
|
|
2087
2373
|
const res = await api(`/cli/forms/${encodeURIComponent(handle)}/publish`, {
|
|
2088
2374
|
method: "POST",
|
|
2089
2375
|
token: requireToken(),
|
|
2090
|
-
body: JSON.stringify(
|
|
2091
|
-
flags["allow-breaking"] === true ? { allowBreaking: true } : {}
|
|
2092
|
-
)
|
|
2376
|
+
body: JSON.stringify(flags["allow-breaking"] === true ? { allowBreaking: true } : {})
|
|
2093
2377
|
});
|
|
2094
2378
|
if (res.status === 401) die("Token invalid \u2014 run `fillo login` again.");
|
|
2095
2379
|
if (res.status === 404) {
|
|
@@ -2116,7 +2400,9 @@ async function publish(handle, flags) {
|
|
|
2116
2400
|
process.exit(1);
|
|
2117
2401
|
}
|
|
2118
2402
|
if (res.status === 409 && typeof body.warningUrl === "string" && body.warningUrl) {
|
|
2119
|
-
console.error(
|
|
2403
|
+
console.error(
|
|
2404
|
+
`\x1B[31m\u2717\x1B[0m ${terminalText(body.error ?? `publish failed (${res.status}).`)}`
|
|
2405
|
+
);
|
|
2120
2406
|
console.error(` Storage settings: ${terminalText(body.warningUrl)}`);
|
|
2121
2407
|
process.exit(1);
|
|
2122
2408
|
}
|
|
@@ -2284,6 +2570,8 @@ function printPushed(formId, url, updated, draft = false) {
|
|
|
2284
2570
|
if (draft) console.log(` ${dim("Review and publish it from the Fillo dashboard.")}`);
|
|
2285
2571
|
else if (url) console.log(` Live at ${terminalText(url)}`);
|
|
2286
2572
|
console.log(` Embed: <FilloForm formId="${terminalText(formId)}" />
|
|
2573
|
+
`);
|
|
2574
|
+
console.log(` Verify: [data-fillo-form-id="${terminalText(formId)}"] on the actual app route
|
|
2287
2575
|
`);
|
|
2288
2576
|
}
|
|
2289
2577
|
function syncError(response, body, credential) {
|
|
@@ -2329,6 +2617,8 @@ function printSynced(body, requestedStage) {
|
|
|
2329
2617
|
if (body.warning) console.log(` ${dim(`Before publishing: ${body.warning}`)}`);
|
|
2330
2618
|
if (body.warningUrl) console.log(` Storage settings: ${terminalText(body.warningUrl)}`);
|
|
2331
2619
|
console.log(` Embed: <FilloForm formId="${terminalText(formId)}" />
|
|
2620
|
+
`);
|
|
2621
|
+
console.log(` Verify: [data-fillo-form-id="${terminalText(formId)}"] on the actual app route
|
|
2332
2622
|
`);
|
|
2333
2623
|
}
|
|
2334
2624
|
async function pushWithSync(items, credential, requestedStage) {
|
|
@@ -2374,7 +2664,9 @@ async function push(file, flags) {
|
|
|
2374
2664
|
);
|
|
2375
2665
|
}
|
|
2376
2666
|
if (!syncToken && !accountToken && stage) {
|
|
2377
|
-
die(
|
|
2667
|
+
die(
|
|
2668
|
+
`To stage changes, run \`fillo login\` or set ${SYNC_TOKEN_ENV} from Settings > Developers.`
|
|
2669
|
+
);
|
|
2378
2670
|
}
|
|
2379
2671
|
const handle = typeof flags.handle === "string" ? flags.handle : void 0;
|
|
2380
2672
|
const items = toPushItems(await loadSchema(file, flags["allow-code"] === true), handle);
|
|
@@ -2419,7 +2711,9 @@ async function push(file, flags) {
|
|
|
2419
2711
|
const body = await readJson(res);
|
|
2420
2712
|
if (!res.ok || !body.formId) {
|
|
2421
2713
|
if (res.status === 409 && typeof body.warningUrl === "string" && body.warningUrl) {
|
|
2422
|
-
console.error(
|
|
2714
|
+
console.error(
|
|
2715
|
+
`\x1B[31m\u2717\x1B[0m ${terminalText(body.error ?? `push failed (${res.status}).`)}`
|
|
2716
|
+
);
|
|
2423
2717
|
console.error(` Storage settings: ${terminalText(body.warningUrl)}`);
|
|
2424
2718
|
process.exit(1);
|
|
2425
2719
|
}
|
|
@@ -2438,7 +2732,9 @@ async function push(file, flags) {
|
|
|
2438
2732
|
async function init(flags) {
|
|
2439
2733
|
const email3 = typeof flags.email === "string" ? flags.email.trim() : "";
|
|
2440
2734
|
if (!email3) {
|
|
2441
|
-
die(
|
|
2735
|
+
die(
|
|
2736
|
+
"Usage: fillo init --email you@company.com (or run `fillo login` for an existing account)."
|
|
2737
|
+
);
|
|
2442
2738
|
}
|
|
2443
2739
|
const res = await api("/workspaces/provision", {
|
|
2444
2740
|
method: "POST",
|
|
@@ -2448,13 +2744,17 @@ async function init(flags) {
|
|
|
2448
2744
|
if (!res.ok || !body.key) die(body.error ?? `init failed (${res.status}).`);
|
|
2449
2745
|
const { claimUrl: _legacyWorkspaceLink, ...current } = readConfig();
|
|
2450
2746
|
writeConfig({ ...current, pk: body.key });
|
|
2451
|
-
console.log(
|
|
2452
|
-
|
|
2747
|
+
console.log(
|
|
2748
|
+
`
|
|
2749
|
+
\x1B[32m\u2713\x1B[0m Workspace ready. A workspace link was sent to ${bold(terminalText(email3))}.`
|
|
2750
|
+
);
|
|
2453
2751
|
if (body.limits) {
|
|
2454
2752
|
const expiry = Date.parse(body.limits.expiresAt);
|
|
2455
2753
|
if (Number.isFinite(expiry)) {
|
|
2456
2754
|
const days = Math.max(1, Math.round((expiry - Date.now()) / 864e5));
|
|
2457
|
-
console.log(
|
|
2755
|
+
console.log(
|
|
2756
|
+
` ${dim(`Preview: up to ${body.limits.responses} responses for ~${days} days.`)}`
|
|
2757
|
+
);
|
|
2458
2758
|
}
|
|
2459
2759
|
}
|
|
2460
2760
|
console.log(`
|
|
@@ -2519,7 +2819,8 @@ async function agent(subcommand, flags) {
|
|
|
2519
2819
|
if (subcommand === "event") {
|
|
2520
2820
|
const status2 = enumFlag(flags, "status", AGENT_EVENT_STATUSES);
|
|
2521
2821
|
const message = flagString(flags, "message");
|
|
2522
|
-
if (!status2)
|
|
2822
|
+
if (!status2)
|
|
2823
|
+
die('Usage: fillo agent event --status <status> --message "<what the human does next>"');
|
|
2523
2824
|
const action = enumFlag(flags, "action", AGENT_ACTIONS);
|
|
2524
2825
|
const formStatus = enumFlag(flags, "form-status", FORM_STATUSES);
|
|
2525
2826
|
const formId = optionalStringFlag(flags, "form-id");
|
|
@@ -2645,9 +2946,7 @@ function pathEntryExists(path) {
|
|
|
2645
2946
|
var MANAGED_SKILL_MARKER = ".fillo-managed.json";
|
|
2646
2947
|
function isCliManagedFilloSkill(target) {
|
|
2647
2948
|
try {
|
|
2648
|
-
const marker = JSON.parse(
|
|
2649
|
-
readFileSync(join(target, MANAGED_SKILL_MARKER), "utf8")
|
|
2650
|
-
);
|
|
2949
|
+
const marker = JSON.parse(readFileSync(join(target, MANAGED_SKILL_MARKER), "utf8"));
|
|
2651
2950
|
return marker.managedBy === "@usefillo/cli" && marker.skill === "build-with-fillo" && marker.format === 1 && marker.allowAutomaticUpdates === true;
|
|
2652
2951
|
} catch {
|
|
2653
2952
|
return false;
|
|
@@ -2702,19 +3001,14 @@ function installSkill(flags) {
|
|
|
2702
3001
|
if (!existsSync(BUNDLED_SKILL_DIR)) {
|
|
2703
3002
|
die("This CLI package is missing its bundled Fillo skill. Reinstall @usefillo/cli@latest.");
|
|
2704
3003
|
}
|
|
2705
|
-
const destinations = Array.from(
|
|
2706
|
-
new Set(skillDirectories)
|
|
2707
|
-
).map((skillDirectory) => {
|
|
3004
|
+
const destinations = Array.from(new Set(skillDirectories)).map((skillDirectory) => {
|
|
2708
3005
|
const skillsRoot = resolve(scopeRoot, skillDirectory);
|
|
2709
3006
|
const target = join(skillsRoot, "build-with-fillo");
|
|
2710
3007
|
const relativeSkillsRoot = relative(scopeRoot, skillsRoot);
|
|
2711
3008
|
if (relativeSkillsRoot === ".." || relativeSkillsRoot.startsWith(`..${sep}`) || isAbsolute(relativeSkillsRoot)) {
|
|
2712
3009
|
die("The skill destination must stay inside the selected project or home scope.");
|
|
2713
3010
|
}
|
|
2714
|
-
const destinationParts = [
|
|
2715
|
-
...relativeSkillsRoot.split(sep).filter(Boolean),
|
|
2716
|
-
"build-with-fillo"
|
|
2717
|
-
];
|
|
3011
|
+
const destinationParts = [...relativeSkillsRoot.split(sep).filter(Boolean), "build-with-fillo"];
|
|
2718
3012
|
assertSafeSkillDestination(scopeRoot, destinationParts);
|
|
2719
3013
|
const targetExists = pathEntryExists(target);
|
|
2720
3014
|
const current = targetExists && sameSkillBundle(BUNDLED_SKILL_DIR, target);
|
|
@@ -2735,10 +3029,7 @@ function installSkill(flags) {
|
|
|
2735
3029
|
if (destination.current) continue;
|
|
2736
3030
|
mkdirSync(destination.skillsRoot, { recursive: true });
|
|
2737
3031
|
assertSafeSkillDestination(scopeRoot, destination.destinationParts.slice(0, 2));
|
|
2738
|
-
const staging = join(
|
|
2739
|
-
destination.skillsRoot,
|
|
2740
|
-
`.build-with-fillo.install-${randomUUID()}`
|
|
2741
|
-
);
|
|
3032
|
+
const staging = join(destination.skillsRoot, `.build-with-fillo.install-${randomUUID()}`);
|
|
2742
3033
|
try {
|
|
2743
3034
|
cpSync(BUNDLED_SKILL_DIR, staging, { recursive: true, errorOnExist: true });
|
|
2744
3035
|
assertSafeSkillDestination(scopeRoot, destination.destinationParts);
|
|
@@ -2784,18 +3075,15 @@ async function postAgentEvent(apiBase, run, token, body) {
|
|
|
2784
3075
|
}
|
|
2785
3076
|
async function attachAgentAccount(apiBase, run, progressToken) {
|
|
2786
3077
|
const accountToken = requireTokenFor(apiBase);
|
|
2787
|
-
const res = await fetch(
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
2797
|
-
}
|
|
2798
|
-
);
|
|
3078
|
+
const res = await fetch(`${apiBase}/api/v1/agent-runs/${encodeURIComponent(run)}/account`, {
|
|
3079
|
+
method: "POST",
|
|
3080
|
+
redirect: "error",
|
|
3081
|
+
headers: {
|
|
3082
|
+
Authorization: `Bearer ${accountToken}`,
|
|
3083
|
+
"X-Fillo-Agent-Run-Token": progressToken
|
|
3084
|
+
},
|
|
3085
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
3086
|
+
});
|
|
2799
3087
|
const payload = await res.json().catch(() => ({}));
|
|
2800
3088
|
if (!res.ok) {
|
|
2801
3089
|
const hint = res.status === 401 ? " Run `fillo login` and try again." : "";
|
|
@@ -2925,10 +3213,7 @@ var FLAGS_BY_COMMAND = {
|
|
|
2925
3213
|
version: []
|
|
2926
3214
|
};
|
|
2927
3215
|
function validateFlags(command, flags) {
|
|
2928
|
-
const allowed = /* @__PURE__ */ new Set([
|
|
2929
|
-
...GLOBAL_FLAGS,
|
|
2930
|
-
...command ? FLAGS_BY_COMMAND[command] ?? [] : []
|
|
2931
|
-
]);
|
|
3216
|
+
const allowed = /* @__PURE__ */ new Set([...GLOBAL_FLAGS, ...command ? FLAGS_BY_COMMAND[command] ?? [] : []]);
|
|
2932
3217
|
for (const [key, value] of Object.entries(flags)) {
|
|
2933
3218
|
if (!allowed.has(key)) die(`Unknown flag for ${command ?? "fillo"}: --${key}`);
|
|
2934
3219
|
if (BOOLEAN_FLAGS.has(key)) {
|
|
@@ -27,6 +27,11 @@ Never require a provider-specific agent command.
|
|
|
27
27
|
`formId`.
|
|
28
28
|
- Fully custom UI: use `FilloProvider` and hooks in React, or
|
|
29
29
|
`createFormController()` elsewhere.
|
|
30
|
+
Every interactive embed must have exactly one submission identity: a
|
|
31
|
+
published `formId`, or a `defineForm()` / `<Fillo.Form>` value plus a
|
|
32
|
+
client. A plain `FormSchema` plus a client is not a code-defined form and
|
|
33
|
+
cannot resolve a target. Use explicit `renderOnly` only for a deliberately
|
|
34
|
+
non-submitting UI preview.
|
|
30
35
|
3. Ask only for missing product decisions that change the result: purpose,
|
|
31
36
|
placement, required questions or files, conditional behavior, and what
|
|
32
37
|
happens after submit. Infer routine implementation details from the repo.
|
|
@@ -76,7 +81,9 @@ could not be verified.
|
|
|
76
81
|
- Keep conditional questions in schema data with `visibleIf`; never vary the
|
|
77
82
|
schema structure per visitor.
|
|
78
83
|
- Pass a client to code-defined forms that must sync or collect responses.
|
|
79
|
-
|
|
84
|
+
Never render a plain schema with only a client: add the actual returned
|
|
85
|
+
`formId`, convert the schema to `defineForm()`, or opt into `renderOnly` for
|
|
86
|
+
a deliberately transportless preview.
|
|
80
87
|
- Import the default stylesheet unless the app deliberately owns every form
|
|
81
88
|
style. Keep overrides local and preserve accessible labels, errors, focus,
|
|
82
89
|
disabled states, and keyboard behavior.
|
|
@@ -93,7 +100,14 @@ identity secrets, workspace capability links, or short-lived run tokens.
|
|
|
93
100
|
## Verify and hand off
|
|
94
101
|
|
|
95
102
|
1. Run the host repository's typecheck and proportionate build or tests.
|
|
96
|
-
|
|
103
|
+
A successful build, public API check, or hosted Fillo page does not prove
|
|
104
|
+
that the host app embedded the correct form.
|
|
105
|
+
2. Open the actual host-app route in a browser. Confirm its active root has
|
|
106
|
+
`data-fillo-form-id="<actual returned formId>"`; do not accept a schema
|
|
107
|
+
handle, a hosted `/f/...` page, or a different form id as evidence. If the
|
|
108
|
+
form includes files, confirm the embedded picker is enabled and shows its
|
|
109
|
+
browse/drop affordance. Then inspect desktop and mobile states: loading,
|
|
110
|
+
validation, conditional paths,
|
|
97
111
|
keyboard focus, error, success, and narrow text. Off localhost (tunnel,
|
|
98
112
|
staging), the cosmetic-only `preview` prop/attribute shows the same
|
|
99
113
|
developer chrome — see
|
|
@@ -42,6 +42,27 @@ Keep Fillo JSX schema authoring in a `"use client"` module. `onSubmitted` is
|
|
|
42
42
|
for navigation, analytics, or another host-side follow-up after storage; it is
|
|
43
43
|
not the response transport.
|
|
44
44
|
|
|
45
|
+
The identity combinations are intentional and exhaustive:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// Published/dashboard/CLI-owned schema.
|
|
49
|
+
<FilloForm formId={actualReturnedFormId} />
|
|
50
|
+
|
|
51
|
+
// Inline snapshot of that same published form.
|
|
52
|
+
<FilloForm form={schema} formId={actualReturnedFormId} />
|
|
53
|
+
|
|
54
|
+
// App-owned schema with a stable defineForm() identity.
|
|
55
|
+
<FilloForm form={codeForm} client={client} />
|
|
56
|
+
|
|
57
|
+
// Deliberately local UI preview: cannot submit, upload, or save progress.
|
|
58
|
+
<FilloForm form={schema} renderOnly />
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Never use `<FilloForm form={plainSchema} client={client} />`. A plain schema
|
|
62
|
+
has no stable Fillo identity, so the client cannot know which form owns its
|
|
63
|
+
responses or uploads. Current SDK types reject this combination and the
|
|
64
|
+
runtime reports `form_target_required` for JavaScript callers.
|
|
65
|
+
|
|
45
66
|
## Vite apps
|
|
46
67
|
|
|
47
68
|
Vite exposes only `VITE_`-prefixed env vars to browser code, through
|
|
@@ -110,11 +131,11 @@ unconnected. On a tunnel, staging deploy, or local production build that
|
|
|
110
131
|
chrome stays quiet; opt in with the cosmetic-only `preview` flag:
|
|
111
132
|
|
|
112
133
|
```tsx
|
|
113
|
-
<FilloForm form={
|
|
134
|
+
<FilloForm form={feedbackCodeForm} client={client}
|
|
114
135
|
preview={process.env.NEXT_PUBLIC_STAGE !== "production"} />
|
|
115
136
|
```
|
|
116
137
|
|
|
117
|
-
DOM equivalents: `renderForm(el, { form, client, preview: true })` or
|
|
138
|
+
DOM equivalents: `renderForm(el, { form: feedbackCodeForm, client, preview: true })` or
|
|
118
139
|
`<fillo-form data-preview>`. `data-preview="false"` and `data-preview="0"`
|
|
119
140
|
count as off (frameworks stringify booleans onto data-* attributes); any other
|
|
120
141
|
presence is on. `preview` renders a visible "Preview" badge and
|
|
@@ -123,6 +144,10 @@ submissions authenticate with a credential, never a prop. Remove it before
|
|
|
123
144
|
respondents see the page. Pass `devNotices={false}` (`devNotices: false` in
|
|
124
145
|
DOM) when the page provides its own context; the badge stays.
|
|
125
146
|
|
|
147
|
+
`preview` is only developer chrome; it does not make a transportless form
|
|
148
|
+
valid. Use `renderOnly` (or `<fillo-form data-render-only>`) when the surface is
|
|
149
|
+
intentionally local and must not submit or upload.
|
|
150
|
+
|
|
126
151
|
## Styling and custom UI
|
|
127
152
|
|
|
128
153
|
Use the lowest-control surface that satisfies the request:
|
|
@@ -6,6 +6,7 @@ Confirm the exact error and installed package version before changing code.
|
|
|
6
6
|
| --- | --- |
|
|
7
7
|
| Published-id embed returns 404 | Confirm the id or slug and that the form is published. Do not reveal whether an inaccessible draft exists. |
|
|
8
8
|
| Code-defined form renders but cannot save | Pass a client, keep a stable id, verify the key belongs to the intended workspace, and check expected-origin restrictions. |
|
|
9
|
+
| `form_target_required` or the embed renders unavailable while the hosted page works | The host app rendered a plain schema without a submission identity. Pass the actual returned `formId`, or use `defineForm()` plus a client. Use `renderOnly` only for a deliberate non-submitting preview. Verify the app route exposes the same id in `data-fillo-form-id`. |
|
|
9
10
|
| Publishable key is `undefined` in a Vite app | Read `import.meta.env.VITE_FILLO_KEY`, not `process.env`; declare it in `src/vite-env.d.ts` under strict TypeScript and restart the dev server after `.env` changes. |
|
|
10
11
|
| Schema write reports `trusted_sync_required` | Log in and use `fillo push --stage`, or use a server-held `FILLO_SYNC_TOKEN`. Do not weaken the workspace policy. |
|
|
11
12
|
| `fillo push --stage` has nothing to stage | The published schema already matches; do not create another form. |
|
|
@@ -13,6 +14,7 @@ Confirm the exact error and installed package version before changing code.
|
|
|
13
14
|
| File form cannot publish | Connect supported storage and verify the provider before retrying publish. |
|
|
14
15
|
| Test submit only says "This form is unavailable." | Run on localhost, or set the cosmetic-only `preview` prop / `data-preview` attribute: dev chrome shows the real failure with its machine code (for example `form_not_published`) and the connect-storage link. |
|
|
15
16
|
| Upload field says "Connect file storage to enable uploads" | Expected dev-chrome pre-emption: sync reported `storage_required`. Open the linked storage settings, connect a destination, then publish. |
|
|
17
|
+
| Upload field says uploads are unavailable in a render-only preview | The embed explicitly disabled transport. Remove `renderOnly` and provide a real identity path before testing uploads. |
|
|
16
18
|
| DOM form duplicates after navigation | Mount after the target exists and call `destroy()` in cleanup. |
|
|
17
19
|
| React context or hook error | Keep hooks inside `FilloForm` or `FilloProvider` and check for two installed copies of `@usefillo/react`. |
|
|
18
20
|
| Fillo JSX fails in Next.js | Move schema JSX to a `"use client"` module; use object-form `defineForm()` for framework-neutral schema. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usefillo/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Create and publish Fillo forms, and install the Fillo Agent Skill.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@types/node": "^22.10.0",
|
|
27
27
|
"tsup": "^8.4.0",
|
|
28
28
|
"typescript": "^5.8.3",
|
|
29
|
-
"@usefillo/core": "0.
|
|
29
|
+
"@usefillo/core": "0.12.0"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsup && node scripts/copy-skill.mjs",
|