Skip to content

Commit b06b2db

Browse files
claude[bot]claudeos-sales
authored
fix(cli): one column width for the file family in both os generate migration formats (#18014)
Fixes #17883 Clause-②: no `os generate migration` gave the file family — `file` / `image` / `avatar` / `video` / `audio` — two different column widths depending on `--format`. The typescript format emitted a bare `table.string(name)`, which `generate.ts` itself states is knex's `varchar(255)`, while `--format sql` emitted `VARCHAR(2048)` for the same field. ## The stop condition, measured first Triage ruled the direction (the two halves must agree) with a hard fence: **if 255 turns out to be deliberate, stop and route the card to `needs-user-decision` instead of forcing the alignment.** `generate.ts:1456` says `VARCHAR(50)` and `VARCHAR(2048)` "were widths this file invented", and invented numbers have history. Five readings, taken on this branch's base `611795e40`: 1. **ADR-0104** records the maintainer ruling on #15041 verbatim: *"The driver is the side that moves; the generator's `VARCHAR(2048)` already states the ruled end-state and stands."* Its column section adds the operative criterion: the driver's width mirror "answers for the family what the generator answers, so the pin that sweeps the other string classes can sweep this one — **a width the two sides disagree on would be this fork again, one level down**." 2. **`driver-sql`'s own constant header** (`MEDIA_ID_VARCHAR_CHARS = 2048`) states it as a rule, not a taste: *"A driver that created knex's default `varchar(255)` here would open a fresh divergence between the two producers of the same column."* 3. **`media-column-move.ts:101`** — `MEDIA_ID_MOVE_WIDTH = 2048`, used in the `type varchar(2048) using (…)` retype. Read as a control only; not touched. 4. **History**: `git log -S` on the arm shows the family joined the typescript switch's REFERENCE arm in `431979e67` (#14834), a card about giving every `FieldType` member an *entry* — a presence fix. That arm's derivation is the target row's `id` column (`table.string('id').primary()`), which was never the file family's derivation. The 255 was **inherited by sharing an arm**, never chosen for this family. 5. **No dialect depends on it**: the driver's own ceiling is `MAX_VARCHAR_CHARS = 16383`, and both the SQL format and the driver already ship 2048 for this family everywhere. No reading makes 255 deliberate, so the alignment stands and the card does not move to `needs-user-decision`. The typescript half is the side that was out of step. ## The change The family leaves the reference arm and takes one of its own. The width is **read from the SQL format's own entry** rather than retyped beside it, so the two formats cannot drift apart again — a second literal `2048` would be the same defect one line over. ```diff - table.string('cover_image').nullable(); + table.string('cover_image', 2048).nullable(); ``` ⛔ The SQL half did not move. 2048 is the already-shipped target; the repair is the typescript half joining it, never the two halves meeting in the middle. The new pin asserts the agreed width **against `driver-sql`'s own constant** precisely so that two halves that had both drifted to knex's default could not satisfy it. ## Acceptance — both directions, five types, with the control the file documents One probe object, one field per type, both formats, before and after the fix. The whole delta is five lines: ``` table.string('id').primary(); - table.string('f_file').nullable(); + table.string('f_file', 2048).nullable(); - table.string('f_image').nullable(); + table.string('f_image', 2048).nullable(); - table.string('f_avatar').nullable(); + table.string('f_avatar', 2048).nullable(); - table.string('f_video').nullable(); + table.string('f_video', 2048).nullable(); - table.string('f_audio').nullable(); + table.string('f_audio', 2048).nullable(); table.text('f_text').nullable(); [control — byte-identical] table.string('f_lookup').nullable(); [control — byte-identical] table.string('f_master_detail').nullable(); [control — byte-identical] table.string('f_user').nullable(); [control — byte-identical] table.string('f_tree').nullable(); [control — byte-identical] table.string('f_autonumber').nullable(); [control — byte-identical] ``` - **Field by field, all five**: `--format sql` gives `VARCHAR(2048)` and the typescript format gives `table.string(name, 2048)` for `file`, `image`, `avatar`, `video` and `audio`. One verified is not five, so the pin sweeps `FILE_REFERENCE_TYPES` imported from the spec rather than a list written out. - **The control did not move**: `f_text` — the non-file family the file itself documents at `generate.ts:1403`-`:1405`, where both halves already agree — is byte-identical before and after, and so is every other non-family column and the entire `--format sql` emission (the diff above has no hunk on the SQL side at all). ⛔ No control was invented; the documented one was used. **Reverse verification.** With the fix committed, the pre-fix `generate.ts` was put back on disk and the new pin run against it: **5 of 8 tests red**, one per family member, with the three control/anti-vacuity tests staying green — the discriminating direction. On-disk proof of the mutation rather than an editor's exit code: the injected resolver went 4 occurrences ⇒ 0, the blob hash moved `8d8c34a0` ⇒ `1fccbeff`. Restore verified by state, not by exit code: blob back to `8d8c34a0`, `git diff HEAD` empty. The subject resolves through a relative intra-package import (`./generate.js`), never through a dependency's `exports`, so no `dist` sits between the edit and the verdict. ## Premise falsified — acceptance item 4 pointed at the wrong table The dispatch order asked for the self-describing table at `generate.ts:1461` to be updated, on the reading that it "still records the pre-#15989 three-way reading (`driver varchar(255)`) and is wrong on its first column". Re-derived on this branch, that is not so: ``` :1460 // f_phone driver varchar(255) sql gen varchar(50) ts gen varchar(255) :1461 // f_url driver varchar(255) sql gen varchar(2048) ts gen varchar(255) ``` Those two rows are about `phone` and `url` — the **STRING family**, not the file family. #15989 moved the file family to 2048; it did not touch `url`, whose driver column is still `declaredVarcharLength(field)` = `DEFAULT_STRING_VARCHAR_CHARS` = 255 with no declared `maxLength`. The rows are the measured *pre-*#16091 reading kept deliberately as the rationale for the entries that replaced them ("`VARCHAR(50)` and `VARCHAR(2048)` **were** widths this file invented"). Rewriting `driver varchar(255)` to 2048 there would have introduced an error, so those lines are byte-identical in this PR. The stale text about the file family is a different paragraph — `generate.ts:1375`-`:1381`, which held the family out of scope on the ground that it "is in the driver's `JSON_COLUMN_TYPES` today", untrue since #15989 landed. That paragraph is rewritten, which serves what acceptance item 4 actually asked for: no table left out of date the moment the fix lands. The same stale sentence in `generate-field-type-vocabulary.pin.test.ts`'s header is corrected with it. ## Verification Base `611795e40`; readings taken at `3b3ba8a35`. Exit codes captured before any pipe. | Run | Result | |:--|:--| | `pnpm --filter '@objectstack/cli^...' build` | `VERDICT command-exit 0` (dependency closure) | | `pnpm --filter @objectstack/cli exec vitest run --project unit` | `VERDICT command-exit 0` — 206 files, 2958 tests passed | | `pnpm --filter @objectstack/cli typecheck` | `VERDICT command-exit 0` (`tsc --noEmit` + `check:test-typecheck`, ledger held) | | derived gate family, `scripts/pm/dispatch-gates.mjs --commands` | 62 derived, **62 run, 0 NOT-MEASURED, 0 UNRUN**, every one exit 0 | | `eslint . --no-inline-config --format json` | exit 0 — **6705 files, 0 errors, 0 warnings** (whole tree, not a narrowing) | Both typecheck programs were shown to contain the changed files (`tsc -p tsconfig.json --listFiles`: 1532 files, `commands/generate.ts` and the new pin both present), so the green is about them and not around them. Three runs needed a prerequisite before they measured anything, and each was re-run to a real verdict rather than recorded as a pass: `check:dual-build-cjs-loads` and `check:i18n-coverage` both exited **3** (`PREREQUISITE NOT MET` — built output missing) until the build they prescribe was run, then exit 0; `check:type-check-debt` exited 124 at a 240s budget and then **3** under `--max-old-space-size=4096` (the child `tsc` OOM'd inside my own cap), and exit 0 at 6144 MB, which the gate names as its own CI-shaped ceiling. The `integration` tier is declared to CI: this diff touches no spawn entry, no `bin/`, no `test/helpers/serve-process.ts` and no driver or kernel boot path. ## Changeset — measured, not assumed `patch` on `@objectstack/cli`. `packages/cli` is published (not private), `files[]` is `dist README.md CHANGELOG.md`, and `tsconfig.build.json` compiles `src`. After `pnpm --filter @objectstack/cli build`, the changed subject is present in the shipped path: `packages/cli/dist/commands/generate.js` carries the new resolver (4 occurrences) and the sized emission (1). Positive control, a known shipped symbol in the same file: 3 hits. Negative control, a nonsense token: 0 hits. Something published moves, so a changeset is owed; a bug fix in a released package takes `patch`. ## Acceptance notes - ⛔ Fences honoured: `media-column-move.ts` is read as a control and not edited; the `os migrate files-to-references` retype statement is untouched; no width outside the file family moved; `packages/spec` is not touched; `content/docs/releases/**` is not touched. No export was added to a published entry point. - `noted, not filed`: on MySQL an index over a `varchar(2048)` utf8mb4 column exceeds InnoDB's 3072-byte index key limit, and since #16317 both formats emit declared `unique` indexes. This is not created here — the SQL format and the driver already build the column at 2048 — and it is unmeasured (no MySQL cell in this container), so it is an observation rather than a card. Successor: the `driver-sql` seat, on the next card that touches the media column width; on a per-dialect pin it would surface in `Temporal Conformance (live PG + MySQL)`. - `noted, not filed`: `generate-string-family-width.pin.test.ts` excludes `FILE_REFERENCE_TYPES` from the character-catch-all sweep and gives a per-deployment reason that is now only half true (the driver's arm is per-deployment; the generator's width is not). The exclusion is still correct and the file family now has its own pin, so nothing is uncovered. Successor: whoever next edits that pin's derivation. - The new pin lives at `packages/cli/src/commands/generate-file-reference-width.pin.test.ts`, beside its seven sibling `generate-*.pin.test.ts` files, rather than under `packages/cli/test/**` as the claim comment's declared file face spelled it. Same package, unit tier, and `check:tier-file-adoption` / `check:cross-package-test-inputs` both pass. --- _Generated by [Claude Code](https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c)_ --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: os-sales <sales@objectstack.ai> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
1 parent a83dbb6 commit b06b2db

4 files changed

Lines changed: 321 additions & 19 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"@objectstack/cli": patch
3+
---
4+
5+
`os generate migration` gives the file family — `file` / `image` / `avatar` / `video` / `audio` — the **same column width in both formats**. The typescript format emitted a bare `table.string(name)`, knex's `varchar(255)`, while `--format sql` emitted `VARCHAR(2048)` for the same field, so one command answered one field with two widths depending on the flag (#17883).
6+
7+
2048 is not a new number: ADR-0104 ruled the generator's `VARCHAR(2048)` the end-state for this family, `driver-sql` moved to it (`MEDIA_ID_VARCHAR_CHARS`, #15989), and `os migrate files-to-references --apply` retypes the column to `varchar(2048)`. The typescript format was the one producer left at 255 — so a deployment scaffolded from it declared a width the migration it will later run retypes away from.
8+
9+
```diff
10+
- table.string('cover_image').nullable();
11+
+ table.string('cover_image', 2048).nullable();
12+
```
13+
14+
- **No regeneration is required of anyone.** `syncSchema` / `initObjects` are additive and never alter an existing column's type, and a `sys_file` id is far shorter than 255, so nothing stored today is at risk either way. What moves is the **declared** width of tables generated from now on.
15+
- **The width is now read from the sql format's own entry** instead of being retyped beside it, so the two formats cannot drift apart again; `generate-file-reference-width.pin.test.ts` measures both against `driver-sql`'s constant, which is what stops the two halves from "meeting in the middle" at some third value.
16+
- ⛔ **Nothing outside the family moved.** The `text` family, the reference types the file family used to share an arm with (`lookup` / `master_detail` / `user` / `tree`), `autonumber`, and every `--format sql` answer are byte-identical.

‎packages/cli/src/commands/generate-field-type-vocabulary.pin.test.ts‎

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,14 @@
9393
* `FIELD_TYPE_MAP` and a scalar column in `FIELD_TYPE_SQL_MAP` is a
9494
* contradiction whoever is right, and `autonumber` was exactly that.
9595
*
96-
* ⚠️ Still NOT asserted, deliberately: the FILE_REFERENCE_TYPES family. Those
97-
* five ARE in the driver's `JSON_COLUMN_TYPES` today while this generator gives
98-
* them a varchar — but that is #14657's ADR-0104 D3 answer against a driver
99-
* that is still pre-D3, i.e. a decision about which side moves, not a wrong
100-
* value to correct. It is recorded below as a measured divergence so it cannot
101-
* be mistaken for coverage, and filed rather than fixed here.
96+
* ⚰️ The FILE_REFERENCE_TYPES family used to be excluded from all of this,
97+
* deliberately: those five were in the driver's `JSON_COLUMN_TYPES` while this
98+
* generator gave them a varchar — #14657's ADR-0104 D3 answer against a driver
99+
* that was still pre-D3, i.e. a decision about which side moves rather than a
100+
* wrong value to correct. The ruling on #15041 decided it and #15989 landed it,
101+
* so the exclusion below became coverage. [#17883] The width the two migration
102+
* formats emit for the family is a second question and has a pin of its own —
103+
* `generate-file-reference-width.pin.test.ts`.
102104
*
103105
* The runtime fallbacks (`|| 'unknown'`, `|| 'TEXT'`, `default:`) stay and are
104106
* NOT dead: they answer a `type` string that is not a `FieldType` at all, which
@@ -375,9 +377,18 @@ function sqlColumn(type: string): string | null {
375377
return m ? m[1] : null;
376378
}
377379

378-
/** The `table.x('f_type')` call one field type contributes, or `null` for none. */
380+
/**
381+
* The `table.x('f_type'[, …])` call one field type contributes, or `null` for
382+
* none.
383+
*
384+
* [#17883] The argument list is part of the call and is captured with it. The
385+
* reader used to stop at the name, so a SIZED call — `table.string(name, 2048)`,
386+
* which is what the file family takes since #17883 — read as no column at all,
387+
* and every assertion below would have been answering a question about absence
388+
* instead of one about the column.
389+
*/
379390
function tsColumn(type: string): string | null {
380-
const m = TS_OUT.match(new RegExp(`^ {4}(table\\.\\w+\\('f_${type}'\\)).*$`, 'm'));
391+
const m = TS_OUT.match(new RegExp(`^ {4}(table\\.\\w+\\('f_${type}'(?:, [^)]*)?\\)).*$`, 'm'));
381392
return m ? m[1] : null;
382393
}
383394

@@ -669,7 +680,12 @@ describe('#14828 — the SQL answers are the platform’s, not this file’s inv
669680

670681
for (const type of FILE_REFERENCE_TYPES) {
671682
expect(sqlColumn(type)).toBe('VARCHAR(2048)');
672-
expect(tsColumn(type)).toBe(`table.string('f_${type}')`);
683+
// [#17883] Was a bare `table.string('f_type')` — knex's varchar(255),
684+
// which left THIS generator's two formats disagreeing about the family
685+
// after the driver had already moved to 2048. The width agreement itself
686+
// is `generate-file-reference-width.pin.test.ts`; what stays here is the
687+
// spelling, so the two pins fail in different sentences.
688+
expect(tsColumn(type)).toBe(`table.string('f_${type}', 2048)`);
673689
}
674690
});
675691
});
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* THE #17883 PIN: one `os generate migration`, one column width for the file
5+
* family — whichever `--format` the author picked.
6+
*
7+
* ## The defect
8+
*
9+
* `generate.ts` carries two migration formats, and they answered the same
10+
* FILE_REFERENCE_TYPES field with two different widths:
11+
*
12+
* ```
13+
* --format sql f_file VARCHAR(2048) FIELD_TYPE_SQL_MAP
14+
* typescript f_file table.string('f_file') knex's varchar(255)
15+
* ```
16+
*
17+
* `generate.ts` states that second equality itself, in as many words on the
18+
* `autonumber` entry: "`table.string(name)` = knex's `varchar(255)`
19+
* (`DEFAULT_STRING_VARCHAR_CHARS`)". The typescript half reached it by riding
20+
* the REFERENCE_VALUE_TYPES arm, whose derivation is the TARGET row's `id`
21+
* column — a derivation that was never the file family's, whose value is an
22+
* opaque `sys_file` id.
23+
*
24+
* ## Why the typescript half is the side that moves
25+
*
26+
* 2048 is not a width invented to settle a disagreement. It is the width
27+
* ADR-0104 ruled and the rest of the tree has already shipped:
28+
*
29+
* - ADR-0104, recording the maintainer ruling on #15041: "The driver is the
30+
* side that moves; the generator's `VARCHAR(2048)` already states the ruled
31+
* end-state and stands."
32+
* - #15989 moved `driver-sql`: `MEDIA_ID_VARCHAR_CHARS = 2048` and
33+
* `table.string(name, MEDIA_ID_VARCHAR_CHARS)` on the moved arm.
34+
* - `os migrate files-to-references --apply` retypes the column to
35+
* `varchar(2048)` (`MEDIA_ID_MOVE_WIDTH`, transcribed from the same width).
36+
*
37+
* So a deployment scaffolded from the typescript format was the one place left
38+
* standing at 255 — its declared width disagreeing with the width the migration
39+
* it will later run retypes to. ⛔ The repair is the typescript half joining
40+
* the shipped target, never the two halves meeting in the middle, which is why
41+
* the sweep below asserts the AGREED width against the driver's own constant
42+
* rather than merely asserting the two formats equal. Two formats that had both
43+
* drifted to 255 would satisfy an equality-only pin perfectly.
44+
*
45+
* ## The control, and where it comes from
46+
*
47+
* ⛔ Not invented here: `generate.ts` documents one. Its `text` entry carries a
48+
* measured row for `f_text` — "sql gen f_text varchar(255) / ts gen f_text
49+
* varchar(255)" — a NON-file family whose two halves already agree. Measured
50+
* before and after the repair, `f_text` and every other non-family column in
51+
* the same emission are byte-identical; only the five family rows move. The
52+
* sweep pins that shape: the family agrees at the driver's width, and the
53+
* neighbours it used to share an arm with keep the unsized call.
54+
*
55+
* ## What this pin does NOT assert
56+
*
57+
* Nothing about live DDL. `generate-string-family-width.pin.test.ts` carries
58+
* the measured-on-PostgreSQL evidence for the character families; this file
59+
* measures agreement between two emitters and the driver's own stated width,
60+
* which is the whole content of the card. And nothing about the JSON-vs-varchar
61+
* question that #15989 settled — `generate-field-type-vocabulary.pin.test.ts`
62+
* holds that one, including the control that reddens if the driver reverts.
63+
*/
64+
65+
import fs from 'node:fs';
66+
import path from 'node:path';
67+
import { fileURLToPath } from 'node:url';
68+
69+
import { FILE_REFERENCE_TYPES, REFERENCE_VALUE_TYPES } from '@objectstack/spec/data';
70+
import { describe, expect, it } from 'vitest';
71+
72+
import { generateMigrationSql, generateMigrationTs } from './generate.js';
73+
74+
/** `packages/drivers/driver-sql/src` — declared for `@objectstack/cli#test` in `turbo.json`. */
75+
const DRIVER_SQL_SRC = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../drivers/driver-sql/src');
76+
const SQL_DRIVER_SOURCE = fs.readFileSync(path.join(DRIVER_SQL_SRC, 'sql-driver.ts'), 'utf8');
77+
78+
/** The control the generator documents, plus the arm the family used to ride. */
79+
const CONTROL_TYPES = ['text', ...REFERENCE_VALUE_TYPES, 'autonumber'] as const;
80+
81+
function probeConfig(): Record<string, unknown> {
82+
const fields: Record<string, Record<string, unknown>> = {};
83+
for (const type of [...FILE_REFERENCE_TYPES, ...CONTROL_TYPES]) fields[`f_${type}`] = { type };
84+
return { objects: { probe: { name: 'probe', label: 'Probe', fields } } };
85+
}
86+
87+
const SQL_OUT = generateMigrationSql(probeConfig());
88+
const TS_OUT = generateMigrationTs(probeConfig());
89+
90+
/** The column declaration one field type contributes to the sql format. */
91+
function sqlColumn(type: string): string | null {
92+
const m = SQL_OUT.match(new RegExp(`^ {2}"f_${type}" (.+?),?$`, 'm'));
93+
return m ? m[1] : null;
94+
}
95+
96+
/** The `table.x('f_type'[, …])` call one field type contributes to the typescript format. */
97+
function tsColumn(type: string): string | null {
98+
const m = TS_OUT.match(new RegExp(`^ {4}(table\\.\\w+\\('f_${type}'(?:, [^)]*)?\\)).*$`, 'm'));
99+
return m ? m[1] : null;
100+
}
101+
102+
/** The character width one format states, or `null` when it states none. */
103+
function sqlWidth(type: string): number | null {
104+
const m = /^VARCHAR\((\d+)\)$/.exec(sqlColumn(type) ?? '');
105+
return m ? Number(m[1]) : null;
106+
}
107+
108+
/**
109+
* The width the typescript format's call carries. `null` for a bare
110+
* `table.string(name)` — which is a WIDTH, knex's 255, and is exactly the
111+
* answer this card is about; the sweep names it rather than reading it as
112+
* absence.
113+
*/
114+
function tsWidth(type: string): number | null {
115+
const m = /^table\.string\('f_[a-z_]+', (\d+)\)$/.exec(tsColumn(type) ?? '');
116+
return m ? Number(m[1]) : null;
117+
}
118+
119+
/** The driver's own width for a moved media column, read where it is declared. */
120+
function driverMediaChars(): number {
121+
const m = SQL_DRIVER_SOURCE.match(/const MEDIA_ID_VARCHAR_CHARS = (\d+);/);
122+
if (!m) {
123+
throw new Error(
124+
'MEDIA_ID_VARCHAR_CHARS is no longer declared in driver-sql/src/sql-driver.ts. That constant ' +
125+
'is the width #15989 moved the driver to, and this pin has nothing to measure the generators ' +
126+
'against without it — re-read ADR-0104 and #15989 before re-anchoring it.',
127+
);
128+
}
129+
return Number(m[1]);
130+
}
131+
132+
describe('#17883 — both migration formats give the file family one width', () => {
133+
it('control — the probe really emitted both formats, and the readers discriminate', () => {
134+
// Non-vacuity: readers that matched nothing would make every `toBe(null)`
135+
// below pass while measuring literally nothing.
136+
expect(FILE_REFERENCE_TYPES.size).toBe(5);
137+
expect(SQL_OUT).toContain('CREATE TABLE IF NOT EXISTS "probe"');
138+
expect(TS_OUT).toContain("db.schema.createTable('probe'");
139+
expect(sqlColumn('file')).not.toBeNull();
140+
expect(tsColumn('file')).not.toBeNull();
141+
// …and they tell two different answers apart, in both formats.
142+
expect(sqlColumn('text')).not.toBe(sqlColumn('file'));
143+
expect(tsColumn('text')).not.toBe(tsColumn('file'));
144+
// A width reader that always answered `null` would make the sweep vacuous.
145+
expect(sqlWidth('lookup')).not.toBeNull();
146+
expect(sqlWidth('text')).toBeNull();
147+
});
148+
149+
it('the driver states a media width this pin can anchor to', () => {
150+
// The anchor is READ, never transcribed: a literal here would be a third
151+
// copy of the number the card exists to stop copying.
152+
expect(driverMediaChars()).toBeGreaterThan(255);
153+
expect(SQL_DRIVER_SOURCE).toContain('table.string(name, MEDIA_ID_VARCHAR_CHARS)');
154+
});
155+
156+
for (const type of FILE_REFERENCE_TYPES) {
157+
it(`${type} takes the same width in both formats, at the shipped target`, () => {
158+
const width = driverMediaChars();
159+
expect(
160+
sqlWidth(type),
161+
`os generate migration --format sql no longer states the ruled end-state width for ${type}. ` +
162+
'ADR-0104 ruled that the GENERATOR states it and the driver moves to it — so a change here ' +
163+
'is the two halves meeting in the middle, which #17883 forbids.',
164+
).toBe(width);
165+
expect(
166+
tsWidth(type),
167+
`os generate migration (typescript) gave ${type} a different width from the sql format. A ` +
168+
'bare `table.string(name)` reads as null here and is knex\'s varchar(255) — the #17883 ' +
169+
'defect exactly: one command, one field, two widths depending on --format.',
170+
).toBe(width);
171+
// Stated once more as the emitted text, so a reader of a failure sees the
172+
// two lines rather than two numbers.
173+
expect(sqlColumn(type)).toBe(`VARCHAR(${width})`);
174+
expect(tsColumn(type)).toBe(`table.string('f_${type}', ${width})`);
175+
});
176+
}
177+
178+
it('the control does not move: the non-file families keep their own answers', () => {
179+
// `f_text` is the control `generate.ts` documents on its own `text` entry —
180+
// a non-file family whose two halves already agree. ⛔ Not invented here.
181+
expect(sqlColumn('text')).toBe('TEXT');
182+
expect(tsColumn('text')).toBe("table.text('f_text')");
183+
184+
// And the arm the family used to ride: a reference column holds the target
185+
// row's id, `table.string('id').primary()` = knex's default width, so it
186+
// takes the UNSIZED call. If the repair had widened this class too, it
187+
// would have carried the file family's width into columns that never
188+
// wanted it.
189+
for (const type of [...REFERENCE_VALUE_TYPES, 'autonumber']) {
190+
expect(tsWidth(type), `${type} was widened along with the file family`).toBeNull();
191+
expect(tsColumn(type)).toBe(`table.string('f_${type}')`);
192+
expect(sqlWidth(type)).not.toBe(driverMediaChars());
193+
}
194+
});
195+
});

0 commit comments

Comments
 (0)