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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/example-apps-spec-input-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@objectstack/spec": patch
---

Add `*Input` authoring-type aliases (`DatasourceInput`, `ConnectorInput`, `SharingRuleInput`, `JobInput`, `WebhookInput`, `EmailTemplateDefinitionInput`, `RoleInput`, `PermissionSetInput`, `ObjectExtensionInput`) alongside the existing `FieldInput`/`ActionInput`/`ReportInput`/`PortalInput` convention. These are `z.input<typeof XSchema>` aliases so authored literals keep `.default()` fields optional and accept CEL/Expression string shorthands — matching how `defineX()` helpers already accept input. No runtime change.
13 changes: 12 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,16 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Type check
- name: Type check (@objectstack/spec)
run: pnpm --filter @objectstack/spec exec tsc --noEmit

# Example apps are AI-authoring reference templates; a red typecheck is a
# bad signal to copy from. tsup transpiles them without a full typecheck,
# so build alone will not catch type drift — typecheck them explicitly.
# They import from built workspace packages, so the packages must be built
# first for cross-package type resolution to succeed.
- name: Build workspace packages
run: pnpm exec turbo run build --filter='./packages/*'

- name: Type check example apps
run: pnpm --filter './examples/*' run typecheck
5 changes: 2 additions & 3 deletions examples/app-crm/src/actions/convert-lead.action.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { UI } from '@objectstack/spec';
import type * as UI from '@objectstack/spec/ui';

