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
39 changes: 36 additions & 3 deletions examples/app-showcase/objectstack.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.

import { defineStack } from '@objectstack/spec';
import { ConnectorRestPlugin } from '@objectstack/connector-rest';
import { ConnectorSlackPlugin } from '@objectstack/connector-slack';

import * as objects from './src/objects/index.js';
import { TaskViews, ProjectViews } from './src/views/index.js';
Expand All @@ -26,6 +28,11 @@ import { allDatasources } from './src/datasources/index.js';
import { allPortals } from './src/portals/index.js';
import { ShowcaseSeedData } from './src/data/index.js';

// Ambient `process` for the env-var overrides below — the showcase tsconfig
// doesn't pull in `@types/node`, but the CLI provides the real `process` at
// runtime. Keeps `pnpm typecheck` green without widening the type surface.
declare const process: { env: Record<string, string | undefined> };

/**
* Showcase — a kitchen-sink workspace that exercises every metadata type,
* every view type, every chart type, and the major end-to-end capability
Expand All @@ -49,9 +56,35 @@ export default defineStack({
description: 'Kitchen-sink workspace covering all metadata types, all view types, and the major capability chains.',
},

// `approvals` loads ApprovalsServicePlugin so the `approval` flow node
// (ADR-0019) is contributed to the engine — the showcase flows use it.
requires: ['ui', 'automation', 'approvals'],
// Capability tokens the CLI resolves to platform plugins:
// • automation — AutomationServicePlugin (flow engine + node executors).
// • approvals — ApprovalsServicePlugin, so the `approval` flow node
// (ADR-0019) is contributed to the engine.
// • messaging — MessagingServicePlugin, so the `notify` node delivers to
// the inbox channel (`sys_inbox_message` rows) instead of
// degrading to a logged no-op.
// • triggers — record-change + schedule FlowTrigger plugins, so the
// autolaunched / schedule flows below actually auto-fire.
// • job — JobServicePlugin, the timing backend the schedule trigger
// delegates to (interval / cron jobs).
requires: ['ui', 'automation', 'approvals', 'messaging', 'triggers', 'job'],

// Concrete connectors for the `connector_action` node. The baseline engine
// ships the dispatch node + an empty registry; these plugins populate it.
// • rest → points at the running server itself, so the REST connector
// flow's call + response are observable on the flow run with no
// external dependency. Override the target with SHOWCASE_SELF_URL.
// • slack → registered so TaskCompletedSlackFlow resolves its connector;
// live posting needs a real bot token (set SLACK_BOT_TOKEN).
plugins: [
new ConnectorRestPlugin({
name: 'rest',
baseUrl: process.env.SHOWCASE_SELF_URL ?? 'http://127.0.0.1:3000',
}),
new ConnectorSlackPlugin({
token: process.env.SLACK_BOT_TOKEN ?? 'xoxb-showcase-demo-token',
}),
],

// Infrastructure
datasources: allDatasources,
Expand Down
2 changes: 2 additions & 0 deletions examples/app-showcase/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"verify": "pnpm typecheck && pnpm test"
},
"dependencies": {
"@objectstack/connector-rest": "workspace:*",
"@objectstack/connector-slack": "workspace:*",
"@objectstack/runtime": "workspace:*",
"@objectstack/spec": "workspace:*"
},
Expand Down
110 changes: 108 additions & 2 deletions examples/app-showcase/src/flows/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,12 @@ export const BudgetApprovalFlow = defineFlow({
{ id: 'e1', source: 'start', target: 'manager_review' },
{ id: 'e2', source: 'manager_review', target: 'needs_exec', label: 'approve' },
{ id: 'e3', source: 'manager_review', target: 'rejected', label: 'reject' },
{ id: 'e4', source: 'needs_exec', target: 'exec_review', label: 'true' },
{ id: 'e5', source: 'needs_exec', target: 'approved', label: 'false' },
// Decision branching is edge-condition driven (flow spec): the engine
// routes a decision node by evaluating each out-edge's `condition`. Carry
// the predicate on the edges (the node `config.condition` alone is not
// evaluated by the engine), so budgets ≤ $500k skip the executive step.
{ id: 'e4', source: 'needs_exec', target: 'exec_review', label: 'true', condition: 'budget > 500000' },
{ id: 'e5', source: 'needs_exec', target: 'approved', label: 'false', condition: 'budget <= 500000' },
{ id: 'e6', source: 'exec_review', target: 'approved', label: 'approve' },
{ id: 'e7', source: 'exec_review', target: 'rejected', label: 'reject' },
],
Expand Down Expand Up @@ -258,10 +262,112 @@ export const TaskCompletedSlackFlow = defineFlow({
],
});

/**
* Scheduled Digest — the worked `schedule` trigger example.
*
* A `type: 'schedule'` flow whose start node carries an interval descriptor.
* The automation engine parses that into a schedule binding; the schedule
* trigger plugin (`@objectstack/plugin-trigger-schedule`, paired with the job
* service) registers a job that fires this flow every interval. Each tick runs
* the `notify` node, dropping a fresh `sys_inbox_message` row — so the
* scheduled fire is observable end-to-end with no manual `engine.execute()`.
*
* Install `requires: ['automation', 'triggers', 'job', 'messaging']` and this
* flow auto-launches on the interval.
*/
export const ScheduledDigestFlow = defineFlow({
name: 'showcase_scheduled_digest',
label: 'Scheduled Project Digest (interval)',
description: 'Fires on a fixed interval and posts a digest to an inbox — demonstrates the schedule trigger.',
type: 'schedule',
nodes: [
{
id: 'start',
type: 'start',
label: 'Every 20s',
config: {
// Interval keeps the demo observable in-session; production digests
// would use a cron expression, e.g. { type: 'cron', expression: '0 8 * * *' }.
schedule: { type: 'interval', intervalMs: 20000 },
},
},
{
id: 'digest',
type: 'notify',
label: 'Post Digest to Inbox',
config: {
topic: 'project.digest',
recipients: ['admin@objectos.ai'],
channels: ['inbox'],
severity: 'info',
title: 'Scheduled project digest',
message: 'Your periodic project digest is ready — open Projects for the latest health.',
actionUrl: '/showcase_project',
},
},
{ id: 'end', type: 'end', label: 'End' },
],
edges: [
{ id: 'e1', source: 'start', target: 'digest' },
{ id: 'e2', source: 'digest', target: 'end' },
],
});

