@planning-inspectorate/data-model 2.24.0 → 2.25.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.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/pins_data_model/models/model_appeal_document.py +1 -1
  3. package/pins_data_model/models/model_appeal_event.py +1 -1
  4. package/pins_data_model/models/model_appeal_event_estimate.py +1 -1
  5. package/pins_data_model/models/model_appeal_has.py +1 -1
  6. package/pins_data_model/models/model_appeal_representation.py +1 -1
  7. package/pins_data_model/models/model_appeal_s78.py +1 -1
  8. package/pins_data_model/models/model_dart_response.py +1 -1
  9. package/pins_data_model/models/model_entraid.py +1 -1
  10. package/pins_data_model/models/model_folder.py +1 -1
  11. package/pins_data_model/models/model_gettimesheets_response.py +1 -1
  12. package/pins_data_model/models/model_listed_building.py +1 -1
  13. package/pins_data_model/models/model_listed_buildings.py +1 -1
  14. package/pins_data_model/models/model_nsip_document.py +1 -1
  15. package/pins_data_model/models/model_nsip_exam_timetable.py +1 -1
  16. package/pins_data_model/models/model_nsip_invoice.py +1 -1
  17. package/pins_data_model/models/model_nsip_meeting.py +1 -1
  18. package/pins_data_model/models/model_nsip_project.py +1 -1
  19. package/pins_data_model/models/model_nsip_project_update.py +1 -1
  20. package/pins_data_model/models/model_nsip_representation.py +1 -1
  21. package/pins_data_model/models/model_nsip_subscription.py +1 -1
  22. package/pins_data_model/models/model_pins_inspector.py +1 -1
  23. package/pins_data_model/models/model_pins_lpa.py +1 -1
  24. package/pins_data_model/models/model_s51_advice.py +1 -1
  25. package/pins_data_model/models/model_service_user.py +1 -1
  26. package/schemas/commands/appeals-components/lpa-questionnaire-enforcement-common.schema.json +60 -0
  27. package/schemas/commands/appeals-components/lpa-questionnaire-enforcement.schema.json +2 -50
  28. package/schemas/commands/lpa-questionnaire.schema.json +18 -2
  29. package/src/enums.cjs +41 -0
  30. package/src/enums.d.ts +37 -0
  31. package/src/enums.js +37 -0
  32. package/src/index.cjs +41 -0
  33. package/src/lib/gen-enum-impl.js +196 -0
  34. package/src/lib/gen-enum-impl.test.js +183 -0
  35. package/src/lib/gen-enums.js +11 -87
  36. package/src/schemas.d.ts +21 -4
  37. package/tests/appeals/commands/enforcement-listed-questionnaire.test.js +2 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/data-model",
3
- "version": "2.24.0",
3
+ "version": "2.25.0",
4
4
  "description": "JSON Schemas for the Planning Inspectorate's Data Model",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-document.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-event.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-event-estimate.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-has.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-representation.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: appeal-s78.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: dart-response.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: entraid.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: folder.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: gettimesheets-response.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: listed-building.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: listed-buildings.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-document.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-exam-timetable.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-invoice.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-meeting.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-project.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-project-update.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:40+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-representation.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: nsip-subscription.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: pins-inspector.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:40+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: pins-lpa.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:40+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: s51-advice.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: service-user.schema.json
3
- # timestamp: 2026-02-17T11:07:50+00:00
3
+ # timestamp: 2026-02-19T12:13:41+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -0,0 +1,60 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "commands/appeals-components/lpa-questionnaire-enforcement-common.schema.json",
4
+ "title": "LPAQ Enforcement submission properties",
5
+ "description": "Schema defining any common enforcement specific properties for LPAQ submissions",
6
+ "type": "object",
7
+ "additionalProperties": true,
8
+ "required": [
9
+ "noticeRelatesToBuildingEngineeringMiningOther",
10
+ "siteAreaSquareMetres",
11
+ "changeOfUseRefuseOrWaste",
12
+ "changeOfUseMineralExtraction",
13
+ "changeOfUseMineralStorage",
14
+ "relatesToErectionOfBuildingOrBuildings",
15
+ "relatesToBuildingWithAgriculturalPurpose",
16
+ "relatesToBuildingSingleDwellingHouse"
17
+ ],
18
+ "allOf": [
19
+ {
20
+ "properties": {
21
+ "noticeRelatesToBuildingEngineeringMiningOther": {
22
+ "type": ["boolean", "null"]
23
+ },
24
+ "siteAreaSquareMetres": {
25
+ "type": ["number", "null"],
26
+ "examples": [45.7],
27
+ "description": "The site area, in square meters"
28
+ },
29
+ "areaOfAllegedBreachInSquareMetres": {
30
+ "type": ["number", "null"],
31
+ "examples": [45.7],
32
+ "description": "Area of the alleged breach, in square meters"
33
+ },
34
+ "floorSpaceCreatedByBreachInSquareMetres": {
35
+ "type": ["number", "null"],
36
+ "examples": [45.7],
37
+ "description": "Floor space created by the breach, in square meters"
38
+ },
39
+ "changeOfUseRefuseOrWaste": {
40
+ "type": ["boolean", "null"]
41
+ },
42
+ "changeOfUseMineralExtraction": {
43
+ "type": ["boolean", "null"]
44
+ },
45
+ "changeOfUseMineralStorage": {
46
+ "type": ["boolean", "null"]
47
+ },
48
+ "relatesToErectionOfBuildingOrBuildings": {
49
+ "type": ["boolean", "null"]
50
+ },
51
+ "relatesToBuildingWithAgriculturalPurpose": {
52
+ "type": ["boolean", "null"]
53
+ },
54
+ "relatesToBuildingSingleDwellingHouse": {
55
+ "type": ["boolean", "null"]
56
+ }
57
+ }
58
+ }
59
+ ]
60
+ }
@@ -2,61 +2,13 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "commands/appeals-components/lpa-questionnaire-enforcement.schema.json",
4
4
  "title": "LPAQ Enforcement submission properties",
5
- "description": "Schema defining any enfrocement specific properties for LPAQ submissions",
5
+ "description": "Schema defining any enforcement specific properties for LPAQ submissions",
6
6
  "type": "object",
7
7
  "additionalProperties": true,
