@verdaccio/plugin-verifier 1.0.0-next-9.1

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.
Files changed (72) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +255 -0
  4. package/bin/verdaccio-plugin-verifier +6 -0
  5. package/build/_virtual/_rolldown/runtime.cjs +23 -0
  6. package/build/cli.cjs +15 -0
  7. package/build/cli.cjs.map +1 -0
  8. package/build/cli.d.ts +2 -0
  9. package/build/cli.d.ts.map +1 -0
  10. package/build/cli.js +14 -0
  11. package/build/cli.js.map +1 -0
  12. package/build/commands/verify.cjs +76 -0
  13. package/build/commands/verify.cjs.map +1 -0
  14. package/build/commands/verify.d.ts +11 -0
  15. package/build/commands/verify.d.ts.map +1 -0
  16. package/build/commands/verify.js +74 -0
  17. package/build/commands/verify.js.map +1 -0
  18. package/build/diagnostics.cjs +209 -0
  19. package/build/diagnostics.cjs.map +1 -0
  20. package/build/diagnostics.d.ts +13 -0
  21. package/build/diagnostics.d.ts.map +1 -0
  22. package/build/diagnostics.js +207 -0
  23. package/build/diagnostics.js.map +1 -0
  24. package/build/index.cjs +11 -0
  25. package/build/index.d.ts +5 -0
  26. package/build/index.d.ts.map +1 -0
  27. package/build/index.js +4 -0
  28. package/build/sanity-checks.cjs +27 -0
  29. package/build/sanity-checks.cjs.map +1 -0
  30. package/build/sanity-checks.d.ts +11 -0
  31. package/build/sanity-checks.d.ts.map +1 -0
  32. package/build/sanity-checks.js +22 -0
  33. package/build/sanity-checks.js.map +1 -0
  34. package/build/types.d.ts +80 -0
  35. package/build/types.d.ts.map +1 -0
  36. package/build/verify-plugin.cjs +87 -0
  37. package/build/verify-plugin.cjs.map +1 -0
  38. package/build/verify-plugin.d.ts +19 -0
  39. package/build/verify-plugin.d.ts.map +1 -0
  40. package/build/verify-plugin.js +85 -0
  41. package/build/verify-plugin.js.map +1 -0
  42. package/package.json +63 -0
  43. package/src/cli.ts +14 -0
  44. package/src/commands/verify.ts +100 -0
  45. package/src/diagnostics.ts +274 -0
  46. package/src/index.ts +10 -0
  47. package/src/sanity-checks.ts +27 -0
  48. package/src/types.ts +83 -0
  49. package/src/verify-plugin.ts +115 -0
  50. package/tests/diagnostics.spec.ts +323 -0
  51. package/tests/fixtures/entry-point-bad-json/package.json +1 -0
  52. package/tests/fixtures/entry-point-exports-default/package.json +7 -0
  53. package/tests/fixtures/entry-point-exports-import-default/package.json +9 -0
  54. package/tests/fixtures/entry-point-exports-import-string/package.json +7 -0
  55. package/tests/fixtures/entry-point-exports-string/package.json +5 -0
  56. package/tests/fixtures/entry-point-main/package.json +3 -0
  57. package/tests/fixtures/entry-point-module/package.json +3 -0
  58. package/tests/fixtures/package.json +3 -0
  59. package/tests/fixtures/verdaccio-invalid-plugin/index.js +2 -0
  60. package/tests/fixtures/verdaccio-null-plugin/index.js +4 -0
  61. package/tests/fixtures/verdaccio-throwing-plugin/index.js +4 -0
  62. package/tests/fixtures/verdaccio-valid-auth-es6-plugin/index.js +14 -0
  63. package/tests/fixtures/verdaccio-valid-auth-plugin/index.js +20 -0
  64. package/tests/fixtures/verdaccio-valid-filter-plugin/index.js +12 -0
  65. package/tests/fixtures/verdaccio-valid-middleware-plugin/index.js +10 -0
  66. package/tests/fixtures/verdaccio-valid-storage-plugin/index.js +30 -0
  67. package/tests/fixtures/verdaccio-wrong-category-plugin/index.js +11 -0
  68. package/tests/verify-plugin.spec.ts +218 -0
  69. package/tsconfig.build.json +4 -0
  70. package/tsconfig.json +24 -0
  71. package/vite.config.mjs +69 -0
  72. package/vitest.config.mjs +11 -0
