@choiceapps/communityservices-sdk 0.1.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 (294) hide show
  1. package/AGENTS.md +43 -0
  2. package/API.md +86 -0
  3. package/CHANGELOG.md +12 -0
  4. package/LICENSE +201 -0
  5. package/NOTICE +11 -0
  6. package/README.md +100 -0
  7. package/SSVF.md +131 -0
  8. package/browser/attachment-conflict.mjs +6 -0
  9. package/browser/case-plans-ui.mjs +23 -0
  10. package/browser/case-plans.d.mts +20 -0
  11. package/browser/case-plans.mjs +89 -0
  12. package/browser/client-creation.d.mts +16 -0
  13. package/browser/client-creation.mjs +60 -0
  14. package/browser/client-profile-ui.mjs +59 -0
  15. package/browser/client-profile.d.mts +24 -0
  16. package/browser/client-profile.mjs +94 -0
  17. package/browser/client-workflows.mjs +135 -0
  18. package/browser/connection.d.mts +4 -0
  19. package/browser/connection.mjs +41 -0
  20. package/browser/current-living-situation.d.mts +44 -0
  21. package/browser/current-living-situation.mjs +161 -0
  22. package/browser/date-of-birth-ui.mjs +68 -0
  23. package/browser/date-of-birth.d.mts +28 -0
  24. package/browser/date-of-birth.mjs +112 -0
  25. package/browser/enrollment-workflow.d.mts +174 -0
  26. package/browser/enrollment-workflow.mjs +518 -0
  27. package/browser/exit-details.mjs +21 -0
  28. package/browser/file-store.mjs +32 -0
  29. package/browser/gwt-mapped.mjs +46 -0
  30. package/browser/household-exit.d.mts +34 -0
  31. package/browser/household-exit.mjs +102 -0
  32. package/browser/households-ui.mjs +20 -0
  33. package/browser/households.d.mts +22 -0
  34. package/browser/households.mjs +115 -0
  35. package/browser/index.d.mts +78 -0
  36. package/browser/index.mjs +46 -0
  37. package/browser/member-entry-date.d.mts +30 -0
  38. package/browser/member-entry-date.mjs +71 -0
  39. package/browser/need-recording.d.mts +37 -0
  40. package/browser/need-recording.mjs +68 -0
  41. package/browser/need-ui.mjs +138 -0
  42. package/browser/page-lock.mjs +11 -0
  43. package/browser/password-recovery.mjs +49 -0
  44. package/browser/playwright-session.mjs +196 -0
  45. package/browser/program-discovery.d.mts +22 -0
  46. package/browser/program-discovery.mjs +80 -0
  47. package/browser/recovery-diagnostics.mjs +9 -0
  48. package/browser/service-recording.d.mts +45 -0
  49. package/browser/service-recording.mjs +148 -0
  50. package/browser/service-ui.mjs +160 -0
  51. package/browser/session-authentication.mjs +40 -0
  52. package/browser/shelter-inventory.d.mts +103 -0
  53. package/browser/shelter-inventory.mjs +273 -0
  54. package/browser/shelter-ui.mjs +382 -0
  55. package/browser/shelters.d.mts +139 -0
  56. package/browser/shelters.mjs +425 -0
  57. package/browser/ssvf-rules.d.mts +21 -0
  58. package/browser/ssvf-rules.mjs +145 -0
  59. package/browser/ui-login.mjs +135 -0
  60. package/dist/cjs/bulk.d.ts +33 -0
  61. package/dist/cjs/bulk.d.ts.map +1 -0
  62. package/dist/cjs/bulk.js +42 -0
  63. package/dist/cjs/bulk.js.map +1 -0
  64. package/dist/cjs/control.d.ts +35 -0
  65. package/dist/cjs/control.d.ts.map +1 -0
  66. package/dist/cjs/control.js +141 -0
  67. package/dist/cjs/control.js.map +1 -0
  68. package/dist/cjs/effective-date.d.ts +3 -0
  69. package/dist/cjs/effective-date.d.ts.map +1 -0
  70. package/dist/cjs/effective-date.js +27 -0
  71. package/dist/cjs/effective-date.js.map +1 -0
  72. package/dist/cjs/encoding.d.ts +32 -0
  73. package/dist/cjs/encoding.d.ts.map +1 -0
  74. package/dist/cjs/encoding.js +121 -0
  75. package/dist/cjs/encoding.js.map +1 -0
  76. package/dist/cjs/enrollment-verifications.d.ts +39 -0
  77. package/dist/cjs/enrollment-verifications.d.ts.map +1 -0
  78. package/dist/cjs/enrollment-verifications.js +96 -0
  79. package/dist/cjs/enrollment-verifications.js.map +1 -0
  80. package/dist/cjs/errors.d.ts +33 -0
  81. package/dist/cjs/errors.d.ts.map +1 -0
  82. package/dist/cjs/errors.js +45 -0
  83. package/dist/cjs/errors.js.map +1 -0
  84. package/dist/cjs/generated/v2-models.d.ts +1482 -0
  85. package/dist/cjs/generated/v2-models.d.ts.map +1 -0
  86. package/dist/cjs/generated/v2-models.js +3 -0
  87. package/dist/cjs/generated/v2-models.js.map +1 -0
  88. package/dist/cjs/generated/v2.d.ts +1149 -0
  89. package/dist/cjs/generated/v2.d.ts.map +1 -0
  90. package/dist/cjs/generated/v2.js +252 -0
  91. package/dist/cjs/generated/v2.js.map +1 -0
  92. package/dist/cjs/generated/v3-models.d.ts +1420 -0
  93. package/dist/cjs/generated/v3-models.d.ts.map +1 -0
  94. package/dist/cjs/generated/v3-models.js +3 -0
  95. package/dist/cjs/generated/v3-models.js.map +1 -0
  96. package/dist/cjs/generated/v3.d.ts +1149 -0
  97. package/dist/cjs/generated/v3.d.ts.map +1 -0
  98. package/dist/cjs/generated/v3.js +252 -0
  99. package/dist/cjs/generated/v3.js.map +1 -0
  100. package/dist/cjs/gwt-enrollments.d.ts +131 -0
  101. package/dist/cjs/gwt-enrollments.d.ts.map +1 -0
  102. package/dist/cjs/gwt-enrollments.js +280 -0
  103. package/dist/cjs/gwt-enrollments.js.map +1 -0
  104. package/dist/cjs/index.d.ts +3 -0
  105. package/dist/cjs/index.d.ts.map +1 -0
  106. package/dist/cjs/index.js +19 -0
  107. package/dist/cjs/index.js.map +1 -0
  108. package/dist/cjs/metadata.d.ts +100 -0
  109. package/dist/cjs/metadata.d.ts.map +1 -0
  110. package/dist/cjs/metadata.js +165 -0
  111. package/dist/cjs/metadata.js.map +1 -0
  112. package/dist/cjs/package.json +1 -0
  113. package/dist/cjs/program-discovery.d.ts +28 -0
  114. package/dist/cjs/program-discovery.d.ts.map +1 -0
  115. package/dist/cjs/program-discovery.js +58 -0
  116. package/dist/cjs/program-discovery.js.map +1 -0
  117. package/dist/cjs/race.d.ts +4 -0
  118. package/dist/cjs/race.d.ts.map +1 -0
  119. package/dist/cjs/race.js +40 -0
  120. package/dist/cjs/race.js.map +1 -0
  121. package/dist/cjs/runtime.d.ts +108 -0
  122. package/dist/cjs/runtime.d.ts.map +1 -0
  123. package/dist/cjs/runtime.js +38 -0
  124. package/dist/cjs/runtime.js.map +1 -0
  125. package/dist/cjs/subassessments.d.ts +29 -0
  126. package/dist/cjs/subassessments.d.ts.map +1 -0
  127. package/dist/cjs/subassessments.js +107 -0
  128. package/dist/cjs/subassessments.js.map +1 -0
  129. package/dist/cjs/testing.d.ts +45 -0
  130. package/dist/cjs/testing.d.ts.map +1 -0
  131. package/dist/cjs/testing.js +66 -0
  132. package/dist/cjs/testing.js.map +1 -0
  133. package/dist/cjs/transport.d.ts +19 -0
  134. package/dist/cjs/transport.d.ts.map +1 -0
  135. package/dist/cjs/transport.js +373 -0
  136. package/dist/cjs/transport.js.map +1 -0
  137. package/dist/cjs/v2.d.ts +13 -0
  138. package/dist/cjs/v2.d.ts.map +1 -0
  139. package/dist/cjs/v2.js +29 -0
  140. package/dist/cjs/v2.js.map +1 -0
  141. package/dist/cjs/v3.d.ts +13 -0
  142. package/dist/cjs/v3.d.ts.map +1 -0
  143. package/dist/cjs/v3.js +29 -0
  144. package/dist/cjs/v3.js.map +1 -0
  145. package/dist/cjs/validation.d.ts +33 -0
  146. package/dist/cjs/validation.d.ts.map +1 -0
  147. package/dist/cjs/validation.js +77 -0
  148. package/dist/cjs/validation.js.map +1 -0
  149. package/dist/cjs/verification.d.ts +82 -0
  150. package/dist/cjs/verification.d.ts.map +1 -0
  151. package/dist/cjs/verification.js +122 -0
  152. package/dist/cjs/verification.js.map +1 -0
  153. package/dist/esm/bulk.d.ts +33 -0
  154. package/dist/esm/bulk.d.ts.map +1 -0
  155. package/dist/esm/bulk.js +39 -0
  156. package/dist/esm/bulk.js.map +1 -0
  157. package/dist/esm/control.d.ts +35 -0
  158. package/dist/esm/control.d.ts.map +1 -0
  159. package/dist/esm/control.js +132 -0
  160. package/dist/esm/control.js.map +1 -0
  161. package/dist/esm/effective-date.d.ts +3 -0
  162. package/dist/esm/effective-date.d.ts.map +1 -0
  163. package/dist/esm/effective-date.js +24 -0
  164. package/dist/esm/effective-date.js.map +1 -0
  165. package/dist/esm/encoding.d.ts +32 -0
  166. package/dist/esm/encoding.d.ts.map +1 -0
  167. package/dist/esm/encoding.js +117 -0
  168. package/dist/esm/encoding.js.map +1 -0
  169. package/dist/esm/enrollment-verifications.d.ts +39 -0
  170. package/dist/esm/enrollment-verifications.d.ts.map +1 -0
  171. package/dist/esm/enrollment-verifications.js +93 -0
  172. package/dist/esm/enrollment-verifications.js.map +1 -0
  173. package/dist/esm/errors.d.ts +33 -0
  174. package/dist/esm/errors.d.ts.map +1 -0
  175. package/dist/esm/errors.js +33 -0
  176. package/dist/esm/errors.js.map +1 -0
  177. package/dist/esm/generated/v2-models.d.ts +1482 -0
  178. package/dist/esm/generated/v2-models.d.ts.map +1 -0
  179. package/dist/esm/generated/v2-models.js +2 -0
  180. package/dist/esm/generated/v2-models.js.map +1 -0
  181. package/dist/esm/generated/v2.d.ts +1149 -0
  182. package/dist/esm/generated/v2.d.ts.map +1 -0
  183. package/dist/esm/generated/v2.js +247 -0
  184. package/dist/esm/generated/v2.js.map +1 -0
  185. package/dist/esm/generated/v3-models.d.ts +1420 -0
  186. package/dist/esm/generated/v3-models.d.ts.map +1 -0
  187. package/dist/esm/generated/v3-models.js +2 -0
  188. package/dist/esm/generated/v3-models.js.map +1 -0
  189. package/dist/esm/generated/v3.d.ts +1149 -0
  190. package/dist/esm/generated/v3.d.ts.map +1 -0
  191. package/dist/esm/generated/v3.js +247 -0
  192. package/dist/esm/generated/v3.js.map +1 -0
  193. package/dist/esm/gwt-enrollments.d.ts +131 -0
  194. package/dist/esm/gwt-enrollments.d.ts.map +1 -0
  195. package/dist/esm/gwt-enrollments.js +274 -0
  196. package/dist/esm/gwt-enrollments.js.map +1 -0
  197. package/dist/esm/index.d.ts +3 -0
  198. package/dist/esm/index.d.ts.map +1 -0
  199. package/dist/esm/index.js +3 -0
  200. package/dist/esm/index.js.map +1 -0
  201. package/dist/esm/metadata.d.ts +100 -0
  202. package/dist/esm/metadata.d.ts.map +1 -0
  203. package/dist/esm/metadata.js +157 -0
  204. package/dist/esm/metadata.js.map +1 -0
  205. package/dist/esm/program-discovery.d.ts +28 -0
  206. package/dist/esm/program-discovery.d.ts.map +1 -0
  207. package/dist/esm/program-discovery.js +55 -0
  208. package/dist/esm/program-discovery.js.map +1 -0
  209. package/dist/esm/race.d.ts +4 -0
  210. package/dist/esm/race.d.ts.map +1 -0
  211. package/dist/esm/race.js +37 -0
  212. package/dist/esm/race.js.map +1 -0
  213. package/dist/esm/runtime.d.ts +108 -0
  214. package/dist/esm/runtime.d.ts.map +1 -0
  215. package/dist/esm/runtime.js +33 -0
  216. package/dist/esm/runtime.js.map +1 -0
  217. package/dist/esm/subassessments.d.ts +29 -0
  218. package/dist/esm/subassessments.d.ts.map +1 -0
  219. package/dist/esm/subassessments.js +103 -0
  220. package/dist/esm/subassessments.js.map +1 -0
  221. package/dist/esm/testing.d.ts +45 -0
  222. package/dist/esm/testing.d.ts.map +1 -0
  223. package/dist/esm/testing.js +60 -0
  224. package/dist/esm/testing.js.map +1 -0
  225. package/dist/esm/transport.d.ts +19 -0
  226. package/dist/esm/transport.d.ts.map +1 -0
  227. package/dist/esm/transport.js +369 -0
  228. package/dist/esm/transport.js.map +1 -0
  229. package/dist/esm/v2.d.ts +13 -0
  230. package/dist/esm/v2.d.ts.map +1 -0
  231. package/dist/esm/v2.js +13 -0
  232. package/dist/esm/v2.js.map +1 -0
  233. package/dist/esm/v3.d.ts +13 -0
  234. package/dist/esm/v3.d.ts.map +1 -0
  235. package/dist/esm/v3.js +13 -0
  236. package/dist/esm/v3.js.map +1 -0
  237. package/dist/esm/validation.d.ts +33 -0
  238. package/dist/esm/validation.d.ts.map +1 -0
  239. package/dist/esm/validation.js +72 -0
  240. package/dist/esm/validation.js.map +1 -0
  241. package/dist/esm/verification.d.ts +82 -0
  242. package/dist/esm/verification.d.ts.map +1 -0
  243. package/dist/esm/verification.js +117 -0
  244. package/dist/esm/verification.js.map +1 -0
  245. package/docs/README.md +56 -0
  246. package/docs/assessments.md +103 -0
  247. package/docs/browser-session.md +129 -0
  248. package/docs/capabilities.md +75 -0
  249. package/docs/configuration.md +81 -0
  250. package/docs/current-living-situation.md +56 -0
  251. package/docs/dates-and-answers.md +40 -0
  252. package/docs/development.md +81 -0
  253. package/docs/durable-records.md +156 -0
  254. package/docs/enrollments.md +217 -0
  255. package/docs/files-and-notes.md +39 -0
  256. package/docs/getting-started.md +56 -0
  257. package/docs/independent-needs.md +77 -0
  258. package/docs/integration-methods.md +60 -0
  259. package/docs/profiles.md +68 -0
  260. package/docs/reference.md +64 -0
  261. package/docs/rest-client.md +97 -0
  262. package/docs/services.md +66 -0
  263. package/docs/shelter-inventory.md +64 -0
  264. package/docs/shelters.md +75 -0
  265. package/docs/troubleshooting.md +61 -0
  266. package/docs/using-the-sdk.md +203 -0
  267. package/docs/vendor-issues/README.md +46 -0
  268. package/docs/vendor-issues/api-coverage.md +27 -0
  269. package/docs/vendor-issues/attachment-authentication.md +40 -0
  270. package/docs/vendor-issues/date-semantics.md +51 -0
  271. package/docs/vendor-issues/empty-service-list-404.md +51 -0
  272. package/docs/vendor-issues/enrollment-complete-target-error.md +178 -0
  273. package/docs/vendor-issues/enrollment-effective-date-column.md +112 -0
  274. package/docs/vendor-issues/household-head-validation.md +26 -0
  275. package/docs/vendor-issues/write-contracts.md +51 -0
  276. package/examples/README.md +41 -0
  277. package/examples/current-living-situation.mts +27 -0
  278. package/examples/durable-records.mts +78 -0
  279. package/examples/enrollment-workflow.mts +46 -0
  280. package/examples/gwt-enrollment-session.ts +17 -0
  281. package/examples/gwt-file-store.mjs +2 -0
  282. package/examples/household-enrollment-workflow.mts +43 -0
  283. package/examples/household-exit-workflow.mts +35 -0
  284. package/examples/household-lifecycle-workflow.mts +34 -0
  285. package/examples/independent-needs.mts +30 -0
  286. package/examples/program-discovery.ts +19 -0
  287. package/examples/quickstart.mjs +13 -0
  288. package/examples/shelter-inventory.mts +30 -0
  289. package/examples/shelters.mts +33 -0
  290. package/examples/usage.ts +23 -0
  291. package/examples/verification-snapshot.ts +9 -0
  292. package/llms.txt +26 -0
  293. package/package.json +103 -0
  294. package/specs/provenance.json +17 -0
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.encodeAnswers = encodeAnswers;
4
+ exports.encodeNewSubassessmentRow = encodeNewSubassessmentRow;
5
+ const metadata_js_1 = require("./metadata.js");
6
+ const errors_js_1 = require("./errors.js");
7
+ const race_js_1 = require("./race.js");
8
+ function encodeValue(question, answer, options) {
9
+ const field = question.fieldName ?? `#${question.questionId}`;
10
+ if (["label", "blank", "sub_assessment"].includes(question.questionType))
11
+ throw new errors_js_1.ValidationError("assessment encoding", [field]);
12
+ if (question.questionType === "provider" && options.endpoint === "mobile")
13
+ throw new errors_js_1.ValidationError("mobile provider answers are unsupported", [field]);
14
+ if ("state" in answer) {
15
+ if (question.questionType !== "lookup")
16
+ return undefined;
17
+ const codes = options.stateCodes?.[answer.state];
18
+ if (!codes)
19
+ throw new errors_js_1.ValidationError("answer-state code configuration required", [field]);
20
+ const found = question.options.filter(option => option.code !== null && codes.includes(option.code));
21
+ if (!found.length)
22
+ return undefined;
23
+ if (found.length !== 1 || !found[0])
24
+ throw new errors_js_1.ValidationError("ambiguous answer-state option", [field]);
25
+ const option = question.fieldName === "HUD_RACEETHNICITY" && found[0].code !== null
26
+ ? (0, race_js_1.resolveRaceCombination)(question, [{ code: found[0].code }]) : found[0];
27
+ return lookupLabel(question, option, options);
28
+ }
29
+ if (question.questionType === "lookup") {
30
+ if (question.fieldName === "HUD_RACEETHNICITY") {
31
+ const selectors = "options" in answer ? answer.options : "option" in answer ? [answer.option]
32
+ : typeof answer.value === "string" ? [{ name: answer.value }]
33
+ : Array.isArray(answer.value) ? answer.value.map(name => ({ name })) : undefined;
34
+ if (!selectors)
35
+ throw new errors_js_1.ValidationError("race requires category selectors", [field]);
36
+ return lookupLabel(question, (0, race_js_1.resolveRaceCombination)(question, selectors), options);
37
+ }
38
+ if ("options" in answer)
39
+ throw new errors_js_1.ValidationError("multiple options require a supported combination field", [field]);
40
+ if ("option" in answer)
41
+ return lookupLabel(question, (0, metadata_js_1.resolvePicklistOption)(question, answer.option), options);
42
+ if (typeof answer.value !== "string")
43
+ throw new errors_js_1.ValidationError("lookup requires a code or name selector", [field]);
44
+ return lookupLabel(question, (0, metadata_js_1.resolvePicklistOption)(question, { name: answer.value }), options);
45
+ }
46
+ if (!("value" in answer))
47
+ throw new errors_js_1.ValidationError("picklist selector on a non-lookup question", [field]);
48
+ const value = answer.value;
49
+ if (Array.isArray(value)) {
50
+ if (!options.multiValue)
51
+ throw new errors_js_1.ValidationError("multi-value encoding requires an explicit encoder", [field]);
52
+ return options.multiValue(question, value);
53
+ }
54
+ switch (question.questionType) {
55
+ case "yes_no":
56
+ if (value === true || value === "Yes")
57
+ return "Yes";
58
+ if (value === false || value === "No")
59
+ return "No";
60
+ throw new errors_js_1.ValidationError("yes_no encoding", [field]);
61
+ case "int":
62
+ case "provider":
63
+ if ((typeof value !== "number" && typeof value !== "string") || value === "" || !Number.isSafeInteger(Number(value)))
64
+ throw new errors_js_1.ValidationError("integer encoding", [field]);
65
+ return String(value);
66
+ case "money":
67
+ if ((typeof value !== "number" && typeof value !== "string") || value === "" || !Number.isFinite(Number(value)))
68
+ throw new errors_js_1.ValidationError("money encoding", [field]);
69
+ return String(value);
70
+ case "date":
71
+ case "textbox":
72
+ case "textarea": return String(value);
73
+ default: throw new errors_js_1.ValidationError("unsupported question type", [field]);
74
+ }
75
+ }
76
+ function lookupLabel(question, option, options) {
77
+ if (options.endpoint === "mobile")
78
+ return String(option.id);
79
+ const label = options.writeLabel?.(question, option);
80
+ if (!label)
81
+ throw new errors_js_1.ValidationError("verified lookup write label required", [question.fieldName ?? `#${question.questionId}`]);
82
+ return label;
83
+ }
84
+ function encodeAnswers(questions, answers, options) {
85
+ const index = new Map();
86
+ for (const question of questions)
87
+ if (question.fieldName) {
88
+ if (index.has(question.fieldName))
89
+ throw new errors_js_1.ValidationError("ambiguous question field", [question.fieldName]);
90
+ index.set(question.fieldName, question);
91
+ }
92
+ const unknown = Object.keys(answers).filter(field => !index.has(field));
93
+ if (unknown.length)
94
+ throw new errors_js_1.ValidationError("unknown assessment fields", unknown);
95
+ const result = [];
96
+ for (const [field, answer] of Object.entries(answers)) {
97
+ const question = index.get(field);
98
+ if (!question)
99
+ throw new errors_js_1.ValidationError("unknown assessment fields", [field]);
100
+ const value = encodeValue(question, answer, options);
101
+ if (value !== undefined)
102
+ result.push({ question_id: question.questionId, question_type: question.questionType, virt_field_name: field, answer_value: value });
103
+ }
104
+ return result;
105
+ }
106
+ /** Builds one new row only. Never accepts or echoes an existing recordset ID. */
107
+ function encodeNewSubassessmentRow(parent, answers, options) {
108
+ if (parent.questionType !== "sub_assessment")
109
+ throw new errors_js_1.ValidationError("subassessment parent required");
110
+ const encoded = encodeAnswers(parent.children, answers, options);
111
+ const included = new Set(encoded.map(q => q.virt_field_name));
112
+ for (const question of parent.children) {
113
+ if (!question.fieldName || included.has(question.fieldName) || ["label", "blank", "yes_no"].includes(question.questionType))
114
+ continue;
115
+ if (question.questionType === "provider" && options.endpoint === "mobile")
116
+ throw new errors_js_1.ValidationError("mobile provider answers are unsupported", [question.fieldName]);
117
+ encoded.push({ question_id: question.questionId, question_type: question.questionType, virt_field_name: question.fieldName, answer_value: "" });
118
+ }
119
+ return { subAssessments: encoded };
120
+ }
121
+ //# sourceMappingURL=encoding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.js","sourceRoot":"","sources":["../../src/encoding.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PicklistOption, PicklistSelector, QuestionDefinition } from \"./metadata.js\";\nimport { resolvePicklistOption } from \"./metadata.js\";\nimport { ValidationError } from \"./errors.js\";\nimport { resolveRaceCombination } from \"./race.js\";\n\nexport type AnswerState = \"client_doesnt_know\" | \"client_refused\" | \"data_not_collected\";\nexport type AnswerValue = { value: string | number | boolean | string[] } | { state: AnswerState } | { option: PicklistSelector } | { options: readonly PicklistSelector[] };\nexport type Answers = Record<string, AnswerValue>;\nexport interface EncodingOptions {\n endpoint: \"standalone\" | \"enrollment\" | \"mobile\";\n /** Tenant-verified display labels, including exact whitespace/HUD suffixes, for label-based endpoints. */\n writeLabel?: (question: QuestionDefinition, option: PicklistOption) => string | undefined;\n /** Application configuration supplies the relevant option codes. No answer-state labels are guessed. */\n stateCodes?: Partial<Record<AnswerState, readonly string[]>>;\n multiValue?: (question: QuestionDefinition, values: readonly string[]) => string;\n}\nexport interface EncodedQuestion {\n question_id: number;\n question_type: string;\n virt_field_name: string;\n answer_value: string;\n}\n\nfunction encodeValue(question: QuestionDefinition, answer: AnswerValue, options: EncodingOptions): string | undefined {\n const field = question.fieldName ?? `#${question.questionId}`;\n if ([\"label\", \"blank\", \"sub_assessment\"].includes(question.questionType)) throw new ValidationError(\"assessment encoding\", [field]);\n if (question.questionType === \"provider\" && options.endpoint === \"mobile\") throw new ValidationError(\"mobile provider answers are unsupported\", [field]);\n if (\"state\" in answer) {\n if (question.questionType !== \"lookup\") return undefined;\n const codes = options.stateCodes?.[answer.state];\n if (!codes) throw new ValidationError(\"answer-state code configuration required\", [field]);\n const found = question.options.filter(option => option.code !== null && codes.includes(option.code));\n if (!found.length) return undefined;\n if (found.length !== 1 || !found[0]) throw new ValidationError(\"ambiguous answer-state option\", [field]);\n const option = question.fieldName === \"HUD_RACEETHNICITY\" && found[0].code !== null\n ? resolveRaceCombination(question, [{ code: found[0].code }]) : found[0];\n return lookupLabel(question, option, options);\n }\n if (question.questionType === \"lookup\") {\n if (question.fieldName === \"HUD_RACEETHNICITY\") {\n const selectors = \"options\" in answer ? answer.options : \"option\" in answer ? [answer.option]\n : typeof answer.value === \"string\" ? [{ name: answer.value }]\n : Array.isArray(answer.value) ? answer.value.map(name => ({ name })) : undefined;\n if (!selectors) throw new ValidationError(\"race requires category selectors\", [field]);\n return lookupLabel(question, resolveRaceCombination(question, selectors), options);\n }\n if (\"options\" in answer) throw new ValidationError(\"multiple options require a supported combination field\", [field]);\n if (\"option\" in answer) return lookupLabel(question, resolvePicklistOption(question, answer.option), options);\n if (typeof answer.value !== \"string\") throw new ValidationError(\"lookup requires a code or name selector\", [field]);\n return lookupLabel(question, resolvePicklistOption(question, { name: answer.value }), options);\n }\n if (!(\"value\" in answer)) throw new ValidationError(\"picklist selector on a non-lookup question\", [field]);\n const value = answer.value;\n if (Array.isArray(value)) {\n if (!options.multiValue) throw new ValidationError(\"multi-value encoding requires an explicit encoder\", [field]);\n return options.multiValue(question, value);\n }\n switch (question.questionType) {\n case \"yes_no\":\n if (value === true || value === \"Yes\") return \"Yes\";\n if (value === false || value === \"No\") return \"No\";\n throw new ValidationError(\"yes_no encoding\", [field]);\n case \"int\": case \"provider\":\n if ((typeof value !== \"number\" && typeof value !== \"string\") || value === \"\" || !Number.isSafeInteger(Number(value))) throw new ValidationError(\"integer encoding\", [field]);\n return String(value);\n case \"money\":\n if ((typeof value !== \"number\" && typeof value !== \"string\") || value === \"\" || !Number.isFinite(Number(value))) throw new ValidationError(\"money encoding\", [field]);\n return String(value);\n case \"date\": case \"textbox\": case \"textarea\": return String(value);\n default: throw new ValidationError(\"unsupported question type\", [field]);\n }\n}\n\nfunction lookupLabel(question: QuestionDefinition, option: PicklistOption, options: EncodingOptions): string {\n if (options.endpoint === \"mobile\") return String(option.id);\n const label = options.writeLabel?.(question, option);\n if (!label) throw new ValidationError(\"verified lookup write label required\", [question.fieldName ?? `#${question.questionId}`]);\n return label;\n}\n\nexport function encodeAnswers(questions: readonly QuestionDefinition[], answers: Answers, options: EncodingOptions): EncodedQuestion[] {\n const index = new Map<string, QuestionDefinition>();\n for (const question of questions) if (question.fieldName) {\n if (index.has(question.fieldName)) throw new ValidationError(\"ambiguous question field\", [question.fieldName]);\n index.set(question.fieldName, question);\n }\n const unknown = Object.keys(answers).filter(field => !index.has(field));\n if (unknown.length) throw new ValidationError(\"unknown assessment fields\", unknown);\n const result: EncodedQuestion[] = [];\n for (const [field, answer] of Object.entries(answers)) {\n const question = index.get(field);\n if (!question) throw new ValidationError(\"unknown assessment fields\", [field]);\n const value = encodeValue(question, answer, options);\n if (value !== undefined) result.push({ question_id: question.questionId, question_type: question.questionType, virt_field_name: field, answer_value: value });\n }\n return result;\n}\n\n/** Builds one new row only. Never accepts or echoes an existing recordset ID. */\nexport function encodeNewSubassessmentRow(parent: QuestionDefinition, answers: Answers, options: EncodingOptions): { subAssessments: EncodedQuestion[] } {\n if (parent.questionType !== \"sub_assessment\") throw new ValidationError(\"subassessment parent required\");\n const encoded = encodeAnswers(parent.children, answers, options);\n const included = new Set(encoded.map(q => q.virt_field_name));\n for (const question of parent.children) {\n if (!question.fieldName || included.has(question.fieldName) || [\"label\", \"blank\", \"yes_no\"].includes(question.questionType)) continue;\n if (question.questionType === \"provider\" && options.endpoint === \"mobile\") throw new ValidationError(\"mobile provider answers are unsupported\", [question.fieldName]);\n encoded.push({ question_id: question.questionId, question_type: question.questionType, virt_field_name: question.fieldName, answer_value: \"\" });\n }\n return { subAssessments: encoded };\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import type { WellSkyClient } from "./generated/v3.js";
2
+ import type { SubAssessmentAnswer } from "./generated/v3-models.js";
3
+ import type { RequestOptions } from "./runtime.js";
4
+ /** Structural interface accepted by both V2 and V3 clients. Only a GET is used. */
5
+ export interface VerificationAssessmentReader {
6
+ assessments: Pick<WellSkyClient["assessments"], "get">;
7
+ }
8
+ export interface EnrollmentVerificationRequest extends RequestOptions {
9
+ clientId: number;
10
+ /** An instrument exposing all four standard verification groups. */
11
+ assessmentCode: string;
12
+ /** Exact enrollment instant with Z or a numeric offset. No host timezone is inferred. */
13
+ enrollmentDate: string;
14
+ }
15
+ export type VerificationField = "MONTHLYINCOME" | "SVP_NONCASHBENEFITS" | "HUDHEALTHINSURANCESUBA" | "DISABILITIES_1";
16
+ export type VerificationRecord = SubAssessmentAnswer & {
17
+ recordset_id: number;
18
+ };
19
+ export interface VerificationGroupSnapshot {
20
+ fieldName: VerificationField;
21
+ records: VerificationRecord[];
22
+ excluded: {
23
+ record: VerificationRecord;
24
+ reason: "not_started" | "ended";
25
+ }[];
26
+ }
27
+ export interface EnrollmentVerificationSnapshot {
28
+ clientId: number;
29
+ assessmentCode: string;
30
+ enrollmentDate: string;
31
+ /** Current recorded versions filtered by row dates, not an answer-history reconstruction. */
32
+ source: "current-assessment";
33
+ groups: VerificationGroupSnapshot[];
34
+ }
35
+ /** Read current recorded verification rows and select start <= enrollment <= end.
36
+ * Never writes, changes dates, applies a seven-hour correction, or treats isActive as a date filter.
37
+ * Missing groups, ambiguous rows and invalid/zoneless dates fail rather than imply no records. */
38
+ export declare function readEnrollmentVerifications(client: VerificationAssessmentReader, input: EnrollmentVerificationRequest): Promise<EnrollmentVerificationSnapshot>;
39
+ //# sourceMappingURL=enrollment-verifications.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrollment-verifications.d.ts","sourceRoot":"","sources":["../../src/enrollment-verifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,mFAAmF;AACnF,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,KAAK,CAAC,CAAC;CACxD;AACD,MAAM,WAAW,6BAA8B,SAAQ,cAAc;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,cAAc,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,cAAc,EAAE,MAAM,CAAC;CACxB;AACD,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG,qBAAqB,GAAG,wBAAwB,GAAG,gBAAgB,CAAC;AACtH,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,GAAG;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAChF,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,QAAQ,EAAE;QAAE,MAAM,EAAE,kBAAkB,CAAC;QAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAA;KAAE,EAAE,CAAC;CAC7E;AACD,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,EAAE,yBAAyB,EAAE,CAAC;CACrC;AAsCD;;kGAEkG;AAClG,wBAAsB,2BAA2B,CAAC,MAAM,EAAE,4BAA4B,EAAE,KAAK,EAAE,6BAA6B,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAoCrK"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readEnrollmentVerifications = readEnrollmentVerifications;
4
+ const errors_js_1 = require("./errors.js");
5
+ const runtime_js_1 = require("./runtime.js");
6
+ const fields = [
7
+ { field: "MONTHLYINCOME", start: "MONTHLYINCOMESTART", end: "MONTHLYINCOMEEND" },
8
+ { field: "SVP_NONCASHBENEFITS", start: "SVP_NONCASHBENEFITSSTART", end: "SVP_NONCASHBENEFITSEND" },
9
+ { field: "HUDHEALTHINSURANCESUBA", start: "HUDHEALTHINSURANCESUBASTART", end: "HUDHEALTHINSURANCESUBAEND" },
10
+ { field: "DISABILITIES_1", start: "DISABILITIES_1START", end: "DISABILITIES_1END" },
11
+ ];
12
+ /** Parses ISO and WellSky's offset-bearing SQL date strings without local-time conversion.
13
+ * Nanoseconds preserve fractional boundaries instead of rounding a future row into the snapshot. */
14
+ function verificationInstant(value) {
15
+ if (typeof value !== "string")
16
+ return undefined;
17
+ const parts = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?(Z|[+-]\d{2}(?::?\d{2})?)$/.exec(value);
18
+ if (!parts)
19
+ return undefined;
20
+ const [, calendar, hour, minute, second, fraction = "", zone] = parts;
21
+ if (!calendar || !zone || Number(hour) > 23 || Number(minute) > 59 || Number(second) > 59)
22
+ return undefined;
23
+ const day = new Date(`${calendar}T00:00:00Z`);
24
+ if (!Number.isFinite(day.getTime()) || day.toISOString().slice(0, 10) !== calendar)
25
+ return undefined;
26
+ const offset = zone.slice(1).replace(":", "");
27
+ const offsetHours = zone === "Z" ? 0 : Number(offset.slice(0, 2));
28
+ const offsetMinutes = zone === "Z" ? 0 : Number(offset.slice(2) || "0");
29
+ if (offsetHours > 14 || offsetMinutes > 59 || (offsetHours === 14 && offsetMinutes !== 0))
30
+ return undefined;
31
+ const signedOffset = (zone.startsWith("-") ? -1 : 1) * (offsetHours * 60 + offsetMinutes);
32
+ const millis = day.getTime() + (Number(hour) * 3600 + Number(minute) * 60 + Number(second) - signedOffset * 60) * 1000;
33
+ return BigInt(millis) * 1000000n + BigInt(fraction.padEnd(9, "0"));
34
+ }
35
+ function recordDate(record, field) {
36
+ const matches = record.sub_assessment_list?.filter(q => q.virt_field_name === field) ?? [];
37
+ const question = matches[0];
38
+ if (matches.length !== 1 || !question || question.question_type !== "date")
39
+ throw new runtime_js_1.WellSkyError("protocol", `verification date field ${field}`);
40
+ const value = question.answer_value;
41
+ if (value === null || value === undefined || value === "")
42
+ return null;
43
+ if (typeof value !== "string")
44
+ throw new runtime_js_1.WellSkyError("protocol", `verification date field ${field}`);
45
+ return value;
46
+ }
47
+ /** Read current recorded verification rows and select start <= enrollment <= end.
48
+ * Never writes, changes dates, applies a seven-hour correction, or treats isActive as a date filter.
49
+ * Missing groups, ambiguous rows and invalid/zoneless dates fail rather than imply no records. */
50
+ async function readEnrollmentVerifications(client, input) {
51
+ const operation = "read enrollment verifications";
52
+ const at = verificationInstant(input.enrollmentDate);
53
+ if (at === undefined)
54
+ throw new errors_js_1.ValidationError(operation, ["enrollmentDate"]);
55
+ if (!Number.isSafeInteger(input.clientId) || input.clientId < 1)
56
+ throw new errors_js_1.ValidationError(operation, ["clientId"]);
57
+ if (typeof input.assessmentCode !== "string" || !input.assessmentCode.trim())
58
+ throw new errors_js_1.ValidationError(operation, ["assessmentCode"]);
59
+ // Do not send dateEffective: it can suppress backdated rows needed for this projection.
60
+ const response = await client.assessments.get({
61
+ path: { clientId: input.clientId, assessmentCode: input.assessmentCode },
62
+ ...(input.signal === undefined ? {} : { signal: input.signal }),
63
+ ...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
64
+ ...(input.correlationId === undefined ? {} : { correlationId: input.correlationId }),
65
+ });
66
+ const questions = (0, runtime_js_1.toItems)(response);
67
+ const groups = [];
68
+ const seenIds = new Set();
69
+ for (const { field, start, end } of fields) {
70
+ const matches = questions.filter(q => q.virt_field_name === field);
71
+ const parent = matches[0];
72
+ if (matches.length !== 1 || !parent || parent.question_type !== "sub_assessment" || !Array.isArray(parent.sub_assessment_answer_list))
73
+ throw new runtime_js_1.WellSkyError("protocol", `verification group ${field}`);
74
+ const group = { fieldName: field, records: [], excluded: [] };
75
+ for (const raw of parent.sub_assessment_answer_list) {
76
+ const id = raw.recordset_id;
77
+ if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 1 || seenIds.has(id))
78
+ throw new runtime_js_1.WellSkyError("protocol", "verification record identity");
79
+ seenIds.add(id);
80
+ const record = { ...structuredClone(raw), recordset_id: id };
81
+ const startValue = recordDate(record, start), endValue = recordDate(record, end);
82
+ const from = verificationInstant(startValue), through = endValue === null ? null : verificationInstant(endValue);
83
+ if (from === undefined || through === undefined || (through !== null && through < from))
84
+ throw new runtime_js_1.WellSkyError("protocol", `verification interval ${field}`);
85
+ if (from > at)
86
+ group.excluded.push({ record, reason: "not_started" });
87
+ else if (through !== null && through < at)
88
+ group.excluded.push({ record, reason: "ended" });
89
+ else
90
+ group.records.push(record);
91
+ }
92
+ groups.push(group);
93
+ }
94
+ return { clientId: input.clientId, assessmentCode: input.assessmentCode, enrollmentDate: input.enrollmentDate, source: "current-assessment", groups };
95
+ }
96
+ //# sourceMappingURL=enrollment-verifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrollment-verifications.js","sourceRoot":"","sources":["../../src/enrollment-verifications.ts"],"names":[],"mappings":"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","sourcesContent":["import type { WellSkyClient } from \"./generated/v3.js\";\nimport type { SubAssessmentAnswer } from \"./generated/v3-models.js\";\nimport { ValidationError } from \"./errors.js\";\nimport { toItems, WellSkyError } from \"./runtime.js\";\nimport type { RequestOptions } from \"./runtime.js\";\n\n/** Structural interface accepted by both V2 and V3 clients. Only a GET is used. */\nexport interface VerificationAssessmentReader {\n assessments: Pick<WellSkyClient[\"assessments\"], \"get\">;\n}\nexport interface EnrollmentVerificationRequest extends RequestOptions {\n clientId: number;\n /** An instrument exposing all four standard verification groups. */\n assessmentCode: string;\n /** Exact enrollment instant with Z or a numeric offset. No host timezone is inferred. */\n enrollmentDate: string;\n}\nexport type VerificationField = \"MONTHLYINCOME\" | \"SVP_NONCASHBENEFITS\" | \"HUDHEALTHINSURANCESUBA\" | \"DISABILITIES_1\";\nexport type VerificationRecord = SubAssessmentAnswer & { recordset_id: number };\nexport interface VerificationGroupSnapshot {\n fieldName: VerificationField;\n records: VerificationRecord[];\n excluded: { record: VerificationRecord; reason: \"not_started\" | \"ended\" }[];\n}\nexport interface EnrollmentVerificationSnapshot {\n clientId: number;\n assessmentCode: string;\n enrollmentDate: string;\n /** Current recorded versions filtered by row dates, not an answer-history reconstruction. */\n source: \"current-assessment\";\n groups: VerificationGroupSnapshot[];\n}\n\nconst fields: { field: VerificationField; start: string; end: string }[] = [\n { field: \"MONTHLYINCOME\", start: \"MONTHLYINCOMESTART\", end: \"MONTHLYINCOMEEND\" },\n { field: \"SVP_NONCASHBENEFITS\", start: \"SVP_NONCASHBENEFITSSTART\", end: \"SVP_NONCASHBENEFITSEND\" },\n { field: \"HUDHEALTHINSURANCESUBA\", start: \"HUDHEALTHINSURANCESUBASTART\", end: \"HUDHEALTHINSURANCESUBAEND\" },\n { field: \"DISABILITIES_1\", start: \"DISABILITIES_1START\", end: \"DISABILITIES_1END\" },\n];\n\n/** Parses ISO and WellSky's offset-bearing SQL date strings without local-time conversion.\n * Nanoseconds preserve fractional boundaries instead of rounding a future row into the snapshot. */\nfunction verificationInstant(value: unknown): bigint | undefined {\n if (typeof value !== \"string\") return undefined;\n const parts = /^(\\d{4}-\\d{2}-\\d{2})[T ](\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,9}))?(Z|[+-]\\d{2}(?::?\\d{2})?)$/.exec(value);\n if (!parts) return undefined;\n const [, calendar, hour, minute, second, fraction = \"\", zone] = parts;\n if (!calendar || !zone || Number(hour) > 23 || Number(minute) > 59 || Number(second) > 59) return undefined;\n const day = new Date(`${calendar}T00:00:00Z`);\n if (!Number.isFinite(day.getTime()) || day.toISOString().slice(0, 10) !== calendar) return undefined;\n const offset = zone.slice(1).replace(\":\", \"\");\n const offsetHours = zone === \"Z\" ? 0 : Number(offset.slice(0, 2));\n const offsetMinutes = zone === \"Z\" ? 0 : Number(offset.slice(2) || \"0\");\n if (offsetHours > 14 || offsetMinutes > 59 || (offsetHours === 14 && offsetMinutes !== 0)) return undefined;\n const signedOffset = (zone.startsWith(\"-\") ? -1 : 1) * (offsetHours * 60 + offsetMinutes);\n const millis = day.getTime() + (Number(hour) * 3600 + Number(minute) * 60 + Number(second) - signedOffset * 60) * 1000;\n return BigInt(millis) * 1_000_000n + BigInt(fraction.padEnd(9, \"0\"));\n}\n\nfunction recordDate(record: VerificationRecord, field: string): string | null {\n const matches = record.sub_assessment_list?.filter(q => q.virt_field_name === field) ?? [];\n const question = matches[0];\n if (matches.length !== 1 || !question || question.question_type !== \"date\") throw new WellSkyError(\"protocol\", `verification date field ${field}`);\n const value = question.answer_value;\n if (value === null || value === undefined || value === \"\") return null;\n if (typeof value !== \"string\") throw new WellSkyError(\"protocol\", `verification date field ${field}`);\n return value;\n}\n\n/** Read current recorded verification rows and select start <= enrollment <= end.\n * Never writes, changes dates, applies a seven-hour correction, or treats isActive as a date filter.\n * Missing groups, ambiguous rows and invalid/zoneless dates fail rather than imply no records. */\nexport async function readEnrollmentVerifications(client: VerificationAssessmentReader, input: EnrollmentVerificationRequest): Promise<EnrollmentVerificationSnapshot> {\n const operation = \"read enrollment verifications\";\n const at = verificationInstant(input.enrollmentDate);\n if (at === undefined) throw new ValidationError(operation, [\"enrollmentDate\"]);\n if (!Number.isSafeInteger(input.clientId) || input.clientId < 1) throw new ValidationError(operation, [\"clientId\"]);\n if (typeof input.assessmentCode !== \"string\" || !input.assessmentCode.trim()) throw new ValidationError(operation, [\"assessmentCode\"]);\n // Do not send dateEffective: it can suppress backdated rows needed for this projection.\n const response = await client.assessments.get({\n path: { clientId: input.clientId, assessmentCode: input.assessmentCode },\n ...(input.signal === undefined ? {} : { signal: input.signal }),\n ...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),\n ...(input.correlationId === undefined ? {} : { correlationId: input.correlationId }),\n });\n const questions = toItems(response);\n const groups: VerificationGroupSnapshot[] = [];\n const seenIds = new Set<number>();\n for (const { field, start, end } of fields) {\n const matches = questions.filter(q => q.virt_field_name === field);\n const parent = matches[0];\n if (matches.length !== 1 || !parent || parent.question_type !== \"sub_assessment\" || !Array.isArray(parent.sub_assessment_answer_list)) throw new WellSkyError(\"protocol\", `verification group ${field}`);\n const group: VerificationGroupSnapshot = { fieldName: field, records: [], excluded: [] };\n for (const raw of parent.sub_assessment_answer_list) {\n const id = raw.recordset_id;\n if (typeof id !== \"number\" || !Number.isSafeInteger(id) || id < 1 || seenIds.has(id)) throw new WellSkyError(\"protocol\", \"verification record identity\");\n seenIds.add(id);\n const record: VerificationRecord = { ...structuredClone(raw), recordset_id: id };\n const startValue = recordDate(record, start), endValue = recordDate(record, end);\n const from = verificationInstant(startValue), through = endValue === null ? null : verificationInstant(endValue);\n if (from === undefined || through === undefined || (through !== null && through < from)) throw new WellSkyError(\"protocol\", `verification interval ${field}`);\n if (from > at) group.excluded.push({ record, reason: \"not_started\" });\n else if (through !== null && through < at) group.excluded.push({ record, reason: \"ended\" });\n else group.records.push(record);\n }\n groups.push(group);\n }\n return { clientId: input.clientId, assessmentCode: input.assessmentCode, enrollmentDate: input.enrollmentDate, source: \"current-assessment\", groups };\n}\n"]}
@@ -0,0 +1,33 @@
1
+ import { WellSkyError } from "./runtime.js";
2
+ export declare class TenantMismatchError extends WellSkyError {
3
+ constructor(operation: string);
4
+ }
5
+ export declare class AuthError extends WellSkyError {
6
+ constructor(operation: string, status?: number);
7
+ }
8
+ export declare class ThrottledError extends WellSkyError {
9
+ constructor(operation: string, retryAfterMs: number, status?: number, requestId?: string);
10
+ }
11
+ export declare class CooldownError extends WellSkyError {
12
+ readonly until: string;
13
+ constructor(operation: string, until: number);
14
+ }
15
+ export declare class ValidationError extends WellSkyError {
16
+ readonly fields: readonly string[];
17
+ constructor(operation: string, fields?: readonly string[]);
18
+ }
19
+ export declare class UpstreamError extends WellSkyError {
20
+ constructor(operation: string, status: number, requestId?: string, retryAfterMs?: number);
21
+ }
22
+ export declare class AmbiguousCommitError extends WellSkyError {
23
+ constructor(operation: string);
24
+ }
25
+ export declare class DefinitionDriftError extends WellSkyError {
26
+ readonly fields: readonly string[];
27
+ constructor(fields: readonly string[]);
28
+ }
29
+ export declare class UnknownPicklistValueError extends WellSkyError {
30
+ readonly field: string;
31
+ constructor(field: string);
32
+ }
33
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,qBAAa,mBAAoB,SAAQ,YAAY;gBACvC,SAAS,EAAE,MAAM;CAC9B;AACD,qBAAa,SAAU,SAAQ,YAAY;gBAC7B,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAC/C;AACD,qBAAa,cAAe,SAAQ,YAAY;gBAClC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CACtF;AACD,qBAAa,aAAc,SAAQ,YAAY;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACX,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAC7C;AACD,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;gBACvB,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,SAAS,MAAM,EAAO;CAC9D;AACD,qBAAa,aAAc,SAAQ,YAAY;gBACjC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM;CACzF;AACD,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,SAAS,EAAE,MAAM;CAC9B;AACD,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;gBACvB,MAAM,EAAE,SAAS,MAAM,EAAE;CACtC;AACD,qBAAa,yBAA0B,SAAQ,YAAY;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBACX,KAAK,EAAE,MAAM;CAC1B"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnknownPicklistValueError = exports.DefinitionDriftError = exports.AmbiguousCommitError = exports.UpstreamError = exports.ValidationError = exports.CooldownError = exports.ThrottledError = exports.AuthError = exports.TenantMismatchError = void 0;
4
+ const runtime_js_1 = require("./runtime.js");
5
+ class TenantMismatchError extends runtime_js_1.WellSkyError {
6
+ constructor(operation) { super("tenant_mismatch", operation); this.name = "TenantMismatchError"; }
7
+ }
8
+ exports.TenantMismatchError = TenantMismatchError;
9
+ class AuthError extends runtime_js_1.WellSkyError {
10
+ constructor(operation, status) { super("auth", operation, status === undefined ? {} : { status }); this.name = "AuthError"; }
11
+ }
12
+ exports.AuthError = AuthError;
13
+ class ThrottledError extends runtime_js_1.WellSkyError {
14
+ constructor(operation, retryAfterMs, status = 429, requestId) { super("throttled", operation, { status, retryAfterMs, ...(requestId === undefined ? {} : { requestId }) }); this.name = "ThrottledError"; }
15
+ }
16
+ exports.ThrottledError = ThrottledError;
17
+ class CooldownError extends runtime_js_1.WellSkyError {
18
+ until;
19
+ constructor(operation, until) { super("cooldown", operation); this.name = "CooldownError"; this.until = new Date(until).toISOString(); }
20
+ }
21
+ exports.CooldownError = CooldownError;
22
+ class ValidationError extends runtime_js_1.WellSkyError {
23
+ fields;
24
+ constructor(operation, fields = []) { super("validation", operation); this.name = "ValidationError"; this.fields = [...fields]; }
25
+ }
26
+ exports.ValidationError = ValidationError;
27
+ class UpstreamError extends runtime_js_1.WellSkyError {
28
+ constructor(operation, status, requestId, retryAfterMs) { super("http", operation, { status, ...(requestId === undefined ? {} : { requestId }), ...(retryAfterMs === undefined ? {} : { retryAfterMs }) }); this.name = "UpstreamError"; }
29
+ }
30
+ exports.UpstreamError = UpstreamError;
31
+ class AmbiguousCommitError extends runtime_js_1.WellSkyError {
32
+ constructor(operation) { super("ambiguous_commit", operation); this.name = "AmbiguousCommitError"; }
33
+ }
34
+ exports.AmbiguousCommitError = AmbiguousCommitError;
35
+ class DefinitionDriftError extends runtime_js_1.WellSkyError {
36
+ fields;
37
+ constructor(fields) { super("definition_drift", "assessment metadata"); this.name = "DefinitionDriftError"; this.fields = [...fields]; }
38
+ }
39
+ exports.DefinitionDriftError = DefinitionDriftError;
40
+ class UnknownPicklistValueError extends runtime_js_1.WellSkyError {
41
+ field;
42
+ constructor(field) { super("unknown_picklist_value", "picklist resolution"); this.name = "UnknownPicklistValueError"; this.field = field; }
43
+ }
44
+ exports.UnknownPicklistValueError = UnknownPicklistValueError;
45
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":";;;AAAA,6CAA4C;AAE5C,MAAa,mBAAoB,SAAQ,yBAAY;IACnD,YAAY,SAAiB,IAAI,KAAK,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC,CAAC,CAAC;CAC3G;AAFD,kDAEC;AACD,MAAa,SAAU,SAAQ,yBAAY;IACzC,YAAY,SAAiB,EAAE,MAAe,IAAI,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC;CAC/I;AAFD,8BAEC;AACD,MAAa,cAAe,SAAQ,yBAAY;IAC9C,YAAY,SAAiB,EAAE,YAAoB,EAAE,MAAM,GAAG,GAAG,EAAE,SAAkB,IAAI,KAAK,CAAC,WAAW,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC,CAAC,CAAC;CACrO;AAFD,wCAEC;AACD,MAAa,aAAc,SAAQ,yBAAY;IACpC,KAAK,CAAS;IACvB,YAAY,SAAiB,EAAE,KAAa,IAAI,KAAK,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;CACzJ;AAHD,sCAGC;AACD,MAAa,eAAgB,SAAQ,yBAAY;IACtC,MAAM,CAAoB;IACnC,YAAY,SAAiB,EAAE,SAA4B,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;CAC7J;AAHD,0CAGC;AACD,MAAa,aAAc,SAAQ,yBAAY;IAC7C,YAAY,SAAiB,EAAE,MAAc,EAAE,SAAkB,EAAE,YAAqB,IAAI,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC,CAAC,CAAC;CAC7Q;AAFD,sCAEC;AACD,MAAa,oBAAqB,SAAQ,yBAAY;IACpD,YAAY,SAAiB,IAAI,KAAK,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC,CAAC,CAAC;CAC7G;AAFD,oDAEC;AACD,MAAa,oBAAqB,SAAQ,yBAAY;IAC3C,MAAM,CAAoB;IACnC,YAAY,MAAyB,IAAI,KAAK,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;CAC5J;AAHD,oDAGC;AACD,MAAa,yBAA0B,SAAQ,yBAAY;IAChD,KAAK,CAAS;IACvB,YAAY,KAAa,IAAI,KAAK,CAAC,wBAAwB,EAAE,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC;CACpJ;AAHD,8DAGC","sourcesContent":["import { WellSkyError } from \"./runtime.js\";\n\nexport class TenantMismatchError extends WellSkyError {\n constructor(operation: string) { super(\"tenant_mismatch\", operation); this.name = \"TenantMismatchError\"; }\n}\nexport class AuthError extends WellSkyError {\n constructor(operation: string, status?: number) { super(\"auth\", operation, status === undefined ? {} : { status }); this.name = \"AuthError\"; }\n}\nexport class ThrottledError extends WellSkyError {\n constructor(operation: string, retryAfterMs: number, status = 429, requestId?: string) { super(\"throttled\", operation, { status, retryAfterMs, ...(requestId === undefined ? {} : { requestId }) }); this.name = \"ThrottledError\"; }\n}\nexport class CooldownError extends WellSkyError {\n readonly until: string;\n constructor(operation: string, until: number) { super(\"cooldown\", operation); this.name = \"CooldownError\"; this.until = new Date(until).toISOString(); }\n}\nexport class ValidationError extends WellSkyError {\n readonly fields: readonly string[];\n constructor(operation: string, fields: readonly string[] = []) { super(\"validation\", operation); this.name = \"ValidationError\"; this.fields = [...fields]; }\n}\nexport class UpstreamError extends WellSkyError {\n constructor(operation: string, status: number, requestId?: string, retryAfterMs?: number) { super(\"http\", operation, { status, ...(requestId === undefined ? {} : { requestId }), ...(retryAfterMs === undefined ? {} : { retryAfterMs }) }); this.name = \"UpstreamError\"; }\n}\nexport class AmbiguousCommitError extends WellSkyError {\n constructor(operation: string) { super(\"ambiguous_commit\", operation); this.name = \"AmbiguousCommitError\"; }\n}\nexport class DefinitionDriftError extends WellSkyError {\n readonly fields: readonly string[];\n constructor(fields: readonly string[]) { super(\"definition_drift\", \"assessment metadata\"); this.name = \"DefinitionDriftError\"; this.fields = [...fields]; }\n}\nexport class UnknownPicklistValueError extends WellSkyError {\n readonly field: string;\n constructor(field: string) { super(\"unknown_picklist_value\", \"picklist resolution\"); this.name = \"UnknownPicklistValueError\"; this.field = field; }\n}\n"]}