@tiangong-ai/cli 0.0.50 → 0.0.55

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 (218) hide show
  1. package/AGENTS.md +22 -2
  2. package/README.md +200 -7
  3. package/dist/cli.js +10 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/data/builtins.d.ts +1 -0
  6. package/dist/data/builtins.js +40 -0
  7. package/dist/data/builtins.js.map +1 -0
  8. package/dist/data/catalog.d.ts +25 -0
  9. package/dist/data/catalog.js +305 -0
  10. package/dist/data/catalog.js.map +1 -0
  11. package/dist/data/commands.d.ts +9 -0
  12. package/dist/data/commands.js +307 -0
  13. package/dist/data/commands.js.map +1 -0
  14. package/dist/data/connectors/airnow-hourly-observations.d.ts +2 -0
  15. package/dist/data/connectors/airnow-hourly-observations.js +522 -0
  16. package/dist/data/connectors/airnow-hourly-observations.js.map +1 -0
  17. package/dist/data/connectors/airnow-hourly-observations.schemas.d.ts +299 -0
  18. package/dist/data/connectors/airnow-hourly-observations.schemas.js +188 -0
  19. package/dist/data/connectors/airnow-hourly-observations.schemas.js.map +1 -0
  20. package/dist/data/connectors/bluesky-public-posts.d.ts +2 -0
  21. package/dist/data/connectors/bluesky-public-posts.js +705 -0
  22. package/dist/data/connectors/bluesky-public-posts.js.map +1 -0
  23. package/dist/data/connectors/bluesky-public-posts.schemas.d.ts +733 -0
  24. package/dist/data/connectors/bluesky-public-posts.schemas.js +422 -0
  25. package/dist/data/connectors/bluesky-public-posts.schemas.js.map +1 -0
  26. package/dist/data/connectors/epa-eis-records.d.ts +2 -0
  27. package/dist/data/connectors/epa-eis-records.js +566 -0
  28. package/dist/data/connectors/epa-eis-records.js.map +1 -0
  29. package/dist/data/connectors/epa-eis-records.schemas.d.ts +199 -0
  30. package/dist/data/connectors/epa-eis-records.schemas.js +146 -0
  31. package/dist/data/connectors/epa-eis-records.schemas.js.map +1 -0
  32. package/dist/data/connectors/federal-register-documents.d.ts +2 -0
  33. package/dist/data/connectors/federal-register-documents.js +517 -0
  34. package/dist/data/connectors/federal-register-documents.js.map +1 -0
  35. package/dist/data/connectors/federal-register-documents.schemas.d.ts +357 -0
  36. package/dist/data/connectors/federal-register-documents.schemas.js +243 -0
  37. package/dist/data/connectors/federal-register-documents.schemas.js.map +1 -0
  38. package/dist/data/connectors/gdelt-doc-search.d.ts +2 -0
  39. package/dist/data/connectors/gdelt-doc-search.js +658 -0
  40. package/dist/data/connectors/gdelt-doc-search.js.map +1 -0
  41. package/dist/data/connectors/gdelt-doc-search.schemas.d.ts +446 -0
  42. package/dist/data/connectors/gdelt-doc-search.schemas.js +330 -0
  43. package/dist/data/connectors/gdelt-doc-search.schemas.js.map +1 -0
  44. package/dist/data/connectors/gdelt-file-feeds.d.ts +4 -0
  45. package/dist/data/connectors/gdelt-file-feeds.js +591 -0
  46. package/dist/data/connectors/gdelt-file-feeds.js.map +1 -0
  47. package/dist/data/connectors/gdelt-file-feeds.schemas.d.ts +40 -0
  48. package/dist/data/connectors/gdelt-file-feeds.schemas.js +256 -0
  49. package/dist/data/connectors/gdelt-file-feeds.schemas.js.map +1 -0
  50. package/dist/data/connectors/nasa-firms-fire.d.ts +2 -0
  51. package/dist/data/connectors/nasa-firms-fire.js +578 -0
  52. package/dist/data/connectors/nasa-firms-fire.js.map +1 -0
  53. package/dist/data/connectors/nasa-firms-fire.schemas.d.ts +369 -0
  54. package/dist/data/connectors/nasa-firms-fire.schemas.js +238 -0
  55. package/dist/data/connectors/nasa-firms-fire.schemas.js.map +1 -0
  56. package/dist/data/connectors/open-meteo-air-quality.d.ts +2 -0
  57. package/dist/data/connectors/open-meteo-air-quality.js +401 -0
  58. package/dist/data/connectors/open-meteo-air-quality.js.map +1 -0
  59. package/dist/data/connectors/open-meteo-air-quality.schemas.d.ts +335 -0
  60. package/dist/data/connectors/open-meteo-air-quality.schemas.js +225 -0
  61. package/dist/data/connectors/open-meteo-air-quality.schemas.js.map +1 -0
  62. package/dist/data/connectors/open-meteo-flood.d.ts +2 -0
  63. package/dist/data/connectors/open-meteo-flood.js +446 -0
  64. package/dist/data/connectors/open-meteo-flood.js.map +1 -0
  65. package/dist/data/connectors/open-meteo-flood.schemas.d.ts +388 -0
  66. package/dist/data/connectors/open-meteo-flood.schemas.js +227 -0
  67. package/dist/data/connectors/open-meteo-flood.schemas.js.map +1 -0
  68. package/dist/data/connectors/open-meteo-historical-weather.d.ts +2 -0
  69. package/dist/data/connectors/open-meteo-historical-weather.js +468 -0
  70. package/dist/data/connectors/open-meteo-historical-weather.js.map +1 -0
  71. package/dist/data/connectors/open-meteo-historical-weather.schemas.d.ts +420 -0
  72. package/dist/data/connectors/open-meteo-historical-weather.schemas.js +282 -0
  73. package/dist/data/connectors/open-meteo-historical-weather.schemas.js.map +1 -0
  74. package/dist/data/connectors/openaq-air-quality.d.ts +2 -0
  75. package/dist/data/connectors/openaq-air-quality.js +734 -0
  76. package/dist/data/connectors/openaq-air-quality.js.map +1 -0
  77. package/dist/data/connectors/openaq-air-quality.schemas.d.ts +1067 -0
  78. package/dist/data/connectors/openaq-air-quality.schemas.js +512 -0
  79. package/dist/data/connectors/openaq-air-quality.schemas.js.map +1 -0
  80. package/dist/data/connectors/regulations-gov-attachments.d.ts +2 -0
  81. package/dist/data/connectors/regulations-gov-attachments.js +486 -0
  82. package/dist/data/connectors/regulations-gov-attachments.js.map +1 -0
  83. package/dist/data/connectors/regulations-gov-attachments.schemas.d.ts +270 -0
  84. package/dist/data/connectors/regulations-gov-attachments.schemas.js +181 -0
  85. package/dist/data/connectors/regulations-gov-attachments.schemas.js.map +1 -0
  86. package/dist/data/connectors/regulations-gov-comments.d.ts +2 -0
  87. package/dist/data/connectors/regulations-gov-comments.js +718 -0
  88. package/dist/data/connectors/regulations-gov-comments.js.map +1 -0
  89. package/dist/data/connectors/regulations-gov-comments.schemas.d.ts +717 -0
  90. package/dist/data/connectors/regulations-gov-comments.schemas.js +389 -0
  91. package/dist/data/connectors/regulations-gov-comments.schemas.js.map +1 -0
  92. package/dist/data/connectors/usbr-project-records.d.ts +2 -0
  93. package/dist/data/connectors/usbr-project-records.js +467 -0
  94. package/dist/data/connectors/usbr-project-records.js.map +1 -0
  95. package/dist/data/connectors/usbr-project-records.schemas.d.ts +194 -0
  96. package/dist/data/connectors/usbr-project-records.schemas.js +137 -0
  97. package/dist/data/connectors/usbr-project-records.schemas.js.map +1 -0
  98. package/dist/data/connectors/usbr-rise.d.ts +2 -0
  99. package/dist/data/connectors/usbr-rise.js +625 -0
  100. package/dist/data/connectors/usbr-rise.js.map +1 -0
  101. package/dist/data/connectors/usbr-rise.schemas.d.ts +623 -0
  102. package/dist/data/connectors/usbr-rise.schemas.js +407 -0
  103. package/dist/data/connectors/usbr-rise.schemas.js.map +1 -0
  104. package/dist/data/connectors/usgs-water-instantaneous-values.d.ts +2 -0
  105. package/dist/data/connectors/usgs-water-instantaneous-values.js +521 -0
  106. package/dist/data/connectors/usgs-water-instantaneous-values.js.map +1 -0
  107. package/dist/data/connectors/usgs-water-instantaneous-values.schemas.d.ts +496 -0
  108. package/dist/data/connectors/usgs-water-instantaneous-values.schemas.js +309 -0
  109. package/dist/data/connectors/usgs-water-instantaneous-values.schemas.js.map +1 -0
  110. package/dist/data/connectors/youtube-public-content.d.ts +2 -0
  111. package/dist/data/connectors/youtube-public-content.js +974 -0
  112. package/dist/data/connectors/youtube-public-content.js.map +1 -0
  113. package/dist/data/connectors/youtube-public-content.schemas.d.ts +773 -0
  114. package/dist/data/connectors/youtube-public-content.schemas.js +567 -0
  115. package/dist/data/connectors/youtube-public-content.schemas.js.map +1 -0
  116. package/dist/data/contracts.d.ts +374 -0
  117. package/dist/data/contracts.js +10 -0
  118. package/dist/data/contracts.js.map +1 -0
  119. package/dist/data/runtime/artifacts.d.ts +6 -0
  120. package/dist/data/runtime/artifacts.js +103 -0
  121. package/dist/data/runtime/artifacts.js.map +1 -0
  122. package/dist/data/runtime/bounded-http.d.ts +10 -0
  123. package/dist/data/runtime/bounded-http.js +403 -0
  124. package/dist/data/runtime/bounded-http.js.map +1 -0
  125. package/dist/data/runtime/canonical-json.d.ts +3 -0
  126. package/dist/data/runtime/canonical-json.js +59 -0
  127. package/dist/data/runtime/canonical-json.js.map +1 -0
  128. package/dist/data/runtime/credentials.d.ts +14 -0
  129. package/dist/data/runtime/credentials.js +67 -0
  130. package/dist/data/runtime/credentials.js.map +1 -0
  131. package/dist/data/runtime/csv.d.ts +4 -0
  132. package/dist/data/runtime/csv.js +51 -0
  133. package/dist/data/runtime/csv.js.map +1 -0
  134. package/dist/data/runtime/errors.d.ts +22 -0
  135. package/dist/data/runtime/errors.js +101 -0
  136. package/dist/data/runtime/errors.js.map +1 -0
  137. package/dist/data/runtime/execute.d.ts +11 -0
  138. package/dist/data/runtime/execute.js +358 -0
  139. package/dist/data/runtime/execute.js.map +1 -0
  140. package/dist/data/runtime/receipts.d.ts +13 -0
  141. package/dist/data/runtime/receipts.js +38 -0
  142. package/dist/data/runtime/receipts.js.map +1 -0
  143. package/dist/data/schemas/catalog.v1.json +73 -0
  144. package/dist/data/schemas/core-receipt.v1.json +117 -0
  145. package/dist/data/schemas/describe.v1.json +24 -0
  146. package/dist/data/schemas/discovery.v1.json +130 -0
  147. package/dist/data/schemas/doctor.v1.json +40 -0
  148. package/dist/data/schemas/error.v1.json +48 -0
  149. package/dist/data/schemas/manifest.v1.json +174 -0
  150. package/dist/data/schemas/run-request.v1.json +37 -0
  151. package/dist/data/schemas/run-result.v1.json +77 -0
  152. package/dist/data/schemas.d.ts +27 -0
  153. package/dist/data/schemas.js +69 -0
  154. package/dist/data/schemas.js.map +1 -0
  155. package/dist/env.js.map +1 -1
  156. package/dist/errors.js.map +1 -1
  157. package/dist/http.js.map +1 -1
  158. package/dist/kb/config.js.map +1 -1
  159. package/dist/kb/pipeline-health.js.map +1 -1
  160. package/dist/research/orchestration.js +71 -11
  161. package/dist/research/orchestration.js.map +1 -1
  162. package/dist/research/workspace/acquisition.js +70 -5
  163. package/dist/research/workspace/acquisition.js.map +1 -1
  164. package/dist/research/workspace/audit-bundle.js +7 -2
  165. package/dist/research/workspace/audit-bundle.js.map +1 -1
  166. package/dist/research/workspace/broker.js.map +1 -1
  167. package/dist/research/workspace/constants.js.map +1 -1
  168. package/dist/research/workspace/credentials.d.ts +3 -0
  169. package/dist/research/workspace/credentials.js +20 -3
  170. package/dist/research/workspace/credentials.js.map +1 -1
  171. package/dist/research/workspace/data-evidence-adapter.d.ts +60 -0
  172. package/dist/research/workspace/data-evidence-adapter.js +314 -0
  173. package/dist/research/workspace/data-evidence-adapter.js.map +1 -0
  174. package/dist/research/workspace/discovery-status.js +10 -7
  175. package/dist/research/workspace/discovery-status.js.map +1 -1
  176. package/dist/research/workspace/discovery.js +12 -11
  177. package/dist/research/workspace/discovery.js.map +1 -1
  178. package/dist/research/workspace/evidence-ledger.d.ts +8 -1
  179. package/dist/research/workspace/evidence-ledger.js +46 -4
  180. package/dist/research/workspace/evidence-ledger.js.map +1 -1
  181. package/dist/research/workspace/evidence.d.ts +31 -0
  182. package/dist/research/workspace/evidence.js +77 -0
  183. package/dist/research/workspace/evidence.js.map +1 -1
  184. package/dist/research/workspace/executor.js.map +1 -1
  185. package/dist/research/workspace/external-skills.d.ts +49 -49
  186. package/dist/research/workspace/external-skills.js.map +1 -1
  187. package/dist/research/workspace/platform-capabilities.js.map +1 -1
  188. package/dist/research/workspace/preflight.d.ts +4 -4
  189. package/dist/research/workspace/preflight.js +33 -8
  190. package/dist/research/workspace/preflight.js.map +1 -1
  191. package/dist/research/workspace/projects.js +203 -76
  192. package/dist/research/workspace/projects.js.map +1 -1
  193. package/dist/research/workspace/publication.js.map +1 -1
  194. package/dist/research/workspace/research-policy-wizard.js.map +1 -1
  195. package/dist/research/workspace/research-policy.js.map +1 -1
  196. package/dist/research/workspace/runtime.d.ts +7 -0
  197. package/dist/research/workspace/runtime.js +82 -23
  198. package/dist/research/workspace/runtime.js.map +1 -1
  199. package/dist/research/workspace/sanitization.js.map +1 -1
  200. package/dist/research/workspace/schemas.js +1 -1
  201. package/dist/research/workspace/schemas.js.map +1 -1
  202. package/dist/research/workspace/scientific-design.js.map +1 -1
  203. package/dist/research/workspace/scientific-review.js.map +1 -1
  204. package/dist/research/workspace/setup-catalog.d.ts +9 -9
  205. package/dist/research/workspace/setup-catalog.js +2 -2
  206. package/dist/research/workspace/setup-catalog.js.map +1 -1
  207. package/dist/research/workspace/setup-declarative.js.map +1 -1
  208. package/dist/research/workspace/setup-instructions.d.ts +42 -0
  209. package/dist/research/workspace/setup-instructions.js +413 -0
  210. package/dist/research/workspace/setup-instructions.js.map +1 -0
  211. package/dist/research/workspace/setup-invocation.js.map +1 -1
  212. package/dist/research/workspace/setup-wizard.js +7 -0
  213. package/dist/research/workspace/setup-wizard.js.map +1 -1
  214. package/dist/research/workspace/setup.d.ts +18 -15
  215. package/dist/research/workspace/setup.js +68 -3
  216. package/dist/research/workspace/setup.js.map +1 -1
  217. package/dist/research/workspace/workspace.js.map +1 -1
  218. package/package.json +3 -3
