Skip to content

Commit bc0538a

Browse files
feat(spec)!: retire the datasource driver spelling mongo — refused naming mongodb, its D2 conversion leaves the load path (F1 of #23543) (#23600)
Part of #23543 Clause-②: yes This PR lands family **F1** of ruling `6182001882` only. F5-jsx, F3, F2, F4 and F5-http stay on #23543 and follow one PR each, in the ruled order. ## What Ruling F1 A: `'mongo'` retires as a `datasource.driver` value, refused with the prescription naming `mongodb`; stored rows are rewritten by the D3 chain; `datasource-driver-mongo-to-mongodb` sets `retiredFromLoadPath`. - **Authoring refusal.** `DriverType` (`packages/spec/src/data/datasource.zod.ts`) refuses `mongo` by name, case- and whitespace-insensitive. The message is the prescription: "Datasource `driver: 'mongo'` was removed in @objectstack/spec 18 (ADR-0087 D2) … Use `driver: 'mongodb'`." It ends with the house `os migrate meta --from 16` sentence. At `defineStack` this is `STACK_SCHEMA_INVALID` / 422 with one issue at `datasources.0.driver`. - **Where the spelling is declared.** A new `retiredAliases` column on the driver vocabulary row (`packages/spec/src/data/driver/config-registry.zod.ts`): `{ id: 'mongodb', aliases: ['mongodb'], retiredAliases: { mongo: PRESCRIPTION } }`. The refusal is derived from that column (exported as `RETIRED_DRIVER_ID_GUIDANCE`), so the table remains the one vocabulary. - **Selection face.** `mongo` is no longer a boot selection. Both hosts refuse `OS_DATABASE_DRIVER=mongo` alike, and their "Supported drivers" list names `mongodb`. The cross-host parity pin is flipped to say so. - **The D2 entry.** `datasource-driver-mongo-to-mongodb` gets `retiredFromLoadPath: true` and `retiredAfter: '17.7.0'` (the newest published stable release under the `18.0.0-next` label, the same stamp PR #23523 used). It is already a member of step 17's `conversionIds`, so `os migrate meta --from 16` and every `includeRetired` seam keep replaying it. - **The D3 entry.** `18.datasource-driver-mongo-alias-retired` is the family's semantic entry, with `conversionIds: ['datasource-driver-mongo-to-mongodb']`. It names what the chain cannot reach: `OS_DATABASE_DRIVER`, a standalone stack's `databaseDriver`, and REST callers. - **The verdict-list pin** in `conversions.test.ts` moves the entry from the kept list to the retired list, and the entry's `describe` block is rewritten for the new semantics. The entry also has a row in the removal-version pin's `JOIN`. ## Mechanism assumptions, measured 1. **"Retire = delete the alias" is false as stated.** I deleted `'mongo'` from the row outright, ran the suites, and probed. Measured on `fa6a6085c`: - **Credential leak (security).** The read-path credential redactor judges a stored body as written (`redactStoredMetadataBody`). It keys mongodb's passthrough-secret table by canonical id. With the alias gone, a raw stored legacy `driver: 'mongo'` row served all ten passthrough secrets in cleartext: `options.proxyPassword`, `options.key`, `options.passphrase`, `options.tlsCertificateKeyFilePassword`, `AWS_SESSION_TOKEN` and the five KMS secrets. - **Four pins went red.** These were `stored.test.ts` (a stored mongo row's `uri`/`user` were no longer renamed), the key-alias conversion's "row whose driver id is itself being renamed" case, `refusedPassthroughSecretPaths('mongo')`, and the "unconverted row keeps its contract" case. - **What I did instead.** The spelling leaves the selection face and the authoring surface, and it stays on the CONTRACT face only (`resolveDriverId('mongo')` still answers `mongodb`). This is the same posture the retired in-memory engine's spellings have. - **Why the D2 order is untouched.** The `datasource-config-driver-key-aliases` conversion still reaches a stored mongo row through the contract face, so the order of the step-17 list does not change. 2. **There was no existing value-retirement mechanism for this field.** `driver` is an open namespace (`z.string()`), so `enumWithRetiredValues` cannot carry it. The refusal reuses that helper's prescription type (`RetiredValueGuidance`) and its writing conventions. It is spelled as a `pattern` (`^(?!\s*[Mm][Oo][Nn][Gg][Oo]\s*$)`), not a refinement. My first spelling was a refinement, and the build's dropped-refinement gate refused it. A `pattern` survives the JSON Schema projection, so a document validated against the published `json-schema/**` is refused too. `config-registry.test.ts` pins that projection. 3. **`packages/cli/src/utils/config.ts:621` and `:654`.** Both docblocks used `driver: 'mongo'` as the example of "a conversion the load still applies". They now say the defect was measured on that spelling, which has since retired. 4. **`conversions.test.ts`.** Done as the assumption said. The verdict list now keeps 7 entries, and the retired list asserts this one with `retiredAfter: '17.7.0'`. ## Pin sweep (whole repo, `'mongo'` / `"mongo"` and the conversion id) - **Flipped to the new semantics:** - `config-registry.test.ts`: the selection face loses `mongo`. A new describe covers the authoring refusal (code `invalid_format`, path `driver`, message first sentence), the config gate still running behind it, selection withdrawn, contract kept, no false refusals (`mongodb`, `com.vendor.mongolike`, prototype words), the JSON Schema `pattern`, and the retired spelling being no live spelling. - `conversions.test.ts`, as above. Its new defineStack case asserts `STACK_SCHEMA_INVALID` / 422, the path, and the prescription's first sentence. - `driver-vocabulary-parity.test.ts` (cli): both hosts refuse `mongo`, each naming `mongodb`. - **Re-pointed.** These suites need a spelling the load still converts, which `mongo` no longer is: - `stack-authored-input.test.ts` (spec); - `migrate-meta-load-conversions.test.ts`, `migrate-meta-composed-load-conversions.test.ts`, and the load-path case of `migrate-meta-composed.test.ts` (cli). - They now carry a flow-level `errorHandling.retryDelayMs`. `retry-policy-converged` renames it at load, and it is a step-17 member, so `--from 16` lists it. - **Fixture spelling only:** two `datasource.test.ts` fixtures now write `mongodb`. Their refusal assertions are unchanged. - **Unchanged on purpose:** - `migrate-meta-write.test.ts` and `migrate-meta-protocol-range.test.ts` still write `driver: 'mongo'` as a refused spelling the chain rewrites, which is still true. - The redaction, credential-refusal, pool-support, factory and admin-restore tests still pass `mongo` as a stored legacy row, which the contract face keeps judging as mongodb. - **Docs and comments that became false:** - `content/docs/data-modeling/drivers.mdx`, plus a short paragraph on the retirement; - `packages/drivers/driver-mongodb/README.md`, which is published, hence the `patch` changeset row; - the `liveness/datasource.json` note; - comments in `driver-catalog.ts`, `mongo.zod.ts` and `mongo.test.ts`. ## Changeset `.changeset/23543-f1-retire-mongo-driver-alias.md`: `@objectstack/spec` minor and `@objectstack/driver-mongodb` patch. It is BREAKING, carries `Clause-②: yes (narrowing)` as ruling item 7 spells it, gives the FROM → TO migration for all three places the spelling lives, and registers `datasource-driver-mongo-alias-retired` as its ADR-0087 disposition. `check-adr-0087-registration --base origin/main` exits 0 and reads it as `BREAKING+bang+clause-②-narrowing`, `registered`. ## Verification (head `acf0e6c1f`, after merging `origin/main` `32e03d2fd`) - `@objectstack/spec`: - `test`: 678 files / 20738 tests passed. - `typecheck` (tsc, scripts, test layer): exit 0. - `test:repo`: 61 / 1033 passed. - `check:generated`: all 14 artifacts up to date; `api-surface/data.json` and `export-origins/data.json` were regenerated for the one new export. - `@objectstack/cli` (unit tier), the six files the change touches or pins: 6 / 113 passed. The full unit tier and typecheck readings are in the report comment on the card. - Readings for `@objectstack/runtime` and `@objectstack/service-datasource` are in the report. - No `packages/drivers/driver-*` source reads a `datasource.driver` value (zero hits for the resolvers, `DatasourceSchema` or `DriverType` in `src`), so no driver suite is owed beyond CI. - Gate families: `dispatch-gates.mjs --commands` derived 117 commands, run one by one with exit codes captured before any pipe and reconciled with `--ran`. Verdicts are in the report. ## Acceptance notes - **The `mongo` refusal at the boot hosts.** It is the hosts' existing "Supported drivers" refusal, which lists `mongodb`. It is not a dedicated prescription sentence like the retired in-memory engine's. `RETIRED_DRIVER_ID_GUIDANCE` is exported if a host should name it later. - **Load-path suites (for F3 and F2).** After this PR, the conversions that are still on the load path and that `os migrate meta --from 16` replays are `retry-policy-converged` and F3's two config lifts. Once F3 and F2 land, the three cli load-path suites and `stack-authored-input.test.ts` have no real subject left. They will need a synthetic probe, or the suites retired, in whichever of those PRs lands last. --- _Generated by [Claude Code](https://claude.ai/code/session_01LFqdRsuM5nkqoJdHd5ztVk)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent dd4ef38 commit bc0538a

23 files changed

Lines changed: 557 additions & 162 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/driver-mongodb': patch
4+
---
5+
6+
feat(spec)!: the datasource driver spelling `mongo` is retired — an authored `driver: 'mongo'` is refused with the prescription naming `mongodb`, `OS_DATABASE_DRIVER=mongo` is no longer a driver selection, and `datasource-driver-mongo-to-mongodb` leaves the load path
7+
8+
Clause-②: yes (narrowing)
9+
10+
**BREAKING (authoring):** `mongo` was a second accepted spelling of the MongoDB driver id `mongodb` through 17: the loader rewrote an authored `datasource.driver: 'mongo'` to `mongodb` with a notice, and both boot hosts accepted `OS_DATABASE_DRIVER=mongo`. At 18 it is retired. `DriverType` refuses it by name at every door that parses a datasource (`defineStack`, `os validate`, `os build`, `os lint`, the metadata and datasource APIs): "Datasource `driver: 'mongo'` was removed in @objectstack/spec 18 (ADR-0087 D2) … Use `driver: 'mongodb'`." The refusal is case- and whitespace-insensitive, and it is published in the JSON Schema too (a `pattern` on `driver`), so a document validated against `json-schema/**` is refused as well. Both boot hosts now refuse `OS_DATABASE_DRIVER=mongo` (and a standalone stack's `databaseDriver: 'mongo'`) with their "Supported drivers" list, which names `mongodb`. The ADR-0087 D2 conversion `datasource-driver-mongo-to-mongodb` is retired from the load path (`retiredAfter: '17.7.0'`), so the authoring funnel no longer rewrites the spelling.
11+
12+
Migration:
13+
14+
- `datasource.driver: 'mongo'` → `driver: 'mongodb'`. Change the value; the driver it builds and the `config` contract it is judged against are unchanged.
15+
- `OS_DATABASE_DRIVER=mongo` → `OS_DATABASE_DRIVER=mongodb`, or unset it and let a `mongodb://` or `mongodb+srv://` database URL select the driver. The same for `databaseDriver: 'mongo'` passed to the standalone stack.
16+
- A script or integration that creates or updates a datasource through the metadata or datasource REST API with `driver: 'mongo'` → send `driver: 'mongodb'`.
17+
18+
Run `os migrate meta --from 16` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. The D3 entry `datasource-driver-mongo-alias-retired` lists what the chain cannot reach: environment variables, launch scripts and API callers.
19+
20+
Stored metadata needs no edit. Every stored-row rehydration seam replays the retired conversion, so a `sys_metadata` datasource row written as `mongo` (the Setup → Datasources form wrote that until the canonical id was renamed to `mongodb`) keeps loading as `mongodb`. The spelling also stays on the spec's driver CONTRACT face (`resolveDriverId('mongo')` still answers `mongodb`): a reader that judges a stored body as written, such as the read-path credential redactor, keeps judging a legacy row as mongodb. Measured with the spelling deleted outright, that redactor served all ten mongodb passthrough secrets of such a row in cleartext.
21+
22+
`@objectstack/spec` also exports `RETIRED_DRIVER_ID_GUIDANCE` (retired spelling → its prescription), the table the refusal is derived from. `@objectstack/driver-mongodb`'s README no longer lists `mongo` as a recognised `OS_DATABASE_DRIVER` value.
23+
24+
<!-- adr-0087: registered datasource-driver-mongo-alias-retired -->

‎content/docs/data-modeling/drivers.mdx‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ libSQL data stayed untouched. Pass the token with `--database-auth-token`
7777
| **MySQL** | `@objectstack/driver-sql` (peer: `mysql2`) | `SqlDriver` | `mysql` \| `mysql2` (supported, with three dialect caveats — see [below](#mysql-dialect-caveats)) |
7878
| **SQLite** | `@objectstack/driver-sql` (peer: `better-sqlite3`) | `SqlDriver` | `sqlite` \| `sql` |
7979
| **SQLite (WASM)** | `@objectstack/driver-sqlite-wasm` | `SqliteWasmDriver` | `sqlite-wasm` \| `wasm-sqlite` \| `wasm` |
80-
| **MongoDB** | `@objectstack/driver-mongodb` | `MongoDBDriver` | `mongodb` \| `mongo` (single-tenant only — see [below](#multi-tenancy-not-supported)) |
80+
| **MongoDB** | `@objectstack/driver-mongodb` | `MongoDBDriver` | `mongodb` (single-tenant only — see [below](#multi-tenancy-not-supported)) |
8181
| **Turso / libSQL** | `@objectstack/driver-turso` (optional install — see the callout above) | `TursoDriver` | `turso` \| `libsql` |
8282
| **Memory** | `@objectstack/driver-memory` | `InMemoryDriver` | none — not a boot store (see [Memory Driver](#memory-driver)) |
8383

@@ -100,9 +100,14 @@ driver ships that contract as a zod schema, exported from `@objectstack/spec/dat
100100
| `mysql` \| `mysql2` \| `mariadb` | `MysqlConfigSchema` | `url`, `host`, `port`, `database`, `username`, `password`, `ssl`, `autoMigrate` |
101101
| `sqlite` \| `sqlite3` | `SqliteConfigSchema` | `filename`, `autoMigrate` |
102102
| `sqlite-wasm` \| `wasm-sqlite` | `SqliteWasmConfigSchema` | `filename`, `persist` |
103-
| `mongo` \| `mongodb` | `MongoConfigSchema` | `url`, `host`, `port`, `database`, `username`, `password`, `authSource`, `options` |
103+
| `mongodb` | `MongoConfigSchema` | `url`, `host`, `port`, `database`, `username`, `password`, `authSource`, `options` |
104104
| `memory` \| `in-memory` \| `mingo` | `MemoryConfigSchema` | `initialData`, `strictMode`, `persistence` |
105105

106+
`mongo` was a second spelling of `mongodb` until 18. It is retired: an authored
107+
`driver: 'mongo'` is refused with the prescription naming `mongodb`, and
108+
`OS_DATABASE_DRIVER=mongo` is no longer a driver selection. `os migrate meta --from 16`
109+
lists the edit for existing sources, and stored datasource rows keep loading.
110+
106111
An unrecognised key is rejected with its correction, at authoring time and in the
107112
Setup → Datasources wizard alike:
108113

@@ -185,11 +190,11 @@ ObjectStack — is what makes them secrets:
185190

186191
| Driver | Key | What it carries |
187192
| :--- | :--- | :--- |
188-
| `mongo` \| `mongodb` | `options.proxyPassword` | SOCKS5 proxy password |
189-
| `mongo` \| `mongodb` | `options.tlsCertificateKeyFilePassword` | TLS key-file passphrase |
190-
| `mongo` \| `mongodb` | `options.key` | TLS private key material (PEM) |
191-
| `mongo` \| `mongodb` | `options.passphrase` | TLS key passphrase |
192-
| `mongo` \| `mongodb` | `options.authMechanismProperties.AWS_SESSION_TOKEN` | AWS STS session token (`MONGODB-AWS` auth) |
193+
| `mongodb` | `options.proxyPassword` | SOCKS5 proxy password |
194+
| `mongodb` | `options.tlsCertificateKeyFilePassword` | TLS key-file passphrase |
195+
| `mongodb` | `options.key` | TLS private key material (PEM) |
196+
| `mongodb` | `options.passphrase` | TLS key passphrase |
197+
| `mongodb` | `options.authMechanismProperties.AWS_SESSION_TOKEN` | AWS STS session token (`MONGODB-AWS` auth) |
193198
| `turso` \| `libsql` | `encryptionKey` | AES-256 key for the local database file |
194199

195200
Unlike `password` / `authToken` (typed `never` in their schemas — the parse

‎packages/cli/src/utils/config.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -617,8 +617,9 @@ const AUTHORED_SOURCE_PRELUDE: readonly string[] = [
617617
* A call the current schema ACCEPTS has already run the producer's load-time
618618
* ADR-0087 D2 conversion pass when it returns. That pass runs in both of its
619619
* modes, and no option skips it. So its result is canonical already. Handed
620-
* to the chain as it was, a conversion the load still applies
621-
* (`driver: 'mongo'`) had already happened: `applied` came back empty,
620+
* to the chain as it was, a conversion the load still applies (it was
621+
* measured on `driver: 'mongo'`, a spelling since retired from the load path
622+
* at 18) had already happened: `applied` came back empty,
622623
* `--write` wrote nothing, and every later load still printed the notice
623624
* that sends the author to this command.
624625
*
@@ -651,7 +652,8 @@ const AUTHORED_SOURCE_PRELUDE: readonly string[] = [
651652
* what the author wrote. It asks in the producer's `strict: false` mode, with
652653
* the producer's internal parameter that skips the load-time ADR-0087 D2
653654
* conversion pass ({@link SPEC_AUTHORED_INPUT_OPTION}, #22256). Without that
654-
* parameter a conversion the load still applies (`driver: 'mongo'`) was
655+
* parameter a conversion the load still applies (measured on
656+
* `driver: 'mongo'`, since retired from the load path at 18) was
655657
* applied while the input was produced again, so the chain never listed it
656658
* and `--write` never wrote it — the one-package defect, inside every package
657659
* body. With it, composition runs its own rule over the authored inputs, and

‎packages/cli/src/utils/driver-vocabulary-parity.test.ts‎

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,12 +266,28 @@ describe('driver vocabulary parity: `os start` and `os migrate` answer alike (#6
266266
expect(resolveStandaloneDatabase({ artifactPath: '/nonexistent/objectstack.json' }).driver).toBe('sqlite');
267267
});
268268

269-
it('`mongo` and `mongodb` both select the renamed canonical id on both hosts', () => {
269+
// `mongo` was retired as a value at 18 (#23543 F1): it left the selection
270+
// face, so both hosts refuse it alike, each enumerating `mongodb` among the
271+
// spellings that work. It stays on the CONTRACT face only, so a stored legacy
272+
// `datasource.driver: 'mongo'` row keeps its contract and its redaction.
273+
it('`mongodb` selects the canonical id on both hosts; the retired `mongo` is refused by both, naming `mongodb`', () => {
270274
const url = URL_FOR.mongodb!;
271-
for (const spelling of ['mongo', 'mongodb']) {
272-
expect(cliVerdict(spelling, url)).toEqual({ accepted: true, driverId: 'mongodb' });
273-
expect(standaloneVerdict(spelling, url)).toEqual({ accepted: true, driverId: 'mongodb' });
275+
expect(cliVerdict('mongodb', url)).toEqual({ accepted: true, driverId: 'mongodb' });
276+
expect(standaloneVerdict('mongodb', url)).toEqual({ accepted: true, driverId: 'mongodb' });
277+
278+
expect(cliVerdict('mongo', url)).toEqual({ accepted: false });
279+
expect(standaloneVerdict('mongo', url)).toEqual({ accepted: false });
280+
const cli = refusalMessage(() => resolveStorageDefinition(resolveDriverType('mongo', url), { databaseUrl: url, isDev: false }));
281+
process.env.OS_DATABASE_DRIVER = 'mongo';
282+
process.env.OS_DATABASE_URL = url;
283+
const standalone = refusalMessage(() => resolveStandaloneDatabase({ artifactPath: '/nonexistent/objectstack.json' }));
284+
for (const [host, message] of [['os start', cli], ['os migrate', standalone]] as const) {
285+
expect(message, host).toContain('"mongo"');
286+
expect(message, host).toMatch(/Supported drivers: [^.]*\bmongodb\b/);
287+
expect(message, host).not.toMatch(/Supported drivers: [^.]*\bmongo\b(?!db)/);
274288
}
289+
290+
expect(resolveDatabaseDriverId('mongo')).toBeUndefined();
275291
expect(resolveDriverId('mongo')).toBe('mongodb');
276292
});
277293
});

‎packages/cli/test/migrate-meta-composed-load-conversions.test.ts‎

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
/**
44
* `os migrate meta` on a COMPOSED project —
55
* `composeStacks([defineStack(…), …], { manifest: 'preserve' })` — whose
6-
* package body carries a spelling the load still converts
7-
* (`datasources[].driver: 'mongo'`) (#22256, the composed half).
6+
* package body carries a spelling the load still converts (a flow-level retry
7+
* policy's `retryDelayMs`, renamed `backoffMs` by `retry-policy-converged`)
8+
* (#22256, the composed half). It was measured on `datasources[].driver:
9+
* 'mongo'`, until that spelling was retired as a value at 18 and its
10+
* conversion left the load path (#23543 F1).
811
*
912
* ## The defect
1013
*
@@ -153,7 +156,7 @@ const json = (run: Run): any => JSON.parse(run.stdout);
153156
const sites = (list: Array<{ conversionId: string; path: string; from?: string; to?: string }>) =>
154157
list.map(({ conversionId, path, from, to }) => ({ conversionId, path, from, to }));
155158
const conversionIds = (list: readonly { conversionId: string }[]) => list.map((c) => c.conversionId);
156-
const bodyDriver = (config: any): string => config.packages[0].manifest.datasources[0].driver;
159+
const bodyErrorHandling = (config: any): Record<string, unknown> => config.packages[0].manifest.flows[0].errorHandling;
157160

158161
beforeAll(() => {
159162
root = mkdtempSync(join(tmpdir(), 'os-migrate-meta-composed-load-conversions-'));
@@ -171,9 +174,10 @@ afterAll(() => {
171174

172175
/**
173176
* The service package: a stack the current schema accepts, whose one
174-
* non-canonical spelling is a datasource driver id the load still converts
175-
* (`retiredFromLoadPath: false`); the `url` is what makes a mongo datasource
176-
* valid, so the call is accepted.
177+
* non-canonical spelling is a key the load still converts (`retiredFromLoadPath`
178+
* unset): the flow-level retry policy's `retryDelayMs`, which
179+
* `retry-policy-converged` renames `backoffMs` before the parse, so the call is
180+
* accepted.
177181
*/
178182
const SERVICE_STACK = `import { defineStack } from '@objectstack/spec';
179183
@@ -188,7 +192,16 @@ export const ServiceStack = defineStack({
188192
},
189193
},
190194
],
191-
datasources: [{ name: 'docs', label: 'Docs', driver: 'mongo', config: { url: 'mongodb://mongo.internal:27017/docs' } }],
195+
flows: [
196+
{
197+
name: 'probe_sync',
198+
label: 'Sync',
199+
type: 'autolaunched',
200+
errorHandling: { strategy: 'retry', maxRetries: 3, retryDelayMs: 2000 },
201+
nodes: [{ id: 'start', type: 'start', label: 'Start' }, { id: 'end', type: 'end', label: 'End' }],
202+
edges: [{ id: 'e1', source: 'start', target: 'end' }],
203+
},
204+
],
192205
});
193206
`;
194207

@@ -213,13 +226,15 @@ const composed = (service: string): Record<string, string> => ({
213226
'src/app.stack.ts': APP_STACK,
214227
});
215228

216-
const MONGO_CONVERSION = 'datasource-driver-mongo-to-mongodb';
217-
const MONGO_BODY_SITE = {
218-
conversionId: MONGO_CONVERSION,
219-
path: 'packages[0].manifest.datasources[0].driver',
220-
from: 'mongo',
221-
to: 'mongodb',
229+
const RETRY_CONVERSION = 'retry-policy-converged';
230+
const RETRY_BODY_SITE = {
231+
conversionId: RETRY_CONVERSION,
232+
path: 'packages[0].manifest.flows[0].errorHandling.backoffMs',
233+
from: 'retryDelayMs',
234+
to: 'backoffMs',
222235
};
236+
const AUTHORED_DELAY = 'retryDelayMs: 2000';
237+
const CANONICAL_DELAY = 'backoffMs: 2000';
223238
const TIME_BODY_SITE = {
224239
conversionId: 'time-default-utc-suffix-dropped',
225240
path: 'packages[0].manifest.objects[0].fields.starts_at.defaultValue',
@@ -235,16 +250,16 @@ describe('an accepted input: the chain reads the package body as the author wrot
235250
// The control for the last reading below: today the strict load converts it.
236251
const strictBefore = await quietLoad(dir);
237252
expect(strictBefore.stackProvenance).toBe(true);
238-
expect(conversionIds(strictBefore.stackConversions)).toEqual([MONGO_CONVERSION]);
253+
expect(conversionIds(strictBefore.stackConversions)).toEqual([RETRY_CONVERSION]);
239254

240255
const dry = json(await runMeta(dir, ['--from', '16', '--json']));
241-
expect(sites(dry.applied)).toEqual([MONGO_BODY_SITE]);
256+
expect(sites(dry.applied)).toEqual([RETRY_BODY_SITE]);
242257
expect(dry.schemaValid).toBe(true);
243258

244259
const run = await runMeta(dir, ['--from', '16', '--write', '--json']);
245260
expect(run.exitCode, run.stdout + run.stderr).toBeUndefined();
246261
const out = json(run);
247-
expect(sites(out.applied)).toEqual([MONGO_BODY_SITE]);
262+
expect(sites(out.applied)).toEqual([RETRY_BODY_SITE]);
248263
expect(out.write.status).toBe('written');
249264
expect(out.write.files).toEqual([{ file: 'src/service.stack.ts', sites: 1 }]);
250265
expect(out.write.manual).toEqual([]);
@@ -253,7 +268,7 @@ describe('an accepted input: the chain reads the package body as the author wrot
253268
// The site and nothing else: every other file keeps its bytes.
254269
expect(snapshot(dir)).toEqual({
255270
...before,
256-
'src/service.stack.ts': edit(before['src/service.stack.ts']!, "driver: 'mongo'", "driver: 'mongodb'"),
271+
'src/service.stack.ts': edit(before['src/service.stack.ts']!, AUTHORED_DELAY, CANONICAL_DELAY),
257272
});
258273

259274
const again = json(await runMeta(dir, ['--from', '16', '--json']));
@@ -270,11 +285,13 @@ describe('an accepted input: the chain reads the package body as the author wrot
270285
const dir = writeProject(composed(SERVICE_STACK));
271286

272287
const authored = await quietLoad(dir, true);
273-
expect(bodyDriver(authored.config)).toBe('mongo');
288+
expect(bodyErrorHandling(authored.config)).toMatchObject({ retryDelayMs: 2000 });
289+
expect(bodyErrorHandling(authored.config)).not.toHaveProperty('backoffMs');
274290
expect(authored.stackProvenance).toBe(true);
275291

276292
const strict = await quietLoad(dir);
277-
expect(bodyDriver(strict.config)).toBe('mongodb');
293+
expect(bodyErrorHandling(strict.config)).toMatchObject({ backoffMs: 2000 });
294+
expect(bodyErrorHandling(strict.config)).not.toHaveProperty('retryDelayMs');
278295
}, RUN_TIMEOUT);
279296

280297
it('an input defined twice starts from the innermost argument, and is listed once', async () => {
@@ -284,16 +301,16 @@ describe('an accepted input: the chain reads the package body as the author wrot
284301
'\n}));\n',
285302
)));
286303
const out = json(await runMeta(dir, ['--from', '16', '--json']));
287-
expect(sites(out.applied)).toEqual([MONGO_BODY_SITE]);
304+
expect(sites(out.applied)).toEqual([RETRY_BODY_SITE]);
288305
expect(out.schemaValid).toBe(true);
289306
}, RUN_TIMEOUT);
290307
});
291308

292309
describe('a refused input the shim produces again, carrying a load-path spelling too', () => {
293310
// The `time` default with the UTC suffix is refused by the current schema,
294311
// so the shim hands the call's argument through and produces it again in
295-
// `strict: false` mode for the composer; the driver id is the input's one
296-
// load-path conversion.
312+
// `strict: false` mode for the composer; the flow's retry delay key is the
313+
// input's one load-path conversion.
297314
const MIXED = edit(
298315
SERVICE_STACK,
299316
" name: { type: 'text', label: 'Name' },\n",
@@ -305,20 +322,20 @@ describe('a refused input the shim produces again, carrying a load-path spelling
305322
const before = snapshot(dir);
306323

307324
const dry = json(await runMeta(dir, ['--from', '16', '--json']));
308-
expect(sites(dry.applied)).toEqual([MONGO_BODY_SITE, TIME_BODY_SITE]);
325+
expect(sites(dry.applied)).toEqual([RETRY_BODY_SITE, TIME_BODY_SITE]);
309326

310327
const run = await runMeta(dir, ['--from', '16', '--write', '--json']);
311328
expect(run.exitCode, run.stdout + run.stderr).toBeUndefined();
312329
const out = json(run);
313-
expect(sites(out.applied)).toEqual([MONGO_BODY_SITE, TIME_BODY_SITE]);
330+
expect(sites(out.applied)).toEqual([RETRY_BODY_SITE, TIME_BODY_SITE]);
314331
expect(out.write.status).toBe('written');
315332
expect(out.write.files).toEqual([{ file: 'src/service.stack.ts', sites: 2 }]);
316333
expect(snapshot(dir)).toEqual({
317334
...before,
318335
'src/service.stack.ts': edit(
319336
edit(MIXED, "defaultValue: '10:00Z'", "defaultValue: '10:00'"),
320-
"driver: 'mongo'",
321-
"driver: 'mongodb'",
337+
AUTHORED_DELAY,
338+
CANONICAL_DELAY,
322339
),
323340
});
324341

@@ -332,7 +349,7 @@ describe('a refused input the shim produces again, carrying a load-path spelling
332349
});
333350

334351
describe('control: an already-canonical composed project', () => {
335-
const CANONICAL = edit(SERVICE_STACK, "driver: 'mongo'", "driver: 'mongodb'");
352+
const CANONICAL = edit(SERVICE_STACK, AUTHORED_DELAY, CANONICAL_DELAY);
336353

337354
it('writes nothing, and its --json summary is the one the built composition gives', async () => {
338355
const dir = writeProject(composed(CANONICAL));

0 commit comments

Comments
 (0)