@nfcard/validation 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
Binary file
package/package.json CHANGED
@@ -1,39 +1,45 @@
1
- {
2
- "name": "@nfcard/validation",
3
- "version": "0.16.0",
4
- "description": "Shared Zod validation schemas for the NFCard product family.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "import": "./dist/index.js",
11
- "types": "./dist/index.d.ts"
12
- }
13
- },
14
- "files": [
15
- "dist",
16
- "src"
17
- ],
18
- "dependencies": {
19
- "zod": "^3.24.0",
20
- "@nfcard/types": "0.13.0"
21
- },
22
- "devDependencies": {
23
- "tsup": "^8.0.0"
24
- },
25
- "publishConfig": {
26
- "access": "public",
27
- "registry": "https://registry.npmjs.org/"
28
- },
29
- "repository": {
30
- "type": "git",
31
- "url": "https://bitbucket.org/fox-hole/nfcard-shared.git",
32
- "directory": "validation"
33
- },
34
- "scripts": {
35
- "test": "vitest run",
36
- "test:watch": "vitest",
37
- "build": "tsup src/index.ts --format esm --dts --clean"
38
- }
39
- }
1
+ {
2
+ "name": "@nfcard/validation",
3
+ "version": "0.17.0",
4
+ "description": "Shared Zod validation schemas for the NFCard product family.",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "types": "./src/index.ts",
8
+ "exports": {
9
+ ".": "./src/index.ts"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "src"
14
+ ],
15
+ "scripts": {
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
18
+ "build": "tsup src/index.ts --format esm --dts --clean",
19
+ "prepublishOnly": "pnpm build"
20
+ },
21
+ "dependencies": {
22
+ "zod": "^3.24.0",
23
+ "@nfcard/types": "workspace:*"
24
+ },
25
+ "devDependencies": {
26
+ "tsup": "^8.0.0"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public",
30
+ "registry": "https://registry.npmjs.org/",
31
+ "main": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "exports": {
34
+ ".": {
35
+ "import": "./dist/index.js",
36
+ "types": "./dist/index.d.ts"
37
+ }
38
+ }
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://bitbucket.org/fox-hole/nfcard-shared.git",
43
+ "directory": "validation"
44
+ }
45
+ }
@@ -81,6 +81,22 @@ describe('cardDesignSchema — free-transform element layer (NFCARD-197/198)', (
81
81
  expect(res.success).toBe(true)
82
82
  })
83
83
 
84
+ it('NFCARD-480: accepts a HIDDEN chip (shape -1) — the order gate must not 400 it', () => {
85
+ const res = cardDesignSchema.safeParse(withElements([chipEl({ shape: -1 }), textEl(), qrEl()]))
86
+ expect(res.success).toBe(true)
87
+ })
88
+
89
+ it('NFCARD-480: a CELL never takes the hidden sentinel (cellShape -1 still rejected)', () => {
90
+ const validPattern = {
91
+ family: 'spiral', variant: 'pulse', density: 0.3, holeMinMm: 0.5, holeMaxMm: 1.8,
92
+ intensity: 0.5, cellShape: 0, cellRotationDeg: 0, chipShape: 0, chipRotationDeg: 0,
93
+ }
94
+ expect(cardDesignSchema.safeParse(withElements([chipEl()], { pattern: validPattern })).success).toBe(true)
95
+ const res = cardDesignSchema.safeParse(withElements([chipEl()], { pattern: { ...validPattern, cellShape: -1 } }))
96
+ expect(res.success).toBe(false)
97
+ expect(codes(res)).toContain('cellSidesRange')
98
+ })
99
+
84
100
  it('rejects a QR below the scannable size floor', () => {
85
101
  const res = cardDesignSchema.safeParse(withElements([chipEl(), qrEl({ sizeMm: 12 })]))
86
102
  expect(res.success).toBe(false)
package/src/index.test.ts CHANGED
@@ -974,104 +974,104 @@ describe('physicalConfig.cardDesign (nested, not stripped)', () => {
974
974
  expect(physicalConfigSchema.safeParse(validPhysicalConfig).success).toBe(true);
975
975
  });
976
976
  });
