@toa.io/storages.mongodb 1.0.0-alpha.288 → 1.0.0-alpha.289

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.0.0-alpha.289](https://github.com/toa-io/toa/compare/v1.0.0-alpha.288...v1.0.0-alpha.289) (2026-09-07)
7
+
8
+ * A deploy moves only what changed, and a component sees none of the runtime's environment (#1073) ([f38e3db](https://github.com/toa-io/toa/commit/f38e3db533f24db866c57bfa1ef294eff1eaf499)), closes [#1073](https://github.com/toa-io/toa/issues/1073) [#1064](https://github.com/toa-io/toa/issues/1064) [#1066](https://github.com/toa-io/toa/issues/1066) [#1067](https://github.com/toa-io/toa/issues/1067) [#1068](https://github.com/toa-io/toa/issues/1068) [#1069](https://github.com/toa-io/toa/issues/1069) [#1071](https://github.com/toa-io/toa/issues/1071) [#1070](https://github.com/toa-io/toa/issues/1070) [#1072](https://github.com/toa-io/toa/issues/1072)
9
+
10
+ ### BREAKING CHANGES
11
+
12
+ * a component that read `process.env.TOA_*` reads `context` instead;
13
+ `echo(input)` no longer substitutes from the environment; a bash operation sees no
14
+ `TOA_*`; images no longer set `USER node` — see migrations/289.md.
15
+
16
+
6
17
  # [1.0.0-alpha.288](https://github.com/toa-io/toa/compare/v1.0.0-alpha.287...v1.0.0-alpha.288) (2026-09-06)
7
18
 
8
19
  **Note:** Version bump only for package @toa.io/storages.mongodb
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/storages.mongodb",
3
- "version": "1.0.0-alpha.288",
3
+ "version": "1.0.0-alpha.289",
4
4
  "type": "module",
5
5
  "description": "Toa MongoDB Storage Connector",
6
6
  "author": "temich <tema.gurtovoy@gmail.com>",
@@ -20,13 +20,13 @@
20
20
  "test": "echo \"Error: run tests from root\" && exit 1"
21
21
  },
22
22
  "dependencies": {
23
- "@toa.io/conveyor": "1.0.0-alpha.286",
24
- "@toa.io/core": "1.0.0-alpha.288",
25
- "@toa.io/generic": "1.0.0-alpha.286",
26
- "@toa.io/pointer": "1.0.0-alpha.287",
23
+ "@toa.io/conveyor": "1.0.0-alpha.289",
24
+ "@toa.io/core": "1.0.0-alpha.289",
25
+ "@toa.io/generic": "1.0.0-alpha.289",
26
+ "@toa.io/pointer": "1.0.0-alpha.289",
27
27
  "mongodb": "7.6.0",
28
28
  "openspan": "1.0.0-alpha.286",
29
29
  "saslprep": "1.0.3"
30
30
  },
31
- "gitHead": "d3d696f0a4e287c3c297ac81028d600ad7574112"
31
+ "gitHead": "4fcb482beac5896305d8084cdbc1d5fcf372d108"
32
32
  }
package/src/client.js CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { console } from 'openspan'
8
+ import { environment } from '@toa.io/generic'
8
9
  import { Connector } from '@toa.io/core'
9
10
  import { resolve } from '@toa.io/pointer'
10
11
  import { ID } from './deployment.js'