8
- "required": [
9
- "noticeRelatesToBuildingEngineeringMiningOther",
10
- "siteAreaSquareMetres",
11
- "changeOfUseRefuseOrWaste",
12
- "changeOfUseMineralExtraction",
13
- "changeOfUseMineralStorage",
14
- "relatesToErectionOfBuildingOrBuildings",
15
- "relatesToBuildingWithAgriculturalPurpose",
16
- "relatesToBuildingSingleDwellingHouse",
17
- "affectedTrunkRoadName",
18
- "isSiteOnCrownLand",
19
- "article4AffectedDevelopmentRights"
20
- ],
8
+ "required": ["affectedTrunkRoadName", "isSiteOnCrownLand", "article4AffectedDevelopmentRights"],
21
9
  "allOf": [
22
10
  {
23
11
  "properties": {
24
- "noticeRelatesToBuildingEngineeringMiningOther": {
25
- "type": ["boolean", "null"]
26
- },
27
- "siteAreaSquareMetres": {
28
- "type": ["number", "null"],
29
- "examples": [45.7],
30
- "description": "The site area, in square meters"
31
- },
32
- "areaOfAllegedBreachInSquareMetres": {
33
- "type": ["number", "null"],
34
- "examples": [45.7],
35
- "description": "Area of the alleged breach, in square meters"
36
- },
37
- "floorSpaceCreatedByBreachInSquareMetres": {
38
- "type": ["number", "null"],
39
- "examples": [45.7],
40
- "description": "Floor space created by the breach, in square meters"
41
- },
42
- "changeOfUseRefuseOrWaste": {
43
- "type": ["boolean", "null"]
44
- },
45
- "changeOfUseMineralExtraction": {
46
- "type": ["boolean", "null"]
47
- },
48
- "changeOfUseMineralStorage": {
49
- "type": ["boolean", "null"]
50
- },
51
- "relatesToErectionOfBuildingOrBuildings": {
52
- "type": ["boolean", "null"]
53
- },
54
- "relatesToBuildingWithAgriculturalPurpose": {
55
- "type": ["boolean", "null"]
56
- },
57
- "relatesToBuildingSingleDwellingHouse": {
58
- "type": ["boolean", "null"]
59
- },
60
12
  "affectedTrunkRoadName": {
61
13
  "type": ["string", "null"],
62
14
  "examples": ["Buckingham palace road"],
@@ -71,9 +71,9 @@
71
71
  ]
72
72
  },
73
73
  {
74
- "$comment": "Enforcement + Enforcement listed building",
74
+ "$comment": "Enforcement",
75
75
  "allOf": [
76
- { "properties": { "caseType": { "enum": ["C", "F"] } } },
76
+ { "properties": { "caseType": { "enum": ["C"] } } },
77
77
  { "$ref": "appeals-components/lpa-questionnaire-common.schema.json" },
78
78
  { "$ref": "appeals-components/lpa-questionnaire-has.schema.json" },
79
79
  { "$ref": "appeals-components/lpa-procedure-preference.schema.json" },
@@ -81,6 +81,7 @@
81
81
  { "$ref": "appeals-components/infrastructure-levy.schema.json" },
82
82
  { "$ref": "appeals-components/environmental-impact-assessment.schema.json" },
83
83
  { "$ref": "appeals-components/site-designations-protection.schema.json" },
84
+ { "$ref": "appeals-components/lpa-questionnaire-enforcement-common.schema.json" },
84
85
  { "$ref": "appeals-components/lpa-questionnaire-enforcement.schema.json" }
85
86
  ]
86
87
  },
@@ -93,6 +94,21 @@
93
94
  { "$ref": "appeals-components/infrastructure-levy.schema.json" },
94
95
  { "$ref": "appeals-components/lpa-questionnaire-ldc.schema.json" }
95
96
  ]
97
+ },
98
+ {
99
+ "$comment": "Enforcement listed building",
100
+ "allOf": [
101
+ { "properties": { "caseType": { "enum": ["F"] } } },
102
+ { "$ref": "appeals-components/lpa-questionnaire-common.schema.json" },
103
+ { "$ref": "appeals-components/lpa-questionnaire-has.schema.json" },
104
+ { "$ref": "appeals-components/lpa-procedure-preference.schema.json" },
105
+ { "$ref": "appeals-components/changed-listed-building-number.schema.json" },
106
+ { "$ref": "appeals-components/infrastructure-levy.schema.json" },
107
+ { "$ref": "appeals-components/environmental-impact-assessment.schema.json" },
108
+ { "$ref": "appeals-components/site-designations-protection.schema.json" },
109
+ { "$ref": "appeals-components/lpa-questionnaire-enforcement-common.schema.json" },
110
+ { "$ref": "appeals-components/lpa-questionnaire-listed-building.schema.json" }
111
+ ]
96
112
  }
97
113
  ]
98
114
  },
package/src/enums.cjs CHANGED
@@ -1,5 +1,34 @@
1
1
  'use strict';
2
2
 
