@hyperscale0/hsx 3.2.0 → 4.0.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/dist/src/ast.d.ts +17 -2
  4. package/dist/src/ast.d.ts.map +1 -1
  5. package/dist/src/ast.js.map +1 -1
  6. package/dist/src/cli.js +1 -1
  7. package/dist/src/compile.d.ts +6 -0
  8. package/dist/src/compile.d.ts.map +1 -1
  9. package/dist/src/compile.js +1194 -97
  10. package/dist/src/compile.js.map +1 -1
  11. package/dist/src/cost.d.ts +1 -1
  12. package/dist/src/cost.d.ts.map +1 -1
  13. package/dist/src/cost.js +52 -9
  14. package/dist/src/cost.js.map +1 -1
  15. package/dist/src/headers.d.ts +2 -2
  16. package/dist/src/headers.d.ts.map +1 -1
  17. package/dist/src/headers.js +3 -2
  18. package/dist/src/headers.js.map +1 -1
  19. package/dist/src/index.d.ts +3 -2
  20. package/dist/src/index.d.ts.map +1 -1
  21. package/dist/src/index.js.map +1 -1
  22. package/dist/src/lex.d.ts +1 -1
  23. package/dist/src/lex.d.ts.map +1 -1
  24. package/dist/src/lex.js +5 -0
  25. package/dist/src/lex.js.map +1 -1
  26. package/dist/src/parse.js +91 -5
  27. package/dist/src/parse.js.map +1 -1
  28. package/dist/src/std-bundle.d.ts.map +1 -1
  29. package/dist/src/std-bundle.js +10 -9
  30. package/dist/src/std-bundle.js.map +1 -1
  31. package/dist/src/version.d.ts +2 -2
  32. package/dist/src/version.js +2 -2
  33. package/docs/README.md +51 -27
  34. package/docs/headers.md +43 -40
  35. package/examples/cost-table.json +40 -324
  36. package/examples/library.hsx +12 -57
  37. package/package.json +7 -5
  38. package/src/ast.ts +11 -1
  39. package/src/cli.ts +1 -1
  40. package/src/compile.ts +1630 -114
  41. package/src/cost.ts +60 -16
  42. package/src/headers.ts +3 -2
  43. package/src/index.ts +12 -1
  44. package/src/lex.ts +5 -0
  45. package/src/parse.ts +87 -5
  46. package/src/std-bundle.ts +10 -9
  47. package/src/version.ts +2 -2
  48. package/std/approvals.hsx +3 -3
  49. package/std/collections.hsx +45 -4
  50. package/std/escrow.hsx +17 -8
  51. package/std/financing.hsx +292 -42
  52. package/std/insurance.hsx +4 -5
  53. package/std/lending.hsx +7 -8
  54. package/std/marketplace.hsx +90 -6
  55. package/std/money.hsx +15 -49
  56. package/std/reporting.hsx +1286 -0
  57. package/std/travel.hsx +5 -6
