Skip to content

refactor: extract pure computation helpers and correction functions#53

Closed
Wolfvin wants to merge 1 commit into
kalenderjawa:masterfrom
Wolfvin:refactor/extract-pure-helpers
Closed

refactor: extract pure computation helpers and correction functions#53
Wolfvin wants to merge 1 commit into
kalenderjawa:masterfrom
Wolfvin:refactor/extract-pure-helpers

Conversation

@Wolfvin

@Wolfvin Wolfvin commented Jun 13, 2026

Copy link
Copy Markdown

Refactoring Summary

This PR introduces structural improvements to the Pustaka Kalender Jawa codebase without changing any behavioral output. All changes have been verified using a 3-layer regression testing approach.

Changes Made

1. Extracted Pure Computation Helpers (silpin.ts)

The konversiHari and konversiPasaran functions contained inline arithmetic mixed with async Promise wrapping. The pure computation has been extracted into hitungDino(h, dn) and hitungPasaran(p, ps) — small, testable, pure functions that compute the adjusted index. The async wrappers now delegate to these helpers.

2. Extracted Correction Functions (koreksi.ts)

koreksiDino and koreksiPasaran were inline helpers in index.ts. Moved to dedicated koreksi.ts module with proper JSDoc documentation.

3. Moved Interface to Shared Types

BATASAN_TYPE renamed to BatasanType and moved from batur.ts to types.ts, consistent with the project pattern.

4. Exported Bounds Constants

MIN_TAHUNJAWA and MAX_TAHUNJAWA are now exported from batur.ts.

5. Improved Documentation

Added JSDoc comments explaining Javanese calendar concepts.

3-Layer Verification

All 6 behavioral clusters were captured before refactoring and validated after:

Cluster Fingerprint Status
periksa-batasan 51znv8u PASS
konversi-hari 64oy02k PASS
konversi-pasaran 2pfjg1m PASS
cari-wulan-registry 1dmh965 PASS
cari-taun-registry 27zqef3 PASS
cari-rumus-wulan-taun i77j9w9 PASS
  • Verification 1: Regrets fingerprint validation — all GREEN
  • Verification 2: Direct output comparison — all outputs identical to pre-refactor
  • Verification 3: Cross-fingerprint verification — all recomputed fingerprints match
  • Existing tests: All 105 Vitest tests pass

See docs/REFACTOR_VERIFICATION.md for the full verification report.

Summary by CodeRabbit

  • Refactor

    • Code restructured for improved organization and maintainability
    • Internal logic consolidated while preserving public APIs
    • All 105 tests passing with comprehensive verification completed
  • Documentation

    • Verification documentation added to track refactoring changes

This refactoring improves code organization without changing any
behavioral output. Verified with 3-layer regression testing
using the Regrets tool (all 6 clusters GREEN, all outputs identical).

Changes:
- Extract hitungDino/hitungPasaran pure helpers from silpin.ts
  (computes adjusted day/pasaran index without async wrapping)
- Extract koreksiDino/koreksiPasaran to new koreksi.ts module
  (cycle wrapping functions for 7-day week and 5-day pasaran)
- Move BATASAN_TYPE to types.ts as BatasanType (shared interface)
- Export MIN_TAHUNJAWA/MAX_TAHUNJAWA constants from batur.ts
- Add JSDoc documentation to all refactored functions

Verification details in docs/REFACTOR_VERIFICATION.md
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR refactors a Javanese calendar library by extracting pure computation helpers, consolidating shared types, and reorganizing correction logic across modules. A new BatasanType interface centralizes validation results, periksaBatasan is simplified and exports year bounds, a dedicated koreksi.ts module provides index normalization, and silpin.ts splits index computations into pure helpers while maintaining all public APIs.

Changes

Structural refactoring with pure helpers and shared types

