evernote-upgrade-migration

SKILL.md

Evernote Upgrade & Migration

Overview

Guide for upgrading Evernote SDK versions, handling breaking changes, and migrating legacy integrations to current API patterns.

Prerequisites

  • Existing Evernote integration
  • Test environment for validation
  • Understanding of current implementation

SDK Version History

Version Node.js Key Changes
2.0.x 10+ Promise-based API, ES6 modules
1.25.x 8+ Legacy callback pattern
1.x 6+ Original SDK

Instructions

Step 1: Check Current Version

# Check installed version
npm list evernote

# Check available versions
npm view evernote versions

# Check for outdated packages
npm outdated evernote

Step 2: Review Breaking Changes

// SDK 1.x -> 2.x Breaking Changes

// 1. Client initialization changed
// OLD (1.x):
var Evernote = require('evernote').Evernote;
var client = new Evernote.Client({
  consumerKey: 'key',
  consumerSecret: 'secret',
  sandbox: true
});

// NEW (2.x):
const Evernote = require('evernote');
const client = new Evernote.Client({
  consumerKey: 'key',
  consumerSecret: 'secret',
  sandbox: true
});

// 2. API calls return Promises (not callbacks)
// OLD (1.x):
noteStore.listNotebooks(function(err, notebooks) {
  if (err) console.error(err);
  else console.log(notebooks);
});

// NEW (2.x):
noteStore.listNotebooks()
  .then(notebooks => console.log(notebooks))
  .catch(err => console.error(err));

// Or with async/await:
const notebooks = await noteStore.listNotebooks();

// 3. Type constructors
// OLD (1.x):
var note = new Evernote.Note();

// NEW (2.x):
const note = new Evernote.Types.Note();

Step 3: Migration Script

// scripts/migrate-to-v2.js
const fs = require('fs');
const path = require('path');
const glob = require('glob');

