@hyperscale0/hsx 5.5.0 → 5.7.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,24 @@
1
+ # HSX 5.7.0
2
+
3
+ Follows UDL 4.9.0. Object `entryActions` admits enrolled customers through named
4
+ actor-bound create actions. Product-scoped limits resolve across records with
5
+ matching parties. `programTax` moves use an operator-owned pass-through account.
6
+
7
+ Late charge creation accepts a slice reference for plans with multiple instalments.
8
+ Financing, collections and lending samples expose repayment child actions.
9
+ Aggregate selectors retain their selected instrument family, including child
10
+ records, so shared limits compare the matching contracts across Builds.
11
+
12
+ # HSX 5.6.1
13
+
14
+ Action summaries without authored text use the action title without the instrument id.
15
+
16
+ # HSX 5.6.0
17
+
18
+ The compiler records the standard block origin of every instrument compiled
19
+ from a bundled std header: block key, record path and header digest. Travel
20
+ booking cancellations declare `purpose: option`. Follows UDL 4.8.0.
21
+
1
22
  # HSX 5.5.0
2
23
 
3
24
  Std money moves declare posted economic purposes. Fee legs and recipient choices
@@ -15,6 +15,11 @@ export interface CompileOriginMapEntry {
15
15
  line: number;
16
16
  column: number;
17
17
  };
18
+ standardBlock?: {
19
+ key: string;
20
+ recordPath: string;
21
+ headerDigest: string;
22
+ };
18
23
  }
