@toa.io/extensions.exposition 1.0.0-alpha.272 → 1.0.0-alpha.274

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 (156) hide show
  1. package/CHANGELOG.md +211 -0
  2. package/components/exposition.atom/manifest.toa.yaml +3 -1
  3. package/components/exposition.octets/manifest.toa.yaml +28 -10
  4. package/components/exposition.octets/operations/put.js +20 -5
  5. package/components/identity.bans/manifest.toa.yaml +26 -7
  6. package/components/identity.bans/operations/tsconfig.tsbuildinfo +1 -1
  7. package/components/identity.basic/manifest.toa.yaml +106 -30
  8. package/components/identity.basic/operations/authenticate.js +7 -4
  9. package/components/identity.basic/operations/authenticate.js.map +1 -1
  10. package/components/identity.basic/operations/incept.js +3 -2
  11. package/components/identity.basic/operations/incept.js.map +1 -1
  12. package/components/identity.basic/operations/transit.js +17 -6
  13. package/components/identity.basic/operations/transit.js.map +1 -1
  14. package/components/identity.basic/operations/tsconfig.tsbuildinfo +1 -1
  15. package/components/identity.basic/operations/types.d.ts +2 -2
  16. package/components/identity.basic/source/authenticate.ts +8 -4
  17. package/components/identity.basic/source/incept.ts +3 -2
  18. package/components/identity.basic/source/transit.ts +20 -6
  19. package/components/identity.basic/source/types.ts +2 -2
  20. package/components/identity.credentials/manifest.toa.yaml +34 -13
  21. package/components/identity.credentials/operations/tsconfig.tsbuildinfo +1 -1
  22. package/components/identity.federation/manifest.toa.yaml +120 -28
  23. package/components/identity.federation/operations/authenticate.js +3 -2
  24. package/components/identity.federation/operations/authenticate.js.map +1 -1
  25. package/components/identity.federation/operations/create.js +4 -2
  26. package/components/identity.federation/operations/create.js.map +1 -1
  27. package/components/identity.federation/operations/lib/errors.d.ts +70 -11
  28. package/components/identity.federation/operations/lib/errors.js +40 -11
  29. package/components/identity.federation/operations/lib/errors.js.map +1 -1
  30. package/components/identity.federation/operations/lib/exchange.js +2 -2
  31. package/components/identity.federation/operations/lib/exchange.js.map +1 -1
  32. package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
  33. package/components/identity.federation/operations/types/configuration.d.ts +3 -2
  34. package/components/identity.federation/source/authenticate.ts +3 -2
  35. package/components/identity.federation/source/create.ts +4 -2
  36. package/components/identity.federation/source/lib/errors.ts +49 -12
  37. package/components/identity.federation/source/lib/exchange.ts +2 -2
  38. package/components/identity.federation/source/types/configuration.ts +4 -2
  39. package/components/identity.keys/manifest.toa.yaml +14 -6
  40. package/components/identity.keys/operations/tsconfig.tsbuildinfo +1 -1
  41. package/components/identity.otp/manifest.toa.yaml +44 -15
  42. package/components/identity.otp/operations/authenticate.js +9 -4
  43. package/components/identity.otp/operations/authenticate.js.map +1 -1
  44. package/components/identity.otp/operations/tsconfig.tsbuildinfo +1 -1
  45. package/components/identity.otp/source/authenticate.ts +11 -4
  46. package/components/identity.passkeys/manifest.toa.yaml +38 -8
  47. package/components/identity.passkeys/operations/authenticate.js +3 -2
  48. package/components/identity.passkeys/operations/authenticate.js.map +1 -1
  49. package/components/identity.passkeys/operations/create.js +6 -3
  50. package/components/identity.passkeys/operations/create.js.map +1 -1
  51. package/components/identity.passkeys/operations/tsconfig.tsbuildinfo +1 -1
  52. package/components/identity.passkeys/operations/use.js +6 -3
  53. package/components/identity.passkeys/operations/use.js.map +1 -1
  54. package/components/identity.passkeys/source/authenticate.ts +3 -2
  55. package/components/identity.passkeys/source/create.ts +7 -3
  56. package/components/identity.passkeys/source/use.ts +7 -3
  57. package/components/identity.roles/manifest.toa.yaml +41 -10
  58. package/components/identity.roles/operations/grant.js +3 -2
  59. package/components/identity.roles/operations/grant.js.map +1 -1
  60. package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
  61. package/components/identity.roles/source/grant.ts +3 -2
  62. package/components/identity.tokens/manifest.toa.yaml +99 -39
  63. package/components/identity.tokens/operations/authenticate.js +6 -3
  64. package/components/identity.tokens/operations/authenticate.js.map +1 -1
  65. package/components/identity.tokens/operations/decrypt.js +10 -5
  66. package/components/identity.tokens/operations/decrypt.js.map +1 -1
  67. package/components/identity.tokens/operations/encrypt.js +4 -3
  68. package/components/identity.tokens/operations/encrypt.js.map +1 -1
  69. package/components/identity.tokens/operations/lib/types.d.ts +3 -2
  70. package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
  71. package/components/identity.tokens/source/authenticate.test.ts +7 -2
  72. package/components/identity.tokens/source/authenticate.ts +7 -3
  73. package/components/identity.tokens/source/decrypt.test.ts +13 -7
  74. package/components/identity.tokens/source/decrypt.ts +12 -5
  75. package/components/identity.tokens/source/encrypt.test.ts +9 -4
  76. package/components/identity.tokens/source/encrypt.ts +4 -3
  77. package/components/identity.tokens/source/lib/types.ts +3 -2
  78. package/context.toa.yaml +2 -2
  79. package/documentation/components.md +1 -1
  80. package/documentation/identity.md +4 -1
  81. package/features/configuration.feature +207 -0
  82. package/features/cors.feature +1 -1
  83. package/features/dev.feature +1 -1
  84. package/features/identity.federation.feature +18 -9
  85. package/features/{identtiy.tokens.custom.feature → identity.tokens.custom.feature} +39 -0
  86. package/features/introspection.map.feature +16 -0
  87. package/features/map.feature +1 -1
  88. package/features/octets.download.feature +1 -1
  89. package/features/octets.meta.feature +1 -1
  90. package/features/passkeys.feature +8 -8
  91. package/features/require.feature +2 -2
  92. package/features/response.feature +3 -3
  93. package/features/steps/Components.ts +41 -4
  94. package/features/steps/Gateway.ts +61 -23
  95. package/features/steps/HTTP.ts +3 -5
  96. package/features/steps/Parameters.ts +2 -3
  97. package/features/steps/Realtime.ts +4 -4
  98. package/features/steps/Workspace.ts +8 -3
  99. package/features/steps/components/audit/manifest.toa.yaml +12 -5
  100. package/features/steps/components/echo/manifest.toa.yaml +33 -8
  101. package/features/steps/components/notify/manifest.toa.yaml +12 -5
  102. package/features/steps/components/octets.tester/manifest.toa.yaml +43 -14
  103. package/features/steps/components/orders/manifest.toa.yaml +22 -5
  104. package/features/steps/components/pots/manifest.toa.yaml +71 -17
  105. package/features/steps/components/pricing/manifest.toa.yaml +8 -2
  106. package/features/steps/components/sequences/manifest.toa.yaml +3 -1
  107. package/features/steps/components/users/manifest.toa.yaml +18 -3
  108. package/features/steps/components/users.properties/manifest.toa.yaml +12 -2
  109. package/package.json +5 -6
  110. package/schemas/annotation.cos.yaml +37 -17
  111. package/schemas/directive.cos.yaml +7 -3
  112. package/schemas/io/throttle.cos.yaml +47 -8
  113. package/schemas/method.cos.yaml +21 -9
  114. package/schemas/node.cos.yaml +22 -7
  115. package/schemas/octets/delete.cos.yaml +15 -2
  116. package/schemas/octets/get.cos.yaml +5 -2
  117. package/schemas/octets/put.cos.yaml +6 -3
  118. package/schemas/octets/workflow.cos.yaml +0 -1
  119. package/schemas/query.cos.yaml +31 -14
  120. package/schemas/querystring.cos.yaml +19 -9
  121. package/schemas/range.cos.yaml +10 -2
  122. package/schemas/route.cos.yaml +6 -2
  123. package/source/Branch.test.ts +37 -0
  124. package/source/Branch.ts +37 -0
  125. package/source/Factory.ts +5 -5
  126. package/source/Gateway.ts +20 -13
  127. package/source/HTTP/formats/yaml.test.ts +10 -2
  128. package/source/HTTP/formats/yaml.ts +21 -1
  129. package/source/RTD/Tree.ts +4 -1
  130. package/source/Tenant.ts +3 -3
  131. package/source/deployment.ts +3 -4
  132. package/source/directives/map/Headers.ts +8 -1
  133. package/source/directives/octets/Put.ts +6 -3
  134. package/source/root.ts +2 -2
  135. package/transpiled/Branch.d.ts +23 -0
  136. package/transpiled/Branch.js +16 -0
  137. package/transpiled/Branch.js.map +1 -1
  138. package/transpiled/Factory.js +4 -4
  139. package/transpiled/Factory.js.map +1 -1
  140. package/transpiled/Gateway.js +17 -7
  141. package/transpiled/Gateway.js.map +1 -1
  142. package/transpiled/HTTP/formats/yaml.js +15 -1
  143. package/transpiled/HTTP/formats/yaml.js.map +1 -1
  144. package/transpiled/RTD/Tree.js +4 -1
  145. package/transpiled/RTD/Tree.js.map +1 -1
  146. package/transpiled/Tenant.d.ts +1 -1
  147. package/transpiled/Tenant.js +1 -1
  148. package/transpiled/Tenant.js.map +1 -1
  149. package/transpiled/deployment.js +3 -4
  150. package/transpiled/deployment.js.map +1 -1
  151. package/transpiled/directives/map/Headers.js +7 -1
  152. package/transpiled/directives/map/Headers.js.map +1 -1
  153. package/transpiled/directives/octets/Put.js.map +1 -1
  154. package/transpiled/root.js +1 -1
  155. package/transpiled/root.js.map +1 -1
  156. package/transpiled/tsconfig.tsbuildinfo +1 -1