@@ -0,0 +1,718 @@
1
+ import { DATA_MANIFEST_SCHEMA_VERSION } from "../contracts.js";
2
+ import { DataRuntimeError } from "../runtime/errors.js";
3
+ import { REGULATIONS_GOV_DETAIL_INPUT_SCHEMA, REGULATIONS_GOV_DETAIL_OUTPUT_SCHEMA, REGULATIONS_GOV_SEARCH_INPUT_SCHEMA, REGULATIONS_GOV_SEARCH_OUTPUT_SCHEMA, } from "./regulations-gov-comments.schemas.js";
4
+ const API_PATH_PREFIX = "/v4/";
5
+ const SEARCH_PATH = "/v4/comments";
6
+ const PROVIDER_TIME_ZONE = "America/New_York";
7
+ const MAX_SEARCH_WINDOW_DAYS = 366;
8
+ const MAX_PROVIDER_PAGES = 20;
9
+ const MAX_DETAIL_COMMENTS = 100;
10
+ export const regulationsGovCommentsConnector = {
11
+ schemaVersion: DATA_MANIFEST_SCHEMA_VERSION,
12
+ capabilityId: "regulations-gov.comments",
13
+ capabilityVersion: "1.0.0",
14
+ minimumCliVersion: "0.0.55",
15
+ provider: { providerId: "regulations-gov", name: "Regulations.gov" },
16
+ sourceCategory: "public-regulatory-comments",
17
+ endpoints: [
18
+ {
19
+ endpointId: "regulations-gov-api",
20
+ baseUrl: "https://api.regulations.gov",
21
+ pathPrefixes: [API_PATH_PREFIX],
22
+ allowedMethods: ["GET"],
23
+ allowedContentTypes: ["application/vnd.api+json", "application/json"],
24
+ },
25
+ ],
26
+ license: {
27
+ name: "Regulations.gov API terms and public-data limitations",
28
+ url: "https://open.gsa.gov/api/regulationsgov/",
29
+ restrictions: [
30
+ "Treat returned comments as public submissions with agency-dependent fields, moderation, duplication, withdrawal, restriction, and publication practices.",
31
+ "Do not infer representative public opinion, vote counts, or statistically valid sentiment from comment volume or content.",
32
+ "Handle comment text and attachment links as potentially containing personal, sensitive, unsafe, or untrusted content.",
33
+ ],
34
+ },
35
+ credentials: [
36
+ {
37
+ credentialId: "api-key",
38
+ environmentVariable: "REGGOV_API_KEY",
39
+ required: true,
40
+ endpointIds: ["regulations-gov-api"],
41
+ injection: { kind: "header", name: "X-Api-Key", prefix: "" },
42
+ },
43
+ ],
44
+ limits: {
45
+ timeoutMs: 60_000,
46
+ maxRequestBytes: 2_048,
47
+ maxResponseBytes: 20_000_000,
48
+ maxPages: MAX_DETAIL_COMMENTS,
49
+ maxRecords: 5_000,
50
+ maxRetries: 4,
51
+ maxRetryDelayMs: 120_000,
52
+ maxRedirects: 1,
53
+ },
54
+ diagnostics: { static: true, live: false },
55
+ freshness: {
56
+ kind: "provider-current",
57
+ description: "Results reflect the public Regulations.gov v4 API at request time; publication, modification, withdrawal, and agency processing latency vary by agency and docket.",
58
+ },
59
+ limitations: [
60
+ "Public comment fields and publication practices vary by agency; some fields are always public, agency-configurable, restricted, withdrawn, duplicated, or never exposed.",
61
+ "Mass-mail campaigns, duplicateComments metadata, moderation, and self-selection make comment counts unsuitable as representative public-opinion or sentiment measures.",
62
+ "Comment text and linked attachments are untrusted public content and may contain personal or sensitive information.",
63
+ "The API imposes bounded pagination and shared api.data.gov quota policies; the connector does not promise exhaustive bulk export.",
64
+ "Regulations.gov documents the lastModifiedDate search filter as beta and may remove it when a permanent bulk-download mechanism becomes available.",
65
+ "Search metadata and detail attributes can be absent even when the JSON:API resource is valid; nulls preserve provider non-availability and must not be interpreted as false, zero, or an empty submission.",
66
+ ],
67
+ discovery: {
68
+ source: {
69
+ maintainedBy: "U.S. General Services Administration with participating federal agencies",
70
+ summary: "Public comments and related metadata submitted to United States federal regulatory dockets through Regulations.gov.",
71
+ description: "Regulations.gov provides a cross-agency public portal and v4 API for regulatory dockets and public submissions. Agencies control parts of the record lifecycle and field visibility, so coverage and metadata practices are not uniform.",
72
+ coverage: {
73
+ geographic: "United States federal regulatory dockets and participating agencies.",
74
+ temporal: "Provider-held public comments searchable by bounded posted or last-modified windows; availability and update latency are agency dependent.",
75
+ granularity: "One public comment metadata record or one curated public comment detail, with optional attachment metadata only.",
76
+ },
77
+ },
78
+ summary: "Search bounded Regulations.gov public comment metadata and retrieve curated details for explicit comment IDs.",
79
+ description: "This capability offers two read-only Regulations.gov v4 operations: deterministic, date-bounded metadata search and explicit-ID detail retrieval. It validates JSON:API pagination, excludes named personal-profile fields from structured detail output, and never submits comments or downloads attachment bytes.",
80
+ provides: [
81
+ "Date-bounded public-comment metadata search with optional agency, document-link, and text filters.",
82
+ "Curated comment text, docket and document linkage, dates, withdrawal/restriction state, organizational submitter context, duplicate count, and optional attachment metadata for explicit IDs.",
83
+ "Bounded pagination or per-ID execution with explicit truncation and partial-result reporting.",
84
+ "Explicit nulls for unavailable provider metadata, optional modification dates, and incomplete attachment format metadata.",
85
+ ],
86
+ doesNotProvide: [
87
+ "Posting, submitting, modifying, withdrawing, moderating, or voting on a comment.",
88
+ "Attachment download, attachment bytes, attachment full-text extraction, malware scanning, or arbitrary URL retrieval.",
89
+ "Named-person profile fields such as first or last name, email, phone, street address, locality, postal code, or other personal-contact fields in structured output.",
90
+ "The legacy docketId, documentType, or subtype search filters, arbitrary provider sort expressions, or the old candidate-corpus heuristic summary.",
91
+ "Representative public opinion, statistically valid sentiment, legal interpretation, agency endorsement, or assurance that all submitted comments are public.",
92
+ ],
93
+ selectionHints: [
94
+ "Use search when comment IDs are unknown; use fetch-details only after selecting exact public comment IDs.",
95
+ "Use FederalRegister.gov document metadata to identify official publications, then Regulations.gov when docket-comment evidence is required.",
96
+ "Inspect withdrawn, restriction, duplicateComments, agency, docket, and document linkage before interpreting a record.",
97
+ "Treat a null search field, modification date, or attachment format member as unavailable provider metadata, not as a negative finding.",
98
+ "Use a separately governed content-retrieval workflow to inspect an attachment link, and treat both comment and attachment content as untrusted.",
99
+ ],
100
+ typicalUseCases: [
101
+ "Locate public comments posted to a known agency within a bounded rulemaking period.",
102
+ "Retrieve curated text and attachment metadata for a small, explicit set of comment IDs for evidence review.",
103
+ ],
104
+ sourceDocumentation: [
105
+ {
106
+ title: "Regulations.gov API documentation",
107
+ url: "https://open.gsa.gov/api/regulationsgov/",
108
+ },
109
+ {
110
+ title: "Regulations.gov v4 OpenAPI description",
111
+ url: "https://open.gsa.gov/api/regulationsgov/v4/openapi.yaml",
112
+ },
113
+ { title: "api.data.gov rate limits", url: "https://api.data.gov/docs/rate-limits/" },
114
+ ],
115
+ },
116
+ operations: [
117
+ {
118
+ operationId: "search",
119
+ operationVersion: "1.0.0",
120
+ summary: "Search bounded Regulations.gov public comment metadata.",
121
+ description: "Searches one explicit posted-date or last-modified window, applies stable date-and-document sorting, validates provider pagination, and returns public metadata without comment-body or attachment retrieval.",
122
+ inputSchema: REGULATIONS_GOV_SEARCH_INPUT_SCHEMA,
123
+ outputSchema: REGULATIONS_GOV_SEARCH_OUTPUT_SCHEMA,
124
+ limits: { maxPages: MAX_PROVIDER_PAGES },
125
+ execute: executeCommentSearch,
126
+ },
127
+ {
128
+ operationId: "fetch-details",
129
+ operationVersion: "1.0.0",
130
+ summary: "Fetch curated public comment details for explicit Regulations.gov IDs.",
131
+ description: "Retrieves up to 100 exact comment IDs in caller order, optionally includes normalized attachment metadata, omits named personal-profile fields, and never downloads linked files.",
132
+ inputSchema: REGULATIONS_GOV_DETAIL_INPUT_SCHEMA,
133
+ outputSchema: REGULATIONS_GOV_DETAIL_OUTPUT_SCHEMA,
134
+ limits: { maxPages: MAX_DETAIL_COMMENTS, maxRecords: MAX_DETAIL_COMMENTS },
135
+ execute: executeCommentDetails,
136
+ },
137
+ ],
138
+ };
139
+ async function executeCommentSearch(context) {
140
+ const query = normalizeSearchQuery(context.input);
141
+ const records = [];
142
+ const pages = [];
143
+ const observations = [];
144
+ let provider = null;
145
+ let stopReason = "completed";
146
+ let failedPage = null;
147
+ let failure;
148
+ for (let pageNumber = 1; pageNumber <= context.limits.maxPages; pageNumber += 1) {
149
+ try {
150
+ const response = await context.http.request({
151
+ endpointId: "regulations-gov-api",
152
+ method: "GET",
153
+ path: SEARCH_PATH,
154
+ query: buildSearchParameters(query, pageNumber),
155
+ credentialId: "api-key",
156
+ });
157
+ const parsed = parseSearchPage(response.json(), pageNumber, query.pageSize, Math.max(0, context.limits.maxRecords - records.length), records.length);
158
+ validateProviderConsistency(provider, parsed.meta);
159
+ provider ??= parsed.meta;
160
+ observations.push({ ...response.observation, sourceId: `comments:page:${pageNumber}` });
161
+ records.push(...parsed.records);
162
+ pages.push({
163
+ pageNumber,
164
+ inputRecords: parsed.rawRecordCount,
165
+ emittedRecords: parsed.records.length,
166
+ });
167
+ if (records.length >= context.limits.maxRecords &&
168
+ (parsed.meta.hasNextPage || parsed.rawRecordCount > parsed.records.length)) {
169
+ stopReason = "max-records";
170
+ break;
171
+ }
172
+ if (!parsed.meta.hasNextPage) {
173
+ stopReason = pageNumber === 1 && parsed.rawRecordCount === 0 ? "no-results" : "completed";
174
+ break;
175
+ }
176
+ if (pageNumber >= context.limits.maxPages) {
177
+ stopReason = "max-pages";
178
+ break;
179
+ }
180
+ }
181
+ catch (error) {
182
+ if (isCredentialFailure(error))
183
+ throw error;
184
+ if (pages.length === 0)
185
+ throw normalizeProviderFailure(error);
186
+ failedPage = pageNumber;
187
+ failure = error;
188
+ stopReason = "partial";
189
+ break;
190
+ }
191
+ }
192
+ if (!provider) {
193
+ throw providerInvalid("Regulations.gov search completed without pagination metadata.");
194
+ }
195
+ const partial = failedPage !== null;
196
+ const truncated = stopReason === "max-pages" || stopReason === "max-records";
197
+ const errors = partial
198
+ ? [
199
+ {
200
+ code: "partial-result",
201
+ message: "A later Regulations.gov comment page could not be retrieved or validated.",
202
+ retryable: failure instanceof DataRuntimeError ? (failure.options.retryable ?? false) : false,
203
+ userActionRequired: false,
204
+ details: {
205
+ missingPages: [failedPage],
206
+ causeCode: failure instanceof DataRuntimeError ? failure.code : "provider-response-invalid",
207
+ },
208
+ },
209
+ ]
210
+ : [];
211
+ return {
212
+ status: partial ? "partial" : "success",
213
+ data: {
214
+ source: sourceDescriptor(),
215
+ query,
216
+ provider,
217
+ pages,
218
+ records,
219
+ stopReason,
220
+ },
221
+ summary: {
222
+ recordCount: records.length,
223
+ pageCount: pages.length,
224
+ chunkCount: 0,
225
+ truncated,
226
+ completeness: partial ? "partial" : "complete",
227
+ ...(partial
228
+ ? { missing: [{ kind: "page", identifiers: [String(failedPage)] }] }
229
+ : {}),
230
+ },
231
+ warnings: [
232
+ "Public comments are self-selected submissions and must not be treated as representative public opinion or statistically valid sentiment.",
233
+ "Agency publication, duplication, withdrawal, restriction, and field-visibility practices vary by docket and record.",
234
+ "Missing provider fields are preserved as null and do not establish a false, zero, or empty value.",
235
+ ...(truncated ? ["The comment search stopped at an explicit page or record limit."] : []),
236
+ ],
237
+ errors,
238
+ observations,
239
+ };
240
+ }
241
+ async function executeCommentDetails(context) {
242
+ const query = normalizeDetailQuery(context.input, context);
243
+ const records = [];
244
+ const failures = [];
245
+ const observations = [];
246
+ const failureValues = [];
247
+ for (const [requestIndex, commentId] of query.commentIds.entries()) {
248
+ try {
249
+ const response = await context.http.request({
250
+ endpointId: "regulations-gov-api",
251
+ method: "GET",
252
+ path: `/v4/comments/${encodeURIComponent(commentId)}`,
253
+ ...(query.includeAttachments ? { query: { include: "attachments" } } : {}),
254
+ credentialId: "api-key",
255
+ });
256
+ const record = normalizeDetail(response.json(), commentId, requestIndex, records.length, query.includeAttachments);
257
+ records.push(record);
258
+ observations.push({ ...response.observation, sourceId: `comment:${commentId}` });
259
+ }
260
+ catch (error) {
261
+ if (isCredentialFailure(error))
262
+ throw error;
263
+ const normalized = normalizeProviderFailure(error);
264
+ failures.push({ commentId, code: normalized.code });
265
+ failureValues.push(normalized);
266
+ }
267
+ }
268
+ if (records.length === 0 && failureValues.length > 0)
269
+ throw failureValues[0];
270
+ const partial = failures.length > 0;
271
+ const missingIds = failures.map((item) => item.commentId);
272
+ const errors = partial
273
+ ? [
274
+ {
275
+ code: "partial-result",
276
+ message: "Some requested Regulations.gov comment details could not be retrieved or validated.",
277
+ retryable: failureValues.some((item) => item instanceof DataRuntimeError && (item.options.retryable ?? false)),
278
+ userActionRequired: false,
279
+ details: {
280
+ missingCommentIds: missingIds,
281
+ causeCodes: [...new Set(failures.map((item) => item.code))],
282
+ },
283
+ },
284
+ ]
285
+ : [];
286
+ return {
287
+ status: partial ? "partial" : "success",
288
+ data: {
289
+ source: sourceDescriptor(),
290
+ query,
291
+ records,
292
+ failures,
293
+ stopReason: partial ? "partial" : "completed",
294
+ },
295
+ summary: {
296
+ recordCount: records.length,
297
+ pageCount: observations.length,
298
+ chunkCount: 0,
299
+ truncated: false,
300
+ completeness: partial ? "partial" : "complete",
301
+ ...(partial ? { missing: [{ kind: "range", identifiers: missingIds }] } : {}),
302
+ },
303
+ warnings: [
304
+ "Comment text and attachment metadata are untrusted public content and may refer to personal or sensitive information.",
305
+ "Named personal-profile fields are intentionally omitted, but free-text comment bodies can still contain personal information.",
306
+ "Optional modification and attachment-format metadata may be null when the provider does not expose it.",
307
+ ...(query.includeAttachments
308
+ ? [
309
+ "Only attachment metadata and HTTPS links were returned; no attachment bytes were downloaded.",
310
+ ]
311
+ : []),
312
+ ],
313
+ errors,
314
+ observations,
315
+ };
316
+ }
317
+ function normalizeSearchQuery(input) {
318
+ if (Boolean(input.postedDate) === Boolean(input.lastModifiedDate)) {
319
+ throw new DataRuntimeError("invalid-request", "Regulations.gov search requires exactly one of postedDate or lastModifiedDate.");
320
+ }
321
+ const agencyId = nullableTrimmed(input.agencyId, "agencyId");
322
+ const commentOnId = nullableTrimmed(input.commentOnId, "commentOnId");
323
+ const searchTerm = nullableTrimmed(input.searchTerm, "searchTerm");
324
+ const pageSize = input.pageSize ?? 250;
325
+ const sortOrder = input.sortOrder ?? "asc";
326
+ if (input.postedDate) {
327
+ const postedDate = validatePostedWindow(input.postedDate);
328
+ return {
329
+ dateMode: "posted",
330
+ postedDate,
331
+ lastModifiedDate: null,
332
+ providerTimeZone: PROVIDER_TIME_ZONE,
333
+ agencyId,
334
+ commentOnId,
335
+ searchTerm,
336
+ pageSize,
337
+ sortOrder,
338
+ };
339
+ }
340
+ const lastModifiedDate = validateModifiedWindow(input.lastModifiedDate);
341
+ return {
342
+ dateMode: "last-modified",
343
+ postedDate: null,
344
+ lastModifiedDate,
345
+ providerTimeZone: PROVIDER_TIME_ZONE,
346
+ agencyId,
347
+ commentOnId,
348
+ searchTerm,
349
+ pageSize,
350
+ sortOrder,
351
+ };
352
+ }
353
+ function normalizeDetailQuery(input, context) {
354
+ const maximum = Math.min(MAX_DETAIL_COMMENTS, context.limits.maxPages, context.limits.maxRecords);
355
+ if (input.commentIds.length > maximum) {
356
+ throw new DataRuntimeError("invalid-request", `The commentIds array exceeds the effective ${maximum}-comment execution limit.`, { details: { requestedComments: input.commentIds.length, maximumComments: maximum } });
357
+ }
358
+ return { commentIds: [...input.commentIds], includeAttachments: input.includeAttachments };
359
+ }
360
+ function validatePostedWindow(window) {
361
+ const from = parseExactDate(window.from, "postedDate.from");
362
+ const to = parseExactDate(window.to, "postedDate.to");
363
+ if (from > to) {
364
+ throw new DataRuntimeError("invalid-request", "postedDate.from must not follow postedDate.to.");
365
+ }
366
+ const inclusiveDays = (to - from) / 86_400_000 + 1;
367
+ if (inclusiveDays > MAX_SEARCH_WINDOW_DAYS) {
368
+ throw new DataRuntimeError("invalid-request", `The postedDate window must not exceed ${MAX_SEARCH_WINDOW_DAYS} inclusive days.`, { details: { inclusiveDays, maximumDays: MAX_SEARCH_WINDOW_DAYS } });
369
+ }
370
+ return { from: window.from, to: window.to };
371
+ }
372
+ function validateModifiedWindow(window) {
373
+ const from = parseRfc3339(window.from, "lastModifiedDate.from");
374
+ const to = parseRfc3339(window.to, "lastModifiedDate.to");
375
+ if (from > to) {
376
+ throw new DataRuntimeError("invalid-request", "lastModifiedDate.from must not follow lastModifiedDate.to.");
377
+ }
378
+ const durationDays = (to - from) / 86_400_000;
379
+ if (durationDays > MAX_SEARCH_WINDOW_DAYS) {
380
+ throw new DataRuntimeError("invalid-request", `The lastModifiedDate window must not exceed ${MAX_SEARCH_WINDOW_DAYS} days.`, { details: { durationDays, maximumDays: MAX_SEARCH_WINDOW_DAYS } });
381
+ }
382
+ return { from: window.from, to: window.to };
383
+ }
384
+ function buildSearchParameters(query, pageNumber) {
385
+ const dateField = query.dateMode === "posted" ? "postedDate" : "lastModifiedDate";
386
+ const inputWindow = query.postedDate ?? query.lastModifiedDate;
387
+ if (!inputWindow)
388
+ throw new DataRuntimeError("internal-error", "Date window was not normalized.");
389
+ const from = query.dateMode === "posted" ? inputWindow.from : formatProviderWallTime(inputWindow.from);
390
+ const to = query.dateMode === "posted" ? inputWindow.to : formatProviderWallTime(inputWindow.to);
391
+ return {
392
+ ...(query.agencyId ? { "filter[agencyId]": query.agencyId } : {}),
393
+ [`filter[${dateField}][ge]`]: from,
394
+ [`filter[${dateField}][le]`]: to,
395
+ ...(query.commentOnId ? { "filter[commentOnId]": query.commentOnId } : {}),
396
+ ...(query.searchTerm ? { "filter[searchTerm]": query.searchTerm } : {}),
397
+ "page[number]": pageNumber,
398
+ "page[size]": query.pageSize,
399
+ sort: query.sortOrder === "asc" ? `${dateField},documentId` : `-${dateField},-documentId`,
400
+ };
401
+ }
402
+ function parseSearchPage(value, requestedPage, requestedPageSize, remainingRecords, recordOffset) {
403
+ const payload = requireObject(value, "Regulations.gov search response");
404
+ const data = requireArray(payload.data, "data");
405
+ const meta = parseProviderMeta(payload.meta, requestedPage, requestedPageSize, data.length);
406
+ const records = data
407
+ .slice(0, remainingRecords)
408
+ .map((item, index) => normalizeSearchRecord(item, requestedPage, recordOffset + index));
409
+ return { meta, rawRecordCount: data.length, records };
410
+ }
411
+ function parseProviderMeta(value, requestedPage, requestedPageSize, recordCount) {
412
+ const meta = requireObject(value, "meta");
413
+ const result = {
414
+ hasNextPage: requireBoolean(meta.hasNextPage, "meta.hasNextPage"),
415
+ hasPreviousPage: requireBoolean(meta.hasPreviousPage, "meta.hasPreviousPage"),
416
+ numberOfElements: requireNonNegativeInteger(meta.numberOfElements, "meta.numberOfElements"),
417
+ pageNumber: requirePositiveInteger(meta.pageNumber, "meta.pageNumber"),
418
+ pageSize: requirePositiveInteger(meta.pageSize, "meta.pageSize"),
419
+ totalElements: requireNonNegativeInteger(meta.totalElements, "meta.totalElements"),
420
+ totalPages: requireNonNegativeInteger(meta.totalPages, "meta.totalPages"),
421
+ firstPage: requireBoolean(meta.firstPage, "meta.firstPage"),
422
+ lastPage: requireBoolean(meta.lastPage, "meta.lastPage"),
423
+ };
424
+ if (result.pageNumber !== requestedPage || result.pageSize !== requestedPageSize) {
425
+ throw providerInvalid("Regulations.gov pagination metadata does not match the request.");
426
+ }
427
+ if (result.pageSize < 5 || result.pageSize > 250 || result.totalPages > MAX_PROVIDER_PAGES) {
428
+ throw providerInvalid("Regulations.gov pagination metadata exceeds provider bounds.");
429
+ }
430
+ if (result.numberOfElements !== recordCount || recordCount > result.pageSize) {
431
+ throw providerInvalid("Regulations.gov page counts do not match the response data.");
432
+ }
433
+ const expectedPages = Math.min(MAX_PROVIDER_PAGES, Math.ceil(result.totalElements / result.pageSize));
434
+ if (result.totalPages !== expectedPages) {
435
+ throw providerInvalid("Regulations.gov totalPages is inconsistent with totalElements.");
436
+ }
437
+ const expectedHasPrevious = requestedPage > 1;
438
+ const expectedHasNext = requestedPage < result.totalPages;
439
+ if (result.hasPreviousPage !== expectedHasPrevious ||
440
+ result.firstPage !== !expectedHasPrevious ||
441
+ result.hasNextPage !== expectedHasNext ||
442
+ result.lastPage !== !expectedHasNext) {
443
+ throw providerInvalid("Regulations.gov page-position flags are inconsistent.");
444
+ }
445
+ return result;
446
+ }
447
+ function validateProviderConsistency(current, next) {
448
+ if (!current)
449
+ return;
450
+ if (current.pageSize !== next.pageSize ||
451
+ current.totalElements !== next.totalElements ||
452
+ current.totalPages !== next.totalPages) {
453
+ throw providerInvalid("Regulations.gov pagination totals changed between pages.");
454
+ }
455
+ }
456
+ function normalizeSearchRecord(value, pageNumber, recordIndex) {
457
+ const resource = requireObject(value, `data[${recordIndex}]`);
458
+ if (requireString(resource.type, "data.type") !== "comments") {
459
+ throw providerInvalid("Regulations.gov search resources must have type comments.");
460
+ }
461
+ const attributes = requireObject(resource.attributes, "data.attributes");
462
+ return {
463
+ recordIndex,
464
+ sourcePageNumber: pageNumber,
465
+ commentId: requireString(resource.id, "data.id"),
466
+ agencyId: nullableString(attributes.agencyId, "attributes.agencyId"),
467
+ documentType: nullableString(attributes.documentType, "attributes.documentType"),
468
+ highlightedContent: nullableString(attributes.highlightedContent, "attributes.highlightedContent"),
469
+ lastModifiedDateTime: nullableProviderRfc3339(attributes.lastModifiedDate, "attributes.lastModifiedDate"),
470
+ objectId: nullableString(attributes.objectId, "attributes.objectId"),
471
+ postedDateTime: nullableProviderRfc3339(attributes.postedDate, "attributes.postedDate"),
472
+ title: nullableString(attributes.title, "attributes.title"),
473
+ withdrawn: nullableBoolean(attributes.withdrawn, "attributes.withdrawn"),
474
+ };
475
+ }
476
+ function normalizeDetail(value, requestedCommentId, requestIndex, recordIndex, includeAttachments) {
477
+ const payload = requireObject(value, "Regulations.gov detail response");
478
+ const resource = requireObject(payload.data, "data");
479
+ const commentId = requireString(resource.id, "data.id");
480
+ if (commentId !== requestedCommentId) {
481
+ throw providerInvalid("Regulations.gov returned a different comment ID than requested.");
482
+ }
483
+ if (requireString(resource.type, "data.type") !== "comments") {
484
+ throw providerInvalid("Regulations.gov detail resource must have type comments.");
485
+ }
486
+ const attributes = requireObject(resource.attributes, "data.attributes");
487
+ return {
488
+ recordIndex,
489
+ requestIndex,
490
+ commentId,
491
+ agencyId: requireString(attributes.agencyId, "attributes.agencyId"),
492
+ commentText: requireString(attributes.comment, "attributes.comment", true),
493
+ commentOnDocumentId: requireString(attributes.commentOnDocumentId, "attributes.commentOnDocumentId"),
494
+ docketId: requireString(attributes.docketId, "attributes.docketId"),
495
+ documentType: requireString(attributes.documentType, "attributes.documentType"),
496
+ postedDateTime: requireProviderRfc3339(attributes.postedDate, "attributes.postedDate"),
497
+ modifiedDateTime: nullableProviderRfc3339(attributes.modifyDate, "attributes.modifyDate"),
498
+ receivedDateTime: requireProviderRfc3339(attributes.receiveDate, "attributes.receiveDate"),
499
+ title: requireString(attributes.title, "attributes.title", true),
500
+ trackingNumber: requireString(attributes.trackingNbr, "attributes.trackingNbr", true),
501
+ withdrawn: requireBoolean(attributes.withdrawn, "attributes.withdrawn"),
502
+ reasonWithdrawn: nullableString(attributes.reasonWithdrawn, "attributes.reasonWithdrawn"),
503
+ restriction: {
504
+ type: nullableString(attributes.restrictReasonType, "attributes.restrictReasonType"),
505
+ reason: nullableString(attributes.restrictReason, "attributes.restrictReason"),
506
+ },
507
+ submitterContext: {
508
+ organization: nullableString(attributes.organization, "attributes.organization"),
509
+ governmentAgency: nullableString(attributes.govAgency, "attributes.govAgency"),
510
+ governmentAgencyType: nullableString(attributes.govAgencyType, "attributes.govAgencyType"),
511
+ },
512
+ duplicateComments: nullableNonNegativeInteger(attributes.duplicateComments, "attributes.duplicateComments"),
513
+ attachments: includeAttachments ? normalizeAttachments(payload, resource) : [],
514
+ };
515
+ }
516
+ function normalizeAttachments(payload, resource) {
517
+ const relationshipIds = attachmentRelationshipIds(resource.relationships);
518
+ if (relationshipIds.length === 0)
519
+ return [];
520
+ const included = requireArray(payload.included, "included");
521
+ const byId = new Map();
522
+ for (const [index, value] of included.entries()) {
523
+ const item = requireObject(value, `included[${index}]`);
524
+ if (requireString(item.type, `included[${index}].type`) !== "attachments")
525
+ continue;
526
+ const id = requireString(item.id, `included[${index}].id`);
527
+ byId.set(id, normalizeAttachment(item, id, index));
528
+ }
529
+ return relationshipIds.map((id) => {
530
+ const attachment = byId.get(id);
531
+ if (!attachment) {
532
+ throw providerInvalid(`Included attachment metadata is missing for relationship ${id}.`);
533
+ }
534
+ return attachment;
535
+ });
536
+ }
537
+ function attachmentRelationshipIds(value) {
538
+ if (value === null || value === undefined)
539
+ return [];
540
+ const relationships = requireObject(value, "data.relationships");
541
+ if (relationships.attachments === null || relationships.attachments === undefined)
542
+ return [];
543
+ const attachments = requireObject(relationships.attachments, "data.relationships.attachments");
544
+ const data = requireArray(attachments.data, "data.relationships.attachments.data");
545
+ return data.map((value, index) => {
546
+ const reference = requireObject(value, `data.relationships.attachments.data[${index}]`);
547
+ if (requireString(reference.type, `data.relationships.attachments.data[${index}].type`) !==
548
+ "attachments") {
549
+ throw providerInvalid("Attachment relationships must use type attachments.");
550
+ }
551
+ return requireString(reference.id, `data.relationships.attachments.data[${index}].id`);
552
+ });
553
+ }
554
+ function normalizeAttachment(value, attachmentId, includedIndex) {
555
+ const attributes = requireObject(value.attributes, `included[${includedIndex}].attributes`);
556
+ const fileFormats = requireArray(attributes.fileFormats ?? [], `included[${includedIndex}].attributes.fileFormats`).map((value, index) => {
557
+ const item = requireObject(value, `fileFormats[${index}]`);
558
+ return {
559
+ url: nullableHttpsUrl(item.fileUrl, `fileFormats[${index}].fileUrl`),
560
+ format: nullableString(item.format, `fileFormats[${index}].format`),
561
+ sizeBytes: nullableNonNegativeInteger(item.size, `fileFormats[${index}].size`),
562
+ };
563
+ });
564
+ const authors = requireArray(attributes.authors ?? [], `included[${includedIndex}].attributes.authors`).map((author, index) => requireString(author, `authors[${index}]`));
565
+ return {
566
+ attachmentId,
567
+ title: nullableString(attributes.title, "attachment.title"),
568
+ agencyNote: nullableString(attributes.agencyNote, "attachment.agencyNote"),
569
+ authors,
570
+ abstract: nullableString(attributes.docAbstract, "attachment.docAbstract"),
571
+ order: nullableNonNegativeInteger(attributes.docOrder, "attachment.docOrder"),
572
+ modifiedDateTime: nullableProviderRfc3339(attributes.modifyDate, "attachment.modifyDate"),
573
+ publication: nullableString(attributes.publication, "attachment.publication"),
574
+ restriction: {
575
+ type: nullableString(attributes.restrictReasonType, "attachment.restrictReasonType"),
576
+ reason: nullableString(attributes.restrictReason, "attachment.restrictReason"),
577
+ },
578
+ fileFormats,
579
+ };
580
+ }
581
+ function sourceDescriptor() {
582
+ return {
583
+ providerId: "regulations-gov",
584
+ service: "Regulations.gov API",
585
+ apiVersion: "v4",
586
+ publicComments: true,
587
+ };
588
+ }
589
+ function formatProviderWallTime(value) {
590
+ const instant = new Date(value);
591
+ const parts = new Intl.DateTimeFormat("en-CA", {
592
+ timeZone: PROVIDER_TIME_ZONE,
593
+ year: "numeric",
594
+ month: "2-digit",
595
+ day: "2-digit",
596
+ hour: "2-digit",
597
+ minute: "2-digit",
598
+ second: "2-digit",
599
+ hourCycle: "h23",
600
+ }).formatToParts(instant);
601
+ const values = Object.fromEntries(parts.map((part) => [part.type, part.value]));
602
+ return `${values.year}-${values.month}-${values.day} ${values.hour}:${values.minute}:${values.second}`;
603
+ }
604
+ function parseExactDate(value, field) {
605
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
606
+ throw new DataRuntimeError("invalid-request", `${field} must use YYYY-MM-DD.`);
607
+ }
608
+ const instant = Date.parse(`${value}T00:00:00Z`);
609
+ if (!Number.isFinite(instant) || new Date(instant).toISOString().slice(0, 10) !== value) {
610
+ throw new DataRuntimeError("invalid-request", `${field} must be a valid calendar date.`);
611
+ }
612
+ return instant;
613
+ }
614
+ function parseRfc3339(value, field) {
615
+ const instant = Date.parse(value);
616
+ if (!Number.isFinite(instant)) {
617
+ throw new DataRuntimeError("invalid-request", `${field} must be a valid RFC3339 timestamp.`);
618
+ }
619
+ return instant;
620
+ }
621
+ function requireProviderRfc3339(value, field) {
622
+ const text = requireString(value, field);
623
+ if (!Number.isFinite(Date.parse(text)))
624
+ throw providerInvalid(`${field} must be RFC3339.`);
625
+ return text;
626
+ }
627
+ function nullableProviderRfc3339(value, field) {
628
+ if (value === null || value === undefined)
629
+ return null;
630
+ return requireProviderRfc3339(value, field);
631
+ }
632
+ function nullableTrimmed(value, field) {
633
+ if (value === undefined)
634
+ return null;
635
+ const trimmed = value.trim();
636
+ if (!trimmed)
637
+ throw new DataRuntimeError("invalid-request", `${field} must not be blank.`);
638
+ return trimmed;
639
+ }
640
+ function requireObject(value, field) {
641
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
642
+ throw providerInvalid(`${field} must be an object.`);
643
+ }
644
+ return value;
645
+ }
646
+ function requireArray(value, field) {
647
+ if (!Array.isArray(value))
648
+ throw providerInvalid(`${field} must be an array.`);
649
+ return value;
650
+ }
651
+ function requireString(value, field, allowEmpty = false) {
652
+ if (typeof value !== "string" || (!allowEmpty && value.length === 0)) {
653
+ throw providerInvalid(`${field} must be ${allowEmpty ? "a string" : "a non-empty string"}.`);
654
+ }
655
+ return value;
656
+ }
657
+ function nullableString(value, field) {
658
+ if (value === null || value === undefined)
659
+ return null;
660
+ return requireString(value, field, true);
661
+ }
662
+ function requireBoolean(value, field) {
663
+ if (typeof value !== "boolean")
664
+ throw providerInvalid(`${field} must be boolean.`);
665
+ return value;
666
+ }
667
+ function nullableBoolean(value, field) {
668
+ if (value === null || value === undefined)
669
+ return null;
670
+ return requireBoolean(value, field);
671
+ }
672
+ function requirePositiveInteger(value, field) {
673
+ if (!Number.isSafeInteger(value) || value < 1) {
674
+ throw providerInvalid(`${field} must be a positive safe integer.`);
675
+ }
676
+ return value;
677
+ }
678
+ function requireNonNegativeInteger(value, field) {
679
+ if (!Number.isSafeInteger(value) || value < 0) {
680
+ throw providerInvalid(`${field} must be a non-negative safe integer.`);
681
+ }
682
+ return value;
683
+ }
684
+ function nullableNonNegativeInteger(value, field) {
685
+ if (value === null || value === undefined)
686
+ return null;
687
+ return requireNonNegativeInteger(value, field);
688
+ }
689
+ function requireHttpsUrl(value, field) {
690
+ const text = requireString(value, field);
691
+ try {
692
+ const url = new URL(text);
693
+ if (url.protocol !== "https:" || url.username || url.password)
694
+ throw new Error("unsafe URL");
695
+ return url.toString();
696
+ }
697
+ catch {
698
+ throw providerInvalid(`${field} must be a credential-free HTTPS URL.`);
699
+ }
700
+ }
701
+ function nullableHttpsUrl(value, field) {
702
+ if (value === null || value === undefined)
703
+ return null;
704
+ return requireHttpsUrl(value, field);
705
+ }
706
+ function isCredentialFailure(error) {
707
+ return (error instanceof DataRuntimeError &&
708
+ ["credential-missing", "credential-invalid", "provider-auth-blocked"].includes(error.code));
709
+ }
710
+ function normalizeProviderFailure(error) {
711
+ if (error instanceof DataRuntimeError)
712
+ return error;
713
+ return new DataRuntimeError("provider-response-invalid", "The Regulations.gov response could not be retrieved or normalized.");
714
+ }
715
+ function providerInvalid(message) {
716
+ return new DataRuntimeError("provider-response-invalid", message);
717
+ }
718
+ //# sourceMappingURL=regulations-gov-comments.js.map