@helios-lang/contract-utils 0.2.70 → 0.2.71

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helios-lang/contract-utils",
3
- "version": "0.2.70",
3
+ "version": "0.2.71",
4
4
  "description": "Convenience and type-safety utilities for using Helios validators from within Typescript",
5
5
  "main": "src/index.js",
6
6
  "types": "types/index.d.ts",
@@ -27,10 +27,10 @@
27
27
  "dependencies": {
28
28
  "@helios-lang/cli-utils": "^0.1.6",
29
29
  "@helios-lang/codec-utils": "^0.1.35",
30
- "@helios-lang/compiler-utils": "^0.1.57",
31
- "@helios-lang/ledger": "^0.5.5",
32
- "@helios-lang/type-utils": "^0.1.22",
33
- "@helios-lang/uplc": "^0.3.13"
30
+ "@helios-lang/compiler-utils": "^0.1.61",
31
+ "@helios-lang/ledger": "^0.5.6",
32
+ "@helios-lang/type-utils": "^0.1.23",
33
+ "@helios-lang/uplc": "^0.4.13"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "pnpm run prettify && pnpm run build:types && pnpm run test:suite",
@@ -40,8 +40,8 @@
40
40
  "test": "pnpm run test:pretty && pnpm run test:types && pnpm run test:suite",
41
41
  "test:pretty": "prettier . --check",
42
42
  "test:suite": "node --test",
43
- "testing": "node --test --watch",
44
- "testing:debug": "node --inspect-brk --test --watch",
45
- "test:types": "pnpm run build:types"
43
+ "test:types": "pnpm run build:types",
44
+ "testing": "HL_TEST_TRACE=ok node --test --watch",
45
+ "testing:debug": "HL_TEST_TRACE=ok node --inspect-brk --test --watch"
46
46
  }
47
47
  }
package/src/cast/Cast.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  ValidatorHash,
21
21
  Value
22
22
  } from "@helios-lang/ledger"
