sendscript 1.0.6 → 2.0.1

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/references.mjs ADDED
@@ -0,0 +1,59 @@
1
+ import { awaitSymbol, call, ref } from './symbol.mjs'
2
+
3
+ function instrument (path) {
4
+ function reference (...args) {
5
+ const called = instrument(path)
6
+
7
+ called.toJSON = () => ({
8
+ [call]: call,
9
+ call: true,
10
+ ref: reference,
11
+ args
12
+ })
13
+
14
+ return called
15
+ }
16
+
17
+ reference.then = (resolve) => {
18
+ const awaited = instrument(path)
19
+ delete awaited.then
20
+
21
+ awaited.toJSON = () => ({
22
+ [awaitSymbol]: awaitSymbol,
23
+ await: true,
24
+ ref: reference
25
+ })
26
+
27
+ return resolve(awaited)
28
+ }
29
+
30
+ reference.toJSON = () => ({
31
+ [ref]: ref,
32
+ reference: true,
33
+ path
34
+ })
35
+
36
+ return reference
37
+ }
38
+
39
+ export default function module (schema, parentPath = []) {
40
+ return schema.reduce((acc, item) => {
41
+ if (typeof item === 'string') {
42
+ // leaf function
43
+ acc[item] = instrument([...parentPath, item])
44
+ } else if (Array.isArray(item)) {
45
+ const [name, children] = item
46
+
47
+ if (Array.isArray(children)) {
48
+ // recurse: children can be strings or [name, children] arrays
49
+ acc[name] = module(children, [...parentPath, name])
50
+ } else {
51
+ throw new Error(`Expected children array for namespace "${name}"`)
52
+ }
53
+ } else {
54
+ throw new Error('Schema items must be strings or [name, children] arrays')
55
+ }
56
+
57
+ return acc
58
+ }, {})
59
+ }
@@ -0,0 +1,8 @@
1
+ import { test } from 'tap'
2
+ import references from './references.mjs'
3
+
4
+ test('invalid uses of references', t => {
5
+ t.throws(() => references([['a']]))
6
+ t.throws(() => references([{}]))
7
+ t.end()
8
+ })
package/stringify.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import Debug from './debug.mjs'
2
- import isNil from './is-nil.mjs'
3
2
  import {
4
3
  awaitSymbol,
5
4
  call,
@@ -8,59 +7,72 @@ import {
8
7
 
9
8
  const debug = Debug.extend('stringify')
10
9
 
11
- const replaced = Symbol('replaced')
12
- const keywords = ['ref', 'call', 'quote', 'await']
10
+ const keywords = ['ref', 'call', 'quote', 'await', 'leaf']
13
11
  const isKeyword = (v) => keywords.includes(v)
14
- let awaitId = -1
15
12
 
16
- function replacer (key, value) {
17
- debug(this, key, value)
18
-
19
- if (isNil(value)) {
20
- return value
21
- }
22
-
23
- if (value[ref]) {
24
- const result = ['ref', value.name]
13
+ const isPlainObject = (value) => {
14
+ if (!value || typeof value !== 'object') return false
15
+ const proto = Object.getPrototypeOf(value)
16
+ return proto === Object.prototype || proto === null
17
+ }
25
18
 
26
- result[replaced] = replaced
19
+ // Recursively transform a program tree, encoding SendScript operators and leaf values
20
+ function transformValue (value, leafSerializer) {
21
+ debug(value)
27
22
 
28
- return result
23
+ if (value === null) {
24
+ return null
29
25
  }
30
26
 
31
- if (value[call]) {
32
- const result = ['call', value.ref, value.args]
33
-
34
- result[replaced] = replaced
35
-
36
- return result
27
+ // Normalize SendScript wrapper functions (ref, call, await)
28
+ if (typeof value === 'function' && typeof value.toJSON === 'function') {
29
+ return transformValue(value.toJSON(), leafSerializer)
37
30
  }
38
31
 
39
- if (value[awaitSymbol]) {
40
- awaitId += 1
41
- const result = ['await', value.ref, awaitId]
32
+ // Encode SendScript operators
33
+ if (value && value[ref]) {
34
+ return ['ref', ...value.path]
35
+ }
42
36
 
43
- result[replaced] = replaced
37
+ if (value && value[call]) {
38
+ return ['call', transformValue(value.ref, leafSerializer), transformValue(value.args, leafSerializer)]
39
+ }
44
40
 
45
- return result
41
+ if (value && value[awaitSymbol]) {
42
+ return ['await', transformValue(value.ref, leafSerializer)]
46
43
  }
47
44
 
48
- // Quote only the reserved string and not the complete array. Quoted values
49
- // will be unquoted on parse. A quoted quote also.
50
- if (!value[replaced] && Array.isArray(value)) {
45
+ // Handle arrays: quote keyword operators, transform other arrays recursively
46
+ if (Array.isArray(value)) {
51
47
  const [operator, ...rest] = value
52
48
 
53
49
  if (isKeyword(operator)) {
54
- const quoted = ['quote', operator]
55
- quoted[replaced] = replaced
50
+ // Quote reserved keyword strings to preserve them as data
51
+ return [['quote', operator], ...rest.map((item) => transformValue(item, leafSerializer))]
52
+ }
53
+
54
+ return value.map((item) => transformValue(item, leafSerializer))
55
+ }
56
+
57
+ // Recurse into plain objects
58
+ if (isPlainObject(value)) {
59
+ const result = {}
56
60
 
57
- return [quoted, ...rest]
61
+ for (const key of Object.keys(value)) {
62
+ result[key] = transformValue(value[key], leafSerializer)
58
63
  }
64
+
65
+ return result
59
66
  }
60
67
 
61
- return value
68
+ // Encode non-JSON leaf values (Date, RegExp, BigInt, etc.)
69
+ return ['leaf', leafSerializer(value)]
62
70
  }
63
71
 
64
- export default function stringify (program) {
65
- return JSON.stringify(program, replacer)
72
+ export default function stringify (leafSerializer = JSON.stringify) {
73
+ function stringify (program) {
74
+ return JSON.stringify(transformValue(program, leafSerializer))
75
+ }
76
+
77
+ return stringify
66
78
  }
package/index.mjs DELETED
@@ -1,11 +0,0 @@
1
- import stringify from './stringify.mjs'
2
- import makeModule from './module.mjs'
3
- import parse from './parse.mjs'
4
-
5
- export default function sendscript (module) {
6
- return {
7
- stringify,
8
- parse: parse(module),
9
- module: makeModule(module)
10
- }
11
- }
package/module.mjs DELETED
@@ -1,52 +0,0 @@
1
- import {
2
- awaitSymbol,
3
- call,
4
- ref
5
- } from './symbol.mjs'
6
-
7
- function instrument (name) {
8
- function reference (...args) {
9
- const called = instrument(name)
10
-
11
- called.toJSON = () => ({
12
- [call]: call,
13
- call: true,
14
- ref: reference,
15
- args
16
- })
17
-
18
- return called
19
- }
20
-
21
- reference.then = (resolve) => {
22
- const awaited = instrument(name)
23
-
24
- delete awaited.then
25
-
26
- awaited.toJSON = () => ({
27
- [awaitSymbol]: awaitSymbol,
28
- await: true,
29
- ref: reference
30
- })
31
-
32
- return resolve(awaited)
33
- }
34
-
35
- reference.toJSON = () => ({
36
- [ref]: ref,
37
- reference: true,
38
- name
39
- })
40
-
41
- return reference
42
- }
43
-
44
- export default function module (schema) {
45
- if (!Array.isArray(schema)) return module(Object.keys(schema))
46
-
47
- return schema.reduce((api, name) => {
48
- api[name] = instrument(name)
49
-
50
- return api
51
- }, {})
52
- }