@@ -27,7 +27,7 @@ Feature: Request requirements
27
27
  Then the following reply is sent:
28
28
  """
29
29
  201 Created
30
- etag: ${{ etag }}
30
+ etag: "${{ etag }}"
31
31
 
32
32
  id: ${{ id }}
33
33
  """
@@ -53,7 +53,7 @@ Feature: Request requirements
53
53
  host: nex.toa.io
54
54
  content-type: application/yaml
55
55
  accept: text/plain
56
- if-match: ${{ etag }}
56
+ if-match: "${{ etag }}"
57
57
 
58
58
  title: Bye
59
59
  """
@@ -19,8 +19,8 @@ Feature: Response
19
19
  Then the following reply is sent:
20
20
  """
21
21
  200 OK
22
+ vary: origin, accept
22
23
  content-type: application/json
23
- vary: accept
24
24
 
25
25
  {"hello":"world"}
26
26
  """
@@ -33,8 +33,8 @@ Feature: Response
33
33
  Then the following reply is sent:
34
34
  """
35
35
  200 OK
36
+ vary: origin, accept
36
37
  content-type: application/yaml
37
- vary: accept
38
38
 
39
39
  hello: world
40
40
  """
@@ -48,8 +48,8 @@ Feature: Response
48
48
  Then the following reply is sent:
49
49
  """
