@hyperscale0/hsx 5.0.0 → 5.1.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,21 @@
1
+ # HSX 5.1.0
2
+
3
+ Child records of an attached instrument compile as their own attachments
4
+ (`<attachment>_<record>` with a `parent`), so a guarded child action is
5
+ discoverable and admissible from the object it belongs to (UDL 4.3.0).
6
+
7
+ Standard library review: card authorizations require an active holder,
8
+ a lending commitment records its investor account and caps tickets per
9
+ investor rather than per wallet, pool contributions are bounded at 366
10
+ paid records, portfolio ageing counts earned profit as outstanding, and
11
+ a late charge assesses a zero amount without refusing. Docs corrected.
12
+
13
+ # HSX 5.0.1
14
+
15
+ Per-action subject evidence compiles: `fields { price: money = subject.price }`
16
+ and `set: { x: { field: subject.y } }` accept requirements declared on the
17
+ action alone (UDL 4.2.1).
18
+
1
19
  # HSX 5.0.0
2
20
 
3
21
  Money moves refuse a zero amount unless the action declares `allowZero: true`,
@@ -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,CAo4Ff"}
@@ -1831,7 +1831,7 @@ export function compile(source, options = {}) {
1831
1831
  else
1832
1832
  a[key] = data(expr);
1833
1833
  }
1834
- if (attachmentInfo) {
1834
+ if (attachmentInfo && !attachmentInfo.child) {
1835
1835
  if (attachmentInfo.exposed.has(name)) {
1836
1836
  a.publicAction = attachmentInfo.exposed.get(name);
1837
1837
  }
@@ -1893,6 +1893,21 @@ export function compile(source, options = {}) {
1893
1893
  }
1894
1894
  document.instruments.push(inst);
1895
1895
  for (const [key, definition] of records) {
1896
+ const childAttachment = attachmentInfo
1897
+ ? {
1898
+ ...attachmentInfo,
1899
+ attachmentName: `${attachmentInfo.attachmentName}_${key}`,
1900
+ child: true,
1901
+ exposed: new Map(),
1902
+ }
1903
+ : undefined;
1904
+ if (childAttachment)
1905
+ childAttachment.attachments.push({
1906
+ name: childAttachment.attachmentName,
1907
+ parent: attachmentInfo.attachmentName,
1908
+ instrument: `${id}_${key}`,
1909
+ parties: childAttachment.parties,
1910
+ });
1896
1911
  const child = {
1897
1912
  kind: "instrument",
1898
1913
  name: key,
@@ -1913,9 +1928,7 @@ export function compile(source, options = {}) {
1913
1928
  addInstrument(child, `${id}_${key}`, emptyBlock, origin, new Map([
1914
1929
  ...environment,
1915
1930
  ["parent", { kind: "name", value: id, span: origin }],
1916
- ]), enums, attachmentInfo
1917
- ? { ...attachmentInfo, exposed: new Map() }
1918
- : undefined, childFamily);
1931
+ ]), enums, childAttachment, childFamily);
1919
1932
  }
1920
1933
  };
