@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 +22 -0
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +98 -18
- package/dist/src/compile.js.map +1 -1
- package/dist/src/examples-bundle.js +2 -2
- package/dist/src/examples-bundle.js.map +1 -1
- package/dist/src/std-bundle.js +6 -6
- 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 +32 -18
- package/examples/insurance.hsx +2 -2
- package/examples/travel.hsx +2 -2
- package/package.json +3 -3
- package/src/compile.ts +126 -18
- package/src/examples-bundle.ts +2 -2
- package/src/std-bundle.ts +6 -6
- package/src/version.ts +1 -1
- package/std/cards.hsx +2 -0
- package/std/financing.hsx +1 -1
- package/std/insurance.hsx +9 -2
- package/std/lending.hsx +3 -2
- package/std/money.hsx +1 -0
- package/std/reporting.hsx +32 -4
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,
|
|
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"}
|
package/dist/src/compile.js
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
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 =
|
|
2015
|
-
for (const field of
|
|
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", `${
|
|
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: `${
|
|
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
|
|
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.
|
|
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
|
|
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
|
}
|