@voxgig/create-sdkgen 0.1.1 → 0.1.2

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 (62) hide show
  1. package/package.json +2 -2
  2. package/project/standard/.env.local +1 -0
  3. package/project/standard/.sdk/build/apidef.js +8 -0
  4. package/project/standard/.sdk/build/docgen.js +15 -0
  5. package/project/standard/.sdk/build/sdkgen.js +16 -0
  6. package/project/standard/.sdk/def/README.md +4 -0
  7. package/project/standard/.sdk/model/.model-config/model-config.jsonic +17 -0
  8. package/project/standard/.sdk/model/.model-config/model-config.jsonic~ +16 -0
  9. package/project/standard/.sdk/model/README.md +2 -0
  10. package/project/standard/.sdk/model/api/api-def.jsonic +5 -0
  11. package/project/standard/.sdk/model/api/api-entity-index.jsonic +2 -0
  12. package/project/standard/.sdk/model/config.jsonic +13 -0
  13. package/project/standard/.sdk/model/feature/README.md +2 -0
  14. package/project/standard/.sdk/model/feature/feature-index.jsonic +3 -0
  15. package/project/standard/.sdk/model/flow/flow-index.jsonic +2 -0
  16. package/project/standard/.sdk/model/guide.jsonic +5 -0
  17. package/project/standard/.sdk/model/sdk.fragment.jsonic +38 -0
  18. package/project/standard/.sdk/model/target/README.md +2 -0
  19. package/project/standard/.sdk/model/target/target-index.jsonic +2 -0
  20. package/project/standard/.sdk/model/target/target-index.jsonic~ +4 -0
  21. package/project/standard/.sdk/package.json +34 -0
  22. package/project/standard/.sdk/src/DocStaticRoot.ts +45 -0
  23. package/project/standard/.sdk/src/Root.ts +228 -0
  24. package/project/standard/.sdk/src/cmp/README.md +2 -0
  25. package/project/standard/.sdk/src/tsconfig.json +16 -0
  26. package/project/standard/.sdk/test/.model-config/model-config.json +7 -0
  27. package/project/standard/.sdk/test/.model-config/model-config.jsonic +4 -0
  28. package/project/standard/.sdk/test/primary/auth.jsonic +20 -0
  29. package/project/standard/.sdk/test/primary/body.jsonic +27 -0
  30. package/project/standard/.sdk/test/primary/contextify.jsonic +5 -0
  31. package/project/standard/.sdk/test/primary/done.jsonic +7 -0
  32. package/project/standard/.sdk/test/primary/error.jsonic +19 -0
  33. package/project/standard/.sdk/test/primary/findparam.jsonic +22 -0
  34. package/project/standard/.sdk/test/primary/fullurl.jsonic +50 -0
  35. package/project/standard/.sdk/test/primary/headers.jsonic +12 -0
  36. package/project/standard/.sdk/test/primary/inward.jsonic +3 -0
  37. package/project/standard/.sdk/test/primary/method.jsonic +11 -0
  38. package/project/standard/.sdk/test/primary/operator.jsonic +60 -0
  39. package/project/standard/.sdk/test/primary/options.jsonic +60 -0
  40. package/project/standard/.sdk/test/primary/outward.jsonic +3 -0
  41. package/project/standard/.sdk/test/primary/params.jsonic +10 -0
  42. package/project/standard/.sdk/test/primary/query.jsonic +9 -0
  43. package/project/standard/.sdk/test/primary/reqform.jsonic +9 -0
  44. package/project/standard/.sdk/test/primary/request.jsonic +31 -0
  45. package/project/standard/.sdk/test/primary/resbasic.jsonic +15 -0
  46. package/project/standard/.sdk/test/primary/resbody.jsonic +5 -0
  47. package/project/standard/.sdk/test/primary/resform.jsonic +11 -0
  48. package/project/standard/.sdk/test/primary/resheaders.jsonic +9 -0
  49. package/project/standard/.sdk/test/primary/response.jsonic +20 -0
  50. package/project/standard/.sdk/test/primary/spec.jsonic +32 -0
  51. package/project/standard/.sdk/test/struct/getpath.jsonic +143 -0
  52. package/project/standard/.sdk/test/struct/inject.jsonic +76 -0
  53. package/project/standard/.sdk/test/struct/merge.jsonic +161 -0
  54. package/project/standard/.sdk/test/struct/minor.jsonic +693 -0
  55. package/project/standard/.sdk/test/struct/select.jsonic +166 -0
  56. package/project/standard/.sdk/test/struct/transform.jsonic +453 -0
  57. package/project/standard/.sdk/test/struct/validate.jsonic +287 -0
  58. package/project/standard/.sdk/test/struct/walk.jsonic +57 -0
  59. package/project/standard/.sdk/test/test.json +15080 -0
  60. package/project/standard/.sdk/test/test.jsonic +44 -0
  61. package/project/standard/.sdk/tm/LICENSE +22 -0
  62. package/project/standard/.sdk/tm/README.md +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxgig/create-sdkgen",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "main": "dist/create-sdkgen.js",
