bharosa 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ ebab801a241565a3b115993b6ae8470d144f8d69f79cdb8acc274ff8c200cf3e
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
- # 0.3.3
1
+ # 0.4.0 — unpublished release candidate
2
2
 
3
- Default both clients to https://api.bharosa.finance. Explicit base URL overrides remain supported. Existing clients using the app origin remain compatible. No analytics changes.
3
+ Add account-scoped Growth scheme performance and category return statistics. Change the default origin from published 0.3.2’s app.bharosa.finance to api.bharosa.finance, preserving explicit overrides. The reviewed host-default delta is included directly; no intermediate 0.3.3 publication is required. No investor monetary-performance, fund-AUM or stated-TER methods. API deployment and SDK publication are separate.
4
4
 
5
5
  # Changelog
6
6
 
@@ -1 +1 @@
1
- ad8f930964df34c3a28e3309302d19ba76ab9d48190c024a26b0795422f0b648
1
+ 29463b6a150e368a47ac11e2f8f841e3808d94d3ff9b70221fe3804c93b91004
package/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  A TypeScript client for Bharosa accounts, investors, portfolios, and mutual funds. Use one resource interface from TypeScript, ESM, or CommonJS, with no runtime dependencies.
4
4
 
5
- **SDK version:** `bharosa@0.3.3`. Both clients use the production API at `https://api.bharosa.finance` by default. Account access and credentials are still required; installing the SDK does not grant access.
5
+ **Local release candidate (unpublished):** `bharosa@0.4.0`. Both clients use the production API at `https://api.bharosa.finance` by default. Account access and credentials are still required; installing the SDK does not grant access.
6
6
 
7
7
  ## Start here
8
8
 
9
- Use Node.js 22.12+ in a supported LTS release. Install the published SDK in your backend application:
9
+ Use Node.js 22.12+ in a supported LTS release. Install the reviewed local candidate in your backend application:
10
10
 
11
11
  ```sh
12
- npm install bharosa@0.3.3 --ignore-scripts
12
+ npm install /absolute/path/to/bharosa-0.4.0.tgz --ignore-scripts
13
13
  ```
14
14
 
15
15
  Source contributors can build and pack a local archive using the commands in CONTRIBUTING.md.
@@ -124,3 +124,7 @@ Available in SDK 0.3.0 and activated at `https://api.bharosa.finance`. Server ap
124
124
  ### Optional API origin override
125
125
 
126
126
  Set `baseUrl` only for another deployment or tests. Omitted or `undefined` uses `https://api.bharosa.finance`; invalid explicit values fail validation and never fall back. Overrides must be HTTPS origins without credentials, paths, queries or fragments. Local HTTP also requires `allowLocalhostHttp: true`. The SDK does not read `BHAROSA_API_BASE_URL` automatically.