/**
* Row-level action on crm_lead — launches the Convert Lead screen flow wizard.
* Shown as a button in the lead list row menu and in the lead record header.
*/
export const ConvertLeadAction: UI.Action = {
export const ConvertLeadAction: UI.ActionInput = {
name: 'crm_convert_lead',
label: 'Convert Lead',
description: 'Open the Convert Lead wizard to create an Opportunity from this Lead.',
icon: 'ArrowRightCircle',
objectName: 'crm_lead',
type: 'flow',
Expand Down
5 changes: 2 additions & 3 deletions examples/app-crm/src/actions/park-lead.action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { UI } from '@objectstack/spec';
import type * as UI from '@objectstack/spec/ui';

/**
* Row-level action on crm_lead — reassign the lead's owner.
Expand All @@ -11,10 +11,9 @@ import type { UI } from '@objectstack/spec';
* UndoManager — Ctrl+Z works too). Prompts for the new owner via one param
* (pre-filled with "Triage Queue").
*/
export const ParkLeadAction: UI.Action = {
export const ParkLeadAction: UI.ActionInput = {
name: 'crm_park_lead',
label: 'Reassign Lead',
description: 'Reassign this lead to a new owner (undoable).',
icon: 'UserPlus',
objectName: 'crm_lead',
// `type: 'api'` with a non-URL target routes to the console runtime's generic
Expand Down
1 change: 0 additions & 1 deletion examples/app-crm/src/agents/sales-assistant.agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export const DealManagementSkill = defineSkill({
export const SalesAssistantAgent = defineAgent({
name: 'crm_sales_assistant',
label: 'Sales Assistant',
description: 'AI assistant that helps sales reps manage their pipeline.',
role: 'You are a helpful sales operations assistant for a CRM system.',
instructions:
'Help sales reps find contacts, update opportunities, and summarise their pipeline. Always be concise and ask before making destructive changes.',
Expand Down
2 changes: 1 addition & 1 deletion examples/app-crm/src/analytics/crm.cube.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { Cube } from '@objectstack/spec';
import type { Cube } from '@objectstack/spec/data';

/**
* Opportunity Pipeline Cube — revenue metrics broken down by stage,
Expand Down
2 changes: 1 addition & 1 deletion examples/app-crm/src/api/crm-endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { ApiEndpoint } from '@objectstack/spec';
import type { ApiEndpoint } from '@objectstack/spec/api';

/**
* Custom REST endpoint — exposes pipeline summary metrics.
Expand Down
6 changes: 3 additions & 3 deletions examples/app-crm/src/connectors/crm-connectors.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { Connector } from '@objectstack/spec';
import type { ConnectorInput } from '@objectstack/spec/integration';

/**
* HubSpot connector — sync contacts and deals bi-directionally.
* Uses OAuth2 for authentication; actual credentials come from environment.
*/
export const HubSpotConnector: Connector = {
export const HubSpotConnector: ConnectorInput = {
name: 'hubspot_crm',
label: 'HubSpot CRM',
type: 'saas',
Expand Down Expand Up @@ -91,7 +91,7 @@ export const HubSpotConnector: Connector = {
/**
* Slack connector — post notifications to channels.
*/
export const SlackConnector: Connector = {
export const SlackConnector: ConnectorInput = {
name: 'slack_notifications',
label: 'Slack',
type: 'api',
Expand Down
2 changes: 1 addition & 1 deletion examples/app-crm/src/data/crm-mappings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { Mapping } from '@objectstack/spec';
import type { Mapping } from '@objectstack/spec/data';

/**
* CSV import mapping for bulk lead upload.
Expand Down
6 changes: 3 additions & 3 deletions examples/app-crm/src/datasources/crm.datasource.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { Datasource } from '@objectstack/spec';
import type { DatasourceInput } from '@objectstack/spec/data';

/**
* Primary CRM datasource — in-memory SQLite for the example.
* In production, swap `driver` to 'postgres' and supply real `config`.
*/
export const CrmDatasource: Datasource = {
export const CrmDatasource: DatasourceInput = {
name: 'crm_primary',
label: 'CRM Primary Database',
driver: 'sqlite',
Expand All @@ -23,7 +23,7 @@ export const CrmDatasource: Datasource = {
/**
* Read-replica for analytics queries — demonstrates datasource routing.
*/
export const CrmAnalyticsDatasource: Datasource = {
export const CrmAnalyticsDatasource: DatasourceInput = {
name: 'crm_analytics',
label: 'CRM Analytics Read Replica',
driver: 'sqlite',
Expand Down
4 changes: 2 additions & 2 deletions examples/app-crm/src/emails/deal-won.email.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { EmailTemplateDefinition } from '@objectstack/spec/system';
import type { EmailTemplateDefinitionInput } from '@objectstack/spec/system';

/**
* Sent when an opportunity moves to Closed Won.
* Referenced by the `notify_owner_deal_won` workflow action.
*/
export const DealWonEmail: EmailTemplateDefinition = {
export const DealWonEmail: EmailTemplateDefinitionInput = {
name: 'crm.deal_won',
label: 'Deal Won — Owner Congrats',
category: 'workflow',
Expand Down
4 changes: 2 additions & 2 deletions examples/app-crm/src/emails/lead-follow-up.email.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { EmailTemplateDefinition } from '@objectstack/spec/system';
import type { EmailTemplateDefinitionInput } from '@objectstack/spec/system';

/**
* Follow-up nudge sent by the Stale Opportunity workflow when no
* activity has been logged on a Lead for the configured threshold.
*/
export const LeadFollowUpEmail: EmailTemplateDefinition = {
export const LeadFollowUpEmail: EmailTemplateDefinitionInput = {
name: 'crm.lead_followup',
label: 'Lead — Follow-Up Reminder',
category: 'notification',
Expand Down
4 changes: 2 additions & 2 deletions examples/app-crm/src/emails/welcome.email.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { EmailTemplateDefinition } from '@objectstack/spec/system';
import type { EmailTemplateDefinitionInput } from '@objectstack/spec/system';

/**
* Welcome email sent to a Contact after it's added to the CRM.
* Demonstrates marketing-category templates with a clear CTA.
*/
export const WelcomeEmail: EmailTemplateDefinition = {
export const WelcomeEmail: EmailTemplateDefinitionInput = {
name: 'crm.welcome',
label: 'Welcome — New Contact',
category: 'marketing',
Expand Down
4 changes: 2 additions & 2 deletions examples/app-crm/src/extensions/contact.extension.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { ObjectExtension } from '@objectstack/spec';
import type { ObjectExtensionInput } from '@objectstack/spec/data';

/**
* Extends the built-in crm_contact object with social-media fields.
* Demonstrates ObjectExtension — additive fields without re-declaring the
* whole object schema.
*/
export const ContactExtension: ObjectExtension = {
export const ContactExtension: ObjectExtensionInput = {
extend: 'crm_contact',
label: 'Contact (CRM Extended)',
fields: {
Expand Down
8 changes: 4 additions & 4 deletions examples/app-crm/src/jobs/crm-jobs.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { Job } from '@objectstack/spec';
import type { JobInput } from '@objectstack/spec/system';

/**
* Nightly lead-scoring job — recomputes `lead_score` for all open leads.
* Handler key 'scoreLeads' must be registered in defineStack({ functions }).
*/
export const LeadScoringJob: Job = {
export const LeadScoringJob: JobInput = {
name: 'crm_lead_scoring',
label: 'Nightly Lead Score Refresh',
description: 'Recalculates lead_score for all open leads using engagement signals.',
Expand All @@ -28,7 +28,7 @@ export const LeadScoringJob: Job = {
/**
* Weekly pipeline report — aggregates deal data and emails managers.
*/
export const PipelineReportJob: Job = {
export const PipelineReportJob: JobInput = {
name: 'crm_pipeline_report',
label: 'Weekly Pipeline Report',
description: 'Generates and emails weekly pipeline summary to sales managers.',
Expand All @@ -51,7 +51,7 @@ export const PipelineReportJob: Job = {
* Daily renewal reminder sweep — kicks off renewal_reminder_flow for
* opportunities nearing contract expiry.
*/
export const RenewalSweepJob: Job = {
export const RenewalSweepJob: JobInput = {
name: 'crm_renewal_sweep',
label: 'Daily Renewal Reminder Sweep',
description: 'Scans contracts expiring within 30 days and enqueues reminder flows.',
Expand Down
2 changes: 1 addition & 1 deletion examples/app-crm/src/pages/welcome.page.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { UI } from '@objectstack/spec';
import type * as UI from '@objectstack/spec/ui';

/**
* Example custom page — a CRM landing page.
Expand Down
2 changes: 1 addition & 1 deletion examples/app-crm/src/portals/customer.portal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { Portal } from '@objectstack/spec';
import type { Portal } from '@objectstack/spec/ui';

/**
* Customer Self-Service Portal — external users can view their account,
Expand Down
4 changes: 2 additions & 2 deletions examples/app-crm/src/reports/sales-by-stage.report.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { UI } from '@objectstack/spec';
import type * as UI from '@objectstack/spec/ui';

/**
* Example report — total opportunity amount grouped by stage.
Expand All @@ -11,7 +11,7 @@ import type { UI } from '@objectstack/spec';
* its "grouped by stage" label), so both forms compute the same number and the
* reconciliation harness can verify them.
*/
export const SalesByStageReport: UI.Report = {
export const SalesByStageReport: UI.ReportInput = {
name: 'crm_sales_by_stage',
label: 'Sales by Stage',
description: 'Total opportunity amount grouped by sales stage.',
Expand Down
2 changes: 1 addition & 1 deletion examples/app-crm/src/security/crm-policy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { Policy } from '@objectstack/spec';
import type { Policy } from '@objectstack/spec/security';

/**
* Default CRM security policy applied to all users.
Expand Down
17 changes: 8 additions & 9 deletions examples/app-crm/src/security/sales-roles.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { Identity, Security } from '@objectstack/spec';
import type * as Identity from '@objectstack/spec/identity';
import type * as Security from '@objectstack/spec/security';

/**
* Example roles — a small sales hierarchy.
*/
export const SalesRepRole: Identity.Role = {
export const SalesRepRole: Identity.RoleInput = {
name: 'sales_rep',
label: 'Sales Representative',
description: 'Front-line sales representative.',
};

export const SalesManagerRole: Identity.Role = {
export const SalesManagerRole: Identity.RoleInput = {
name: 'sales_manager',
label: 'Sales Manager',
description: 'Manages a team of sales reps.',
parentRole: 'sales_rep',
parent: 'sales_rep',
};

/** Referenced by the Discount Approval second step. */
export const FinanceApproverRole: Identity.Role = {
export const FinanceApproverRole: Identity.RoleInput = {
name: 'finance_approver',
label: 'Finance Approver',
description: 'Finance team member authorised to approve discounts above 30%.',
Expand All @@ -30,10 +31,9 @@ export const FinanceApproverRole: Identity.Role = {
*
* Note: `objects` is a Record keyed by object name, not an array.
*/
export const SalesUserPermissionSet: Security.PermissionSet = {
export const SalesUserPermissionSet: Security.PermissionSetInput = {
name: 'crm_sales_user',
label: 'CRM Sales User',
description: 'Standard CRUD on CRM objects for sales team members.',
isProfile: false,
objects: {
crm_account: { allowRead: true, allowCreate: true, allowEdit: true, allowDelete: false },
Expand All @@ -52,10 +52,9 @@ export const SalesUserPermissionSet: Security.PermissionSet = {
* `crm_lead` (not a short `lead`). INSERT-only — guests can never read, edit, or
* delete any record.
*/
export const GuestPortalProfile: Security.PermissionSet = {
export const GuestPortalProfile: Security.PermissionSetInput = {
name: 'guest_portal',
label: 'Guest (Public Forms)',
description: 'Anonymous Web-to-Lead submitters — INSERT-only on crm_lead.',
isProfile: true,
objects: {
crm_lead: { allowRead: false, allowCreate: true, allowEdit: false, allowDelete: false },
Expand Down
8 changes: 4 additions & 4 deletions examples/app-crm/src/security/sharing-rules.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { SharingRule } from '@objectstack/spec';
import type { SharingRuleInput } from '@objectstack/spec/security';

/**
* Criteria-based sharing: share high-value opportunities (amount > 100000)
* with the Sales Manager role so managers always have read access to big deals.
*/
export const HighValueOpportunitySharingRule: SharingRule = {
export const HighValueOpportunitySharingRule: SharingRuleInput = {
type: 'criteria',
name: 'share_high_value_opps_with_managers',
label: 'High-Value Deals → Sales Managers',
Expand All @@ -25,7 +25,7 @@ export const HighValueOpportunitySharingRule: SharingRule = {
* Owner-based sharing: leads owned by a Sales Rep are shared read-only
* with their manager so managers can coach on individual pipelines.
*/
export const RepLeadSharingRule: SharingRule = {
export const RepLeadSharingRule: SharingRuleInput = {
type: 'owner',
name: 'share_rep_leads_with_manager',
label: "Rep's Leads → Manager (read-only)",
Expand All @@ -47,7 +47,7 @@ export const RepLeadSharingRule: SharingRule = {
* Criteria-based: share activities linked to won deals with the whole
* Sales team so everyone can learn from successful engagement patterns.
*/
export const WonDealActivitySharingRule: SharingRule = {
export const WonDealActivitySharingRule: SharingRuleInput = {
type: 'criteria',
name: 'share_won_deal_activities',
label: 'Won-Deal Activities → All Sales',
Expand Down
2 changes: 1 addition & 1 deletion examples/app-crm/src/themes/crm.theme.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { Theme } from '@objectstack/spec';
import type { Theme } from '@objectstack/spec/ui';

/**
* Default CRM brand theme — light mode with professional blue palette.
Expand Down
2 changes: 1 addition & 1 deletion examples/app-crm/src/translations/crm.translation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import type { TranslationBundle } from '@objectstack/spec';
import type { TranslationBundle } from '@objectstack/spec/system';

/**
* CRM translation bundle — English + Simplified Chinese.
Expand Down
Loading
Loading