@nfcard/validation 0.22.1 → 0.23.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.d.ts CHANGED
@@ -5256,6 +5256,22 @@ declare const bookingSchema: z.ZodEffects<z.ZodUnknown, {
5256
5256
  handle: string;
5257
5257
  label?: undefined;
5258
5258
  } | undefined, unknown>;
5259
+ declare const profileBackgroundSchema: z.ZodEffects<z.ZodUnknown, {
5260
+ kind: "solid";
5261
+ from?: undefined;
5262
+ to?: undefined;
5263
+ angleDeg?: undefined;
5264
+ } | {
5265
+ kind: "gradient";
5266
+ from: string;
5267
+ to: string;
5268
+ angleDeg?: undefined;
5269
+ } | {
5270
+ kind: "gradient";
5271
+ from: string;
5272
+ to: string;
5273
+ angleDeg: number;
5274
+ } | undefined, unknown>;
5259
5275
  declare const digitalConfigSchema: z.ZodObject<{
5260
5276
  templateId: z.ZodEnum<["minimal", "bold", "card", "aurora", "classic", "spotlight", "marquee", "riso"]>;
5261
5277
  accentColor: z.ZodString;
@@ -5361,11 +5377,43 @@ declare const digitalConfigSchema: z.ZodObject<{
5361
5377
  handle: string;
5362
5378
  label?: undefined;
5363
5379
  } | undefined, unknown>>;
5380
+ background: z.ZodOptional<z.ZodEffects<z.ZodUnknown, {
5381
+ kind: "solid";
5382
+ from?: undefined;
5383
+ to?: undefined;
5384
+ angleDeg?: undefined;
5385
+ } | {
5386
+ kind: "gradient";
5387
+ from: string;
5388
+ to: string;
5389
+ angleDeg?: undefined;
5390
+ } | {
5391
+ kind: "gradient";
5392
+ from: string;
5393
+ to: string;
5394
+ angleDeg: number;
5395
+ } | undefined, unknown>>;
5364
5396
  }, "strip", z.ZodTypeAny, {
5365
5397
  fontKey: string;
5366
5398
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
5367
5399
  accentColor: string;
5368
5400
  showAvatar: boolean;
5401
+ background?: {
5402
+ kind: "solid";
5403
+ from?: undefined;
5404
+ to?: undefined;
5405
+ angleDeg?: undefined;
5406
+ } | {
5407
+ kind: "gradient";
5408
+ from: string;
5409
+ to: string;
5410
+ angleDeg?: undefined;
5411
+ } | {
5412
+ kind: "gradient";
5413
+ from: string;
5414
+ to: string;
5415
+ angleDeg: number;
5416
+ } | undefined;
5369
5417
  primaryColor?: string | undefined;
5370
5418
  secondaryColor?: string | undefined;
5371
5419
  textColor?: string | undefined;
@@ -5419,6 +5467,7 @@ declare const digitalConfigSchema: z.ZodObject<{
5419
5467
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
5420
5468
  accentColor: string;
5421
5469
  showAvatar: boolean;
5470
+ background?: unknown;
5422
5471
  primaryColor?: string | undefined;
5423
5472
  secondaryColor?: string | undefined;
5424
5473
  textColor?: string | undefined;
@@ -8997,11 +9046,43 @@ declare const configurationCreateSchema: z.ZodObject<{
8997
9046
  handle: string;
8998
9047
  label?: undefined;
8999
9048
  } | undefined, unknown>>;
9049
+ background: z.ZodOptional<z.ZodEffects<z.ZodUnknown, {
9050
+ kind: "solid";
9051
+ from?: undefined;
9052
+ to?: undefined;
9053
+ angleDeg?: undefined;
9054
+ } | {
9055
+ kind: "gradient";
9056
+ from: string;
9057
+ to: string;
9058
+ angleDeg?: undefined;
9059
+ } | {
9060
+ kind: "gradient";
9061
+ from: string;
9062
+ to: string;
9063
+ angleDeg: number;
9064
+ } | undefined, unknown>>;
9000
9065
  }, "strip", z.ZodTypeAny, {
9001
9066
  fontKey: string;
9002
9067
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
9003
9068
  accentColor: string;
9004
9069
  showAvatar: boolean;
9070
+ background?: {
9071
+ kind: "solid";
9072
+ from?: undefined;
9073
+ to?: undefined;
9074
+ angleDeg?: undefined;
9075
+ } | {
9076
+ kind: "gradient";
9077
+ from: string;
9078
+ to: string;
9079
+ angleDeg?: undefined;
9080
+ } | {
9081
+ kind: "gradient";
9082
+ from: string;
9083
+ to: string;
9084
+ angleDeg: number;
9085
+ } | undefined;
9005
9086
  primaryColor?: string | undefined;
9006
9087
  secondaryColor?: string | undefined;
9007
9088
  textColor?: string | undefined;
@@ -9055,6 +9136,7 @@ declare const configurationCreateSchema: z.ZodObject<{
9055
9136
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
9056
9137
  accentColor: string;
9057
9138
  showAvatar: boolean;
9139
+ background?: unknown;
9058
9140
  primaryColor?: string | undefined;
9059
9141
  secondaryColor?: string | undefined;
9060
9142
  textColor?: string | undefined;
@@ -9444,6 +9526,22 @@ declare const configurationCreateSchema: z.ZodObject<{
9444
9526
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
9445
9527
  accentColor: string;
9446
9528
  showAvatar: boolean;
9529
+ background?: {
9530
+ kind: "solid";
9531
+ from?: undefined;
9532
+ to?: undefined;
9533
+ angleDeg?: undefined;
9534
+ } | {
9535
+ kind: "gradient";
9536
+ from: string;
9537
+ to: string;
9538
+ angleDeg?: undefined;
9539
+ } | {
9540
+ kind: "gradient";
9541
+ from: string;
9542
+ to: string;
9543
+ angleDeg: number;
9544
+ } | undefined;
9447
9545
  primaryColor?: string | undefined;
9448
9546
  secondaryColor?: string | undefined;
9449
9547
  textColor?: string | undefined;
@@ -9831,6 +9929,7 @@ declare const configurationCreateSchema: z.ZodObject<{
9831
9929
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
9832
9930
  accentColor: string;
9833
9931
  showAvatar: boolean;
9932
+ background?: unknown;
9834
9933
  primaryColor?: string | undefined;
9835
9934
  secondaryColor?: string | undefined;
9836
9935
  textColor?: string | undefined;
@@ -13394,11 +13493,43 @@ declare const configurationUpdateSchema: z.ZodObject<{
13394
13493
  handle: string;
13395
13494
  label?: undefined;
13396
13495
  } | undefined, unknown>>;
13496
+ background: z.ZodOptional<z.ZodEffects<z.ZodUnknown, {
13497
+ kind: "solid";
13498
+ from?: undefined;
13499
+ to?: undefined;
13500
+ angleDeg?: undefined;
13501
+ } | {
13502
+ kind: "gradient";
13503
+ from: string;
13504
+ to: string;
13505
+ angleDeg?: undefined;
13506
+ } | {
13507
+ kind: "gradient";
13508
+ from: string;
13509
+ to: string;
13510
+ angleDeg: number;
13511
+ } | undefined, unknown>>;
13397
13512
  }, "strip", z.ZodTypeAny, {
13398
13513
  fontKey: string;
13399
13514
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
13400
13515
  accentColor: string;
13401
13516
  showAvatar: boolean;
13517
+ background?: {
13518
+ kind: "solid";
13519
+ from?: undefined;
13520
+ to?: undefined;
13521
+ angleDeg?: undefined;
13522
+ } | {
13523
+ kind: "gradient";
13524
+ from: string;
13525
+ to: string;
13526
+ angleDeg?: undefined;
13527
+ } | {
13528
+ kind: "gradient";
13529
+ from: string;
13530
+ to: string;
13531
+ angleDeg: number;
13532
+ } | undefined;
13402
13533
  primaryColor?: string | undefined;
13403
13534
  secondaryColor?: string | undefined;
13404
13535
  textColor?: string | undefined;
@@ -13452,6 +13583,7 @@ declare const configurationUpdateSchema: z.ZodObject<{
13452
13583
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
13453
13584
  accentColor: string;
13454
13585
  showAvatar: boolean;
13586
+ background?: unknown;
13455
13587
  primaryColor?: string | undefined;
13456
13588
  secondaryColor?: string | undefined;
13457
13589
  textColor?: string | undefined;
@@ -13812,6 +13944,22 @@ declare const configurationUpdateSchema: z.ZodObject<{
13812
13944
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
13813
13945
  accentColor: string;
13814
13946
  showAvatar: boolean;
13947
+ background?: {
13948
+ kind: "solid";
13949
+ from?: undefined;
13950
+ to?: undefined;
13951
+ angleDeg?: undefined;
13952
+ } | {
13953
+ kind: "gradient";
13954
+ from: string;
13955
+ to: string;
13956
+ angleDeg?: undefined;
13957
+ } | {
13958
+ kind: "gradient";
13959
+ from: string;
13960
+ to: string;
13961
+ angleDeg: number;
13962
+ } | undefined;
13815
13963
  primaryColor?: string | undefined;
13816
13964
  secondaryColor?: string | undefined;
13817
13965
  textColor?: string | undefined;
@@ -14188,6 +14336,7 @@ declare const configurationUpdateSchema: z.ZodObject<{
14188
14336
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
14189
14337
  accentColor: string;
14190
14338
  showAvatar: boolean;
14339
+ background?: unknown;
14191
14340
  primaryColor?: string | undefined;
14192
14341
  secondaryColor?: string | undefined;
14193
14342
  textColor?: string | undefined;
@@ -14344,6 +14493,22 @@ declare const cardUpdateSchema: z.ZodObject<{
14344
14493
  handle: string;
14345
14494
  label?: undefined;
14346
14495
  } | undefined, unknown>>>>;
14496
+ background: z.ZodOptional<z.ZodOptional<z.ZodOptional<z.ZodEffects<z.ZodUnknown, {
14497
+ kind: "solid";
14498
+ from?: undefined;
14499
+ to?: undefined;
14500
+ angleDeg?: undefined;
14501
+ } | {
14502
+ kind: "gradient";
14503
+ from: string;
14504
+ to: string;
14505
+ angleDeg?: undefined;
14506
+ } | {
14507
+ kind: "gradient";
14508
+ from: string;
14509
+ to: string;
14510
+ angleDeg: number;
14511
+ } | undefined, unknown>>>>;
14347
14512
  profilePhotoUrl: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
14348
14513
  companyLogoUrl: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
14349
14514
  }, "strip", z.ZodTypeAny, {
@@ -14351,6 +14516,22 @@ declare const cardUpdateSchema: z.ZodObject<{
14351
14516
  companyLogoUrl?: string | undefined;
14352
14517
  fontKey?: string | undefined;
14353
14518
  templateId?: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso" | undefined;
14519
+ background?: {
14520
+ kind: "solid";
14521
+ from?: undefined;
14522
+ to?: undefined;
14523
+ angleDeg?: undefined;
14524
+ } | {
14525
+ kind: "gradient";
14526
+ from: string;
14527
+ to: string;
14528
+ angleDeg?: undefined;
14529
+ } | {
14530
+ kind: "gradient";
14531
+ from: string;
14532
+ to: string;
14533
+ angleDeg: number;
14534
+ } | undefined;
14354
14535
  accentColor?: string | undefined;
14355
14536
  primaryColor?: string | undefined;
14356
14537
  secondaryColor?: string | undefined;
@@ -14406,6 +14587,7 @@ declare const cardUpdateSchema: z.ZodObject<{
14406
14587
  companyLogoUrl?: string | undefined;
14407
14588
  fontKey?: string | undefined;
14408
14589
  templateId?: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso" | undefined;
14590
+ background?: unknown;
14409
14591
  accentColor?: string | undefined;
14410
14592
  primaryColor?: string | undefined;
14411
14593
  secondaryColor?: string | undefined;
@@ -14449,6 +14631,22 @@ declare const cardUpdateSchema: z.ZodObject<{
14449
14631
  companyLogoUrl?: string | undefined;
14450
14632
  fontKey?: string | undefined;
14451
14633
  templateId?: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso" | undefined;
14634
+ background?: {
14635
+ kind: "solid";
14636
+ from?: undefined;
14637
+ to?: undefined;
14638
+ angleDeg?: undefined;
14639
+ } | {
14640
+ kind: "gradient";
14641
+ from: string;
14642
+ to: string;
14643
+ angleDeg?: undefined;
14644
+ } | {
14645
+ kind: "gradient";
14646
+ from: string;
14647
+ to: string;
14648
+ angleDeg: number;
14649
+ } | undefined;
14452
14650
  accentColor?: string | undefined;
14453
14651
  primaryColor?: string | undefined;
14454
14652
  secondaryColor?: string | undefined;
@@ -14507,6 +14705,7 @@ declare const cardUpdateSchema: z.ZodObject<{
14507
14705
  companyLogoUrl?: string | undefined;
14508
14706
  fontKey?: string | undefined;
14509
14707
  templateId?: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso" | undefined;
14708
+ background?: unknown;
14510
14709
  accentColor?: string | undefined;
14511
14710
  primaryColor?: string | undefined;
14512
14711
  secondaryColor?: string | undefined;
@@ -14738,11 +14937,43 @@ declare const adminCardCreateSchema: z.ZodObject<{
14738
14937
  handle: string;
14739
14938
  label?: undefined;
14740
14939
  } | undefined, unknown>>;
14940
+ background: z.ZodOptional<z.ZodEffects<z.ZodUnknown, {
14941
+ kind: "solid";
14942
+ from?: undefined;
14943
+ to?: undefined;
14944
+ angleDeg?: undefined;
14945
+ } | {
14946
+ kind: "gradient";
14947
+ from: string;
14948
+ to: string;
14949
+ angleDeg?: undefined;
14950
+ } | {
14951
+ kind: "gradient";
14952
+ from: string;
14953
+ to: string;
14954
+ angleDeg: number;
14955
+ } | undefined, unknown>>;
14741
14956
  }, "strip", z.ZodTypeAny, {
14742
14957
  fontKey: string;
14743
14958
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
14744
14959
  accentColor: string;
14745
14960
  showAvatar: boolean;
14961
+ background?: {
14962
+ kind: "solid";
14963
+ from?: undefined;
14964
+ to?: undefined;
14965
+ angleDeg?: undefined;
14966
+ } | {
14967
+ kind: "gradient";
14968
+ from: string;
14969
+ to: string;
14970
+ angleDeg?: undefined;
14971
+ } | {
14972
+ kind: "gradient";
14973
+ from: string;
14974
+ to: string;
14975
+ angleDeg: number;
14976
+ } | undefined;
14746
14977
  primaryColor?: string | undefined;
14747
14978
  secondaryColor?: string | undefined;
14748
14979
  textColor?: string | undefined;
@@ -14796,6 +15027,7 @@ declare const adminCardCreateSchema: z.ZodObject<{
14796
15027
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
14797
15028
  accentColor: string;
14798
15029
  showAvatar: boolean;
15030
+ background?: unknown;
14799
15031
  primaryColor?: string | undefined;
14800
15032
  secondaryColor?: string | undefined;
14801
15033
  textColor?: string | undefined;
@@ -18181,6 +18413,22 @@ declare const adminCardCreateSchema: z.ZodObject<{
18181
18413
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
18182
18414
  accentColor: string;
18183
18415
  showAvatar: boolean;
18416
+ background?: {
18417
+ kind: "solid";
18418
+ from?: undefined;
18419
+ to?: undefined;
18420
+ angleDeg?: undefined;
18421
+ } | {
18422
+ kind: "gradient";
18423
+ from: string;
18424
+ to: string;
18425
+ angleDeg?: undefined;
18426
+ } | {
18427
+ kind: "gradient";
18428
+ from: string;
18429
+ to: string;
18430
+ angleDeg: number;
18431
+ } | undefined;
18184
18432
  primaryColor?: string | undefined;
18185
18433
  secondaryColor?: string | undefined;
18186
18434
  textColor?: string | undefined;
@@ -18555,6 +18803,7 @@ declare const adminCardCreateSchema: z.ZodObject<{
18555
18803
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
18556
18804
  accentColor: string;
18557
18805
  showAvatar: boolean;
18806
+ background?: unknown;
18558
18807
  primaryColor?: string | undefined;
18559
18808
  secondaryColor?: string | undefined;
18560
18809
  textColor?: string | undefined;
@@ -19172,11 +19421,43 @@ declare const profileCreateSchema: z.ZodObject<{
19172
19421
  handle: string;
19173
19422
  label?: undefined;
19174
19423
  } | undefined, unknown>>;
19424
+ background: z.ZodOptional<z.ZodEffects<z.ZodUnknown, {
19425
+ kind: "solid";
19426
+ from?: undefined;
19427
+ to?: undefined;
19428
+ angleDeg?: undefined;
19429
+ } | {
19430
+ kind: "gradient";
19431
+ from: string;
19432
+ to: string;
19433
+ angleDeg?: undefined;
19434
+ } | {
19435
+ kind: "gradient";
19436
+ from: string;
19437
+ to: string;
19438
+ angleDeg: number;
19439
+ } | undefined, unknown>>;
19175
19440
  }, "strip", z.ZodTypeAny, {
19176
19441
  fontKey: string;
19177
19442
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
19178
19443
  accentColor: string;
19179
19444
  showAvatar: boolean;
19445
+ background?: {
19446
+ kind: "solid";
19447
+ from?: undefined;
19448
+ to?: undefined;
19449
+ angleDeg?: undefined;
19450
+ } | {
19451
+ kind: "gradient";
19452
+ from: string;
19453
+ to: string;
19454
+ angleDeg?: undefined;
19455
+ } | {
19456
+ kind: "gradient";
19457
+ from: string;
19458
+ to: string;
19459
+ angleDeg: number;
19460
+ } | undefined;
19180
19461
  primaryColor?: string | undefined;
19181
19462
  secondaryColor?: string | undefined;
19182
19463
  textColor?: string | undefined;
@@ -19230,6 +19511,7 @@ declare const profileCreateSchema: z.ZodObject<{
19230
19511
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
19231
19512
  accentColor: string;
19232
19513
  showAvatar: boolean;
19514
+ background?: unknown;
19233
19515
  primaryColor?: string | undefined;
19234
19516
  secondaryColor?: string | undefined;
19235
19517
  textColor?: string | undefined;
@@ -19296,6 +19578,22 @@ declare const profileCreateSchema: z.ZodObject<{
19296
19578
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
19297
19579
  accentColor: string;
19298
19580
  showAvatar: boolean;
19581
+ background?: {
19582
+ kind: "solid";
19583
+ from?: undefined;
19584
+ to?: undefined;
19585
+ angleDeg?: undefined;
19586
+ } | {
19587
+ kind: "gradient";
19588
+ from: string;
19589
+ to: string;
19590
+ angleDeg?: undefined;
19591
+ } | {
19592
+ kind: "gradient";
19593
+ from: string;
19594
+ to: string;
19595
+ angleDeg: number;
19596
+ } | undefined;
19299
19597
  primaryColor?: string | undefined;
19300
19598
  secondaryColor?: string | undefined;
19301
19599
  textColor?: string | undefined;
@@ -19379,6 +19677,7 @@ declare const profileCreateSchema: z.ZodObject<{
19379
19677
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
19380
19678
  accentColor: string;
19381
19679
  showAvatar: boolean;
19680
+ background?: unknown;
19382
19681
  primaryColor?: string | undefined;
19383
19682
  secondaryColor?: string | undefined;
19384
19683
  textColor?: string | undefined;
@@ -19621,11 +19920,43 @@ declare const signupWithProfileSchema: z.ZodObject<{
19621
19920
  handle: string;
19622
19921
  label?: undefined;
19623
19922
  } | undefined, unknown>>;
19923
+ background: z.ZodOptional<z.ZodEffects<z.ZodUnknown, {
19924
+ kind: "solid";
19925
+ from?: undefined;
19926
+ to?: undefined;
19927
+ angleDeg?: undefined;
19928
+ } | {
19929
+ kind: "gradient";
19930
+ from: string;
19931
+ to: string;
19932
+ angleDeg?: undefined;
19933
+ } | {
19934
+ kind: "gradient";
19935
+ from: string;
19936
+ to: string;
19937
+ angleDeg: number;
19938
+ } | undefined, unknown>>;
19624
19939
  }, "strip", z.ZodTypeAny, {
19625
19940
  fontKey: string;
19626
19941
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
19627
19942
  accentColor: string;
19628
19943
  showAvatar: boolean;
19944
+ background?: {
19945
+ kind: "solid";
19946
+ from?: undefined;
19947
+ to?: undefined;
19948
+ angleDeg?: undefined;
19949
+ } | {
19950
+ kind: "gradient";
19951
+ from: string;
19952
+ to: string;
19953
+ angleDeg?: undefined;
19954
+ } | {
19955
+ kind: "gradient";
19956
+ from: string;
19957
+ to: string;
19958
+ angleDeg: number;
19959
+ } | undefined;
19629
19960
  primaryColor?: string | undefined;
19630
19961
  secondaryColor?: string | undefined;
19631
19962
  textColor?: string | undefined;
@@ -19679,6 +20010,7 @@ declare const signupWithProfileSchema: z.ZodObject<{
19679
20010
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
19680
20011
  accentColor: string;
19681
20012
  showAvatar: boolean;
20013
+ background?: unknown;
19682
20014
  primaryColor?: string | undefined;
19683
20015
  secondaryColor?: string | undefined;
19684
20016
  textColor?: string | undefined;
@@ -19743,6 +20075,22 @@ declare const signupWithProfileSchema: z.ZodObject<{
19743
20075
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
19744
20076
  accentColor: string;
19745
20077
  showAvatar: boolean;
20078
+ background?: {
20079
+ kind: "solid";
20080
+ from?: undefined;
20081
+ to?: undefined;
20082
+ angleDeg?: undefined;
20083
+ } | {
20084
+ kind: "gradient";
20085
+ from: string;
20086
+ to: string;
20087
+ angleDeg?: undefined;
20088
+ } | {
20089
+ kind: "gradient";
20090
+ from: string;
20091
+ to: string;
20092
+ angleDeg: number;
20093
+ } | undefined;
19746
20094
  primaryColor?: string | undefined;
19747
20095
  secondaryColor?: string | undefined;
19748
20096
  textColor?: string | undefined;
@@ -19830,6 +20178,7 @@ declare const signupWithProfileSchema: z.ZodObject<{
19830
20178
  templateId: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso";
19831
20179
  accentColor: string;
19832
20180
  showAvatar: boolean;
20181
+ background?: unknown;
19833
20182
  primaryColor?: string | undefined;
19834
20183
  secondaryColor?: string | undefined;
19835
20184
  textColor?: string | undefined;
@@ -20068,6 +20417,22 @@ declare const profileUpdateSchema: z.ZodObject<{
20068
20417
  handle: string;
20069
20418
  label?: undefined;
20070
20419
  } | undefined, unknown>>>>;
20420
+ background: z.ZodOptional<z.ZodOptional<z.ZodOptional<z.ZodEffects<z.ZodUnknown, {
20421
+ kind: "solid";
20422
+ from?: undefined;
20423
+ to?: undefined;
20424
+ angleDeg?: undefined;
20425
+ } | {
20426
+ kind: "gradient";
20427
+ from: string;
20428
+ to: string;
20429
+ angleDeg?: undefined;
20430
+ } | {
20431
+ kind: "gradient";
20432
+ from: string;
20433
+ to: string;
20434
+ angleDeg: number;
20435
+ } | undefined, unknown>>>>;
20071
20436
  profilePhotoUrl: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
20072
20437
  companyLogoUrl: z.ZodOptional<z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>>;
20073
20438
  }, "strip", z.ZodTypeAny, {
@@ -20075,6 +20440,22 @@ declare const profileUpdateSchema: z.ZodObject<{
20075
20440
  companyLogoUrl?: string | undefined;
20076
20441
  fontKey?: string | undefined;
20077
20442
  templateId?: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso" | undefined;
20443
+ background?: {
20444
+ kind: "solid";
20445
+ from?: undefined;
20446
+ to?: undefined;
20447
+ angleDeg?: undefined;
20448
+ } | {
20449
+ kind: "gradient";
20450
+ from: string;
20451
+ to: string;
20452
+ angleDeg?: undefined;
20453
+ } | {
20454
+ kind: "gradient";
20455
+ from: string;
20456
+ to: string;
20457
+ angleDeg: number;
20458
+ } | undefined;
20078
20459
  accentColor?: string | undefined;
20079
20460
  primaryColor?: string | undefined;
20080
20461
  secondaryColor?: string | undefined;
@@ -20130,6 +20511,7 @@ declare const profileUpdateSchema: z.ZodObject<{
20130
20511
  companyLogoUrl?: string | undefined;
20131
20512
  fontKey?: string | undefined;
20132
20513
  templateId?: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso" | undefined;
20514
+ background?: unknown;
20133
20515
  accentColor?: string | undefined;
20134
20516
  primaryColor?: string | undefined;
20135
20517
  secondaryColor?: string | undefined;
@@ -20199,6 +20581,22 @@ declare const profileUpdateSchema: z.ZodObject<{
20199
20581
  companyLogoUrl?: string | undefined;
20200
20582
  fontKey?: string | undefined;
20201
20583
  templateId?: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso" | undefined;
20584
+ background?: {
20585
+ kind: "solid";
20586
+ from?: undefined;
20587
+ to?: undefined;
20588
+ angleDeg?: undefined;
20589
+ } | {
20590
+ kind: "gradient";
20591
+ from: string;
20592
+ to: string;
20593
+ angleDeg?: undefined;
20594
+ } | {
20595
+ kind: "gradient";
20596
+ from: string;
20597
+ to: string;
20598
+ angleDeg: number;
20599
+ } | undefined;
20202
20600
  accentColor?: string | undefined;
20203
20601
  primaryColor?: string | undefined;
20204
20602
  secondaryColor?: string | undefined;
@@ -20284,6 +20682,7 @@ declare const profileUpdateSchema: z.ZodObject<{
20284
20682
  companyLogoUrl?: string | undefined;
20285
20683
  fontKey?: string | undefined;
20286
20684
  templateId?: "minimal" | "bold" | "card" | "aurora" | "classic" | "spotlight" | "marquee" | "riso" | undefined;
20685
+ background?: unknown;
20287
20686
  accentColor?: string | undefined;
20288
20687
  primaryColor?: string | undefined;
20289
20688
  secondaryColor?: string | undefined;
@@ -20466,4 +20865,4 @@ declare function validatePhysicalConfig(data: unknown): ValidationResult<z.infer
20466
20865
  declare function validateDigitalConfig(data: unknown): ValidationResult<z.infer<typeof digitalConfigSchema>>;
20467
20866
  declare function validateConfiguration(data: unknown): ValidationResult<z.infer<typeof configurationCreateSchema>>;
20468
20867
 
20469
- export { ADVANCE_FALLBACK_EM, CARD_H_MM, CARD_W_MM, CHIP_RADIUS_MM, CHIP_SCALE_FLOOR, type ChipAxis, EDGE_MARGIN_MM, EMPTY_TEXT_FLOOR_EM, ITALIC_OVERHANG_EM, MAX_CTA_BUTTONS, MIN_WALL_MM, NFC_COIL_DIAMETER_MM, NFC_RADIAL_CLEARANCE_MM, type PrintabilityCtx, type Pt, QR_MATRIX_CELLS, QR_MIN_SIZE_MM, QR_MODULE_MIN_MM, TEXT_LINE_FACTOR_EM, TEXT_MIN_EM_MM, type ValidationResult, type Violation, adminCardCreateSchema, bookingSchema, cardAssetSchema, cardDesignMaterialSchema, cardDesignPatternSchema, cardDesignSchema, cardElementSchema, cardElementsSchema, cardProfileUpdateSchema, cardUpdateSchema, chipAnchorToMm, chipPlacementMaxXY, comboIdSchema, configurationCreateSchema, configurationUpdateSchema, contactDetailTogglesSchema, contactExchangeSchema, cornersOnCard, createOrderSchema, ctaButtonSchema, ctaButtonsSchema, digitalConfigSchema, digitalVisibilitySchema, elementPrintability, elementTransformSchema, entrySourceSchema, footprintOf, hubConfigSchema, hubProfilesUpdateSchema, isFiniteTransform, materialSchema, mmToNearestChipAnchor, obbCorners, patternFamilySchema, physicalConfigSchema, physicalContentOverridesSchema, profileAccessTokenCreateSchema, profileCreateSchema, profilePinVerifySchema, profileUpdateSchema, profileUserDataSchema, profileVisibilitySchema, qrModeSchema, sanitizeHttpUrl, sanitizeText, signupWithProfileSchema, socialLinksSchema, socialOverridesSchema, textAdvanceEm, tipJarSchema, userDataSchema, validateConfiguration, validateDigitalConfig, validatePhysicalConfig, validateUserData, webAddressSchema };
20868
+ export { ADVANCE_FALLBACK_EM, CARD_H_MM, CARD_W_MM, CHIP_RADIUS_MM, CHIP_SCALE_FLOOR, type ChipAxis, EDGE_MARGIN_MM, EMPTY_TEXT_FLOOR_EM, ITALIC_OVERHANG_EM, MAX_CTA_BUTTONS, MIN_WALL_MM, NFC_COIL_DIAMETER_MM, NFC_RADIAL_CLEARANCE_MM, type PrintabilityCtx, type Pt, QR_MATRIX_CELLS, QR_MIN_SIZE_MM, QR_MODULE_MIN_MM, TEXT_LINE_FACTOR_EM, TEXT_MIN_EM_MM, type ValidationResult, type Violation, adminCardCreateSchema, bookingSchema, cardAssetSchema, cardDesignMaterialSchema, cardDesignPatternSchema, cardDesignSchema, cardElementSchema, cardElementsSchema, cardProfileUpdateSchema, cardUpdateSchema, chipAnchorToMm, chipPlacementMaxXY, comboIdSchema, configurationCreateSchema, configurationUpdateSchema, contactDetailTogglesSchema, contactExchangeSchema, cornersOnCard, createOrderSchema, ctaButtonSchema, ctaButtonsSchema, digitalConfigSchema, digitalVisibilitySchema, elementPrintability, elementTransformSchema, entrySourceSchema, footprintOf, hubConfigSchema, hubProfilesUpdateSchema, isFiniteTransform, materialSchema, mmToNearestChipAnchor, obbCorners, patternFamilySchema, physicalConfigSchema, physicalContentOverridesSchema, profileAccessTokenCreateSchema, profileBackgroundSchema, profileCreateSchema, profilePinVerifySchema, profileUpdateSchema, profileUserDataSchema, profileVisibilitySchema, qrModeSchema, sanitizeHttpUrl, sanitizeText, signupWithProfileSchema, socialLinksSchema, socialOverridesSchema, textAdvanceEm, tipJarSchema, userDataSchema, validateConfiguration, validateDigitalConfig, validatePhysicalConfig, validateUserData, webAddressSchema };
package/dist/index.js CHANGED
@@ -763,6 +763,18 @@ var bookingSchema = z.unknown().transform((raw) => {
763
763
  const label = typeof rec.label === "string" ? sanitizeText(rec.label, 40) : "";
764
764
  return label ? { provider: rec.provider, handle, label } : { provider: rec.provider, handle };
765
765
  });
766
+ var profileBackgroundSchema = z.unknown().transform((raw) => {
767
+ if (!raw || typeof raw !== "object") return void 0;
768
+ const rec = raw;
769
+ if (rec.kind === "solid") return { kind: "solid" };
770
+ if (rec.kind !== "gradient") return void 0;
771
+ const hex = /^#[0-9A-Fa-f]{6}$/;
772
+ const from = typeof rec.from === "string" && hex.test(rec.from) ? rec.from : null;
773
+ const to = typeof rec.to === "string" && hex.test(rec.to) ? rec.to : null;
774
+ if (!from || !to) return void 0;
775
+ const angleDeg = typeof rec.angleDeg === "number" && Number.isFinite(rec.angleDeg) ? (Math.round(rec.angleDeg) % 360 + 360) % 360 : void 0;
776
+ return angleDeg === void 0 ? { kind: "gradient", from, to } : { kind: "gradient", from, to, angleDeg };
777
+ });
766
778
  var digitalConfigSchema = z.object({
767
779
  templateId: z.enum(["minimal", "bold", "card", "aurora", "classic", "spotlight", "marquee", "riso"]),
768
780
  accentColor: z.string().regex(/^#[0-9A-Fa-f]{6}$/),
@@ -783,7 +795,10 @@ var digitalConfigSchema = z.object({
783
795
  // none). Optional, so existing profiles/configs validate unchanged.
784
796
  ctaButtons: ctaButtonsSchema.optional(),
785
797
  tipJar: tipJarSchema.optional(),
786
- booking: bookingSchema.optional()
798
+ booking: bookingSchema.optional(),
799
+ // NFCARD-641 — absent means solid `secondaryColor`, so every profile that
800
+ // exists today validates unchanged and no backfill is needed.
801
+ background: profileBackgroundSchema.optional()
787
802
  });
788
803
  var entrySourceSchema = z.object({
789
804
  output: z.enum(["with-card", "digital-only"]).optional(),
@@ -1031,6 +1046,7 @@ export {
1031
1046
  physicalConfigSchema,
1032
1047
  physicalContentOverridesSchema,
1033
1048
  profileAccessTokenCreateSchema,
1049
+ profileBackgroundSchema,
1034
1050
  profileCreateSchema,
1035
1051
  profilePinVerifySchema,
1036
1052
  profileUpdateSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nfcard/validation",
3
- "version": "0.22.1",
3
+ "version": "0.23.0",
4
4
  "description": "Shared Zod validation schemas for the NFCard product family.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "zod": "^3.24.0",
20
- "@nfcard/types": "0.16.0"
20
+ "@nfcard/types": "0.17.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "tsup": "^8.0.0"
@@ -270,79 +270,79 @@ describe('per-element override fields survive validation (#1  P0 contract)', ()
270
270
  expect(a).toMatchObject({ rasterBgDataUrl: 'data:,bg', rasterAspect: 1.5 })
271
271
  })
272
272
  })
273
-
274
- // ── NFCARD-561: qr.mode makes "point to profile" data, not a string match ──
275
- //
276
- // The API used to detect it with a QR_PROFILE_TARGETS sentinel set
277
- // ('' / https://nfcard.hu / http://nfcard.hu). Any near-miss — a www. host, a
278
- // trailing path, a stale buyer-typed value — printed verbatim, and a printed
279
- // card cannot be corrected afterwards.
280
-
281
- describe('qr.mode (NFCARD-561)', () => {
282
- it("accepts mode 'redirect' with NO url — the target is resolved at card creation", () => {
283
- const res = cardDesignSchema.safeParse(
284
- withElements([chipEl(), { ...qrEl(), url: undefined, mode: 'redirect' }]),
285
- )
286
- expect(res.success).toBe(true)
287
- })
288
-
289
- it("accepts mode 'redirect' with an empty-string url (the pre-561 free-layout spelling)", () => {
290
- const res = cardDesignSchema.safeParse(
291
- withElements([chipEl(), { ...qrEl(), url: '', mode: 'redirect' }]),
292
- )
293
- expect(res.success).toBe(true)
294
- })
295
-
296
- it("accepts mode 'raw' alongside a buyer-typed url", () => {
297
- const res = cardDesignSchema.safeParse(
298
- withElements([chipEl(), { ...qrEl(), url: 'https://acme.example/team', mode: 'raw' }]),
299
- )
300
- expect(res.success).toBe(true)
301
- })
302
-
303
- it('rejects an unknown mode', () => {
304
- const res = cardDesignSchema.safeParse(
305
- withElements([chipEl(), { ...qrEl(), mode: 'profile' }]),
306
- )
307
- expect(res.success).toBe(false)
308
- })
309
-
310
- it('still rejects a malformed url whatever the mode', () => {
311
- const res = cardDesignSchema.safeParse(
312
- withElements([chipEl(), { ...qrEl(), url: 'not-a-url', mode: 'raw' }]),
313
- )
314
- expect(codes(res)).toContain('invalidUrl')
315
- })
316
-
317
- it('a mode-less QR element still validates (pre-561 designs are untouched)', () => {
318
- expect(cardDesignSchema.safeParse(withElements([chipEl(), qrEl()])).success).toBe(true)
319
- })
320
-
321
- it('carries mode through the legacy qr channel too', () => {
322
- const parsed = cardDesignSchema.parse({
323
- ...legacyDesign,
324
- qr: { enabled: true, mode: 'redirect' },
325
- }) as { qr: Record<string, unknown> }
326
- expect(parsed.qr).toMatchObject({ enabled: true, mode: 'redirect' })
327
- })
328
- })
329
-
330
- describe('printability — a redirect QR needs no url (NFCARD-561)', () => {
331
- const at = (over = {}) =>
332
- ({ ...qrEl(), transform: { xMm: 0, yMm: 0, rotationDeg: 0, scale: 1, z: 2 }, ...over }) as never
333
-
334
- it("does not raise qrEmptyUrl for mode 'redirect' with no url", () => {
335
- const codes = elementPrintability(at({ url: undefined, mode: 'redirect' })).map((v) => v.code)
336
- expect(codes).not.toContain('qrEmptyUrl')
337
- })
338
-
339
- it("still raises qrEmptyUrl for mode 'raw' with no url", () => {
340
- const codes = elementPrintability(at({ url: '', mode: 'raw' })).map((v) => v.code)
341
- expect(codes).toContain('qrEmptyUrl')
342
- })
343
-
344
- it('still raises qrEmptyUrl for a mode-less QR with no url (legacy behaviour)', () => {
345
- const codes = elementPrintability(at({ url: '' })).map((v) => v.code)
346
- expect(codes).toContain('qrEmptyUrl')
347
- })
348
- })
273
+
274
+ // ── NFCARD-561: qr.mode makes "point to profile" data, not a string match ──
275
+ //
276
+ // The API used to detect it with a QR_PROFILE_TARGETS sentinel set
277
+ // ('' / https://nfcard.hu / http://nfcard.hu). Any near-miss — a www. host, a
278
+ // trailing path, a stale buyer-typed value — printed verbatim, and a printed
279
+ // card cannot be corrected afterwards.
280
+
281
+ describe('qr.mode (NFCARD-561)', () => {
282
+ it("accepts mode 'redirect' with NO url — the target is resolved at card creation", () => {
283
+ const res = cardDesignSchema.safeParse(
284
+ withElements([chipEl(), { ...qrEl(), url: undefined, mode: 'redirect' }]),
285
+ )
286
+ expect(res.success).toBe(true)
287
+ })
288
+
289
+ it("accepts mode 'redirect' with an empty-string url (the pre-561 free-layout spelling)", () => {
290
+ const res = cardDesignSchema.safeParse(
291
+ withElements([chipEl(), { ...qrEl(), url: '', mode: 'redirect' }]),
292
+ )
293
+ expect(res.success).toBe(true)
294
+ })
295
+
296
+ it("accepts mode 'raw' alongside a buyer-typed url", () => {
297
+ const res = cardDesignSchema.safeParse(
298
+ withElements([chipEl(), { ...qrEl(), url: 'https://acme.example/team', mode: 'raw' }]),
299
+ )
300
+ expect(res.success).toBe(true)
301
+ })
302
+
303
+ it('rejects an unknown mode', () => {
304
+ const res = cardDesignSchema.safeParse(
305
+ withElements([chipEl(), { ...qrEl(), mode: 'profile' }]),
306
+ )
307
+ expect(res.success).toBe(false)
308
+ })
309
+
310
+ it('still rejects a malformed url whatever the mode', () => {
311
+ const res = cardDesignSchema.safeParse(
312
+ withElements([chipEl(), { ...qrEl(), url: 'not-a-url', mode: 'raw' }]),
313
+ )
314
+ expect(codes(res)).toContain('invalidUrl')
315
+ })
316
+
317
+ it('a mode-less QR element still validates (pre-561 designs are untouched)', () => {
318
+ expect(cardDesignSchema.safeParse(withElements([chipEl(), qrEl()])).success).toBe(true)
319
+ })
320
+
321
+ it('carries mode through the legacy qr channel too', () => {
322
+ const parsed = cardDesignSchema.parse({
323
+ ...legacyDesign,
324
+ qr: { enabled: true, mode: 'redirect' },
325
+ }) as { qr: Record<string, unknown> }
326
+ expect(parsed.qr).toMatchObject({ enabled: true, mode: 'redirect' })
327
+ })
328
+ })
329
+
330
+ describe('printability — a redirect QR needs no url (NFCARD-561)', () => {
331
+ const at = (over = {}) =>
332
+ ({ ...qrEl(), transform: { xMm: 0, yMm: 0, rotationDeg: 0, scale: 1, z: 2 }, ...over }) as never
333
+
334
+ it("does not raise qrEmptyUrl for mode 'redirect' with no url", () => {
335
+ const codes = elementPrintability(at({ url: undefined, mode: 'redirect' })).map((v) => v.code)
336
+ expect(codes).not.toContain('qrEmptyUrl')
337
+ })
338
+
339
+ it("still raises qrEmptyUrl for mode 'raw' with no url", () => {
340
+ const codes = elementPrintability(at({ url: '', mode: 'raw' })).map((v) => v.code)
341
+ expect(codes).toContain('qrEmptyUrl')
342
+ })
343
+
344
+ it('still raises qrEmptyUrl for a mode-less QR with no url (legacy behaviour)', () => {
345
+ const codes = elementPrintability(at({ url: '' })).map((v) => v.code)
346
+ expect(codes).toContain('qrEmptyUrl')
347
+ })
348
+ })
package/src/index.ts CHANGED
Binary file
@@ -0,0 +1,116 @@
1
+ /**
2
+ * NFCARD-641 (564d/S4) Phase 1 — the profile background write gate.
3
+ *
4
+ * Lenient by design, like tipJar/booking/ctaButtons: a malformed background is
5
+ * dropped to `undefined` rather than 400-ing the whole profile save. The tests
6
+ * that matter are therefore the ones proving it drops rather than *mangles* —
7
+ * a half-applied gradient would render as a surprise nobody asked for.
8
+ */
9
+ import { describe, expect, it } from 'vitest'
10
+ import { digitalConfigSchema, profileBackgroundSchema } from './index'
11
+
12
+ const base = {
13
+ templateId: 'classic' as const,
14
+ accentColor: '#F47B20',
15
+ fontKey: 'inter',
16
+ showAvatar: true,
17
+ }
18
+
19
+ describe('profileBackgroundSchema', () => {
20
+ it('accepts a solid background', () => {
21
+ expect(profileBackgroundSchema.parse({ kind: 'solid' })).toEqual({ kind: 'solid' })
22
+ })
23
+
24
+ it('accepts a two-stop gradient and keeps both stops', () => {
25
+ expect(
26
+ profileBackgroundSchema.parse({ kind: 'gradient', from: '#101820', to: '#F47B20' }),
27
+ ).toEqual({ kind: 'gradient', from: '#101820', to: '#F47B20' })
28
+ })
29
+
30
+ it('🔴 drops a HALF-valid gradient rather than inventing the missing stop', () => {
31
+ // Storing `{from}` with no `to` would render as a gradient to… something.
32
+ // Both stops or nothing.
33
+ expect(profileBackgroundSchema.parse({ kind: 'gradient', from: '#101820' })).toBeUndefined()
34
+ expect(profileBackgroundSchema.parse({ kind: 'gradient', to: '#101820' })).toBeUndefined()
35
+ expect(
36
+ profileBackgroundSchema.parse({ kind: 'gradient', from: '#101820', to: 'red' }),
37
+ ).toBeUndefined()
38
+ expect(
39
+ profileBackgroundSchema.parse({ kind: 'gradient', from: 'nothex', to: '#101820' }),
40
+ ).toBeUndefined()
41
+ })
42
+
43
+ it('🔴 rejects any non-hex stop — nothing user-typed reaches the CSS', () => {
44
+ for (const hostile of [
45
+ 'red',
46
+ '#fff',
47
+ '#1018200',
48
+ 'var(--brand)',
49
+ 'url(javascript:alert(1))',
50
+ '#101820; background: url(x)',
51
+ 'rgb(0,0,0)',
52
+ ]) {
53
+ expect(
54
+ profileBackgroundSchema.parse({ kind: 'gradient', from: hostile, to: '#101820' }),
55
+ hostile,
56
+ ).toBeUndefined()
57
+ }
58
+ })
59
+
60
+ it('normalises the angle into [0,360) so the render never defends against it', () => {
61
+ const at = (angleDeg: unknown) =>
62
+ profileBackgroundSchema.parse({ kind: 'gradient', from: '#101820', to: '#F47B20', angleDeg })
63
+ expect(at(160)).toMatchObject({ angleDeg: 160 })
64
+ expect(at(0)).toMatchObject({ angleDeg: 0 })
65
+ expect(at(-40)).toMatchObject({ angleDeg: 320 })
66
+ expect(at(400)).toMatchObject({ angleDeg: 40 })
67
+ expect(at(160.4)).toMatchObject({ angleDeg: 160 })
68
+ // Not a usable number → omit the key entirely and let the render default.
69
+ for (const bad of ['160', NaN, Infinity, null, {}]) {
70
+ expect(at(bad), String(bad)).not.toHaveProperty('angleDeg')
71
+ }
72
+ })
73
+
74
+ it('drops an unknown kind, and anything that is not an object', () => {
75
+ expect(profileBackgroundSchema.parse({ kind: 'image', url: 'https://x/y.png' })).toBeUndefined()
76
+ expect(profileBackgroundSchema.parse({ kind: 'gradient' })).toBeUndefined()
77
+ for (const v of [null, undefined, 'gradient', 42, []]) {
78
+ expect(profileBackgroundSchema.parse(v), String(v)).toBeUndefined()
79
+ }
80
+ })
81
+
82
+ it('does not yet accept an image — that is NFCARD-645', () => {
83
+ // Pinned deliberately: shipping the type without the sanitiser would be an
84
+ // unguarded URL on the render path.
85
+ expect(
86
+ profileBackgroundSchema.parse({ kind: 'image', url: 'https://cdn.example.com/a.jpg' }),
87
+ ).toBeUndefined()
88
+ })
89
+ })
90
+
91
+ describe('digitalConfigSchema × background', () => {
92
+ it('every profile that exists today still validates — background is optional', () => {
93
+ const parsed = digitalConfigSchema.parse(base)
94
+ expect(parsed.background).toBeUndefined()
95
+ })
96
+
97
+ it('carries a valid gradient through', () => {
98
+ const parsed = digitalConfigSchema.parse({
99
+ ...base,
100
+ background: { kind: 'gradient', from: '#101820', to: '#F47B20', angleDeg: 160 },
101
+ })
102
+ expect(parsed.background).toEqual({
103
+ kind: 'gradient',
104
+ from: '#101820',
105
+ to: '#F47B20',
106
+ angleDeg: 160,
107
+ })
108
+ })
109
+
110
+ it('a malformed background does not fail the whole save', () => {
111
+ // The lenient contract: the profile still saves, minus the background.
112
+ const parsed = digitalConfigSchema.parse({ ...base, background: { kind: 'gradient', from: 'x' } })
113
+ expect(parsed.background).toBeUndefined()
114
+ expect(parsed.templateId).toBe('classic')
115
+ })
116
+ })