23
- import { None, expectSome, isSome } from "@helios-lang/type-utils"
23
+ import { None, expectSome } from "@helios-lang/type-utils"
24
24
  import {
25
25
  ByteArrayData,
26
26
  ConstrData,
@@ -59,6 +59,19 @@ import {
59
59
  * @typedef {Cast<TStrict, TPermissive> | ConfigurableCast<TStrict, TPermissive>} CastLike
60
60
  */
61
61
 
62
+ /**
63
+ * @typedef {Object} SchemaToUplcContext
64
+ * @property {Record<string, TypeSchema>} defs - symbol table permitting recursive schema references
65
+ * @property {string} dataPath - provides developer-facing cues for any parsing errors, showing the deep field path of any error
66
+ */
67
+
68
+ /**
69
+ * @typedef {Object} UplcToSchemaContext
70
+ * @property {Record<string, TypeSchema>} defs - symbol table permitting recursive schema references
71
+ * @property {string} dataPath - provides developer-facing cues for any parsing errors, showing the deep field path of any error
72
+ * @property {CastConfig} config - has isMainnet indicator
73
+ */
74
+
62
75
  /**
63
76
  * @template TStrict
64
77
  * @template TPermissive=TStrict
@@ -87,10 +100,15 @@ export class Cast {
87
100
 
88
101
  /**
89
102
  * @param {UplcData} data
103
+ * @param {string} [dataPath] - can be used to indicate the kind or context of data-decoding
90
104
  * @returns {TStrict}
91
105
  */
92
- fromUplcData(data) {
93
- return uplcToSchema(this.schema, data, this.config)
106
+ fromUplcData(data, dataPath = "") {
107
+ return uplcToSchemaWithDataPath(this.schema, data, {
108
+ config: this.config,
109
+ dataPath,
110
+ defs: {}
111
+ })
94
112
  }
95
113
 
96
114
  /**
@@ -105,7 +123,10 @@ export class Cast {
105
123
  * @returns {UplcData}
106
124
  */
107
125
  toUplcData(x, dataPath = "") {
108
- const t = schemaToUplcWithDataPath(this.schema, x, {}, dataPath)
126
+ const t = schemaToUplcWithDataPath(this.schema, x, {
127
+ defs: {},
128
+ dataPath
129
+ })
109
130
  t.rawData = x
110
131
  return t
111
132
  }
@@ -114,19 +135,18 @@ export class Cast {
114
135
  /**
115
136
  * @param {TypeSchema} schema
116
137
  * @param {any} x
117
- * @param {Record<string, TypeSchema>} defs
118
- * @param {string} dataPath
138
+ * @param {SchemaToUplcContext} context
119
139
  * @returns {UplcData}
120
140
  */
121
- function schemaToUplcWithDataPath(schema, x, defs = {}, dataPath = "") {
141
+ function schemaToUplcWithDataPath(schema, x, context) {
122
142
  try {
123
- const t = schemaToUplc(schema, x, defs, dataPath)
124
- t.dataPath = dataPath
143
+ const t = schemaToUplc(schema, x, context)
144
+ t.dataPath = context.dataPath
125
145
  return t
126
146
  } catch (e) {
127
147
  if (!e.uplcDataPath) {
128
- e.message = `${e.message}\n ... at ${dataPath}`
129
- e.uplcDataPath = dataPath
148
+ e.message = `${e.message}\n ... at ${context.dataPath}`
149
+ e.uplcDataPath = context.dataPath
130
150
  }
131
151
  debugger
132
152
  throw e
@@ -136,21 +156,25 @@ function schemaToUplcWithDataPath(schema, x, defs = {}, dataPath = "") {
136
156
  /**
137
157
  * @param {TypeSchema} schema
138
158
  * @param {any} x
139
- * @param {Record<string, TypeSchema>} defs
140
- * @param {string} dataPath
159
+ * @param {SchemaToUplcContext} inputContextOnly
141
160
  * @returns {UplcData}
142
161
  */
143
- function schemaToUplc(schema, x, defs = {}, dataPath = "") {
162
+ function schemaToUplc(schema, x, inputContextOnly) {
163
+ // const { defs = {}, dataPath = "" } = inputContext
164
+ // important: DON'T extend the inputContext for use in nested calls.
165
+ const { dataPath, defs } = inputContextOnly
166
+
167
+ // Only the defs can be passed down, and it's simpler syntactically
168
+ // ... just use the defs directly in the nested calls.
169
+
144
170
  const kind = schema.kind
145
171
  switch (kind) {
146
172
  case "reference": {
147
173
  const def = expectSome(defs[schema.id])
148
- return schemaToUplcWithDataPath(
149
- def,
150
- x,
174
+ return schemaToUplcWithDataPath(def, x, {
151
175
  defs,
152
- `${dataPath}::ref{${schema.id}}`
153
- )
176
+ dataPath: `${dataPath}::ref{${schema.id}}`
177
+ })
154
178
  }
155
179
  case "internal": {
156
180
  const name = schema.name
@@ -228,12 +252,10 @@ function schemaToUplc(schema, x, defs = {}, dataPath = "") {
228
252
  case "list":
229
253
  return new ListData(
230
254
  x.map((x, i) =>
231
- schemaToUplcWithDataPath(
232
- schema.itemType,
233
- x,
255
+ schemaToUplcWithDataPath(schema.itemType, x, {
234
256
  defs,
235
- `${dataPath}.list[${i}]`
236
- )
257
+ dataPath: `${dataPath}.list[${i}]`
258
+ })
237
259
  )
238
260
  )
239
261
  case "map": {
@@ -244,42 +266,37 @@ function schemaToUplc(schema, x, defs = {}, dataPath = "") {
244
266
  ? x
245
267
  : Object.entries(x)
246
268
  return new MapData(
247
- entries.map(([k, v]) => [
248
- schemaToUplcWithDataPath(
249
- schema.keyType,
250
- k,
251
- defs,
252
- `${dataPath}[mapKey '${k}']`
253
- ),
254
- schemaToUplcWithDataPath(
255
- schema.valueType,
256
- v,
257
- defs,
258
- `${dataPath}[map].${k}`
259
- )
260
- ])
269
+ entries.map(([k, v], i) => {
270
+ const displayKey = "string" == typeof k ? `'${k}'` : `@{i}`
271
+ return [
272
+ schemaToUplcWithDataPath(schema.keyType, k, {
273
+ defs,
274
+ dataPath: `${dataPath}[mapKey ${displayKey}]`
275
+ }),
276
+ schemaToUplcWithDataPath(schema.valueType, v, {
277
+ defs,
278
+ dataPath: `${dataPath}[mapVal ${displayKey}]`
279
+ })
280
+ ]
281
+ })
261
282
  )
262
283
  }
263
284
  case "tuple":
264
285
  return new ListData(
265
286
  x.map((x, i) =>
266
- schemaToUplcWithDataPath(
267
- schema.itemTypes[i],
268
- x,
287
+ schemaToUplcWithDataPath(schema.itemTypes[i], x, {
269
288
  defs,
270
- `${dataPath}[tuple@${i}]`
271
- )
289
+ dataPath: `${dataPath}[tuple@${i}]`
290
+ })
272
291
  )
273
292
  )
274
293
  case "option":
275
294
  return encodeOptionData(
276
295
  x
277
- ? schemaToUplcWithDataPath(
278
- schema.someType,
279
- x,
296
+ ? schemaToUplcWithDataPath(schema.someType, x, {
280
297
  defs,
281
- `${dataPath}::Some`
282
- )
298
+ dataPath: `${dataPath}::Some`
299
+ })
283
300
  : None
284
301
  )
285
302
  case "struct": {
@@ -290,63 +307,81 @@ function schemaToUplc(schema, x, defs = {}, dataPath = "") {
290
307
  return schemaToUplcWithDataPath(
291
308
  schema.fieldTypes[0].type,
292
309
  x[singleFieldName],
293
- defs,
294
- `${dataPath}[sStruct.${singleFieldName}]`
310
+ {
311
+ defs,
312
+ dataPath: `${dataPath}[sfStruct.${singleFieldName}]`
313
+ }
295
314
  )
296
315
  case "list":
297
316
  return new ListData(
298
317
  schema.fieldTypes.map(({ name, type }) =>
299
- schemaToUplcWithDataPath(
300
- type,
301
- x[name],
302
- {},
303
- `${dataPath}[fStruct].${name}`
304
- )
318
+ schemaToUplcWithDataPath(type, x[name], {
319
+ defs,
320
+ dataPath: `${dataPath}[fStruct].${name}`
321
+ })
305
322
  )
306
323
  )
307
324
  case "map": {
308
325
  // first make sure all fields are present
309
326
  schema.fieldTypes.forEach((ft) => {
327
+ // todo: ? allow Option[T] fields to be missing?
328
+
310
329
  if (!(ft.name in x)) {
311
- throw new Error(`missing field ${ft.name}`)
330
+ if (ft.key && ft.key in x) {
331
+ const encodingInfo = ft.key
332
+ ? `incorrectly specified as encoding-key '${ft.key}'`
333
+ : ""
334
+ throw new Error(
335
+ `field '${ft.name}' ${encodingInfo}`
336
+ )
337
+ }
338
+ throw new Error(`missing field '${ft.name}'`)
312
339
  }
313
340
  })
314
341
 
315
- // respect order of entries
342
+ // respect order of entries in provided data
343
+ // todo: ? allow encoding-order to match schema-defined order instead ?
344
+ // todo: ? allow the encoding to follow the encoding keys in sorted order?
316
345
  /**
317
346
  * @type {[UplcData, UplcData][]}
318
347
  */
319
348
  const pairs = []
320
349
 
321
- Object.entries(x).forEach(([key, value]) => {
350
+ Object.entries(x).forEach(([fieldName, value]) => {
322
351
  const ft = schema.fieldTypes.find(
323
- (ft) => ft.name == key
352
+ (ft) => ft.name == fieldName
324
353
  )
325
354
 
326
355
  if (ft) {
327
- debugger
328
- // const rawKey = ft.key || ft.name
329
- const rawKey = ft.name
356
+ const encodingKey = ft.key || ft.name
357
+ const encodingInfo = ft.key ? ` as '${ft.key}'` : ""
330
358
  const keyData = new ByteArrayData(
331
- encodeUtf8(rawKey)
359
+ encodeUtf8(encodingKey)
332
360
  )
361
+
333
362
  const valueData = schemaToUplcWithDataPath(
334
363
  ft.type,
335
364
  value,
336
- defs,
337
- `${dataPath}[mStruct][${key}]`
365
+ {
366
+ defs,
367
+ dataPath: `${dataPath}[mStruct].${fieldName}${encodingInfo}`
368
+ }
338
369
  )
339
370
 
340
371
  pairs.push([keyData, valueData])
341
372
  }
342
373
  })
343
374
 
344
- // wrapped in ConstrData
345
- return new ConstrData(0, [new MapData(pairs)])
375
+ // A struct targeting CIP-68 compliance must have ConstrData
376
+ // ... due to its explicitly-declared Enum variant in developer-land.
377
+ //
378
+ // We just return our MapData, and we don't care whether it's used in any particular context.
379
+
380
+ return new MapData(pairs)
346
381
  }
347
382
  default:
348
383
  throw new Error(
349
- `unhandled struct format '${schema.format}'`
384
+ `unhandled struct format '${/** @type {any} */ (schema).format}'`
350
385
  )
351
386
  }
352
387
  }
@@ -364,15 +399,15 @@ function schemaToUplc(schema, x, defs = {}, dataPath = "") {
364
399
  )
365
400
  }
366
401
 
402
+ // Gives the encoding of nested data a context to prevent a second ConstrData wrapper on the MapData
403
+ // ... only for the first field.
367
404
  return new ConstrData(
368
405
  tag,
369
- schema.variantTypes[tag].fieldTypes.map((f) =>
370
- schemaToUplcWithDataPath(
371
- f.type,
372
- variantFields[f.name],
406
+ schema.variantTypes[tag].fieldTypes.map((f, i) =>
407
+ schemaToUplcWithDataPath(f.type, variantFields[f.name], {
373
408
  defs,
374
- `${dataPath}[${schema.name}::${variantName}].${f.name}`
375
- )
409
+ dataPath: `${dataPath}[${schema.name}::${variantName}].${f.name}`
410
+ })
376
411
  )
377
412
  )
378
413
  }
@@ -382,12 +417,10 @@ function schemaToUplc(schema, x, defs = {}, dataPath = "") {
382
417
  return new ConstrData(
383
418
  schema.tag,
384
419
  schema.fieldTypes.map(({ name, type }) =>
385
- schemaToUplcWithDataPath(
386
- type,
387
- x[name],
388
- {},
389
- `${dataPath}.${name}`
390
- )
420
+ schemaToUplcWithDataPath(type, x[name], {
421
+ defs,
422
+ dataPath: `${dataPath}[enumVariant ${schema.id}.${name}`
423
+ })
391
424
  )
392
425
  )
393
426
  default:
@@ -395,16 +428,41 @@ function schemaToUplc(schema, x, defs = {}, dataPath = "") {
395
428
  }
396
429
  }
397
430
 
431
+ /**
432
+ * @param {TypeSchema} schema
433
+ * @param {UplcData} data
434
+ * @param {UplcToSchemaContext} context
435
+ * @returns {any}
436
+ */
437
+ function uplcToSchemaWithDataPath(schema, data, context) {
438
+ try {
439
+ const t = uplcToSchema(schema, data, context)
440
+ return t
441
+ } catch (e) {
442
+ if (!e.uplcDataPath) {
443
+ e.message =
444
+ `decoding UPLC data: ${e.message}` +
445
+ (context.dataPath ? `\n ... at ${context.dataPath}` : "")
446
+ e.uplcDataPath = context.dataPath
447
+ }
448
+ debugger
449
+ throw e
450
+ }
451
+ }
452
+
398
453
  /**
399
454
  * This should fail when deviating
400
455
  * @param {TypeSchema} schema
401
456
  * @param {UplcData} data
402
- * @param {CastConfig} config
403
- * @param {Record<string, TypeSchema>} defs
457
+ * @param {UplcToSchemaContext} inputContextOnly
404
458
  * @returns {any}
405
459
  */
406
- function uplcToSchema(schema, data, config, defs = {}) {
460
+ function uplcToSchema(schema, data, inputContextOnly) {
407
461
  const kind = schema.kind
462
+ const { config, defs } = inputContextOnly
463
+ // Note: don't use the inputContext directly in nested calls.
464
+ const { dataPath, ...context } = inputContextOnly
465
+ // use { ... context } augmented with a de-novo `dataPath` attr
408
466
 
409
467
  switch (kind) {
410
468
  case "internal": {
@@ -482,24 +540,44 @@ function uplcToSchema(schema, data, config, defs = {}) {
482
540
  }
483
541
  }
484
542
  case "list":
485
- return ListData.expect(data).items.map((x) =>
486
- uplcToSchema(schema.itemType, x, config, defs)
543
+ return ListData.expect(data).items.map((x, i) =>
544
+ uplcToSchemaWithDataPath(schema.itemType, x, {
545
+ ...context,
546
+ dataPath: `${dataPath}[${i}]`
547
+ })
487
548
  )
488
549
  case "map":
489
550
  return new Map(
490
- MapData.expect(data).items.map(([k, v]) => [
491
- uplcToSchema(schema.keyType, k, config, defs),
492
- uplcToSchema(schema.valueType, v, config, defs)
493
- ])
551
+ MapData.expect(data).items.map(([k, v], i) => {
552
+ const key = uplcToSchemaWithDataPath(schema.keyType, k, {
553
+ ...context,
554
+ dataPath: `${dataPath}[mapKey @${i}]`
555
+ })
556
+ const displayKey =
557
+ "string" == typeof key ? `'${key}'` : `@{i}`
558
+ return [
559
+ key,
560
+ uplcToSchemaWithDataPath(schema.valueType, v, {
561
+ ...context,
562
+ dataPath: `${dataPath}[mapVal ${displayKey}]`
563
+ })
564
+ ]
565
+ })
494
566
  )
495
567
  case "tuple":
496
568
  return ListData.expect(data).items.map((x, i) =>
497
- uplcToSchema(schema.itemTypes[i], x, config, defs)
569
+ uplcToSchemaWithDataPath(schema.itemTypes[i], x, {
570
+ ...context,
571
+ dataPath: `${dataPath}[tuple@${i}]`
572
+ })
498
573
  )
499
574
  case "option": {
500
575
  const optionData = decodeOptionData(data)
501
576
  return optionData
502
- ? uplcToSchema(schema.someType, optionData, config, defs)
577
+ ? uplcToSchemaWithDataPath(schema.someType, optionData, {
578
+ ...context,
579
+ dataPath: `${dataPath}::Some`
580
+ })
503
581
  : None
504
582
  }
505
583
  case "struct": {
@@ -507,11 +585,10 @@ function uplcToSchema(schema, data, config, defs = {}) {
507
585
  switch (schema.format) {
508
586
  case "singleton":
509
587
  return {
510
- [schema.fieldTypes[0].name]: uplcToSchema(
588
+ [schema.fieldTypes[0].name]: uplcToSchemaWithDataPath(
511
589
  schema.fieldTypes[0].type,
512
590
  data,
513
- config,
514
- defs
591
+ { ...context, dataPath: `${dataPath}[sfStruct]` }
515
592
  )
516
593
  }
517
594
  case "list": {
@@ -524,53 +601,83 @@ function uplcToSchema(schema, data, config, defs = {}) {
524
601
  }
525
602
 
526
603
  return Object.fromEntries(
527
- fields.map((field, i) => [
528
- schema.fieldTypes[i].name,
529
- uplcToSchema(
530
- schema.fieldTypes[i].type,
531
- field,
532
- config,
533
- defs
534
- )
535
- ])
604
+ fields.map((field, i) => {
605
+ // "field-list" structs don't have encoding-keys, by definition.
606
+ const { name, type } = schema.fieldTypes[i]
607
+ return [
608
+ name,
609
+ uplcToSchemaWithDataPath(type, field, {
610
+ ...context,
611
+ dataPath: `${dataPath}[fStruct].${name}`
612
+ })
613
+ ]
614
+ })
536
615
  )
537
616
  }
538
617
  case "map": {
539
- // wrapped in ConstrData
540
- const entries = MapData.expect(
541
- ConstrData.expect(data).fields[0]
542
- ).items
618
+ // never uses a ConstrData (one MAY be used in the Enum layer, if needed)
619
+ const encodedEntries = MapData.expect(data).items
543
620
 
544
- if (entries.length != schema.fieldTypes.length) {
621
+ if (encodedEntries.length != schema.fieldTypes.length) {
545
622
  throw new Error(
546
- `expected ${schema.fieldTypes.length} fields in struct, got ${entries.length} fields`
623
+ `expected ${schema.fieldTypes.length} fields in struct, got ${encodedEntries.length} fields`
547
624
  )
548
625
  }
549
626
 
627
+ /**
628
+ * @param {string} targetName
629
+ * @returns {number}
630
+ */
631
+ function findSerializedFieldIndex(targetName) {
632
+ return encodedEntries.findIndex(([key, _]) => {
633
+ return (
634
+ targetName ==
635
+ decodeUtf8(ByteArrayData.expect(key).bytes)
636
+ )
637
+ })
638
+ }
639
+ // loops over the Schema fields:
550
640
  return Object.fromEntries(
551
641
  schema.fieldTypes
552
- .map(({ name, type }) => {
642
+ .map(({ name, type, key: encodingKey }) => {
553
643
  const i = expectSome(
554
- entries.findIndex(
555
- ([key, _]) =>
556
- decodeUtf8(
557
- ByteArrayData.expect(key).bytes
558
- ) == name
644
+ findSerializedFieldIndex(
645
+ encodingKey || name
559
646
  )
560
647
  )
561
648
 
562
649
  if (i == -1) {
650
+ // todo: ? allow Option[T] fields to be missing?
651
+ if (encodingKey) {
652
+ if (
653
+ findSerializedFieldIndex(name) > -1
654
+ ) {
655
+ throw new Error(
656
+ `field '${name}' encoded by name (must be encoded as '${encodingKey}')`
657
+ )
658
+ }
659
+ throw new Error(
660
+ `field '${name}' missing (must be encoded as '${encodingKey}')`
661
+ )
662
+ }
563
663
  throw new Error(
564
- `field ${name} not found in MapData`
664
+ `field '${name}' missing from field map`
565
665
  )
566
666
  }
567
667
 
568
- const pair = entries[i]
668
+ const encodedDataPair = encodedEntries[i]
569
669
 
570
670
  return /** @type {const} */ ([
571
671
  i,
572
672
  name,
573
- uplcToSchema(type, pair[1], config, defs)
673
+ uplcToSchemaWithDataPath(
674
+ type,
675
+ encodedDataPair[1],
676
+ {
677
+ ...context,
678
+ dataPath: `${dataPath}[mStruct].${name}`
679
+ }
680
+ )
574
681
  ])
575
682
  })
576
683
  .sort((a, b) => a[0] - b[0])
@@ -579,7 +686,7 @@ function uplcToSchema(schema, data, config, defs = {}) {
579
686
  }
580
687
  default:
581
688
  throw new Error(
582
- `unhandled struct format '${schema.format}'`
689
+ `unhandled struct format '${/** @type {any} */ (schema).format}'`
583
690
  )
584
691
  }
585
692
  }
@@ -601,15 +708,18 @@ function uplcToSchema(schema, data, config, defs = {}) {
601
708
  )
602
709
  }
603
710
 
711
+ const fieldCount = variantSchema.fieldTypes.length
604
712
  return {
605
713
  [variantSchema.name]: Object.fromEntries(
606
714
  fields.map((f, i) => [
607
715
  variantSchema.fieldTypes[i].name,
608
- uplcToSchema(
716
+ uplcToSchemaWithDataPath(
609
717
  variantSchema.fieldTypes[i].type,
610
718
  f,
611
- config,
612
- defs
719
+ {
720
+ ...context,
721
+ dataPath: `${dataPath}[${schema.name}::${variantSchema.name}]`
722
+ }
613
723
  )
614
724
  ])
615
725
  )
@@ -622,7 +732,10 @@ function uplcToSchema(schema, data, config, defs = {}) {
622
732
  return Object.fromEntries(
623
733
  fields.map((field, i) => [
624
734
  schema.fieldTypes[i].name,
625
- uplcToSchema(schema.fieldTypes[i].type, field, config, defs)
735
+ uplcToSchemaWithDataPath(schema.fieldTypes[i].type, field, {
736
+ ...context,
737
+ dataPath: `${dataPath}[enumVariant ${schema.id}].${schema.fieldTypes[i].name}`
738
+ })
626
739
  ])
627
740
  )
628
741
  }
@@ -1,9 +1,7 @@
1
1
  import { bytesToHex } from "@helios-lang/codec-utils"
2
- import { expectSome, isLeft, isString } from "@helios-lang/type-utils"
3
- import { ConstrData, IntData, UplcDataValue } from "@helios-lang/uplc"
2
+ import { expectSome, isLeft, isString, None } from "@helios-lang/type-utils"
3
+ import { ConstrData, UplcDataValue, UplcRuntimeError } from "@helios-lang/uplc"
4
4
  import { Cast } from "./Cast.js"
5
- import { None } from "@helios-lang/type-utils"
6
- import { BasicUplcLogger } from "@helios-lang/uplc"
7
5
 
8
6
  /**
9
7
  * @typedef {import("@helios-lang/type-utils").TypeSchema} TypeSchema
@@ -128,17 +126,7 @@ export class UserFunc {
128
126
  const result = this.profile(namedArgs, logOptions).result
129
127
 
130
128
  if (isLeft(result)) {
131
- const stackTrace = result.left.callSites.map((s) => s.toString())
132
-
133
- let msg = result.left.error
134
-
135
- // only add the stackTrace if the logOptions aren't given
136
- // TODO: make sure logOptions actually logs the stack trace
137
- if (!logOptions && stackTrace.length > 0) {
138
- msg = `Stack trace:\n ${stackTrace.join("\n ")}\n${msg}`
139
- }
140
-
141
- throw new Error(msg)
129
+ throw new UplcRuntimeError(result.left.error, result.left.callSites)
142
130
  } else if (!isString(result.right) && result.right.kind == "data") {
143
131
  return /** @type {any} */ (result.right.value)
144
132
  } else if (this.props.returns) {
@@ -9,6 +9,8 @@ const NONE = "null"
9
9
  * @param {TypeSchema} schema
10
10
  * @returns {[string, string]} - the first entry is the strict canonical type, the second is the permissive type
11
11
  */
12
+ // XXX * @param {string} [encodingKey] - optional field-name (encoding-key) for map-typed struct fields only
13
+ // XXX export function genTypes(schema, encodingKey) {
12
14
  export function genTypes(schema) {
13
15
  const kind = schema.kind
14
16
 
@@ -126,10 +128,13 @@ export function genTypes(schema) {
126
128
  return [`(${c}) | ${NONE}`, `(${p}) | null | undefined`]
127
129
  }
128
130
  case "struct": {
129
- const fieldTypes = schema.fieldTypes.map(({ name, type }) => ({
130
- name: name,
131
- types: genTypes(type)
132
- }))
131
+ const fieldTypes = schema.fieldTypes.map(
132
+ ({ name, type /* key - not used for type */ }) => ({
133
+ name: name,
134
+ // .. the field's definition name is always used, not the encoding key
135
+ types: genTypes(type)
136
+ })
137
+ )
133
138
 
134
139
  return [
135
140
  `{${fieldTypes.map(({ name, types: [c, p] }) => `${name}: ${c}`).join(", ")}}`,
@@ -152,7 +157,7 @@ export function genTypes(schema) {
152
157
  ]
153
158
  }
154
159
  case "variant": {
155
- // similar to structure
160
+ // similar to a list-typed struct (no encoding keys allowed)
156
161
  const fieldTypes = schema.fieldTypes.map(({ name, type }) => ({
157
162
  name: name,
158
163
  types: genTypes(type)
@@ -5,9 +5,9 @@ import {
5
5
  StakingValidatorHash,
6
6
  ValidatorHash
7
7
  } from "@helios-lang/ledger"
8
- import { Cast } from "../cast/Cast.js"
9
8
  import { None, expectSome } from "@helios-lang/type-utils"
10
9
  import { UplcProgramV1, UplcProgramV2, UplcSourceMap } from "@helios-lang/uplc"
10
+ import { Cast } from "../cast/Cast.js"
11
11
 
12
12
  /**
13
13
  * @typedef {import("@helios-lang/uplc").PlutusVersion} PlutusVersion
@@ -26,6 +26,17 @@ export function configureCast<TStrict, TPermissive>(cast: CastLike<TStrict, TPer
26
26
  * @template TPermissive
27
27
  * @typedef {Cast<TStrict, TPermissive> | ConfigurableCast<TStrict, TPermissive>} CastLike
28
28
  */
29
+ /**
30
+ * @typedef {Object} SchemaToUplcContext
31
+ * @property {Record<string, TypeSchema>} defs - symbol table permitting recursive schema references
32
+ * @property {string} dataPath - provides developer-facing cues for any parsing errors, showing the deep field path of any error
33
+ */
34
+ /**
35
+ * @typedef {Object} UplcToSchemaContext
36
+ * @property {Record<string, TypeSchema>} defs - symbol table permitting recursive schema references
37
+ * @property {string} dataPath - provides developer-facing cues for any parsing errors, showing the deep field path of any error
38
+ * @property {CastConfig} config - has isMainnet indicator
39
+ */
29
40
  /**
30
41
  * @template TStrict
31
42
  * @template TPermissive=TStrict
@@ -48,9 +59,10 @@ export class Cast<TStrict, TPermissive> {
48
59
  readonly config: CastConfig;
49
60
  /**
50
61
  * @param {UplcData} data
62
+ * @param {string} [dataPath] - can be used to indicate the kind or context of data-decoding
51
63
  * @returns {TStrict}
52
64
  */
53
- fromUplcData(data: UplcData): TStrict;
65
+ fromUplcData(data: UplcData, dataPath?: string | undefined): TStrict;
54
66
  /**
55
67
  * converts javascript object to UPLC data, according to the schema.
56
68
  * @remarks
@@ -74,4 +86,28 @@ export type ConfigurableCast<TStrict, TPermissive> = (config: CastConfig) => Cas
74
86
  * StrictType and PermissiveType work for both Cast and ConfigurableCast
75
87
  */
76
88
  export type CastLike<TStrict, TPermissive> = Cast<TStrict, TPermissive> | ConfigurableCast<TStrict, TPermissive>;
89
+ export type SchemaToUplcContext = {
90
+ /**
91
+ * - symbol table permitting recursive schema references
92
+ */
93
+ defs: Record<string, TypeSchema>;
94
+ /**
95
+ * - provides developer-facing cues for any parsing errors, showing the deep field path of any error
96
+ */
97
+ dataPath: string;
98
+ };
99
+ export type UplcToSchemaContext = {
100
+ /**
101
+ * - symbol table permitting recursive schema references
102
+ */
103
+ defs: Record<string, TypeSchema>;
104
+ /**
105
+ * - provides developer-facing cues for any parsing errors, showing the deep field path of any error
106
+ */
107
+ dataPath: string;
108
+ /**
109
+ * - has isMainnet indicator
110
+ */
111
+ config: CastConfig;
112
+ };
77
113
  //# sourceMappingURL=Cast.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Cast.d.ts","sourceRoot":"","sources":["../../src/cast/Cast.js"],"names":[],"mappings":"AAynBA;;;;;;GAMG;AACH,8BANa,OAAO,EACP,WAAW,QACb,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,UAC9B,UAAU,GACR,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAQtC;AAjmBD;;;GAGG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;GAKG;AAEH;;;GAGG;AACH,kBAHa,OAAO,EACP,WAAW;IAepB;;;OAGG;IACH,oBAHW,UAAU,UACV,UAAU,EAKpB;IAnBD;;;OAGG;IACH,iBAFU,UAAU,CAEd;IAEN;;;OAGG;IACH,iBAFU,UAAU,CAEd;IAWN;;;OAGG;IACH,mBAHW,QAAQ,GACN,OAAO,CAInB;IAED;;;;;;;;;;OAUG;IACH,cAJW,WAAW,aACX,MAAM,GACJ,QAAQ,CAMpB;CACJ;uBAzEY,OAAO,mBAAmB,EAAE,QAAQ;yBACpC,OAAO,qBAAqB,EAAE,UAAU;yBAIxC;IACZ,SAAa,EAAE,OAAO,CAAA;CACnB;6BAIS,OAAO,EACP,WAAW,IACX,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;;;;qBAKlD,OAAO,EACP,WAAW,IACX,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,GAAG,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC"}
1
+ {"version":3,"file":"Cast.d.ts","sourceRoot":"","sources":["../../src/cast/Cast.js"],"names":[],"mappings":"AA0uBA;;;;;;GAMG;AACH,8BANa,OAAO,EACP,WAAW,QACb,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,UAC9B,UAAU,GACR,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAQtC;AAltBD;;;GAGG;AAEH;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;GAKG;AAEH;;;;GAIG;AAEH;;;;;GAKG;AAEH;;;GAGG;AACH,kBAHa,OAAO,EACP,WAAW;IAepB;;;OAGG;IACH,oBAHW,UAAU,UACV,UAAU,EAKpB;IAnBD;;;OAGG;IACH,iBAFU,UAAU,CAEd;IAEN;;;OAGG;IACH,iBAFU,UAAU,CAEd;IAWN;;;;OAIG;IACH,mBAJW,QAAQ,kCAEN,OAAO,CAQnB;IAED;;;;;;;;;;OAUG;IACH,cAJW,WAAW,aACX,MAAM,GACJ,QAAQ,CASpB;CACJ;uBA9FY,OAAO,mBAAmB,EAAE,QAAQ;yBACpC,OAAO,qBAAqB,EAAE,UAAU;yBAIxC;IACZ,SAAa,EAAE,OAAO,CAAA;CACnB;6BAIS,OAAO,EACP,WAAW,IACX,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;;;;qBAKlD,OAAO,EACP,WAAW,IACX,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,GAAG,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;;;;;UAKlE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;;;;cAC1B,MAAM;;;;;;UAKN,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;;;;cAC1B,MAAM;;;;YACN,UAAU"}
@@ -1 +1 @@
1
- {"version":3,"file":"UserFunc.d.ts","sourceRoot":"","sources":["../../src/cast/UserFunc.js"],"names":[],"mappings":"AAOA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;GAgBG;AAEH;;;GAGG;AAEH;;;GAGG;AACH,sBAHwC,KAAK,SAAhC;IAAE,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,EAC1B,IAAI;IAgBb;;;OAGG;IACH,kBAHW,WAAW,SACX,aAAa,EAKvB;IApBD;;;OAGG;IACH,eAFU,WAAW,CAEjB;IAEJ;;;;OAIG;IACH,uBAAK;IAWL;;OAEG;IACH,mBAEC;IAED;;;;OAIG;IACH,gBAJW,KAAK,eACL,MAAM,CAAC,YAAY,CAAC,GAClB,IAAI,CA0ChB;IAED;;;;OAIG;IACH,sBAJW,WAAW,CAAC,KAAK,CAAC,eAClB,MAAM,CAAC,YAAY,CAAC,GAClB,IAAI,SAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,CAwB/C;IAED;;;;OAIG;IACH,mBAJW,WAAW,CAAC,KAAK,CAAC,eAClB,MAAM,CAAC,YAAY,CAAC,GAClB,SAAS,CAwGrB;CACJ;yBA1PY,OAAO,yBAAyB,EAAE,UAAU;wBAC5C,OAAO,mBAAmB,EAAE,SAAS;uBACrC,OAAO,mBAAmB,EAAE,QAAQ;2BACpC,OAAO,mBAAmB,EAAE,YAAY;0BACxC,OAAO,mBAAmB,EAAE,WAAW;wBACvC,OAAO,mBAAmB,EAAE,SAAS;yBACrC,OAAO,WAAW,EAAE,UAAU;;;;;4BAM9B;IACZ,IAAQ,EAAE,MAAM,CAAA;IAChB,qBAAyB,EAAE,OAAO,CAAA;IAClC,qBAAyB,EAAE,OAAO,CAAA;IAClC,SAAa,EAAE;QACf,IAAU,EAAE,MAAM,CAAA;QAClB,IAAU,EAAE,UAAU,CAAA;QACtB,UAAgB,EAAE,OAAO,CAAA;KACpB,EAAE,CAAA;IACP,OAAW,CAAC,EAAE,UAAU,CAAA;IACxB,gBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7C,UAAc,EAAE,UAAU,CAAA;CACvB;wBAIS,KAAK,IACL,GAAE,CAAgB,IAAX,MAAM,KAAK,GAAG,CAAC,SAAS,gBAAgB,GAAG,MAAM,GAAG,QAAQ,GAAC"}
1
+ {"version":3,"file":"UserFunc.d.ts","sourceRoot":"","sources":["../../src/cast/UserFunc.js"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;GAgBG;AAEH;;;GAGG;AAEH;;;GAGG;AACH,sBAHwC,KAAK,SAAhC;IAAE,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,EAC1B,IAAI;IAgBb;;;OAGG;IACH,kBAHW,WAAW,SACX,aAAa,EAKvB;IApBD;;;OAGG;IACH,eAFU,WAAW,CAEjB;IAEJ;;;;OAIG;IACH,uBAAK;IAWL;;OAEG;IACH,mBAEC;IAED;;;;OAIG;IACH,gBAJW,KAAK,eACL,MAAM,CAAC,YAAY,CAAC,GAClB,IAAI,CA0ChB;IAED;;;;OAIG;IACH,sBAJW,WAAW,CAAC,KAAK,CAAC,eAClB,MAAM,CAAC,YAAY,CAAC,GAClB,IAAI,SAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,CAc/C;IAED;;;;OAIG;IACH,mBAJW,WAAW,CAAC,KAAK,CAAC,eAClB,MAAM,CAAC,YAAY,CAAC,GAClB,SAAS,CAwGrB;CACJ;yBAhPY,OAAO,yBAAyB,EAAE,UAAU;wBAC5C,OAAO,mBAAmB,EAAE,SAAS;uBACrC,OAAO,mBAAmB,EAAE,QAAQ;2BACpC,OAAO,mBAAmB,EAAE,YAAY;0BACxC,OAAO,mBAAmB,EAAE,WAAW;wBACvC,OAAO,mBAAmB,EAAE,SAAS;yBACrC,OAAO,WAAW,EAAE,UAAU;;;;;4BAM9B;IACZ,IAAQ,EAAE,MAAM,CAAA;IAChB,qBAAyB,EAAE,OAAO,CAAA;IAClC,qBAAyB,EAAE,OAAO,CAAA;IAClC,SAAa,EAAE;QACf,IAAU,EAAE,MAAM,CAAA;QAClB,IAAU,EAAE,UAAU,CAAA;QACtB,UAAgB,EAAE,OAAO,CAAA;KACpB,EAAE,CAAA;IACP,OAAW,CAAC,EAAE,UAAU,CAAA;IACxB,gBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7C,UAAc,EAAE,UAAU,CAAA;CACvB;wBAIS,KAAK,IACL,GAAE,CAAgB,IAAX,MAAM,KAAK,GAAG,CAAC,SAAS,gBAAgB,GAAG,MAAM,GAAG,QAAQ,GAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"TypeSchema.d.ts","sourceRoot":"","sources":["../../src/codegen/TypeSchema.js"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,iCAHW,UAAU,GACR,CAAC,MAAM,EAAE,MAAM,CAAC,CAiK5B;yBAzKY,OAAO,yBAAyB,EAAE,UAAU"}
1
+ {"version":3,"file":"TypeSchema.d.ts","sourceRoot":"","sources":["../../src/codegen/TypeSchema.js"],"names":[],"mappings":"AAMA;;;;GAIG;AAGH,iCALW,UAAU,GACR,CAAC,MAAM,EAAE,MAAM,CAAC,CAsK5B;yBA9KY,OAAO,yBAAyB,EAAE,UAAU"}