|
| 1 | +--- |
| 2 | +"@objectstack/spec": major |
| 3 | +--- |
| 4 | + |
| 5 | +feat(spec)!: the bare type name is now the AUTHOR state — 1384 aliases flipped, 102 `XInput` synonyms retired (ADR-0122 phase 2, #6083) |
| 6 | + |
| 7 | +A Zod schema denotes two types: `z.input` (what an author writes — defaulted keys |
| 8 | +optional, pre-transform) and `z.infer` (what `.parse()` returns). Until protocol 17 the |
| 9 | +bare name `X` meant the second one in 1384 places and the first one in 86, with nothing |
| 10 | +recorded about which was which. |
| 11 | + |
| 12 | +**[ADR-0122](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0122-schema-type-alias-naming-convention.md) |
| 13 | +settles it: the bare name `X` is the AUTHOR state, `XParsed` is the PARSED state.** |
| 14 | +Phase 1 (16.x, additive) gave every schema with two distinct shapes its `XParsed` name so |
| 15 | +nothing would be stranded. **This release is phase 2: it flips the bare names.** It is the |
| 16 | +breaking half, and it is the reason `@objectstack/spec` goes to 17.0.0. |
| 17 | + |
| 18 | +```ts |
| 19 | +// before (16.x) // after (17.0.0) |
| 20 | +export type Connector = z.infer<…>; export type Connector = z.input<…>; |
| 21 | +export type ConnectorParsed = z.infer<…>; export type ConnectorParsed = z.infer<…>; |
| 22 | +export type ConnectorInput = z.input<…>; // ConnectorInput: RETIRED |
| 23 | +``` |
| 24 | + |
| 25 | +## FROM → TO |
| 26 | + |
| 27 | +There are exactly two migrations, and each has a mechanical test. |
| 28 | + |
| 29 | +### 1. `XInput` → `X` (102 names removed) |
| 30 | + |
| 31 | +The flip made `XInput` a character-for-character synonym of the bare name, and ADR-0122 |
| 32 | +D3 forbids a permanent synonym. Every retired name has the same fix: **drop the `Input` |
| 33 | +suffix.** |
| 34 | + |
| 35 | +```diff |
| 36 | +- import type { ConnectorInput } from '@objectstack/spec/integration'; |
| 37 | +- const c: ConnectorInput = { name: 'acme', label: 'Acme', type: 'saas' }; |
| 38 | ++ import type { Connector } from '@objectstack/spec/integration'; |
| 39 | ++ const c: Connector = { name: 'acme', label: 'Acme', type: 'saas' }; |
| 40 | +``` |
| 41 | + |
| 42 | +Find them: `rg '\b\w+Input\b' --type ts` over your own code, then keep only the hits that |
| 43 | +resolve to `@objectstack/spec`. Every one of them is a compile error on upgrade — there is |
| 44 | +no silent failure in this direction, because the name is gone. |
| 45 | + |
| 46 | +The 102 retired names, by module: |
| 47 | + |
| 48 | +| module | retired | |
| 49 | +|:---|:---| |
| 50 | +| `api/auth` | `SessionUserInput`, `LoginRequestInput` | |
| 51 | +| `api/dispatcher` | `DispatcherRouteInput`, `DispatcherConfigInput` | |
| 52 | +| `api/endpoint` | `ApiEndpointInput` | |
| 53 | +| `api/plugin-rest-api` | `RequestValidationConfigInput`, `ResponseEnvelopeConfigInput`, `ErrorHandlingConfigInput`, `OpenApiGenerationConfigInput`, `RestApiPluginConfigInput` | |
| 54 | +| `api/protocol` | `NotificationPreferencesInput`, `NotificationInput` | |
| 55 | +| `api/query-adapter` | `RestQueryAdapterInput`, `ODataQueryAdapterInput`, `QueryAdapterConfigInput` | |
| 56 | +| `api/rest-server` | `RestApiConfigInput`, `CrudEndpointsConfigInput`, `MetadataEndpointsConfigInput`, `BatchEndpointsConfigInput`, `RouteGenerationConfigInput`, `RestServerConfigInput` | |
| 57 | +| `api/versioning` | `VersioningConfigInput` | |
| 58 | +| `automation` | `FlowFunctionDeclarationInput`, `ActionDescriptorInput`, `TimeRelativeTriggerInput`, `WebhookInput` | |
| 59 | +| `data/analytics` | `CubeInput`, `AnalyticsQueryInput` | |
| 60 | +| `data/datasource` | `DatasourceInput` | |
| 61 | +| `data/field` | `FieldParseInput` (→ `Field`), `CurrencyConfigInput` | |
| 62 | +| `data/mapping` | `MappingInput` | |
| 63 | +| `data/object` | `ObjectFieldGroupInput`, `RowCrudActionOverrideInput`, `ServiceObjectInput`, `ObjectExtensionInput` | |
| 64 | +| `data/seed`, `data/seed-loader` | `SeedInput`, `SeedLoaderConfigInput`, `SeedLoaderRequestInput` | |
| 65 | +| `identity` | `EvalUserInput`, `PositionInput` | |
| 66 | +| `integration/connector` | `ConnectorInput` | |
| 67 | +| `kernel` | `ClusterCapabilityConfigInput`, `ExecutionContextInput`, `ObjectStackManifestInput`, `PackageArtifactInput`, `PluginVendorInput`, `PluginQualityMetricsInput`, `PluginStatisticsInput`, `PluginRegistryEntryInput`, `PluginSearchFiltersInput`, `PluginInstallConfigInput`, `ServiceRegistryConfigInput`, `StartupOptionsInput` | |
| 68 | +| `security` | `ExplainRequestInput`, `AdminScopeInput`, `PermissionSetInput`, `SharingRuleInput` | |
| 69 | +| `system` | `CacheTierInput`, `CacheConfigInput`, `DistributedCacheConfigInput`, `BackupConfigInput`, `FailoverConfigInput`, `DisasterRecoveryPlanInput`, `EmailTemplateDefinitionInput`, `KeyRotationPolicyInput`, `EncryptionConfigInput`, `FieldEncryptionInput`, `EnvironmentArtifactInput`, `RouteHandlerMetadataInput`, `MiddlewareConfigInput`, `ServerCapabilitiesInput`, `JobInput`, `FeatureInput`, `PlanInput`, `SecurityContextConfigInput`, `StackServerConfigInput`, `RowLevelIsolationStrategyInput`, `SchemaLevelIsolationStrategyInput`, `DatabaseLevelIsolationStrategyInput`, `TenantSecurityPolicyInput`, `TranslationBundleInput`, `TaskRetryPolicyInput`, `TaskInput`, `QueueConfigInput`, `BatchTaskInput`, `BatchProgressInput`, `WorkerConfigInput` | |
| 70 | +| `ui` | `ActionInput`, `InlineActionInput`, `NavigationContributionInput`, `AppInput`, `DashboardInput`, `DatasetDimensionInput`, `DatasetMeasureInput`, `DatasetInput`, `PageInput`, `JoinedReportBlockInput`, `ReportInput`, `ReportChartInput`, `ReportSortInput`, `ThemeInput` | |
| 71 | + |
| 72 | +**Nine `*Input` names are NOT retired** and need no change: `ExpressionInput`, |
| 73 | +`CronExpressionInput`, `TemplateExpressionInput` and `PredicateInput` are the bare aliases |
| 74 | +of their own `…InputSchema`, and `FormFieldInput`, `QueryInput`, `FieldInput`, |
| 75 | +`ObjectStackDefinitionInput` and `NavigationItemInput` are composed types (recursive or |
| 76 | +`Partial`-shaped) that no bare alias denotes. |
| 77 | + |
| 78 | +### 2. `X` → `XParsed` **only where you hold a parse result** |
| 79 | + |
| 80 | +If you annotate a value you *wrote*, do nothing — the bare name is now correct, and this |
| 81 | +is the whole point of the change: |
| 82 | + |
| 83 | +```ts |
| 84 | +// This did not compile in 16.x unless you knew to write `ConnectorInput`. |
| 85 | +// In 17.0.0 it is simply right, in every domain. |
| 86 | +const c: Connector = { name: 'acme_erp', label: 'Acme ERP', type: 'saas' }; |
| 87 | +``` |
| 88 | + |
| 89 | +If you annotate a value that came *out of* `.parse()` (or out of a `defineX()` factory, or |
| 90 | +off the wire after the engine parsed it) and you read a defaulted key from it, move that |
| 91 | +annotation to `XParsed`: |
| 92 | + |
| 93 | +```diff |
| 94 | +- const parsed: Connector = ConnectorSchema.parse(raw); |
| 95 | ++ const parsed: ConnectorParsed = ConnectorSchema.parse(raw); |
| 96 | + if (parsed.enabled) { … } // `enabled` is `boolean` here, `boolean | undefined` on `Connector` |
| 97 | +``` |
| 98 | + |
| 99 | +**The grep that finds these:** `rg 'Schema\.parse\(' -A2` and `rg ': *\w+ *= *await'` in |
| 100 | +your own code, then check each annotation. **The reliable finder is the compiler**: every |
| 101 | +site that reads a defaulted key off an author-state value is a `TS18048` / |
| 102 | +`TS2532` ("possibly undefined") or a `TS2345`. Upgrade, run `tsc`, and fix what it names. In |
| 103 | +this repo — 1127 files annotate a value with a spec type — that came to **40 files outside |
| 104 | +`packages/spec`**, and every one of them was a compile error first, never a silent change. |
| 105 | + |
| 106 | +**The one case tsc cannot name for you:** a *function's declared return type*. A parse |
| 107 | +result is structurally assignable to the author state, so |
| 108 | + |
| 109 | +```ts |
| 110 | +function loadConnector(): Connector { return ConnectorSchema.parse(raw); } // still compiles! |
| 111 | +``` |
| 112 | + |
| 113 | +keeps compiling while quietly promising callers less than it delivers. If you have |
| 114 | +factories or loaders that return a parsed value, re-declare them as `XParsed` by hand. |
| 115 | +`@objectstack/spec`'s own 24 `defineX` factories were migrated exactly this way — |
| 116 | +`defineApp(...)` now returns `AppParsed`, `defineConnector(...)` returns `ConnectorParsed`, |
| 117 | +and so on for every factory whose schema has two shapes. |
| 118 | + |
| 119 | +## What did NOT change |
| 120 | + |
| 121 | +- **No runtime behaviour.** Not one `.parse()` call, `.default()`, `.transform()` or schema |
| 122 | + shape moved. This release changes which type name describes which value, nothing else. |
| 123 | +- **`json-schema/` and `authorable-surface/` are byte-identical.** Those generators read |
| 124 | + runtime `z.ZodType` exports, never type aliases. |
| 125 | +- **Your metadata files.** `*.object.ts`, `*.view.ts`, connector and flow definitions |
| 126 | + authored with `defineX(...)` are untouched. Bare-literal metadata files typed with |
| 127 | + `XInput` need the suffix dropped and nothing else. |
| 128 | + |
| 129 | +## Also in this release |
| 130 | + |
| 131 | +- **`check:spec-parsed-alias` is inverted.** It used to require every bare `z.infer` alias |
| 132 | + to be paired or pinned; the flip empties that population, so it now refuses a bare name |
| 133 | + that reads `z.infer` (the flip, enforced), refuses an `XInput` synonym of a bare name |
| 134 | + (the retirement, enforced), and keeps the paired-or-pinned and stale-pin arms on the |
| 135 | + flipped form. |
| 136 | +- **57 previously ungoverned aliases were audited.** Inverting the gate widened it to the |
| 137 | + 86 aliases that already read `z.input`, which phase 1 never examined. 22 gained an |
| 138 | + `XParsed`; 35 were proved isomorphic and pinned, adding 35 to the pin registry (716 → 751 |
| 139 | + on the merged tree, after #5055's four retirements and #5775's one addition). This closes |
| 140 | + #5507's remaining scope. |
| 141 | +- **`@objectstack/spec` public surface: 106 export names removed, 24 added.** The removals |
| 142 | + are the 102 `XInput` aliases (plus re-exports); the additions are the 22 new `XParsed` |
| 143 | + names (plus re-exports). All type-only — no runtime code, no bundle-size change. |
| 144 | + |
| 145 | +<!-- adr-0087: registered spec-type-alias-input-suffix-retired --> |
0 commit comments