const migrations = [
  // Client import
  {
    pattern: /require\(['"]evernote['"]\)\.Evernote/g,
    replacement: "require('evernote')"
  },

  // Type constructors
  {
    pattern: /new Evernote\.Note\(\)/g,
    replacement: 'new Evernote.Types.Note()'
  },
  {
    pattern: /new Evernote\.Notebook\(\)/g,
    replacement: 'new Evernote.Types.Notebook()'
  },
  {
    pattern: /new Evernote\.Tag\(\)/g,
    replacement: 'new Evernote.Types.Tag()'
  },
  {
    pattern: /new Evernote\.Resource\(\)/g,
    replacement: 'new Evernote.Types.Resource()'
  },

  // NoteFilter
  {
    pattern: /new Evernote\.NoteFilter\(\)/g,
    replacement: 'new Evernote.NoteStore.NoteFilter()'
  },

  // NotesMetadataResultSpec
  {
    pattern: /new Evernote\.NotesMetadataResultSpec\(\)/g,
    replacement: 'new Evernote.NoteStore.NotesMetadataResultSpec()'
  }
];

function migrateFile(filePath) {
  let content = fs.readFileSync(filePath, 'utf8');
  let modified = false;

  for (const { pattern, replacement } of migrations) {
    if (pattern.test(content)) {
      content = content.replace(pattern, replacement);
      modified = true;
    }
  }

  if (modified) {
    // Create backup
    fs.writeFileSync(`${filePath}.bak`, fs.readFileSync(filePath));
    // Write migrated file
    fs.writeFileSync(filePath, content);
    console.log(`Migrated: ${filePath}`);
    return true;
  }

  return false;
}

// Run migration
const files = glob.sync('src/**/*.js');
let migratedCount = 0;

for (const file of files) {
  if (migrateFile(file)) {
    migratedCount++;
  }
}

console.log(`\nMigration complete. ${migratedCount} files modified.`);
console.log('Backup files created with .bak extension');

Step 4: Convert Callbacks to Promises

// utils/promisify-legacy.js

/**
 * Wrapper for legacy callback-based code
 */
function promisifyNoteStore(noteStore) {
  const promisified = {};

  // List of methods that need promisification
  const methods = [
    'listNotebooks',
    'getNotebook',
    'createNotebook',
    'updateNotebook',
    'listTags',
    'getTag',
    'createTag',
    'getNote',
    'createNote',
    'updateNote',
    'deleteNote',
    'findNotesMetadata',
    'getNoteContent',
    'getResource'
  ];

  for (const method of methods) {
    promisified[method] = (...args) => {
      return new Promise((resolve, reject) => {
        noteStore[method](...args, (err, result) => {
          if (err) reject(err);
          else resolve(result);
        });
      });
    };
  }

  return promisified;
}

module.exports = { promisifyNoteStore };

Step 5: Upgrade Dependencies

# Update to latest version
npm install evernote@latest

# If using TypeScript, update types
npm install @types/evernote@latest --save-dev

# Update related dependencies
npm update

Step 6: Test Suite Updates

// tests/migration-tests.js
const assert = require('assert');
const Evernote = require('evernote');

describe('SDK v2 Migration Tests', () => {
  let client;
  let noteStore;

  before(() => {
    client = new Evernote.Client({
      token: process.env.EVERNOTE_ACCESS_TOKEN,
      sandbox: true
    });
    noteStore = client.getNoteStore();
  });

  describe('Type Constructors', () => {
    it('should create Note using Types namespace', () => {
      const note = new Evernote.Types.Note();
      assert(note);
      note.title = 'Test';
      assert.equal(note.title, 'Test');
    });

    it('should create NoteFilter using NoteStore namespace', () => {
      const filter = new Evernote.NoteStore.NoteFilter({
        words: 'test'
      });
      assert(filter);
      assert.equal(filter.words, 'test');
    });
  });

  describe('Promise-based API', () => {
    it('should return Promise from listNotebooks', async () => {
      const result = noteStore.listNotebooks();
      assert(result instanceof Promise);
    });

    it('should resolve notebooks array', async () => {
      const notebooks = await noteStore.listNotebooks();
      assert(Array.isArray(notebooks));
    });
  });

  describe('Error Handling', () => {
    it('should reject with EDAMUserException for invalid data', async () => {
      const note = new Evernote.Types.Note();
      // Missing required fields

      try {
        await noteStore.createNote(note);
        assert.fail('Should have thrown');
      } catch (error) {
        assert(error.errorCode !== undefined);
      }
    });
  });
});

Step 7: Compatibility Layer

// utils/compatibility.js

/**
 * Compatibility layer for gradual migration
 * Supports both callback and Promise patterns
 */
class CompatibleNoteStore {
  constructor(noteStore) {
    this.store = noteStore;
  }

  listNotebooks(callback) {
    const promise = this.store.listNotebooks();

    if (callback) {
      // Legacy callback support
      promise
        .then(result => callback(null, result))
        .catch(error => callback(error, null));
      return;
    }

    return promise;
  }

  getNote(guid, withContent, withResources, withRecognition, withAlternate, callback) {
    const promise = this.store.getNote(
      guid,
      withContent,
      withResources,
      withRecognition,
      withAlternate
    );

    if (callback) {
      promise
        .then(result => callback(null, result))
        .catch(error => callback(error, null));
      return;
    }

    return promise;
  }

  createNote(note, callback) {
    const promise = this.store.createNote(note);

    if (callback) {
      promise
        .then(result => callback(null, result))
        .catch(error => callback(error, null));
      return;
    }

    return promise;
  }

  // Add more methods as needed
}

module.exports = CompatibleNoteStore;

Step 8: Deprecation Warnings

// utils/deprecation-warnings.js

const deprecations = new Set();

function warnOnce(message) {
  if (!deprecations.has(message)) {
    deprecations.add(message);
    console.warn(`[DEPRECATION WARNING] ${message}`);
  }
}

// Proxy to add deprecation warnings
function wrapWithDeprecationWarnings(noteStore) {
  return new Proxy(noteStore, {
    get(target, prop) {
      // Warn about deprecated patterns
      if (prop === 'getNote') {
        return (...args) => {
          if (args.length === 6 && typeof args[5] === 'function') {
            warnOnce(
              'Callback pattern is deprecated. ' +
              'Use Promise: noteStore.getNote(...).then(note => ...)'
            );
          }
          return target[prop](...args);
        };
      }

      return target[prop];
    }
  });
}

module.exports = { wrapWithDeprecationWarnings };

Migration Checklist

## SDK v2 Migration Checklist

### Pre-Migration
- [ ] Backup current codebase
- [ ] Document current SDK version
- [ ] Review breaking changes
- [ ] Set up test environment

### Code Changes
- [ ] Update import statements
- [ ] Update type constructors (Types namespace)
- [ ] Update filter constructors (NoteStore namespace)
- [ ] Convert callbacks to Promises/async-await
- [ ] Update error handling for Promise rejections

### Testing
- [ ] Run migration script on test branch
- [ ] Execute full test suite
- [ ] Test OAuth flow end-to-end
- [ ] Test note CRUD operations
- [ ] Test search functionality
- [ ] Verify error handling

### Deployment
- [ ] Deploy to staging
- [ ] Run integration tests
- [ ] Monitor for errors
- [ ] Deploy to production
- [ ] Monitor metrics

### Cleanup
- [ ] Remove compatibility layers (after validation period)
- [ ] Remove backup files
- [ ] Update documentation
- [ ] Archive migration scripts

Output

  • SDK version comparison
  • Automated migration script
  • Callback to Promise conversion
  • Compatibility layer for gradual migration
  • Migration test suite
  • Deprecation warning system

Error Handling

Issue Cause Solution
Evernote.Note is not a constructor Old import style Use Evernote.Types.Note
callback is not a function Mixed patterns Use Promise or callback, not both
Cannot read property 'then' Using old callback-only method Update to Promise-based method

Resources

Next Steps

For CI/CD integration, see evernote-ci-integration.

Weekly Installs
13
GitHub Stars
1.6K
First Seen
Feb 18, 2026
Installed on
codex13
mcpjam12
claude-code12
junie12
windsurf12
zencoder12