qlcodes 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -55,9 +55,58 @@ Output :
55
55
  }
56
56
  ```
57
57
 
58
+ Alternatively, you may need to retrive matching codes via a known key for some technologies
59
+
60
+ ```js
61
+ import { lens } from "qlcodes";
62
+
63
+ const state = lens("insufficient_privilege");
64
+
65
+ console.log(state);
66
+ ```
67
+
68
+ Output :
69
+
70
+ ```js
71
+ {
72
+ "key": "insufficient_privilege",
73
+ "qlcs": "qlcodes_success",
74
+ "matches": [
75
+ {
76
+ "code": "42501",
77
+ "keys": [
78
+ "insufficient_privilege",
79
+ "authorization_id_does_not_have_privilege_to_perform_specified_operation_on_identified_object"
80
+ ],
81
+ "reasons": [
82
+ "The authorization ID does not have the privilege to perform the specified operation on the identified object."
83
+ ],
84
+ "use": [
85
+ "pgsql",
86
+ "ibm"
87
+ ],
88
+ "class": "42 - Syntax Error or Access Rule Violation"
89
+ },
90
+ {
91
+ "code": "258",
92
+ "keys": [
93
+ "insufficient_privilege"
94
+ ],
95
+ "reasons": [
96
+ "Insufficient privilege"
97
+ ],
98
+ "use": [
99
+ "sap_hana"
100
+ ],
101
+ "class": "NA - Non associable class codes"
102
+ }
103
+ ]
104
+ }
105
+ ```
106
+
58
107
  ### Mismatches
59
108
 
60
- If the provided SQLSTATE does not match any known entry, we return a normalized fallback object
109
+ If the provided SQLSTATE code or provided key does not match any known entry, we return a normalized fallback object
61
110
  This guarantees that lens() always returns a predictable object shape.
62
111
 
63
112
  There are three mismatch levels that we detect.
package/dist/index.mjs CHANGED
@@ -1,69 +1,120 @@
1
- import { readFileSync } from "fs";
1
+ import data from "./qlCodes.json" with { type: "json" };
2
2
 
3
- const data = JSON.parse(readFileSync("./qlCodes.json"));
3
+ const keyRegex = /^[a-zA-Z0-9_]+$/;
4
+ const codeRegex = /^[A-Z0-9]{5}$/;
4
5
 
5
- export const lens = (code) => {
6
- let response = {
7
- code,
8
- qlcs: "",
9
- matches: [],
10
- };
6
+ const NON_ASSOCIATED_CLASS = "NA";
11
7
 
12
- try {
13
- // ---- validation ----
14
- if (
15
- code === "" ||
16
- (!code && code !== "0") ||
17
- !/^[A-Z0-9]{5}$/.test(`${code}`.padStart(5, "0"))
18
- ) {
19
- throw "qlcodes_malformed";
20
- }
8
+ const QLCODES_ERR_MALFORMED = "qlcodes_malformed";
9
+ const QLCODES_ERR_NOT_FOUND = "qlcodes_no_code_found";
10
+ const QLCODES_ERR_UNEXPECTED_ERR = "qlcodes_unexpected_error";
21
11
 
22
- const paddedCode = `${code}`.padStart(5, "0");
12
+ const QLCODES_SUCCESS = "qlcodes_success";
23
13
 
24
- // ---- find matching class ----
25
- const classEntry = Object.entries(data).find(([id]) =>
26
- paddedCode.startsWith(id)
27
- );
14
+ const _validateSearch = (search, response) => {
15
+ if (
16
+ search === "" ||
17
+ (!search && search !== "0") ||
18
+ (!codeRegex.test(`${search}`.padStart(5, "0")) &&
19
+ !keyRegex.test(`${search}`))
20
+ ) {
21
+ throw QLCODES_ERR_MALFORMED;
22
+ } else if (codeRegex.test(`${search}`)) {
23
+ response.code = search;
24
+ } else {
25
+ response.key = search;
26
+ }
27
+ };
28
28
 
29
- const classesToSearch = [];
29
+ const _getHitKeyOrCode = (cls, response, paddedSearch) => {
30
+ if (!response.key && !response.code) {
31
+ return [];
32
+ }
33
+ const matchingInKeys = (x) => x.keys.find((k) => paddedSearch === k);
34
+ const matchingCode = (x) =>
35
+ x.code === paddedSearch || x.code === paddedSearch;
30
36
 
31
- if (classEntry) {
32
- classesToSearch.push({
33
- id: classEntry[0],
34
- data: classEntry[1],
35
- });
36
- }
37
+ return cls.data.messageSet.filter(
38
+ response.key ? matchingInKeys : matchingCode
39
+ );
40
+ };
37
41
 
38
- // Always include NA as fallback / collision source
39
- if (data["NA"]) {
40
- classesToSearch.push({
41
- id: "NA",
42
- data: data["NA"],
42
+ const _collectAllMatches = (classesToSearch, response, paddedSearch) => {
43
+ const matches = [];
44
+ for (const cls of classesToSearch) {
45
+ for (const hit of _getHitKeyOrCode(cls, response, paddedSearch)) {
46
+ matches.push({
47
+ ...hit,
48
+ class: `${cls.id} - ${cls.data.label}`,
43
49
  });
44
50
  }
51
+ }
52
+ return matches;
53
+ };
45
54
 
46
- // ---- collect all matches ----
47
- for (const cls of classesToSearch) {
48
- const hits = cls.data.messageSet.filter(
49
- (x) => x.code === paddedCode || x.code === code
50
- );
51
-
52
- for (const hit of hits) {
53
- response.matches.push({
54
- ...hit,
55
- class: `${cls.id} - ${cls.data.label}`,
56
- });
57
- }
58
- }
55
+ const _classesForCodeSearch = (entries, paddedSearch) => {
56
+ const classesToSearch = [];
57
+ const classEntry = entries.find(([id]) => paddedSearch.startsWith(id));
58
+ if (classEntry) {
59
+ classesToSearch.push({
60
+ id: classEntry[0],
61
+ data: classEntry[1],
62
+ });
63
+ }
64
+ // Always include NA as fallback / collision source
65
+ if (data[NON_ASSOCIATED_CLASS]) {
66
+ classesToSearch.push({
67
+ id: NON_ASSOCIATED_CLASS,
68
+ data: data[NON_ASSOCIATED_CLASS],
69
+ });
70
+ }
71
+ return classesToSearch;
72
+ };
73
+
74
+ const _classesForKeySearche = (entries) => {
75
+ const classesToSearch = [];
76
+ entries.forEach(([id]) => {
77
+ classesToSearch.push({
78
+ id,
79
+ data: data[id],
80
+ });
81
+ });
82
+ return classesToSearch;
83
+ };
84
+
85
+ export const lens = (search) => {
86
+ let response = {
87
+ code: undefined,
88
+ key: undefined,
89
+ qlcs: "",
90
+ matches: [],
91
+ };
92
+
93
+ try {
94
+ _validateSearch(search, response);
95
+
96
+ const paddedSearch = `${search}`.padStart(5, "0");
97
+ const classes = Object.entries(data);
98
+
99
+ const classesToSearch = response.code
100
+ ? _classesForCodeSearch(classes, paddedSearch)
101
+ : response.key
102
+ ? _classesForKeySearche(classes)
103
+ : [];
104
+
105
+ response.matches = _collectAllMatches(
106
+ classesToSearch,
107
+ response,
108
+ paddedSearch
109
+ );
59
110
 
60
111
  if (response.matches.length === 0) {
61
- throw "qlcodes_no_code_found";
112
+ throw QLCODES_ERR_NOT_FOUND;
62
113
  }
63
114
 
64
- response.qlcs = "qlcodes_success";
115
+ response.qlcs = QLCODES_SUCCESS;
65
116
  } catch (err) {
66
- response.qlcs = typeof err === "string" ? err : "qlcodes_unexpected_error";
117
+ response.qlcs = typeof err === "string" ? err : QLCODES_ERR_UNEXPECTED_ERR;
67
118
  }
68
119
 
69
120
  return response;