@fintekkers/ledger-models 0.4.5 → 0.4.9

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.
@@ -14,4 +14,6 @@ export enum TransactionTypeProto {
14
14
  WITHDRAWAL = 4,
15
15
  MATURATION = 5,
16
16
  MATURATION_OFFSET = 6,
17
+ PRINCIPAL_PAYDOWN = 7,
18
+ PRINCIPAL_PAYDOWN_OFFSET = 8,
17
19
  }
@@ -32,7 +32,9 @@ proto.fintekkers.models.transaction.TransactionTypeProto = {
32
32
  DEPOSIT: 3,
33
33
  WITHDRAWAL: 4,
34
34
  MATURATION: 5,
35
- MATURATION_OFFSET: 6
35
+ MATURATION_OFFSET: 6,
36
+ PRINCIPAL_PAYDOWN: 7,
37
+ PRINCIPAL_PAYDOWN_OFFSET: 8
36
38
  };
37
39
 
38
40
  goog.object.extend(exports, proto.fintekkers.models.transaction);
@@ -28,10 +28,16 @@ declare class Security {
28
28
  private static _uuidToProto;
29
29
  private static _zonedDateTimeToProto;
30
30
  /**
31
- * Throws if this Security is in link mode. Use to guard accessors that
32
- * would otherwise return proto3 default values on a link reference.
31
+ * Lazy hydration. If this Security is in link mode, swap in the resolved
32
+ * proto from LinkCache. On cache miss, throws caller must pre-warm via
33
+ * LinkResolver. See docs/adr/lazy-link-hydration.md.
34
+ *
35
+ * TS variant is cache-only (no fetcher hook) because the gRPC stubs are
36
+ * async and chaining the resolver into every getter would force every
37
+ * accessor to become async. Pre-warming through LinkResolver keeps the
38
+ * sync getter API.
33
39
  */
34
- private assertNotLink;
40
+ private ensureHydrated;
35
41
  /**
36
42
  * Factory method to create the appropriate Security subclass based on
37
43
  * the proto's product_type. Dispatch rules:
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  const field_pb_1 = require("../../../fintekkers/models/position/field_pb");
4
27
  const security_pb_1 = require("../../../fintekkers/models/security/security_pb");
@@ -8,6 +31,7 @@ const date_1 = require("../utils/date");
8
31
  const product_type_pb_1 = require("../../../fintekkers/models/security/product_type_pb");
9
32
  const identifier_1 = require("./identifier");
10
33
  const product_hierarchy_1 = require("./product_hierarchy");
34
+ const LinkCacheModule = __importStar(require("../../util/link-cache"));
11
35
  class Security {
12
36
  constructor(proto) {
13
37
  this.proto = proto;
@@ -54,15 +78,33 @@ class Security {
54
78
  return asOf.toProto();
55
79
  }
56
80
  /**
57
- * Throws if this Security is in link mode. Use to guard accessors that
58
- * would otherwise return proto3 default values on a link reference.
81
+ * Lazy hydration. If this Security is in link mode, swap in the resolved
82
+ * proto from LinkCache. On cache miss, throws caller must pre-warm via
83
+ * LinkResolver. See docs/adr/lazy-link-hydration.md.
84
+ *
85
+ * TS variant is cache-only (no fetcher hook) because the gRPC stubs are
86
+ * async and chaining the resolver into every getter would force every
87
+ * accessor to become async. Pre-warming through LinkResolver keeps the
88
+ * sync getter API.
59
89
  */
60
- assertNotLink(accessor) {
61
- if (this.proto.getIsLink()) {
62
- throw new Error(`Cannot read ${accessor} on a link-mode Security (is_link=true). `
63
- + `Resolve via SecurityService.GetByIds first. `
64
- + `See docs/adr/is_link_pattern.md.`);
90
+ ensureHydrated() {
91
+ if (!this.proto.getIsLink())
92
+ return;
93
+ const uuidProto = this.proto.getUuid();
94
+ if (!uuidProto) {
95
+ throw new Error("Cannot read fields on link-mode Security with no UUID set.");
96
+ }
97
+ const uuidKey = uuid_1.UUID.fromU8Array(uuidProto.getRawUuid_asU8()).toString();
98
+ const asOfProto = this.proto.getAsOf();
99
+ const asOf = asOfProto ? new datetime_1.ZonedDateTime(asOfProto) : null;
100
+ const cached = LinkCacheModule.SECURITY.get(uuidKey, asOf);
101
+ if (cached) {
102
+ this.proto = cached;
103
+ return;
65
104
  }
105
+ throw new Error(`Cannot read fields on link-mode Security uuid=${uuidKey} `
106
+ + `— LinkCache miss. Pre-warm via LinkResolver. `
107
+ + `See docs/adr/lazy-link-hydration.md.`);
66
108
  }
67
109
  /**
68
110
  * Factory method to create the appropriate Security subclass based on
@@ -236,15 +278,15 @@ class Security {
236
278
  return new datetime_1.ZonedDateTime(asOf);
237
279
  }
238
280
  getAssetClass() {
239
- this.assertNotLink('assetClass');
281
+ this.ensureHydrated();
240
282
  return this.proto.getAssetClass();
241
283
  }
242
284
  getProductClass() {
243
- this.assertNotLink('productClass');
285
+ this.ensureHydrated();
244
286
  throw new Error('Not implemented yet. See Java implementation for reference');
245
287
  }
246
288
  getProductType() {
247
- this.assertNotLink('productType');
289
+ this.ensureHydrated();
248
290
  const securityType = this.proto.getProductType();
249
291
  const securityTypeString = Object.keys(product_type_pb_1.ProductTypeProto).find(key => product_type_pb_1.ProductTypeProto[key] === securityType);
250
292
  return securityTypeString || 'UNKNOWN_SECURITY_TYPE';
@@ -254,7 +296,7 @@ class Security {
254
296
  * Empty list if none are set. Throws on a link-mode Security.
255
297
  */
256
298
  getIdentifiers() {
257
- this.assertNotLink('identifiers');
299
+ this.ensureHydrated();
258
300
  const list = this.proto.getIdentifiersList();
259
301
  if (!list)
260
302
  return [];
@@ -265,7 +307,7 @@ class Security {
265
307
  * or undefined if none is present. Throws on a link-mode Security.
266
308
  */
267
309
  getIdentifierByType(type) {
268
- this.assertNotLink('identifierByType');
310
+ this.ensureHydrated();
269
311
  const list = this.proto.getIdentifiersList();
270
312
  if (!list)
271
313
  return undefined;
@@ -286,7 +328,7 @@ class Security {
286
328
  * properly-formed bond).
287
329
  */
288
330
  getIssueDate() {
289
- this.assertNotLink('issueDate');
331
+ this.ensureHydrated();
290
332
  const bond = this.getBondLikeDetails();
291
333
  const date = bond ? bond.getIssueDate() : undefined;
292
334
  return (0, date_1.localDateProtoToDate)(date);
@@ -298,7 +340,7 @@ class Security {
298
340
  * code paths.
299
341
  */
300
342
  getMaturityDate() {
301
- this.assertNotLink('maturityDate');
343
+ this.ensureHydrated();
302
344
  const bond = this.getBondLikeDetails();
303
345
  const date = bond ? bond.getMaturityDate() : undefined;
304
346
  return (0, date_1.localDateProtoToDate)(date);
@@ -315,7 +357,7 @@ class Security {
315
357
  return (_a = this.proto.getBondDetails()) !== null && _a !== void 0 ? _a : undefined;
316
358
  }
317
359
  getIssuerName() {
318
- this.assertNotLink('issuerName');
360
+ this.ensureHydrated();
319
361
  return this.proto.getIssuerName();
320
362
  }
321
363
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"security.js","sourceRoot":"","sources":["security.ts"],"names":[],"mappings":";;AAAA,2EAA0E;AAC1E,iFAAgF;AAGhF,gDAAkD;AAClD,wCAAqC;AACrC,wCAAqD;AACrD,yFAAuF;AAEvF,6CAA0C;AAC1C,2DAAqD;AAErD,MAAM,QAAQ;IAGZ,YAAY,KAAoB;QAC9B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,MAAM,CAAC,IAAU,EAAE,IAAmB;QAC3C,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;QAC/G,MAAM,KAAK,GAAG,IAAI,2BAAa,EAAE,CAAC;QAClC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtB,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,IAAU;QAC5B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,IAAI,2BAAa,EAAE,CAAC;QAClC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACtB,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,MAAM,CAAC,YAAY,CAAC,IAAU;QACpC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC;IAEO,MAAM,CAAC,qBAAqB,CAAC,IAAmB;QACtD,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED;;;OAGG;IACK,aAAa,CAAC,QAAgB;QACpC,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE;YAC1B,MAAM,IAAI,KAAK,CACb,eAAe,QAAQ,2CAA2C;kBAChE,8CAA8C;kBAC9C,kCAAkC,CACrC,CAAC;SACH;IACH,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,MAAM,CAAC,KAAoB;QAChC,MAAM,WAAW,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAI,MAAM,CAAC,IAAI,CAAC,kCAAgB,CAA0C;aACnF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,kCAAgB,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC;QAElD,8EAA8E;QAC9E,IAAI,WAAW,KAAK,kCAAgB,CAAC,IAAI,EAAE;YACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC;YAC/C,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;SAC5B;QACD,IAAI,WAAW,KAAK,kCAAgB,CAAC,YAAY,EAAE;YACjD,MAAM,gBAAgB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC,OAAO,CAAC;YAC/D,OAAO,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAC;SACpC;QACD,IAAI,WAAW,KAAK,kCAAgB,CAAC,eAAe,EAAE;YACpD,MAAM,sBAAsB,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC,OAAO,CAAC;YAC3E,OAAO,IAAI,sBAAsB,CAAC,KAAK,CAAC,CAAC;SAC1C;QAED,6DAA6D;QAC7D,IAAI,MAAM,IAAI,IAAA,kCAAc,EAAC,MAAgB,EAAE,MAAM,CAAC,EAAE;YACtD,MAAM,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC;YACvD,OAAO,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;SAChC;QAED,mDAAmD;QACnD,IAAI,MAAM,IAAI,IAAA,kCAAc,EAAC,MAAgB,EAAE,OAAO,CAAC,EAAE;YACvD,MAAM,aAAa,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;YACzD,OAAO,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC;SACjC;QAED,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED;;;;;;;;;;;OAWG;IACH,MAAM;QACJ,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;QACtC,MAAM,MAAM,GAAI,MAAM,CAAC,IAAI,CAAC,kCAAgB,CAA0C;aACnF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,kCAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,OAAO,IAAA,kCAAc,EAAC,MAAgB,EAAE,MAAM,CAAC,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,KAAK;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,kCAAgB,CAAC,eAAe,CAAC;IAC1E,CAAC;IAED;;;OAGG;IACH,OAAO;QACL,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;QACtC,MAAM,MAAM,GAAI,MAAM,CAAC,IAAI,CAAC,kCAAgB,CAA0C;aACnF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,kCAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,OAAO,IAAA,kCAAc,EAAC,MAAgB,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,kCAAgB,CAAC,QAAQ,CAAC;IACnE,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACN,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;QACtC,MAAM,MAAM,GAAI,MAAM,CAAC,IAAI,CAAC,kCAAgB,CAA0C;aACnF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,kCAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,OAAO,IAAA,kCAAc,EAAC,MAAgB,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IAED;;;;OAIG;IACH,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,kCAAgB,CAAC,OAAO,CAAC;IAClE,CAAC;IAGD,QAAQ;QACN,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;QAC1E,MAAM,KAAK,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;YACjC,CAAC,CAAC,IAAI,uBAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;YACnC,CAAC,CAAC,iBAAiB,CAAC;QACtB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;QACxE,OAAO,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,MAAM,GAAG,CAAC;IAC/D,CAAC;IAED,SAAS;QACP,OAAO,CAAC,qBAAU,CAAC,EAAE,EAAE,qBAAU,CAAC,WAAW,EAAE,qBAAU,CAAC,KAAK,EAAE,qBAAU,CAAC,WAAW,EAAE,qBAAU,CAAC,UAAU,CAAC,CAAC;IAClH,CAAC;IAED,QAAQ,CAAC,KAAiB;QACxB,QAAQ,KAAK,EAAE;YACb,KAAK,qBAAU,CAAC,EAAE,CAAC;YACnB,KAAK,qBAAU,CAAC,WAAW;gBACzB,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;YACtB,KAAK,qBAAU,CAAC,KAAK;gBACnB,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;YACxB,KAAK,qBAAU,CAAC,WAAW;gBACzB,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC;YAC9B,KAAK,qBAAU,CAAC,aAAa;gBAC3B,OAAO,IAAI,CAAC,eAAe,EAAE,CAAC;YAChC,KAAK,qBAAU,CAAC,YAAY;gBAC1B,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;YAC/B,KAAK,qBAAU,CAAC,UAAU,CAAC,CAAC;gBAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;gBAC7C,OAAO,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,uBAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;aACjE;YACD,KAAK,qBAAU,CAAC,KAAK,CAAC;YACtB,KAAK,qBAAU,CAAC,cAAc;gBAC5B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACzC,KAAK,qBAAU,CAAC,aAAa;gBAC3B,+DAA+D;gBAC/D,uDAAuD;gBACvD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACvD,KAAK,qBAAU,CAAC,UAAU;gBACxB,gEAAgE;gBAChE,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;YAC7B;gBACE,MAAM,IAAI,KAAK,CAAC,yCAAyC,KAAK,EAAE,CAAC,CAAC;SACrE;IACH,CAAC;IAED,KAAK;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC/C,OAAO,WAAI,CAAC,WAAW,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;IAClD,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;IAChC,CAAC;IAED,OAAO;QACL,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC/C,OAAO,IAAI,wBAAa,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,aAAa;QACX,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;IACpC,CAAC;IAED,eAAe;QACb,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IAED,cAAc;QACZ,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QAClC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;QACjD,MAAM,kBAAkB,GAAI,MAAM,CAAC,IAAI,CAAC,kCAAgB,CAA0C,CAAC,IAAI,CACrG,GAAG,CAAC,EAAE,CAAC,kCAAgB,CAAC,GAAG,CAAC,KAAK,YAAY,CAC9C,CAAC;QAEF,OAAO,kBAAkB,IAAI,uBAAuB,CAAC;IACvD,CAAC;IAED;;;OAGG;IACH,cAAc;QACZ,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QAClC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;QAC7C,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,uBAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED;;;OAGG;IACH,mBAAmB,CAAC,IAAyB;QAC3C,IAAI,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;QAC7C,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAC;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB,EAAE,KAAK,IAAI,CAAC,CAAC;QAC7D,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,uBAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnD,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,YAAY;QACV,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACpD,OAAO,IAAA,2BAAoB,EAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED;;;;;OAKG;IACH,eAAe;QACb,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACvD,OAAO,IAAA,2BAAoB,EAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAED;;;;OAIG;IACO,kBAAkB;;QAC1B,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,KAAK,UAAU;YAAE,OAAO,SAAS,CAAC;QACtE,OAAO,MAAA,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,mCAAI,SAAS,CAAC;IAClD,CAAC;IAED,aAAa;QACX,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC;IACpC,CAAC;IAED;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,OAAa,IAAI,IAAI,EAAE;QAC/B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE;YAAE,OAAO,KAAK,CAAC;QAC3C,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QAC7C,IAAI,CAAC,YAAY;YAAE,OAAO,KAAK,CAAC;QAChC,MAAM,EAAE,GAAG,YAAY,CAAC,YAAY,EAAE,CAAC;QACvC,IAAI,CAAC,EAAE;YAAE,OAAO,KAAK,CAAC;QACtB,MAAM,SAAS,GAAG,EAAE,CAAC,UAAU,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,GAAG,CAAC,CAAC;QAC3E,OAAO,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IACpC,CAAC;IAED,MAAM,CAAC,KAAe;QACpB,IAAI,KAAK,YAAY,QAAQ,EAAE;YAC7B,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;SAC3C;aAAM;YACL,OAAO,KAAK,CAAC;SACd;IACH,CAAC;CACF;AAED,kBAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"security.js","sourceRoot":"","sources":["security.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const security_1 = __importDefault(require("./security"));
30
+ const uuid_1 = require("../utils/uuid");
31
+ const datetime_1 = require("../utils/datetime");
32
+ const LinkCache = __importStar(require("../../util/link-cache"));
33
+ const security_pb_1 = require("../../../fintekkers/models/security/security_pb");
34
+ const local_timestamp_pb_1 = require("../../../fintekkers/models/util/local_timestamp_pb");
35
+ const timestamp_pb_1 = require("google-protobuf/google/protobuf/timestamp_pb");
36
+ function makeAsOf(epochSecondsOffset = 0) {
37
+ const ts = new timestamp_pb_1.Timestamp();
38
+ ts.setSeconds(1700000000 + epochSecondsOffset);
39
+ ts.setNanos(0);
40
+ const proto = new local_timestamp_pb_1.LocalTimestampProto();
41
+ proto.setTimestamp(ts);
42
+ proto.setTimeZone('UTC');
43
+ return new datetime_1.ZonedDateTime(proto);
44
+ }
45
+ function makeFullProto(uuid, asOf, issuer = 'ACME') {
46
+ const p = new security_pb_1.SecurityProto();
47
+ p.setUuid(uuid.toUUIDProto());
48
+ p.setAsOf(asOf.toProto());
49
+ p.setIssuerName(issuer);
50
+ p.setAssetClass('Equity');
51
+ return p;
52
+ }
53
+ describe('Security lazy hydrate', () => {
54
+ beforeEach(() => {
55
+ LinkCache.SECURITY.clear();
56
+ });
57
+ // ---- A. Hydration on accessors ----
58
+ test('A — getAssetClass on link-mode wrapper hydrates from cache', () => {
59
+ const uuid = uuid_1.UUID.fromU8Array(new Uint8Array(16).fill(0x42));
60
+ const asOf = makeAsOf();
61
+ const resolved = makeFullProto(uuid, asOf, 'ACME-resolved');
62
+ LinkCache.SECURITY.put(uuid.toString(), resolved, asOf);
63
+ const wrapper = new security_1.default(security_1.default.linkOf(uuid, asOf));
64
+ expect(wrapper.isLink()).toBe(true);
65
+ expect(wrapper.getAssetClass()).toBe('Equity');
66
+ // After first accessor, wrapper has swapped in the resolved proto.
67
+ expect(wrapper.proto.getIsLink()).toBe(false);
68
+ });
69
+ test('A — link-safe accessors do not require hydration', () => {
70
+ const uuid = uuid_1.UUID.fromU8Array(new Uint8Array(16).fill(0x07));
71
+ const asOf = makeAsOf();
72
+ // Cache is empty — link-safe accessors must still work.
73
+ const wrapper = new security_1.default(security_1.default.linkOf(uuid, asOf));
74
+ expect(wrapper.isLink()).toBe(true);
75
+ expect(wrapper.getID().toString()).toBe(uuid.toString());
76
+ expect(wrapper.getAsOf().getSeconds()).toBe(asOf.getSeconds());
77
+ });
78
+ // ---- B. Cache behavior ----
79
+ test('B.i — first accessor call hydrates from a pre-populated cache', () => {
80
+ const uuid = uuid_1.UUID.fromU8Array(new Uint8Array(16).fill(0x01));
81
+ const asOf = makeAsOf();
82
+ const resolved = makeFullProto(uuid, asOf, 'hydrated');
83
+ LinkCache.SECURITY.put(uuid.toString(), resolved, asOf);
84
+ const wrapper = new security_1.default(security_1.default.linkOf(uuid, asOf));
85
+ expect(wrapper.getIssuerName()).toBe('hydrated');
86
+ });
87
+ test('B.ii — second accessor call hits the swapped proto, not the cache', () => {
88
+ const uuid = uuid_1.UUID.fromU8Array(new Uint8Array(16).fill(0x02));
89
+ const asOf = makeAsOf();
90
+ const resolved = makeFullProto(uuid, asOf, 'firstRead');
91
+ LinkCache.SECURITY.put(uuid.toString(), resolved, asOf);
92
+ const wrapper = new security_1.default(security_1.default.linkOf(uuid, asOf));
93
+ expect(wrapper.getAssetClass()).toBe('Equity');
94
+ // Evict the cache; wrapper has already swapped in the proto so the
95
+ // second read must succeed without touching the cache.
96
+ LinkCache.SECURITY.evict(uuid.toString());
97
+ expect(wrapper.getIssuerName()).toBe('firstRead');
98
+ });
99
+ test('B.iii — fresh wrapper for same (uuid, asOf) reads cache populated by prior wrapper', () => {
100
+ const uuid = uuid_1.UUID.fromU8Array(new Uint8Array(16).fill(0x03));
101
+ const asOf = makeAsOf();
102
+ const resolved = makeFullProto(uuid, asOf, 'shared');
103
+ LinkCache.SECURITY.put(uuid.toString(), resolved, asOf);
104
+ // Two independent wrappers share the cache entry.
105
+ const wrapperA = new security_1.default(security_1.default.linkOf(uuid, asOf));
106
+ const wrapperB = new security_1.default(security_1.default.linkOf(uuid, asOf));
107
+ expect(wrapperA.getIssuerName()).toBe('shared');
108
+ expect(wrapperB.getIssuerName()).toBe('shared');
109
+ });
110
+ // ---- C. asOf semantics ----
111
+ test('C — link asOf differing from cached asOf is a miss', () => {
112
+ const uuid = uuid_1.UUID.fromU8Array(new Uint8Array(16).fill(0x04));
113
+ const asOfT1 = makeAsOf(0);
114
+ const asOfT2 = makeAsOf(86400);
115
+ LinkCache.SECURITY.put(uuid.toString(), makeFullProto(uuid, asOfT2, 'T2'), asOfT2);
116
+ const wrapper = new security_1.default(security_1.default.linkOf(uuid, asOfT1));
117
+ expect(() => wrapper.getIssuerName()).toThrow(/LinkCache miss/);
118
+ });
119
+ // ---- D. Resolve failure ----
120
+ test('D — cache miss throws with uuid in message', () => {
121
+ const uuid = uuid_1.UUID.fromU8Array(new Uint8Array(16).fill(0x05));
122
+ const asOf = makeAsOf();
123
+ const wrapper = new security_1.default(security_1.default.linkOf(uuid, asOf));
124
+ expect(() => wrapper.getAssetClass()).toThrow(new RegExp(uuid.toString()));
125
+ });
126
+ });
127
+ //# sourceMappingURL=security.lazy-hydrate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.lazy-hydrate.test.js","sourceRoot":"","sources":["security.lazy-hydrate.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,123 @@
1
+ import Security from './security';
2
+ import { UUID } from '../utils/uuid';
3
+ import { ZonedDateTime } from '../utils/datetime';
4
+
5
+ import * as LinkCache from '../../util/link-cache';
6
+
7
+ import { SecurityProto } from '../../../fintekkers/models/security/security_pb';
8
+ import { LocalTimestampProto } from '../../../fintekkers/models/util/local_timestamp_pb';
9
+ import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
10
+
11
+ function makeAsOf(epochSecondsOffset: number = 0): ZonedDateTime {
12
+ const ts = new Timestamp();
13
+ ts.setSeconds(1700000000 + epochSecondsOffset);
14
+ ts.setNanos(0);
15
+ const proto = new LocalTimestampProto();
16
+ proto.setTimestamp(ts);
17
+ proto.setTimeZone('UTC');
18
+ return new ZonedDateTime(proto);
19
+ }
20
+
21
+ function makeFullProto(uuid: UUID, asOf: ZonedDateTime, issuer = 'ACME'): SecurityProto {
22
+ const p = new SecurityProto();
23
+ p.setUuid(uuid.toUUIDProto());
24
+ p.setAsOf(asOf.toProto());
25
+ p.setIssuerName(issuer);
26
+ p.setAssetClass('Equity');
27
+ return p;
28
+ }
29
+
30
+ describe('Security lazy hydrate', () => {
31
+ beforeEach(() => {
32
+ LinkCache.SECURITY.clear();
33
+ });
34
+
35
+ // ---- A. Hydration on accessors ----
36
+
37
+ test('A — getAssetClass on link-mode wrapper hydrates from cache', () => {
38
+ const uuid = UUID.fromU8Array(new Uint8Array(16).fill(0x42));
39
+ const asOf = makeAsOf();
40
+ const resolved = makeFullProto(uuid, asOf, 'ACME-resolved');
41
+ LinkCache.SECURITY.put(uuid.toString(), resolved, asOf);
42
+
43
+ const wrapper = new Security(Security.linkOf(uuid, asOf));
44
+ expect(wrapper.isLink()).toBe(true);
45
+
46
+ expect(wrapper.getAssetClass()).toBe('Equity');
47
+ // After first accessor, wrapper has swapped in the resolved proto.
48
+ expect(wrapper.proto.getIsLink()).toBe(false);
49
+ });
50
+
51
+ test('A — link-safe accessors do not require hydration', () => {
52
+ const uuid = UUID.fromU8Array(new Uint8Array(16).fill(0x07));
53
+ const asOf = makeAsOf();
54
+ // Cache is empty — link-safe accessors must still work.
55
+ const wrapper = new Security(Security.linkOf(uuid, asOf));
56
+
57
+ expect(wrapper.isLink()).toBe(true);
58
+ expect(wrapper.getID().toString()).toBe(uuid.toString());
59
+ expect(wrapper.getAsOf().getSeconds()).toBe(asOf.getSeconds());
60
+ });
61
+
62
+ // ---- B. Cache behavior ----
63
+
64
+ test('B.i — first accessor call hydrates from a pre-populated cache', () => {
65
+ const uuid = UUID.fromU8Array(new Uint8Array(16).fill(0x01));
66
+ const asOf = makeAsOf();
67
+ const resolved = makeFullProto(uuid, asOf, 'hydrated');
68
+ LinkCache.SECURITY.put(uuid.toString(), resolved, asOf);
69
+
70
+ const wrapper = new Security(Security.linkOf(uuid, asOf));
71
+ expect(wrapper.getIssuerName()).toBe('hydrated');
72
+ });
73
+
74
+ test('B.ii — second accessor call hits the swapped proto, not the cache', () => {
75
+ const uuid = UUID.fromU8Array(new Uint8Array(16).fill(0x02));
76
+ const asOf = makeAsOf();
77
+ const resolved = makeFullProto(uuid, asOf, 'firstRead');
78
+ LinkCache.SECURITY.put(uuid.toString(), resolved, asOf);
79
+
80
+ const wrapper = new Security(Security.linkOf(uuid, asOf));
81
+ expect(wrapper.getAssetClass()).toBe('Equity');
82
+
83
+ // Evict the cache; wrapper has already swapped in the proto so the
84
+ // second read must succeed without touching the cache.
85
+ LinkCache.SECURITY.evict(uuid.toString());
86
+ expect(wrapper.getIssuerName()).toBe('firstRead');
87
+ });
88
+
89
+ test('B.iii — fresh wrapper for same (uuid, asOf) reads cache populated by prior wrapper', () => {
90
+ const uuid = UUID.fromU8Array(new Uint8Array(16).fill(0x03));
91
+ const asOf = makeAsOf();
92
+ const resolved = makeFullProto(uuid, asOf, 'shared');
93
+ LinkCache.SECURITY.put(uuid.toString(), resolved, asOf);
94
+
95
+ // Two independent wrappers share the cache entry.
96
+ const wrapperA = new Security(Security.linkOf(uuid, asOf));
97
+ const wrapperB = new Security(Security.linkOf(uuid, asOf));
98
+ expect(wrapperA.getIssuerName()).toBe('shared');
99
+ expect(wrapperB.getIssuerName()).toBe('shared');
100
+ });
101
+
102
+ // ---- C. asOf semantics ----
103
+
104
+ test('C — link asOf differing from cached asOf is a miss', () => {
105
+ const uuid = UUID.fromU8Array(new Uint8Array(16).fill(0x04));
106
+ const asOfT1 = makeAsOf(0);
107
+ const asOfT2 = makeAsOf(86400);
108
+ LinkCache.SECURITY.put(uuid.toString(), makeFullProto(uuid, asOfT2, 'T2'), asOfT2);
109
+
110
+ const wrapper = new Security(Security.linkOf(uuid, asOfT1));
111
+ expect(() => wrapper.getIssuerName()).toThrow(/LinkCache miss/);
112
+ });
113
+
114
+ // ---- D. Resolve failure ----
115
+
116
+ test('D — cache miss throws with uuid in message', () => {
117
+ const uuid = UUID.fromU8Array(new Uint8Array(16).fill(0x05));
118
+ const asOf = makeAsOf();
119
+ const wrapper = new Security(Security.linkOf(uuid, asOf));
120
+
121
+ expect(() => wrapper.getAssetClass()).toThrow(new RegExp(uuid.toString()));
122
+ });
123
+ });
@@ -9,6 +9,7 @@ import { ProductTypeProto } from "../../../fintekkers/models/security/product_ty
9
9
  import { IdentifierTypeProto } from "../../../fintekkers/models/security/identifier/identifier_type_pb";
10
10
  import { Identifier } from "./identifier";
11
11
  import { isDescendantOf } from "./product_hierarchy";
12
+ import * as LinkCacheModule from "../../util/link-cache";
12
13
 
13
14
  class Security {
14
15
  proto: SecurityProto;
@@ -60,17 +61,34 @@ class Security {
60
61
  }
61
62
 
62
63
  /**
63
- * Throws if this Security is in link mode. Use to guard accessors that
64
- * would otherwise return proto3 default values on a link reference.
64
+ * Lazy hydration. If this Security is in link mode, swap in the resolved
65
+ * proto from LinkCache. On cache miss, throws caller must pre-warm via
66
+ * LinkResolver. See docs/adr/lazy-link-hydration.md.
67
+ *
68
+ * TS variant is cache-only (no fetcher hook) because the gRPC stubs are
69
+ * async and chaining the resolver into every getter would force every
70
+ * accessor to become async. Pre-warming through LinkResolver keeps the
71
+ * sync getter API.
65
72
  */
66
- private assertNotLink(accessor: string): void {
67
- if (this.proto.getIsLink()) {
68
- throw new Error(
69
- `Cannot read ${accessor} on a link-mode Security (is_link=true). `
70
- + `Resolve via SecurityService.GetByIds first. `
71
- + `See docs/adr/is_link_pattern.md.`
72
- );
73
+ private ensureHydrated(): void {
74
+ if (!this.proto.getIsLink()) return;
75
+ const uuidProto = this.proto.getUuid();
76
+ if (!uuidProto) {
77
+ throw new Error("Cannot read fields on link-mode Security with no UUID set.");
78
+ }
79
+ const uuidKey = UUID.fromU8Array(uuidProto.getRawUuid_asU8()).toString();
80
+ const asOfProto = this.proto.getAsOf();
81
+ const asOf = asOfProto ? new ZonedDateTime(asOfProto) : null;
82
+ const cached = LinkCacheModule.SECURITY.get(uuidKey, asOf);
83
+ if (cached) {
84
+ this.proto = cached;
85
+ return;
73
86
  }
87
+ throw new Error(
88
+ `Cannot read fields on link-mode Security uuid=${uuidKey} `
89
+ + `— LinkCache miss. Pre-warm via LinkResolver. `
90
+ + `See docs/adr/lazy-link-hydration.md.`
91
+ );
74
92
  }
75
93
 
76
94
  /**
@@ -258,17 +276,17 @@ class Security {
258
276
  }
259
277
 
260
278
  getAssetClass(): string {
261
- this.assertNotLink('assetClass');
279
+ this.ensureHydrated();
262
280
  return this.proto.getAssetClass();
263
281
  }
264
282
 
265
283
  getProductClass(): string {
266
- this.assertNotLink('productClass');
284
+ this.ensureHydrated();
267
285
  throw new Error('Not implemented yet. See Java implementation for reference');
268
286
  }
269
287
 
270
288
  getProductType(): string {
271
- this.assertNotLink('productType');
289
+ this.ensureHydrated();
272
290
  const securityType = this.proto.getProductType();
273
291
  const securityTypeString = (Object.keys(ProductTypeProto) as Array<keyof typeof ProductTypeProto>).find(
274
292
  key => ProductTypeProto[key] === securityType
@@ -282,7 +300,7 @@ class Security {
282
300
  * Empty list if none are set. Throws on a link-mode Security.
283
301
  */
284
302
  getIdentifiers(): Identifier[] {
285
- this.assertNotLink('identifiers');
303
+ this.ensureHydrated();
286
304
  const list = this.proto.getIdentifiersList();
287
305
  if (!list) return [];
288
306
  return list.map(p => new Identifier(p));
@@ -293,7 +311,7 @@ class Security {
293
311
  * or undefined if none is present. Throws on a link-mode Security.
294
312
  */
295
313
  getIdentifierByType(type: IdentifierTypeProto): Identifier | undefined {
296
- this.assertNotLink('identifierByType');
314
+ this.ensureHydrated();
297
315
  const list = this.proto.getIdentifiersList();
298
316
  if (!list) return undefined;
299
317
  const found = list.find(p => p.getIdentifierType() === type);
@@ -314,7 +332,7 @@ class Security {
314
332
  * properly-formed bond).
315
333
  */
316
334
  getIssueDate(): Date | null {
317
- this.assertNotLink('issueDate');
335
+ this.ensureHydrated();
318
336
  const bond = this.getBondLikeDetails();
319
337
  const date = bond ? bond.getIssueDate() : undefined;
320
338
  return localDateProtoToDate(date);
@@ -327,7 +345,7 @@ class Security {
327
345
  * code paths.
328
346
  */
329
347
  getMaturityDate(): Date | null {
330
- this.assertNotLink('maturityDate');
348
+ this.ensureHydrated();
331
349
  const bond = this.getBondLikeDetails();
332
350
  const date = bond ? bond.getMaturityDate() : undefined;
333
351
  return localDateProtoToDate(date);
@@ -344,7 +362,7 @@ class Security {
344
362
  }
345
363
 
346
364
  getIssuerName(): string {
347
- this.assertNotLink('issuerName');
365
+ this.ensureHydrated();
348
366
  return this.proto.getIssuerName();
349
367
  }
350
368
 
@@ -11,10 +11,12 @@ class TransactionType {
11
11
  case transaction_type_pb_1.TransactionTypeProto.BUY:
12
12
  case transaction_type_pb_1.TransactionTypeProto.DEPOSIT:
13
13
  case transaction_type_pb_1.TransactionTypeProto.MATURATION_OFFSET:
14
+ case transaction_type_pb_1.TransactionTypeProto.PRINCIPAL_PAYDOWN_OFFSET:
14
15
  return 1;
15
16
  case transaction_type_pb_1.TransactionTypeProto.SELL:
16
17
  case transaction_type_pb_1.TransactionTypeProto.WITHDRAWAL:
17
18
  case transaction_type_pb_1.TransactionTypeProto.MATURATION:
19
+ case transaction_type_pb_1.TransactionTypeProto.PRINCIPAL_PAYDOWN:
18
20
  return -1;
19
21
  case transaction_type_pb_1.TransactionTypeProto.UNKNOWN:
20
22
  throw new Error('Unknown transaction type: ' + this.toString());
@@ -1 +1 @@
1
- {"version":3,"file":"transaction_type.js","sourceRoot":"","sources":["transaction_type.ts"],"names":[],"mappings":";;;AAAA,oGAAkG;AAElG,MAAa,eAAe;IAGxB,YAAY,KAA2B;QACnC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAYD,sBAAsB;QAClB,QAAQ,IAAI,CAAC,KAAK,EAAE;YAChB,KAAK,0CAAoB,CAAC,GAAG,CAAC;YAC9B,KAAK,0CAAoB,CAAC,OAAO,CAAC;YAClC,KAAK,0CAAoB,CAAC,iBAAiB;gBACvC,OAAO,CAAC,CAAC;YACb,KAAK,0CAAoB,CAAC,IAAI,CAAC;YAC/B,KAAK,0CAAoB,CAAC,UAAU,CAAC;YACrC,KAAK,0CAAoB,CAAC,UAAU;gBAChC,OAAO,CAAC,CAAC,CAAC;YACd,KAAK,0CAAoB,CAAC,OAAO;gBAC7B,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;SACvE;IACL,CAAC;IAED;;;;;;;;OAQG;IACH,QAAQ;;QACJ,OAAO,MAAA,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,mCAAI,SAAS,CAAC;IAClE,CAAC;CACJ;AA5CD,0CA4CC;AAnCG;IACI,eAAe,CAAC,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEtD,MAAM,CAAC,IAAI,CAAC,0CAAoB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QAC5C,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,0CAAoB,CAAC,GAAwC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;AACP,CAAC,GAAA,CAAA"}
1
+ {"version":3,"file":"transaction_type.js","sourceRoot":"","sources":["transaction_type.ts"],"names":[],"mappings":";;;AAAA,oGAAkG;AAElG,MAAa,eAAe;IAGxB,YAAY,KAA2B;QACnC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;IAYD,sBAAsB;QAClB,QAAQ,IAAI,CAAC,KAAK,EAAE;YAChB,KAAK,0CAAoB,CAAC,GAAG,CAAC;YAC9B,KAAK,0CAAoB,CAAC,OAAO,CAAC;YAClC,KAAK,0CAAoB,CAAC,iBAAiB,CAAC;YAC5C,KAAK,0CAAoB,CAAC,wBAAwB;gBAC9C,OAAO,CAAC,CAAC;YACb,KAAK,0CAAoB,CAAC,IAAI,CAAC;YAC/B,KAAK,0CAAoB,CAAC,UAAU,CAAC;YACrC,KAAK,0CAAoB,CAAC,UAAU,CAAC;YACrC,KAAK,0CAAoB,CAAC,iBAAiB;gBACvC,OAAO,CAAC,CAAC,CAAC;YACd,KAAK,0CAAoB,CAAC,OAAO;gBAC7B,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;SACvE;IACL,CAAC;IAED;;;;;;;;OAQG;IACH,QAAQ;;QACJ,OAAO,MAAA,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,mCAAI,SAAS,CAAC;IAClE,CAAC;CACJ;AA9CD,0CA8CC;AArCG;IACI,eAAe,CAAC,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEtD,MAAM,CAAC,IAAI,CAAC,0CAAoB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;QAC5C,eAAe,CAAC,SAAS,CAAC,GAAG,CAAC,0CAAoB,CAAC,GAAwC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;AACP,CAAC,GAAA,CAAA"}
@@ -22,10 +22,12 @@ export class TransactionType {
22
22
  case TransactionTypeProto.BUY:
23
23
  case TransactionTypeProto.DEPOSIT:
24
24
  case TransactionTypeProto.MATURATION_OFFSET:
25
+ case TransactionTypeProto.PRINCIPAL_PAYDOWN_OFFSET:
25
26
  return 1;
26
27
  case TransactionTypeProto.SELL:
27
28
  case TransactionTypeProto.WITHDRAWAL:
28
29
  case TransactionTypeProto.MATURATION:
30
+ case TransactionTypeProto.PRINCIPAL_PAYDOWN:
29
31
  return -1;
30
32
  case TransactionTypeProto.UNKNOWN:
31
33
  throw new Error('Unknown transaction type: ' + this.toString());
@@ -0,0 +1,56 @@
1
+ import { SecurityProto } from '../../fintekkers/models/security/security_pb';
2
+ import { PortfolioProto } from '../../fintekkers/models/portfolio/portfolio_pb';
3
+ import { PriceProto } from '../../fintekkers/models/price/price_pb';
4
+ import { TransactionProto } from '../../fintekkers/models/transaction/transaction_pb';
5
+ import { ZonedDateTime } from '../models/utils/datetime';
6
+ /**
7
+ * LinkCache — process-wide cache of resolved proto bodies backing link-mode
8
+ * wrappers. TypeScript mirror of `common.util.LinkCache` (Java) and
9
+ * `fintekkers.wrappers.util.link_cache` (Python). See
10
+ * `docs/adr/lazy-link-hydration.md`.
11
+ *
12
+ * Read semantics (`get`):
13
+ * - `requestedAsOf == null` ("latest acceptable") — cache hit allowed;
14
+ * subject to `ttlForLatestMs` to bound cross-process staleness this
15
+ * process can't observe. Past TTL ⇒ miss.
16
+ * - `requestedAsOf != null` (bitemporal-precise) — cache hit only when the
17
+ * cached entry's asOf equals the requested. No TTL — history doesn't
18
+ * change, so a past vintage cached arbitrarily long is fine.
19
+ *
20
+ * Write semantics (`put`): newest-vintage wins. An older-vintage put does
21
+ * not evict a newer cached entry.
22
+ *
23
+ * Later Portfolio can likely be 1 day, security 1 day, transaction 1 minute,
24
+ * price 30 seconds — once per-entity TTLs are wired up, the shared singletons
25
+ * below should be constructed with those values.
26
+ */
27
+ export declare const DEFAULT_TTL_FOR_LATEST_MS = 600000;
28
+ export declare class LinkCache<V> {
29
+ private ttlForLatestMs;
30
+ private map;
31
+ constructor(ttlForLatestMs?: number);
32
+ /**
33
+ * @param uuidKey the entity uuid rendered as a stable string key
34
+ * (use uuid.toString())
35
+ * @param requestedAsOf null = "latest acceptable" (TTL-bounded);
36
+ * non-null = exact-vintage match required (no TTL)
37
+ * @returns the cached value if the lookup is a hit; otherwise undefined
38
+ * (caller must refetch)
39
+ */
40
+ get(uuidKey: string, requestedAsOf: ZonedDateTime | null): V | undefined;
41
+ /**
42
+ * Newest-wins write: if a cached entry for `uuidKey` already exists with
43
+ * an asOf strictly after the incoming asOf, the write is ignored.
44
+ */
45
+ put(uuidKey: string, value: V, asOf: ZonedDateTime): void;
46
+ evict(uuidKey: string): void;
47
+ clear(): void;
48
+ /** Test helper. */
49
+ size(): number;
50
+ private _sameAsOf;
51
+ private _isStrictlyAfter;
52
+ }
53
+ export declare const SECURITY: LinkCache<SecurityProto>;
54
+ export declare const PORTFOLIO: LinkCache<PortfolioProto>;
55
+ export declare const PRICE: LinkCache<PriceProto>;
56
+ export declare const TRANSACTION: LinkCache<TransactionProto>;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TRANSACTION = exports.PRICE = exports.PORTFOLIO = exports.SECURITY = exports.LinkCache = exports.DEFAULT_TTL_FOR_LATEST_MS = void 0;
4
+ /**
5
+ * LinkCache — process-wide cache of resolved proto bodies backing link-mode
6
+ * wrappers. TypeScript mirror of `common.util.LinkCache` (Java) and
7
+ * `fintekkers.wrappers.util.link_cache` (Python). See
8
+ * `docs/adr/lazy-link-hydration.md`.
9
+ *
10
+ * Read semantics (`get`):
11
+ * - `requestedAsOf == null` ("latest acceptable") — cache hit allowed;
12
+ * subject to `ttlForLatestMs` to bound cross-process staleness this
13
+ * process can't observe. Past TTL ⇒ miss.
14
+ * - `requestedAsOf != null` (bitemporal-precise) — cache hit only when the
15
+ * cached entry's asOf equals the requested. No TTL — history doesn't
16
+ * change, so a past vintage cached arbitrarily long is fine.
17
+ *
18
+ * Write semantics (`put`): newest-vintage wins. An older-vintage put does
19
+ * not evict a newer cached entry.
20
+ *
21
+ * Later Portfolio can likely be 1 day, security 1 day, transaction 1 minute,
22
+ * price 30 seconds — once per-entity TTLs are wired up, the shared singletons
23
+ * below should be constructed with those values.
24
+ */
25
+ exports.DEFAULT_TTL_FOR_LATEST_MS = 600000;
26
+ class LinkCache {
27
+ constructor(ttlForLatestMs = exports.DEFAULT_TTL_FOR_LATEST_MS) {
28
+ this.ttlForLatestMs = ttlForLatestMs;
29
+ this.map = new Map();
30
+ if (ttlForLatestMs < 0) {
31
+ throw new Error(`ttlForLatestMs must be non-negative; got ${ttlForLatestMs}`);
32
+ }
33
+ }
34
+ /**
35
+ * @param uuidKey the entity uuid rendered as a stable string key
36
+ * (use uuid.toString())
37
+ * @param requestedAsOf null = "latest acceptable" (TTL-bounded);
38
+ * non-null = exact-vintage match required (no TTL)
39
+ * @returns the cached value if the lookup is a hit; otherwise undefined
40
+ * (caller must refetch)
41
+ */
42
+ get(uuidKey, requestedAsOf) {
43
+ const entry = this.map.get(uuidKey);
44
+ if (!entry)
45
+ return undefined;
46
+ if (requestedAsOf == null) {
47
+ if (Date.now() - entry.cachedAtMs > this.ttlForLatestMs) {
48
+ return undefined;
49
+ }
50
+ return entry.value;
51
+ }
52
+ return this._sameAsOf(entry.asOf, requestedAsOf) ? entry.value : undefined;
53
+ }
54
+ /**
55
+ * Newest-wins write: if a cached entry for `uuidKey` already exists with
56
+ * an asOf strictly after the incoming asOf, the write is ignored.
57
+ */
58
+ put(uuidKey, value, asOf) {
59
+ if (!uuidKey || !value || !asOf) {
60
+ throw new Error(`uuidKey / value / asOf must all be set; got uuidKey=${uuidKey} value=${value ? '<non-null>' : 'null'} asOf=${asOf}`);
61
+ }
62
+ const existing = this.map.get(uuidKey);
63
+ if (existing && this._isStrictlyAfter(existing.asOf, asOf)) {
64
+ return;
65
+ }
66
+ this.map.set(uuidKey, { value, asOf, cachedAtMs: Date.now() });
67
+ }
68
+ evict(uuidKey) {
69
+ this.map.delete(uuidKey);
70
+ }
71
+ clear() {
72
+ this.map.clear();
73
+ }
74
+ /** Test helper. */
75
+ size() {
76
+ return this.map.size;
77
+ }
78
+ _sameAsOf(a, b) {
79
+ return a.getSeconds() === b.getSeconds() && a.getNanoSeconds() === b.getNanoSeconds();
80
+ }
81
+ _isStrictlyAfter(a, b) {
82
+ if (a.getSeconds() !== b.getSeconds())
83
+ return a.getSeconds() > b.getSeconds();
84
+ return a.getNanoSeconds() > b.getNanoSeconds();
85
+ }
86
+ }
87
+ exports.LinkCache = LinkCache;
88
+ exports.SECURITY = new LinkCache();
89
+ exports.PORTFOLIO = new LinkCache();
90
+ exports.PRICE = new LinkCache();
91
+ exports.TRANSACTION = new LinkCache();
92
+ //# sourceMappingURL=link-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-cache.js","sourceRoot":"","sources":["link-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const link_cache_1 = require("./link-cache");
13
+ const datetime_1 = require("../models/utils/datetime");
14
+ const local_timestamp_pb_1 = require("../../fintekkers/models/util/local_timestamp_pb");
15
+ const timestamp_pb_1 = require("google-protobuf/google/protobuf/timestamp_pb");
16
+ const security_pb_1 = require("../../fintekkers/models/security/security_pb");
17
+ function makeAsOf(epochSecondsOffset = 0) {
18
+ const ts = new timestamp_pb_1.Timestamp();
19
+ ts.setSeconds(1700000000 + epochSecondsOffset);
20
+ ts.setNanos(0);
21
+ const proto = new local_timestamp_pb_1.LocalTimestampProto();
22
+ proto.setTimestamp(ts);
23
+ proto.setTimeZone('UTC');
24
+ return new datetime_1.ZonedDateTime(proto);
25
+ }
26
+ function makeSecurityProto(name) {
27
+ const p = new security_pb_1.SecurityProto();
28
+ p.setIssuerName(name);
29
+ return p;
30
+ }
31
+ describe('LinkCache', () => {
32
+ beforeEach(() => {
33
+ link_cache_1.SECURITY.clear();
34
+ link_cache_1.PORTFOLIO.clear();
35
+ link_cache_1.PRICE.clear();
36
+ link_cache_1.TRANSACTION.clear();
37
+ });
38
+ // ---- A. Basic get/put ----
39
+ test('get on empty cache returns undefined', () => {
40
+ expect(link_cache_1.SECURITY.get('uuid-1', makeAsOf())).toBeUndefined();
41
+ });
42
+ test('put then get with matching asOf returns value', () => {
43
+ const asOf = makeAsOf();
44
+ const val = makeSecurityProto('ACME');
45
+ link_cache_1.SECURITY.put('uuid-1', val, asOf);
46
+ expect(link_cache_1.SECURITY.get('uuid-1', asOf)).toBe(val);
47
+ });
48
+ test('get with different asOf returns undefined', () => {
49
+ link_cache_1.SECURITY.put('uuid-1', makeSecurityProto('v1'), makeAsOf(0));
50
+ expect(link_cache_1.SECURITY.get('uuid-1', makeAsOf(10))).toBeUndefined();
51
+ });
52
+ // ---- B. Null asOf semantics ----
53
+ test('null asOf within TTL returns value', () => {
54
+ const cache = new link_cache_1.LinkCache(60000);
55
+ cache.put('uuid-1', makeSecurityProto('v1'), makeAsOf());
56
+ expect(cache.get('uuid-1', null)).toBeDefined();
57
+ });
58
+ test('null asOf past TTL returns undefined', () => __awaiter(void 0, void 0, void 0, function* () {
59
+ const cache = new link_cache_1.LinkCache(50);
60
+ cache.put('uuid-1', makeSecurityProto('v1'), makeAsOf());
61
+ yield new Promise((resolve) => setTimeout(resolve, 100));
62
+ expect(cache.get('uuid-1', null)).toBeUndefined();
63
+ }));
64
+ test('non-null asOf is not subject to TTL', () => __awaiter(void 0, void 0, void 0, function* () {
65
+ const cache = new link_cache_1.LinkCache(50);
66
+ const asOf = makeAsOf();
67
+ const val = makeSecurityProto('v1');
68
+ cache.put('uuid-1', val, asOf);
69
+ yield new Promise((resolve) => setTimeout(resolve, 100));
70
+ // Bitemporal: history doesn't change, exact-asOf reads never expire.
71
+ expect(cache.get('uuid-1', asOf)).toBe(val);
72
+ }));
73
+ // ---- C. Newest-wins merge ----
74
+ test('put with older asOf does not overwrite newer', () => {
75
+ var _a;
76
+ link_cache_1.SECURITY.put('uuid-1', makeSecurityProto('newer'), makeAsOf(100));
77
+ link_cache_1.SECURITY.put('uuid-1', makeSecurityProto('older'), makeAsOf(50));
78
+ expect((_a = link_cache_1.SECURITY.get('uuid-1', makeAsOf(100))) === null || _a === void 0 ? void 0 : _a.getIssuerName()).toBe('newer');
79
+ expect(link_cache_1.SECURITY.get('uuid-1', makeAsOf(50))).toBeUndefined();
80
+ });
81
+ test('put with newer asOf replaces older', () => {
82
+ var _a;
83
+ link_cache_1.SECURITY.put('uuid-1', makeSecurityProto('older'), makeAsOf(50));
84
+ link_cache_1.SECURITY.put('uuid-1', makeSecurityProto('newer'), makeAsOf(100));
85
+ expect((_a = link_cache_1.SECURITY.get('uuid-1', makeAsOf(100))) === null || _a === void 0 ? void 0 : _a.getIssuerName()).toBe('newer');
86
+ });
87
+ // ---- D. Evict & clear ----
88
+ test('evict removes entry', () => {
89
+ link_cache_1.SECURITY.put('uuid-1', makeSecurityProto('v1'), makeAsOf());
90
+ link_cache_1.SECURITY.evict('uuid-1');
91
+ expect(link_cache_1.SECURITY.get('uuid-1', makeAsOf())).toBeUndefined();
92
+ });
93
+ test('clear empties cache', () => {
94
+ link_cache_1.SECURITY.put('a', makeSecurityProto('a'), makeAsOf());
95
+ link_cache_1.SECURITY.put('b', makeSecurityProto('b'), makeAsOf());
96
+ link_cache_1.SECURITY.clear();
97
+ expect(link_cache_1.SECURITY.size()).toBe(0);
98
+ });
99
+ // ---- E. Singleton isolation ----
100
+ test('singletons have independent state', () => {
101
+ link_cache_1.SECURITY.put('uuid-1', makeSecurityProto('sec'), makeAsOf());
102
+ expect(link_cache_1.PORTFOLIO.get('uuid-1', makeAsOf())).toBeUndefined();
103
+ expect(link_cache_1.PRICE.get('uuid-1', makeAsOf())).toBeUndefined();
104
+ expect(link_cache_1.TRANSACTION.get('uuid-1', makeAsOf())).toBeUndefined();
105
+ });
106
+ });
107
+ //# sourceMappingURL=link-cache.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-cache.test.js","sourceRoot":"","sources":["link-cache.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ import { LinkCache, SECURITY, PORTFOLIO, PRICE, TRANSACTION } from './link-cache';
2
+ import { ZonedDateTime } from '../models/utils/datetime';
3
+ import { LocalTimestampProto } from '../../fintekkers/models/util/local_timestamp_pb';
4
+ import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
5
+ import { SecurityProto } from '../../fintekkers/models/security/security_pb';
6
+
7
+ function makeAsOf(epochSecondsOffset: number = 0): ZonedDateTime {
8
+ const ts = new Timestamp();
9
+ ts.setSeconds(1700000000 + epochSecondsOffset);
10
+ ts.setNanos(0);
11
+ const proto = new LocalTimestampProto();
12
+ proto.setTimestamp(ts);
13
+ proto.setTimeZone('UTC');
14
+ return new ZonedDateTime(proto);
15
+ }
16
+
17
+ function makeSecurityProto(name: string): SecurityProto {
18
+ const p = new SecurityProto();
19
+ p.setIssuerName(name);
20
+ return p;
21
+ }
22
+
23
+ describe('LinkCache', () => {
24
+ beforeEach(() => {
25
+ SECURITY.clear();
26
+ PORTFOLIO.clear();
27
+ PRICE.clear();
28
+ TRANSACTION.clear();
29
+ });
30
+
31
+ // ---- A. Basic get/put ----
32
+
33
+ test('get on empty cache returns undefined', () => {
34
+ expect(SECURITY.get('uuid-1', makeAsOf())).toBeUndefined();
35
+ });
36
+
37
+ test('put then get with matching asOf returns value', () => {
38
+ const asOf = makeAsOf();
39
+ const val = makeSecurityProto('ACME');
40
+ SECURITY.put('uuid-1', val, asOf);
41
+ expect(SECURITY.get('uuid-1', asOf)).toBe(val);
42
+ });
43
+
44
+ test('get with different asOf returns undefined', () => {
45
+ SECURITY.put('uuid-1', makeSecurityProto('v1'), makeAsOf(0));
46
+ expect(SECURITY.get('uuid-1', makeAsOf(10))).toBeUndefined();
47
+ });
48
+
49
+ // ---- B. Null asOf semantics ----
50
+
51
+ test('null asOf within TTL returns value', () => {
52
+ const cache = new LinkCache<SecurityProto>(60_000);
53
+ cache.put('uuid-1', makeSecurityProto('v1'), makeAsOf());
54
+ expect(cache.get('uuid-1', null)).toBeDefined();
55
+ });
56
+
57
+ test('null asOf past TTL returns undefined', async () => {
58
+ const cache = new LinkCache<SecurityProto>(50);
59
+ cache.put('uuid-1', makeSecurityProto('v1'), makeAsOf());
60
+ await new Promise((resolve) => setTimeout(resolve, 100));
61
+ expect(cache.get('uuid-1', null)).toBeUndefined();
62
+ });
63
+
64
+ test('non-null asOf is not subject to TTL', async () => {
65
+ const cache = new LinkCache<SecurityProto>(50);
66
+ const asOf = makeAsOf();
67
+ const val = makeSecurityProto('v1');
68
+ cache.put('uuid-1', val, asOf);
69
+ await new Promise((resolve) => setTimeout(resolve, 100));
70
+ // Bitemporal: history doesn't change, exact-asOf reads never expire.
71
+ expect(cache.get('uuid-1', asOf)).toBe(val);
72
+ });
73
+
74
+ // ---- C. Newest-wins merge ----
75
+
76
+ test('put with older asOf does not overwrite newer', () => {
77
+ SECURITY.put('uuid-1', makeSecurityProto('newer'), makeAsOf(100));
78
+ SECURITY.put('uuid-1', makeSecurityProto('older'), makeAsOf(50));
79
+ expect(SECURITY.get('uuid-1', makeAsOf(100))?.getIssuerName()).toBe('newer');
80
+ expect(SECURITY.get('uuid-1', makeAsOf(50))).toBeUndefined();
81
+ });
82
+
83
+ test('put with newer asOf replaces older', () => {
84
+ SECURITY.put('uuid-1', makeSecurityProto('older'), makeAsOf(50));
85
+ SECURITY.put('uuid-1', makeSecurityProto('newer'), makeAsOf(100));
86
+ expect(SECURITY.get('uuid-1', makeAsOf(100))?.getIssuerName()).toBe('newer');
87
+ });
88
+
89
+ // ---- D. Evict & clear ----
90
+
91
+ test('evict removes entry', () => {
92
+ SECURITY.put('uuid-1', makeSecurityProto('v1'), makeAsOf());
93
+ SECURITY.evict('uuid-1');
94
+ expect(SECURITY.get('uuid-1', makeAsOf())).toBeUndefined();
95
+ });
96
+
97
+ test('clear empties cache', () => {
98
+ SECURITY.put('a', makeSecurityProto('a'), makeAsOf());
99
+ SECURITY.put('b', makeSecurityProto('b'), makeAsOf());
100
+ SECURITY.clear();
101
+ expect(SECURITY.size()).toBe(0);
102
+ });
103
+
104
+ // ---- E. Singleton isolation ----
105
+
106
+ test('singletons have independent state', () => {
107
+ SECURITY.put('uuid-1', makeSecurityProto('sec'), makeAsOf());
108
+ expect(PORTFOLIO.get('uuid-1', makeAsOf())).toBeUndefined();
109
+ expect(PRICE.get('uuid-1', makeAsOf())).toBeUndefined();
110
+ expect(TRANSACTION.get('uuid-1', makeAsOf())).toBeUndefined();
111
+ });
112
+ });
@@ -0,0 +1,110 @@
1
+ import { SecurityProto } from '../../fintekkers/models/security/security_pb';
2
+ import { PortfolioProto } from '../../fintekkers/models/portfolio/portfolio_pb';
3
+ import { PriceProto } from '../../fintekkers/models/price/price_pb';
4
+ import { TransactionProto } from '../../fintekkers/models/transaction/transaction_pb';
5
+ import { ZonedDateTime } from '../models/utils/datetime';
6
+
7
+ /**
8
+ * LinkCache — process-wide cache of resolved proto bodies backing link-mode
9
+ * wrappers. TypeScript mirror of `common.util.LinkCache` (Java) and
10
+ * `fintekkers.wrappers.util.link_cache` (Python). See
11
+ * `docs/adr/lazy-link-hydration.md`.
12
+ *
13
+ * Read semantics (`get`):
14
+ * - `requestedAsOf == null` ("latest acceptable") — cache hit allowed;
15
+ * subject to `ttlForLatestMs` to bound cross-process staleness this
16
+ * process can't observe. Past TTL ⇒ miss.
17
+ * - `requestedAsOf != null` (bitemporal-precise) — cache hit only when the
18
+ * cached entry's asOf equals the requested. No TTL — history doesn't
19
+ * change, so a past vintage cached arbitrarily long is fine.
20
+ *
21
+ * Write semantics (`put`): newest-vintage wins. An older-vintage put does
22
+ * not evict a newer cached entry.
23
+ *
24
+ * Later Portfolio can likely be 1 day, security 1 day, transaction 1 minute,
25
+ * price 30 seconds — once per-entity TTLs are wired up, the shared singletons
26
+ * below should be constructed with those values.
27
+ */
28
+ export const DEFAULT_TTL_FOR_LATEST_MS = 600_000;
29
+
30
+ interface CacheEntry<V> {
31
+ value: V;
32
+ /** Bitemporal asOf carried by the cached value. */
33
+ asOf: ZonedDateTime;
34
+ /** Wall-clock ms at the moment this entry was cached. */
35
+ cachedAtMs: number;
36
+ }
37
+
38
+ export class LinkCache<V> {
39
+ private map = new Map<string, CacheEntry<V>>();
40
+
41
+ constructor(private ttlForLatestMs: number = DEFAULT_TTL_FOR_LATEST_MS) {
42
+ if (ttlForLatestMs < 0) {
43
+ throw new Error(`ttlForLatestMs must be non-negative; got ${ttlForLatestMs}`);
44
+ }
45
+ }
46
+
47
+ /**
48
+ * @param uuidKey the entity uuid rendered as a stable string key
49
+ * (use uuid.toString())
50
+ * @param requestedAsOf null = "latest acceptable" (TTL-bounded);
51
+ * non-null = exact-vintage match required (no TTL)
52
+ * @returns the cached value if the lookup is a hit; otherwise undefined
53
+ * (caller must refetch)
54
+ */
55
+ get(uuidKey: string, requestedAsOf: ZonedDateTime | null): V | undefined {
56
+ const entry = this.map.get(uuidKey);
57
+ if (!entry) return undefined;
58
+ if (requestedAsOf == null) {
59
+ if (Date.now() - entry.cachedAtMs > this.ttlForLatestMs) {
60
+ return undefined;
61
+ }
62
+ return entry.value;
63
+ }
64
+ return this._sameAsOf(entry.asOf, requestedAsOf) ? entry.value : undefined;
65
+ }
66
+
67
+ /**
68
+ * Newest-wins write: if a cached entry for `uuidKey` already exists with
69
+ * an asOf strictly after the incoming asOf, the write is ignored.
70
+ */
71
+ put(uuidKey: string, value: V, asOf: ZonedDateTime): void {
72
+ if (!uuidKey || !value || !asOf) {
73
+ throw new Error(
74
+ `uuidKey / value / asOf must all be set; got uuidKey=${uuidKey} value=${value ? '<non-null>' : 'null'} asOf=${asOf}`
75
+ );
76
+ }
77
+ const existing = this.map.get(uuidKey);
78
+ if (existing && this._isStrictlyAfter(existing.asOf, asOf)) {
79
+ return;
80
+ }
81
+ this.map.set(uuidKey, { value, asOf, cachedAtMs: Date.now() });
82
+ }
83
+
84
+ evict(uuidKey: string): void {
85
+ this.map.delete(uuidKey);
86
+ }
87
+
88
+ clear(): void {
89
+ this.map.clear();
90
+ }
91
+
92
+ /** Test helper. */
93
+ size(): number {
94
+ return this.map.size;
95
+ }
96
+
97
+ private _sameAsOf(a: ZonedDateTime, b: ZonedDateTime): boolean {
98
+ return a.getSeconds() === b.getSeconds() && a.getNanoSeconds() === b.getNanoSeconds();
99
+ }
100
+
101
+ private _isStrictlyAfter(a: ZonedDateTime, b: ZonedDateTime): boolean {
102
+ if (a.getSeconds() !== b.getSeconds()) return a.getSeconds() > b.getSeconds();
103
+ return a.getNanoSeconds() > b.getNanoSeconds();
104
+ }
105
+ }
106
+
107
+ export const SECURITY = new LinkCache<SecurityProto>();
108
+ export const PORTFOLIO = new LinkCache<PortfolioProto>();
109
+ export const PRICE = new LinkCache<PriceProto>();
110
+ export const TRANSACTION = new LinkCache<TransactionProto>();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fintekkers/ledger-models",
3
3
  "todo": "Replace the version with build script version number",
4
- "version": "0.4.5",
4
+ "version": "0.4.9",
5
5
  "description": "ledger model protos ",
6
6
  "authors": [
7
7
  "David Doherty",