diff --git a/.changeset/retire-default-dispatcher-routes.md b/.changeset/retire-default-dispatcher-routes.md new file mode 100644 index 0000000000..08a6de142d --- /dev/null +++ b/.changeset/retire-default-dispatcher-routes.md @@ -0,0 +1,27 @@ +--- +"@objectstack/spec": minor +--- + +feat(spec)!: delete `DEFAULT_DISPATCHER_ROUTES` — the dead route table that +underwrote a false compliance verdict (#3586, #3563 follow-up) + +The const was consumed by nothing in the runtime — only its own tests and +`api-surface.json`. It listed dispatcher branches that never existed +(`/workflow`, `/realtime`) while omitting eight real prefixes (`/keys`, +`/mcp`, `/mcp/skill`, `/actions`, `/security`, `/share-links`, `/ready`, +`/openapi.json`), and `CLIENT_SPEC_COMPLIANCE.md` anchored a "FULLY +COMPLIANT" verdict on it while 27 real routes had no SDK expression. + +The audited, guard-enforced source of truth for the dispatcher's route +surface is `packages/runtime/src/route-ledger.ts` (#3569): the conformance +suite fails when the registry and the ledger drift, which the dead table +never could. + +Also swept the last GraphQL fixture debris that #3562's surface removal +left behind: registry test fixtures renamed to honest OData naming, the +tautological `config.graphql` assertions dropped, and the stale +`"type": "graphql"` JSDoc example in `registry.zod.ts` corrected. + +Breaking for anyone importing `DEFAULT_DISPATCHER_ROUTES` (a repo-wide and +objectui-wide grep shows zero consumers); shipped as minor per the +launch-window convention, cf. #3562/#3581. diff --git a/packages/spec/api-surface.json b/packages/spec/api-surface.json index 170c8ccb5e..39ae510879 100644 --- a/packages/spec/api-surface.json +++ b/packages/spec/api-surface.json @@ -2378,7 +2378,6 @@ "DEFAULT_BATCH_ROUTES (const)", "DEFAULT_DATA_CRUD_ROUTES (const)", "DEFAULT_DISCOVERY_ROUTES (const)", - "DEFAULT_DISPATCHER_ROUTES (const)", "DEFAULT_I18N_ROUTES (const)", "DEFAULT_METADATA_ROUTES (const)", "DEFAULT_NOTIFICATION_ROUTES (const)", diff --git a/packages/spec/src/api/dispatcher.test.ts b/packages/spec/src/api/dispatcher.test.ts index 693f2cbefd..57aa35ddd2 100644 --- a/packages/spec/src/api/dispatcher.test.ts +++ b/packages/spec/src/api/dispatcher.test.ts @@ -4,7 +4,6 @@ import { DispatcherConfigSchema, DispatcherErrorCode, DispatcherErrorResponseSchema, - DEFAULT_DISPATCHER_ROUTES, type DispatcherRoute, type DispatcherConfig, } from './dispatcher.zod'; @@ -120,71 +119,9 @@ describe('DispatcherConfigSchema', () => { }); }); -describe('DEFAULT_DISPATCHER_ROUTES', () => { - it('should have routes for all protocol namespaces', () => { - expect(DEFAULT_DISPATCHER_ROUTES.length).toBeGreaterThanOrEqual(15); - }); - - it('should include required services', () => { - const services = DEFAULT_DISPATCHER_ROUTES.map(r => r.service); - expect(services).toContain('metadata'); - expect(services).toContain('data'); - expect(services).toContain('auth'); - }); - - it('should include the storage service', () => { - const services = DEFAULT_DISPATCHER_ROUTES.map(r => r.service); - expect(services).toContain('file-storage'); - // The feed route was retired (ADR-0052 §5 / #3180) — comments/activity are - // served by the generic data API on sys_comment / sys_activity. - expect(DEFAULT_DISPATCHER_ROUTES.find(r => r.prefix.includes('feed'))).toBeUndefined(); - }); - - it('should include optional services', () => { - const services = DEFAULT_DISPATCHER_ROUTES.map(r => r.service); - expect(services).toContain('ai'); - expect(services).toContain('i18n'); - expect(services).toContain('ui'); - expect(services).toContain('workflow'); - expect(services).toContain('realtime'); - expect(services).toContain('notification'); - expect(services).toContain('analytics'); - expect(services).toContain('automation'); - }); - - it('should have discovery as public route', () => { - const discovery = DEFAULT_DISPATCHER_ROUTES.find(r => r.prefix.includes('discovery')); - expect(discovery).toBeDefined(); - expect(discovery!.authRequired).toBe(false); - }); - - it('should mark required services with required criticality', () => { - const required = DEFAULT_DISPATCHER_ROUTES.filter(r => r.criticality === 'required'); - const requiredServices = required.map(r => r.service); - expect(requiredServices).toContain('metadata'); - expect(requiredServices).toContain('data'); - expect(requiredServices).toContain('auth'); - }); - - it('should have all prefixes starting with /', () => { - DEFAULT_DISPATCHER_ROUTES.forEach(route => { - expect(route.prefix).toMatch(/^\//); - }); - }); - - it('should be parseable by DispatcherConfigSchema', () => { - expect(() => DispatcherConfigSchema.parse({ - routes: DEFAULT_DISPATCHER_ROUTES, - })).not.toThrow(); - }); - - it('should include health route', () => { - const health = DEFAULT_DISPATCHER_ROUTES.find(r => r.prefix.includes('health')); - expect(health).toBeDefined(); - expect(health!.authRequired).toBe(false); - expect(health!.criticality).toBe('required'); - }); -}); +// DEFAULT_DISPATCHER_ROUTES tests removed with the const (#3586) — the +// dispatcher's real route surface is asserted by the route-ledger conformance +// suite in packages/runtime. // ============================================================================ // Dispatcher Error Schemas diff --git a/packages/spec/src/api/dispatcher.zod.ts b/packages/spec/src/api/dispatcher.zod.ts index e89072d159..ea568a5e08 100644 --- a/packages/spec/src/api/dispatcher.zod.ts +++ b/packages/spec/src/api/dispatcher.zod.ts @@ -128,40 +128,13 @@ export type DispatcherConfig = z.infer; export type DispatcherConfigInput = z.input; // ============================================================================ -// Default Route Table +// Default Route Table — REMOVED (#3586) // ============================================================================ - -/** - * Default route table for the ObjectStack HttpDispatcher. - * Maps all Protocol namespaces to their corresponding services. - * - * This is the recommended baseline configuration. Plugins can extend - * this table by declaring routes in their manifest's contributes.routes. - */ -export const DEFAULT_DISPATCHER_ROUTES: DispatcherRouteInput[] = [ - // Discovery (public) - { prefix: '/api/v1/discovery', service: 'metadata', authRequired: false, criticality: 'required' }, - - // Health (public) - { prefix: '/api/v1/health', service: 'metadata', authRequired: false, criticality: 'required' }, - - // Required Services - { prefix: '/api/v1/meta', service: 'metadata', criticality: 'required' }, - { prefix: '/api/v1/data', service: 'data', criticality: 'required' }, - { prefix: '/api/v1/auth', service: 'auth', criticality: 'required' }, - - // Optional Services (plugin-provided) - { prefix: '/api/v1/packages', service: 'metadata' }, - { prefix: '/api/v1/ui', service: 'ui' }, // @deprecated — use /api/v1/meta/view and /api/v1/meta/dashboard instead - { prefix: '/api/v1/workflow', service: 'workflow' }, - { prefix: '/api/v1/analytics', service: 'analytics' }, - { prefix: '/api/v1/automation', service: 'automation' }, - { prefix: '/api/v1/storage', service: 'file-storage' }, - { prefix: '/api/v1/i18n', service: 'i18n' }, - { prefix: '/api/v1/notifications', service: 'notification' }, - { prefix: '/api/v1/realtime', service: 'realtime' }, - { prefix: '/api/v1/ai', service: 'ai' }, -]; +// DEFAULT_DISPATCHER_ROUTES was deleted: nothing in the runtime ever consumed +// it, it listed routes that never existed (/workflow, /realtime) while +// omitting eight real ones, and it underwrote a false compliance verdict. +// The audited, guard-enforced source of truth for the dispatcher's route +// surface is packages/runtime/src/route-ledger.ts. // ============================================================================ // Dispatcher Error Codes diff --git a/packages/spec/src/api/query-adapter.test.ts b/packages/spec/src/api/query-adapter.test.ts index 32221a0cf5..05a48b9104 100644 --- a/packages/spec/src/api/query-adapter.test.ts +++ b/packages/spec/src/api/query-adapter.test.ts @@ -144,7 +144,6 @@ describe('QueryAdapterConfigSchema', () => { const config = QueryAdapterConfigSchema.parse({}); expect(config.rest).toBeUndefined(); - expect(config.graphql).toBeUndefined(); expect(config.odata).toBeUndefined(); }); @@ -177,7 +176,6 @@ describe('QueryAdapterConfigSchema', () => { }); expect(config.rest?.filterStyle).toBe('rsql'); - expect(config.graphql).toBeUndefined(); expect(config.odata).toBeUndefined(); }); }); diff --git a/packages/spec/src/api/registry.test.ts b/packages/spec/src/api/registry.test.ts index 2c8266159b..6bfac8a60f 100644 --- a/packages/spec/src/api/registry.test.ts +++ b/packages/spec/src/api/registry.test.ts @@ -414,15 +414,15 @@ describe('API Registry Protocol', () => { ], }, { - id: 'graphql_api', - name: 'GraphQL API', + id: 'odata_api', + name: 'OData API', type: 'odata', version: 'v1', - basePath: '/graphql', + basePath: '/odata', endpoints: [ { - id: 'graphql_query', - path: '/graphql', + id: 'odata_query', + path: '/odata', responses: [], }, ], @@ -459,11 +459,11 @@ describe('API Registry Protocol', () => { endpoints: [], }, { - id: 'graphql_api', - name: 'GraphQL API', + id: 'odata_api', + name: 'OData API', type: 'odata' as const, version: 'v1', - basePath: '/graphql', + basePath: '/odata', endpoints: [], }, ], diff --git a/packages/spec/src/api/registry.zod.ts b/packages/spec/src/api/registry.zod.ts index e82f810200..bfe7f09a0c 100644 --- a/packages/spec/src/api/registry.zod.ts +++ b/packages/spec/src/api/registry.zod.ts @@ -680,7 +680,7 @@ export type ConflictResolutionStrategy = z.infer