@toa.io/extensions.origins 0.10.0-dev.9 → 0.20.0-alpha.0

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 (96) hide show
  1. package/package.json +17 -11
  2. package/readme.md +70 -67
  3. package/schemas/annotation.cos.yaml +3 -0
  4. package/schemas/manifest.cos.yaml +4 -0
  5. package/source/Factory.ts +88 -0
  6. package/source/annotation.test.ts +150 -0
  7. package/source/annotation.ts +83 -0
  8. package/source/extension.test.ts +161 -0
  9. package/source/extension.ts +60 -0
  10. package/source/index.ts +2 -0
  11. package/source/manifest.test.ts +30 -0
  12. package/source/manifest.ts +11 -0
  13. package/source/protocols/amqp/.test/aspect.fixtures.js +1 -1
  14. package/source/protocols/amqp/.test/mock.comq.js +2 -2
  15. package/source/protocols/amqp/aspect.js +17 -24
  16. package/source/protocols/amqp/deployment.js +8 -2
  17. package/source/protocols/http/.aspect/permissions.js +20 -15
  18. package/source/protocols/http/aspect.js +18 -38
  19. package/source/protocols/index.ts +16 -0
  20. package/transpiled/Factory.d.ts +17 -0
  21. package/transpiled/Factory.js +65 -0
  22. package/transpiled/Factory.js.map +1 -0
  23. package/transpiled/annotation.d.ts +10 -0
  24. package/transpiled/annotation.js +91 -0
  25. package/transpiled/annotation.js.map +1 -0
  26. package/transpiled/constants.d.ts +1 -0
  27. package/transpiled/constants.js +3 -0
  28. package/transpiled/constants.js.map +1 -0
  29. package/transpiled/env.d.ts +5 -0
  30. package/transpiled/env.js +40 -0
  31. package/transpiled/env.js.map +1 -0
  32. package/transpiled/extension.d.ts +10 -0
  33. package/transpiled/extension.js +49 -0
  34. package/transpiled/extension.js.map +1 -0
  35. package/transpiled/index.d.ts +2 -0
  36. package/transpiled/index.js +9 -0
  37. package/transpiled/index.js.map +1 -0
  38. package/transpiled/manifest.d.ts +2 -0
  39. package/transpiled/manifest.js +36 -0
  40. package/transpiled/manifest.js.map +1 -0
  41. package/transpiled/protocols/amqp/aspect.d.ts +11 -0
  42. package/transpiled/protocols/amqp/aspect.js +53 -0
  43. package/transpiled/protocols/amqp/aspect.js.map +1 -0
  44. package/transpiled/protocols/amqp/deployment.d.ts +5 -0
  45. package/transpiled/protocols/amqp/deployment.js +55 -0
  46. package/transpiled/protocols/amqp/deployment.js.map +1 -0
  47. package/transpiled/protocols/amqp/id.d.ts +1 -0
  48. package/transpiled/protocols/amqp/id.js +3 -0
  49. package/transpiled/protocols/amqp/id.js.map +1 -0
  50. package/transpiled/protocols/amqp/index.d.ts +5 -0
  51. package/transpiled/protocols/amqp/index.js +10 -0
  52. package/transpiled/protocols/amqp/index.js.map +1 -0
  53. package/transpiled/protocols/amqp/protocols.d.ts +2 -0
  54. package/transpiled/protocols/amqp/protocols.js +3 -0
  55. package/transpiled/protocols/amqp/protocols.js.map +1 -0
  56. package/transpiled/protocols/http/.aspect/permissions.d.ts +7 -0
  57. package/transpiled/protocols/http/.aspect/permissions.js +52 -0
  58. package/transpiled/protocols/http/.aspect/permissions.js.map +1 -0
  59. package/transpiled/protocols/http/aspect.d.ts +11 -0
  60. package/transpiled/protocols/http/aspect.js +88 -0
  61. package/transpiled/protocols/http/aspect.js.map +1 -0
  62. package/transpiled/protocols/http/id.d.ts +1 -0
  63. package/transpiled/protocols/http/id.js +3 -0
  64. package/transpiled/protocols/http/id.js.map +1 -0
  65. package/transpiled/protocols/http/index.d.ts +4 -0
  66. package/transpiled/protocols/http/index.js +8 -0
  67. package/transpiled/protocols/http/index.js.map +1 -0
  68. package/transpiled/protocols/http/protocols.d.ts +2 -0
  69. package/transpiled/protocols/http/protocols.js +3 -0
  70. package/transpiled/protocols/http/protocols.js.map +1 -0
  71. package/transpiled/protocols/index.d.ts +9 -0
  72. package/transpiled/protocols/index.js +10 -0
  73. package/transpiled/protocols/index.js.map +1 -0
  74. package/transpiled/tsconfig.tsbuildinfo +1 -0
  75. package/tsconfig.json +12 -0
  76. package/source/.deployment/index.js +0 -5
  77. package/source/.deployment/uris.js +0 -37
  78. package/source/.test/constants.js +0 -3
  79. package/source/.test/deployment.fixtures.js +0 -20
  80. package/source/.test/factory.fixtures.js +0 -13
  81. package/source/deployment.js +0 -28
  82. package/source/deployment.test.js +0 -173
  83. package/source/factory.js +0 -46
  84. package/source/factory.test.js +0 -140
  85. package/source/index.js +0 -9
  86. package/source/manifest.js +0 -38
  87. package/source/manifest.test.js +0 -75
  88. package/source/protocols/amqp/aspect.test.js +0 -119
  89. package/source/protocols/http/aspect.test.js +0 -239
  90. package/source/protocols/index.js +0 -6
  91. package/source/schemas/annotations.cos.yaml +0 -1
  92. package/source/schemas/index.js +0 -8
  93. package/source/schemas/manifest.cos.yaml +0 -2
  94. package/types/amqp.d.ts +0 -9
  95. package/types/deployment.d.ts +0 -7
  96. package/types/http.d.ts +0 -28
