Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .changeset/schema-name-suffix-strip.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
---
"@objectstack/spec": patch
---

fix(spec): strip the `Schema` suffix by anchored regex when deriving published JSON Schema names (#4592)

`build-schemas.ts` / `build-docs.ts` turned an exported const name into its
published schema name with `key.replace('Schema', '')` — a **string** pattern,
which replaces the FIRST occurrence. Every const whose name also contains
`Schema` in prefix/middle position lost that inner segment instead of its
suffix, so four schemas were published — `$id` URL, `json-schema.manifest.json`
key, docs page section, and import example — under type names that exist
nowhere in the export surface. Both generators now share one anchored helper
(`schemaNameFromExportKey`, `key.replace(/Schema$/, '')`).

Corrected names, FROM → TO (the fix if you referenced an old `$id` under
`https://schema.objectstack.io/v17/...` is to swap in the new name — the TS
exports themselves never changed):

| exported const | old (wrong) schema name | new schema name |
|:---|:---|:---|
| `SchemaModeSchema` | `data/ModeSchema` | `data/SchemaMode` |
| `SchemaChangeSchema` | `system/ChangeSchema` | `system/SchemaChange` |
| `SchemaLevelIsolationStrategySchema` | `system/LevelIsolationStrategySchema` | `system/SchemaLevelIsolationStrategy` |
| `DocumentSchemaValidationSchema` | `data/DocumentValidationSchema` | `data/DocumentSchemaValidation` |

The four old manifest keys are removed as a deliberate retirement per the
#2978 rule (they never named a real exported type), and the four
`no schema const export` / `no type export` pairs they caused in
`docs-import-surface.baseline.json` are deleted (152 → 144 accepted gaps) —
the four reference-doc pages regain real, compilable import examples.
6 changes: 3 additions & 3 deletions content/docs/references/data/datasource.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,8 @@ Can be a built-in driver or a plugin-contributed driver (e.g., "com.vendor.snowf
## TypeScript Usage

```typescript
import { DatasourceSchema, DriverDefinitionSchema, DriverType, ExternalDatasourceSettingsSchema } from '@objectstack/spec/data';
import type { Datasource, DriverDefinition, ExternalDatasourceSettings } from '@objectstack/spec/data';
import { DatasourceSchema, DriverDefinitionSchema, DriverType, ExternalDatasourceSettingsSchema, SchemaModeSchema } from '@objectstack/spec/data';
import type { Datasource, DriverDefinition, ExternalDatasourceSettings, SchemaMode } from '@objectstack/spec/data';

// Validate data
const result = DatasourceSchema.parse(data);
Expand DownExpand Up@@ -93,7 +93,7 @@ External datasource federation settings (schemaMode != "managed")

---

## ModeSchema
## SchemaMode

Schema ownership mode

Expand Down
6 changes: 3 additions & 3 deletions content/docs/references/data/driver-nosql.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,8 +16,8 @@ Supported NoSQL database types
## TypeScript Usage

```typescript
import { AggregationPipelineSchema, AggregationStageSchema, ConsistencyLevelSchema, NoSQLDataTypeMappingSchema, NoSQLDatabaseTypeSchema, NoSQLDriverConfigSchema, NoSQLIndexSchema, NoSQLIndexTypeSchema, NoSQLOperationTypeSchema, NoSQLQueryOptionsSchema, NoSQLTransactionOptionsSchema, ReplicationConfigSchema, ShardingConfigSchema } from '@objectstack/spec/data';
import type { AggregationPipeline, AggregationStage, ConsistencyLevel, NoSQLDataTypeMapping, NoSQLDatabaseType, NoSQLDriverConfig, NoSQLIndex, NoSQLIndexType, NoSQLOperationType, NoSQLQueryOptions, NoSQLTransactionOptions, ReplicationConfig, ShardingConfig } from '@objectstack/spec/data';
import { AggregationPipelineSchema, AggregationStageSchema, ConsistencyLevelSchema, DocumentSchemaValidationSchema, NoSQLDataTypeMappingSchema, NoSQLDatabaseTypeSchema, NoSQLDriverConfigSchema, NoSQLIndexSchema, NoSQLIndexTypeSchema, NoSQLOperationTypeSchema, NoSQLQueryOptionsSchema, NoSQLTransactionOptionsSchema, ReplicationConfigSchema, ShardingConfigSchema } from '@objectstack/spec/data';
import type { AggregationPipeline, AggregationStage, ConsistencyLevel, DocumentSchemaValidation, NoSQLDataTypeMapping, NoSQLDatabaseType, NoSQLDriverConfig, NoSQLIndex, NoSQLIndexType, NoSQLOperationType, NoSQLQueryOptions, NoSQLTransactionOptions, ReplicationConfig, ShardingConfig } from '@objectstack/spec/data';

// Validate data
const result = AggregationPipelineSchema.parse(data);
Expand DownExpand Up@@ -64,7 +64,7 @@ const result = AggregationPipelineSchema.parse(data);

---

## DocumentValidationSchema
## DocumentSchemaValidation

### Properties

Expand Down
40 changes: 20 additions & 20 deletions content/docs/references/system/deploy-bundle.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,31 +28,13 @@ Target: 2-5 second deploys vs. 2-15 minute traditional Docker/CI/CD.
## TypeScript Usage

```typescript
import { DeployBundleSchema, DeployDiffSchema, DeployManifestSchema, DeployStatusEnum, DeployValidationIssueSchema, DeployValidationResultSchema, MigrationPlanSchema, MigrationStatementSchema } from '@objectstack/spec/system';
import type { DeployBundle, DeployDiff, DeployManifest, DeployValidationIssue, DeployValidationResult, MigrationPlan, MigrationStatement } from '@objectstack/spec/system';
import { DeployBundleSchema, DeployDiffSchema, DeployManifestSchema, DeployStatusEnum, DeployValidationIssueSchema, DeployValidationResultSchema, MigrationPlanSchema, MigrationStatementSchema, SchemaChangeSchema } from '@objectstack/spec/system';
import type { DeployBundle, DeployDiff, DeployManifest, DeployValidationIssue, DeployValidationResult, MigrationPlan, MigrationStatement, SchemaChange } from '@objectstack/spec/system';

// Validate data
const result = DeployBundleSchema.parse(data);
```

---

## ChangeSchema

Individual schema change

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **entityType** | `Enum<'object' \| 'field' \| 'index' \| 'view' \| 'flow' \| 'permission'>` | ✅ | Entity type |
| **entityName** | `string` | ✅ | Entity name |
| **parentEntity** | `string` | optional | Parent entity name |
| **changeType** | `Enum<'added' \| 'modified' \| 'removed'>` | ✅ | Change type |
| **oldValue** | `any` | optional | Previous value |
| **newValue** | `any` | optional | New value |


---

## DeployBundle
Expand DownExpand Up@@ -188,3 +170,21 @@ Single DDL migration statement

---

## SchemaChange

Individual schema change

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **entityType** | `Enum<'object' \| 'field' \| 'index' \| 'view' \| 'flow' \| 'permission'>` | ✅ | Entity type |
| **entityName** | `string` | ✅ | Entity name |
| **parentEntity** | `string` | optional | Parent entity name |
| **changeType** | `Enum<'added' \| 'modified' \| 'removed'>` | ✅ | Change type |
| **oldValue** | `any` | optional | Previous value |
| **newValue** | `any` | optional | New value |


---

32 changes: 16 additions & 16 deletions content/docs/references/system/tenant.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,8 +28,8 @@ Isolation Levels:
## TypeScript Usage

```typescript
import { DatabaseLevelIsolationStrategySchema, DatabaseProviderSchema, QuotaEnforcementResultSchema, RowLevelIsolationStrategySchema, TenantSchema, TenantConnectionConfigSchema, TenantIsolationConfigSchema, TenantIsolationLevel, TenantQuotaSchema, TenantSecurityPolicySchema, TenantUsageSchema } from '@objectstack/spec/system';
import type { DatabaseLevelIsolationStrategy, DatabaseProvider, QuotaEnforcementResult, RowLevelIsolationStrategy, Tenant, TenantConnectionConfig, TenantIsolationConfig, TenantIsolationLevel, TenantQuota, TenantSecurityPolicy, TenantUsage } from '@objectstack/spec/system';
import { DatabaseLevelIsolationStrategySchema, DatabaseProviderSchema, QuotaEnforcementResultSchema, RowLevelIsolationStrategySchema, SchemaLevelIsolationStrategySchema, TenantSchema, TenantConnectionConfigSchema, TenantIsolationConfigSchema, TenantIsolationLevel, TenantQuotaSchema, TenantSecurityPolicySchema, TenantUsageSchema } from '@objectstack/spec/system';
import type { DatabaseLevelIsolationStrategy, DatabaseProvider, QuotaEnforcementResult, RowLevelIsolationStrategy, SchemaLevelIsolationStrategy, Tenant, TenantConnectionConfig, TenantIsolationConfig, TenantIsolationLevel, TenantQuota, TenantSecurityPolicy, TenantUsage } from '@objectstack/spec/system';

// Validate data
const result = DatabaseLevelIsolationStrategySchema.parse(data);
Expand DownExpand Up@@ -63,20 +63,6 @@ Database provider for tenant data
* `memory`


---

## LevelIsolationStrategySchema

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **strategy** | `'isolated_schema'` | ✅ | Schema-level isolation strategy |
| **schema** | `{ namingPattern: string; includePublicSchema: boolean; sharedSchema: string; autoCreateSchema: boolean }` | optional | Schema configuration |
| **migrations** | `{ strategy: Enum<'parallel' \| 'sequential' \| 'on_demand'>; maxConcurrent: integer; rollbackOnError: boolean }` | optional | Migration configuration |
| **performance** | `{ poolPerSchema: boolean; schemaCacheTTL: integer }` | optional | Performance settings |


---

## QuotaEnforcementResult
Expand DownExpand Up@@ -107,6 +93,20 @@ Quota enforcement check result
| **performance** | `{ usePartialIndexes: boolean; usePartitioning: boolean; poolSizePerTenant?: integer }` | optional | Performance settings |


---

## SchemaLevelIsolationStrategy

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **strategy** | `'isolated_schema'` | ✅ | Schema-level isolation strategy |
| **schema** | `{ namingPattern: string; includePublicSchema: boolean; sharedSchema: string; autoCreateSchema: boolean }` | optional | Schema configuration |
| **migrations** | `{ strategy: Enum<'parallel' \| 'sequential' \| 'on_demand'>; maxConcurrent: integer; rollbackOnError: boolean }` | optional | Migration configuration |
| **performance** | `{ poolPerSchema: boolean; schemaCacheTTL: integer }` | optional | Performance settings |


---

## Tenant
Expand Down
28 changes: 14 additions & 14 deletions packages/spec/authorable-surface.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -3264,16 +3264,16 @@
"data/Document:tags",
"data/Document:template",
"data/Document:versioning",
"data/DocumentSchemaValidation:enabled",
"data/DocumentSchemaValidation:jsonSchema",
"data/DocumentSchemaValidation:validationAction",
"data/DocumentSchemaValidation:validationLevel",
"data/DocumentTemplate:description",
"data/DocumentTemplate:fileType",
"data/DocumentTemplate:fileUrl",
"data/DocumentTemplate:id",
"data/DocumentTemplate:name",
"data/DocumentTemplate:placeholders",
"data/DocumentValidationSchema:enabled",
"data/DocumentValidationSchema:jsonSchema",
"data/DocumentValidationSchema:validationAction",
"data/DocumentValidationSchema:validationLevel",
"data/DocumentVersion:checksum",
"data/DocumentVersion:createdAt",
"data/DocumentVersion:createdBy",
Expand DownExpand Up@@ -5798,12 +5798,6 @@
"system/ChangeRequest:status",
"system/ChangeRequest:title",
"system/ChangeRequest:type",
"system/ChangeSchema:changeType",
"system/ChangeSchema:entityName",
"system/ChangeSchema:entityType",
"system/ChangeSchema:newValue",
"system/ChangeSchema:oldValue",
"system/ChangeSchema:parentEntity",
"system/ChangeSet:author",
"system/ChangeSet:createdAt",
"system/ChangeSet:dependencies",
Expand DownExpand Up@@ -6206,10 +6200,6 @@
"system/LWWRegister:type",
"system/LWWRegister:value",
"system/LWWRegister:vectorClock",
"system/LevelIsolationStrategySchema:migrations",
"system/LevelIsolationStrategySchema:performance",
"system/LevelIsolationStrategySchema:schema",
"system/LevelIsolationStrategySchema:strategy",
"system/License:customFeatures",
"system/License:customLimits",
"system/License:expiresAt",
Expand DownExpand Up@@ -6664,6 +6654,16 @@
"system/SMSTemplate:maxLength",
"system/SMSTemplate:message",
"system/SMSTemplate:variables",
"system/SchemaChange:changeType",
"system/SchemaChange:entityName",
"system/SchemaChange:entityType",
"system/SchemaChange:newValue",
"system/SchemaChange:oldValue",
"system/SchemaChange:parentEntity",
"system/SchemaLevelIsolationStrategy:migrations",
"system/SchemaLevelIsolationStrategy:performance",
"system/SchemaLevelIsolationStrategy:schema",
"system/SchemaLevelIsolationStrategy:strategy",
"system/SearchConfig:analyzers",
"system/SearchConfig:facets",
"system/SearchConfig:indexes",
Expand Down
8 changes: 0 additions & 8 deletions packages/spec/docs-import-surface.baseline.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,8 +45,6 @@
"data/DateGranularity — no type export",
"data/DateMacroPlaceholder — no type export",
"data/DimensionType — no type export",
"data/DocumentValidationSchema — no schema const export",
"data/DocumentValidationSchema — no type export",
"data/DriverSslToggle — no type export",
"data/DriverType — no type export",
"data/EqualityOperator — no type export",
Expand All@@ -57,8 +55,6 @@
"data/Index — no type export",
"data/InstantValue — no type export",
"data/LocationValue — no type export",
"data/ModeSchema — no schema const export",
"data/ModeSchema — no type export",
"data/Object — no type export",
"data/ObjectOwnershipEnum — no type export",
"data/Query — no type export",
Expand DownExpand Up@@ -95,17 +91,13 @@
"studio/ActivationEvent — no type export",
"studio/PanelLocation — no type export",
"system/AddFieldOperation — no type export",
"system/ChangeSchema — no schema const export",
"system/ChangeSchema — no type export",
"system/CreateObjectOperation — no type export",
"system/DeleteObjectOperation — no type export",
"system/DeployStatusEnum — no type export",
"system/EnvironmentArtifactFunctionLanguageEnum — no type export",
"system/EnvironmentArtifactHashAlgorithmEnum — no type export",
"system/ExecuteSqlOperation — no type export",
"system/KernelServiceMap — no type export",
"system/LevelIsolationStrategySchema — no schema const export",
"system/LevelIsolationStrategySchema — no type export",
"system/Locale — no type export",
"system/MetadataState — no type export",
"system/MigrationDependency — no type export",
Expand Down
10 changes: 5 additions & 5 deletions packages/spec/json-schema.manifest.json
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
{
"description": "Ratchet manifest of every JSON Schema emitted by scripts/build-schemas.ts. Auto-appended when new schemas are added (commit the change). A listed schema that a build no longer emits fails gen:schema \u2014 remove a key ONLY for a deliberate retirement. See #2978.",
"description": "Ratchet manifest of every JSON Schema emitted by scripts/build-schemas.ts. Auto-appended when new schemas are added (commit the change). A listed schema that a build no longer emits fails gen:schema remove a key ONLY for a deliberate retirement. See #2978.",
"schemas": [
"ai/AIModelConfig",
"ai/AIUsageRecord",
Expand DownExpand Up@@ -724,8 +724,8 @@
"data/Dimension",
"data/DimensionType",
"data/Document",
"data/DocumentSchemaValidation",
"data/DocumentTemplate",
"data/DocumentValidationSchema",
"data/DocumentVersion",
"data/DriverCapabilities",
"data/DriverConfig",
Expand DownExpand Up@@ -778,7 +778,6 @@
"data/LocationValue",
"data/Mapping",
"data/Metric",
"data/ModeSchema",
"data/MongoConfig",
"data/MysqlConfig",
"data/NoSQLDataTypeMapping",
Expand DownExpand Up@@ -813,6 +812,7 @@
"data/SQLDialect",
"data/SQLDriverConfig",
"data/SSLConfig",
"data/SchemaMode",
"data/ScriptBody",
"data/ScriptValidation",
"data/Seed",
Expand DownExpand Up@@ -1229,7 +1229,6 @@
"system/ChangeImpact",
"system/ChangePriority",
"system/ChangeRequest",
"system/ChangeSchema",
"system/ChangeSet",
"system/ChangeStatus",
"system/ChangeType",
Expand DownExpand Up@@ -1319,7 +1318,6 @@
"system/KeyManagementProvider",
"system/KeyRotationPolicy",
"system/LWWRegister",
"system/LevelIsolationStrategySchema",
"system/License",
"system/LicenseMetricType",
"system/LifecycleAction",
Expand DownExpand Up@@ -1415,6 +1413,8 @@
"system/SamplingDecision",
"system/SamplingStrategyType",
"system/Schedule",
"system/SchemaChange",
"system/SchemaLevelIsolationStrategy",
"system/SearchConfig",
"system/SearchIndexConfig",
"system/SearchProvider",
Expand Down
4 changes: 3 additions & 1 deletion packages/spec/scripts/build-docs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,7 @@ import {
type CategorySurface,
} from './lib/docs-import-surface';
import { createSink } from './lib/generated-output';
import { schemaNameFromExportKey } from './lib/schema-name';

const SCHEMA_DIR = path.resolve(__dirname, '../json-schema');
const SRC_DIR = path.resolve(__dirname, '../src');
Expand DownExpand Up@@ -130,7 +131,8 @@ function scanCategories() {
let match;
while ((match = regex.exec(content)) !== null) {
const rawName = match[1];
const finalName = rawName.endsWith('Schema') ? rawName.replace('Schema', '') : rawName;
// Suffix-only strip — shared with build-schemas.ts; see lib/schema-name.ts (#4592).
const finalName = schemaNameFromExportKey(rawName);
schemaCategoryMap.set(finalName, category);
schemaZodFileMap.set(finalName, slug);
}
Expand Down
4 changes: 3 additions & 1 deletion packages/spec/scripts/build-schemas.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@ process.env.OS_EAGER_SCHEMAS = '1';
import fs from 'fs';
import path from 'path';
import { z } from 'zod';
import { schemaNameFromExportKey } from './lib/schema-name';
import { CONVERSIONS_BY_MAJOR } from '../src/conversions/registry';
import { MIGRATIONS_BY_MAJOR } from '../src/migrations/registry';
import * as AI from '../src/ai';
Expand DownExpand Up@@ -197,7 +198,8 @@ for (const [namespaceName, namespaceExports] of Object.entries(Protocol)) {
for (const [key, value] of Object.entries(namespaceExports)) {
// Check if it looks like a Zod Schema
if (value instanceof z.ZodType) {
const schemaName = key.endsWith('Schema') ? key.replace('Schema', '') : key;
// Suffix-only strip — shared with build-docs.ts; see lib/schema-name.ts (#4592).
const schemaName = schemaNameFromExportKey(key);

try {
// Convert to JSON Schema using Zod v4's built-in toJSONSchema().
Expand Down
22 changes: 22 additions & 0 deletions packages/spec/scripts/lib/schema-name.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

/**
* Published JSON Schema name for an exported schema const.
*
* The convention is `<Name>Schema` (const) → `<Name>` (schema/doc/type name),
* and the strip must be anchored to the END of the identifier: a string-pattern
* `String.prototype.replace('Schema', '')` removes the FIRST occurrence, which
* mangles every name that also contains `Schema` in prefix/middle position —
* `SchemaModeSchema` became `ModeSchema` instead of `SchemaMode`, publishing a
* `$id`, a manifest key and a docs section under a type name that does not
* exist (#4592).
*
* Single source of truth for `build-schemas.ts` (the `$id` / manifest /
* authorable-surface key) and `build-docs.ts` (the docs section + import
* example), so the two generators cannot drift apart again. The inverse
* lookup (`lib/docs-import-surface.ts`) appends the suffix and is already
* anchored by construction.
*/
export function schemaNameFromExportKey(key: string): string {
return key.replace(/Schema$/, '');
}
Loading
Loading