@mudraforge/statutory 2.4.1 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [2.4.2] - 2026-10-24
11
+
12
+ ### Added
13
+ - **npm Package Update Notifier**: Lightweight, zero-dependency update notifier querying `registry.npmjs.org` with a 24-hour temporary file cache (`checkForPackageUpdate()`).
14
+ - **Unified `check-update` CLI Command**: Now audits both npm package code updates against the npm registry and legal rule updates against the Cloudflare Edge manifest.
15
+ - **Non-Intrusive Terminal Footers**: Automatically surfaces `[NOTICE]` prompts when a newer npm release is available, with opt-out support via `NO_UPDATE_NOTIFIER=1` or `CI=true`.
16
+
17
+ ---
18
+
19
+ ## [2.4.1] - 2026-10-24
20
+
21
+ ### Added
22
+ - **Developer Navigation & Usability**: Compact, audited Table of Contents in `README.md` targeting all key documentation sections.
23
+ - **Package Metadata**: Official `homepage` pointing to `https://mudraforge.in/docs` and `bugs` pointing to GitHub Issues.
24
+ - **Supported Application Patterns**: Documented clear integration boundaries across AI Agents (local `SKILL.md`), Finance Software (declarative threshold schemas), and Developer Tooling.
25
+ - **Corporate M&A Restructuring Domain**: Section 50B slump sale capital gains based on WDV net worth (Section 43(6)), Form 3CEA CA certification schema, and Section 281 clearance checklist.
26
+ - **Dual-Act Successor Mapping**: Section 393 (*Income-tax Act, 2025*) successor provision mapping for Section 194Q.
27
+ - **Cryptographic Trust Anchor & Anti-Tamper Hardening**: Ed25519 digital signature verification for remote bundles, CRL key revocation enforcement, replay protections, Windows reserved device name protection, and strict production bypass locks (36 passing regression suites).
28
+
29
+ ### Changed
30
+ - **Statutory Rate Enforcements**: Section 194J non-PAN deduction rate updated to the statutory 20% penalty tier under Section 206AA.
31
+ - **Quotas & Error Semantics**: Documented Cloudflare Edge API rate limits, error codes, and HTTP semantics.
32
+
33
+ ---
34
+
10
35
  ## [2.4.0] - 2026-10-22
11
36
 
12
37
  ### Added
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  | Item | Value |
17
17
  |---|---|
18
- | **Package Release** | **`v2.4.1`** (Public Beta) |
18
+ | **Package Release** | **`v2.4.2`** (Public Beta) |
19
19
  | **Knowledge Specification** | **Google Open Knowledge Format (OKF v0.2) + MudraForge Statutory Schema Extension** |
20
20
  | **Regulatory Snapshot** | **CBDT / CBIC / MCA / MSME Baseline: October 2026** |
21
21
  | **Corpus Quality Card** | 100% Citation Resolution (15/15 URLs) in [`docs/CORPUS_QUALITY.md`](./docs/CORPUS_QUALITY.md) |
package/bin/cli.js CHANGED
@@ -16,6 +16,8 @@ import {
16
16
  checkUpdates,
17
17
  syncOkf,
18
18
  getLocalManifest,
19
+ checkForPackageUpdate,
20
+ STATUTORY_CLIENT_VERSION,
19
21
  DEFAULT_LOCAL_OKF_DIR
20
22
  } from '../src/index.js';
21
23
 
@@ -68,10 +70,36 @@ async function main() {
68
70
  printHelp();
69
71
  break;
70
72
  }
73
+
74
+ // Non-intrusive background npm package update notice
75
+ if (!['check-update', 'status', 'version'].includes(command)) {
76
+ try {
77
+ const updateInfo = await checkForPackageUpdate();
78
+ if (updateInfo && updateInfo.updateAvailable) {
79
+ console.log('───────────────────────────────────────────────────────────────────────────');
80
+ console.log(`[NOTICE] A new version of @mudraforge/statutory is available: ${updateInfo.currentVersion} -> ${updateInfo.latestVersion}`);
81
+ console.log(`Run: npm install @mudraforge/statutory@latest or npx @mudraforge/statutory@latest`);
82
+ console.log('───────────────────────────────────────────────────────────────────────────\n');
83
+ }
84
+ } catch (_) {}
85
+ }
71
86
  }
