@soundscript/soundscript 0.1.1 → 0.1.3

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 (73) hide show
  1. package/README.md +1 -1
  2. package/async.d.ts +3 -3
  3. package/async.js +2 -2
  4. package/async.js.map +1 -1
  5. package/codec.d.ts +3 -2
  6. package/codec.js +2 -2
  7. package/codec.js.map +1 -1
  8. package/compare.js +5 -10
  9. package/compare.js.map +1 -1
  10. package/decode.d.ts +6 -4
  11. package/decode.js +5 -5
  12. package/decode.js.map +1 -1
  13. package/derive.d.ts +6 -0
  14. package/derive.js +8 -0
  15. package/derive.js.map +1 -0
  16. package/encode.d.ts +11 -9
  17. package/encode.js +5 -5
  18. package/encode.js.map +1 -1
  19. package/experimental/thunk.js.map +1 -0
  20. package/fetch.d.ts +67 -0
  21. package/fetch.js +6 -0
  22. package/fetch.js.map +1 -0
  23. package/hash.js +9 -14
  24. package/hash.js.map +1 -1
  25. package/json.d.ts +29 -2
  26. package/json.js +124 -1
  27. package/json.js.map +1 -1
  28. package/numerics.d.ts +523 -0
  29. package/numerics.js +1357 -0
  30. package/numerics.js.map +1 -0
  31. package/package.json +122 -35
  32. package/random.d.ts +19 -0
  33. package/random.js +4 -0
  34. package/random.js.map +1 -0
  35. package/result.d.ts +21 -5
  36. package/result.js +55 -19
  37. package/result.js.map +1 -1
  38. package/soundscript/async.sts +7 -7
  39. package/soundscript/codec.sts +8 -7
  40. package/soundscript/compare.sts +5 -13
  41. package/soundscript/decode.sts +15 -13
  42. package/soundscript/derive.sts +7 -0
  43. package/soundscript/encode.sts +18 -16
  44. package/soundscript/fetch.sts +11 -0
  45. package/soundscript/hash.sts +9 -17
  46. package/soundscript/json.sts +209 -3
  47. package/soundscript/numerics.sts +1937 -0
  48. package/soundscript/random.sts +5 -0
  49. package/soundscript/result.sts +93 -24
  50. package/soundscript/text.sts +4 -0
  51. package/soundscript/typeclasses.sts +22 -16
  52. package/soundscript/url.sts +4 -0
  53. package/soundscript/value.sts +133 -0
  54. package/text.d.ts +24 -0
  55. package/text.js +4 -0
  56. package/text.js.map +1 -0
  57. package/typeclasses.d.ts +2 -2
  58. package/typeclasses.js +10 -9
  59. package/typeclasses.js.map +1 -1
  60. package/url.d.ts +37 -0
  61. package/url.js +4 -0
  62. package/url.js.map +1 -0
  63. package/value.d.ts +9 -0
  64. package/value.js +105 -0
  65. package/value.js.map +1 -0
  66. package/experimental/component.d.ts +0 -40
  67. package/experimental/component.js +0 -46
  68. package/experimental/component.js.map +0 -1
  69. package/soundscript/experimental/component.sts +0 -69
  70. package/thunk.js.map +0 -1
  71. /package/{thunk.d.ts → experimental/thunk.d.ts} +0 -0
  72. /package/{thunk.js → experimental/thunk.js} +0 -0
  73. /package/soundscript/{thunk.sts → experimental/thunk.sts} +0 -0
@@ -2,6 +2,22 @@ import type { Decoder } from '@soundscript/soundscript/decode';
2
2
  import type { Encoder } from '@soundscript/soundscript/encode';
3
3
  import { Failure } from '@soundscript/soundscript/failures';
4
4
  import { isErr, type Result, resultOf } from '@soundscript/soundscript/result';
5
+ import {
6
+ F32,
7
+ F64,
8
+ format as formatNumeric,
9
+ I8,
10
+ I16,
11
+ I32,
12
+ I64,
13
+ isNumeric,
14
+ toHostNumber,
15
+ type Numeric,
16
+ U8,
17
+ U16,
18
+ U32,
19
+ U64,
20
+ } from './numerics.js';
5
21
 
6
22
  export type JsonArray = JsonValue[];
