diff --git a/.changeset/connector-template-cluster-removed.md b/.changeset/connector-template-cluster-removed.md new file mode 100644 index 0000000000..20f8d8ade0 --- /dev/null +++ b/.changeset/connector-template-cluster-removed.md @@ -0,0 +1,62 @@ +--- +'@objectstack/spec': major +--- + +The per-provider connector "template" cluster is removed (#4480, ADR-0049) + +`@objectstack/spec/integration` no longer exports the six per-provider +connector schemas and their sub-schema/type/example clusters (~110 exports, +2,672 lines): + +- `DatabaseConnectorSchema` (+ `DatabaseProviderSchema`, `DatabasePoolConfigSchema`, + `SslConfigSchema`, `CdcConfigSchema`, `DatabaseTableSchema`, the three + `*ConnectorExample` constants) +- `FileStorageConnectorSchema` (+ bucket/versioning/multipart/filter configs, examples) +- `GitHubConnectorSchema` (+ repository/commit/PR/actions/release/issue configs, examples) +- `MessageQueueConnectorSchema` (+ its queue/topic/consumer configs, examples) +- `SaasConnectorSchema` (+ examples) +- `VercelConnectorSchema` (+ its deployment/domain/env configs, examples) + +The six generated reference pages under `docs/references/integration/` go with +them. + +**Why removal, not completion.** These files were the losing side of an +architecture decision the same module's live half already records. ADR-0023 +rejected hand-modelling each external system's shape inside the spec — +"re-inventing OpenAPI inside this schema" — and ADR-0097's connector protocol +does the opposite: one `ConnectorSchema`, with provider shapes coming from the +provider itself (`connector-openapi` materializes instances from an OpenAPI +document, `connector-mcp` from an MCP server). The templates hardcoded +Postgres/S3/GitHub/RabbitMQ/Vercel shapes into spec files nothing ever read: + +- `engine.registerConnector()` validates against `ConnectorSchema` from + `connector.zod.ts` — never the templates +- the `connectors:` stack collection parses `DeclarativeConnectorEntrySchema` — + never the templates +- nothing else in the monorepo, objectui included, imported any of the six + +They were also semantically wrong where they overlapped the live platform: +`DatabaseConnectorSchema` modelled "tables to sync", CDC, and `readReplicaConfig` +— a second, independent declaration of read-replica routing (the first, +`datasource.readReplicas`, was removed in #4468), complete with a `weight` +field for a load balancer that does not exist. External-database access is +datasource federation (ADR-0015), which is live and is not a connector. + +**Migration.** There is nothing to migrate: these schemas validated no stored +metadata (the `connectors:` collection never used them) and no runtime read +their output. If you imported one as a TypeScript type for your own code, +model your provider config yourself, or — the supported path — declare a +provider-bound connector instance and let connector-openapi / connector-mcp +derive the shape: + +```ts +// before (typed against a dead spec export) +import { DatabaseConnector } from '@objectstack/spec/integration'; + +// after (the live protocol) +import { Connector, DeclarativeConnectorEntry } from '@objectstack/spec/integration'; +``` + +The base protocol — `ConnectorSchema`, `DeclarativeConnectorEntrySchema`, the +ADR-0097 provider contract, connector-descriptor, connector auth — is +unchanged. diff --git a/content/docs/getting-started/quick-reference.mdx b/content/docs/getting-started/quick-reference.mdx index 6600eb94a7..603d216b20 100644 --- a/content/docs/getting-started/quick-reference.mdx +++ b/content/docs/getting-started/quick-reference.mdx @@ -193,19 +193,18 @@ Environments, marketplace, licensing, and multi-tenancy. | **[Plugin Security](/docs/references/cloud/plugin-security)** | `plugin-security.zod.ts` | PluginSecurityProtocol, SBOM | Plugin security policies | | **[Tenant](/docs/references/cloud/tenant)** | `tenant.zod.ts` | Tenant | Multi-tenancy isolation | -## Integration Protocol (7 schemas) +## Integration Protocol (1 schema) -External system connectors and adapters. +External system connectors — one protocol (ADR-0097): a connector entry is +either a catalog descriptor or a provider-bound instance that a generic +executor (connector-openapi / connector-mcp) materializes at boot. The +per-provider schema "templates" (SaaS / database / file-storage / +message-queue / GitHub / Vercel) were removed in #4480: provider shapes come +from the provider itself, not from hand-written spec files. | Protocol | Source File | Key Schemas | Purpose | |:---------|:-----------|:------------|:--------| -| **[Connector](/docs/references/integration/connector)** | `connector.zod.ts` | Connector | Generic connector interface | -| **[SaaS Connector](/docs/references/integration/connector)** | `connector/saas.zod.ts` | SaaSConnector | SaaS integrations | -| **[Database Connector](/docs/references/integration/connector)** | `connector/database.zod.ts` | DatabaseConnector | Database adapters | -| **[File Storage](/docs/references/integration/connector)** | `connector/file-storage.zod.ts` | FileStorageConnector | Cloud storage | -| **[Message Queue](/docs/references/integration/message-queue)** | `connector/message-queue.zod.ts` | MessageQueueConnector | Queue integrations | -| **[GitHub](/docs/references/integration/connector)** | `connector/github.zod.ts` | GitHubConnector | GitHub API integration | -| **[Vercel](/docs/references/integration/connector)** | `connector/vercel.zod.ts` | VercelConnector | Vercel deployment | +| **[Connector](/docs/references/integration/connector)** | `connector.zod.ts` | Connector | The connector protocol — auth, sync, webhooks, rate limiting | ## Shared Protocol (5 schemas) diff --git a/content/docs/references/index.mdx b/content/docs/references/index.mdx index 07c912df63..0929d1f3bd 100644 --- a/content/docs/references/index.mdx +++ b/content/docs/references/index.mdx @@ -1,6 +1,6 @@ --- title: Protocol Reference -description: Complete reference of all 139 ObjectStack protocol specifications +description: Complete reference of all 133 ObjectStack protocol specifications --- # Protocol Reference @@ -30,7 +30,7 @@ These reference pages are **auto-generated** from the Zod source files in `packa | [QA Protocol](#qa-protocol) | 1 | Test Suites and BDD Scenarios | | [Studio Protocol](#studio-protocol) | 1 | Studio plugin development | -**Total: 175 Zod schemas** (across 14 protocol modules + 1 root stack schema) +**Total: 169 Zod schemas** (across 14 protocol modules + 1 root stack schema) --- @@ -313,19 +313,18 @@ Defines marketplace and multi-tenancy capabilities. ## Integration Protocol **Location:** `packages/spec/src/integration/` -**Count:** 7 schemas +**Count:** 1 schema -Defines external system connectors and adapters. +Defines external system connectors — one protocol (ADR-0097). A connector +entry is either a catalog descriptor or a provider-bound instance that a +generic executor (connector-openapi / connector-mcp) materializes at boot. +The per-provider "templates" (`connector/saas.zod.ts` and five siblings) were +removed in #4480: they hand-modelled each external system's shape inside the +spec, which ADR-0023 rejected, and nothing ever consumed them. | File | Schema | Purpose | | :--- | :--- | :--- | -| `connector.zod.ts` | `ConnectorSchema` | Generic connector interface | -| `connector/saas.zod.ts` | `SaaSConnectorSchema` | SaaS platform connectors (Salesforce, HubSpot, etc.) | -| `connector/database.zod.ts` | `DatabaseConnectorSchema` | Database connection adapters | -| `connector/file-storage.zod.ts` | `FileStorageConnectorSchema` | Cloud storage connectors (S3, Azure Blob, etc.) | -| `connector/message-queue.zod.ts` | `MessageQueueConnectorSchema` | Message queue integrations (RabbitMQ, Kafka, etc.) | -| `connector/github.zod.ts` | `GitHubConnectorSchema` | GitHub API integration | -| `connector/vercel.zod.ts` | `VercelConnectorSchema` | Vercel deployment integration | +| `connector.zod.ts` | `ConnectorSchema` | The connector protocol — auth, sync, webhooks, rate limiting | **Learn more:** [Integration Protocol Reference](/docs/references/integration) diff --git a/content/docs/references/integration/connector-database.mdx b/content/docs/references/integration/connector-database.mdx deleted file mode 100644 index b550393cf7..0000000000 --- a/content/docs/references/integration/connector-database.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Connector Database -description: Connector Database protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -Database Connector Protocol Template - -Specialized connector for database systems (PostgreSQL, MySQL, SQL Server, etc.) - -Extends the base connector with database-specific features like schema discovery, - -CDC (Change Data Capture), and connection pooling. - - -**Source:** `packages/spec/src/integration/connector/database.zod.ts` - - -## TypeScript Usage - -```typescript -import { CdcConfig, DatabaseConnector, DatabasePoolConfig, DatabaseTable, SslConfig } from '@objectstack/spec/integration'; -import type { CdcConfig, DatabaseConnector, DatabasePoolConfig, DatabaseTable, SslConfig } from '@objectstack/spec/integration'; - -// Validate data -const result = CdcConfig.parse(data); -``` - ---- - -## CdcConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable CDC | -| **method** | `Enum<'log_based' \| 'trigger_based' \| 'query_based' \| 'custom'>` | ✅ | CDC method | -| **slotName** | `string` | optional | Replication slot name (for log-based CDC) | -| **publicationName** | `string` | optional | Publication name (for PostgreSQL) | -| **startPosition** | `string` | optional | Starting position/LSN for CDC stream | -| **batchSize** | `number` | ✅ | CDC batch size | -| **pollIntervalMs** | `number` | ✅ | CDC polling interval in ms | - - ---- - -## DatabaseConnector - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Unique connector identifier | -| **label** | `string` | ✅ | Display label | -| **type** | `'database'` | ✅ | | -| **description** | `string` | optional | Connector description | -| **icon** | `string` | optional | Icon identifier | -| **authentication** | `{ type: 'oauth2'; authorizationUrl: string; tokenUrl: string; clientId: string; … } \| { type: 'api-key'; key: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; password: string } \| { type: 'bearer'; token: string } \| { type: 'none' }` | optional | Authentication configuration (runtime shape with inline secrets). Provider-bound declarative instances use `auth.credentialRef` instead. | -| **provider** | `Enum<'postgresql' \| 'mysql' \| 'mariadb' \| 'mssql' \| 'oracle' \| 'mongodb' \| 'redis' \| 'cassandra' \| 'snowflake' \| 'bigquery' \| 'redshift' \| 'custom'>` | ✅ | Database provider type | -| **providerConfig** | `Record` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires `provider`. | -| **auth** | `{ type: 'none' } \| { type: 'bearer'; credentialRef: string } \| { type: 'api-key'; credentialRef: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; credentialRef: string }` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0097). | -| **actions** | `{ key: string; label: string; description?: string; inputSchema?: Record; … }[]` | optional | | -| **triggers** | `{ key: string; label: string; description?: string; type: Enum<'polling' \| 'webhook'>; … }[]` | optional | Trigger definitions (not yet enforced — never read at registration; see #3197) | -| **syncConfig** | `{ strategy?: Enum<'full' \| 'incremental' \| 'upsert' \| 'append_only'>; direction?: Enum<'import' \| 'export' \| 'bidirectional'>; schedule?: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; realtimeSync?: boolean; … }` | optional | Data sync configuration | -| **fieldMappings** | `{ source: string; target: string; transform?: { type: 'constant'; value: any } \| { type: 'cast'; targetType: Enum<'string' \| 'number' \| 'boolean' \| 'date'> } \| { type: 'lookup'; table: string; keyField: string; valueField: string } \| { type: 'javascript'; expression: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } } \| { type: 'map'; mappings: Record }; defaultValue?: any; … }[]` | optional | Field mapping rules | -| **webhooks** | `{ name: string; label?: string; object?: string; triggers?: Enum<'create' \| 'update' \| 'delete'>[]; … }[]` | optional | Webhook configurations (not yet enforced — never read at registration; see #3197) | -| **rateLimitConfig** | `{ strategy?: Enum<'fixed_window' \| 'sliding_window' \| 'token_bucket' \| 'leaky_bucket'>; maxRequests: number; windowSeconds: number; burstCapacity?: number; … }` | optional | Rate limiting configuration | -| **retryConfig** | `{ strategy?: Enum<'exponential_backoff' \| 'linear_backoff' \| 'fixed_delay' \| 'no_retry'>; maxAttempts?: number; initialDelayMs?: number; maxDelayMs?: number; … }` | optional | Retry configuration | -| **connectionTimeoutMs** | `number` | optional | Connection timeout in ms | -| **requestTimeoutMs** | `number` | optional | Request timeout in ms | -| **status** | `Enum<'active' \| 'inactive' \| 'error' \| 'configuring'>` | optional | Connector status | -| **enabled** | `boolean` | optional | Enable connector. On declarative stack entries, false marks a deliberate catalog-only descriptor (#2612). | -| **errorMapping** | `{ rules: { sourceCode: string \| number; sourceMessage?: string; targetCode: string; targetCategory: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; … }[]; defaultCategory?: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; unmappedBehavior: Enum<'passthrough' \| 'generic_error' \| 'throw'>; logUnmapped?: boolean }` | optional | Error mapping configuration | -| **health** | `{ healthCheck?: object; circuitBreaker?: object }` | optional | Health and resilience configuration | -| **metadata** | `Record` | optional | Custom connector metadata | -| **connectionConfig** | `{ host: string; port: number; database: string; username: string; … }` | ✅ | Database connection configuration | -| **poolConfig** | `{ min?: number; max?: number; idleTimeoutMs?: number; connectionTimeoutMs?: number; … }` | optional | Connection pool configuration | -| **sslConfig** | `{ enabled?: boolean; rejectUnauthorized?: boolean; ca?: string; cert?: string; … }` | optional | SSL/TLS configuration | -| **tables** | `{ name: string; label: string; schema?: string; tableName: string; … }[]` | ✅ | Tables to sync | -| **cdcConfig** | `{ enabled?: boolean; method: Enum<'log_based' \| 'trigger_based' \| 'query_based' \| 'custom'>; slotName?: string; publicationName?: string; … }` | optional | CDC configuration | -| **readReplicaConfig** | `{ enabled?: boolean; hosts: { host: string; port: number; weight?: number }[] }` | optional | Read replica configuration | -| **queryTimeoutMs** | `number` | optional | Query timeout in ms | -| **enableQueryLogging** | `boolean` | optional | Enable SQL query logging | - - ---- - -## DatabasePoolConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **min** | `number` | ✅ | Minimum connections in pool | -| **max** | `number` | ✅ | Maximum connections in pool | -| **idleTimeoutMs** | `number` | ✅ | Idle connection timeout in ms | -| **connectionTimeoutMs** | `number` | ✅ | Connection establishment timeout in ms | -| **acquireTimeoutMs** | `number` | ✅ | Connection acquisition timeout in ms | -| **evictionRunIntervalMs** | `number` | ✅ | Connection eviction check interval in ms | -| **testOnBorrow** | `boolean` | ✅ | Test connection before use | - - ---- - -## DatabaseTable - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Table name in ObjectStack (snake_case) | -| **label** | `string` | ✅ | Display label | -| **schema** | `string` | optional | Database schema name | -| **tableName** | `string` | ✅ | Actual table name in database | -| **primaryKey** | `string` | ✅ | Primary key column | -| **enabled** | `boolean` | optional | Enable sync for this table | -| **fieldMappings** | `{ source: string; target: string; transform?: { type: 'constant'; value: any } \| { type: 'cast'; targetType: Enum<'string' \| 'number' \| 'boolean' \| 'date'> } \| { type: 'lookup'; table: string; keyField: string; valueField: string } \| { type: 'javascript'; expression: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } } \| { type: 'map'; mappings: Record }; defaultValue?: any; … }[]` | optional | Table-specific field mappings | -| **whereClause** | `string` | optional | SQL WHERE clause for filtering | - - ---- - -## SslConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable SSL/TLS | -| **rejectUnauthorized** | `boolean` | ✅ | Reject unauthorized certificates | -| **ca** | `string` | optional | Certificate Authority certificate | -| **cert** | `string` | optional | Client certificate | -| **key** | `string` | optional | Client private key | - - ---- - diff --git a/content/docs/references/integration/connector-file-storage.mdx b/content/docs/references/integration/connector-file-storage.mdx deleted file mode 100644 index 5235de8e66..0000000000 --- a/content/docs/references/integration/connector-file-storage.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Connector File Storage -description: Connector File Storage protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -File Storage Connector Protocol Template - -Specialized connector for file storage systems (S3, Azure Blob, Google Cloud Storage, etc.) - -Extends the base connector with file-specific features like multipart uploads, - -versioning, and metadata extraction. - - -**Source:** `packages/spec/src/integration/connector/file-storage.zod.ts` - - -## TypeScript Usage - -```typescript -import { FileAccessPattern, FileFilterConfig, FileMetadataConfig, FileStorageConnector, FileStorageProvider, FileVersioningConfig, StorageBucket } from '@objectstack/spec/integration'; -import type { FileAccessPattern, FileFilterConfig, FileMetadataConfig, FileStorageConnector, FileStorageProvider, FileVersioningConfig, StorageBucket } from '@objectstack/spec/integration'; - -// Validate data -const result = FileAccessPattern.parse(data); -``` - ---- - -## FileAccessPattern - -File access pattern - -### Allowed Values - -* `public_read` -* `private` -* `authenticated_read` -* `bucket_owner_read` -* `bucket_owner_full` - - ---- - -## FileFilterConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **includePatterns** | `string[]` | optional | File patterns to include (glob) | -| **excludePatterns** | `string[]` | optional | File patterns to exclude (glob) | -| **minFileSize** | `number` | optional | Minimum file size in bytes | -| **maxFileSize** | `number` | optional | Maximum file size in bytes | -| **allowedExtensions** | `string[]` | optional | Allowed file extensions | -| **blockedExtensions** | `string[]` | optional | Blocked file extensions | - - ---- - -## FileMetadataConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **extractMetadata** | `boolean` | ✅ | Extract file metadata | -| **metadataFields** | `Enum<'content_type' \| 'file_size' \| 'last_modified' \| 'etag' \| 'checksum' \| 'creator' \| 'created_at' \| 'custom'>[]` | optional | Metadata fields to extract | -| **customMetadata** | `Record` | optional | Custom metadata key-value pairs | - - ---- - -## FileStorageConnector - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Unique connector identifier | -| **label** | `string` | ✅ | Display label | -| **type** | `'file_storage'` | ✅ | | -| **description** | `string` | optional | Connector description | -| **icon** | `string` | optional | Icon identifier | -| **authentication** | `{ type: 'oauth2'; authorizationUrl: string; tokenUrl: string; clientId: string; … } \| { type: 'api-key'; key: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; password: string } \| { type: 'bearer'; token: string } \| { type: 'none' }` | optional | Authentication configuration (runtime shape with inline secrets). Provider-bound declarative instances use `auth.credentialRef` instead. | -| **provider** | `Enum<'s3' \| 'azure_blob' \| 'gcs' \| 'dropbox' \| 'box' \| 'onedrive' \| 'google_drive' \| 'sharepoint' \| 'ftp' \| 'local' \| 'custom'>` | ✅ | File storage provider type | -| **providerConfig** | `Record` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires `provider`. | -| **auth** | `{ type: 'none' } \| { type: 'bearer'; credentialRef: string } \| { type: 'api-key'; credentialRef: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; credentialRef: string }` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0097). | -| **actions** | `{ key: string; label: string; description?: string; inputSchema?: Record; … }[]` | optional | | -| **triggers** | `{ key: string; label: string; description?: string; type: Enum<'polling' \| 'webhook'>; … }[]` | optional | Trigger definitions (not yet enforced — never read at registration; see #3197) | -| **syncConfig** | `{ strategy?: Enum<'full' \| 'incremental' \| 'upsert' \| 'append_only'>; direction?: Enum<'import' \| 'export' \| 'bidirectional'>; schedule?: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; realtimeSync?: boolean; … }` | optional | Data sync configuration | -| **fieldMappings** | `{ source: string; target: string; transform?: { type: 'constant'; value: any } \| { type: 'cast'; targetType: Enum<'string' \| 'number' \| 'boolean' \| 'date'> } \| { type: 'lookup'; table: string; keyField: string; valueField: string } \| { type: 'javascript'; expression: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } } \| { type: 'map'; mappings: Record }; defaultValue?: any; … }[]` | optional | Field mapping rules | -| **webhooks** | `{ name: string; label?: string; object?: string; triggers?: Enum<'create' \| 'update' \| 'delete'>[]; … }[]` | optional | Webhook configurations (not yet enforced — never read at registration; see #3197) | -| **rateLimitConfig** | `{ strategy?: Enum<'fixed_window' \| 'sliding_window' \| 'token_bucket' \| 'leaky_bucket'>; maxRequests: number; windowSeconds: number; burstCapacity?: number; … }` | optional | Rate limiting configuration | -| **retryConfig** | `{ strategy?: Enum<'exponential_backoff' \| 'linear_backoff' \| 'fixed_delay' \| 'no_retry'>; maxAttempts?: number; initialDelayMs?: number; maxDelayMs?: number; … }` | optional | Retry configuration | -| **connectionTimeoutMs** | `number` | optional | Connection timeout in ms | -| **requestTimeoutMs** | `number` | optional | Request timeout in ms | -| **status** | `Enum<'active' \| 'inactive' \| 'error' \| 'configuring'>` | optional | Connector status | -| **enabled** | `boolean` | optional | Enable connector. On declarative stack entries, false marks a deliberate catalog-only descriptor (#2612). | -| **errorMapping** | `{ rules: { sourceCode: string \| number; sourceMessage?: string; targetCode: string; targetCategory: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; … }[]; defaultCategory?: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; unmappedBehavior: Enum<'passthrough' \| 'generic_error' \| 'throw'>; logUnmapped?: boolean }` | optional | Error mapping configuration | -| **health** | `{ healthCheck?: object; circuitBreaker?: object }` | optional | Health and resilience configuration | -| **metadata** | `Record` | optional | Custom connector metadata | -| **storageConfig** | `{ endpoint?: string; region?: string; pathStyle?: boolean }` | optional | Storage configuration | -| **buckets** | `{ name: string; label: string; bucketName: string; region?: string; … }[]` | ✅ | Buckets/containers to sync | -| **metadataConfig** | `{ extractMetadata?: boolean; metadataFields?: Enum<'content_type' \| 'file_size' \| 'last_modified' \| 'etag' \| 'checksum' \| 'creator' \| 'created_at' \| 'custom'>[]; customMetadata?: Record }` | optional | Metadata extraction configuration | -| **multipartConfig** | `{ enabled?: boolean; partSize?: number; maxConcurrentParts?: number; threshold?: number }` | optional | Multipart upload configuration | -| **versioningConfig** | `{ enabled?: boolean; maxVersions?: number; retentionDays?: number }` | optional | File versioning configuration | -| **encryption** | `{ enabled?: boolean; algorithm?: Enum<'AES256' \| 'aws:kms' \| 'custom'>; kmsKeyId?: string }` | optional | Encryption configuration | -| **lifecyclePolicy** | `{ enabled?: boolean; deleteAfterDays?: number; archiveAfterDays?: number }` | optional | Lifecycle policy | -| **contentProcessing** | `{ extractText?: boolean; generateThumbnails?: boolean; thumbnailSizes?: { width: number; height: number }[]; virusScan?: boolean }` | optional | Content processing configuration | -| **bufferSize** | `number` | optional | Buffer size in bytes | -| **transferAcceleration** | `boolean` | optional | Enable transfer acceleration | - - ---- - -## FileStorageProvider - -File storage provider type - -### Allowed Values - -* `s3` -* `azure_blob` -* `gcs` -* `dropbox` -* `box` -* `onedrive` -* `google_drive` -* `sharepoint` -* `ftp` -* `local` -* `custom` - - ---- - -## FileVersioningConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable file versioning | -| **maxVersions** | `number` | optional | Maximum versions to retain | -| **retentionDays** | `number` | optional | Version retention period in days | - - ---- - -## StorageBucket - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Bucket identifier in ObjectStack (snake_case) | -| **label** | `string` | ✅ | Display label | -| **bucketName** | `string` | ✅ | Actual bucket/container name in storage system | -| **region** | `string` | optional | Storage region | -| **enabled** | `boolean` | ✅ | Enable sync for this bucket | -| **prefix** | `string` | optional | Prefix/path within bucket | -| **accessPattern** | `Enum<'public_read' \| 'private' \| 'authenticated_read' \| 'bucket_owner_read' \| 'bucket_owner_full'>` | optional | Access pattern | -| **fileFilters** | `{ includePatterns?: string[]; excludePatterns?: string[]; minFileSize?: number; maxFileSize?: number; … }` | optional | File filter configuration | - - ---- - diff --git a/content/docs/references/integration/connector-github.mdx b/content/docs/references/integration/connector-github.mdx deleted file mode 100644 index df78fc7bbb..0000000000 --- a/content/docs/references/integration/connector-github.mdx +++ /dev/null @@ -1,246 +0,0 @@ ---- -title: Connector Github -description: Connector Github protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -GitHub Connector Protocol - -Specialized connector for GitHub integration enabling automated - -version control operations, CI/CD workflows, and release management. - -Use Cases: - -- Automated code commits and pull requests - -- GitHub Actions workflow management - -- Issue and project tracking - -- Release and tag management - -- Repository administration - -@example - -```typescript - -import \{ GitHubConnector \} from '@objectstack/spec/integration'; - -const githubConnector: GitHubConnector = \{ - -name: 'github_enterprise', - -label: 'GitHub Enterprise', - -type: 'saas', - -provider: 'github', - -baseUrl: 'https://api.github.com', - -authentication: \{ - -type: 'oauth2', - -clientId: '$\{GITHUB_CLIENT_ID\}', - -clientSecret: '$\{GITHUB_CLIENT_SECRET\}', - -authorizationUrl: 'https://github.com/login/oauth/authorize', - -tokenUrl: 'https://github.com/login/oauth/access_token', - -grantType: 'authorization_code', - -scopes: ['repo', 'workflow', 'admin:org'], - -\}, - -repositories: [ - -\{ - -owner: 'objectstack-ai', - -name: 'spec', - -defaultBranch: 'main', - -autoMerge: false, - -\}, - -], - -\}; - -``` - - -**Source:** `packages/spec/src/integration/connector/github.zod.ts` - - -## TypeScript Usage - -```typescript -import { GitHubActionsWorkflow, GitHubCommitConfig, GitHubConnector, GitHubIssueTracking, GitHubProvider, GitHubPullRequestConfig, GitHubReleaseConfig, GitHubRepository } from '@objectstack/spec/integration'; -import type { GitHubActionsWorkflow, GitHubCommitConfig, GitHubConnector, GitHubIssueTracking, GitHubProvider, GitHubPullRequestConfig, GitHubReleaseConfig, GitHubRepository } from '@objectstack/spec/integration'; - -// Validate data -const result = GitHubActionsWorkflow.parse(data); -``` - ---- - -## GitHubActionsWorkflow - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Workflow name | -| **path** | `string` | ✅ | Workflow file path (e.g., .github/workflows/ci.yml) | -| **enabled** | `boolean` | ✅ | Enable workflow | -| **triggers** | `Enum<'push' \| 'pull_request' \| 'release' \| 'schedule' \| 'workflow_dispatch' \| 'repository_dispatch'>[]` | optional | Workflow triggers | -| **env** | `Record` | optional | Environment variables | -| **secrets** | `string[]` | optional | Required secrets | - - ---- - -## GitHubCommitConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **authorName** | `string` | optional | Commit author name | -| **authorEmail** | `string` | optional | Commit author email | -| **signCommits** | `boolean` | ✅ | Sign commits with GPG | -| **messageTemplate** | `string` | optional | Commit message template | -| **useConventionalCommits** | `boolean` | ✅ | Use conventional commits format | - - ---- - -## GitHubConnector - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Unique connector identifier | -| **label** | `string` | ✅ | Display label | -| **type** | `'saas'` | ✅ | | -| **description** | `string` | optional | Connector description | -| **icon** | `string` | optional | Icon identifier | -| **authentication** | `{ type: 'oauth2'; authorizationUrl: string; tokenUrl: string; clientId: string; … } \| { type: 'api-key'; key: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; password: string } \| { type: 'bearer'; token: string } \| { type: 'none' }` | optional | Authentication configuration (runtime shape with inline secrets). Provider-bound declarative instances use `auth.credentialRef` instead. | -| **provider** | `Enum<'github' \| 'github_enterprise'>` | ✅ | GitHub provider | -| **providerConfig** | `Record` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires `provider`. | -| **auth** | `{ type: 'none' } \| { type: 'bearer'; credentialRef: string } \| { type: 'api-key'; credentialRef: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; credentialRef: string }` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0097). | -| **actions** | `{ key: string; label: string; description?: string; inputSchema?: Record; … }[]` | optional | | -| **triggers** | `{ key: string; label: string; description?: string; type: Enum<'polling' \| 'webhook'>; … }[]` | optional | Trigger definitions (not yet enforced — never read at registration; see #3197) | -| **syncConfig** | `{ strategy?: Enum<'full' \| 'incremental' \| 'upsert' \| 'append_only'>; direction?: Enum<'import' \| 'export' \| 'bidirectional'>; schedule?: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; realtimeSync?: boolean; … }` | optional | Data sync configuration | -| **fieldMappings** | `{ source: string; target: string; transform?: { type: 'constant'; value: any } \| { type: 'cast'; targetType: Enum<'string' \| 'number' \| 'boolean' \| 'date'> } \| { type: 'lookup'; table: string; keyField: string; valueField: string } \| { type: 'javascript'; expression: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } } \| { type: 'map'; mappings: Record }; defaultValue?: any; … }[]` | optional | Field mapping rules | -| **webhooks** | `{ name: string; label?: string; object?: string; triggers?: Enum<'create' \| 'update' \| 'delete'>[]; … }[]` | optional | Webhook configurations (not yet enforced — never read at registration; see #3197) | -| **rateLimitConfig** | `{ strategy?: Enum<'fixed_window' \| 'sliding_window' \| 'token_bucket' \| 'leaky_bucket'>; maxRequests: number; windowSeconds: number; burstCapacity?: number; … }` | optional | Rate limiting configuration | -| **retryConfig** | `{ strategy?: Enum<'exponential_backoff' \| 'linear_backoff' \| 'fixed_delay' \| 'no_retry'>; maxAttempts?: number; initialDelayMs?: number; maxDelayMs?: number; … }` | optional | Retry configuration | -| **connectionTimeoutMs** | `number` | optional | Connection timeout in ms | -| **requestTimeoutMs** | `number` | optional | Request timeout in ms | -| **status** | `Enum<'active' \| 'inactive' \| 'error' \| 'configuring'>` | optional | Connector status | -| **enabled** | `boolean` | optional | Enable connector. On declarative stack entries, false marks a deliberate catalog-only descriptor (#2612). | -| **errorMapping** | `{ rules: { sourceCode: string \| number; sourceMessage?: string; targetCode: string; targetCategory: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; … }[]; defaultCategory?: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; unmappedBehavior: Enum<'passthrough' \| 'generic_error' \| 'throw'>; logUnmapped?: boolean }` | optional | Error mapping configuration | -| **health** | `{ healthCheck?: object; circuitBreaker?: object }` | optional | Health and resilience configuration | -| **metadata** | `Record` | optional | Custom connector metadata | -| **baseUrl** | `string` | optional | GitHub API base URL | -| **repositories** | `{ owner: string; name: string; defaultBranch?: string; autoMerge?: boolean; … }[]` | ✅ | Repositories to manage | -| **commitConfig** | `{ authorName?: string; authorEmail?: string; signCommits?: boolean; messageTemplate?: string; … }` | optional | Commit configuration | -| **pullRequestConfig** | `{ titleTemplate?: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; bodyTemplate?: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; defaultReviewers?: string[]; defaultAssignees?: string[]; … }` | optional | Pull request configuration | -| **workflows** | `{ name: string; path: string; enabled?: boolean; triggers?: Enum<'push' \| 'pull_request' \| 'release' \| 'schedule' \| 'workflow_dispatch' \| 'repository_dispatch'>[]; … }[]` | optional | GitHub Actions workflows | -| **releaseConfig** | `{ tagPattern?: string; semanticVersioning?: boolean; autoReleaseNotes?: boolean; releaseNameTemplate?: string; … }` | optional | Release configuration | -| **issueTracking** | `{ enabled?: boolean; defaultLabels?: string[]; templatePaths?: string[]; autoAssign?: boolean; … }` | optional | Issue tracking configuration | -| **enableWebhooks** | `boolean` | optional | Enable GitHub webhooks | -| **webhookEvents** | `Enum<'push' \| 'pull_request' \| 'issues' \| 'issue_comment' \| 'release' \| 'workflow_run' \| 'deployment' \| 'deployment_status' \| 'check_run' \| 'check_suite' \| 'status'>[]` | optional | Webhook events to subscribe to | - - ---- - -## GitHubIssueTracking - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable issue tracking | -| **defaultLabels** | `string[]` | optional | Default issue labels | -| **templatePaths** | `string[]` | optional | Issue template paths | -| **autoAssign** | `boolean` | ✅ | Auto-assign issues | -| **autoCloseStale** | `{ enabled: boolean; daysBeforeStale: integer; daysBeforeClose: integer; staleLabel: string }` | optional | Auto-close stale issues configuration | - - ---- - -## GitHubProvider - -GitHub provider type - -### Allowed Values - -* `github` -* `github_enterprise` - - ---- - -## GitHubPullRequestConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **titleTemplate** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }` | optional | PR title template — supports `{{var}`} interpolation | -| **bodyTemplate** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }` | optional | PR body template — supports `{{var}`} interpolation | -| **defaultReviewers** | `string[]` | optional | Default reviewers (usernames) | -| **defaultAssignees** | `string[]` | optional | Default assignees (usernames) | -| **defaultLabels** | `string[]` | optional | Default labels | -| **draftByDefault** | `boolean` | optional | Create draft PRs by default | -| **deleteHeadBranch** | `boolean` | optional | Delete head branch after merge | - - ---- - -## GitHubReleaseConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **tagPattern** | `string` | ✅ | Tag name pattern (e.g., v*, release/*) | -| **semanticVersioning** | `boolean` | ✅ | Use semantic versioning | -| **autoReleaseNotes** | `boolean` | ✅ | Generate release notes automatically | -| **releaseNameTemplate** | `string` | optional | Release name template | -| **preReleasePattern** | `string` | optional | Pre-release pattern (e.g., *-alpha, *-beta) | -| **draftByDefault** | `boolean` | ✅ | Create draft releases by default | - - ---- - -## GitHubRepository - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **owner** | `string` | ✅ | Repository owner (organization or username) | -| **name** | `string` | ✅ | Repository name | -| **defaultBranch** | `string` | ✅ | Default branch name | -| **autoMerge** | `boolean` | ✅ | Enable auto-merge for pull requests | -| **branchProtection** | `{ requiredReviewers: integer; requireStatusChecks: boolean; enforceAdmins: boolean; allowForcePushes: boolean; … }` | optional | Branch protection configuration | -| **topics** | `string[]` | optional | Repository topics | - - ---- - diff --git a/content/docs/references/integration/connector-message-queue.mdx b/content/docs/references/integration/connector-message-queue.mdx deleted file mode 100644 index 3af90419ab..0000000000 --- a/content/docs/references/integration/connector-message-queue.mdx +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Connector Message Queue -description: Connector Message Queue protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -Message Queue Connector Protocol Template - -Specialized connector for message queue systems (RabbitMQ, Kafka, SQS, etc.) - -Extends the base connector with message queue-specific features like topics, - -consumer groups, and message acknowledgment patterns. - - -**Source:** `packages/spec/src/integration/connector/message-queue.zod.ts` - - -## TypeScript Usage - -```typescript -import { AckMode, DeliveryGuarantee, DlqConfig, MessageFormat, MessageQueueConnector, ProducerConfig, TopicQueue } from '@objectstack/spec/integration'; -import type { AckMode, DeliveryGuarantee, DlqConfig, MessageFormat, MessageQueueConnector, ProducerConfig, TopicQueue } from '@objectstack/spec/integration'; - -// Validate data -const result = AckMode.parse(data); -``` - ---- - -## AckMode - -Message acknowledgment mode - -### Allowed Values - -* `auto` -* `manual` -* `client` - - ---- - -## DeliveryGuarantee - -Message delivery guarantee - -### Allowed Values - -* `at_most_once` -* `at_least_once` -* `exactly_once` - - ---- - -## DlqConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable DLQ | -| **queueName** | `string` | ✅ | Dead letter queue/topic name | -| **maxRetries** | `number` | ✅ | Max retries before DLQ | -| **retryDelayMs** | `number` | ✅ | Retry delay in ms | - - ---- - -## MessageFormat - -Message format/serialization - -### Allowed Values - -* `json` -* `xml` -* `protobuf` -* `avro` -* `text` -* `binary` - - ---- - -## MessageQueueConnector - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Unique connector identifier | -| **label** | `string` | ✅ | Display label | -| **type** | `'message_queue'` | ✅ | | -| **description** | `string` | optional | Connector description | -| **icon** | `string` | optional | Icon identifier | -| **authentication** | `{ type: 'oauth2'; authorizationUrl: string; tokenUrl: string; clientId: string; … } \| { type: 'api-key'; key: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; password: string } \| { type: 'bearer'; token: string } \| { type: 'none' }` | optional | Authentication configuration (runtime shape with inline secrets). Provider-bound declarative instances use `auth.credentialRef` instead. | -| **provider** | `Enum<'rabbitmq' \| 'kafka' \| 'redis_pubsub' \| 'redis_streams' \| 'aws_sqs' \| 'aws_sns' \| 'google_pubsub' \| 'azure_service_bus' \| 'azure_event_hubs' \| 'nats' \| 'pulsar' \| 'activemq' \| 'custom'>` | ✅ | Message queue provider type | -| **providerConfig** | `Record` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires `provider`. | -| **auth** | `{ type: 'none' } \| { type: 'bearer'; credentialRef: string } \| { type: 'api-key'; credentialRef: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; credentialRef: string }` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0097). | -| **actions** | `{ key: string; label: string; description?: string; inputSchema?: Record; … }[]` | optional | | -| **triggers** | `{ key: string; label: string; description?: string; type: Enum<'polling' \| 'webhook'>; … }[]` | optional | Trigger definitions (not yet enforced — never read at registration; see #3197) | -| **syncConfig** | `{ strategy?: Enum<'full' \| 'incremental' \| 'upsert' \| 'append_only'>; direction?: Enum<'import' \| 'export' \| 'bidirectional'>; schedule?: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; realtimeSync?: boolean; … }` | optional | Data sync configuration | -| **fieldMappings** | `{ source: string; target: string; transform?: { type: 'constant'; value: any } \| { type: 'cast'; targetType: Enum<'string' \| 'number' \| 'boolean' \| 'date'> } \| { type: 'lookup'; table: string; keyField: string; valueField: string } \| { type: 'javascript'; expression: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } } \| { type: 'map'; mappings: Record }; defaultValue?: any; … }[]` | optional | Field mapping rules | -| **webhooks** | `{ name: string; label?: string; object?: string; triggers?: Enum<'create' \| 'update' \| 'delete'>[]; … }[]` | optional | Webhook configurations (not yet enforced — never read at registration; see #3197) | -| **rateLimitConfig** | `{ strategy?: Enum<'fixed_window' \| 'sliding_window' \| 'token_bucket' \| 'leaky_bucket'>; maxRequests: number; windowSeconds: number; burstCapacity?: number; … }` | optional | Rate limiting configuration | -| **retryConfig** | `{ strategy?: Enum<'exponential_backoff' \| 'linear_backoff' \| 'fixed_delay' \| 'no_retry'>; maxAttempts?: number; initialDelayMs?: number; maxDelayMs?: number; … }` | optional | Retry configuration | -| **connectionTimeoutMs** | `number` | optional | Connection timeout in ms | -| **requestTimeoutMs** | `number` | optional | Request timeout in ms | -| **status** | `Enum<'active' \| 'inactive' \| 'error' \| 'configuring'>` | optional | Connector status | -| **enabled** | `boolean` | optional | Enable connector. On declarative stack entries, false marks a deliberate catalog-only descriptor (#2612). | -| **errorMapping** | `{ rules: { sourceCode: string \| number; sourceMessage?: string; targetCode: string; targetCategory: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; … }[]; defaultCategory?: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; unmappedBehavior: Enum<'passthrough' \| 'generic_error' \| 'throw'>; logUnmapped?: boolean }` | optional | Error mapping configuration | -| **health** | `{ healthCheck?: object; circuitBreaker?: object }` | optional | Health and resilience configuration | -| **metadata** | `Record` | optional | Custom connector metadata | -| **brokerConfig** | `{ brokers: string[]; clientId?: string; connectionTimeoutMs?: number; requestTimeoutMs?: number }` | ✅ | Broker connection configuration | -| **topics** | `{ name: string; label: string; topicName: string; enabled?: boolean; … }[]` | ✅ | Topics/queues to sync | -| **deliveryGuarantee** | `Enum<'at_most_once' \| 'at_least_once' \| 'exactly_once'>` | optional | Message delivery guarantee | -| **sslConfig** | `{ enabled?: boolean; rejectUnauthorized?: boolean; ca?: string; cert?: string; … }` | optional | SSL/TLS configuration | -| **saslConfig** | `{ mechanism: Enum<'plain' \| 'scram-sha-256' \| 'scram-sha-512' \| 'aws'>; username?: string; password?: string }` | optional | SASL authentication configuration | -| **schemaRegistry** | `{ url: string; auth?: object }` | optional | Schema registry configuration | -| **preserveOrder** | `boolean` | optional | Preserve message ordering | -| **enableMetrics** | `boolean` | optional | Enable message queue metrics | -| **enableTracing** | `boolean` | optional | Enable distributed tracing | - - ---- - -## ProducerConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable producer | -| **acks** | `Enum<'0' \| '1' \| 'all'>` | ✅ | Acknowledgment level | -| **compressionType** | `Enum<'none' \| 'gzip' \| 'snappy' \| 'lz4' \| 'zstd'>` | ✅ | Compression type | -| **batchSize** | `number` | ✅ | Batch size in bytes | -| **lingerMs** | `number` | ✅ | Linger time in ms | -| **maxInFlightRequests** | `number` | ✅ | Max in-flight requests | -| **idempotence** | `boolean` | ✅ | Enable idempotent producer | -| **transactional** | `boolean` | ✅ | Enable transactional producer | -| **transactionTimeoutMs** | `number` | optional | Transaction timeout in ms | - - ---- - -## TopicQueue - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Topic/queue identifier in ObjectStack (snake_case) | -| **label** | `string` | ✅ | Display label | -| **topicName** | `string` | ✅ | Actual topic/queue name in message queue system | -| **enabled** | `boolean` | ✅ | Enable sync for this topic/queue | -| **mode** | `Enum<'consumer' \| 'producer' \| 'both'>` | ✅ | Consumer, producer, or both | -| **messageFormat** | `Enum<'json' \| 'xml' \| 'protobuf' \| 'avro' \| 'text' \| 'binary'>` | ✅ | Message format/serialization | -| **partitions** | `number` | optional | Number of partitions (for Kafka) | -| **replicationFactor** | `number` | optional | Replication factor (for Kafka) | -| **consumerConfig** | `{ enabled: boolean; consumerGroup?: string; concurrency: number; prefetchCount: number; … }` | optional | Consumer-specific configuration | -| **producerConfig** | `{ enabled: boolean; acks: Enum<'0' \| '1' \| 'all'>; compressionType: Enum<'none' \| 'gzip' \| 'snappy' \| 'lz4' \| 'zstd'>; batchSize: number; … }` | optional | Producer-specific configuration | -| **dlqConfig** | `{ enabled: boolean; queueName: string; maxRetries: number; retryDelayMs: number }` | optional | Dead letter queue configuration | -| **routingKey** | `string` | optional | Routing key pattern | -| **messageFilter** | `{ headers?: Record; attributes?: Record }` | optional | Message filter criteria | - - ---- - diff --git a/content/docs/references/integration/connector-saas.mdx b/content/docs/references/integration/connector-saas.mdx deleted file mode 100644 index 2cf29adfae..0000000000 --- a/content/docs/references/integration/connector-saas.mdx +++ /dev/null @@ -1,125 +0,0 @@ ---- -title: Connector Saas -description: Connector Saas protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -SaaS Connector Protocol Template - -Specialized connector for SaaS applications (Salesforce, HubSpot, Stripe, etc.) - -Extends the base connector with SaaS-specific features like OAuth flows, - -object type discovery, and API version management. - - -**Source:** `packages/spec/src/integration/connector/saas.zod.ts` - - -## TypeScript Usage - -```typescript -import { ApiVersionConfig, SaasConnector, SaasObjectType, SaasProvider } from '@objectstack/spec/integration'; -import type { ApiVersionConfig, SaasConnector, SaasObjectType, SaasProvider } from '@objectstack/spec/integration'; - -// Validate data -const result = ApiVersionConfig.parse(data); -``` - ---- - -## ApiVersionConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **version** | `string` | ✅ | API version (e.g., "v2", "2023-10-01") | -| **isDefault** | `boolean` | ✅ | Is this the default version | -| **deprecationDate** | `string` | optional | API version deprecation date (ISO 8601) | -| **sunsetDate** | `string` | optional | API version sunset date (ISO 8601) | - - ---- - -## SaasConnector - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Unique connector identifier | -| **label** | `string` | ✅ | Display label | -| **type** | `'saas'` | ✅ | | -| **description** | `string` | optional | Connector description | -| **icon** | `string` | optional | Icon identifier | -| **authentication** | `{ type: 'oauth2'; authorizationUrl: string; tokenUrl: string; clientId: string; … } \| { type: 'api-key'; key: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; password: string } \| { type: 'bearer'; token: string } \| { type: 'none' }` | optional | Authentication configuration (runtime shape with inline secrets). Provider-bound declarative instances use `auth.credentialRef` instead. | -| **provider** | `Enum<'salesforce' \| 'hubspot' \| 'stripe' \| 'shopify' \| 'zendesk' \| 'intercom' \| 'mailchimp' \| 'slack' \| 'microsoft_dynamics' \| 'servicenow' \| 'netsuite' \| 'custom'>` | ✅ | SaaS provider type | -| **providerConfig** | `Record` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires `provider`. | -| **auth** | `{ type: 'none' } \| { type: 'bearer'; credentialRef: string } \| { type: 'api-key'; credentialRef: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; credentialRef: string }` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0097). | -| **actions** | `{ key: string; label: string; description?: string; inputSchema?: Record; … }[]` | optional | | -| **triggers** | `{ key: string; label: string; description?: string; type: Enum<'polling' \| 'webhook'>; … }[]` | optional | Trigger definitions (not yet enforced — never read at registration; see #3197) | -| **syncConfig** | `{ strategy?: Enum<'full' \| 'incremental' \| 'upsert' \| 'append_only'>; direction?: Enum<'import' \| 'export' \| 'bidirectional'>; schedule?: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; realtimeSync?: boolean; … }` | optional | Data sync configuration | -| **fieldMappings** | `{ source: string; target: string; transform?: { type: 'constant'; value: any } \| { type: 'cast'; targetType: Enum<'string' \| 'number' \| 'boolean' \| 'date'> } \| { type: 'lookup'; table: string; keyField: string; valueField: string } \| { type: 'javascript'; expression: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } } \| { type: 'map'; mappings: Record }; defaultValue?: any; … }[]` | optional | Field mapping rules | -| **webhooks** | `{ name: string; label?: string; object?: string; triggers?: Enum<'create' \| 'update' \| 'delete'>[]; … }[]` | optional | Webhook configurations (not yet enforced — never read at registration; see #3197) | -| **rateLimitConfig** | `{ strategy?: Enum<'fixed_window' \| 'sliding_window' \| 'token_bucket' \| 'leaky_bucket'>; maxRequests: number; windowSeconds: number; burstCapacity?: number; … }` | optional | Rate limiting configuration | -| **retryConfig** | `{ strategy?: Enum<'exponential_backoff' \| 'linear_backoff' \| 'fixed_delay' \| 'no_retry'>; maxAttempts?: number; initialDelayMs?: number; maxDelayMs?: number; … }` | optional | Retry configuration | -| **connectionTimeoutMs** | `number` | optional | Connection timeout in ms | -| **requestTimeoutMs** | `number` | optional | Request timeout in ms | -| **status** | `Enum<'active' \| 'inactive' \| 'error' \| 'configuring'>` | optional | Connector status | -| **enabled** | `boolean` | optional | Enable connector. On declarative stack entries, false marks a deliberate catalog-only descriptor (#2612). | -| **errorMapping** | `{ rules: { sourceCode: string \| number; sourceMessage?: string; targetCode: string; targetCategory: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; … }[]; defaultCategory?: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; unmappedBehavior: Enum<'passthrough' \| 'generic_error' \| 'throw'>; logUnmapped?: boolean }` | optional | Error mapping configuration | -| **health** | `{ healthCheck?: object; circuitBreaker?: object }` | optional | Health and resilience configuration | -| **metadata** | `Record` | optional | Custom connector metadata | -| **baseUrl** | `string` | ✅ | API base URL | -| **apiVersion** | `{ version: string; isDefault?: boolean; deprecationDate?: string; sunsetDate?: string }` | optional | API version configuration | -| **objectTypes** | `{ name: string; label: string; apiName: string; enabled?: boolean; … }[]` | ✅ | Syncable object types | -| **oauthSettings** | `{ scopes: string[]; refreshTokenUrl?: string; revokeTokenUrl?: string; autoRefresh?: boolean }` | optional | OAuth-specific configuration | -| **paginationConfig** | `{ type?: Enum<'cursor' \| 'offset' \| 'page'>; defaultPageSize?: number; maxPageSize?: number }` | optional | Pagination configuration | -| **sandboxConfig** | `{ enabled?: boolean; baseUrl?: string }` | optional | Sandbox environment configuration | -| **customHeaders** | `Record` | optional | Custom HTTP headers for all requests | - - ---- - -## SaasObjectType - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Object type name (snake_case) | -| **label** | `string` | ✅ | Display label | -| **apiName** | `string` | ✅ | API name in external system | -| **enabled** | `boolean` | optional | Enable sync for this object | -| **supportsCreate** | `boolean` | optional | Supports record creation | -| **supportsUpdate** | `boolean` | optional | Supports record updates | -| **supportsDelete** | `boolean` | optional | Supports record deletion | -| **fieldMappings** | `{ source: string; target: string; transform?: { type: 'constant'; value: any } \| { type: 'cast'; targetType: Enum<'string' \| 'number' \| 'boolean' \| 'date'> } \| { type: 'lookup'; table: string; keyField: string; valueField: string } \| { type: 'javascript'; expression: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } } \| { type: 'map'; mappings: Record }; defaultValue?: any; … }[]` | optional | Object-specific field mappings | - - ---- - -## SaasProvider - -SaaS provider type - -### Allowed Values - -* `salesforce` -* `hubspot` -* `stripe` -* `shopify` -* `zendesk` -* `intercom` -* `mailchimp` -* `slack` -* `microsoft_dynamics` -* `servicenow` -* `netsuite` -* `custom` - - ---- - diff --git a/content/docs/references/integration/connector-vercel.mdx b/content/docs/references/integration/connector-vercel.mdx deleted file mode 100644 index 43e1b0fd4e..0000000000 --- a/content/docs/references/integration/connector-vercel.mdx +++ /dev/null @@ -1,300 +0,0 @@ ---- -title: Connector Vercel -description: Connector Vercel protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -Vercel Connector Protocol - -Specialized connector for Vercel deployment platform enabling automated - -deployments, preview environments, and production releases. - -Use Cases: - -- Automated deployments from Git - -- Preview deployments for pull requests - -- Production releases - -- Environment variable management - -- Domain and SSL configuration - -- Edge function deployment - -@example - -```typescript - -import \{ VercelConnector \} from '@objectstack/spec/integration'; - -const vercelConnector: VercelConnector = \{ - -name: 'vercel_production', - -label: 'Vercel Production', - -type: 'saas', - -provider: 'vercel', - -baseUrl: 'https://api.vercel.com', - -authentication: \{ - -type: 'bearer', - -token: '$\{VERCEL_TOKEN\}', - -\}, - -projects: [ - -\{ - -name: 'objectstack-app', - -framework: 'nextjs', - -gitRepository: \{ - -type: 'github', - -repo: 'objectstack-ai/app', - -\}, - -\}, - -], - -\}; - -``` - - -**Source:** `packages/spec/src/integration/connector/vercel.zod.ts` - - -## TypeScript Usage - -```typescript -import { BuildConfig, DeploymentConfig, DomainConfig, EdgeFunctionConfig, EnvironmentVariables, GitRepositoryConfig, VercelConnector, VercelFramework, VercelMonitoring, VercelProject, VercelProvider, VercelTeam } from '@objectstack/spec/integration'; -import type { BuildConfig, DeploymentConfig, DomainConfig, EdgeFunctionConfig, EnvironmentVariables, GitRepositoryConfig, VercelConnector, VercelFramework, VercelMonitoring, VercelProject, VercelProvider, VercelTeam } from '@objectstack/spec/integration'; - -// Validate data -const result = BuildConfig.parse(data); -``` - ---- - -## BuildConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **buildCommand** | `string` | optional | Build command (e.g., npm run build) | -| **outputDirectory** | `string` | optional | Output directory (e.g., .next, dist) | -| **installCommand** | `string` | optional | Install command (e.g., npm install, pnpm install) | -| **devCommand** | `string` | optional | Development command (e.g., npm run dev) | -| **nodeVersion** | `string` | optional | Node.js version (e.g., 18.x, 20.x) | -| **env** | `Record` | optional | Build environment variables | - - ---- - -## DeploymentConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **autoDeployment** | `boolean` | ✅ | Enable automatic deployments | -| **regions** | `Enum<'iad1' \| 'sfo1' \| 'gru1' \| 'lhr1' \| 'fra1' \| 'sin1' \| 'syd1' \| 'hnd1' \| 'icn1'>[]` | optional | Deployment regions | -| **enablePreview** | `boolean` | ✅ | Enable preview deployments | -| **previewComments** | `boolean` | ✅ | Post preview URLs in PR comments | -| **productionProtection** | `boolean` | ✅ | Require approval for production deployments | -| **deployHooks** | `{ name: string; url: string; branch?: string }[]` | optional | Deploy hooks | - - ---- - -## DomainConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **domain** | `string` | ✅ | Domain name (e.g., app.example.com) | -| **httpsRedirect** | `boolean` | ✅ | Redirect HTTP to HTTPS | -| **customCertificate** | `{ cert: string; key: string; ca?: string }` | optional | Custom SSL certificate | -| **gitBranch** | `string` | optional | Git branch to deploy to this domain | - - ---- - -## EdgeFunctionConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Edge function name | -| **path** | `string` | ✅ | Function path (e.g., /api/*) | -| **regions** | `string[]` | optional | Specific regions for this function | -| **memoryLimit** | `integer` | ✅ | Memory limit in MB | -| **timeout** | `integer` | ✅ | Timeout in seconds | - - ---- - -## EnvironmentVariables - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **key** | `string` | ✅ | Environment variable name | -| **value** | `string` | ✅ | Environment variable value | -| **target** | `Enum<'production' \| 'preview' \| 'development'>[]` | ✅ | Target environments | -| **isSecret** | `boolean` | ✅ | Encrypt this variable | -| **gitBranch** | `string` | optional | Specific git branch | - - ---- - -## GitRepositoryConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **type** | `Enum<'github' \| 'gitlab' \| 'bitbucket'>` | ✅ | Git provider | -| **repo** | `string` | ✅ | Repository identifier (e.g., owner/repo) | -| **productionBranch** | `string` | ✅ | Production branch name | -| **autoDeployProduction** | `boolean` | ✅ | Auto-deploy production branch | -| **autoDeployPreview** | `boolean` | ✅ | Auto-deploy preview branches | - - ---- - -## VercelConnector - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Unique connector identifier | -| **label** | `string` | ✅ | Display label | -| **type** | `'saas'` | ✅ | | -| **description** | `string` | optional | Connector description | -| **icon** | `string` | optional | Icon identifier | -| **authentication** | `{ type: 'oauth2'; authorizationUrl: string; tokenUrl: string; clientId: string; … } \| { type: 'api-key'; key: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; password: string } \| { type: 'bearer'; token: string } \| { type: 'none' }` | optional | Authentication configuration (runtime shape with inline secrets). Provider-bound declarative instances use `auth.credentialRef` instead. | -| **provider** | `Enum<'vercel'>` | ✅ | Vercel provider | -| **providerConfig** | `Record` | optional | Provider-specific config validated by the provider factory at boot (e.g. `{ spec, baseUrl }` for openapi, where spec is an inline document, a package-relative file path like './billing-openapi.json', or an http(s) URL). Requires `provider`. | -| **auth** | `{ type: 'none' } \| { type: 'bearer'; credentialRef: string } \| { type: 'api-key'; credentialRef: string; headerName?: string; paramName?: string } \| { type: 'basic'; username: string; credentialRef: string }` | optional | Declarative instance auth — references credentials via `credentialRef` (resolved at boot), never inline secrets. Requires `provider` (ADR-0097). | -| **actions** | `{ key: string; label: string; description?: string; inputSchema?: Record; … }[]` | optional | | -| **triggers** | `{ key: string; label: string; description?: string; type: Enum<'polling' \| 'webhook'>; … }[]` | optional | Trigger definitions (not yet enforced — never read at registration; see #3197) | -| **syncConfig** | `{ strategy?: Enum<'full' \| 'incremental' \| 'upsert' \| 'append_only'>; direction?: Enum<'import' \| 'export' \| 'bidirectional'>; schedule?: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; realtimeSync?: boolean; … }` | optional | Data sync configuration | -| **fieldMappings** | `{ source: string; target: string; transform?: { type: 'constant'; value: any } \| { type: 'cast'; targetType: Enum<'string' \| 'number' \| 'boolean' \| 'date'> } \| { type: 'lookup'; table: string; keyField: string; valueField: string } \| { type: 'javascript'; expression: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object } } \| { type: 'map'; mappings: Record }; defaultValue?: any; … }[]` | optional | Field mapping rules | -| **webhooks** | `{ name: string; label?: string; object?: string; triggers?: Enum<'create' \| 'update' \| 'delete'>[]; … }[]` | optional | Webhook configurations (not yet enforced — never read at registration; see #3197) | -| **rateLimitConfig** | `{ strategy?: Enum<'fixed_window' \| 'sliding_window' \| 'token_bucket' \| 'leaky_bucket'>; maxRequests: number; windowSeconds: number; burstCapacity?: number; … }` | optional | Rate limiting configuration | -| **retryConfig** | `{ strategy?: Enum<'exponential_backoff' \| 'linear_backoff' \| 'fixed_delay' \| 'no_retry'>; maxAttempts?: number; initialDelayMs?: number; maxDelayMs?: number; … }` | optional | Retry configuration | -| **connectionTimeoutMs** | `number` | optional | Connection timeout in ms | -| **requestTimeoutMs** | `number` | optional | Request timeout in ms | -| **status** | `Enum<'active' \| 'inactive' \| 'error' \| 'configuring'>` | optional | Connector status | -| **enabled** | `boolean` | optional | Enable connector. On declarative stack entries, false marks a deliberate catalog-only descriptor (#2612). | -| **errorMapping** | `{ rules: { sourceCode: string \| number; sourceMessage?: string; targetCode: string; targetCategory: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; … }[]; defaultCategory?: Enum<'validation' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'timeout' \| 'server_error' \| 'integration_error'>; unmappedBehavior: Enum<'passthrough' \| 'generic_error' \| 'throw'>; logUnmapped?: boolean }` | optional | Error mapping configuration | -| **health** | `{ healthCheck?: object; circuitBreaker?: object }` | optional | Health and resilience configuration | -| **metadata** | `Record` | optional | Custom connector metadata | -| **baseUrl** | `string` | optional | Vercel API base URL | -| **team** | `{ teamId?: string; teamName?: string }` | optional | Vercel team configuration | -| **projects** | `{ name: string; framework?: Enum<'nextjs' \| 'react' \| 'vue' \| 'nuxtjs' \| 'gatsby' \| 'remix' \| 'astro' \| 'sveltekit' \| 'solid' \| 'angular' \| 'static' \| 'other'>; gitRepository?: object; buildConfig?: object; … }[]` | ✅ | Vercel projects | -| **monitoring** | `{ enableWebAnalytics?: boolean; enableSpeedInsights?: boolean; logDrains?: { name: string; url: string; headers?: Record; sources?: Enum<'static' \| 'lambda' \| 'edge'>[] }[] }` | optional | Monitoring configuration | -| **enableWebhooks** | `boolean` | optional | Enable Vercel webhooks | -| **webhookEvents** | `Enum<'deployment.created' \| 'deployment.succeeded' \| 'deployment.failed' \| 'deployment.ready' \| 'deployment.error' \| 'deployment.canceled' \| 'deployment-checks-completed' \| 'deployment-prepared' \| 'project.created' \| 'project.removed'>[]` | optional | Webhook events to subscribe to | - - ---- - -## VercelFramework - -Frontend framework - -### Allowed Values - -* `nextjs` -* `react` -* `vue` -* `nuxtjs` -* `gatsby` -* `remix` -* `astro` -* `sveltekit` -* `solid` -* `angular` -* `static` -* `other` - - ---- - -## VercelMonitoring - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enableWebAnalytics** | `boolean` | ✅ | Enable Vercel Web Analytics | -| **enableSpeedInsights** | `boolean` | ✅ | Enable Vercel Speed Insights | -| **logDrains** | `{ name: string; url: string; headers?: Record; sources?: Enum<'static' \| 'lambda' \| 'edge'>[] }[]` | optional | Log drains configuration | - - ---- - -## VercelProject - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Vercel project name | -| **framework** | `Enum<'nextjs' \| 'react' \| 'vue' \| 'nuxtjs' \| 'gatsby' \| 'remix' \| 'astro' \| 'sveltekit' \| 'solid' \| 'angular' \| 'static' \| 'other'>` | optional | Frontend framework | -| **gitRepository** | `{ type: Enum<'github' \| 'gitlab' \| 'bitbucket'>; repo: string; productionBranch: string; autoDeployProduction: boolean; … }` | optional | Git repository configuration | -| **buildConfig** | `{ buildCommand?: string; outputDirectory?: string; installCommand?: string; devCommand?: string; … }` | optional | Build configuration | -| **deploymentConfig** | `{ autoDeployment: boolean; regions?: Enum<'iad1' \| 'sfo1' \| 'gru1' \| 'lhr1' \| 'fra1' \| 'sin1' \| 'syd1' \| 'hnd1' \| 'icn1'>[]; enablePreview: boolean; previewComments: boolean; … }` | optional | Deployment configuration | -| **domains** | `{ domain: string; httpsRedirect: boolean; customCertificate?: object; gitBranch?: string }[]` | optional | Custom domains | -| **environmentVariables** | `{ key: string; value: string; target: Enum<'production' \| 'preview' \| 'development'>[]; isSecret: boolean; … }[]` | optional | Environment variables | -| **edgeFunctions** | `{ name: string; path: string; regions?: string[]; memoryLimit: integer; … }[]` | optional | Edge functions | -| **rootDirectory** | `string` | optional | Root directory (for monorepos) | - - ---- - -## VercelProvider - -Vercel provider type - -### Allowed Values - -* `vercel` - - ---- - -## VercelTeam - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **teamId** | `string` | optional | Team ID or slug | -| **teamName** | `string` | optional | Team name | - - ---- - diff --git a/content/docs/references/integration/index.mdx b/content/docs/references/integration/index.mdx index 2ac0bbf02b..f4cbea5ec3 100644 --- a/content/docs/references/integration/index.mdx +++ b/content/docs/references/integration/index.mdx @@ -7,10 +7,4 @@ This section contains all protocol schemas for the integration layer of ObjectSt - - - - - - diff --git a/content/docs/references/integration/message-queue.mdx b/content/docs/references/integration/message-queue.mdx deleted file mode 100644 index 8f8aae5f92..0000000000 --- a/content/docs/references/integration/message-queue.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Message Queue -description: Message Queue protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -## TypeScript Usage - -```typescript -import { ConsumerConfig, MessageQueueProvider } from '@objectstack/spec/integration'; -import type { ConsumerConfig, MessageQueueProvider } from '@objectstack/spec/integration'; - -// Validate data -const result = ConsumerConfig.parse(data); -``` - ---- - -## ConsumerConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable consumer | -| **consumerGroup** | `string` | optional | Consumer group ID | -| **concurrency** | `number` | ✅ | Number of concurrent consumers | -| **prefetchCount** | `number` | ✅ | Prefetch count | -| **ackMode** | `Enum<'auto' \| 'manual' \| 'client'>` | ✅ | Message acknowledgment mode | -| **autoCommit** | `boolean` | ✅ | Auto-commit offsets | -| **autoCommitIntervalMs** | `number` | ✅ | Auto-commit interval in ms | -| **sessionTimeoutMs** | `number` | ✅ | Session timeout in ms | -| **rebalanceTimeoutMs** | `number` | optional | Rebalance timeout in ms | - - ---- - -## MessageQueueProvider - -Message queue provider type - -### Allowed Values - -* `rabbitmq` -* `kafka` -* `redis_pubsub` -* `redis_streams` -* `aws_sqs` -* `aws_sns` -* `google_pubsub` -* `azure_service_bus` -* `azure_event_hubs` -* `nats` -* `pulsar` -* `activemq` -* `custom` - - ---- - diff --git a/content/docs/references/integration/meta.json b/content/docs/references/integration/meta.json index ab617bca90..b125c0f212 100644 --- a/content/docs/references/integration/meta.json +++ b/content/docs/references/integration/meta.json @@ -7,17 +7,6 @@ "mapping", "---Transport & Storage---", "http", - "message-queue", - "object-storage", - "offline", - "---Tenancy---", - "tenant", - "---More---", - "connector-database", - "connector-file-storage", - "connector-github", - "connector-message-queue", - "connector-saas", - "connector-vercel" + "offline" ] } \ No newline at end of file diff --git a/content/docs/references/integration/object-storage.mdx b/content/docs/references/integration/object-storage.mdx deleted file mode 100644 index 8c5c882c5e..0000000000 --- a/content/docs/references/integration/object-storage.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Object Storage -description: Object Storage protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -## TypeScript Usage - -```typescript -import { MultipartUploadConfig } from '@objectstack/spec/integration'; -import type { MultipartUploadConfig } from '@objectstack/spec/integration'; - -// Validate data -const result = MultipartUploadConfig.parse(data); -``` - ---- - -## MultipartUploadConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable multipart uploads | -| **partSize** | `number` | ✅ | Part size in bytes (min 5MB) | -| **maxConcurrentParts** | `number` | ✅ | Maximum concurrent part uploads | -| **threshold** | `number` | ✅ | File size threshold for multipart upload in bytes | - - ---- - diff --git a/content/docs/references/integration/tenant.mdx b/content/docs/references/integration/tenant.mdx deleted file mode 100644 index e0624b519d..0000000000 --- a/content/docs/references/integration/tenant.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: Tenant -description: Tenant protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} - -## TypeScript Usage - -```typescript -import { DatabaseProvider } from '@objectstack/spec/integration'; -import type { DatabaseProvider } from '@objectstack/spec/integration'; - -// Validate data -const result = DatabaseProvider.parse(data); -``` - ---- - -## DatabaseProvider - -Database provider type - -### Allowed Values - -* `postgresql` -* `mysql` -* `mariadb` -* `mssql` -* `oracle` -* `mongodb` -* `redis` -* `cassandra` -* `snowflake` -* `bigquery` -* `redshift` -* `custom` - - ---- - diff --git a/content/docs/releases/v17.mdx b/content/docs/releases/v17.mdx index 2eb5ff6827..6b4a59e7a2 100644 --- a/content/docs/releases/v17.mdx +++ b/content/docs/releases/v17.mdx @@ -1024,6 +1024,7 @@ import or the authored key. | `ChartInteraction.zoom` / `.clickAction` | never implemented (#3752) | | The `workflow` service slot — `CoreServiceName 'workflow'`, `IWorkflowService`, `WorkflowProtocol`, the `Get/WorkflowState/Config/Transition` schema cluster, discovery `routes.workflow` / `services.workflow` / `features.workflow`, the `RestApiRouteCategory 'workflow'` member and the stray `graphql` provider entry | declared end to end and implemented nowhere: nothing ever registered or resolved the slot (ADR-0115 Evidence 5), no method of `WorkflowProtocol` was ever implemented, no host ever mounted `/api/v1/workflow`. State machines are `state_machine` validation rules; approvals are flow nodes (ADR-0019); record-triggered automation is hooks + `record_change` flows (#4451) | | `datasource.readReplicas` | replica connections nothing ever opened — no driver reads the key and no query path splits reads from writes, so every statement went to the primary. #4410 had just taught the schema to validate each entry against the declared driver's contract, which made a dead slot look rigorously alive (#4468) | +| The per-provider connector "template" cluster (`@objectstack/spec/integration` — `DatabaseConnectorSchema`, `FileStorageConnectorSchema`, `GitHubConnectorSchema`, `MessageQueueConnectorSchema`, `SaasConnectorSchema`, `VercelConnectorSchema`, their ~100 sub-schema/type/example exports, and the six generated reference pages) | the losing side of a decided architecture fight, left standing: ADR-0023 rejected hand-modelling each external system's shape inside the spec, and the live ADR-0097 protocol gets provider shapes from the provider itself (connector-openapi / connector-mcp materialize at boot). Zero consumers — `engine.registerConnector()` validates against `ConnectorSchema` from `connector.zod.ts` alone, and nothing referenced the six files, not even their own module's live half. `DatabaseConnectorSchema` also declared read-replica routing a *second* time (`readReplicaConfig`, see the row above), down to a `weight` field for a load balancer that does not exist (#4480) | | The `kernel` metadata-loader envelope family — `MetadataFormat`, `MetadataStats`, `MetadataLoadOptions`, `MetadataSaveOptions`, `MetadataExportOptions`, `MetadataImportOptions`, `MetadataLoadResult`, `MetadataSaveResult`, `MetadataWatchEvent`, `MetadataCollectionInfo`, `MetadataLoaderContract` (`@objectstack/spec/kernel`) | eleven names that each existed **twice**, with a different shape, on `./kernel` and `./system` — so which type you got depended on your import path. Every consumer imported the `./system` copy; the `./kernel` copies had zero consumers. Import them from `@objectstack/spec/system` (#4411, ADR-0049). `MetadataManagerConfig` / `MetadataFallbackStrategy` are unaffected and still ship from both entries | The Console side follows: `@object-ui/types` drops its diff --git a/packages/spec/PROTOCOL_MAP.md b/packages/spec/PROTOCOL_MAP.md index 6ebe74467c..682dff19db 100644 --- a/packages/spec/PROTOCOL_MAP.md +++ b/packages/spec/PROTOCOL_MAP.md @@ -117,13 +117,7 @@ This document serves as the **Grand Map** of the ObjectStack specification. It l | File | Status | Description | | :--- | :--- | :--- | -| [`connector.zod.ts`](src/integration/connector.zod.ts) | ⭐ | **Connector Definition**. Metadata for external API integrations (OpenAPI wrapper). | -| [`connector/saas.zod.ts`](src/integration/connector/saas.zod.ts) | | **SaaS Connectors**. Specifics for SaaS APIs (Salesforce, Stripe). | -| [`connector/database.zod.ts`](src/integration/connector/database.zod.ts) | | **DB Connectors**. External database integration. | -| [`connector/file-storage.zod.ts`](src/integration/connector/file-storage.zod.ts) | | **Storage Connectors**. S3, Blob Storage integrations. | -| [`connector/message-queue.zod.ts`](src/integration/connector/message-queue.zod.ts) | | **MQ Connectors**. Kafka, RabbitMQ integrations. | -| [`connector/github.zod.ts`](src/integration/connector/github.zod.ts) | | **GitHub Connector**. Logic for Git integration. | -| [`connector/vercel.zod.ts`](src/integration/connector/vercel.zod.ts) | | **Vercel Connector**. Deployment integration. | +| [`connector.zod.ts`](src/integration/connector.zod.ts) | ⭐ | **Connector Protocol** (ADR-0097). One schema; provider shapes come from the provider itself (connector-openapi / connector-mcp), not from per-provider spec files — the six `connector/*.zod.ts` "templates" were removed in #4480 (zero consumers; ADR-0023 rejected that design). | --- diff --git a/packages/spec/api-surface.json b/packages/spec/api-surface.json index 48b3c2768e..4e78b190ca 100644 --- a/packages/spec/api-surface.json +++ b/packages/spec/api-surface.json @@ -3912,15 +3912,7 @@ "WriteObservabilityOptions (interface)" ], "./integration": [ - "AckMode (type)", - "AckModeSchema (const)", - "ApiVersionConfig (type)", - "ApiVersionConfigSchema (const)", - "BuildConfig (type)", - "BuildConfigSchema (const)", "CONNECTOR_UPSTREAM_UNAVAILABLE (const)", - "CdcConfig (type)", - "CdcConfigSchema (const)", "CircuitBreakerConfig (type)", "CircuitBreakerConfigSchema (const)", "ConflictResolution (type)", @@ -3955,80 +3947,18 @@ "ConnectorType (type)", "ConnectorTypeSchema (const)", "ConnectorUpstreamUnavailableError (class)", - "ConsumerConfig (type)", - "ConsumerConfigSchema (const)", "DataSyncConfig (type)", "DataSyncConfigSchema (const)", - "DatabaseConnector (type)", - "DatabaseConnectorSchema (const)", - "DatabasePoolConfig (type)", - "DatabasePoolConfigSchema (const)", - "DatabaseProvider (type)", - "DatabaseProviderSchema (const)", - "DatabaseTable (type)", - "DatabaseTableSchema (const)", "DeclarativeConnectorEntry (type)", "DeclarativeConnectorEntrySchema (const)", - "DeliveryGuarantee (type)", - "DeliveryGuaranteeSchema (const)", - "DeploymentConfig (type)", - "DeploymentConfigSchema (const)", - "DlqConfig (type)", - "DlqConfigSchema (const)", - "DomainConfig (type)", - "DomainConfigSchema (const)", - "EdgeFunctionConfig (type)", - "EdgeFunctionConfigSchema (const)", - "EnvironmentVariables (type)", - "EnvironmentVariablesSchema (const)", "ErrorMappingConfig (type)", "ErrorMappingConfigSchema (const)", "ErrorMappingRule (type)", "ErrorMappingRuleSchema (const)", "FieldMapping (type)", "FieldMappingSchema (const)", - "FileAccessPattern (type)", - "FileAccessPatternSchema (const)", - "FileFilterConfig (type)", - "FileFilterConfigSchema (const)", - "FileMetadataConfig (type)", - "FileMetadataConfigSchema (const)", - "FileStorageConnector (type)", - "FileStorageConnectorSchema (const)", - "FileStorageProvider (type)", - "FileStorageProviderSchema (const)", - "FileVersioningConfig (type)", - "FileVersioningConfigSchema (const)", - "GitHubActionsWorkflow (type)", - "GitHubActionsWorkflowSchema (const)", - "GitHubCommitConfig (type)", - "GitHubCommitConfigSchema (const)", - "GitHubConnector (type)", - "GitHubConnectorSchema (const)", - "GitHubIssueTracking (type)", - "GitHubIssueTrackingSchema (const)", - "GitHubProvider (type)", - "GitHubProviderSchema (const)", - "GitHubPullRequestConfig (type)", - "GitHubPullRequestConfigSchema (const)", - "GitHubReleaseConfig (type)", - "GitHubReleaseConfigSchema (const)", - "GitHubRepository (type)", - "GitHubRepositorySchema (const)", - "GitRepositoryConfig (type)", - "GitRepositoryConfigSchema (const)", "HealthCheckConfig (type)", "HealthCheckConfigSchema (const)", - "MessageFormat (type)", - "MessageFormatSchema (const)", - "MessageQueueConnector (type)", - "MessageQueueConnectorSchema (const)", - "MessageQueueProvider (type)", - "MessageQueueProviderSchema (const)", - "MultipartUploadConfig (type)", - "MultipartUploadConfigSchema (const)", - "ProducerConfig (type)", - "ProducerConfigSchema (const)", "RateLimitConfig (type)", "RateLimitConfigSchema (const)", "RateLimitStrategy (type)", @@ -4036,57 +3966,16 @@ "ResolvedConnectorAuth (type)", "RetryConfig (type)", "RetryConfigSchema (const)", - "SaaSConnector (type)", - "SaasConnector (type)", - "SaasConnectorSchema (const)", - "SaasObjectType (type)", - "SaasObjectTypeSchema (const)", - "SaasProvider (type)", - "SaasProviderSchema (const)", - "SslConfig (type)", - "SslConfigSchema (const)", - "StorageBucket (type)", - "StorageBucketSchema (const)", "SyncStrategy (type)", "SyncStrategySchema (const)", - "TopicQueue (type)", - "TopicQueueSchema (const)", - "VercelConnector (type)", - "VercelConnectorSchema (const)", - "VercelFramework (type)", - "VercelFrameworkSchema (const)", - "VercelMonitoring (type)", - "VercelMonitoringSchema (const)", - "VercelProject (type)", - "VercelProjectSchema (const)", - "VercelProvider (type)", - "VercelProviderSchema (const)", - "VercelTeam (type)", - "VercelTeamSchema (const)", "WebhookConfig (type)", "WebhookConfigSchema (const)", "WebhookEvent (type)", "WebhookEventSchema (const)", "WebhookSignatureAlgorithm (type)", "WebhookSignatureAlgorithmSchema (const)", - "azureBlobConnectorExample (const)", "defineConnector (function)", - "githubEnterpriseConnectorExample (const)", - "githubPublicConnectorExample (const)", - "googleDriveConnectorExample (const)", - "hubspotConnectorExample (const)", - "isConnectorUpstreamUnavailable (function)", - "kafkaConnectorExample (const)", - "mongoConnectorExample (const)", - "postgresConnectorExample (const)", - "pubsubConnectorExample (const)", - "rabbitmqConnectorExample (const)", - "s3ConnectorExample (const)", - "salesforceConnectorExample (const)", - "snowflakeConnectorExample (const)", - "sqsConnectorExample (const)", - "vercelNextJsConnectorExample (const)", - "vercelStaticSiteConnectorExample (const)" + "isConnectorUpstreamUnavailable (function)" ], "./security": [ "AccessMatrix (type)", diff --git a/packages/spec/authorable-surface.json b/packages/spec/authorable-surface.json index 5fc17011c1..c8f296470a 100644 --- a/packages/spec/authorable-surface.json +++ b/packages/spec/authorable-surface.json @@ -4132,23 +4132,6 @@ "identity/VerificationToken:expires", "identity/VerificationToken:identifier", "identity/VerificationToken:token", - "integration/ApiVersionConfig:deprecationDate", - "integration/ApiVersionConfig:isDefault", - "integration/ApiVersionConfig:sunsetDate", - "integration/ApiVersionConfig:version", - "integration/BuildConfig:buildCommand", - "integration/BuildConfig:devCommand", - "integration/BuildConfig:env", - "integration/BuildConfig:installCommand", - "integration/BuildConfig:nodeVersion", - "integration/BuildConfig:outputDirectory", - "integration/CdcConfig:batchSize", - "integration/CdcConfig:enabled", - "integration/CdcConfig:method", - "integration/CdcConfig:pollIntervalMs", - "integration/CdcConfig:publicationName", - "integration/CdcConfig:slotName", - "integration/CdcConfig:startPosition", "integration/CircuitBreakerConfig:enabled", "integration/CircuitBreakerConfig:failureThreshold", "integration/CircuitBreakerConfig:fallbackStrategy", @@ -4200,15 +4183,6 @@ "integration/ConnectorTrigger:key", "integration/ConnectorTrigger:label", "integration/ConnectorTrigger:type", - "integration/ConsumerConfig:ackMode", - "integration/ConsumerConfig:autoCommit", - "integration/ConsumerConfig:autoCommitIntervalMs", - "integration/ConsumerConfig:concurrency", - "integration/ConsumerConfig:consumerGroup", - "integration/ConsumerConfig:enabled", - "integration/ConsumerConfig:prefetchCount", - "integration/ConsumerConfig:rebalanceTimeoutMs", - "integration/ConsumerConfig:sessionTimeoutMs", "integration/DataSyncConfig:batchSize", "integration/DataSyncConfig:conflictResolution", "integration/DataSyncConfig:deleteMode", @@ -4218,52 +4192,6 @@ "integration/DataSyncConfig:schedule", "integration/DataSyncConfig:strategy", "integration/DataSyncConfig:timestampField", - "integration/DatabaseConnector:actions", - "integration/DatabaseConnector:auth", - "integration/DatabaseConnector:authentication", - "integration/DatabaseConnector:cdcConfig", - "integration/DatabaseConnector:connectionConfig", - "integration/DatabaseConnector:connectionTimeoutMs", - "integration/DatabaseConnector:description", - "integration/DatabaseConnector:enableQueryLogging", - "integration/DatabaseConnector:enabled", - "integration/DatabaseConnector:errorMapping", - "integration/DatabaseConnector:fieldMappings", - "integration/DatabaseConnector:health", - "integration/DatabaseConnector:icon", - "integration/DatabaseConnector:label", - "integration/DatabaseConnector:metadata", - "integration/DatabaseConnector:name", - "integration/DatabaseConnector:poolConfig", - "integration/DatabaseConnector:provider", - "integration/DatabaseConnector:providerConfig", - "integration/DatabaseConnector:queryTimeoutMs", - "integration/DatabaseConnector:rateLimitConfig", - "integration/DatabaseConnector:readReplicaConfig", - "integration/DatabaseConnector:requestTimeoutMs", - "integration/DatabaseConnector:retryConfig", - "integration/DatabaseConnector:sslConfig", - "integration/DatabaseConnector:status", - "integration/DatabaseConnector:syncConfig", - "integration/DatabaseConnector:tables", - "integration/DatabaseConnector:triggers", - "integration/DatabaseConnector:type", - "integration/DatabaseConnector:webhooks", - "integration/DatabasePoolConfig:acquireTimeoutMs", - "integration/DatabasePoolConfig:connectionTimeoutMs", - "integration/DatabasePoolConfig:evictionRunIntervalMs", - "integration/DatabasePoolConfig:idleTimeoutMs", - "integration/DatabasePoolConfig:max", - "integration/DatabasePoolConfig:min", - "integration/DatabasePoolConfig:testOnBorrow", - "integration/DatabaseTable:enabled", - "integration/DatabaseTable:fieldMappings", - "integration/DatabaseTable:label", - "integration/DatabaseTable:name", - "integration/DatabaseTable:primaryKey", - "integration/DatabaseTable:schema", - "integration/DatabaseTable:tableName", - "integration/DatabaseTable:whereClause", "integration/DeclarativeConnectorEntry:actions", "integration/DeclarativeConnectorEntry:auth", "integration/DeclarativeConnectorEntry:authentication", @@ -4287,30 +4215,6 @@ "integration/DeclarativeConnectorEntry:triggers", "integration/DeclarativeConnectorEntry:type", "integration/DeclarativeConnectorEntry:webhooks", - "integration/DeploymentConfig:autoDeployment", - "integration/DeploymentConfig:deployHooks", - "integration/DeploymentConfig:enablePreview", - "integration/DeploymentConfig:previewComments", - "integration/DeploymentConfig:productionProtection", - "integration/DeploymentConfig:regions", - "integration/DlqConfig:enabled", - "integration/DlqConfig:maxRetries", - "integration/DlqConfig:queueName", - "integration/DlqConfig:retryDelayMs", - "integration/DomainConfig:customCertificate", - "integration/DomainConfig:domain", - "integration/DomainConfig:gitBranch", - "integration/DomainConfig:httpsRedirect", - "integration/EdgeFunctionConfig:memoryLimit", - "integration/EdgeFunctionConfig:name", - "integration/EdgeFunctionConfig:path", - "integration/EdgeFunctionConfig:regions", - "integration/EdgeFunctionConfig:timeout", - "integration/EnvironmentVariables:gitBranch", - "integration/EnvironmentVariables:isSecret", - "integration/EnvironmentVariables:key", - "integration/EnvironmentVariables:target", - "integration/EnvironmentVariables:value", "integration/ErrorMappingConfig:defaultCategory", "integration/ErrorMappingConfig:logUnmapped", "integration/ErrorMappingConfig:rules", @@ -4329,123 +4233,6 @@ "integration/FieldMapping:syncMode", "integration/FieldMapping:target", "integration/FieldMapping:transform", - "integration/FileFilterConfig:allowedExtensions", - "integration/FileFilterConfig:blockedExtensions", - "integration/FileFilterConfig:excludePatterns", - "integration/FileFilterConfig:includePatterns", - "integration/FileFilterConfig:maxFileSize", - "integration/FileFilterConfig:minFileSize", - "integration/FileMetadataConfig:customMetadata", - "integration/FileMetadataConfig:extractMetadata", - "integration/FileMetadataConfig:metadataFields", - "integration/FileStorageConnector:actions", - "integration/FileStorageConnector:auth", - "integration/FileStorageConnector:authentication", - "integration/FileStorageConnector:buckets", - "integration/FileStorageConnector:bufferSize", - "integration/FileStorageConnector:connectionTimeoutMs", - "integration/FileStorageConnector:contentProcessing", - "integration/FileStorageConnector:description", - "integration/FileStorageConnector:enabled", - "integration/FileStorageConnector:encryption", - "integration/FileStorageConnector:errorMapping", - "integration/FileStorageConnector:fieldMappings", - "integration/FileStorageConnector:health", - "integration/FileStorageConnector:icon", - "integration/FileStorageConnector:label", - "integration/FileStorageConnector:lifecyclePolicy", - "integration/FileStorageConnector:metadata", - "integration/FileStorageConnector:metadataConfig", - "integration/FileStorageConnector:multipartConfig", - "integration/FileStorageConnector:name", - "integration/FileStorageConnector:provider", - "integration/FileStorageConnector:providerConfig", - "integration/FileStorageConnector:rateLimitConfig", - "integration/FileStorageConnector:requestTimeoutMs", - "integration/FileStorageConnector:retryConfig", - "integration/FileStorageConnector:status", - "integration/FileStorageConnector:storageConfig", - "integration/FileStorageConnector:syncConfig", - "integration/FileStorageConnector:transferAcceleration", - "integration/FileStorageConnector:triggers", - "integration/FileStorageConnector:type", - "integration/FileStorageConnector:versioningConfig", - "integration/FileStorageConnector:webhooks", - "integration/FileVersioningConfig:enabled", - "integration/FileVersioningConfig:maxVersions", - "integration/FileVersioningConfig:retentionDays", - "integration/GitHubActionsWorkflow:enabled", - "integration/GitHubActionsWorkflow:env", - "integration/GitHubActionsWorkflow:name", - "integration/GitHubActionsWorkflow:path", - "integration/GitHubActionsWorkflow:secrets", - "integration/GitHubActionsWorkflow:triggers", - "integration/GitHubCommitConfig:authorEmail", - "integration/GitHubCommitConfig:authorName", - "integration/GitHubCommitConfig:messageTemplate", - "integration/GitHubCommitConfig:signCommits", - "integration/GitHubCommitConfig:useConventionalCommits", - "integration/GitHubConnector:actions", - "integration/GitHubConnector:auth", - "integration/GitHubConnector:authentication", - "integration/GitHubConnector:baseUrl", - "integration/GitHubConnector:commitConfig", - "integration/GitHubConnector:connectionTimeoutMs", - "integration/GitHubConnector:description", - "integration/GitHubConnector:enableWebhooks", - "integration/GitHubConnector:enabled", - "integration/GitHubConnector:errorMapping", - "integration/GitHubConnector:fieldMappings", - "integration/GitHubConnector:health", - "integration/GitHubConnector:icon", - "integration/GitHubConnector:issueTracking", - "integration/GitHubConnector:label", - "integration/GitHubConnector:metadata", - "integration/GitHubConnector:name", - "integration/GitHubConnector:provider", - "integration/GitHubConnector:providerConfig", - "integration/GitHubConnector:pullRequestConfig", - "integration/GitHubConnector:rateLimitConfig", - "integration/GitHubConnector:releaseConfig", - "integration/GitHubConnector:repositories", - "integration/GitHubConnector:requestTimeoutMs", - "integration/GitHubConnector:retryConfig", - "integration/GitHubConnector:status", - "integration/GitHubConnector:syncConfig", - "integration/GitHubConnector:triggers", - "integration/GitHubConnector:type", - "integration/GitHubConnector:webhookEvents", - "integration/GitHubConnector:webhooks", - "integration/GitHubConnector:workflows", - "integration/GitHubIssueTracking:autoAssign", - "integration/GitHubIssueTracking:autoCloseStale", - "integration/GitHubIssueTracking:defaultLabels", - "integration/GitHubIssueTracking:enabled", - "integration/GitHubIssueTracking:templatePaths", - "integration/GitHubPullRequestConfig:bodyTemplate", - "integration/GitHubPullRequestConfig:defaultAssignees", - "integration/GitHubPullRequestConfig:defaultLabels", - "integration/GitHubPullRequestConfig:defaultReviewers", - "integration/GitHubPullRequestConfig:deleteHeadBranch", - "integration/GitHubPullRequestConfig:draftByDefault", - "integration/GitHubPullRequestConfig:titleTemplate", - "integration/GitHubReleaseConfig:autoReleaseNotes", - "integration/GitHubReleaseConfig:draftByDefault", - "integration/GitHubReleaseConfig:preReleasePattern", - "integration/GitHubReleaseConfig:releaseNameTemplate", - "integration/GitHubReleaseConfig:semanticVersioning", - "integration/GitHubReleaseConfig:tagPattern", - "integration/GitHubRepository:autoMerge", - "integration/GitHubRepository:branchProtection", - "integration/GitHubRepository:defaultBranch", - "integration/GitHubRepository:name", - "integration/GitHubRepository:owner", - "integration/GitHubRepository:topics", - "integration/GitRepositoryConfig:autoDeployPreview", - "integration/GitRepositoryConfig:autoDeployProduction", - "integration/GitRepositoryConfig:productionBranch", - "integration/GitRepositoryConfig:repo", - "integration/GitRepositoryConfig:type", "integration/HealthCheckConfig:enabled", "integration/HealthCheckConfig:endpoint", "integration/HealthCheckConfig:expectedStatus", @@ -4454,51 +4241,6 @@ "integration/HealthCheckConfig:method", "integration/HealthCheckConfig:timeoutMs", "integration/HealthCheckConfig:unhealthyThreshold", - "integration/MessageQueueConnector:actions", - "integration/MessageQueueConnector:auth", - "integration/MessageQueueConnector:authentication", - "integration/MessageQueueConnector:brokerConfig", - "integration/MessageQueueConnector:connectionTimeoutMs", - "integration/MessageQueueConnector:deliveryGuarantee", - "integration/MessageQueueConnector:description", - "integration/MessageQueueConnector:enableMetrics", - "integration/MessageQueueConnector:enableTracing", - "integration/MessageQueueConnector:enabled", - "integration/MessageQueueConnector:errorMapping", - "integration/MessageQueueConnector:fieldMappings", - "integration/MessageQueueConnector:health", - "integration/MessageQueueConnector:icon", - "integration/MessageQueueConnector:label", - "integration/MessageQueueConnector:metadata", - "integration/MessageQueueConnector:name", - "integration/MessageQueueConnector:preserveOrder", - "integration/MessageQueueConnector:provider", - "integration/MessageQueueConnector:providerConfig", - "integration/MessageQueueConnector:rateLimitConfig", - "integration/MessageQueueConnector:requestTimeoutMs", - "integration/MessageQueueConnector:retryConfig", - "integration/MessageQueueConnector:saslConfig", - "integration/MessageQueueConnector:schemaRegistry", - "integration/MessageQueueConnector:sslConfig", - "integration/MessageQueueConnector:status", - "integration/MessageQueueConnector:syncConfig", - "integration/MessageQueueConnector:topics", - "integration/MessageQueueConnector:triggers", - "integration/MessageQueueConnector:type", - "integration/MessageQueueConnector:webhooks", - "integration/MultipartUploadConfig:enabled", - "integration/MultipartUploadConfig:maxConcurrentParts", - "integration/MultipartUploadConfig:partSize", - "integration/MultipartUploadConfig:threshold", - "integration/ProducerConfig:acks", - "integration/ProducerConfig:batchSize", - "integration/ProducerConfig:compressionType", - "integration/ProducerConfig:enabled", - "integration/ProducerConfig:idempotence", - "integration/ProducerConfig:lingerMs", - "integration/ProducerConfig:maxInFlightRequests", - "integration/ProducerConfig:transactionTimeoutMs", - "integration/ProducerConfig:transactional", "integration/RateLimitConfig:burstCapacity", "integration/RateLimitConfig:maxRequests", "integration/RateLimitConfig:rateLimitHeaders", @@ -4513,113 +4255,6 @@ "integration/RetryConfig:retryOnNetworkError", "integration/RetryConfig:retryableStatusCodes", "integration/RetryConfig:strategy", - "integration/SaasConnector:actions", - "integration/SaasConnector:apiVersion", - "integration/SaasConnector:auth", - "integration/SaasConnector:authentication", - "integration/SaasConnector:baseUrl", - "integration/SaasConnector:connectionTimeoutMs", - "integration/SaasConnector:customHeaders", - "integration/SaasConnector:description", - "integration/SaasConnector:enabled", - "integration/SaasConnector:errorMapping", - "integration/SaasConnector:fieldMappings", - "integration/SaasConnector:health", - "integration/SaasConnector:icon", - "integration/SaasConnector:label", - "integration/SaasConnector:metadata", - "integration/SaasConnector:name", - "integration/SaasConnector:oauthSettings", - "integration/SaasConnector:objectTypes", - "integration/SaasConnector:paginationConfig", - "integration/SaasConnector:provider", - "integration/SaasConnector:providerConfig", - "integration/SaasConnector:rateLimitConfig", - "integration/SaasConnector:requestTimeoutMs", - "integration/SaasConnector:retryConfig", - "integration/SaasConnector:sandboxConfig", - "integration/SaasConnector:status", - "integration/SaasConnector:syncConfig", - "integration/SaasConnector:triggers", - "integration/SaasConnector:type", - "integration/SaasConnector:webhooks", - "integration/SaasObjectType:apiName", - "integration/SaasObjectType:enabled", - "integration/SaasObjectType:fieldMappings", - "integration/SaasObjectType:label", - "integration/SaasObjectType:name", - "integration/SaasObjectType:supportsCreate", - "integration/SaasObjectType:supportsDelete", - "integration/SaasObjectType:supportsUpdate", - "integration/SslConfig:ca", - "integration/SslConfig:cert", - "integration/SslConfig:enabled", - "integration/SslConfig:key", - "integration/SslConfig:rejectUnauthorized", - "integration/StorageBucket:accessPattern", - "integration/StorageBucket:bucketName", - "integration/StorageBucket:enabled", - "integration/StorageBucket:fileFilters", - "integration/StorageBucket:label", - "integration/StorageBucket:name", - "integration/StorageBucket:prefix", - "integration/StorageBucket:region", - "integration/TopicQueue:consumerConfig", - "integration/TopicQueue:dlqConfig", - "integration/TopicQueue:enabled", - "integration/TopicQueue:label", - "integration/TopicQueue:messageFilter", - "integration/TopicQueue:messageFormat", - "integration/TopicQueue:mode", - "integration/TopicQueue:name", - "integration/TopicQueue:partitions", - "integration/TopicQueue:producerConfig", - "integration/TopicQueue:replicationFactor", - "integration/TopicQueue:routingKey", - "integration/TopicQueue:topicName", - "integration/VercelConnector:actions", - "integration/VercelConnector:auth", - "integration/VercelConnector:authentication", - "integration/VercelConnector:baseUrl", - "integration/VercelConnector:connectionTimeoutMs", - "integration/VercelConnector:description", - "integration/VercelConnector:enableWebhooks", - "integration/VercelConnector:enabled", - "integration/VercelConnector:errorMapping", - "integration/VercelConnector:fieldMappings", - "integration/VercelConnector:health", - "integration/VercelConnector:icon", - "integration/VercelConnector:label", - "integration/VercelConnector:metadata", - "integration/VercelConnector:monitoring", - "integration/VercelConnector:name", - "integration/VercelConnector:projects", - "integration/VercelConnector:provider", - "integration/VercelConnector:providerConfig", - "integration/VercelConnector:rateLimitConfig", - "integration/VercelConnector:requestTimeoutMs", - "integration/VercelConnector:retryConfig", - "integration/VercelConnector:status", - "integration/VercelConnector:syncConfig", - "integration/VercelConnector:team", - "integration/VercelConnector:triggers", - "integration/VercelConnector:type", - "integration/VercelConnector:webhookEvents", - "integration/VercelConnector:webhooks", - "integration/VercelMonitoring:enableSpeedInsights", - "integration/VercelMonitoring:enableWebAnalytics", - "integration/VercelMonitoring:logDrains", - "integration/VercelProject:buildConfig", - "integration/VercelProject:deploymentConfig", - "integration/VercelProject:domains", - "integration/VercelProject:edgeFunctions", - "integration/VercelProject:environmentVariables", - "integration/VercelProject:framework", - "integration/VercelProject:gitRepository", - "integration/VercelProject:name", - "integration/VercelProject:rootDirectory", - "integration/VercelTeam:teamId", - "integration/VercelTeam:teamName", "integration/WebhookConfig:description", "integration/WebhookConfig:events", "integration/WebhookConfig:headers", diff --git a/packages/spec/json-schema.manifest.json b/packages/spec/json-schema.manifest.json index ecdd492db9..5f3fbb5f60 100644 --- a/packages/spec/json-schema.manifest.json +++ b/packages/spec/json-schema.manifest.json @@ -880,10 +880,6 @@ "identity/Session", "identity/User", "identity/VerificationToken", - "integration/AckMode", - "integration/ApiVersionConfig", - "integration/BuildConfig", - "integration/CdcConfig", "integration/CircuitBreakerConfig", "integration/ConflictResolution", "integration/Connector", @@ -899,59 +895,16 @@ "integration/ConnectorStatus", "integration/ConnectorTrigger", "integration/ConnectorType", - "integration/ConsumerConfig", "integration/DataSyncConfig", - "integration/DatabaseConnector", - "integration/DatabasePoolConfig", - "integration/DatabaseProvider", - "integration/DatabaseTable", "integration/DeclarativeConnectorEntry", - "integration/DeliveryGuarantee", - "integration/DeploymentConfig", - "integration/DlqConfig", - "integration/DomainConfig", - "integration/EdgeFunctionConfig", - "integration/EnvironmentVariables", "integration/ErrorMappingConfig", "integration/ErrorMappingRule", "integration/FieldMapping", - "integration/FileAccessPattern", - "integration/FileFilterConfig", - "integration/FileMetadataConfig", - "integration/FileStorageConnector", - "integration/FileStorageProvider", - "integration/FileVersioningConfig", - "integration/GitHubActionsWorkflow", - "integration/GitHubCommitConfig", - "integration/GitHubConnector", - "integration/GitHubIssueTracking", - "integration/GitHubProvider", - "integration/GitHubPullRequestConfig", - "integration/GitHubReleaseConfig", - "integration/GitHubRepository", - "integration/GitRepositoryConfig", "integration/HealthCheckConfig", - "integration/MessageFormat", - "integration/MessageQueueConnector", - "integration/MessageQueueProvider", - "integration/MultipartUploadConfig", - "integration/ProducerConfig", "integration/RateLimitConfig", "integration/RateLimitStrategy", "integration/RetryConfig", - "integration/SaasConnector", - "integration/SaasObjectType", - "integration/SaasProvider", - "integration/SslConfig", - "integration/StorageBucket", "integration/SyncStrategy", - "integration/TopicQueue", - "integration/VercelConnector", - "integration/VercelFramework", - "integration/VercelMonitoring", - "integration/VercelProject", - "integration/VercelProvider", - "integration/VercelTeam", "integration/WebhookConfig", "integration/WebhookEvent", "integration/WebhookSignatureAlgorithm", diff --git a/packages/spec/src/integration/connector/database.test.ts b/packages/spec/src/integration/connector/database.test.ts deleted file mode 100644 index c97d106252..0000000000 --- a/packages/spec/src/integration/connector/database.test.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - DatabaseProviderSchema, - DatabasePoolConfigSchema, - SslConfigSchema, - CdcConfigSchema, - DatabaseTableSchema, - DatabaseConnectorSchema, -} from './database.zod'; - -const baseAuth = { type: 'none' as const }; - -const minimalTable = { - name: 'customer', - label: 'Customer', - tableName: 'customers', - primaryKey: 'id', -}; - -const minimalConnector = { - name: 'pg_main', - label: 'PostgreSQL Main', - type: 'database' as const, - provider: 'postgresql' as const, - authentication: baseAuth, - connectionConfig: { - host: 'localhost', - port: 5432, - database: 'mydb', - username: 'user', - password: 'pass', - }, - tables: [minimalTable], -}; - -describe('DatabaseProviderSchema', () => { - it('should accept all valid providers', () => { - const providers = ['postgresql', 'mysql', 'mariadb', 'mssql', 'oracle', 'mongodb', 'redis', 'cassandra', 'snowflake', 'bigquery', 'redshift', 'custom']; - for (const p of providers) { - expect(DatabaseProviderSchema.parse(p)).toBe(p); - } - }); - - it('should reject invalid provider', () => { - expect(() => DatabaseProviderSchema.parse('sqlite')).toThrow(); - }); -}); - -describe('DatabasePoolConfigSchema', () => { - it('should apply defaults', () => { - const result = DatabasePoolConfigSchema.parse({}); - expect(result.min).toBe(2); - expect(result.max).toBe(10); - expect(result.idleTimeoutMs).toBe(30000); - expect(result.testOnBorrow).toBe(true); - }); - - it('should accept custom values', () => { - const result = DatabasePoolConfigSchema.parse({ min: 0, max: 50, idleTimeoutMs: 5000 }); - expect(result.min).toBe(0); - expect(result.max).toBe(50); - }); - - it('should reject max below 1', () => { - expect(() => DatabasePoolConfigSchema.parse({ max: 0 })).toThrow(); - }); - - it('should reject idleTimeoutMs below 1000', () => { - expect(() => DatabasePoolConfigSchema.parse({ idleTimeoutMs: 500 })).toThrow(); - }); -}); - -describe('SslConfigSchema', () => { - it('should apply defaults', () => { - const result = SslConfigSchema.parse({}); - expect(result.enabled).toBe(false); - expect(result.rejectUnauthorized).toBe(true); - }); - - it('should accept full config', () => { - const result = SslConfigSchema.parse({ - enabled: true, - rejectUnauthorized: false, - ca: 'ca-cert', - cert: 'client-cert', - key: 'client-key', - }); - expect(result.enabled).toBe(true); - expect(result.ca).toBe('ca-cert'); - }); -}); - -describe('CdcConfigSchema', () => { - it('should accept valid CDC config', () => { - const result = CdcConfigSchema.parse({ method: 'log_based' }); - expect(result.enabled).toBe(false); - expect(result.batchSize).toBe(1000); - expect(result.pollIntervalMs).toBe(1000); - }); - - it('should accept all CDC methods', () => { - for (const m of ['log_based', 'trigger_based', 'query_based', 'custom']) { - expect(() => CdcConfigSchema.parse({ method: m })).not.toThrow(); - } - }); - - it('should reject missing method', () => { - expect(() => CdcConfigSchema.parse({ enabled: true })).toThrow(); - }); - - it('should reject batchSize out of range', () => { - expect(() => CdcConfigSchema.parse({ method: 'log_based', batchSize: 0 })).toThrow(); - expect(() => CdcConfigSchema.parse({ method: 'log_based', batchSize: 10001 })).toThrow(); - }); - - it('should accept optional fields', () => { - const result = CdcConfigSchema.parse({ - method: 'log_based', - enabled: true, - slotName: 'slot1', - publicationName: 'pub1', - startPosition: '0/1234', - }); - expect(result.slotName).toBe('slot1'); - }); -}); - -describe('DatabaseTableSchema', () => { - it('should accept valid table', () => { - const result = DatabaseTableSchema.parse(minimalTable); - expect(result.enabled).toBe(true); - }); - - it('should accept table with all optional fields', () => { - const data = { - ...minimalTable, - schema: 'public', - enabled: false, - fieldMappings: [{ source: 'ext_id', target: 'id' }], - whereClause: 'status = \'active\'', - }; - expect(() => DatabaseTableSchema.parse(data)).not.toThrow(); - }); - - it('should reject non-snake_case name', () => { - expect(() => DatabaseTableSchema.parse({ ...minimalTable, name: 'Customer' })).toThrow(); - }); - - it('should reject missing required fields', () => { - expect(() => DatabaseTableSchema.parse({ name: 'tbl' })).toThrow(); - }); -}); - -describe('DatabaseConnectorSchema', () => { - it('should accept minimal valid connector', () => { - expect(() => DatabaseConnectorSchema.parse(minimalConnector)).not.toThrow(); - }); - - it('should apply defaults', () => { - const result = DatabaseConnectorSchema.parse(minimalConnector); - expect(result.queryTimeoutMs).toBe(30000); - expect(result.enableQueryLogging).toBe(false); - expect(result.enabled).toBe(true); - }); - - it('should accept full connector', () => { - const full = { - ...minimalConnector, - poolConfig: { min: 5, max: 25 }, - sslConfig: { enabled: true }, - cdcConfig: { method: 'log_based', enabled: true }, - readReplicaConfig: { - enabled: true, - hosts: [{ host: 'replica1', port: 5432, weight: 0.5 }], - }, - queryTimeoutMs: 60000, - enableQueryLogging: true, - }; - expect(() => DatabaseConnectorSchema.parse(full)).not.toThrow(); - }); - - it('should reject wrong type literal', () => { - expect(() => DatabaseConnectorSchema.parse({ ...minimalConnector, type: 'saas' })).toThrow(); - }); - - it('should reject invalid port', () => { - expect(() => DatabaseConnectorSchema.parse({ - ...minimalConnector, - connectionConfig: { ...minimalConnector.connectionConfig, port: 0 }, - })).toThrow(); - expect(() => DatabaseConnectorSchema.parse({ - ...minimalConnector, - connectionConfig: { ...minimalConnector.connectionConfig, port: 70000 }, - })).toThrow(); - }); - - it('should reject queryTimeoutMs out of range', () => { - expect(() => DatabaseConnectorSchema.parse({ ...minimalConnector, queryTimeoutMs: 500 })).toThrow(); - expect(() => DatabaseConnectorSchema.parse({ ...minimalConnector, queryTimeoutMs: 500000 })).toThrow(); - }); - - it('should reject missing tables', () => { - const { tables: _, ...noTables } = minimalConnector; - expect(() => DatabaseConnectorSchema.parse(noTables)).toThrow(); - }); - - it('should reject read replica with invalid weight', () => { - expect(() => DatabaseConnectorSchema.parse({ - ...minimalConnector, - readReplicaConfig: { - enabled: true, - hosts: [{ host: 'r1', port: 5432, weight: 2 }], - }, - })).toThrow(); - }); -}); diff --git a/packages/spec/src/integration/connector/database.zod.ts b/packages/spec/src/integration/connector/database.zod.ts deleted file mode 100644 index a038a7e21e..0000000000 --- a/packages/spec/src/integration/connector/database.zod.ts +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { z } from 'zod'; -import { - ConnectorSchema, - FieldMappingSchema, -} from '../connector.zod'; - -/** - * Database Connector Protocol Template - * - * Specialized connector for database systems (PostgreSQL, MySQL, SQL Server, etc.) - * Extends the base connector with database-specific features like schema discovery, - * CDC (Change Data Capture), and connection pooling. - */ - -/** - * Database Provider Types - */ -import { lazySchema } from '../../shared/lazy-schema'; -export const DatabaseProviderSchema = lazySchema(() => z.enum([ - 'postgresql', - 'mysql', - 'mariadb', - 'mssql', - 'oracle', - 'mongodb', - 'redis', - 'cassandra', - 'snowflake', - 'bigquery', - 'redshift', - 'custom', -]).describe('Database provider type')); - -export type DatabaseProvider = z.infer; - -/** - * Database Connection Pool Configuration - */ -export const DatabasePoolConfigSchema = lazySchema(() => z.object({ - min: z.number().min(0).default(2).describe('Minimum connections in pool'), - max: z.number().min(1).default(10).describe('Maximum connections in pool'), - idleTimeoutMs: z.number().min(1000).default(30000).describe('Idle connection timeout in ms'), - connectionTimeoutMs: z.number().min(1000).default(10000).describe('Connection establishment timeout in ms'), - acquireTimeoutMs: z.number().min(1000).default(30000).describe('Connection acquisition timeout in ms'), - evictionRunIntervalMs: z.number().min(1000).default(30000).describe('Connection eviction check interval in ms'), - testOnBorrow: z.boolean().default(true).describe('Test connection before use'), -})); - -export type DatabasePoolConfig = z.infer; - -/** - * SSL/TLS Configuration - */ -export const SslConfigSchema = lazySchema(() => z.object({ - enabled: z.boolean().default(false).describe('Enable SSL/TLS'), - rejectUnauthorized: z.boolean().default(true).describe('Reject unauthorized certificates'), - ca: z.string().optional().describe('Certificate Authority certificate'), - cert: z.string().optional().describe('Client certificate'), - key: z.string().optional().describe('Client private key'), -})); - -export type SslConfig = z.infer; - -/** - * Change Data Capture (CDC) Configuration - */ -export const CdcConfigSchema = lazySchema(() => z.object({ - enabled: z.boolean().default(false).describe('Enable CDC'), - - method: z.enum([ - 'log_based', // Transaction log parsing (e.g., PostgreSQL logical replication) - 'trigger_based', // Database triggers for change tracking - 'query_based', // Timestamp-based queries - 'custom', // Custom CDC implementation - ]).describe('CDC method'), - - slotName: z.string().optional().describe('Replication slot name (for log-based CDC)'), - - publicationName: z.string().optional().describe('Publication name (for PostgreSQL)'), - - startPosition: z.string().optional().describe('Starting position/LSN for CDC stream'), - - batchSize: z.number().min(1).max(10000).default(1000).describe('CDC batch size'), - - pollIntervalMs: z.number().min(100).default(1000).describe('CDC polling interval in ms'), -})); - -export type CdcConfig = z.infer; - -/** - * Database Table Configuration - */ -export const DatabaseTableSchema = lazySchema(() => z.object({ - name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Table name in ObjectStack (snake_case)'), - label: z.string().describe('Display label'), - schema: z.string().optional().describe('Database schema name'), - tableName: z.string().describe('Actual table name in database'), - primaryKey: z.string().describe('Primary key column'), - enabled: z.boolean().default(true).describe('Enable sync for this table'), - fieldMappings: z.array(FieldMappingSchema).optional().describe('Table-specific field mappings'), - whereClause: z.string().optional().describe('SQL WHERE clause for filtering'), -})); - -export type DatabaseTable = z.infer; - -/** - * Database Connector Configuration Schema - */ -export const DatabaseConnectorSchema = lazySchema(() => ConnectorSchema.extend({ - type: z.literal('database'), - - /** - * Database provider - */ - provider: DatabaseProviderSchema.describe('Database provider type'), - - /** - * Connection configuration - */ - connectionConfig: z.object({ - host: z.string().describe('Database host'), - port: z.number().min(1).max(65535).describe('Database port'), - database: z.string().describe('Database name'), - username: z.string().describe('Database username'), - password: z.string().describe('Database password (typically from ENV)'), - options: z.record(z.string(), z.unknown()).optional().describe('Driver-specific connection options'), - }).describe('Database connection configuration'), - - /** - * Connection pool configuration - */ - poolConfig: DatabasePoolConfigSchema.optional().describe('Connection pool configuration'), - - /** - * SSL/TLS configuration - */ - sslConfig: SslConfigSchema.optional().describe('SSL/TLS configuration'), - - /** - * Tables to sync - */ - tables: z.array(DatabaseTableSchema).describe('Tables to sync'), - - /** - * Change Data Capture configuration - */ - cdcConfig: CdcConfigSchema.optional().describe('CDC configuration'), - - /** - * Read replica configuration - */ - readReplicaConfig: z.object({ - enabled: z.boolean().default(false).describe('Use read replicas'), - hosts: z.array(z.object({ - host: z.string().describe('Replica host'), - port: z.number().min(1).max(65535).describe('Replica port'), - weight: z.number().min(0).max(1).default(1).describe('Load balancing weight'), - })).describe('Read replica hosts'), - }).optional().describe('Read replica configuration'), - - /** - * Query timeout - */ - queryTimeoutMs: z.number().min(1000).max(300000).optional().default(30000).describe('Query timeout in ms'), - - /** - * Enable query logging - */ - enableQueryLogging: z.boolean().optional().default(false).describe('Enable SQL query logging'), -})); - -export type DatabaseConnector = z.infer; - -// ============================================================================ -// Helper Functions & Examples -// ============================================================================ - -/** - * Example: PostgreSQL Connector Configuration - */ -export const postgresConnectorExample = { - name: 'postgres_production', - label: 'Production PostgreSQL', - type: 'database', - provider: 'postgresql', - authentication: { - type: 'basic', - username: '${DB_USERNAME}', - password: '${DB_PASSWORD}', - }, - connectionConfig: { - host: 'db.example.com', - port: 5432, - database: 'production', - username: '${DB_USERNAME}', - password: '${DB_PASSWORD}', - }, - poolConfig: { - min: 2, - max: 20, - idleTimeoutMs: 30000, - connectionTimeoutMs: 10000, - acquireTimeoutMs: 30000, - evictionRunIntervalMs: 30000, - testOnBorrow: true, - }, - sslConfig: { - enabled: true, - rejectUnauthorized: true, - }, - tables: [ - { - name: 'customer', - label: 'Customer', - schema: 'public', - tableName: 'customers', - primaryKey: 'id', - enabled: true, - }, - { - name: 'order', - label: 'Order', - schema: 'public', - tableName: 'orders', - primaryKey: 'id', - enabled: true, - whereClause: 'status != \'archived\'', - }, - ], - cdcConfig: { - enabled: true, - method: 'log_based', - slotName: 'objectstack_replication_slot', - publicationName: 'objectstack_publication', - batchSize: 1000, - pollIntervalMs: 1000, - }, - syncConfig: { - strategy: 'incremental', - direction: 'bidirectional', - realtimeSync: true, - conflictResolution: 'latest_wins', - batchSize: 1000, - deleteMode: 'soft_delete', - }, - status: 'active', - enabled: true, -}; - -/** - * Example: MongoDB Connector Configuration - */ -export const mongoConnectorExample = { - name: 'mongodb_analytics', - label: 'MongoDB Analytics', - type: 'database', - provider: 'mongodb', - authentication: { - type: 'basic', - username: '${MONGO_USERNAME}', - password: '${MONGO_PASSWORD}', - }, - connectionConfig: { - host: 'mongodb.example.com', - port: 27017, - database: 'analytics', - username: '${MONGO_USERNAME}', - password: '${MONGO_PASSWORD}', - options: { - authSource: 'admin', - replicaSet: 'rs0', - }, - }, - tables: [ - { - name: 'event', - label: 'Event', - tableName: 'events', - primaryKey: 'id', - enabled: true, - }, - ], - cdcConfig: { - enabled: true, - method: 'log_based', - batchSize: 1000, - pollIntervalMs: 500, - }, - syncConfig: { - strategy: 'incremental', - direction: 'import', - batchSize: 1000, - }, - status: 'active', - enabled: true, -}; - -/** - * Example: Snowflake Connector Configuration - */ -export const snowflakeConnectorExample = { - name: 'snowflake_warehouse', - label: 'Snowflake Data Warehouse', - type: 'database', - provider: 'snowflake', - authentication: { - type: 'basic', - username: '${SNOWFLAKE_USERNAME}', - password: '${SNOWFLAKE_PASSWORD}', - }, - connectionConfig: { - host: 'account.snowflakecomputing.com', - port: 443, - database: 'ANALYTICS_DB', - username: '${SNOWFLAKE_USERNAME}', - password: '${SNOWFLAKE_PASSWORD}', - options: { - warehouse: 'COMPUTE_WH', - schema: 'PUBLIC', - role: 'ANALYST', - }, - }, - tables: [ - { - name: 'sales_summary', - label: 'Sales Summary', - schema: 'PUBLIC', - tableName: 'SALES_SUMMARY', - primaryKey: 'ID', - enabled: true, - }, - ], - syncConfig: { - strategy: 'full', - direction: 'import', - schedule: '0 2 * * *', // Daily at 2 AM - batchSize: 5000, - }, - queryTimeoutMs: 60000, - status: 'active', - enabled: true, -}; diff --git a/packages/spec/src/integration/connector/file-storage.test.ts b/packages/spec/src/integration/connector/file-storage.test.ts deleted file mode 100644 index 6f99f3ec3d..0000000000 --- a/packages/spec/src/integration/connector/file-storage.test.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - FileStorageProviderSchema, - FileAccessPatternSchema, - FileMetadataConfigSchema, - MultipartUploadConfigSchema, - FileVersioningConfigSchema, - FileFilterConfigSchema, - StorageBucketSchema, - FileStorageConnectorSchema, -} from './file-storage.zod'; - -// Shared base connector fields for FileStorageConnector -const baseConnector = { - name: 's3_assets', - label: 'S3 Assets', - type: 'file_storage' as const, - authentication: { type: 'none' as const }, - provider: 's3' as const, - buckets: [ - { name: 'my_bucket', label: 'My Bucket', bucketName: 'actual-bucket' }, - ], -}; - -describe('FileStorageProviderSchema', () => { - it('should accept valid providers', () => { - for (const v of ['s3', 'azure_blob', 'gcs', 'dropbox', 'box', 'onedrive', 'google_drive', 'sharepoint', 'ftp', 'local', 'custom']) { - expect(FileStorageProviderSchema.parse(v)).toBe(v); - } - }); - - it('should reject invalid provider', () => { - expect(() => FileStorageProviderSchema.parse('invalid')).toThrow(); - }); -}); - -describe('FileAccessPatternSchema', () => { - it('should accept valid access patterns', () => { - for (const v of ['public_read', 'private', 'authenticated_read', 'bucket_owner_read', 'bucket_owner_full']) { - expect(FileAccessPatternSchema.parse(v)).toBe(v); - } - }); - - it('should reject invalid access pattern', () => { - expect(() => FileAccessPatternSchema.parse('unknown')).toThrow(); - }); -}); - -describe('FileMetadataConfigSchema', () => { - it('should accept valid config with defaults', () => { - const result = FileMetadataConfigSchema.parse({}); - expect(result.extractMetadata).toBe(true); - }); - - it('should accept full config', () => { - const data = { - extractMetadata: false, - metadataFields: ['content_type', 'file_size', 'etag'], - customMetadata: { env: 'prod' }, - }; - expect(() => FileMetadataConfigSchema.parse(data)).not.toThrow(); - }); - - it('should reject invalid metadataFields value', () => { - expect(() => FileMetadataConfigSchema.parse({ metadataFields: ['bad_field'] })).toThrow(); - }); -}); - -describe('MultipartUploadConfigSchema', () => { - it('should apply defaults', () => { - const result = MultipartUploadConfigSchema.parse({}); - expect(result.enabled).toBe(true); - expect(result.partSize).toBe(5 * 1024 * 1024); - expect(result.maxConcurrentParts).toBe(5); - expect(result.threshold).toBe(100 * 1024 * 1024); - }); - - it('should reject partSize below minimum', () => { - expect(() => MultipartUploadConfigSchema.parse({ partSize: 100 })).toThrow(); - }); - - it('should reject maxConcurrentParts out of range', () => { - expect(() => MultipartUploadConfigSchema.parse({ maxConcurrentParts: 0 })).toThrow(); - expect(() => MultipartUploadConfigSchema.parse({ maxConcurrentParts: 11 })).toThrow(); - }); -}); - -describe('FileVersioningConfigSchema', () => { - it('should apply defaults', () => { - const result = FileVersioningConfigSchema.parse({}); - expect(result.enabled).toBe(false); - }); - - it('should accept valid config', () => { - const result = FileVersioningConfigSchema.parse({ enabled: true, maxVersions: 10, retentionDays: 30 }); - expect(result.maxVersions).toBe(10); - }); - - it('should reject maxVersions out of range', () => { - expect(() => FileVersioningConfigSchema.parse({ maxVersions: 0 })).toThrow(); - expect(() => FileVersioningConfigSchema.parse({ maxVersions: 101 })).toThrow(); - }); -}); - -describe('FileFilterConfigSchema', () => { - it('should accept empty config', () => { - expect(() => FileFilterConfigSchema.parse({})).not.toThrow(); - }); - - it('should accept full config', () => { - const data = { - includePatterns: ['*.jpg'], - excludePatterns: ['*.tmp'], - minFileSize: 0, - maxFileSize: 1024, - allowedExtensions: ['.jpg'], - blockedExtensions: ['.exe'], - }; - expect(() => FileFilterConfigSchema.parse(data)).not.toThrow(); - }); - - it('should reject negative minFileSize', () => { - expect(() => FileFilterConfigSchema.parse({ minFileSize: -1 })).toThrow(); - }); - - it('should reject maxFileSize less than 1', () => { - expect(() => FileFilterConfigSchema.parse({ maxFileSize: 0 })).toThrow(); - }); -}); - -describe('StorageBucketSchema', () => { - it('should accept valid bucket', () => { - const data = { name: 'my_bucket', label: 'My Bucket', bucketName: 'actual-bucket-name' }; - const result = StorageBucketSchema.parse(data); - expect(result.enabled).toBe(true); - }); - - it('should accept bucket with all optional fields', () => { - const data = { - name: 'docs_bucket', - label: 'Documents', - bucketName: 'docs-bucket', - region: 'us-east-1', - enabled: false, - prefix: 'docs/', - accessPattern: 'private', - fileFilters: { allowedExtensions: ['.pdf'] }, - }; - expect(() => StorageBucketSchema.parse(data)).not.toThrow(); - }); - - it('should reject non-snake_case name', () => { - expect(() => StorageBucketSchema.parse({ name: 'MyBucket', label: 'X', bucketName: 'b' })).toThrow(); - }); - - it('should reject missing required fields', () => { - expect(() => StorageBucketSchema.parse({ name: 'b' })).toThrow(); - }); -}); - -describe('FileStorageConnectorSchema', () => { - it('should accept minimal valid connector', () => { - expect(() => FileStorageConnectorSchema.parse(baseConnector)).not.toThrow(); - }); - - it('should apply defaults', () => { - const result = FileStorageConnectorSchema.parse(baseConnector); - expect(result.transferAcceleration).toBe(false); - expect(result.bufferSize).toBe(64 * 1024); - expect(result.enabled).toBe(true); - }); - - it('should accept full connector config', () => { - const full = { - ...baseConnector, - storageConfig: { endpoint: 'https://s3.example.com', region: 'us-east-1', pathStyle: true }, - metadataConfig: { extractMetadata: true, metadataFields: ['content_type'] }, - multipartConfig: { enabled: true }, - versioningConfig: { enabled: true, maxVersions: 5 }, - encryption: { enabled: true, algorithm: 'AES256' }, - lifecyclePolicy: { enabled: true, deleteAfterDays: 90 }, - contentProcessing: { extractText: true, generateThumbnails: true, thumbnailSizes: [{ width: 100, height: 100 }], virusScan: false }, - bufferSize: 2048, - transferAcceleration: true, - }; - expect(() => FileStorageConnectorSchema.parse(full)).not.toThrow(); - }); - - it('should reject wrong type literal', () => { - expect(() => FileStorageConnectorSchema.parse({ ...baseConnector, type: 'database' })).toThrow(); - }); - - it('should reject invalid provider', () => { - expect(() => FileStorageConnectorSchema.parse({ ...baseConnector, provider: 'invalid' })).toThrow(); - }); - - it('should reject missing buckets', () => { - const { buckets: _, ...noBuckets } = baseConnector; - expect(() => FileStorageConnectorSchema.parse(noBuckets)).toThrow(); - }); - - it('should reject bufferSize below minimum', () => { - expect(() => FileStorageConnectorSchema.parse({ ...baseConnector, bufferSize: 100 })).toThrow(); - }); - - it('should reject invalid storageConfig endpoint', () => { - expect(() => FileStorageConnectorSchema.parse({ ...baseConnector, storageConfig: { endpoint: 'not-a-url' } })).toThrow(); - }); -}); diff --git a/packages/spec/src/integration/connector/file-storage.zod.ts b/packages/spec/src/integration/connector/file-storage.zod.ts deleted file mode 100644 index 151f26c92d..0000000000 --- a/packages/spec/src/integration/connector/file-storage.zod.ts +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { z } from 'zod'; -import { - ConnectorSchema, -} from '../connector.zod'; - -/** - * File Storage Connector Protocol Template - * - * Specialized connector for file storage systems (S3, Azure Blob, Google Cloud Storage, etc.) - * Extends the base connector with file-specific features like multipart uploads, - * versioning, and metadata extraction. - */ - -/** - * File Storage Provider Types - */ -import { lazySchema } from '../../shared/lazy-schema'; -export const FileStorageProviderSchema = lazySchema(() => z.enum([ - 's3', // Amazon S3 - 'azure_blob', // Azure Blob Storage - 'gcs', // Google Cloud Storage - 'dropbox', // Dropbox - 'box', // Box - 'onedrive', // Microsoft OneDrive - 'google_drive', // Google Drive - 'sharepoint', // SharePoint - 'ftp', // FTP/SFTP - 'local', // Local file system - 'custom', // Custom file storage -]).describe('File storage provider type')); - -export type FileStorageProvider = z.infer; - -/** - * File Access Pattern - */ -export const FileAccessPatternSchema = lazySchema(() => z.enum([ - 'public_read', // Public read access - 'private', // Private access - 'authenticated_read', // Requires authentication - 'bucket_owner_read', // Bucket owner has read access - 'bucket_owner_full', // Bucket owner has full control -]).describe('File access pattern')); - -export type FileAccessPattern = z.infer; - -/** - * File Metadata Configuration - */ -export const FileMetadataConfigSchema = lazySchema(() => z.object({ - extractMetadata: z.boolean().default(true).describe('Extract file metadata'), - - metadataFields: z.array(z.enum([ - 'content_type', - 'file_size', - 'last_modified', - 'etag', - 'checksum', - 'creator', - 'created_at', - 'custom', - ])).optional().describe('Metadata fields to extract'), - - customMetadata: z.record(z.string(), z.string()).optional().describe('Custom metadata key-value pairs'), -})); - -export type FileMetadataConfig = z.infer; - -/** - * Multipart Upload Configuration - */ -export const MultipartUploadConfigSchema = lazySchema(() => z.object({ - enabled: z.boolean().default(true).describe('Enable multipart uploads'), - - partSize: z.number().min(5 * 1024 * 1024).default(5 * 1024 * 1024).describe('Part size in bytes (min 5MB)'), - - maxConcurrentParts: z.number().min(1).max(10).default(5).describe('Maximum concurrent part uploads'), - - threshold: z.number().min(5 * 1024 * 1024).default(100 * 1024 * 1024).describe('File size threshold for multipart upload in bytes'), -})); - -export type MultipartUploadConfig = z.infer; - -/** - * File Versioning Configuration - */ -export const FileVersioningConfigSchema = lazySchema(() => z.object({ - enabled: z.boolean().default(false).describe('Enable file versioning'), - - maxVersions: z.number().min(1).max(100).optional().describe('Maximum versions to retain'), - - retentionDays: z.number().min(1).optional().describe('Version retention period in days'), -})); - -export type FileVersioningConfig = z.infer; - -/** - * File Filter Configuration - */ -export const FileFilterConfigSchema = lazySchema(() => z.object({ - includePatterns: z.array(z.string()).optional().describe('File patterns to include (glob)'), - - excludePatterns: z.array(z.string()).optional().describe('File patterns to exclude (glob)'), - - minFileSize: z.number().min(0).optional().describe('Minimum file size in bytes'), - - maxFileSize: z.number().min(1).optional().describe('Maximum file size in bytes'), - - allowedExtensions: z.array(z.string()).optional().describe('Allowed file extensions'), - - blockedExtensions: z.array(z.string()).optional().describe('Blocked file extensions'), -})); - -export type FileFilterConfig = z.infer; - -/** - * File Storage Bucket/Container Configuration - */ -export const StorageBucketSchema = lazySchema(() => z.object({ - name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Bucket identifier in ObjectStack (snake_case)'), - label: z.string().describe('Display label'), - bucketName: z.string().describe('Actual bucket/container name in storage system'), - region: z.string().optional().describe('Storage region'), - enabled: z.boolean().default(true).describe('Enable sync for this bucket'), - prefix: z.string().optional().describe('Prefix/path within bucket'), - accessPattern: FileAccessPatternSchema.optional().describe('Access pattern'), - fileFilters: FileFilterConfigSchema.optional().describe('File filter configuration'), -})); - -export type StorageBucket = z.infer; - -/** - * File Storage Connector Configuration Schema - */ -export const FileStorageConnectorSchema = lazySchema(() => ConnectorSchema.extend({ - type: z.literal('file_storage'), - - /** - * File storage provider - */ - provider: FileStorageProviderSchema.describe('File storage provider type'), - - /** - * Storage configuration - */ - storageConfig: z.object({ - endpoint: z.string().url().optional().describe('Custom endpoint URL'), - region: z.string().optional().describe('Default region'), - pathStyle: z.boolean().optional().default(false).describe('Use path-style URLs (for S3-compatible)'), - }).optional().describe('Storage configuration'), - - /** - * Buckets/containers to sync - */ - buckets: z.array(StorageBucketSchema).describe('Buckets/containers to sync'), - - /** - * File metadata configuration - */ - metadataConfig: FileMetadataConfigSchema.optional().describe('Metadata extraction configuration'), - - /** - * Multipart upload configuration - */ - multipartConfig: MultipartUploadConfigSchema.optional().describe('Multipart upload configuration'), - - /** - * File versioning configuration - */ - versioningConfig: FileVersioningConfigSchema.optional().describe('File versioning configuration'), - - /** - * Enable server-side encryption - */ - encryption: z.object({ - enabled: z.boolean().default(false).describe('Enable server-side encryption'), - algorithm: z.enum(['AES256', 'aws:kms', 'custom']).optional().describe('Encryption algorithm'), - kmsKeyId: z.string().optional().describe('KMS key ID (for aws:kms)'), - }).optional().describe('Encryption configuration'), - - /** - * Lifecycle policy - */ - lifecyclePolicy: z.object({ - enabled: z.boolean().default(false).describe('Enable lifecycle policy'), - deleteAfterDays: z.number().min(1).optional().describe('Delete files after N days'), - archiveAfterDays: z.number().min(1).optional().describe('Archive files after N days'), - }).optional().describe('Lifecycle policy'), - - /** - * Content processing configuration - */ - contentProcessing: z.object({ - extractText: z.boolean().default(false).describe('Extract text from documents'), - generateThumbnails: z.boolean().default(false).describe('Generate image thumbnails'), - thumbnailSizes: z.array(z.object({ - width: z.number().min(1), - height: z.number().min(1), - })).optional().describe('Thumbnail sizes'), - virusScan: z.boolean().default(false).describe('Scan for viruses'), - }).optional().describe('Content processing configuration'), - - /** - * Download/upload buffer size - */ - bufferSize: z.number().min(1024).default(64 * 1024).describe('Buffer size in bytes'), - - /** - * Enable transfer acceleration (for supported providers) - */ - transferAcceleration: z.boolean().default(false).describe('Enable transfer acceleration'), -})); - -export type FileStorageConnector = z.infer; - -// ============================================================================ -// Helper Functions & Examples -// ============================================================================ - -/** - * Example: Amazon S3 Connector Configuration - */ -export const s3ConnectorExample = { - name: 's3_production_assets', - label: 'Production S3 Assets', - type: 'file_storage', - provider: 's3', - authentication: { - type: 'api_key', - apiKey: '${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}', - headerName: 'Authorization', - }, - storageConfig: { - region: 'us-east-1', - pathStyle: false, - }, - buckets: [ - { - name: 'product_images', - label: 'Product Images', - bucketName: 'my-company-product-images', - region: 'us-east-1', - enabled: true, - prefix: 'products/', - accessPattern: 'public_read', - fileFilters: { - allowedExtensions: ['.jpg', '.jpeg', '.png', '.webp'], - maxFileSize: 10 * 1024 * 1024, // 10MB - }, - }, - { - name: 'customer_documents', - label: 'Customer Documents', - bucketName: 'my-company-customer-docs', - region: 'us-east-1', - enabled: true, - accessPattern: 'private', - fileFilters: { - allowedExtensions: ['.pdf', '.docx', '.xlsx'], - maxFileSize: 50 * 1024 * 1024, // 50MB - }, - }, - ], - metadataConfig: { - extractMetadata: true, - metadataFields: ['content_type', 'file_size', 'last_modified', 'etag'], - }, - multipartConfig: { - enabled: true, - partSize: 5 * 1024 * 1024, // 5MB - maxConcurrentParts: 5, - threshold: 100 * 1024 * 1024, // 100MB - }, - versioningConfig: { - enabled: true, - maxVersions: 10, - }, - encryption: { - enabled: true, - algorithm: 'aws:kms', - kmsKeyId: '${AWS_KMS_KEY_ID}', - }, - contentProcessing: { - extractText: true, - generateThumbnails: true, - thumbnailSizes: [ - { width: 150, height: 150 }, - { width: 300, height: 300 }, - { width: 600, height: 600 }, - ], - virusScan: true, - }, - syncConfig: { - strategy: 'incremental', - direction: 'bidirectional', - realtimeSync: true, - conflictResolution: 'latest_wins', - batchSize: 100, - }, - transferAcceleration: true, - status: 'active', - enabled: true, -}; - -/** - * Example: Google Drive Connector Configuration - */ -export const googleDriveConnectorExample = { - name: 'google_drive_team', - label: 'Google Drive Team Folder', - type: 'file_storage', - provider: 'google_drive', - authentication: { - type: 'oauth2', - clientId: '${GOOGLE_CLIENT_ID}', - clientSecret: '${GOOGLE_CLIENT_SECRET}', - authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth', - tokenUrl: 'https://oauth2.googleapis.com/token', - grantType: 'authorization_code', - scopes: ['https://www.googleapis.com/auth/drive.file'], - }, - buckets: [ - { - name: 'team_drive', - label: 'Team Drive', - bucketName: 'shared-team-drive', - enabled: true, - fileFilters: { - excludePatterns: ['*.tmp', '~$*'], - }, - }, - ], - metadataConfig: { - extractMetadata: true, - metadataFields: ['content_type', 'file_size', 'last_modified', 'creator', 'created_at'], - }, - versioningConfig: { - enabled: true, - maxVersions: 5, - }, - syncConfig: { - strategy: 'incremental', - direction: 'bidirectional', - realtimeSync: true, - conflictResolution: 'latest_wins', - batchSize: 50, - }, - status: 'active', - enabled: true, -}; - -/** - * Example: Azure Blob Storage Connector Configuration - */ -export const azureBlobConnectorExample = { - name: 'azure_blob_storage', - label: 'Azure Blob Storage', - type: 'file_storage', - provider: 'azure_blob', - authentication: { - type: 'api_key', - apiKey: '${AZURE_STORAGE_ACCOUNT_KEY}', - headerName: 'x-ms-blob-type', - }, - storageConfig: { - endpoint: 'https://myaccount.blob.core.windows.net', - }, - buckets: [ - { - name: 'archive_container', - label: 'Archive Container', - bucketName: 'archive', - enabled: true, - accessPattern: 'private', - }, - ], - metadataConfig: { - extractMetadata: true, - metadataFields: ['content_type', 'file_size', 'last_modified', 'etag'], - }, - encryption: { - enabled: true, - algorithm: 'AES256', - }, - lifecyclePolicy: { - enabled: true, - archiveAfterDays: 90, - deleteAfterDays: 365, - }, - syncConfig: { - strategy: 'incremental', - direction: 'import', - schedule: '0 1 * * *', // Daily at 1 AM - batchSize: 200, - }, - status: 'active', - enabled: true, -}; diff --git a/packages/spec/src/integration/connector/github.test.ts b/packages/spec/src/integration/connector/github.test.ts deleted file mode 100644 index 778878e585..0000000000 --- a/packages/spec/src/integration/connector/github.test.ts +++ /dev/null @@ -1,364 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - GitHubConnectorSchema, - GitHubRepositorySchema, - GitHubCommitConfigSchema, - GitHubPullRequestConfigSchema, - GitHubActionsWorkflowSchema, - GitHubReleaseConfigSchema, - GitHubIssueTrackingSchema, - githubPublicConnectorExample, - githubEnterpriseConnectorExample, - type GitHubConnector, -} from './github.zod'; - -describe('GitHubRepositorySchema', () => { - it('should accept minimal repository config', () => { - const repo = { - owner: 'objectstack-ai', - name: 'spec', - }; - - const result = GitHubRepositorySchema.parse(repo); - expect(result.defaultBranch).toBe('main'); - expect(result.autoMerge).toBe(false); - }); - - it('should accept repository with branch protection', () => { - const repo = { - owner: 'objectstack-ai', - name: 'spec', - defaultBranch: 'main', - branchProtection: { - requiredReviewers: 2, - requireStatusChecks: true, - enforceAdmins: true, - }, - }; - - expect(() => GitHubRepositorySchema.parse(repo)).not.toThrow(); - }); - - it('should accept repository with topics', () => { - const repo = { - owner: 'objectstack-ai', - name: 'spec', - topics: ['objectstack', 'low-code', 'metadata'], - }; - - expect(() => GitHubRepositorySchema.parse(repo)).not.toThrow(); - }); -}); - -describe('GitHubCommitConfigSchema', () => { - it('should accept minimal commit config', () => { - const config = {}; - - const result = GitHubCommitConfigSchema.parse(config); - expect(result.signCommits).toBe(false); - expect(result.useConventionalCommits).toBe(true); - }); - - it('should accept full commit config', () => { - const config = { - authorName: 'ObjectStack Bot', - authorEmail: 'bot@objectstack.ai', - signCommits: true, - messageTemplate: '{{type}}: {{message}}', - useConventionalCommits: true, - }; - - expect(() => GitHubCommitConfigSchema.parse(config)).not.toThrow(); - }); - - it('should validate email format', () => { - expect(() => GitHubCommitConfigSchema.parse({ - authorEmail: 'invalid-email', - })).toThrow(); - - expect(() => GitHubCommitConfigSchema.parse({ - authorEmail: 'valid@email.com', - })).not.toThrow(); - }); -}); - -describe('GitHubPullRequestConfigSchema', () => { - it('should accept minimal PR config', () => { - const config = {}; - - const result = GitHubPullRequestConfigSchema.parse(config); - expect(result.draftByDefault).toBe(false); - expect(result.deleteHeadBranch).toBe(true); - }); - - it('should accept PR config with templates and reviewers', () => { - const config = { - titleTemplate: '{{type}}: {{description}}', - bodyTemplate: '## Changes\n\n{{changes}}', - defaultReviewers: ['reviewer1', 'reviewer2'], - defaultAssignees: ['assignee1'], - defaultLabels: ['automated', 'needs-review'], - }; - - expect(() => GitHubPullRequestConfigSchema.parse(config)).not.toThrow(); - }); -}); - -describe('GitHubActionsWorkflowSchema', () => { - it('should accept minimal workflow', () => { - const workflow = { - name: 'CI', - path: '.github/workflows/ci.yml', - }; - - const result = GitHubActionsWorkflowSchema.parse(workflow); - expect(result.enabled).toBe(true); - }); - - it('should accept all trigger types', () => { - const triggers = ['push', 'pull_request', 'release', 'schedule', 'workflow_dispatch', 'repository_dispatch'] as const; - - triggers.forEach(trigger => { - const workflow = { - name: 'Test', - path: '.github/workflows/test.yml', - triggers: [trigger], - }; - expect(() => GitHubActionsWorkflowSchema.parse(workflow)).not.toThrow(); - }); - }); - - it('should accept workflow with env and secrets', () => { - const workflow = { - name: 'Deploy', - path: '.github/workflows/deploy.yml', - env: { - NODE_ENV: 'production', - API_URL: 'https://api.example.com', - }, - secrets: ['DEPLOY_TOKEN', 'AWS_SECRET_KEY'], - }; - - expect(() => GitHubActionsWorkflowSchema.parse(workflow)).not.toThrow(); - }); -}); - -describe('GitHubReleaseConfigSchema', () => { - it('should accept minimal release config', () => { - const config = {}; - - const result = GitHubReleaseConfigSchema.parse(config); - expect(result.tagPattern).toBe('v*'); - expect(result.semanticVersioning).toBe(true); - expect(result.autoReleaseNotes).toBe(true); - }); - - it('should accept full release config', () => { - const config = { - tagPattern: 'release/*', - semanticVersioning: true, - autoReleaseNotes: true, - releaseNameTemplate: 'Release {{version}}', - preReleasePattern: '*-rc*', - draftByDefault: true, - }; - - expect(() => GitHubReleaseConfigSchema.parse(config)).not.toThrow(); - }); -}); - -describe('GitHubIssueTrackingSchema', () => { - it('should accept minimal issue tracking config', () => { - const config = {}; - - const result = GitHubIssueTrackingSchema.parse(config); - expect(result.enabled).toBe(true); - expect(result.autoAssign).toBe(false); - }); - - it('should accept auto-close stale issues config', () => { - const config = { - autoCloseStale: { - enabled: true, - daysBeforeStale: 30, - daysBeforeClose: 7, - staleLabel: 'wontfix', - }, - }; - - const result = GitHubIssueTrackingSchema.parse(config); - expect(result.autoCloseStale?.enabled).toBe(true); - expect(result.autoCloseStale?.daysBeforeStale).toBe(30); - }); -}); - -describe('GitHubConnectorSchema', () => { - describe('Basic Properties', () => { - it('should accept minimal GitHub connector', () => { - const connector: GitHubConnector = { - name: 'github_test', - label: 'GitHub Test', - type: 'saas', - provider: 'github', - authentication: { - type: 'oauth2', - clientId: 'test-client-id', - clientSecret: 'test-client-secret', - authorizationUrl: 'https://github.com/login/oauth/authorize', - tokenUrl: 'https://github.com/login/oauth/access_token', - grantType: 'authorization_code', - }, - repositories: [ - { - owner: 'test-org', - name: 'test-repo', - }, - ], - }; - - const result = GitHubConnectorSchema.parse(connector); - expect(result.baseUrl).toBe('https://api.github.com'); - expect(result.enableWebhooks).toBe(true); - }); - - it('should enforce snake_case for connector name', () => { - const validNames = ['github_test', 'github_production', '_internal']; - validNames.forEach(name => { - expect(() => GitHubConnectorSchema.parse({ - name, - label: 'Test', - type: 'saas', - provider: 'github', - authentication: { type: 'oauth2', clientId: 'x', clientSecret: 'y', authorizationUrl: 'https://x.com', tokenUrl: 'https://y.com', grantType: 'authorization_code' }, - repositories: [{ owner: 'x', name: 'y' }], - })).not.toThrow(); - }); - - const invalidNames = ['githubTest', 'GitHub-Test', '123github']; - invalidNames.forEach(name => { - expect(() => GitHubConnectorSchema.parse({ - name, - label: 'Test', - type: 'saas', - provider: 'github', - authentication: { type: 'oauth2', clientId: 'x', clientSecret: 'y', authorizationUrl: 'https://x.com', tokenUrl: 'https://y.com', grantType: 'authorization_code' }, - repositories: [{ owner: 'x', name: 'y' }], - })).toThrow(); - }); - }); - - it('should accept GitHub Enterprise provider', () => { - const connector: GitHubConnector = { - name: 'github_enterprise', - label: 'GitHub Enterprise', - type: 'saas', - provider: 'github_enterprise', - baseUrl: 'https://github.enterprise.com/api/v3', - authentication: { - type: 'oauth2', - clientId: 'test-client-id', - clientSecret: 'test-client-secret', - authorizationUrl: 'https://github.enterprise.com/login/oauth/authorize', - tokenUrl: 'https://github.enterprise.com/login/oauth/access_token', - grantType: 'authorization_code', - }, - repositories: [ - { - owner: 'enterprise-org', - name: 'app', - }, - ], - }; - - expect(() => GitHubConnectorSchema.parse(connector)).not.toThrow(); - }); - }); - - describe('Complete Configuration', () => { - it('should accept full GitHub connector with all features', () => { - const connector: GitHubConnector = { - name: 'github_full', - label: 'GitHub Full Config', - type: 'saas', - provider: 'github', - baseUrl: 'https://api.github.com', - - authentication: { - type: 'oauth2', - clientId: '${GITHUB_CLIENT_ID}', - clientSecret: '${GITHUB_CLIENT_SECRET}', - authorizationUrl: 'https://github.com/login/oauth/authorize', - tokenUrl: 'https://github.com/login/oauth/access_token', - grantType: 'authorization_code', - scopes: ['repo', 'workflow'], - }, - - repositories: [ - { - owner: 'objectstack-ai', - name: 'spec', - defaultBranch: 'main', - autoMerge: false, - branchProtection: { - requiredReviewers: 1, - requireStatusChecks: true, - }, - topics: ['objectstack', 'metadata'], - }, - ], - - commitConfig: { - authorName: 'Bot', - authorEmail: 'bot@example.com', - useConventionalCommits: true, - }, - - pullRequestConfig: { - defaultReviewers: ['reviewer'], - defaultLabels: ['automated'], - }, - - workflows: [ - { - name: 'CI', - path: '.github/workflows/ci.yml', - triggers: ['push', 'pull_request'], - }, - ], - - releaseConfig: { - semanticVersioning: true, - autoReleaseNotes: true, - }, - - issueTracking: { - enabled: true, - autoCloseStale: { - enabled: true, - daysBeforeStale: 60, - daysBeforeClose: 7, - staleLabel: 'stale', - }, - }, - - enableWebhooks: true, - webhookEvents: ['push', 'pull_request', 'release'], - - status: 'active', - enabled: true, - }; - - expect(() => GitHubConnectorSchema.parse(connector)).not.toThrow(); - }); - }); - - describe('Example Configurations', () => { - it('should accept GitHub.com public connector example', () => { - expect(() => GitHubConnectorSchema.parse(githubPublicConnectorExample)).not.toThrow(); - }); - - it('should accept GitHub Enterprise connector example', () => { - expect(() => GitHubConnectorSchema.parse(githubEnterpriseConnectorExample)).not.toThrow(); - }); - }); -}); diff --git a/packages/spec/src/integration/connector/github.zod.ts b/packages/spec/src/integration/connector/github.zod.ts deleted file mode 100644 index bc740fd8e9..0000000000 --- a/packages/spec/src/integration/connector/github.zod.ts +++ /dev/null @@ -1,530 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { z } from 'zod'; -import { TemplateExpressionInputSchema } from '../../shared/expression.zod'; -import { - ConnectorSchema, -} from '../connector.zod'; - -/** - * GitHub Connector Protocol - * - * Specialized connector for GitHub integration enabling automated - * version control operations, CI/CD workflows, and release management. - * - * Use Cases: - * - Automated code commits and pull requests - * - GitHub Actions workflow management - * - Issue and project tracking - * - Release and tag management - * - Repository administration - * - * @example - * ```typescript - * import { GitHubConnector } from '@objectstack/spec/integration'; - * - * const githubConnector: GitHubConnector = { - * name: 'github_enterprise', - * label: 'GitHub Enterprise', - * type: 'saas', - * provider: 'github', - * baseUrl: 'https://api.github.com', - * authentication: { - * type: 'oauth2', - * clientId: '${GITHUB_CLIENT_ID}', - * clientSecret: '${GITHUB_CLIENT_SECRET}', - * authorizationUrl: 'https://github.com/login/oauth/authorize', - * tokenUrl: 'https://github.com/login/oauth/access_token', - * grantType: 'authorization_code', - * scopes: ['repo', 'workflow', 'admin:org'], - * }, - * repositories: [ - * { - * owner: 'objectstack-ai', - * name: 'spec', - * defaultBranch: 'main', - * autoMerge: false, - * }, - * ], - * }; - * ``` - */ - -/** - * GitHub Provider Type - */ -import { lazySchema } from '../../shared/lazy-schema'; -export const GitHubProviderSchema = lazySchema(() => z.enum([ - 'github', // GitHub.com - 'github_enterprise', // GitHub Enterprise Server -]).describe('GitHub provider type')); - -export type GitHubProvider = z.infer; - -/** - * GitHub Repository Configuration - * Defines a repository to integrate with - */ -export const GitHubRepositorySchema = lazySchema(() => z.object({ - /** - * Repository owner (organization or user) - */ - owner: z.string().describe('Repository owner (organization or username)'), - - /** - * Repository name - */ - name: z.string().describe('Repository name'), - - /** - * Default branch name - */ - defaultBranch: z.string().optional().default('main').describe('Default branch name'), - - /** - * Enable auto-merge for PRs - */ - autoMerge: z.boolean().optional().default(false).describe('Enable auto-merge for pull requests'), - - /** - * Branch protection rules - */ - branchProtection: z.object({ - requiredReviewers: z.number().int().min(0).optional().default(1).describe('Required number of reviewers'), - requireStatusChecks: z.boolean().optional().default(true).describe('Require status checks to pass'), - enforceAdmins: z.boolean().optional().default(false).describe('Enforce protections for admins'), - allowForcePushes: z.boolean().optional().default(false).describe('Allow force pushes'), - allowDeletions: z.boolean().optional().default(false).describe('Allow branch deletions'), - }).optional().describe('Branch protection configuration'), - - /** - * Repository topics/tags - */ - topics: z.array(z.string()).optional().describe('Repository topics'), -})); - -export type GitHubRepository = z.infer; - -/** - * GitHub Commit Configuration - */ -export const GitHubCommitConfigSchema = lazySchema(() => z.object({ - /** - * Commit author name - */ - authorName: z.string().optional().describe('Commit author name'), - - /** - * Commit author email - */ - authorEmail: z.string().email().optional().describe('Commit author email'), - - /** - * GPG sign commits - */ - signCommits: z.boolean().optional().default(false).describe('Sign commits with GPG'), - - /** - * Commit message template - */ - messageTemplate: z.string().optional().describe('Commit message template'), - - /** - * Conventional commits format - */ - useConventionalCommits: z.boolean().optional().default(true).describe('Use conventional commits format'), -})); - -export type GitHubCommitConfig = z.infer; - -/** - * GitHub Pull Request Configuration - */ -export const GitHubPullRequestConfigSchema = lazySchema(() => z.object({ - /** - * Default PR title template - */ - titleTemplate: TemplateExpressionInputSchema.optional().describe('PR title template — supports {{var}} interpolation'), - - /** - * Default PR body template - */ - bodyTemplate: TemplateExpressionInputSchema.optional().describe('PR body template — supports {{var}} interpolation'), - - /** - * Default reviewers - */ - defaultReviewers: z.array(z.string()).optional().describe('Default reviewers (usernames)'), - - /** - * Default assignees - */ - defaultAssignees: z.array(z.string()).optional().describe('Default assignees (usernames)'), - - /** - * Default labels - */ - defaultLabels: z.array(z.string()).optional().describe('Default labels'), - - /** - * Enable draft PRs by default - */ - draftByDefault: z.boolean().optional().default(false).describe('Create draft PRs by default'), - - /** - * Auto-delete head branch after merge - */ - deleteHeadBranch: z.boolean().optional().default(true).describe('Delete head branch after merge'), -})); - -export type GitHubPullRequestConfig = z.infer; - -/** - * GitHub Actions Workflow Configuration - */ -export const GitHubActionsWorkflowSchema = lazySchema(() => z.object({ - /** - * Workflow name - */ - name: z.string().describe('Workflow name'), - - /** - * Workflow file path - */ - path: z.string().describe('Workflow file path (e.g., .github/workflows/ci.yml)'), - - /** - * Enable workflow - */ - enabled: z.boolean().optional().default(true).describe('Enable workflow'), - - /** - * Workflow triggers - */ - triggers: z.array(z.enum([ - 'push', - 'pull_request', - 'release', - 'schedule', - 'workflow_dispatch', - 'repository_dispatch', - ])).optional().describe('Workflow triggers'), - - /** - * Environment variables - */ - env: z.record(z.string(), z.string()).optional().describe('Environment variables'), - - /** - * Secrets required - */ - secrets: z.array(z.string()).optional().describe('Required secrets'), -})); - -export type GitHubActionsWorkflow = z.infer; - -/** - * GitHub Release Configuration - */ -export const GitHubReleaseConfigSchema = lazySchema(() => z.object({ - /** - * Tag name pattern - */ - tagPattern: z.string().optional().default('v*').describe('Tag name pattern (e.g., v*, release/*)'), - - /** - * Use semantic versioning - */ - semanticVersioning: z.boolean().optional().default(true).describe('Use semantic versioning'), - - /** - * Generate release notes automatically - */ - autoReleaseNotes: z.boolean().optional().default(true).describe('Generate release notes automatically'), - - /** - * Release name template - */ - releaseNameTemplate: z.string().optional().describe('Release name template'), - - /** - * Pre-release pattern - */ - preReleasePattern: z.string().optional().describe('Pre-release pattern (e.g., *-alpha, *-beta)'), - - /** - * Create draft releases - */ - draftByDefault: z.boolean().optional().default(false).describe('Create draft releases by default'), -})); - -export type GitHubReleaseConfig = z.infer; - -/** - * GitHub Issue Tracking Configuration - */ -export const GitHubIssueTrackingSchema = lazySchema(() => z.object({ - /** - * Enable issue tracking - */ - enabled: z.boolean().optional().default(true).describe('Enable issue tracking'), - - /** - * Default issue labels - */ - defaultLabels: z.array(z.string()).optional().describe('Default issue labels'), - - /** - * Issue template paths - */ - templatePaths: z.array(z.string()).optional().describe('Issue template paths'), - - /** - * Auto-assign issues - */ - autoAssign: z.boolean().optional().default(false).describe('Auto-assign issues'), - - /** - * Auto-close stale issues - */ - autoCloseStale: z.object({ - enabled: z.boolean().default(false), - daysBeforeStale: z.number().int().min(1).optional().default(60), - daysBeforeClose: z.number().int().min(1).optional().default(7), - staleLabel: z.string().optional().default('stale'), - }).optional().describe('Auto-close stale issues configuration'), -})); - -export type GitHubIssueTracking = z.infer; - -/** - * GitHub Connector Schema - * Complete GitHub integration configuration - */ -export const GitHubConnectorSchema = lazySchema(() => ConnectorSchema.extend({ - type: z.literal('saas'), - - /** - * GitHub provider type - */ - provider: GitHubProviderSchema.describe('GitHub provider'), - - /** - * GitHub API base URL - */ - baseUrl: z.string().url().optional().default('https://api.github.com').describe('GitHub API base URL'), - - /** - * Repositories to integrate - */ - repositories: z.array(GitHubRepositorySchema).describe('Repositories to manage'), - - /** - * Commit configuration - */ - commitConfig: GitHubCommitConfigSchema.optional().describe('Commit configuration'), - - /** - * Pull request configuration - */ - pullRequestConfig: GitHubPullRequestConfigSchema.optional().describe('Pull request configuration'), - - /** - * GitHub Actions workflows - */ - workflows: z.array(GitHubActionsWorkflowSchema).optional().describe('GitHub Actions workflows'), - - /** - * Release configuration - */ - releaseConfig: GitHubReleaseConfigSchema.optional().describe('Release configuration'), - - /** - * Issue tracking configuration - */ - issueTracking: GitHubIssueTrackingSchema.optional().describe('Issue tracking configuration'), - - /** - * Enable webhooks - */ - enableWebhooks: z.boolean().optional().default(true).describe('Enable GitHub webhooks'), - - /** - * Webhook events to subscribe - */ - webhookEvents: z.array(z.enum([ - 'push', - 'pull_request', - 'issues', - 'issue_comment', - 'release', - 'workflow_run', - 'deployment', - 'deployment_status', - 'check_run', - 'check_suite', - 'status', - ])).optional().describe('Webhook events to subscribe to'), -})); - -export type GitHubConnector = z.infer; - -// ============================================================================ -// Helper Functions & Examples -// ============================================================================ - -/** - * Example: GitHub.com Connector Configuration - */ -export const githubPublicConnectorExample = { - name: 'github_public', - label: 'GitHub.com', - type: 'saas', - provider: 'github', - baseUrl: 'https://api.github.com', - - authentication: { - type: 'oauth2', - clientId: '${GITHUB_CLIENT_ID}', - clientSecret: '${GITHUB_CLIENT_SECRET}', - authorizationUrl: 'https://github.com/login/oauth/authorize', - tokenUrl: 'https://github.com/login/oauth/access_token', - scopes: ['repo', 'workflow', 'write:packages'], - }, - - repositories: [ - { - owner: 'objectstack-ai', - name: 'spec', - defaultBranch: 'main', - autoMerge: false, - branchProtection: { - requiredReviewers: 1, - requireStatusChecks: true, - enforceAdmins: false, - allowForcePushes: false, - allowDeletions: false, - }, - topics: ['objectstack', 'low-code', 'metadata-driven'], - }, - ], - - commitConfig: { - authorName: 'ObjectStack Bot', - authorEmail: 'bot@objectstack.ai', - signCommits: false, - useConventionalCommits: true, - }, - - pullRequestConfig: { - titleTemplate: '{{type}}: {{description}}', - defaultReviewers: ['team-lead'], - defaultLabels: ['automated', 'ai-generated'], - draftByDefault: false, - deleteHeadBranch: true, - }, - - workflows: [ - { - name: 'CI', - path: '.github/workflows/ci.yml', - enabled: true, - triggers: ['push', 'pull_request'], - }, - { - name: 'Release', - path: '.github/workflows/release.yml', - enabled: true, - triggers: ['release'], - }, - ], - - releaseConfig: { - tagPattern: 'v*', - semanticVersioning: true, - autoReleaseNotes: true, - releaseNameTemplate: 'Release {{version}}', - draftByDefault: false, - }, - - issueTracking: { - enabled: true, - defaultLabels: ['needs-triage'], - autoAssign: false, - autoCloseStale: { - enabled: true, - daysBeforeStale: 60, - daysBeforeClose: 7, - staleLabel: 'stale', - }, - }, - - enableWebhooks: true, - webhookEvents: ['push', 'pull_request', 'release', 'workflow_run'], - - status: 'active', - enabled: true, -}; - -/** - * Example: GitHub Enterprise Connector Configuration - */ -export const githubEnterpriseConnectorExample = { - name: 'github_enterprise', - label: 'GitHub Enterprise', - type: 'saas', - provider: 'github_enterprise', - baseUrl: 'https://github.enterprise.com/api/v3', - - authentication: { - type: 'oauth2', - clientId: '${GITHUB_ENTERPRISE_CLIENT_ID}', - clientSecret: '${GITHUB_ENTERPRISE_CLIENT_SECRET}', - authorizationUrl: 'https://github.enterprise.com/login/oauth/authorize', - tokenUrl: 'https://github.enterprise.com/login/oauth/access_token', - scopes: ['repo', 'admin:org', 'workflow'], - }, - - repositories: [ - { - owner: 'enterprise-org', - name: 'internal-app', - defaultBranch: 'develop', - autoMerge: true, - branchProtection: { - requiredReviewers: 2, - requireStatusChecks: true, - enforceAdmins: true, - allowForcePushes: false, - allowDeletions: false, - }, - }, - ], - - commitConfig: { - authorName: 'CI Bot', - authorEmail: 'ci-bot@enterprise.com', - signCommits: true, - useConventionalCommits: true, - }, - - pullRequestConfig: { - titleTemplate: '[{{branch}}] {{description}}', - bodyTemplate: `## Changes\n\n{{changes}}\n\n## Testing\n\n{{testing}}`, - defaultReviewers: ['tech-lead', 'security-team'], - defaultLabels: ['automated'], - draftByDefault: true, - deleteHeadBranch: true, - }, - - releaseConfig: { - tagPattern: 'release/*', - semanticVersioning: true, - autoReleaseNotes: true, - preReleasePattern: '*-rc*', - draftByDefault: true, - }, - - status: 'active', - enabled: true, -}; diff --git a/packages/spec/src/integration/connector/message-queue.test.ts b/packages/spec/src/integration/connector/message-queue.test.ts deleted file mode 100644 index b3c84ea2d6..0000000000 --- a/packages/spec/src/integration/connector/message-queue.test.ts +++ /dev/null @@ -1,258 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - MessageQueueProviderSchema, - MessageFormatSchema, - AckModeSchema, - DeliveryGuaranteeSchema, - ConsumerConfigSchema, - ProducerConfigSchema, - DlqConfigSchema, - TopicQueueSchema, - MessageQueueConnectorSchema, -} from './message-queue.zod'; - -const baseAuth = { type: 'none' as const }; - -const minimalTopic = { - name: 'order_events', - label: 'Order Events', - topicName: 'orders', -}; - -const minimalConnector = { - name: 'kafka_main', - label: 'Kafka Main', - type: 'message_queue' as const, - provider: 'kafka' as const, - authentication: baseAuth, - brokerConfig: { - brokers: ['localhost:9092'], - }, - topics: [minimalTopic], -}; - -describe('MessageQueueProviderSchema', () => { - it('should accept all valid providers', () => { - const providers = ['rabbitmq', 'kafka', 'redis_pubsub', 'redis_streams', 'aws_sqs', 'aws_sns', 'google_pubsub', 'azure_service_bus', 'azure_event_hubs', 'nats', 'pulsar', 'activemq', 'custom']; - for (const p of providers) { - expect(MessageQueueProviderSchema.parse(p)).toBe(p); - } - }); - - it('should reject invalid provider', () => { - expect(() => MessageQueueProviderSchema.parse('zeromq')).toThrow(); - }); -}); - -describe('MessageFormatSchema', () => { - it('should accept valid formats', () => { - for (const f of ['json', 'xml', 'protobuf', 'avro', 'text', 'binary']) { - expect(MessageFormatSchema.parse(f)).toBe(f); - } - }); - - it('should reject invalid format', () => { - expect(() => MessageFormatSchema.parse('yaml')).toThrow(); - }); -}); - -describe('AckModeSchema', () => { - it('should accept valid modes', () => { - for (const m of ['auto', 'manual', 'client']) { - expect(AckModeSchema.parse(m)).toBe(m); - } - }); - - it('should reject invalid mode', () => { - expect(() => AckModeSchema.parse('batch')).toThrow(); - }); -}); - -describe('DeliveryGuaranteeSchema', () => { - it('should accept valid guarantees', () => { - for (const g of ['at_most_once', 'at_least_once', 'exactly_once']) { - expect(DeliveryGuaranteeSchema.parse(g)).toBe(g); - } - }); - - it('should reject invalid guarantee', () => { - expect(() => DeliveryGuaranteeSchema.parse('best_effort')).toThrow(); - }); -}); - -describe('ConsumerConfigSchema', () => { - it('should apply defaults', () => { - const result = ConsumerConfigSchema.parse({}); - expect(result.enabled).toBe(true); - expect(result.concurrency).toBe(1); - expect(result.prefetchCount).toBe(10); - expect(result.ackMode).toBe('manual'); - expect(result.autoCommit).toBe(false); - expect(result.autoCommitIntervalMs).toBe(5000); - expect(result.sessionTimeoutMs).toBe(30000); - }); - - it('should accept custom values', () => { - const result = ConsumerConfigSchema.parse({ - consumerGroup: 'my-group', - concurrency: 10, - prefetchCount: 100, - ackMode: 'auto', - rebalanceTimeoutMs: 5000, - }); - expect(result.consumerGroup).toBe('my-group'); - expect(result.concurrency).toBe(10); - }); - - it('should reject concurrency out of range', () => { - expect(() => ConsumerConfigSchema.parse({ concurrency: 0 })).toThrow(); - expect(() => ConsumerConfigSchema.parse({ concurrency: 101 })).toThrow(); - }); - - it('should reject prefetchCount out of range', () => { - expect(() => ConsumerConfigSchema.parse({ prefetchCount: 0 })).toThrow(); - expect(() => ConsumerConfigSchema.parse({ prefetchCount: 1001 })).toThrow(); - }); -}); - -describe('ProducerConfigSchema', () => { - it('should apply defaults', () => { - const result = ProducerConfigSchema.parse({}); - expect(result.enabled).toBe(true); - expect(result.acks).toBe('all'); - expect(result.compressionType).toBe('none'); - expect(result.idempotence).toBe(true); - expect(result.transactional).toBe(false); - }); - - it('should accept custom values', () => { - const result = ProducerConfigSchema.parse({ - acks: '1', - compressionType: 'snappy', - batchSize: 32768, - lingerMs: 10, - }); - expect(result.acks).toBe('1'); - expect(result.compressionType).toBe('snappy'); - }); - - it('should reject invalid acks', () => { - expect(() => ProducerConfigSchema.parse({ acks: '2' })).toThrow(); - }); - - it('should reject invalid compressionType', () => { - expect(() => ProducerConfigSchema.parse({ compressionType: 'brotli' })).toThrow(); - }); -}); - -describe('DlqConfigSchema', () => { - it('should accept valid DLQ config', () => { - const result = DlqConfigSchema.parse({ queueName: 'my-dlq' }); - expect(result.enabled).toBe(false); - expect(result.maxRetries).toBe(3); - expect(result.retryDelayMs).toBe(60000); - }); - - it('should reject missing queueName', () => { - expect(() => DlqConfigSchema.parse({})).toThrow(); - }); - - it('should reject maxRetries out of range', () => { - expect(() => DlqConfigSchema.parse({ queueName: 'dlq', maxRetries: -1 })).toThrow(); - expect(() => DlqConfigSchema.parse({ queueName: 'dlq', maxRetries: 101 })).toThrow(); - }); -}); - -describe('TopicQueueSchema', () => { - it('should accept minimal topic', () => { - const result = TopicQueueSchema.parse(minimalTopic); - expect(result.enabled).toBe(true); - expect(result.mode).toBe('both'); - expect(result.messageFormat).toBe('json'); - }); - - it('should accept topic with all options', () => { - const data = { - ...minimalTopic, - enabled: false, - mode: 'consumer', - messageFormat: 'avro', - partitions: 10, - replicationFactor: 3, - consumerConfig: { consumerGroup: 'grp' }, - producerConfig: { acks: '1' }, - dlqConfig: { queueName: 'dlq' }, - routingKey: 'order.*', - messageFilter: { headers: { type: 'order' } }, - }; - expect(() => TopicQueueSchema.parse(data)).not.toThrow(); - }); - - it('should reject non-snake_case name', () => { - expect(() => TopicQueueSchema.parse({ ...minimalTopic, name: 'OrderEvents' })).toThrow(); - }); - - it('should reject missing required fields', () => { - expect(() => TopicQueueSchema.parse({ name: 'topic' })).toThrow(); - }); -}); - -describe('MessageQueueConnectorSchema', () => { - it('should accept minimal valid connector', () => { - expect(() => MessageQueueConnectorSchema.parse(minimalConnector)).not.toThrow(); - }); - - it('should apply defaults', () => { - const result = MessageQueueConnectorSchema.parse(minimalConnector); - expect(result.deliveryGuarantee).toBe('at_least_once'); - expect(result.preserveOrder).toBe(true); - expect(result.enableMetrics).toBe(true); - expect(result.enableTracing).toBe(false); - expect(result.enabled).toBe(true); - }); - - it('should accept full connector', () => { - const full = { - ...minimalConnector, - brokerConfig: { - brokers: ['broker1:9092', 'broker2:9092'], - clientId: 'my-client', - connectionTimeoutMs: 5000, - requestTimeoutMs: 5000, - }, - deliveryGuarantee: 'exactly_once', - sslConfig: { enabled: true, rejectUnauthorized: true }, - saslConfig: { mechanism: 'scram-sha-256', username: 'u', password: 'p' }, - schemaRegistry: { url: 'https://registry.example.com' }, - preserveOrder: false, - enableMetrics: false, - enableTracing: true, - }; - expect(() => MessageQueueConnectorSchema.parse(full)).not.toThrow(); - }); - - it('should reject wrong type literal', () => { - expect(() => MessageQueueConnectorSchema.parse({ ...minimalConnector, type: 'database' })).toThrow(); - }); - - it('should reject invalid provider', () => { - expect(() => MessageQueueConnectorSchema.parse({ ...minimalConnector, provider: 'unknown' })).toThrow(); - }); - - it('should reject missing brokerConfig', () => { - const { brokerConfig: _, ...noConfig } = minimalConnector; - expect(() => MessageQueueConnectorSchema.parse(noConfig)).toThrow(); - }); - - it('should reject missing topics', () => { - const { topics: _, ...noTopics } = minimalConnector; - expect(() => MessageQueueConnectorSchema.parse(noTopics)).toThrow(); - }); - - it('should reject invalid schemaRegistry url', () => { - expect(() => MessageQueueConnectorSchema.parse({ - ...minimalConnector, - schemaRegistry: { url: 'not-a-url' }, - })).toThrow(); - }); -}); diff --git a/packages/spec/src/integration/connector/message-queue.zod.ts b/packages/spec/src/integration/connector/message-queue.zod.ts deleted file mode 100644 index b285e7fd03..0000000000 --- a/packages/spec/src/integration/connector/message-queue.zod.ts +++ /dev/null @@ -1,501 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { z } from 'zod'; -import { - ConnectorSchema, -} from '../connector.zod'; - -/** - * Message Queue Connector Protocol Template - * - * Specialized connector for message queue systems (RabbitMQ, Kafka, SQS, etc.) - * Extends the base connector with message queue-specific features like topics, - * consumer groups, and message acknowledgment patterns. - */ - -/** - * Message Queue Provider Types - */ -import { lazySchema } from '../../shared/lazy-schema'; -export const MessageQueueProviderSchema = lazySchema(() => z.enum([ - 'rabbitmq', // RabbitMQ - 'kafka', // Apache Kafka - 'redis_pubsub', // Redis Pub/Sub - 'redis_streams', // Redis Streams - 'aws_sqs', // Amazon SQS - 'aws_sns', // Amazon SNS - 'google_pubsub', // Google Cloud Pub/Sub - 'azure_service_bus', // Azure Service Bus - 'azure_event_hubs', // Azure Event Hubs - 'nats', // NATS - 'pulsar', // Apache Pulsar - 'activemq', // Apache ActiveMQ - 'custom', // Custom message queue -]).describe('Message queue provider type')); - -export type MessageQueueProvider = z.infer; - -/** - * Message Format - */ -export const MessageFormatSchema = lazySchema(() => z.enum([ - 'json', - 'xml', - 'protobuf', - 'avro', - 'text', - 'binary', -]).describe('Message format/serialization')); - -export type MessageFormat = z.infer; - -/** - * Message Acknowledgment Mode - */ -export const AckModeSchema = lazySchema(() => z.enum([ - 'auto', // Auto-acknowledge - 'manual', // Manual acknowledge after processing - 'client', // Client-controlled acknowledge -]).describe('Message acknowledgment mode')); - -export type AckMode = z.infer; - -/** - * Delivery Guarantee - */ -export const DeliveryGuaranteeSchema = lazySchema(() => z.enum([ - 'at_most_once', // Fire and forget - 'at_least_once', // May deliver duplicates - 'exactly_once', // Guaranteed exactly once delivery -]).describe('Message delivery guarantee')); - -export type DeliveryGuarantee = z.infer; - -/** - * Consumer Configuration - */ -export const ConsumerConfigSchema = lazySchema(() => z.object({ - enabled: z.boolean().optional().default(true).describe('Enable consumer'), - - consumerGroup: z.string().optional().describe('Consumer group ID'), - - concurrency: z.number().min(1).max(100).optional().default(1).describe('Number of concurrent consumers'), - - prefetchCount: z.number().min(1).max(1000).optional().default(10).describe('Prefetch count'), - - ackMode: AckModeSchema.optional().default('manual'), - - autoCommit: z.boolean().optional().default(false).describe('Auto-commit offsets'), - - autoCommitIntervalMs: z.number().min(100).optional().default(5000).describe('Auto-commit interval in ms'), - - sessionTimeoutMs: z.number().min(1000).optional().default(30000).describe('Session timeout in ms'), - - rebalanceTimeoutMs: z.number().min(1000).optional().describe('Rebalance timeout in ms'), -})); - -export type ConsumerConfig = z.infer; - -/** - * Producer Configuration - */ -export const ProducerConfigSchema = lazySchema(() => z.object({ - enabled: z.boolean().optional().default(true).describe('Enable producer'), - - acks: z.enum(['0', '1', 'all']).optional().default('all').describe('Acknowledgment level'), - - compressionType: z.enum(['none', 'gzip', 'snappy', 'lz4', 'zstd']).optional().default('none').describe('Compression type'), - - batchSize: z.number().min(1).optional().default(16384).describe('Batch size in bytes'), - - lingerMs: z.number().min(0).optional().default(0).describe('Linger time in ms'), - - maxInFlightRequests: z.number().min(1).optional().default(5).describe('Max in-flight requests'), - - idempotence: z.boolean().optional().default(true).describe('Enable idempotent producer'), - - transactional: z.boolean().optional().default(false).describe('Enable transactional producer'), - - transactionTimeoutMs: z.number().min(1000).optional().describe('Transaction timeout in ms'), -})); - -export type ProducerConfig = z.infer; - -/** - * Dead Letter Queue Configuration - */ -export const DlqConfigSchema = lazySchema(() => z.object({ - enabled: z.boolean().optional().default(false).describe('Enable DLQ'), - - queueName: z.string().describe('Dead letter queue/topic name'), - - maxRetries: z.number().min(0).max(100).optional().default(3).describe('Max retries before DLQ'), - - retryDelayMs: z.number().min(0).optional().default(60000).describe('Retry delay in ms'), -})); - -export type DlqConfig = z.infer; - -/** - * Topic/Queue Configuration - */ -export const TopicQueueSchema = lazySchema(() => z.object({ - name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Topic/queue identifier in ObjectStack (snake_case)'), - label: z.string().describe('Display label'), - topicName: z.string().describe('Actual topic/queue name in message queue system'), - enabled: z.boolean().optional().default(true).describe('Enable sync for this topic/queue'), - - /** - * Consumer or Producer - */ - mode: z.enum(['consumer', 'producer', 'both']).optional().default('both').describe('Consumer, producer, or both'), - - /** - * Message format - */ - messageFormat: MessageFormatSchema.optional().default('json'), - - /** - * Partition/shard configuration - */ - partitions: z.number().min(1).optional().describe('Number of partitions (for Kafka)'), - - /** - * Replication factor - */ - replicationFactor: z.number().min(1).optional().describe('Replication factor (for Kafka)'), - - /** - * Consumer configuration - */ - consumerConfig: ConsumerConfigSchema.optional().describe('Consumer-specific configuration'), - - /** - * Producer configuration - */ - producerConfig: ProducerConfigSchema.optional().describe('Producer-specific configuration'), - - /** - * Dead letter queue configuration - */ - dlqConfig: DlqConfigSchema.optional().describe('Dead letter queue configuration'), - - /** - * Message routing key (for RabbitMQ) - */ - routingKey: z.string().optional().describe('Routing key pattern'), - - /** - * Message filter - */ - messageFilter: z.object({ - headers: z.record(z.string(), z.string()).optional().describe('Filter by message headers'), - attributes: z.record(z.string(), z.unknown()).optional().describe('Filter by message attributes'), - }).optional().describe('Message filter criteria'), -})); - -export type TopicQueue = z.infer; - -/** - * Message Queue Connector Configuration Schema - */ -export const MessageQueueConnectorSchema = lazySchema(() => ConnectorSchema.extend({ - type: z.literal('message_queue'), - - /** - * Message queue provider - */ - provider: MessageQueueProviderSchema.describe('Message queue provider type'), - - /** - * Broker configuration - */ - brokerConfig: z.object({ - brokers: z.array(z.string()).describe('Broker addresses (host:port)'), - clientId: z.string().optional().describe('Client ID'), - connectionTimeoutMs: z.number().min(1000).optional().default(30000).describe('Connection timeout in ms'), - requestTimeoutMs: z.number().min(1000).optional().default(30000).describe('Request timeout in ms'), - }).describe('Broker connection configuration'), - - /** - * Topics/queues to sync - */ - topics: z.array(TopicQueueSchema).describe('Topics/queues to sync'), - - /** - * Delivery guarantee - */ - deliveryGuarantee: DeliveryGuaranteeSchema.optional().default('at_least_once'), - - /** - * SSL/TLS configuration - */ - sslConfig: z.object({ - enabled: z.boolean().optional().default(false).describe('Enable SSL/TLS'), - rejectUnauthorized: z.boolean().optional().default(true).describe('Reject unauthorized certificates'), - ca: z.string().optional().describe('CA certificate'), - cert: z.string().optional().describe('Client certificate'), - key: z.string().optional().describe('Client private key'), - }).optional().describe('SSL/TLS configuration'), - - /** - * SASL authentication (for Kafka) - */ - saslConfig: z.object({ - mechanism: z.enum(['plain', 'scram-sha-256', 'scram-sha-512', 'aws']).describe('SASL mechanism'), - username: z.string().optional().describe('SASL username'), - password: z.string().optional().describe('SASL password'), - }).optional().describe('SASL authentication configuration'), - - /** - * Schema registry configuration (for Kafka/Avro) - */ - schemaRegistry: z.object({ - url: z.string().url().describe('Schema registry URL'), - auth: z.object({ - username: z.string().optional(), - password: z.string().optional(), - }).optional(), - }).optional().describe('Schema registry configuration'), - - /** - * Message ordering - */ - preserveOrder: z.boolean().optional().default(true).describe('Preserve message ordering'), - - /** - * Enable metrics - */ - enableMetrics: z.boolean().optional().default(true).describe('Enable message queue metrics'), - - /** - * Enable distributed tracing - */ - enableTracing: z.boolean().optional().default(false).describe('Enable distributed tracing'), -})); - -export type MessageQueueConnector = z.infer; - -// ============================================================================ -// Helper Functions & Examples -// ============================================================================ - -/** - * Example: Apache Kafka Connector Configuration - */ -export const kafkaConnectorExample = { - name: 'kafka_production', - label: 'Production Kafka Cluster', - type: 'message_queue', - provider: 'kafka', - authentication: { - type: 'none', - }, - brokerConfig: { - brokers: ['kafka-1.example.com:9092', 'kafka-2.example.com:9092', 'kafka-3.example.com:9092'], - clientId: 'objectstack-client', - connectionTimeoutMs: 30000, - requestTimeoutMs: 30000, - }, - topics: [ - { - name: 'order_events', - label: 'Order Events', - topicName: 'orders', - enabled: true, - mode: 'consumer', - messageFormat: 'json', - partitions: 10, - replicationFactor: 3, - consumerConfig: { - enabled: true, - consumerGroup: 'objectstack-consumer-group', - concurrency: 5, - prefetchCount: 100, - ackMode: 'manual', - autoCommit: false, - sessionTimeoutMs: 30000, - }, - dlqConfig: { - enabled: true, - queueName: 'orders-dlq', - maxRetries: 3, - retryDelayMs: 60000, - }, - }, - { - name: 'user_activity', - label: 'User Activity', - topicName: 'user-activity', - enabled: true, - mode: 'producer', - messageFormat: 'json', - partitions: 5, - replicationFactor: 3, - producerConfig: { - enabled: true, - acks: 'all', - compressionType: 'snappy', - batchSize: 16384, - lingerMs: 10, - maxInFlightRequests: 5, - idempotence: true, - }, - }, - ], - deliveryGuarantee: 'at_least_once', - saslConfig: { - mechanism: 'scram-sha-256', - username: '${KAFKA_USERNAME}', - password: '${KAFKA_PASSWORD}', - }, - sslConfig: { - enabled: true, - rejectUnauthorized: true, - }, - preserveOrder: true, - enableMetrics: true, - enableTracing: true, - status: 'active', - enabled: true, -}; - -/** - * Example: RabbitMQ Connector Configuration - */ -export const rabbitmqConnectorExample = { - name: 'rabbitmq_events', - label: 'RabbitMQ Event Bus', - type: 'message_queue', - provider: 'rabbitmq', - authentication: { - type: 'basic', - username: '${RABBITMQ_USERNAME}', - password: '${RABBITMQ_PASSWORD}', - }, - brokerConfig: { - brokers: ['amqp://rabbitmq.example.com:5672'], - clientId: 'objectstack-rabbitmq-client', - }, - topics: [ - { - name: 'notifications', - label: 'Notifications', - topicName: 'notifications', - enabled: true, - mode: 'both', - messageFormat: 'json', - routingKey: 'notification.*', - consumerConfig: { - enabled: true, - prefetchCount: 10, - ackMode: 'manual', - }, - producerConfig: { - enabled: true, - }, - dlqConfig: { - enabled: true, - queueName: 'notifications-dlq', - maxRetries: 3, - retryDelayMs: 30000, - }, - }, - ], - deliveryGuarantee: 'at_least_once', - status: 'active', - enabled: true, -}; - -/** - * Example: AWS SQS Connector Configuration - */ -export const sqsConnectorExample = { - name: 'aws_sqs_queue', - label: 'AWS SQS Queue', - type: 'message_queue', - provider: 'aws_sqs', - authentication: { - type: 'api_key', - apiKey: '${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}', - headerName: 'Authorization', - }, - brokerConfig: { - brokers: ['https://sqs.us-east-1.amazonaws.com'], - }, - topics: [ - { - name: 'task_queue', - label: 'Task Queue', - topicName: 'task-queue', - enabled: true, - mode: 'consumer', - messageFormat: 'json', - consumerConfig: { - enabled: true, - concurrency: 10, - prefetchCount: 10, - ackMode: 'manual', - }, - dlqConfig: { - enabled: true, - queueName: 'task-queue-dlq', - maxRetries: 3, - retryDelayMs: 120000, - }, - }, - ], - deliveryGuarantee: 'at_least_once', - retryConfig: { - strategy: 'exponential_backoff', - maxAttempts: 3, - initialDelayMs: 1000, - maxDelayMs: 60000, - backoffMultiplier: 2, - }, - status: 'active', - enabled: true, -}; - -/** - * Example: Google Cloud Pub/Sub Connector Configuration - */ -export const pubsubConnectorExample = { - name: 'gcp_pubsub', - label: 'Google Cloud Pub/Sub', - type: 'message_queue', - provider: 'google_pubsub', - authentication: { - type: 'oauth2', - clientId: '${GCP_CLIENT_ID}', - clientSecret: '${GCP_CLIENT_SECRET}', - authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth', - tokenUrl: 'https://oauth2.googleapis.com/token', - grantType: 'client_credentials', - scopes: ['https://www.googleapis.com/auth/pubsub'], - }, - brokerConfig: { - brokers: ['pubsub.googleapis.com'], - }, - topics: [ - { - name: 'analytics_events', - label: 'Analytics Events', - topicName: 'projects/my-project/topics/analytics-events', - enabled: true, - mode: 'both', - messageFormat: 'json', - consumerConfig: { - enabled: true, - consumerGroup: 'objectstack-subscription', - concurrency: 5, - prefetchCount: 100, - ackMode: 'manual', - }, - }, - ], - deliveryGuarantee: 'at_least_once', - enableMetrics: true, - status: 'active', - enabled: true, -}; diff --git a/packages/spec/src/integration/connector/saas.test.ts b/packages/spec/src/integration/connector/saas.test.ts deleted file mode 100644 index c0be38fd3e..0000000000 --- a/packages/spec/src/integration/connector/saas.test.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - SaasProviderSchema, - ApiVersionConfigSchema, - SaasObjectTypeSchema, - SaasConnectorSchema, -} from './saas.zod'; - -const baseAuth = { type: 'none' as const }; - -const minimalObjectType = { - name: 'account', - label: 'Account', - apiName: 'Account', -}; - -const minimalConnector = { - name: 'sf_prod', - label: 'Salesforce Prod', - type: 'saas' as const, - provider: 'salesforce' as const, - authentication: baseAuth, - baseUrl: 'https://api.example.com', - objectTypes: [minimalObjectType], -}; - -describe('SaasProviderSchema', () => { - it('should accept all valid providers', () => { - const providers = ['salesforce', 'hubspot', 'stripe', 'shopify', 'zendesk', 'intercom', 'mailchimp', 'slack', 'microsoft_dynamics', 'servicenow', 'netsuite', 'custom']; - for (const p of providers) { - expect(SaasProviderSchema.parse(p)).toBe(p); - } - }); - - it('should reject invalid provider', () => { - expect(() => SaasProviderSchema.parse('quickbooks')).toThrow(); - }); -}); - -describe('ApiVersionConfigSchema', () => { - it('should accept valid config', () => { - const result = ApiVersionConfigSchema.parse({ version: 'v59.0' }); - expect(result.version).toBe('v59.0'); - expect(result.isDefault).toBe(false); - }); - - it('should accept full config', () => { - const data = { - version: '2023-10-01', - isDefault: true, - deprecationDate: '2024-01-01', - sunsetDate: '2024-06-01', - }; - const result = ApiVersionConfigSchema.parse(data); - expect(result.isDefault).toBe(true); - expect(result.deprecationDate).toBe('2024-01-01'); - }); - - it('should reject missing version', () => { - expect(() => ApiVersionConfigSchema.parse({})).toThrow(); - }); -}); - -describe('SaasObjectTypeSchema', () => { - it('should accept minimal object type', () => { - const result = SaasObjectTypeSchema.parse(minimalObjectType); - expect(result.enabled).toBe(true); - expect(result.supportsCreate).toBe(true); - expect(result.supportsUpdate).toBe(true); - expect(result.supportsDelete).toBe(true); - }); - - it('should accept object type with all fields', () => { - const data = { - ...minimalObjectType, - enabled: false, - supportsCreate: false, - supportsUpdate: false, - supportsDelete: false, - fieldMappings: [{ source: 'Name', target: 'name' }], - }; - expect(() => SaasObjectTypeSchema.parse(data)).not.toThrow(); - }); - - it('should reject non-snake_case name', () => { - expect(() => SaasObjectTypeSchema.parse({ ...minimalObjectType, name: 'Account' })).toThrow(); - }); - - it('should reject missing required fields', () => { - expect(() => SaasObjectTypeSchema.parse({ name: 'acct' })).toThrow(); - }); -}); - -describe('SaasConnectorSchema', () => { - it('should accept minimal valid connector', () => { - expect(() => SaasConnectorSchema.parse(minimalConnector)).not.toThrow(); - }); - - it('should apply defaults', () => { - const result = SaasConnectorSchema.parse(minimalConnector); - expect(result.enabled).toBe(true); - expect(result.status).toBe('inactive'); - }); - - it('should accept full connector', () => { - const full = { - ...minimalConnector, - apiVersion: { version: 'v59.0', isDefault: true }, - oauthSettings: { - scopes: ['api', 'refresh_token'], - refreshTokenUrl: 'https://login.example.com/token', - revokeTokenUrl: 'https://login.example.com/revoke', - autoRefresh: true, - }, - paginationConfig: { - type: 'cursor', - defaultPageSize: 50, - maxPageSize: 500, - }, - sandboxConfig: { - enabled: true, - baseUrl: 'https://sandbox.example.com', - }, - customHeaders: { 'X-Custom': 'value' }, - }; - expect(() => SaasConnectorSchema.parse(full)).not.toThrow(); - }); - - it('should reject wrong type literal', () => { - expect(() => SaasConnectorSchema.parse({ ...minimalConnector, type: 'database' })).toThrow(); - }); - - it('should reject invalid baseUrl', () => { - expect(() => SaasConnectorSchema.parse({ ...minimalConnector, baseUrl: 'not-a-url' })).toThrow(); - }); - - it('should reject invalid provider', () => { - expect(() => SaasConnectorSchema.parse({ ...minimalConnector, provider: 'unknown' })).toThrow(); - }); - - it('should reject missing objectTypes', () => { - const { objectTypes: _, ...noTypes } = minimalConnector; - expect(() => SaasConnectorSchema.parse(noTypes)).toThrow(); - }); - - it('should reject missing baseUrl', () => { - const { baseUrl: _, ...noUrl } = minimalConnector; - expect(() => SaasConnectorSchema.parse(noUrl)).toThrow(); - }); - - it('should reject invalid paginationConfig', () => { - expect(() => SaasConnectorSchema.parse({ - ...minimalConnector, - paginationConfig: { type: 'cursor', defaultPageSize: 0 }, - })).toThrow(); - }); - - it('should reject invalid oauthSettings URLs', () => { - expect(() => SaasConnectorSchema.parse({ - ...minimalConnector, - oauthSettings: { scopes: ['api'], refreshTokenUrl: 'not-a-url' }, - })).toThrow(); - }); -}); diff --git a/packages/spec/src/integration/connector/saas.zod.ts b/packages/spec/src/integration/connector/saas.zod.ts deleted file mode 100644 index 2aaac3a5fa..0000000000 --- a/packages/spec/src/integration/connector/saas.zod.ts +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { z } from 'zod'; -import { - ConnectorSchema, - FieldMappingSchema, -} from '../connector.zod'; - -/** - * SaaS Connector Protocol Template - * - * Specialized connector for SaaS applications (Salesforce, HubSpot, Stripe, etc.) - * Extends the base connector with SaaS-specific features like OAuth flows, - * object type discovery, and API version management. - */ - -/** - * SaaS Provider Types - */ -import { lazySchema } from '../../shared/lazy-schema'; -export const SaasProviderSchema = lazySchema(() => z.enum([ - 'salesforce', - 'hubspot', - 'stripe', - 'shopify', - 'zendesk', - 'intercom', - 'mailchimp', - 'slack', - 'microsoft_dynamics', - 'servicenow', - 'netsuite', - 'custom', -]).describe('SaaS provider type')); - -export type SaasProvider = z.infer; - -/** - * API Version Configuration - */ -export const ApiVersionConfigSchema = lazySchema(() => z.object({ - version: z.string().describe('API version (e.g., "v2", "2023-10-01")'), - isDefault: z.boolean().default(false).describe('Is this the default version'), - deprecationDate: z.string().optional().describe('API version deprecation date (ISO 8601)'), - sunsetDate: z.string().optional().describe('API version sunset date (ISO 8601)'), -})); - -export type ApiVersionConfig = z.infer; - -/** - * SaaS Object Type Schema - * Represents a syncable entity in the SaaS system (e.g., Account, Contact, Deal) - */ -export const SaasObjectTypeSchema = lazySchema(() => z.object({ - name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Object type name (snake_case)'), - label: z.string().describe('Display label'), - apiName: z.string().describe('API name in external system'), - enabled: z.boolean().default(true).describe('Enable sync for this object'), - supportsCreate: z.boolean().default(true).describe('Supports record creation'), - supportsUpdate: z.boolean().default(true).describe('Supports record updates'), - supportsDelete: z.boolean().default(true).describe('Supports record deletion'), - fieldMappings: z.array(FieldMappingSchema).optional().describe('Object-specific field mappings'), -})); - -export type SaasObjectType = z.infer; - -/** - * SaaS Connector Configuration Schema - */ -export const SaasConnectorSchema = lazySchema(() => ConnectorSchema.extend({ - type: z.literal('saas'), - - /** - * SaaS provider - */ - provider: SaasProviderSchema.describe('SaaS provider type'), - - /** - * Base URL for API requests - */ - baseUrl: z.string().url().describe('API base URL'), - - /** - * API version configuration - */ - apiVersion: ApiVersionConfigSchema.optional().describe('API version configuration'), - - /** - * Supported object types to sync - */ - objectTypes: z.array(SaasObjectTypeSchema).describe('Syncable object types'), - - /** - * OAuth-specific settings - */ - oauthSettings: z.object({ - scopes: z.array(z.string()).describe('Required OAuth scopes'), - refreshTokenUrl: z.string().url().optional().describe('Token refresh endpoint'), - revokeTokenUrl: z.string().url().optional().describe('Token revocation endpoint'), - autoRefresh: z.boolean().default(true).describe('Automatically refresh expired tokens'), - }).optional().describe('OAuth-specific configuration'), - - /** - * Pagination settings - */ - paginationConfig: z.object({ - type: z.enum(['cursor', 'offset', 'page']).default('cursor').describe('Pagination type'), - defaultPageSize: z.number().min(1).max(1000).default(100).describe('Default page size'), - maxPageSize: z.number().min(1).max(10000).default(1000).describe('Maximum page size'), - }).optional().describe('Pagination configuration'), - - /** - * Sandbox/test environment settings - */ - sandboxConfig: z.object({ - enabled: z.boolean().default(false).describe('Use sandbox environment'), - baseUrl: z.string().url().optional().describe('Sandbox API base URL'), - }).optional().describe('Sandbox environment configuration'), - - /** - * Custom request headers - */ - customHeaders: z.record(z.string(), z.string()).optional().describe('Custom HTTP headers for all requests'), -})); - -export type SaasConnector = z.infer; -export type SaaSConnector = SaasConnector; // Alias for alternative capitalization - -// ============================================================================ -// Helper Functions & Examples -// ============================================================================ - -/** - * Example: Salesforce Connector Configuration - */ -export const salesforceConnectorExample = { - name: 'salesforce_production', - label: 'Salesforce Production', - type: 'saas', - provider: 'salesforce', - baseUrl: 'https://example.my.salesforce.com', - apiVersion: { - version: 'v59.0', - isDefault: true, - }, - authentication: { - type: 'oauth2', - clientId: '${SALESFORCE_CLIENT_ID}', - clientSecret: '${SALESFORCE_CLIENT_SECRET}', - authorizationUrl: 'https://login.salesforce.com/services/oauth2/authorize', - tokenUrl: 'https://login.salesforce.com/services/oauth2/token', - grantType: 'authorization_code', - scopes: ['api', 'refresh_token', 'offline_access'], - }, - objectTypes: [ - { - name: 'account', - label: 'Account', - apiName: 'Account', - enabled: true, - supportsCreate: true, - supportsUpdate: true, - supportsDelete: true, - }, - { - name: 'contact', - label: 'Contact', - apiName: 'Contact', - enabled: true, - supportsCreate: true, - supportsUpdate: true, - supportsDelete: true, - }, - ], - syncConfig: { - strategy: 'incremental', - direction: 'bidirectional', - schedule: '0 */6 * * *', // Every 6 hours - realtimeSync: true, - conflictResolution: 'latest_wins', - batchSize: 200, - deleteMode: 'soft_delete', - }, - rateLimitConfig: { - strategy: 'token_bucket', - maxRequests: 100, - windowSeconds: 20, - respectUpstreamLimits: true, - }, - retryConfig: { - strategy: 'exponential_backoff', - maxAttempts: 3, - initialDelayMs: 1000, - maxDelayMs: 30000, - backoffMultiplier: 2, - retryableStatusCodes: [408, 429, 500, 502, 503, 504], - retryOnNetworkError: true, - jitter: true, - }, - status: 'active', - enabled: true, -}; - -/** - * Example: HubSpot Connector Configuration - */ -export const hubspotConnectorExample = { - name: 'hubspot_crm', - label: 'HubSpot CRM', - type: 'saas', - provider: 'hubspot', - baseUrl: 'https://api.hubapi.com', - authentication: { - type: 'api_key', - apiKey: '${HUBSPOT_API_KEY}', - headerName: 'Authorization', - }, - objectTypes: [ - { - name: 'company', - label: 'Company', - apiName: 'companies', - enabled: true, - supportsCreate: true, - supportsUpdate: true, - supportsDelete: true, - }, - { - name: 'deal', - label: 'Deal', - apiName: 'deals', - enabled: true, - supportsCreate: true, - supportsUpdate: true, - supportsDelete: true, - }, - ], - syncConfig: { - strategy: 'incremental', - direction: 'import', - schedule: '0 */4 * * *', // Every 4 hours - conflictResolution: 'source_wins', - batchSize: 100, - }, - rateLimitConfig: { - strategy: 'token_bucket', - maxRequests: 100, - windowSeconds: 10, - }, - status: 'active', - enabled: true, -}; diff --git a/packages/spec/src/integration/connector/vercel.test.ts b/packages/spec/src/integration/connector/vercel.test.ts deleted file mode 100644 index 43bc7b7bd2..0000000000 --- a/packages/spec/src/integration/connector/vercel.test.ts +++ /dev/null @@ -1,416 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { - VercelConnectorSchema, - VercelProjectSchema, - GitRepositoryConfigSchema, - BuildConfigSchema, - DeploymentConfigSchema, - DomainConfigSchema, - EnvironmentVariablesSchema, - EdgeFunctionConfigSchema, - vercelNextJsConnectorExample, - vercelStaticSiteConnectorExample, - type VercelConnector, -} from './vercel.zod'; - -describe('GitRepositoryConfigSchema', () => { - it('should accept minimal git repo config', () => { - const config = { - type: 'github' as const, - repo: 'owner/repo', - }; - - const result = GitRepositoryConfigSchema.parse(config); - expect(result.productionBranch).toBe('main'); - expect(result.autoDeployProduction).toBe(true); - expect(result.autoDeployPreview).toBe(true); - }); - - it('should accept all git provider types', () => { - const providers = ['github', 'gitlab', 'bitbucket'] as const; - - providers.forEach(type => { - const config = { type, repo: 'owner/repo' }; - expect(() => GitRepositoryConfigSchema.parse(config)).not.toThrow(); - }); - }); -}); - -describe('BuildConfigSchema', () => { - it('should accept empty build config', () => { - const config = {}; - expect(() => BuildConfigSchema.parse(config)).not.toThrow(); - }); - - it('should accept full build config', () => { - const config = { - buildCommand: 'npm run build', - outputDirectory: '.next', - installCommand: 'npm ci', - devCommand: 'npm run dev', - nodeVersion: '20.x', - env: { - NODE_ENV: 'production', - API_URL: 'https://api.example.com', - }, - }; - - expect(() => BuildConfigSchema.parse(config)).not.toThrow(); - }); -}); - -describe('DeploymentConfigSchema', () => { - it('should accept minimal deployment config', () => { - const config = {}; - - const result = DeploymentConfigSchema.parse(config); - expect(result.autoDeployment).toBe(true); - expect(result.enablePreview).toBe(true); - expect(result.previewComments).toBe(true); - }); - - it('should accept deployment with regions', () => { - const config = { - regions: ['iad1', 'sfo1', 'fra1'], - }; - - expect(() => DeploymentConfigSchema.parse(config)).not.toThrow(); - }); - - it('should accept deployment with deploy hooks', () => { - const config = { - deployHooks: [ - { - name: 'main-deploy', - url: 'https://api.vercel.com/v1/integrations/deploy/xxx', - branch: 'main', - }, - ], - }; - - expect(() => DeploymentConfigSchema.parse(config)).not.toThrow(); - }); -}); - -describe('DomainConfigSchema', () => { - it('should accept minimal domain config', () => { - const config = { - domain: 'app.example.com', - }; - - const result = DomainConfigSchema.parse(config); - expect(result.httpsRedirect).toBe(true); - }); - - it('should accept domain with custom SSL', () => { - const config = { - domain: 'secure.example.com', - customCertificate: { - cert: '-----BEGIN CERTIFICATE-----', - key: '-----BEGIN PRIVATE KEY-----', - ca: '-----BEGIN CERTIFICATE-----', - }, - }; - - expect(() => DomainConfigSchema.parse(config)).not.toThrow(); - }); -}); - -describe('EnvironmentVariablesSchema', () => { - it('should accept environment variable', () => { - const envVar = { - key: 'API_KEY', - value: 'secret-value', - target: ['production'] as const, - }; - - const result = EnvironmentVariablesSchema.parse(envVar); - expect(result.isSecret).toBe(false); - }); - - it('should accept secret environment variable', () => { - const envVar = { - key: 'DATABASE_URL', - value: 'postgresql://...', - target: ['production', 'preview'] as const, - isSecret: true, - }; - - expect(() => EnvironmentVariablesSchema.parse(envVar)).not.toThrow(); - }); - - it('should accept all target environments', () => { - const targets = ['production', 'preview', 'development'] as const; - - targets.forEach(target => { - const envVar = { - key: 'TEST', - value: 'value', - target: [target], - }; - expect(() => EnvironmentVariablesSchema.parse(envVar)).not.toThrow(); - }); - }); -}); - -describe('EdgeFunctionConfigSchema', () => { - it('should accept minimal edge function', () => { - const func = { - name: 'api-handler', - path: '/api/*', - }; - - const result = EdgeFunctionConfigSchema.parse(func); - expect(result.memoryLimit).toBe(1024); - expect(result.timeout).toBe(10); - }); - - it('should accept edge function with custom limits', () => { - const func = { - name: 'heavy-processor', - path: '/api/process', - regions: ['iad1', 'sfo1'], - memoryLimit: 3008, - timeout: 60, - }; - - expect(() => EdgeFunctionConfigSchema.parse(func)).not.toThrow(); - }); - - it('should enforce memory limits', () => { - expect(() => EdgeFunctionConfigSchema.parse({ - name: 'test', - path: '/test', - memoryLimit: 100, // Too low - })).toThrow(); - - expect(() => EdgeFunctionConfigSchema.parse({ - name: 'test', - path: '/test', - memoryLimit: 5000, // Too high - })).toThrow(); - }); - - it('should enforce timeout limits', () => { - expect(() => EdgeFunctionConfigSchema.parse({ - name: 'test', - path: '/test', - timeout: 0, // Too low - })).toThrow(); - - expect(() => EdgeFunctionConfigSchema.parse({ - name: 'test', - path: '/test', - timeout: 400, // Too high - })).toThrow(); - }); -}); - -describe('VercelProjectSchema', () => { - it('should accept minimal project', () => { - const project = { - name: 'my-app', - }; - - expect(() => VercelProjectSchema.parse(project)).not.toThrow(); - }); - - it('should accept all framework types', () => { - const frameworks = ['nextjs', 'react', 'vue', 'nuxtjs', 'gatsby', 'remix', 'astro', 'sveltekit', 'solid', 'angular', 'static', 'other'] as const; - - frameworks.forEach(framework => { - const project = { - name: 'test-app', - framework, - }; - expect(() => VercelProjectSchema.parse(project)).not.toThrow(); - }); - }); - - it('should accept full project configuration', () => { - const project = { - name: 'full-app', - framework: 'nextjs' as const, - gitRepository: { - type: 'github' as const, - repo: 'owner/repo', - productionBranch: 'main', - }, - buildConfig: { - buildCommand: 'npm run build', - outputDirectory: '.next', - }, - deploymentConfig: { - regions: ['iad1', 'sfo1'], - enablePreview: true, - }, - domains: [ - { domain: 'app.example.com' }, - ], - environmentVariables: [ - { - key: 'API_KEY', - value: 'test', - target: ['production'] as const, - }, - ], - edgeFunctions: [ - { - name: 'api', - path: '/api/*', - }, - ], - rootDirectory: 'apps/web', - }; - - expect(() => VercelProjectSchema.parse(project)).not.toThrow(); - }); -}); - -describe('VercelConnectorSchema', () => { - describe('Basic Properties', () => { - it('should accept minimal Vercel connector', () => { - const connector: VercelConnector = { - name: 'vercel_test', - label: 'Vercel Test', - type: 'saas', - provider: 'vercel', - authentication: { - type: 'bearer', - token: 'test-token', - }, - projects: [ - { - name: 'test-project', - }, - ], - }; - - const result = VercelConnectorSchema.parse(connector); - expect(result.baseUrl).toBe('https://api.vercel.com'); - expect(result.enableWebhooks).toBe(true); - }); - - it('should enforce snake_case for connector name', () => { - const validNames = ['vercel_test', 'vercel_production', '_internal']; - validNames.forEach(name => { - expect(() => VercelConnectorSchema.parse({ - name, - label: 'Test', - type: 'saas', - provider: 'vercel', - authentication: { type: 'bearer', token: 'x' }, - projects: [{ name: 'test' }], - })).not.toThrow(); - }); - - const invalidNames = ['vercelTest', 'Vercel-Test', '123vercel']; - invalidNames.forEach(name => { - expect(() => VercelConnectorSchema.parse({ - name, - label: 'Test', - type: 'saas', - provider: 'vercel', - authentication: { type: 'bearer', token: 'x' }, - projects: [{ name: 'test' }], - })).toThrow(); - }); - }); - }); - - describe('Team Configuration', () => { - it('should accept team configuration', () => { - const connector: VercelConnector = { - name: 'vercel_team', - label: 'Vercel Team', - type: 'saas', - provider: 'vercel', - authentication: { - type: 'bearer', - token: 'test-token', - }, - team: { - teamId: 'team_xxx', - teamName: 'My Team', - }, - projects: [ - { - name: 'team-project', - }, - ], - }; - - expect(() => VercelConnectorSchema.parse(connector)).not.toThrow(); - }); - }); - - describe('Monitoring Configuration', () => { - it('should accept monitoring configuration', () => { - const connector: VercelConnector = { - name: 'vercel_monitored', - label: 'Vercel Monitored', - type: 'saas', - provider: 'vercel', - authentication: { - type: 'bearer', - token: 'test-token', - }, - projects: [{ name: 'test' }], - monitoring: { - enableWebAnalytics: true, - enableSpeedInsights: true, - logDrains: [ - { - name: 'datadog', - url: 'https://logs.datadoghq.com', - headers: { 'DD-API-KEY': 'xxx' }, - sources: ['lambda', 'edge'], - }, - ], - }, - }; - - expect(() => VercelConnectorSchema.parse(connector)).not.toThrow(); - }); - }); - - describe('Webhook Configuration', () => { - it('should accept webhook events', () => { - const events = [ - 'deployment.created', - 'deployment.succeeded', - 'deployment.failed', - 'deployment.ready', - 'deployment.error', - 'deployment.canceled', - 'deployment-checks-completed', - 'deployment-prepared', - 'project.created', - 'project.removed', - ] as const; - - const connector: VercelConnector = { - name: 'vercel_webhooks', - label: 'Vercel Webhooks', - type: 'saas', - provider: 'vercel', - authentication: { type: 'bearer', token: 'x' }, - projects: [{ name: 'test' }], - enableWebhooks: true, - webhookEvents: [...events], - }; - - expect(() => VercelConnectorSchema.parse(connector)).not.toThrow(); - }); - }); - - describe('Example Configurations', () => { - it('should accept Next.js connector example', () => { - expect(() => VercelConnectorSchema.parse(vercelNextJsConnectorExample)).not.toThrow(); - }); - - it('should accept static site connector example', () => { - expect(() => VercelConnectorSchema.parse(vercelStaticSiteConnectorExample)).not.toThrow(); - }); - }); -}); diff --git a/packages/spec/src/integration/connector/vercel.zod.ts b/packages/spec/src/integration/connector/vercel.zod.ts deleted file mode 100644 index 7b0c80133b..0000000000 --- a/packages/spec/src/integration/connector/vercel.zod.ts +++ /dev/null @@ -1,645 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { z } from 'zod'; -import { - ConnectorSchema, -} from '../connector.zod'; - -/** - * Vercel Connector Protocol - * - * Specialized connector for Vercel deployment platform enabling automated - * deployments, preview environments, and production releases. - * - * Use Cases: - * - Automated deployments from Git - * - Preview deployments for pull requests - * - Production releases - * - Environment variable management - * - Domain and SSL configuration - * - Edge function deployment - * - * @example - * ```typescript - * import { VercelConnector } from '@objectstack/spec/integration'; - * - * const vercelConnector: VercelConnector = { - * name: 'vercel_production', - * label: 'Vercel Production', - * type: 'saas', - * provider: 'vercel', - * baseUrl: 'https://api.vercel.com', - * authentication: { - * type: 'bearer', - * token: '${VERCEL_TOKEN}', - * }, - * projects: [ - * { - * name: 'objectstack-app', - * framework: 'nextjs', - * gitRepository: { - * type: 'github', - * repo: 'objectstack-ai/app', - * }, - * }, - * ], - * }; - * ``` - */ - -/** - * Vercel Provider Type - */ -import { lazySchema } from '../../shared/lazy-schema'; -export const VercelProviderSchema = lazySchema(() => z.enum([ - 'vercel', -]).describe('Vercel provider type')); - -export type VercelProvider = z.infer; - -/** - * Vercel Framework Types - */ -export const VercelFrameworkSchema = lazySchema(() => z.enum([ - 'nextjs', - 'react', - 'vue', - 'nuxtjs', - 'gatsby', - 'remix', - 'astro', - 'sveltekit', - 'solid', - 'angular', - 'static', - 'other', -]).describe('Frontend framework')); - -export type VercelFramework = z.infer; - -/** - * Git Repository Configuration - */ -export const GitRepositoryConfigSchema = lazySchema(() => z.object({ - /** - * Git provider type - */ - type: z.enum(['github', 'gitlab', 'bitbucket']).describe('Git provider'), - - /** - * Repository identifier (owner/repo) - */ - repo: z.string().describe('Repository identifier (e.g., owner/repo)'), - - /** - * Production branch - */ - productionBranch: z.string().optional().default('main').describe('Production branch name'), - - /** - * Auto-deploy production branch - */ - autoDeployProduction: z.boolean().optional().default(true).describe('Auto-deploy production branch'), - - /** - * Auto-deploy preview branches - */ - autoDeployPreview: z.boolean().optional().default(true).describe('Auto-deploy preview branches'), -})); - -export type GitRepositoryConfig = z.infer; - -/** - * Build Configuration - */ -export const BuildConfigSchema = lazySchema(() => z.object({ - /** - * Build command - */ - buildCommand: z.string().optional().describe('Build command (e.g., npm run build)'), - - /** - * Output directory - */ - outputDirectory: z.string().optional().describe('Output directory (e.g., .next, dist)'), - - /** - * Install command - */ - installCommand: z.string().optional().describe('Install command (e.g., npm install, pnpm install)'), - - /** - * Development command - */ - devCommand: z.string().optional().describe('Development command (e.g., npm run dev)'), - - /** - * Node.js version - */ - nodeVersion: z.string().optional().describe('Node.js version (e.g., 18.x, 20.x)'), - - /** - * Environment variables - */ - env: z.record(z.string(), z.string()).optional().describe('Build environment variables'), -})); - -export type BuildConfig = z.infer; - -/** - * Deployment Configuration - */ -export const DeploymentConfigSchema = lazySchema(() => z.object({ - /** - * Enable automatic deployments - */ - autoDeployment: z.boolean().optional().default(true).describe('Enable automatic deployments'), - - /** - * Deployment regions - */ - regions: z.array(z.enum([ - 'iad1', // US East (Washington, D.C.) - 'sfo1', // US West (San Francisco) - 'gru1', // South America (São Paulo) - 'lhr1', // Europe West (London) - 'fra1', // Europe Central (Frankfurt) - 'sin1', // Asia (Singapore) - 'syd1', // Australia (Sydney) - 'hnd1', // Asia (Tokyo) - 'icn1', // Asia (Seoul) - ])).optional().describe('Deployment regions'), - - /** - * Enable preview deployments - */ - enablePreview: z.boolean().optional().default(true).describe('Enable preview deployments'), - - /** - * Preview deployment comments on PRs - */ - previewComments: z.boolean().optional().default(true).describe('Post preview URLs in PR comments'), - - /** - * Production deployment protection - */ - productionProtection: z.boolean().optional().default(true).describe('Require approval for production deployments'), - - /** - * Deploy hooks - */ - deployHooks: z.array(z.object({ - name: z.string().describe('Hook name'), - url: z.string().url().describe('Deploy hook URL'), - branch: z.string().optional().describe('Target branch'), - })).optional().describe('Deploy hooks'), -})); - -export type DeploymentConfig = z.infer; - -/** - * Domain Configuration - */ -export const DomainConfigSchema = lazySchema(() => z.object({ - /** - * Domain name - */ - domain: z.string().describe('Domain name (e.g., app.example.com)'), - - /** - * Enable HTTPS redirect - */ - httpsRedirect: z.boolean().optional().default(true).describe('Redirect HTTP to HTTPS'), - - /** - * Custom SSL certificate - */ - customCertificate: z.object({ - cert: z.string().describe('SSL certificate'), - key: z.string().describe('Private key'), - ca: z.string().optional().describe('Certificate authority'), - }).optional().describe('Custom SSL certificate'), - - /** - * Git branch for this domain - */ - gitBranch: z.string().optional().describe('Git branch to deploy to this domain'), -})); - -export type DomainConfig = z.infer; - -/** - * Environment Variables Configuration - */ -export const EnvironmentVariablesSchema = lazySchema(() => z.object({ - /** - * Variable name - */ - key: z.string().describe('Environment variable name'), - - /** - * Variable value - */ - value: z.string().describe('Environment variable value'), - - /** - * Target environments - */ - target: z.array(z.enum(['production', 'preview', 'development'])).describe('Target environments'), - - /** - * Is secret (encrypted) - */ - isSecret: z.boolean().optional().default(false).describe('Encrypt this variable'), - - /** - * Git branch (for preview/development) - */ - gitBranch: z.string().optional().describe('Specific git branch'), -})); - -export type EnvironmentVariables = z.infer; - -/** - * Edge Function Configuration - */ -export const EdgeFunctionConfigSchema = lazySchema(() => z.object({ - /** - * Function name - */ - name: z.string().describe('Edge function name'), - - /** - * Function path - */ - path: z.string().describe('Function path (e.g., /api/*)'), - - /** - * Regions to deploy - */ - regions: z.array(z.string()).optional().describe('Specific regions for this function'), - - /** - * Memory limit (MB) - */ - memoryLimit: z.number().int().min(128).max(3008).optional().default(1024).describe('Memory limit in MB'), - - /** - * Timeout (seconds) - */ - timeout: z.number().int().min(1).max(300).optional().default(10).describe('Timeout in seconds'), -})); - -export type EdgeFunctionConfig = z.infer; - -/** - * Vercel Project Configuration - */ -export const VercelProjectSchema = lazySchema(() => z.object({ - /** - * Project name - */ - name: z.string().describe('Vercel project name'), - - /** - * Framework - */ - framework: VercelFrameworkSchema.optional().describe('Frontend framework'), - - /** - * Git repository - */ - gitRepository: GitRepositoryConfigSchema.optional().describe('Git repository configuration'), - - /** - * Build configuration - */ - buildConfig: BuildConfigSchema.optional().describe('Build configuration'), - - /** - * Deployment configuration - */ - deploymentConfig: DeploymentConfigSchema.optional().describe('Deployment configuration'), - - /** - * Custom domains - */ - domains: z.array(DomainConfigSchema).optional().describe('Custom domains'), - - /** - * Environment variables - */ - environmentVariables: z.array(EnvironmentVariablesSchema).optional().describe('Environment variables'), - - /** - * Edge functions - */ - edgeFunctions: z.array(EdgeFunctionConfigSchema).optional().describe('Edge functions'), - - /** - * Root directory - */ - rootDirectory: z.string().optional().describe('Root directory (for monorepos)'), -})); - -export type VercelProject = z.infer; - -/** - * Vercel Monitoring Configuration - */ -export const VercelMonitoringSchema = lazySchema(() => z.object({ - /** - * Enable Web Analytics - */ - enableWebAnalytics: z.boolean().optional().default(false).describe('Enable Vercel Web Analytics'), - - /** - * Enable Speed Insights - */ - enableSpeedInsights: z.boolean().optional().default(false).describe('Enable Vercel Speed Insights'), - - /** - * Enable Log Drains - */ - logDrains: z.array(z.object({ - name: z.string().describe('Log drain name'), - url: z.string().url().describe('Log drain URL'), - headers: z.record(z.string(), z.string()).optional().describe('Custom headers'), - sources: z.array(z.enum(['static', 'lambda', 'edge'])).optional().describe('Log sources'), - })).optional().describe('Log drains configuration'), -})); - -export type VercelMonitoring = z.infer; - -/** - * Vercel Team Configuration - */ -export const VercelTeamSchema = lazySchema(() => z.object({ - /** - * Team ID or slug - */ - teamId: z.string().optional().describe('Team ID or slug'), - - /** - * Team name - */ - teamName: z.string().optional().describe('Team name'), -})); - -export type VercelTeam = z.infer; - -/** - * Vercel Connector Schema - * Complete Vercel integration configuration - */ -export const VercelConnectorSchema = lazySchema(() => ConnectorSchema.extend({ - type: z.literal('saas'), - - /** - * Vercel provider - */ - provider: VercelProviderSchema.describe('Vercel provider'), - - /** - * Vercel API base URL - */ - baseUrl: z.string().url().optional().default('https://api.vercel.com').describe('Vercel API base URL'), - - /** - * Team configuration - */ - team: VercelTeamSchema.optional().describe('Vercel team configuration'), - - /** - * Projects to manage - */ - projects: z.array(VercelProjectSchema).describe('Vercel projects'), - - /** - * Monitoring configuration - */ - monitoring: VercelMonitoringSchema.optional().describe('Monitoring configuration'), - - /** - * Enable webhooks - */ - enableWebhooks: z.boolean().optional().default(true).describe('Enable Vercel webhooks'), - - /** - * Webhook events to subscribe - */ - webhookEvents: z.array(z.enum([ - 'deployment.created', - 'deployment.succeeded', - 'deployment.failed', - 'deployment.ready', - 'deployment.error', - 'deployment.canceled', - 'deployment-checks-completed', - 'deployment-prepared', - 'project.created', - 'project.removed', - ])).optional().describe('Webhook events to subscribe to'), -})); - -export type VercelConnector = z.infer; - -// ============================================================================ -// Helper Functions & Examples -// ============================================================================ - -/** - * Example: Vercel Next.js Project Configuration - */ -export const vercelNextJsConnectorExample = { - name: 'vercel_production', - label: 'Vercel Production', - type: 'saas', - provider: 'vercel', - baseUrl: 'https://api.vercel.com', - - authentication: { - type: 'bearer', - token: '${VERCEL_TOKEN}', - }, - - projects: [ - { - name: 'objectstack-app', - framework: 'nextjs', - - gitRepository: { - type: 'github', - repo: 'objectstack-ai/app', - productionBranch: 'main', - autoDeployProduction: true, - autoDeployPreview: true, - }, - - buildConfig: { - buildCommand: 'npm run build', - outputDirectory: '.next', - installCommand: 'npm ci', - devCommand: 'npm run dev', - nodeVersion: '20.x', - env: { - NEXT_PUBLIC_API_URL: 'https://api.objectstack.ai', - }, - }, - - deploymentConfig: { - autoDeployment: true, - regions: ['iad1', 'sfo1', 'fra1'], - enablePreview: true, - previewComments: true, - productionProtection: true, - }, - - domains: [ - { - domain: 'app.objectstack.ai', - httpsRedirect: true, - gitBranch: 'main', - }, - { - domain: 'staging.objectstack.ai', - httpsRedirect: true, - gitBranch: 'develop', - }, - ], - - environmentVariables: [ - { - key: 'DATABASE_URL', - value: '${DATABASE_URL}', - target: ['production', 'preview'], - isSecret: true, - }, - { - key: 'NEXT_PUBLIC_ANALYTICS_ID', - value: 'UA-XXXXXXXX-X', - target: ['production'], - isSecret: false, - }, - ], - - edgeFunctions: [ - { - name: 'api-middleware', - path: '/api/*', - regions: ['iad1', 'sfo1'], - memoryLimit: 1024, - timeout: 10, - }, - ], - }, - ], - - monitoring: { - enableWebAnalytics: true, - enableSpeedInsights: true, - logDrains: [ - { - name: 'datadog-logs', - url: 'https://http-intake.logs.datadoghq.com/api/v2/logs', - headers: { - 'DD-API-KEY': '${DATADOG_API_KEY}', - }, - sources: ['lambda', 'edge'], - }, - ], - }, - - enableWebhooks: true, - webhookEvents: [ - 'deployment.succeeded', - 'deployment.failed', - 'deployment.ready', - ], - - status: 'active', - enabled: true, -}; - -/** - * Example: Vercel Static Site Configuration - */ -export const vercelStaticSiteConnectorExample = { - name: 'vercel_docs', - label: 'Vercel Documentation', - type: 'saas', - provider: 'vercel', - baseUrl: 'https://api.vercel.com', - - authentication: { - type: 'bearer', - token: '${VERCEL_TOKEN}', - }, - - team: { - teamId: 'team_xxxxxx', - teamName: 'ObjectStack', - }, - - projects: [ - { - name: 'objectstack-docs', - framework: 'static', - - gitRepository: { - type: 'github', - repo: 'objectstack-ai/docs', - productionBranch: 'main', - autoDeployProduction: true, - autoDeployPreview: true, - }, - - buildConfig: { - buildCommand: 'npm run build', - outputDirectory: 'dist', - installCommand: 'npm ci', - nodeVersion: '18.x', - }, - - deploymentConfig: { - autoDeployment: true, - regions: ['iad1', 'lhr1', 'sin1'], - enablePreview: true, - previewComments: true, - productionProtection: false, - }, - - domains: [ - { - domain: 'docs.objectstack.ai', - httpsRedirect: true, - }, - ], - - environmentVariables: [ - { - key: 'ALGOLIA_APP_ID', - value: '${ALGOLIA_APP_ID}', - target: ['production', 'preview'], - isSecret: false, - }, - { - key: 'ALGOLIA_API_KEY', - value: '${ALGOLIA_API_KEY}', - target: ['production', 'preview'], - isSecret: true, - }, - ], - }, - ], - - monitoring: { - enableWebAnalytics: true, - enableSpeedInsights: false, - }, - - enableWebhooks: false, - - status: 'active', - enabled: true, -}; diff --git a/packages/spec/src/integration/index.ts b/packages/spec/src/integration/index.ts index e6d75ceaf3..8dc614a76f 100644 --- a/packages/spec/src/integration/index.ts +++ b/packages/spec/src/integration/index.ts @@ -2,14 +2,25 @@ /** * Integration Protocol Exports - * - * External System Connection Protocols - * - Connector configurations for SaaS, databases, file storage, message queues - * - GitHub integration (version control, CI/CD) - * - Vercel integration (deployment, hosting) + * + * External System Connection Protocols (ADR-0097) + * - The connector protocol: one `ConnectorSchema`, provider-bound declarative + * instances, and the registry descriptor `GET /automation/connectors` serves * - Authentication methods (OAuth2, API Key, JWT, SAML) * - Data synchronization and field mapping * - Webhooks, rate limiting, and retry strategies + * + * The per-provider "Connector Templates" (`connector/saas.zod.ts`, + * `connector/database.zod.ts`, file-storage, message-queue, github, vercel) + * were removed in #4480. They were the losing side of an architecture decision + * this module's live half already records: ADR-0023 rejected hand-modelling + * each external system's shape inside the spec, and ADR-0097's answer is the + * opposite direction — provider shapes come from the provider itself + * (connector-openapi materializes from an OpenAPI document, connector-mcp from + * an MCP server), while the platform defines only the unified protocol. The + * six files had zero consumers: nothing in this module referenced them, no + * runtime read them, and `engine.registerConnector()` validates against + * `ConnectorSchema` from `./connector.zod` alone. */ // Core Connector Protocol @@ -22,11 +33,3 @@ export * from './connector-provider-errors'; // Connector registry vocabulary — origin/state and the descriptor // `GET /automation/connectors` serves (ADR-0022, ADR-0097 §4, #3017) export * from './connector-descriptor'; - -// Connector Templates -export * from './connector/saas.zod'; -export * from './connector/database.zod'; -export * from './connector/file-storage.zod'; -export * from './connector/message-queue.zod'; -export * from './connector/github.zod'; -export * from './connector/vercel.zod';