@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,218 @@
1
+ import { join } from 'node:path';
2
+ import { describe, expect, it } from 'vitest';
3
+
4
+ import { PLUGIN_CATEGORY } from '@verdaccio/core';
5
+
6
+ import { verifyPlugin } from '../src/verify-plugin';
7
+
8
+ const fixturesPath = join(__dirname, 'fixtures');
9
+
10
+ describe('verifyPlugin', () => {
11
+ describe('authentication plugins', () => {
12
+ it('should verify a valid auth plugin (CommonJS)', async () => {
13
+ const result = await verifyPlugin({
14
+ pluginPath: 'valid-auth-plugin',
15
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
16
+ pluginsFolder: fixturesPath,
17
+ });
18
+
19
+ expect(result.success).toBe(true);
20
+ expect(result.pluginsLoaded).toBe(1);
21
+ expect(result.error).toBeUndefined();
22
+ expect(result.diagnostics).toBeUndefined();
23
+ });
24
+
25
+ it('should verify a valid auth plugin (ES6 default export)', async () => {
26
+ const result = await verifyPlugin({
27
+ pluginPath: 'valid-auth-es6-plugin',
28
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
29
+ pluginsFolder: fixturesPath,
30
+ });
31
+
32
+ expect(result.success).toBe(true);
33
+ expect(result.pluginsLoaded).toBe(1);
34
+ });
35
+
36
+ it('should fail sanity check when a middleware plugin is tested as auth', async () => {
37
+ const result = await verifyPlugin({
38
+ pluginPath: 'wrong-category-plugin',
39
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
40
+ pluginsFolder: fixturesPath,
41
+ });
42
+
43
+ expect(result.success).toBe(false);
44
+ expect(result.pluginsLoaded).toBe(0);
45
+ expect(result.diagnostics).toBeDefined();
46
+
47
+ const sanityStep = result.diagnostics!.find((d) => d.phase === 'sanity-check');
48
+ expect(sanityStep?.pass).toBe(false);
49
+ expect(sanityStep?.message).toContain('authentication');
50
+ expect(sanityStep?.message).toContain('register_middlewares');
51
+ });
52
+ });
53
+
54
+ describe('storage plugins', () => {
55
+ it('should verify a valid storage plugin', async () => {
56
+ const result = await verifyPlugin({
57
+ pluginPath: 'valid-storage-plugin',
58
+ category: PLUGIN_CATEGORY.STORAGE,
59
+ pluginsFolder: fixturesPath,
60
+ });
61
+
62
+ expect(result.success).toBe(true);
63
+ expect(result.pluginsLoaded).toBe(1);
64
+ });
65
+ });
66
+
67
+ describe('middleware plugins', () => {
68
+ it('should verify a valid middleware plugin', async () => {
69
+ const result = await verifyPlugin({
70
+ pluginPath: 'valid-middleware-plugin',
71
+ category: PLUGIN_CATEGORY.MIDDLEWARE,
72
+ pluginsFolder: fixturesPath,
73
+ });
74
+
75
+ expect(result.success).toBe(true);
76
+ expect(result.pluginsLoaded).toBe(1);
77
+ });
78
+ });
79
+
80
+ describe('filter plugins', () => {
81
+ it('should verify a valid filter plugin', async () => {
82
+ const result = await verifyPlugin({
83
+ pluginPath: 'valid-filter-plugin',
84
+ category: PLUGIN_CATEGORY.FILTER,
85
+ pluginsFolder: fixturesPath,
86
+ });
87
+
88
+ expect(result.success).toBe(true);
89
+ expect(result.pluginsLoaded).toBe(1);
90
+ });
91
+ });
92
+
93
+ describe('diagnostics', () => {
94
+ it('should diagnose plugin directory not found', async () => {
95
+ const result = await verifyPlugin({
96
+ pluginPath: 'non-existent-plugin',
97
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
98
+ pluginsFolder: fixturesPath,
99
+ });
100
+
101
+ expect(result.success).toBe(false);
102
+ expect(result.diagnostics).toBeDefined();
103
+ expect(result.diagnostics).toHaveLength(1);
104
+
105
+ const step = result.diagnostics![0];
106
+ expect(step.phase).toBe('resolve');
107
+ expect(step.pass).toBe(false);
108
+ expect(step.message).toContain('verdaccio-non-existent-plugin');
109
+ });
110
+
111
+ it('should diagnose non-existent plugins folder', async () => {
112
+ const result = await verifyPlugin({
113
+ pluginPath: 'some-plugin',
114
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
115
+ pluginsFolder: '/does/not/exist',
116
+ });
117
+
118
+ expect(result.success).toBe(false);
119
+ expect(result.diagnostics).toBeDefined();
120
+
121
+ const step = result.diagnostics![0];
122
+ expect(step.phase).toBe('resolve');
123
+ expect(step.pass).toBe(false);
124
+ expect(step.message).toContain('does not exist');
125
+ });
126
+
127
+ it('should diagnose invalid export shape', async () => {
128
+ const result = await verifyPlugin({
129
+ pluginPath: 'invalid-plugin',
130
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
131
+ pluginsFolder: fixturesPath,
132
+ });
133
+
134
+ expect(result.success).toBe(false);
135
+ expect(result.diagnostics).toBeDefined();
136
+
137
+ const resolveStep = result.diagnostics!.find((d) => d.phase === 'resolve');
138
+ expect(resolveStep?.pass).toBe(true);
139
+
140
+ const exportStep = result.diagnostics!.find((d) => d.phase === 'export');
141
+ expect(exportStep?.pass).toBe(false);
142
+ expect(exportStep?.message).toContain('does not export a function');
143
+ });
144
+
145
+ it('should diagnose instantiation failure', async () => {
146
+ const result = await verifyPlugin({
147
+ pluginPath: 'throwing-plugin',
148
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
149
+ pluginsFolder: fixturesPath,
150
+ });
151
+
152
+ expect(result.success).toBe(false);
153
+ expect(result.diagnostics).toBeDefined();
154
+
155
+ const instantiateStep = result.diagnostics!.find((d) => d.phase === 'instantiate');
156
+ expect(instantiateStep?.pass).toBe(false);
157
+ expect(instantiateStep?.message).toContain('Plugin initialization failed');
158
+ });
159
+
160
+ it('should diagnose failed sanity check and list available methods', async () => {
161
+ const result = await verifyPlugin({
162
+ pluginPath: 'wrong-category-plugin',
163
+ category: PLUGIN_CATEGORY.STORAGE,
164
+ pluginsFolder: fixturesPath,
165
+ });
166
+
167
+ expect(result.success).toBe(false);
168
+ expect(result.diagnostics).toBeDefined();
169
+
170
+ const steps = result.diagnostics!;
171
+ expect(steps.find((d) => d.phase === 'resolve')?.pass).toBe(true);
172
+ expect(steps.find((d) => d.phase === 'export')?.pass).toBe(true);
173
+ expect(steps.find((d) => d.phase === 'instantiate')?.pass).toBe(true);
174
+
175
+ const sanityStep = steps.find((d) => d.phase === 'sanity-check');
176
+ expect(sanityStep?.pass).toBe(false);
177
+ expect(sanityStep?.message).toContain('storage');
178
+ expect(sanityStep?.message).toContain('register_middlewares');
179
+ });
180
+
181
+ it('should show all steps passing on success', async () => {
182
+ const result = await verifyPlugin({
183
+ pluginPath: 'valid-auth-plugin',
184
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
185
+ pluginsFolder: fixturesPath,
186
+ });
187
+
188
+ expect(result.success).toBe(true);
189
+ expect(result.diagnostics).toBeUndefined();
190
+ });
191
+ });
192
+
193
+ describe('custom sanity check', () => {
194
+ it('should use a custom sanity check when provided', async () => {
195
+ const result = await verifyPlugin({
196
+ pluginPath: 'valid-auth-plugin',
197
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
198
+ pluginsFolder: fixturesPath,
199
+ sanityCheck: (plugin) => typeof plugin.authenticate === 'function',
200
+ });
201
+
202
+ expect(result.success).toBe(true);
203
+ expect(result.pluginsLoaded).toBe(1);
204
+ });
205
+
206
+ it('should fail with a custom sanity check that does not pass', async () => {
207
+ const result = await verifyPlugin({
208
+ pluginPath: 'valid-auth-plugin',
209
+ category: PLUGIN_CATEGORY.AUTHENTICATION,
210
+ pluginsFolder: fixturesPath,
211
+ sanityCheck: () => false,
212
+ });
213
+
214
+ expect(result.success).toBe(false);
215
+ expect(result.pluginsLoaded).toBe(0);
216
+ });
217
+ });
218
+ });
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "exclude": ["tests/**"]
4
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "extends": "../../../tsconfig.reference.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./build",
6
+ "composite": true,
7
+ "declaration": true
8
+ },
9
+ "include": ["src/**/*.ts"],
10
+ "references": [
11
+ {
12
+ "path": "../../core/core"
13
+ },
14
+ {
15
+ "path": "../../core/types"
16
+ },
17
+ {
18
+ "path": "../../loaders"
19
+ },
20
+ {
21
+ "path": "../../logger"
22
+ }
23
+ ]
24
+ }
@@ -0,0 +1,69 @@
1
+ import { builtinModules, createRequire } from 'node:module';
2
+ import path from 'node:path';
3
+
4
+ import dts from 'vite-plugin-dts';
5
+ import { defineConfig } from 'vite';
6
+
7
+ const dirname = import.meta.dirname;
8
+ const require = createRequire(path.resolve(dirname, 'package.json'));
9
+ const pkg = require('./package.json');
10
+
11
+ const nodeBuiltins = new Set([
12
+ ...builtinModules,
13
+ ...builtinModules.map((m) => `node:${m}`),
14
+ ]);
15
+
16
+ const externalDeps = new Set([
17
+ ...Object.keys(pkg.dependencies ?? {}),
18
+ ...Object.keys(pkg.devDependencies ?? {}),
19
+ ...Object.keys(pkg.peerDependencies ?? {}),
20
+ ]);
21
+
22
+ const isExternal = (id) => {
23
+ if (nodeBuiltins.has(id)) return true;
24
+ if (externalDeps.has(id)) return true;
25
+ if ([...externalDeps].some((dep) => id.startsWith(`${dep}/`))) return true;
26
+ return false;
27
+ };
28
+
29
+ const sharedOutput = {
30
+ preserveModules: true,
31
+ preserveModulesRoot: 'src',
32
+ };
33
+
34
+ export default defineConfig({
35
+ plugins: [
36
+ dts({
37
+ tsconfigPath: path.resolve(dirname, 'tsconfig.build.json'),
38
+ }),
39
+ ],
40
+ build: {
41
+ outDir: 'build',
42
+ emptyOutDir: true,
43
+ sourcemap: true,
44
+ minify: false,
45
+ lib: {
46
+ entry: [
47
+ path.resolve(dirname, 'src/index.ts'),
48
+ path.resolve(dirname, 'src/cli.ts'),
49
+ ],
50
+ },
51
+ rollupOptions: {
52
+ external: isExternal,
53
+ output: [
54
+ {
55
+ format: 'es',
56
+ entryFileNames: '[name].js',
57
+ chunkFileNames: '[name].js',
58
+ ...sharedOutput,
59
+ },
60
+ {
61
+ format: 'cjs',
62
+ entryFileNames: '[name].cjs',
63
+ chunkFileNames: '[name].cjs',
64
+ ...sharedOutput,
65
+ },
66
+ ],
67
+ },
68
+ },
69
+ });
@@ -0,0 +1,11 @@
1
+ // @ts-check
2
+ import { defineConfig } from 'vitest/config';
3
+
4
+ export default defineConfig({
5
+ test: {
6
+ coverage: {
7
+ exclude: ['tests/fixtures/**'],
8
+ },
9
+ globals: true,
10
+ },
11
+ });