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
50 changes: 50 additions & 0 deletions .changeset/plugin-ordering-provider-declarations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
"@objectstack/core": patch
"@objectstack/plugin-auth": patch
"@objectstack/plugin-email": patch
"@objectstack/plugin-hono-server": patch
"@objectstack/plugin-security": patch
"@objectstack/service-storage": patch
"@objectstack/service-settings": patch
"@objectstack/service-realtime": patch
"@objectstack/service-i18n": patch
"@objectstack/service-analytics": patch
"@objectstack/service-messaging": patch
"@objectstack/service-cluster": patch
"@objectstack/service-sms": patch
"@objectstack/service-cache": patch
"@objectstack/service-queue": patch
"@objectstack/service-job": patch
"@objectstack/service-datasource": patch
"@objectstack/service-automation": patch
"@objectstack/mcp": patch
---

chore(plugins,services): declare `providesServices` on the 20 remaining init-time service providers (ADR-0116 follow-up, #4131)

ADR-0116 gave the kernel a declared ordering contract, but only
`ObjectQLPlugin` and `MetadataPlugin` had declared what their `init()`
registers. The pre-Phase-1 ordering check can only *name a provider* for
services someone declared, so its coverage was two plugins wide.

An audit of every plugin's `init()` body (brace-matched, comments stripped,
each call classified by whether it sits inside a `try`/`if`) found 20 plugins
that register a service on every path without declaring it. All 20 now
declare `providesServices`. Purely additive: no ordering changes, no new
failure modes — a `providesServices` entry only lets the kernel say *who*
provides a service when it reports a misordering, and enriches the Phase-1
`getService` miss diagnostic.

Three needed a closer read before declaring, because they register the same
service from several branches (`cache`, `queue`, `job`): each early-return
branch plus the fallback registers it, so every path does — the declaration
is honest. ADR-0116's rule that a *conditionally* registered service must
never be declared is unchanged and was applied throughout.

