@hyperscale0/hsx 1.0.0-rc.1 → 1.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.
- package/CHANGELOG.md +18 -1
- package/README.md +4 -2
- package/bin/hsx.ts +2 -0
- package/dist/bin/hsx.js +2 -0
- package/dist/bin/hsx.js.map +1 -1
- package/dist/src/ast.d.ts +6 -0
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/ast.js +24 -0
- package/dist/src/ast.js.map +1 -1
- package/dist/src/cli.d.ts +3 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +25 -5
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +1 -3
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +1 -2
- package/dist/src/compile.js.map +1 -1
- package/dist/src/emit.d.ts +1 -2
- package/dist/src/emit.d.ts.map +1 -1
- package/dist/src/emit.js +1 -223
- package/dist/src/emit.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/lsp/server.d.ts +32 -0
- package/dist/src/lsp/server.d.ts.map +1 -0
- package/dist/src/lsp/server.js +391 -0
- package/dist/src/lsp/server.js.map +1 -0
- package/dist/src/modules.d.ts +2 -0
- package/dist/src/modules.d.ts.map +1 -1
- package/dist/src/modules.js +7 -11
- package/dist/src/modules.js.map +1 -1
- package/dist/src/std-bundle.d.ts +2 -0
- package/dist/src/std-bundle.d.ts.map +1 -0
- package/dist/src/std-bundle.js +88 -0
- package/dist/src/std-bundle.js.map +1 -0
- package/dist/src/std-library.d.ts +5 -0
- package/dist/src/std-library.d.ts.map +1 -0
- package/dist/src/std-library.js +9 -0
- package/dist/src/std-library.js.map +1 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/docs/README.md +3 -0
- package/docs/guide/01-first-program.md +1 -1
- package/docs/llms-full.txt +7 -6
- package/docs/llms.txt +2 -2
- package/docs/playground.md +54 -0
- package/docs/reference/cli.md +5 -3
- package/docs/reference/diagnostics.md +1 -1
- package/docs/reference/grammar.md +1 -1
- package/docs/reference/std/advance.md +1 -1
- package/docs/reference/std/cancellable_booking.md +1 -1
- package/docs/reference/std/captured_payment.md +1 -1
- package/docs/reference/std/conditional_disbursement.md +1 -1
- package/docs/reference/std/credit_facility.md +1 -1
- package/docs/reference/std/held_payment.md +1 -1
- package/docs/reference/std/instant_transfer.md +1 -1
- package/docs/reference/std/metered.md +1 -1
- package/docs/reference/std/pooled_split.md +1 -1
- package/docs/reference/std/premium_forward.md +1 -1
- package/docs/reference/std/reconciled_payout.md +1 -1
- package/docs/reference/std/recurring_collection.md +1 -1
- package/docs/reference/std/rotating_pool.md +1 -1
- package/docs/reference/std/scheduled.md +1 -1
- package/docs/reference/std/security_deposit.md +1 -1
- package/docs/reference/std/settlement_batch.md +1 -1
- package/docs/reference/std/swap.md +1 -1
- package/docs/reference/std/threshold_pool.md +1 -1
- package/docs/reference/std/weighted_distribution.md +1 -1
- package/docs/reference/types.md +1 -1
- package/docs/reference/udl-output.md +2 -3
- package/examples/02-imports-and-archetypes/README.md +1 -4
- package/examples/04-complete-product/README.md +0 -3
- package/package.json +11 -3
- package/skills/hsx/SKILL.md +1 -1
- package/src/ast.ts +30 -0
- package/src/cli.ts +28 -9
- package/src/compile.ts +1 -4
- package/src/emit.ts +2 -248
- package/src/index.ts +8 -6
- package/src/lsp/server.ts +460 -0
- package/src/modules.ts +12 -10
- package/src/std-bundle.ts +91 -0
- package/src/std-library.ts +12 -0
- package/src/version.ts +1 -1
package/src/emit.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
type UdlDocument,
|
|
5
5
|
type UdlIssueCode,
|
|
6
6
|
} from "@hyperscale0/udl";
|
|
7
|
-
import type {
|
|
7
|
+
import type { TypedInstrument, TypedProgram } from "./ir.ts";
|
|
8
8
|
|
|
9
9
|
export interface OriginMapEntry {
|
|
10
10
|
readonly path: string;
|
|
@@ -27,7 +27,6 @@ export type GeneralLowerResult =
|
|
|
27
27
|
readonly value: {
|
|
28
28
|
readonly canonicalUdl: string;
|
|
29
29
|
readonly document: UdlDocument;
|
|
30
|
-
readonly frame: Record<string, JsonValue>;
|
|
31
30
|
readonly originMap: readonly OriginMapEntry[];
|
|
32
31
|
};
|
|
33
32
|
};
|
|
@@ -58,11 +57,7 @@ export function lowerGeneralProgram(program: TypedProgram): GeneralLowerResult {
|
|
|
58
57
|
udl: 1,
|
|
59
58
|
version: 1,
|
|
60
59
|
};
|
|
61
|
-
const
|
|
62
|
-
const unresolved = [
|
|
63
|
-
...unresolvedCompilerMarkers(candidate),
|
|
64
|
-
...unresolvedCompilerMarkers(frame, "$.frame"),
|
|
65
|
-
];
|
|
60
|
+
const unresolved = unresolvedCompilerMarkers(candidate);
|
|
66
61
|
if (unresolved.length > 0) {
|
|
67
62
|
const origins = originMapFor(program);
|
|
68
63
|
return {
|
|
@@ -101,7 +96,6 @@ export function lowerGeneralProgram(program: TypedProgram): GeneralLowerResult {
|
|
|
101
96
|
value: {
|
|
102
97
|
canonicalUdl: serializeUdl(document),
|
|
103
98
|
document,
|
|
104
|
-
frame,
|
|
105
99
|
originMap: originMapFor(program),
|
|
106
100
|
},
|
|
107
101
|
};
|
|
@@ -182,240 +176,6 @@ export function requiredFields(instrument: TypedInstrument): string[] {
|
|
|
182
176
|
];
|
|
183
177
|
}
|
|
184
178
|
|
|
185
|
-
function frameFor(program: TypedProgram): Record<string, JsonValue> {
|
|
186
|
-
const actors = new Map<string, Record<string, JsonValue>>();
|
|
187
|
-
const moneyEvents: Record<string, JsonValue>[] = [];
|
|
188
|
-
const design: string[] = [];
|
|
189
|
-
const fees: Record<string, JsonValue>[] = [];
|
|
190
|
-
const mechanics = new Set<string>();
|
|
191
|
-
const eventKeys = new Set<string>();
|
|
192
|
-
const ensureActor = (rawKey: string, role: string): string => {
|
|
193
|
-
const key = frameKey(rawKey);
|
|
194
|
-
if (!actors.has(key)) {
|
|
195
|
-
actors.set(key, {
|
|
196
|
-
key,
|
|
197
|
-
label: titleize(key),
|
|
198
|
-
maxCount: 1,
|
|
199
|
-
minCount: 1,
|
|
200
|
-
role,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
return key;
|
|
204
|
-
};
|
|
205
|
-
const uniqueEventKey = (raw: string): string => {
|
|
206
|
-
const base = frameKey(raw);
|
|
207
|
-
let key = base;
|
|
208
|
-
let ordinal = 2;
|
|
209
|
-
while (eventKeys.has(key)) {
|
|
210
|
-
const suffix = `_${ordinal}`;
|
|
211
|
-
key = `${base.slice(0, 40 - suffix.length)}${suffix}`;
|
|
212
|
-
ordinal += 1;
|
|
213
|
-
}
|
|
214
|
-
eventKeys.add(key);
|
|
215
|
-
return key;
|
|
216
|
-
};
|
|
217
|
-
for (const instrument of program.instruments) {
|
|
218
|
-
const actorByField = new Map<string, string>();
|
|
219
|
-
const parties = instrument.slots.parties;
|
|
220
|
-
if (parties && typeof parties === "object" && !Array.isArray(parties)) {
|
|
221
|
-
for (const [role, value] of Object.entries(parties)) {
|
|
222
|
-
if (typeof value !== "string") continue;
|
|
223
|
-
const actor = ensureActor(role, frameActorRole(role, "source"));
|
|
224
|
-
actorByField.set(value, actor);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
for (const action of instrument.actions) {
|
|
228
|
-
if (action.slots.due) mechanics.add("recurring_billing");
|
|
229
|
-
const moves = action.slots.moves;
|
|
230
|
-
if (!Array.isArray(moves)) continue;
|
|
231
|
-
moves.forEach((move, index) => {
|
|
232
|
-
if (moneyEvents.length >= 20) return;
|
|
233
|
-
if (!move || typeof move !== "object" || Array.isArray(move)) return;
|
|
234
|
-
const bind =
|
|
235
|
-
move.bind &&
|
|
236
|
-
typeof move.bind === "object" &&
|
|
237
|
-
!Array.isArray(move.bind)
|
|
238
|
-
? move.bind
|
|
239
|
-
: {};
|
|
240
|
-
const source = frameActorForBinding(
|
|
241
|
-
bind.sourceAccountId,
|
|
242
|
-
actorByField,
|
|
243
|
-
"source",
|
|
244
|
-
ensureActor,
|
|
245
|
-
);
|
|
246
|
-
const destination = frameActorForBinding(
|
|
247
|
-
bind.destinationAccountId,
|
|
248
|
-
actorByField,
|
|
249
|
-
"destination",
|
|
250
|
-
ensureActor,
|
|
251
|
-
);
|
|
252
|
-
const operation =
|
|
253
|
-
typeof move.operation === "string" ? move.operation : "transfer";
|
|
254
|
-
if (source === "escrow" || destination === "escrow") {
|
|
255
|
-
mechanics.add("escrow");
|
|
256
|
-
}
|
|
257
|
-
moneyEvents.push({
|
|
258
|
-
allocationTotalBps: 0,
|
|
259
|
-
amount: bindingWords(bind.amount) || "the declared amount",
|
|
260
|
-
amountDependencies: [],
|
|
261
|
-
amountMode: "fixed",
|
|
262
|
-
amountSchedule: [],
|
|
263
|
-
distribution: "single",
|
|
264
|
-
fromActor: source,
|
|
265
|
-
key: uniqueEventKey(
|
|
266
|
-
typeof move.key === "string"
|
|
267
|
-
? move.key
|
|
268
|
-
: `${instrument.id}_${action.name}_${index + 1}`,
|
|
269
|
-
),
|
|
270
|
-
kind: frameEventKind(operation, source, destination),
|
|
271
|
-
label: `${titleize(action.name)} movement`,
|
|
272
|
-
occurrence: action.slots.due ? "repeatable" : "once",
|
|
273
|
-
timing: action.name === "create" ? "on_create" : "on_lifecycle",
|
|
274
|
-
toActor: destination,
|
|
275
|
-
trigger: `when ${action.name.replaceAll("_", " ")} runs`,
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
if (design.length < 10) {
|
|
280
|
-
design.push(
|
|
281
|
-
`${instrument.id}: ${instrument.fields.length} fields and ${instrument.actions.length} actions`,
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
const feeRules = instrument.slots.feeRules;
|
|
285
|
-
if (Array.isArray(feeRules)) {
|
|
286
|
-
feeRules.forEach((fee, index) => {
|
|
287
|
-
if (fees.length >= 4) return;
|
|
288
|
-
if (!fee || typeof fee !== "object" || Array.isArray(fee)) return;
|
|
289
|
-
fees.push({
|
|
290
|
-
label: `${titleize(instrument.id)} fee ${index + 1}`,
|
|
291
|
-
on:
|
|
292
|
-
typeof fee.baseField === "string"
|
|
293
|
-
? fee.baseField
|
|
294
|
-
: `${instrument.id} movement`,
|
|
295
|
-
structure:
|
|
296
|
-
typeof fee.position === "string"
|
|
297
|
-
? `${fee.position.replaceAll("_", " ")} fee`
|
|
298
|
-
: "declared fee",
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
if (actors.size === 0) ensureActor("platform", "platform");
|
|
304
|
-
if (moneyEvents.length === 0) {
|
|
305
|
-
const actor = actors.keys().next().value ?? "platform";
|
|
306
|
-
moneyEvents.push({
|
|
307
|
-
allocationTotalBps: 0,
|
|
308
|
-
amount: "the declared settlement amount",
|
|
309
|
-
amountDependencies: [],
|
|
310
|
-
amountMode: "fixed",
|
|
311
|
-
amountSchedule: [],
|
|
312
|
-
distribution: "single",
|
|
313
|
-
fromActor: actor,
|
|
314
|
-
key: uniqueEventKey(`${program.name}_create`),
|
|
315
|
-
kind: "charge",
|
|
316
|
-
label: `${program.title} movement`,
|
|
317
|
-
occurrence: "once",
|
|
318
|
-
timing: "on_create",
|
|
319
|
-
toActor: actor,
|
|
320
|
-
trigger: "when the settlement is created",
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
if (mechanics.size === 0) mechanics.add("escrow");
|
|
324
|
-
return {
|
|
325
|
-
actors: [...actors.values()],
|
|
326
|
-
confidence: "high",
|
|
327
|
-
conservationGroups: [],
|
|
328
|
-
design,
|
|
329
|
-
feePolicy: fees.length > 0 ? "defined" : "none",
|
|
330
|
-
fees,
|
|
331
|
-
headline: program.title,
|
|
332
|
-
mechanics: [...mechanics],
|
|
333
|
-
moneyEvents,
|
|
334
|
-
offPlatform: [],
|
|
335
|
-
openQuestions: [],
|
|
336
|
-
rules: [],
|
|
337
|
-
subjects: program.subjects.map((subject) => ({
|
|
338
|
-
kind: subject.kind,
|
|
339
|
-
title: subject.title,
|
|
340
|
-
})),
|
|
341
|
-
summary: `${program.title} compiles ${program.instruments.length} financial instrument${program.instruments.length === 1 ? "" : "s"}.`,
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function frameActorForBinding(
|
|
346
|
-
value: JsonValue | undefined,
|
|
347
|
-
actorByField: ReadonlyMap<string, string>,
|
|
348
|
-
direction: "destination" | "source",
|
|
349
|
-
ensureActor: (key: string, role: string) => string,
|
|
350
|
-
): string {
|
|
351
|
-
const path = bindingWords(value);
|
|
352
|
-
if (path.includes("escrowAccountId")) return "escrow";
|
|
353
|
-
const field = path.startsWith("fields.")
|
|
354
|
-
? path.slice("fields.".length)
|
|
355
|
-
: path;
|
|
356
|
-
const declared = actorByField.get(field);
|
|
357
|
-
if (declared) return declared;
|
|
358
|
-
if (field.toLowerCase().includes("platform")) {
|
|
359
|
-
return ensureActor("platform", "platform");
|
|
360
|
-
}
|
|
361
|
-
return ensureActor(
|
|
362
|
-
direction === "source" ? "payer" : "beneficiary",
|
|
363
|
-
direction === "source" ? "payer" : "beneficiary",
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
function frameActorRole(
|
|
368
|
-
role: string,
|
|
369
|
-
direction: "destination" | "source",
|
|
370
|
-
): string {
|
|
371
|
-
return [
|
|
372
|
-
"payer",
|
|
373
|
-
"beneficiary",
|
|
374
|
-
"platform",
|
|
375
|
-
"provider",
|
|
376
|
-
"guardian",
|
|
377
|
-
"holder",
|
|
378
|
-
].includes(role)
|
|
379
|
-
? role
|
|
380
|
-
: direction === "source"
|
|
381
|
-
? "payer"
|
|
382
|
-
: "beneficiary";
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
function frameEventKind(
|
|
386
|
-
operation: string,
|
|
387
|
-
source: string,
|
|
388
|
-
destination: string,
|
|
389
|
-
): string {
|
|
390
|
-
if (operation.endsWith(".reserve")) return "hold";
|
|
391
|
-
if (operation.endsWith(".void") || operation.endsWith(".return")) {
|
|
392
|
-
return "refund";
|
|
393
|
-
}
|
|
394
|
-
if (operation.startsWith("payout.")) return "payout";
|
|
395
|
-
if (source === "escrow" || destination === "escrow") return "release";
|
|
396
|
-
return "charge";
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function frameKey(value: string): string {
|
|
400
|
-
const snake = value
|
|
401
|
-
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
|
402
|
-
.replace(/[^A-Za-z0-9]+/g, "_")
|
|
403
|
-
.replace(/^_+|_+$/g, "")
|
|
404
|
-
.toLowerCase();
|
|
405
|
-
const leading = /^[a-z]/.test(snake) ? snake : `event_${snake}`;
|
|
406
|
-
return leading.slice(0, 40) || "event";
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
function bindingWords(value: JsonValue | undefined): string {
|
|
410
|
-
if (typeof value === "string") return value;
|
|
411
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return "";
|
|
412
|
-
const binding = value as Readonly<Record<string, JsonValue>>;
|
|
413
|
-
if (binding.from === "const" && typeof binding.value === "string")
|
|
414
|
-
return binding.value;
|
|
415
|
-
if (typeof binding.path === "string") return binding.path;
|
|
416
|
-
return "";
|
|
417
|
-
}
|
|
418
|
-
|
|
419
179
|
function originMapFor(program: TypedProgram): OriginMapEntry[] {
|
|
420
180
|
const entries: OriginMapEntry[] = [{ path: "$", span: program.origin }];
|
|
421
181
|
program.subjects.forEach((subject, index) => {
|
|
@@ -439,9 +199,3 @@ function originMapFor(program: TypedProgram): OriginMapEntry[] {
|
|
|
439
199
|
});
|
|
440
200
|
return entries;
|
|
441
201
|
}
|
|
442
|
-
|
|
443
|
-
function titleize(value: string): string {
|
|
444
|
-
return value
|
|
445
|
-
.replaceAll("_", " ")
|
|
446
|
-
.replace(/\b[a-z]/g, (letter) => letter.toUpperCase());
|
|
447
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -30,10 +30,12 @@ export { type OriginMapEntry } from "./emit.ts";
|
|
|
30
30
|
export { lowerGeneralProgram } from "./emit.ts";
|
|
31
31
|
export { parseProgram } from "./parse.ts";
|
|
32
32
|
export { checkGeneralProgram, type GeneralCheckOptions } from "./typecheck.ts";
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
export {
|
|
34
|
+
byteOffsetToCodeUnit,
|
|
35
|
+
type BlockExpr,
|
|
36
|
+
type ExposeDecl,
|
|
37
|
+
type Program,
|
|
38
|
+
type ProgramDecl,
|
|
39
|
+
type UseDecl,
|
|
39
40
|
} from "./ast.ts";
|
|
41
|
+
export { bundledStandardLibrary, type StandardLibrary } from "./std-library.ts";
|