1921
1934
  const compileObject = (decl) => {
@@ -1986,6 +1999,7 @@ export function compile(source, options = {}) {
1986
1999
  span: entry.value.span,
1987
2000
  };
1988
2001
  const templateFamily = resolveFamily(targetTemplate, entry, false);
2002
+ const firstAttachedInstrument = document.instruments.length;
1989
2003
  try {
1990
2004
  addInstrument(template, instId, tunableBlock, entry.span, new Map(), new Map(), {
1991
2005
  subjectKindId: decl.name,
@@ -1993,6 +2007,7 @@ export function compile(source, options = {}) {
1993
2007
  renames,
1994
2008
  exposed,
1995
2009
  parties,
2010
+ attachments,
1996
2011
  }, templateFamily ? { ...templateFamily } : undefined);
1997
2012
  }
1998
2013
  catch (error) {
@@ -2002,17 +2017,22 @@ export function compile(source, options = {}) {
2002
2017
  continue;
2003
2018
  }
2004
2019
  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)) {
2020
+ for (const createdInst of document.instruments
2021
+ .slice(firstAttachedInstrument)
2022
+ .filter((instrument) => instrument.id === instId ||
2023
+ instrument.actions.create?.publicAction)) {
2024
+ if (!createdInst.actions.create)
2025
+ continue;
2026
+ const owned = new Set(createdInst.calculate.map((node) => node.target));
2027
+ for (const action of Object.values(createdInst.actions)) {
2008
2028
  for (const node of action.calculate ?? [])
2009
2029
  owned.add(node.target);
2010
2030
  for (const move of action.moves)
2011
2031
  if ("capture" in move && move.capture)
2012
2032
  owned.add(move.capture);
2013
2033
  }
2014
- const create = attachedInst.actions.create;
2015
- for (const field of attachedInst.fields) {
2034
+ const create = createdInst.actions.create;
2035
+ for (const field of createdInst.fields) {
2016
2036
  if (field.type === "account" ||
2017
2037
  (field.type === "ref" && field.targetKind === "instrument") ||
2018
2038
  (field.type === "list" &&
@@ -2020,13 +2040,14 @@ export function compile(source, options = {}) {
2020
2040
  field.targetKind === "instrument") ||
2021
2041
  field.optional ||
2022
2042
  "value" in field ||
2023
- owned.has(field.name))
2043
+ owned.has(field.name) ||
2044
+ create.input.some((input) => input.name === field.name))
2024
2045
  continue;
2025
2046
  create.subject ??= { requirements: [], adapters: [] };
2026
2047
  const existing = create.subject.requirements.find((item) => item.field.name === field.name);
2027
2048
  if (existing) {
2028
2049
  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");
2050
+ 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
2051
  continue;
2031
2052
  }
2032
2053
  const objectField = renames.get(field.name);
@@ -2038,7 +2059,7 @@ export function compile(source, options = {}) {
2038
2059
  requirementOrigins.set(requirement, {
2039
2060
  source: declarationSources.get(template) ?? "program",
2040
2061
  span: entry.span,
2041
- message: `${instId}.create.fields.${field.name}`,
2062
+ message: `${createdInst.id}.create.fields.${field.name}`,
2042
2063
  });
2043
2064
  }
2044
2065
  }
@@ -2099,17 +2120,15 @@ export function compile(source, options = {}) {
2099
2120
  .sort((a, b) => a.span.start - b.span.start)
2100
2121
  .map((d) => diagnostic(d, "check")),
2101
2122
  };
2102
- // Propagate mandatory invoked action requirements
2123
+ // Propagate invoked requirements with the conditions on each invocation path.
2103
2124
  let changedInvocations = true;
2104
2125
  let invocationIterations = 0;
2105
2126
  while (changedInvocations && invocationIterations < 32) {
2106
2127
  changedInvocations = false;
2107
2128
  invocationIterations++;
2108
2129
  for (const inst of document.instruments) {
2109
- for (const action of Object.values(inst.actions)) {
2130
+ for (const [actionName, action] of Object.entries(inst.actions)) {
2110
2131
  for (const call of action.invoke ?? []) {
2111
- if (call.guard)
2112
- continue;
2113
2132
  let targetIds = [];
2114
2133
  if ("instrument" in call) {
2115
2134
  targetIds = [call.instrument];
@@ -2141,7 +2160,9 @@ export function compile(source, options = {}) {
2141
2160
  if (!action.subject) {
2142
2161
  action.subject = { requirements: [], adapters: [] };
2143
2162
  }
2144
- for (const adapter of targetAction.subject.adapters) {
2163
+ for (const adapter of call.guard
2164
+ ? []
2165
+ : targetAction.subject.adapters) {
2145
2166
  if (!action.subject.adapters.some((existing) => existing.binding === adapter.binding)) {
2146
2167
  action.subject.adapters.push(adapter);
2147
2168
  changedInvocations = true;
@@ -2150,9 +2171,47 @@ export function compile(source, options = {}) {
2150
2171
  for (const targetReq of targetAction.subject.requirements) {
2151
2172
  const targetObjFieldName = targetReq.objectField ?? targetReq.field.name;
2152
2173
  const existing = action.subject.requirements.find((cr) => (cr.objectField ?? cr.field.name) === targetObjFieldName);
2174
+ const guardValue = (value) => {
2175
+ if (!("field" in value) ||
2176
+ !value.field.startsWith("subject."))
2177
+ return value;
2178
+ const requirement = action.subject?.requirements.find((item) => item.field.name === value.field.slice(8));
2179
+ return {
2180
+ field: `subject.${requirement?.objectField ?? requirement?.field.name ?? value.field.slice(8)}`,
2181
+ };
2182
+ };
2183
+ const guardType = call.guard &&
2184
+ [call.guard.left, call.guard.right]
2185
+ .flatMap((value) => "field" in value
2186
+ ? [
2187
+ resolveField(document, inst, value.field, action.input, action)?.type,
2188
+ ]
2189
+ : [])
2190
+ .find((type) => type === "money" || type === "date");
2191
+ const condition = call.guard
2192
+ ? {
2193
+ instrument: inst.id,
2194
+ action: actionName,
2195
+ guard: {
2196
+ ...call.guard,
2197
+ left: guardValue(call.guard.left),
2198
+ right: guardValue(call.guard.right),
2199
+ },
2200
+ ...(guardType === "money" || guardType === "date"
2201
+ ? { valueType: guardType }
2202
+ : {}),
2203
+ }
2204
+ : undefined;
2205
+ const when = condition
2206
+ ? (targetReq.when ?? [[]]).map((path) => [
2207
+ condition,
2208
+ ...path.filter((item) => canonicalJson(item) !== canonicalJson(condition)),
2209
+ ])
2210
+ : targetReq.when;
2153
2211
  if (!existing) {
2154
2212
  const inherited = {
2155
2213
  field: { ...targetReq.field, name: targetObjFieldName },
2214
+ ...(when ? { when: when.map((path) => [...path]) } : {}),
2156
2215
  };
2157
2216
  action.subject.requirements.push(inherited);
2158
2217
  requirementOrigins.set(inherited, requirementOrigins.get(targetReq));
@@ -2170,6 +2229,20 @@ export function compile(source, options = {}) {
2170
2229
  related: [first, second],
2171
2230
  });
2172
2231
  }
2232
+ else if (existing.when) {
2233
+ if (!when) {
2234
+ delete existing.when;
2235
+ changedInvocations = true;
2236
+ }
2237
+ else {
2238
+ for (const path of when) {
2239
+ if (!existing.when.some((known) => canonicalJson(known) === canonicalJson(path))) {
2240
+ existing.when.push(path);
2241
+ changedInvocations = true;
2242
+ }
2243
+ }
2244
+ }
2245
+ }
2173
2246
  }
2174
2247
  }
2175
2248
  }