@mostlyrightmd/core 0.1.0-rc.7

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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/discovery/index.cjs +1646 -0
  4. package/dist/discovery/index.cjs.map +1 -0
  5. package/dist/discovery/index.d.cts +313 -0
  6. package/dist/discovery/index.d.ts +313 -0
  7. package/dist/discovery/index.mjs +1609 -0
  8. package/dist/discovery/index.mjs.map +1 -0
  9. package/dist/formats/index.cjs +498 -0
  10. package/dist/formats/index.cjs.map +1 -0
  11. package/dist/formats/index.d.cts +97 -0
  12. package/dist/formats/index.d.ts +97 -0
  13. package/dist/formats/index.mjs +465 -0
  14. package/dist/formats/index.mjs.map +1 -0
  15. package/dist/index.cjs +1624 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +559 -0
  18. package/dist/index.d.ts +559 -0
  19. package/dist/index.global.js +1582 -0
  20. package/dist/index.global.js.map +1 -0
  21. package/dist/index.mjs +1557 -0
  22. package/dist/index.mjs.map +1 -0
  23. package/dist/internal/bounds.cjs +125 -0
  24. package/dist/internal/bounds.cjs.map +1 -0
  25. package/dist/internal/bounds.d.cts +36 -0
  26. package/dist/internal/bounds.d.ts +36 -0
  27. package/dist/internal/bounds.mjs +81 -0
  28. package/dist/internal/bounds.mjs.map +1 -0
  29. package/dist/internal/cache/fs.cjs +217 -0
  30. package/dist/internal/cache/fs.cjs.map +1 -0
  31. package/dist/internal/cache/fs.d.cts +57 -0
  32. package/dist/internal/cache/fs.d.ts +57 -0
  33. package/dist/internal/cache/fs.mjs +179 -0
  34. package/dist/internal/cache/fs.mjs.map +1 -0
  35. package/dist/internal/cache/index.browser.cjs +1184 -0
  36. package/dist/internal/cache/index.browser.cjs.map +1 -0
  37. package/dist/internal/cache/index.browser.d.cts +20 -0
  38. package/dist/internal/cache/index.browser.d.ts +20 -0
  39. package/dist/internal/cache/index.browser.mjs +36 -0
  40. package/dist/internal/cache/index.browser.mjs.map +1 -0
  41. package/dist/internal/cache/index.cjs +1389 -0
  42. package/dist/internal/cache/index.cjs.map +1 -0
  43. package/dist/internal/cache/index.d.cts +16 -0
  44. package/dist/internal/cache/index.d.ts +16 -0
  45. package/dist/internal/cache/index.mjs +40 -0
  46. package/dist/internal/cache/index.mjs.map +1 -0
  47. package/dist/internal/chunk-PKJXHY27.mjs +1137 -0
  48. package/dist/internal/chunk-PKJXHY27.mjs.map +1 -0
  49. package/dist/internal/convert.cjs +161 -0
  50. package/dist/internal/convert.cjs.map +1 -0
  51. package/dist/internal/convert.d.cts +44 -0
  52. package/dist/internal/convert.d.ts +44 -0
  53. package/dist/internal/convert.mjs +117 -0
  54. package/dist/internal/convert.mjs.map +1 -0
  55. package/dist/internal/fs-O6XR4WWW.mjs +183 -0
  56. package/dist/internal/fs-O6XR4WWW.mjs.map +1 -0
  57. package/dist/internal/keys-B7C8C88N.d.cts +191 -0
  58. package/dist/internal/keys-B7C8C88N.d.ts +191 -0
  59. package/dist/internal/merge/index.cjs +75 -0
  60. package/dist/internal/merge/index.cjs.map +1 -0
  61. package/dist/internal/merge/index.d.cts +74 -0
  62. package/dist/internal/merge/index.d.ts +74 -0
  63. package/dist/internal/merge/index.mjs +46 -0
  64. package/dist/internal/merge/index.mjs.map +1 -0
  65. package/dist/internal/pairs.cjs +328 -0
  66. package/dist/internal/pairs.cjs.map +1 -0
  67. package/dist/internal/pairs.d.cts +105 -0
  68. package/dist/internal/pairs.d.ts +105 -0
  69. package/dist/internal/pairs.mjs +298 -0
  70. package/dist/internal/pairs.mjs.map +1 -0
  71. package/dist/qc/index.cjs +247 -0
  72. package/dist/qc/index.cjs.map +1 -0
  73. package/dist/qc/index.d.cts +140 -0
  74. package/dist/qc/index.d.ts +140 -0
  75. package/dist/qc/index.mjs +212 -0
  76. package/dist/qc/index.mjs.map +1 -0
  77. package/dist/temporal/index.cjs +504 -0
  78. package/dist/temporal/index.cjs.map +1 -0
  79. package/dist/temporal/index.d.cts +121 -0
  80. package/dist/temporal/index.d.ts +121 -0
  81. package/dist/temporal/index.mjs +474 -0
  82. package/dist/temporal/index.mjs.map +1 -0
  83. package/dist/transforms/index.cjs +399 -0
  84. package/dist/transforms/index.cjs.map +1 -0
  85. package/dist/transforms/index.d.cts +193 -0
  86. package/dist/transforms/index.d.ts +193 -0
  87. package/dist/transforms/index.mjs +362 -0
  88. package/dist/transforms/index.mjs.map +1 -0
  89. package/dist/validator.cjs +1870 -0
  90. package/dist/validator.cjs.map +1 -0
  91. package/dist/validator.d.cts +30 -0
  92. package/dist/validator.d.ts +30 -0
  93. package/dist/validator.mjs +1843 -0
  94. package/dist/validator.mjs.map +1 -0
  95. package/package.json +115 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/internal/merge/index.ts","../../../src/internal/merge/observations.ts","../../../src/internal/merge/climate.ts"],"sourcesContent":["// @mostlyrightmd/core/internal/merge — multi-source dedup policies.\n//\n// Subpath export consumed by:\n// - @mostlyrightmd/meta/research orchestrator (calls both at the join step).\n// - @mostlyrightmd/weather barrel (re-exports mergeClimate for backward compat).\n//\n// Same import discipline as `@mostlyrightmd/core/internal/{bounds,convert}` —\n// deep subpath keeps the main `@mostlyrightmd/core` browser bundle thin.\n\nexport { SOURCE_PRIORITY, mergeObservations, type ObservationKey } from \"./observations.js\";\nexport { mergeClimate, type ClimateKey } from \"./climate.js\";\n","// Observation source-priority dedup — AWC > IEM > GHCNh with strict-> + first-seen tiebreak.\n//\n// Byte-faithful TS port of\n// `packages/core/src/mostlyright/_internal/merge/observations.py::merge_observations`.\n//\n// Why this lives in @mostlyrightmd/core (not @mostlyrightmd/weather): the merge\n// policy is a mostlyright-wide invariant — every settlement-grade path that\n// joins multi-source observations depends on it. Putting it in core keeps\n// the dep direction clean (weather + meta → core), avoids a circular\n// import between weather/parsers and the orchestrator, and matches the\n// Python layout (`_internal/merge/`).\n//\n// Type strategy: a STRUCTURAL `ObservationKey` interface (4 fields) avoids\n// pulling the full `Observation` shape from @mostlyrightmd/weather into core.\n// The function is generic over `T extends ObservationKey` so callers can\n// pass the full row type without losing fields.\n\n/**\n * Source priority — strictly greater means \"wins on tiebreak\".\n * Verbatim from Python `SOURCE_PRIORITY` at\n * `packages/core/src/mostlyright/_internal/merge/observations.py:18`.\n *\n * Frozen so consumers cannot mutate the policy at runtime.\n */\nexport const SOURCE_PRIORITY: Readonly<Record<string, number>> = Object.freeze({\n awc: 3,\n iem: 2,\n ghcnh: 1,\n});\n\n/**\n * Subset of `Observation` that `mergeObservations` needs to dedup +\n * priority-rank. The four fields below form the dedup key (first three)\n * plus the source string that the priority map keys on. Unknown sources\n * resolve to priority 0 (lose to any known source).\n */\nexport interface ObservationKey {\n readonly station_code: string;\n readonly observed_at: string;\n readonly observation_type: string;\n readonly source: string;\n}\n\n/**\n * Deduplicate observation rows by `(station_code, observed_at,\n * observation_type)`, keeping the row whose source has the highest\n * {@link SOURCE_PRIORITY}.\n *\n * Tiebreak: **STRICT `>`** — on equal priority the FIRST row seen wins.\n * This is the byte-faithful semantics of Python v0.14.1, which preserved\n * input order through `dict.values()`. TS uses `Map.values()` with the\n * same insertion-order guarantee.\n *\n * The order-dependent tiebreak is intentional: callers can rely on a\n * canonical fetch order (AWC live → IEM yearly chunk → GHCNh yearly\n * chunk) to deterministically pick the AWC row over IEM/GHCNh on tied\n * priority (which only happens for unknown sources in practice).\n *\n * Unknown source strings resolve to priority 0 (lose to any of awc/iem/\n * ghcnh). Empty input returns an empty array.\n *\n * Output order is `Map.values()` insertion order — first row per key\n * wins both priority AND output position when no later row outranks it.\n *\n * Generic over `T extends ObservationKey` so the consumer (e.g. the\n * `research()` orchestrator in TS-W2 Plan 06) can pass the full\n * `Observation` row type without losing fields. The returned array is\n * a freshly-allocated `T[]`, not the input array.\n */\nexport function mergeObservations<T extends ObservationKey>(\n rows: ReadonlyArray<T>,\n): ReadonlyArray<T> {\n const best = new Map<string, T>();\n for (const row of rows) {\n // Null-byte separator: station_code is `[A-Z]{3,4}`, observed_at is\n // `\\d{4}-...Z`, observation_type is `METAR|SPECI` — none can carry a\n // literal `\\x00`. Defense-in-depth against upstream parser bugs that\n // might leak weird characters.\n const key = `${row.station_code}\\x00${row.observed_at}\\x00${row.observation_type}`;\n const existing = best.get(key);\n if (existing === undefined) {\n best.set(key, row);\n continue;\n }\n const priority = SOURCE_PRIORITY[row.source] ?? 0;\n const existingPriority = SOURCE_PRIORITY[existing.source] ?? 0;\n if (priority > existingPriority) {\n // STRICT `>`: on equal priority the first-seen row stays.\n best.set(key, row);\n }\n }\n return Array.from(best.values());\n}\n","// Climate-row dedup — keep highest `report_type_priority` per (station, date).\n//\n// Migrated from `packages-ts/weather/src/_parsers/cli.ts::mergeClimate`\n// (TS-W1 Wave 4) to its canonical home under @mostlyrightmd/core/internal/merge\n// in TS-W2 Plan 04. Behavior is unchanged — only the module location moves.\n//\n// Byte-faithful TS port of `mostlyright._internal.merge.climate.merge_climate`\n// (Python), itself a lift of `_dedup_climate_rows` from\n// `monorepo-v0.14.1/ingest/storage/parquet.py:477-494`.\n//\n// Type strategy: structural `ClimateKey` interface (3 fields) so this\n// module does not pull `ClimateObservation` from @mostlyrightmd/weather into\n// @mostlyrightmd/core. Callers pass the full row type and the generic\n// preserves it.\n\n/**\n * Subset of `ClimateObservation` that `mergeClimate` needs.\n * The first two fields form the dedup key; `report_type_priority` is the\n * tiebreak field (codegen-sourced from `REPORT_TYPE_PRIORITY`).\n */\nexport interface ClimateKey {\n readonly station_code: string;\n readonly observation_date: string;\n readonly report_type_priority: number;\n}\n\n/**\n * Deduplicate climate rows by `(station_code, observation_date)`.\n *\n * Keeps the row with the highest `report_type_priority` using **STRICT `>`**\n * (not `>=`). First-seen wins at equal priority — this preserves the\n * overnight `final` (which IS the Kalshi settlement value) when a\n * `preliminary` arrives first in input order.\n *\n * Generic over `T extends ClimateKey` so consumers can pass the full\n * `ClimateObservation` shape without losing fields.\n *\n * Empty input returns an empty array.\n */\nexport function mergeClimate<T extends ClimateKey>(rows: ReadonlyArray<T>): ReadonlyArray<T> {\n const best = new Map<string, T>();\n for (const row of rows) {\n // Null-byte separator — station_code is `[A-Z]{3,4}`, observation_date\n // is `YYYY-MM-DD`; neither can carry a literal `\\x00`.\n const key = `${row.station_code}\\x00${row.observation_date}`;\n const existing = best.get(key);\n if (existing === undefined) {\n best.set(key, row);\n continue;\n }\n // Strict `>`; first-seen wins on equal priority.\n if (row.report_type_priority > existing.report_type_priority) {\n best.set(key, row);\n }\n }\n return Array.from(best.values());\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACwBO,IAAM,kBAAoD,OAAO,OAAO;AAAA,EAC7E,KAAK;AAAA,EACL,KAAK;AAAA,EACL,OAAO;AACT,CAAC;AAyCM,SAAS,kBACd,MACkB;AAClB,QAAM,OAAO,oBAAI,IAAe;AAChC,aAAW,OAAO,MAAM;AAKtB,UAAM,MAAM,GAAG,IAAI,YAAY,KAAO,IAAI,WAAW,KAAO,IAAI,gBAAgB;AAChF,UAAM,WAAW,KAAK,IAAI,GAAG;AAC7B,QAAI,aAAa,QAAW;AAC1B,WAAK,IAAI,KAAK,GAAG;AACjB;AAAA,IACF;AACA,UAAM,WAAW,gBAAgB,IAAI,MAAM,KAAK;AAChD,UAAM,mBAAmB,gBAAgB,SAAS,MAAM,KAAK;AAC7D,QAAI,WAAW,kBAAkB;AAE/B,WAAK,IAAI,KAAK,GAAG;AAAA,IACnB;AAAA,EACF;AACA,SAAO,MAAM,KAAK,KAAK,OAAO,CAAC;AACjC;;;ACrDO,SAAS,aAAmC,MAA0C;AAC3F,QAAM,OAAO,oBAAI,IAAe;AAChC,aAAW,OAAO,MAAM;AAGtB,UAAM,MAAM,GAAG,IAAI,YAAY,KAAO,IAAI,gBAAgB;AAC1D,UAAM,WAAW,KAAK,IAAI,GAAG;AAC7B,QAAI,aAAa,QAAW;AAC1B,WAAK,IAAI,KAAK,GAAG;AACjB;AAAA,IACF;AAEA,QAAI,IAAI,uBAAuB,SAAS,sBAAsB;AAC5D,WAAK,IAAI,KAAK,GAAG;AAAA,IACnB;AAAA,EACF;AACA,SAAO,MAAM,KAAK,KAAK,OAAO,CAAC;AACjC;","names":[]}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Source priority — strictly greater means "wins on tiebreak".