@@ -0,0 +1,209 @@
1
+ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
2
+ const require_sanity_checks = require("./sanity-checks.cjs");
3
+ let debug = require("debug");
4
+ debug = require_runtime.__toESM(debug);
5
+ let node_path = require("node:path");
6
+ let _verdaccio_core = require("@verdaccio/core");
7
+ let node_fs = require("node:fs");
8
+ let node_module = require("node:module");
9
+ let node_url = require("node:url");
10
+ //#region src/diagnostics.ts
11
+ var debug$1 = (0, debug.default)("verdaccio:plugin:verifier:diagnostics");
12
+ var requireModule = (0, node_module.createRequire)(typeof __filename !== "undefined" ? __filename : {}.url);
13
+ function isValidExport(plugin) {
14
+ return typeof plugin === "function" || typeof plugin?.default === "function";
15
+ }
16
+ function isES6(plugin) {
17
+ return plugin && typeof plugin === "object" && "default" in plugin;
18
+ }
19
+ /**
20
+ * Resolve the ESM entry point for a directory-based plugin.
21
+ * import() doesn't support directory imports, so we resolve via package.json.
22
+ */
23
+ function resolveEntryPoint(dirPath) {
24
+ const pkgPath = (0, node_path.join)(dirPath, "package.json");
25
+ if ((0, node_fs.existsSync)(pkgPath)) try {
26
+ const pkg = JSON.parse((0, node_fs.readFileSync)(pkgPath, "utf-8"));
27
+ if (pkg.exports) {
28
+ const dotExport = pkg.exports["."];
29
+ if (typeof dotExport === "string") return (0, node_path.join)(dirPath, dotExport);
30
+ if (dotExport?.import?.default) return (0, node_path.join)(dirPath, dotExport.import.default);
31
+ if (dotExport?.import && typeof dotExport.import === "string") return (0, node_path.join)(dirPath, dotExport.import);
32
+ if (dotExport?.default) return (0, node_path.join)(dirPath, dotExport.default);
33
+ }
34
+ if (pkg.module) return (0, node_path.join)(dirPath, pkg.module);
35
+ if (pkg.main) return (0, node_path.join)(dirPath, pkg.main);
36
+ } catch {}
37
+ return (0, node_path.join)(dirPath, "index.js");
38
+ }
39
+ /**
40
+ * Try to load a module, falling back from require() to import() for ESM.
41
+ * Handles CJS require(), ESM require() shim errors (bundlers), and
42
+ * ERR_REQUIRE_ESM — always falls through to dynamic import() on any
43
+ * require() failure.
44
+ */
45
+ async function tryResolve(modulePath) {
46
+ try {
47
+ return { module: requireModule(modulePath) };
48
+ } catch (requireErr) {
49
+ debug$1("require() failed for %o: %s — trying dynamic import", modulePath, requireErr.message);
50
+ }
51
+ try {
52
+ let importPath = modulePath;
53
+ if ((0, node_fs.existsSync)(modulePath) && (0, node_fs.existsSync)((0, node_path.join)(modulePath, "package.json"))) {
54
+ importPath = resolveEntryPoint(modulePath);
55
+ debug$1("resolved ESM entry point: %o", importPath);
56
+ }
57
+ return { module: await (importPath.startsWith("/") ? import((0, node_url.pathToFileURL)(importPath).href) : import(importPath)) };
58
+ } catch (importErr) {
59
+ return {
60
+ module: null,
61
+ error: importErr.message
62
+ };
63
+ }
64
+ }
65
+ /**
66
+ * Runs step-by-step diagnostics to identify exactly which phase of
67
+ * plugin loading fails. This replicates the same resolution logic
68
+ * as `asyncLoadPlugin` but tests each step independently.
69
+ */
70
+ async function runDiagnostics(options) {
71
+ const { pluginPath, category, pluginConfig = {}, sanityCheck: customSanityCheck, prefix = _verdaccio_core.PLUGIN_PREFIX, pluginsFolder } = options;
72
+ const steps = [];
73
+ const pluginName = pluginPath.startsWith("@") && pluginPath.includes("/") ? pluginPath : `${prefix}-${pluginPath}`;
74
+ debug$1("running diagnostics for %o (resolved name: %o)", pluginPath, pluginName);
75
+ let pluginModule = null;
76
+ let resolvedFrom = "";
77
+ if (pluginsFolder) {
78
+ const absFolder = (0, node_path.resolve)(pluginsFolder);
79
+ const pluginDir = (0, node_path.join)(absFolder, pluginName);
80
+ debug$1("checking plugins folder: %o", pluginDir);
81
+ if (!(0, node_fs.existsSync)(absFolder)) {
82
+ steps.push({
83
+ phase: "resolve",
84
+ pass: false,
85
+ message: `Plugins folder does not exist: ${absFolder}`
86
+ });
87
+ return steps;
88
+ }
89
+ if (!(0, node_fs.existsSync)(pluginDir)) {
90
+ debug$1("plugin directory not found: %o", pluginDir);
91
+ steps.push({
92
+ phase: "resolve",
93
+ pass: false,
94
+ message: `Plugin directory not found: ${pluginDir} — expected a folder named "${pluginName}" inside "${absFolder}"`
95
+ });
96
+ return steps;
97
+ }
98
+ const result = await tryResolve(pluginDir);
99
+ if (result.module) {
100
+ pluginModule = result.module;
101
+ resolvedFrom = pluginDir;
102
+ debug$1("resolved from plugins folder: %o", pluginDir);
103
+ } else {
104
+ const missingDep = parseMissingDependency(result.error ?? "", pluginDir);
105
+ steps.push({
106
+ phase: "resolve",
107
+ pass: false,
108
+ message: missingDep ? `Plugin found at ${pluginDir} but has a missing dependency: ${missingDep}` : `Plugin found at ${pluginDir} but failed to load: ${result.error}`
109
+ });
110
+ return steps;
111
+ }
112
+ }
113
+ if (!pluginModule) {
114
+ const result = await tryResolve(pluginName);
115
+ if (result.module) {
116
+ pluginModule = result.module;
117
+ resolvedFrom = pluginName;
118
+ debug$1("resolved from node_modules: %o", pluginName);
119
+ } else {
120
+ const missingDep = parseMissingDependency(result.error ?? "", pluginName);
121
+ steps.push({
122
+ phase: "resolve",
123
+ pass: false,
124
+ message: missingDep ? `Package "${pluginName}" found but has a missing dependency: ${missingDep}` : `Package "${pluginName}" not found in node_modules — try: npm install ${pluginName}`
125
+ });
126
+ return steps;
127
+ }
128
+ }
129
+ steps.push({
130
+ phase: "resolve",
131
+ pass: true,
132
+ message: `Module resolved from ${resolvedFrom}`
133
+ });
134
+ if (!isValidExport(pluginModule)) {
135
+ const exportKeys = pluginModule ? Object.keys(pluginModule).join(", ") : "none";
136
+ steps.push({
137
+ phase: "export",
138
+ pass: false,
139
+ message: `Module does not export a function or class (default export). Exported keys: [${exportKeys}]`
140
+ });
141
+ return steps;
142
+ }
143
+ const moduleType = isES6(pluginModule) ? "ES6 (default export)" : "CommonJS (factory function)";
144
+ steps.push({
145
+ phase: "export",
146
+ pass: true,
147
+ message: `Valid ${moduleType} plugin export detected`
148
+ });
149
+ let instance;
150
+ try {
151
+ if (isES6(pluginModule)) instance = new pluginModule.default(pluginConfig, {
152
+ config: pluginConfig,
153
+ logger: console
154
+ });
155
+ else instance = pluginModule(pluginConfig, {
156
+ config: pluginConfig,
157
+ logger: console
158
+ });
159
+ } catch (err) {
160
+ steps.push({
161
+ phase: "instantiate",
162
+ pass: false,
163
+ message: `Plugin threw during instantiation: ${err.message}`
164
+ });
165
+ return steps;
166
+ }
167
+ if (!instance || typeof instance !== "object" && typeof instance !== "function") {
168
+ steps.push({
169
+ phase: "instantiate",
170
+ pass: false,
171
+ message: `Plugin constructor/factory returned ${instance === null ? "null" : typeof instance} instead of an object`
172
+ });
173
+ return steps;
174
+ }
175
+ steps.push({
176
+ phase: "instantiate",
177
+ pass: true,
178
+ message: "Plugin instantiated successfully"
179
+ });
180
+ if (!(customSanityCheck ?? require_sanity_checks.getSanityCheck(category))(instance)) {
181
+ const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(instance) ?? {}).filter((m) => m !== "constructor").concat(Object.keys(instance));
182
+ const unique = [...new Set(methods)];
183
+ steps.push({
184
+ phase: "sanity-check",
185
+ pass: false,
186
+ message: `Plugin does not implement the required methods for category "${category}". Available methods: [${unique.join(", ")}]`
187
+ });
188
+ return steps;
189
+ }
190
+ steps.push({
191
+ phase: "sanity-check",
192
+ pass: true,
193
+ message: `Plugin passes sanity check for category "${category}"`
194
+ });
195
+ return steps;
196
+ }
197
+ /**
198
+ * When a MODULE_NOT_FOUND error is about a transitive dependency
199
+ * (not the plugin itself), extract the missing module name.
200
+ */
201
+ function parseMissingDependency(message, pluginPath) {
202
+ const match = message.match(/Cannot find module '([^']+)'/);
203
+ if (match && match[1] && !match[1].includes(pluginPath)) return match[1];
204
+ return null;
205
+ }
206
+ //#endregion
207
+ exports.runDiagnostics = runDiagnostics;
208
+
209
+ //# sourceMappingURL=diagnostics.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.cjs","names":[],"sources":["../src/diagnostics.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { PLUGIN_PREFIX } from '@verdaccio/core';\n\nimport { getSanityCheck } from './sanity-checks';\nimport type { DiagnosticStep, VerifyPluginOptions } from './types';\n\nconst debug = buildDebug('verdaccio:plugin:verifier:diagnostics');\n\n// createRequire needs an absolute path; works in both ESM and CJS contexts\nconst requireModule = createRequire(\n typeof __filename !== 'undefined' ? __filename : import.meta.url\n);\n\nfunction isValidExport(plugin: any): boolean {\n return typeof plugin === 'function' || typeof plugin?.default === 'function';\n}\n\nfunction isES6(plugin: any): boolean {\n return plugin && typeof plugin === 'object' && 'default' in plugin;\n}\n\n/**\n * Resolve the ESM entry point for a directory-based plugin.\n * import() doesn't support directory imports, so we resolve via package.json.\n */\nexport function resolveEntryPoint(dirPath: string): string {\n const pkgPath = join(dirPath, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n if (pkg.exports) {\n const dotExport = pkg.exports['.'];\n if (typeof dotExport === 'string') {\n return join(dirPath, dotExport);\n }\n if (dotExport?.import?.default) {\n return join(dirPath, dotExport.import.default);\n }\n if (dotExport?.import && typeof dotExport.import === 'string') {\n return join(dirPath, dotExport.import);\n }\n if (dotExport?.default) {\n return join(dirPath, dotExport.default);\n }\n }\n if (pkg.module) {\n return join(dirPath, pkg.module);\n }\n if (pkg.main) {\n return join(dirPath, pkg.main);\n }\n } catch {\n // fall through\n }\n }\n return join(dirPath, 'index.js');\n}\n\n/**\n * Try to load a module, falling back from require() to import() for ESM.\n * Handles CJS require(), ESM require() shim errors (bundlers), and\n * ERR_REQUIRE_ESM — always falls through to dynamic import() on any\n * require() failure.\n */\nasync function tryResolve(modulePath: string): Promise<{ module: any; error?: string }> {\n // Try require() first (fast path for CJS modules)\n try {\n return { module: requireModule(modulePath) };\n } catch (requireErr: any) {\n debug('require() failed for %o: %s — trying dynamic import', modulePath, requireErr.message);\n }\n\n // Fallback to dynamic import() for ESM modules\n try {\n let importPath = modulePath;\n if (existsSync(modulePath) && existsSync(join(modulePath, 'package.json'))) {\n importPath = resolveEntryPoint(modulePath);\n debug('resolved ESM entry point: %o', importPath);\n }\n const importUrl = importPath.startsWith('/') ? pathToFileURL(importPath).href : importPath;\n const mod = await import(importUrl);\n return { module: mod };\n } catch (importErr: any) {\n return { module: null, error: importErr.message };\n }\n}\n\n/**\n * Runs step-by-step diagnostics to identify exactly which phase of\n * plugin loading fails. This replicates the same resolution logic\n * as `asyncLoadPlugin` but tests each step independently.\n */\nexport async function runDiagnostics(options: VerifyPluginOptions): Promise<DiagnosticStep[]> {\n const {\n pluginPath,\n category,\n pluginConfig = {},\n sanityCheck: customSanityCheck,\n prefix = PLUGIN_PREFIX,\n pluginsFolder,\n } = options;\n\n const steps: DiagnosticStep[] = [];\n const isScoped = pluginPath.startsWith('@') && pluginPath.includes('/');\n const pluginName = isScoped ? pluginPath : `${prefix}-${pluginPath}`;\n\n debug('running diagnostics for %o (resolved name: %o)', pluginPath, pluginName);\n\n // --- Phase 1: Resolve ---\n let pluginModule: any = null;\n let resolvedFrom = '';\n\n // Try plugins folder first\n if (pluginsFolder) {\n const absFolder = resolve(pluginsFolder);\n const pluginDir = join(absFolder, pluginName);\n debug('checking plugins folder: %o', pluginDir);\n\n if (!existsSync(absFolder)) {\n steps.push({\n phase: 'resolve',\n pass: false,\n message: `Plugins folder does not exist: ${absFolder}`,\n });\n return steps;\n }\n\n if (!existsSync(pluginDir)) {\n debug('plugin directory not found: %o', pluginDir);\n steps.push({\n phase: 'resolve',\n pass: false,\n message: `Plugin directory not found: ${pluginDir} — expected a folder named \"${pluginName}\" inside \"${absFolder}\"`,\n });\n return steps;\n }\n\n const result = await tryResolve(pluginDir);\n if (result.module) {\n pluginModule = result.module;\n resolvedFrom = pluginDir;\n debug('resolved from plugins folder: %o', pluginDir);\n } else {\n const missingDep = parseMissingDependency(result.error ?? '', pluginDir);\n steps.push({\n phase: 'resolve',\n pass: false,\n message: missingDep\n ? `Plugin found at ${pluginDir} but has a missing dependency: ${missingDep}`\n : `Plugin found at ${pluginDir} but failed to load: ${result.error}`,\n });\n return steps;\n }\n }\n\n // Try node_modules if not found in plugins folder\n if (!pluginModule) {\n const result = await tryResolve(pluginName);\n if (result.module) {\n pluginModule = result.module;\n resolvedFrom = pluginName;\n debug('resolved from node_modules: %o', pluginName);\n } else {\n const missingDep = parseMissingDependency(result.error ?? '', pluginName);\n steps.push({\n phase: 'resolve',\n pass: false,\n message: missingDep\n ? `Package \"${pluginName}\" found but has a missing dependency: ${missingDep}`\n : `Package \"${pluginName}\" not found in node_modules — try: npm install ${pluginName}`,\n });\n return steps;\n }\n }\n\n steps.push({\n phase: 'resolve',\n pass: true,\n message: `Module resolved from ${resolvedFrom}`,\n });\n\n // --- Phase 2: Export validation ---\n if (!isValidExport(pluginModule)) {\n const exportKeys = pluginModule ? Object.keys(pluginModule).join(', ') : 'none';\n steps.push({\n phase: 'export',\n pass: false,\n message: `Module does not export a function or class (default export). Exported keys: [${exportKeys}]`,\n });\n return steps;\n }\n\n const moduleType = isES6(pluginModule) ? 'ES6 (default export)' : 'CommonJS (factory function)';\n steps.push({\n phase: 'export',\n pass: true,\n message: `Valid ${moduleType} plugin export detected`,\n });\n\n // --- Phase 3: Instantiation ---\n let instance: any;\n try {\n if (isES6(pluginModule)) {\n instance = new pluginModule.default(pluginConfig, { config: pluginConfig, logger: console });\n } else {\n instance = pluginModule(pluginConfig, { config: pluginConfig, logger: console });\n }\n } catch (err: any) {\n steps.push({\n phase: 'instantiate',\n pass: false,\n message: `Plugin threw during instantiation: ${err.message}`,\n });\n return steps;\n }\n\n if (!instance || (typeof instance !== 'object' && typeof instance !== 'function')) {\n steps.push({\n phase: 'instantiate',\n pass: false,\n message: `Plugin constructor/factory returned ${instance === null ? 'null' : typeof instance} instead of an object`,\n });\n return steps;\n }\n\n steps.push({\n phase: 'instantiate',\n pass: true,\n message: 'Plugin instantiated successfully',\n });\n\n // --- Phase 4: Sanity check ---\n const sanityCheck = customSanityCheck ?? getSanityCheck(category);\n const passed = sanityCheck(instance);\n\n if (!passed) {\n const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(instance) ?? {})\n .filter((m) => m !== 'constructor')\n .concat(Object.keys(instance));\n const unique = [...new Set(methods)];\n\n steps.push({\n phase: 'sanity-check',\n pass: false,\n message: `Plugin does not implement the required methods for category \"${category}\". Available methods: [${unique.join(', ')}]`,\n });\n return steps;\n }\n\n steps.push({\n phase: 'sanity-check',\n pass: true,\n message: `Plugin passes sanity check for category \"${category}\"`,\n });\n\n return steps;\n}\n\n/**\n * When a MODULE_NOT_FOUND error is about a transitive dependency\n * (not the plugin itself), extract the missing module name.\n */\nfunction parseMissingDependency(message: string, pluginPath: string): string | null {\n const match = message.match(/Cannot find module '([^']+)'/);\n if (match && match[1] && !match[1].includes(pluginPath)) {\n return match[1];\n }\n return null;\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { DiagnosticStep, VerifyPluginOptions } from './types';
2
+ /**
3
+ * Resolve the ESM entry point for a directory-based plugin.
4
+ * import() doesn't support directory imports, so we resolve via package.json.
5
+ */
6
+ export declare function resolveEntryPoint(dirPath: string): string;
7
+ /**
8
+ * Runs step-by-step diagnostics to identify exactly which phase of
9
+ * plugin loading fails. This replicates the same resolution logic
10
+ * as `asyncLoadPlugin` but tests each step independently.
11
+ */
12
+ export declare function runDiagnostics(options: VerifyPluginOptions): Promise<DiagnosticStep[]>;
13
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAiBnE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA+BzD;AA+BD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAoK5F"}
@@ -0,0 +1,207 @@
1
+ import { getSanityCheck } from "./sanity-checks.js";
2
+ import buildDebug from "debug";
3
+ import { join, resolve } from "node:path";
4
+ import { PLUGIN_PREFIX } from "@verdaccio/core";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { createRequire } from "node:module";
7
+ import { pathToFileURL } from "node:url";
8
+ //#region src/diagnostics.ts
9
+ var debug = buildDebug("verdaccio:plugin:verifier:diagnostics");
10
+ var requireModule = createRequire(typeof __filename !== "undefined" ? __filename : import.meta.url);
11
+ function isValidExport(plugin) {
12
+ return typeof plugin === "function" || typeof plugin?.default === "function";
13
+ }
14
+ function isES6(plugin) {
15
+ return plugin && typeof plugin === "object" && "default" in plugin;
16
+ }
17
+ /**
18
+ * Resolve the ESM entry point for a directory-based plugin.
19
+ * import() doesn't support directory imports, so we resolve via package.json.
20
+ */
21
+ function resolveEntryPoint(dirPath) {
22
+ const pkgPath = join(dirPath, "package.json");
23
+ if (existsSync(pkgPath)) try {
24
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
25
+ if (pkg.exports) {
26
+ const dotExport = pkg.exports["."];
27
+ if (typeof dotExport === "string") return join(dirPath, dotExport);
28
+ if (dotExport?.import?.default) return join(dirPath, dotExport.import.default);
29
+ if (dotExport?.import && typeof dotExport.import === "string") return join(dirPath, dotExport.import);
30
+ if (dotExport?.default) return join(dirPath, dotExport.default);
31
+ }
32
+ if (pkg.module) return join(dirPath, pkg.module);
33
+ if (pkg.main) return join(dirPath, pkg.main);
34
+ } catch {}
35
+ return join(dirPath, "index.js");
36
+ }
37
+ /**
38
+ * Try to load a module, falling back from require() to import() for ESM.
39
+ * Handles CJS require(), ESM require() shim errors (bundlers), and
40
+ * ERR_REQUIRE_ESM — always falls through to dynamic import() on any
41
+ * require() failure.
42
+ */
43
+ async function tryResolve(modulePath) {
44
+ try {
45
+ return { module: requireModule(modulePath) };
46
+ } catch (requireErr) {
47
+ debug("require() failed for %o: %s — trying dynamic import", modulePath, requireErr.message);
48
+ }
49
+ try {
50
+ let importPath = modulePath;
51
+ if (existsSync(modulePath) && existsSync(join(modulePath, "package.json"))) {
52
+ importPath = resolveEntryPoint(modulePath);
53
+ debug("resolved ESM entry point: %o", importPath);
54
+ }
55
+ return { module: await (importPath.startsWith("/") ? import(pathToFileURL(importPath).href) : import(importPath)) };
56
+ } catch (importErr) {
57
+ return {
58
+ module: null,
59
+ error: importErr.message
60
+ };
61
+ }
62
+ }
63
+ /**
64
+ * Runs step-by-step diagnostics to identify exactly which phase of
65
+ * plugin loading fails. This replicates the same resolution logic
66
+ * as `asyncLoadPlugin` but tests each step independently.
67
+ */
68
+ async function runDiagnostics(options) {
69
+ const { pluginPath, category, pluginConfig = {}, sanityCheck: customSanityCheck, prefix = PLUGIN_PREFIX, pluginsFolder } = options;
70
+ const steps = [];
71
+ const pluginName = pluginPath.startsWith("@") && pluginPath.includes("/") ? pluginPath : `${prefix}-${pluginPath}`;
72
+ debug("running diagnostics for %o (resolved name: %o)", pluginPath, pluginName);
73
+ let pluginModule = null;
74
+ let resolvedFrom = "";
75
+ if (pluginsFolder) {
76
+ const absFolder = resolve(pluginsFolder);
77
+ const pluginDir = join(absFolder, pluginName);
78
+ debug("checking plugins folder: %o", pluginDir);
79
+ if (!existsSync(absFolder)) {
80
+ steps.push({
81
+ phase: "resolve",
82
+ pass: false,
83
+ message: `Plugins folder does not exist: ${absFolder}`
84
+ });
85
+ return steps;
86
+ }
87
+ if (!existsSync(pluginDir)) {
88
+ debug("plugin directory not found: %o", pluginDir);
89
+ steps.push({
90
+ phase: "resolve",
91
+ pass: false,
92
+ message: `Plugin directory not found: ${pluginDir} — expected a folder named "${pluginName}" inside "${absFolder}"`
93
+ });
94
+ return steps;
95
+ }
96
+ const result = await tryResolve(pluginDir);
97
+ if (result.module) {
98
+ pluginModule = result.module;
99
+ resolvedFrom = pluginDir;
100
+ debug("resolved from plugins folder: %o", pluginDir);
101
+ } else {
102
+ const missingDep = parseMissingDependency(result.error ?? "", pluginDir);
103
+ steps.push({
104
+ phase: "resolve",
105
+ pass: false,
106
+ message: missingDep ? `Plugin found at ${pluginDir} but has a missing dependency: ${missingDep}` : `Plugin found at ${pluginDir} but failed to load: ${result.error}`
107
+ });
108
+ return steps;
109
+ }
110
+ }
111
+ if (!pluginModule) {
112
+ const result = await tryResolve(pluginName);
113
+ if (result.module) {
114
+ pluginModule = result.module;
115
+ resolvedFrom = pluginName;
116
+ debug("resolved from node_modules: %o", pluginName);
117
+ } else {
118
+ const missingDep = parseMissingDependency(result.error ?? "", pluginName);
119
+ steps.push({
120
+ phase: "resolve",
121
+ pass: false,
122
+ message: missingDep ? `Package "${pluginName}" found but has a missing dependency: ${missingDep}` : `Package "${pluginName}" not found in node_modules — try: npm install ${pluginName}`
123
+ });
124
+ return steps;
125
+ }
126
+ }
127
+ steps.push({
128
+ phase: "resolve",
129
+ pass: true,
130
+ message: `Module resolved from ${resolvedFrom}`
131
+ });
132
+ if (!isValidExport(pluginModule)) {
133
+ const exportKeys = pluginModule ? Object.keys(pluginModule).join(", ") : "none";
134
+ steps.push({
135
+ phase: "export",
136
+ pass: false,
137
+ message: `Module does not export a function or class (default export). Exported keys: [${exportKeys}]`
138
+ });
139
+ return steps;
140
+ }
141
+ const moduleType = isES6(pluginModule) ? "ES6 (default export)" : "CommonJS (factory function)";
142
+ steps.push({
143
+ phase: "export",
144
+ pass: true,
145
+ message: `Valid ${moduleType} plugin export detected`
146
+ });
147
+ let instance;
148
+ try {
149
+ if (isES6(pluginModule)) instance = new pluginModule.default(pluginConfig, {
150
+ config: pluginConfig,
151
+ logger: console
152
+ });
153
+ else instance = pluginModule(pluginConfig, {
154
+ config: pluginConfig,
155
+ logger: console
156
+ });
157
+ } catch (err) {
158
+ steps.push({
159
+ phase: "instantiate",
160
+ pass: false,
161
+ message: `Plugin threw during instantiation: ${err.message}`
162
+ });
163
+ return steps;
164
+ }
165
+ if (!instance || typeof instance !== "object" && typeof instance !== "function") {
166
+ steps.push({
167
+ phase: "instantiate",
168
+ pass: false,
169
+ message: `Plugin constructor/factory returned ${instance === null ? "null" : typeof instance} instead of an object`
170
+ });
171
+ return steps;
172
+ }
173
+ steps.push({
174
+ phase: "instantiate",
175
+ pass: true,
176
+ message: "Plugin instantiated successfully"
177
+ });
178
+ if (!(customSanityCheck ?? getSanityCheck(category))(instance)) {
179
+ const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(instance) ?? {}).filter((m) => m !== "constructor").concat(Object.keys(instance));
180
+ const unique = [...new Set(methods)];
181
+ steps.push({
182
+ phase: "sanity-check",
183
+ pass: false,
184
+ message: `Plugin does not implement the required methods for category "${category}". Available methods: [${unique.join(", ")}]`
185
+ });
186
+ return steps;
187
+ }
188
+ steps.push({
189
+ phase: "sanity-check",
190
+ pass: true,
191
+ message: `Plugin passes sanity check for category "${category}"`
192
+ });
193
+ return steps;
194
+ }
195
+ /**
196
+ * When a MODULE_NOT_FOUND error is about a transitive dependency
197
+ * (not the plugin itself), extract the missing module name.
198
+ */
199
+ function parseMissingDependency(message, pluginPath) {
200
+ const match = message.match(/Cannot find module '([^']+)'/);
201
+ if (match && match[1] && !match[1].includes(pluginPath)) return match[1];
202
+ return null;
203
+ }
204
+ //#endregion
205
+ export { runDiagnostics };
206
+
207
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.js","names":[],"sources":["../src/diagnostics.ts"],"sourcesContent":["import buildDebug from 'debug';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { PLUGIN_PREFIX } from '@verdaccio/core';\n\nimport { getSanityCheck } from './sanity-checks';\nimport type { DiagnosticStep, VerifyPluginOptions } from './types';\n\nconst debug = buildDebug('verdaccio:plugin:verifier:diagnostics');\n\n// createRequire needs an absolute path; works in both ESM and CJS contexts\nconst requireModule = createRequire(\n typeof __filename !== 'undefined' ? __filename : import.meta.url\n);\n\nfunction isValidExport(plugin: any): boolean {\n return typeof plugin === 'function' || typeof plugin?.default === 'function';\n}\n\nfunction isES6(plugin: any): boolean {\n return plugin && typeof plugin === 'object' && 'default' in plugin;\n}\n\n/**\n * Resolve the ESM entry point for a directory-based plugin.\n * import() doesn't support directory imports, so we resolve via package.json.\n */\nexport function resolveEntryPoint(dirPath: string): string {\n const pkgPath = join(dirPath, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n if (pkg.exports) {\n const dotExport = pkg.exports['.'];\n if (typeof dotExport === 'string') {\n return join(dirPath, dotExport);\n }\n if (dotExport?.import?.default) {\n return join(dirPath, dotExport.import.default);\n }\n if (dotExport?.import && typeof dotExport.import === 'string') {\n return join(dirPath, dotExport.import);\n }\n if (dotExport?.default) {\n return join(dirPath, dotExport.default);\n }\n }\n if (pkg.module) {\n return join(dirPath, pkg.module);\n }\n if (pkg.main) {\n return join(dirPath, pkg.main);\n }\n } catch {\n // fall through\n }\n }\n return join(dirPath, 'index.js');\n}\n\n/**\n * Try to load a module, falling back from require() to import() for ESM.\n * Handles CJS require(), ESM require() shim errors (bundlers), and\n * ERR_REQUIRE_ESM — always falls through to dynamic import() on any\n * require() failure.\n */\nasync function tryResolve(modulePath: string): Promise<{ module: any; error?: string }> {\n // Try require() first (fast path for CJS modules)\n try {\n return { module: requireModule(modulePath) };\n } catch (requireErr: any) {\n debug('require() failed for %o: %s — trying dynamic import', modulePath, requireErr.message);\n }\n\n // Fallback to dynamic import() for ESM modules\n try {\n let importPath = modulePath;\n if (existsSync(modulePath) && existsSync(join(modulePath, 'package.json'))) {\n importPath = resolveEntryPoint(modulePath);\n debug('resolved ESM entry point: %o', importPath);\n }\n const importUrl = importPath.startsWith('/') ? pathToFileURL(importPath).href : importPath;\n const mod = await import(importUrl);\n return { module: mod };\n } catch (importErr: any) {\n return { module: null, error: importErr.message };\n }\n}\n\n/**\n * Runs step-by-step diagnostics to identify exactly which phase of\n * plugin loading fails. This replicates the same resolution logic\n * as `asyncLoadPlugin` but tests each step independently.\n */\nexport async function runDiagnostics(options: VerifyPluginOptions): Promise<DiagnosticStep[]> {\n const {\n pluginPath,\n category,\n pluginConfig = {},\n sanityCheck: customSanityCheck,\n prefix = PLUGIN_PREFIX,\n pluginsFolder,\n } = options;\n\n const steps: DiagnosticStep[] = [];\n const isScoped = pluginPath.startsWith('@') && pluginPath.includes('/');\n const pluginName = isScoped ? pluginPath : `${prefix}-${pluginPath}`;\n\n debug('running diagnostics for %o (resolved name: %o)', pluginPath, pluginName);\n\n // --- Phase 1: Resolve ---\n let pluginModule: any = null;\n let resolvedFrom = '';\n\n // Try plugins folder first\n if (pluginsFolder) {\n const absFolder = resolve(pluginsFolder);\n const pluginDir = join(absFolder, pluginName);\n debug('checking plugins folder: %o', pluginDir);\n\n if (!existsSync(absFolder)) {\n steps.push({\n phase: 'resolve',\n pass: false,\n message: `Plugins folder does not exist: ${absFolder}`,\n });\n return steps;\n }\n\n if (!existsSync(pluginDir)) {\n debug('plugin directory not found: %o', pluginDir);\n steps.push({\n phase: 'resolve',\n pass: false,\n message: `Plugin directory not found: ${pluginDir} — expected a folder named \"${pluginName}\" inside \"${absFolder}\"`,\n });\n return steps;\n }\n\n const result = await tryResolve(pluginDir);\n if (result.module) {\n pluginModule = result.module;\n resolvedFrom = pluginDir;\n debug('resolved from plugins folder: %o', pluginDir);\n } else {\n const missingDep = parseMissingDependency(result.error ?? '', pluginDir);\n steps.push({\n phase: 'resolve',\n pass: false,\n message: missingDep\n ? `Plugin found at ${pluginDir} but has a missing dependency: ${missingDep}`\n : `Plugin found at ${pluginDir} but failed to load: ${result.error}`,\n });\n return steps;\n }\n }\n\n // Try node_modules if not found in plugins folder\n if (!pluginModule) {\n const result = await tryResolve(pluginName);\n if (result.module) {\n pluginModule = result.module;\n resolvedFrom = pluginName;\n debug('resolved from node_modules: %o', pluginName);\n } else {\n const missingDep = parseMissingDependency(result.error ?? '', pluginName);\n steps.push({\n phase: 'resolve',\n pass: false,\n message: missingDep\n ? `Package \"${pluginName}\" found but has a missing dependency: ${missingDep}`\n : `Package \"${pluginName}\" not found in node_modules — try: npm install ${pluginName}`,\n });\n return steps;\n }\n }\n\n steps.push({\n phase: 'resolve',\n pass: true,\n message: `Module resolved from ${resolvedFrom}`,\n });\n\n // --- Phase 2: Export validation ---\n if (!isValidExport(pluginModule)) {\n const exportKeys = pluginModule ? Object.keys(pluginModule).join(', ') : 'none';\n steps.push({\n phase: 'export',\n pass: false,\n message: `Module does not export a function or class (default export). Exported keys: [${exportKeys}]`,\n });\n return steps;\n }\n\n const moduleType = isES6(pluginModule) ? 'ES6 (default export)' : 'CommonJS (factory function)';\n steps.push({\n phase: 'export',\n pass: true,\n message: `Valid ${moduleType} plugin export detected`,\n });\n\n // --- Phase 3: Instantiation ---\n let instance: any;\n try {\n if (isES6(pluginModule)) {\n instance = new pluginModule.default(pluginConfig, { config: pluginConfig, logger: console });\n } else {\n instance = pluginModule(pluginConfig, { config: pluginConfig, logger: console });\n }\n } catch (err: any) {\n steps.push({\n phase: 'instantiate',\n pass: false,\n message: `Plugin threw during instantiation: ${err.message}`,\n });\n return steps;\n }\n\n if (!instance || (typeof instance !== 'object' && typeof instance !== 'function')) {\n steps.push({\n phase: 'instantiate',\n pass: false,\n message: `Plugin constructor/factory returned ${instance === null ? 'null' : typeof instance} instead of an object`,\n });\n return steps;\n }\n\n steps.push({\n phase: 'instantiate',\n pass: true,\n message: 'Plugin instantiated successfully',\n });\n\n // --- Phase 4: Sanity check ---\n const sanityCheck = customSanityCheck ?? getSanityCheck(category);\n const passed = sanityCheck(instance);\n\n if (!passed) {\n const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(instance) ?? {})\n .filter((m) => m !== 'constructor')\n .concat(Object.keys(instance));\n const unique = [...new Set(methods)];\n\n steps.push({\n phase: 'sanity-check',\n pass: false,\n message: `Plugin does not implement the required methods for category \"${category}\". Available methods: [${unique.join(', ')}]`,\n });\n return steps;\n }\n\n steps.push({\n phase: 'sanity-check',\n pass: true,\n message: `Plugin passes sanity check for category \"${category}\"`,\n });\n\n return steps;\n}\n\n/**\n * When a MODULE_NOT_FOUND error is about a transitive dependency\n * (not the plugin itself), extract the missing module name.\n */\nfunction parseMissingDependency(message: string, pluginPath: string): string | null {\n const match = message.match(/Cannot find module '([^']+)'/);\n if (match && match[1] && !match[1].includes(pluginPath)) {\n return match[1];\n }\n return null;\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_sanity_checks = require("./sanity-checks.cjs");
3
+ const require_diagnostics = require("./diagnostics.cjs");
4
+ const require_verify_plugin = require("./verify-plugin.cjs");
5
+ exports.authSanityCheck = require_sanity_checks.authSanityCheck;
6
+ exports.filterSanityCheck = require_sanity_checks.filterSanityCheck;
7
+ exports.getSanityCheck = require_sanity_checks.getSanityCheck;
8
+ exports.middlewareSanityCheck = require_sanity_checks.middlewareSanityCheck;
9
+ exports.runDiagnostics = require_diagnostics.runDiagnostics;
10
+ exports.storageSanityCheck = require_sanity_checks.storageSanityCheck;
11
+ exports.verifyPlugin = require_verify_plugin.verifyPlugin;
@@ -0,0 +1,5 @@
1
+ export { verifyPlugin } from './verify-plugin';
2
+ export { authSanityCheck, storageSanityCheck, middlewareSanityCheck, filterSanityCheck, getSanityCheck, } from './sanity-checks';
3
+ export { runDiagnostics } from './diagnostics';
4
+ export type { VerifyPluginOptions, VerifyResult, PluginCategory, DiagnosticStep } from './types';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC"}
package/build/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import { authSanityCheck, filterSanityCheck, getSanityCheck, middlewareSanityCheck, storageSanityCheck } from "./sanity-checks.js";
2
+ import { runDiagnostics } from "./diagnostics.js";
3
+ import { verifyPlugin } from "./verify-plugin.js";
4
+ export { authSanityCheck, filterSanityCheck, getSanityCheck, middlewareSanityCheck, runDiagnostics, storageSanityCheck, verifyPlugin };
@@ -0,0 +1,27 @@
1
+ require("./_virtual/_rolldown/runtime.cjs");
2
+ let _verdaccio_core = require("@verdaccio/core");
3
+ //#region src/sanity-checks.ts
4
+ var authSanityCheck = _verdaccio_core.pluginUtils.authSanityCheck;
5
+ var storageSanityCheck = _verdaccio_core.pluginUtils.storageSanityCheck;
6
+ var middlewareSanityCheck = _verdaccio_core.pluginUtils.middlewareSanityCheck;
7
+ var filterSanityCheck = _verdaccio_core.pluginUtils.filterSanityCheck;
8
+ /**
9
+ * Returns the appropriate sanity check function for the given plugin category.
10
+ */
11
+ function getSanityCheck(category) {
12
+ switch (category) {
13
+ case _verdaccio_core.PLUGIN_CATEGORY.AUTHENTICATION: return authSanityCheck;
14
+ case _verdaccio_core.PLUGIN_CATEGORY.STORAGE: return storageSanityCheck;
15
+ case _verdaccio_core.PLUGIN_CATEGORY.MIDDLEWARE: return middlewareSanityCheck;
16
+ case _verdaccio_core.PLUGIN_CATEGORY.FILTER: return filterSanityCheck;
17
+ default: return () => true;
18
+ }
19
+ }
20
+ //#endregion
21
+ exports.authSanityCheck = authSanityCheck;
22
+ exports.filterSanityCheck = filterSanityCheck;
23
+ exports.getSanityCheck = getSanityCheck;
24
+ exports.middlewareSanityCheck = middlewareSanityCheck;
25
+ exports.storageSanityCheck = storageSanityCheck;
26
+
27
+ //# sourceMappingURL=sanity-checks.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanity-checks.cjs","names":[],"sources":["../src/sanity-checks.ts"],"sourcesContent":["import { PLUGIN_CATEGORY, pluginUtils } from '@verdaccio/core';\n\nimport type { PluginCategory } from './types';\n\n// Re-export sanity checks from @verdaccio/core for convenience.\nexport const authSanityCheck = pluginUtils.authSanityCheck;\nexport const storageSanityCheck = pluginUtils.storageSanityCheck;\nexport const middlewareSanityCheck = pluginUtils.middlewareSanityCheck;\nexport const filterSanityCheck = pluginUtils.filterSanityCheck;\n\n/**\n * Returns the appropriate sanity check function for the given plugin category.\n */\nexport function getSanityCheck(category: PluginCategory): (plugin: any) => boolean {\n switch (category) {\n case PLUGIN_CATEGORY.AUTHENTICATION:\n return authSanityCheck;\n case PLUGIN_CATEGORY.STORAGE:\n return storageSanityCheck;\n case PLUGIN_CATEGORY.MIDDLEWARE:\n return middlewareSanityCheck;\n case PLUGIN_CATEGORY.FILTER:\n return filterSanityCheck;\n default:\n return () => true;\n }\n}\n"],"mappings":";;;AAKA,IAAa,kBAAkB,gBAAA,YAAY;AAC3C,IAAa,qBAAqB,gBAAA,YAAY;AAC9C,IAAa,wBAAwB,gBAAA,YAAY;AACjD,IAAa,oBAAoB,gBAAA,YAAY;;;;AAK7C,SAAgB,eAAe,UAAoD;AACjF,SAAQ,UAAR;EACE,KAAK,gBAAA,gBAAgB,eACnB,QAAO;EACT,KAAK,gBAAA,gBAAgB,QACnB,QAAO;EACT,KAAK,gBAAA,gBAAgB,WACnB,QAAO;EACT,KAAK,gBAAA,gBAAgB,OACnB,QAAO;EACT,QACE,cAAa"}
@@ -0,0 +1,11 @@
1
+ import { pluginUtils } from '@verdaccio/core';
2
+ import { PluginCategory } from './types';
3
+ export declare const authSanityCheck: typeof pluginUtils.authSanityCheck;
4
+ export declare const storageSanityCheck: typeof pluginUtils.storageSanityCheck;
5
+ export declare const middlewareSanityCheck: typeof pluginUtils.middlewareSanityCheck;
6
+ export declare const filterSanityCheck: typeof pluginUtils.filterSanityCheck;
7
+ /**
8
+ * Returns the appropriate sanity check function for the given plugin category.
9
+ */
10
+ export declare function getSanityCheck(category: PluginCategory): (plugin: any) => boolean;
11
+ //# sourceMappingURL=sanity-checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanity-checks.d.ts","sourceRoot":"","sources":["../src/sanity-checks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,eAAO,MAAM,eAAe,oCAA8B,CAAC;AAC3D,eAAO,MAAM,kBAAkB,uCAAiC,CAAC;AACjE,eAAO,MAAM,qBAAqB,0CAAoC,CAAC;AACvE,eAAO,MAAM,iBAAiB,sCAAgC,CAAC;AAE/D;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,CAajF"}
@@ -0,0 +1,22 @@
1
+ import { PLUGIN_CATEGORY, pluginUtils } from "@verdaccio/core";
2
+ //#region src/sanity-checks.ts
3
+ var authSanityCheck = pluginUtils.authSanityCheck;
4
+ var storageSanityCheck = pluginUtils.storageSanityCheck;
5
+ var middlewareSanityCheck = pluginUtils.middlewareSanityCheck;
6
+ var filterSanityCheck = pluginUtils.filterSanityCheck;
7
+ /**
8
+ * Returns the appropriate sanity check function for the given plugin category.
9
+ */
10
+ function getSanityCheck(category) {
11
+ switch (category) {
12
+ case PLUGIN_CATEGORY.AUTHENTICATION: return authSanityCheck;
13
+ case PLUGIN_CATEGORY.STORAGE: return storageSanityCheck;
14
+ case PLUGIN_CATEGORY.MIDDLEWARE: return middlewareSanityCheck;
15
+ case PLUGIN_CATEGORY.FILTER: return filterSanityCheck;
16
+ default: return () => true;
17
+ }
18
+ }
19
+ //#endregion
20
+ export { authSanityCheck, filterSanityCheck, getSanityCheck, middlewareSanityCheck, storageSanityCheck };
21
+
22
+ //# sourceMappingURL=sanity-checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanity-checks.js","names":[],"sources":["../src/sanity-checks.ts"],"sourcesContent":["import { PLUGIN_CATEGORY, pluginUtils } from '@verdaccio/core';\n\nimport type { PluginCategory } from './types';\n\n// Re-export sanity checks from @verdaccio/core for convenience.\nexport const authSanityCheck = pluginUtils.authSanityCheck;\nexport const storageSanityCheck = pluginUtils.storageSanityCheck;\nexport const middlewareSanityCheck = pluginUtils.middlewareSanityCheck;\nexport const filterSanityCheck = pluginUtils.filterSanityCheck;\n\n/**\n * Returns the appropriate sanity check function for the given plugin category.\n */\nexport function getSanityCheck(category: PluginCategory): (plugin: any) => boolean {\n switch (category) {\n case PLUGIN_CATEGORY.AUTHENTICATION:\n return authSanityCheck;\n case PLUGIN_CATEGORY.STORAGE:\n return storageSanityCheck;\n case PLUGIN_CATEGORY.MIDDLEWARE:\n return middlewareSanityCheck;\n case PLUGIN_CATEGORY.FILTER:\n return filterSanityCheck;\n default:\n return () => true;\n }\n}\n"],"mappings":";;AAKA,IAAa,kBAAkB,YAAY;AAC3C,IAAa,qBAAqB,YAAY;AAC9C,IAAa,wBAAwB,YAAY;AACjD,IAAa,oBAAoB,YAAY;;;;AAK7C,SAAgB,eAAe,UAAoD;AACjF,SAAQ,UAAR;EACE,KAAK,gBAAgB,eACnB,QAAO;EACT,KAAK,gBAAgB,QACnB,QAAO;EACT,KAAK,gBAAgB,WACnB,QAAO;EACT,KAAK,gBAAgB,OACnB,QAAO;EACT,QACE,cAAa"}