The same audit found 12 plugins that hard-resolve a service during `init()`
(11 of them `manifest`) without declaring `requiresServices`. None is a live
exposure — every one already declares a hard `dependencies` entry on the
provider, so the kernel orders them correctly today. Those are tracked
separately: with a hard dependency in place, `requiresServices` mostly
restates what the kernel already enforces, and its real value is on
*soft*-dependency consumers, of which `AppPlugin` is currently the only one.
5 changes: 5 additions & 0 deletions packages/core/src/api-registry-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ export function createApiRegistryPlugin(

return {
name: 'com.objectstack.core.api-registry',
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices: ['api-registry'],
type: 'standard',
version: '1.0.0',

Expand Down
5 changes: 5 additions & 0 deletions packages/mcp/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ export interface MCPServerPluginOptions {
*/
export class MCPServerPlugin implements Plugin {
name = 'com.objectstack.mcp';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['mcp'];
version = '1.0.0';
type = 'standard' as const;
dependencies: string[] = [];
Expand Down
5 changes: 5 additions & 0 deletions packages/plugins/plugin-auth/src/auth-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ export interface AuthPluginOptions extends Partial<AuthConfig> {
*/
export class AuthPlugin implements Plugin {
name = 'com.objectstack.auth';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['auth', 'tenancy'];
type = 'standard';
version = '1.0.0';
dependencies: string[] = ['com.objectstack.engine.objectql']; // manifest service required
Expand Down
5 changes: 5 additions & 0 deletions packages/plugins/plugin-email/src/email-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ export interface EmailServicePluginOptions {
*/
export class EmailServicePlugin implements Plugin {
name = 'com.objectstack.service.email';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['email'];
version = '1.0.0';
type = 'standard';
dependencies = ['com.objectstack.engine.objectql'];
Expand Down
5 changes: 5 additions & 0 deletions packages/plugins/plugin-hono-server/src/hono-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ const DISCOVERY_ROUTE_SEGMENTS: Partial<Record<keyof ApiRoutes, string>> = {
*/
export class HonoServerPlugin implements Plugin {
name = 'com.objectstack.server.hono';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['http.server', 'http-server'];
type = 'server';
version = '0.9.0';

Expand Down
5 changes: 5 additions & 0 deletions packages/plugins/plugin-security/src/security-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ export { describeHighPrivilegeBits } from '@objectstack/spec/security';

export class SecurityPlugin implements Plugin {
name = 'com.objectstack.security';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['security.permissions', 'security.rls', 'security.fieldMasker', 'security.bootstrapPermissionSets', 'security.fallbackPermissionSet'];
type = 'standard';
version = '1.0.0';
dependencies = ['com.objectstack.engine.objectql'];
Expand Down
5 changes: 5 additions & 0 deletions packages/services/service-analytics/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ export interface AnalyticsServicePluginOptions {
*/
export class AnalyticsServicePlugin implements Plugin {
name = 'com.objectstack.service-analytics';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['analytics'];
version = '1.0.0';
type = 'standard' as const;
dependencies: string[] = [];
Expand Down
5 changes: 5 additions & 0 deletions packages/services/service-automation/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,11 @@ export function findInertDeclaredConnectors(
*/
export class AutomationServicePlugin implements Plugin {
name = 'com.objectstack.service-automation';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['automation'];
version = '1.0.0';
type = 'standard' as const;
// Soft dependency on metadata: we look it up at start() and tolerate absence.
Expand Down
5 changes: 5 additions & 0 deletions packages/services/service-cache/src/cache-service-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ export interface CacheServicePluginOptions {
*/
export class CacheServicePlugin implements Plugin {
name = 'com.objectstack.service.cache';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['cache'];
version = '1.0.0';
type = 'standard';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export interface ClusterServicePluginOptions {
*/
export class ClusterServicePlugin implements Plugin {
name = 'com.objectstack.service.cluster';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['cluster'];
version = '1.0.0';
type = 'standard';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ export interface DatasourceAdminServicePluginOptions {
*/
export class DatasourceAdminServicePlugin implements Plugin {
name = 'com.objectstack.service-datasource-admin';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['datasource-connection', 'datasource-admin'];
version = '1.0.0';
type = 'standard' as const;
dependencies: string[] = [];
Expand Down
5 changes: 5 additions & 0 deletions packages/services/service-datasource/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export interface ExternalDatasourceServicePluginOptions {
*/
export class ExternalDatasourceServicePlugin implements Plugin {
name = 'com.objectstack.service-external-datasource';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['external-datasource'];
version = '1.0.0';
type = 'standard' as const;
dependencies: string[] = [];
Expand Down
5 changes: 5 additions & 0 deletions packages/services/service-i18n/src/i18n-service-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ export interface I18nServicePluginOptions {
*/
export class I18nServicePlugin implements Plugin {
name = 'com.objectstack.service.i18n';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['i18n'];
version = '1.0.0';
type = 'standard';

Expand Down
5 changes: 5 additions & 0 deletions packages/services/service-job/src/job-service-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ export interface JobServicePluginOptions {
*/
export class JobServicePlugin implements Plugin {
name = 'com.objectstack.service.job';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['job'];
version = '1.1.0';
type = 'standard';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ export interface MessagingServicePluginOptions {
*/
export class MessagingServicePlugin implements Plugin {
name = 'com.objectstack.service.messaging';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['messaging', 'notification'];
version = '1.0.0';
type = 'standard' as const;
dependencies = ['com.objectstack.engine.objectql'];
Expand Down
5 changes: 5 additions & 0 deletions packages/services/service-queue/src/queue-service-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export interface QueueServicePluginOptions {
*/
export class QueueServicePlugin implements Plugin {
name = 'com.objectstack.service.queue';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['queue'];
version = '1.1.0';
type = 'standard';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export interface RealtimeServicePluginOptions {
*/
export class RealtimeServicePlugin implements Plugin {
name = 'com.objectstack.service.realtime';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['realtime'];
version = '1.0.0';
type = 'standard';
dependencies = ['com.objectstack.engine.objectql'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ export interface SettingsServicePluginOptions {
*/
export class SettingsServicePlugin implements Plugin {
name = SETTINGS_PLUGIN_ID;
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['settings'];
version = SETTINGS_PLUGIN_VERSION;
type = 'standard' as const;

Expand Down
5 changes: 5 additions & 0 deletions packages/services/service-sms/src/sms-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ function providerFromSettings(values: Record<string, unknown>): {
*/
export class SmsServicePlugin implements Plugin {
name = 'com.objectstack.service.sms';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['sms'];
version = '1.0.0';
type = 'standard' as const;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ export interface StorageServicePluginOptions {
*/
export class StorageServicePlugin implements Plugin {
name = 'com.objectstack.service.storage';
/**
* Services init() registers on every path (ADR-0116, #4131) — lets the
* kernel name this plugin when a consumer requires one before it inits.
*/
providesServices = ['file-storage'];
version = '1.0.0';
type = 'standard';

Expand Down
Loading