@cervio/plugin-core 1.2.0 → 1.3.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/dist/index.d.ts +15 -2
- package/dist/index.js +66 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -94,6 +94,15 @@ declare function parseZipManifest(file: Blob | ArrayBuffer | Uint8Array, options
|
|
|
94
94
|
declare function computePluginContentSha256(zip: PluginZip, rawManifest: Record<string, unknown>): PluginActionResult<string>;
|
|
95
95
|
declare function sha256Hex(buffer: Buffer | Uint8Array): string;
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Non-blocking advice from `validatePluginDir` on how fast the plugin opens.
|
|
99
|
+
* A plugin with hints is still valid and can be pushed.
|
|
100
|
+
*/
|
|
101
|
+
type PluginStartupHint = {
|
|
102
|
+
code: 'boot_requests_missing' | 'mark_ready_missing' | 'boot_request_hardcoded_view';
|
|
103
|
+
message: string;
|
|
104
|
+
};
|
|
105
|
+
|
|
97
106
|
type PackPluginResult = {
|
|
98
107
|
buffer: Buffer;
|
|
99
108
|
pluginKey: string;
|
|
@@ -108,10 +117,14 @@ declare function packPlugin(opts: {
|
|
|
108
117
|
dir: string;
|
|
109
118
|
options?: ParseManifestOptions;
|
|
110
119
|
}): Promise<PluginActionResult<PackPluginResult>>;
|
|
111
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Validate an on-disk plugin directory without uploading. `hints` is
|
|
122
|
+
* non-blocking advice on start-up speed (boot_requests, markReady).
|
|
123
|
+
*/
|
|
112
124
|
declare function validatePluginDir(dir: string, options?: ParseManifestOptions): Promise<PluginActionResult<{
|
|
113
125
|
pluginKey: string;
|
|
114
126
|
contentSha256: string;
|
|
127
|
+
hints: PluginStartupHint[];
|
|
115
128
|
}>>;
|
|
116
129
|
declare function validatePluginZipBuffer(buffer: Buffer, options?: ParseManifestOptions): Promise<PluginActionResult<{
|
|
117
130
|
pluginKey: string;
|
|
@@ -119,4 +132,4 @@ declare function validatePluginZipBuffer(buffer: Buffer, options?: ParseManifest
|
|
|
119
132
|
}>>;
|
|
120
133
|
|
|
121
134
|
export { ParseManifestOptions, ParsedPluginManifest, PluginActionResult, PluginBootRequest, PluginCervioApiRequirement, PluginConnectionRequirement, PluginEntityRequirement, PluginSettingsFieldRequirement, PluginType, PluginZip, compareSemverDesc, computePluginContentSha256, isPolicyPluginType, manifestJsonForContentHash, nextPatchVersion, normalizePluginKey, normalizeToPolicyPluginType, packPlugin, parseManifestObject, parseZipManifest, sha256Hex, validatePluginDir, validatePluginZipBuffer };
|
|
122
|
-
export type { PackPluginResult, PolicyHookCaller, PolicyHookEvent, PolicyHookInput, PolicyHookResult, PolicyHost };
|
|
135
|
+
export type { PackPluginResult, PluginStartupHint, PolicyHookCaller, PolicyHookEvent, PolicyHookInput, PolicyHookResult, PolicyHost };
|
package/dist/index.js
CHANGED
|
@@ -634,6 +634,44 @@ function computePluginContentSha256(zip, rawManifest) {
|
|
|
634
634
|
function sha256Hex(buffer) {
|
|
635
635
|
return createHash("sha256").update(buffer).digest("hex");
|
|
636
636
|
}
|
|
637
|
+
|
|
638
|
+
// src/startup-hints.ts
|
|
639
|
+
var ANSWERED_AT_BOOT = /* @__PURE__ */ new Set(["/api/v1/me", "/api/v1/entities"]);
|
|
640
|
+
var MARK_READY_MARKER = "__cervioPluginReady";
|
|
641
|
+
var VIEW_ID_IN_PATH = /\/api\/v1\/views\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\//i;
|
|
642
|
+
var isIframePlugin = (t) => t === "route" || t === "task" || t === "task_form" || t === "form";
|
|
643
|
+
function pluginStartupHints(input) {
|
|
644
|
+
if (!isIframePlugin(input.pluginType)) return [];
|
|
645
|
+
const hints = [];
|
|
646
|
+
const readsData = input.cervioApis.some(
|
|
647
|
+
(a) => a.method.toUpperCase() === "GET" && !ANSWERED_AT_BOOT.has(a.path_prefix)
|
|
648
|
+
);
|
|
649
|
+
if (input.pluginType === "route" && readsData && input.bootRequests.length === 0) {
|
|
650
|
+
hints.push({
|
|
651
|
+
code: "boot_requests_missing",
|
|
652
|
+
message: "No boot_requests: list the GET calls of the first screen in manifest.json (same path and query as the code). Cervio starts them while the plugin loads (measured: data on screen 350-500 ms sooner on the first open, 75-220 ms on later ones)."
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
if (!input.bundleText.includes(MARK_READY_MARKER)) {
|
|
656
|
+
hints.push({
|
|
657
|
+
code: "mark_ready_missing",
|
|
658
|
+
message: "markReady() is never called: call it from '@cervio/plugin-sdk' once the first data is on screen (also on error). Cervio keeps its loading skeleton until then and uses it to time the plugin open."
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
const viewKeys = input.settings.filter((s) => s.type === "string" && /view/i.test(s.key)).map((s) => s.key);
|
|
662
|
+
for (const b of input.bootRequests) {
|
|
663
|
+
const m = VIEW_ID_IN_PATH.exec(b.path);
|
|
664
|
+
if (!m) continue;
|
|
665
|
+
const placeholder = viewKeys.length ? `{settings.${viewKeys[0]}}` : "{settings.<key>}";
|
|
666
|
+
hints.push({
|
|
667
|
+
code: "boot_request_hardcoded_view",
|
|
668
|
+
message: `boot_requests path "${b.path}" has a fixed view id: write ${placeholder} instead` + (viewKeys.length ? "" : " and add a string setting for the view") + ", so the prefetch follows the view chosen in each organization."
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
return hints;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// src/pack.ts
|
|
637
675
|
var ZIP_MTIME = /* @__PURE__ */ new Date("2000-01-01T00:00:00Z");
|
|
638
676
|
async function collectFiles(dir, base = "") {
|
|
639
677
|
const entries = await promises.readdir(dir, { withFileTypes: true });
|
|
@@ -729,16 +767,43 @@ async function packPlugin(opts) {
|
|
|
729
767
|
}
|
|
730
768
|
};
|
|
731
769
|
}
|
|
770
|
+
async function readBundleText(root) {
|
|
771
|
+
const dist = path.join(
|
|
772
|
+
/*turbopackIgnore: true*/
|
|
773
|
+
root,
|
|
774
|
+
"dist"
|
|
775
|
+
);
|
|
776
|
+
let files;
|
|
777
|
+
try {
|
|
778
|
+
files = await collectFiles(dist);
|
|
779
|
+
} catch {
|
|
780
|
+
return "";
|
|
781
|
+
}
|
|
782
|
+
const parts = [];
|
|
783
|
+
for (const f of files) {
|
|
784
|
+
if (/\.(m?js|html)$/i.test(f.rel)) parts.push(await promises.readFile(f.abs, "utf8"));
|
|
785
|
+
}
|
|
786
|
+
return parts.join("\n");
|
|
787
|
+
}
|
|
732
788
|
async function validatePluginDir(dir, options) {
|
|
733
789
|
const packed = await packPlugin({ dir, options });
|
|
734
790
|
if (!packed.ok) return packed;
|
|
735
791
|
const parsed = await parseZipManifest(packed.value.buffer, options);
|
|
736
792
|
if (!parsed.ok) return parsed;
|
|
793
|
+
const fields = parsed.value;
|
|
794
|
+
const hints = pluginStartupHints({
|
|
795
|
+
pluginType: fields.pluginType,
|
|
796
|
+
cervioApis: fields.cervioApis,
|
|
797
|
+
bootRequests: fields.bootRequests,
|
|
798
|
+
settings: fields.settings,
|
|
799
|
+
bundleText: isPolicyPluginType(fields.pluginType) ? "" : await readBundleText(path.resolve(dir))
|
|
800
|
+
});
|
|
737
801
|
return {
|
|
738
802
|
ok: true,
|
|
739
803
|
value: {
|
|
740
804
|
pluginKey: packed.value.pluginKey,
|
|
741
|
-
contentSha256: packed.value.contentSha256
|
|
805
|
+
contentSha256: packed.value.contentSha256,
|
|
806
|
+
hints
|
|
742
807
|
}
|
|
743
808
|
};
|
|
744
809
|
}
|