@toa.io/core 1.0.0-alpha.2 → 1.0.0-alpha.200

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 (50) hide show
  1. package/package.json +6 -9
  2. package/src/assignment.js +11 -2
  3. package/src/call.js +6 -0
  4. package/src/cascade.js +2 -3
  5. package/src/component.js +15 -18
  6. package/src/composition.js +1 -1
  7. package/src/connector.js +3 -8
  8. package/src/contract/contract.js +22 -0
  9. package/src/contract/reply.js +26 -9
  10. package/src/contract/request.js +15 -5
  11. package/src/contract/schemas/query.yaml +15 -3
  12. package/src/discovery.js +2 -5
  13. package/src/effect.js +19 -0
  14. package/src/entities/changeset.js +8 -15
  15. package/src/entities/entity.js +51 -12
  16. package/src/entities/factory.js +16 -4
  17. package/src/entities/set.js +13 -0
  18. package/src/exceptions.js +27 -19
  19. package/src/exposition.js +3 -2
  20. package/src/guard.js +17 -0
  21. package/src/index.js +6 -0
  22. package/src/locator.js +7 -2
  23. package/src/observation.js +1 -9
  24. package/src/operation.js +28 -7
  25. package/src/query/criteria.js +3 -3
  26. package/src/query/options.js +3 -2
  27. package/src/query.js +3 -1
  28. package/src/receiver.js +13 -10
  29. package/src/remote.js +5 -7
  30. package/src/state.js +85 -47
  31. package/src/transition.js +10 -8
  32. package/src/transmission.js +12 -3
  33. package/src/unmanaged.js +11 -0
  34. package/test/component.test.js +4 -3
  35. package/test/contract/conditions.test.js +5 -5
  36. package/test/contract/request.test.js +7 -7
  37. package/test/entities/entity.fixtures.js +5 -2
  38. package/test/entities/entity.test.js +6 -45
  39. package/test/state.test.js +0 -6
  40. package/types/bindings.d.ts +7 -5
  41. package/types/component.d.ts +4 -1
  42. package/types/extensions.d.ts +4 -3
  43. package/types/index.ts +1 -0
  44. package/types/locator.d.ts +2 -1
  45. package/types/operations.d.ts +6 -0
  46. package/types/query.d.ts +2 -0
  47. package/types/remote.d.ts +18 -0
  48. package/types/request.d.ts +3 -0
  49. package/types/storages.d.ts +11 -9
  50. package/src/contract/conditions.js +0 -21
package/src/exceptions.js CHANGED
@@ -4,7 +4,6 @@ const { swap } = require('@toa.io/generic')
4
4
 