127
+
128
+ ## Growth performance and category returns
129
+
130
+ Adds `mutualFundSchemes.performance.retrieve` and `fundCategories.statistics.retrieve` for authenticated account access. See [analytics methods, coverage and limitations](docs/analytics.md). These API routes are deployed; this SDK candidate is not yet published. `BharosaMarket` retains its four monthly operations and cannot call account analytics.
@@ -0,0 +1,665 @@
1
+ "use strict";
2
+ // Generated from the reviewed analytics OpenAPI snapshot. Transport only.
3
+ module.exports = {
4
+ "operations": {
5
+ "analytics_scheme": {
6
+ "path": "/v1/accounts/{account}/market-data/schemes/{id}/performance",
7
+ "method": "GET",
8
+ "status": 200,
9
+ "marketData": true,
10
+ "readOnly": true,
11
+ "parameters": [
12
+ {
13
+ "name": "account",
14
+ "in": "path",
15
+ "required": true,
16
+ "schema": {
17
+ "type": "string",
18
+ "pattern": "^(me|acct_[a-f0-9]{32})$"
19
+ }
20
+ },
21
+ {
22
+ "name": "id",
23
+ "in": "path",
24
+ "required": true,
25
+ "schema": {
26
+ "type": "string",
27
+ "pattern": "^[1-9][0-9]{0,18}$"
28
+ }
29
+ },
30
+ {
31
+ "name": "as_of",
32
+ "in": "query",
33
+ "required": true,
34
+ "schema": {
35
+ "type": "string",
36
+ "format": "date",
37
+ "pattern": "^\\d{4}-\\d{2}-01$"
38
+ }
39
+ },
40
+ {
41
+ "name": "periods",
42
+ "in": "query",
43
+ "required": false,
44
+ "schema": {
45
+ "type": "string",
46
+ "pattern": "^(1y|3y|5y|since_inception)(,(1y|3y|5y|since_inception)){0,3}$"
47
+ }
48
+ }
49
+ ],
50
+ "response": {
51
+ "type": "object",
52
+ "properties": {
53
+ "methodology_version": {
54
+ "const": "ts-analytics-v1"
55
+ },
56
+ "requested_as_of": {
57
+ "type": "string",
58
+ "format": "date",
59
+ "pattern": "^\\d{4}-\\d{2}-01$"
60
+ },
61
+ "currency": {
62
+ "const": "INR"
63
+ },
64
+ "source_cutoff": {
65
+ "type": "string",
66
+ "format": "date",
67
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
68
+ },
69
+ "source_version": {
70
+ "type": "string"
71
+ },
72
+ "warnings": {
73
+ "type": "array",
74
+ "items": {
75
+ "type": "string"
76
+ }
77
+ },
78
+ "object": {
79
+ "const": "scheme_performance"
80
+ },
81
+ "scheme_id": {
82
+ "type": "string"
83
+ },
84
+ "fund_id": {
85
+ "type": "string"
86
+ },
87
+ "plan": {
88
+ "type": "string"
89
+ },
90
+ "option": {
91
+ "type": "string"
92
+ },
93
+ "basis": {
94
+ "type": "string"
95
+ },
96
+ "periods": {
97
+ "type": "array",
98
+ "items": {
99
+ "type": "object",
100
+ "properties": {
101
+ "period": {
102
+ "enum": [
103
+ "1y",
104
+ "3y",
105
+ "5y",
106
+ "since_inception"
107
+ ]
108
+ },
109
+ "requested_start": {
110
+ "anyOf": [
111
+ {
112
+ "type": "string",
113
+ "format": "date",
114
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
115
+ },
116
+ {
117
+ "type": "null"
118
+ }
119
+ ]
120
+ },
121
+ "requested_end": {
122
+ "type": "string",
123
+ "format": "date",
124
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
125
+ },
126
+ "status": {
127
+ "enum": [
128
+ "available",
129
+ "unavailable"
130
+ ]
131
+ },
132
+ "reason": {
133
+ "anyOf": [
134
+ {
135
+ "type": "string"
136
+ },
137
+ {
138
+ "type": "null"
139
+ }
140
+ ]
141
+ },
142
+ "total_return_pct": {
143
+ "anyOf": [
144
+ {
145
+ "type": "object",
146
+ "properties": {
147
+ "status": {
148
+ "const": "available"
149
+ },
150
+ "value": {
151
+ "type": "string",
152
+ "pattern": "^-?\\d+(\\.\\d{1,8})?$"
153
+ },
154
+ "reason": {
155
+ "type": "null"
156
+ }
157
+ },
158
+ "required": [
159
+ "status",
160
+ "value",
161
+ "reason"
162
+ ],
163
+ "additionalProperties": true
164
+ },
165
+ {
166
+ "type": "object",
167
+ "properties": {
168
+ "status": {
169
+ "const": "unavailable"
170
+ },
171
+ "value": {
172
+ "type": "null"
173
+ },
174
+ "reason": {
175
+ "type": "string"
176
+ }
177
+ },
178
+ "required": [
179
+ "status",
180
+ "value",
181
+ "reason"
182
+ ],
183
+ "additionalProperties": true
184
+ }
185
+ ]
186
+ },
187
+ "cagr_pct": {
188
+ "anyOf": [
189
+ {
190
+ "type": "object",
191
+ "properties": {
192
+ "status": {
193
+ "const": "available"
194
+ },
195
+ "value": {
196
+ "type": "string",
197
+ "pattern": "^-?\\d+(\\.\\d{1,8})?$"
198
+ },
199
+ "reason": {
200
+ "type": "null"
201
+ }
202
+ },
203
+ "required": [
204
+ "status",
205
+ "value",
206
+ "reason"
207
+ ],
208
+ "additionalProperties": true
209
+ },
210
+ {
211
+ "type": "object",
212
+ "properties": {
213
+ "status": {
214
+ "const": "unavailable"
215
+ },
216
+ "value": {
217
+ "type": "null"
218
+ },
219
+ "reason": {
220
+ "type": "string"
221
+ }
222
+ },
223
+ "required": [
224
+ "status",
225
+ "value",
226
+ "reason"
227
+ ],
228
+ "additionalProperties": true
229
+ }
230
+ ]
231
+ },
232
+ "start_observation_date": {
233
+ "anyOf": [
234
+ {
235
+ "type": "string",
236
+ "format": "date",
237
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
238
+ },
239
+ {
240
+ "type": "null"
241
+ }
242
+ ]
243
+ },
244
+ "end_observation_date": {
245
+ "anyOf": [
246
+ {
247
+ "type": "string",
248
+ "format": "date",
249
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
250
+ },
251
+ {
252
+ "type": "null"
253
+ }
254
+ ]
255
+ },
256
+ "start_nav": {
257
+ "anyOf": [
258
+ {
259
+ "type": "string"
260
+ },
261
+ {
262
+ "type": "null"
263
+ }
264
+ ]
265
+ },
266
+ "end_nav": {
267
+ "anyOf": [
268
+ {
269
+ "type": "string"
270
+ },
271
+ {
272
+ "type": "null"
273
+ }
274
+ ]
275
+ },
276
+ "start_lag_days": {
277
+ "anyOf": [
278
+ {
279
+ "type": "integer",
280
+ "minimum": 0
281
+ },
282
+ {
283
+ "type": "null"
284
+ }
285
+ ]
286
+ },
287
+ "end_lag_days": {
288
+ "anyOf": [
289
+ {
290
+ "type": "integer",
291
+ "minimum": 0
292
+ },
293
+ {
294
+ "type": "null"
295
+ }
296
+ ]
297
+ },
298
+ "elapsed_days": {
299
+ "anyOf": [
300
+ {
301
+ "type": "number"
302
+ },
303
+ {
304
+ "type": "null"
305
+ }
306
+ ]
307
+ },
308
+ "annualization_basis": {
309
+ "const": "observed_nav_dates_365_25"
310
+ }
311
+ },
312
+ "required": [
313
+ "period",
314
+ "requested_start",
315
+ "requested_end",
316
+ "status",
317
+ "reason",
318
+ "total_return_pct",
319
+ "cagr_pct",
320
+ "start_observation_date",
321
+ "end_observation_date",
322
+ "start_nav",
323
+ "end_nav",
324
+ "start_lag_days",
325
+ "end_lag_days",
326
+ "elapsed_days",
327
+ "annualization_basis"
328
+ ],
329
+ "additionalProperties": true
330
+ },
331
+ "minItems": 1,
332
+ "maxItems": 4
333
+ }
334
+ },
335
+ "required": [
336
+ "methodology_version",
337
+ "requested_as_of",
338
+ "currency",
339
+ "source_cutoff",
340
+ "source_version",
341
+ "warnings",
342
+ "object",
343
+ "scheme_id",
344
+ "fund_id",
345
+ "plan",
346
+ "option",
347
+ "basis",
348
+ "periods"
349
+ ],
350
+ "additionalProperties": true
351
+ }
352
+ },
353
+ "analytics_category": {
354
+ "path": "/v1/accounts/{account}/market-data/categories/{id}/statistics",
355
+ "method": "GET",
356
+ "status": 200,
357
+ "marketData": true,
358
+ "readOnly": true,
359
+ "parameters": [
360
+ {
361
+ "name": "account",
362
+ "in": "path",
363
+ "required": true,
364
+ "schema": {
365
+ "type": "string",
366
+ "pattern": "^(me|acct_[a-f0-9]{32})$"
367
+ }
368
+ },
369
+ {
370
+ "name": "id",
371
+ "in": "path",
372
+ "required": true,
373
+ "schema": {
374
+ "type": "string",
375
+ "minLength": 1,
376
+ "maxLength": 100
377
+ }
378
+ },
379
+ {
380
+ "name": "as_of",
381
+ "in": "query",
382
+ "required": true,
383
+ "schema": {
384
+ "type": "string",
385
+ "format": "date",
386
+ "pattern": "^\\d{4}-\\d{2}-01$"
387
+ }
388
+ },
389
+ {
390
+ "name": "periods",
391
+ "in": "query",
392
+ "required": false,
393
+ "schema": {
394
+ "type": "string",
395
+ "pattern": "^(1y|3y|5y|since_inception)(,(1y|3y|5y|since_inception)){0,3}$"
396
+ }
397
+ },
398
+ {
399
+ "name": "metrics",
400
+ "in": "query",
401
+ "required": false,
402
+ "schema": {
403
+ "type": "string",
404
+ "const": "return",
405
+ "default": "return"
406
+ }
407
+ },
408
+ {
409
+ "name": "plan",
410
+ "in": "query",
411
+ "required": false,
412
+ "schema": {
413
+ "enum": [
414
+ "direct",
415
+ "regular"
416
+ ]
417
+ }
418
+ },
419
+ {
420
+ "name": "option",
421
+ "in": "query",
422
+ "required": false,
423
+ "schema": {
424
+ "const": "growth"
425
+ }
426
+ }
427
+ ],
428
+ "response": {
429
+ "type": "object",
430
+ "properties": {
431
+ "methodology_version": {
432
+ "const": "ts-analytics-v1"
433
+ },
434
+ "requested_as_of": {
435
+ "type": "string",
436
+ "format": "date",
437
+ "pattern": "^\\d{4}-\\d{2}-01$"
438
+ },
439
+ "currency": {
440
+ "const": "INR"
441
+ },
442
+ "source_cutoff": {
443
+ "type": "string",
444
+ "format": "date",
445
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
446
+ },
447
+ "source_version": {
448
+ "type": "string"
449
+ },
450
+ "warnings": {
451
+ "type": "array",
452
+ "items": {
453
+ "type": "string"
454
+ }
455
+ },
456
+ "object": {
457
+ "const": "category_statistics"
458
+ },
459
+ "category": {
460
+ "type": "string"
461
+ },
462
+ "cohort": {
463
+ "type": "object",
464
+ "properties": {
465
+ "policy": {
466
+ "const": "current_classification_as_of_prices"
467
+ },
468
+ "plan": {
469
+ "enum": [
470
+ "direct",
471
+ "regular"
472
+ ]
473
+ },
474
+ "option": {
475
+ "const": "growth"
476
+ },
477
+ "fund_count": {
478
+ "type": "integer",
479
+ "minimum": 0
480
+ },
481
+ "selected_scheme_count": {
482
+ "type": "integer",
483
+ "minimum": 0
484
+ },
485
+ "classification_date": {
486
+ "anyOf": [
487
+ {
488
+ "type": "string",
489
+ "format": "date",
490
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
491
+ },
492
+ {
493
+ "type": "null"
494
+ }
495
+ ]
496
+ }
497
+ },
498
+ "required": [
499
+ "policy",
500
+ "plan",
501
+ "option",
502
+ "fund_count",
503
+ "selected_scheme_count",
504
+ "classification_date"
505
+ ],
506
+ "additionalProperties": true
507
+ },
508
+ "metrics": {
509
+ "type": "object",
510
+ "additionalProperties": {
511
+ "anyOf": [
512
+ {
513
+ "type": "object",
514
+ "properties": {
515
+ "status": {
516
+ "const": "available"
517
+ },
518
+ "value": {
519
+ "type": "string",
520
+ "pattern": "^-?\\d+(\\.\\d{1,8})?$"
521
+ },
522
+ "reason": {
523
+ "type": "null"
524
+ },
525
+ "method": {
526
+ "const": "equal_weighted_arithmetic_mean"
527
+ },
528
+ "unit": {
529
+ "type": "string"
530
+ },
531
+ "eligible_count": {
532
+ "type": "integer",
533
+ "minimum": 0
534
+ },
535
+ "valid_count": {
536
+ "type": "integer",
537
+ "minimum": 0
538
+ },
539
+ "stale_count": {
540
+ "type": "integer",
541
+ "minimum": 0
542
+ },
543
+ "invalid_count": {
544
+ "type": "integer",
545
+ "minimum": 0
546
+ },
547
+ "missing_count": {
548
+ "type": "integer",
549
+ "minimum": 0
550
+ },
551
+ "coverage_pct": {
552
+ "type": "string",
553
+ "pattern": "^\\d+(\\.\\d+)?$"
554
+ },
555
+ "exclusions": {
556
+ "type": "object",
557
+ "additionalProperties": {
558
+ "type": "integer",
559
+ "minimum": 0
560
+ }
561
+ }
562
+ },
563
+ "required": [
564
+ "status",
565
+ "value",
566
+ "reason",
567
+ "method",
568
+ "unit",
569
+ "eligible_count",
570
+ "valid_count",
571
+ "stale_count",
572
+ "invalid_count",
573
+ "missing_count",
574
+ "coverage_pct",
575
+ "exclusions"
576
+ ],
577
+ "additionalProperties": true
578
+ },
579
+ {
580
+ "type": "object",
581
+ "properties": {
582
+ "status": {
583
+ "const": "unavailable"
584
+ },
585
+ "value": {
586
+ "type": "null"
587
+ },
588
+ "reason": {
589
+ "type": "string"
590
+ },
591
+ "method": {
592
+ "const": "equal_weighted_arithmetic_mean"
593
+ },
594
+ "unit": {
595
+ "type": "string"
596
+ },
597
+ "eligible_count": {
598
+ "type": "integer",
599
+ "minimum": 0
600
+ },
601
+ "valid_count": {
602
+ "type": "integer",
603
+ "minimum": 0
604
+ },
605
+ "stale_count": {
606
+ "type": "integer",
607
+ "minimum": 0
608
+ },
609
+ "invalid_count": {
610
+ "type": "integer",
611
+ "minimum": 0
612
+ },
613
+ "missing_count": {
614
+ "type": "integer",
615
+ "minimum": 0
616
+ },
617
+ "coverage_pct": {
618
+ "type": "string",
619
+ "pattern": "^\\d+(\\.\\d+)?$"
620
+ },
621
+ "exclusions": {
622
+ "type": "object",
623
+ "additionalProperties": {
624
+ "type": "integer",
625
+ "minimum": 0
626
+ }
627
+ }
628
+ },
629
+ "required": [
630
+ "status",
631
+ "value",
632
+ "reason",
633
+ "method",
634
+ "unit",
635
+ "eligible_count",
636
+ "valid_count",
637
+ "stale_count",
638
+ "invalid_count",
639
+ "missing_count",
640
+ "coverage_pct",
641
+ "exclusions"
642
+ ],
643
+ "additionalProperties": true
644
+ }
645
+ ]
646
+ }
647
+ }
648
+ },
649
+ "required": [
650
+ "methodology_version",
651
+ "requested_as_of",
652
+ "currency",
653
+ "source_cutoff",
654
+ "source_version",
655
+ "warnings",
656
+ "object",
657
+ "category",
658
+ "cohort",
659
+ "metrics"
660
+ ],
661
+ "additionalProperties": true
662
+ }
663
+ }
664
+ }
665
+ };
@@ -0,0 +1,2 @@
1
+ import type { Operation } from "./schema.cjs";
2
+ export const operations: Readonly<Record<string, Operation>>;
@@ -0,0 +1,2 @@
1
+ import type { Operation } from "./schema.js";
2
+ export const operations: Readonly<Record<string, Operation>>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,103 @@
1
+ import type { RequestOptions } from "./client-types.cjs";
2
+ export type AnalyticsPeriod = "1y" | "3y" | "5y" | "since_inception";
3
+ export type AnalyticsMetric = {
4
+ status: "available";
5
+ value: string;
6
+ reason: null;
7
+ } | {
8
+ status: "unavailable";
9
+ value: null;
10
+ reason: string;
11
+ };
12
+ export interface PerformanceSelection {
13
+ asOf: string;
14
+ periods?: AnalyticsPeriod[];
15
+ }
16
+ export interface AnalyticsEnvelope {
17
+ methodology_version: "ts-analytics-v1";
18
+ requested_as_of: string;
19
+ currency: "INR";
20
+ source_cutoff: string;
21
+ source_version: string;
22
+ warnings: string[];
23
+ }
24
+ export interface PerformancePeriod {
25
+ period: AnalyticsPeriod;
26
+ requested_start: string | null;
27
+ requested_end: string;
28
+ status: "available" | "unavailable";
29
+ reason: string | null;
30
+ total_return_pct: AnalyticsMetric;
31
+ cagr_pct: AnalyticsMetric;
32
+ }
33
+ export interface AnalyticsFreshnessPolicy {
34
+ nav_days: number;
35
+ aum_days: number;
36
+ ter_days: number;
37
+ category_min_funds: number;
38
+ category_min_coverage_pct: number;
39
+ }
40
+ export interface SchemePerformance extends AnalyticsEnvelope {
41
+ calculation_precision: "decimal_inputs_float64_returns";
42
+ freshness_policy: AnalyticsFreshnessPolicy;
43
+ available_history_start: string | null;
44
+ inception_basis: "verified_first_scheme_nav" | null;
45
+ scheme_nav_inception_date: string | null;
46
+ object: "scheme_performance";
47
+ scheme_id: string;
48
+ fund_id: string;
49
+ plan: string;
50
+ option: string;
51
+ basis: string;
52
+ periods: (PerformancePeriod & {
53
+ start_observation_date: string | null;
54
+ end_observation_date: string | null;
55
+ start_nav: string | null;
56
+ end_nav: string | null;
57
+ start_lag_days: number | null;
58
+ end_lag_days: number | null;
59
+ elapsed_days: number | null;
60
+ annualization_basis: "observed_nav_dates_365_25";
61
+ })[];
62
+ }
63
+ export interface CategorySelection {
64
+ asOf: string;
65
+ periods?: Exclude<AnalyticsPeriod, "since_inception">[];
66
+ metrics?: "return"[];
67
+ plan?: "direct" | "regular";
68
+ option?: "growth";
69
+ }
70
+ export interface CategoryStatistics extends AnalyticsEnvelope {
71
+ freshness_policy: AnalyticsFreshnessPolicy;
72
+ object: "category_statistics";
73
+ category: string;
74
+ cohort: {
75
+ policy: "current_classification_as_of_prices";
76
+ plan: "direct" | "regular";
77
+ option: "growth";
78
+ fund_count: number;
79
+ selected_scheme_count: number;
80
+ classification_date: string | null;
81
+ };
82
+ metrics: Record<string, AnalyticsMetric & {
83
+ method: "equal_weighted_arithmetic_mean";
84
+ return_type: "total_return" | "cagr" | null;
85
+ observation_date_min: string | null;
86
+ observation_date_max: string | null;
87
+ observed_elapsed_days_min: number | null;
88
+ observed_elapsed_days_max: number | null;
89
+ start_lag_days_max: number | null;
90
+ end_lag_days_max: number | null;
91
+ unit: string;
92
+ eligible_count: number;
93
+ valid_count: number;
94
+ missing_count: number;
95
+ stale_count: number;
96
+ invalid_count: number;
97
+ coverage_pct: string;
98
+ exclusions: Record<string, number>;
99
+ }>;
100
+ }
101
+ export interface AnalyticsResource<P, R> {
102
+ retrieve(id: string, params: P, options?: RequestOptions): Promise<R>;
103
+ }
@@ -0,0 +1,103 @@
1
+ import type { RequestOptions } from "./client-types.js";
2
+ export type AnalyticsPeriod = "1y" | "3y" | "5y" | "since_inception";
3
+ export type AnalyticsMetric = {
4
+ status: "available";
5
+ value: string;
6
+ reason: null;
7
+ } | {
8
+ status: "unavailable";
9
+ value: null;
10
+ reason: string;
11
+ };
12
+ export interface PerformanceSelection {
13
+ asOf: string;
14
+ periods?: AnalyticsPeriod[];
15
+ }
16
+ export interface AnalyticsEnvelope {
17
+ methodology_version: "ts-analytics-v1";
18
+ requested_as_of: string;
19
+ currency: "INR";
20
+ source_cutoff: string;
21
+ source_version: string;
22
+ warnings: string[];
23
+ }
24
+ export interface PerformancePeriod {
25
+ period: AnalyticsPeriod;
26
+ requested_start: string | null;
27
+ requested_end: string;
28
+ status: "available" | "unavailable";
29
+ reason: string | null;
30
+ total_return_pct: AnalyticsMetric;
31
+ cagr_pct: AnalyticsMetric;
32
+ }
33
+ export interface AnalyticsFreshnessPolicy {
34
+ nav_days: number;
35
+ aum_days: number;
36
+ ter_days: number;
37
+ category_min_funds: number;
38
+ category_min_coverage_pct: number;
39
+ }
40
+ export interface SchemePerformance extends AnalyticsEnvelope {
41
+ calculation_precision: "decimal_inputs_float64_returns";
42
+ freshness_policy: AnalyticsFreshnessPolicy;
43
+ available_history_start: string | null;
44
+ inception_basis: "verified_first_scheme_nav" | null;
45
+ scheme_nav_inception_date: string | null;
46
+ object: "scheme_performance";
47
+ scheme_id: string;
48
+ fund_id: string;
49
+ plan: string;
50
+ option: string;
51
+ basis: string;
52
+ periods: (PerformancePeriod & {
53
+ start_observation_date: string | null;
54
+ end_observation_date: string | null;
55
+ start_nav: string | null;
56
+ end_nav: string | null;
57
+ start_lag_days: number | null;
58
+ end_lag_days: number | null;
59
+ elapsed_days: number | null;
60
+ annualization_basis: "observed_nav_dates_365_25";
61
+ })[];
62
+ }
63
+ export interface CategorySelection {
64
+ asOf: string;
65
+ periods?: Exclude<AnalyticsPeriod, "since_inception">[];
66
+ metrics?: "return"[];
67
+ plan?: "direct" | "regular";
68
+ option?: "growth";
69
+ }
70
+ export interface CategoryStatistics extends AnalyticsEnvelope {
71
+ freshness_policy: AnalyticsFreshnessPolicy;
72
+ object: "category_statistics";
73
+ category: string;
74
+ cohort: {
75
+ policy: "current_classification_as_of_prices";
76
+ plan: "direct" | "regular";
77
+ option: "growth";
78
+ fund_count: number;
79
+ selected_scheme_count: number;
80
+ classification_date: string | null;
81
+ };
82
+ metrics: Record<string, AnalyticsMetric & {
83
+ method: "equal_weighted_arithmetic_mean";
84
+ return_type: "total_return" | "cagr" | null;
85
+ observation_date_min: string | null;
86
+ observation_date_max: string | null;
87
+ observed_elapsed_days_min: number | null;
88
+ observed_elapsed_days_max: number | null;
89
+ start_lag_days_max: number | null;
90
+ end_lag_days_max: number | null;
91
+ unit: string;
92
+ eligible_count: number;
93
+ valid_count: number;
94
+ missing_count: number;
95
+ stale_count: number;
96
+ invalid_count: number;
97
+ coverage_pct: string;
98
+ exclusions: Record<string, number>;
99
+ }>;
100
+ }
101
+ export interface AnalyticsResource<P, R> {
102
+ retrieve(id: string, params: P, options?: RequestOptions): Promise<R>;
103
+ }
@@ -1,3 +1,4 @@
1
+ import type * as A from "./analytics-types.cjs";
1
2
  import type { MonthlyMarketClient } from "./monthly-types.cjs";