7
23
  export type JsonObject = {
@@ -35,8 +51,26 @@ export type JsonLikeValue =
35
51
  | JsonLikeObject
36
52
  | JsonLikeArray;
37
53
 
54
+ export type MachineJsonArray = MachineJsonLikeValue[];
55
+ export type MachineJsonObject = {
56
+ [key: string]: MachineJsonLikeValue;
57
+ };
58
+ export type MachineJsonLikeValue =
59
+ | string
60
+ | number
61
+ | boolean
62
+ | bigint
63
+ | null
64
+ | undefined
65
+ | Numeric
66
+ | MachineJsonObject
67
+ | MachineJsonArray;
68
+
69
+ export type MachineJsonNumericMode = 'tagged' | 'decimal-string' | 'json-number';
70
+
38
71
  export interface JsonParseOptions {
39
72
  int64?: 'default' | 'lossless';
73
+ numerics?: 'tagged';
40
74
  }
41
75
 
42
76
  export type JsonStringifyBigintMode = 'number' | 'reject' | 'string';
@@ -44,6 +78,7 @@ export type JsonStringifyBigintMode = 'number' | 'reject' | 'string';
44
78
  export interface JsonStringifyOptions {
45
79
  int64?: 'default' | 'string' | 'lossless';
46
80
  readonly bigint?: JsonStringifyBigintMode;
81
+ numerics?: MachineJsonNumericMode;
47
82
  }
48
83
 
49
84
  const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
@@ -66,12 +101,21 @@ export function parseJson(
66
101
  text: string,
67
102
  options: JsonParseOptions & { int64: 'lossless' },
68
103
  ): Result<LosslessJsonValue, JsonParseFailure>;
104
+ export function parseJson(
105
+ text: string,
106
+ options: JsonParseOptions & { numerics: 'tagged' },
107
+ ): Result<MachineJsonLikeValue, JsonParseFailure>;
69
108
  export function parseJson(
70
109
  text: string,
71
110
  options: JsonParseOptions = {},
72
- ): Result<JsonValue | LosslessJsonValue, JsonParseFailure> {
111
+ ): Result<JsonValue | LosslessJsonValue | MachineJsonLikeValue, JsonParseFailure> {
73
112
  return resultOf(
74
- () => options.int64 === 'lossless' ? parseLosslessJson(text) : JSON.parse(text),
113
+ () => {
114
+ const parsed = options.int64 === 'lossless' ? parseLosslessJson(text) : JSON.parse(text);
115
+ return options.numerics === 'tagged'
116
+ ? decodeTaggedMachineJsonValue(parsed as JsonValue | LosslessJsonValue)
117
+ : parsed;
118
+ },
75
119
  (cause) => new JsonParseFailure(cause),
76
120
  );
77
121
  }
@@ -82,11 +126,34 @@ export function stringifyJson(
82
126
  options: JsonStringifyOptions & { int64: 'string' | 'lossless' },
83
127
  ): Result<string, JsonStringifyFailure>;
84
128
  export function stringifyJson(
85
- value: JsonValue | LosslessJsonValue,
129
+ value: MachineJsonLikeValue,
130
+ options: JsonStringifyOptions & { numerics: MachineJsonNumericMode },
131
+ ): Result<string, JsonStringifyFailure>;
132
+ export function stringifyJson(
133
+ value: JsonValue | LosslessJsonValue | MachineJsonLikeValue,
86
134
  options: JsonStringifyOptions = {},
87
135
  ): Result<string, JsonStringifyFailure> {
88
136
  return resultOf(
89
137
  () => {
138
+ if (options.numerics) {
139
+ const normalized = normalizeMachineJsonLikeValue(
140
+ value as MachineJsonLikeValue,
141
+ new Set<object>(),
142
+ options.numerics,
143
+ 'root',
144
+ );
145
+ const encoded = stringifyJsonLikeInternal(
146
+ normalized,
147
+ new Set<object>(),
148
+ options.bigint ?? 'reject',
149
+ 'root',
150
+ );
151
+ if (encoded === undefined) {
152
+ throw new TypeError('JSON stringification produced no top-level value.');
153
+ }
154
+ return encoded;
155
+ }
156
+
90
157
  if (options.int64 === 'string') {
91
158
  return stringifyJsonWithInt64Mode(value as LosslessJsonValue, 'string');
92
159
  }
@@ -322,6 +389,145 @@ function isJsonLikeValueInternal(
322
389
  }
323
390
  }
324
391
 
392
+ function normalizeMachineJsonLikeValue(
393
+ value: MachineJsonLikeValue,
394
+ visited: Set<object>,
395
+ numericMode: MachineJsonNumericMode,
396
+ position: 'array' | 'object' | 'root',
397
+ ): JsonLikeValue {
398
+ if (isNumeric(value)) {
399
+ return normalizeMachineNumericJsonValue(value, numericMode);
400
+ }
401
+
402
+ switch (typeof value) {
403
+ case 'string':
404
+ case 'number':
405
+ case 'boolean':
406
+ case 'bigint':
407
+ return value;
408
+ case 'undefined':
409
+ return position === 'array' ? null : undefined;
410
+ case 'object':
411
+ if (value === null) {
412
+ return null;
413
+ }
414
+
415
+ if (visited.has(value)) {
416
+ throw new TypeError('Converting circular structure to machine JSON text.');
417
+ }
418
+
419
+ visited.add(value);
420
+ try {
421
+ if (Array.isArray(value)) {
422
+ return value.map((entry) =>
423
+ normalizeMachineJsonLikeValue(entry, visited, numericMode, 'array') ?? null
424
+ );
425
+ }
426
+
427
+ const result: Record<string, JsonLikeValue> = {};
428
+ for (const key of Object.keys(value)) {
429
+ const normalized = normalizeMachineJsonLikeValue(
430
+ (value as MachineJsonObject)[key]!,
431
+ visited,
432
+ numericMode,
433
+ 'object',
434
+ );
435
+ if (normalized !== undefined) {
436
+ result[key] = normalized;
437
+ }
438
+ }
439
+ return result;
440
+ } finally {
441
+ visited.delete(value);
442
+ }
443
+ default:
444
+ throw new TypeError(`Unsupported machine JSON value kind: ${typeof value}`);
445
+ }
446
+ }
447
+
448
+ function normalizeMachineNumericJsonValue(
449
+ value: Numeric,
450
+ numericMode: MachineJsonNumericMode,
451
+ ): JsonLikeValue {
452
+ switch (numericMode) {
453
+ case 'tagged':
454
+ return value.toJSON();
455
+ case 'decimal-string':
456
+ return formatNumeric(value);
457
+ case 'json-number':
458
+ switch (value.__soundscript_numeric_kind) {
459
+ case 'i64':
460
+ case 'u64':
461
+ throw new TypeError('json-number machine JSON mode does not support bigint-backed machine numerics.');
462
+ default:
463
+ return toHostNumber(value);
464
+ }
465
+ }
466
+ }
467
+
468
+ function decodeTaggedMachineJsonValue(value: JsonValue | LosslessJsonValue): MachineJsonLikeValue {
469
+ if (Array.isArray(value)) {
470
+ return value.map((entry) => decodeTaggedMachineJsonValue(entry));
471
+ }
472
+
473
+ if (value && typeof value === 'object') {
474
+ const taggedNumeric = decodeTaggedMachineNumeric(value);
475
+ if (taggedNumeric) {
476
+ return taggedNumeric;
477
+ }
478
+
479
+ const result: Record<string, MachineJsonLikeValue> = {};
480
+ for (const key of Object.keys(value)) {
481
+ result[key] = decodeTaggedMachineJsonValue(value[key]!);
482
+ }
483
+ return result;
484
+ }
485
+
486
+ return value;
487
+ }
488
+
489
+ function decodeTaggedMachineNumeric(
490
+ value: Record<string, JsonValue | LosslessJsonValue>,
491
+ ): Numeric | undefined {
492
+ const numericKind = value.$numeric;
493
+ const numericValue = value.value;
494
+ const keys = Object.keys(value);
495
+ if (
496
+ typeof numericKind !== 'string' ||
497
+ typeof numericValue !== 'string' ||
498
+ keys.length !== 2 ||
499
+ !keys.includes('$numeric') ||
500
+ !keys.includes('value')
501
+ ) {
502
+ return undefined;
503
+ }
504
+
505
+ switch (numericKind) {
506
+ case 'f64':
507
+ return F64.parse(numericValue);
508
+ case 'f32':
509
+ return F32.parse(numericValue);
510
+ case 'i8':
511
+ return I8.parse(numericValue);
512
+ case 'i16':
513
+ return I16.parse(numericValue);
514
+ case 'i32':
515
+ return I32.parse(numericValue);
516
+ case 'i64':
517
+ return I64.parse(numericValue);
518
+ case 'u8':
519
+ return U8.parse(numericValue);
520
+ case 'u16':
521
+ return U16.parse(numericValue);
522
+ case 'u32':
523
+ return U32.parse(numericValue);
524
+ case 'u64':
525
+ return U64.parse(numericValue);
526
+ default:
527
+ return undefined;
528
+ }
529
+ }
530
+
325
531
  function stringifyJsonLikeInternal(
326
532
  value: JsonLikeValue,
327
533
  visited: Set<object>,