50
50
  200 OK
51
+ vary: origin, accept
51
52
  content-type: application/json
52
- vary: accept
53
53
 
54
54
  {"hello":"world"}
55
55
  """
@@ -1,21 +1,26 @@
1
1
  import * as assert from 'node:assert'
2
+ import { dirname, join } from 'node:path'
2
3
  import { after, binding, given } from 'cucumber-tsflow'
3
4
  import * as boot from '@toa.io/boot'
4
5
  import { timeout } from '@toa.io/generic'
5
6
  import { type Connector } from '@toa.io/core'
6
- import { parse } from '@toa.io/yaml'
7
+ import { load as parse } from 'js-yaml'
8
+ import { Gateway } from './Gateway'
7
9
  import { Workspace } from './Workspace'
8
10
  import { components as map } from './map'
9
11
 
10
12
  const MAP = 'introspection'
13
+ const VALUES = 'configuration'
11
14
 
12
- @binding([Workspace])
15
+ @binding([Workspace, Gateway])
13
16
  export class Components {
14
17
  private readonly workspace: Workspace
18
+ private readonly gateway: Gateway
15
19
  private compositions: Record<string, Connector> = {}
16
20
 
17
- public constructor (workspace: Workspace) {
21
+ public constructor (workspace: Workspace, gateway: Gateway) {
18
22
  this.workspace = workspace
23
+ this.gateway = gateway
19
24
  }
20
25
 
21
26
  @given('the `{word}` is running')
@@ -25,7 +30,7 @@ export class Components {
25
30
 
26
31
  @given('the `{word}` is running with the following manifest:')
27
32
  public async patchAndRun (name: string, yaml: string): Promise<void> {
28
- const manifest = parse(yaml)
33
+ const manifest = parse(yaml) as object
29
34
 
30
35
  await this.runComponent(name, manifest)
31
36
  }
@@ -41,6 +46,25 @@ export class Components {
41
46
  await timeout(50) // discovery
42
47
  }
43
48
 
49
+ /** The values component, as the configuration extension ships it; the service hosts it alone. */
50
+ @given('the configuration values are running')
51
+ public async runValues (): Promise<void> {
52
+ assert.ok(!(VALUES in this.compositions), `Composition '${VALUES}' is already running`)
53
+
54
+ await this.gateway.start()
55
+
56
+ this.compositions[VALUES] = await boot.composition([values()])
57
+
58
+ await this.compositions[VALUES].connect()
59
+ await timeout(50) // discovery
60
+ }
61
+
62
+ /** What the deployment would tell the values service, as the scenario needs it. */
63
+ @given('the configuration values are deployed:')
64
+ public deployValues (yaml: string): void {
65
+ process.env.TOA_CONFIGURATION_VALUES = JSON.stringify(parse(yaml))
66
+ }
67
+
44
68
  @given('the `{word}` is stopped')
45
69
  public async stop (name: string): Promise<void> {
46
70
  assert.ok(name in this.compositions, `Composition '${name}' is not running`)
@@ -54,11 +78,18 @@ export class Components {
54
78
  const promises = Object.values(this.compositions).map((composition) => composition.disconnect())
55
79
 
56
80
  await Promise.all(promises)
81
+
82
+ delete process.env.TOA_CONFIGURATION_VALUES
57
83
  }
58
84
 
59
85
  private async runComponent (name: string, manifest?: object): Promise<void> {
60
86
  assert.ok(!(name in this.compositions), `Composition '${name}' is already running`)
61
87
 
88
+ // the gateway first: a component announces itself when it opens, and that only
89
+ // reaches a gateway that is already listening. Started the other way round, the
90
+ // component waits for a knock, which the request that follows does not.
91
+ await this.gateway.start()
92
+
62
93
  const path = await this.workspace.addComponent(name, manifest)
63
94
 
64
95
  this.compositions[name] = await boot.composition([path])
@@ -67,3 +98,9 @@ export class Components {
67
98
  await timeout(50) // discovery
68
99
  }
69
100
  }
101
+
102
+ function values (): string {
103
+ const root = dirname(require.resolve('@toa.io/extensions.configuration/package.json'))
104
+
105
+ return join(root, 'components', 'configuration.values')
106
+ }
@@ -1,8 +1,8 @@
1
1
  import { after, afterAll, binding, given } from 'cucumber-tsflow'
2
2
  import * as boot from '@toa.io/boot'
3
3
  import { type Connector } from '@toa.io/core'
4
- import { parse } from '@toa.io/yaml'
5
- import { encode, timeout } from '@toa.io/generic'
4
+ import { load as parse } from 'js-yaml'
5
+ import { timeout } from '@toa.io/generic'
6
6
  import { Factory } from '../../source'
7
7
  import * as syntax from '../../source/RTD/syntax'
8
8
  import { shortcuts } from '../../source/Directive'
@@ -14,15 +14,16 @@ let instance: Connector | null = null
14
14
  @binding()
15
15
  export class Gateway {
16
16
  private default: boolean = true
17
+ private written: string[] = []
17
18
 
18
19
  @given('the annotation:')
19
20
  public async annotate (yaml: string): Promise<void> {
20
- const annotation = parse(yaml)
21
+ const annotation = parse(yaml) as Partial<http.Options> & { '/'?: object }
21
22
 
22
- if ('/' in annotation) {
23
+ if (annotation['/'] !== undefined) {
23
24
  const tree = syntax.parse(annotation['/'], shortcuts)
24
25
 
25
- process.env.TOA_EXPOSITION = encode(tree)
26
+ process.env.TOA_EXPOSITION = JSON.stringify(tree)
26
27
  }
27
28
 
28
29
  const { debug, authorities } = annotation
@@ -34,7 +35,7 @@ export class Gateway {
34
35
  if (authorities !== undefined)
35
36
  properties.authorities = authorities
36
37
 
37
- process.env.TOA_EXPOSITION_PROPERTIES = encode(properties)
38
+ process.env.TOA_EXPOSITION_PROPERTIES = JSON.stringify(properties)
38
39
 
39
40
  await Gateway.stop()
40
41
 
@@ -47,7 +48,15 @@ export class Gateway {
47
48
  */
48
49
  @given('the annotation of the introspection map')
49
50
  public async annotateMap (): Promise<void> {
50
- const tree: syntax.Node = { routes: [], methods: [], directives: [] }
51
+ await this.annotateMapUnder()
52
+ }
53
+
54
+ /** The same routes under a root of the scenario's own: what an application declares around the map. */
55
+ @given('the annotation of the introspection map under:')
56
+ public async annotateMapUnder (yaml?: string): Promise<void> {
57
+ const tree: syntax.Node = yaml === undefined
58
+ ? { routes: [], methods: [], directives: [] }
59
+ : syntax.parse((parse(yaml) as { '/': object })['/'], shortcuts)
51
60
 
52
61
  for (const manifest of await manifests()) {
53
62
  const node = manifest.extensions?.[EXPOSITION] as syntax.Node | undefined
@@ -58,8 +67,8 @@ export class Gateway {
58
67
  tree.routes.push(...node.routes)
59
68
  }
60
69
 
61
- process.env.TOA_EXPOSITION = encode(tree)
62
- process.env.TOA_EXPOSITION_PROPERTIES = encode(DEFAULT_PROPERTIES)
70
+ process.env.TOA_EXPOSITION = JSON.stringify(tree)
71
+ process.env.TOA_EXPOSITION_PROPERTIES = JSON.stringify(DEFAULT_PROPERTIES)
63
72
 
64
73
  await Gateway.stop()
65
74
 
@@ -71,10 +80,25 @@ export class Gateway {
71
80
  const [name, namespace = 'default'] = id.split('.').reverse()
72
81
  const key = `TOA_CONFIGURATION_${namespace.toUpperCase()}_${name.toUpperCase()}`
73
82
  const def = DEFAULT_CONFIGURATION[id] ?? {}
74
- const patch: object = parse(yaml)
83
+ const patch = parse(yaml) as object
75
84
  const configuration = Object.assign({}, def, patch)
76
85
 
77
- process.env[key] = encode(configuration)
86
+ process.env[key] = JSON.stringify(configuration)
87
+
88
+ await Gateway.stop()
89
+
90
+ this.default = false
91
+ }
92
+
93
+ /** The secrets a scenario's configuration refers to. */
94
+ @given('the configuration secrets:')
95
+ public async secrets (yaml: string): Promise<void> {
96
+ const secrets = parse(yaml) as Record<string, string>
97
+
98
+ for (const [name, value] of Object.entries(secrets)) {
99
+ this.written.push(SECRET + name)
100
+ process.env[SECRET + name] = value
101
+ }
78
102
 
79
103
  await Gateway.stop()
80
104
 
@@ -96,7 +120,7 @@ export class Gateway {
96
120
  return
97
121
 
98
122
  process.env.TOA_EXPOSITION ??= DEFAULT_TREE
99
- process.env.TOA_EXPOSITION_PROPERTIES ??= encode(DEFAULT_PROPERTIES)
123
+ process.env.TOA_EXPOSITION_PROPERTIES ??= JSON.stringify(DEFAULT_PROPERTIES)
100
124
 
101
125
  this.writeConfiguration()
102
126
 
@@ -116,6 +140,11 @@ export class Gateway {
116
140
  public async cleanup (): Promise<void> {
117
141
  delete process.env.__TESTING_EXPOSITION_BRANCH_TTL
118
142
 
143
+ for (const key of this.written)
144
+ delete process.env[key]
145
+
146
+ this.written = []
147
+
119
148
  if (this.default)
120
149
  return
121
150
 
@@ -136,14 +165,18 @@ export class Gateway {
136
165
  const [name, namespace = 'default'] = id.split('.').reverse()
137
166
  const key = `TOA_CONFIGURATION_${namespace.toUpperCase()}_${name.toUpperCase()}`
138
167
 
139
- process.env[key] ??= encode(configuration)
168
+ process.env[key] ??= JSON.stringify(configuration)
140
169
  }
170
+
171
+ for (const [name, value] of Object.entries(DEFAULT_SECRETS))
172
+ process.env[SECRET + name] ??= value
141
173
  }
142
174
  }
143
175
 
144
176
  const EXPOSITION = '@toa.io/extensions.exposition'
177
+ const SECRET = 'TOA_CONFIGURATION__'
145
178
 
146
- const DEFAULT_TREE = encode({
179
+ const DEFAULT_TREE = JSON.stringify({
147
180
  routes: [],
148
181
  methods: [],
149
182
  directives: [
@@ -161,18 +194,23 @@ const DEFAULT_PROPERTIES: Partial<http.Options> = {
161
194
  }
162
195
  }
163
196
 
197
+ // the identity components boot inside the gateway, and without a variable each would wait
198
+ // for the values service, which these features do not run
164
199
  const DEFAULT_CONFIGURATION: Record<string, object> = {
200
+ 'identity.basic': {},
201
+ 'identity.federation': {},
202
+ 'identity.otp': {},
203
+ 'identity.passkeys': {},
165
204
  'identity.tokens': {
166
205
  keys: [
167
- {
168
- id: 'key0',
169
- key: 'sTxL6qVOadKkUJwh3FveU53XgTEo3Sdfg7k2FfiIKfs'
170
- },
171
- {
172
- id: 'legacy0',
173
- key: 'k3.local.pIZT8-9Fa6U_QtfQHOSStfGtmyzPINyKQq2Xk-hd7vA',
174
- format: 'paseto'
175
- }
206
+ { id: 'key0', key: '$IDENTITY_TOKENS_KEY0' },
207
+ { id: 'legacy0', key: '$IDENTITY_TOKENS_LEGACY0', format: 'paseto' }
176
208
  ]
177
209
  }
178
210
  }
211
+
212
+ // a secret is given as a reference, and comes to the component as an object
213
+ const DEFAULT_SECRETS: Record<string, string> = {
214
+ IDENTITY_TOKENS_KEY0: 'sTxL6qVOadKkUJwh3FveU53XgTEo3Sdfg7k2FfiIKfs',
215
+ IDENTITY_TOKENS_LEGACY0: 'k3.local.pIZT8-9Fa6U_QtfQHOSStfGtmyzPINyKQq2Xk-hd7vA'
216
+ }
@@ -13,7 +13,6 @@ import type { Readable } from 'node:stream'
13
13
  @binding([Gateway, Parameters, Captures])
14
14
  export class HTTP extends http.Agent {
15
15
  private readonly gateway: Gateway
16
- private fetched: Response | null = null
17
16
 
18
17
  public constructor (gateway: Gateway, parameters: Parameters, captures: Captures) {
19
18
  super(parameters.origin, captures)
@@ -24,7 +23,7 @@ export class HTTP extends http.Agent {
24
23
  public override async request (input: string): Promise<any> {
25
24
  await this.gateway.start()
26
25
 
27
- this.fetched = await super.request(input)
26
+ await super.request(input)
28
27
  }
29
28
 
30
29
  @then('the following reply is sent:')
@@ -34,10 +33,9 @@ export class HTTP extends http.Agent {
34
33
 
35
34
  @then('response body contains {word}-encoded value:')
36
35
  public async bodyIs (format: string, yaml: string): Promise<void> {
37
- assert.ok(this.fetched !== null, 'Response is null')
36
+ assert.ok(this.bytes !== null, 'Response body is not available')
38
37
 
39
- const buf = await this.fetched.arrayBuffer()
40
- const value = encoders[format]?.(buf as unknown as Buffer)
38
+ const value = encoders[format]?.(this.bytes)
41
39
  const expected = YAML.load(yaml)
42
40
 
43
41
  assert.deepEqual(value, expected, 'Values are not equal')
@@ -1,7 +1,6 @@
1
1
  import { join } from 'node:path'
2
2
  import * as dotenv from 'dotenv'
3
3
  import { setDefaultTimeout } from '@cucumber/cucumber'
4
- import { encode } from '@toa.io/generic'
5
4
 
6
5
  dotenv.config({ path: join(__dirname, '.env') })
7
6
 
@@ -19,14 +18,14 @@ process.env.TOA_DEV = '1'
19
18
 
20
19
  // export traces to the local Tempo (`docker compose up tempo grafana`),
21
20
  // unavailability of the endpoint is harmless
22
- process.env.TOA_TELEMETRY_TRACES ??= encode({
21
+ process.env.TOA_TELEMETRY_TRACES ??= JSON.stringify({
23
22
  exporters: {
24
23
  console: null,
25
24
  otlp: { endpoint: 'http://localhost:4318' }
26
25
  }
27
26
  })
28
27
 
29
- process.env.TOA_STORAGES = encode({
28
+ process.env.TOA_STORAGES = JSON.stringify({
30
29
  octets: {
31
30
  provider: 'tmp',
32
31
  directory: Math.random().toString(36).substring(2)
@@ -3,8 +3,8 @@ import * as assert from 'node:assert'
3
3
  import { after, binding, given, afterAll, then } from 'cucumber-tsflow'
4
4
  import { Factory } from '@toa.io/extensions.realtime'
5
5
  import * as boot from '@toa.io/boot'
6
- import { encode, match } from '@toa.io/generic'
7
- import { parse } from '@toa.io/yaml'
6
+ import { match } from '@toa.io/generic'
7
+ import { load as parse } from 'js-yaml'
8
8
  import { Agent } from '@toa.io/agent'
9
9
  import { Parameters } from './Parameters'
10
10
  import { Gateway } from './Gateway'
@@ -29,13 +29,13 @@ export class Realtime {
29
29
  public async start (yaml: string): Promise<void> {
30
30
  await Realtime.stop()
31
31
 
32
- const annotation = parse(yaml)
32
+ const annotation = parse(yaml) as Record<string, string>
33
33
  const routes = []
34
34
 
35
35
  for (const [event, property] of Object.entries(annotation))
36
36
  routes.push({ event, properties: [property] })
37
37
 
38
- process.env.TOA_REALTIME = encode(routes)
38
+ process.env.TOA_REALTIME = JSON.stringify(routes)
39
39
 
40
40
  const factory = new Factory(boot)
41
41
 
@@ -1,7 +1,8 @@
1
1
  import { join } from 'node:path'
2
2
  import { tmpdir, devNull } from 'node:os'
3
- import { mkdtemp, cp } from 'node:fs/promises'
4
- import * as yaml from '@toa.io/yaml'
3
+ import { mkdtemp, cp, readFile, writeFile } from 'node:fs/promises'
4
+ import { load, dump } from 'js-yaml'
5
+ import { overwrite } from '@toa.io/generic'
5
6
 
6
7
  export class Workspace {
7
8
  private root: string = devNull
@@ -35,6 +36,10 @@ export class Workspace {
35
36
  private async patchManifest (target: string, patch: object): Promise<void> {
36
37
  const path = join(target, 'manifest.toa.yaml')
37
38
 
38
- await yaml.patch(path, patch)
39
+ const manifest = load(await readFile(path, 'utf8')) as object
40
+
41
+ overwrite(manifest, patch)
42
+
43
+ await writeFile(path, dump(manifest, { noRefs: true, lineWidth: -1 }), 'utf8')
39
44
  }
40
45
  }
@@ -3,11 +3,18 @@ name: audit
3
3
  operations:
4
4
  record:
5
5
  input:
6
- id: string
7
- title: string
8
- volume: number
9
- price: number
10
- output: undefined
6
+ properties:
7
+ id:
8
+ type: string
9
+ title:
10
+ type: string
11
+ volume:
12
+ type: number
13
+ price:
14
+ type: number
15
+ type: object
16
+ output:
17
+ {}
11
18
 
12
19
  receivers:
13
20
  orders.created: record
@@ -3,24 +3,48 @@ name: echo
3
3
  operations:
4
4
  compute:
5
5
  input:
6
- name*: string
6
+ properties:
7
+ name:
8
+ type: string
9
+ type: object
10
+ required:
11
+ - name
7
12
  affect:
8
13
  input:
9
- name*: string
14
+ properties:
15
+ name:
16
+ type: string
17
+ type: object
18
+ required:
19
+ - name
10
20
  identity:
11
21
  input:
12
- identity:
13
- id: string
14
- roles: [string]
22
+ properties:
23
+ identity:
24
+ properties:
25
+ id:
26
+ type: string
27
+ roles:
28
+ type: array
29
+ items:
30
+ type: string
31
+ type: object
32
+ type: object
15
33
  parameters:
16
- input: &parameters
34
+ input:
35
+ type: object
36
+ properties:
37
+ a:
38
+ type: string
39
+ b:
40
+ type: string
41
+ output:
17
42
  type: object
18
43
  properties:
19
44
  a:
20
45
  type: string
21
46
  b:
22
47
  type: string
23
- output: *parameters
24
48
  echo:
25
49
  input:
26
50
  type: object
@@ -28,4 +52,5 @@ operations:
28
52
  input:
29
53
  type: object
30
54
  properties:
31
- buf: string
55
+ buf:
56
+ type: string
@@ -3,11 +3,18 @@ name: notify
3
3
  operations:
4
4
  send:
5
5
  input:
6
- id: string
7
- title: string
8
- volume: number
9
- price: number
10
- output: undefined
6
+ properties:
7
+ id:
8
+ type: string
9
+ title:
10
+ type: string
11
+ volume:
12
+ type: number
13
+ price:
14
+ type: number
15
+ type: object
16
+ output:
17
+ {}
11
18
 
12
19
  receivers:
13
20
  orders.created: send
@@ -6,13 +6,27 @@ storages: octets
6
6
  operations:
7
7
  foo: &operation
8
8
  input:
9
- authority*: string
10
- identity: string
11
- storage*: string
12
- path*: string
13
- entry*: object
14
- parameters: object
15
- steps: object
9
+ properties:
10
+ identity:
11
+ type: string
12
+ parameters:
13
+ type: object
14
+ steps:
15
+ type: object
16
+ authority:
17
+ type: string
18
+ storage:
19
+ type: string
20
+ path:
21
+ type: string
22
+ entry:
23
+ type: object
24
+ type: object
25
+ required:
26
+ - authority
27
+ - storage
28
+ - path
29
+ - entry
16
30
  bar: *operation
17
31
  baz: *operation
18
32
  err: *operation
@@ -25,18 +39,33 @@ operations:
25
39
  identity: *operation
26
40
  redirect:
27
41
  input:
28
- authority*: string
29
- path*: string
30
- parameters: <string>
42
+ properties:
43
+ parameters:
44
+ type: object
45
+ patternProperties:
46
+ ^.+$:
47
+ type: string
48
+ authority:
49
+ type: string
50
+ path:
51
+ type: string
52
+ type: object
53
+ required:
54
+ - authority
55
+ - path
31
56
  output:
32
57
  type: object
33
58
  properties:
34
- url: string
59
+ url:
60
+ type: string
35
61
  options:
36
62
  type: object
37
63
  properties:
38
- method: string
39
- headers: object
40
- body: string
64
+ method:
65
+ type: string
66
+ headers:
67
+ type: object
68
+ body:
69
+ type: string
41
70
  required:
42
71
  - url
@@ -2,14 +2,31 @@ name: orders
2
2
 
3
3
  entity:
4
4
  schema:
5
- title: string
6
- volume: number
7
- price?: number
5
+ properties:
6
+ title:
7
+ type: string
8
+ volume:
9
+ type: number
10
+ price:
11
+ type: number
12
+ type: object
13
+ required:
14
+ - title
15
+ - volume
8
16
 
9
17
  operations:
10
18
  create:
11
19
  concurrency: retry
12
20
  query: false
13
21
  input:
14
- title*: .
15
- volume*: .
22
+ properties:
23
+ title:
24
+ type: string
25
+ default: .
26
+ volume:
27
+ type: string
28
+ default: .
29
+ type: object
30
+ required:
31
+ - title
32
+ - volume