5
5
  const codes = {
6
6
  System: 0,
7
- NotImplemented: 10,
8
7
 
9
8
  Contract: 200,
10
9
  RequestSyntax: 201,
@@ -12,6 +11,7 @@ const codes = {
12
11
  RequestConflict: 203,
13
12
  ResponseContract: 211,
14
13
  EntityContract: 212,
14
+ EntityGuard: 213,
15
15
  QuerySyntax: 221,
16
16
 
17
17
  State: 300,
@@ -19,6 +19,7 @@ const codes = {
19
19
  StatePrecondition: 303,
20
20
  StateConcurrency: 304,
21
21
  StateInitialization: 305,
22
+ Duplicate: 306,
22
23
 
23
24
  Communication: 400,
24
25
  Transmission: 401
@@ -31,9 +32,12 @@ class Exception {
31
32
  code
32
33
  message
33
34
 
34
- constructor (code, message) {
35
+ constructor (code, message, cause) {
35
36
  this.code = code
36
37
  this.message = message
38
+
39
+ if (cause !== undefined)
40
+ this.cause = cause
37
41
  }
38
42
  }
39
43
 
@@ -48,31 +52,30 @@ class SystemException extends Exception {
48
52
  }
49
53
 
50
54
  class ContractException extends Exception {
51
- keyword
52
- property
53
- schema
54
- path
55
-
56
- constructor (code, error) {
57
- super(code || codes.Contract, error.message)
58
-
59
- this.keyword = error.keyword
60
- this.property = error.property
61
- this.schema = error.schema
62
- this.path = error.path
55
+ constructor (code, error, cause) {
56
+ super(code || codes.Contract, typeof error === 'string' ? error : error?.message, cause)
57
+
58
+ if (typeof error === 'object' && error !== null)
59
+ for (const k of ['keyword', 'property', 'schema', 'path', 'params'])
60
+ if (k in error)
61
+ this[k] = error[k]
63
62
  }
64
63
  }
65
64
 
66
65
  class RequestContractException extends ContractException {
67
- constructor (error) { super(codes.RequestContract, error) }
66
+ constructor (error, cause) { super(codes.RequestContract, error, cause) }
68
67
  }
69
68
 
70
69
  class ResponseContractException extends ContractException {
71
- constructor (error) { super(codes.ResponseContract, error) }
70
+ constructor (error, cause) { super(codes.ResponseContract, error, cause) }
72
71
  }
73
72
 
74
73
  class EntityContractException extends ContractException {
75
- constructor (error) { super(codes.EntityContract, error) }
74
+ constructor (error, cause) { super(codes.EntityContract, error, cause) }
75
+ }
76
+
77
+ class EntityGuardException extends ContractException {
78
+ constructor (name, cause) { super(codes.EntityGuard, name, cause) }
76
79
  }
77
80
 
78
81
  // #region exports
@@ -81,14 +84,19 @@ exports.SystemException = SystemException
81
84
  exports.RequestContractException = RequestContractException
82
85
  exports.ResponseContractException = ResponseContractException
83
86
  exports.EntityContractException = EntityContractException
87
+ exports.EntityGuardException = EntityGuardException
84
88
 
85
89
  for (const [name, code] of Object.entries(codes)) {
86
90
  const classname = name + 'Exception'
87
91
 
88
92
  if (exports[classname] === undefined) {
89
93
  exports[classname] = class extends Exception {
90
- constructor (message) {
91
- super(code, message || classname)
94
+ constructor (message, cause) {
95
+ message = message
96
+ ? `${classname}: ${message}`
97
+ : classname
98
+
99
+ super(code, message ?? classname, cause)
92
100
  }
93
101
  }
94
102
  }
package/src/exposition.js CHANGED
@@ -20,8 +20,9 @@ class Exposition extends Connector {
20
20
  }
21
21
 
22
22
  const expose = (manifest) => {
23
- const { namespace, name, operations, events, entity } = manifest
24
- return { namespace, name, operations, events, entity }
23
+ const { namespace, name, entity, operations, events } = manifest
24
+
25
+ return { namespace, name, entity, operations, events }
25
26
  }
26
27
 
27
28
  exports.Exposition = Exposition
package/src/guard.js ADDED
@@ -0,0 +1,17 @@
1
+ 'use strict'
2
+
3
+ class Guard {
4
+ name
5
+ #bridge
6
+
7
+ constructor (name, bridge) {
8
+ this.name = name
9
+ this.#bridge = bridge
10
+ }
11
+
12
+ fit (state) {
13
+ return this.#bridge.fit(state)
14
+ }
15
+ }
16
+
17
+ exports.Guard = Guard
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ const { Composition } = require('./composition')
5
5
  const { Connector } = require('./connector')
6
6
  const { Context } = require('./context')
7
7
  const { Discovery } = require('./discovery')
8
+ const { Effect } = require('./effect')
8
9
  const { Emission } = require('./emission')
9
10
  const { Event } = require('./event')
10
11
  const { Exposition } = require('./exposition')
@@ -19,6 +20,8 @@ const { Component } = require('./component')
19
20
  const { State } = require('./state')
20
21
  const { Transition } = require('./transition')
21
22
  const { Transmission } = require('./transmission')
23
+ const { Unmanaged } = require('./unmanaged')
24
+ const { Guard } = require('./guard')
22
25
 
23
26
  exports.entities = require('./entities')
24
27
  exports.exceptions = require('./exceptions')
@@ -32,6 +35,7 @@ exports.Composition = Composition
32
35
  exports.Connector = Connector
33
36
  exports.Context = Context
34
37
  exports.Discovery = Discovery
38
+ exports.Effect = Effect
35
39
  exports.Emission = Emission
36
40
  exports.Event = Event
37
41
  exports.Exposition = Exposition
@@ -45,3 +49,5 @@ exports.Remote = Remote
45
49
  exports.State = State
46
50
  exports.Transition = Transition
47
51
  exports.Transmission = Transmission
52
+ exports.Unmanaged = Unmanaged
53
+ exports.Guard = Guard
package/src/locator.js CHANGED
@@ -12,6 +12,7 @@ class Locator {
12
12
  id
13
13
  label
14
14
  uppercase
15
+ lowercase
15
16
 
16
17
  /**
17
18
  * @param {string} name
@@ -26,6 +27,7 @@ class Locator {
26
27
  this.id = concat(namespace, '.') + name
27
28
  this.label = (concat(namespace, '-') + name).toLowerCase()
28
29
  this.uppercase = (concat(namespace, '_') + name).toUpperCase()
30
+ this.lowercase = (concat(namespace, '_') + name).toLowerCase()
29
31
  }
30
32
 
31
33
  hostname (prefix) {
@@ -39,8 +41,11 @@ class Locator {
39
41
  static parse (string) {
40
42
  const [namespace, name] = string.split(DOT)
41
43
 
42
- if (name === undefined) return new Locator(namespace)
43
- else return new Locator(name, namespace)
44
+ if (name === undefined) {
45
+ return new Locator(namespace)
46
+ } else {
47
+ return new Locator(name, namespace)
48
+ }
44
49
  }
45
50
  }
46
51
 
@@ -3,16 +3,8 @@
3
3
  const { Operation } = require('./operation')
4
4
 
5
5
  class Observation extends Operation {
6
- async acquire (store) {
7
- const scope = await this.query(store.request.query)
8
- const state = scope === null ? null : scope.get()
9
-
10
- store.scope = scope
11
- store.state = state
12
- }
13
-
14
6
  async run (store) {
15
- if (store.scope === null) store.reply = null
7
+ if (store.scope === null || (store.scope?.deleted === true && store.request.query?.options?.deleted !== true)) store.reply = null
16
8
  else await super.run(store)
17
9
  }
18
10
  }
package/src/operation.js CHANGED
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const { Connector } = require('./connector')
4
- const { SystemException } = require('./exceptions')
4
+ const { SystemException, RequestContractException } = require('./exceptions')
5
+ const { Readable } = require('node:stream')
5
6
 
6
7
  class Operation extends Connector {
7
8
  scope
@@ -26,8 +27,15 @@ class Operation extends Connector {
26
27
 
27
28
  async invoke (request) {
28
29
  try {
29
- if (request.authentic !== true) this.#contracts.request.fit(request)
30
- if ('query' in request) request.query = this.#query.parse(request.query)
30
+ if (request.authentic !== true)
31
+ this.#contracts.request.fit(request)
32
+
33
+ if ('query' in request)
34
+ request.query = this.#query.parse(request.query)
35
+
36
+ // validate entity
37
+ if ('entity' in request)
38
+ this.scope.fit(request.entity)
31
39
 
32
40
  const store = { request }
33
41
 
@@ -47,14 +55,24 @@ class Operation extends Connector {
47
55
  return store.reply
48
56
  }
49
57
 
50
- async acquire () {}
58
+ async acquire (store) {
59
+ if (this.#scope === 'none')
60
+ return
61
+
62
+ const scope = await this.query(store.request.query)
63
+ const raw = scope === null || scope instanceof Readable
64
+
65
+ store.scope = scope
66
+ store.state = raw ? scope : scope.get()
67
+ }
51
68
 
52
69
  async run (store) {
53
70
  const { request, state } = store
54
- // noinspection UnnecessaryLocalVariableJS
55
- const reply = await this.#cascade.run(request.input, state) || {}
71
+ const reply = await this.#cascade.run(request.input, state)
56
72
 
57
- // this.#contracts.reply.fit(reply)
73
+ // validate reply only on local environments
74
+ if (process.env.TOA_ENV === 'local' && !(reply instanceof Readable))
75
+ this.#contracts.reply.fit(reply)
58
76
 
59
77
  store.reply = reply
60
78
  }
@@ -62,6 +80,9 @@ class Operation extends Connector {
62
80
  async commit () {}
63
81
 
64
82
  async query (query) {
83
+ if (query === undefined)
84
+ throw new RequestContractException('Request query is required')
85
+
65
86
  return this.scope[this.#scope](query)
66
87
  }
67
88
  }
@@ -33,9 +33,9 @@ const coerce = (node, properties) => {
33
33
  }
34
34
 
35
35
  const COERCE = {
36
- number: Number,
37
- integer: parseInt,
38
- boolean: Boolean
36
+ number: Number.parseFloat,
37
+ integer: Number.parseInt,
38
+ boolean: (value) => value === 'true'
39
39
  }
40
40
 
41
41
  exports.criteria = criteria
@@ -33,8 +33,9 @@ const projection = (projection, properties) => {
33
33
  }
34
34
  }
35
35
 
36
- if (projection.includes('_version') === false)
37
- projection.push('_version')
36
+ for (const property of ['_version', '_created', '_updated', '_deleted'])
37
+ if (!projection.includes(property))
38
+ projection.push(property)
38
39
  }
39
40
 
40
41
  exports.options = options
package/src/query.js CHANGED
@@ -19,13 +19,15 @@ class Query {
19
19
  parse (query) {
20
20
  /** @type {toa.core.storages.Query} */
21
21
  const result = {}
22
- const { id, version, criteria, ...rest } = query
22
+ const { id, ids, version, criteria, search, ...rest } = query
23
23
 
24
24
  const options = this.#options(rest)
25
25
 
26
26
  if (id !== undefined) result.id = id
27
+ if (ids !== undefined) result.ids = ids
27
28
  if (version !== undefined) result.version = version
28
29
  if (criteria !== undefined) result.criteria = parse.criteria(criteria, this.#properties)
30
+ if (search !== undefined) result.search = search
29
31
  if (options !== undefined) result.options = options
30
32
 
31
33
  return result
package/src/receiver.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const { console } = require('openspan')
3
4
  const { add } = require('@toa.io/generic')
4
5
  const { Connector } = require('./connector')
5
6
 
@@ -16,18 +17,13 @@ class Receiver extends Connector {
16
17
  /** @type {string} */
17
18
  #endpoint
18
19
 
19
- /** @type {toa.core.Component} */
20
+ /** @type {unknown[]} */
21
+ #arguments
22
+
20
23
  #local
21
24
 
22
- /** @type {toa.core.bridges.Receiver} */
23
25
  #bridge
24
26
 
25
- /**
26
- *
27
- * @param {toa.norm.component.Receiver} definition
28
- * @param {toa.core.Component} local
29
- * @param {toa.core.bridges.Receiver} bridge
30
- */
31
27
  constructor (definition, local, bridge) {
32
28
  super()
33
29
 
@@ -36,6 +32,7 @@ class Receiver extends Connector {
36
32
  this.#conditioned = conditioned
37
33
  this.#adaptive = adaptive
38
34
  this.#endpoint = operation
35
+ this.#arguments = definition.arguments
39
36
 
40
37
  this.#local = local
41
38
  this.#bridge = bridge
@@ -54,11 +51,17 @@ class Receiver extends Connector {
54
51
 
55
52
  add(request, extensions)
56
53
 
57
- await this.#local.invoke(this.#endpoint, request)
54
+ try {
55
+ await this.#local.invoke(this.#endpoint, request)
56
+ } catch (error) {
57
+ console.error('Receiver error', error)
58
+
59
+ throw error
60
+ }
58
61
  }
59
62
 
60
63
  async #request (payload) {
61
- return this.#adaptive ? await this.#bridge.request(payload) : { input: payload }
64
+ return this.#adaptive ? await this.#bridge.request(payload, ...(this.#arguments ?? [])) : { input: payload }
62
65
  }
63
66
  }
64
67
 
package/src/remote.js CHANGED
@@ -1,16 +1,14 @@
1
1
  'use strict'
2
2
 
3
- const { console } = require('@toa.io/console')
4
-
3
+ const assert = require('node:assert')
5
4
  const { Component } = require('./component')
6
5
 
7
6
  class Remote extends Component {
8
- async open () {
9
- console.info(`Remote '${this.locator.id}' connected`)
10
- }
7
+ explain (endpoint) {
8
+ assert.ok(endpoint in this.operations,
9
+ `Endpoint '${endpoint}' is not provided by '${this.locator.id}'`)
11
10
 
12
- async dispose () {
13
- console.info(`Remote '${this.locator.id}' disconnected`)
11
+ return this.operations[endpoint].explain()
14
12
  }
15
13
  }
16
14
 
package/src/state.js CHANGED
@@ -1,97 +1,135 @@
1
1
  'use strict'
2
2
 
3
- const { empty } = require('@toa.io/generic')
3
+ const { stat } = require('fs')
4
+ const { StatePreconditionException, StateNotFoundException } = require('./exceptions')
4
5
 
5
- const {
6
- StatePreconditionException,
7
- StateNotFoundException,
8
- StateInitializationException
9
- } = require('./exceptions')
10
-
11
- /**
12
- * @implements {toa.core.State}
13
- */
14
6
  class State {
15
- /** @type {toa.core.Storage} */
16
- #storage
7
+ storage
17
8
 
18
- /** @type {toa.core.entity.Factory} */
19
- #entity
9
+ #associated
10
+ #entities
20
11
  #emission
21
- #dependent
22
12
 
23
- constructor (storage, entity, emission, dependent) {
24
- this.#storage = storage
25
- this.#entity = entity
13
+ constructor (storage, entity, emission, associated) {
14
+ this.storage = storage
15
+ this.#entities = entity
26
16
  this.#emission = emission
27
- this.#dependent = dependent === true
17
+ this.#associated = associated === true
28
18
  }
29
19
 
30
20
  init (id) {
31
- return this.#entity.init(id)
21
+ return this.#entities.init(id)
22
+ }
23
+
24
+ fit (values) {
25
+ return this.#entities.fit(values)
32
26
  }
33
27
 
34
28
  async object (query) {
35
- const record = await this.#storage.get(query)
29
+ const record = await this.storage.get(query)
36
30
 
37
31
  if (record === null) {
38
- if (this.#dependent && query.id !== undefined && query.version === undefined) return this.init(query.id)
39
- else if (query.version !== undefined) throw new StatePreconditionException()
40
- }
41
-
42
- if (record === null) return null
43
- else return this.#entity.object(record)
32
+ if (this.#associated && query.id !== undefined && query.criteria === undefined && query.version === undefined)
33
+ return this.init(query.id)
34
+ else if (query.version !== undefined)
35
+ throw new StatePreconditionException()
36
+
37
+ return null
38
+ } else
39
+ return this.#entities.object(record)
44
40
  }
45
41
 
46
42
  async objects (query) {
47
- const recordset = await this.#storage.find(query)
43
+ const recordset = await this.storage.find(query)
44
+ const missing = this.#associated && query.ids !== undefined && recordset.length < query.ids.length
45
+ const init = missing ? query.ids.filter((id) => !recordset.some((record) => record.id === id)) : undefined
46
+
47
+ return this.#entities.objects(recordset, init)
48
+ }
48
49
 
49
- return this.#entity.objects(recordset)
50
+ async stream (query) {
51
+ return this.storage.stream(query)
50
52
  }
51
53
 
52
54
  changeset (query) {
53
- return this.#entity.changeset(query)
55
+ return this.#entities.changeset(query)
54
56
  }
55
57
 
56
58
  none () {
57
59
  return null
58
60
  }
59
61
 
60
- async commit (state) {
61
- const event = state.event()
62
+ async ensure (query, properties, input) {
63
+ const object = this.#entities.init()
64
+ const blank = object.get()
65
+
66
+ Object.assign(blank, properties)
67
+
68
+ object.set(blank)
69
+
70
+ const record = await this.storage.ensure(query, properties, object.get())
71
+
72
+ if (record.id !== blank.id) // exists
73
+ return this.#entities.object(record)
62
74
 
63
- let ok = true
75
+ const event = object.event(input)
64
76
 
65
- if (!empty(event.changeset)) {
66
- const object = state.get()
77
+ await this.#emission.emit(event)
67
78
 
68
- ok = await this.#storage.store(object)
79
+ return object
80
+ }
81
+
82
+ async commit (state, input) {
83
+ if (state.constructor.name === 'EntitySet')
84
+ return this.massCommit(state, input)
85
+
86
+ const data = state.get()
87
+ const ok = await this.storage.store(data)
88
+
89
+ // #20
90
+ if (ok === true) {
91
+ const event = state.event(input)
69
92
 
70
- // #20
71
93
  await this.#emission.emit(event)
72
94
  }
73
95
 
74
96
  return ok
75
97
  }
76
98
 
77
- async apply (state) {
78
- const { changeset, insert } = state.export()
99
+ async massCommit (state, input) {
100
+ const data = state.get()
101
+ const ok = await this.storage.massStore(data)
79
102
 
80
- let upsert
103
+ // #20
104
+ if (ok === true) {
105
+ const events = state.events(input)
81
106
 
82
- if (this.#dependent && state.query.id !== undefined && state.query.version === undefined) {
83
- upsert = insert
107
+ await Promise.all(events.map((event) => this.#emission.emit(event)))
84
108
  }
85
109
 
86
- const result = await this.#storage.upsert(state.query, changeset, upsert)
110
+ return ok
111
+ }
112
+
113
+ async apply (state) {
114
+ const changeset = state.export()
115
+
116
+ const result = await this.storage.upsert(state.query, changeset)
87
117
 
88
118
  if (result === null) {
89
- if (state.query.version !== undefined) throw new StatePreconditionException()
90
- else throw new StateNotFoundException()
119
+ if (state.query.version !== undefined) {
120
+ throw new StatePreconditionException()
121
+ } else {
122
+ throw new StateNotFoundException()
123
+ }
124
+ } else {
125
+ // same as above
126
+ await this.#emission.emit({
127
+ changeset,
128
+ state: result
129
+ })
91
130
  }
92
131
 
93
- // same as above
94
- await this.#emission.emit({ changeset, state: result })
132
+ return result
95
133
  }
96
134
  }
97
135
 
package/src/transition.js CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { retry } = require('@toa.io/generic')
4
-
5
4
  const { Operation } = require('./operation')
6
5
  const { StateConcurrencyException, StateNotFoundException } = require('./exceptions')
7
6
 
@@ -23,8 +22,9 @@ class Transition extends Operation {
23
22
 
24
23
  store.scope = request.query ? await this.query(request.query) : this.scope.init()
25
24
 
26
- if (store.scope === null) throw new StateNotFoundException()
27
-
25
+ if (store.scope === null || (store.scope.deleted === true && request.query?.options?.deleted !== true))
26
+ throw new StateNotFoundException()
27
+
28
28
  store.state = store.scope.get()
29
29
  }
30
30
 
@@ -35,11 +35,13 @@ class Transition extends Operation {
35
35
 
36
36
  scope.set(state)
37
37
 
38
- const ok = await this.scope.commit(scope)
38
+ const result = await this.scope.commit(scope, store.request.input)
39
39
 
40
- if (ok !== true) {
41
- if (this.#concurrency === 'retry') retry()
42
- else throw new StateConcurrencyException()
40
+ if (result === false) {
41
+ if (this.#concurrency === 'retry')
42
+ return retry()
43
+ else
44
+ throw new StateConcurrencyException()
43
45
  }
44
46
  }
45
47
 
@@ -54,7 +56,7 @@ const RETRY = {
54
56
  base: 10,
55
57
  max: 5000,
56
58
  dispersion: 1,
57
- retries: Infinity
59
+ retries: 32
58
60
  }
59
61
 
60
62
  exports.Transition = Transition
@@ -18,13 +18,22 @@ class Transmission extends Connector {
18
18
  let i = 0
19
19
 
20
20
  while (reply === false && i < this.#bindings.length) {
21
- reply = await this.#bindings[i].request(request)
21
+ const binding = this.#bindings[i]
22
+
22
23
  i++
24
+
25
+ if (request?.task === true) {
26
+ if (binding.task === undefined)
27
+ continue
28
+
29
+ await binding.task(request)
30
+ reply = null
31
+ } else
32
+ reply = await binding.request(request)
23
33
  }
24
34
 
25
- if (reply === false) {
35
+ if (reply === false)
26
36
  throw new TransmissionException(`All (${this.#bindings.length}) bindings rejected.`)
27
- }
28
37
 
29
38
  return reply
30
39
  }
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+
3
+ const { Operation } = require('./operation')
4
+
5
+ class Unmanaged extends Operation {
6
+ acquire (context) {
7
+ context.state = this.scope.storage.raw
8
+ }
9
+ }
10
+
11
+ exports.Unmanaged = Unmanaged