3
+ const APPEAL_HAS_CASE_DECISION_OUTCOME = Object.freeze({
4
+ ALLOWED: 'allowed',
5
+ DISMISSED: 'dismissed',
6
+ INVALID: 'invalid',
7
+ SPLIT_DECISION: 'split_decision',
8
+ });
9
+
10
+ const APPEAL_HAS_CASE_PROCEDURE = Object.freeze({
11
+ HEARING: 'hearing',
12
+ INQUIRY: 'inquiry',
13
+ WRITTEN: 'written',
14
+ });
15
+
16
+ const APPEAL_HAS_CASE_STATUS = Object.freeze({
17
+ ASSIGN_CASE_OFFICER: 'assign_case_officer',
18
+ AWAITING_EVENT: 'awaiting_event',
19
+ AWAITING_TRANSFER: 'awaiting_transfer',
20
+ CLOSED: 'closed',
21
+ COMPLETE: 'complete',
22
+ EVENT: 'event',
23
+ INVALID: 'invalid',
24
+ ISSUE_DETERMINATION: 'issue_determination',
25
+ LPA_QUESTIONNAIRE: 'lpa_questionnaire',
26
+ READY_TO_START: 'ready_to_start',
27
+ TRANSFERRED: 'transferred',
28
+ VALIDATION: 'validation',
29
+ WITHDRAWN: 'withdrawn',
30
+ });
31
+
3
32
  const APPEAL_ALLOCATION_LEVEL = Object.freeze({
4
33
  A: 'A',
5
34
  B: 'B',
@@ -410,6 +439,11 @@ const METHOD = Object.freeze({
410
439
  POST: 'post',
411
440
  });
412
441
 
442
+ const NSIP_PROJECT_SOURCE_SYSTEM = Object.freeze({
443
+ BACK_OFFICE_APPLICATIONS: 'back-office-applications',
444
+ HORIZON: 'horizon',
445
+ });
446
+
413
447
  const NSIP_CA_AND_TP_EVIDENCE = Object.freeze({
414
448
  AWAITING_SUBMISSION: 'awaiting_submission',
415
449
  NOT_APPLICABLE: 'not_applicable',
@@ -655,8 +689,11 @@ const NSIP_SECURITY_CLASSIFICATION = Object.freeze({
655
689
  });
656
690
 
657
691
  const NSIP_SOURCE_SYSTEM = Object.freeze({
692
+ BACK_OFFICE_APPEALS: 'back-office-appeals',
658
693
  BACK_OFFICE_APPLICATIONS: 'back-office-applications',
659
694
  HORIZON: 'horizon',
695
+ NI_FILE: 'ni_file',
696
+ SHAREPOINT: 'sharepoint',
660
697
  });
661
698
 
662
699
  const NSIP_STAGE = Object.freeze({
@@ -762,6 +799,9 @@ exports.APPEAL_EIA_DEVELOPMENT_DESCRIPTION = APPEAL_EIA_DEVELOPMENT_DESCRIPTION;
762
799
  exports.APPEAL_EIA_ENVIRONMENTAL_IMPACT_SCHEDULE = APPEAL_EIA_ENVIRONMENTAL_IMPACT_SCHEDULE;
763
800
  exports.APPEAL_EVENT_STATUS = APPEAL_EVENT_STATUS;
764
801
  exports.APPEAL_EVENT_TYPE = APPEAL_EVENT_TYPE;
802
+ exports.APPEAL_HAS_CASE_DECISION_OUTCOME = APPEAL_HAS_CASE_DECISION_OUTCOME;
803
+ exports.APPEAL_HAS_CASE_PROCEDURE = APPEAL_HAS_CASE_PROCEDURE;
804
+ exports.APPEAL_HAS_CASE_STATUS = APPEAL_HAS_CASE_STATUS;
765
805
  exports.APPEAL_INVALID_OR_INCOMPLETE_DETAILS = APPEAL_INVALID_OR_INCOMPLETE_DETAILS;
766
806
  exports.APPEAL_KNOWS_ALL_OWNERS = APPEAL_KNOWS_ALL_OWNERS;
767
807
  exports.APPEAL_KNOWS_OTHER_OWNERS = APPEAL_KNOWS_OTHER_OWNERS;
@@ -797,6 +837,7 @@ exports.NSIP_ORIGIN = NSIP_ORIGIN;
797
837
  exports.NSIP_PLANNING_INSPECTORATE_ROLE = NSIP_PLANNING_INSPECTORATE_ROLE;
798
838
  exports.NSIP_POLICY_COMPLIANCE_DOCUMENT = NSIP_POLICY_COMPLIANCE_DOCUMENT;
799
839
  exports.NSIP_PRINCIPAL_AREA_DISAGREEMENT_SUMMARY_STMT = NSIP_PRINCIPAL_AREA_DISAGREEMENT_SUMMARY_STMT;
840
+ exports.NSIP_PROJECT_SOURCE_SYSTEM = NSIP_PROJECT_SOURCE_SYSTEM;
800
841
  exports.NSIP_PROJECT_TYPE = NSIP_PROJECT_TYPE;
801
842
  exports.NSIP_PUBLIC_SECTOR_EQUALITY_DUTY = NSIP_PUBLIC_SECTOR_EQUALITY_DUTY;
802
843
  exports.NSIP_PUBLISHED_STATUS = NSIP_PUBLISHED_STATUS;
package/src/enums.d.ts CHANGED
@@ -1,3 +1,32 @@
1
+ export const APPEAL_HAS_CASE_DECISION_OUTCOME = {
2
+ ALLOWED: 'allowed',
3
+ DISMISSED: 'dismissed',
4
+ INVALID: 'invalid',
5
+ SPLIT_DECISION: 'split_decision',
6
+ } as const;
7
+
8
+ export const APPEAL_HAS_CASE_PROCEDURE = {
9
+ HEARING: 'hearing',
10
+ INQUIRY: 'inquiry',
11
+ WRITTEN: 'written',
12
+ } as const;
13
+
14
+ export const APPEAL_HAS_CASE_STATUS = {
15
+ ASSIGN_CASE_OFFICER: 'assign_case_officer',
16
+ AWAITING_EVENT: 'awaiting_event',
17
+ AWAITING_TRANSFER: 'awaiting_transfer',
18
+ CLOSED: 'closed',
19
+ COMPLETE: 'complete',
20
+ EVENT: 'event',
21
+ INVALID: 'invalid',
22
+ ISSUE_DETERMINATION: 'issue_determination',
23
+ LPA_QUESTIONNAIRE: 'lpa_questionnaire',
24
+ READY_TO_START: 'ready_to_start',
25
+ TRANSFERRED: 'transferred',
26
+ VALIDATION: 'validation',
27
+ WITHDRAWN: 'withdrawn',
28
+ } as const;
29
+
1
30
  export const APPEAL_ALLOCATION_LEVEL = {
2
31
  A: 'A',
3
32
  B: 'B',
@@ -408,6 +437,11 @@ export const METHOD = {
408
437
  POST: 'post',
409
438
  } as const;
410
439
 
440
+ export const NSIP_PROJECT_SOURCE_SYSTEM = {
441
+ BACK_OFFICE_APPLICATIONS: 'back-office-applications',
442
+ HORIZON: 'horizon',
443
+ } as const;
444
+
411
445
  export const NSIP_CA_AND_TP_EVIDENCE = {
412
446
  AWAITING_SUBMISSION: 'awaiting_submission',
413
447
  NOT_APPLICABLE: 'not_applicable',
@@ -653,8 +687,11 @@ export const NSIP_SECURITY_CLASSIFICATION = {
653
687
  } as const;
654
688
 
655
689
  export const NSIP_SOURCE_SYSTEM = {
690
+ BACK_OFFICE_APPEALS: 'back-office-appeals',
656
691
  BACK_OFFICE_APPLICATIONS: 'back-office-applications',
657
692
  HORIZON: 'horizon',
693
+ NI_FILE: 'ni_file',
694
+ SHAREPOINT: 'sharepoint',
658
695
  } as const;
659
696
 
660
697
  export const NSIP_STAGE = {
package/src/enums.js CHANGED
@@ -1,3 +1,32 @@
1
+ export const APPEAL_HAS_CASE_DECISION_OUTCOME = Object.freeze({
2
+ ALLOWED: 'allowed',
3
+ DISMISSED: 'dismissed',
4
+ INVALID: 'invalid',
5
+ SPLIT_DECISION: 'split_decision',
6
+ });
7
+
8
+ export const APPEAL_HAS_CASE_PROCEDURE = Object.freeze({
9
+ HEARING: 'hearing',
10
+ INQUIRY: 'inquiry',
11
+ WRITTEN: 'written',
12
+ });
13
+
14
+ export const APPEAL_HAS_CASE_STATUS = Object.freeze({
15
+ ASSIGN_CASE_OFFICER: 'assign_case_officer',
16
+ AWAITING_EVENT: 'awaiting_event',
17
+ AWAITING_TRANSFER: 'awaiting_transfer',
18
+ CLOSED: 'closed',
19
+ COMPLETE: 'complete',
20
+ EVENT: 'event',
21
+ INVALID: 'invalid',
22
+ ISSUE_DETERMINATION: 'issue_determination',
23
+ LPA_QUESTIONNAIRE: 'lpa_questionnaire',
24
+ READY_TO_START: 'ready_to_start',
25
+ TRANSFERRED: 'transferred',
26
+ VALIDATION: 'validation',
27
+ WITHDRAWN: 'withdrawn',
28
+ });
29
+
1
30
  export const APPEAL_ALLOCATION_LEVEL = Object.freeze({
2
31
  A: 'A',
3
32
  B: 'B',
@@ -408,6 +437,11 @@ export const METHOD = Object.freeze({
408
437
  POST: 'post',
409
438
  });
410
439
 
440
+ export const NSIP_PROJECT_SOURCE_SYSTEM = Object.freeze({
441
+ BACK_OFFICE_APPLICATIONS: 'back-office-applications',
442
+ HORIZON: 'horizon',
443
+ });
444
+
411
445
  export const NSIP_CA_AND_TP_EVIDENCE = Object.freeze({
412
446
  AWAITING_SUBMISSION: 'awaiting_submission',
413
447
  NOT_APPLICABLE: 'not_applicable',
@@ -653,8 +687,11 @@ export const NSIP_SECURITY_CLASSIFICATION = Object.freeze({
653
687
  });
654
688
 
655
689
  export const NSIP_SOURCE_SYSTEM = Object.freeze({
690
+ BACK_OFFICE_APPEALS: 'back-office-appeals',
656
691
  BACK_OFFICE_APPLICATIONS: 'back-office-applications',
657
692
  HORIZON: 'horizon',
693
+ NI_FILE: 'ni_file',
694
+ SHAREPOINT: 'sharepoint',
658
695
  });
659
696
 
660
697
  export const NSIP_STAGE = Object.freeze({
package/src/index.cjs CHANGED
@@ -81,6 +81,35 @@ function loadSchemaSync(filePath) {
81
81
  return jsoncParser.parse(content.toString());
82
82
  }
83
83
 
84
+ const APPEAL_HAS_CASE_DECISION_OUTCOME = Object.freeze({
85
+ ALLOWED: 'allowed',
86
+ DISMISSED: 'dismissed',
87
+ INVALID: 'invalid',
88
+ SPLIT_DECISION: 'split_decision',
89
+ });
90
+
91
+ const APPEAL_HAS_CASE_PROCEDURE = Object.freeze({
92
+ HEARING: 'hearing',
93
+ INQUIRY: 'inquiry',
94
+ WRITTEN: 'written',
95
+ });
96
+
97
+ const APPEAL_HAS_CASE_STATUS = Object.freeze({
98
+ ASSIGN_CASE_OFFICER: 'assign_case_officer',
99
+ AWAITING_EVENT: 'awaiting_event',
100
+ AWAITING_TRANSFER: 'awaiting_transfer',
101
+ CLOSED: 'closed',
102
+ COMPLETE: 'complete',
103
+ EVENT: 'event',
104
+ INVALID: 'invalid',
105
+ ISSUE_DETERMINATION: 'issue_determination',
106
+ LPA_QUESTIONNAIRE: 'lpa_questionnaire',
107
+ READY_TO_START: 'ready_to_start',
108
+ TRANSFERRED: 'transferred',
109
+ VALIDATION: 'validation',
110
+ WITHDRAWN: 'withdrawn',
111
+ });
112
+
84
113
  const APPEAL_ALLOCATION_LEVEL = Object.freeze({
85
114
  A: 'A',
86
115
  B: 'B',
@@ -491,6 +520,11 @@ const METHOD = Object.freeze({
491
520
  POST: 'post',
492
521
  });
493
522
 
523
+ const NSIP_PROJECT_SOURCE_SYSTEM = Object.freeze({
524
+ BACK_OFFICE_APPLICATIONS: 'back-office-applications',
525
+ HORIZON: 'horizon',
526
+ });
527
+
494
528
  const NSIP_CA_AND_TP_EVIDENCE = Object.freeze({
495
529
  AWAITING_SUBMISSION: 'awaiting_submission',
496
530
  NOT_APPLICABLE: 'not_applicable',
@@ -736,8 +770,11 @@ const NSIP_SECURITY_CLASSIFICATION = Object.freeze({
736
770
  });
737
771
 
738
772
  const NSIP_SOURCE_SYSTEM = Object.freeze({
773
+ BACK_OFFICE_APPEALS: 'back-office-appeals',
739
774
  BACK_OFFICE_APPLICATIONS: 'back-office-applications',
740
775
  HORIZON: 'horizon',
776
+ NI_FILE: 'ni_file',
777
+ SHAREPOINT: 'sharepoint',
741
778
  });
742
779
 
743
780
  const NSIP_STAGE = Object.freeze({
@@ -875,6 +912,9 @@ exports.APPEAL_EIA_DEVELOPMENT_DESCRIPTION = APPEAL_EIA_DEVELOPMENT_DESCRIPTION;
875
912
  exports.APPEAL_EIA_ENVIRONMENTAL_IMPACT_SCHEDULE = APPEAL_EIA_ENVIRONMENTAL_IMPACT_SCHEDULE;
876
913
  exports.APPEAL_EVENT_STATUS = APPEAL_EVENT_STATUS;
877
914
  exports.APPEAL_EVENT_TYPE = APPEAL_EVENT_TYPE;
915
+ exports.APPEAL_HAS_CASE_DECISION_OUTCOME = APPEAL_HAS_CASE_DECISION_OUTCOME;
916
+ exports.APPEAL_HAS_CASE_PROCEDURE = APPEAL_HAS_CASE_PROCEDURE;
917
+ exports.APPEAL_HAS_CASE_STATUS = APPEAL_HAS_CASE_STATUS;
878
918
  exports.APPEAL_INVALID_OR_INCOMPLETE_DETAILS = APPEAL_INVALID_OR_INCOMPLETE_DETAILS;
879
919
  exports.APPEAL_KNOWS_ALL_OWNERS = APPEAL_KNOWS_ALL_OWNERS;
880
920
  exports.APPEAL_KNOWS_OTHER_OWNERS = APPEAL_KNOWS_OTHER_OWNERS;
@@ -910,6 +950,7 @@ exports.NSIP_ORIGIN = NSIP_ORIGIN;
910
950
  exports.NSIP_PLANNING_INSPECTORATE_ROLE = NSIP_PLANNING_INSPECTORATE_ROLE;
911
951
  exports.NSIP_POLICY_COMPLIANCE_DOCUMENT = NSIP_POLICY_COMPLIANCE_DOCUMENT;
912
952
  exports.NSIP_PRINCIPAL_AREA_DISAGREEMENT_SUMMARY_STMT = NSIP_PRINCIPAL_AREA_DISAGREEMENT_SUMMARY_STMT;
953
+ exports.NSIP_PROJECT_SOURCE_SYSTEM = NSIP_PROJECT_SOURCE_SYSTEM;
913
954
  exports.NSIP_PROJECT_TYPE = NSIP_PROJECT_TYPE;
914
955
  exports.NSIP_PUBLIC_SECTOR_EQUALITY_DUTY = NSIP_PUBLIC_SECTOR_EQUALITY_DUTY;
915
956
  exports.NSIP_PUBLISHED_STATUS = NSIP_PUBLISHED_STATUS;
@@ -0,0 +1,196 @@
1
+ import snakeCase from 'lodash.snakecase';
2
+
3
+ const SPACING = ' ';
4
+ const NEW_LINE = '\n';
5
+
6
+ /**
7
+ * Generate JS code to define exported constants for the given enum values
8
+ *
9
+ * @param {string} name
10
+ * @param {string[]} values
11
+ * @param {Object} opts
12
+ * @param {boolean} [opts.ts] - whether to generate the TS type or JS code
13
+ * @returns {string}
14
+ */
15
+ export function generateConstants(name, values, { ts = false } = {}) {
16
+ const freezeStart = ts ? '' : 'Object.freeze(';
17
+ const freezeEnd = ts ? ' as const' : ')';
18
+ const exportPrefix = 'export ';
19
+ const lines = [`${exportPrefix}const ${formatName(name)} = ${freezeStart}{`];
20
+ /**
21
+ * @param {string} line
22
+ * @param {number} spacing
23
+ */
24
+ const addWithSpace = (line, spacing = 0) => lines.push(SPACING.repeat(spacing) + line);
25
+
26
+ for (const value of values.sort()) {
27
+ addWithSpace(`${formatName(value)}: '${value}',`, 1);
28
+ }
29
+
30
+ lines.push(`}${freezeEnd};`);
31
+
32
+ return lines.join(NEW_LINE);
33
+ }
34
+
35
+ /**
36
+ * Format a name for use as a constant value: UPPER_SNAKE_CASE
37
+ *
38
+ * @param {string} name
39
+ * @returns {string}
40
+ */
41
+ function formatName(name) {
42
+ return snakeCase(name).toUpperCase();
43
+ }
44
+
45
+ /**
46
+ * @param {{schemaName: string, key: string, values: string[]}[]} enums
47
+ * @returns {Record<string, string[]>} map of enum name to values
48
+ */
49
+ export function generateEnumMap(enums) {
50
+ /** @type {Object<string, string[]>} */
51
+ const enumProps = {};
52
+
53
+ // only allow one duplicate - else the superset logic gets complex
54
+ const duplicateKeys = new Set();
55
+ const warnings = [];
56
+ const infos = [];
57
+
58
+ for (const e of enums) {
59
+ const prefix = schemaPrefix(e.schemaName);
60
+ const key = prefix ? prefix + '_' + e.key : e.key;
61
+
62
+ const existingEnum = enumProps[key];
63
+ const thisEnum = e.values;
64
+ if (!existingEnum) {
65
+ enumProps[key] = e.values;
66
+ } else if (!enumsMatch(existingEnum, thisEnum)) {
67
+ const existingEnumProps = enums.find((e) => e.values === existingEnum);
68
+ const setIndex = indexOfSuperset(existingEnum, thisEnum);
69
+ // handle duplicate enums - but one must be a superset of the other
70
+ if (setIndex === -1) {
71
+ throw new Error(
72
+ `Enums ${existingEnumProps.key} from ${existingEnumProps.schemaName} and ${e.key} from ${e.schemaName} have the same name but one is not a superset of the other`
73
+ );
74
+ }
75
+ // and only allow one duplicate
76
+ if (duplicateKeys.has(key)) {
77
+ throw new Error(
78
+ `enum ${e.key} is used by multiple schemas, including ${existingEnumProps.schemaName} and ${e.schemaName}`
79
+ );
80
+ }
81
+ duplicateKeys.add(key);
82
+ warnings.push([
83
+ '⚠️ enum key',
84
+ key,
85
+ 'is used with different values in',
86
+ existingEnumProps.schemaName,
87
+ 'and',
88
+ e.schemaName
89
+ ]);
90
+
91
+ if (setIndex === 1) {
92
+ // existing enum is the superset
93
+ // so this enum goes with a specific name
94
+ enumProps[e.schemaName.replace('.schema.json', '') + '_' + e.key] = e.values;
95
+ } else if (setIndex === 2) {
96
+ // this enum is the superset
97
+ // so 'swap in' this enum
98
+ enumProps[key] = e.values;
99
+ enumProps[existingEnumProps.schemaName.replace('.schema.json', '') + '_' + existingEnumProps.key] =
100
+ existingEnumProps.values;
101
+ }
102
+ } else {
103
+ // if the enums do match, nothing to change
104
+ infos.push(['ℹ️ enum key', key, 'is used multiple times but uses the same values']);
105
+ }
106
+ }
107
+ for (const info of infos) {
108
+ console.log(...info);
109
+ }
110
+ for (const warning of warnings) {
111
+ console.log(...warning);
112
+ }
113
+ return enumProps;
114
+ }
115
+
116
+ /**
117
+ * Do list1 and list2 match?
118
+ *
119
+ * @param {string[]} list1
120
+ * @param {string[]} list2
121
+ */
122
+ export function enumsMatch(list1, list2) {
123
+ const set1 = new Set(list1);
124
+ const set2 = new Set(list2);
125
+ // get all elements that are in set1 or set2 but not in both
126
+ // if there are none, they match
127
+ return set1.symmetricDifference(set2).size === 0;
128
+ }
129
+
130
+ /**
131
+ * Is list1 a superset of list2, or vice versa?
132
+ *
133
+ * @param {string[]} list1
134
+ * @param {string[]} list2
135
+ * @returns {1 | 2 | -1} -1 if neither list is a superset of the other
136
+ */
137
+ export function indexOfSuperset(list1, list2) {
138
+ const set1 = new Set(list1);
139
+ const set2 = new Set(list2);
140
+ if (set1.isSupersetOf(set2)) {
141
+ return 1;
142
+ }
143
+ if (set2.isSupersetOf(set1)) {
144
+ return 2;
145
+ }
146
+ return -1;
147
+ }
148
+
149
+ /**
150
+ *
151
+ * @param {{schemaName: string, key: string, values: string[]}[]} enums
152
+ * @param {Object<string, {type?: string|string[], enum?: string[]}>} properties
153
+ * @param {string} schemaName
154
+ */
155
+ export function collectEnumProps(enums, properties = {}, schemaName) {
156
+ for (const [key, prop] of Object.entries(properties)) {
157
+ const hasStringEnum = (p) =>
158
+ p && p.enum && Array.isArray(p.enum) && p.type && (p.type === 'string' || p.type.includes('string'));
159
+ const getEnumValues = (p) => p.filter((e) => e !== null);
160
+ const collectEnumFromProp = (p) => {
161
+ if (hasStringEnum(p)) {
162
+ const values = getEnumValues(p.enum);
163
+ const asSet = new Set(values);
164
+ if (values.length > asSet.size) {
165
+ throw new Error(`enum ${key} from ${schemaName} has duplicate elements`);
166
+ }
167
+ enums.push({
168
+ schemaName,
169
+ key,
170
+ values: values
171
+ });
172
+ }
173
+ };
174
+
175
+ collectEnumFromProp(prop);
176
+ collectEnumFromProp(prop.items);
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Generate a prefix based on the schema name
182
+ *
183
+ * @param {string} schemaName
184
+ * @returns {string|undefined}
185
+ */
186
+ export function schemaPrefix(schemaName) {
187
+ if (schemaName === 'message') {
188
+ return 'message';
189
+ }
190
+ if (schemaName.toLowerCase().includes('appeal')) {
191
+ return 'appeal';
192
+ }
193
+ if (schemaName.toLowerCase().includes('nsip')) {
194
+ return 'nsip';
195
+ }
196
+ }
@@ -0,0 +1,183 @@
1
+ import { describe, it } from 'node:test';
2
+ import { collectEnumProps, generateConstants, generateEnumMap } from './gen-enum-impl.js';
3
+ import assert from 'node:assert';
4
+
5
+ describe('gen-enum-impl', () => {
6
+ describe('generateConstants', () => {
7
+ it('should generate constant definitions', () => {
8
+ const output = generateConstants('some_EnumValue', ['value-1', 'other-value', 'a-third-value']);
9
+ assert.strictEqual(
10
+ output,
11
+ `export const SOME_ENUM_VALUE = Object.freeze({\n A_THIRD_VALUE: 'a-third-value',\n OTHER_VALUE: 'other-value',\n VALUE_1: 'value-1',\n});`
12
+ );
13
+ });
14
+ it('should generate constant definitions as typescript', () => {
15
+ const output = generateConstants('some_EnumValue', ['value-1', 'other-value', 'a-third-value'], { ts: true });
16
+ assert.strictEqual(
17
+ output,
18
+ `export const SOME_ENUM_VALUE = {\n A_THIRD_VALUE: 'a-third-value',\n OTHER_VALUE: 'other-value',\n VALUE_1: 'value-1',\n} as const;`
19
+ );
20
+ });
21
+ });
22
+
23
+ describe('generateEnumMap', () => {
24
+ it('should add all enums to a map by key with prefix', () => {
25
+ const enums = [
26
+ {
27
+ schemaName: 'schema-1',
28
+ key: 'key3',
29
+ values: ['enum-1', 'enum-2']
30
+ },
31
+ {
32
+ schemaName: 'appeal-1.schema.json',
33
+ key: 'key5',
34
+ values: ['enum-4', 'enum-5']
35
+ },
36
+ {
37
+ schemaName: 'nsip-1.schema.json',
38
+ key: 'key5',
39
+ values: ['enum-6', 'enum-7']
40
+ }
41
+ ];
42
+ const map = generateEnumMap(enums);
43
+ assert.deepStrictEqual(map, {
44
+ appeal_key5: ['enum-4', 'enum-5'],
45
+ nsip_key5: ['enum-6', 'enum-7'],
46
+ key3: ['enum-1', 'enum-2']
47
+ });
48
+ });
49
+ it('should ignore duplicates if the enums match', () => {
50
+ const enums = [
51
+ {
52
+ schemaName: 'schema-1',
53
+ key: 'key3',
54
+ values: ['enum-1', 'enum-2']
55
+ },
56
+ {
57
+ schemaName: 'appeal-1.schema.json',
58
+ key: 'key5',
59
+ values: ['enum-4', 'enum-5']
60
+ },
61
+ {
62
+ schemaName: 'appeal-2.schema.json',
63
+ key: 'key5',
64
+ values: ['enum-4', 'enum-5']
65
+ }
66
+ ];
67
+ const map = generateEnumMap(enums);
68
+ assert.deepStrictEqual(map, {
69
+ appeal_key5: ['enum-4', 'enum-5'],
70
+ key3: ['enum-1', 'enum-2']
71
+ });
72
+ });
73
+ it('should add a more specific prefix if there is a duplicate key', () => {
74
+ const enums = [
75
+ {
76
+ schemaName: 'schema-1',
77
+ key: 'key3',
78
+ values: ['enum-1', 'enum-2']
79
+ },
80
+ {
81
+ schemaName: 'appeal-1.schema.json',
82
+ key: 'key5',
83
+ values: ['enum-4', 'enum-5']
84
+ },
85
+ {
86
+ schemaName: 'appeal-2.schema.json',
87
+ key: 'key5',
88
+ values: ['enum-4', 'enum-5', 'enum-6']
89
+ }
90
+ ];
91
+ const map = generateEnumMap(enums);
92
+ assert.deepStrictEqual(map, {
93
+ appeal_key5: ['enum-4', 'enum-5', 'enum-6'],
94
+ 'appeal-1_key5': ['enum-4', 'enum-5'],
95
+ key3: ['enum-1', 'enum-2']
96
+ });
97
+ });
98
+ it('should error if duplicate key enums are not a superset', () => {
99
+ const enums = [
100
+ {
101
+ schemaName: 'schema-1',
102
+ key: 'key3',
103
+ values: ['enum-1', 'enum-2']
104
+ },
105
+ {
106
+ schemaName: 'appeal-1.schema.json',
107
+ key: 'key5',
108
+ values: ['enum-4', 'enum-5']
109
+ },
110
+ {
111
+ schemaName: 'appeal-2.schema.json',
112
+ key: 'key5',
113
+ values: ['enum-4', 'enum-6']
114
+ }
115
+ ];
116
+ assert.throws(() => generateEnumMap(enums), { message: /not a superset of the other/ });
117
+ });
118
+ it('should error if duplicate keys are present more than twice', () => {
119
+ const enums = [
120
+ {
121
+ schemaName: 'schema-1',
122
+ key: 'key3',
123
+ values: ['enum-1', 'enum-2']
124
+ },
125
+ {
126
+ schemaName: 'appeal-1.schema.json',
127
+ key: 'key5',
128
+ values: ['enum-4', 'enum-5']
129
+ },
130
+ {
131
+ schemaName: 'appeal-2.schema.json',
132
+ key: 'key5',
133
+ values: ['enum-4', 'enum-5', 'enum-6']
134
+ },
135
+ {
136
+ schemaName: 'appeal-2.schema.json',
137
+ key: 'key5',
138
+ values: ['enum-4', 'enum-5', 'enum-6', 'enum-7']
139
+ }
140
+ ];
141
+ assert.throws(() => generateEnumMap(enums), { message: /used by multiple schemas/ });
142
+ });
143
+ });
144
+
145
+ describe('collectEnumProps', () => {
146
+ it('should collect all enums', () => {
147
+ const enums = [];
148
+ collectEnumProps(
149
+ enums,
150
+ {
151
+ key1: 'value-1',
152
+ key2: 'other-value',
153
+ key3: {
154
+ type: 'string',
155
+ enum: ['enum-1', 'enum-2']
156
+ }
157
+ },
158
+ 'schema-1'
159
+ );
160
+ collectEnumProps(
161
+ enums,
162
+ {
163
+ key5: {
164
+ type: 'string',
165
+ enum: ['enum-4', 'enum-5']
166
+ }
167
+ },
168
+ 'appeal-1.schema.json'
169
+ );
170
+ assert.strictEqual(enums.length, 2);
171
+ assert.deepStrictEqual(enums[0], {
172
+ schemaName: 'schema-1',
173
+ key: 'key3',
174
+ values: ['enum-1', 'enum-2']
175
+ });
176
+ assert.deepStrictEqual(enums[1], {
177
+ schemaName: 'appeal-1.schema.json',
178
+ key: 'key5',
179
+ values: ['enum-4', 'enum-5']
180
+ });
181
+ });
182
+ });
183
+ });
@@ -1,16 +1,14 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
- import url from 'url';
4
3
  import { loadAllSchemas } from '../index.js';
5
- import snakeCase from 'lodash.snakecase';
6
4
  import { loadSchema } from './load.js';
5
+ import { collectEnumProps, generateConstants, generateEnumMap } from './gen-enum-impl.js';
7
6
 
8
- const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
7
+ const __dirname = import.meta.dirname;
9
8
 
10
9
  export const constPath = path.join(__dirname, '..', 'enums.js');
11
10
  export const constTsPath = path.join(__dirname, '..', 'enums.d.ts');
12
11
 
13
- const SPACING = ' ';
14
12
  const NEW_LINE = '\n';
15
13
 
16
14
  /**
@@ -19,16 +17,17 @@ const NEW_LINE = '\n';
19
17
  async function run() {
20
18
  const s = await loadAllSchemas();
21
19
 
22
- /** @type {Object<string, string[]>} */
23
- const enumProps = {};
20
+ const allEnums = [];
24
21
 
25
22
  for (const schemaName of Object.keys(s.schemas).sort()) {
26
23
  const schema = s.schemas[schemaName];
27
- collectEnumProps(enumProps, schema.properties, schemaPrefix(schemaName));
24
+ collectEnumProps(allEnums, schema.properties, schemaName);
28
25
  }
29
26
 
30
27
  const staticEnums = await loadSchema(path.join(__dirname, 'enums-static.schema.json'));
31
- collectEnumProps(enumProps, staticEnums.properties, 'message');
28
+ collectEnumProps(allEnums, staticEnums.properties, 'message');
29
+
30
+ const enumProps = generateEnumMap(allEnums);
32
31
 
33
32
  let output = '';
34
33
  let tsOutput = '';
@@ -42,82 +41,7 @@ async function run() {
42
41
  await fs.writeFile(constTsPath, tsOutput);
43
42
  }
44
43
 
45
- /**
46
- * Generate JS code to define exported constants for the given enum values
47
- *
48
- * @param {string} name
49
- * @param {string[]} values
50
- * @param {Object} opts
51
- * @param {boolean} [opts.ts] - whether to generate the TS type or JS code
52
- * @returns {string}
53
- */
54
- function generateConstants(name, values, { ts = false } = {}) {
55
- const freezeStart = ts ? '' : 'Object.freeze(';
56
- const freezeEnd = ts ? ' as const' : ')';
57
- const exportPrefix = 'export ';
58
- const lines = [`${exportPrefix}const ${formatName(name)} = ${freezeStart}{`];
59
- /**
60
- * @param {string} line
61
- * @param {number} spacing
62
- */
63
- const addWithSpace = (line, spacing = 0) => lines.push(SPACING.repeat(spacing) + line);
64
-
65
- for (const value of values.sort()) {
66
- addWithSpace(`${formatName(value)}: '${value}',`, 1);
67
- }
68
-
69
- lines.push(`}${freezeEnd};`);
70
-
71
- return lines.join(NEW_LINE);
72
- }
73
-
74
- /**
75
- * Format a name for use as a constant value: UPPER_SNAKE_CASE
76
- *
77
- * @param {string} name
78
- * @returns {string}
79
- */
80
- function formatName(name) {
81
- return snakeCase(name).toUpperCase();
82
- }
83
-
84
- /**
85
- *
86
- * @param {Object<string, string[]>} map
87
- * @param {Object<string, {type?: string|string[], enum?: string[]}>} properties
88
- * @param {string} [prefix]
89
- */
90
- function collectEnumProps(map, properties = {}, prefix) {
91
- for (const [key, prop] of Object.entries(properties)) {
92
- const name = prefix ? prefix + '_' + key : key;
93
-
94
- const hasStringEnum = (p) =>
95
- p && p.enum && Array.isArray(p.enum) && p.type && (p.type === 'string' || p.type.includes('string'));
96
- const getEnumValues = (p) => p.filter((e) => e !== null);
97
- const collectEnumFromProp = (p) => {
98
- if (hasStringEnum(p)) {
99
- map[name] = getEnumValues(p.enum);
100
- }
101
- };
102
-
103
- collectEnumFromProp(prop);
104
- collectEnumFromProp(prop.items);
105
- }
106
- }
107
-
108
- /**
109
- * Generate a prefix based on the schema name
110
- *
111
- * @param {string} schemaName
112
- * @returns {string|undefined}
113
- */
114
- function schemaPrefix(schemaName) {
115
- if (schemaName.toLowerCase().includes('appeal')) {
116
- return 'appeal';
117
- }
118
- if (schemaName.toLowerCase().includes('nsip')) {
119
- return 'nsip';
120
- }
121
- }
122
-
123
- run().catch(console.error);
44
+ run().catch((err) => {
45
+ console.error(err);
46
+ process.exit(1);
47
+ });
package/src/schemas.d.ts CHANGED
@@ -4613,7 +4613,7 @@ export type LPAQCasAdvertSubmissionProperties = {
4613
4613
  didAppellantSubmitCompletePhotosAndPlans?: boolean | null;
4614
4614
  };
4615
4615
  /**
4616
- * Schema defining any enfrocement specific properties for LPAQ submissions
4616
+ * Schema defining any common enforcement specific properties for LPAQ submissions
4617
4617
  */
4618
4618
  export type LPAQEnforcementSubmissionProperties = {
4619
4619
  noticeRelatesToBuildingEngineeringMiningOther?: boolean | null;
@@ -4635,6 +4635,11 @@ export type LPAQEnforcementSubmissionProperties = {
4635
4635
  relatesToErectionOfBuildingOrBuildings?: boolean | null;
4636
4636
  relatesToBuildingWithAgriculturalPurpose?: boolean | null;
4637
4637
  relatesToBuildingSingleDwellingHouse?: boolean | null;
4638
+ };
4639
+ /**
4640
+ * Schema defining any enforcement specific properties for LPAQ submissions
4641
+ */
4642
+ export type LPAQEnforcementSubmissionProperties1 = {
4638
4643
  /**
4639
4644
  * Name of trunk road within 67M of site
4640
4645
  */
@@ -4705,7 +4710,7 @@ export interface LPAQuestionnaireCommand {
4705
4710
  LPAProcedurePreferenceProperties &
4706
4711
  ChangedListedBuildingNumbersProperties)
4707
4712
  | ({
4708
- caseType?: 'C' | 'F';
4713
+ caseType?: 'C';
4709
4714
  } & LPAQCommonSubmissionProperties &
4710
4715
  LPAQHASSubmissionProperties &
4711
4716
  LPAProcedurePreferenceProperties &
@@ -4713,13 +4718,25 @@ export interface LPAQuestionnaireCommand {
4713
4718
  InfrastructureLevyProperties &
4714
4719
  EnvironmentalImpactAssessmentProperties &
4715
4720
  SiteDesignationsAndProtectionSpecificProperties &
4716
- LPAQEnforcementSubmissionProperties)
4721
+ LPAQEnforcementSubmissionProperties &
4722
+ LPAQEnforcementSubmissionProperties1)
4717
4723
  | ({
4718
4724
  caseType?: 'X';
4719
4725
  } & LPAQCommonSubmissionProperties &
4720
4726
  LPAProcedurePreferenceProperties &
4721
4727
  InfrastructureLevyProperties &
4722
- LPAQLDCSubmissionProperties);
4728
+ LPAQLDCSubmissionProperties)
4729
+ | ({
4730
+ caseType?: 'F';
4731
+ } & LPAQCommonSubmissionProperties &
4732
+ LPAQHASSubmissionProperties &
4733
+ LPAProcedurePreferenceProperties &
4734
+ ChangedListedBuildingNumbersProperties &
4735
+ InfrastructureLevyProperties &
4736
+ EnvironmentalImpactAssessmentProperties &
4737
+ SiteDesignationsAndProtectionSpecificProperties &
4738
+ LPAQEnforcementSubmissionProperties &
4739
+ LPAQListedBuildingSubmissionProperties);
4723
4740
  documents: {
4724
4741
  /**
4725
4742
  * The unique identifier for the document
@@ -71,9 +71,8 @@ const exampleEnforcementListedQuestionnaireSchema = {
71
71
  relatesToErectionOfBuildingOrBuildings: null,
72
72
  relatesToBuildingWithAgriculturalPurpose: null,
73
73
  relatesToBuildingSingleDwellingHouse: null,
74
- affectedTrunkRoadName: null,
75
- isSiteOnCrownLand: null,
76
- article4AffectedDevelopmentRights: null
74
+ preserveGrantLoan: null,
75
+ consultHistoricEngland: null
77
76
  },
78
77
  documents: [document]
79
78
  };