@@ -0,0 +1,161 @@
1
+ import { generate } from 'randomstring'
2
+ import { encode } from 'msgpackr'
3
+ import { Locator } from '@toa.io/core'
4
+ import { deployment, type Instance } from './extension'
5
+ import type { Annotation, Properties } from './annotation'
6
+ import type { Manifest } from './manifest'
7
+ import type { Dependency } from '@toa.io/operations'
8
+
9
+ const locator = new Locator(generate(), generate())
10
+ const NAMESPACE = locator.namespace.toUpperCase()
11
+ const NAME = locator.name.toUpperCase()
12
+
13
+ it('should deploy pointer variables', async () => {
14
+ const manifest: Manifest = { queue: null }
15
+ const instance = { locator, manifest } as unknown as Instance
16
+ const url = 'amqp://host-' + generate()
17
+
18
+ const annotation: Annotation = {
19
+ [locator.id]: {
20
+ queue: url
21
+ }
22
+ }
23
+
24
+ const deploy = deployment([instance], annotation)
25
+
26
+ const expected: Dependency = {
27
+ variables: {
28
+ [locator.label]: expect.arrayContaining([
29
+ {
30
+ name: `TOA_ORIGINS_${NAMESPACE}_${NAME}_QUEUE`,
31
+ value: url
32
+ }
33
+ ])
34
+ }
35
+ }
36
+
37
+ expect(deploy)
38
+ .toMatchObject(expected)
39
+ })
40
+
41
+ it('should deploy default origin', async () => {
42
+ const example = 'http://api.example.com'
43
+ const manifest: Manifest = { example }
44
+ const instance = { locator, manifest } as unknown as Instance
45
+ const annotation: Annotation = {}
46
+ const deploy = deployment([instance], annotation)
47
+
48
+ const expected: Dependency = {
49
+ variables: {
50
+ [locator.label]: expect.arrayContaining([
51
+ {
52
+ name: `TOA_ORIGINS_${NAMESPACE}_${NAME}_EXAMPLE`,
53
+ value: example
54
+ }
55
+ ])
56
+ }
57
+ }
58
+
59
+ expect(deploy)
60
+ .toMatchObject(expected)
61
+ })
62
+
63
+ it('should deploy properties', async () => {
64
+ const manifest: Manifest = {}
65
+ const instance = { locator, manifest } as unknown as Instance
66
+ const properties: Properties = {
67
+ '.http': {
68
+ '/^http:\\/\\/\\w+api.example.com/': true
69
+ }
70
+ }
71
+
72
+ const annotation: Annotation = {
73
+ [locator.id]: properties
74
+ }
75
+
76
+ const deploy = deployment([instance], annotation)
77
+ const value = encode(properties).toString('base64')
78
+
79
+ const expected: Dependency = {
80
+ variables: {
81
+ [locator.label]: expect.arrayContaining([
82
+ {
83
+ name: `TOA_ORIGINS_${NAMESPACE}_${NAME}__PROPERTIES`,
84
+ value
85
+ }
86
+ ])
87
+ }
88
+ }
89
+
90
+ expect(deploy)
91
+ .toMatchObject(expected)
92
+ })
93
+
94
+ it('should deploy properties with null manifest', async () => {
95
+ const manifest: Manifest = null
96
+ const instance = { locator, manifest } as unknown as Instance
97
+ const properties: Properties = {
98
+ '.http': {
99
+ '/^http:\\/\\/\\w+api.example.com/': true
100
+ }
101
+ }
102
+
103
+ const annotation: Annotation = {
104
+ [locator.id]: properties
105
+ }
106
+
107
+ const deploy = deployment([instance], annotation)
108
+ const value = encode(properties).toString('base64')
109
+
110
+ const expected: Dependency = {
111
+ variables: {
112
+ [locator.label]: expect.arrayContaining([
113
+ {
114
+ name: `TOA_ORIGINS_${NAMESPACE}_${NAME}__PROPERTIES`,
115
+ value
116
+ }
117
+ ])
118
+ }
119
+ }
120
+
121
+ expect(deploy)
122
+ .toMatchObject(expected)
123
+ })
124
+
125
+ it('should deploy credentials for amqp', async () => {
126
+ const manifest: Manifest = { queue: null }
127
+ const instance = { locator, manifest } as unknown as Instance
128
+ const url = 'amqp://host-' + generate()
129
+
130
+ const annotation: Annotation = {
131
+ [locator.id]: {
132
+ queue: url
133
+ }
134
+ }
135
+
136
+ const deploy = deployment([instance], annotation)
137
+
138
+ const expected: Dependency = {
139
+ variables: {
140
+ [locator.label]: expect.arrayContaining([
141
+ {
142
+ name: `TOA_ORIGINS_${NAMESPACE}_${NAME}_QUEUE_USERNAME`,
143
+ secret: {
144
+ name: `toa-origins-${locator.label}-queue`,
145
+ key: 'username'
146
+ }
147
+ },
148
+ {
149
+ name: `TOA_ORIGINS_${NAMESPACE}_${NAME}_QUEUE_PASSWORD`,
150
+ secret: {
151
+ name: `toa-origins-${locator.label}-queue`,
152
+ key: 'password'
153
+ }
154
+ }
155
+ ])
156
+ }
157
+ }
158
+
159
+ expect(deploy)
160
+ .toMatchObject(expected)
161
+ })
@@ -0,0 +1,60 @@
1
+ import { encode } from 'msgpackr'
2
+ import { createVariables, type Request } from '@toa.io/pointer'
3
+ import { merge } from '@toa.io/generic'
4
+ import { normalize, split, type Annotation, type Properties, type Origins } from './annotation'
5
+ import { type Manifest, validate } from './manifest'
6
+ import type { Locator } from '@toa.io/core'
7
+ import type { Dependency, Variables } from '@toa.io/operations'
8
+ import type { context } from '@toa.io/norm'
9
+
10
+ export function deployment (instances: Instance[], annotation: Annotation = {}): Dependency {
11
+ normalize(instances, annotation)
12
+
13
+ const variables: Variables = {}
14
+
15
+ for (const instance of instances) {
16
+ const component = annotation[instance.locator.id]
17
+ const { origins, properties } = split(component)
18
+ const instanceVariables = createInstanceVariables(instance, origins)
19
+ const propertiesVariable = createPropertiesVariable(instance.locator, properties)
20
+
21
+ merge(variables, instanceVariables)
22
+ merge(variables, propertiesVariable)
23
+ }
24
+
25
+ return { variables }
26
+ }
27
+
28
+ export function manifest (manifest: Manifest): Manifest {
29
+ validate(manifest)
30
+
31
+ return manifest
32
+ }
33
+
34
+ function createInstanceVariables (instance: Instance, origins: Origins): Variables {
35
+ if (instance.manifest === null) return {}
36
+
37
+ const label: string = instance.locator.label
38
+ const id = ID_PREFIX + label
39
+ const selectors = Object.keys(instance.manifest)
40
+ const request: Request = { group: label, selectors }
41
+
42
+ return createVariables(id, origins, [request])
43
+ }
44
+
45
+ function createPropertiesVariable (locator: Locator, properties: Properties): Variables {
46
+ const name = ENV_PREFIX + locator.uppercase + PROPERTIES_SUFFIX
47
+ const value = encode(properties).toString('base64')
48
+
49
+ return {
50
+ [locator.label]: [
51
+ { name, value }
52
+ ]
53
+ }
54
+ }
55
+
56
+ export const ID_PREFIX = 'origins-'
57
+ export const ENV_PREFIX = 'TOA_ORIGINS_'
58
+ export const PROPERTIES_SUFFIX = '__PROPERTIES'
59
+
60
+ export type Instance = context.Dependency<Manifest>
@@ -0,0 +1,2 @@
1
+ export { deployment, manifest } from './extension'
2
+ export { Factory } from './Factory'
@@ -0,0 +1,30 @@
1
+ import { type Manifest, validate } from './manifest'
2
+
3
+ let manifest: Manifest
4
+
5
+ it('should not throw if valid', async () => {
6
+ manifest = {
7
+ one: 'http://localhost',
8
+ two: null
9
+ }
10
+
11
+ expect(run).not.toThrow()
12
+ })
13
+
14
+ it('should throw if not a uri', async () => {
15
+ manifest = {
16
+ one: 'not a URI'
17
+ }
18
+
19
+ expect(run).toThrow('must match format')
20
+ })
21
+
22
+ it('should not throw on null manifest', async () => {
23
+ manifest = null
24
+
25
+ expect(run).not.toThrow()
26
+ })
27
+
28
+ function run (): void {
29
+ validate(manifest)
30
+ }
@@ -0,0 +1,11 @@
1
+ import { resolve } from 'node:path'
2
+ import * as schemas from '@toa.io/schemas'
3
+
4
+ export function validate (manifest: Manifest): void {
5
+ if (manifest !== null) schema.validate(manifest)
6
+ }
7
+
8
+ const path = resolve(__dirname, '../schemas/manifest.cos.yaml')
9
+ const schema = schemas.schema(path)
10
+
11
+ export type Manifest = Record<string, string | null> | null
@@ -9,7 +9,7 @@ const originCount = random(5) + 2
9
9
  for (let j = 0; j < originCount; j++) {
10
10
  const origin = generate()
11
11
 
12
- manifest[origin] = 'amqp://' + generate()
12
+ manifest[origin] = ['amqp://' + generate()]
13
13
  }