package/src/cost.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { UdlDocument } from "@hyperscale0/udl";
1
+ import { resolveField, type UdlDocument } from "@hyperscale0/udl";
2
2
  export interface UdlCostManifest {
3
3
  actions: Record<
4
4
  string,
@@ -12,21 +12,65 @@ export interface UdlCostManifest {
12
12
  /** Counts sealed instructions after lowering. Prices belong to the executor's tariff. */
13
13
  export function buildUdlCostManifest(document: UdlDocument): UdlCostManifest {
14
14
  const actions: UdlCostManifest["actions"] = {};
15
- for (const instrument of document.instruments)
16
- for (const [name, action] of Object.entries(instrument.actions)) {
17
- actions[`${instrument.id}.${name}`] = {
18
- transfers: action.moves.length,
19
- accounts:
20
- name === "create"
21
- ? instrument.fields.filter(
22
- (f) => f.type === "account" && f.owner === "self",
23
- ).length
24
- : 0,
25
- invocations: (action.invoke ?? []).reduce(
26
- (sum, call) => sum + ("selection" in call ? call.selection.limit : 1),
27
- 0,
28
- ),
29
- };
15
+ const active = new Set<string>();
16
+ const visit = (
17
+ id: string,
18
+ name: string,
19
+ ): UdlCostManifest["actions"][string] => {
20
+ const key = `${id}.${name}`;
21
+ if (actions[key]) return actions[key];
22
+ if (active.has(key)) throw new Error(`Invocation cycle at ${key}`);
23
+ const instrument = document.instruments.find((item) => item.id === id);
24
+ const action = instrument?.actions[name];
25
+ if (!instrument || !action) throw new Error(`Unknown invocation ${key}`);
26
+ active.add(key);
27
+ const cost = {
28
+ transfers: action.moves.length,
29
+ accounts:
30
+ name === "create"
31
+ ? instrument.fields.filter(
32
+ (field) => field.type === "account" && field.owner === "self",
33
+ ).length
34
+ : 0,
35
+ invocations: 0,
36
+ };
37
+ for (const call of action.invoke ?? []) {
38
+ const ref =
39
+ "reference" in call
40
+ ? resolveField(document, instrument, call.reference, action.input)
41
+ : undefined;
42
+ const targets =
43
+ "instrument" in call
44
+ ? call.instrument
45
+ : "selection" in call
46
+ ? call.selection.instrument
47
+ : ref?.type === "ref"
48
+ ? ref.target
49
+ : [];
50
+ const children = (typeof targets === "string" ? [targets] : targets).map(
51
+ (target) => visit(target, call.action),
52
+ );
53
+ const count =
54
+ "selection" in call
55
+ ? call.selection.limit
56
+ : "instrument" in call
57
+ ? (call.range?.maximum ?? 1)
58
+ : 1;
59
+ // A union selects one target per row. Each meter takes its worst case.
60
+ cost.transfers +=
61
+ count * Math.max(0, ...children.map((child) => child.transfers));
62
+ cost.accounts +=
63
+ count * Math.max(0, ...children.map((child) => child.accounts));
64
+ cost.invocations +=
65
+ count *
66
+ (1 + Math.max(0, ...children.map((child) => child.invocations)));
30
67
  }
68
+ active.delete(key);
69
+ actions[key] = cost;
70
+ return cost;
71
+ };
72
+ for (const instrument of document.instruments)
73
+ for (const name of Object.keys(instrument.actions))
74
+ visit(instrument.id, name);
31
75
  return { actions };
32
76
  }
package/src/headers.ts CHANGED
@@ -16,6 +16,7 @@ export const HEADER_NAMES = [
16
16
  "travel",
17
17
  "cards",
18
18
  "savings",
19
+ "reporting",
19
20
  ] as const;
20
21
 
21
22
  /** The compiler frontend owns header metadata used by docs and catalogue consumers. */
@@ -23,7 +24,7 @@ export function headerManifest(
23
24
  library: StandardLibrary = bundledStandardLibrary,
24
25
  ) {
25
26
  return {
26
- version: 3,
27
+ version: 4,
27
28
  headers: HEADER_NAMES.map((name) => {
28
29
  const source = library.source(name);
29
30
  if (!source) throw new Error(`Missing standard header ${name}`);
@@ -110,7 +111,7 @@ export function headerManifest(
110
111
  requiredImport: `use ${name}`,
111
112
  instancePlaceholder: "${instance}",
112
113
  source:
113
- "${instance} = " +
114
+ "attach ${instance} = " +
114
115
  `${name}.${decl.name} { ` +
115
116
  bindings
116
117
  .map((t) => t.name + ": ${" + t.name + "}")
package/src/index.ts CHANGED
@@ -4,12 +4,23 @@ export {
4
4
  type CompileOptions,
5
5
  type CompileResult,
6
6
  type CompileOriginMapEntry,
7
+ type AdapterBindingTarget,
7
8
  } from "./compile.ts";
9
+ export type { ProviderAdapter } from "@hyperscale0/adl";
8
10
  export { parseProgram } from "./parse.ts";
9
11
  export { lex, KEYWORDS } from "./lex.ts";
10
12
  export { format } from "./format.ts";
11
13
  export { bundledStandardLibrary, type StandardLibrary } from "./std-library.ts";
12
14
  export { buildUdlCostManifest, type UdlCostManifest } from "./cost.ts";
13
- export type { Program, Decl, Expr, Span, Diagnostic } from "./ast.ts";
15
+ export type {
16
+ Program,
17
+ Decl,
18
+ AssignmentDecl,
19
+ ObjectDecl,
20
+ InstrumentDecl,
21
+ Expr,
22
+ Span,
23
+ Diagnostic,
24
+ } from "./ast.ts";
14
25
 
15
26
  export { headerManifest, HEADER_NAMES } from "./headers.ts";
package/src/lex.ts CHANGED
@@ -42,6 +42,11 @@ export const KEYWORDS = [
42
42
  "capture",
43
43
  "fee",
44
44
  "shares",
45
+ "object",
46
+ "attach",
47
+ "subject",
48
+ "rename",
49
+ "columns",
45
50
  ] as const;
46
51
  export interface Token {
47
52
  kind: "name" | "number" | "string" | "date" | "punct" | "eof";
package/src/parse.ts CHANGED
@@ -172,14 +172,30 @@ class Parser {
172
172
  });
173
173
  continue;
174
174
  }
175
+ if (this.eat("object")) {
176
+ const name = this.identifier();
177
+ const title =
178
+ this.peek().kind === "string"
179
+ ? (JSON.parse(this.take().text) as string)
180
+ : name;
181
+ const body = this.block();
182
+ program.decls.push({
183
+ kind: "object",
184
+ name,
185
+ title,
186
+ body,
187
+ span: this.span(start),
188
+ });
189
+ continue;
190
+ }
175
191
  const name = this.identifier();
176
192
  this.expect("=");
177
193
  const object = this.path();
178
194
  const body = this.block();
179
195
  program.decls.push({
180
- kind: "object",
196
+ kind: "assignment",
181
197
  name,
182
- object,
198
+ target: object,
183
199
  body,
184
200
  span: this.span(start),
185
201
  });
@@ -194,14 +210,74 @@ class Parser {
194
210
  if (this.peek().kind === "eof")
195
211
  this.fail(`unclosed block, expected ${end}`, `add ${end}`);
196
212
  const start = this.peek().span.start;
213
+ if (this.eat("attach")) {
214
+ const name = this.identifier();
215
+ this.expect("=");
216
+ const target = this.path();
217
+ const body = this.block();
218
+ entries.push({
219
+ key: `attach ${name} = ${target}`,
220
+ value: body,
221
+ span: this.span(start),
222
+ });
223
+ this.separators();
224
+ continue;
225
+ }
226
+ if (this.eat("rename")) {
227
+ const body = this.block();
228
+ entries.push({
229
+ key: "rename",
230
+ value: body,
231
+ span: this.span(start),
232
+ });
233
+ this.separators();
234
+ continue;
235
+ }
236
+ if (this.eat("expose")) {
237
+ const action = this.identifier();
238
+ let publicName = action;
239
+ if (this.eat("as")) {
240
+ publicName = this.identifier();
241
+ }
242
+ entries.push({
243
+ key: "expose",
244
+ value: {
245
+ kind: "call",
246
+ name: action,
247
+ args: [{ kind: "name", value: publicName, span: this.span(start) }],
248
+ span: this.span(start),
249
+ },
250
+ span: this.span(start),
251
+ });
252
+ this.separators();
253
+ continue;
254
+ }
197
255
  let key = this.path();
256
+ if (key === "columns" && !this.at(":")) {
257
+ let value: Expr;
258
+ if (this.at("[")) {
259
+ value = this.atom();
260
+ } else if (this.at("{")) {
261
+ value = this.block();
262
+ } else {
263
+ value = { kind: "list", items: [], span: this.span(start) };
264
+ }
265
+ entries.push({
266
+ key: "columns",
267
+ value,
268
+ span: this.span(start),
269
+ });
270
+ this.separators();
271
+ continue;
272
+ }
198
273
  if (key === "action" && !this.at(":")) key += " " + this.identifier();
199
274
  if (key === "when") {
200
275
  const tunable = this.identifier();
201
- this.expect("is");
276
+ const relation = this.eat("has") ? "has" : "is";
277
+ if (relation === "is") this.expect("is");
202
278
  const choice = this.identifier();
203
279
  entries.push({
204
- key: `when ${tunable} is ${choice}`,
280
+ key: `when ${tunable} ${relation} ${choice}`,
205
281
  value: this.block(),
206
282
  span: this.span(start),
207
283
  });
@@ -241,7 +317,8 @@ class Parser {
241
317
  private operand(): BlockExpr {
242
318
  const value = this.atom();
243
319
  return this.record({
244
- [value.kind === "name" && /^(self|input|party)\./.test(value.value)
320
+ [value.kind === "name" &&
321
+ /^(self|input|party|subject)\./.test(value.value)
245
322
  ? "field"
246
323
  : "literal"]: value,
247
324
  });
@@ -263,6 +340,8 @@ class Parser {
263
340
  };
264
341
  if (this.eat("for")) values.action = this.atom();
265
342
  if (this.eat("is")) values.decision = this.atom();
343
+ for (const key of ["protectedRequest", "differentFromInitiator"])
344
+ if (this.eat(key)) values[key] = this.atom();
266
345
  return this.record(values);
267
346
  }
268
347
  if (this.eat("unique")) {
@@ -315,6 +394,7 @@ class Parser {
315
394
  this.expect(key);
316
395
  values[key] = this.atom();
317
396
  }
397
+ if (this.eat("instruction")) values.instruction = this.atom();
318
398
  return this.record(values);
319
399
  }
320
400
  const left = this.operand();
@@ -360,6 +440,8 @@ class Parser {
360
440
  }
361
441
  for (const key of ["fee", "capture", "key"])
362
442
  if (this.eat(key)) values[key] = this.atom();
443
+ if (this.eat("boundary"))
444
+ values.boundary = this.record({ adapter: this.atom() });
363
445
  return this.record(values);
364
446
  }
365
447
  private block(): BlockExpr {