@cosmicdrift/kumiko-framework 0.258.0 → 0.259.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/package.json +3 -3
- package/src/__tests__/anonymous-access.integration.test.ts +3 -3
- package/src/__tests__/derived-fields.integration.test.ts +1 -1
- package/src/__tests__/entity-list-limits.integration.test.ts +1 -1
- package/src/__tests__/error-contract.integration.test.ts +1 -1
- package/src/__tests__/ownership-where-write-path.integration.test.ts +1 -1
- package/src/__tests__/ownership.integration.test.ts +7 -3
- package/src/__tests__/schema-cli-rebuild.integration.test.ts +2 -2
- package/src/__tests__/transition-guard.integration.test.ts +3 -3
- package/src/api/__tests__/api.test.ts +21 -3
- package/src/api/__tests__/batch.integration.test.ts +3 -3
- package/src/api/__tests__/body-limit.test.ts +7 -1
- package/src/api/__tests__/dispatcher-live.integration.test.ts +1 -1
- package/src/api/__tests__/http-route-rate-limit.integration.test.ts +7 -1
- package/src/api/__tests__/metrics-endpoint.test.ts +7 -1
- package/src/api/__tests__/nested-write.integration.test.ts +3 -3
- package/src/api/__tests__/server-boot-guards.test.ts +11 -4
- package/src/bun-db/__tests__/write-brand.test.ts +4 -1
- package/src/changes.json +6 -0
- package/src/db/__tests__/apply-entity-event-tenant.integration.test.ts +1 -1
- package/src/db/__tests__/assert-exists-in.integration.test.ts +1 -1
- package/src/db/__tests__/assert-no-unreachable-live-rows.integration.test.ts +2 -2
- package/src/db/__tests__/collect-table-metas.test.ts +9 -5
- package/src/db/__tests__/compound-types.test.ts +1 -1
- package/src/db/__tests__/config-seed.integration.test.ts +3 -3
- package/src/db/__tests__/eagerload.integration.test.ts +7 -7
- package/src/db/__tests__/entity-table-from-registry.test.ts +8 -2
- package/src/db/__tests__/entity-table-meta-source.test.ts +7 -3
- package/src/db/__tests__/entity-table-types.test.ts +4 -4
- package/src/db/__tests__/event-store-executor-list.integration.test.ts +9 -2
- package/src/db/__tests__/event-store-executor-reference-search.integration.test.ts +4 -4
- package/src/db/__tests__/event-store-executor-reference-sort.integration.test.ts +3 -3
- package/src/db/__tests__/event-store-executor-write-verbs.integration.test.ts +4 -4
- package/src/db/__tests__/event-store-executor.integration.test.ts +11 -6
- package/src/db/__tests__/event-store-system-stream.integration.test.ts +2 -2
- package/src/db/__tests__/implicit-projection-equivalence.integration.test.ts +6 -4
- package/src/db/__tests__/located-timestamp.test.ts +1 -1
- package/src/db/__tests__/money.test.ts +1 -1
- package/src/db/__tests__/multi-row-insert.integration.test.ts +3 -3
- package/src/db/__tests__/schema-migration.integration.test.ts +30 -14
- package/src/db/__tests__/source-shadow-create.integration.test.ts +1 -1
- package/src/db/__tests__/table-builder-indexes.test.ts +14 -12
- package/src/db/__tests__/table-builder-required.test.ts +2 -2
- package/src/db/__tests__/tenant-db-where-merge.integration.test.ts +1 -1
- package/src/db/__tests__/tenant-db-where-merge.test.ts +5 -2
- package/src/db/__tests__/tenant-db.integration.test.ts +2 -2
- package/src/db/__tests__/unique-violation-mapping.integration.test.ts +2 -2
- package/src/engine/__tests__/boot-validator-action-wiring.test.ts +50 -8
- package/src/engine/__tests__/boot-validator-dashboard.test.ts +138 -1
- package/src/engine/__tests__/boot-validator-detail-for.test.ts +28 -4
- package/src/engine/__tests__/boot-validator-entity-list.test.ts +20 -4
- package/src/engine/__tests__/boot-validator-i18n-keys.test.ts +9 -3
- package/src/engine/__tests__/boot-validator.test.ts +488 -91
- package/src/engine/__tests__/define-feature-entity-mapping.test.ts +2 -2
- package/src/engine/__tests__/engine.test.ts +128 -37
- package/src/engine/__tests__/entity-handlers.test.ts +3 -3
- package/src/engine/__tests__/entity-presave-wiring.integration.test.ts +4 -4
- package/src/engine/__tests__/entity-restore-tenant-isolation.integration.test.ts +1 -1
- package/src/engine/__tests__/entity-write-crosstenant.integration.test.ts +1 -1
- package/src/engine/__tests__/extend-entity-projection.test.ts +1 -1
- package/src/engine/__tests__/extends-registrar.test.ts +13 -3
- package/src/engine/__tests__/factories-long-text.test.ts +10 -6
- package/src/engine/__tests__/feature-crud-shorthand.test.ts +1 -1
- package/src/engine/__tests__/feature-crud-verb-access.integration.test.ts +1 -1
- package/src/engine/__tests__/multiselect-filter.integration.test.ts +1 -1
- package/src/engine/__tests__/nav.test.ts +1 -1
- package/src/engine/__tests__/pipeline-handler.integration.test.ts +1 -1
- package/src/engine/__tests__/pipeline-observability.integration.test.ts +1 -1
- package/src/engine/__tests__/pipeline-performance.integration.test.ts +1 -1
- package/src/engine/__tests__/projection-detail-actions.test.ts +6 -1
- package/src/engine/__tests__/projection-detail-singleton.test.ts +6 -1
- package/src/engine/__tests__/projection-detail-tabs.test.ts +6 -1
- package/src/engine/__tests__/projection-list-filter.integration.test.ts +7 -2
- package/src/engine/__tests__/projection.test.ts +1 -1
- package/src/engine/__tests__/registry.test.ts +13 -3
- package/src/engine/__tests__/schema-builder.test.ts +39 -11
- package/src/engine/__tests__/screen.test.ts +21 -15
- package/src/engine/__tests__/search-payload-extension.test.ts +15 -3
- package/src/engine/__tests__/store-table.test.ts +6 -3
- package/src/engine/__tests__/validate-projection-allowlist.test.ts +12 -4
- package/src/engine/__tests__/validation-hooks.test.ts +7 -1
- package/src/engine/boot-validator/__tests__/ownership.test.ts +2 -2
- package/src/engine/boot-validator/query-refs.ts +11 -0
- package/src/engine/boot-validator/screens.ts +45 -2
- package/src/engine/build-config-feature-schema.ts +2 -2
- package/src/engine/index.ts +2 -0
- package/src/engine/types/index.ts +2 -0
- package/src/es-ops/__tests__/runner.integration.test.ts +1 -1
- package/src/event-store/__tests__/upcaster.integration.test.ts +1 -1
- package/src/files/__tests__/file-field-pipeline.integration.test.ts +1 -1
- package/src/files/__tests__/files.integration.test.ts +1 -1
- package/src/files/file-ref-entity.ts +9 -9
- package/src/i18n/__tests__/i18n.test.ts +7 -1
- package/src/migrations/__tests__/kumiko-drift.integration.test.ts +3 -1
- package/src/migrations/__tests__/pending-rebuilds.integration.test.ts +2 -2
- package/src/pipeline/__tests__/archive-stream.integration.test.ts +1 -1
- package/src/pipeline/__tests__/cascade-handler.integration.test.ts +23 -8
- package/src/pipeline/__tests__/cascade-handler.test.ts +18 -3
- package/src/pipeline/__tests__/causation-chain.integration.test.ts +1 -1
- package/src/pipeline/__tests__/ctx-bridge.integration.test.ts +5 -3
- package/src/pipeline/__tests__/dispatcher.test.ts +42 -6
- package/src/pipeline/__tests__/domain-events-projections.integration.test.ts +2 -2
- package/src/pipeline/__tests__/fetch-for-writing.integration.test.ts +1 -1
- package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +42 -6
- package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +2 -2
- package/src/pipeline/__tests__/msp-multi-hop.integration.test.ts +1 -1
- package/src/pipeline/__tests__/msp-rebuild.integration.test.ts +6 -2
- package/src/pipeline/__tests__/multi-stream-projection.integration.test.ts +6 -2
- package/src/pipeline/__tests__/perf-rebuild.integration.test.ts +1 -1
- package/src/pipeline/__tests__/post-query-hook.integration.test.ts +3 -3
- package/src/pipeline/__tests__/projection-rebuild.integration.test.ts +3 -3
- package/src/pipeline/__tests__/query-projection.integration.test.ts +1 -1
- package/src/pipeline/__tests__/rebuild-poison-quarantine.integration.test.ts +2 -2
- package/src/pipeline/__tests__/search-consumer-named-events.integration.test.ts +13 -2
- package/src/pipeline/__tests__/try-append-event.integration.test.ts +1 -1
- package/src/search/__tests__/reindex-entity.integration.test.ts +6 -1
- package/src/search/__tests__/search-pii-derived-index.integration.test.ts +7 -1
- package/src/stack/__tests__/ownership-boot-guard.integration.test.ts +1 -1
- package/src/testing/__tests__/e2e-generator.test.ts +15 -5
- package/src/testing/shared-entities.ts +4 -2
- package/src/ui-types/index.ts +2 -0
|
@@ -5,11 +5,11 @@ import { createEntity, createTextField } from "../factories";
|
|
|
5
5
|
|
|
6
6
|
const noteEntity = createEntity({
|
|
7
7
|
table: "dfm_notes",
|
|
8
|
-
fields: { title: createTextField({ required: true }) },
|
|
8
|
+
fields: { title: createTextField({ required: true, personal: false, reason: "test_fixture" }) },
|
|
9
9
|
});
|
|
10
10
|
const tagEntity = createEntity({
|
|
11
11
|
table: "dfm_tags",
|
|
12
|
-
fields: { label: createTextField({ required: true }) },
|
|
12
|
+
fields: { label: createTextField({ required: true, personal: false, reason: "test_fixture" }) },
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
const bareCreate = {
|
|
@@ -63,7 +63,7 @@ describe("defineFeature", () => {
|
|
|
63
63
|
createEntity({
|
|
64
64
|
table: "Users",
|
|
65
65
|
fields: {
|
|
66
|
-
email: createTextField({ searchable: true }),
|
|
66
|
+
email: createTextField({ searchable: true, personal: false, reason: "test_fixture" }),
|
|
67
67
|
},
|
|
68
68
|
}),
|
|
69
69
|
);
|
|
@@ -331,7 +331,7 @@ describe("defineFeature", () => {
|
|
|
331
331
|
|
|
332
332
|
describe("field factories", () => {
|
|
333
333
|
test("createTextField has sensible defaults", () => {
|
|
334
|
-
const field = createTextField();
|
|
334
|
+
const field = createTextField({ personal: false, reason: "test_fixture" });
|
|
335
335
|
expect(field.type).toBe("text");
|
|
336
336
|
expect(field.maxLength).toBe(200);
|
|
337
337
|
expect(field.searchable).toBe(false);
|
|
@@ -339,7 +339,13 @@ describe("field factories", () => {
|
|
|
339
339
|
});
|
|
340
340
|
|
|
341
341
|
test("createTextField accepts overrides", () => {
|
|
342
|
-
const field = createTextField({
|
|
342
|
+
const field = createTextField({
|
|
343
|
+
searchable: true,
|
|
344
|
+
maxLength: 500,
|
|
345
|
+
format: "email",
|
|
346
|
+
personal: false,
|
|
347
|
+
reason: "test_fixture",
|
|
348
|
+
});
|
|
343
349
|
expect(field.searchable).toBe(true);
|
|
344
350
|
expect(field.maxLength).toBe(500);
|
|
345
351
|
expect(field.format).toBe("email");
|
|
@@ -367,7 +373,9 @@ describe("createRegistry", () => {
|
|
|
367
373
|
"user",
|
|
368
374
|
createEntity({
|
|
369
375
|
table: "Users",
|
|
370
|
-
fields: {
|
|
376
|
+
fields: {
|
|
377
|
+
email: createTextField({ searchable: true, personal: false, reason: "test_fixture" }),
|
|
378
|
+
},
|
|
371
379
|
}),
|
|
372
380
|
);
|
|
373
381
|
});
|
|
@@ -493,8 +501,12 @@ describe("createRegistry", () => {
|
|
|
493
501
|
createEntity({
|
|
494
502
|
table: "employees",
|
|
495
503
|
fields: {
|
|
496
|
-
name: createTextField(),
|
|
497
|
-
salary: createTextField({
|
|
504
|
+
name: createTextField({ personal: false, reason: "test_fixture" }),
|
|
505
|
+
salary: createTextField({
|
|
506
|
+
access: { write: ["Admin"] },
|
|
507
|
+
personal: false,
|
|
508
|
+
reason: "test_fixture",
|
|
509
|
+
}),
|
|
498
510
|
},
|
|
499
511
|
}),
|
|
500
512
|
);
|
|
@@ -509,7 +521,13 @@ describe("createRegistry", () => {
|
|
|
509
521
|
|
|
510
522
|
test("allows unmapped write handlers when feature has no field-access rules", () => {
|
|
511
523
|
const feature = defineFeature("admin", (r) => {
|
|
512
|
-
r.entity(
|
|
524
|
+
r.entity(
|
|
525
|
+
"setting",
|
|
526
|
+
createEntity({
|
|
527
|
+
table: "settings",
|
|
528
|
+
fields: { key: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
529
|
+
}),
|
|
530
|
+
);
|
|
513
531
|
// No field-access rules on entity → "reset" without entity prefix is fine
|
|
514
532
|
r.writeHandler("reset", z.object({}), async () => ({ isSuccess: true, data: null }), {
|
|
515
533
|
access: { openToAll: true },
|
|
@@ -526,8 +544,12 @@ describe("createRegistry", () => {
|
|
|
526
544
|
createEntity({
|
|
527
545
|
table: "employees",
|
|
528
546
|
fields: {
|
|
529
|
-
name: createTextField(),
|
|
530
|
-
salary: createTextField({
|
|
547
|
+
name: createTextField({ personal: false, reason: "test_fixture" }),
|
|
548
|
+
salary: createTextField({
|
|
549
|
+
access: { write: ["Admin"] },
|
|
550
|
+
personal: false,
|
|
551
|
+
reason: "test_fixture",
|
|
552
|
+
}),
|
|
531
553
|
},
|
|
532
554
|
}),
|
|
533
555
|
);
|
|
@@ -553,7 +575,11 @@ describe("createRegistry", () => {
|
|
|
553
575
|
createEntity({
|
|
554
576
|
table: "employees",
|
|
555
577
|
fields: {
|
|
556
|
-
salary: createTextField({
|
|
578
|
+
salary: createTextField({
|
|
579
|
+
access: { read: ["Admin"] },
|
|
580
|
+
personal: false,
|
|
581
|
+
reason: "test_fixture",
|
|
582
|
+
}),
|
|
557
583
|
},
|
|
558
584
|
}),
|
|
559
585
|
);
|
|
@@ -580,7 +606,11 @@ describe("createRegistry", () => {
|
|
|
580
606
|
createEntity({
|
|
581
607
|
table: "employees",
|
|
582
608
|
fields: {
|
|
583
|
-
salary: createTextField({
|
|
609
|
+
salary: createTextField({
|
|
610
|
+
access: { read: ["Admin"] },
|
|
611
|
+
personal: false,
|
|
612
|
+
reason: "test_fixture",
|
|
613
|
+
}),
|
|
584
614
|
},
|
|
585
615
|
}),
|
|
586
616
|
);
|
|
@@ -610,9 +640,13 @@ describe("createRegistry", () => {
|
|
|
610
640
|
createEntity({
|
|
611
641
|
table: "Users",
|
|
612
642
|
fields: {
|
|
613
|
-
email: createTextField({ searchable: true }),
|
|
614
|
-
firstName: createTextField(),
|
|
615
|
-
lastName: createTextField({
|
|
643
|
+
email: createTextField({ searchable: true, personal: false, reason: "test_fixture" }),
|
|
644
|
+
firstName: createTextField({ personal: false, reason: "test_fixture" }),
|
|
645
|
+
lastName: createTextField({
|
|
646
|
+
searchable: true,
|
|
647
|
+
personal: false,
|
|
648
|
+
reason: "test_fixture",
|
|
649
|
+
}),
|
|
616
650
|
isEnabled: createBooleanField(),
|
|
617
651
|
},
|
|
618
652
|
}),
|
|
@@ -628,14 +662,17 @@ describe("createRegistry", () => {
|
|
|
628
662
|
const feature = defineFeature("crm", (r) => {
|
|
629
663
|
r.entity(
|
|
630
664
|
"customer",
|
|
631
|
-
createEntity({
|
|
665
|
+
createEntity({
|
|
666
|
+
table: "Customers",
|
|
667
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
668
|
+
}),
|
|
632
669
|
);
|
|
633
670
|
r.entity(
|
|
634
671
|
"order",
|
|
635
672
|
createEntity({
|
|
636
673
|
table: "Orders",
|
|
637
674
|
fields: {
|
|
638
|
-
note: createTextField({ searchable: true }),
|
|
675
|
+
note: createTextField({ searchable: true, personal: false, reason: "test_fixture" }),
|
|
639
676
|
customerId: {
|
|
640
677
|
type: "reference",
|
|
641
678
|
entity: "customer",
|
|
@@ -662,7 +699,10 @@ describe("createRegistry", () => {
|
|
|
662
699
|
const feature = defineFeature("crm", (r) => {
|
|
663
700
|
r.entity(
|
|
664
701
|
"customer",
|
|
665
|
-
createEntity({
|
|
702
|
+
createEntity({
|
|
703
|
+
table: "Customers",
|
|
704
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
705
|
+
}),
|
|
666
706
|
);
|
|
667
707
|
r.entity(
|
|
668
708
|
"order",
|
|
@@ -680,7 +720,13 @@ describe("createRegistry", () => {
|
|
|
680
720
|
|
|
681
721
|
test("throws at boot when searchable is combined with multiple on a reference field (fw#2660)", () => {
|
|
682
722
|
const feature = defineFeature("crm", (r) => {
|
|
683
|
-
r.entity(
|
|
723
|
+
r.entity(
|
|
724
|
+
"tag",
|
|
725
|
+
createEntity({
|
|
726
|
+
table: "Tags",
|
|
727
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
728
|
+
}),
|
|
729
|
+
);
|
|
684
730
|
r.entity(
|
|
685
731
|
"post",
|
|
686
732
|
createEntity({
|
|
@@ -705,14 +751,17 @@ describe("createRegistry", () => {
|
|
|
705
751
|
const feature = defineFeature("crm", (r) => {
|
|
706
752
|
r.entity(
|
|
707
753
|
"customer",
|
|
708
|
-
createEntity({
|
|
754
|
+
createEntity({
|
|
755
|
+
table: "Customers",
|
|
756
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
757
|
+
}),
|
|
709
758
|
);
|
|
710
759
|
r.entity(
|
|
711
760
|
"order",
|
|
712
761
|
createEntity({
|
|
713
762
|
table: "Orders",
|
|
714
763
|
fields: {
|
|
715
|
-
note: createTextField({ sortable: true }),
|
|
764
|
+
note: createTextField({ sortable: true, personal: false, reason: "test_fixture" }),
|
|
716
765
|
customerId: {
|
|
717
766
|
type: "reference",
|
|
718
767
|
entity: "customer",
|
|
@@ -736,7 +785,13 @@ describe("createRegistry", () => {
|
|
|
736
785
|
|
|
737
786
|
test("resolves a cross-feature sortable reference to its bare entity name (fw#2741)", () => {
|
|
738
787
|
const users = defineFeature("users", (r) => {
|
|
739
|
-
r.entity(
|
|
788
|
+
r.entity(
|
|
789
|
+
"user",
|
|
790
|
+
createEntity({
|
|
791
|
+
table: "Users",
|
|
792
|
+
fields: { email: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
793
|
+
}),
|
|
794
|
+
);
|
|
740
795
|
});
|
|
741
796
|
const crm = defineFeature("crm", (r) => {
|
|
742
797
|
r.entity(
|
|
@@ -765,7 +820,10 @@ describe("createRegistry", () => {
|
|
|
765
820
|
const feature = defineFeature("crm", (r) => {
|
|
766
821
|
r.entity(
|
|
767
822
|
"customer",
|
|
768
|
-
createEntity({
|
|
823
|
+
createEntity({
|
|
824
|
+
table: "Customers",
|
|
825
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
826
|
+
}),
|
|
769
827
|
);
|
|
770
828
|
r.entity(
|
|
771
829
|
"order",
|
|
@@ -785,7 +843,10 @@ describe("createRegistry", () => {
|
|
|
785
843
|
const feature = defineFeature("crm", (r) => {
|
|
786
844
|
r.entity(
|
|
787
845
|
"customer",
|
|
788
|
-
createEntity({
|
|
846
|
+
createEntity({
|
|
847
|
+
table: "Customers",
|
|
848
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
849
|
+
}),
|
|
789
850
|
);
|
|
790
851
|
r.entity(
|
|
791
852
|
"order",
|
|
@@ -808,7 +869,13 @@ describe("createRegistry", () => {
|
|
|
808
869
|
|
|
809
870
|
test("throws at boot when sortable is combined with multiple on a reference field (fw#2741)", () => {
|
|
810
871
|
const feature = defineFeature("crm", (r) => {
|
|
811
|
-
r.entity(
|
|
872
|
+
r.entity(
|
|
873
|
+
"tag",
|
|
874
|
+
createEntity({
|
|
875
|
+
table: "Tags",
|
|
876
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
877
|
+
}),
|
|
878
|
+
);
|
|
812
879
|
r.entity(
|
|
813
880
|
"post",
|
|
814
881
|
createEntity({
|
|
@@ -883,12 +950,12 @@ describe("entity options", () => {
|
|
|
883
950
|
|
|
884
951
|
describe("sortable fields", () => {
|
|
885
952
|
test("createTextField supports sortable property", () => {
|
|
886
|
-
const field = createTextField({ sortable: true });
|
|
953
|
+
const field = createTextField({ sortable: true, personal: false, reason: "test_fixture" });
|
|
887
954
|
expect(field.sortable).toBe(true);
|
|
888
955
|
});
|
|
889
956
|
|
|
890
957
|
test("sortable defaults to false", () => {
|
|
891
|
-
const field = createTextField();
|
|
958
|
+
const field = createTextField({ personal: false, reason: "test_fixture" });
|
|
892
959
|
expect(field.sortable).toBe(false);
|
|
893
960
|
});
|
|
894
961
|
|
|
@@ -899,9 +966,9 @@ describe("sortable fields", () => {
|
|
|
899
966
|
createEntity({
|
|
900
967
|
table: "Items",
|
|
901
968
|
fields: {
|
|
902
|
-
name: createTextField({ sortable: true }),
|
|
903
|
-
email: createTextField(),
|
|
904
|
-
rank: createTextField({ sortable: true }),
|
|
969
|
+
name: createTextField({ sortable: true, personal: false, reason: "test_fixture" }),
|
|
970
|
+
email: createTextField({ personal: false, reason: "test_fixture" }),
|
|
971
|
+
rank: createTextField({ sortable: true, personal: false, reason: "test_fixture" }),
|
|
905
972
|
},
|
|
906
973
|
}),
|
|
907
974
|
);
|
|
@@ -984,7 +1051,13 @@ describe("createApp", () => {
|
|
|
984
1051
|
|
|
985
1052
|
test("softDeleteDefault is true by default, configurable via softDelete", () => {
|
|
986
1053
|
const feature = defineFeature("test", (r) => {
|
|
987
|
-
r.entity(
|
|
1054
|
+
r.entity(
|
|
1055
|
+
"item",
|
|
1056
|
+
createEntity({
|
|
1057
|
+
table: "Items",
|
|
1058
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
1059
|
+
}),
|
|
1060
|
+
);
|
|
988
1061
|
});
|
|
989
1062
|
|
|
990
1063
|
const app1 = createApp({ roles: ["Admin"], features: [feature] });
|
|
@@ -1319,7 +1392,7 @@ describe("createApp", () => {
|
|
|
1319
1392
|
createEntity({
|
|
1320
1393
|
table: "Invoices",
|
|
1321
1394
|
fields: {
|
|
1322
|
-
title: createTextField(),
|
|
1395
|
+
title: createTextField({ personal: false, reason: "test_fixture" }),
|
|
1323
1396
|
lines: createEmbeddedListField(
|
|
1324
1397
|
{ amount: { type: "money" } },
|
|
1325
1398
|
{ totalsMatch: { amount: "title" } },
|
|
@@ -1403,7 +1476,7 @@ describe("createApp", () => {
|
|
|
1403
1476
|
"doc",
|
|
1404
1477
|
createEntity({
|
|
1405
1478
|
table: "Docs",
|
|
1406
|
-
fields: { title: createTextField() },
|
|
1479
|
+
fields: { title: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
1407
1480
|
transitions: {
|
|
1408
1481
|
title: { a: ["b"] },
|
|
1409
1482
|
},
|
|
@@ -1443,7 +1516,7 @@ describe("createApp", () => {
|
|
|
1443
1516
|
"doc",
|
|
1444
1517
|
createEntity({
|
|
1445
1518
|
table: "Docs",
|
|
1446
|
-
fields: { title: createTextField() },
|
|
1519
|
+
fields: { title: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
1447
1520
|
transitions: {
|
|
1448
1521
|
nonExistent: { a: ["b"] },
|
|
1449
1522
|
},
|
|
@@ -1794,11 +1867,17 @@ describe("r.relation()", () => {
|
|
|
1794
1867
|
const feature = defineFeature("test", (r) => {
|
|
1795
1868
|
r.entity(
|
|
1796
1869
|
"user",
|
|
1797
|
-
createEntity({
|
|
1870
|
+
createEntity({
|
|
1871
|
+
table: "Users",
|
|
1872
|
+
fields: { departmentId: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
1873
|
+
}),
|
|
1798
1874
|
);
|
|
1799
1875
|
r.entity(
|
|
1800
1876
|
"department",
|
|
1801
|
-
createEntity({
|
|
1877
|
+
createEntity({
|
|
1878
|
+
table: "Departments",
|
|
1879
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
1880
|
+
}),
|
|
1802
1881
|
);
|
|
1803
1882
|
r.relation("user", "department", {
|
|
1804
1883
|
type: "belongsTo",
|
|
@@ -1814,7 +1893,13 @@ describe("r.relation()", () => {
|
|
|
1814
1893
|
test("registers manyToMany relation", () => {
|
|
1815
1894
|
const feature = defineFeature("test", (r) => {
|
|
1816
1895
|
r.entity("user", createEntity({ table: "Users", fields: {} }));
|
|
1817
|
-
r.entity(
|
|
1896
|
+
r.entity(
|
|
1897
|
+
"role",
|
|
1898
|
+
createEntity({
|
|
1899
|
+
table: "Roles",
|
|
1900
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
1901
|
+
}),
|
|
1902
|
+
);
|
|
1818
1903
|
r.relation("user", "roles", {
|
|
1819
1904
|
type: "manyToMany",
|
|
1820
1905
|
target: "role",
|
|
@@ -1848,7 +1933,13 @@ describe("registry relations", () => {
|
|
|
1848
1933
|
test("getRelations returns relations within same feature", () => {
|
|
1849
1934
|
const f1 = defineFeature("users", (r) => {
|
|
1850
1935
|
r.entity("user", createEntity({ table: "Users", fields: {} }));
|
|
1851
|
-
r.entity(
|
|
1936
|
+
r.entity(
|
|
1937
|
+
"role",
|
|
1938
|
+
createEntity({
|
|
1939
|
+
table: "Roles",
|
|
1940
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
1941
|
+
}),
|
|
1942
|
+
);
|
|
1852
1943
|
r.relation("user", "roles", {
|
|
1853
1944
|
type: "manyToMany",
|
|
1854
1945
|
target: "role",
|
|
@@ -24,15 +24,15 @@ const VALID_UUID = "00000000-0000-4000-8000-000000000001";
|
|
|
24
24
|
const noteEntity = createEntity({
|
|
25
25
|
table: "notes",
|
|
26
26
|
fields: {
|
|
27
|
-
title: createTextField({ required: true }),
|
|
28
|
-
body: createTextField(),
|
|
27
|
+
title: createTextField({ required: true, personal: false, reason: "test_fixture" }),
|
|
28
|
+
body: createTextField({ personal: false, reason: "test_fixture" }),
|
|
29
29
|
},
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
const noteEntitySoftDelete = createEntity({
|
|
33
33
|
table: "notes_soft",
|
|
34
34
|
fields: {
|
|
35
|
-
title: createTextField({ required: true }),
|
|
35
|
+
title: createTextField({ required: true, personal: false, reason: "test_fixture" }),
|
|
36
36
|
},
|
|
37
37
|
softDelete: true,
|
|
38
38
|
});
|
|
@@ -13,9 +13,9 @@ import { from } from "../ownership";
|
|
|
13
13
|
const contactEntity = createEntity({
|
|
14
14
|
table: "presave_wiring_contacts",
|
|
15
15
|
fields: {
|
|
16
|
-
firstName: createTextField({ required: true }),
|
|
17
|
-
lastName: createTextField({ required: true }),
|
|
18
|
-
displayName: createTextField(),
|
|
16
|
+
firstName: createTextField({ required: true, personal: false, reason: "test_fixture" }),
|
|
17
|
+
lastName: createTextField({ required: true, personal: false, reason: "test_fixture" }),
|
|
18
|
+
displayName: createTextField({ personal: false, reason: "test_fixture" }),
|
|
19
19
|
// authorId is never set by the client — only deriveAuthorId (a preSave
|
|
20
20
|
// hook) writes it. secretNote's ownership rule checks authorId, so
|
|
21
21
|
// create only succeeds if the hook ran BEFORE the field-ownership check
|
|
@@ -64,7 +64,7 @@ const contactFeature = defineFeature("presave-wiring", (r) => {
|
|
|
64
64
|
|
|
65
65
|
const throwingEntity = createEntity({
|
|
66
66
|
table: "presave_wiring_throwing",
|
|
67
|
-
fields: { name: createTextField({ required: true }) },
|
|
67
|
+
fields: { name: createTextField({ required: true, personal: false, reason: "test_fixture" }) },
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
const throwingFeature = defineFeature("presave-wiring-throw", (r) => {
|
|
@@ -33,7 +33,7 @@ const thingEntity = createEntity({
|
|
|
33
33
|
table: "ctrestore_things",
|
|
34
34
|
softDelete: true,
|
|
35
35
|
fields: {
|
|
36
|
-
label: createTextField({ required: true }),
|
|
36
|
+
label: createTextField({ required: true, personal: false, reason: "test_fixture" }),
|
|
37
37
|
},
|
|
38
38
|
access: { write: { Admin: "all", SystemAdmin: from("user:tenantId", "tenantId") } },
|
|
39
39
|
});
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
const thingEntity = createEntity({
|
|
39
39
|
table: "ctwrite_things",
|
|
40
40
|
fields: {
|
|
41
|
-
label: createTextField({ required: true }),
|
|
41
|
+
label: createTextField({ required: true, personal: false, reason: "test_fixture" }),
|
|
42
42
|
},
|
|
43
43
|
access: { write: { Admin: "all", SystemAdmin: from("user:tenantId", "tenantId") } },
|
|
44
44
|
});
|
|
@@ -11,7 +11,7 @@ import { createRegistry } from "../registry";
|
|
|
11
11
|
function exampleEntity(name = "unit") {
|
|
12
12
|
return createEntity({
|
|
13
13
|
table: name,
|
|
14
|
-
fields: { name: createTextField() },
|
|
14
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -85,7 +85,7 @@ describe("extendsRegistrar", () => {
|
|
|
85
85
|
"vehicle",
|
|
86
86
|
createEntity({
|
|
87
87
|
table: "Vehicles",
|
|
88
|
-
fields: { name: createTextField() },
|
|
88
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
89
89
|
}),
|
|
90
90
|
);
|
|
91
91
|
r.useExtension("customFields", "vehicle");
|
|
@@ -108,7 +108,13 @@ describe("extendsRegistrar", () => {
|
|
|
108
108
|
});
|
|
109
109
|
const consumer = defineFeature("credit", (r) => {
|
|
110
110
|
r.requires("audit");
|
|
111
|
-
r.entity(
|
|
111
|
+
r.entity(
|
|
112
|
+
"credit",
|
|
113
|
+
createEntity({
|
|
114
|
+
table: "Credits",
|
|
115
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
112
118
|
r.writeHandler("create", z.object({ name: z.string() }), async () => ({
|
|
113
119
|
isSuccess: true as const,
|
|
114
120
|
data: { id: "c1" },
|
|
@@ -134,7 +140,11 @@ describe("extendsRegistrar", () => {
|
|
|
134
140
|
const consumer = defineFeature("fleet", (r) => {
|
|
135
141
|
r.entity(
|
|
136
142
|
"vehicle",
|
|
137
|
-
createEntity({
|
|
143
|
+
createEntity({
|
|
144
|
+
table: "Vehicles",
|
|
145
|
+
idType: "uuid",
|
|
146
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
147
|
+
}),
|
|
138
148
|
);
|
|
139
149
|
// Explicit handlers — the entity mapping is inferred from the
|
|
140
150
|
// "vehicle:" prefix via tryMapEntity, so the extension's preSave
|
|
@@ -23,7 +23,7 @@ describe("createLongTextField — runtime shape", () => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
test("required: true is preserved as literal in the return type", () => {
|
|
26
|
-
const f = createLongTextField({ required: true });
|
|
26
|
+
const f = createLongTextField({ required: true, personal: false, reason: "test_fixture" });
|
|
27
27
|
expect(f.required).toBe(true);
|
|
28
28
|
// Literal-type-pin: TypeScript should narrow `required` to `true`,
|
|
29
29
|
// not `boolean`. If this test compiles with `f.required: boolean`,
|
|
@@ -32,7 +32,11 @@ describe("createLongTextField — runtime shape", () => {
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
test("maxLength is propagated through", () => {
|
|
35
|
-
const f = createLongTextField({
|
|
35
|
+
const f = createLongTextField({
|
|
36
|
+
maxLength: 1_000_000,
|
|
37
|
+
personal: false,
|
|
38
|
+
reason: "test_fixture",
|
|
39
|
+
});
|
|
36
40
|
expect(f.maxLength).toBe(1_000_000);
|
|
37
41
|
});
|
|
38
42
|
|
|
@@ -55,13 +59,13 @@ describe("LongTextFieldDef — type-level non-indexable enforcement", () => {
|
|
|
55
59
|
// back to LongTextFieldDef, the @ts-expect-error fails (because
|
|
56
60
|
// the call would suddenly type-check), turning the test red.
|
|
57
61
|
// @ts-expect-error sortable is NOT allowed on longText
|
|
58
|
-
const f1 = createLongTextField({ sortable: true });
|
|
62
|
+
const f1 = createLongTextField({ sortable: true, personal: false, reason: "test_fixture" });
|
|
59
63
|
// @ts-expect-error searchable is NOT allowed on longText
|
|
60
|
-
const f2 = createLongTextField({ searchable: true });
|
|
64
|
+
const f2 = createLongTextField({ searchable: true, personal: false, reason: "test_fixture" });
|
|
61
65
|
// @ts-expect-error filterable is NOT allowed on longText
|
|
62
|
-
const f3 = createLongTextField({ filterable: true });
|
|
66
|
+
const f3 = createLongTextField({ filterable: true, personal: false, reason: "test_fixture" });
|
|
63
67
|
// @ts-expect-error format is NOT allowed on longText
|
|
64
|
-
const f4 = createLongTextField({ format: "email" });
|
|
68
|
+
const f4 = createLongTextField({ format: "email", personal: false, reason: "test_fixture" });
|
|
65
69
|
|
|
66
70
|
// Runtime sanity: trotz der @ts-expect-error-ignored options ist
|
|
67
71
|
// das field zur Laufzeit erfolgreich erzeugt — type-level-blockade
|
|
@@ -4,7 +4,7 @@ import { createEntity, createTextField } from "../factories";
|
|
|
4
4
|
|
|
5
5
|
const taskEntity = createEntity({
|
|
6
6
|
table: "crud_shorthand_tasks",
|
|
7
|
-
fields: { title: createTextField({ required: true }) },
|
|
7
|
+
fields: { title: createTextField({ required: true, personal: false, reason: "test_fixture" }) },
|
|
8
8
|
softDelete: true,
|
|
9
9
|
});
|
|
10
10
|
|
|
@@ -6,7 +6,7 @@ import { createEntity, createTextField } from "../factories";
|
|
|
6
6
|
|
|
7
7
|
const propertyEntity = createEntity({
|
|
8
8
|
table: "crud_verb_access_properties",
|
|
9
|
-
fields: { title: createTextField({ required: true }) },
|
|
9
|
+
fields: { title: createTextField({ required: true, personal: false, reason: "test_fixture" }) },
|
|
10
10
|
softDelete: true,
|
|
11
11
|
});
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ import { createEntity, createMultiSelectField, createTextField } from "../factor
|
|
|
15
15
|
const equipmentEntity = createEntity({
|
|
16
16
|
table: "ms_filter_equipment",
|
|
17
17
|
fields: {
|
|
18
|
-
name: createTextField({ required: true }),
|
|
18
|
+
name: createTextField({ required: true, personal: false, reason: "test_fixture" }),
|
|
19
19
|
tags: createMultiSelectField({
|
|
20
20
|
options: ["vip", "urgent", "loaner"] as const,
|
|
21
21
|
filterable: true,
|
|
@@ -12,7 +12,7 @@ function validateBoot(features: Parameters<typeof validateBootRaw>[0]): void {
|
|
|
12
12
|
function productEntity() {
|
|
13
13
|
return createEntity({
|
|
14
14
|
table: "products",
|
|
15
|
-
fields: { name: createTextField() },
|
|
15
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -170,7 +170,7 @@ const logHandler = defineWriteHandler({
|
|
|
170
170
|
// unsafeProjection.* that targets it).
|
|
171
171
|
const widgetEntity = createEntity({
|
|
172
172
|
table: "pipeline_widget",
|
|
173
|
-
fields: { label: createTextField({ required: true }) },
|
|
173
|
+
fields: { label: createTextField({ required: true, personal: false, reason: "test_fixture" }) },
|
|
174
174
|
});
|
|
175
175
|
const widgetTable = buildEntityTable("widget", widgetEntity);
|
|
176
176
|
const widgetExecutor = createEventStoreExecutor(widgetTable, widgetEntity, {
|
|
@@ -52,7 +52,7 @@ const observedHandler = defineWriteHandler({
|
|
|
52
52
|
|
|
53
53
|
const obsEntity = createEntity({
|
|
54
54
|
table: "obs_smoke_things",
|
|
55
|
-
fields: { label: createTextField({ required: true }) },
|
|
55
|
+
fields: { label: createTextField({ required: true, personal: false, reason: "test_fixture" }) },
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
const obsFeature = defineFeature("obstest", (r) => {
|
|
@@ -62,7 +62,7 @@ const trivialFreeform = defineWriteHandler({
|
|
|
62
62
|
const productEntity = createEntity({
|
|
63
63
|
table: "perf_smoke_products",
|
|
64
64
|
fields: {
|
|
65
|
-
sku: createTextField({ required: true }),
|
|
65
|
+
sku: createTextField({ required: true, personal: false, reason: "test_fixture" }),
|
|
66
66
|
qty: createNumberField({ default: 0 }),
|
|
67
67
|
},
|
|
68
68
|
});
|
|
@@ -87,7 +87,12 @@ describe("validateBoot — projectionDetail actions (fw#2166)", () => {
|
|
|
87
87
|
|
|
88
88
|
test("navigate action with params targeting an entityEdit of the SAME entity (via detailFor) throws — params are a no-op on an update target (review finding 3b)", () => {
|
|
89
89
|
const feature = defineFeature("app", (r) => {
|
|
90
|
-
r.entity(
|
|
90
|
+
r.entity(
|
|
91
|
+
"rent",
|
|
92
|
+
createEntity({
|
|
93
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
91
96
|
r.screen({
|
|
92
97
|
id: "rent-detail",
|
|
93
98
|
type: "projectionDetail",
|
|
@@ -39,7 +39,12 @@ describe("validateBoot — projectionDetail singleton (fw#2312)", () => {
|
|
|
39
39
|
|
|
40
40
|
test("singleton + detailFor throws", () => {
|
|
41
41
|
const feature = defineFeature("app", (r) => {
|
|
42
|
-
r.entity(
|
|
42
|
+
r.entity(
|
|
43
|
+
"user",
|
|
44
|
+
createEntity({
|
|
45
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
43
48
|
r.queryHandler("me", z.object({}), async () => ({ id: "u1" }), {
|
|
44
49
|
access: { openToAll: true },
|
|
45
50
|
});
|
|
@@ -100,7 +100,12 @@ describe("validateBoot — projectionDetail tabs (fw record-layout)", () => {
|
|
|
100
100
|
|
|
101
101
|
test("mode: tabs on entityEdit throws — tabs are projectionDetail-only", () => {
|
|
102
102
|
const feature = defineFeature("app", (r) => {
|
|
103
|
-
r.entity(
|
|
103
|
+
r.entity(
|
|
104
|
+
"rent",
|
|
105
|
+
createEntity({
|
|
106
|
+
fields: { name: createTextField({ personal: false, reason: "test_fixture" }) },
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
104
109
|
r.screen({
|
|
105
110
|
id: "rent-edit",
|
|
106
111
|
type: "entityEdit",
|
|
@@ -22,8 +22,13 @@ import { createEntity, createTextField } from "../factories";
|
|
|
22
22
|
const memberEntity = createEntity({
|
|
23
23
|
table: "pl_filter_members",
|
|
24
24
|
fields: {
|
|
25
|
-
name: createTextField({ required: true }),
|
|
26
|
-
status: createTextField({
|
|
25
|
+
name: createTextField({ required: true, personal: false, reason: "test_fixture" }),
|
|
26
|
+
status: createTextField({
|
|
27
|
+
required: true,
|
|
28
|
+
filterable: true,
|
|
29
|
+
personal: false,
|
|
30
|
+
reason: "test_fixture",
|
|
31
|
+
}),
|
|
27
32
|
},
|
|
28
33
|
});
|
|
29
34
|
|