@hyperscale0/hsx 1.0.130 → 1.0.132
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/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +77 -13
- package/dist/src/compile.js.map +1 -1
- package/dist/src/examples-bundle.js +4 -4
- package/dist/src/examples-bundle.js.map +1 -1
- package/dist/src/std-bundle.js +9 -9
- package/dist/src/std-bundle.js.map +1 -1
- package/docs/README.md +48 -22
- package/examples/cards.hsx +3 -1
- package/examples/marketplace.hsx +3 -1
- package/examples/rental-deposit.hsx +2 -0
- package/examples/wallet.hsx +1 -1
- package/package.json +4 -4
- package/src/compile.ts +100 -13
- package/src/examples-bundle.ts +4 -4
- package/src/std-bundle.ts +9 -9
- package/std/cards.hsx +16 -9
- package/std/escrow.hsx +32 -6
- package/std/financing.hsx +155 -68
- package/std/insurance.hsx +3 -3
- package/std/lending.hsx +16 -8
- package/std/marketplace.hsx +4 -3
- package/std/money.hsx +28 -14
- package/std/savings.hsx +3 -2
- package/std/wallet.hsx +10 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compile.ts"],"names":[],"mappings":"AAOA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAgBL,KAAK,WAAW,EAQjB,MAAM,kBAAkB,CAAC;AAU1B,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,
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/compile.ts"],"names":[],"mappings":"AAOA,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAgBL,KAAK,WAAW,EAQjB,MAAM,kBAAkB,CAAC;AAU1B,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,CAo5Hf"}
|
package/dist/src/compile.js
CHANGED
|
@@ -285,6 +285,9 @@ export function compile(source, options = {}) {
|
|
|
285
285
|
const declarationSources = new Map();
|
|
286
286
|
const standardOrigins = new Map();
|
|
287
287
|
const requirementOrigins = new Map();
|
|
288
|
+
// Instruments that lost a std action to an empty all() parent. Their
|
|
289
|
+
// lifecycles drop the states only that action reached.
|
|
290
|
+
const trimmed = new Set();
|
|
288
291
|
const used = new Set();
|
|
289
292
|
for (const use of program.decls.filter((d) => d.kind === "use")) {
|
|
290
293
|
if (used.has(use.name))
|
|
@@ -366,6 +369,11 @@ export function compile(source, options = {}) {
|
|
|
366
369
|
const assignments = new Map();
|
|
367
370
|
const attachmentSubjects = new Map();
|
|
368
371
|
const names = new Set();
|
|
372
|
+
// A record's create turns public only through a program-level expose,
|
|
373
|
+
// which applies after lowering, so creation input needs the list first.
|
|
374
|
+
const exposedCreates = new Set(program.decls.flatMap((decl) => decl.kind === "expose" && decl.target.endsWith(".create")
|
|
375
|
+
? [decl.target.split(".").slice(0, -1).join("_")]
|
|
376
|
+
: []));
|
|
369
377
|
for (const decl of program.decls) {
|
|
370
378
|
if (decl.kind === "expose" || decl.kind === "hide" || decl.kind === "use")
|
|
371
379
|
continue;
|
|
@@ -2019,6 +2027,14 @@ export function compile(source, options = {}) {
|
|
|
2019
2027
|
})
|
|
2020
2028
|
: undefined;
|
|
2021
2029
|
const charges = vat ? [charge, vat] : [charge];
|
|
2030
|
+
// A payer's limit reads this: a buyer pays the charges on
|
|
2031
|
+
// top, a seller pays them out of the amount.
|
|
2032
|
+
derived("debit", "Payer debit", {
|
|
2033
|
+
op: "sum",
|
|
2034
|
+
values: paidBy === "buyer"
|
|
2035
|
+
? [transfer.amount, ...charges]
|
|
2036
|
+
: [transfer.amount],
|
|
2037
|
+
});
|
|
2022
2038
|
const net = paidBy === "seller"
|
|
2023
2039
|
? derived("net", "Amount after fee", {
|
|
2024
2040
|
op: "subtract",
|
|
@@ -2085,7 +2101,7 @@ export function compile(source, options = {}) {
|
|
|
2085
2101
|
}
|
|
2086
2102
|
if (actionFeeCalculations.length)
|
|
2087
2103
|
a.calculate = [...(a.calculate ?? []), ...actionFeeCalculations];
|
|
2088
|
-
if (attachmentInfo
|
|
2104
|
+
if (attachmentInfo) {
|
|
2089
2105
|
if (attachmentInfo.exposed.has(name)) {
|
|
2090
2106
|
if (automatic(a.actor))
|
|
2091
2107
|
fail(row, `${name} runs on the clock or its parent, not a caller`, "expose only caller actions");
|
|
@@ -2139,6 +2155,11 @@ export function compile(source, options = {}) {
|
|
|
2139
2155
|
message: `${id}.${name}.subject.${requirement.field.name}`,
|
|
2140
2156
|
});
|
|
2141
2157
|
}
|
|
2158
|
+
// A parent list may name a list parameter such as `[parent, on]`.
|
|
2159
|
+
if (typeof a.actor === "object" &&
|
|
2160
|
+
"parent" in a.actor &&
|
|
2161
|
+
Array.isArray(a.actor.parent))
|
|
2162
|
+
a.actor.parent = a.actor.parent.flat();
|
|
2142
2163
|
// A parent actor over an empty all() names no instrument that could
|
|
2143
2164
|
// invoke the action. A std action then does not exist in this program;
|
|
2144
2165
|
// in user code the all() is a mistake.
|
|
@@ -2151,6 +2172,7 @@ export function compile(source, options = {}) {
|
|
|
2151
2172
|
fail(parent ?? row, `${parent?.kind === "call" ? `${parent.name}(${text(parent.args[0])})` : `the parent actor of ${name}`} matches no instrument`, "name an instrument this program declares or attaches");
|
|
2152
2173
|
}
|
|
2153
2174
|
delete lifecycle.transitions[name];
|
|
2175
|
+
trimmed.add(id);
|
|
2154
2176
|
continue;
|
|
2155
2177
|
}
|
|
2156
2178
|
inst.actions[name] = a;
|
|
@@ -2363,8 +2385,7 @@ export function compile(source, options = {}) {
|
|
|
2363
2385
|
const attachedInst = document.instruments.find((i) => i.id === instId);
|
|
2364
2386
|
for (const createdInst of document.instruments
|
|
2365
2387
|
.slice(firstAttachedInstrument)
|
|
2366
|
-
.filter((instrument) => instrument.id === instId ||
|
|
2367
|
-
instrument.actions.create?.publicAction)) {
|
|
2388
|
+
.filter((instrument) => instrument.id === instId || exposedCreates.has(instrument.id))) {
|
|
2368
2389
|
if (!createdInst.actions.create)
|
|
2369
2390
|
continue;
|
|
2370
2391
|
const owned = new Set(createdInst.calculate.map((node) => node.target));
|
|
@@ -2377,8 +2398,14 @@ export function compile(source, options = {}) {
|
|
|
2377
2398
|
}
|
|
2378
2399
|
const create = createdInst.actions.create;
|
|
2379
2400
|
for (const field of createdInst.fields) {
|
|
2401
|
+
// The engine resolves a lone candidate itself; with several (a
|
|
2402
|
+
// plan's slices) the caller names one, as late_charge does.
|
|
2403
|
+
if (field.type === "ref" && field.targetKind === "instrument") {
|
|
2404
|
+
if (!create.input.some((input) => input.name === field.name))
|
|
2405
|
+
create.input.push({ ...field, optional: true });
|
|
2406
|
+
continue;
|
|
2407
|
+
}
|
|
2380
2408
|
if (field.type === "account" ||
|
|
2381
|
-
(field.type === "ref" && field.targetKind === "instrument") ||
|
|
2382
2409
|
(field.type === "list" &&
|
|
2383
2410
|
field.item === "ref" &&
|
|
2384
2411
|
field.targetKind === "instrument") ||
|
|
@@ -2387,18 +2414,22 @@ export function compile(source, options = {}) {
|
|
|
2387
2414
|
owned.has(field.name) ||
|
|
2388
2415
|
create.input.some((input) => input.name === field.name))
|
|
2389
2416
|
continue;
|
|
2390
|
-
create.subject
|
|
2391
|
-
const existing = create.subject.requirements.find((item) => item.field.name === field.name);
|
|
2417
|
+
const existing = create.subject?.requirements.find((item) => item.field.name === field.name);
|
|
2392
2418
|
if (existing) {
|
|
2393
2419
|
if (canonicalJson(existing.field) !== canonicalJson(field))
|
|
2394
2420
|
failWithCode(entry, "subject_field_conflict", `${createdInst.id}.create.subject.${field.name} conflicts with its instrument field`, "use the instrument field's type and constraints");
|
|
2395
2421
|
continue;
|
|
2396
2422
|
}
|
|
2423
|
+
// A creation value belongs to this agreement. Only a rename shares
|
|
2424
|
+
// it as object metadata; a matching authored field name does not,
|
|
2425
|
+
// so two agreements never read each other's amount.
|
|
2397
2426
|
const objectField = renames.get(field.name);
|
|
2398
|
-
|
|
2399
|
-
field
|
|
2400
|
-
|
|
2401
|
-
}
|
|
2427
|
+
if (!objectField) {
|
|
2428
|
+
create.input.push({ ...field });
|
|
2429
|
+
continue;
|
|
2430
|
+
}
|
|
2431
|
+
const requirement = { field, objectField };
|
|
2432
|
+
create.subject ??= { requirements: [], adapters: [] };
|
|
2402
2433
|
create.subject.requirements.push(requirement);
|
|
2403
2434
|
requirementOrigins.set(requirement, {
|
|
2404
2435
|
source: declarationSources.get(template) ?? "program",
|
|
@@ -2412,6 +2443,12 @@ export function compile(source, options = {}) {
|
|
|
2412
2443
|
Object.values(attachedInst.actions).some((action) => action.subject?.requirements.some((requirement) => requirement.field.name === oldName));
|
|
2413
2444
|
if (!found) {
|
|
2414
2445
|
const renameEntry = renameEntries.get(oldName) ?? entry;
|
|
2446
|
+
const record = document.instruments
|
|
2447
|
+
.slice(firstAttachedInstrument)
|
|
2448
|
+
.find((instrument) => instrument.id.startsWith(`${instId}_`) &&
|
|
2449
|
+
instrument.fields.some((field) => field.name === oldName));
|
|
2450
|
+
if (record)
|
|
2451
|
+
failWithCode(renameEntry, "subject_field_unknown", `rename source '${oldName}' belongs to the ${record.id.slice(instId.length + 1)} record of ${targetTemplate}; rename binds only fields of ${targetTemplate} itself`, `remove this rename; supply ${oldName} as input when the record is created, or set it as a tunable where the header offers one`);
|
|
2415
2452
|
const declared = attachedInst
|
|
2416
2453
|
? Object.values(attachedInst.actions).flatMap((action) => action.subject?.requirements.map((requirement) => requirement.field.name) ?? [])
|
|
2417
2454
|
: [];
|
|
@@ -2796,9 +2833,19 @@ export function compile(source, options = {}) {
|
|
|
2796
2833
|
return !candidates.every((candidate) => !targets.includes(candidate) &&
|
|
2797
2834
|
targets.some((target) => kin(target, candidate)));
|
|
2798
2835
|
});
|
|
2799
|
-
// A count over no matchable member is zero.
|
|
2836
|
+
// A count over no matchable member is zero, in a requirement or a calculation.
|
|
2800
2837
|
for (const inst of document.instruments)
|
|
2801
|
-
for (const action of Object.values(inst.actions))
|
|
2838
|
+
for (const action of Object.values(inst.actions)) {
|
|
2839
|
+
if (action.calculate)
|
|
2840
|
+
action.calculate = action.calculate.map((calculation) => calculation.op === "aggregate" &&
|
|
2841
|
+
calculation.measure === "count" &&
|
|
2842
|
+
!matchable(calculation.selection, inst, action).length
|
|
2843
|
+
? {
|
|
2844
|
+
target: calculation.target,
|
|
2845
|
+
op: "sum",
|
|
2846
|
+
values: [{ literal: 0 }],
|
|
2847
|
+
}
|
|
2848
|
+
: calculation);
|
|
2802
2849
|
action.requires = action.requires.map((requirement) => requirement.kind === "aggregate" &&
|
|
2803
2850
|
requirement.measure === "count" &&
|
|
2804
2851
|
!matchable(requirement.selection, inst, action).length
|
|
@@ -2809,6 +2856,7 @@ export function compile(source, options = {}) {
|
|
|
2809
2856
|
right: requirement.value,
|
|
2810
2857
|
}
|
|
2811
2858
|
: requirement);
|
|
2859
|
+
}
|
|
2812
2860
|
const checkSelection = (selection, at, scope) => {
|
|
2813
2861
|
walk(selection.overlaps, "overlaps", site(selection, "overlaps", at), scope);
|
|
2814
2862
|
walk(selection.anchor, "anchor", site(selection, "anchor", at), scope);
|
|
@@ -2932,7 +2980,7 @@ export function compile(source, options = {}) {
|
|
|
2932
2980
|
(edge.to !== "preserve" &&
|
|
2933
2981
|
!inst.lifecycle.states.includes(edge.to))))
|
|
2934
2982
|
continue;
|
|
2935
|
-
let specialized =
|
|
2983
|
+
let specialized = trimmed.has(inst.id);
|
|
2936
2984
|
for (const [key, action] of Object.entries(inst.actions)) {
|
|
2937
2985
|
const constant = (value) => {
|
|
2938
2986
|
if ("literal" in value)
|
|
@@ -3018,6 +3066,22 @@ export function compile(source, options = {}) {
|
|
|
3018
3066
|
if (Object.values(instrument.actions).filter((action) => action.publicAction === decl.name).length > 1)
|
|
3019
3067
|
fail(decl, `public action ${decl.name} is used more than once on ${parts.join(".")}`, "give each exposed action a distinct public name");
|
|
3020
3068
|
}
|
|
3069
|
+
// A public action on a fresh attachment creates it first with no input
|
|
3070
|
+
// (in the engine executor), so creation that needs input must be public.
|
|
3071
|
+
for (const kind of document.objects)
|
|
3072
|
+
for (const attachment of kind.attachments) {
|
|
3073
|
+
if (attachment.parent)
|
|
3074
|
+
continue;
|
|
3075
|
+
const instrument = byId.get(attachment.instrument);
|
|
3076
|
+
const create = instrument.actions.create;
|
|
3077
|
+
const needed = create?.input.filter((field) => !field.optional) ?? [];
|
|
3078
|
+
const entry = Object.values(instrument.actions).find((action) => action.publicAction);
|
|
3079
|
+
if (!needed.length || create.publicAction || !entry)
|
|
3080
|
+
continue;
|
|
3081
|
+
fail(objects
|
|
3082
|
+
.get(kind.id)
|
|
3083
|
+
.body.entries.find((row) => new RegExp(`^attach\\s+${attachment.name}\\s*=`).test(row.key)) ?? objects.get(kind.id), `${attachment.name} starts from ${entry.publicAction}, but its create needs ${needed.map((field) => field.name).join(", ")} and is not exposed`, `expose create on ${attachment.name} so the caller supplies ${needed.length === 1 ? "it" : "them"}, or rename { ${needed[0].name}: ${needed[0].name} } to read object metadata`);
|
|
3084
|
+
}
|
|
3021
3085
|
for (const kind of document.objects) {
|
|
3022
3086
|
for (const name of kind.entryActions ?? []) {
|
|
3023
3087
|
const eligible = kind.attachments.some((attachment) => {
|