@hyperscale0/hsx 5.0.1 → 5.2.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ # HSX 5.2.0
2
+
3
+ An instrument field can bind an account owned by an ADL adapter:
4
+ `account(adapter(binding), cash, "premium")` resolves at execution
5
+ through the Product's adapter binding to the provider's account in the
6
+ tenant's ledger (UDL 4.4.0). Providers stay adapters, not parties. The
7
+ standard insurance header pays the insurer its premium net of the
8
+ tenant's commission with a plain move, reverses both portions on refund,
9
+ and reserves claim funds from the insurer-owned account.
10
+
11
+ # HSX 5.1.0
12
+
13
+ Child records of an attached instrument compile as their own attachments
14
+ (`<attachment>_<record>` with a `parent`), so a guarded child action is
15
+ discoverable and admissible from the object it belongs to (UDL 4.3.0).
16
+
17
+ Standard library review: card authorizations require an active holder,
18
+ a lending commitment records its investor account and caps tickets per
19
+ investor rather than per wallet, pool contributions are bounded at 366
20
+ paid records, portfolio ageing counts earned profit as outstanding, and
21
+ a late charge assesses a zero amount without refusing. Docs corrected.
22
+
1
23
  # HSX 5.0.1
2
24
 
3
25
  Per-action subject evidence compiles: `fields { price: money = subject.price }`
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compile.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAcL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAC;AAQ1B,OAAO,EAIL,KAAK,UAAU,EAKf,KAAK,IAAI,EACV,MAAM,UAAU,CAAC;AAClB,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACpC;AACD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AACD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,cAAc;IAC7B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CAClE;AACD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,WAAW,CAAC;QACtB,YAAY,EAAE,eAAe,CAAC;QAC9B,SAAS,EAAE,qBAAqB,EAAE,CAAC;KACpC,CAAC;CACH;AA2TD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAiyFf"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compile.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAcL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAC;AAQ1B,OAAO,EAIL,KAAK,UAAU,EAKf,KAAK,IAAI,EACV,MAAM,UAAU,CAAC;AAClB,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CACpC;AACD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AACD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,cAAc;IAC7B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CAClE;AACD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,SAAS,CAAC,EAAE;QACV,QAAQ,EAAE,WAAW,CAAC;QACtB,YAAY,EAAE,eAAe,CAAC;QAC9B,SAAS,EAAE,qBAAqB,EAAE,CAAC;KACpC,CAAC;CACH;AA2TD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,aAAa,CA64Ff"}
@@ -1203,7 +1203,14 @@ export function compile(source, options = {}) {
1203
1203
  if (t.kind === "call") {
1204
1204
  if (t.args.length < 1 || t.args.length > 4)
1205
1205
  fail(t, "account needs an owner, optional book, mode and key", "write account(buyer, claim, contra)");
1206
- f.owner = text(resolve(t.args[0]));
1206
+ const owner = t.args[0];
1207
+ if (owner.kind === "call" && owner.name === "adapter") {
1208
+ if (owner.args.length !== 1)
1209
+ fail(owner, "adapter needs one binding", 'write account(adapter(binding), cash, "premium")');
1210
+ f.owner = { adapter: text(resolve(owner.args[0])) };
1211
+ }
1212
+ else
1213
+ f.owner = text(resolve(owner));
1207
1214
  f.book = t.args[1] ? text(t.args[1]) : "cash";
1208
1215
  if (t.args[2]) {
1209
1216
  const mode = text(t.args[2]);
@@ -1831,7 +1838,7 @@ export function compile(source, options = {}) {
1831
1838
  else
1832
1839
  a[key] = data(expr);
1833
1840
  }
1834
- if (attachmentInfo) {
1841
+ if (attachmentInfo && !attachmentInfo.child) {
1835
1842
  if (attachmentInfo.exposed.has(name)) {
1836
1843
  a.publicAction = attachmentInfo.exposed.get(name);
1837
1844
  }
@@ -1893,6 +1900,21 @@ export function compile(source, options = {}) {
1893
1900
  }
1894
1901
  document.instruments.push(inst);
1895
1902
  for (const [key, definition] of records) {
1903
+ const childAttachment = attachmentInfo
1904
+ ? {
1905
+ ...attachmentInfo,
1906
+ attachmentName: `${attachmentInfo.attachmentName}_${key}`,
1907
+ child: true,
1908
+ exposed: new Map(),
1909
+ }
1910
+ : undefined;
1911
+ if (childAttachment)
1912
+ childAttachment.attachments.push({
1913
+ name: childAttachment.attachmentName,
1914
+ parent: attachmentInfo.attachmentName,
1915
+ instrument: `${id}_${key}`,
1916
+ parties: childAttachment.parties,
1917
+ });
1896
1918
  const child = {
1897
1919
  kind: "instrument",
1898
1920
  name: key,
@@ -1913,9 +1935,7 @@ export function compile(source, options = {}) {
1913
1935
  addInstrument(child, `${id}_${key}`, emptyBlock, origin, new Map([
1914
1936
  ...environment,
1915
1937
  ["parent", { kind: "name", value: id, span: origin }],
1916
- ]), enums, attachmentInfo
1917
- ? { ...attachmentInfo, exposed: new Map() }
1918
- : undefined, childFamily);
1938
+ ]), enums, childAttachment, childFamily);
1919
1939
  }
1920
1940
  };
1921
1941
  const compileObject = (decl) => {
@@ -1986,6 +2006,7 @@ export function compile(source, options = {}) {
1986
2006
  span: entry.value.span,
1987
2007
  };
1988
2008
  const templateFamily = resolveFamily(targetTemplate, entry, false);
2009
+ const firstAttachedInstrument = document.instruments.length;
1989
2010
  try {
1990
2011
  addInstrument(template, instId, tunableBlock, entry.span, new Map(), new Map(), {
1991
2012
  subjectKindId: decl.name,
@@ -1993,6 +2014,7 @@ export function compile(source, options = {}) {
1993
2014
  renames,
1994
2015
  exposed,
1995
2016
  parties,
2017
+ attachments,
1996
2018
  }, templateFamily ? { ...templateFamily } : undefined);
1997
2019
  }
1998
2020
  catch (error) {
@@ -2002,17 +2024,22 @@ export function compile(source, options = {}) {
2002
2024
  continue;
2003
2025
  }
2004
2026
  const attachedInst = document.instruments.find((i) => i.id === instId);
2005
- if (attachedInst?.actions.create) {
2006
- const owned = new Set(attachedInst.calculate.map((node) => node.target));
2007
- for (const action of Object.values(attachedInst.actions)) {
2027
+ for (const createdInst of document.instruments
2028
+ .slice(firstAttachedInstrument)
2029
+ .filter((instrument) => instrument.id === instId ||
2030
+ instrument.actions.create?.publicAction)) {
2031
+ if (!createdInst.actions.create)
2032
+ continue;
2033
+ const owned = new Set(createdInst.calculate.map((node) => node.target));
2034
+ for (const action of Object.values(createdInst.actions)) {
2008
2035
  for (const node of action.calculate ?? [])
2009
2036
  owned.add(node.target);
2010
2037
  for (const move of action.moves)
2011
2038
  if ("capture" in move && move.capture)
2012
2039
  owned.add(move.capture);
2013
2040
  }
2014
- const create = attachedInst.actions.create;
2015
- for (const field of attachedInst.fields) {
2041
+ const create = createdInst.actions.create;
2042
+ for (const field of createdInst.fields) {
2016
2043
  if (field.type === "account" ||
2017
2044
  (field.type === "ref" && field.targetKind === "instrument") ||
2018
2045
  (field.type === "list" &&
@@ -2020,13 +2047,14 @@ export function compile(source, options = {}) {
2020
2047
  field.targetKind === "instrument") ||
2021
2048
  field.optional ||
2022
2049
  "value" in field ||
2023
- owned.has(field.name))
2050
+ owned.has(field.name) ||
2051
+ create.input.some((input) => input.name === field.name))
2024
2052
  continue;
2025
2053
  create.subject ??= { requirements: [], adapters: [] };
2026
2054
  const existing = create.subject.requirements.find((item) => item.field.name === field.name);
2027
2055
  if (existing) {
2028
2056
  if (canonicalJson(existing.field) !== canonicalJson(field))
2029
- failWithCode(entry, "subject_field_conflict", `${instId}.create.subject.${field.name} conflicts with its instrument field`, "use the instrument field's type and constraints");
2057
+ failWithCode(entry, "subject_field_conflict", `${createdInst.id}.create.subject.${field.name} conflicts with its instrument field`, "use the instrument field's type and constraints");
2030
2058
  continue;
2031
2059
  }
2032
2060
  const objectField = renames.get(field.name);
@@ -2038,7 +2066,7 @@ export function compile(source, options = {}) {
2038
2066
  requirementOrigins.set(requirement, {
2039
2067
  source: declarationSources.get(template) ?? "program",
2040
2068
  span: entry.span,
2041
- message: `${instId}.create.fields.${field.name}`,
2069
+ message: `${createdInst.id}.create.fields.${field.name}`,
2042
2070
  });
2043
2071
  }
2044
2072
  }
@@ -2099,17 +2127,15 @@ export function compile(source, options = {}) {
2099
2127
  .sort((a, b) => a.span.start - b.span.start)
2100
2128
  .map((d) => diagnostic(d, "check")),
2101
2129
  };
2102
- // Propagate mandatory invoked action requirements
2130
+ // Propagate invoked requirements with the conditions on each invocation path.
2103
2131
  let changedInvocations = true;
2104
2132
  let invocationIterations = 0;
2105
2133
  while (changedInvocations && invocationIterations < 32) {
2106
2134
  changedInvocations = false;
2107
2135
  invocationIterations++;
2108
2136
  for (const inst of document.instruments) {
2109
- for (const action of Object.values(inst.actions)) {
2137
+ for (const [actionName, action] of Object.entries(inst.actions)) {
2110
2138
  for (const call of action.invoke ?? []) {
2111
- if (call.guard)
2112
- continue;
2113
2139
  let targetIds = [];
2114
2140
  if ("instrument" in call) {
2115
2141
  targetIds = [call.instrument];
@@ -2141,7 +2167,9 @@ export function compile(source, options = {}) {
2141
2167
  if (!action.subject) {
2142
2168
  action.subject = { requirements: [], adapters: [] };
2143
2169
  }
2144
- for (const adapter of targetAction.subject.adapters) {
2170
+ for (const adapter of call.guard
2171
+ ? []
2172
+ : targetAction.subject.adapters) {
2145
2173
  if (!action.subject.adapters.some((existing) => existing.binding === adapter.binding)) {
2146
2174
  action.subject.adapters.push(adapter);
2147
2175
  changedInvocations = true;
@@ -2150,9 +2178,47 @@ export function compile(source, options = {}) {
2150
2178
  for (const targetReq of targetAction.subject.requirements) {
2151
2179
  const targetObjFieldName = targetReq.objectField ?? targetReq.field.name;
2152
2180
  const existing = action.subject.requirements.find((cr) => (cr.objectField ?? cr.field.name) === targetObjFieldName);
2181
+ const guardValue = (value) => {
2182
+ if (!("field" in value) ||
2183
+ !value.field.startsWith("subject."))
2184
+ return value;
2185
+ const requirement = action.subject?.requirements.find((item) => item.field.name === value.field.slice(8));
2186
+ return {
2187
+ field: `subject.${requirement?.objectField ?? requirement?.field.name ?? value.field.slice(8)}`,
2188
+ };
2189
+ };
2190
+ const guardType = call.guard &&
2191
+ [call.guard.left, call.guard.right]
2192
+ .flatMap((value) => "field" in value
2193
+ ? [
2194
+ resolveField(document, inst, value.field, action.input, action)?.type,
2195
+ ]
2196
+ : [])
2197
+ .find((type) => type === "money" || type === "date");
2198
+ const condition = call.guard
2199
+ ? {
2200
+ instrument: inst.id,
2201
+ action: actionName,
2202
+ guard: {
2203
+ ...call.guard,
2204
+ left: guardValue(call.guard.left),
2205
+ right: guardValue(call.guard.right),
2206
+ },
2207
+ ...(guardType === "money" || guardType === "date"
2208
+ ? { valueType: guardType }
2209
+ : {}),
2210
+ }
2211
+ : undefined;
2212
+ const when = condition
2213
+ ? (targetReq.when ?? [[]]).map((path) => [
2214
+ condition,
2215
+ ...path.filter((item) => canonicalJson(item) !== canonicalJson(condition)),
2216
+ ])
2217
+ : targetReq.when;
2153
2218
  if (!existing) {
2154
2219
  const inherited = {
2155
2220
  field: { ...targetReq.field, name: targetObjFieldName },
2221
+ ...(when ? { when: when.map((path) => [...path]) } : {}),
2156
2222
  };
2157
2223
  action.subject.requirements.push(inherited);
2158
2224
  requirementOrigins.set(inherited, requirementOrigins.get(targetReq));
@@ -2170,6 +2236,20 @@ export function compile(source, options = {}) {
2170
2236
  related: [first, second],
2171
2237
  });
2172
2238
  }
2239
+ else if (existing.when) {
2240
+ if (!when) {
2241
+ delete existing.when;
2242
+ changedInvocations = true;
2243
+ }
2244
+ else {
2245
+ for (const path of when) {
2246
+ if (!existing.when.some((known) => canonicalJson(known) === canonicalJson(path))) {
2247
+ existing.when.push(path);
2248
+ changedInvocations = true;
2249
+ }
2250
+ }
2251
+ }
2252
+ }
2173
2253
  }
2174
2254
  }
2175
2255
  }