@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,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* resolveAuthoringRoot() — the single resource-root abstraction shared by the
|
|
3
|
+
* ERP developer CLI (tools/erp-cli/erp.mjs) and the MCP server
|
|
4
|
+
* (tools/erp-mcp-server/index.mjs).
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS
|
|
7
|
+
* A tenant / third-party plugin developer installs the SDK (CLI + MCP),
|
|
8
|
+
* `erp login`s to a deployed ERP, and authors plugins WITH NO ACCESS TO THE
|
|
9
|
+
* PLATFORM MONOREPO — platform source is secret to them. Before this module
|
|
10
|
+
* every schema/doc/example/catalog read in the CLI and MCP server was a
|
|
11
|
+
* hardcoded repo-relative path, so a packaged install had nothing to read.
|
|
12
|
+
*
|
|
13
|
+
* TWO MODES
|
|
14
|
+
* - monorepo (default when a repo checkout is detected, or ERP_SDK_MODE=monorepo):
|
|
15
|
+
* EXACT prior behavior — read live from backend/platform-runtime/schema,
|
|
16
|
+
* ai/, backend/modules, and re-derive the engine catalog from Java source.
|
|
17
|
+
* Platform developers see everything, byte-identical.
|
|
18
|
+
* - packaged (ERP_SDK_MODE=packaged, or no repo present):
|
|
19
|
+
* read ONLY from the shipped authoring bundle (erp-authoring-bundle/,
|
|
20
|
+
* produced by `erp bundle build`), overlaid by a local cache dir
|
|
21
|
+
* ~/.erp/cache/<envKey>/ populated by `erp env sync` (later batch).
|
|
22
|
+
* NO Java source scan, ever, on the client.
|
|
23
|
+
*
|
|
24
|
+
* The classification boundary between the two = the secret-code boundary.
|
|
25
|
+
* Nothing under ai/, no Java, no internal gap-analysis doc is ever in the bundle.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
import os from "node:os";
|
|
30
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
31
|
+
|
|
32
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
33
|
+
export const REPO_ROOT = path.resolve(__dirname, "..", "..");
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Schema registry — every real, formal JSON Schema the platform ships today.
|
|
37
|
+
// Single source of truth: both erp.mjs's SCHEMA_FILES and index.mjs's SCHEMAS
|
|
38
|
+
// are derived from this so they can never drift, and the bundle emitter copies
|
|
39
|
+
// exactly this set into erp-authoring-bundle/schemas/.
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
export const SCHEMA_REGISTRY = {
|
|
42
|
+
page: {
|
|
43
|
+
file: "frontend/packages/page-engine/schema/page-definition.schema.json",
|
|
44
|
+
description: "A page's rows/overlays/variants/designer metadata — the top-level page.json contract.",
|
|
45
|
+
},
|
|
46
|
+
"page-template": {
|
|
47
|
+
file: "frontend/packages/page-engine/schema/page-template.schema.json",
|
|
48
|
+
description: "A reusable page template (see the generic-capability generator pattern).",
|
|
49
|
+
},
|
|
50
|
+
"block-definition": {
|
|
51
|
+
file: "frontend/packages/block-engine/schema/block-definition.schema.json",
|
|
52
|
+
description: "A block TYPE's own declared property/event contract (what core.grid, core.container, etc. accept).",
|
|
53
|
+
},
|
|
54
|
+
"block-instance": {
|
|
55
|
+
file: "frontend/packages/block-engine/schema/block-instance-config.schema.json",
|
|
56
|
+
description: "One block INSTANCE inside a page (instanceId/blockType/properties/events) — same shape validate-page-properties.mjs checks.",
|
|
57
|
+
},
|
|
58
|
+
form: {
|
|
59
|
+
file: "frontend/packages/form-engine/schema/form-definition.schema.json",
|
|
60
|
+
description: "A form's field/section/validation contract.",
|
|
61
|
+
},
|
|
62
|
+
action: {
|
|
63
|
+
file: "frontend/packages/action-engine/schema/action-definition.schema.json",
|
|
64
|
+
description: "A page-JSON/UI action-chain definition (frontend action engine — distinct from the server-side Action Engine, see ai/patterns/action-engine-server-side.md).",
|
|
65
|
+
},
|
|
66
|
+
rule: {
|
|
67
|
+
file: "frontend/packages/rule-engine/schema/rule-definition.schema.json",
|
|
68
|
+
description: "Frontend rule-engine definition (page-level conditional visibility/computed-value rules).",
|
|
69
|
+
},
|
|
70
|
+
"plugin-manifest": {
|
|
71
|
+
file: "backend/platform-runtime/schema/spk-plugin-manifest.schema.json",
|
|
72
|
+
description: "A real .spk business-application plugin's own spk-assembly/plugin.json contract (id/version/permissions/roles/dependencies).",
|
|
73
|
+
},
|
|
74
|
+
"composite-pack-manifest": {
|
|
75
|
+
file: "frontend/packages/plugin-sdk/schema/plugin-manifest.schema.json",
|
|
76
|
+
description: "The newer composite-pack / Country-or-Industry-Pack / Code-Plugin-style manifest contract (contractVersion/publisher/distribution/12 extension-point contributions) — a genuinely different artifact type from an ordinary .spk plugin.json (use 'plugin-manifest' for that).",
|
|
77
|
+
},
|
|
78
|
+
"entity-definition": {
|
|
79
|
+
file: "backend/platform-runtime/schema/entity-definition.schema.json",
|
|
80
|
+
description: "A real Entity Engine entity (engine-entity) — fields/relationships, produces real DDL, zero hand-written CRUD Java.",
|
|
81
|
+
},
|
|
82
|
+
"entity-rule-definition": {
|
|
83
|
+
file: "backend/platform-runtime/schema/entity-rule-definition.schema.json",
|
|
84
|
+
description: "A declarative entity-lifecycle rule (engine-rule) — conditions/actions firing at BEFORE_CREATE/AFTER_UPDATE/etc.",
|
|
85
|
+
},
|
|
86
|
+
"workflow-definition": {
|
|
87
|
+
file: "backend/platform-runtime/schema/workflow-definition.schema.json",
|
|
88
|
+
description: "A real multi-stage approval workflow (engine-workflow) — stagesJson/tasksJson/transitionsJson (each itself a JSON-encoded string) + a top-level approvalPermissions array.",
|
|
89
|
+
},
|
|
90
|
+
menu: {
|
|
91
|
+
file: "backend/platform-runtime/schema/menu-definition.schema.json",
|
|
92
|
+
description: "Menu Designer's content model (@erp/menu-engine) — hierarchical sidenav tree (nodes/navigation/visibility/security/badge/personalization/advanced).",
|
|
93
|
+
},
|
|
94
|
+
"provider-definition": {
|
|
95
|
+
file: "backend/platform-runtime/schema/provider-definition.schema.json",
|
|
96
|
+
description: "Provider Designer's content model (ProviderAuthoringController) — kind/connectionRef/basePath/supports.",
|
|
97
|
+
},
|
|
98
|
+
"mobile-nav": {
|
|
99
|
+
file: "backend/platform-runtime/schema/mobile-nav-definition.schema.json",
|
|
100
|
+
description: "Mobile Navigation Designer's content model (@erp/mobile-nav-engine) — a FLAT item list (bottom-tabs/drawer/hybrid), deliberately distinct from menu-definition.",
|
|
101
|
+
},
|
|
102
|
+
"data-service-definition": {
|
|
103
|
+
file: "backend/platform-runtime/schema/data-service-definition.schema.json",
|
|
104
|
+
description: "Data Service layer's content model (engine-entity DataServiceSpec) — provider/entity/dataView source + filters/parameters/composite steps.",
|
|
105
|
+
},
|
|
106
|
+
"data-view-definition": {
|
|
107
|
+
file: "backend/platform-runtime/schema/data-view-definition.schema.json",
|
|
108
|
+
description: "Data View layer's content model (engine-entity DataViewSpec) — a real multi-table SQL join over plain physical tables.",
|
|
109
|
+
},
|
|
110
|
+
"report-definition": {
|
|
111
|
+
file: "backend/platform-runtime/schema/report-definition.schema.json",
|
|
112
|
+
description: "Report Designer's content model (@erp/report-engine) — band/element grid.",
|
|
113
|
+
},
|
|
114
|
+
"print-template-definition": {
|
|
115
|
+
file: "backend/platform-runtime/schema/print-template-definition.schema.json",
|
|
116
|
+
description: "Print Template Designer's content model (@erp/print-engine) — Report's band/element sibling with a physical mm page + merge-tag text. Dashboards have NO separate schema — use `page`.",
|
|
117
|
+
},
|
|
118
|
+
"connector-definition": {
|
|
119
|
+
file: "backend/platform-runtime/schema/connector-definition.schema.json",
|
|
120
|
+
description: "Generic External Provider Connector Engine's content model (engine-provider-connector) — domain/providerKey/connectorKind(DIRECT|HTTP)/baseUrl/auth/request-template/response-mapping.",
|
|
121
|
+
},
|
|
122
|
+
"plugin-seed-data": {
|
|
123
|
+
file: "backend/platform-runtime/schema/plugin-seed-data.schema.json",
|
|
124
|
+
description: "metadata/seed-data/*.json — Entity-Engine data rows a plugin seeds on install/upgrade, idempotently (upsert by keyFields). See ai/patterns/plugin-install-data-seeding.md.",
|
|
125
|
+
},
|
|
126
|
+
"entity-status-date-sweep-config": {
|
|
127
|
+
file: "backend/platform-runtime/schema/entity-status-date-sweep-config.schema.json",
|
|
128
|
+
description: "One config row for the generic engine-entity.status-date-sweep job — flip an entity's own status/flag field once its own date column crosses now (±day_offset days).",
|
|
129
|
+
},
|
|
130
|
+
"entity-aggregation-config": {
|
|
131
|
+
file: "backend/platform-runtime/schema/entity-aggregation-config.schema.json",
|
|
132
|
+
description: "One config row for the generic engine-entity.aggregation-sweep job — fold count/sum/avg/min/max across a source entity and upsert the result into a target entity.",
|
|
133
|
+
},
|
|
134
|
+
"entity-cadence-config": {
|
|
135
|
+
file: "backend/platform-runtime/schema/entity-cadence-config.schema.json",
|
|
136
|
+
description: "One config row for the generic engine-entity.cadence-generator job — create new target-entity rows on a cadence, source-driven or standalone, deduped.",
|
|
137
|
+
},
|
|
138
|
+
"entity-compliance-config": {
|
|
139
|
+
file: "backend/platform-runtime/schema/entity-compliance-config.schema.json",
|
|
140
|
+
description: "One config row for the generic engine-entity.compliance-sweep job — evaluate a rule-engine boolean expression against every row of an entity and flag/escalate offenders.",
|
|
141
|
+
},
|
|
142
|
+
"entity-cross-plugin-action-config": {
|
|
143
|
+
file: "backend/platform-runtime/schema/entity-cross-plugin-action-config.schema.json",
|
|
144
|
+
description: "One config row for the generic engine-entity.cross-plugin-action job — a trigger_entity row entering a triggering status fans out create/update onto every related_entity row keyed back to it (offboarding fan-out; trigger may be another plugin's entity).",
|
|
145
|
+
},
|
|
146
|
+
"entity-document-generator-config": {
|
|
147
|
+
file: "backend/platform-runtime/schema/entity-document-generator-config.schema.json",
|
|
148
|
+
description: "One config row for the generic engine-entity.document-generator job — generate a structured-data artifact (json/csv) per eligible row via the FileService, store it in a DMS cabinet, and stamp the file id onto the row.",
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const REPO_REL_TO_NAME = Object.fromEntries(
|
|
153
|
+
Object.entries(SCHEMA_REGISTRY).map(([name, v]) => [v.file.replace(/\\/g, "/"), name]),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// Mode detection + bundle / cache locations
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
const BUNDLE_DIR_DEFAULT = path.join(REPO_ROOT, "tools", "erp-authoring-bundle");
|
|
160
|
+
// A packaged npm install (@erp/plugin-sdk) ships its own bundle/ sibling
|
|
161
|
+
// directory next to erp-cli/ (mirrors tools/erp-cli + tools/erp-authoring-bundle
|
|
162
|
+
// being siblings under tools/ in the monorepo) — see packages/erp-plugin-sdk.
|
|
163
|
+
// Prefer it when present so a packaged install never depends on REPO_ROOT
|
|
164
|
+
// resolving anywhere real.
|
|
165
|
+
const OWN_BUNDLE_DIR = path.join(__dirname, "..", "bundle");
|
|
166
|
+
|
|
167
|
+
export function bundleDir() {
|
|
168
|
+
if (process.env.ERP_BUNDLE_DIR) return path.resolve(process.env.ERP_BUNDLE_DIR);
|
|
169
|
+
if (existsSync(OWN_BUNDLE_DIR)) return OWN_BUNDLE_DIR;
|
|
170
|
+
return BUNDLE_DIR_DEFAULT;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function cacheDir() {
|
|
174
|
+
return path.join(os.homedir(), ".erp", "cache", process.env.ERP_ENV_KEY || "default");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** monorepo | packaged. ERP_SDK_MODE overrides; else auto-detect a repo checkout. */
|
|
178
|
+
export function detectMode() {
|
|
179
|
+
const m = (process.env.ERP_SDK_MODE || "").toLowerCase();
|
|
180
|
+
if (m === "packaged") return "packaged";
|
|
181
|
+
if (m === "monorepo") return "monorepo";
|
|
182
|
+
return existsSync(path.join(REPO_ROOT, "backend", "platform-runtime", "schema")) ? "monorepo" : "packaged";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function isPackaged() {
|
|
186
|
+
return detectMode() === "packaged";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Throw a clear, actionable error for a feature that genuinely needs the monorepo. */
|
|
190
|
+
export function requireMonorepo(feature) {
|
|
191
|
+
if (isPackaged()) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`"${feature}" is not available in packaged SDK mode — it needs platform build tooling ` +
|
|
194
|
+
`(block-engine dist / page-validator / live engine-api logs) that is not shipped in the authoring bundle. ` +
|
|
195
|
+
`Deferred to a later SDK batch. Use schema/catalog/docs/examples/scaffold tools instead.`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** First existing path from the cache overlay then the bundle, else null. */
|
|
201
|
+
function overlaid(relFromRoot) {
|
|
202
|
+
const c = path.join(cacheDir(), relFromRoot);
|
|
203
|
+
if (existsSync(c)) return c;
|
|
204
|
+
const b = path.join(bundleDir(), relFromRoot);
|
|
205
|
+
return existsSync(b) ? b : null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// Resource resolvers — the only functions callers should use
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
/** Absolute path to a schema file, by its registry name. */
|
|
213
|
+
export function schemaPathByName(name) {
|
|
214
|
+
const entry = SCHEMA_REGISTRY[name];
|
|
215
|
+
if (!entry) throw new Error(`unknown schema "${name}". Known: ${Object.keys(SCHEMA_REGISTRY).join(", ")}`);
|
|
216
|
+
if (isPackaged()) {
|
|
217
|
+
const p = overlaid(path.join("schemas", `${name}.schema.json`));
|
|
218
|
+
if (!p) throw new Error(`schema "${name}" not found in authoring bundle (${bundleDir()}) — run \`erp bundle build\` or \`erp env sync\`.`);
|
|
219
|
+
return p;
|
|
220
|
+
}
|
|
221
|
+
return path.join(REPO_ROOT, entry.file);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Absolute path to a schema file addressed by its repo-relative path (reverse-maps to a name). */
|
|
225
|
+
export function schemaPathByRepoRel(repoRel) {
|
|
226
|
+
const name = REPO_REL_TO_NAME[repoRel.replace(/\\/g, "/")];
|
|
227
|
+
if (name) return schemaPathByName(name);
|
|
228
|
+
// Not in the registry (e.g. a $defs-only helper schema) — monorepo only.
|
|
229
|
+
requireMonorepo(`schema file ${repoRel}`);
|
|
230
|
+
return path.join(REPO_ROOT, repoRel);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function loadSchemaByName(name) {
|
|
234
|
+
return { name, path: SCHEMA_REGISTRY[name].file, description: SCHEMA_REGISTRY[name].description, schema: JSON.parse(readFileSync(schemaPathByName(name), "utf8")) };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Roots for `erp docs search` / `erp_search_docs`, each {dir, source, relBase}.
|
|
238
|
+
* relBase is what reported file paths are shown relative to — REPO_ROOT in
|
|
239
|
+
* monorepo mode (byte-identical to prior behavior), the doc dir in packaged mode. */
|
|
240
|
+
export function docsSearchRoots() {
|
|
241
|
+
if (isPackaged()) {
|
|
242
|
+
const roots = [];
|
|
243
|
+
const cache = path.join(cacheDir(), "docs");
|
|
244
|
+
const bun = path.join(bundleDir(), "docs");
|
|
245
|
+
if (existsSync(cache)) roots.push({ dir: cache, source: "developer-docs (synced)", relBase: cache });
|
|
246
|
+
if (existsSync(bun)) roots.push({ dir: bun, source: "developer-docs (bundled)", relBase: bun });
|
|
247
|
+
return roots;
|
|
248
|
+
}
|
|
249
|
+
return [{ dir: path.join(REPO_ROOT, "ai"), source: "ai/domains doc", relBase: REPO_ROOT }];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** backend/modules root for filename-substring example search. */
|
|
253
|
+
export function examplesModulesRoot() {
|
|
254
|
+
if (isPackaged()) {
|
|
255
|
+
return path.join(bundleDir(), "examples", "backend", "modules");
|
|
256
|
+
}
|
|
257
|
+
return path.join(REPO_ROOT, "backend", "modules");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Absolute path to a curated example file addressed by its repo-relative path. */
|
|
261
|
+
export function exampleFileAbs(repoRel) {
|
|
262
|
+
if (isPackaged()) {
|
|
263
|
+
return overlaid(path.join("examples", repoRel)) || path.join(bundleDir(), "examples", repoRel);
|
|
264
|
+
}
|
|
265
|
+
return path.join(REPO_ROOT, repoRel);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** The frozen engine catalog (packaged mode only). Returns null in monorepo mode. */
|
|
269
|
+
export function frozenCatalog() {
|
|
270
|
+
if (!isPackaged()) return null;
|
|
271
|
+
const p = overlaid("catalog.json");
|
|
272
|
+
if (!p) throw new Error(`engine catalog not found in authoring bundle (${bundleDir()}) — run \`erp bundle build\` or \`erp env sync\`.`);
|
|
273
|
+
return JSON.parse(readFileSync(p, "utf8"));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function readManifest() {
|
|
277
|
+
const p = overlaid("manifest.json") || path.join(bundleDir(), "manifest.json");
|
|
278
|
+
return existsSync(p) ? JSON.parse(readFileSync(p, "utf8")) : null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
// Block-engine / page-engine module resolution — the packaged-mode port.
|
|
283
|
+
//
|
|
284
|
+
// The page validator (tools/page-validator/validate-page-properties.mjs) and
|
|
285
|
+
// `erp blocks list` / erp_list_blocks need the REAL @erp/block-engine +
|
|
286
|
+
// @erp/page-engine validators (validateInstanceConfig / validatePageDefinition
|
|
287
|
+
// / coreBlockTypes / BlockRegistry). In monorepo mode those come straight from
|
|
288
|
+
// each package's built dist (byte-identical to prior behavior). In packaged
|
|
289
|
+
// mode they come from `validators/{block,page}-engine.mjs` — a self-contained
|
|
290
|
+
// esbuild bundle of that exact same dist entrypoint (every workspace dep
|
|
291
|
+
// inlined), emitted by `erp bundle build` and synced by `erp env sync`. Same
|
|
292
|
+
// real code, no repo, no npm install, no build step on the client.
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
const BLOCK_ENGINE_DIST = "frontend/packages/block-engine/dist/index.js";
|
|
295
|
+
const PAGE_ENGINE_DIST = "frontend/packages/page-engine/dist/index.js";
|
|
296
|
+
|
|
297
|
+
function engineModuleUrl(kind) {
|
|
298
|
+
const packagedRel = path.join("validators", `${kind}-engine.mjs`);
|
|
299
|
+
const monorepoRel = kind === "block" ? BLOCK_ENGINE_DIST : PAGE_ENGINE_DIST;
|
|
300
|
+
if (isPackaged()) {
|
|
301
|
+
const p = overlaid(packagedRel);
|
|
302
|
+
if (!p) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
`packaged-mode ${kind}-engine validator not found in authoring bundle (${bundleDir()}) — ` +
|
|
305
|
+
`run \`erp bundle build\` (monorepo) or \`erp env sync\` (deployed env) to populate validators/.`,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
return pathToFileURL(p).href;
|
|
309
|
+
}
|
|
310
|
+
const abs = path.join(REPO_ROOT, monorepoRel);
|
|
311
|
+
if (!existsSync(abs)) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`@erp/${kind}-engine has no built dist at ${monorepoRel} — build it first ` +
|
|
314
|
+
`(same dist-only-resolution requirement as @erp/block-adapter-mui).`,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
return pathToFileURL(abs).href;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function blockEngineModuleUrl() {
|
|
321
|
+
return engineModuleUrl("block");
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function pageEngineModuleUrl() {
|
|
325
|
+
return engineModuleUrl("page");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Packaged mode: absolute path to the frozen block catalog (blocks.json). Monorepo: null (derive live from the dist). */
|
|
329
|
+
export function blocksCatalogPath() {
|
|
330
|
+
if (!isPackaged()) return null;
|
|
331
|
+
const p = overlaid("blocks.json");
|
|
332
|
+
if (!p) {
|
|
333
|
+
throw new Error(
|
|
334
|
+
`block catalog (blocks.json) not found in authoring bundle (${bundleDir()}) — run \`erp bundle build\` or \`erp env sync\`.`,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
return p;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** A live/ per-environment JSON file synced from GET /api/v1/dev/bundle (packaged mode only). Returns null if absent. */
|
|
341
|
+
export function readLiveJson(name) {
|
|
342
|
+
const p = overlaid(path.join("live", name));
|
|
343
|
+
return p && existsSync(p) ? JSON.parse(readFileSync(p, "utf8")) : null;
|
|
344
|
+
}
|