Layer / File(s) Summary
Shared BatasanType definition
src/types.ts
Exported interface BatasanType with status: boolean and message: string for boundary validation results.
Boundary validation with shared type
src/batur.ts
Imports BatasanType from types, exports MIN_TAHUNJAWA/MAX_TAHUNJAWA constants, and simplifies periksaBatasan to compute inScope boolean and derive status and message.
Pure correction helpers module
src/koreksi.ts
New module exporting koreksiDino (normalizes 1–7 day indices) and koreksiPasaran (normalizes 1–5 pasaran indices), both using modulo-wrapping with remainder-0-to-max adjustment.
Correction helpers integration in main module
src/index.ts
Imports koreksiDino and koreksiPasaran from koreksi.ts, removes local implementations, updates header comment to document extraction.
Index computation extraction in silpin.ts
src/silpin.ts
Extracts pure helpers hitungDino and hitungPasaran for wrapping indices; refactors konversiHari and konversiPasaran to use helpers before registry lookup; adds registry function JSDoc; exports new helpers.
Refactoring verification report
docs/REFACTOR_VERIFICATION.md
Documents four structural refactors, verification methodology (fingerprint, direct output, cross-fingerprint), and confirms 105 Vitest tests pass.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A rabbit hops through refactored code,
Pure helpers spring from tangled roads,
Shared types unite the scattered ways,
Clean modular paths light all our days!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main structural refactoring work: extracting pure computation helpers and correction functions, which aligns with the primary changes across multiple files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/koreksi.ts (1)

17-29: ⚡ Quick win

Add input validation to both correction helpers for robustness. Both koreksiDino and koreksiPasaran lack validation for non-positive or non-integer inputs; passing d <= 0 or p <= 0 causes negative array indexing (DINTEN_ARR[-1] or PASARAN_ARR[-1]undefined). While current callers initialize indices from .urutan properties (≥ 1) and only increment, extracting these functions into a standalone module benefits from defensive programming.

  • src/koreksi.ts#L17-L29: validate d >= 1 and Number.isInteger(d) before processing in koreksiDino.
  • src/koreksi.ts#L37-L51: validate p >= 1 and Number.isInteger(p) before processing in koreksiPasaran.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/koreksi.ts` around lines 17 - 29, Add defensive input validation to both