977
-
978
- // -- entrySourceSchema (NFCARD-392) --------------------------------
979
-
980
- describe('entrySourceSchema on configurationCreateSchema', () => {
981
- const base = {
982
- sessionId: '550e8400-e29b-41d4-a716-446655440000',
983
- userData: validUserData,
984
- physicalConfig: validPhysicalConfig,
985
- digitalConfig: validDigitalConfig,
986
- };
987
-
988
- it('accepts a full entry-attribution blob', () => {
989
- const r = configurationCreateSchema.safeParse({
990
- ...base,
991
- entrySource: {
992
- output: 'with-card',
993
- entryFlow: 'order',
994
- ref: 'PROMO24',
995
- path: '/configurator',
996
- referrer: 'google.com',
997
- utm: { utm_source: 'ads', utm_campaign: 'launch' },
998
- },
999
- });
1000
- expect(r.success).toBe(true);
1001
- if (r.success) expect(r.data.entrySource?.output).toBe('with-card');
1002
- });
1003
-
1004
- it('is optional — older clients omit it entirely', () => {
1005
- const r = configurationCreateSchema.safeParse(base);
1006
- expect(r.success).toBe(true);
1007
- if (r.success) expect(r.data.entrySource).toBeUndefined();
1008
- });
1009
-
1010
- it('rejects unknown keys (strict) and out-of-enum values', () => {
1011
- expect(
1012
- configurationCreateSchema.safeParse({
1013
- ...base,
1014
- entrySource: { output: 'with-card', evil: 'payload' },
1015
- }).success,
1016
- ).toBe(false);
1017
- expect(
1018
- configurationCreateSchema.safeParse({
1019
- ...base,
1020
- entrySource: { output: 'both-please' },
1021
- }).success,
1022
- ).toBe(false);
1023
- });
1024
-
1025
- it('bounds the utm map (max 10 entries, capped value length)', () => {
1026
- const utm: Record<string, string> = {};
1027
- for (let i = 0; i < 11; i++) utm['k' + i] = 'v';
1028
- expect(
1029
- configurationCreateSchema.safeParse({ ...base, entrySource: { utm } }).success,
1030
- ).toBe(false);
1031
- expect(
1032
- configurationCreateSchema.safeParse({
1033
- ...base,
1034
- entrySource: { referrer: 'x'.repeat(300) },
1035
- }).success,
1036
- ).toBe(false);
1037
- });
1038
-
1039
- it('is create-only: the update schema strips/ignores it', () => {
1040
- const r = configurationUpdateSchema.safeParse({
1041
- entrySource: { output: 'with-card' },
1042
- });
1043
- // z.object strips unknown keys by default - the field must not survive.
1044
- expect(r.success).toBe(true);
1045
- if (r.success) expect((r.data as Record<string, unknown>).entrySource).toBeUndefined();
1046
- });
1047
- });
1048
-
1049
- describe('entrySourceSchema material/template preselects (NFCARD-393)', () => {
1050
- const base = {
1051
- sessionId: '550e8400-e29b-41d4-a716-446655440000',
1052
- userData: validUserData,
1053
- physicalConfig: validPhysicalConfig,
1054
- digitalConfig: validDigitalConfig,
1055
- };
1056
-
1057
- it('accepts the deep-link preselects', () => {
1058
- const r = configurationCreateSchema.safeParse({
1059
- ...base,
1060
- entrySource: { output: 'with-card', material: '3dprint_standard', template: 'tpl-pvc-white' },
1061
- });
1062
- expect(r.success).toBe(true);
1063
- if (r.success) {
1064
- expect(r.data.entrySource?.material).toBe('3dprint_standard');
1065
- expect(r.data.entrySource?.template).toBe('tpl-pvc-white');
1066
- }
1067
- });
1068
-
1069
- it('rejects a non-enum material', () => {
1070
- expect(
1071
- configurationCreateSchema.safeParse({
1072
- ...base,
1073
- entrySource: { material: 'carbon-fiber' },
1074
- }).success,
1075
- ).toBe(false);
1076
- });
1077
- });
977
+
978
+ // -- entrySourceSchema (NFCARD-392) --------------------------------
979
+
980
+ describe('entrySourceSchema on configurationCreateSchema', () => {
981
+ const base = {
982
+ sessionId: '550e8400-e29b-41d4-a716-446655440000',
983
+ userData: validUserData,
984
+ physicalConfig: validPhysicalConfig,
985
+ digitalConfig: validDigitalConfig,
986
+ };
987
+
988
+ it('accepts a full entry-attribution blob', () => {
989
+ const r = configurationCreateSchema.safeParse({
990
+ ...base,
991
+ entrySource: {
992
+ output: 'with-card',
993
+ entryFlow: 'order',
994
+ ref: 'PROMO24',
995
+ path: '/configurator',
996
+ referrer: 'google.com',
997
+ utm: { utm_source: 'ads', utm_campaign: 'launch' },
998
+ },
999
+ });
1000
+ expect(r.success).toBe(true);
1001
+ if (r.success) expect(r.data.entrySource?.output).toBe('with-card');
1002
+ });
1003
+
1004
+ it('is optional — older clients omit it entirely', () => {
1005
+ const r = configurationCreateSchema.safeParse(base);
1006
+ expect(r.success).toBe(true);
1007
+ if (r.success) expect(r.data.entrySource).toBeUndefined();
1008
+ });
1009
+
1010
+ it('rejects unknown keys (strict) and out-of-enum values', () => {
1011
+ expect(
1012
+ configurationCreateSchema.safeParse({
1013
+ ...base,
1014
+ entrySource: { output: 'with-card', evil: 'payload' },
1015
+ }).success,
1016
+ ).toBe(false);
1017
+ expect(
1018
+ configurationCreateSchema.safeParse({
1019
+ ...base,
1020
+ entrySource: { output: 'both-please' },
1021
+ }).success,
1022
+ ).toBe(false);
1023
+ });
1024
+
1025
+ it('bounds the utm map (max 10 entries, capped value length)', () => {
1026
+ const utm: Record<string, string> = {};
1027
+ for (let i = 0; i < 11; i++) utm['k' + i] = 'v';
1028
+ expect(
1029
+ configurationCreateSchema.safeParse({ ...base, entrySource: { utm } }).success,
1030
+ ).toBe(false);
1031
+ expect(
1032
+ configurationCreateSchema.safeParse({
1033
+ ...base,
1034
+ entrySource: { referrer: 'x'.repeat(300) },
1035
+ }).success,
1036
+ ).toBe(false);
1037
+ });
1038
+
1039
+ it('is create-only: the update schema strips/ignores it', () => {
1040
+ const r = configurationUpdateSchema.safeParse({
1041
+ entrySource: { output: 'with-card' },
1042
+ });
1043
+ // z.object strips unknown keys by default - the field must not survive.
1044
+ expect(r.success).toBe(true);
1045
+ if (r.success) expect((r.data as Record<string, unknown>).entrySource).toBeUndefined();
1046
+ });
1047
+ });
1048
+
1049
+ describe('entrySourceSchema material/template preselects (NFCARD-393)', () => {
1050
+ const base = {
1051
+ sessionId: '550e8400-e29b-41d4-a716-446655440000',
1052
+ userData: validUserData,
1053
+ physicalConfig: validPhysicalConfig,
1054
+ digitalConfig: validDigitalConfig,
1055
+ };
1056
+
1057
+ it('accepts the deep-link preselects', () => {
1058
+ const r = configurationCreateSchema.safeParse({
1059
+ ...base,
1060
+ entrySource: { output: 'with-card', material: '3dprint_standard', template: 'tpl-pvc-white' },
1061
+ });
1062
+ expect(r.success).toBe(true);
1063
+ if (r.success) {
1064
+ expect(r.data.entrySource?.material).toBe('3dprint_standard');
1065
+ expect(r.data.entrySource?.template).toBe('tpl-pvc-white');
1066
+ }
1067
+ });
1068
+
1069
+ it('rejects a non-enum material', () => {
1070
+ expect(
1071
+ configurationCreateSchema.safeParse({
1072
+ ...base,
1073
+ entrySource: { material: 'carbon-fiber' },
1074
+ }).success,
1075
+ ).toBe(false);
1076
+ });
1077
+ });
package/src/index.ts CHANGED
Binary file