@toa.io/core 1.0.0-alpha.284 → 1.0.0-alpha.287

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 (290) hide show
  1. package/CHANGELOG.md +1 -185
  2. package/package.json +19 -7
  3. package/source/assignment.ts +27 -0
  4. package/{src/call.js → source/call.ts} +16 -21
  5. package/{src/cascade.js → source/cascade.ts} +6 -8
  6. package/{src/component.js → source/component.ts} +27 -25
  7. package/{src/composition.js → source/composition.ts} +9 -3
  8. package/{src/connector.js → source/connector.ts} +36 -55
  9. package/source/context.ts +67 -0
  10. package/source/contract/contract.ts +23 -0
  11. package/{src/contract/reply.js → source/contract/reply.ts} +26 -19
  12. package/source/contract/request.ts +85 -0
  13. package/source/contract/schemas.ts +39 -0
  14. package/{src/discovery.js → source/discovery.ts} +15 -8
  15. package/source/effect.ts +14 -0
  16. package/{src/emission.js → source/emission.ts} +5 -3
  17. package/source/entities/changeset.ts +37 -0
  18. package/source/entities/entity.ts +137 -0
  19. package/source/entities/factory.ts +52 -0
  20. package/source/entities/index.ts +4 -0
  21. package/{src/entities/newid.js → source/entities/newid.ts} +1 -1
  22. package/{src/entities/set.js → source/entities/set.ts} +8 -5
  23. package/source/event.ts +57 -0
  24. package/{src/exceptions.js → source/exceptions.ts} +52 -33
  25. package/source/exposition.ts +33 -0
  26. package/source/guard.ts +16 -0
  27. package/{src/index.js → source/index.ts} +8 -0
  28. package/{src/locator.js → source/locator.ts} +17 -30
  29. package/source/observation.ts +16 -0
  30. package/source/operation.ts +138 -0
  31. package/{src/outbox/outbox.js → source/outbox/outbox.ts} +87 -53
  32. package/source/query/criteria.ts +90 -0
  33. package/source/query/options.ts +35 -0
  34. package/{src/query.js → source/query.ts} +14 -17
  35. package/source/receiver.ts +120 -0
  36. package/source/reflection.ts +20 -0
  37. package/source/remote.ts +20 -0
  38. package/source/state.ts +179 -0
  39. package/source/transition.ts +79 -0
  40. package/{src/transmission.js → source/transmission.ts} +8 -8
  41. package/source/types/atomicity.ts +63 -0
  42. package/source/types/bindings.ts +59 -0
  43. package/source/types/bridges.ts +54 -0
  44. package/source/types/extensions.ts +89 -0
  45. package/{types → source/types}/index.ts +9 -16
  46. package/{types/message.d.ts → source/types/message.ts} +2 -1
  47. package/source/types/operations.ts +31 -0
  48. package/source/types/outbox.ts +35 -0
  49. package/source/types/receiver.ts +10 -0
  50. package/{types/request.d.ts → source/types/request.ts} +8 -9
  51. package/source/types/state.ts +9 -0
  52. package/source/types/storages.ts +129 -0
  53. package/source/unmanaged.ts +8 -0
  54. package/test/call.fixtures.js +3 -1
  55. package/test/call.test.js +28 -7
  56. package/test/component.test.js +10 -5
  57. package/test/connector.fixtures.js +7 -7
  58. package/test/connector.test.js +15 -2
  59. package/test/context.fixtures.js +1 -1
  60. package/test/context.test.js +4 -4
  61. package/test/contract/conditions.test.js +6 -2
  62. package/test/contract/contract.fixtures.js +3 -4
  63. package/test/contract/request.test.js +68 -34
  64. package/test/discovery.test.js +10 -3
  65. package/test/emission.test.js +16 -8
  66. package/test/entities/entity.fixtures.js +5 -6
  67. package/test/entities/entity.test.js +54 -20
  68. package/test/entities/factory.fixtures.js +11 -10
  69. package/test/entities/factory.test.js +47 -10
  70. package/test/entities/set.test.js +1 -1
  71. package/test/event.test.js +29 -11
  72. package/test/locator.test.js +10 -4
  73. package/test/outbox.test.js +49 -15
  74. package/test/query.test.js +69 -7
  75. package/test/receiver.fixtures.js +2 -2
  76. package/test/receiver.test.js +53 -27
  77. package/test/reflection.test.js +3 -3
  78. package/test/state.fixtures.js +10 -3
  79. package/test/state.test.js +77 -14
  80. package/test/transmission.test.js +25 -12
  81. package/transpiled/assignment.d.ts +6 -0
  82. package/transpiled/assignment.js +20 -0
  83. package/transpiled/assignment.js.map +1 -0
  84. package/transpiled/call.d.ts +10 -0
  85. package/transpiled/call.js +52 -0
  86. package/transpiled/call.js.map +1 -0
  87. package/transpiled/cascade.d.ts +7 -0
  88. package/transpiled/cascade.js +28 -0
  89. package/transpiled/cascade.js.map +1 -0
  90. package/transpiled/component.d.ts +15 -0
  91. package/transpiled/component.js +61 -0
  92. package/transpiled/component.js.map +1 -0
  93. package/transpiled/composition.d.ts +6 -0
  94. package/transpiled/composition.js +23 -0
  95. package/transpiled/composition.js.map +1 -0
  96. package/transpiled/connector.d.ts +47 -0
  97. package/transpiled/connector.js +143 -0
  98. package/transpiled/connector.js.map +1 -0
  99. package/transpiled/context.d.ts +18 -0
  100. package/transpiled/context.js +41 -0
  101. package/transpiled/context.js.map +1 -0
  102. package/transpiled/contract/contract.d.ts +10 -0
  103. package/transpiled/contract/contract.js +14 -0
  104. package/transpiled/contract/contract.js.map +1 -0
  105. package/transpiled/contract/index.d.ts +2 -0
  106. package/transpiled/contract/index.js +3 -0
  107. package/transpiled/contract/index.js.map +1 -0
  108. package/transpiled/contract/reply.d.ts +7 -0
  109. package/transpiled/contract/reply.js +48 -0
  110. package/transpiled/contract/reply.js.map +1 -0
  111. package/transpiled/contract/request.d.ts +24 -0
  112. package/transpiled/contract/request.js +59 -0
  113. package/transpiled/contract/request.js.map +1 -0
  114. package/transpiled/contract/schemas.d.ts +4 -0
  115. package/transpiled/contract/schemas.js +36 -0
  116. package/transpiled/contract/schemas.js.map +1 -0
  117. package/transpiled/discovery.d.ts +12 -0
  118. package/transpiled/discovery.js +34 -0
  119. package/transpiled/discovery.js.map +1 -0
  120. package/transpiled/effect.d.ts +5 -0
  121. package/transpiled/effect.js +11 -0
  122. package/transpiled/effect.js.map +1 -0
  123. package/transpiled/emission.d.ts +8 -0
  124. package/transpiled/emission.js +14 -0
  125. package/transpiled/emission.js.map +1 -0
  126. package/transpiled/entities/changeset.d.ts +10 -0
  127. package/transpiled/entities/changeset.js +27 -0
  128. package/transpiled/entities/changeset.js.map +1 -0
  129. package/transpiled/entities/entity.d.ts +19 -0
  130. package/transpiled/entities/entity.js +109 -0
  131. package/transpiled/entities/entity.js.map +1 -0
  132. package/transpiled/entities/factory.d.ts +22 -0
  133. package/transpiled/entities/factory.js +34 -0
  134. package/transpiled/entities/factory.js.map +1 -0
  135. package/transpiled/entities/index.d.ts +4 -0
  136. package/transpiled/entities/index.js +5 -0
  137. package/transpiled/entities/index.js.map +1 -0
  138. package/transpiled/entities/newid.d.ts +1 -0
  139. package/transpiled/entities/newid.js +7 -0
  140. package/transpiled/entities/newid.js.map +1 -0
  141. package/transpiled/entities/set.d.ts +10 -0
  142. package/transpiled/entities/set.js +19 -0
  143. package/transpiled/entities/set.js.map +1 -0
  144. package/transpiled/event.d.ts +14 -0
  145. package/transpiled/event.js +38 -0
  146. package/transpiled/event.js.map +1 -0
  147. package/transpiled/exceptions.d.ts +61 -0
  148. package/transpiled/exceptions.js +93 -0
  149. package/transpiled/exceptions.js.map +1 -0
  150. package/transpiled/exposition.d.ts +18 -0
  151. package/transpiled/exposition.js +18 -0
  152. package/transpiled/exposition.js.map +1 -0
  153. package/transpiled/guard.d.ts +7 -0
  154. package/transpiled/guard.js +12 -0
  155. package/transpiled/guard.js.map +1 -0
  156. package/transpiled/index.d.ts +35 -0
  157. package/transpiled/index.js +29 -0
  158. package/transpiled/index.js.map +1 -0
  159. package/transpiled/locator.d.ts +12 -0
  160. package/transpiled/locator.js +32 -0
  161. package/transpiled/locator.js.map +1 -0
  162. package/transpiled/observation.d.ts +5 -0
  163. package/transpiled/observation.js +12 -0
  164. package/transpiled/observation.js.map +1 -0
  165. package/transpiled/operation.d.ts +49 -0
  166. package/transpiled/operation.js +77 -0
  167. package/transpiled/operation.js.map +1 -0
  168. package/transpiled/outbox/index.d.ts +1 -0
  169. package/transpiled/outbox/index.js +2 -0
  170. package/transpiled/outbox/index.js.map +1 -0
  171. package/transpiled/outbox/outbox.d.ts +46 -0
  172. package/transpiled/outbox/outbox.js +267 -0
  173. package/transpiled/outbox/outbox.js.map +1 -0
  174. package/transpiled/query/criteria.d.ts +6 -0
  175. package/transpiled/query/criteria.js +74 -0
  176. package/transpiled/query/criteria.js.map +1 -0
  177. package/transpiled/query/options.d.ts +3 -0
  178. package/transpiled/query/options.js +27 -0
  179. package/transpiled/query/options.js.map +1 -0
  180. package/transpiled/query.d.ts +9 -0
  181. package/transpiled/query.js +57 -0
  182. package/transpiled/query.js.map +1 -0
  183. package/transpiled/receiver.d.ts +20 -0
  184. package/transpiled/receiver.js +92 -0
  185. package/transpiled/receiver.js.map +1 -0
  186. package/transpiled/reflection.d.ts +9 -0
  187. package/transpiled/reflection.js +13 -0
  188. package/transpiled/reflection.js.map +1 -0
  189. package/transpiled/remote.d.ts +10 -0
  190. package/transpiled/remote.js +12 -0
  191. package/transpiled/remote.js.map +1 -0
  192. package/transpiled/state.d.ts +33 -0
  193. package/transpiled/state.js +126 -0
  194. package/transpiled/state.js.map +1 -0
  195. package/transpiled/transition.d.ts +14 -0
  196. package/transpiled/transition.js +51 -0
  197. package/transpiled/transition.js.map +1 -0
  198. package/transpiled/transmission.d.ts +8 -0
  199. package/transpiled/transmission.js +30 -0
  200. package/transpiled/transmission.js.map +1 -0
  201. package/transpiled/types/atomicity.d.ts +56 -0
  202. package/transpiled/types/atomicity.js +2 -0
  203. package/transpiled/types/atomicity.js.map +1 -0
  204. package/transpiled/types/bindings.d.ts +42 -0
  205. package/transpiled/types/bindings.js +2 -0
  206. package/transpiled/types/bindings.js.map +1 -0
  207. package/transpiled/types/bridges.d.ts +38 -0
  208. package/transpiled/types/bridges.js +2 -0
  209. package/transpiled/types/bridges.js.map +1 -0
  210. package/transpiled/types/extensions.d.ts +62 -0
  211. package/transpiled/types/extensions.js +2 -0
  212. package/transpiled/types/extensions.js.map +1 -0
  213. package/transpiled/types/index.d.ts +13 -0
  214. package/transpiled/types/index.js +8 -0
  215. package/transpiled/types/index.js.map +1 -0
  216. package/transpiled/types/message.d.ts +5 -0
  217. package/transpiled/types/message.js +2 -0
  218. package/transpiled/types/message.js.map +1 -0
  219. package/transpiled/types/operations.d.ts +15 -0
  220. package/transpiled/types/operations.js +2 -0
  221. package/transpiled/types/operations.js.map +1 -0
  222. package/transpiled/types/outbox.d.ts +28 -0
  223. package/transpiled/types/outbox.js +2 -0
  224. package/transpiled/types/outbox.js.map +1 -0
  225. package/transpiled/types/receiver.d.ts +9 -0
  226. package/transpiled/types/receiver.js +2 -0
  227. package/transpiled/types/receiver.js.map +1 -0
  228. package/transpiled/types/request.d.ts +55 -0
  229. package/transpiled/types/request.js +2 -0
  230. package/transpiled/types/request.js.map +1 -0
  231. package/transpiled/types/state.d.ts +9 -0
  232. package/transpiled/types/state.js +2 -0
  233. package/transpiled/types/state.js.map +1 -0
  234. package/transpiled/types/storages.d.ts +106 -0
  235. package/transpiled/types/storages.js +2 -0
  236. package/transpiled/types/storages.js.map +1 -0
  237. package/transpiled/unmanaged.d.ts +5 -0
  238. package/transpiled/unmanaged.js +7 -0
  239. package/transpiled/unmanaged.js.map +1 -0
  240. package/tsconfig.json +10 -0
  241. package/tsconfig.tsbuildinfo +1 -0
  242. package/src/assignment.js +0 -28
  243. package/src/context.js +0 -57
  244. package/src/contract/contract.js +0 -18
  245. package/src/contract/request.js +0 -77
  246. package/src/contract/schemas/error.yaml +0 -9
  247. package/src/contract/schemas/index.js +0 -11
  248. package/src/contract/schemas/query.yaml +0 -43
  249. package/src/contract/schemas/source.yaml +0 -23
  250. package/src/effect.js +0 -15
  251. package/src/entities/changeset.js +0 -36
  252. package/src/entities/entity.js +0 -119
  253. package/src/entities/factory.js +0 -40
  254. package/src/entities/index.js +0 -1
  255. package/src/event.js +0 -54
  256. package/src/exposition.js +0 -24
  257. package/src/guard.js +0 -13
  258. package/src/observation.js +0 -8
  259. package/src/operation.js +0 -95
  260. package/src/query/criteria.js +0 -44
  261. package/src/query/options.js +0 -37
  262. package/src/receiver.js +0 -123
  263. package/src/reflection.js +0 -24
  264. package/src/remote.js +0 -14
  265. package/src/state.js +0 -138
  266. package/src/transition.js +0 -61
  267. package/src/unmanaged.js +0 -7
  268. package/types/atomicity.d.ts +0 -79
  269. package/types/bindings.d.ts +0 -42
  270. package/types/bridges.ts +0 -33
  271. package/types/call.d.ts +0 -13
  272. package/types/component.d.ts +0 -12
  273. package/types/connector.d.ts +0 -18
  274. package/types/context.d.ts +0 -24
  275. package/types/entity.d.ts +0 -46
  276. package/types/event.d.ts +0 -11
  277. package/types/exception.d.ts +0 -4
  278. package/types/extensions.d.ts +0 -73
  279. package/types/guard.d.ts +0 -5
  280. package/types/locator.d.ts +0 -13
  281. package/types/operations.d.ts +0 -8
  282. package/types/outbox.d.ts +0 -53
  283. package/types/query.d.ts +0 -17
  284. package/types/receiver.d.ts +0 -12
  285. package/types/reflection.d.ts +0 -14
  286. package/types/remote.d.ts +0 -19
  287. package/types/state.d.ts +0 -38
  288. package/types/storages.d.ts +0 -96
  289. /package/{src/contract/index.js → source/contract/index.ts} +0 -0
  290. /package/{src/outbox/index.js → source/outbox/index.ts} +0 -0
