@sparkerp/plugin-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/bundle/blocks.json +12229 -0
- package/bundle/catalog.json +32475 -0
- package/bundle/docs/applications/hcm/employee-profile.md +144 -0
- package/bundle/docs/concepts/artifact-types.md +80 -0
- package/bundle/docs/concepts/glossary.md +124 -0
- package/bundle/docs/concepts/overview.md +88 -0
- package/bundle/docs/concepts/page-data-flow.md +84 -0
- package/bundle/docs/concepts/plugin-lifecycle.md +81 -0
- package/bundle/docs/concepts/sdk-modes.md +71 -0
- package/bundle/docs/docs.json +109 -0
- package/bundle/docs/guides/add-a-cadence-job.md +134 -0
- package/bundle/docs/guides/add-a-compliance-job.md +139 -0
- package/bundle/docs/guides/add-a-create-edit-form.md +167 -0
- package/bundle/docs/guides/add-a-custom-block.md +225 -0
- package/bundle/docs/guides/add-a-data-provider.md +216 -0
- package/bundle/docs/guides/add-a-kpi.md +154 -0
- package/bundle/docs/guides/add-a-reminder-job.md +181 -0
- package/bundle/docs/guides/add-an-aggregation-job.md +153 -0
- package/bundle/docs/guides/add-an-approval-workflow.md +222 -0
- package/bundle/docs/guides/add-an-entity.md +170 -0
- package/bundle/docs/guides/add-business-rules.md +147 -0
- package/bundle/docs/guides/add-i18n.md +121 -0
- package/bundle/docs/guides/add-menus.md +149 -0
- package/bundle/docs/guides/build-a-code-plugin.md +141 -0
- package/bundle/docs/guides/build-a-page.md +213 -0
- package/bundle/docs/guides/build-a-polyglot-service-plugin.md +201 -0
- package/bundle/docs/guides/build-a-tenant-extension-service.md +149 -0
- package/bundle/docs/guides/create-a-plugin.md +172 -0
- package/bundle/docs/guides/expose-a-plugin-operation-as-an-ai-tool.md +114 -0
- package/bundle/docs/guides/index.md +53 -0
- package/bundle/docs/guides/publish-and-upgrade.md +152 -0
- package/bundle/docs/guides/responsive-plugin.md +117 -0
- package/bundle/docs/guides/seed-data-on-install.md +127 -0
- package/bundle/docs/guides/set-up-the-sdk.md +120 -0
- package/bundle/docs/guides/use-the-mcp-server.md +104 -0
- package/bundle/docs/guides/validate-and-test.md +119 -0
- package/bundle/docs/guides/wire-a-pages-data.md +153 -0
- package/bundle/docs/quickstart/index.md +118 -0
- package/bundle/docs/recipes/condition-based-compliance-flag.md +70 -0
- package/bundle/docs/recipes/cross-plugin-offboarding-fanout.md +68 -0
- package/bundle/docs/recipes/cross-row-aggregation.md +67 -0
- package/bundle/docs/recipes/entity-with-approval-workflow.md +72 -0
- package/bundle/docs/recipes/index.md +40 -0
- package/bundle/docs/recipes/install-lifecycle-data-seeding.md +76 -0
- package/bundle/docs/recipes/kpi-dashboard-page.md +92 -0
- package/bundle/docs/recipes/master-detail-page.md +84 -0
- package/bundle/docs/recipes/n-days-before-reminder.md +72 -0
- package/bundle/docs/recipes/per-row-document-generator.md +65 -0
- package/bundle/docs/recipes/related-record-lookup-field.md +74 -0
- package/bundle/docs/recipes/resolved-reference-grid-column.md +63 -0
- package/bundle/docs/recipes/scheduled-record-generation.md +68 -0
- package/bundle/docs/reference/action.md +18 -0
- package/bundle/docs/reference/block-definition.md +35 -0
- package/bundle/docs/reference/block-instance.md +33 -0
- package/bundle/docs/reference/composite-pack-manifest.md +33 -0
- package/bundle/docs/reference/connector-definition.md +36 -0
- package/bundle/docs/reference/data-service-definition.md +22 -0
- package/bundle/docs/reference/data-view-definition.md +26 -0
- package/bundle/docs/reference/entity-aggregation-config.md +38 -0
- package/bundle/docs/reference/entity-cadence-config.md +33 -0
- package/bundle/docs/reference/entity-compliance-config.md +37 -0
- package/bundle/docs/reference/entity-cross-plugin-action-config.md +37 -0
- package/bundle/docs/reference/entity-definition.md +18 -0
- package/bundle/docs/reference/entity-document-generator-config.md +35 -0
- package/bundle/docs/reference/entity-rule-definition.md +29 -0
- package/bundle/docs/reference/entity-status-date-sweep-config.md +27 -0
- package/bundle/docs/reference/form.md +35 -0
- package/bundle/docs/reference/index.md +37 -0
- package/bundle/docs/reference/menu.md +26 -0
- package/bundle/docs/reference/mobile-nav.md +22 -0
- package/bundle/docs/reference/page-template.md +29 -0
- package/bundle/docs/reference/page.md +44 -0
- package/bundle/docs/reference/plugin-manifest.md +54 -0
- package/bundle/docs/reference/plugin-seed-data.md +21 -0
- package/bundle/docs/reference/print-template-definition.md +22 -0
- package/bundle/docs/reference/provider-definition.md +22 -0
- package/bundle/docs/reference/report-definition.md +22 -0
- package/bundle/docs/reference/rule.md +30 -0
- package/bundle/docs/reference/workflow-definition.md +28 -0
- package/bundle/docs/troubleshooting/grid-shows-zero-rows.md +82 -0
- package/bundle/docs/troubleshooting/job-never-runs.md +89 -0
- package/bundle/docs/troubleshooting/known-gotchas.md +193 -0
- package/bundle/docs/troubleshooting/publishing-errors.md +119 -0
- package/bundle/docs/tutorial/01-create-the-plugin.md +157 -0
- package/bundle/docs/tutorial/02-category-entity-and-seed.md +94 -0
- package/bundle/docs/tutorial/03-equipment-entity.md +121 -0
- package/bundle/docs/tutorial/04-equipment-register-page.md +153 -0
- package/bundle/docs/tutorial/05-checkout-entity-and-page.md +116 -0
- package/bundle/docs/tutorial/06-checkout-approval-workflow.md +139 -0
- package/bundle/docs/tutorial/07-return-due-reminder-job.md +129 -0
- package/bundle/docs/tutorial/08-menus-i18n-publish.md +123 -0
- package/bundle/docs/tutorial/index.md +72 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-aggregation-configs.json +18 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-cadence-configs.json +9 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-compliance-configs.json +74 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-cross-plugin-configs.json +9 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-document-configs.json +9 -0
- package/bundle/examples/backend/modules/hcm-assets/spk-assembly/metadata/seed-data/hcm-assets-sweep-configs.json +260 -0
- package/bundle/examples/backend/modules/hcm-compensation/spk-assembly/metadata/entities/hcm_salary_planning_entry.json +41 -0
- package/bundle/examples/backend/modules/hcm-compensation/spk-assembly/metadata/page/salary-planning.json +1 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/page/promotions.json +10944 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.movement.json +44 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.promotion-acknowledge.json +19 -0
- package/bundle/examples/backend/modules/hcm-employee/spk-assembly/metadata/workflow/employee.resignation.json +29 -0
- package/bundle/examples/example-patterns.mjs +94 -0
- package/bundle/manifest.json +14 -0
- package/bundle/schemas/action.schema.json +102 -0
- package/bundle/schemas/block-definition.schema.json +222 -0
- package/bundle/schemas/block-instance.schema.json +139 -0
- package/bundle/schemas/composite-pack-manifest.schema.json +160 -0
- package/bundle/schemas/connector-definition.schema.json +29 -0
- package/bundle/schemas/data-service-definition.schema.json +64 -0
- package/bundle/schemas/data-view-definition.schema.json +148 -0
- package/bundle/schemas/entity-aggregation-config.schema.json +32 -0
- package/bundle/schemas/entity-cadence-config.schema.json +27 -0
- package/bundle/schemas/entity-compliance-config.schema.json +31 -0
- package/bundle/schemas/entity-cross-plugin-action-config.schema.json +31 -0
- package/bundle/schemas/entity-definition.schema.json +126 -0
- package/bundle/schemas/entity-document-generator-config.schema.json +29 -0
- package/bundle/schemas/entity-rule-definition.schema.json +75 -0
- package/bundle/schemas/entity-status-date-sweep-config.schema.json +21 -0
- package/bundle/schemas/form.schema.json +281 -0
- package/bundle/schemas/menu.schema.json +106 -0
- package/bundle/schemas/mobile-nav.schema.json +53 -0
- package/bundle/schemas/page-template.schema.json +46 -0
- package/bundle/schemas/page.schema.json +292 -0
- package/bundle/schemas/plugin-manifest.schema.json +139 -0
- package/bundle/schemas/plugin-seed-data.schema.json +24 -0
- package/bundle/schemas/print-template-definition.schema.json +90 -0
- package/bundle/schemas/provider-definition.schema.json +27 -0
- package/bundle/schemas/report-definition.schema.json +96 -0
- package/bundle/schemas/rule.schema.json +82 -0
- package/bundle/schemas/workflow-definition.schema.json +42 -0
- package/bundle/validators/block-engine.mjs +23222 -0
- package/bundle/validators/page-engine.mjs +21645 -0
- package/erp-cli/authoring-root.mjs +344 -0
- package/erp-cli/erp.mjs +4065 -0
- package/erp-cli/example-patterns.mjs +94 -0
- package/erp-mcp-server/index.mjs +818 -0
- package/package.json +27 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Make a plugin work on desktop, tablet, and mobile
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Make a plugin work on desktop, tablet, and mobile
|
|
7
|
+
|
|
8
|
+
## What you're doing
|
|
9
|
+
|
|
10
|
+
The platform is **mobile-first**: a page you author with no responsive settings
|
|
11
|
+
renders as a single stacked column and already works on a phone. This guide is
|
|
12
|
+
about the deliberate adjustments — hiding a column on small screens, a
|
|
13
|
+
desktop-only side panel, a device-restricted menu item.
|
|
14
|
+
|
|
15
|
+
## The default you get for free
|
|
16
|
+
|
|
17
|
+
- Rows and columns collapse to a single column below the `sm` breakpoint.
|
|
18
|
+
- A `core.grid` becomes horizontally scrollable inside its own container.
|
|
19
|
+
- KPI rows wrap.
|
|
20
|
+
|
|
21
|
+
So the tutorial pages — header, KPI row, filter row, grid, dialogs — are usable
|
|
22
|
+
on mobile with zero extra work. **Verify that's true before adding overrides.**
|
|
23
|
+
|
|
24
|
+
## The breakpoint model
|
|
25
|
+
|
|
26
|
+
- The base breakpoint is **`xs`** (mobile). It has no key inside
|
|
27
|
+
`breakpointOverrides` — its own hidden state is the sibling property
|
|
28
|
+
**`hiddenAtXs`** on the row / column / item.
|
|
29
|
+
- Wider breakpoints are **`sm`, `md`, `lg`, `xl`**, set under `breakpoints`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"id": "item-3",
|
|
34
|
+
"kind": "block",
|
|
35
|
+
"hiddenAtXs": true,
|
|
36
|
+
"breakpoints": {
|
|
37
|
+
"md": { "hidden": false },
|
|
38
|
+
"lg": { "span": 4 }
|
|
39
|
+
},
|
|
40
|
+
"block": { "...": "..." }
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Read that as: hidden on mobile, shown again from `md` up, and 4/12 columns wide
|
|
45
|
+
from `lg` up. This is how you express "desktop-only content" — author it hidden
|
|
46
|
+
at `xs`, re-show it at a wider breakpoint.
|
|
47
|
+
|
|
48
|
+
- **`spanAtXs`** (on a column) is an independent 1–12 width for mobile, not a
|
|
49
|
+
mirror of `sm`.
|
|
50
|
+
|
|
51
|
+
## The complete example — a detail panel that's a dialog on mobile, a column on desktop
|
|
52
|
+
|
|
53
|
+
The tutorial keeps it simple: detail is always a `core.dialog`, which works
|
|
54
|
+
everywhere. If you want a side-by-side layout on desktop, put the detail content
|
|
55
|
+
in a second column that is `hiddenAtXs: true` with `breakpoints.lg.hidden:
|
|
56
|
+
false`, and keep the dialog for `xs`–`md`.
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
"rows": [
|
|
60
|
+
{ "id": "row-main", "columns": [
|
|
61
|
+
{ "id": "col-list", "items": [ /* grid */ ], "spanAtXs": 12, "breakpoints": { "lg": { "span": 7 } } },
|
|
62
|
+
{ "id": "col-detail", "hiddenAtXs": true, "breakpoints": { "lg": { "hidden": false, "span": 5 } },
|
|
63
|
+
"items": [ /* detail card bound to page.detailRecord */ ] }
|
|
64
|
+
] }
|
|
65
|
+
]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Device-restricted menu items
|
|
69
|
+
|
|
70
|
+
A menu node's `visibility.devices`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
"visibility": { "visible": true, "enabled": true, "devices": ["desktop", "tablet"] }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`[]` = every device. Use this for a bulk-admin screen you don't want on phones.
|
|
77
|
+
|
|
78
|
+
## Page variants (advanced)
|
|
79
|
+
|
|
80
|
+
A page can carry `variants` — alternate layouts selected by role, device class,
|
|
81
|
+
or a route param:
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
"variants": [
|
|
85
|
+
{ "id": "mobile", "rule": { "deviceClass": "mobile" }, "rows": [ /* a trimmed layout */ ] }
|
|
86
|
+
]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Use this only when the mobile experience is genuinely a different screen, not
|
|
90
|
+
just a reflow. For most list pages the default stacking plus a couple of
|
|
91
|
+
`hiddenAtXs` flags is enough.
|
|
92
|
+
|
|
93
|
+
## How to verify it worked
|
|
94
|
+
|
|
95
|
+
`erp plugin test` validates the breakpoint shape. For the real check, publish and
|
|
96
|
+
open the page in the ERP, then use your browser's device-emulation to switch
|
|
97
|
+
between phone, tablet, and desktop widths. Confirm:
|
|
98
|
+
|
|
99
|
+
- nothing overflows the viewport horizontally (the page body must never
|
|
100
|
+
scroll sideways — only inner containers);
|
|
101
|
+
- every action reachable on desktop is reachable on mobile (even if in a
|
|
102
|
+
different place);
|
|
103
|
+
- text stays legible (no fixed pixel widths forcing tiny text).
|
|
104
|
+
|
|
105
|
+
## Common mistakes
|
|
106
|
+
|
|
107
|
+
| Symptom | Cause | Fix |
|
|
108
|
+
| --- | --- | --- |
|
|
109
|
+
| page scrolls sideways on mobile | a fixed-width block or `minWidth` on a container | use relative units; let the grid scroll inside its own container |
|
|
110
|
+
| `xs` override ignored | put an `xs` key inside `breakpointOverrides` | there is no `xs` key there — use `hiddenAtXs` / `spanAtXs` |
|
|
111
|
+
| desktop-only panel shows on mobile | set `breakpoints.lg.hidden:false` without `hiddenAtXs:true` | you must hide at `xs` first, then re-show |
|
|
112
|
+
| a dialog is unusable on a phone | fixed `minWidth: 520px` on the dialog content | use `max-width: 100%` / flexible widths |
|
|
113
|
+
|
|
114
|
+
## What to read next
|
|
115
|
+
|
|
116
|
+
- [Build a page](./build-a-page.md)
|
|
117
|
+
- [Add menus](./add-menus.md)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Seed config and reference data on install
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Seed config and reference data on install
|
|
7
|
+
|
|
8
|
+
## What you're doing
|
|
9
|
+
|
|
10
|
+
Making data rows travel **with** the `.spk` instead of a manual per-tenant
|
|
11
|
+
`POST`. Two things need seeding:
|
|
12
|
+
|
|
13
|
+
1. **reference data** you own — equipment categories, status pick-lists, default
|
|
14
|
+
settings.
|
|
15
|
+
2. **rows in shared platform config tables** — `entity_status_date_sweep_config`,
|
|
16
|
+
`entity_aggregation_config`, `entity_cadence_config`, etc. — that make your
|
|
17
|
+
scheduled jobs exist.
|
|
18
|
+
|
|
19
|
+
One file per data set under `spk-assembly/metadata/seed-data/`. The
|
|
20
|
+
`PluginDataSeedInstaller` upserts them on every install, upgrade, and
|
|
21
|
+
auto-provision — **idempotently**, keyed by the fields you nominate.
|
|
22
|
+
|
|
23
|
+
## The complete example
|
|
24
|
+
|
|
25
|
+
`spk-assembly/metadata/seed-data/office-equipment-categories.json` —
|
|
26
|
+
[real file](../tutorial/example-plugin/spk-assembly/metadata/seed-data/office-equipment-categories.json):
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"entity": "oeq_category",
|
|
31
|
+
"keyFields": ["category_code"],
|
|
32
|
+
"source": "office-equipment",
|
|
33
|
+
"rows": [
|
|
34
|
+
{ "category_code": "LAPTOP", "category_name": "Laptops", "description": "Portable computers", "status": "ACTIVE", "seeded_by": "office-equipment" },
|
|
35
|
+
{ "category_code": "MONITOR", "category_name": "Monitors", "description": "External displays", "status": "ACTIVE", "seeded_by": "office-equipment" },
|
|
36
|
+
{ "category_code": "PHONE", "category_name": "Phones", "description": "Mobile handsets", "status": "ACTIVE", "seeded_by": "office-equipment" },
|
|
37
|
+
{ "category_code": "HEADSET", "category_name": "Headsets", "description": "Audio headsets", "status": "ACTIVE", "seeded_by": "office-equipment" }
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Line by line
|
|
43
|
+
|
|
44
|
+
- **`entity`** — the target entity `name`. It must already exist — ship it in
|
|
45
|
+
`metadata/entities/` if your plugin owns it.
|
|
46
|
+
- **`keyFields`** — the natural key. On re-install, a row matching **all**
|
|
47
|
+
`keyFields` is *updated only if a non-key value changed*, otherwise inserted.
|
|
48
|
+
Never duplicated. Choose a key that is genuinely unique (`category_code`, not
|
|
49
|
+
`category_name`).
|
|
50
|
+
- **`source`** — your plugin id. Written into a `seeded_by` field if the entity
|
|
51
|
+
has one — handy for `WHERE seeded_by = 'office-equipment'` cleanup later.
|
|
52
|
+
- **`rows`** — plain field maps. **No tenant id** — the installer supplies it.
|
|
53
|
+
|
|
54
|
+
## Idempotency in practice
|
|
55
|
+
|
|
56
|
+
Publish the plugin, then publish it again with a fifth category added. The
|
|
57
|
+
installer log shows:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
PluginDataSeedInstaller: office-equipment-categories.json -> entity oeq_category (tenant 2): 1 created, 0 updated, 4 unchanged
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The four existing rows are left alone; only the new one is inserted. Verified
|
|
64
|
+
against the tutorial module — the first install logged `4 created`, and no
|
|
65
|
+
re-run ever duplicates them.
|
|
66
|
+
|
|
67
|
+
## Seeding shared platform config (the important use)
|
|
68
|
+
|
|
69
|
+
The job guides ([reminder](./add-a-reminder-job.md),
|
|
70
|
+
[aggregation](./add-an-aggregation-job.md), [cadence](./add-a-cadence-job.md),
|
|
71
|
+
[compliance](./add-a-compliance-job.md)) all seed a row into a shared config
|
|
72
|
+
entity. For those you also ship:
|
|
73
|
+
|
|
74
|
+
1. **the shared config entity file** (`entity_status_date_sweep_config.json`
|
|
75
|
+
etc.) so a fresh environment that predates it gains the table. The installer
|
|
76
|
+
is idempotent-additive: an existing table just gains any missing columns.
|
|
77
|
+
Ship only the additive fields your rows need — see the tutorial's
|
|
78
|
+
[`entity_status_date_sweep_config.json`](../tutorial/example-plugin/spk-assembly/metadata/entities/entity_status_date_sweep_config.json).
|
|
79
|
+
2. **an `AFTER_CREATE` register rule** on the config entity, calling
|
|
80
|
+
`ensure<Job>Registered`, so the first seeded row auto-registers the job. (The
|
|
81
|
+
status-date-sweep job's register rule ships with the platform; the others you
|
|
82
|
+
ship yourself — see each job guide.)
|
|
83
|
+
|
|
84
|
+
## Ground yourself first
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
erp schema pull plugin-seed-data
|
|
88
|
+
erp examples patterns --kind jobs # "install-lifecycle-data-seeding"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## How to verify it worked
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
erp schema validate spk-assembly/metadata/seed-data/office-equipment-categories.json --schema plugin-seed-data
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
OK — ... matches schema "plugin-seed-data"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
After publishing:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
erp api get "/api/v1/entities/oeq_category/records/query?size=10"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{ "rows": [
|
|
109
|
+
{ "id": 1, "category_code": "LAPTOP", "category_name": "Laptops", "status": "ACTIVE", "seeded_by": "office-equipment" },
|
|
110
|
+
{ "id": 2, "category_code": "MONITOR", "category_name": "Monitors", "status": "ACTIVE", "seeded_by": "office-equipment" }
|
|
111
|
+
], "total": 4 }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Common mistakes
|
|
115
|
+
|
|
116
|
+
| Symptom | Cause | Fix |
|
|
117
|
+
| --- | --- | --- |
|
|
118
|
+
| re-install duplicates rows | `keyFields` not actually unique | pick a real key |
|
|
119
|
+
| seed fails: entity not found | didn't ship the `metadata/entities/*.json` | ship it |
|
|
120
|
+
| job doesn't register despite seeding | no `AFTER_CREATE` register rule (for aggregation/cadence/compliance) | ship it |
|
|
121
|
+
| a `null` in `keyFields` matches nothing / everything | key field is optional and unset in some rows | keep key fields non-null in every row |
|
|
122
|
+
|
|
123
|
+
## What to read next
|
|
124
|
+
|
|
125
|
+
- [Add a scheduled reminder job](./add-a-reminder-job.md)
|
|
126
|
+
- Recipe: [install-lifecycle data seeding](../recipes/install-lifecycle-data-seeding.md)
|
|
127
|
+
- Tutorial chapter 2: [the category entity + seeded data](../tutorial/02-category-entity-and-seed.md)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Set up the SDK and connect to an environment
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Set up the SDK and connect to an environment
|
|
7
|
+
|
|
8
|
+
## What you're doing
|
|
9
|
+
|
|
10
|
+
Before you can build anything you need the **plugin SDK** talking to **your ERP**.
|
|
11
|
+
The SDK is two programs:
|
|
12
|
+
|
|
13
|
+
- **the `erp` CLI** — scaffolds plugins, validates them, and publishes them to your ERP.
|
|
14
|
+
- **the `erp-plugin-language` MCP server** — the thing your AI coding agent (Claude
|
|
15
|
+
Code, Cursor, Codex) calls to look up schemas, widgets, and worked examples so
|
|
16
|
+
it doesn't have to guess.
|
|
17
|
+
|
|
18
|
+
An **environment** (or *env*) is one named ERP you talk to — `dev`, `staging`,
|
|
19
|
+
`prod`. The CLI remembers a base URL and a login token per env in
|
|
20
|
+
`~/.erp/config.json`, so you can switch between them with one command.
|
|
21
|
+
|
|
22
|
+
You do **not** need access to the ERP's source code. Everything the tools need to
|
|
23
|
+
ground themselves — JSON Schemas, the catalog of platform capabilities, curated
|
|
24
|
+
examples, these docs — ships in an **authoring bundle** that the SDK reads
|
|
25
|
+
offline and can refresh from your ERP.
|
|
26
|
+
|
|
27
|
+
## The complete sequence
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# 1. point the CLI at your ERP and give the env a name
|
|
31
|
+
erp env use dev --base-url https://erp.example.com
|
|
32
|
+
|
|
33
|
+
# 2. log in (opens your browser; RFC 8628 device-authorization grant)
|
|
34
|
+
erp login
|
|
35
|
+
|
|
36
|
+
# 3. confirm who you are and which tenant you're on
|
|
37
|
+
erp whoami
|
|
38
|
+
|
|
39
|
+
# 4. pull this env's authoring bundle into ~/.erp/cache/
|
|
40
|
+
erp env sync
|
|
41
|
+
|
|
42
|
+
# 5. install the MCP server config into the current project folder
|
|
43
|
+
erp mcp install
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
> Until the SDK is published to npm you run it directly. Everywhere these docs
|
|
47
|
+
> say `erp`, substitute `node <sdk>/tools/erp-cli/erp.mjs`. Set a shell alias:
|
|
48
|
+
> `alias erp='node /path/to/tools/erp-cli/erp.mjs'`.
|
|
49
|
+
|
|
50
|
+
## Line by line
|
|
51
|
+
|
|
52
|
+
**`erp env use dev --base-url https://erp.example.com`** creates an env named
|
|
53
|
+
`dev` and makes it current. Run it again with a different name to add another
|
|
54
|
+
env; `erp env list` shows them all with a `*` next to the current one. `erp env
|
|
55
|
+
use staging` (no `--base-url`) just switches to an env you already defined.
|
|
56
|
+
|
|
57
|
+
**`erp login`** opens your browser, you approve the CLI, and an access token +
|
|
58
|
+
refresh token are written to `~/.erp/config.json` (file mode `0600`). Networked
|
|
59
|
+
commands auto-refresh an expired access token, so you rarely log in again. In CI,
|
|
60
|
+
skip the browser: set `ERP_TOKEN=<access token>` and the CLI uses that instead.
|
|
61
|
+
|
|
62
|
+
**`erp whoami`** prints the env, base URL, tenant id, your user id, and your
|
|
63
|
+
roles. This is the fastest check that your session is live and pointed where you
|
|
64
|
+
think.
|
|
65
|
+
|
|
66
|
+
**`erp env sync`** downloads the env's authoring bundle (schemas, the frozen
|
|
67
|
+
capability catalog, blocks, validators, docs, plus the live list of entity and
|
|
68
|
+
theme names in your tenant) into `~/.erp/cache/<env>/`. If your ERP doesn't
|
|
69
|
+
expose the bundle endpoint yet this is a no-op and the SDK falls back to the
|
|
70
|
+
bundle shipped inside the SDK package — you can keep going. Re-run it whenever
|
|
71
|
+
your ERP is upgraded. `erp env sync --check` reports drift without downloading.
|
|
72
|
+
|
|
73
|
+
**`erp mcp install`** writes a `.mcp.json` in the current directory pointing at
|
|
74
|
+
the `erp-plugin-language` MCP server. Your AI agent picks it up automatically the
|
|
75
|
+
next time it opens this folder.
|
|
76
|
+
|
|
77
|
+
## How to verify it worked
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
erp whoami
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Expected — a JSON blob like this (your values differ):
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"env": "dev",
|
|
88
|
+
"baseUrl": "https://erp.example.com",
|
|
89
|
+
"tenantId": 2,
|
|
90
|
+
"valid": true,
|
|
91
|
+
"userId": "you@example.com",
|
|
92
|
+
"roles": ["HR Administrator", "Studio Staff"]
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`"valid": true` means the token is good. Then check the MCP wiring:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cat .mcp.json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
You should see a server entry named `erp-plugin-language`. Restart your AI agent
|
|
103
|
+
in this folder and ask it to call `erp_list_schemas` — it should return a list
|
|
104
|
+
of schema names, not an error.
|
|
105
|
+
|
|
106
|
+
## Common mistakes
|
|
107
|
+
|
|
108
|
+
| Symptom | Cause | Fix |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `erp whoami` says `"valid": false` | token expired and no refresh token | `erp login` again |
|
|
111
|
+
| `could not reach <url>` | wrong base URL, or `/` mangled by Git Bash on Windows | check `erp env list`; on Git Bash set `MSYS_NO_PATHCONV=1` |
|
|
112
|
+
| `erp env sync` downloads nothing | your ERP has no `/api/v1/dev/bundle` endpoint yet | fine — the SDK's built-in bundle is used |
|
|
113
|
+
| AI agent can't see the `erp_*` tools | `.mcp.json` written in a different folder, or agent not restarted | run `erp mcp install` in your project root, restart the agent |
|
|
114
|
+
| `--tenant 5` "does not re-authenticate you" errors | `--tenant` only overrides the header; your token must already be valid for that tenant | log in as a user who has access to that tenant |
|
|
115
|
+
|
|
116
|
+
## What to read next
|
|
117
|
+
|
|
118
|
+
- [Create a plugin from scratch](./create-a-plugin.md)
|
|
119
|
+
- [Use the MCP server with an AI agent](./use-the-mcp-server.md)
|
|
120
|
+
- [The tutorial](../tutorial/index.md) builds a real module end to end.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use the MCP server with an AI agent
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Use the MCP server with an AI agent
|
|
7
|
+
|
|
8
|
+
## What you're doing
|
|
9
|
+
|
|
10
|
+
Letting your AI coding agent (Claude Code, Cursor, Codex) build plugins without
|
|
11
|
+
guessing. The `erp-plugin-language` **MCP server** exposes tools the agent calls
|
|
12
|
+
to fetch the real schemas, the real widget catalog, real worked examples, and to
|
|
13
|
+
validate what it wrote — all grounded in *your* ERP's authoring bundle.
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
erp mcp install # writes .mcp.json in the current folder
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Restart your agent in that folder. It now has the `erp_*` tools.
|
|
22
|
+
|
|
23
|
+
## The tools, and when to reach for each
|
|
24
|
+
|
|
25
|
+
### Grounding — before writing an artifact
|
|
26
|
+
|
|
27
|
+
| Tool | Returns | Use when |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `erp_list_schemas` | every schema name | "what artifact types exist?" |
|
|
30
|
+
| `erp_get_schema {"name":"entity-definition"}` | one full JSON Schema | before writing any entity / page / provider / rule / workflow / seed file |
|
|
31
|
+
| `erp_list_blocks` / `erp_list_blocks {"type":"core.grid"}` | the widget catalog; one widget's full property + event list | before adding a block to a page |
|
|
32
|
+
| `erp_list_grammar_rules` | the page/action-chain grammar | when an action chain isn't behaving |
|
|
33
|
+
| `erp_platform_catalog` / `erp_platform_describe {"className":"..."}` | the live inventory of platform engines, REST mappings, tables; real method signatures for extension points | before assuming "there's no built-in X" |
|
|
34
|
+
|
|
35
|
+
### Examples — copy a proven shape
|
|
36
|
+
|
|
37
|
+
| Tool | Returns |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| `erp_examples_patterns` | curated **named shapes** (`single-stage-self-decide`, `related-record-lookup-field`, `n-days-before-reminder-sweep`, …), each pointing at a real shipped file |
|
|
40
|
+
| `erp_search_examples {"pattern":"employee-search","kind":"data-service"}` | real shipped metadata files matched by name |
|
|
41
|
+
| `erp_search_docs {"query":"..."}` | these docs + the live engine catalog + Java contracts |
|
|
42
|
+
|
|
43
|
+
### Validation — after writing
|
|
44
|
+
|
|
45
|
+
| Tool | Checks |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `erp_validate_page` | one page against the contract + live block registry |
|
|
48
|
+
| `erp_validate_plugin_pages` | every page in a plugin dir |
|
|
49
|
+
| `erp_validate_connector_definition` | a connector JSON |
|
|
50
|
+
| `erp_lint_plugin_semantics {"pluginDir":"..."}` | PAGE-DS-001 / DS-001 / I18N-001 / SEED-001 / JOB-001 across the whole plugin |
|
|
51
|
+
| `erp_plugin_scaffold` | a starting skeleton for a plugin or extension point |
|
|
52
|
+
|
|
53
|
+
### Diagnostics
|
|
54
|
+
|
|
55
|
+
| Tool | Returns |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| `erp_tail_logs {"grep":"<X-Correlation-Id>"}` | the server-side stack trace for a failed call (join on the response's `X-Correlation-Id` header) |
|
|
58
|
+
|
|
59
|
+
## A worked interaction
|
|
60
|
+
|
|
61
|
+
You: *"Add a category lookup field to the equipment form."*
|
|
62
|
+
|
|
63
|
+
A well-behaved agent will:
|
|
64
|
+
|
|
65
|
+
1. `erp_examples_patterns` → finds `related-record-lookup-field`, reads the real
|
|
66
|
+
`promotions.json` it points at;
|
|
67
|
+
2. `erp_get_schema {"name":"data-service-definition"}` and
|
|
68
|
+
`erp_search_examples {"pattern":"employee-search","kind":"data-service"}` →
|
|
69
|
+
learns the `search` + `dataView` recipe;
|
|
70
|
+
3. writes `oeq-category-select-options-view.json`, `oeq-category-search.json`,
|
|
71
|
+
and the `core.lookup` block;
|
|
72
|
+
4. `erp_validate_plugin_pages` and `erp_lint_plugin_semantics` → confirms clean.
|
|
73
|
+
|
|
74
|
+
Every step is grounded, nothing is guessed.
|
|
75
|
+
|
|
76
|
+
## The verification-you-can-run trap
|
|
77
|
+
|
|
78
|
+
Ask the agent to prove each claim with a command whose output you can see —
|
|
79
|
+
`erp plugin test`, `erp api get ...`, `erp api post .../execute` — not "it should
|
|
80
|
+
work now". If the agent can't show you the output, the step isn't done.
|
|
81
|
+
|
|
82
|
+
## How to verify the MCP wiring itself
|
|
83
|
+
|
|
84
|
+
Ask the agent to call `erp_list_schemas`. It should return a list of ~25 schema
|
|
85
|
+
names. If it errors or says the tool doesn't exist:
|
|
86
|
+
|
|
87
|
+
- `cat .mcp.json` — is there an `erp-plugin-language` entry?
|
|
88
|
+
- did you restart the agent in this folder?
|
|
89
|
+
- run `erp mcp install` again in the project root.
|
|
90
|
+
|
|
91
|
+
## Common mistakes
|
|
92
|
+
|
|
93
|
+
| Symptom | Cause | Fix |
|
|
94
|
+
| --- | --- | --- |
|
|
95
|
+
| agent invents block properties | didn't call `erp_list_blocks` for that type | tell it to ground first |
|
|
96
|
+
| agent's page fails install on `metadata.dataSource` | skipped `erp_lint_plugin_semantics` | run it before publish |
|
|
97
|
+
| agent "can't find" a tool | `.mcp.json` in wrong folder / agent not restarted | `erp mcp install` in root, restart |
|
|
98
|
+
| schemas look stale | bundle not synced | `erp env sync` |
|
|
99
|
+
|
|
100
|
+
## What to read next
|
|
101
|
+
|
|
102
|
+
- [Set up the SDK](./set-up-the-sdk.md)
|
|
103
|
+
- [Validate and test a plugin](./validate-and-test.md)
|
|
104
|
+
- [Expose a plugin operation as an AI tool](./expose-a-plugin-operation-as-an-ai-tool.md) — the *other* MCP concept: the ERP product's own embedded AI assistant calling your plugin at runtime, not your coding agent while you build
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Validate and test a plugin
|
|
3
|
+
audience: tenant
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Validate and test a plugin
|
|
7
|
+
|
|
8
|
+
## What you're doing
|
|
9
|
+
|
|
10
|
+
Catching mistakes before you publish. The SDK runs the **same checks the
|
|
11
|
+
platform runs at install time**, so a clean local run means a clean install.
|
|
12
|
+
Three commands, cheapest first.
|
|
13
|
+
|
|
14
|
+
## The complete sequence
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
erp schema validate spk-assembly/metadata/entities/oeq_equipment.json --schema entity-definition
|
|
18
|
+
erp plugin validate spk-assembly
|
|
19
|
+
erp plugin test spk-assembly
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Plus, over MCP, the semantic linter directly:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
erp_lint_plugin_semantics {"pluginDir": "developer-docs/tutorial/example-plugin/spk-assembly"}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Line by line
|
|
29
|
+
|
|
30
|
+
### `erp schema validate <file> --schema <name>`
|
|
31
|
+
|
|
32
|
+
Checks **one** JSON file against **one** schema. Use it while authoring a single
|
|
33
|
+
artifact. Fast, no backend needed.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
OK — spk-assembly/metadata/entities/oeq_equipment.json matches schema "entity-definition"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### `erp plugin validate <dir>`
|
|
40
|
+
|
|
41
|
+
Validates every **page** in the plugin against the page contract *and* the live
|
|
42
|
+
block registry (does each `blockType` exist, does each property it sets actually
|
|
43
|
+
belong to that block).
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
validated 3 page(s) — clean
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `erp plugin test <dir>`
|
|
50
|
+
|
|
51
|
+
Runs `plugin validate` **plus** the semantic lints — the checks that catch the
|
|
52
|
+
"looks fine, silently broken" class of bug:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Plugin Tests
|
|
56
|
+
────────────────────────────────────────
|
|
57
|
+
✓ page:equipment-catalog.json
|
|
58
|
+
✓ page:equipment-checkout.json
|
|
59
|
+
✓ page:equipment-register.json
|
|
60
|
+
✓ plugin.json:valid-json
|
|
61
|
+
1 passed, 0 failed, 0 semantic warning(s), 3 skipped
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## The semantic lint codes — what each one catches
|
|
65
|
+
|
|
66
|
+
| Code | Catches | Fix |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| **PAGE-DS-001** | a page has a `core.grid` but `metadata.dataSource` is missing or doesn't match any `metadata/provider/*.json` `name` — the "0 rows, no error" bug | see [Wire a page's data](./wire-a-pages-data.md) |
|
|
69
|
+
| **DS-001** | a Data Service `filter` compares a value of the wrong type (e.g. a string against a numeric column) | fix the filter, or set `valueType` |
|
|
70
|
+
| **I18N-001** | a page references an i18n key with no entry in `metadata/i18n/en.json` (or a placeholder value) | add the key — see [Add i18n](./add-i18n.md) |
|
|
71
|
+
| **SEED-001** | a `metadata/seed-data/*.json` targets an entity the plugin doesn't ship and that isn't a known shared config entity, or `keyFields` look non-unique | ship the entity, or fix the key |
|
|
72
|
+
| **JOB-001** | a new bespoke `ErpJobContribution` Java class where a generic sweep engine + JSON config would do | delete the Java, add a config row — see the job guides |
|
|
73
|
+
| page-semantic-lint | a foreign-key field bound to a plain text/number input instead of `core.lookup`; decorative filters that aren't wired | use `core.lookup`; wire the filter to `externalFilter` |
|
|
74
|
+
|
|
75
|
+
These are **warnings**, not hard failures — review each. But the platform's
|
|
76
|
+
install-time equivalents of PAGE-DS-001 / I18N-001 / SEED-001 **do** block a bad
|
|
77
|
+
install, so treat them as errors.
|
|
78
|
+
|
|
79
|
+
## Wire it into CI
|
|
80
|
+
|
|
81
|
+
The docs in this tree keep every example honest with a runner that validates
|
|
82
|
+
every artifact and runs `erp plugin test`:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
node developer-docs/examples/test-examples.mjs
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
validating tutorial module artifacts against live schemas...
|
|
90
|
+
ok tutorial/entities/oeq_equipment.json
|
|
91
|
+
...
|
|
92
|
+
running `erp plugin test` on the tutorial module...
|
|
93
|
+
4 passed, 0 failed, 0 semantic warning(s), 3 skipped
|
|
94
|
+
|
|
95
|
+
All examples valid.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Run it in CI on every change to `developer-docs/`. A broken example fails the
|
|
99
|
+
build.
|
|
100
|
+
|
|
101
|
+
## How to verify it worked
|
|
102
|
+
|
|
103
|
+
A green `erp plugin test` and a green `test-examples.mjs`. That's the bar for
|
|
104
|
+
"safe to publish".
|
|
105
|
+
|
|
106
|
+
## Common mistakes
|
|
107
|
+
|
|
108
|
+
| Symptom | Cause | Fix |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `erp plugin validate` says "no page JSON found" | you pointed it at the plugin root, not `spk-assembly` | pass `<plugin>/spk-assembly` |
|
|
111
|
+
| `erp schema validate` fails on a page file | page files are wrapped; `--schema page` checks the inner contract | rely on `erp plugin validate` / `test` for pages |
|
|
112
|
+
| lint clean locally, install still fails | a check the platform runs that the SDK doesn't (rare) | read the install error; report it |
|
|
113
|
+
| `erp plugin test` skips `security-rule-scan` etc. | those need extra inputs | fine for a JSON-only plugin |
|
|
114
|
+
|
|
115
|
+
## What to read next
|
|
116
|
+
|
|
117
|
+
- [Publish and upgrade a plugin](./publish-and-upgrade.md)
|
|
118
|
+
- [Wire a page's data](./wire-a-pages-data.md)
|
|
119
|
+
- Troubleshooting: [known gotchas](../troubleshooting/known-gotchas.md)
|