19
24
  export interface AdapterBindingTarget {
20
25
  adapter: ProviderAdapter;
@@ -1 +1 @@
1
- {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compile.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAeL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAC;AAS1B,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;AAqTD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,aAAa,CAmrGf"}
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compile.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAeL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAC;AAS1B,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;IAC9C,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E;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;AAqTD,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,aAAa,CA8yGf"}
@@ -277,6 +277,7 @@ export function compile(source, options = {}) {
277
277
  fail(program, "this release supports SAR", "write currency SAR or omit currency");
278
278
  const templates = new Map();
279
279
  const declarationSources = new Map();
280
+ const standardOrigins = new Map();
280
281
  const requirementOrigins = new Map();
281
282
  const used = new Set();
282
283
  for (const use of program.decls.filter((d) => d.kind === "use")) {
@@ -287,6 +288,10 @@ export function compile(source, options = {}) {
287
288
  if (!content)
288
289
  fail(use, `unknown header ${use.name}`, "choose a published header");
289
290
  sources.set(use.name, content);
291
+ const bundled = bundledStandardLibrary.source(use.name);
292
+ const headerDigest = bundled === content
293
+ ? Array.from(sha256(new TextEncoder().encode(content)), (byte) => byte.toString(16).padStart(2, "0")).join("")
294
+ : undefined;
290
295
  let header;
291
296
  try {
292
297
  header = parseHeader(content, use.name);
@@ -310,6 +315,14 @@ export function compile(source, options = {}) {
310
315
  fail(parentDecl, `duplicate instrument ${prefix}`, "give each instrument a distinct name");
311
316
  templates.set(prefix, parentDecl);
312
317
  declarationSources.set(parentDecl, use.name);
318
+ if (headerDigest) {
319
+ const [, root, ...record] = prefix.split(".");
320
+ standardOrigins.set(parentDecl, {
321
+ key: `${use.name}.${root}`,
322
+ recordPath: record.join("."),
323
+ headerDigest,
324
+ });
325
+ }
313
326
  const recs = entries(asBlock(entries(parentDecl.body).get("records")));
314
327
  for (const [recName, recBlock] of recs) {
315
328
  const recDecl = {
@@ -441,7 +454,7 @@ export function compile(source, options = {}) {
441
454
  }
442
455
  return { module: moduleName, exportPath, revision };
443
456
  };
444
- const addInstrument = (decl, id, arguments_, origin, inherited = new Map(), inheritedEnums = new Map(), attachmentInfo, familyDeclaration) => {
457
+ const addInstrument = (decl, id, arguments_, origin, inherited = new Map(), inheritedEnums = new Map(), attachmentInfo, familyDeclaration, standardOrigin) => {
445
458
  const resolveFamilyInstruments = (family, currentInstId, expr) => {
446
459
  const found = new Set();
447
460
  if (currentInstId && familyDeclaration) {
@@ -1001,6 +1014,7 @@ export function compile(source, options = {}) {
1001
1014
  "parameterDiagnostics",
1002
1015
  "reports",
1003
1016
  "revisioned",
1017
+ "scope",
1004
1018
  ].includes(key) &&
1005
1019
  !key.startsWith("action "))
1006
1020
  fail(decl, `unknown instrument clause ${key}`, "use fields, lifecycle, actions, invariants, or records");
@@ -1633,7 +1647,7 @@ export function compile(source, options = {}) {
1633
1647
  const a = {
1634
1648
  summary: slots.has("summary")
1635
1649
  ? String(data(slots.get("summary")))
1636
- : `${title(name)} ${id}`,
1650
+ : title(name),
1637
1651
  publicAction: camel(`${name}_${id}`),
1638
1652
  event: `${id}.${name}`,
1639
1653
  actor: "caller",
@@ -2020,9 +2034,38 @@ export function compile(source, options = {}) {
2020
2034
  inst.actions[name] = a;
2021
2035
  inst.actionOrder.push(name);
2022
2036
  }
2023
- for (const key of ["invariants", "reports", "revisioned"])
2037
+ for (const key of [
2038
+ "invariants",
2039
+ "reports",
2040
+ "revisioned",
2041
+ "scope",
2042
+ ])
2024
2043
  if (body.has(key))
2025
2044
  inst[key] = data(body.get(key));
2045
+ let usesTax = false;
2046
+ for (const action of Object.values(inst.actions)) {
2047
+ for (const move of action.moves) {
2048
+ if (!("from" in move))
2049
+ continue;
2050
+ for (const endpoint of ["from", "to"]) {
2051
+ if (move[endpoint] === "party.programTax") {
2052
+ move[endpoint] = "self.programTaxPayable";
2053
+ usesTax = true;
2054
+ }
2055
+ }
2056
+ }
2057
+ }
2058
+ if (usesTax) {
2059
+ if (inst.fields.some((field) => field.name === "programTaxPayable"))
2060
+ fail(decl, "programTaxPayable is reserved for the tax account", "rename the authored field");
2061
+ inst.fields.push({
2062
+ name: "programTaxPayable",
2063
+ type: "account",
2064
+ owner: "programOperator",
2065
+ book: "cash",
2066
+ key: "programTax",
2067
+ });
2068
+ }
2026
2069
  const shape = udlInstrumentSchema.safeParse(inst);
2027
2070
  if (!shape.success) {
2028
2071
  const issue = shape.error.issues[0];
@@ -2037,6 +2080,7 @@ export function compile(source, options = {}) {
2037
2080
  origins.push({
2038
2081
  path: `$.instruments[${document.instruments.length}]`,
2039
2082
  span: { ...origin, ...lineColAt(source, origin.start) },
2083
+ ...(standardOrigin ? { standardBlock: standardOrigin } : {}),
2040
2084
  });
2041
2085
  if (familyDeclaration && familyDeclaration.revision !== undefined) {
2042
2086
  inst.family = {
@@ -2082,7 +2126,14 @@ export function compile(source, options = {}) {
2082
2126
  addInstrument(child, `${id}_${key}`, emptyBlock, origin, new Map([
2083
2127
  ...environment,
2084
2128
  ["parent", { kind: "name", value: id, span: origin }],
2085
- ]), enums, childAttachment, childFamily);
2129
+ ]), enums, childAttachment, childFamily, standardOrigin
2130
+ ? {
2131
+ ...standardOrigin,
2132
+ recordPath: [standardOrigin.recordPath, key]
2133
+ .filter(Boolean)
2134
+ .join("."),
2135
+ }
2136
+ : undefined);
2086
2137
  }
2087
2138
  };
2088
2139
  const compileObject = (decl) => {
@@ -2090,8 +2141,9 @@ export function compile(source, options = {}) {
2090
2141
  for (const key of body.keys()) {
2091
2142
  if (key !== "fields" &&
2092
2143
  key !== "columns" &&
2144
+ key !== "entryActions" &&
2093
2145
  !key.startsWith("attach ")) {
2094
- fail(decl, `unknown object clause ${key}`, "use fields, columns, or attach");
2146
+ fail(decl, `unknown object clause ${key}`, "use fields, columns, entryActions, or attach");
2095
2147
  }
2096
2148
  }
2097
2149
  const authoredFields = [];
@@ -2170,7 +2222,7 @@ export function compile(source, options = {}) {
2170
2222
  exposed,
2171
2223
  parties,
2172
2224
  attachments,
2173
- }, templateFamily ? { ...templateFamily } : undefined);
2225
+ }, templateFamily ? { ...templateFamily } : undefined, standardOrigins.get(template));
2174
2226
  }
2175
2227
  catch (error) {
2176
2228
  if (!(error instanceof CompileFailure))
@@ -2250,9 +2302,17 @@ export function compile(source, options = {}) {
2250
2302
  fail(columnsExpr, `The object list selects ${columns.length} columns; this release supports 8. The object may retain all its fields.`, `Remove ${columns.length === 9 ? "one name" : `${columns.length - 8} names`} from \`columns\`, not from \`fields\`.`);
2251
2303
  }
2252
2304
  }
2305
+ const entryExpr = body.get("entryActions");
2306
+ let entryActions;
2307
+ if (entryExpr) {
2308
+ if (entryExpr.kind !== "list" || !entryExpr.items.length)
2309
+ fail(entryExpr, "entryActions needs a nonempty list", "name exposed create actions bound to actor");
2310
+ entryActions = entryExpr.items.map(text);
2311
+ }
2253
2312
  document.objects.push({
2254
2313
  id: decl.name,
2255
2314
  title: decl.title,
2315
+ ...(entryActions ? { entryActions } : {}),
2256
2316
  authoredFields: authoredNames,
2257
2317
  attachments,
2258
2318
  fields: authoredFields,
@@ -2268,7 +2328,7 @@ export function compile(source, options = {}) {
2268
2328
  if (!template)
2269
2329
  fail(decl, `unknown instrument ${decl.target}`, `add use ${decl.target.split(".")[0]} and choose a declared instrument`);
2270
2330
  const templateFamily = resolveFamily(decl.target, decl, false);
2271
- addInstrument(template, decl.name, decl.body, decl.span, new Map(), new Map(), undefined, templateFamily ? { ...templateFamily } : undefined);
2331
+ addInstrument(template, decl.name, decl.body, decl.span, new Map(), new Map(), undefined, templateFamily ? { ...templateFamily } : undefined, standardOrigins.get(template));
2272
2332
  }
2273
2333
  if (decl.kind === "object") {
2274
2334
  compileObject(decl);
@@ -2281,6 +2341,25 @@ export function compile(source, options = {}) {
2281
2341
  .sort((a, b) => a.span.start - b.span.start)
2282
2342
  .map((d) => diagnostic(d, "check")),
2283
2343
  };
2344
+ // Resolve selectors after every attachment and child has its family.
2345
+ const retainSelectionFamilies = (value) => {
2346
+ if (!value || typeof value !== "object")
2347
+ return;
2348
+ const record = value;
2349
+ const selection = record.selection;
2350
+ if (selection && !selection.family) {
2351
+ const families = [selection.instrument]
2352
+ .flat()
2353
+ .map((id) => document.instruments.find((item) => item.id === id)?.family);
2354
+ const family = families[0];
2355
+ if (family &&
2356
+ families.every((item) => canonicalJson(item) === canonicalJson(family)))
2357
+ selection.family = family;
2358
+ }
2359
+ for (const child of Object.values(record))
2360
+ retainSelectionFamilies(child);
2361
+ };
2362
+ retainSelectionFamilies(document);
2284
2363
  // Propagate invoked requirements with the conditions on each invocation path.
2285
2364
  let changedInvocations = true;
2286
2365
  let invocationIterations = 0;
@@ -2536,6 +2615,19 @@ export function compile(source, options = {}) {
2536
2615
  if (Object.values(instrument.actions).filter((action) => action.publicAction === decl.name).length > 1)
2537
2616
  fail(decl, `public action ${decl.name} is used more than once on ${parts.join(".")}`, "give each exposed action a distinct public name");
2538
2617
  }
2618
+ for (const kind of document.objects) {
2619
+ for (const name of kind.entryActions ?? []) {
2620
+ const eligible = kind.attachments.some((attachment) => {
2621
+ const create = document.instruments.find((instrument) => instrument.id === attachment.instrument)?.actions.create;
2622
+ return (create?.publicAction === name &&
2623
+ typeof create.actor === "object" &&
2624
+ "party" in create.actor &&
2625
+ create.actor.party === "actor");
2626
+ });
2627
+ if (!eligible)
2628
+ fail(objects.get(kind.id), `entry action ${name} must create an attachment as actor`, "expose its create action and bind its acting party to actor");
2629
+ }
2630
+ }
2539
2631
  const usedParties = new Set();
2540
2632
  const collectParties = (value) => {
2541
2633
  if (typeof value === "string") {