@xnetjs/data 0.3.0 → 0.5.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/{chunk-DWEXKD6E.js → chunk-FB63TZVI.js} +7 -1
- package/dist/{chunk-K7DOZWWT.js → chunk-GZYJDV6X.js} +9 -4
- package/dist/chunk-L7NRPLE5.js +116 -0
- package/dist/{chunk-H4GA4BBK.js → chunk-MD553LDL.js} +11 -2
- package/dist/{chunk-3RUDVTMH.js → chunk-Q67VT5XL.js} +307 -138
- package/dist/{clone-CfxzMs4w.d.ts → clone-_owA3IXN.d.ts} +9 -170
- package/dist/database/index.d.ts +3 -3
- package/dist/database/index.js +17 -3
- package/dist/{database-row-6Q6TRVE4.js → database-row-5MEIOJ52.js} +1 -1
- package/dist/{database-view-5PLSOBI2.js → database-view-DM7YCTZT.js} +1 -1
- package/dist/form-types-BlZvpDEE.d.ts +645 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +40 -12
- package/dist/meeting-LMUYTZBF.js +20 -0
- package/dist/schema/index.d.ts +191 -29
- package/dist/schema/index.js +24 -10
- package/package.json +7 -7
- package/dist/view-types-DSde-uT_.d.ts +0 -316
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./chunk-QWFTRZQT.js";
|
|
17
17
|
import {
|
|
18
18
|
DatabaseRowSchema
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-FB63TZVI.js";
|
|
20
20
|
import {
|
|
21
21
|
DatabaseFieldSchema
|
|
22
22
|
} from "./chunk-KQAT4XBL.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from "./chunk-6OMG4I7M.js";
|
|
26
26
|
import {
|
|
27
27
|
DatabaseViewSchema
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-MD553LDL.js";
|
|
29
29
|
import {
|
|
30
30
|
createNodeQueryDescriptor
|
|
31
31
|
} from "./chunk-S5RP5RKY.js";
|
|
@@ -85,7 +85,7 @@ function isCellValue(value) {
|
|
|
85
85
|
|
|
86
86
|
// src/database/row-operations.ts
|
|
87
87
|
async function createRow(store, options) {
|
|
88
|
-
const { databaseId, cells = {}, before, after } = options;
|
|
88
|
+
const { databaseId, cells = {}, before, after, id, submissionMeta } = options;
|
|
89
89
|
let sortKey;
|
|
90
90
|
if (!before && !after) {
|
|
91
91
|
const { rows } = await queryRows(store, databaseId, { limit: 1, sortDirection: "desc" });
|
|
@@ -104,10 +104,12 @@ async function createRow(store, options) {
|
|
|
104
104
|
{
|
|
105
105
|
type: "create",
|
|
106
106
|
options: {
|
|
107
|
+
...id ? { id } : {},
|
|
107
108
|
schemaId: DatabaseRowSchema.schema["@id"],
|
|
108
109
|
properties: {
|
|
109
110
|
database: databaseId,
|
|
110
111
|
sortKey,
|
|
112
|
+
...submissionMeta ? { submissionMeta } : {},
|
|
111
113
|
...dynamicProperties
|
|
112
114
|
}
|
|
113
115
|
}
|
|
@@ -1286,6 +1288,296 @@ function requiresDateColumn(type) {
|
|
|
1286
1288
|
return type === "calendar" || type === "timeline";
|
|
1287
1289
|
}
|
|
1288
1290
|
|
|
1291
|
+
// src/database/filter-engine.ts
|
|
1292
|
+
function filterRows(rows, columns, filter) {
|
|
1293
|
+
if (!filter || filter.conditions.length === 0) {
|
|
1294
|
+
return rows;
|
|
1295
|
+
}
|
|
1296
|
+
return rows.filter((row) => evaluateGroup(row, columns, filter));
|
|
1297
|
+
}
|
|
1298
|
+
function evaluateGroup(row, columns, group) {
|
|
1299
|
+
if (group.conditions.length === 0) {
|
|
1300
|
+
return true;
|
|
1301
|
+
}
|
|
1302
|
+
const results = group.conditions.map((condition) => {
|
|
1303
|
+
if (isFilterGroup(condition)) {
|
|
1304
|
+
return evaluateGroup(row, columns, condition);
|
|
1305
|
+
}
|
|
1306
|
+
return evaluateCondition(row, columns, condition);
|
|
1307
|
+
});
|
|
1308
|
+
if (group.operator === "and") {
|
|
1309
|
+
return results.every(Boolean);
|
|
1310
|
+
} else {
|
|
1311
|
+
return results.some(Boolean);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
function evaluateCondition(row, columns, condition) {
|
|
1315
|
+
const column = columns.find((c) => c.id === condition.columnId);
|
|
1316
|
+
if (!column) {
|
|
1317
|
+
return true;
|
|
1318
|
+
}
|
|
1319
|
+
const cellValue = row.cells[condition.columnId];
|
|
1320
|
+
const filterValue = condition.value;
|
|
1321
|
+
return evaluateOperator(cellValue, filterValue, condition.operator, column.type);
|
|
1322
|
+
}
|
|
1323
|
+
function evaluateOperator(cellValue, filterValue, operator, columnType) {
|
|
1324
|
+
switch (operator) {
|
|
1325
|
+
// ─── Equality ─────────────────────────────────────────────────────────────
|
|
1326
|
+
case "equals":
|
|
1327
|
+
return cellValue === filterValue;
|
|
1328
|
+
case "notEquals":
|
|
1329
|
+
return cellValue !== filterValue;
|
|
1330
|
+
// ─── Text Operators ───────────────────────────────────────────────────────
|
|
1331
|
+
case "contains":
|
|
1332
|
+
if (typeof cellValue === "string") {
|
|
1333
|
+
return cellValue.toLowerCase().includes(String(filterValue).toLowerCase());
|
|
1334
|
+
}
|
|
1335
|
+
if (Array.isArray(cellValue)) {
|
|
1336
|
+
return cellValue.includes(filterValue);
|
|
1337
|
+
}
|
|
1338
|
+
return false;
|
|
1339
|
+
case "notContains":
|
|
1340
|
+
return !evaluateOperator(cellValue, filterValue, "contains", columnType);
|
|
1341
|
+
case "startsWith":
|
|
1342
|
+
return typeof cellValue === "string" && cellValue.toLowerCase().startsWith(String(filterValue).toLowerCase());
|
|
1343
|
+
case "endsWith":
|
|
1344
|
+
return typeof cellValue === "string" && cellValue.toLowerCase().endsWith(String(filterValue).toLowerCase());
|
|
1345
|
+
// ─── Empty Operators ──────────────────────────────────────────────────────
|
|
1346
|
+
case "isEmpty":
|
|
1347
|
+
return cellValue === null || cellValue === void 0 || cellValue === "" || Array.isArray(cellValue) && cellValue.length === 0;
|
|
1348
|
+
case "isNotEmpty":
|
|
1349
|
+
return !evaluateOperator(cellValue, null, "isEmpty", columnType);
|
|
1350
|
+
// ─── Comparison Operators ─────────────────────────────────────────────────
|
|
1351
|
+
case "greaterThan":
|
|
1352
|
+
return Number(cellValue) > Number(filterValue);
|
|
1353
|
+
case "lessThan":
|
|
1354
|
+
return Number(cellValue) < Number(filterValue);
|
|
1355
|
+
case "greaterOrEqual":
|
|
1356
|
+
return Number(cellValue) >= Number(filterValue);
|
|
1357
|
+
case "lessOrEqual":
|
|
1358
|
+
return Number(cellValue) <= Number(filterValue);
|
|
1359
|
+
// ─── Date Operators ───────────────────────────────────────────────────────
|
|
1360
|
+
case "before":
|
|
1361
|
+
if (cellValue == null) return false;
|
|
1362
|
+
return new Date(cellValue) < new Date(filterValue);
|
|
1363
|
+
case "after":
|
|
1364
|
+
if (cellValue == null) return false;
|
|
1365
|
+
return new Date(cellValue) > new Date(filterValue);
|
|
1366
|
+
case "between": {
|
|
1367
|
+
if (cellValue == null) return false;
|
|
1368
|
+
const [start, end] = filterValue;
|
|
1369
|
+
const date = new Date(cellValue);
|
|
1370
|
+
return date >= new Date(start) && date <= new Date(end);
|
|
1371
|
+
}
|
|
1372
|
+
// ─── Multi-Select Operators ───────────────────────────────────────────────
|
|
1373
|
+
case "hasAny": {
|
|
1374
|
+
const anyValues = filterValue;
|
|
1375
|
+
if (!Array.isArray(cellValue)) return false;
|
|
1376
|
+
return anyValues.some((v) => cellValue.includes(v));
|
|
1377
|
+
}
|
|
1378
|
+
case "hasAll": {
|
|
1379
|
+
const allValues = filterValue;
|
|
1380
|
+
if (!Array.isArray(cellValue)) return false;
|
|
1381
|
+
return allValues.every((v) => cellValue.includes(v));
|
|
1382
|
+
}
|
|
1383
|
+
case "hasNone": {
|
|
1384
|
+
const noneValues = filterValue;
|
|
1385
|
+
if (!Array.isArray(cellValue)) return true;
|
|
1386
|
+
return !noneValues.some((v) => cellValue.includes(v));
|
|
1387
|
+
}
|
|
1388
|
+
default:
|
|
1389
|
+
return true;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
function createEqualsFilter(columnId, value) {
|
|
1393
|
+
return {
|
|
1394
|
+
operator: "and",
|
|
1395
|
+
conditions: [{ columnId, operator: "equals", value }]
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
function createAnyOfFilter(columnId, values) {
|
|
1399
|
+
return {
|
|
1400
|
+
operator: "or",
|
|
1401
|
+
conditions: values.map((value) => ({
|
|
1402
|
+
columnId,
|
|
1403
|
+
operator: "equals",
|
|
1404
|
+
value
|
|
1405
|
+
}))
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1408
|
+
function combineFiltersAnd(filters) {
|
|
1409
|
+
return {
|
|
1410
|
+
operator: "and",
|
|
1411
|
+
conditions: filters
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
function combineFiltersOr(filters) {
|
|
1415
|
+
return {
|
|
1416
|
+
operator: "or",
|
|
1417
|
+
conditions: filters
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
// src/database/form-types.ts
|
|
1422
|
+
var compact = (obj) => {
|
|
1423
|
+
const out = {};
|
|
1424
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1425
|
+
if (value !== void 0 && value !== "" && value !== false) out[key] = value;
|
|
1426
|
+
}
|
|
1427
|
+
return out;
|
|
1428
|
+
};
|
|
1429
|
+
function publicFormQuestion(question, column, resolvedOptions) {
|
|
1430
|
+
if (!column) return null;
|
|
1431
|
+
const type = column.type;
|
|
1432
|
+
if (!isFormFieldTypeAllowed(type, "public")) return null;
|
|
1433
|
+
const configOptions = column.config?.options;
|
|
1434
|
+
const options = resolvedOptions ?? configOptions ?? [];
|
|
1435
|
+
return compact({
|
|
1436
|
+
fieldId: question.fieldId,
|
|
1437
|
+
label: question.label,
|
|
1438
|
+
description: question.description,
|
|
1439
|
+
required: question.required,
|
|
1440
|
+
type,
|
|
1441
|
+
options: options.length > 0 ? options.map((o) => compact({ id: o.id, name: o.name, color: o.color })) : void 0
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
function publishableRules(rules, asked) {
|
|
1445
|
+
const published = {};
|
|
1446
|
+
for (const [fieldId, rule] of Object.entries(rules ?? {})) {
|
|
1447
|
+
if (asked.has(fieldId) && rule.when.every((cond) => asked.has(cond.columnId))) {
|
|
1448
|
+
published[fieldId] = rule;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
return Object.keys(published).length > 0 ? published : void 0;
|
|
1452
|
+
}
|
|
1453
|
+
function buildPublicFormDefinition(config, rules, columns, fieldOptions) {
|
|
1454
|
+
const byId = new Map(columns.map((c) => [c.id, c]));
|
|
1455
|
+
const questions = config.questions.map((q) => publicFormQuestion(q, byId.get(q.fieldId), fieldOptions?.[q.fieldId])).filter((q) => q !== null);
|
|
1456
|
+
const asked = new Set(questions.map((q) => q.fieldId));
|
|
1457
|
+
return compact({
|
|
1458
|
+
title: config.title,
|
|
1459
|
+
description: config.description,
|
|
1460
|
+
questions,
|
|
1461
|
+
rules: publishableRules(rules, asked),
|
|
1462
|
+
submitLabel: config.submitLabel,
|
|
1463
|
+
confirmation: config.confirmation
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
async function submissionRowId(tokenHash, nonce) {
|
|
1467
|
+
const bytes = new TextEncoder().encode(`form-submission\0${tokenHash}\0${nonce}`);
|
|
1468
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
1469
|
+
const hex = [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1470
|
+
return `formsub_${hex.slice(0, 24)}`;
|
|
1471
|
+
}
|
|
1472
|
+
var PUBLIC_SAFE_FORM_FIELD_TYPES = [
|
|
1473
|
+
"text",
|
|
1474
|
+
"number",
|
|
1475
|
+
"checkbox",
|
|
1476
|
+
"date",
|
|
1477
|
+
"dateRange",
|
|
1478
|
+
"select",
|
|
1479
|
+
"multiSelect",
|
|
1480
|
+
"url",
|
|
1481
|
+
"email",
|
|
1482
|
+
"phone"
|
|
1483
|
+
];
|
|
1484
|
+
function isFormFieldTypeAllowed(type, audience) {
|
|
1485
|
+
if (isComputedFieldType(type) || isAutoFieldType(type)) return false;
|
|
1486
|
+
if (type === "richText") return false;
|
|
1487
|
+
if (audience === "public") {
|
|
1488
|
+
return PUBLIC_SAFE_FORM_FIELD_TYPES.includes(type);
|
|
1489
|
+
}
|
|
1490
|
+
return true;
|
|
1491
|
+
}
|
|
1492
|
+
function isFormQuestionVisible(rule, answers, columns) {
|
|
1493
|
+
if (!rule || rule.when.length === 0) return true;
|
|
1494
|
+
const pseudoRow = { id: "__form_draft__", cells: answers };
|
|
1495
|
+
const matched = filterRows([pseudoRow], columns, {
|
|
1496
|
+
operator: rule.match === "any" ? "or" : "and",
|
|
1497
|
+
conditions: rule.when
|
|
1498
|
+
});
|
|
1499
|
+
return matched.length === 1;
|
|
1500
|
+
}
|
|
1501
|
+
function visibleFormQuestions(config, rules, answers, columns, audience) {
|
|
1502
|
+
const byId = new Map(columns.map((c) => [c.id, c]));
|
|
1503
|
+
return config.questions.filter((q) => {
|
|
1504
|
+
const column = byId.get(q.fieldId);
|
|
1505
|
+
if (!column) return false;
|
|
1506
|
+
if (!isFormFieldTypeAllowed(column.type, audience)) return false;
|
|
1507
|
+
return isFormQuestionVisible(rules?.[q.fieldId], answers, columns);
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
function isPlausibleValue(type, value) {
|
|
1511
|
+
if (value == null) return true;
|
|
1512
|
+
switch (type) {
|
|
1513
|
+
case "number":
|
|
1514
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
1515
|
+
case "checkbox":
|
|
1516
|
+
return typeof value === "boolean";
|
|
1517
|
+
case "multiSelect":
|
|
1518
|
+
return Array.isArray(value);
|
|
1519
|
+
case "text":
|
|
1520
|
+
case "url":
|
|
1521
|
+
case "email":
|
|
1522
|
+
case "phone":
|
|
1523
|
+
case "select":
|
|
1524
|
+
case "date":
|
|
1525
|
+
return typeof value === "string";
|
|
1526
|
+
default:
|
|
1527
|
+
return true;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
function selectOptionsOf(column) {
|
|
1531
|
+
const config = column.config;
|
|
1532
|
+
if (!config?.options) return null;
|
|
1533
|
+
return new Set(config.options.map((o) => o.id));
|
|
1534
|
+
}
|
|
1535
|
+
var isEmptyAnswer = (value) => value == null || value === "" || Array.isArray(value) && value.length === 0;
|
|
1536
|
+
function answerDriftErrors(answers, configured, byId, audience) {
|
|
1537
|
+
const errors = [];
|
|
1538
|
+
for (const fieldId of Object.keys(answers)) {
|
|
1539
|
+
if (!configured.has(fieldId)) continue;
|
|
1540
|
+
const column = byId.get(fieldId);
|
|
1541
|
+
if (!column) {
|
|
1542
|
+
errors.push({ fieldId, reason: "unknown-field" });
|
|
1543
|
+
} else if (!isFormFieldTypeAllowed(column.type, audience)) {
|
|
1544
|
+
errors.push({ fieldId, reason: "type-not-allowed" });
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
return errors;
|
|
1548
|
+
}
|
|
1549
|
+
function answerError(question, column, value) {
|
|
1550
|
+
if (isEmptyAnswer(value)) {
|
|
1551
|
+
return question.required ? "required" : void 0;
|
|
1552
|
+
}
|
|
1553
|
+
if (!isPlausibleValue(column.type, value)) return "bad-value";
|
|
1554
|
+
if (column.type === "select" || column.type === "multiSelect") {
|
|
1555
|
+
const known = selectOptionsOf(column);
|
|
1556
|
+
const ids = Array.isArray(value) ? value.map(String) : [String(value)];
|
|
1557
|
+
if (known && ids.some((id) => !known.has(id))) return "bad-option";
|
|
1558
|
+
}
|
|
1559
|
+
return void 0;
|
|
1560
|
+
}
|
|
1561
|
+
function validateFormSubmission(config, rules, answers, columns, audience) {
|
|
1562
|
+
const cells = {};
|
|
1563
|
+
const byId = new Map(columns.map((c) => [c.id, c]));
|
|
1564
|
+
const configured = new Set(config.questions.map((q) => q.fieldId));
|
|
1565
|
+
const visible = visibleFormQuestions(config, rules, answers, columns, audience);
|
|
1566
|
+
const errors = answerDriftErrors(answers, configured, byId, audience);
|
|
1567
|
+
for (const question of visible) {
|
|
1568
|
+
const column = byId.get(question.fieldId);
|
|
1569
|
+
if (!column) continue;
|
|
1570
|
+
const value = answers[question.fieldId] ?? null;
|
|
1571
|
+
const reason = answerError(question, column, value);
|
|
1572
|
+
if (reason) {
|
|
1573
|
+
errors.push({ fieldId: question.fieldId, reason });
|
|
1574
|
+
} else if (!isEmptyAnswer(value)) {
|
|
1575
|
+
cells[question.fieldId] = value;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return { ok: errors.length === 0, errors, cells };
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1289
1581
|
// src/database/view-operations.ts
|
|
1290
1582
|
import { nanoid as nanoid3 } from "nanoid";
|
|
1291
1583
|
import * as Y4 from "yjs";
|
|
@@ -1535,136 +1827,6 @@ function operatorRequiresValue(operator) {
|
|
|
1535
1827
|
return !["isEmpty", "isNotEmpty"].includes(operator);
|
|
1536
1828
|
}
|
|
1537
1829
|
|
|
1538
|
-
// src/database/filter-engine.ts
|
|
1539
|
-
function filterRows(rows, columns, filter) {
|
|
1540
|
-
if (!filter || filter.conditions.length === 0) {
|
|
1541
|
-
return rows;
|
|
1542
|
-
}
|
|
1543
|
-
return rows.filter((row) => evaluateGroup(row, columns, filter));
|
|
1544
|
-
}
|
|
1545
|
-
function evaluateGroup(row, columns, group) {
|
|
1546
|
-
if (group.conditions.length === 0) {
|
|
1547
|
-
return true;
|
|
1548
|
-
}
|
|
1549
|
-
const results = group.conditions.map((condition) => {
|
|
1550
|
-
if (isFilterGroup(condition)) {
|
|
1551
|
-
return evaluateGroup(row, columns, condition);
|
|
1552
|
-
}
|
|
1553
|
-
return evaluateCondition(row, columns, condition);
|
|
1554
|
-
});
|
|
1555
|
-
if (group.operator === "and") {
|
|
1556
|
-
return results.every(Boolean);
|
|
1557
|
-
} else {
|
|
1558
|
-
return results.some(Boolean);
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
function evaluateCondition(row, columns, condition) {
|
|
1562
|
-
const column = columns.find((c) => c.id === condition.columnId);
|
|
1563
|
-
if (!column) {
|
|
1564
|
-
return true;
|
|
1565
|
-
}
|
|
1566
|
-
const cellValue = row.cells[condition.columnId];
|
|
1567
|
-
const filterValue = condition.value;
|
|
1568
|
-
return evaluateOperator(cellValue, filterValue, condition.operator, column.type);
|
|
1569
|
-
}
|
|
1570
|
-
function evaluateOperator(cellValue, filterValue, operator, columnType) {
|
|
1571
|
-
switch (operator) {
|
|
1572
|
-
// ─── Equality ─────────────────────────────────────────────────────────────
|
|
1573
|
-
case "equals":
|
|
1574
|
-
return cellValue === filterValue;
|
|
1575
|
-
case "notEquals":
|
|
1576
|
-
return cellValue !== filterValue;
|
|
1577
|
-
// ─── Text Operators ───────────────────────────────────────────────────────
|
|
1578
|
-
case "contains":
|
|
1579
|
-
if (typeof cellValue === "string") {
|
|
1580
|
-
return cellValue.toLowerCase().includes(String(filterValue).toLowerCase());
|
|
1581
|
-
}
|
|
1582
|
-
if (Array.isArray(cellValue)) {
|
|
1583
|
-
return cellValue.includes(filterValue);
|
|
1584
|
-
}
|
|
1585
|
-
return false;
|
|
1586
|
-
case "notContains":
|
|
1587
|
-
return !evaluateOperator(cellValue, filterValue, "contains", columnType);
|
|
1588
|
-
case "startsWith":
|
|
1589
|
-
return typeof cellValue === "string" && cellValue.toLowerCase().startsWith(String(filterValue).toLowerCase());
|
|
1590
|
-
case "endsWith":
|
|
1591
|
-
return typeof cellValue === "string" && cellValue.toLowerCase().endsWith(String(filterValue).toLowerCase());
|
|
1592
|
-
// ─── Empty Operators ──────────────────────────────────────────────────────
|
|
1593
|
-
case "isEmpty":
|
|
1594
|
-
return cellValue === null || cellValue === void 0 || cellValue === "" || Array.isArray(cellValue) && cellValue.length === 0;
|
|
1595
|
-
case "isNotEmpty":
|
|
1596
|
-
return !evaluateOperator(cellValue, null, "isEmpty", columnType);
|
|
1597
|
-
// ─── Comparison Operators ─────────────────────────────────────────────────
|
|
1598
|
-
case "greaterThan":
|
|
1599
|
-
return Number(cellValue) > Number(filterValue);
|
|
1600
|
-
case "lessThan":
|
|
1601
|
-
return Number(cellValue) < Number(filterValue);
|
|
1602
|
-
case "greaterOrEqual":
|
|
1603
|
-
return Number(cellValue) >= Number(filterValue);
|
|
1604
|
-
case "lessOrEqual":
|
|
1605
|
-
return Number(cellValue) <= Number(filterValue);
|
|
1606
|
-
// ─── Date Operators ───────────────────────────────────────────────────────
|
|
1607
|
-
case "before":
|
|
1608
|
-
if (cellValue == null) return false;
|
|
1609
|
-
return new Date(cellValue) < new Date(filterValue);
|
|
1610
|
-
case "after":
|
|
1611
|
-
if (cellValue == null) return false;
|
|
1612
|
-
return new Date(cellValue) > new Date(filterValue);
|
|
1613
|
-
case "between": {
|
|
1614
|
-
if (cellValue == null) return false;
|
|
1615
|
-
const [start, end] = filterValue;
|
|
1616
|
-
const date = new Date(cellValue);
|
|
1617
|
-
return date >= new Date(start) && date <= new Date(end);
|
|
1618
|
-
}
|
|
1619
|
-
// ─── Multi-Select Operators ───────────────────────────────────────────────
|
|
1620
|
-
case "hasAny": {
|
|
1621
|
-
const anyValues = filterValue;
|
|
1622
|
-
if (!Array.isArray(cellValue)) return false;
|
|
1623
|
-
return anyValues.some((v) => cellValue.includes(v));
|
|
1624
|
-
}
|
|
1625
|
-
case "hasAll": {
|
|
1626
|
-
const allValues = filterValue;
|
|
1627
|
-
if (!Array.isArray(cellValue)) return false;
|
|
1628
|
-
return allValues.every((v) => cellValue.includes(v));
|
|
1629
|
-
}
|
|
1630
|
-
case "hasNone": {
|
|
1631
|
-
const noneValues = filterValue;
|
|
1632
|
-
if (!Array.isArray(cellValue)) return true;
|
|
1633
|
-
return !noneValues.some((v) => cellValue.includes(v));
|
|
1634
|
-
}
|
|
1635
|
-
default:
|
|
1636
|
-
return true;
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
function createEqualsFilter(columnId, value) {
|
|
1640
|
-
return {
|
|
1641
|
-
operator: "and",
|
|
1642
|
-
conditions: [{ columnId, operator: "equals", value }]
|
|
1643
|
-
};
|
|
1644
|
-
}
|
|
1645
|
-
function createAnyOfFilter(columnId, values) {
|
|
1646
|
-
return {
|
|
1647
|
-
operator: "or",
|
|
1648
|
-
conditions: values.map((value) => ({
|
|
1649
|
-
columnId,
|
|
1650
|
-
operator: "equals",
|
|
1651
|
-
value
|
|
1652
|
-
}))
|
|
1653
|
-
};
|
|
1654
|
-
}
|
|
1655
|
-
function combineFiltersAnd(filters) {
|
|
1656
|
-
return {
|
|
1657
|
-
operator: "and",
|
|
1658
|
-
conditions: filters
|
|
1659
|
-
};
|
|
1660
|
-
}
|
|
1661
|
-
function combineFiltersOr(filters) {
|
|
1662
|
-
return {
|
|
1663
|
-
operator: "or",
|
|
1664
|
-
conditions: filters
|
|
1665
|
-
};
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
1830
|
// src/database/sort-engine.ts
|
|
1669
1831
|
function sortRows(rows, columns, sorts) {
|
|
1670
1832
|
if (!sorts || sorts.length === 0) {
|
|
@@ -5410,6 +5572,18 @@ export {
|
|
|
5410
5572
|
isFilterCondition,
|
|
5411
5573
|
supportsGrouping,
|
|
5412
5574
|
requiresDateColumn,
|
|
5575
|
+
filterRows,
|
|
5576
|
+
createEqualsFilter,
|
|
5577
|
+
createAnyOfFilter,
|
|
5578
|
+
combineFiltersAnd,
|
|
5579
|
+
combineFiltersOr,
|
|
5580
|
+
buildPublicFormDefinition,
|
|
5581
|
+
submissionRowId,
|
|
5582
|
+
PUBLIC_SAFE_FORM_FIELD_TYPES,
|
|
5583
|
+
isFormFieldTypeAllowed,
|
|
5584
|
+
isFormQuestionVisible,
|
|
5585
|
+
visibleFormQuestions,
|
|
5586
|
+
validateFormSubmission,
|
|
5413
5587
|
getViews2,
|
|
5414
5588
|
getView2,
|
|
5415
5589
|
getViewByType,
|
|
@@ -5436,11 +5610,6 @@ export {
|
|
|
5436
5610
|
isValidOperator,
|
|
5437
5611
|
getOperatorLabel,
|
|
5438
5612
|
operatorRequiresValue,
|
|
5439
|
-
filterRows,
|
|
5440
|
-
createEqualsFilter,
|
|
5441
|
-
createAnyOfFilter,
|
|
5442
|
-
combineFiltersAnd,
|
|
5443
|
-
combineFiltersOr,
|
|
5444
5613
|
sortRows,
|
|
5445
5614
|
createSort,
|
|
5446
5615
|
toggleSortDirection,
|