entodicton 9.7.1-beta.1 → 9.7.1-beta.11

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/client.js CHANGED
@@ -12,7 +12,7 @@ const _ = require('lodash')
12
12
  const stringify = require('json-stable-stringify')
13
13
  const Lines = require('./lines')
14
14
  const flattens = require('./src/flatten')
15
- const { sortJson, appendNoDups, InitCalls, updateQueries, safeNoDups, stableId, where, suggestAssociationsFix, suggestAssociationsFixFromSummaries, validProps } = require('./src/helpers')
15
+ const { helpDebugHierarchy, sortJson, appendNoDups, InitCalls, updateQueries, safeNoDups, stableId, where, suggestAssociationsFix, suggestAssociationsFixFromSummaries, validProps, OverrideCheck, } = require('./src/helpers')
16
16
  const runtime = require('./runtime')
17
17
  const db = require('./src/debug')
18
18
 
@@ -479,7 +479,7 @@ const getConfigForTest = (config, testConfig) => {
479
479
  return configForTest
480
480
  }
481
481
 
482
- const runTest = async (config, expected, { args, verbose, testConfig, debug, timings = { server: 0, client: 0 } }) => {
482
+ const runTest = async (config, expected, { args, verbose, testConfig, debug, timings = { server: 0, client: 0 }, index = 0 }) => {
483
483
  const test = expected.query
484
484
  if (args.query && args.query != test) {
485
485
  // no run this
@@ -512,10 +512,11 @@ const runTest = async (config, expected, { args, verbose, testConfig, debug, tim
512
512
  defaultInnerProcess(config, errorHandler, result)
513
513
  }
514
514
  if (verbose) {
515
- const widths = [100, 60]
515
+ const widths = [4, 100, 60]
516
516
  const lines = new Lines(widths)
517
- lines.setElement(0, 0, test)
518
- lines.setElement(0, 1, `time on server: ${result.times.toFixed(2)} client: ${(result.clientSideTimes / 1000).toFixed(2)}`)
517
+ lines.setElement(0, 0, index)
518
+ lines.setElement(0, 1, test)
519
+ lines.setElement(0, 2, `time on server: ${result.times.toFixed(2)} client: ${(result.clientSideTimes / 1000).toFixed(2)}`)
519
520
  timings.server += result.times
520
521
  timings.client += result.clientSideTimes / 1000
521
522
  lines.log()
@@ -618,18 +619,22 @@ const runTest = async (config, expected, { args, verbose, testConfig, debug, tim
618
619
 
619
620
  const runTestsHelper = async (config, tests, failed, juicyBits) => {
620
621
  const { stopAtFirstError } = juicyBits
622
+ let index = 0
621
623
  while (true) {
622
624
  if (tests.length === 0) {
623
625
  return Promise.resolve(failed)
624
626
  }
625
627
  const test = tests.shift()
628
+ juicyBits.index = index
626
629
  const result = await runTest(config, test, juicyBits)
627
630
  if (result != null) {
631
+ result.index = index
628
632
  failed.push(result)
629
633
  if (stopAtFirstError) {
630
634
  return failed
631
635
  }
632
636
  }
637
+ index += 1
633
638
  }
634
639
  }
635
640
 
@@ -678,13 +683,6 @@ const saveTests = (config, testFile, testConfig) => {
678
683
  return saveTestsHelper(testFile, config, tests, tests.map((test) => test.query), testConfig)
679
684
  }
680
685
 
681
- /*
682
- const showExamples = (testFile) => {
683
- const tests = JSON.parse(fs.readFileSync(testFile))
684
- Object.keys(tests).forEach((test) => console.log(test))
685
- }
686
- */
687
-
688
686
  const showInfo = (description, section, config) => {
689
687
  console.log(JSON.stringify(config.getInfo(), null, 2))
690
688
  }
@@ -1314,7 +1312,6 @@ const knowledgeModuleImpl = async ({
1314
1312
 
1315
1313
  const helpDebugWord = 'In order to get a debug break when a specific word is created set the DEBUG_WORD environment variable to the JSON of the association to break on. For example DEBUG_WORD=\'"the"\''
1316
1314
  const helpDebugAssociation = 'In order to get a debug break when a specific association is created set the DEBUG_ASSOCIATION environment variable to the JSON of the association to break on. For example DEBUG_ASSOCIATION=\'[["the", 0], ["mammal", 1]]\''
1317
- const helpDebugHierarchy = 'In order to get a debug break when a specific hierarchy is created set the DEBUG_HIERARCHY environment variable to the JSON of the child-parent pair to break on. For example DEBUG_HIERARCHY=\'[["cat", 1], ["mammel", 1]]\''
1318
1315
  const helpDebugPriority = 'In order to get a debug break when a specific set of priorities is created set set DEBUG_PRIORITY environment variable to the JSON of the priorities that you want to break on. For example DEBUG_PRIORITY=\'[["verb", 0], ["article", 0]]\''
1319
1316
  const helpDebugContextualPriority = 'In order to get a debug break when a specific set of contextual priorities is created set set DEBUG_CONTEXTUAL_PRIORITY environment variable to the JSON of the priorities that you want to break on. For example DEBUG_CONTEXTUAL_PRIORITY=\'{ context: [["verb", 0], ["article", 0], select: 1}\''
1320
1317
  const helpDebugBridge = 'In order to get a debug break when a specific bridge is created set the DEBUG_BRIDGE environment variable to id to break on. For example DEBUG_BRIDGE=\'car\''
@@ -1528,7 +1525,7 @@ const knowledgeModuleImpl = async ({
1528
1525
  console.log('Test queries')
1529
1526
  let counter = 0
1530
1527
  for (const test of config.tests) {
1531
- console.log(`${counter} - ${test.query}`)
1528
+ console.log(`#${counter} - ${test.query}`)
1532
1529
  counter += 1
1533
1530
  }
1534
1531
  }
@@ -1757,7 +1754,7 @@ const knowledgeModuleImpl = async ({
1757
1754
  if (hasError) {
1758
1755
  console.log('**************************** ERRORS ************************')
1759
1756
  for (const result of results) {
1760
- console.log('Utterance: ', result.utterance)
1757
+ console.log(`Utterance: #${result.index} ${result.utterance}`)
1761
1758
  if (!result.hasError) {
1762
1759
  continue
1763
1760
  }
@@ -1773,7 +1770,7 @@ const knowledgeModuleImpl = async ({
1773
1770
  newError = true
1774
1771
  headerShown = true
1775
1772
  if (args.vimdiff) {
1776
- vimdiff(actual, expected, `"${label} - ${result.utterance}"`)
1773
+ vimdiff(actual, expected, `"${label} - #${result.index} ${result.utterance}"`)
1777
1774
  }
1778
1775
  result.hasError = true
1779
1776
  }
@@ -1916,12 +1913,17 @@ const knowledgeModuleImpl = async ({
1916
1913
  console.log('use -v arg to write files expected.json and actual.json in the current directory for detailed comparison. Or do -s and then git diff the changes.')
1917
1914
  // console.log(JSON.stringify(contexts))
1918
1915
  let errorCount = 0
1916
+ const failedTests = []
1919
1917
  for (const result of results) {
1920
1918
  if (result.hasError) {
1921
- console.log(`FAILED ${result.utterance}`)
1919
+ console.log(`FAILED #${result.index} ${result.utterance}`)
1920
+ failedTests.push({ index: result.index, utterance: result.utterance })
1922
1921
  errorCount += 1
1923
1922
  }
1924
1923
  }
1924
+ const failedFile = './FAILED.json'
1925
+ console.log(`Failed tests are written to ${failedFile}`)
1926
+ runtime.fs.writeFileSync(failedFile, JSON.stringify(failedTests, 0, 2))
1925
1927
  console.log(`**************************** THERE WERE ${errorCount} TEST FAILURES ************************`)
1926
1928
  }
1927
1929
  }
@@ -2104,4 +2106,5 @@ module.exports = {
2104
2106
  writeTest,
2105
2107
  getConfigForTest,
2106
2108
  debug: db,
2109
+ OverrideCheck,
2107
2110
  }
package/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const { Semantics, Semantic } = require('./src/semantics')
2
2
  const { Generators, Generator } = require('./src/generators')
3
3
  const { Config } = require('./src/config')
4
- const { w, where } = require('./src/helpers')
4
+ const { w, where, OverrideCheck } = require('./src/helpers')
5
5
  const Digraph = require('./src/digraph')
6
6
  const client = require('./client')
7
7
  const flattens = require('./src/flatten')
@@ -28,5 +28,6 @@ module.exports = {
28
28
  flattens: flattens.flattens,
29
29
  flatten: flattens.flatten,
30
30
  unflatten: unflatten.unflatten,
31
- debug
31
+ debug,
32
+ OverrideCheck,
32
33
  }
package/package.json CHANGED
@@ -73,6 +73,6 @@
73
73
  "scriptjs": "^2.5.9",
74
74
  "uuid": "^8.3.2"
75
75
  },
76
- "version": "9.7.1-beta.1",
76
+ "version": "9.7.1-beta.11",
77
77
  "license": "UNLICENSED"
78
78
  }
package/src/config.js CHANGED
@@ -454,7 +454,7 @@ const handleBridgeProps = (config, bridge, { addFirst, uuid } = {}) => {
454
454
  // match: async (args) => bridge.id === args.context.marker && args.context.level === level && !args.context.paraphrase && (args.context.response || args.context.isResponse) && await match(args),
455
455
  match: async (args) => {
456
456
  if (bridge.callId == args.callId) {
457
- debugger
457
+ debugger // eslint-disable-line no-debugger
458
458
  }
459
459
  return args.isA(args.context.marker, bridge.id) && args.context.level === level && !args.context.paraphrase && (args.context.response != null || args.context.isResponse) && await match(args)
460
460
  },
@@ -572,6 +572,7 @@ const handleCalculatedProps = (baseConfig, moreConfig, { addFirst, uuid } = {})
572
572
  'operator',
573
573
  'optional',
574
574
  'parents',
575
+ 'preferOver',
575
576
  'return_type_selector',
576
577
  'scope',
577
578
  'selector',
@@ -601,7 +602,12 @@ const handleCalculatedProps = (baseConfig, moreConfig, { addFirst, uuid } = {})
601
602
  }
602
603
 
603
604
  if (runtime.process.env.DEBUG_HIERARCHY) {
604
- global.entodictonDebugHierarchy = JSON.parse(runtime.process.env.DEBUG_HIERARCHY)
605
+ try {
606
+ global.entodictonDebugHierarchy = JSON.parse(runtime.process.env.DEBUG_HIERARCHY)
607
+ } catch( e ) {
608
+ console.error("Invalid valid for DEBUG_HIERARCHY")
609
+ console.error(helpers.helpDebugHierarchy)
610
+ }
605
611
  }
606
612
 
607
613
  // i keep randomly doing one of the other so I will just make both work the same way
@@ -1655,6 +1661,7 @@ class Config {
1655
1661
  }
1656
1662
  }
1657
1663
 
1664
+ // handler/listener
1658
1665
  updateBridge(id, updater) {
1659
1666
  const bridge = this.config.bridges.find((b) => b.id === id)
1660
1667
  updater({ config: this, bridge })
@@ -2313,6 +2320,7 @@ class Config {
2313
2320
  }
2314
2321
  }
2315
2322
 
2323
+ // TODO Api or API -> pick one and do that
2316
2324
  async setMultiApi (initializer) {
2317
2325
  await this.setApi(() => multiApiImpl(initializer))
2318
2326
  }
@@ -2420,11 +2428,17 @@ class Config {
2420
2428
  if (!api.initialize) {
2421
2429
  throw new Error(`Expected the API for ${this.name} to have an initialize function.`)
2422
2430
  }
2431
+ if (api.baseAPI) {
2432
+ new helpers.OverrideCheck(api.baseAPI).check(api)
2433
+ }
2423
2434
  }
2424
2435
  } else {
2425
2436
  if (!value.initialize) {
2426
2437
  throw new Error(`Expected the API to have an initialize function for ${this.name}. If you are trying to pass in multiple API configs at once use setApiKMs to set the names of the configs that you want set.`)
2427
2438
  }
2439
+ if (value.baseAPI) {
2440
+ new helpers.OverrideCheck(value.baseAPI).check(value)
2441
+ }
2428
2442
  }
2429
2443
 
2430
2444
  if (this._api && this._api.multiApi) {
@@ -145,6 +145,7 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
145
145
  args.objects = config.get('objects')
146
146
  args.getObjects = getObjects(args.objects)
147
147
  }
148
+ args.debug = debug
148
149
  args.enable = (key) => {
149
150
  config.addEnable(key)
150
151
  }
@@ -163,7 +164,19 @@ const setupArgs = (args, config, logs, hierarchy, uuidForScoping) => {
163
164
  }
164
165
  }
165
166
  args.contextHierarchy = new ContextHierarchy()
166
- args.handlerStack = new HandlerStack()
167
+ args.namespaced = {
168
+ get: (km, context, property) => {
169
+ context.namespaced ??= {}
170
+ context.namespaced[km] ??= {}
171
+ return context.namespaced[km][property]
172
+ },
173
+ set: (km, context, property, value) => {
174
+ context.namespaced ??= {}
175
+ context.namespaced[km] ??= {}
176
+ return context.namespaced[km][property] = value
177
+ }
178
+ }
179
+ args.stack = new HandlerStack()
167
180
  args.cleanAssign = cleanAssign
168
181
  args.km = (name) => config.getConfig(name)
169
182
  args.api = (name) => config.getConfig(name).api
package/src/debug.js CHANGED
@@ -27,6 +27,18 @@ const unhit = (name) => {
27
27
  hits[name] = false
28
28
  }
29
29
 
30
+ const hitScoped = async (name, call) => {
31
+ hit(name)
32
+ try {
33
+ const result = await call()
34
+ unhit(name)
35
+ return result
36
+ } catch( e ) {
37
+ unhit(name)
38
+ throw e
39
+ }
40
+ }
41
+
30
42
  const wasHit = (name) => {
31
43
  return hits[name]
32
44
  }
@@ -64,16 +76,19 @@ const _break = (name) => {
64
76
  }
65
77
  }
66
78
 
79
+ const breakAt = _break;
80
+
67
81
  module.exports = {
68
82
  counter,
69
83
  get,
70
84
  _break, // break is a keyword
71
-
85
+ breakAt,
72
86
  // used for tests
73
87
  reset,
74
88
  hit,
75
89
  unhit,
76
90
  wasHit,
77
91
  getDb,
78
- setDb
92
+ setDb,
93
+ hitScoped,
79
94
  }
package/src/flatten.js CHANGED
@@ -1,7 +1,8 @@
1
- const { isArray, isObject, isCompound } = require('./helpers')
1
+ const { isArray, isObject, isCompound, } = require('./helpers')
2
+ const debug = require('./debug')
2
3
  const _ = require('lodash')
3
4
 
4
- const DEBUG = (...args) => {
5
+ const DEBUG = (label, details) => {
5
6
  // DEBUG(...args)
6
7
  }
7
8
 
@@ -46,33 +47,24 @@ const zip = (a1, a2) => {
46
47
  }
47
48
 
48
49
  const flattenListHelper = (markers, values) => {
49
- const flats = []
50
50
  let wasFlattened = false
51
- let singles = []
51
+ const all = []
52
52
  for (const value of values) {
53
53
  if (!isCompound(value)) {
54
- singles.push(value)
54
+ all.push([value])
55
55
  } else {
56
56
  const [more, wf] = flatten(markers, value)
57
57
  if (wf) {
58
58
  wasFlattened = wasFlattened || wf
59
- flats.push(more)
59
+ all.push(more)
60
60
  } else {
61
- singles = singles.concat(more)
61
+ all.push(...more.map((m) => [m]))
62
62
  }
63
63
  }
64
64
  }
65
65
 
66
- // product does weirdness with only choice
67
- if (!wasFlattened) {
68
- return [[singles], wasFlattened]
69
- } else {
70
- const results = []
71
- for (const flat of cartesian(...flats)) {
72
- results.push(singles.concat(flat))
73
- }
74
- return [results, wasFlattened]
75
- }
66
+ const result = cartesian(...all)
67
+ return [result, wasFlattened]
76
68
  }
77
69
 
78
70
  // returns (list, wasFlattened)
package/src/fragments.js CHANGED
@@ -1,6 +1,29 @@
1
1
  const _ = require('lodash')
2
2
  const helpers = require('./helpers')
3
3
 
4
+ function createChangeTracker(obj) {
5
+ const changes = new Set();
6
+
7
+ const handler = {
8
+ set(target, prop, value) {
9
+ changes.add(prop);
10
+ target[prop] = value;
11
+ return true;
12
+ },
13
+ deleteProperty(target, prop) {
14
+ changes.add(prop);
15
+ delete target[prop];
16
+ return true;
17
+ }
18
+ };
19
+
20
+ return {
21
+ proxy: new Proxy(obj, handler),
22
+ getChanges: () => Array.from(changes)
23
+ };
24
+ }
25
+
26
+
4
27
  function fragmentInstantiator (args, contexts) {
5
28
  return new Object({
6
29
  contexts: () => {
@@ -13,13 +36,15 @@ function fragmentInstantiator (args, contexts) {
13
36
  args.pathEquals = helpers.pathEquals
14
37
  while (todo.length > 0) {
15
38
  const { context, path } = todo.pop()
16
- args.context = context
39
+ const { proxy, getChanges } = createChangeTracker(context);
40
+ args.context = proxy
17
41
  args.path = path
18
42
  for (const mapping of mappings) {
19
43
  if (await mapping.match(args)) {
20
44
  await mapping.apply(args)
21
45
  }
22
46
  }
47
+ const ignore = getChanges()
23
48
  for (const key of Object.keys(context)) {
24
49
  // if (['number', 'string', 'boolean'].includes(typeof (context[key]))) {
25
50
  if (!helpers.isCompound(context[key])) {
@@ -28,7 +53,9 @@ function fragmentInstantiator (args, contexts) {
28
53
  if (context[key].instantiated) {
29
54
  continue
30
55
  }
31
- todo.push({ context: context[key], path: [...path, key] })
56
+ if (!ignore.includes(key)) {
57
+ todo.push({ context: context[key], path: [...path, key] })
58
+ }
32
59
  }
33
60
  }
34
61
  if (contexts.length == 1 && instantiated.length == 1) {
package/src/generators.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const { args: contextArgs, normalizeGenerator } = require('./helpers')
2
2
  const Lines = require('../lines')
3
3
  const helpers = require('./helpers')
4
+ const debug = require('./debug')
4
5
 
5
6
  class Generator {
6
7
  // constructor ({ match, apply, uuid, index, km, priority, notes }) {
@@ -65,14 +66,16 @@ class Generator {
65
66
  }
66
67
  const args = Object.assign({}, baseArgs, moreArgs, (baseArgs.getUUIDScoped || (() => { return {} }))(this.uuid))
67
68
  // return this.match(args)
68
- const matches = await this.match(args)
69
- if ((matches && (options.debug || {}).match) ||
70
- callId === this.callId) {
71
- // next line is the matcher
72
- debugger // eslint-disable-line no-debugger
73
- await this.match(args)
74
- }
75
- return matches
69
+ return await debug.hitScoped(args.callId, async () => {
70
+ const matches = await this.match(args)
71
+ if ((matches && (options.debug || {}).match) ||
72
+ callId === this.callId) {
73
+ // next line is the matcher
74
+ debugger // eslint-disable-line no-debugger
75
+ await this.match(args)
76
+ }
77
+ return matches
78
+ })
76
79
  }
77
80
 
78
81
  // apply (baseArgs, objects, g, gs, context, hierarchy, config, response, log, options = {}) {
@@ -121,11 +124,13 @@ class Generator {
121
124
  if (this.property === 'generatorp') {
122
125
  args.g = args.gp
123
126
  }
124
- if ((options.debug || {}).apply ||
125
- callId === this.callId) {
126
- debugger // eslint-disable-line no-debugger
127
- }
128
- return await this._apply(args)
127
+ return await debug.hitScoped(args.callId, async () => {
128
+ if ((options.debug || {}).apply ||
129
+ callId === this.callId) {
130
+ debugger // eslint-disable-line no-debugger
131
+ }
132
+ return await this._apply(args)
133
+ })
129
134
  }
130
135
  }
131
136
 
@@ -220,7 +225,7 @@ class Generators {
220
225
  lines.setElement(0, 2, stack)
221
226
  lines.newRow()
222
227
  lines.setElement(0, 1, 'DEBUG')
223
- lines.setElement(0, 2, `To debug this use args.callId === '${args.calls.current()}'`)
228
+ lines.setElement(0, 2, `To debug this use debug.breakAt('${args.calls.current()}')`)
224
229
  lines.newRow()
225
230
  lines.setElement(0, 1, 'ERROR')
226
231
  lines.setElement(0, 2, errorMessage)
@@ -252,7 +257,7 @@ class Generators {
252
257
  lines.setElement(0, 2, stack)
253
258
  lines.newRow()
254
259
  lines.setElement(0, 1, 'DEBUG')
255
- lines.setElement(0, 2, `To debug this use args.callId === '${args.calls.current()}'`)
260
+ lines.setElement(0, 2, `To debug this use debug.breakAt('${args.calls.current()}')`)
256
261
  lines.newRow()
257
262
  lines.setElement(0, 1, 'TO')
258
263
  lines.setElement(0, 2, `context_id: ${context.context_id}`)
package/src/helpers.js CHANGED
@@ -1,6 +1,27 @@
1
1
  const deepEqual = require('deep-equal')
2
2
  const stringify = require('json-stable-stringify')
3
3
 
4
+
5
+ // check if member function of a class have been overriden by an subclass
6
+ class OverrideCheck {
7
+ constructor(base, suffix='API') {
8
+ this.base = base
9
+ this.checks = Object.getOwnPropertyNames(base.prototype).filter(key => typeof base.prototype[key] === 'function' && key.endsWith(suffix));
10
+ }
11
+
12
+ check(obj) {
13
+ const overriden = obj.constructor !== obj.baseAPI
14
+ if (!overriden) {
15
+ return
16
+ }
17
+ for (const check of this.checks) {
18
+ if (obj[check] == this.base.prototype[check]) {
19
+ throw new Error(`For ${obj.constructor.name} you need to override ${check}`)
20
+ }
21
+ }
22
+ }
23
+ }
24
+
4
25
  function pathEquals (p1, p2) {
5
26
  if (p1.length !== p2.length) {
6
27
  return false
@@ -579,7 +600,11 @@ function setByPath(obj, path, value) {
579
600
  return value;
580
601
  }
581
602
 
603
+ const helpDebugHierarchy = 'In order to get a debug break when a specific hierarchy is created set the DEBUG_HIERARCHY environment variable to the JSON of the child-parent pair to break on. For example DEBUG_HIERARCHY=\'["cat", "mammel"]\''
604
+
582
605
  module.exports = {
606
+ helpDebugHierarchy,
607
+
583
608
  stableId,
584
609
  ecatch,
585
610
  functionsToStrings,
@@ -611,4 +636,5 @@ module.exports = {
611
636
  assignAssumed,
612
637
  watchProperty,
613
638
  pathEquals,
639
+ OverrideCheck,
614
640
  }
package/src/semantics.js CHANGED
@@ -75,13 +75,15 @@ class Semantic {
75
75
  async matches (args, context, options = {}) {
76
76
  args = {...args}
77
77
  this.fixUpArgs(args, context)
78
- const matches = await this.matcher(args)
79
- if (matches && (options.debug || {}).match || args.callId === this.callId) {
80
- // next line is the matcher
81
- debugger // eslint-disable-line no-debugger
82
- await this.matcher(args)
83
- }
84
- return matches
78
+ return await debug.hitScoped(args.callId, async () => {
79
+ const matches = await this.matcher(args)
80
+ if (matches && (options.debug || {}).match || args.callId === this.callId) {
81
+ // next line is the matcher
82
+ debugger // eslint-disable-line no-debugger
83
+ await this.matcher(args)
84
+ }
85
+ return matches
86
+ })
85
87
  }
86
88
 
87
89
  async apply (args, context, s, options = {}) {
@@ -97,21 +99,24 @@ class Semantic {
97
99
  const contextPrime = Object.assign({}, context)
98
100
  this.fixUpArgs(args, contextPrime)
99
101
 
100
- if ((options.debug || {}).apply || args.callId === this.callId) {
101
- debugger // eslint-disable-line no-debugger
102
- }
103
- if (args.breakOnSemantics) {
104
- debugger // eslint-disable-line no-debugger
105
- }
106
- try {
107
- args.handlerStack.push(this)
108
- await this._apply(args)
109
- args.handlerStack.pop()
110
- } catch( e ) {
111
- args.handlerStack.pop()
112
- throw e
113
- }
114
- return contextPrime
102
+ return await debug.hitScoped(args.callId, async () => {
103
+ if ((options.debug || {}).apply || args.callId === this.callId) {
104
+ debugger // eslint-disable-line no-debugger
105
+ }
106
+ if (args.breakOnSemantics) {
107
+ debugger // eslint-disable-line no-debugger
108
+ }
109
+
110
+ try {
111
+ args.stack.push(this)
112
+ await this._apply(args)
113
+ args.stack.pop()
114
+ } catch( e ) {
115
+ args.stack.pop()
116
+ throw e
117
+ }
118
+ return contextPrime
119
+ })
115
120
  }
116
121
  }
117
122
 
@@ -187,6 +192,8 @@ class Semantics {
187
192
  let seenQuestion = false
188
193
  const deferred = []
189
194
  args.log = (message) => { this.logs.push(message) }
195
+ const finallyList = []; // called after the semantic is processed
196
+ args._finally = (call) => finallyList.unshift(call)
190
197
  for (const isemantic in this.semantics) {
191
198
  const semantic = this.semantics[isemantic]
192
199
  // only one question at a time
@@ -253,7 +260,7 @@ class Semantics {
253
260
  lines.setElement(0, 2, semantic.toString())
254
261
  lines.newRow()
255
262
  lines.setElement(0, 1, 'DEBUG')
256
- lines.setElement(0, 2, `To debug this use args.callId === '${args.calls.current()}'`)
263
+ lines.setElement(0, 2, `To debug this use debug.breakAt('${args.calls.current()}')`)
257
264
  lines.newRow()
258
265
  lines.setElement(0, 1, 'TO')
259
266
  lines.setElement(0, 2, `context_id: ${context.context_id}`)
@@ -288,7 +295,7 @@ class Semantics {
288
295
  lines.setElement(0, 2, semantic.toString())
289
296
  lines.newRow()
290
297
  lines.setElement(0, 1, 'DEBUG')
291
- lines.setElement(0, 2, `To debug this use args.callId === '${args.calls.current()}'`)
298
+ lines.setElement(0, 2, `To debug this use debug.breakAt('${args.calls.current()}')`)
292
299
  lines.newRow()
293
300
  lines.setElement(0, 1, 'TO')
294
301
  lines.setElement(0, 2, `context_id: ${context.context_id}`)
@@ -319,6 +326,9 @@ class Semantics {
319
326
  }
320
327
  counter += 1
321
328
  }
329
+ for (const f of finallyList) {
330
+ await f()
331
+ }
322
332
  args.calls.pop()
323
333
  if (!applied && debug) {
324
334
  const widths = Lines.addRemainder([10, 10])