@dyrected/core 2.10.0 → 2.10.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/dist/{app-config-DQhcrGDE.d.cts → app-config-BLC7WoyL.d.cts} +141 -6
- package/dist/{app-config-DQhcrGDE.d.ts → app-config-BLC7WoyL.d.ts} +141 -6
- package/dist/{chunk-TYPDQTE2.js → chunk-H7DVVW2P.js} +5 -0
- package/dist/index.cjs +353 -0
- package/dist/index.d.cts +401 -86
- package/dist/index.d.ts +401 -86
- package/dist/index.js +345 -1
- package/dist/server.cjs +30 -7
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +25 -8
- package/dist/{where-sanitizer-3MNFMYA2.js → where-sanitizer-47QPJQ6O.js} +3 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -101,7 +101,7 @@ import {
|
|
|
101
101
|
validatePreviewUrlExpression,
|
|
102
102
|
wordCount,
|
|
103
103
|
workflowCapabilities
|
|
104
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-H7DVVW2P.js";
|
|
105
105
|
import "./chunk-JSBRDJBE.js";
|
|
106
106
|
|
|
107
107
|
// src/types/workflows.ts
|
|
@@ -389,6 +389,346 @@ function buildSqlOrderBy(sort, existingColumns, numericFields, dialect) {
|
|
|
389
389
|
}).join(", ");
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
+
// src/utils/conditions.ts
|
|
393
|
+
function formatJexlValue(v) {
|
|
394
|
+
if (v === null || v === void 0) return "null";
|
|
395
|
+
if (typeof v === "boolean" || typeof v === "number") return String(v);
|
|
396
|
+
if (typeof v === "string") {
|
|
397
|
+
if (v.startsWith("'") && v.endsWith("'") || v.startsWith('"') && v.endsWith('"') || v.includes("+") || v.includes("?") || v.includes("(") || v.includes(" ")) {
|
|
398
|
+
return v;
|
|
399
|
+
}
|
|
400
|
+
if (v.startsWith("/")) {
|
|
401
|
+
return `'${v}'`;
|
|
402
|
+
}
|
|
403
|
+
if (v === "null" || v === "true" || v === "false") {
|
|
404
|
+
return v;
|
|
405
|
+
}
|
|
406
|
+
if (/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(v)) {
|
|
407
|
+
return v;
|
|
408
|
+
}
|
|
409
|
+
return `'${v}'`;
|
|
410
|
+
}
|
|
411
|
+
return String(v);
|
|
412
|
+
}
|
|
413
|
+
var MatchBuilder = class {
|
|
414
|
+
branches = [];
|
|
415
|
+
/**
|
|
416
|
+
* Adds a conditional branch (`condition ? value : ...`).
|
|
417
|
+
*/
|
|
418
|
+
case(condition, value) {
|
|
419
|
+
this.branches.push({ condition, value });
|
|
420
|
+
return this;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Final fallback value when no cases match.
|
|
424
|
+
*/
|
|
425
|
+
otherwise(fallback = "null") {
|
|
426
|
+
if (this.branches.length === 0) return formatJexlValue(fallback);
|
|
427
|
+
let expr = formatJexlValue(fallback);
|
|
428
|
+
for (let i = this.branches.length - 1; i >= 0; i--) {
|
|
429
|
+
const b = this.branches[i];
|
|
430
|
+
expr = `${b.condition} ? ${formatJexlValue(b.value)} : ${expr}`;
|
|
431
|
+
}
|
|
432
|
+
return expr;
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
var FieldConditionBuilder = class {
|
|
436
|
+
constructor(field) {
|
|
437
|
+
this.field = field;
|
|
438
|
+
}
|
|
439
|
+
field;
|
|
440
|
+
/**
|
|
441
|
+
* Generates equality condition (`field == value`).
|
|
442
|
+
*/
|
|
443
|
+
equals(value) {
|
|
444
|
+
return `${this.field} == ${JSON.stringify(value)}`;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Generates inequality condition (`field != value`).
|
|
448
|
+
*/
|
|
449
|
+
notEquals(value) {
|
|
450
|
+
return `${this.field} != ${JSON.stringify(value)}`;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Generates set membership condition (`field in [val1, val2]`).
|
|
454
|
+
*/
|
|
455
|
+
in(...values) {
|
|
456
|
+
const flat = values.flat();
|
|
457
|
+
return `${this.field} in [${flat.map((v) => JSON.stringify(v)).join(", ")}]`;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Generates inverted set membership condition (`!(field in [val1, val2])`).
|
|
461
|
+
*/
|
|
462
|
+
notIn(...values) {
|
|
463
|
+
const flat = values.flat();
|
|
464
|
+
return `!(${this.field} in [${flat.map((v) => JSON.stringify(v)).join(", ")}])`;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Generates boolean true check (`field == true`).
|
|
468
|
+
*/
|
|
469
|
+
isTrue() {
|
|
470
|
+
return `${this.field} == true`;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Generates boolean false check (`field != true`).
|
|
474
|
+
*/
|
|
475
|
+
isFalse() {
|
|
476
|
+
return `${this.field} != true`;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Checks that the field is neither null/undefined nor an empty string.
|
|
480
|
+
*/
|
|
481
|
+
notEmpty() {
|
|
482
|
+
return `${this.field} != null && ${this.field} != ''`;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Checks that the field is null, undefined, or an empty string.
|
|
486
|
+
*/
|
|
487
|
+
isEmpty() {
|
|
488
|
+
return `${this.field} == null || ${this.field} == ''`;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Numerical greater than (`field > num`).
|
|
492
|
+
*/
|
|
493
|
+
greaterThan(num) {
|
|
494
|
+
return `${this.field} > ${typeof num === "number" ? num : num}`;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Numerical greater than or equal (`field >= num`).
|
|
498
|
+
*/
|
|
499
|
+
greaterThanOrEqual(num) {
|
|
500
|
+
return `${this.field} >= ${typeof num === "number" ? num : num}`;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Numerical less than (`field < num`).
|
|
504
|
+
*/
|
|
505
|
+
lessThan(num) {
|
|
506
|
+
return `${this.field} < ${typeof num === "number" ? num : num}`;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Numerical less than or equal (`field <= num`).
|
|
510
|
+
*/
|
|
511
|
+
lessThanOrEqual(num) {
|
|
512
|
+
return `${this.field} <= ${typeof num === "number" ? num : num}`;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Numerical range check (`field >= min && field <= max`).
|
|
516
|
+
*/
|
|
517
|
+
between(min2, max2) {
|
|
518
|
+
return `${this.field} >= ${min2} && ${this.field} <= ${max2}`;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* String prefix check using JEXL `startsWith(field, prefix)`.
|
|
522
|
+
*/
|
|
523
|
+
startsWith(prefix) {
|
|
524
|
+
return `startsWith(${this.field}, ${JSON.stringify(prefix)})`;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* String suffix check using JEXL `endsWith(field, suffix)`.
|
|
528
|
+
*/
|
|
529
|
+
endsWith(suffix) {
|
|
530
|
+
return `endsWith(${this.field}, ${JSON.stringify(suffix)})`;
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Substring or array item check using JEXL `includes(field, item)`.
|
|
534
|
+
*/
|
|
535
|
+
contains(val) {
|
|
536
|
+
return `includes(${this.field}, ${JSON.stringify(val)})`;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Checks if array/string length exceeds a threshold.
|
|
540
|
+
*/
|
|
541
|
+
hasLengthGreaterThan(length2) {
|
|
542
|
+
return `length(${this.field}) > ${length2}`;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Checks if array/string length is at least a threshold.
|
|
546
|
+
*/
|
|
547
|
+
hasLengthAtLeast(length2) {
|
|
548
|
+
return `length(${this.field}) >= ${length2}`;
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
function createWhen() {
|
|
552
|
+
const whenFn = ((fieldName) => new FieldConditionBuilder(fieldName));
|
|
553
|
+
whenFn.user = (userProp) => new FieldConditionBuilder(`user.${userProp}`);
|
|
554
|
+
whenFn.sibling = (siblingProp) => new FieldConditionBuilder(`siblingData.${siblingProp}`);
|
|
555
|
+
whenFn.concat = (...parts) => {
|
|
556
|
+
if (parts.length === 0) return "''";
|
|
557
|
+
return parts.map((p) => formatJexlValue(p)).join(" + ");
|
|
558
|
+
};
|
|
559
|
+
whenFn.slugify = (field) => `slugify(${field})`;
|
|
560
|
+
whenFn.lower = (field) => `lower(${field})`;
|
|
561
|
+
whenFn.upper = (field) => `upper(${field})`;
|
|
562
|
+
whenFn.trim = (field) => `trim(${field})`;
|
|
563
|
+
whenFn.access = {
|
|
564
|
+
isOwner: (ownerField = "author") => {
|
|
565
|
+
return `${ownerField} == user.id || ${ownerField}.id == user.id`;
|
|
566
|
+
},
|
|
567
|
+
isAdmin: () => {
|
|
568
|
+
return "user.role == 'admin' || (user.roles != null && 'admin' in user.roles)";
|
|
569
|
+
},
|
|
570
|
+
hasRole: (...roles) => {
|
|
571
|
+
return whenFn.userRole(...roles);
|
|
572
|
+
},
|
|
573
|
+
isPublishedOrAdmin: (statusField = "status") => {
|
|
574
|
+
return `${statusField} == 'published' || user.role == 'admin' || (user.roles != null && 'admin' in user.roles)`;
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
whenFn.then = (condition, ifTrue, ifFalse = "null") => {
|
|
578
|
+
return `${condition} ? ${formatJexlValue(ifTrue)} : ${formatJexlValue(ifFalse)}`;
|
|
579
|
+
};
|
|
580
|
+
whenFn.match = () => new MatchBuilder();
|
|
581
|
+
whenFn.cases = (...branches) => {
|
|
582
|
+
const builder = new MatchBuilder();
|
|
583
|
+
let fallback = "null";
|
|
584
|
+
for (const b of branches) {
|
|
585
|
+
if (Array.isArray(b)) {
|
|
586
|
+
builder.case(b[0], b[1]);
|
|
587
|
+
} else {
|
|
588
|
+
fallback = b;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return builder.otherwise(fallback);
|
|
592
|
+
};
|
|
593
|
+
whenFn.all = (...conditions) => {
|
|
594
|
+
const valid = conditions.filter(Boolean);
|
|
595
|
+
if (valid.length === 0) return "true";
|
|
596
|
+
if (valid.length === 1) return valid[0];
|
|
597
|
+
return valid.map((c) => c.includes(" ") ? `(${c})` : c).join(" && ");
|
|
598
|
+
};
|
|
599
|
+
whenFn.and = whenFn.all;
|
|
600
|
+
whenFn.any = (...conditions) => {
|
|
601
|
+
const valid = conditions.filter(Boolean);
|
|
602
|
+
if (valid.length === 0) return "true";
|
|
603
|
+
if (valid.length === 1) return valid[0];
|
|
604
|
+
return valid.map((c) => c.includes(" ") ? `(${c})` : c).join(" || ");
|
|
605
|
+
};
|
|
606
|
+
whenFn.or = whenFn.any;
|
|
607
|
+
whenFn.not = (condition) => {
|
|
608
|
+
if (!condition) return "false";
|
|
609
|
+
return `!(${condition})`;
|
|
610
|
+
};
|
|
611
|
+
whenFn.variant = (...variants) => {
|
|
612
|
+
const flat = variants.flat();
|
|
613
|
+
if (flat.length === 0) return "true";
|
|
614
|
+
if (flat.length === 1) return `variant == '${flat[0]}'`;
|
|
615
|
+
return `variant in [${flat.map((v) => `'${v}'`).join(", ")}]`;
|
|
616
|
+
};
|
|
617
|
+
whenFn.notVariant = (...variants) => {
|
|
618
|
+
const flat = variants.flat();
|
|
619
|
+
if (flat.length === 0) return "false";
|
|
620
|
+
if (flat.length === 1) return `variant != '${flat[0]}'`;
|
|
621
|
+
return `!(variant in [${flat.map((v) => `'${v}'`).join(", ")}])`;
|
|
622
|
+
};
|
|
623
|
+
whenFn.block = (...blocks) => {
|
|
624
|
+
const flat = blocks.flat();
|
|
625
|
+
if (flat.length === 0) return "true";
|
|
626
|
+
if (flat.length === 1) return `block == '${flat[0]}'`;
|
|
627
|
+
return `block in [${flat.map((b) => `'${b}'`).join(", ")}]`;
|
|
628
|
+
};
|
|
629
|
+
whenFn.notBlock = (...blocks) => {
|
|
630
|
+
const flat = blocks.flat();
|
|
631
|
+
if (flat.length === 0) return "false";
|
|
632
|
+
if (flat.length === 1) return `block != '${flat[0]}'`;
|
|
633
|
+
return `!(block in [${flat.map((b) => `'${b}'`).join(", ")}])`;
|
|
634
|
+
};
|
|
635
|
+
whenFn.fieldEquals = (field, value) => {
|
|
636
|
+
return `${field} == ${JSON.stringify(value)}`;
|
|
637
|
+
};
|
|
638
|
+
whenFn.fieldNotEquals = (field, value) => {
|
|
639
|
+
return `${field} != ${JSON.stringify(value)}`;
|
|
640
|
+
};
|
|
641
|
+
whenFn.fieldIn = (field, ...values) => {
|
|
642
|
+
const flat = values.flat();
|
|
643
|
+
return `${field} in [${flat.map((v) => JSON.stringify(v)).join(", ")}]`;
|
|
644
|
+
};
|
|
645
|
+
whenFn.fieldNotIn = (field, ...values) => {
|
|
646
|
+
const flat = values.flat();
|
|
647
|
+
return `!(${field} in [${flat.map((v) => JSON.stringify(v)).join(", ")}])`;
|
|
648
|
+
};
|
|
649
|
+
whenFn.fieldNotEmpty = (field) => {
|
|
650
|
+
return `${field} != null && ${field} != ''`;
|
|
651
|
+
};
|
|
652
|
+
whenFn.fieldEmpty = (field) => {
|
|
653
|
+
return `${field} == null || ${field} == ''`;
|
|
654
|
+
};
|
|
655
|
+
whenFn.fieldIsTrue = (field) => {
|
|
656
|
+
return `${field} == true`;
|
|
657
|
+
};
|
|
658
|
+
whenFn.fieldIsFalse = (field) => {
|
|
659
|
+
return `${field} != true`;
|
|
660
|
+
};
|
|
661
|
+
whenFn.fieldGreaterThan = (field, value) => {
|
|
662
|
+
return `${field} > ${value}`;
|
|
663
|
+
};
|
|
664
|
+
whenFn.fieldGreaterThanOrEqual = (field, value) => {
|
|
665
|
+
return `${field} >= ${value}`;
|
|
666
|
+
};
|
|
667
|
+
whenFn.fieldLessThan = (field, value) => {
|
|
668
|
+
return `${field} < ${value}`;
|
|
669
|
+
};
|
|
670
|
+
whenFn.fieldLessThanOrEqual = (field, value) => {
|
|
671
|
+
return `${field} <= ${value}`;
|
|
672
|
+
};
|
|
673
|
+
whenFn.fieldBetween = (field, min2, max2) => {
|
|
674
|
+
return `${field} >= ${min2} && ${field} <= ${max2}`;
|
|
675
|
+
};
|
|
676
|
+
whenFn.fieldStartsWith = (field, prefix) => {
|
|
677
|
+
return `startsWith(${field}, ${JSON.stringify(prefix)})`;
|
|
678
|
+
};
|
|
679
|
+
whenFn.fieldEndsWith = (field, suffix) => {
|
|
680
|
+
return `endsWith(${field}, ${JSON.stringify(suffix)})`;
|
|
681
|
+
};
|
|
682
|
+
whenFn.fieldContains = (field, item) => {
|
|
683
|
+
return `includes(${field}, ${JSON.stringify(item)})`;
|
|
684
|
+
};
|
|
685
|
+
whenFn.arrayNotEmpty = (field) => {
|
|
686
|
+
return `length(${field}) > 0`;
|
|
687
|
+
};
|
|
688
|
+
whenFn.arrayEmpty = (field) => {
|
|
689
|
+
return `length(${field}) == 0`;
|
|
690
|
+
};
|
|
691
|
+
whenFn.arrayCountGreaterThan = (field, count2) => {
|
|
692
|
+
return `length(${field}) > ${count2}`;
|
|
693
|
+
};
|
|
694
|
+
whenFn.arrayCountAtLeast = (field, count2) => {
|
|
695
|
+
return `length(${field}) >= ${count2}`;
|
|
696
|
+
};
|
|
697
|
+
whenFn.arrayCountLessThan = (field, count2) => {
|
|
698
|
+
return `length(${field}) < ${count2}`;
|
|
699
|
+
};
|
|
700
|
+
whenFn.userRole = (...roles) => {
|
|
701
|
+
const flat = roles.flat();
|
|
702
|
+
if (flat.length === 1) {
|
|
703
|
+
return `user.role == '${flat[0]}' || (user.roles != null && '${flat[0]}' in user.roles)`;
|
|
704
|
+
}
|
|
705
|
+
const roleArray = `[${flat.map((r) => `'${r}'`).join(", ")}]`;
|
|
706
|
+
return `user.role in ${roleArray} || (user.roles != null && includes(${roleArray}, user.role))`;
|
|
707
|
+
};
|
|
708
|
+
whenFn.userEmailDomain = (domain) => {
|
|
709
|
+
const clean = domain.startsWith("@") ? domain : `@${domain}`;
|
|
710
|
+
return `endsWith(user.email, '${clean}')`;
|
|
711
|
+
};
|
|
712
|
+
whenFn.userAttributeEquals = (attr, value) => {
|
|
713
|
+
return `user.${attr} == ${JSON.stringify(value)}`;
|
|
714
|
+
};
|
|
715
|
+
whenFn.isNewDocument = () => {
|
|
716
|
+
return "id == null";
|
|
717
|
+
};
|
|
718
|
+
whenFn.isExistingDocument = () => {
|
|
719
|
+
return "id != null";
|
|
720
|
+
};
|
|
721
|
+
whenFn.statusEquals = (status) => {
|
|
722
|
+
return `status == '${status}'`;
|
|
723
|
+
};
|
|
724
|
+
whenFn.statusIn = (...statuses) => {
|
|
725
|
+
const flat = statuses.flat();
|
|
726
|
+
return `status in [${flat.map((s) => `'${s}'`).join(", ")}]`;
|
|
727
|
+
};
|
|
728
|
+
return whenFn;
|
|
729
|
+
}
|
|
730
|
+
var when = createWhen();
|
|
731
|
+
|
|
392
732
|
// src/index.ts
|
|
393
733
|
function defineCollection(config) {
|
|
394
734
|
return config;
|
|
@@ -439,8 +779,10 @@ function defineTab(args) {
|
|
|
439
779
|
export {
|
|
440
780
|
BUILTIN_JEXL_HELPERS,
|
|
441
781
|
ConfigValidationError,
|
|
782
|
+
FieldConditionBuilder,
|
|
442
783
|
LIFECYCLE_EVENTS_COLLECTION,
|
|
443
784
|
LIFECYCLE_EVENT_NAMES,
|
|
785
|
+
MatchBuilder,
|
|
444
786
|
WORKFLOW_HISTORY_COLLECTION,
|
|
445
787
|
abs,
|
|
446
788
|
addDays,
|
|
@@ -522,6 +864,7 @@ export {
|
|
|
522
864
|
floor,
|
|
523
865
|
formatConfigDiagnostics,
|
|
524
866
|
formatDate,
|
|
867
|
+
formatJexlValue,
|
|
525
868
|
generateDefaultDetailSchema,
|
|
526
869
|
generateOpenApi,
|
|
527
870
|
get,
|
|
@@ -573,6 +916,7 @@ export {
|
|
|
573
916
|
validateDeclarativeAccessExpression,
|
|
574
917
|
validateDeclarativeHookExpression,
|
|
575
918
|
validatePreviewUrlExpression,
|
|
919
|
+
when,
|
|
576
920
|
wordCount,
|
|
577
921
|
workflowCapabilities
|
|
578
922
|
};
|
package/dist/server.cjs
CHANGED
|
@@ -2318,11 +2318,12 @@ function sanitizeWhereClause(where, fields) {
|
|
|
2318
2318
|
function walk(node) {
|
|
2319
2319
|
const result = {};
|
|
2320
2320
|
for (const [key, value] of Object.entries(node)) {
|
|
2321
|
-
|
|
2321
|
+
const upperKey = key.toUpperCase();
|
|
2322
|
+
if (upperKey === "AND" || upperKey === "OR") {
|
|
2322
2323
|
if (Array.isArray(value)) {
|
|
2323
2324
|
const processed = value.map((v) => walk(v)).filter((v) => Object.keys(v).length > 0);
|
|
2324
2325
|
if (processed.length > 0) {
|
|
2325
|
-
result[
|
|
2326
|
+
result[upperKey] = processed;
|
|
2326
2327
|
}
|
|
2327
2328
|
}
|
|
2328
2329
|
continue;
|
|
@@ -3144,6 +3145,11 @@ function registerJexlHelpers(jexlInstance = import_jexl2.default) {
|
|
|
3144
3145
|
try {
|
|
3145
3146
|
jexlInstance.addBinaryOp("and", 20, (left, right) => Boolean(left && right));
|
|
3146
3147
|
jexlInstance.addBinaryOp("or", 10, (left, right) => Boolean(left || right));
|
|
3148
|
+
jexlInstance.addBinaryOp("in", 30, (left, right) => {
|
|
3149
|
+
if (Array.isArray(right)) return right.includes(left);
|
|
3150
|
+
if (typeof right === "string") return right.includes(String(left ?? ""));
|
|
3151
|
+
return false;
|
|
3152
|
+
});
|
|
3147
3153
|
} catch {
|
|
3148
3154
|
}
|
|
3149
3155
|
}
|
|
@@ -4036,6 +4042,17 @@ function validateUpload(file, config) {
|
|
|
4036
4042
|
}
|
|
4037
4043
|
return null;
|
|
4038
4044
|
}
|
|
4045
|
+
function generateUniqueUploadFilename(filename) {
|
|
4046
|
+
const lastDotIndex = filename.lastIndexOf(".");
|
|
4047
|
+
const ext = lastDotIndex !== -1 ? filename.substring(lastDotIndex).toLowerCase() : "";
|
|
4048
|
+
const baseName = lastDotIndex !== -1 ? filename.substring(0, lastDotIndex) : filename;
|
|
4049
|
+
const sanitizedBase = baseName.toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "file";
|
|
4050
|
+
if (/-\d{10,13}-[a-z0-9]{4,10}$/.test(sanitizedBase)) {
|
|
4051
|
+
return `${sanitizedBase}${ext}`;
|
|
4052
|
+
}
|
|
4053
|
+
const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
4054
|
+
return `${sanitizedBase}-${uniqueSuffix}${ext}`;
|
|
4055
|
+
}
|
|
4039
4056
|
|
|
4040
4057
|
// src/auth/jexl.ts
|
|
4041
4058
|
var import_jexl5 = __toESM(require_Jexl(), 1);
|
|
@@ -5366,6 +5383,7 @@ var CollectionController = class {
|
|
|
5366
5383
|
createdBy: user?.sub ?? null,
|
|
5367
5384
|
updatedBy: user?.sub ?? null
|
|
5368
5385
|
};
|
|
5386
|
+
data = DefaultsService.apply(this.collection.fields, data);
|
|
5369
5387
|
if (this.collection.workflow) {
|
|
5370
5388
|
data = initializeWorkflowDocument(data, this.collection.workflow);
|
|
5371
5389
|
}
|
|
@@ -5487,8 +5505,9 @@ var CollectionController = class {
|
|
|
5487
5505
|
const siteId = c.get("siteId");
|
|
5488
5506
|
const workspaceId = c.get("workspaceId");
|
|
5489
5507
|
const prefix = workspaceId ? `${workspaceId}/${siteId}` : siteId;
|
|
5508
|
+
const uniqueFilename = generateUniqueUploadFilename(file.name);
|
|
5490
5509
|
const fileData = await storage.upload({
|
|
5491
|
-
filename:
|
|
5510
|
+
filename: uniqueFilename,
|
|
5492
5511
|
buffer,
|
|
5493
5512
|
mimeType: file.type,
|
|
5494
5513
|
prefix
|
|
@@ -6685,6 +6704,7 @@ var MediaController = class {
|
|
|
6685
6704
|
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
6686
6705
|
const workspaceId = c.get("workspaceId");
|
|
6687
6706
|
const prefix = workspaceId ? `${workspaceId}/${siteId}` : siteId || "default";
|
|
6707
|
+
const uniqueFilename = generateUniqueUploadFilename(file.name);
|
|
6688
6708
|
let imageMetadata = {};
|
|
6689
6709
|
let imageSizes = {};
|
|
6690
6710
|
if (imageService && file.type.startsWith("image/")) {
|
|
@@ -6707,7 +6727,7 @@ var MediaController = class {
|
|
|
6707
6727
|
}
|
|
6708
6728
|
}
|
|
6709
6729
|
const fileData = await storage.upload({
|
|
6710
|
-
filename:
|
|
6730
|
+
filename: uniqueFilename,
|
|
6711
6731
|
buffer,
|
|
6712
6732
|
mimeType: file.type,
|
|
6713
6733
|
prefix
|
|
@@ -6720,9 +6740,10 @@ var MediaController = class {
|
|
|
6720
6740
|
};
|
|
6721
6741
|
if (imageSizes) {
|
|
6722
6742
|
for (const [sizeName, sizeData] of Object.entries(imageSizes)) {
|
|
6723
|
-
const
|
|
6724
|
-
const
|
|
6725
|
-
const
|
|
6743
|
+
const lastDot = uniqueFilename.lastIndexOf(".");
|
|
6744
|
+
const ext = lastDot !== -1 ? uniqueFilename.substring(lastDot) : "";
|
|
6745
|
+
const baseName = lastDot !== -1 ? uniqueFilename.substring(0, lastDot) : uniqueFilename;
|
|
6746
|
+
const sizeFilename = `${baseName}-${sizeName}${ext}`;
|
|
6726
6747
|
try {
|
|
6727
6748
|
const sizeFileData = await storage.upload({
|
|
6728
6749
|
filename: sizeFilename,
|
|
@@ -9850,6 +9871,7 @@ async function serializeViewForApi(view, serializeAccess) {
|
|
|
9850
9871
|
metrics: view.metrics,
|
|
9851
9872
|
features: view.features,
|
|
9852
9873
|
actionOrder: view.actionOrder,
|
|
9874
|
+
components: view.components,
|
|
9853
9875
|
actions: await Promise.all(
|
|
9854
9876
|
(view.actions || []).map(async (action) => ({
|
|
9855
9877
|
name: action.name,
|
|
@@ -9857,6 +9879,7 @@ async function serializeViewForApi(view, serializeAccess) {
|
|
|
9857
9879
|
icon: action.icon,
|
|
9858
9880
|
type: action.type ?? "row",
|
|
9859
9881
|
confirm: action.confirm,
|
|
9882
|
+
submitLabel: action.submitLabel,
|
|
9860
9883
|
fields: action.fields?.map(serializeFieldForApi),
|
|
9861
9884
|
mutation: action.mutation,
|
|
9862
9885
|
// Self-hosted handlers are intentionally omitted: they never leave the server.
|
package/dist/server.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import * as hono_types from 'hono/types';
|
|
|
2
2
|
import * as hono from 'hono';
|
|
3
3
|
import { Context, Hono } from 'hono';
|
|
4
4
|
import { Logger } from 'pino';
|
|
5
|
-
import { D as DyrectedConfig,
|
|
5
|
+
import { D as DyrectedConfig, b5 as DyrectedObservabilityConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-BLC7WoyL.cjs';
|
|
6
6
|
import { JWTPayload } from 'jose';
|
|
7
7
|
import { Tracer, Counter, Attributes, Histogram, Span } from '@opentelemetry/api';
|
|
8
8
|
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
|
package/dist/server.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as hono_types from 'hono/types';
|
|
|
2
2
|
import * as hono from 'hono';
|
|
3
3
|
import { Context, Hono } from 'hono';
|
|
4
4
|
import { Logger } from 'pino';
|
|
5
|
-
import { D as DyrectedConfig,
|
|
5
|
+
import { D as DyrectedConfig, b5 as DyrectedObservabilityConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-BLC7WoyL.js';
|
|
6
6
|
import { JWTPayload } from 'jose';
|
|
7
7
|
import { Tracer, Counter, Attributes, Histogram, Span } from '@opentelemetry/api';
|
|
8
8
|
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
|
package/dist/server.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
touchAuthSession,
|
|
31
31
|
transitionWorkflow,
|
|
32
32
|
verifyCollectionToken
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-H7DVVW2P.js";
|
|
34
34
|
import {
|
|
35
35
|
attachRequestMetrics,
|
|
36
36
|
bindObservabilityRuntime,
|
|
@@ -436,6 +436,17 @@ function validateUpload(file, config) {
|
|
|
436
436
|
}
|
|
437
437
|
return null;
|
|
438
438
|
}
|
|
439
|
+
function generateUniqueUploadFilename(filename) {
|
|
440
|
+
const lastDotIndex = filename.lastIndexOf(".");
|
|
441
|
+
const ext = lastDotIndex !== -1 ? filename.substring(lastDotIndex).toLowerCase() : "";
|
|
442
|
+
const baseName = lastDotIndex !== -1 ? filename.substring(0, lastDotIndex) : filename;
|
|
443
|
+
const sanitizedBase = baseName.toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "file";
|
|
444
|
+
if (/-\d{10,13}-[a-z0-9]{4,10}$/.test(sanitizedBase)) {
|
|
445
|
+
return `${sanitizedBase}${ext}`;
|
|
446
|
+
}
|
|
447
|
+
const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
448
|
+
return `${sanitizedBase}-${uniqueSuffix}${ext}`;
|
|
449
|
+
}
|
|
439
450
|
|
|
440
451
|
// src/auth/jexl.ts
|
|
441
452
|
var import_jexl2 = __toESM(require_Jexl(), 1);
|
|
@@ -1021,7 +1032,7 @@ var CollectionController = class {
|
|
|
1021
1032
|
if (this.collection.admin?.filterable === false) {
|
|
1022
1033
|
where = void 0;
|
|
1023
1034
|
} else {
|
|
1024
|
-
const { sanitizeWhereClause } = await import("./where-sanitizer-
|
|
1035
|
+
const { sanitizeWhereClause } = await import("./where-sanitizer-47QPJQ6O.js");
|
|
1025
1036
|
where = sanitizeWhereClause(where, this.collection.fields);
|
|
1026
1037
|
if (Object.keys(where).length === 0) {
|
|
1027
1038
|
where = void 0;
|
|
@@ -1319,6 +1330,7 @@ var CollectionController = class {
|
|
|
1319
1330
|
createdBy: user?.sub ?? null,
|
|
1320
1331
|
updatedBy: user?.sub ?? null
|
|
1321
1332
|
};
|
|
1333
|
+
data = DefaultsService.apply(this.collection.fields, data);
|
|
1322
1334
|
if (this.collection.workflow) {
|
|
1323
1335
|
data = initializeWorkflowDocument(data, this.collection.workflow);
|
|
1324
1336
|
}
|
|
@@ -1440,8 +1452,9 @@ var CollectionController = class {
|
|
|
1440
1452
|
const siteId = c.get("siteId");
|
|
1441
1453
|
const workspaceId = c.get("workspaceId");
|
|
1442
1454
|
const prefix = workspaceId ? `${workspaceId}/${siteId}` : siteId;
|
|
1455
|
+
const uniqueFilename = generateUniqueUploadFilename(file.name);
|
|
1443
1456
|
const fileData = await storage.upload({
|
|
1444
|
-
filename:
|
|
1457
|
+
filename: uniqueFilename,
|
|
1445
1458
|
buffer,
|
|
1446
1459
|
mimeType: file.type,
|
|
1447
1460
|
prefix
|
|
@@ -2214,7 +2227,7 @@ var CollectionController = class {
|
|
|
2214
2227
|
groupBy = queryGroupBy.trim();
|
|
2215
2228
|
}
|
|
2216
2229
|
}
|
|
2217
|
-
const { sanitizeWhereClause } = await import("./where-sanitizer-
|
|
2230
|
+
const { sanitizeWhereClause } = await import("./where-sanitizer-47QPJQ6O.js");
|
|
2218
2231
|
const sanitizedAggregates = {};
|
|
2219
2232
|
for (const [key, op] of Object.entries(rawAggregates)) {
|
|
2220
2233
|
if (!op || typeof op !== "object" || Array.isArray(op)) {
|
|
@@ -2524,6 +2537,7 @@ var MediaController = class {
|
|
|
2524
2537
|
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
2525
2538
|
const workspaceId = c.get("workspaceId");
|
|
2526
2539
|
const prefix = workspaceId ? `${workspaceId}/${siteId}` : siteId || "default";
|
|
2540
|
+
const uniqueFilename = generateUniqueUploadFilename(file.name);
|
|
2527
2541
|
let imageMetadata = {};
|
|
2528
2542
|
let imageSizes = {};
|
|
2529
2543
|
if (imageService && file.type.startsWith("image/")) {
|
|
@@ -2546,7 +2560,7 @@ var MediaController = class {
|
|
|
2546
2560
|
}
|
|
2547
2561
|
}
|
|
2548
2562
|
const fileData = await storage.upload({
|
|
2549
|
-
filename:
|
|
2563
|
+
filename: uniqueFilename,
|
|
2550
2564
|
buffer,
|
|
2551
2565
|
mimeType: file.type,
|
|
2552
2566
|
prefix
|
|
@@ -2559,9 +2573,10 @@ var MediaController = class {
|
|
|
2559
2573
|
};
|
|
2560
2574
|
if (imageSizes) {
|
|
2561
2575
|
for (const [sizeName, sizeData] of Object.entries(imageSizes)) {
|
|
2562
|
-
const
|
|
2563
|
-
const
|
|
2564
|
-
const
|
|
2576
|
+
const lastDot = uniqueFilename.lastIndexOf(".");
|
|
2577
|
+
const ext = lastDot !== -1 ? uniqueFilename.substring(lastDot) : "";
|
|
2578
|
+
const baseName = lastDot !== -1 ? uniqueFilename.substring(0, lastDot) : uniqueFilename;
|
|
2579
|
+
const sizeFilename = `${baseName}-${sizeName}${ext}`;
|
|
2565
2580
|
try {
|
|
2566
2581
|
const sizeFileData = await storage.upload({
|
|
2567
2582
|
filename: sizeFilename,
|
|
@@ -4581,6 +4596,7 @@ async function serializeViewForApi(view, serializeAccess) {
|
|
|
4581
4596
|
metrics: view.metrics,
|
|
4582
4597
|
features: view.features,
|
|
4583
4598
|
actionOrder: view.actionOrder,
|
|
4599
|
+
components: view.components,
|
|
4584
4600
|
actions: await Promise.all(
|
|
4585
4601
|
(view.actions || []).map(async (action) => ({
|
|
4586
4602
|
name: action.name,
|
|
@@ -4588,6 +4604,7 @@ async function serializeViewForApi(view, serializeAccess) {
|
|
|
4588
4604
|
icon: action.icon,
|
|
4589
4605
|
type: action.type ?? "row",
|
|
4590
4606
|
confirm: action.confirm,
|
|
4607
|
+
submitLabel: action.submitLabel,
|
|
4591
4608
|
fields: action.fields?.map(serializeFieldForApi),
|
|
4592
4609
|
mutation: action.mutation,
|
|
4593
4610
|
// Self-hosted handlers are intentionally omitted: they never leave the server.
|
|
@@ -28,11 +28,12 @@ function sanitizeWhereClause(where, fields) {
|
|
|
28
28
|
function walk(node) {
|
|
29
29
|
const result = {};
|
|
30
30
|
for (const [key, value] of Object.entries(node)) {
|
|
31
|
-
|
|
31
|
+
const upperKey = key.toUpperCase();
|
|
32
|
+
if (upperKey === "AND" || upperKey === "OR") {
|
|
32
33
|
if (Array.isArray(value)) {
|
|
33
34
|
const processed = value.map((v) => walk(v)).filter((v) => Object.keys(v).length > 0);
|
|
34
35
|
if (processed.length > 0) {
|
|
35
|
-
result[
|
|
36
|
+
result[upperKey] = processed;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
continue;
|