3
+ * Verbatim from Python `SOURCE_PRIORITY` at
4
+ * `packages/core/src/mostlyright/_internal/merge/observations.py:18`.
5
+ *
6
+ * Frozen so consumers cannot mutate the policy at runtime.
7
+ */
8
+ declare const SOURCE_PRIORITY: Readonly<Record<string, number>>;
9
+ /**
10
+ * Subset of `Observation` that `mergeObservations` needs to dedup +
11
+ * priority-rank. The four fields below form the dedup key (first three)
12
+ * plus the source string that the priority map keys on. Unknown sources
13
+ * resolve to priority 0 (lose to any known source).
14
+ */
15
+ interface ObservationKey {
16
+ readonly station_code: string;
17
+ readonly observed_at: string;
18
+ readonly observation_type: string;
19
+ readonly source: string;
20
+ }
21
+ /**
22
+ * Deduplicate observation rows by `(station_code, observed_at,
23
+ * observation_type)`, keeping the row whose source has the highest
24
+ * {@link SOURCE_PRIORITY}.
25
+ *
26
+ * Tiebreak: **STRICT `>`** — on equal priority the FIRST row seen wins.
27
+ * This is the byte-faithful semantics of Python v0.14.1, which preserved
28
+ * input order through `dict.values()`. TS uses `Map.values()` with the
29
+ * same insertion-order guarantee.
30
+ *
31
+ * The order-dependent tiebreak is intentional: callers can rely on a
32
+ * canonical fetch order (AWC live → IEM yearly chunk → GHCNh yearly
33
+ * chunk) to deterministically pick the AWC row over IEM/GHCNh on tied
34
+ * priority (which only happens for unknown sources in practice).
35
+ *
36
+ * Unknown source strings resolve to priority 0 (lose to any of awc/iem/
37
+ * ghcnh). Empty input returns an empty array.
38
+ *
39
+ * Output order is `Map.values()` insertion order — first row per key
40
+ * wins both priority AND output position when no later row outranks it.
41
+ *
42
+ * Generic over `T extends ObservationKey` so the consumer (e.g. the
43
+ * `research()` orchestrator in TS-W2 Plan 06) can pass the full
44
+ * `Observation` row type without losing fields. The returned array is
45
+ * a freshly-allocated `T[]`, not the input array.
46
+ */
47
+ declare function mergeObservations<T extends ObservationKey>(rows: ReadonlyArray<T>): ReadonlyArray<T>;
48
+
49
+ /**
50
+ * Subset of `ClimateObservation` that `mergeClimate` needs.
51
+ * The first two fields form the dedup key; `report_type_priority` is the
52
+ * tiebreak field (codegen-sourced from `REPORT_TYPE_PRIORITY`).
53
+ */
54
+ interface ClimateKey {
55
+ readonly station_code: string;
56
+ readonly observation_date: string;
57
+ readonly report_type_priority: number;
58
+ }
59
+ /**
60
+ * Deduplicate climate rows by `(station_code, observation_date)`.
61
+ *
62
+ * Keeps the row with the highest `report_type_priority` using **STRICT `>`**
63
+ * (not `>=`). First-seen wins at equal priority — this preserves the
64
+ * overnight `final` (which IS the Kalshi settlement value) when a
65
+ * `preliminary` arrives first in input order.
66
+ *
67
+ * Generic over `T extends ClimateKey` so consumers can pass the full
68
+ * `ClimateObservation` shape without losing fields.
69
+ *
70
+ * Empty input returns an empty array.
71
+ */
72
+ declare function mergeClimate<T extends ClimateKey>(rows: ReadonlyArray<T>): ReadonlyArray<T>;
73
+
74
+ export { type ClimateKey, type ObservationKey, SOURCE_PRIORITY, mergeClimate, mergeObservations };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Source priority — strictly greater means "wins on tiebreak".
3
+ * Verbatim from Python `SOURCE_PRIORITY` at
4
+ * `packages/core/src/mostlyright/_internal/merge/observations.py:18`.
5
+ *
6
+ * Frozen so consumers cannot mutate the policy at runtime.
7
+ */
8
+ declare const SOURCE_PRIORITY: Readonly<Record<string, number>>;
9
+ /**
10
+ * Subset of `Observation` that `mergeObservations` needs to dedup +
11
+ * priority-rank. The four fields below form the dedup key (first three)
12
+ * plus the source string that the priority map keys on. Unknown sources
13
+ * resolve to priority 0 (lose to any known source).
14
+ */
15
+ interface ObservationKey {
16
+ readonly station_code: string;
17
+ readonly observed_at: string;
18
+ readonly observation_type: string;
19
+ readonly source: string;
20
+ }
21
+ /**
22
+ * Deduplicate observation rows by `(station_code, observed_at,
23
+ * observation_type)`, keeping the row whose source has the highest
24
+ * {@link SOURCE_PRIORITY}.
25
+ *
26
+ * Tiebreak: **STRICT `>`** — on equal priority the FIRST row seen wins.
27
+ * This is the byte-faithful semantics of Python v0.14.1, which preserved
28
+ * input order through `dict.values()`. TS uses `Map.values()` with the
29
+ * same insertion-order guarantee.
30
+ *
31
+ * The order-dependent tiebreak is intentional: callers can rely on a
32
+ * canonical fetch order (AWC live → IEM yearly chunk → GHCNh yearly
33
+ * chunk) to deterministically pick the AWC row over IEM/GHCNh on tied
34
+ * priority (which only happens for unknown sources in practice).
35
+ *
36
+ * Unknown source strings resolve to priority 0 (lose to any of awc/iem/
37
+ * ghcnh). Empty input returns an empty array.
38
+ *
39
+ * Output order is `Map.values()` insertion order — first row per key
40
+ * wins both priority AND output position when no later row outranks it.
41
+ *
42
+ * Generic over `T extends ObservationKey` so the consumer (e.g. the
43
+ * `research()` orchestrator in TS-W2 Plan 06) can pass the full
44
+ * `Observation` row type without losing fields. The returned array is
45
+ * a freshly-allocated `T[]`, not the input array.
46
+ */
47
+ declare function mergeObservations<T extends ObservationKey>(rows: ReadonlyArray<T>): ReadonlyArray<T>;
48
+
49
+ /**
50
+ * Subset of `ClimateObservation` that `mergeClimate` needs.
51
+ * The first two fields form the dedup key; `report_type_priority` is the
52
+ * tiebreak field (codegen-sourced from `REPORT_TYPE_PRIORITY`).
53
+ */
54
+ interface ClimateKey {
55
+ readonly station_code: string;
56
+ readonly observation_date: string;
57
+ readonly report_type_priority: number;
58
+ }
59
+ /**
60
+ * Deduplicate climate rows by `(station_code, observation_date)`.
61
+ *
62
+ * Keeps the row with the highest `report_type_priority` using **STRICT `>`**
63
+ * (not `>=`). First-seen wins at equal priority — this preserves the
64
+ * overnight `final` (which IS the Kalshi settlement value) when a
65
+ * `preliminary` arrives first in input order.
66
+ *
67
+ * Generic over `T extends ClimateKey` so consumers can pass the full
68
+ * `ClimateObservation` shape without losing fields.
69
+ *
70
+ * Empty input returns an empty array.
71
+ */
72
+ declare function mergeClimate<T extends ClimateKey>(rows: ReadonlyArray<T>): ReadonlyArray<T>;
73
+
74
+ export { type ClimateKey, type ObservationKey, SOURCE_PRIORITY, mergeClimate, mergeObservations };
@@ -0,0 +1,46 @@
1
+ // src/internal/merge/observations.ts
2
+ var SOURCE_PRIORITY = Object.freeze({
3
+ awc: 3,
4
+ iem: 2,
5
+ ghcnh: 1
6
+ });
7
+ function mergeObservations(rows) {
8
+ const best = /* @__PURE__ */ new Map();
9
+ for (const row of rows) {
10
+ const key = `${row.station_code}\0${row.observed_at}\0${row.observation_type}`;
11
+ const existing = best.get(key);
12
+ if (existing === void 0) {
13
+ best.set(key, row);
14
+ continue;
15
+ }
16
+ const priority = SOURCE_PRIORITY[row.source] ?? 0;
17
+ const existingPriority = SOURCE_PRIORITY[existing.source] ?? 0;
18
+ if (priority > existingPriority) {
19
+ best.set(key, row);
20
+ }
21
+ }
22
+ return Array.from(best.values());
23
+ }
24
+
25
+ // src/internal/merge/climate.ts
26
+ function mergeClimate(rows) {
27
+ const best = /* @__PURE__ */ new Map();
28
+ for (const row of rows) {
29
+ const key = `${row.station_code}\0${row.observation_date}`;
30
+ const existing = best.get(key);
31
+ if (existing === void 0) {
32
+ best.set(key, row);
33
+ continue;
34
+ }
35
+ if (row.report_type_priority > existing.report_type_priority) {
36
+ best.set(key, row);
37
+ }
38
+ }
39
+ return Array.from(best.values());
40
+ }
41
+ export {
42
+ SOURCE_PRIORITY,
43
+ mergeClimate,
44
+ mergeObservations
45
+ };
46
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/internal/merge/observations.ts","../../../src/internal/merge/climate.ts"],"sourcesContent":["// Observation source-priority dedup — AWC > IEM > GHCNh with strict-> + first-seen tiebreak.\n//\n// Byte-faithful TS port of\n// `packages/core/src/mostlyright/_internal/merge/observations.py::merge_observations`.\n//\n// Why this lives in @mostlyrightmd/core (not @mostlyrightmd/weather): the merge\n// policy is a mostlyright-wide invariant — every settlement-grade path that\n// joins multi-source observations depends on it. Putting it in core keeps\n// the dep direction clean (weather + meta → core), avoids a circular\n// import between weather/parsers and the orchestrator, and matches the\n// Python layout (`_internal/merge/`).\n//\n// Type strategy: a STRUCTURAL `ObservationKey` interface (4 fields) avoids\n// pulling the full `Observation` shape from @mostlyrightmd/weather into core.\n// The function is generic over `T extends ObservationKey` so callers can\n// pass the full row type without losing fields.\n\n/**\n * Source priority — strictly greater means \"wins on tiebreak\".\n * Verbatim from Python `SOURCE_PRIORITY` at\n * `packages/core/src/mostlyright/_internal/merge/observations.py:18`.\n *\n * Frozen so consumers cannot mutate the policy at runtime.\n */\nexport const SOURCE_PRIORITY: Readonly<Record<string, number>> = Object.freeze({\n awc: 3,\n iem: 2,\n ghcnh: 1,\n});\n\n/**\n * Subset of `Observation` that `mergeObservations` needs to dedup +\n * priority-rank. The four fields below form the dedup key (first three)\n * plus the source string that the priority map keys on. Unknown sources\n * resolve to priority 0 (lose to any known source).\n */\nexport interface ObservationKey {\n readonly station_code: string;\n readonly observed_at: string;\n readonly observation_type: string;\n readonly source: string;\n}\n\n/**\n * Deduplicate observation rows by `(station_code, observed_at,\n * observation_type)`, keeping the row whose source has the highest\n * {@link SOURCE_PRIORITY}.\n *\n * Tiebreak: **STRICT `>`** — on equal priority the FIRST row seen wins.\n * This is the byte-faithful semantics of Python v0.14.1, which preserved\n * input order through `dict.values()`. TS uses `Map.values()` with the\n * same insertion-order guarantee.\n *\n * The order-dependent tiebreak is intentional: callers can rely on a\n * canonical fetch order (AWC live → IEM yearly chunk → GHCNh yearly\n * chunk) to deterministically pick the AWC row over IEM/GHCNh on tied\n * priority (which only happens for unknown sources in practice).\n *\n * Unknown source strings resolve to priority 0 (lose to any of awc/iem/\n * ghcnh). Empty input returns an empty array.\n *\n * Output order is `Map.values()` insertion order — first row per key\n * wins both priority AND output position when no later row outranks it.\n *\n * Generic over `T extends ObservationKey` so the consumer (e.g. the\n * `research()` orchestrator in TS-W2 Plan 06) can pass the full\n * `Observation` row type without losing fields. The returned array is\n * a freshly-allocated `T[]`, not the input array.\n */\nexport function mergeObservations<T extends ObservationKey>(\n rows: ReadonlyArray<T>,\n): ReadonlyArray<T> {\n const best = new Map<string, T>();\n for (const row of rows) {\n // Null-byte separator: station_code is `[A-Z]{3,4}`, observed_at is\n // `\\d{4}-...Z`, observation_type is `METAR|SPECI` — none can carry a\n // literal `\\x00`. Defense-in-depth against upstream parser bugs that\n // might leak weird characters.\n const key = `${row.station_code}\\x00${row.observed_at}\\x00${row.observation_type}`;\n const existing = best.get(key);\n if (existing === undefined) {\n best.set(key, row);\n continue;\n }\n const priority = SOURCE_PRIORITY[row.source] ?? 0;\n const existingPriority = SOURCE_PRIORITY[existing.source] ?? 0;\n if (priority > existingPriority) {\n // STRICT `>`: on equal priority the first-seen row stays.\n best.set(key, row);\n }\n }\n return Array.from(best.values());\n}\n","// Climate-row dedup — keep highest `report_type_priority` per (station, date).\n//\n// Migrated from `packages-ts/weather/src/_parsers/cli.ts::mergeClimate`\n// (TS-W1 Wave 4) to its canonical home under @mostlyrightmd/core/internal/merge\n// in TS-W2 Plan 04. Behavior is unchanged — only the module location moves.\n//\n// Byte-faithful TS port of `mostlyright._internal.merge.climate.merge_climate`\n// (Python), itself a lift of `_dedup_climate_rows` from\n// `monorepo-v0.14.1/ingest/storage/parquet.py:477-494`.\n//\n// Type strategy: structural `ClimateKey` interface (3 fields) so this\n// module does not pull `ClimateObservation` from @mostlyrightmd/weather into\n// @mostlyrightmd/core. Callers pass the full row type and the generic\n// preserves it.\n\n/**\n * Subset of `ClimateObservation` that `mergeClimate` needs.\n * The first two fields form the dedup key; `report_type_priority` is the\n * tiebreak field (codegen-sourced from `REPORT_TYPE_PRIORITY`).\n */\nexport interface ClimateKey {\n readonly station_code: string;\n readonly observation_date: string;\n readonly report_type_priority: number;\n}\n\n/**\n * Deduplicate climate rows by `(station_code, observation_date)`.\n *\n * Keeps the row with the highest `report_type_priority` using **STRICT `>`**\n * (not `>=`). First-seen wins at equal priority — this preserves the\n * overnight `final` (which IS the Kalshi settlement value) when a\n * `preliminary` arrives first in input order.\n *\n * Generic over `T extends ClimateKey` so consumers can pass the full\n * `ClimateObservation` shape without losing fields.\n *\n * Empty input returns an empty array.\n */\nexport function mergeClimate<T extends ClimateKey>(rows: ReadonlyArray<T>): ReadonlyArray<T> {\n const best = new Map<string, T>();\n for (const row of rows) {\n // Null-byte separator — station_code is `[A-Z]{3,4}`, observation_date\n // is `YYYY-MM-DD`; neither can carry a literal `\\x00`.\n const key = `${row.station_code}\\x00${row.observation_date}`;\n const existing = best.get(key);\n if (existing === undefined) {\n best.set(key, row);\n continue;\n }\n // Strict `>`; first-seen wins on equal priority.\n if (row.report_type_priority > existing.report_type_priority) {\n best.set(key, row);\n }\n }\n return Array.from(best.values());\n}\n"],"mappings":";AAwBO,IAAM,kBAAoD,OAAO,OAAO;AAAA,EAC7E,KAAK;AAAA,EACL,KAAK;AAAA,EACL,OAAO;AACT,CAAC;AAyCM,SAAS,kBACd,MACkB;AAClB,QAAM,OAAO,oBAAI,IAAe;AAChC,aAAW,OAAO,MAAM;AAKtB,UAAM,MAAM,GAAG,IAAI,YAAY,KAAO,IAAI,WAAW,KAAO,IAAI,gBAAgB;AAChF,UAAM,WAAW,KAAK,IAAI,GAAG;AAC7B,QAAI,aAAa,QAAW;AAC1B,WAAK,IAAI,KAAK,GAAG;AACjB;AAAA,IACF;AACA,UAAM,WAAW,gBAAgB,IAAI,MAAM,KAAK;AAChD,UAAM,mBAAmB,gBAAgB,SAAS,MAAM,KAAK;AAC7D,QAAI,WAAW,kBAAkB;AAE/B,WAAK,IAAI,KAAK,GAAG;AAAA,IACnB;AAAA,EACF;AACA,SAAO,MAAM,KAAK,KAAK,OAAO,CAAC;AACjC;;;ACrDO,SAAS,aAAmC,MAA0C;AAC3F,QAAM,OAAO,oBAAI,IAAe;AAChC,aAAW,OAAO,MAAM;AAGtB,UAAM,MAAM,GAAG,IAAI,YAAY,KAAO,IAAI,gBAAgB;AAC1D,UAAM,WAAW,KAAK,IAAI,GAAG;AAC7B,QAAI,aAAa,QAAW;AAC1B,WAAK,IAAI,KAAK,GAAG;AACjB;AAAA,IACF;AAEA,QAAI,IAAI,uBAAuB,SAAS,sBAAsB;AAC5D,WAAK,IAAI,KAAK,GAAG;AAAA,IACnB;AAAA,EACF;AACA,SAAO,MAAM,KAAK,KAAK,OAAO,CAAC;AACjC;","names":[]}
@@ -0,0 +1,328 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/internal/pairs.ts
21
+ var pairs_exports = {};
22
+ __export(pairs_exports, {
23
+ _obsAggregates: () => _obsAggregates,
24
+ buildPairs: () => buildPairs,
25
+ buildPairsRow: () => buildPairsRow,
26
+ pairsToRows: () => pairsToRows
27
+ });
28
+ module.exports = __toCommonJS(pairs_exports);
29
+
30
+ // src/snapshot.ts
31
+ var _STATION_TZ = Object.freeze({
32
+ // Eastern (UTC-5 standard / UTC-4 DST)
33
+ NYC: "America/New_York",
34
+ JFK: "America/New_York",
35
+ LGA: "America/New_York",
36
+ EWR: "America/New_York",
37
+ ATL: "America/New_York",
38
+ BOS: "America/New_York",
39
+ PHL: "America/New_York",
40
+ DCA: "America/New_York",
41
+ IAD: "America/New_York",
42
+ BWI: "America/New_York",
43
+ MIA: "America/New_York",
44
+ MCO: "America/New_York",
45
+ TPA: "America/New_York",
46
+ CLT: "America/New_York",
47
+ RDU: "America/New_York",
48
+ CLE: "America/New_York",
49
+ PIT: "America/New_York",
50
+ BUF: "America/New_York",
51
+ DTW: "America/Detroit",
52
+ IND: "America/Indiana/Indianapolis",
53
+ CVG: "America/New_York",
54
+ CMH: "America/New_York",
55
+ SYR: "America/New_York",
56
+ ALB: "America/New_York",
57
+ BTV: "America/New_York",
58
+ ORF: "America/New_York",
59
+ RIC: "America/New_York",
60
+ GSO: "America/New_York",
61
+ CHS: "America/New_York",
62
+ SAV: "America/New_York",
63
+ JAX: "America/New_York",
64
+ RSW: "America/New_York",
65
+ PBI: "America/New_York",
66
+ FLL: "America/New_York",
67
+ // Central (UTC-6 standard / UTC-5 DST)
68
+ ORD: "America/Chicago",
69
+ MDW: "America/Chicago",
70
+ DFW: "America/Chicago",
71
+ DAL: "America/Chicago",
72
+ IAH: "America/Chicago",
73
+ HOU: "America/Chicago",
74
+ MSP: "America/Chicago",
75
+ STL: "America/Chicago",
76
+ MCI: "America/Chicago",
77
+ OMA: "America/Chicago",
78
+ MKE: "America/Chicago",
79
+ MSY: "America/Chicago",
80
+ MEM: "America/Chicago",
81
+ BNA: "America/Chicago",
82
+ OKC: "America/Chicago",
83
+ SAT: "America/Chicago",
84
+ AUS: "America/Chicago",
85
+ DSM: "America/Chicago",
86
+ TUL: "America/Chicago",
87
+ LIT: "America/Chicago",
88
+ BIR: "America/Chicago",
89
+ SDF: "America/Chicago",
90
+ HSV: "America/Chicago",
91
+ BHM: "America/Chicago",
92
+ MOB: "America/Chicago",
93
+ BTR: "America/Chicago",
94
+ SHV: "America/Chicago",
95
+ // Mountain (UTC-7 standard / UTC-6 DST)
96
+ DEN: "America/Denver",
97
+ SLC: "America/Denver",
98
+ ABQ: "America/Denver",
99
+ BOI: "America/Boise",
100
+ BZN: "America/Denver",
101
+ GJT: "America/Denver",
102
+ // Arizona: no DST (UTC-7 always)
103
+ PHX: "America/Phoenix",
104
+ TUS: "America/Phoenix",
105
+ // Pacific (UTC-8 standard / UTC-7 DST)
106
+ LAX: "America/Los_Angeles",
107
+ SFO: "America/Los_Angeles",
108
+ SEA: "America/Los_Angeles",
109
+ PDX: "America/Los_Angeles",
110
+ LAS: "America/Los_Angeles",
111
+ SAN: "America/Los_Angeles",
112
+ OAK: "America/Los_Angeles",
113
+ SJC: "America/Los_Angeles",
114
+ SMF: "America/Los_Angeles",
115
+ RNO: "America/Los_Angeles",
116
+ FAT: "America/Los_Angeles",
117
+ SNA: "America/Los_Angeles",
118
+ ONT: "America/Los_Angeles",
119
+ BUR: "America/Los_Angeles",
120
+ // Alaska (UTC-9 standard / UTC-8 DST)
121
+ ANC: "America/Anchorage",
122
+ FAI: "America/Anchorage",
123
+ JNU: "America/Juneau",
124
+ // Hawaii (UTC-10, no DST)
125
+ HNL: "Pacific/Honolulu",
126
+ OGG: "Pacific/Honolulu",
127
+ KOA: "Pacific/Honolulu",
128
+ // International (iter-6 H12): minimal set required to un-skip the
129
+ // case-5 RJTT year-wrap cache behavior test. Python's
130
+ // `mostlyright.snapshot._resolve_tz` falls back to the broader STATIONS
131
+ // registry for intl ICAOs; the TS port hasn't ported that fallback
132
+ // yet (tracked as TS-W6 — exhaustive intl-station tz coverage). This
133
+ // entry closes H12 cleanly without pulling the whole STATIONS map in.
134
+ // ICAO key (RJTT) — international stations have no 3-letter NWS code.
135
+ // Tokyo Haneda — UTC+9 LST, no DST.
136
+ RJTT: "Asia/Tokyo"
137
+ });
138
+ var _JAN_REF = new Date(Date.UTC(2024, 0, 15, 12, 0, 0));
139
+ var _MARKET_CLOSE_HOUR_LST = 16;
140
+ var _MARKET_CLOSE_MINUTE_LST = 30;
141
+ var _OFFSET_CACHE = /* @__PURE__ */ new Map();
142
+ function _lstOffsetHours(stationTz) {
143
+ const cached = _OFFSET_CACHE.get(stationTz);
144
+ if (cached !== void 0) return cached;
145
+ const fmt = new Intl.DateTimeFormat("en-US", {
146
+ timeZone: stationTz,
147
+ hour12: false,
148
+ year: "numeric",
149
+ month: "2-digit",
150
+ day: "2-digit",
151
+ hour: "2-digit",
152
+ minute: "2-digit",
153
+ second: "2-digit"
154
+ });
155
+ const parts = fmt.formatToParts(_JAN_REF);
156
+ const get = (type) => {
157
+ const part = parts.find((p) => p.type === type);
158
+ if (!part) {
159
+ throw new Error(`Intl.DateTimeFormat missing ${type} for tz=${stationTz}`);
160
+ }
161
+ return Number(part.value);
162
+ };
163
+ const year = get("year");
164
+ const month = get("month");
165
+ const day = get("day");
166
+ let hour = get("hour");
167
+ const minute = get("minute");
168
+ const second = get("second");
169
+ if (hour === 24) hour = 0;
170
+ const localAsUtc = Date.UTC(year, month - 1, day, hour, minute, second);
171
+ const offsetMs = localAsUtc - _JAN_REF.getTime();
172
+ const offsetHours = offsetMs / 36e5;
173
+ _OFFSET_CACHE.set(stationTz, offsetHours);
174
+ return offsetHours;
175
+ }
176
+ function _stationCodeNormalized(station) {
177
+ const s = station.trim().toUpperCase();
178
+ if (s.length === 4 && s.startsWith("K")) {
179
+ return s.substring(1);
180
+ }
181
+ return s;
182
+ }
183
+ function _resolveStationTz(station, tzOverride) {
184
+ if (tzOverride) return tzOverride;
185
+ const code = _stationCodeNormalized(station);
186
+ const tz = _STATION_TZ[code];
187
+ if (tz) return tz;
188
+ throw new Error(
189
+ `Unknown station timezone: ${JSON.stringify(code)}. Add it to _STATION_TZ or pass tzOverride="America/...".`
190
+ );
191
+ }
192
+ function marketCloseUtc(dateStr, station, tzOverride) {
193
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dateStr);
194
+ if (!match) {
195
+ throw new Error(`Invalid ISO date for market close: ${JSON.stringify(dateStr)}`);
196
+ }
197
+ const [, yStr, mStr, dStr] = match;
198
+ const year = Number(yStr);
199
+ const month = Number(mStr);
200
+ const day = Number(dStr);
201
+ const tz = _resolveStationTz(station, tzOverride);
202
+ const offsetHours = _lstOffsetHours(tz);
203
+ const marketCloseAsUtcMs = Date.UTC(
204
+ year,
205
+ month - 1,
206
+ day,
207
+ _MARKET_CLOSE_HOUR_LST,
208
+ _MARKET_CLOSE_MINUTE_LST,
209
+ 0
210
+ );
211
+ return new Date(marketCloseAsUtcMs - offsetHours * 36e5);
212
+ }
213
+
214
+ // src/internal/pairs.ts
215
+ function collectNonNull(obs, key) {
216
+ const out = [];
217
+ for (const o of obs) {
218
+ const v = o[key];
219
+ if (typeof v === "number" && Number.isFinite(v)) out.push(v);
220
+ }
221
+ return out;
222
+ }
223
+ function meanOrNull(vs) {
224
+ if (vs.length === 0) return null;
225
+ let s = 0;
226
+ for (const v of vs) s += v;
227
+ return s / vs.length;
228
+ }
229
+ function maxOrNull(vs) {
230
+ if (vs.length === 0) return null;
231
+ let best = vs[0];
232
+ for (let i = 1; i < vs.length; i++) {
233
+ const v = vs[i];
234
+ if (v > best) best = v;
235
+ }
236
+ return best;
237
+ }
238
+ function minOrNull(vs) {
239
+ if (vs.length === 0) return null;
240
+ let best = vs[0];
241
+ for (let i = 1; i < vs.length; i++) {
242
+ const v = vs[i];
243
+ if (v < best) best = v;
244
+ }
245
+ return best;
246
+ }
247
+ function sumOrNull(vs) {
248
+ if (vs.length === 0) return null;
249
+ let s = 0;
250
+ for (const v of vs) s += v;
251
+ return s;
252
+ }
253
+ function _obsAggregates(observations) {
254
+ if (observations.length === 0) {
255
+ return Object.freeze({
256
+ obs_high_f: null,
257
+ obs_low_f: null,
258
+ obs_mean_f: null,
259
+ obs_mean_dewpoint_f: null,
260
+ obs_max_wind_kt: null,
261
+ obs_max_gust_kt: null,
262
+ obs_total_precip_in: null,
263
+ obs_count: 0
264
+ });
265
+ }
266
+ const temps = collectNonNull(observations, "temp_f");
267
+ const dewps = collectNonNull(observations, "dewpoint_f");
268
+ const winds = collectNonNull(observations, "wind_speed_kt");
269
+ const gusts = collectNonNull(observations, "wind_gust_kt");
270
+ const precips = collectNonNull(observations, "precip_1hr_inches");
271
+ return Object.freeze({
272
+ obs_high_f: maxOrNull(temps),
273
+ obs_low_f: minOrNull(temps),
274
+ obs_mean_f: meanOrNull(temps),
275
+ obs_mean_dewpoint_f: meanOrNull(dewps),
276
+ obs_max_wind_kt: maxOrNull(winds),
277
+ obs_max_gust_kt: maxOrNull(gusts),
278
+ obs_total_precip_in: sumOrNull(precips),
279
+ obs_count: observations.length
280
+ });
281
+ }
282
+ function buildPairsRow(dateStr, station, observations, climate, opts = {}) {
283
+ const obsAgg = _obsAggregates(observations);
284
+ const closeUtc = marketCloseUtc(dateStr, station, opts.tzOverride);
285
+ const closeIso = `${closeUtc.toISOString().slice(0, 19)}Z`;
286
+ return Object.freeze({
287
+ date: dateStr,
288
+ station,
289
+ cli_high_f: climate ? climate.high_temp_f : null,
290
+ cli_low_f: climate ? climate.low_temp_f : null,
291
+ cli_report_type: climate ? climate.report_type : null,
292
+ obs_high_f: obsAgg.obs_high_f,
293
+ obs_low_f: obsAgg.obs_low_f,
294
+ obs_mean_f: obsAgg.obs_mean_f,
295
+ obs_mean_dewpoint_f: obsAgg.obs_mean_dewpoint_f,
296
+ obs_max_wind_kt: obsAgg.obs_max_wind_kt,
297
+ obs_max_gust_kt: obsAgg.obs_max_gust_kt,
298
+ obs_total_precip_in: obsAgg.obs_total_precip_in,
299
+ obs_count: obsAgg.obs_count,
300
+ fcst_high_f: null,
301
+ fcst_low_f: null,
302
+ fcst_model: null,
303
+ fcst_issued_at: null,
304
+ fcst_pop_6hr_pct: null,
305
+ fcst_qpf_6hr_in: null,
306
+ market_close_utc: closeIso
307
+ });
308
+ }
309
+ function buildPairs(station, dates, observationsByDate, climateByDate, opts = {}) {
310
+ const out = [];
311
+ for (const date of dates) {
312
+ const obs = observationsByDate[date] ?? [];
313
+ const climate = climateByDate[date] ?? null;
314
+ out.push(buildPairsRow(date, station, obs, climate, opts));
315
+ }
316
+ return Object.freeze(out);
317
+ }
318
+ function pairsToRows(rows) {
319
+ return rows;
320
+ }
321
+ // Annotate the CommonJS export names for ESM import in node:
322
+ 0 && (module.exports = {
323
+ _obsAggregates,
324
+ buildPairs,
325
+ buildPairsRow,
326
+ pairsToRows
327
+ });
328
+ //# sourceMappingURL=pairs.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/pairs.ts","../../src/snapshot.ts"],"sourcesContent":["// buildPairs + _obsAggregates + pairsToRows — settlement-day row builder.\n//\n// Byte-faithful TS port of Python\n// `packages/core/src/mostlyright/_internal/_pairs.py::build_pairs` (Mode 1\n// subset — no forecast wiring; all fcst_* columns unconditionally null).\n//\n// The full Python `_select_best_run` / `_aggregate_fcst_temps_*` paths\n// (IEM MOS + Open-Meteo) are intentionally NOT ported here; forecast\n// support lands in TS-W5+. Same scope cut TS-W1 made for `research()`.\n//\n// Type strategy: structural `PairsObservationLike` + `PairsClimateLike`\n// interfaces. The full `Observation` (from weather/_parsers/awc.ts) and\n// `ClimateObservation` (from weather/_parsers/cli.ts) structurally\n// satisfy them — avoids a circular import + matches the Plan 04\n// `ObservationKey` discipline.\n\nimport { marketCloseUtc } from \"../snapshot.js\";\n\n/** Subset of fields `_obsAggregates` reads from each observation row. */\nexport interface PairsObservationLike {\n readonly temp_f?: number | null;\n readonly dewpoint_f?: number | null;\n readonly wind_speed_kt?: number | null;\n readonly wind_gust_kt?: number | null;\n readonly precip_1hr_inches?: number | null;\n}\n\n/** Subset of `ClimateObservation` fields buildPairs reads from each CLI row. */\nexport interface PairsClimateLike {\n readonly high_temp_f: number | null;\n readonly low_temp_f: number | null;\n readonly report_type: string;\n}\n\n/** Aggregated observation summary for one settlement day. */\nexport interface ObsAggregates {\n readonly obs_high_f: number | null;\n readonly obs_low_f: number | null;\n readonly obs_mean_f: number | null;\n readonly obs_mean_dewpoint_f: number | null;\n readonly obs_max_wind_kt: number | null;\n readonly obs_max_gust_kt: number | null;\n readonly obs_total_precip_in: number | null;\n readonly obs_count: number;\n}\n\n/**\n * One settlement-date row — 20 columns, byte-shape-equivalent to Python\n * `build_pairs_row` output. The `fcst_*` columns are unconditionally\n * `null` in TS-W2 (Mode 1 only — forecast wiring is TS-W5+).\n *\n * Object-key order is preserved verbatim so `JSON.stringify` produces\n * column ordering byte-stable across SDKs.\n */\nexport interface PairsRow {\n readonly date: string;\n readonly station: string;\n readonly cli_high_f: number | null;\n readonly cli_low_f: number | null;\n readonly cli_report_type: string | null;\n readonly obs_high_f: number | null;\n readonly obs_low_f: number | null;\n readonly obs_mean_f: number | null;\n readonly obs_mean_dewpoint_f: number | null;\n readonly obs_max_wind_kt: number | null;\n readonly obs_max_gust_kt: number | null;\n readonly obs_total_precip_in: number | null;\n readonly obs_count: number;\n readonly fcst_high_f: null;\n readonly fcst_low_f: null;\n readonly fcst_model: null;\n readonly fcst_issued_at: null;\n readonly fcst_pop_6hr_pct: null;\n readonly fcst_qpf_6hr_in: null;\n readonly market_close_utc: string;\n}\n\nexport interface BuildPairsOptions {\n /** Forwarded to `marketCloseUtc` (rare — used for synthetic test stations). */\n readonly tzOverride?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Aggregation helpers\n// ---------------------------------------------------------------------------\n\nfunction collectNonNull(\n obs: ReadonlyArray<PairsObservationLike>,\n key: keyof PairsObservationLike,\n): number[] {\n const out: number[] = [];\n for (const o of obs) {\n const v = o[key];\n if (typeof v === \"number\" && Number.isFinite(v)) out.push(v);\n }\n return out;\n}\n\nfunction meanOrNull(vs: number[]): number | null {\n if (vs.length === 0) return null;\n let s = 0;\n for (const v of vs) s += v;\n return s / vs.length;\n}\n\nfunction maxOrNull(vs: number[]): number | null {\n if (vs.length === 0) return null;\n let best = vs[0] as number;\n for (let i = 1; i < vs.length; i++) {\n const v = vs[i] as number;\n if (v > best) best = v;\n }\n return best;\n}\n\nfunction minOrNull(vs: number[]): number | null {\n if (vs.length === 0) return null;\n let best = vs[0] as number;\n for (let i = 1; i < vs.length; i++) {\n const v = vs[i] as number;\n if (v < best) best = v;\n }\n return best;\n}\n\nfunction sumOrNull(vs: number[]): number | null {\n if (vs.length === 0) return null;\n let s = 0;\n for (const v of vs) s += v;\n return s;\n}\n\n// ---------------------------------------------------------------------------\n// Aggregator\n// ---------------------------------------------------------------------------\n\n/**\n * Aggregate one day's observation rows into the 8-field `obs_*` summary.\n *\n * Rules (byte-faithful with Python `_obs_aggregates` at `_pairs.py:97-150`):\n * - obs_high_f / obs_low_f / obs_mean_f: max / min / arithmetic mean over\n * non-null `temp_f`. Mean-of-null-only → null.\n * - obs_mean_dewpoint_f: mean over non-null `dewpoint_f`.\n * - obs_max_wind_kt / obs_max_gust_kt: max over non-null wind/gust.\n * - obs_total_precip_in: sum over non-null precip; `null` if NO non-null\n * precip rows (mirrors Python `sum(precips) if precips else None`).\n * - obs_count: total row count, INCLUDING rows where every measure is null.\n *\n * Numeric-stability note: mean is non-associative for floats. Callers MUST\n * pass observations in a deterministic order to preserve byte-equivalent\n * float aggregation. Plan 06's research orchestrator sorts by\n * `(observed_at, source)` before calling this.\n *\n * Returns a `Object.freeze`-d aggregate with key order matching Python.\n */\nexport function _obsAggregates(observations: ReadonlyArray<PairsObservationLike>): ObsAggregates {\n if (observations.length === 0) {\n return Object.freeze({\n obs_high_f: null,\n obs_low_f: null,\n obs_mean_f: null,\n obs_mean_dewpoint_f: null,\n obs_max_wind_kt: null,\n obs_max_gust_kt: null,\n obs_total_precip_in: null,\n obs_count: 0,\n });\n }\n const temps = collectNonNull(observations, \"temp_f\");\n const dewps = collectNonNull(observations, \"dewpoint_f\");\n const winds = collectNonNull(observations, \"wind_speed_kt\");\n const gusts = collectNonNull(observations, \"wind_gust_kt\");\n const precips = collectNonNull(observations, \"precip_1hr_inches\");\n return Object.freeze({\n obs_high_f: maxOrNull(temps),\n obs_low_f: minOrNull(temps),\n obs_mean_f: meanOrNull(temps),\n obs_mean_dewpoint_f: meanOrNull(dewps),\n obs_max_wind_kt: maxOrNull(winds),\n obs_max_gust_kt: maxOrNull(gusts),\n obs_total_precip_in: sumOrNull(precips),\n obs_count: observations.length,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Row + batch builders\n// ---------------------------------------------------------------------------\n\n/**\n * Build one PairsRow for a given (station, date) from its observation +\n * climate inputs. Mode 1 only — fcst_* are unconditionally null.\n *\n * `market_close_utc` is formatted `YYYY-MM-DDTHH:MM:SSZ` (no milliseconds)\n * via `Date.toISOString().slice(0, 19) + \"Z\"` — mirrors Python strftime.\n */\nexport function buildPairsRow(\n dateStr: string,\n station: string,\n observations: ReadonlyArray<PairsObservationLike>,\n climate: PairsClimateLike | null,\n opts: BuildPairsOptions = {},\n): PairsRow {\n const obsAgg = _obsAggregates(observations);\n const closeUtc = marketCloseUtc(dateStr, station, opts.tzOverride);\n const closeIso = `${closeUtc.toISOString().slice(0, 19)}Z`;\n return Object.freeze({\n date: dateStr,\n station,\n cli_high_f: climate ? climate.high_temp_f : null,\n cli_low_f: climate ? climate.low_temp_f : null,\n cli_report_type: climate ? climate.report_type : null,\n obs_high_f: obsAgg.obs_high_f,\n obs_low_f: obsAgg.obs_low_f,\n obs_mean_f: obsAgg.obs_mean_f,\n obs_mean_dewpoint_f: obsAgg.obs_mean_dewpoint_f,\n obs_max_wind_kt: obsAgg.obs_max_wind_kt,\n obs_max_gust_kt: obsAgg.obs_max_gust_kt,\n obs_total_precip_in: obsAgg.obs_total_precip_in,\n obs_count: obsAgg.obs_count,\n fcst_high_f: null,\n fcst_low_f: null,\n fcst_model: null,\n fcst_issued_at: null,\n fcst_pop_6hr_pct: null,\n fcst_qpf_6hr_in: null,\n market_close_utc: closeIso,\n });\n}\n\n/**\n * Build PairsRows for every date in `dates` (input-order preserved).\n *\n * `observationsByDate[date]` and `climateByDate[date]` are looked up\n * defensively — missing keys are treated as empty obs / null climate.\n *\n * Returns a `Object.freeze`-d array.\n */\nexport function buildPairs(\n station: string,\n dates: ReadonlyArray<string>,\n observationsByDate: Readonly<Record<string, ReadonlyArray<PairsObservationLike>>>,\n climateByDate: Readonly<Record<string, PairsClimateLike | null>>,\n opts: BuildPairsOptions = {},\n): ReadonlyArray<PairsRow> {\n const out: PairsRow[] = [];\n for (const date of dates) {\n const obs = observationsByDate[date] ?? [];\n const climate = climateByDate[date] ?? null;\n out.push(buildPairsRow(date, station, obs, climate, opts));\n }\n return Object.freeze(out);\n}\n\n/**\n * Surface-parity alias of `buildPairs` output. Python's `pairs_to_dataframe`\n * converts the list[dict] into a pandas DataFrame indexed by date; TS has\n * no DataFrame, so this is identity. Exists for cross-SDK signature parity\n * per CROSS-SDK-SYNC.md.\n */\nexport function pairsToRows(rows: ReadonlyArray<PairsRow>): ReadonlyArray<PairsRow> {\n return rows;\n}\n","// Snapshot math — settlement-window and market-close arithmetic.\n//\n// Ported from `packages/core/src/mostlyright/snapshot.py` and\n// `packages/core/src/mostlyright/_internal/_pairs.py:market_close_utc`.\n//\n// Key concepts:\n// - LOCAL STANDARD TIME (LST): station's standard UTC offset, DST ignored.\n// Kalshi NHIGH/NLOW contracts define the settlement window in LST.\n// - Settlement window: midnight-midnight LST for a given date.\n// During US daylight saving the clock window is 1:00 AM–1:00 AM next day\n// (EDT), but the UTC bounds are the same year-round.\n// - CLI publication delay: NWS issues the overnight final CLI ~04:00–10:00\n// UTC the day after observation. Default: 10 h after midnight LST.\n\n// ---------------------------------------------------------------------------\n// Station → IANA timezone database\n// ---------------------------------------------------------------------------\n//\n// Used to extract the LOCAL STANDARD TIME UTC offset via a January reference\n// moment. Ported from `mostlyright.snapshot._STATION_TZ`.\n\nexport const _STATION_TZ: Readonly<Record<string, string>> = Object.freeze({\n // Eastern (UTC-5 standard / UTC-4 DST)\n NYC: \"America/New_York\",\n JFK: \"America/New_York\",\n LGA: \"America/New_York\",\n EWR: \"America/New_York\",\n ATL: \"America/New_York\",\n BOS: \"America/New_York\",\n PHL: \"America/New_York\",\n DCA: \"America/New_York\",\n IAD: \"America/New_York\",\n BWI: \"America/New_York\",\n MIA: \"America/New_York\",\n MCO: \"America/New_York\",\n TPA: \"America/New_York\",\n CLT: \"America/New_York\",\n RDU: \"America/New_York\",\n CLE: \"America/New_York\",\n PIT: \"America/New_York\",\n BUF: \"America/New_York\",\n DTW: \"America/Detroit\",\n IND: \"America/Indiana/Indianapolis\",\n CVG: \"America/New_York\",\n CMH: \"America/New_York\",\n SYR: \"America/New_York\",\n ALB: \"America/New_York\",\n BTV: \"America/New_York\",\n ORF: \"America/New_York\",\n RIC: \"America/New_York\",\n GSO: \"America/New_York\",\n CHS: \"America/New_York\",\n SAV: \"America/New_York\",\n JAX: \"America/New_York\",\n RSW: \"America/New_York\",\n PBI: \"America/New_York\",\n FLL: \"America/New_York\",\n // Central (UTC-6 standard / UTC-5 DST)\n ORD: \"America/Chicago\",\n MDW: \"America/Chicago\",\n DFW: \"America/Chicago\",\n DAL: \"America/Chicago\",\n IAH: \"America/Chicago\",\n HOU: \"America/Chicago\",\n MSP: \"America/Chicago\",\n STL: \"America/Chicago\",\n MCI: \"America/Chicago\",\n OMA: \"America/Chicago\",\n MKE: \"America/Chicago\",\n MSY: \"America/Chicago\",\n MEM: \"America/Chicago\",\n BNA: \"America/Chicago\",\n OKC: \"America/Chicago\",\n SAT: \"America/Chicago\",\n AUS: \"America/Chicago\",\n DSM: \"America/Chicago\",\n TUL: \"America/Chicago\",\n LIT: \"America/Chicago\",\n BIR: \"America/Chicago\",\n SDF: \"America/Chicago\",\n HSV: \"America/Chicago\",\n BHM: \"America/Chicago\",\n MOB: \"America/Chicago\",\n BTR: \"America/Chicago\",\n SHV: \"America/Chicago\",\n // Mountain (UTC-7 standard / UTC-6 DST)\n DEN: \"America/Denver\",\n SLC: \"America/Denver\",\n ABQ: \"America/Denver\",\n BOI: \"America/Boise\",\n BZN: \"America/Denver\",\n GJT: \"America/Denver\",\n // Arizona: no DST (UTC-7 always)\n PHX: \"America/Phoenix\",\n TUS: \"America/Phoenix\",\n // Pacific (UTC-8 standard / UTC-7 DST)\n LAX: \"America/Los_Angeles\",\n SFO: \"America/Los_Angeles\",\n SEA: \"America/Los_Angeles\",\n PDX: \"America/Los_Angeles\",\n LAS: \"America/Los_Angeles\",\n SAN: \"America/Los_Angeles\",\n OAK: \"America/Los_Angeles\",\n SJC: \"America/Los_Angeles\",\n SMF: \"America/Los_Angeles\",\n RNO: \"America/Los_Angeles\",\n FAT: \"America/Los_Angeles\",\n SNA: \"America/Los_Angeles\",\n ONT: \"America/Los_Angeles\",\n BUR: \"America/Los_Angeles\",\n // Alaska (UTC-9 standard / UTC-8 DST)\n ANC: \"America/Anchorage\",\n FAI: \"America/Anchorage\",\n JNU: \"America/Juneau\",\n // Hawaii (UTC-10, no DST)\n HNL: \"Pacific/Honolulu\",\n OGG: \"Pacific/Honolulu\",\n KOA: \"Pacific/Honolulu\",\n // International (iter-6 H12): minimal set required to un-skip the\n // case-5 RJTT year-wrap cache behavior test. Python's\n // `mostlyright.snapshot._resolve_tz` falls back to the broader STATIONS\n // registry for intl ICAOs; the TS port hasn't ported that fallback\n // yet (tracked as TS-W6 — exhaustive intl-station tz coverage). This\n // entry closes H12 cleanly without pulling the whole STATIONS map in.\n // ICAO key (RJTT) — international stations have no 3-letter NWS code.\n // Tokyo Haneda — UTC+9 LST, no DST.\n RJTT: \"Asia/Tokyo\",\n});\n\n/** Reference UTC moment in January (no DST in Northern Hemisphere US). */\nexport const _JAN_REF = new Date(Date.UTC(2024, 0, 15, 12, 0, 0));\n\n/** NWS CLI typical publication delay: 10 h after midnight LST. */\nexport const _CLI_PUBLICATION_DELAY_HOURS = 10.0;\n\n/** Kalshi market typical close time (LST). */\nexport const _MARKET_CLOSE_HOUR_LST = 16;\nexport const _MARKET_CLOSE_MINUTE_LST = 30;\n\n// ---------------------------------------------------------------------------\n// LST offset extraction\n// ---------------------------------------------------------------------------\n\nconst _OFFSET_CACHE = new Map<string, number>();\n\n/**\n * Return the LOCAL STANDARD TIME UTC offset (in hours) for an IANA tz,\n * sampled from January 15 2024 12:00 UTC so the result is never affected\n * by DST in the Northern Hemisphere.\n *\n * Implementation: format `_JAN_REF` in the target tz via Intl.DateTimeFormat\n * and diff against the UTC formatted view to recover the offset.\n */\nexport function _lstOffsetHours(stationTz: string): number {\n const cached = _OFFSET_CACHE.get(stationTz);\n if (cached !== undefined) return cached;\n\n // We compute: localComponents(stationTz, _JAN_REF) − utcComponents(_JAN_REF).\n // The difference gives the tz offset in (hours). Negative for west of UTC.\n const fmt = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: stationTz,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n const parts = fmt.formatToParts(_JAN_REF);\n const get = (type: string): number => {\n const part = parts.find((p) => p.type === type);\n if (!part) {\n throw new Error(`Intl.DateTimeFormat missing ${type} for tz=${stationTz}`);\n }\n return Number(part.value);\n };\n\n const year = get(\"year\");\n const month = get(\"month\");\n const day = get(\"day\");\n let hour = get(\"hour\");\n const minute = get(\"minute\");\n const second = get(\"second\");\n // Some locales return hour \"24\" instead of \"00\" for midnight; normalize.\n if (hour === 24) hour = 0;\n\n // Compute the timezone's wall-clock for _JAN_REF treated as UTC.\n const localAsUtc = Date.UTC(year, month - 1, day, hour, minute, second);\n const offsetMs = localAsUtc - _JAN_REF.getTime();\n const offsetHours = offsetMs / 3_600_000;\n _OFFSET_CACHE.set(stationTz, offsetHours);\n return offsetHours;\n}\n\n// ---------------------------------------------------------------------------\n// Station code normalization + tz lookup\n// ---------------------------------------------------------------------------\n\nfunction _stationCodeNormalized(station: string): string {\n const s = station.trim().toUpperCase();\n if (s.length === 4 && s.startsWith(\"K\")) {\n return s.substring(1);\n }\n return s;\n}\n\n/**\n * Resolve a station code (NWS 3-letter, ICAO 4-letter) to an IANA tz string.\n * Honors `tzOverride` first, then the built-in `_STATION_TZ` map.\n * Throws if no tz can be resolved.\n */\nexport function _resolveStationTz(station: string, tzOverride?: string): string {\n if (tzOverride) return tzOverride;\n const code = _stationCodeNormalized(station);\n const tz = _STATION_TZ[code];\n if (tz) return tz;\n throw new Error(\n `Unknown station timezone: ${JSON.stringify(code)}. Add it to _STATION_TZ or pass tzOverride=\"America/...\".`,\n );\n}\n\n// ---------------------------------------------------------------------------\n// as_of parsing\n// ---------------------------------------------------------------------------\n\nfunction _parseAsOf(asOf: Date | string): Date {\n if (asOf instanceof Date) {\n if (Number.isNaN(asOf.getTime())) {\n throw new Error(\"Invalid Date passed as asOf\");\n }\n return asOf;\n }\n let s = asOf.trim();\n // Python: bare ISO without tz → assume UTC.\n if (s.endsWith(\"Z\")) {\n // Date.parse handles \"Z\" natively.\n } else if (!/[+-]\\d{2}:?\\d{2}$/.test(s)) {\n // No timezone suffix — treat as UTC.\n s = `${s}Z`;\n }\n const ms = Date.parse(s);\n if (!Number.isFinite(ms)) {\n throw new Error(`Invalid as_of string: ${JSON.stringify(asOf)}`);\n }\n return new Date(ms);\n}\n\n// ---------------------------------------------------------------------------\n// Public surface\n// ---------------------------------------------------------------------------\n\nfunction _pad2(n: number): string {\n return n < 10 ? `0${n}` : `${n}`;\n}\n\nfunction _isoDate(year: number, month: number, day: number): string {\n return `${year}-${_pad2(month)}-${_pad2(day)}`;\n}\n\n/**\n * Return the Kalshi settlement date (YYYY-MM-DD LST) for a UTC moment.\n *\n * Kalshi NHIGH/NLOW contracts cover midnight–midnight LOCAL STANDARD TIME.\n * DST is ignored: the window is always fixed to the standard UTC offset.\n */\nexport function settlementDateFor(\n asOf: Date | string,\n station: string,\n tzOverride?: string,\n): string {\n const utcDt = _parseAsOf(asOf);\n const tz = _resolveStationTz(station, tzOverride);\n const offsetHours = _lstOffsetHours(tz);\n // offsetHours is negative for US stations → lstMs < utcMs.\n const lstMs = utcDt.getTime() + offsetHours * 3_600_000;\n const lst = new Date(lstMs);\n // Use getUTC* because we already shifted the epoch by the LST offset.\n return _isoDate(lst.getUTCFullYear(), lst.getUTCMonth() + 1, lst.getUTCDate());\n}\n\n/**\n * Return UTC start/end of the Kalshi settlement window for a date.\n * The window is midnight-midnight LST, expressed in UTC.\n */\nexport function settlementWindowUtc(\n dateStr: string,\n station: string,\n tzOverride?: string,\n): [Date, Date] {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(dateStr);\n if (!match) {\n throw new Error(`Invalid ISO date for settlement window: ${JSON.stringify(dateStr)}`);\n }\n const [, yStr, mStr, dStr] = match;\n const year = Number(yStr);\n const month = Number(mStr);\n const day = Number(dStr);\n const tz = _resolveStationTz(station, tzOverride);\n const offsetHours = _lstOffsetHours(tz);\n\n // midnight LST = 00:00 LST = (00:00 UTC) − offset (offset is negative)\n // Example: UTC-5 → midnight LST = 05:00 UTC.\n const midnightLstAsUtcMs = Date.UTC(year, month - 1, day, 0, 0, 0);\n const startMs = midnightLstAsUtcMs - offsetHours * 3_600_000;\n const start = new Date(startMs);\n const end = new Date(startMs + 24 * 3_600_000);\n return [start, end];\n}\n\n/**\n * Return the UTC time at which the NWS CLI for a date is expected to be\n * available. Default delay is 10 h after midnight LST on the next day.\n */\nexport function cliAvailableAt(\n dateStr: string,\n station: string,\n delayHours: number = _CLI_PUBLICATION_DELAY_HOURS,\n tzOverride?: string,\n): Date {\n const [, windowEnd] = settlementWindowUtc(dateStr, station, tzOverride);\n return new Date(windowEnd.getTime() + delayHours * 3_600_000);\n}\n\n/**\n * Return the UTC time of the Kalshi market close for a settlement date.\n * Kalshi NHIGH/NLOW markets close at 4:30 PM LST on the day of settlement.\n */\nexport function marketCloseUtc(dateStr: string, station: string, tzOverride?: string): Date {\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(dateStr);\n if (!match) {\n throw new Error(`Invalid ISO date for market close: ${JSON.stringify(dateStr)}`);\n }\n const [, yStr, mStr, dStr] = match;\n const year = Number(yStr);\n const month = Number(mStr);\n const day = Number(dStr);\n const tz = _resolveStationTz(station, tzOverride);\n const offsetHours = _lstOffsetHours(tz);\n\n const marketCloseAsUtcMs = Date.UTC(\n year,\n month - 1,\n day,\n _MARKET_CLOSE_HOUR_LST,\n _MARKET_CLOSE_MINUTE_LST,\n 0,\n );\n return new Date(marketCloseAsUtcMs - offsetHours * 3_600_000);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACqBO,IAAM,cAAgD,OAAO,OAAO;AAAA;AAAA,EAEzE,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA;AAAA,EAEL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA;AAAA,EAEL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA;AAAA,EAEL,KAAK;AAAA,EACL,KAAK;AAAA;AAAA,EAEL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,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","names":[]}