@@ -182,7 +183,7 @@ export class Client extends Connector {
182
183
  async resolveURLs() {
183
184
  // Toa's own development stack is not on the conventional ports: the applications built on
184
185
  // Toa are, and they share the machine. See CONTRIBUTING.md.
185
- if (process.env.TOA_DEV === '1') {
186
+ if (environment.get('TOA_DEV') === '1') {
186
187
  return ['mongodb://developer:secret@localhost:31020']
187
188
  } else {
188
189
  return await resolve(ID, this.locator.id)
@@ -194,13 +195,11 @@ export class Client extends Connector {
194
195
  * @return {string}
195
196
  */
196
197
  resolveDB() {
197
- if (process.env.TOA_CONTEXT !== undefined) {
198
- return process.env.TOA_CONTEXT
199
- }
198
+ const context = environment.get('TOA_CONTEXT')
200
199
 
201
- if (process.env.TOA_DEV === '1') {
202
- return 'toa-dev'
203
- }
200
+ if (context !== undefined) return context
201
+
202
+ if (environment.get('TOA_DEV') === '1') return 'toa-dev'
204
203
 
205
204
  throw new Error('Environment variable TOA_CONTEXT is not defined')
206
205
  }
package/src/outbox.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { console } from 'openspan'
2
+ import { environment } from '@toa.io/generic'
2
3
 
3
4
  /**
4
5
  * The outbox rows of one component. Its lifecycle is the Client's, so it is not a Connector.
@@ -160,7 +161,7 @@ const to = ({ id, ...rest }) => ({ _id: id, ...rest })
160
161
  const from = ({ _id, ...rest }) => ({ id: _id, ...rest })
161
162
 
162
163
  function retention() {
163
- const value = Number(process.env.TOA_OUTBOX_RETENTION)
164
+ const value = Number(environment.get('TOA_OUTBOX_RETENTION'))
164
165
 
165
166
  return Number.isNaN(value) || value < 0 ? RETENTION : value
166
167
  }
package/src/storage.js CHANGED
@@ -4,7 +4,6 @@ import { translate } from './translate.js'
4
4
  import { codec } from './record.js'
5
5
  import { Outbox } from './outbox.js'
6
6
  import { Migrations } from './migrations.js'
7
- import { SYSTEM } from './system.js'
8
7
  import { ReturnDocument } from 'mongodb'
9
8
 
10
9
  export class Storage extends Connector {
@@ -73,14 +72,16 @@ export class Storage extends Connector {
73
72
 
74
73
  this.#spans.clear()
75
74
 
76
- // the runtime's own come first: what a component declares is written against a collection
77
- // whose system properties are already what this release holds them as
78
- this.#migrations = new Migrations(this.#client.db, this.#collection, [
79
- ...SYSTEM,
80
- ...(this.#entity.migrations ?? [])
81
- ])
75
+ if (this.#entity.migrations?.length > 0) {
76
+ this.#migrations = new Migrations(
77
+ this.#client.db,
78
+ this.#collection,
79
+ this.#entity.migrations
80
+ )
81
+
82
+ await this.#migrations.run()
83
+ }
82
84
 
83
- await this.#migrations.run()
84
85
  await this.#outbox?.index()
85
86
  }
86
87
 
@@ -5,6 +5,7 @@ import { isDeepStrictEqual } from 'node:util'
5
5
  import { Storage } from '../src/storage.js'
6
6
 
7
7
  let collection
8
+ let db
8
9
  let storage
9
10
 
10
11
  beforeEach(async () => {
@@ -15,25 +16,21 @@ beforeEach(async () => {
15
16
  updateMany: mock.fn(async () => ({ modifiedCount: 0 }))
16
17
  }
17
18
 
18
- // the runtime's own migrations are applied to every collection, so a storage needs the
19
- // database its record of them lives in
20
- const state = {
21
- insertOne: mock.fn(async () => ({})),
22
- findOne: mock.fn(async () => null),
23
- updateOne: mock.fn(async () => ({ modifiedCount: 1 }))
24
- }
19
+ db = { collection: mock.fn(() => null) }
25
20
 
26
- const client = {
27
- collection,
28
- db: { collection: () => state },
29
- link: () => null
30
- }
21
+ const client = { collection, db, link: () => null }
31
22
 
32
23
  storage = new Storage(client, { schema: { properties: {} } })
33
24
 
34
25
  await storage.open()
35
26
  })
36
27
 
28
+ describe('open', () => {
29
+ it('should not touch the migrations record where the entity declares none', () => {
30
+ assert.equal(db.collection.mock.callCount(), 0)
31
+ })
32
+ })
33
+
37
34
  describe('get', () => {
38
35
  it('should filter deleted', async () => {
39
36
  await storage.get({})
package/src/system.js DELETED
@@ -1,44 +0,0 @@
1
- /**
2
- * Migrations the runtime owns, applied to every collection ahead of whatever the component
3
- * declares.
4
- *
5
- * These convert what the runtime itself writes, so they are declared here rather than in a
6
- * migration every component would have to write — the same reason the outbox keeps its own
7
- * indexes. A component cannot write them: it does not own the system properties, and a
8
- * prototype's migrations are not inherited.
9
- *
10
- * They are applied once for the database, recorded beside the component's own, and named so
11
- * that the two can never collide.
12
- */
13
- export const SYSTEM = [
14
- {
15
- id: 'system:0001-epoch-millis',
16
- steps: [
17
- {
18
- /*
19
- * The system timestamps became dates: what sorts, compares and expires as a moment
20
- * rather than as a number that happens to be one. A record written before this holds
21
- * them as milliseconds, and a collection holding both would sort every number ahead of
22
- * every date — so the release that brings this stops the deployment first.
23
- *
24
- * `CREATED` says whether a record has been converted, because every record has one.
25
- * Null passes through both conversions, which is what an undeleted record's `DELETED`
26
- * is; and the widening is not decoration — MongoDB makes a date from a long and
27
- * refuses one from an int, and a small enough millisecond is stored as an int.
28
- */
29
- update: {
30
- filter: { CREATED: { $type: 'number' } },
31
- update: [
32
- {
33
- $set: {
34
- CREATED: { $toDate: { $toLong: '$CREATED' } },
35
- UPDATED: { $toDate: { $toLong: '$UPDATED' } },
36
- DELETED: { $toDate: { $toLong: '$DELETED' } }
37
- }
38
- }
39
- ]
40
- }
41
- }
42
- ]
43
- }
44
- ]