5
5
  "type": "commonjs",
6
6
  "types": "dist/create-sdkgen.d.ts",
@@ -37,7 +37,7 @@
37
37
  "src",
38
38
  "dist",
39
39
  "bin",
40
- "tm",
40
+ "project",
41
41
  "LICENSE"
42
42
  ],
43
43
  "devDependencies": {
@@ -0,0 +1 @@
1
+ PROJECTNAME_APIKEY=<apikey>
@@ -0,0 +1,8 @@
1
+
2
+ const { ApiDef } = require('@voxgig/apidef')
3
+
4
+ const opts = {
5
+ folder: __dirname + '/../model',
6
+ }
7
+
8
+ module.exports = ApiDef.makeBuild(opts)
@@ -0,0 +1,15 @@
1
+
2
+ const { DocGen } = require('@voxgig/docgen')
3
+
4
+ const config = {
5
+ root: __dirname+'/../dist/DocStaticRoot.js',
6
+ folder: __dirname+'/../../doc',
7
+ meta: {
8
+ name: '$$name$$'
9
+ },
10
+ model: {
11
+ folder: __dirname+'/../model',
12
+ },
13
+ }
14
+
15
+ module.exports = DocGen.makeBuild(config)
@@ -0,0 +1,16 @@
1
+
2
+ const { SdkGen } = require('@voxgig/sdkgen')
3
+
4
+ const config = {
5
+ root: __dirname+'/../dist/Root.js',
6
+ folder: __dirname+'/../..',
7
+ meta: {
8
+ name: '$$name$$'
9
+ },
10
+ model: {
11
+ folder: __dirname+'/../model',
12
+ },
13
+ existing: { txt: { merge: true } },
14
+ }
15
+
16
+ module.exports = SdkGen.makeBuild(config)
@@ -0,0 +1,4 @@
1
+ # API Definition
2
+
3
+ API definition (OpenAPI etc) files live here.
4
+
@@ -0,0 +1,17 @@
1
+
2
+
3
+ @'@voxgig/model/model/.model-config/model-config.jsonic'
4
+
5
+ sys: model: builders: {
6
+ apidef: load: 'build/apidef.js'
7
+ sdkgen: load: 'build/sdkgen.js'
8
+ // docgen: load: 'build/docgen.js'
9
+ }
10
+
11
+ sys: model: watch: {
12
+ 'src': {} # To catch fragment updates.
13
+ 'dist': {}
14
+ 'tm': {}
15
+ 'def': {}
16
+ # 'doc/static/src': {}
17
+ }
@@ -0,0 +1,16 @@
1
+
2
+
3
+ @'@voxgig/model/model/.model-config/model-config.jsonic'
4
+
5
+ sys: model: builders: {
6
+ apidef: load: 'build/apidef.js'
7
+ sdkgen: load: 'build/sdkgen.js'
8
+ // docgen: load: 'build/docgen.js'
9
+ }
10
+
11
+ sys: model: watch: {
12
+ 'dist': {}
13
+ 'tm': {}
14
+ 'def': {}
15
+ # 'doc/static/src': {}
16
+ }
@@ -0,0 +1,2 @@
1
+ # SDK Model Definition
2
+
@@ -0,0 +1,5 @@
1
+ # API Definition
2
+
3
+ main: def: {}
4
+
5
+
@@ -0,0 +1,2 @@
1
+ # Entity Models
2
+
@@ -0,0 +1,13 @@
1
+ # SDK Configuration
2
+
3
+ main: sdk: config: {
4
+
5
+ # Authorization header token value prefix
6
+ auth: prefix: 'Bearer'
7
+
8
+
9
+ headers: {
10
+ 'content-type': 'application/json'
11
+ }
12
+
13
+ }
@@ -0,0 +1,2 @@
1
+ # Feature Model Defintions
2
+
@@ -0,0 +1,2 @@
1
+ # Operational Flows
2
+
@@ -0,0 +1,5 @@
1
+ # Guide
2
+
3
+ main: api: guide: {}
4
+
5
+
@@ -0,0 +1,38 @@
1
+
2
+ # Generation guide.
3
+ @"guide.jsonic"
4
+
5
+
6
+ # Model definitions.
7
+ @"@voxgig/apidef/model/apidef.jsonic"
8
+ @"@voxgig/sdkgen/model/sdkgen.jsonic"
9
+
10
+
11
+ # SDK Details.
12
+ name: 'NAME'
13
+ def: 'DEF'
14
+
15
+
16
+ # API Definition
17
+ @"api/api-def.jsonic"
18
+
19
+
20
+ # API Entities
21
+ @"api/api-entity-index.jsonic"
22
+
23
+
24
+ # SDK Configuration.
25
+ @"config.jsonic"
26
+
27
+
28
+ # SDK Features.
29
+ @"feature/feature-index.jsonic"
30
+
31
+
32
+ # SDK Targets.
33
+ @"target/target-index.jsonic"
34
+
35
+
36
+ # Operation workflows.
37
+ @"flow/flow-index.jsonic"
38
+
@@ -0,0 +1,2 @@
1
+ # Language Model Defintions
2
+
@@ -0,0 +1,2 @@
1
+ # SDK Targets.
2
+
@@ -0,0 +1,4 @@
1
+ # SDK Targets.
2
+
3
+ @"ts.jsonic"
4
+
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "build-$$const.name$$-sdk",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "model": "voxgig-model model/sdk.jsonic",
8
+ "model-watch": "voxgig-model -w model/sdk.jsonic",
9
+ "test-model": "voxgig-model test/test.jsonic",
10
+ "test-model-watch": "voxgig-model -w test/test.jsonic",
11
+ "watch": "tsc --build src -w",
12
+ "build": "tsc --build src",
13
+ "test": "echo no-test",
14
+ "clean": "rm -rf node_modules yarn.lock package-lock.json",
15
+ "reset": "npm run clean && npm i && npm run build && npm test",
16
+ "add-target": "voxgig-sdkgen target add $npm_config_target",
17
+ "add-feature": "voxgig-sdkgen feature add $npm_config_feature"
18
+ },
19
+ "author": "",
20
+ "license": "MIT",
21
+ "devDependencies": {
22
+ "@types/node": ">=24",
23
+ "typescript": ">=5",
24
+ "patch-package": ">=8"
25
+ },
26
+ "dependencies": {
27
+ "@voxgig/apidef": ">=2",
28
+ "@voxgig/model": ">=7",
29
+ "@voxgig/sdkgen": ">=0",
30
+ "@voxgig/util": ">=0",
31
+ "@voxgig/struct": ">=0",
32
+ "jostraca": ">=0"
33
+ }
34
+ }
@@ -0,0 +1,45 @@
1
+
2
+ // import {
3
+ // names,
4
+ // cmp,
5
+ // each,
6
+
7
+ // Project,
8
+ // Folder,
9
+
10
+ // Index,
11
+ // } from '@voxgig/docgen'
12
+
13
+ import {
14
+ cmp,
15
+ } from '@voxgig/sdkgen'
16
+
17
+
18
+ const Root = cmp(function Root(props: any) {
19
+
20
+ /*
21
+ const { model, ctx$ } = props
22
+
23
+
24
+ model.const = { name: model.name }
25
+
26
+ names(model.const, model.name)
27
+
28
+ model.const.year = new Date().getFullYear()
29
+
30
+
31
+ ctx$.model = model
32
+
33
+ Project({}, () => {
34
+
35
+ Index({})
36
+
37
+ })
38
+ */
39
+ })
40
+
41
+
42
+ export {
43
+ Root
44
+ }
45
+
@@ -0,0 +1,228 @@
1
+
2
+ import {
3
+ names,
4
+ cmp,
5
+ each,
6
+ getx,
7
+
8
+ Project,
9
+ Folder,
10
+
11
+ Main,
12
+ Entity,
13
+ Feature,
14
+ Readme,
15
+
16
+ } from '@voxgig/sdkgen'
17
+
18
+
19
+ import { transform, select, ismap } from '@voxgig/struct'
20
+
21
+ import { PointUtil, Content } from 'jostraca'
22
+
23
+ const {
24
+ buildPoints,
25
+ SerialPoint,
26
+ } = PointUtil
27
+
28
+
29
+
30
+ const Root = cmp(function Root(props: any) {
31
+ const { model, ctx$ } = props
32
+
33
+ ctx$.util = ctx$.util || {}
34
+ ctx$.util.makeFlow = makeFlow
35
+
36
+ // TODO: move to @voxgig/util as duplicated
37
+ model.const = { name: model.name }
38
+ names(model.const, model.name)
39
+ model.const.year = new Date().getFullYear()
40
+
41
+ ctx$.model = model
42
+
43
+ const target = model.main.sdk.target || {}
44
+ const feature = model.main.sdk.feature || {}
45
+ const entity = model.main.api.entity || {}
46
+
47
+ ctx$.log.debug({
48
+ point: 'cmp-root', target, entity, feature, note: [
49
+ '\ntarget: \n' + Object.keys(target).map(s => ' ' + s).join('\n'),
50
+ '\nentity:\n' + Object.keys(entity).map(s => ' ' + s).join('\n'),
51
+ '\nfeature:\n' + Object.keys(feature).map(s => ' ' + s).join('\n'),
52
+ ].join('\n')
53
+ })
54
+
55
+ names(model, model.name)
56
+ // console.log('MODEL name', model.name, model.Name)
57
+
58
+ // Standard Replacements
59
+ ctx$.stdrep = {}
60
+ names(ctx$.stdrep, model.Name, 'ProjectName')
61
+ // console.log('STDREP', stdrep)
62
+
63
+ Project({}, () => {
64
+
65
+ each(target, (target: any) => {
66
+ names(target, target.name)
67
+
68
+ Folder({ name: target.name }, () => {
69
+
70
+ each(entity, (entity: any) => {
71
+ names(entity, entity.name)
72
+ Entity({ target, entity })
73
+ })
74
+
75
+ // each(feature).filter((feature: any) => feature.active).map((feature: any) => {
76
+ each(feature).filter((feature: any) => feature.active).map((feature: any) => {
77
+ names(feature, feature.name)
78
+ Feature({ target, feature })
79
+ })
80
+
81
+ Main({ target })
82
+
83
+ Readme({ target })
84
+
85
+ })
86
+ })
87
+
88
+ })
89
+ })
90
+
91
+
92
+ function makeFlow(def: any, data: any, stepMakers: Record<string, any>) {
93
+
94
+ const steps: any = {}
95
+ each(stepMakers, (n: any) => {
96
+ // TODO: support after?
97
+ if ('function' === typeof n.val$) {
98
+ steps[n.key$] = (id: any, pdef: any) => makeFlowStep(id(), pdef, n.val$)
99
+ }
100
+ else if ('string' === typeof n.val$) {
101
+ steps[n.key$] = (id: any, pdef: any) => makeFlowStep(id(), pdef, (sd: any, pctx: any) => {
102
+ Content({
103
+ indent: pctx.data.indent,
104
+ extra: {
105
+ __stepdef: sd
106
+ },
107
+ }, n.val$)
108
+ })
109
+ }
110
+ else if (Array.isArray(n)) {
111
+ steps[(n as any).key$] =
112
+ (id: any, pdef: any) => makeFlowStep(id(), pdef, (sd: any, pctx: any) => {
113
+ const extra = {
114
+ __stepdef: sd
115
+ }
116
+ for (let tmdef of n) {
117
+ let tmtxt
118
+ if ('string' === typeof tmdef) {
119
+ tmtxt = tmdef
120
+ }
121
+ else if (Array.isArray(tmdef)) {
122
+ let pass = true
123
+ let cond = tmdef[0]
124
+ tmtxt = tmdef[1]
125
+
126
+ if ('string' === typeof cond) {
127
+ pass = (null != getx(extra, tmdef[0]) || null != getx(pctx.data.model, tmdef[0]))
128
+ }
129
+ else if (ismap(cond)) {
130
+ let children = [{ ...pctx.data.model, ...extra }]
131
+ let found = select(
132
+ children,
133
+ cond
134
+ )
135
+ pass = 0 < found.length
136
+ }
137
+
138
+ if (!pass) {
139
+ tmtxt = tmdef[2]
140
+ }
141
+ }
142
+ else if ('function' === typeof tmdef) {
143
+ tmdef(sd, pctx)
144
+ }
145
+
146
+ if (null != tmtxt) {
147
+ Content({
148
+ indent: pctx.data.indent,
149
+ extra,
150
+ }, tmtxt)
151
+ }
152
+ }
153
+ })
154
+ }
155
+ })
156
+
157
+ each(def.step, (step: any) => {
158
+ names(step, step.entity, 'entity')
159
+ })
160
+
161
+ // console.log('STEPS', stepMakers, steps, def)
162
+
163
+ const spec = transform(def, {
164
+ p: ['`$EACH`', 'step', {
165
+ k: 'FlowStep',
166
+ a: '`.`',
167
+ p: [
168
+ { k: 'GetEntity', a: '`...`' },
169
+ { k: 'EntityMatch', a: '`...`' },
170
+ { k: 'EntityData', a: '`...`' },
171
+ { k: 'EntityAction', a: '`...`' },
172
+ { k: 'ExplainAction', a: '`...`' },
173
+ { k: 'ValidateAction', a: '`...`' },
174
+ ]
175
+ }]
176
+ })
177
+
178
+ const rootPoint = buildPoints(spec, steps) as any
179
+
180
+ data = data || {}
181
+ data.step = {}
182
+
183
+ rootPoint.direct(data)
184
+ }
185
+
186
+
187
+ function makeFlowStep(
188
+ id: string,
189
+ pdef: any,
190
+ before: any,
191
+ after?: any,
192
+ _parent?: any
193
+ ): any {
194
+
195
+ class FlowStep extends SerialPoint {
196
+ pdef: any
197
+
198
+ constructor(id: string, pdef: any) {
199
+ super(id)
200
+ this.pdef = pdef
201
+ }
202
+
203
+ async run(pctx: any): Promise<void> {
204
+ const stepdef = pdef.a
205
+ if (stepdef.ref) {
206
+ const refstep = pctx.data.step[stepdef.ref]
207
+ stepdef.kind = refstep.kind
208
+ stepdef.entity = refstep.entity
209
+ stepdef._ref = stepdef.ref
210
+ delete stepdef.ref
211
+ }
212
+
213
+ before.call(this, stepdef, pctx)
214
+ super.run(pctx)
215
+ after && after.call(this, stepdef, pctx)
216
+ }
217
+ }
218
+
219
+ return new FlowStep(id, pdef)
220
+ }
221
+
222
+
223
+
224
+
225
+ export {
226
+ Root
227
+ }
228
+
@@ -0,0 +1,2 @@
1
+ # Target builder components
2
+
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "esModuleInterop": true,
4
+ "module": "nodenext",
5
+ "noEmitOnError": true,
6
+ "outDir":"../dist",
7
+ "rootDir":".",
8
+ "resolveJsonModule": true,
9
+ "sourceMap": true,
10
+ "strict": true,
11
+ "target": "es2021",
12
+ "declaration": true,
13
+ "declarationDir": "../dist"
14
+ },
15
+ "exclude": ["**/fragment/*.fragment.ts"]
16
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "sys": {
3
+ "model": {
4
+ "builders": {}
5
+ }
6
+ }
7
+ }
@@ -0,0 +1,4 @@
1
+
2
+ @"@voxgig/model/model/.model-config/model-config.jsonic"
3
+
4
+ sys: model: builders: {}
@@ -0,0 +1,20 @@
1
+
2
+ DEF: {
3
+ client: a: test: options: { apikey: 'APIKEY01' }
4
+ client: b: test: options: {}
5
+ }
6
+
7
+
8
+ basic: set: [
9
+
10
+ { ctx: spec:headers:{}
11
+ client: a
12
+ match: out:headers:authorization:'Bearer APIKEY01'
13
+ }
14
+
15
+ # { ctx: spec:headers:{}
16
+ # client: b
17
+ # match: out:headers:authorization:'__UNDEF__' }
18
+
19
+ ]
20
+
@@ -0,0 +1,27 @@
1
+
2
+ basic: set: [
3
+ {
4
+ ctx: {
5
+ op:kind:req, op:reqform:'`data.foo`', op:check:nobody:true
6
+ op:data:foo:{x:1}
7
+ }
8
+ out: {x:1}
9
+ }
10
+
11
+ {
12
+ ctx: {
13
+ op:kind:req, op:reqform:'`data.bar`', op:check:nobody:false
14
+ op:data:foo:{x:2}
15
+ }
16
+ }
17
+
18
+ {
19
+ ctx: {
20
+ op:kind:req, op:reqform:'`data.bar`', op:check:nobody:true
21
+ op:data:foo:{x:3}
22
+ }
23
+ err: 'empty'
24
+ }
25
+
26
+ ]
27
+
@@ -0,0 +1,5 @@
1
+
2
+ basic: set: [
3
+ { in:{op:{kind:req}}, match:out:{op:{kind:req}} }
4
+ ]
5
+
@@ -0,0 +1,7 @@
1
+
2
+ basic: set: [
3
+ { ctx:{result:{ok:true,resdata:{x:1}}}, out:{x:1} }
4
+
5
+ { ctx:{result:{ok:false}}, err:'unknown operation: unknown error' }
6
+ ]
7
+
@@ -0,0 +1,19 @@
1
+
2
+ basic: set: [
3
+ { args:[], err:'SDK: unknown operation: unknown error' }
4
+ { args:[{}], err:'SDK: unknown operation: unknown error' }
5
+ { args:[{},{}], err:'SDK: unknown operation: unknown error' }
6
+
7
+ { args:[{op:{}}], err:'SDK: unknown operation: unknown error' }
8
+ { args:[{op:name:foo}], err:'SDK: foo: unknown error' }
9
+ { args:[{op:name:foo,result:{}}], err:'SDK: foo: unknown error' }
10
+
11
+ { args:[{op:name:foo,result:{err:message:bar}}], err:'SDK: foo: bar' }
12
+ { args:[{op:name:foo}, {message:zed}], err:'SDK: foo: zed' }
13
+
14
+ { args:[{op:name:o0}, {message:e0}], err:'SDK: o0: e0'
15
+ match: err:message:'SDK: o0: e0'
16
+ }
17
+
18
+ ]
19
+
@@ -0,0 +1,22 @@
1
+
2
+
3
+ DEF: {
4
+ ctx0: {op:{}}
5
+ ctx1: .ctx0 & {op:kind:res}
6
+ ctx2: .ctx1 & {op:match:{}}
7
+ ctx3: .ctx2 & {op:match:a:'A'}
8
+ ctx4: .ctx3 & {op:alias:{}}
9
+ ctx5: .ctx4 & {op:alias:b:'a',spec:{}}
10
+ }
11
+
12
+ basic: set: [
13
+ { mark:10, args: [ .....DEF.ctx0, a ] }
14
+ { mark:20, args: [ .....DEF.ctx1, a ] }
15
+ { mark:30, args: [ .....DEF.ctx2, a ] }
16
+ { mark:40, args: [ .....DEF.ctx3, a ], out: 'A'}
17
+ { mark:50, args: [ .....DEF.ctx3, b ] }
18
+ { mark:60, args: [ .....DEF.ctx4, b ] }
19
+ { mark:70, args: [ .....DEF.ctx5, a ], out: 'A' }
20
+ { mark:80, args: [ .....DEF.ctx5, b ], out: 'A', match: ctx:spec:alias:a:'b' }
21
+ ]
22
+