@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 +21 -0
- package/dist/src/compile.d.ts +5 -0
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +99 -7
- package/dist/src/compile.js.map +1 -1
- package/dist/src/examples-bundle.js +3 -3
- package/dist/src/examples-bundle.js.map +1 -1
- package/dist/src/std-bundle.js +2 -2
- package/dist/src/std-bundle.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/docs/README.md +45 -11
- package/docs/examples.md +1 -1
- package/examples/collections.hsx +8 -2
- package/examples/financing.hsx +5 -0
- package/examples/lending.hsx +7 -1
- package/package.json +3 -3
- package/src/compile.ts +127 -3
- package/src/examples-bundle.ts +3 -3
- package/src/std-bundle.ts +2 -2
- package/src/version.ts +1 -1
- package/std/financing.hsx +3 -1
- package/std/travel.hsx +3 -3
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
|
package/dist/src/compile.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/src/compile.js
CHANGED
|
@@ -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
|
-
:
|
|
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 [
|
|
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") {
|