2
3
  import type { MarketDataClient, CurrentHoldingsInput, CurrentHoldingsResult } from "./market-types.cjs";
3
4
  export type * from "./market-types.cjs";
@@ -175,7 +176,11 @@ export type FundObjectReferences = Omit<Fields<FundReferences>, "data"> & {
175
176
  };
176
177
  export interface ClientSurface {
177
178
  readonly marketSnapshots: MonthlyMarketClient;
179
+ readonly fundCategories: {
180
+ readonly statistics: A.AnalyticsResource<A.CategorySelection, A.CategoryStatistics>;
181
+ };
178
182
  readonly mutualFundSchemes: {
183
+ readonly performance: A.AnalyticsResource<A.PerformanceSelection, A.SchemePerformance>;
179
184
  readonly navs: {
180
185
  readonly retrieve: MonthlyMarketClient["schemes"]["nav"];
181
186
  };
@@ -1,3 +1,4 @@
1
+ import type * as A from "./analytics-types.js";
1
2
  import type { MonthlyMarketClient } from "./monthly-types.js";
2
3
  import type { MarketDataClient, CurrentHoldingsInput, CurrentHoldingsResult } from "./market-types.js";
3
4
  export type * from "./market-types.js";
@@ -175,7 +176,11 @@ export type FundObjectReferences = Omit<Fields<FundReferences>, "data"> & {
175
176
  };
176
177
  export interface ClientSurface {
177
178
  readonly marketSnapshots: MonthlyMarketClient;
179
+ readonly fundCategories: {
180
+ readonly statistics: A.AnalyticsResource<A.CategorySelection, A.CategoryStatistics>;
181
+ };
178
182
  readonly mutualFundSchemes: {
183
+ readonly performance: A.AnalyticsResource<A.PerformanceSelection, A.SchemePerformance>;
179
184
  readonly navs: {
180
185
  readonly retrieve: MonthlyMarketClient["schemes"]["nav"];
181
186
  };
package/dist/index.cjs CHANGED
@@ -42,12 +42,50 @@ class Bharosa {
42
42
  clients;
43
43
  investors;
44
44
  portfolios;
45
+ fundCategories;
45
46
  mutualFunds;
46
47
  mutualFundSchemes;
47
48
  marketSnapshots;
48
49
  marketData;
49
50
  constructor(config) {
50
51
  const call = (0, transport_cjs_1.createTransport)(config);
52
+ const analyticsQuery = (q, extra = []) => {
53
+ (0, transport_cjs_1.keys)(q, ["asOf", "periods", ...extra]);
54
+ checkedDate(q.asOf);
55
+ (0, transport_cjs_1.check)(q.asOf.endsWith("-01"));
56
+ if (q.periods !== undefined)
57
+ (0, transport_cjs_1.check)(Array.isArray(q.periods) &&
58
+ q.periods.length > 0 &&
59
+ q.periods.length <= 4 &&
60
+ new Set(q.periods).size === q.periods.length &&
61
+ q.periods.every((p) => ["1y", "3y", "5y", "since_inception"].includes(p)));
62
+ return {
63
+ as_of: q.asOf,
64
+ ...(q.periods === undefined ? {} : { periods: q.periods.join(",") }),
65
+ };
66
+ };
67
+ this.fundCategories = Object.freeze({
68
+ statistics: Object.freeze({
69
+ retrieve: (id, q, o) => {
70
+ const query = analyticsQuery(q, ["metrics", "plan", "option"]);
71
+ if (q.metrics !== undefined)
72
+ (0, transport_cjs_1.check)(Array.isArray(q.metrics) &&
73
+ q.metrics.length > 0 &&
74
+ q.metrics.length <= 3 &&
75
+ new Set(q.metrics).size === q.metrics.length &&
76
+ q.metrics.every((m) => m === "return"));
77
+ (0, transport_cjs_1.check)(!q.periods?.includes("since_inception"));
78
+ (0, transport_cjs_1.check)(!(q.metrics && !q.metrics.includes("return") && q.periods));
79
+ return call("analytics_category", {
80
+ id,
81
+ ...query,
82
+ ...(q.metrics ? { metrics: q.metrics.join(",") } : {}),
83
+ ...(q.plan ? { plan: q.plan } : {}),
84
+ ...(q.option ? { option: q.option } : {}),
85
+ }, undefined, o);
86
+ },
87
+ }),
88
+ });
51
89
  const list = (p) => {
52
90
  (0, transport_cjs_1.keys)(p, ["limit", "startingAfter", "client"]);
53
91
  return Object.fromEntries(Object.entries(p)
@@ -299,6 +337,9 @@ class Bharosa {
299
337
  },
300
338
  };
301
339
  this.mutualFundSchemes = Object.freeze({
340
+ performance: Object.freeze({
341
+ retrieve: (id, q, o) => call("analytics_scheme", { id, ...analyticsQuery(q) }, undefined, o),
342
+ }),
302
343
  navs: Object.freeze({ retrieve: this.marketSnapshots.schemes.nav }),
303
344
  expenseRatios: Object.freeze({
304
345
  retrieve: this.marketSnapshots.schemes.expenseRatio,
package/dist/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ export type * from "./analytics-types.cjs";
1
2
  import type { MonthlyMarketClient } from "./monthly-types.cjs";
2
3
  export type * from "./monthly-types.cjs";
3
4
  import type * as T from "./client-types.cjs";
@@ -9,6 +10,7 @@ export declare class Bharosa implements T.ClientSurface {
9
10
  readonly clients: T.ClientSurface["clients"];
10
11
  readonly investors: T.ClientSurface["investors"];
11
12
  readonly portfolios: T.ClientSurface["portfolios"];
13
+ readonly fundCategories: T.ClientSurface["fundCategories"];
12
14
  readonly mutualFunds: T.ClientSurface["mutualFunds"];
13
15
  readonly mutualFundSchemes: T.ClientSurface["mutualFundSchemes"];
14
16
  readonly marketSnapshots: MonthlyMarketClient;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export type * from "./analytics-types.js";
1
2
  import type { MonthlyMarketClient } from "./monthly-types.js";
2
3
  export type * from "./monthly-types.js";
3
4
  import type * as T from "./client-types.js";
@@ -9,6 +10,7 @@ export declare class Bharosa implements T.ClientSurface {
9
10
  readonly clients: T.ClientSurface["clients"];
10
11
  readonly investors: T.ClientSurface["investors"];
11
12
  readonly portfolios: T.ClientSurface["portfolios"];
13
+ readonly fundCategories: T.ClientSurface["fundCategories"];
12
14
  readonly mutualFunds: T.ClientSurface["mutualFunds"];
13
15
  readonly mutualFundSchemes: T.ClientSurface["mutualFundSchemes"];
14
16
  readonly marketSnapshots: MonthlyMarketClient;
@@ -1,9 +1,12 @@
1
+ import type * as A from "./analytics-types.cjs";
1
2
  import type * as S from "./monthly-types.cjs";
2
3
  import type * as M from "./models.cjs";
3
4
  import type * as D from "./market-types.cjs";
4
5
  import type { CurrentMixEvidence } from "./client-types.cjs";
5
6
  /** Each entry is the decoded response of the corresponding generated operation. */
6
7
  export interface OperationResults {
8
+ analytics_scheme: A.SchemePerformance;
9
+ analytics_category: A.CategoryStatistics;
7
10
  monthly_nav: S.MarketSnapshot<D.NavObservation>;
8
11
  monthly_expense: S.MarketSnapshot<D.ExpenseObservation>;
9
12
  monthly_portfolio: S.MarketSnapshot<D.FundPortfolio>;
@@ -1,9 +1,12 @@
1
+ import type * as A from "./analytics-types.js";
1
2
  import type * as S from "./monthly-types.js";
2
3
  import type * as M from "./models.js";
3
4
  import type * as D from "./market-types.js";
4
5
  import type { CurrentMixEvidence } from "./client-types.js";
5
6
  /** Each entry is the decoded response of the corresponding generated operation. */
6
7
  export interface OperationResults {
8
+ analytics_scheme: A.SchemePerformance;
9
+ analytics_category: A.CategoryStatistics;
7
10
  monthly_nav: S.MarketSnapshot<D.NavObservation>;
8
11
  monthly_expense: S.MarketSnapshot<D.ExpenseObservation>;
9
12
  monthly_portfolio: S.MarketSnapshot<D.FundPortfolio>;
@@ -121,6 +121,73 @@ function responseMatches(id, op, data, params, body, account) {
121
121
  b("valuation_date") !== undefined &&
122
122
  d("valuation_date") !== b("valuation_date")))
123
123
  return false;
124
+ if (id.startsWith("analytics_")) {
125
+ if (id === "analytics_aum") {
126
+ if (d("requested_date") !== params.as_of ||
127
+ (d("observation_date") !== null &&
128
+ String(d("observation_date")) > String(params.as_of)))
129
+ return false;
130
+ if (d("status") === "available" &&
131
+ field(d("data"), "basis") === "reported_fund_total" &&
132
+ field(field(d("data"), "source"), "date") !== d("observation_date"))
133
+ return false;
134
+ if (d("status") === "available" &&
135
+ (field(d("data"), "fund_id") !== params.id ||
136
+ field(d("data"), "date") !== d("observation_date") ||
137
+ String(d("observation_date")) > String(params.as_of)))
138
+ return false;
139
+ }
140
+ else {
141
+ if (d("requested_as_of") !== params.as_of)
142
+ return false;
143
+ const identity = id === "analytics_scheme"
144
+ ? "scheme_id"
145
+ : id === "analytics_portfolio"
146
+ ? "portfolio_id"
147
+ : "category";
148
+ if (d(identity) !== params.id)
149
+ return false;
150
+ if (id === "analytics_category") {
151
+ if (d("cohort", "plan") !== (params.plan ?? "direct") ||
152
+ d("cohort", "option") !== (params.option ?? "growth"))
153
+ return false;
154
+ const requested = String(params.metrics ?? "return").split(",");
155
+ const expected = requested.flatMap((m) => m === "return"
156
+ ? String(params.periods ?? "1y,3y,5y")
157
+ .split(",")
158
+ .map((p) => "return_" + p)
159
+ : [m]);
160
+ for (const key of expected) {
161
+ const metric = d("metrics", key);
162
+ if (!(0, schema_cjs_1.record)(metric))
163
+ return false;
164
+ const eligible = Number(field(metric, "eligible_count")), valid = Number(field(metric, "valid_count"));
165
+ const excluded = [
166
+ "missing_count",
167
+ "stale_count",
168
+ "invalid_count",
169
+ ].reduce((n, k) => n + Number(field(metric, k)), 0);
170
+ if (eligible !== d("cohort", "fund_count") ||
171
+ valid + excluded !== eligible)
172
+ return false;
173
+ if (field(metric, "status") === "available" &&
174
+ (valid < 3 || valid * 100 < eligible * 80))
175
+ return false;
176
+ }
177
+ }
178
+ if (id !== "analytics_category") {
179
+ const expected = String(params.periods ?? "1y,3y,5y").split(",");
180
+ const got = rows(d("periods"));
181
+ if (got.length !== expected.length ||
182
+ got.some((p, i) => field(p, "period") !== expected[i] ||
183
+ field(p, "requested_end") !== params.as_of ||
184
+ (id === "analytics_scheme" &&
185
+ (later(field(p, "start_observation_date"), field(p, "requested_start")) ||
186
+ later(field(p, "end_observation_date"), params.as_of)))))
187
+ return false;
188
+ }
189
+ }
190
+ }
124
191
  if (id.startsWith("monthly_") && id !== "monthly_valuation") {
125
192
  const observation = d("observation_date"), requested = d("requested_date"), data = d("data");
126
193
  if (requested !== params.as_of)
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.check = check;
4
4
  exports.keys = keys;
5
5
  exports.createTransport = createTransport;
6
+ const analytics_contract_cjs_1 = require("./analytics-contract.cjs");
6
7
  const monthly_market_data_cjs_1 = require("./monthly-market-data.cjs");
7
8
  const contract_cjs_1 = require("./contract.cjs");
8
9
  const market_data_cjs_1 = require("./market-data.cjs");
@@ -13,6 +14,7 @@ const operations = {
13
14
  ...contract_cjs_1.operations,
14
15
  ...market_data_cjs_1.operations,
15
16
  ...monthly_market_data_cjs_1.operations,
17
+ ...analytics_contract_cjs_1.operations,
16
18
  };
17
19
  function assertServer() {
18
20
  if (typeof window !== "undefined" ||
@@ -196,13 +198,10 @@ function createTransport(config, marketOnly = false) {
196
198
  check(Number.isInteger(max) && max >= 1 && max <= 16777216);
197
199
  const call = async (id, params = {}, body, opts = {}) => {
198
200
  const op = operations[id];
199
- if (marketOnly)
200
- check([
201
- "monthly_nav",
202
- "monthly_expense",
203
- "monthly_portfolio",
204
- "monthly_holdings",
205
- ].includes(id));
201
+ if (marketOnly &&
202
+ !["monthly_nav", "monthly_expense", "monthly_portfolio", "monthly_holdings"].includes(id)) {
203
+ throw Object.assign(new errors_cjs_1.ValidationError(), { code: "market_only_operation" });
204
+ }
206
205
  let o, serialized, url;
207
206
  try {
208
207
  o = options(opts);
package/dist/validate.cjs CHANGED
@@ -62,7 +62,9 @@ function valid(s, v, depth = 0) {
62
62
  (s.required ?? []).every((k) => Object.hasOwn(v, k)) &&
63
63
  keys.every((k) => Object.hasOwn(s.properties ?? {}, k)
64
64
  ? valid(s.properties?.[k], v[k], depth + 1)
65
- : s.additionalProperties !== false));
65
+ : typeof s.additionalProperties === "object"
66
+ ? valid(s.additionalProperties, v[k], depth + 1)
67
+ : s.additionalProperties !== false));
66
68
  }
67
69
  default:
68
70
  return true;
@@ -0,0 +1,26 @@
1
+ # Growth scheme performance and category returns
2
+
3
+ SDK0.4.0 candidate, unpublished. The two API methods are deployed. Default origin is https://api.bharosa.finance, incorporating the reviewed host-default delta directly on published0.3.2; explicit baseUrl overrides remain supported. Account authorization with accounts:read is required. BharosaMarket does not expose these methods.
4
+
5
+ ```ts
6
+ import { Bharosa } from "bharosa";
7
+ const client = new Bharosa({
8
+ account: "me",
9
+ auth: { type: "account_key", apiKey: process.env.BHAROSA_SECRET_KEY! },
10
+ });
11
+ const scheme = await client.mutualFundSchemes.performance.retrieve("45139", {
12
+ asOf: "2026-10-01", periods: ["1y", "3y"],
13
+ });
14
+ const category = await client.fundCategories.statistics.retrieve("index_fund", {
15
+ asOf: "2026-10-01", periods: ["1y"], metrics: ["return"],
16
+ plan: "direct", option: "growth",
17
+ });
18
+ ```
19
+
20
+ Use an exact first-of-month asOf. Discover category identifiers through marketData.categories.list; index_fund is a real category, equity was only a local fixture label. Category metrics accepts only return and defaults to return. Category returns are equal-weight means with fund deduplication, minimum3valid funds and80%coverage; inspect status/reason and eligible/valid/missing/invalid/stale counts. An HTTP200 can correctly contain an unavailable metric. Current classification is applied to historical prices, with survivorship/reclassification bias; this is not an investable index return.
21
+
22
+ Scheme responses report exact NAV observations and boundary lags, total return, and annualization over observed dates using365.25days. CAGR is unavailable for periods not longer than one year. IDCW and unverified inception remain explicitly unavailable. NAV freshness limits and missing data must be checked before using a value.
23
+
24
+ There are no investor-performance or fund-AUM methods or public types in this release. Stated-TER comparisons remain deferred; existing expense-ratio methods retain their prior behavior. Future AUM belongs to mutualFunds, not schemes. Shared response freshness metadata includes aum_days/ter_days because the deployed envelope contains those fields; their presence does not expose AUM/TER methods.
25
+
26
+ Hosted scheme/category verification used the app-origin compatibility path with synthetic account access and real market data. Authenticated verification on the new api hostname remains a final-trial requirement; do not claim that journey has passed. No SDK example should create credentials or mutate a portfolio.
@@ -6,10 +6,10 @@ Use Node.js 22.12 or later in a supported LTS release. The SDK supports ESM and
6
6
 
7
7
  Obtain your authorized account ID and account key. Bharosa and BharosaMarket default to the production origin `https://api.bharosa.finance`; no API URL environment variable is required. The SDK neither provisions accounts nor runs an OAuth consent flow.
8
8
 
9
- ## Install
9
+ ## Install the unpublished 0.4.0 candidate
10
10
 
11
11
  ```sh
12
- npm install bharosa@0.3.3 --ignore-scripts
12
+ npm install /absolute/path/to/bharosa-0.4.0.tgz --ignore-scripts
13
13
  ```
14
14
 
15
15
  Source contributors can build and pack a local archive as described in CONTRIBUTING.md.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bharosa",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "Typed Bharosa resource SDK with server credentials or caller-owned transport.",
6
6
  "license": "MIT",
@@ -44,7 +44,8 @@
44
44
  "SECURITY.md",
45
45
  "CHANGELOG.md",
46
46
  "FIRST-PARTY-CONTRACT-SHA256",
47
- "MONTHLY-MARKET-CONTRACT-SHA256"
47
+ "MONTHLY-MARKET-CONTRACT-SHA256",
48
+ "ANALYTICS-CONTRACT-SHA256"
48
49
  ],
49
50
  "scripts": {
50
51
  "build": "node scripts/build.mjs",
@@ -58,7 +59,9 @@
58
59
  "check:first-party": "node scripts/generate-first-party.mjs --check",
59
60
  "typecheck": "node tooling/node_modules/typescript/bin/tsc --noEmit && node tooling/node_modules/typescript/bin/tsc -p tsconfig.test.json --noEmit && node tooling/node_modules/typescript/bin/tsc --noEmit --strict --target ES2022 --module NodeNext --moduleResolution NodeNext --typeRoots tooling/node_modules/@types test/api-compatibility.ts",
60
61
  "generate:monthly": "node scripts/generate-monthly.mjs",
61
- "check:monthly": "node scripts/generate-monthly.mjs --check"
62
+ "check:monthly": "node scripts/generate-monthly.mjs --check",
63
+ "generate:analytics": "node scripts/generate-analytics.mjs",
64
+ "check:analytics": "node scripts/generate-analytics.mjs --check"
62
65
  },
63
66
  "publishConfig": {
64
67
  "access": "public",