@@ -0,0 +1,57 @@
1
+ import { console, current, encode, type SpanOptions } from 'openspan'
2
+ import { Connector } from './connector.js'
3
+ import type { Emitter } from './types/bindings.js'
4
+ import type { Event as Bridge } from './types/bridges.js'
5
+ import type { Message } from './types/message.js'
6
+ import type { Event as StateEvent } from './types/state.js'
7
+
8
+ export interface Definition {
9
+ conditioned?: boolean
10
+ subjective?: boolean
11
+ label?: string
12
+ }
13
+
14
+ export class Event extends Connector {
15
+ readonly #emitter: Emitter
16
+ readonly #bridge: Bridge | undefined
17
+ readonly #conditioned: boolean | undefined
18
+ readonly #subjective: boolean | undefined
19
+ readonly #label: string
20
+
21
+ public constructor(definition: Definition, emitter: Emitter, bridge?: Bridge) {
22
+ super()
23
+
24
+ this.#conditioned = definition.conditioned
25
+ this.#subjective = definition.subjective
26
+ this.#label = definition.label ?? 'event'
27
+ this.#emitter = emitter
28
+ this.#bridge = bridge
29
+
30
+ this.depends(emitter)
31
+
32
+ if (bridge !== undefined) this.depends(bridge)
33
+ }
34
+
35
+ public async emit(event: StateEvent): Promise<void> {
36
+ if (this.#conditioned === false || (await this.#bridge?.condition(event)) === true) {
37
+ const payload =
38
+ this.#subjective === true ? await this.#bridge?.payload(event) : event.state
39
+
40
+ const message: Message = { payload }
41
+
42
+ const options: SpanOptions = {
43
+ name: `${this.#label} publish`,
44
+ kind: 'producer',
45
+ attributes: { 'messaging.destination.name': this.#label }
46
+ }
47
+
48
+ await console.span(options, async () => {
49
+ const context = current()
50
+
51
+ if (context !== undefined) message.telemetry = encode(context)
52
+
53
+ await this.#emitter.emit(message)
54
+ })
55
+ }
56
+ }
57
+ }
@@ -1,4 +1,5 @@
1
1
  import { swap } from '@toa.io/generic'
2
+ import type { SchemaError } from '@toa.io/schemas'
2
3
 
3
4
  export const codes = {
4
5
  System: 0,
@@ -23,79 +24,97 @@ export const codes = {
23
24
  Transmission: 401
24
25
  }
25
26
 
26
- /**
27
- * @implements {toa.core.Exception}
28
- */
29
27
  export class Exception {
30
- code
31
- message
28
+ public readonly code: number
29
+ public readonly message: string
30
+ public cause?: unknown;
32
31
 
33
- constructor (code, message, cause) {
32
+ /** what a contract exception copies off the schema error it refused with */
33
+ [key: string]: unknown
34
+
35
+ public constructor(code: number, message: string, cause?: unknown) {
34
36
  this.code = code
35
37
  this.message = message
36
38
 
37
- if (cause !== undefined)
38
- this.cause = cause
39
+ if (cause !== undefined) this.cause = cause
39
40
  }
40
41
  }
41
42
 
42
43
  export class SystemException extends Exception {
43
- stack
44
+ public readonly stack?: string
44
45
 
45
- constructor (error) {
46
- super(codes.System, error.message)
46
+ public constructor(error: Error | string) {
47
+ super(codes.System, typeof error === 'string' ? error : error.message)
47
48
 
48
- if (error.stack !== undefined) this.stack = error.stack
49
+ if (typeof error !== 'string' && error.stack !== undefined) this.stack = error.stack
49
50
  }
50
51
  }
51
52
 
52
53
  export class ContractException extends Exception {
53
- constructor (code, error, cause) {
54
- super(code || codes.Contract, typeof error === 'string' ? error : error?.message, cause)
54
+ public constructor(
55
+ code: number | undefined,
56
+ error: SchemaError | string | null,
57
+ cause?: unknown
58
+ ) {
59
+ super(
60
+ code ?? codes.Contract,
61
+ typeof error === 'string' ? error : (error?.message ?? ''),
62
+ cause
63
+ )
55
64
 
56
65
  if (typeof error === 'object' && error !== null)
57
- for (const k of ['keyword', 'property', 'schema', 'path', 'params'])
58
- if (k in error)
59
- this[k] = error[k]
66
+ for (const k of ['keyword', 'property', 'schema', 'path', 'params'] as const)
67
+ if (k in error) this[k] = (error as unknown as Record<string, unknown>)[k]
60
68
  }
61
69
  }
62
70
 
63
71
  export class RequestContractException extends ContractException {
64
- constructor (error, cause) { super(codes.RequestContract, error, cause) }
72
+ public constructor(error: SchemaError | string, cause?: unknown) {
73
+ super(codes.RequestContract, error, cause)
74
+ }
65
75
  }
66
76
 
67
77
  export class ResponseContractException extends ContractException {
68
- constructor (error, cause) { super(codes.ResponseContract, error, cause) }
78
+ public constructor(error: SchemaError | string, cause?: unknown) {
79
+ super(codes.ResponseContract, error, cause)
80
+ }
69
81
  }
70
82
 
71
83
  export class EntityContractException extends ContractException {
72
- constructor (error, cause) { super(codes.EntityContract, error, cause) }
84
+ public constructor(error: SchemaError | string, cause?: unknown) {
85
+ super(codes.EntityContract, error, cause)
86
+ }
73
87
  }
74
88
 
75
89
  export class EntityGuardException extends ContractException {
76
- constructor (name, cause) { super(codes.EntityGuard, name, cause) }
90
+ public constructor(name: string, cause?: unknown) {
91
+ super(codes.EntityGuard, name, cause)
92
+ }
77
93
  }
78
94
 
79
95
  // #region exports
80
96
 
81
-
82
-
83
-
84
-
85
-
86
-
87
97
  // a module's exports are static, so the ones that follow a code are named rather
88
98
  // than generated onto the namespace
89
- function derive (name) {
99
+ type Derived = new (message?: string, cause?: unknown) => Exception
100
+
101
+ function derive(name: keyof typeof codes): Derived {
90
102
  const classname = name + 'Exception'
91
103
 
92
- return class extends Exception {
93
- constructor (message, cause) {
94
- super(codes[name], message ? `${classname}: ${message}` : classname, cause)
104
+ const derived = class extends Exception {
105
+ public constructor(message?: string, cause?: unknown) {
106
+ super(
107
+ codes[name],
108
+ message === undefined ? classname : `${classname}: ${message}`,
109
+ cause
110
+ )
95
111
  }
96
-
97
- static name = classname
98
112
  }
113
+
114
+ // the name is the class's own, and a class expression has none worth reporting
115
+ Object.defineProperty(derived, 'name', { value: classname })
116
+
117
+ return derived
99
118
  }
100
119
 
101
120
  export const RequestSyntaxException = derive('RequestSyntax')
@@ -0,0 +1,33 @@
1
+ import { Connector } from './connector.js'
2
+ import type { Locator } from './locator.js'
3
+
4
+ interface Manifest {
5
+ namespace: string
6
+ name: string
7
+ entity?: object
8
+ operations?: object
9
+ events?: object
10
+ }
11
+
12
+ export class Exposition extends Connector {
13
+ public readonly locator: Locator
14
+
15
+ readonly #exposition: Partial<Manifest>
16
+
17
+ public constructor(locator: Locator, manifest: Manifest) {
18
+ super()
19
+
20
+ this.locator = locator
21
+ this.#exposition = expose(manifest)
22
+ }
23
+
24
+ public async invoke(): Promise<{ output: Partial<Manifest> }> {
25
+ return { output: this.#exposition }
26
+ }
27
+ }
28
+
29
+ function expose(manifest: Manifest): Partial<Manifest> {
30
+ const { namespace, name, entity, operations, events } = manifest
31
+
32
+ return { namespace, name, entity, operations, events }
33
+ }
@@ -0,0 +1,16 @@
1
+ import type { Guard as Bridge } from './types/bridges.js'
2
+
3
+ export class Guard {
4
+ public readonly name: string
5
+
6
+ readonly #bridge: Bridge
7
+
8
+ public constructor(name: string, bridge: Bridge) {
9
+ this.name = name
10
+ this.#bridge = bridge
11
+ }
12
+
13
+ public fit(state: object, origin: object | null): boolean {
14
+ return this.#bridge.fit(state, origin)
15
+ }
16
+ }
@@ -26,3 +26,11 @@ export { Guard } from './guard.js'
26
26
  export * as entities from './entities/index.js'
27
27
  export * as exceptions from './exceptions.js'
28
28
  export * as contract from './contract/index.js'
29
+
30
+ export type { Exception } from './exceptions.js'
31
+ export type { Invocable } from './component.js'
32
+ export type { Explicable } from './remote.js'
33
+ export type { Scoped, Store } from './operation.js'
34
+ export type { Source as ReflectionSource } from './reflection.js'
35
+ export type { Explanation } from './contract/request.js'
36
+ export type { JSONSchema } from './contract/schemas.js'
@@ -1,22 +1,16 @@
1
1
  import { concat } from '@toa.io/generic'
2
2
 
3
- /**
4
- * @implements {toa.core.Locator}
5
- */
3
+ /** Where a component is, as every name derived from it. */
6
4
  export class Locator {
7
- name
8
- namespace
9
-
10
- id
11
- label
12
- uppercase
13
- lowercase
14
-
15
- /**
16
- * @param {string} name
17
- * @param {string} [namespace]
18
- */
19
- constructor (name, namespace) {
5
+ public readonly name: string
6
+ public readonly namespace: string | undefined
7
+
8
+ public readonly id: string
9
+ public readonly label: string
10
+ public readonly uppercase: string
11
+ public readonly lowercase: string
12
+
13
+ public constructor(name: string, namespace?: string) {
20
14
  if (name === undefined) throw new TypeError('Locator name must be defined')
21
15
 
22
16
  this.name = name
@@ -28,22 +22,15 @@ export class Locator {
28
22
  this.lowercase = (concat(namespace, '_') + name).toLowerCase()
29
23
  }
30
24
 
31
- hostname (prefix) {
32
- return concat(prefix?.toLowerCase(), '-') + this.label
33
- }
34
-
35
- /**
36
- * @param {string} string
37
- * @returns {Locator}
38
- */
39
- static parse (string) {
25
+ public static parse(string: string): Locator {
40
26
  const [namespace, name] = string.split(DOT)
41
27
 
42
- if (name === undefined) {
43
- return new Locator(namespace)
44
- } else {
45
- return new Locator(name, namespace)
46
- }
28
+ if (name === undefined) return new Locator(namespace)
29
+ else return new Locator(name, namespace)
30
+ }
31
+
32
+ public hostname(prefix?: string): string {
33
+ return concat(prefix?.toLowerCase(), '-') + this.label
47
34
  }
48
35
  }
49
36
 
@@ -0,0 +1,16 @@
1
+ import { Operation } from './operation.js'
2
+ import type { Store } from './operation.js'
3
+ import type { Entity } from './entities/entity.js'
4
+
5
+ export class Observation extends Operation {
6
+ protected override async run(store: Store): Promise<void> {
7
+ const scope = store.scope as Entity | null
8
+
9
+ if (
10
+ scope === null ||
11
+ (scope?.deleted === true && (store.request.query as any)?.options?.deleted !== true)
12
+ )
13
+ store.reply = null
14
+ else await super.run(store)
15
+ }
16
+ }
@@ -0,0 +1,138 @@
1
+ import { Readable } from 'node:stream'
2
+ import { Connector } from './connector.js'
3
+ import { SystemException, RequestContractException } from './exceptions.js'
4
+ import type { Cascade } from './cascade.js'
5
+ import type { State } from './state.js'
6
+ import type { Query as Translator } from './query.js'
7
+ import type { Contract } from './contract/contract.js'
8
+ import type { Entity } from './entities/entity.js'
9
+ import type { EntitySet } from './entities/set.js'
10
+ import type { Changeset } from './entities/changeset.js'
11
+ import type { scope as Scope } from './types/operations.js'
12
+ import type { Query, Request } from './types/request.js'
13
+
14
+ /** What an operation acquires for the algorithm to run against. */
15
+ export type Scoped = Entity | EntitySet | Changeset | Readable | null
16
+
17
+ /** What one invocation carries from step to step. */
18
+ export interface Store {
19
+ request: Request
20
+ scope?: Scoped
21
+ state?: any
22
+ reply?: any
23
+ /** set by a transition, which is the only operation that runs its steps again */
24
+ retry?: () => Promise<any>
25
+ }
26
+
27
+ export interface Contracts {
28
+ request: Contract
29
+ reply: Contract
30
+ }
31
+
32
+ export interface Definition {
33
+ scope: Scope
34
+ concurrency?: string
35
+ }
36
+
37
+ export class Operation extends Connector {
38
+ public scope: State
39
+
40
+ /**
41
+ * Whether what this operation acquires may be modified and committed. Only a
42
+ * transition commits, and only a commit needs the pre-image an entity keeps
43
+ * to diff the new state against.
44
+ *
45
+ * @protected
46
+ */
47
+ protected mutable: boolean = false
48
+
49
+ readonly #cascade: Cascade
50
+ readonly #contracts: Contracts
51
+ readonly #query: Translator
52
+ readonly #scope: Scope
53
+
54
+ // eslint-disable-next-line max-params
55
+ public constructor(
56
+ cascade: Cascade,
57
+ scope: State,
58
+ contracts: Contracts,
59
+ query: Translator,
60
+ definition: Definition
61
+ ) {
62
+ super()
63
+
64
+ this.scope = scope
65
+
66
+ this.#cascade = cascade
67
+ this.#contracts = contracts
68
+ this.#query = query
69
+ this.#scope = definition.scope
70
+
71
+ this.depends(cascade)
72
+ }
73
+
74
+ public async invoke(request: Request): Promise<any> {
75
+ try {
76
+ if (request.authentic !== true) this.#contracts.request.fit(request)
77
+
78
+ // the request carries the query onward in its parsed form: what a storage is given,
79
+ // not what the caller sent
80
+ if ('query' in request)
81
+ request.query = this.#query.parse(request.query as Query) as any
82
+
83
+ // validate entity
84
+ if ('entity' in request) this.scope.fit(request.entity)
85
+
86
+ const store = { request }
87
+
88
+ return await this.process(store)
89
+ } catch (e) {
90
+ const exception = e instanceof Error ? new SystemException(e) : e
91
+
92
+ return { exception }
93
+ }
94
+ }
95
+
96
+ protected async process(store: Store): Promise<any> {
97
+ await this.acquire(store)
98
+ await this.run(store)
99
+ await this.commit(store)
100
+
101
+ return store.reply
102
+ }
103
+
104
+ protected async acquire(store: Store): Promise<void> {
105
+ if (this.#scope === 'none') return
106
+
107
+ const scope = await this.query(store.request.query)
108
+ const raw = scope === null || scope instanceof Readable
109
+
110
+ store.scope = scope
111
+ store.state = raw ? scope : (scope as Entity).get()
112
+ }
113
+
114
+ protected async run(store: Store): Promise<void> {
115
+ const { request, state } = store
116
+ const reply = await this.#cascade.run(request.input, state)
117
+
118
+ // validate reply only on local environments
119
+ if (process.env.TOA_ENV === 'local' && !(reply instanceof Readable))
120
+ this.#contracts.reply.fit(reply)
121
+
122
+ store.reply = reply
123
+ }
124
+
125
+ protected async commit(_store: Store): Promise<void> {}
126
+
127
+ protected async query(query?: Query): Promise<Scoped> {
128
+ if (query === undefined)
129
+ throw new RequestContractException('Request query is required')
130
+
131
+ const acquire = this.scope[this.#scope] as (
132
+ query: Query,
133
+ mutable?: boolean
134
+ ) => Promise<Scoped>
135
+
136
+ return acquire.call(this.scope, query, this.mutable)
137
+ }
138
+ }