72
87
 
73
88
  async function handleCheckUpdate() {
74
89
  try {
90
+ console.log('\n[INFO] Checking npm package version against registry.npmjs.org...');
91
+ try {
92
+ const pkgUpdate = await checkForPackageUpdate({ cacheTtlMs: 0 });
93
+ if (pkgUpdate && pkgUpdate.updateAvailable) {
94
+ console.log('───────────────────────────────────────────────────────────────────────────');
95
+ console.log(`[WARN] New npm package version available: v${pkgUpdate.latestVersion} (installed: v${pkgUpdate.currentVersion})`);
96
+ console.log(`Run: npm install @mudraforge/statutory@latest`);
97
+ console.log('───────────────────────────────────────────────────────────────────────────');
98
+ } else if (pkgUpdate) {
99
+ console.log(`[OK] npm package code is up to date (v${pkgUpdate.currentVersion}).`);
100
+ }
101
+ } catch (_) {}
102
+
75
103
  console.log('\n[INFO] Checking OKF knowledge substrate status against Cloudflare edge...');
76
104
  const status = await checkUpdates();
77
105
  console.log('───────────────────────────────────────────────────────────────────────────');
@@ -82,7 +110,7 @@ async function handleCheckUpdate() {
82
110
  console.log('───────────────────────────────────────────────────────────────────────────');
83
111
 
84
112
  if (status.isOutdated) {
85
- console.log(`[WARN] New version available: v${status.latestVersion}`);
113
+ console.log(`[WARN] New legal data version available: v${status.latestVersion}`);
86
114
  console.log(`Changelog: ${status.changelog}`);
87
115
  console.log(`Run: npx @mudraforge/statutory sync to pull the latest rules.\n`);
88
116
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mudraforge/statutory",
3
- "version": "2.4.1",
3
+ "version": "2.4.2",
4
4
  "description": "Open Knowledge Format (OKF) client & AI Agent Skill for Indian and cross-border statutory tax provisions. Zero dependencies.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -10,6 +10,7 @@
10
10
 
11
11
  import fs from 'node:fs';
12
12
  import path from 'node:path';
13
+ import os from 'node:os';
13
14
  import crypto from 'node:crypto';
14
15
  import { fileURLToPath } from 'node:url';
15
16
 
@@ -17,7 +18,7 @@ const __filename = fileURLToPath(import.meta.url);
17
18
  const __dirname = path.dirname(__filename);
18
19
  export const DEFAULT_LOCAL_OKF_DIR = path.resolve(__dirname, '..', 'okf');
19
20
  export const DEFAULT_BASE_URL = 'https://api.mudraforge.in';
20
- export const STATUTORY_CLIENT_VERSION = '2.4.1';
21
+ export const STATUTORY_CLIENT_VERSION = '2.4.2';
21
22
 
22
23
  /**
23
24
  * Pinned official release Merkle root digests.
@@ -430,6 +431,78 @@ export async function checkUpdates(options = {}) {
430
431
  };
431
432
  }
432
433
 
434
+ /**
435
+ * Check if a newer version of the @mudraforge/statutory npm package is published on npmjs.com.
436
+ * Uses a lightweight cached file in os.tmpdir() to prevent frequent network roundtrips.
437
+ *
438
+ * @param {Object} [options]
439
+ * @param {string} [options.currentVersion] - Current installed version (defaults to STATUTORY_CLIENT_VERSION).
440
+ * @param {number} [options.cacheTtlMs] - Cache duration in milliseconds (default: 24 hours).
441
+ * @param {number} [options.timeoutMs] - Request timeout in milliseconds (default: 1500ms).
442
+ * @param {string} [options.registryUrl] - Registry endpoint (default: https://registry.npmjs.org/@mudraforge/statutory/latest).
443
+ * @returns {Promise<{ updateAvailable: boolean, currentVersion: string, latestVersion: string } | null>}
444
+ */
445
+ export async function checkForPackageUpdate(options = {}) {
446
+ if (process.env.NO_UPDATE_NOTIFIER === '1' || process.env.CI === 'true') {
447
+ return null;
448
+ }
449
+
450
+ const currentVersion = options.currentVersion || STATUTORY_CLIENT_VERSION;
451
+ const cacheTtlMs = options.cacheTtlMs !== undefined ? options.cacheTtlMs : 24 * 60 * 60 * 1000;
452
+ const timeoutMs = options.timeoutMs || 1500;
453
+ const cacheFile = path.join(os.tmpdir(), '.mudraforge_statutory_npm_update.json');
454
+
455
+ let cached = null;
456
+ if (cacheTtlMs > 0) {
457
+ try {
458
+ if (fs.existsSync(cacheFile)) {
459
+ const data = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
460
+ if (data && typeof data.lastChecked === 'number' && (Date.now() - data.lastChecked < cacheTtlMs)) {
461
+ cached = data;
462
+ }
463
+ }
464
+ } catch (_) {}
465
+ }
466
+
467
+ let latestVersion = cached?.latestVersion;
468
+
469
+ if (!latestVersion) {
470
+ try {
471
+ const url = options.registryUrl || 'https://registry.npmjs.org/@mudraforge/statutory/latest';
472
+ const res = await fetch(url, {
473
+ signal: AbortSignal.timeout(timeoutMs),
474
+ headers: { 'Accept': 'application/json' }
475
+ });
476
+ if (res.ok) {
477
+ const json = await res.json();
478
+ if (json && json.version && typeof json.version === 'string') {
479
+ latestVersion = json.version;
480
+ if (cacheTtlMs > 0) {
481
+ try {
482
+ fs.writeFileSync(cacheFile, JSON.stringify({
483
+ lastChecked: Date.now(),
484
+ latestVersion
485
+ }), 'utf8');
486
+ } catch (_) {}
487
+ }
488
+ }
489
+ }
490
+ } catch (_) {
491
+ // Fail open and silent on network error, offline, or timeout
492
+ return null;
493
+ }
494
+ }
495
+
496
+ if (!latestVersion) return null;
497
+
498
+ const cmp = compareSemver(latestVersion, currentVersion);
499
+ return {
500
+ updateAvailable: cmp > 0,
501
+ currentVersion,
502
+ latestVersion
503
+ };
504
+ }
505
+
433
506
  /**
434
507
  * Download and synchronize the latest OKF knowledge bundle to disk.
435
508
  * Hardened with:
package/types/index.d.ts CHANGED
@@ -148,6 +148,12 @@ export declare function getLocalManifest(okfDir?: string): any;
148
148
  export declare function lookupLocalStatute(section: string, okfDir?: string): StatutoryRecord | null;
149
149
  export declare function computeTemporalStatus(statute: StatutoryRecord, asOfDate?: string): 'ACTIVE' | 'SUNSET_SUPERSEDED' | 'FUTURE_PENDING';
150
150
  export declare function checkUpdates(options?: { baseUrl?: string; okfDir?: string }): Promise<UpdateInfo>;
151
+ export declare function checkForPackageUpdate(options?: {
152
+ currentVersion?: string;
153
+ cacheTtlMs?: number;
154
+ timeoutMs?: number;
155
+ registryUrl?: string;
156
+ }): Promise<{ updateAvailable: boolean; currentVersion: string; latestVersion: string } | null>;
151
157
  export declare function syncOkf(options?: { baseUrl?: string; okfDir?: string }): Promise<SyncResult>;
152
158
 
153
159
  export declare function lookupStatute(