14
14
 
15
15
  exports.manifest = manifest
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { generate } = require('randomstring')
4
4
 
5
- const connect = jest.fn(async () => ({
5
+ const assert = jest.fn(async () => ({
6
6
  emit: jest.fn(async () => undefined),
7
7
  request: jest.fn(async () => generate()),
8
8
  reply: jest.fn(async () => undefined),
@@ -10,4 +10,4 @@ const connect = jest.fn(async () => ({
10
10
  close: jest.fn(async () => undefined)
11
11
  }))
12
12
 
13
- exports.connect = connect
13
+ exports.assert = assert
@@ -1,33 +1,26 @@
1
1
  'use strict'
2
2
 
3
- const { connect } = require('comq')
3
+ const { assert } = require('comq')
4
4
  const { Connector } = require('@toa.io/core')
5
- const { shards } = require('@toa.io/generic')
5
+ const protocol = require('./index')
6
6
 
7
- const { id } = require('./id')
8
-
9
- /**
10
- * @implements {toa.origins.amqp.Aspect}
11
- */
12
7
  class Aspect extends Connector {
13
- name = id
14
- /** @type {toa.origins.Manifest} */
15
- #manifest
8
+ name = protocol.id
9
+
10
+ #resolve
16
11
 
17
12
  /** @type {Record<string, Partial<comq.IO>>} */
18
13
  #origins = {}
19
14
 
20
- /**
21
- * @param {toa.origins.Manifest} manifest
22
- */
23
- constructor (manifest) {
15
+ constructor (resolve) {
24
16
  super()
25
17
 
26
- this.#manifest = manifest
18
+ this.#resolve = resolve
27
19
  }
28
20
 
29
21
  async open () {
30
- const promises = Object.entries(this.#manifest).map(this.#open)
22
+ const cfg = await this.#resolve()
23
+ const promises = Object.entries(cfg.origins).map(this.#open)
31
24
 
32
25
  await Promise.all(promises)
33
26
  }
@@ -39,12 +32,15 @@ class Aspect extends Connector {
39
32
  }
40
33
 
41
34
  async invoke (origin, method, ...args) {
35
+ if (this.#origins[origin]?.[method] === undefined) {
36
+ throw new Error(`Origin "${origin}" or method "${method}" is undefined`)
37
+ }
38
+
42
39
  return this.#origins[origin][method](...args)
43
40
  }
44
41
 
45
- #open = async ([origin, reference]) => {
46
- const references = shards(reference)
47
- const io = await connect(...references)
42
+ #open = async ([origin, references]) => {
43
+ const io = await assert(...references)
48
44
 
49
45
  this.#origins[origin] = restrict(io)
50
46
  }
@@ -67,11 +63,8 @@ function restrict (io) {
67
63
  }
68
64
  }
69
65
 
70
- /**
71
- * @param {toa.origins.Manifest} manifest
72
- */
73
- function create (manifest) {
74
- return new Aspect(manifest)
66
+ function create (resolve) {
67
+ return new Aspect(resolve)
75
68
  }
76
69
 
77
70
  exports.create = create
@@ -15,9 +15,13 @@ function deployment (instances) {
15
15
  const secrets = []
16
16
 
17
17
  for (const [origin, reference] of Object.entries(manifest)) {
18
- const url = new URL(reference)
18
+ let protocol
19
19
 
20
- if (protocols.includes(url.protocol)) {
20
+ const match = reference.match(RX)
21
+
22
+ if (match !== null) protocol = match.groups.protocol
23
+
24
+ if (protocols.includes(protocol)) {
21
25
  const originSecrets = createSecrets(locator, origin)
22
26
 
23
27
  secrets.push(...originSecrets)
@@ -60,4 +64,6 @@ function createSecret (locator, origin, property) {
60
64
  }
61
65
  }
62
66
 
67
+ const RX = /^(?<protocol>\w{1,12}:)/
68
+
63
69
  exports.deployment = deployment
@@ -1,21 +1,26 @@
1
1
  'use strict'
2
2
 
3
- /**
4
- * @implements {toa.origins.http.Permissions}
5
- */
6
- class Permissions {
7
- #default = process.env.TOA_DEV === '1'
3
+ const { echo } = require('@toa.io/generic')
4
+ const { Connector } = require('@toa.io/core')
8
5
 
6
+ class Permissions extends Connector {
9
7
  /** @type {RegExp[]} */
10
8
  #allowances = []
11
9
 
12
10
  /** @type {RegExp[]} */
13
11
  #denials = []
14
12
 
15
- /**
16
- * @param {toa.origins.http.Properties} properties
17
- */
18
- constructor (properties) {
13
+ #resolve
14
+
15
+ constructor (resolve) {
16
+ super()
17
+
18
+ this.#resolve = resolve
19
+ }
20
+
21
+ async open () {
22
+ const { properties } = await this.#resolve()
23
+
19
24
  if (properties !== undefined) this.#parse(properties)
20
25
  }
21
26
 
@@ -26,9 +31,7 @@ class Permissions {
26
31
 
27
32
  const allowance = this.#allowances.findIndex((regexp) => regexp.test(url))
28
33
 
29
- if (allowance !== -1) return true
30
-
31
- return this.#default
34
+ return allowance !== -1
32
35
  }
33
36
 
34
37
  #parse (properties) {
@@ -44,7 +47,8 @@ class Permissions {
44
47
 
45
48
  if (match === null) throw new Error(`'${key}' is not a regular expression`)
46
49
 
47
- const regex = new RegExp(match.groups.expression)
50
+ const expression = echo(match.groups.expression)
51
+ const regex = new RegExp(expression)
48
52
 
49
53
  this.#addRule(regex, rule)
50
54
  }
@@ -55,8 +59,9 @@ class Permissions {
55
59
  * @param {boolean} rule
56
60
  */
57
61
  #addRule (regex, rule) {
58
- if (rule === true) this.#allowances.push(regex)
59
- else this.#denials.push(regex)
62
+ const rules = rule ? this.#allowances : this.#denials
63
+
64
+ rules.push(regex)
60
65
  }
61
66
  }
62
67
 
@@ -6,43 +6,38 @@ const { Connector } = require('@toa.io/core')
6
6
  const { retry } = require('@toa.io/generic')
7
7
 
8
8
  const { Permissions } = require('./.aspect/permissions')
9
- const { id } = require('./id')
10
9
  const protocols = require('./protocols')
10
+ const protocol = require('./index')
11
11
 
12
- /**
13
- * @implements {toa.origins.http.Aspect}
14
- */
15
12
  class Aspect extends Connector {
16
13
  /** @readonly */
17
- name = id
14
+ name = protocol.id
18
15
 
19
- /** @type {toa.origins.Manifest} */
16
+ #resolve
20
17
  #origins
21
-
22
- /** @type {toa.origins.http.Permissions} */
23
18
  #permissions
24
19
 
25
- /**
26
- * @param {toa.origins.Manifest} manifest
27
- * @param {toa.origins.http.Permissions} permissions
28
- */
29
- constructor (manifest, permissions) {
20
+ constructor (resolve, permissions) {
30
21
  super()
31
22
 
32
- this.#origins = manifest
23
+ this.#resolve = resolve
33
24
  this.#permissions = permissions
25
+
26
+ this.depends(permissions)
27
+ }
28
+
29
+ async open () {
30
+ const { origins } = await this.#resolve()
31
+
32
+ this.#origins = origins
34
33
  }
35
34
 
36
35
  async invoke (name, path, request, options) {
37
36
  let origin = this.#origins[name]
38
37
 
39
38
  if (origin === undefined) {
40
- if (isAbsoluteURL(/** @type {string} */ name)) {
41
- return this.#invokeURL(
42
- /** @type {string} */ name,
43
- /** @type {import('node-fetch').RequestInit} */ path
44
- )
45
- } else throw new Error(`Origin '${name}' is not defined`)
39
+ if (isAbsoluteURL(name)) return this.#invokeURL(name, /** @type {Request} */ path)
40
+ else throw new Error(`Origin '${name}' is not defined`)
46
41
  }
47
42
 
48
43
  // absolute urls are forbidden when using origins
@@ -55,23 +50,12 @@ class Aspect extends Connector {
55
50
  return this.#request(url.href, request, options?.retry)
56
51
  }
57
52
 
58
- /**
59
- * @param {string} url
60
- * @param {import('node-fetch').RequestInit} request
61
- * @return {Promise<void>}
62
- */
63
53
  async #invokeURL (url, request) {
64
54
  if (this.#permissions.test(url) === false) throw new Error(`URL '${url}' is not allowed`)
65
55
 
66
56
  return this.#request(url, request)
67
57
  }
68
58
 
69
- /**
70
- * @param {string} url
71
- * @param {import('node-fetch').RequestInit} request
72
- * @param {toa.generic.retry.Options} [options]
73
- * @return {Promise<import('node-fetch').Response>}
74
- */
75
59
  async #request (url, request, options) {
76
60
  const call = () => fetch(url, request)
77
61
 
@@ -116,14 +100,10 @@ function isAbsoluteURL (path) {
116
100
 
117
101
  const PLACEHOLDER = /\*/g
118
102
 
119
- /**
120
- * @param {toa.origins.Manifest} manifest
121
- * @param {toa.origins.http.Properties} [properties]
122
- */
123
- function create (manifest, properties) {
124
- const permissions = new Permissions(properties)
103
+ function create (resolve) {
104
+ const permissions = new Permissions(resolve)
125
105
 
126
- return new Aspect(manifest, permissions)
106
+ return new Aspect(resolve, permissions)
127
107
  }
128
108
 
129
109
  exports.create = create
@@ -0,0 +1,16 @@
1
+ 'use strict'
2
+
3
+ import { type Resolver } from '../Factory'
4
+ import amqp from './amqp'
5
+ import http from './http'
6
+ import type { extensions } from '@toa.io/core'
7
+
8
+ export const protocols: Protocol[] = [http, amqp]
9
+
10
+ export interface Protocol {
11
+ id: ProtocolID
12
+ protocols: string[]
13
+ create: (resolver: Resolver) => extensions.Aspect
14
+ }
15
+
16
+ export type ProtocolID = 'http' | 'amqp'
@@ -0,0 +1,17 @@
1
+ import { type URIMap } from '@toa.io/pointer';
2
+ import type { Locator, extensions } from '@toa.io/core';
3
+ import type { Manifest } from './manifest';
4
+ export declare class Factory implements extensions.Factory {
5
+ aspect(locator: Locator, manifest: Manifest): extensions.Aspect[];
6
+ private createAspect;
7
+ private resolver;
8
+ private getURIs;
9
+ private filterOrigins;
10
+ private readOrigin;
11
+ private getProperties;
12
+ }
13
+ export interface Configuration {
14
+ origins: URIMap;
15
+ properties: Record<string, boolean>;
16
+ }
17
+ export type Resolver = () => Promise<Configuration>;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Factory = void 0;
4
+ const msgpackr_1 = require("msgpackr");
5
+ const pointer_1 = require("@toa.io/pointer");
6
+ const generic_1 = require("@toa.io/generic");
7
+ const protocols_1 = require("./protocols");
8
+ const extension_1 = require("./extension");
9
+ class Factory {
10
+ aspect(locator, manifest) {
11
+ return protocols_1.protocols.map((protocol) => this.createAspect(locator, manifest, protocol));
12
+ }
13
+ createAspect(locator, manifest, protocol) {
14
+ const resolver = this.resolver(locator, manifest, protocol);
15
+ return protocol.create(resolver);
16
+ }
17
+ resolver(locator, manifest, protocol) {
18
+ return (0, generic_1.memo)(async () => {
19
+ const uris = await this.getURIs(locator, manifest);
20
+ const allProperties = this.getProperties(locator);
21
+ const origins = this.filterOrigins(uris, protocol.protocols);
22
+ const properties = allProperties['.' + protocol.id] ?? {};
23
+ return { origins, properties };
24
+ });
25
+ }
26
+ async getURIs(locator, manifest) {
27
+ const map = {};
28
+ if (manifest === null)
29
+ return map;
30
+ for (const [name, value] of Object.entries(manifest))
31
+ try {
32
+ map[name] = await this.readOrigin(locator, name);
33
+ }
34
+ catch {
35
+ // eslint-disable-next-line max-depth
36
+ if (value === null)
37
+ throw new Error(`Origin value ${name} is not defined`);
38
+ map[name] = [value];
39
+ }
40
+ return map;
41
+ }
42
+ filterOrigins(uris, protocols) {
43
+ const filtered = {};
44
+ for (const [name, references] of Object.entries(uris)) {
45
+ const url = new URL(references[0]);
46
+ if (protocols.includes(url.protocol))
47
+ filtered[name] = references;
48
+ }
49
+ return filtered;
50
+ }
51
+ async readOrigin(locator, name) {
52
+ const id = extension_1.ID_PREFIX + locator.label;
53
+ return await (0, pointer_1.resolve)(id, name);
54
+ }
55
+ getProperties(locator) {
56
+ const variable = extension_1.ENV_PREFIX + locator.uppercase + extension_1.PROPERTIES_SUFFIX;
57
+ const value = process.env[variable];
58
+ if (value === undefined)
59
+ return {};
60
+ const buffer = Buffer.from(value, 'base64');
61
+ return (0, msgpackr_1.decode)(buffer);
62
+ }
63
+ }
64
+ exports.Factory = Factory;
65
+ //# sourceMappingURL=Factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Instance } from './extension';
2
+ export declare function normalize(instances: Instance[], annotation: Annotation): void;
3
+ export declare function split(component: Component): {
4
+ origins: Origins;
5
+ properties: Properties;
6
+ };
7
+ export type Component = Origins | Properties;
8
+ export type Annotation = Record<string, Component>;
9
+ export type Properties = Partial<Record<'.http', Record<string, boolean>>>;
10
+ export type Origins = Record<string, string | string[]>;