@sparkerp/plugin-sdk 0.1.0 → 1.0.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/bundle/blocks.json +169 -0
- package/bundle/catalog.json +614 -5
- package/bundle/docs/applications/hcm/admin-access-policies.md +38 -0
- package/bundle/docs/applications/hcm/admin-approval-hierarchies.md +33 -0
- package/bundle/docs/applications/hcm/admin-data-transfer.md +40 -0
- package/bundle/docs/applications/hcm/admin-hcm-users.md +35 -0
- package/bundle/docs/applications/hcm/admin-logs.md +57 -0
- package/bundle/docs/applications/hcm/admin-permissions-catalog.md +64 -0
- package/bundle/docs/applications/hcm/ai-intelligence-overview.md +37 -0
- package/bundle/docs/applications/hcm/ai-intelligence-people-risk.md +33 -0
- package/bundle/docs/applications/hcm/ai-intelligence-recruitment.md +37 -0
- package/bundle/docs/applications/hcm/ai-intelligence-tools.md +39 -0
- package/bundle/docs/applications/hcm/analytics-operations.md +31 -0
- package/bundle/docs/applications/hcm/analytics-overview.md +31 -0
- package/bundle/docs/applications/hcm/analytics-people.md +36 -0
- package/bundle/docs/applications/hcm/analytics-tools.md +35 -0
- package/bundle/docs/applications/hcm/assets-audits.md +30 -0
- package/bundle/docs/applications/hcm/assets-custody.md +36 -0
- package/bundle/docs/applications/hcm/assets-inventory.md +34 -0
- package/bundle/docs/applications/hcm/assets-maintenance.md +25 -0
- package/bundle/docs/applications/hcm/assets-software-licenses.md +27 -0
- package/bundle/docs/applications/hcm/attendance-core.md +46 -0
- package/bundle/docs/applications/hcm/attendance-exceptions.md +33 -0
- package/bundle/docs/applications/hcm/attendance-location.md +25 -0
- package/bundle/docs/applications/hcm/attendance-policies.md +23 -0
- package/bundle/docs/applications/hcm/attendance-reports.md +20 -0
- package/bundle/docs/applications/hcm/benefits-allowances.md +39 -0
- package/bundle/docs/applications/hcm/benefits-analytics.md +25 -0
- package/bundle/docs/applications/hcm/benefits-employee-processes.md +44 -0
- package/bundle/docs/applications/hcm/benefits-plans.md +60 -0
- package/bundle/docs/applications/hcm/communications-announcements.md +31 -0
- package/bundle/docs/applications/hcm/communications-history.md +23 -0
- package/bundle/docs/applications/hcm/communications-notifications.md +31 -0
- package/bundle/docs/applications/hcm/communications-templates.md +35 -0
- package/bundle/docs/applications/hcm/compensation-allowances-benefits.md +44 -0
- package/bundle/docs/applications/hcm/compensation-analytics.md +30 -0
- package/bundle/docs/applications/hcm/compensation-bonus-incentive.md +35 -0
- package/bundle/docs/applications/hcm/compensation-cycles.md +49 -0
- package/bundle/docs/applications/hcm/compensation-equity.md +36 -0
- package/bundle/docs/applications/hcm/compensation-salary.md +47 -0
- package/bundle/docs/applications/hcm/compliance-analytics.md +25 -0
- package/bundle/docs/applications/hcm/compliance-audits-calendar.md +52 -0
- package/bundle/docs/applications/hcm/compliance-employee-tracking.md +41 -0
- package/bundle/docs/applications/hcm/compliance-regulatory.md +31 -0
- package/bundle/docs/applications/hcm/country-packs-admin.md +33 -0
- package/bundle/docs/applications/hcm/country-packs-compliance.md +31 -0
- package/bundle/docs/applications/hcm/country-packs-localization.md +37 -0
- package/bundle/docs/applications/hcm/country-packs.md +102 -0
- package/bundle/docs/applications/hcm/dashboard.md +38 -0
- package/bundle/docs/applications/hcm/designations.md +32 -0
- package/bundle/docs/applications/hcm/emp-documents-analytics.md +27 -0
- package/bundle/docs/applications/hcm/emp-documents-core.md +33 -0
- package/bundle/docs/applications/hcm/emp-documents-letters.md +40 -0
- package/bundle/docs/applications/hcm/emp-documents-signatures.md +24 -0
- package/bundle/docs/applications/hcm/emp-documents-workflow.md +32 -0
- package/bundle/docs/applications/hcm/employee-directory.md +55 -0
- package/bundle/docs/applications/hcm/employee-documents.md +51 -0
- package/bundle/docs/applications/hcm/employee-info-background.md +41 -0
- package/bundle/docs/applications/hcm/employee-info-core.md +32 -0
- package/bundle/docs/applications/hcm/employee-info-documents.md +31 -0
- package/bundle/docs/applications/hcm/employee-info-other.md +21 -0
- package/bundle/docs/applications/hcm/employee-movements.md +46 -0
- package/bundle/docs/applications/hcm/employee-reference-data.md +40 -0
- package/bundle/docs/applications/hcm/employee-relations-analytics.md +23 -0
- package/bundle/docs/applications/hcm/employee-relations-cases.md +26 -0
- package/bundle/docs/applications/hcm/employee-relations-conflicts-feedback.md +25 -0
- package/bundle/docs/applications/hcm/employee-relations-processes.md +34 -0
- package/bundle/docs/applications/hcm/employee-services-daily.md +32 -0
- package/bundle/docs/applications/hcm/employee-services-manager.md +21 -0
- package/bundle/docs/applications/hcm/employee-services-more.md +41 -0
- package/bundle/docs/applications/hcm/employee-services-overview.md +43 -0
- package/bundle/docs/applications/hcm/expenses-analytics.md +26 -0
- package/bundle/docs/applications/hcm/expenses-claims.md +47 -0
- package/bundle/docs/applications/hcm/expenses-self-service.md +30 -0
- package/bundle/docs/applications/hcm/expenses-setup.md +43 -0
- package/bundle/docs/applications/hcm/health-safety-analytics.md +26 -0
- package/bundle/docs/applications/hcm/health-safety-incidents.md +44 -0
- package/bundle/docs/applications/hcm/health-safety-medical.md +25 -0
- package/bundle/docs/applications/hcm/health-safety-training.md +29 -0
- package/bundle/docs/applications/hcm/helpdesk.md +28 -0
- package/bundle/docs/applications/hcm/holiday-calendar.md +69 -0
- package/bundle/docs/applications/hcm/hr-policies.md +64 -0
- package/bundle/docs/applications/hcm/hr-settings.md +98 -0
- package/bundle/docs/applications/hcm/index.md +272 -0
- package/bundle/docs/applications/hcm/industry-it-software.md +52 -0
- package/bundle/docs/applications/hcm/job-classifications.md +48 -0
- package/bundle/docs/applications/hcm/learning-analytics.md +21 -0
- package/bundle/docs/applications/hcm/learning-assessments.md +22 -0
- package/bundle/docs/applications/hcm/learning-catalog.md +25 -0
- package/bundle/docs/applications/hcm/learning-certifications.md +27 -0
- package/bundle/docs/applications/hcm/learning-enrollment.md +26 -0
- package/bundle/docs/applications/hcm/learning-instructors-providers.md +21 -0
- package/bundle/docs/applications/hcm/leave-accrual-and-carryforward.md +30 -0
- package/bundle/docs/applications/hcm/leave-balance.md +26 -0
- package/bundle/docs/applications/hcm/leave-dashboard-and-reports.md +32 -0
- package/bundle/docs/applications/hcm/leave-encashment.md +27 -0
- package/bundle/docs/applications/hcm/leave-requests.md +26 -0
- package/bundle/docs/applications/hcm/leave-setup.md +30 -0
- package/bundle/docs/applications/hcm/leave-team-and-calendar.md +25 -0
- package/bundle/docs/applications/hcm/navigation-and-approvals.md +61 -0
- package/bundle/docs/applications/hcm/offboarding-and-exit.md +71 -0
- package/bundle/docs/applications/hcm/onboarding-documents-verification.md +43 -0
- package/bundle/docs/applications/hcm/onboarding-orientation-probation.md +65 -0
- package/bundle/docs/applications/hcm/onboarding-overview.md +47 -0
- package/bundle/docs/applications/hcm/onboarding-provisioning-assets.md +48 -0
- package/bundle/docs/applications/hcm/onboarding-reports.md +38 -0
- package/bundle/docs/applications/hcm/onboarding-templates-checklists.md +38 -0
- package/bundle/docs/applications/hcm/onboarding-to-confirmation.md +45 -0
- package/bundle/docs/applications/hcm/org-structure.md +53 -0
- package/bundle/docs/applications/hcm/org-units.md +86 -0
- package/bundle/docs/applications/hcm/organizations.md +81 -0
- package/bundle/docs/applications/hcm/payroll-analytics.md +31 -0
- package/bundle/docs/applications/hcm/payroll-post-run.md +40 -0
- package/bundle/docs/applications/hcm/payroll-runs.md +30 -0
- package/bundle/docs/applications/hcm/payroll-setup.md +38 -0
- package/bundle/docs/applications/hcm/payroll-tax.md +21 -0
- package/bundle/docs/applications/hcm/payroll-transactions.md +43 -0
- package/bundle/docs/applications/hcm/performance-analytics.md +23 -0
- package/bundle/docs/applications/hcm/performance-appraisals.md +50 -0
- package/bundle/docs/applications/hcm/performance-continuous-feedback.md +23 -0
- package/bundle/docs/applications/hcm/performance-cycles-and-goals.md +54 -0
- package/bundle/docs/applications/hcm/performance-okrs.md +33 -0
- package/bundle/docs/applications/hcm/performance-pips.md +29 -0
- package/bundle/docs/applications/hcm/performance-ratings.md +25 -0
- package/bundle/docs/applications/hcm/positions.md +67 -0
- package/bundle/docs/applications/hcm/recruitment-agencies-and-sources.md +26 -0
- package/bundle/docs/applications/hcm/recruitment-analytics.md +25 -0
- package/bundle/docs/applications/hcm/recruitment-candidates.md +33 -0
- package/bundle/docs/applications/hcm/recruitment-offers.md +32 -0
- package/bundle/docs/applications/hcm/recruitment-pipeline.md +46 -0
- package/bundle/docs/applications/hcm/recruitment-requisitions-and-openings.md +33 -0
- package/bundle/docs/applications/hcm/roles-permissions.md +129 -0
- package/bundle/docs/applications/hcm/separation-clearance.md +33 -0
- package/bundle/docs/applications/hcm/separation-documents.md +28 -0
- package/bundle/docs/applications/hcm/separation-final-settlement.md +24 -0
- package/bundle/docs/applications/hcm/separation-reports.md +23 -0
- package/bundle/docs/applications/hcm/separation-resignation.md +43 -0
- package/bundle/docs/applications/hcm/settings-extensibility.md +31 -0
- package/bundle/docs/applications/hcm/settings-general.md +29 -0
- package/bundle/docs/applications/hcm/settings-integrations.md +14 -0
- package/bundle/docs/applications/hcm/settings-module-defaults.md +41 -0
- package/bundle/docs/applications/hcm/settings-process.md +28 -0
- package/bundle/docs/applications/hcm/shift-scheduling.md +33 -0
- package/bundle/docs/applications/hcm/talent-analytics.md +26 -0
- package/bundle/docs/applications/hcm/talent-career.md +31 -0
- package/bundle/docs/applications/hcm/talent-competencies-skills.md +30 -0
- package/bundle/docs/applications/hcm/talent-profiles.md +40 -0
- package/bundle/docs/applications/hcm/talent-succession.md +42 -0
- package/bundle/docs/applications/hcm/teams-and-tags.md +46 -0
- package/bundle/docs/applications/hcm/timesheets.md +33 -0
- package/bundle/docs/applications/hcm/travel-advances-expenses.md +34 -0
- package/bundle/docs/applications/hcm/travel-analytics.md +28 -0
- package/bundle/docs/applications/hcm/travel-bookings.md +35 -0
- package/bundle/docs/applications/hcm/travel-requests.md +38 -0
- package/bundle/docs/applications/hcm/workforce-org-design.md +22 -0
- package/bundle/docs/applications/hcm/workforce-planning-analytics.md +26 -0
- package/bundle/docs/applications/hcm/workforce-planning-core.md +36 -0
- package/bundle/docs/applications/hcm/workforce-planning-scenarios.md +29 -0
- package/bundle/docs/guides/add-app-owned-roles-and-permissions.md +144 -0
- package/bundle/docs/guides/extend-a-shipped-application.md +132 -0
- package/bundle/docs/guides/index.md +2 -0
- package/bundle/docs/reference/entity-aggregation-config.md +1 -1
- package/bundle/docs/reference/entity-document-generator-config.md +1 -1
- package/bundle/docs/tutorial/01-create-the-plugin.md +7 -1
- package/bundle/docs/tutorial/07-return-due-reminder-job.md +28 -5
- package/bundle/docs/tutorial/08-menus-i18n-publish.md +5 -5
- package/bundle/manifest.json +4 -4
- package/bundle/schemas/page.schema.json +13 -0
- package/bundle/schemas/plugin-manifest.schema.json +13 -0
- package/bundle/validators/block-engine.mjs +167 -7
- package/bundle/validators/page-engine.mjs +226 -21
- package/erp-cli/authoring-root.mjs +12 -1
- package/erp-cli/erp.mjs +334 -4
- package/package.json +1 -1
package/erp-cli/erp.mjs
CHANGED
|
@@ -1397,6 +1397,222 @@ public class ${className} implements RestContribution {
|
|
|
1397
1397
|
},
|
|
1398
1398
|
};
|
|
1399
1399
|
|
|
1400
|
+
// 2026-09-23 — the other ~10 curated extension-point interfaces in
|
|
1401
|
+
// engine-plugin-api (`erp platform describe <Name>`) had NO real shipped
|
|
1402
|
+
// implementation anywhere in the codebase yet, unlike rest-contribution's
|
|
1403
|
+
// real HelloRestContribution.java model — so these are generated directly
|
|
1404
|
+
// from each interface's own real method/record signatures instead (never
|
|
1405
|
+
// invented syntax, just not "modeled on a real example" the way
|
|
1406
|
+
// rest-contribution's doc comment could honestly claim). Each nested record
|
|
1407
|
+
// the interface itself declares (e.g. PaymentProviderContribution.ChargeResult)
|
|
1408
|
+
// is referenced via its qualified inner-class import, exactly as javac requires.
|
|
1409
|
+
function contributionTemplate({ interfaceName, methods, extraImports = [] }) {
|
|
1410
|
+
return {
|
|
1411
|
+
describe: `A plugin-contributed ${interfaceName} — no real shipped implementation exists in this codebase yet, so this is generated directly from the real interface contract (see \`erp platform describe ${interfaceName}\`), not modeled on a working example.`,
|
|
1412
|
+
render: ({ packageName, className, pluginId }) => `package ${packageName};
|
|
1413
|
+
|
|
1414
|
+
import com.erp.platform.engine.plugin.api.${interfaceName};
|
|
1415
|
+
${extraImports.map((i) => `import ${i};`).join("\n")}
|
|
1416
|
+
import org.pf4j.Extension;
|
|
1417
|
+
|
|
1418
|
+
/**
|
|
1419
|
+
* Scaffolded by \`erp plugin scaffold\` from the real ${interfaceName}
|
|
1420
|
+
* interface contract — no shipped reference implementation existed in this
|
|
1421
|
+
* codebase yet (see this template's own note in erp.mjs). Replace every
|
|
1422
|
+
* TODO with a real call to the actual provider this plugin integrates.
|
|
1423
|
+
*/
|
|
1424
|
+
@Extension
|
|
1425
|
+
public class ${className} implements ${interfaceName} {
|
|
1426
|
+
${methods}
|
|
1427
|
+
}
|
|
1428
|
+
`,
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
Object.assign(SCAFFOLD_TEMPLATES, {
|
|
1433
|
+
"authentication-provider": contributionTemplate({
|
|
1434
|
+
interfaceName: "AuthenticationProviderContribution",
|
|
1435
|
+
extraImports: ["java.util.Optional"],
|
|
1436
|
+
methods: `
|
|
1437
|
+
@Override
|
|
1438
|
+
public String providerId() {
|
|
1439
|
+
return "${"${pluginId}"}";
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
@Override
|
|
1443
|
+
public Optional<String> authenticate(String username, String credential) {
|
|
1444
|
+
// TODO: call the real identity provider; return Optional.of(subjectId) on success.
|
|
1445
|
+
return Optional.empty();
|
|
1446
|
+
}`.replace("${pluginId}", "REPLACE_provider_id"),
|
|
1447
|
+
}),
|
|
1448
|
+
"banking-provider": contributionTemplate({
|
|
1449
|
+
interfaceName: "BankingProviderContribution",
|
|
1450
|
+
extraImports: ["java.math.BigDecimal", "java.time.LocalDate", "java.util.List"],
|
|
1451
|
+
methods: `
|
|
1452
|
+
@Override
|
|
1453
|
+
public String providerId() {
|
|
1454
|
+
return "REPLACE_provider_id";
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
@Override
|
|
1458
|
+
public AccountBalance getBalance(byte[] decryptedCredentials, String accountId) {
|
|
1459
|
+
// TODO: call the real banking API.
|
|
1460
|
+
throw new UnsupportedOperationException("TODO");
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
@Override
|
|
1464
|
+
public List<BankTransaction> getStatement(byte[] decryptedCredentials, String accountId, LocalDate from, LocalDate to) {
|
|
1465
|
+
// TODO: call the real banking API.
|
|
1466
|
+
throw new UnsupportedOperationException("TODO");
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
@Override
|
|
1470
|
+
public PaymentResult initiatePayment(byte[] decryptedCredentials, String fromAccountId, String toAccountNumber, String toRoutingCode, BigDecimal amount, String currency, String reference, String remarks) {
|
|
1471
|
+
// TODO: call the real banking API.
|
|
1472
|
+
throw new UnsupportedOperationException("TODO");
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
@Override
|
|
1476
|
+
public PaymentResult checkPaymentStatus(byte[] decryptedCredentials, String paymentReference) {
|
|
1477
|
+
// TODO: call the real banking API.
|
|
1478
|
+
throw new UnsupportedOperationException("TODO");
|
|
1479
|
+
}`,
|
|
1480
|
+
}),
|
|
1481
|
+
"calendar-sync-provider": contributionTemplate({
|
|
1482
|
+
interfaceName: "CalendarSyncProviderContribution",
|
|
1483
|
+
extraImports: ["java.time.Instant"],
|
|
1484
|
+
methods: `
|
|
1485
|
+
@Override
|
|
1486
|
+
public String providerId() {
|
|
1487
|
+
return "REPLACE_provider_id";
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
@Override
|
|
1491
|
+
public SyncResult createEvent(byte[] decryptedCredentials, String title, String description, Instant startAt, Instant endAt) {
|
|
1492
|
+
// TODO: call the real calendar API.
|
|
1493
|
+
throw new UnsupportedOperationException("TODO");
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
@Override
|
|
1497
|
+
public SyncResult updateEvent(byte[] decryptedCredentials, String externalEventId, String title, String description, Instant startAt, Instant endAt) {
|
|
1498
|
+
// TODO: call the real calendar API.
|
|
1499
|
+
throw new UnsupportedOperationException("TODO");
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
@Override
|
|
1503
|
+
public boolean deleteEvent(byte[] decryptedCredentials, String externalEventId) {
|
|
1504
|
+
// TODO: call the real calendar API.
|
|
1505
|
+
throw new UnsupportedOperationException("TODO");
|
|
1506
|
+
}`,
|
|
1507
|
+
}),
|
|
1508
|
+
"embedding-provider": contributionTemplate({
|
|
1509
|
+
interfaceName: "EmbeddingProviderContribution",
|
|
1510
|
+
methods: `
|
|
1511
|
+
@Override
|
|
1512
|
+
public String providerId() {
|
|
1513
|
+
return "REPLACE_provider_id";
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
@Override
|
|
1517
|
+
public double[] embed(String text) {
|
|
1518
|
+
// TODO: call the real embedding model.
|
|
1519
|
+
throw new UnsupportedOperationException("TODO");
|
|
1520
|
+
}`,
|
|
1521
|
+
}),
|
|
1522
|
+
"llm-provider": contributionTemplate({
|
|
1523
|
+
interfaceName: "LlmProviderContribution",
|
|
1524
|
+
methods: `
|
|
1525
|
+
@Override
|
|
1526
|
+
public String providerId() {
|
|
1527
|
+
return "REPLACE_provider_id";
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
@Override
|
|
1531
|
+
public String complete(String promptText) {
|
|
1532
|
+
// TODO: call the real LLM.
|
|
1533
|
+
throw new UnsupportedOperationException("TODO");
|
|
1534
|
+
}`,
|
|
1535
|
+
}),
|
|
1536
|
+
"notification-provider": contributionTemplate({
|
|
1537
|
+
interfaceName: "NotificationProviderContribution",
|
|
1538
|
+
methods: `
|
|
1539
|
+
@Override
|
|
1540
|
+
public String channel() {
|
|
1541
|
+
return "REPLACE_channel";
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
@Override
|
|
1545
|
+
public boolean send(String recipient, String subject, String body) {
|
|
1546
|
+
// TODO: call the real notification channel.
|
|
1547
|
+
throw new UnsupportedOperationException("TODO");
|
|
1548
|
+
}`,
|
|
1549
|
+
}),
|
|
1550
|
+
"payment-provider": contributionTemplate({
|
|
1551
|
+
interfaceName: "PaymentProviderContribution",
|
|
1552
|
+
methods: `
|
|
1553
|
+
@Override
|
|
1554
|
+
public String providerId() {
|
|
1555
|
+
return "REPLACE_provider_id";
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
@Override
|
|
1559
|
+
public ChargeResult charge(long amountMinorUnits, String currencyCode, String reference) {
|
|
1560
|
+
// TODO: call the real payment gateway.
|
|
1561
|
+
throw new UnsupportedOperationException("TODO");
|
|
1562
|
+
}`,
|
|
1563
|
+
}),
|
|
1564
|
+
"search-provider": contributionTemplate({
|
|
1565
|
+
interfaceName: "SearchProviderContribution",
|
|
1566
|
+
extraImports: ["java.util.List"],
|
|
1567
|
+
methods: `
|
|
1568
|
+
@Override
|
|
1569
|
+
public String providerId() {
|
|
1570
|
+
return "REPLACE_provider_id";
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
@Override
|
|
1574
|
+
public List<SearchHit> search(long tenantId, String query, int limit) {
|
|
1575
|
+
// TODO: call the real search backend.
|
|
1576
|
+
throw new UnsupportedOperationException("TODO");
|
|
1577
|
+
}`,
|
|
1578
|
+
}),
|
|
1579
|
+
"storage-provider": contributionTemplate({
|
|
1580
|
+
interfaceName: "StorageProviderContribution",
|
|
1581
|
+
methods: `
|
|
1582
|
+
@Override
|
|
1583
|
+
public String providerId() {
|
|
1584
|
+
return "REPLACE_provider_id";
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
@Override
|
|
1588
|
+
public String store(long tenantId, byte[] content) {
|
|
1589
|
+
// TODO: call the real storage backend; return its storagePath.
|
|
1590
|
+
throw new UnsupportedOperationException("TODO");
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
@Override
|
|
1594
|
+
public byte[] retrieve(String storagePath) {
|
|
1595
|
+
// TODO: call the real storage backend.
|
|
1596
|
+
throw new UnsupportedOperationException("TODO");
|
|
1597
|
+
}`,
|
|
1598
|
+
}),
|
|
1599
|
+
"vector-search": contributionTemplate({
|
|
1600
|
+
interfaceName: "VectorSearchContribution",
|
|
1601
|
+
extraImports: ["java.util.List"],
|
|
1602
|
+
methods: `
|
|
1603
|
+
@Override
|
|
1604
|
+
public String collectionId() {
|
|
1605
|
+
return "REPLACE_collection_id";
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
@Override
|
|
1609
|
+
public List<VectorHit> query(double[] queryEmbedding, int limit) {
|
|
1610
|
+
// TODO: call the real vector index.
|
|
1611
|
+
throw new UnsupportedOperationException("TODO");
|
|
1612
|
+
}`,
|
|
1613
|
+
}),
|
|
1614
|
+
});
|
|
1615
|
+
|
|
1400
1616
|
function pluginScaffoldCommand(extensionPoint, opts) {
|
|
1401
1617
|
if (!extensionPoint || !SCAFFOLD_TEMPLATES[extensionPoint]) {
|
|
1402
1618
|
console.log(`usage: erp plugin scaffold <extension-point> [--package <pkg>] [--class <Name>] [--plugin-id <id>] [--out <file.java>]\nKnown extension points: ${Object.keys(SCAFFOLD_TEMPLATES).join(", ")}`);
|
|
@@ -1405,7 +1621,8 @@ function pluginScaffoldCommand(extensionPoint, opts) {
|
|
|
1405
1621
|
const tpl = SCAFFOLD_TEMPLATES[extensionPoint];
|
|
1406
1622
|
const pluginId = opts.pluginId || "my-plugin";
|
|
1407
1623
|
const packageName = opts.package || `com.example.${pluginId.replace(/[^a-zA-Z0-9]/g, "")}`;
|
|
1408
|
-
const
|
|
1624
|
+
const defaultClassName = "My" + extensionPoint.replace(/(^|-)([a-z])/g, (_, __, c) => c.toUpperCase());
|
|
1625
|
+
const className = opts.class || defaultClassName;
|
|
1409
1626
|
const rendered = tpl.render({ packageName, className, pluginId });
|
|
1410
1627
|
if (opts.out) {
|
|
1411
1628
|
mkdirSync(path.dirname(opts.out), { recursive: true });
|
|
@@ -2841,6 +3058,107 @@ async function blocksListCommand(opts) {
|
|
|
2841
3058
|
console.log(`Pass --type <exact-or-partial-name> for one type's full definition (all designer/labelKey detail), e.g. \`erp blocks list --type core.lookup\`.`);
|
|
2842
3059
|
}
|
|
2843
3060
|
|
|
3061
|
+
// ---------------------------------------------------------------------------
|
|
3062
|
+
// erp page check <route> — [REAL, added 2026-09-23]. A real browser render
|
|
3063
|
+
// check via Playwright (`playwright`, already a repo-root dependency), for
|
|
3064
|
+
// exactly the gap this session hit live: the API layer can be 100% verified
|
|
3065
|
+
// through `erp api`, yet a real production bug (the roles-permissions grid
|
|
3066
|
+
// showing zero rows despite a healthy 200 response with real data) only
|
|
3067
|
+
// exists in the DOM, invisible to any REST-only check. This generalizes the
|
|
3068
|
+
// one-off diagnostic scripts written ad hoc during that investigation into
|
|
3069
|
+
// one reusable command, instead of a new throwaway .mjs per page (see
|
|
3070
|
+
// [[feedback-common-playwright-script-not-per-page]]).
|
|
3071
|
+
//
|
|
3072
|
+
// Login is real browser form automation against {baseUrl}/app/admin/user/login
|
|
3073
|
+
// (not a session-storage shortcut) — the CLI's own stored device-flow token
|
|
3074
|
+
// doesn't map onto the frontend's session bootstrap without a much deeper
|
|
3075
|
+
// integration, and driving the real form is what actually exercises the
|
|
3076
|
+
// real login path a user hits. Requires --user/--password (or
|
|
3077
|
+
// ERP_UI_USER/ERP_UI_PASSWORD) since the CLI's stored credentials are a
|
|
3078
|
+
// token, never a password.
|
|
3079
|
+
// ---------------------------------------------------------------------------
|
|
3080
|
+
async function pageCheckCommand(routePath, opts) {
|
|
3081
|
+
if (!routePath) {
|
|
3082
|
+
console.log("usage: erp page check <route> [--user <email>] [--password <pw>] [--base-url <url>] [--timeout <ms>]\n route: the app path after the base URL, e.g. /app/hcm/home/hcm-foundation/roles-permissions\n credentials default to ERP_UI_USER/ERP_UI_PASSWORD env vars if not passed.");
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3085
|
+
let chromium;
|
|
3086
|
+
try {
|
|
3087
|
+
({ chromium } = await import("playwright"));
|
|
3088
|
+
} catch {
|
|
3089
|
+
console.log("`playwright` is not installed/resolvable from here — run `npm install` at the repo root first.");
|
|
3090
|
+
process.exitCode = 1;
|
|
3091
|
+
return;
|
|
3092
|
+
}
|
|
3093
|
+
const cfg = loadConfig();
|
|
3094
|
+
const env = currentEnv(cfg);
|
|
3095
|
+
const baseUrl = (opts.baseUrl || env.baseUrl).replace(/\/+$/, "");
|
|
3096
|
+
const user = opts.user || process.env.ERP_UI_USER;
|
|
3097
|
+
const password = opts.password || process.env.ERP_UI_PASSWORD;
|
|
3098
|
+
const timeout = Number(opts.timeout) || 45000;
|
|
3099
|
+
|
|
3100
|
+
const consoleErrors = [];
|
|
3101
|
+
const pageErrors = [];
|
|
3102
|
+
const failedRequests = [];
|
|
3103
|
+
const notableResponses = [];
|
|
3104
|
+
|
|
3105
|
+
const browser = await chromium.launch();
|
|
3106
|
+
const page = await browser.newPage();
|
|
3107
|
+
page.setDefaultTimeout(timeout);
|
|
3108
|
+
page.on("console", (msg) => { if (msg.type() === "error") consoleErrors.push(msg.text()); });
|
|
3109
|
+
page.on("pageerror", (err) => pageErrors.push(err.message));
|
|
3110
|
+
page.on("requestfailed", (req) => failedRequests.push(`${req.method()} ${req.url()} — ${req.failure()?.errorText ?? "unknown"}`));
|
|
3111
|
+
page.on("response", async (res) => {
|
|
3112
|
+
if (res.status() >= 400) {
|
|
3113
|
+
notableResponses.push({ status: res.status(), url: res.url() });
|
|
3114
|
+
}
|
|
3115
|
+
});
|
|
3116
|
+
|
|
3117
|
+
try {
|
|
3118
|
+
if (user && password) {
|
|
3119
|
+
await page.goto(`${baseUrl}/app/admin/user/login`, { waitUntil: "networkidle" });
|
|
3120
|
+
const inputs = page.locator("input");
|
|
3121
|
+
await inputs.nth(0).fill(user);
|
|
3122
|
+
await inputs.nth(1).fill(password);
|
|
3123
|
+
await page.getByRole("button", { name: /login|sign in/i }).first().click();
|
|
3124
|
+
await page.waitForTimeout(2000);
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
await page.goto(`${baseUrl}${routePath}`, { waitUntil: "networkidle" });
|
|
3128
|
+
await page.waitForTimeout(1500);
|
|
3129
|
+
|
|
3130
|
+
const tableChecks = await page.locator("table").evaluateAll((tables) =>
|
|
3131
|
+
tables.map((t, i) => ({
|
|
3132
|
+
index: i,
|
|
3133
|
+
headerCells: t.querySelectorAll("thead th").length,
|
|
3134
|
+
bodyRows: t.querySelectorAll("tbody tr").length,
|
|
3135
|
+
})),
|
|
3136
|
+
);
|
|
3137
|
+
const untranslatedKeys = await page.locator("body").evaluate((body) => {
|
|
3138
|
+
const matches = body.innerText.match(/⟦[^⟧]+⟧/g);
|
|
3139
|
+
return matches ? [...new Set(matches)] : [];
|
|
3140
|
+
});
|
|
3141
|
+
const bodyTextTail = (await page.locator("body").innerText()).slice(-600);
|
|
3142
|
+
|
|
3143
|
+
const report = {
|
|
3144
|
+
route: routePath,
|
|
3145
|
+
url: `${baseUrl}${routePath}`,
|
|
3146
|
+
tables: tableChecks,
|
|
3147
|
+
untranslatedI18nKeys: untranslatedKeys,
|
|
3148
|
+
consoleErrors,
|
|
3149
|
+
pageErrors,
|
|
3150
|
+
failedRequests,
|
|
3151
|
+
httpErrorResponses: notableResponses,
|
|
3152
|
+
bodyTextTail,
|
|
3153
|
+
};
|
|
3154
|
+
console.log(JSON.stringify(report, null, 2));
|
|
3155
|
+
const hasTableWithNoRows = tableChecks.some((t) => t.bodyRows === 0 && t.headerCells > 0);
|
|
3156
|
+
if (hasTableWithNoRows || untranslatedKeys.length > 0 || pageErrors.length > 0) process.exitCode = 1;
|
|
3157
|
+
} finally {
|
|
3158
|
+
await browser.close();
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
|
|
2844
3162
|
// ---------------------------------------------------------------------------
|
|
2845
3163
|
// erp ai init / erp mcp install
|
|
2846
3164
|
// [REAL] — scaffolds a plugin's ai/ subtree (agents.json/tools.json/
|
|
@@ -3292,8 +3610,18 @@ async function envSyncCommand(opts = {}) {
|
|
|
3292
3610
|
// ---------------------------------------------------------------------------
|
|
3293
3611
|
// erp docs gen-reference — turn the bundle's schemas/ into one Markdown page each
|
|
3294
3612
|
// ---------------------------------------------------------------------------
|
|
3613
|
+
// A schema/property description can legitimately contain placeholder syntax
|
|
3614
|
+
// like "${field:<name>}" or "row #<key>" — real bug found 2026-09-22 (blocked
|
|
3615
|
+
// the whole developer-docs site build): embedded verbatim into a Markdown
|
|
3616
|
+
// table cell, VitePress's Vue-based compiler reads a bare `<name>` as an
|
|
3617
|
+
// unclosed HTML tag and fails the entire build, not just that page. Escape
|
|
3618
|
+
// angle brackets in any free-text description before it goes into the page.
|
|
3619
|
+
function escapeMdAngleBrackets(s) {
|
|
3620
|
+
return String(s).replace(/</g, "<").replace(/>/g, ">");
|
|
3621
|
+
}
|
|
3622
|
+
|
|
3295
3623
|
function renderSchemaMarkdown(name, schema) {
|
|
3296
|
-
const desc = SCHEMA_REGISTRY[name]?.description || schema.description || "";
|
|
3624
|
+
const desc = escapeMdAngleBrackets(SCHEMA_REGISTRY[name]?.description || schema.description || "");
|
|
3297
3625
|
const lines = [
|
|
3298
3626
|
"---",
|
|
3299
3627
|
`title: ${name} schema`,
|
|
@@ -3316,7 +3644,7 @@ function renderSchemaMarkdown(name, schema) {
|
|
|
3316
3644
|
const type = v.type ? (Array.isArray(v.type) ? v.type.join(" \\| ") : v.type) : v.$ref ? `\`${v.$ref}\`` : v.enum ? "enum" : "—";
|
|
3317
3645
|
const notesParts = [];
|
|
3318
3646
|
if (v.enum) notesParts.push("one of: " + v.enum.map((e) => `\`${e}\``).join(", "));
|
|
3319
|
-
if (v.description) notesParts.push(String(v.description).replace(/\n+/g, " ").trim());
|
|
3647
|
+
if (v.description) notesParts.push(escapeMdAngleBrackets(String(v.description).replace(/\n+/g, " ").trim()));
|
|
3320
3648
|
lines.push(`| \`${k}\` | ${type} | ${required.has(k) ? "yes" : ""} | ${notesParts.join(" — ") || ""} |`);
|
|
3321
3649
|
}
|
|
3322
3650
|
lines.push("");
|
|
@@ -3764,7 +4092,7 @@ const HELP = `erp — ERP Developer Platform CLI
|
|
|
3764
4092
|
erp spec generate <pluginId> [--out <file.md>] (mechanically-derived entity/rule/page/permission inventory from LOCAL backend/modules/<id>/spk-assembly metadata — a starting point for ai/domains/*.md, not a replacement for its hand-curated narrative)
|
|
3765
4093
|
erp platform catalog [--engine <engine-name>] [--refresh] (mechanically-derived class/REST-mapping/Flyway-table inventory of every backend/platform-runtime/engine-* module — cached at ai/platform/engine-capability-catalog.json; erp docs search also searches this, labeled [live engine-* source], so a stale doc claim is never the only answer)
|
|
3766
4094
|
erp platform describe <ClassName> [--refresh] (real method signatures/field lists for a curated set of extension-point/gateway-client classes — *Contribution interfaces, WorkflowGatewayClient+PendingHumanTask/InstanceView/etc, DmsGatewayClient/CommunicationGatewayClient per-plugin copies — the concrete "what does this class actually contain" answer without opening the file; erp docs search also surfaces these, labeled [java contract])
|
|
3767
|
-
erp plugin scaffold <extension-point> [--package <pkg>] [--class <Name>] [--plugin-id <id>] [--out <file.java>] (emits a minimal, correctly-typed Java skeleton for
|
|
4095
|
+
erp plugin scaffold <extension-point> [--package <pkg>] [--class <Name>] [--plugin-id <id>] [--out <file.java>] (emits a minimal, correctly-typed Java skeleton for any curated extension point — 12 covered: rest-contribution, seed-data-config, authentication-provider, banking-provider, calendar-sync-provider, embedding-provider, llm-provider, notification-provider, payment-provider, search-provider, storage-provider, vector-search; rest-contribution/seed-data-config are modeled on a real shipped example, the other 10 are generated directly from the real interface contract since no shipped implementation exists yet — run \`erp plugin scaffold\` with no args for the full list)
|
|
3768
4096
|
erp plugin validate <dir>
|
|
3769
4097
|
erp plugin compile <source.tsx> [--out <page.json>]
|
|
3770
4098
|
erp plugin test <dir>
|
|
@@ -3801,6 +4129,7 @@ const HELP = `erp — ERP Developer Platform CLI
|
|
|
3801
4129
|
erp examples search <pattern> [--kind page|workflow|entities|provider] [--max <n>] (real shipped metadata JSON across every backend/modules/* plugin, matched by filename/plugin name — pure-CLI mirror of the MCP erp_search_examples tool)
|
|
3802
4130
|
erp examples patterns [--kind workflow] (curated named SHAPES, not just filenames — e.g. single-stage-self-decide, multi-stage-linear, multi-stage-conditional-branching — each pointing at a real shipped file confirmed to have that shape)
|
|
3803
4131
|
erp blocks list [--type <name>] [--category <cat>] (the real widget/block catalog, derived live from @erp/block-engine's own registry — every property/event/output any block type accepts, no source reading needed; pass --type for one type's full definition)
|
|
4132
|
+
erp page check <route> [--user <email>] [--password <pw>] [--base-url <url>] [--timeout <ms>] (real browser render check via Playwright — logs in through the real login form if --user/--password given, navigates to <route>, reports every table's header/body-row counts, untranslated ⟦i18n.key⟧ placeholders, console errors, page errors, and failed/4xx+/5xx network responses as JSON; exits non-zero if a table has headers but zero rows, an i18n key leaked, or a page error fired — for catching DOM-only bugs no REST-level check like \`erp api\` can see)
|
|
3804
4133
|
erp api get|post|put|delete <path> [--tenant <id>] [--body <json-string-or-@file>] (authenticated call against ANY real backend endpoint using your logged-in session — for post-deploy verification: browse real entity records, live menu/workflow/artifact state, Data Service output. Never hand-write curl with guessed headers for this.)
|
|
3805
4134
|
erp logs tail [--lines <n>] [--grep <text>] [--follow] [--file <path>] (real server-side log, incl. full stack traces — join a failed call's own X-Correlation-Id response header against --grep to see exactly what the server saw for that request; default path is engine-api's own logs/engine-api.log)
|
|
3806
4135
|
erp ai init [dir]
|
|
@@ -4005,6 +4334,7 @@ async function main() {
|
|
|
4005
4334
|
"examples search": () => examplesSearchCommand(positional[0], opts),
|
|
4006
4335
|
"examples patterns": () => examplesPatternsCommand(opts),
|
|
4007
4336
|
"blocks list": () => blocksListCommand(opts),
|
|
4337
|
+
"page check": () => pageCheckCommand(positional[0], opts),
|
|
4008
4338
|
"api get": () => apiCallCommand("GET", positional[0], opts),
|
|
4009
4339
|
"api post": () => apiCallCommand("POST", positional[0], opts),
|
|
4010
4340
|
"api put": () => apiCallCommand("PUT", positional[0], opts),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sparkerp/plugin-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "ERP Developer Platform CLI + MCP server for authoring plugins against a deployed ERP with zero access to the platform monorepo (platform source is secret to third-party developers). Bundles an offline authoring snapshot (schemas/catalog/blocks/docs/examples/validators) so `erp schema list`, `erp blocks list`, `erp docs search`, and page validation all work before you ever run `erp login`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|