functions: in koreksiDino (src/koreksi.ts lines 17-29) and koreksiPasaran
(src/koreksi.ts lines 37-51) verify that the incoming parameter is an integer >=
1 using Number.isInteger(...) and a >= 1 check before any modulo/index math; if
the check fails, throw a clear TypeError (e.g., "koreksiDino: expected integer
>= 1" / "koreksiPasaran: expected integer >= 1"). Keep the existing
normalization logic (modulo 7 and modulo 5 respectively) and use the validated
value to index DINTEN_ARR and PASARAN_ARR safely.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/REFACTOR_VERIFICATION.md`:
- Around line 99-107: Label the final fenced code block that contains the
test-summary lines (the block beginning with the lines like "✓
src/__tests__/_sasi.test.js ..." and ending with the three backticks) by
changing the opening/closing fence from ``` to ```text so the block is
explicitly marked as plain text and avoids MD040.

In `@src/index.ts`:
- Around line 4-5: Update the top-of-file comment to remove the incorrect claim
that hitungDino/hitungPasaran were imported into this file; keep the note that
koreksiDino/koreksiPasaran were extracted to koreksi.ts and, if desired, add a
brief clarifier that hitungDino and hitungPasaran were extracted to silpin.ts
(not imported here). Specifically edit the comment mentioning "imported
hitungDino/hitungPasaran from silpin.ts for reuse" so it either omits that
clause or replaces it with "hitungDino/hitungPasaran were extracted to silpin.ts
(not imported here)"; no code changes required.

In `@src/silpin.ts`:
- Around line 29-32: The index-wrapping logic in hitungDino (and the analogous
function handling pasaran at lines ~39-42) only subtracts one cycle and fails
for larger offsets; replace the conditional subtraction with full modulo
normalization using the correct modulus for each cycle (e.g., for hitungDino
compute normalized = ((dn + h - 1) % 7 + 7) % 7 and return that (or adjust +1/-1
if your indexing is 1-based), and for the pasaran function use modulus 5/6 as
appropriate) so indices always wrap into the valid range for lookups.
- Around line 117-123: The parameter type for cariWulanRegistry is incorrect:
change the signature from cariWulanRegistry(wulan: string): SasiReturnType to
accept both strings and numbers (e.g., cariWulanRegistry(wulan: string |
number): SasiReturnType), remove the unnecessary casts (like (wulan as unknown
as number)) and use wulan directly in the numeric branch to index SASI_ARR
(remember to subtract 1), and ensure any callers still pass string|number or are
updated to pass the correct type if needed.

---

Nitpick comments:
In `@src/koreksi.ts`:
- Around line 17-29: Add defensive input validation to both functions: in
koreksiDino (src/koreksi.ts lines 17-29) and koreksiPasaran (src/koreksi.ts
lines 37-51) verify that the incoming parameter is an integer >= 1 using
Number.isInteger(...) and a >= 1 check before any modulo/index math; if the
check fails, throw a clear TypeError (e.g., "koreksiDino: expected integer >= 1"
/ "koreksiPasaran: expected integer >= 1"). Keep the existing normalization
logic (modulo 7 and modulo 5 respectively) and use the validated value to index
DINTEN_ARR and PASARAN_ARR safely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59d0aa5a-3f24-479e-a7d6-d95a9bccc368

📥 Commits

Reviewing files that changed from the base of the PR and between 3ecc4bf and fac0320.

📒 Files selected for processing (6)
  • docs/REFACTOR_VERIFICATION.md
  • src/batur.ts
  • src/index.ts
  • src/koreksi.ts
  • src/silpin.ts
  • src/types.ts

Comment on lines +99 to +107
```
✓ src/__tests__/_sasi.test.js (14 tests)
✓ src/__tests__/_silpin.test.js (5 tests)
✓ src/__tests__/_kurup.test.js (2 tests)
✓ src/__tests__/_lib.test.js (1 test)
...
Test Files 9 passed (9)
Tests 105 passed (105)
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Label the final fenced block.

The closing code fence is unlabeled, which trips MD040 and makes the rendered output less consistent with the earlier fenced examples. Use a language tag such as text for the test-summary block.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 99-99: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/REFACTOR_VERIFICATION.md` around lines 99 - 107, Label the final fenced
code block that contains the test-summary lines (the block beginning with the
lines like "✓ src/__tests__/_sasi.test.js ..." and ending with the three
backticks) by changing the opening/closing fence from ``` to ```text so the
block is explicitly marked as plain text and avoids MD040.

Source: Linters/SAST tools

Comment thread src/index.ts
Comment on lines +4 to +5
// Refactored: extracted koreksiDino/koreksiPasaran to koreksi.ts,
// imported hitungDino/hitungPasaran from silpin.ts for reuse.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix misleading comment about imported functions.

Line 5 states "imported hitungDino/hitungPasaran from silpin.ts for reuse," but this file does not import those functions (see lines 15-21). According to the review stack context, hitungDino and hitungPasaran are extracted in silpin.ts (layer 5), not imported into index.ts. The comment should only reference the koreksiDino/koreksiPasaran extraction mentioned on line 4.

📝 Suggested correction
 // Pustaka Kalender Jawa
 // Perhtungan, pengkonversian dan penanggalan Jawa
 // 📁 index.ts
-// Refactored: extracted koreksiDino/koreksiPasaran to koreksi.ts,
-// imported hitungDino/hitungPasaran from silpin.ts for reuse.
+// Refactored: extracted koreksiDino/koreksiPasaran to koreksi.ts for reuse.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Refactored: extracted koreksiDino/koreksiPasaran to koreksi.ts,
// imported hitungDino/hitungPasaran from silpin.ts for reuse.
// Pustaka Kalender Jawa
// Perhtungan, pengkonversian dan penanggalan Jawa
// 📁 index.ts
// Refactored: extracted koreksiDino/koreksiPasaran to koreksi.ts for reuse.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.ts` around lines 4 - 5, Update the top-of-file comment to remove
the incorrect claim that hitungDino/hitungPasaran were imported into this file;
keep the note that koreksiDino/koreksiPasaran were extracted to koreksi.ts and,
if desired, add a brief clarifier that hitungDino and hitungPasaran were
extracted to silpin.ts (not imported here). Specifically edit the comment
mentioning "imported hitungDino/hitungPasaran from silpin.ts for reuse" so it
either omits that clause or replaces it with "hitungDino/hitungPasaran were
extracted to silpin.ts (not imported here)"; no code changes required.

Comment thread src/silpin.ts
Comment on lines +29 to +32
function hitungDino(h: number, dn: number): number {
const raw = dn + h - 1;
return raw > 7 ? raw - 7 : raw;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use full modulo normalization for index wrapping.

Line 31 and Line 41 subtract only one cycle. Larger offsets still produce out-of-range indices (e.g., day index > 7, pasaran index > 5), which can cause failed lookups.

Suggested patch
 function hitungDino(h: number, dn: number): number {
   const raw = dn + h - 1;
-  return raw > 7 ? raw - 7 : raw;
+  return ((raw - 1) % 7 + 7) % 7 + 1;
 }

 function hitungPasaran(p: number, ps: number): number {
   const raw = ps + p - 1;
-  return raw > 5 ? raw - 5 : raw;
+  return ((raw - 1) % 5 + 5) % 5 + 1;
 }

Also applies to: 39-42

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/silpin.ts` around lines 29 - 32, The index-wrapping logic in hitungDino
(and the analogous function handling pasaran at lines ~39-42) only subtracts one
cycle and fails for larger offsets; replace the conditional subtraction with
full modulo normalization using the correct modulus for each cycle (e.g., for
hitungDino compute normalized = ((dn + h - 1) % 7 + 7) % 7 and return that (or
adjust +1/-1 if your indexing is 1-based), and for the pasaran function use
modulus 5/6 as appropriate) so indices always wrap into the valid range for
lookups.

Comment thread src/silpin.ts
Comment on lines 117 to +123
function cariWulanRegistry(wulan: string): SasiReturnType {
// console.log(ARANING_WULAN_SETAUN.get(Symbol.for(wulan)))
// cek wulan type number or string
let _wulan = '';

if (typeof wulan === 'string') {
_wulan = wulan;
} else if (typeof wulan === 'number') {
const _sasi = SASI_ARR[wulan - 1];
const _sasi = SASI_ARR[(wulan as unknown as number) - 1];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Confirm current function signature and numeric branch:"
rg -nP 'function\s+cariWulanRegistry\s*\(\s*wulan\s*:\s*.*\)' src/silpin.ts
rg -nP 'typeof\s+wulan\s*===\s*[\"\x27]number[\"\x27]' src/silpin.ts

echo
echo "2) Check call sites passing numeric literals/variables:"
rg -nP '\bcariWulanRegistry\s*\(\s*\d+' src --type ts
rg -nP '\bcariWulanRegistry\s*\(\s*[a-zA-Z_][a-zA-Z0-9_]*\s*\)' src --type ts

Repository: kalenderjawa/pustaka

Length of output: 289


Fix cariWulanRegistry parameter type to match its runtime branches

In src/silpin.ts (lines 117–123), cariWulanRegistry is typed as wulan: string but still contains a typeof wulan === 'number' branch, making numeric handling unreachable for type-checked callers.

Suggested patch
-function cariWulanRegistry(wulan: string): SasiReturnType {
+function cariWulanRegistry(wulan: string | number): SasiReturnType {
   let _wulan = '';

   if (typeof wulan === 'string') {
     _wulan = wulan;
   } else if (typeof wulan === 'number') {
-    const _sasi = SASI_ARR[(wulan as unknown as number) - 1];
+    const _sasi = SASI_ARR[wulan - 1];
     if (_sasi) {
       _wulan = _sasi.wulan;
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function cariWulanRegistry(wulan: string): SasiReturnType {
// console.log(ARANING_WULAN_SETAUN.get(Symbol.for(wulan)))
// cek wulan type number or string
let _wulan = '';
if (typeof wulan === 'string') {
_wulan = wulan;
} else if (typeof wulan === 'number') {
const _sasi = SASI_ARR[wulan - 1];
const _sasi = SASI_ARR[(wulan as unknown as number) - 1];
function cariWulanRegistry(wulan: string | number): SasiReturnType {
let _wulan = '';
if (typeof wulan === 'string') {
_wulan = wulan;
} else if (typeof wulan === 'number') {
const _sasi = SASI_ARR[wulan - 1];
if (_sasi) {
_wulan = _sasi.wulan;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/silpin.ts` around lines 117 - 123, The parameter type for
cariWulanRegistry is incorrect: change the signature from
cariWulanRegistry(wulan: string): SasiReturnType to accept both strings and
numbers (e.g., cariWulanRegistry(wulan: string | number): SasiReturnType),
remove the unnecessary casts (like (wulan as unknown as number)) and use wulan
directly in the numeric branch to index SASI_ARR (remember to subtract 1), and
ensure any callers still pass string|number or are updated to pass the correct
type if needed.

@Wolfvin Wolfvin closed this by deleting the head repository Jul 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant