Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…xistence check Follows the same pattern as flow validation: when pages are defined, modal-type action targets are validated against the page names collection. When no pages are defined, validation is skipped (plugin may provide them). Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Provides actual function bodies for previously dangling target string references: completeTask, startTask, cloneTask, massCompleteTasks, deleteCompletedTasks, exportTasksToCSV (todo); convertLead, addToCampaign, cloneRecord, massUpdateStage, escalateCase, closeCase, markAsPrimaryContact, exportToCSV (crm). Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…s, platform comparison, and cross-reference validation section Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
@copilot 仔细的评估现有的crm系统元数据,和objectstack runtime自己能够完成的完整的action注册和处理使用的所有的流程吗?仔细评估是否已经完成了任务中的所有要求。 |
…ifecycle, and config wiring - Add deferTask/setReminder handlers (todo modal actions) - Add sendEmail/logCall handlers (CRM modal actions) - Create register-handlers.ts for both apps showing engine.registerAction() lifecycle - Wire onEnable lifecycle in objectstack.config.ts for runtime registration - Update action barrel JSDoc to reference handler paths Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…ation - Replace `as string ?? value` with `? String(x) : default` pattern - Replace `Function` type with `(...args: unknown[]) => unknown` - Remove `as any` casts in config lifecycle hooks Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
经过仔细评估,发现了之前实现中的几个关键缺口,已在 b8659bc 和 9ac5165 中修复: 缺口分析:
当前覆盖率(18/18 actions):
完整生命周期: |
There was a problem hiding this comment.
Pull request overview
Adds spec-level validation for modal action targets, fills in missing example action handler implementations/registration wiring, and synchronizes action reference docs with the current target/execute rules.
Changes:
- Extend
defineStack()cross-reference checks to validateaction.type = 'modal'targets againstpages[], with tests. - Add complete action handler implementations +
onEnableregistration wiring for both example apps. - Update
content/docs/references/ui/action.mdxto documenttargetbinding rules andexecutedeprecation.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/spec/src/stack.zod.ts | Adds modal→pages cross-reference validation alongside existing flow validation. |
| packages/spec/src/stack.test.ts | Adds unit tests covering modal target validation pass/fail/skip conditions. |
| examples/app-todo/src/actions/task.handlers.ts | Implements Todo action handlers (script + modal-related handlers). |
| examples/app-todo/src/actions/register-handlers.ts | Demonstrates runtime handler registration via engine.registerAction(). |
| examples/app-todo/src/actions/index.ts | Clarifies action barrel export intent (metadata-only). |
| examples/app-todo/objectstack.config.ts | Wires onEnable hook to register handlers at runtime. |
| examples/app-crm/src/actions/register-handlers.ts | Registers CRM handlers across domains via engine.registerAction(). |
| examples/app-crm/src/actions/index.ts | Clarifies action barrel export intent (metadata-only). |
| examples/app-crm/src/actions/handlers/opportunity.handlers.ts | Implements opportunity handlers. |
| examples/app-crm/src/actions/handlers/lead.handlers.ts | Implements lead handlers. |
| examples/app-crm/src/actions/handlers/index.ts | Barrel re-export for CRM handler implementations. |
| examples/app-crm/src/actions/handlers/global.handlers.ts | Implements global handlers. |
| examples/app-crm/src/actions/handlers/contact.handlers.ts | Implements contact handlers. |
| examples/app-crm/src/actions/handlers/case.handlers.ts | Implements case handlers. |
| examples/app-crm/objectstack.config.ts | Wires onEnable hook to register handlers at runtime. |
| content/docs/references/ui/action.mdx | Documents target rules, execute deprecation, and cross-ref validation behavior. |
| // ConvertLeadAction (type: flow) — also has server-side handler for | ||
| // programmatic conversion outside the screen flow. | ||
| engine.registerAction('lead', 'convertLead', convertLead); | ||
| // CreateCampaignAction (type: modal) — processes campaign assignment form | ||
| engine.registerAction('lead', 'addToCampaign', addToCampaign); |
There was a problem hiding this comment.
ConvertLeadAction is declared as type: 'flow' with target: 'lead_conversion', but this registers a handler under 'convertLead'. If target is the resolvable identifier for flow actions (and defineStack now validates flow targets against flows[]), registering 'convertLead' here likely won't be invoked by the action. If you want a programmatic server-side conversion entrypoint in addition to the flow, consider modeling it as a separate script action (with its own target) rather than registering a handler for the flow action under a different name.
| engine.registerAction('task', 'deferTask', deferTask); | ||
| engine.registerAction('task', 'setReminder', setReminder); |
There was a problem hiding this comment.
Modal handlers are registered under action names ('deferTask', 'setReminder') that don't match the modal actions' target values in task.actions.ts (e.g. defer_task_modal, set_reminder_modal). If the runtime resolves/executes handlers by Action.target (as described earlier in this file), these registrations will be unreachable. Align the registered actionName with whatever identifier the runtime uses for modal execution (likely the action's target or name), or avoid registering modal handlers here and instead invoke script actions from the modal page/form submit flow.
| engine.registerAction('task', 'deferTask', deferTask); | |
| engine.registerAction('task', 'setReminder', setReminder); | |
| engine.registerAction('task', 'defer_task_modal', deferTask); | |
| engine.registerAction('task', 'set_reminder_modal', setReminder); |
| * Exports action metadata definitions only. Used by `Object.values()` in | ||
| * objectstack.config.ts to auto-collect all action declarations for defineStack(). | ||
| * | ||
| * **Handler functions** are exported from `./handlers/` — see register-handlers.ts |
There was a problem hiding this comment.
This comment says handler functions are exported from ./handlers/, but this example app's handlers live in task.handlers.ts (and registration in register-handlers.ts). Updating this path avoids confusing readers following the barrel guidance.
| * **Handler functions** are exported from `./handlers/` — see register-handlers.ts | |
| * **Handler functions** are exported from `./task.handlers` — see register-handlers.ts |
| engine.registerAction('lead', 'addToCampaign', addToCampaign); | ||
|
|
||
| // ─── Opportunity Domain ──────────────────────────────────────────── | ||
| // CloneOpportunityAction (type: script) | ||
| engine.registerAction('opportunity', 'cloneRecord', cloneRecord); | ||
| // MassUpdateStageAction (type: modal) — processes stage selection form | ||
| engine.registerAction('opportunity', 'massUpdateStage', massUpdateStage); | ||
|
|
||
| // ─── Case Domain ─────────────────────────────────────────────────── | ||
| // EscalateCaseAction (type: modal) — processes escalation reason form | ||
| engine.registerAction('case', 'escalateCase', escalateCase); | ||
| // CloseCaseAction (type: modal) — processes resolution form | ||
| engine.registerAction('case', 'closeCase', closeCase); | ||
|
|
||
| // ─── Contact Domain ──────────────────────────────────────────────── | ||
| // MarkPrimaryContactAction (type: script) | ||
| engine.registerAction('contact', 'markAsPrimaryContact', markAsPrimaryContact); | ||
| // SendEmailAction (type: modal) — processes email composer form | ||
| engine.registerAction('contact', 'sendEmail', sendEmail); | ||
|
|
||
| // ─── Global (cross-domain) ───────────────────────────────────────── | ||
| // ExportToCsvAction (type: script) — wildcard '*' applies to all objects | ||
| engine.registerAction('*', 'exportToCSV', exportToCSV); | ||
| // LogCallAction (type: modal) — processes call log form | ||
| engine.registerAction('*', 'logCall', logCall); |
There was a problem hiding this comment.
Several registerAction() calls use names like 'addToCampaign', 'massUpdateStage', 'escalateCase', etc., but the corresponding Action metadata for type: 'modal' uses target values like 'add_to_campaign_modal', 'mass_update_stage_modal', 'escalate_case_modal', etc. Unless the runtime executes actions by some identifier other than Action.target, these handlers won't be found when the action runs. Consider registering the handler under the modal action's actual execution key (e.g. action.target or action.name), or model modal submission as a separate script action that the modal/page triggers explicitly.
| engine.registerAction('lead', 'addToCampaign', addToCampaign); | |
| // ─── Opportunity Domain ──────────────────────────────────────────── | |
| // CloneOpportunityAction (type: script) | |
| engine.registerAction('opportunity', 'cloneRecord', cloneRecord); | |
| // MassUpdateStageAction (type: modal) — processes stage selection form | |
| engine.registerAction('opportunity', 'massUpdateStage', massUpdateStage); | |
| // ─── Case Domain ─────────────────────────────────────────────────── | |
| // EscalateCaseAction (type: modal) — processes escalation reason form | |
| engine.registerAction('case', 'escalateCase', escalateCase); | |
| // CloseCaseAction (type: modal) — processes resolution form | |
| engine.registerAction('case', 'closeCase', closeCase); | |
| // ─── Contact Domain ──────────────────────────────────────────────── | |
| // MarkPrimaryContactAction (type: script) | |
| engine.registerAction('contact', 'markAsPrimaryContact', markAsPrimaryContact); | |
| // SendEmailAction (type: modal) — processes email composer form | |
| engine.registerAction('contact', 'sendEmail', sendEmail); | |
| // ─── Global (cross-domain) ───────────────────────────────────────── | |
| // ExportToCsvAction (type: script) — wildcard '*' applies to all objects | |
| engine.registerAction('*', 'exportToCSV', exportToCSV); | |
| // LogCallAction (type: modal) — processes call log form | |
| engine.registerAction('*', 'logCall', logCall); | |
| engine.registerAction('lead', 'add_to_campaign_modal', addToCampaign); | |
| // ─── Opportunity Domain ──────────────────────────────────────────── | |
| // CloneOpportunityAction (type: script) | |
| engine.registerAction('opportunity', 'cloneRecord', cloneRecord); | |
| // MassUpdateStageAction (type: modal) — processes stage selection form | |
| engine.registerAction('opportunity', 'mass_update_stage_modal', massUpdateStage); | |
| // ─── Case Domain ─────────────────────────────────────────────────── | |
| // EscalateCaseAction (type: modal) — processes escalation reason form | |
| engine.registerAction('case', 'escalate_case_modal', escalateCase); | |
| // CloseCaseAction (type: modal) — processes resolution form | |
| engine.registerAction('case', 'close_case_modal', closeCase); | |
| // ─── Contact Domain ──────────────────────────────────────────────── | |
| // MarkPrimaryContactAction (type: script) | |
| engine.registerAction('contact', 'markAsPrimaryContact', markAsPrimaryContact); | |
| // SendEmailAction (type: modal) — processes email composer form | |
| engine.registerAction('contact', 'send_email_modal', sendEmail); | |
| // ─── Global (cross-domain) ───────────────────────────────────────── | |
| // ExportToCsvAction (type: script) — wildcard '*' applies to all objects | |
| engine.registerAction('*', 'exportToCSV', exportToCSV); | |
| // LogCallAction (type: modal) — processes call log form | |
| engine.registerAction('*', 'log_call_modal', logCall); |
Follow-up to #833: fills three gaps — dangling handler references in examples, stale docs, and missing modal target validation.
Modal cross-reference validation (
stack.zod.ts)validateCrossReferences()now checksaction.type="modal"targets against thepages[]collection, mirroring the existingflow→flows[]patternpageNames.size === 0(plugin may provide them)Example handler implementations (18/18 actions covered)
Complete handler coverage across both example apps with full registration lifecycle:
task.handlers.ts— 6 script handlers (completeTask,startTask,cloneTask,massCompleteTasks,deleteCompletedTasks,exportTasksToCSV) + 2 modal handlers (deferTask,setReminder)handlers/directory with lead (convertLead,addToCampaign), opportunity (cloneRecord,massUpdateStage), case (escalateCase,closeCase), contact (markAsPrimaryContact,sendEmail), and global (exportToCSV,logCall) handlersregister-handlers.tsfor both apps — demonstrates the fullengine.registerAction()registration lifecycle with architecture diagrams showing metadata → handler → registration flowobjectstack.config.tsupdated in both apps withonEnablelifecycle hook that wires handler registration at runtimeindex.ts) export metadata definitions only (safe forObject.values()→defineStack()); handlers are in separate pathsDocumentation (
action.mdx)executefield marked deprecated with migration calloutTests
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.