/**
* Task Completed → REST Ping (self) — the worked `connector_action` example on
* the generic `rest` connector.
*
* Where {@link TaskCompletedSlackFlow} targets the `slack` connector (which
* needs a real bot token + channel), this flow dispatches to the `rest`
* connector contributed by `@objectstack/connector-rest`, configured to point
* at the running server itself. On task completion it issues
* `GET /api/v1/health`; the request and its `{ status: 'ok' }` response are
* captured on the flow run, so the connector dispatch is fully observable
* without any external service or credentials.
*/
export const TaskCompletedRestPingFlow = defineFlow({
name: 'showcase_task_completed_rest_ping',
label: 'REST Ping on Task Completed',
description: 'Calls the local server health endpoint via the rest connector when a task is marked Done.',
type: 'autolaunched',
nodes: [
{
id: 'start',
type: 'start',
label: 'On Task Update',
config: {
objectName: 'showcase_task',
triggerType: 'record-after-update',
condition: 'status == "done" && previous.status != "done"',
},
},
{
id: 'ping',
type: 'connector_action',
label: 'GET /api/v1/health',
connectorConfig: {
connectorId: 'rest',
actionId: 'request',
input: {
method: 'GET',
path: '/api/v1/health',
},
},
},
{ id: 'end', type: 'end', label: 'End' },
],
edges: [
{ id: 'e1', source: 'start', target: 'ping' },
{ id: 'e2', source: 'ping', target: 'end' },
],
});

export const allFlows = [
TaskCompletedFlow,
ReassignWizardFlow,
BudgetApprovalFlow,
TaskCompletedSlackFlow,
TaskAssignedNotifyFlow,
ScheduledDigestFlow,
TaskCompletedRestPingFlow,
];
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading