promptgraph-mcp 2.4.6 → 2.4.7

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 (87) hide show
  1. package/github-import.js +4 -4
  2. package/index.js +47 -14
  3. package/indexer.js +14 -7
  4. package/package.json +1 -1
  5. package/parser.js +21 -115
  6. package/registry/training/bad/accepts-HISTORY.md +250 -0
  7. package/registry/training/bad/argparse-CHANGELOG.md +185 -0
  8. package/registry/training/bad/balanced-match-LICENSE.md +23 -0
  9. package/registry/training/bad/better-sqlite3-README.md +99 -0
  10. package/registry/training/bad/bindings-LICENSE.md +22 -0
  11. package/registry/training/bad/bl-LICENSE.md +13 -0
  12. package/registry/training/bad/body-parser-README.md +494 -0
  13. package/registry/training/bad/bytes-HISTORY.md +97 -0
  14. package/registry/training/bad/camelcase-README.md +135 -0
  15. package/registry/training/bad/chai-README.md +162 -0
  16. package/registry/training/bad/cli-progress-LICENSE.md +24 -0
  17. package/registry/training/bad/content-type-HISTORY.md +29 -0
  18. package/registry/training/bad/cookie-SECURITY.md +25 -0
  19. package/registry/training/bad/cookie-signature-HISTORY.md +70 -0
  20. package/registry/training/bad/cors-README.md +277 -0
  21. package/registry/training/bad/cross-spawn-README.md +89 -0
  22. package/registry/training/bad/deep-extend-CHANGELOG.md +46 -0
  23. package/registry/training/bad/depd-HISTORY.md +103 -0
  24. package/registry/training/bad/esprima-README.md +46 -0
  25. package/registry/training/bad/etag-HISTORY.md +83 -0
  26. package/registry/training/bad/expect-type-SECURITY.md +14 -0
  27. package/registry/training/bad/finalhandler-HISTORY.md +239 -0
  28. package/registry/training/bad/fresh-HISTORY.md +80 -0
  29. package/registry/training/bad/glob-LICENSE.md +63 -0
  30. package/registry/training/bad/hono-README.md +85 -0
  31. package/registry/training/bad/http-errors-HISTORY.md +186 -0
  32. package/registry/training/bad/jose-LICENSE.md +21 -0
  33. package/registry/training/bad/jose-README.md +153 -0
  34. package/registry/training/bad/js-yaml-README.md +299 -0
  35. package/registry/training/bad/json-schema-typed-LICENSE.md +57 -0
  36. package/registry/training/bad/json-stringify-safe-CHANGELOG.md +14 -0
  37. package/registry/training/bad/lru-cache-LICENSE.md +55 -0
  38. package/registry/training/bad/media-typer-HISTORY.md +50 -0
  39. package/registry/training/bad/minimatch-LICENSE.md +55 -0
  40. package/registry/training/bad/minimist-CHANGELOG.md +298 -0
  41. package/registry/training/bad/minimist-README.md +121 -0
  42. package/registry/training/bad/ms-LICENSE.md +21 -0
  43. package/registry/training/bad/negotiator-HISTORY.md +114 -0
  44. package/registry/training/bad/on-finished-HISTORY.md +98 -0
  45. package/registry/training/bad/pkce-challenge-CHANGELOG.md +114 -0
  46. package/registry/training/bad/postcss-README.md +28 -0
  47. package/registry/training/bad/prebuild-install-CHANGELOG.md +131 -0
  48. package/registry/training/bad/proxy-addr-HISTORY.md +161 -0
  49. package/registry/training/bad/qs-CHANGELOG.md +822 -0
  50. package/registry/training/bad/rc-README.md +227 -0
  51. package/registry/training/bad/readable-stream-CONTRIBUTING.md +38 -0
  52. package/registry/training/bad/router-HISTORY.md +228 -0
  53. package/registry/training/bad/semver-README.md +680 -0
  54. package/registry/training/bad/send-README.md +317 -0
  55. package/registry/training/bad/serve-static-README.md +253 -0
  56. package/registry/training/bad/statuses-HISTORY.md +87 -0
  57. package/registry/training/bad/type-is-HISTORY.md +292 -0
  58. package/registry/training/bad/vary-HISTORY.md +39 -0
  59. package/registry/training/bad/vite-LICENSE.md +2230 -0
  60. package/registry/training/bad/which-CHANGELOG.md +166 -0
  61. package/registry/training/bad/zod-README.md +191 -0
  62. package/registry/training/good/skills-store-autopilot.md +85 -0
  63. package/registry/training/good/skills-store-bot-builder.md +70 -0
  64. package/registry/training/good/skills-store-chain.md +136 -0
  65. package/registry/training/good/skills-store-evolve.md +100 -0
  66. package/registry/training/good/skills-store-game.md +27 -0
  67. package/registry/training/good/skills-store-hunt.md +102 -0
  68. package/registry/training/good/skills-store-intel.md +56 -0
  69. package/registry/training/good/skills-store-memory-gc.md +58 -0
  70. package/registry/training/good/skills-store-pcsort.md +207 -0
  71. package/registry/training/good/skills-store-pickup.md +60 -0
  72. package/registry/training/good/skills-store-recon.md +141 -0
  73. package/registry/training/good/skills-store-remember.md +64 -0
  74. package/registry/training/good/skills-store-report.md +117 -0
  75. package/registry/training/good/skills-store-router.md +225 -0
  76. package/registry/training/good/skills-store-search.md +168 -0
  77. package/registry/training/good/skills-store-surface.md +53 -0
  78. package/registry/training/good/skills-store-token-scan.md +141 -0
  79. package/registry/training/good/skills-store-triage.md +97 -0
  80. package/registry/training/good/skills-store-unity.md +733 -0
  81. package/registry/training/good/skills-store-validate.md +135 -0
  82. package/registry/training/good/skills-store-web3-audit.md +217 -0
  83. package/src/filter/classifier.js +88 -0
  84. package/src/filter/cluster.js +52 -0
  85. package/src/filter/dedup.js +36 -0
  86. package/src/filter/hard-filter.js +57 -0
  87. package/src/filter/train.js +64 -0
package/github-import.js CHANGED
@@ -6,6 +6,7 @@ import { globSync } from 'glob';
6
6
  import { indexAll, indexSource } from './indexer.js';
7
7
  import { loadConfig, saveConfig, PROMPTGRAPH_DIR, SKILLS_STORE_DIR } from './config.js';
8
8
  import { validateSkill } from './validator.js';
9
+ import { isSkillFile } from './parser.js';
9
10
 
10
11
  const SKILL_DIRS = ['skills', 'commands', 'prompts', 'agents', 'skills-store', 'slash-commands', 'custom-commands', 'templates'];
11
12
 
@@ -366,17 +367,16 @@ export async function importFromGitHub(repoUrl) {
366
367
  // Remove doc files anywhere in the cloned tree
367
368
  cleanupRepoRoot(dest);
368
369
 
369
- // Validate every .md file — delete those that fail
370
+ // Validate every .md file via isSkillFile — delete low-quality files
370
371
  const allMd = globSync(`${dest}/**/*.md`);
371
372
  let removedInvalid = 0;
372
373
  for (const fp of allMd) {
373
- const v = validateSkill(fp);
374
- if (!v.ok) {
374
+ if (!isSkillFile(fp)) {
375
375
  try { fs.unlinkSync(fp); removedInvalid++; } catch {}
376
376
  }
377
377
  }
378
378
  if (removedInvalid > 0) {
379
- console.log(`Removed ${removedInvalid} invalid .md files (failed validation)`);
379
+ console.log(`Removed ${removedInvalid} low-quality .md files (isSkillFile)`);
380
380
  // Clean up empty dirs left behind
381
381
  removeEmptyDirs(dest);
382
382
  }
package/index.js CHANGED
@@ -16,7 +16,7 @@ const args = process.argv.slice(2);
16
16
  const rawBin = process.argv[1]?.split(/[\\/]/).pop()?.replace(/\.js$/, '');
17
17
  const bin = (rawBin && rawBin !== 'index') ? rawBin : 'pg';
18
18
 
19
- const KNOWN_COMMANDS = new Set(['init', 'reindex', 'update', 'import', 'setup', 'validate', 'marketplace', 'doctor', 'search', 'help', '--help', '-h', 'bundle', 'status']);
19
+ const KNOWN_COMMANDS = new Set(['init', 'reindex', 'update', 'import', 'setup', 'validate', 'marketplace', 'doctor', 'search', 'help', '--help', '-h', 'bundle', 'status', 'train']);
20
20
 
21
21
  function showHelp() {
22
22
  console.log(
@@ -319,24 +319,41 @@ if (args[0] === 'validate') {
319
319
 
320
320
  const raw = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : null;
321
321
 
322
- // Show indexing score breakdown
323
322
  if (raw) {
324
- const { skillScore: _score } = await import('./parser.js').catch(() => ({}));
325
- const willIndex = isSkillFile(file, raw);
323
+ const { filterWithClassifier, isSkillFile: _isSkill } = await import('./parser.js');
324
+ const { hardFilter } = await import('./src/filter/hard-filter.js');
325
+ const { loadModel } = await import('./src/filter/train.js');
326
+ const { embed } = await import('./embedder.js');
327
+ const { classify } = await import('./src/filter/classifier.js');
328
+
329
+ const hfResult = hardFilter(file, raw);
330
+ const willIndex = _isSkill(file, raw);
326
331
  const scoreLabel = willIndex ? chalk.green('✓ will be indexed') : chalk.red('✗ will be skipped by indexer');
327
332
  console.log(chalk.bold('\n Indexing check: ') + scoreLabel);
328
333
 
329
- // Show which signals were detected
330
- const lines = raw.split('\n').filter(l => l.trim());
331
334
  const signals = [];
332
- try { const { data } = (await import('gray-matter')).default(raw); if (data.name) signals.push(chalk.green('+4 frontmatter name:')); } catch {}
333
- if (/^#{1,3}\s+(steps?|usage|instructions?|how\s+to|when\s+to\s+use|workflow)/im.test(raw)) signals.push(chalk.green('+2 instructional headers (## Steps / ## Usage)'));
334
- if (lines.filter(l => /^#{1,3}\s/.test(l)).some(h => /\b(run|use|fix|debug|check|create|deploy|scan|audit)\b/i.test(h))) signals.push(chalk.green('+2 imperative verbs in headers'));
335
- if (raw.includes('```')) signals.push(chalk.green('+1 code block'));
336
- if (lines.some(l => /^\d+\.\s/.test(l))) signals.push(chalk.green('+1 numbered list'));
337
- if (lines.some(l => /^[-*+]\s/.test(l))) signals.push(chalk.green('+1 bullet list'));
338
- const firstH = lines.find(l => /^#{1,3}\s/.test(l))?.replace(/^#+\s*/, '') || '';
339
- if (/^(overview|introduction|about|background|welcome)/i.test(firstH)) signals.push(chalk.red('-3 first header looks like docs ("' + firstH + '")'));
335
+ if (!hfResult.pass) {
336
+ signals.push(chalk.red(`✗ hard filter: ${hfResult.reason}`));
337
+ } else {
338
+ signals.push(chalk.green(' hard filter passed'));
339
+ }
340
+
341
+ const centroids = loadModel();
342
+ if (centroids) {
343
+ try {
344
+ const vec = await embed(raw);
345
+ const decision = classify(vec, centroids, raw, file);
346
+ const pct = (decision.score * 100).toFixed(0);
347
+ if (decision.label === 'skill') signals.push(chalk.green(`✓ classifier: skill (${pct}%)`));
348
+ else if (decision.label === 'unsure') signals.push(chalk.yellow(`? classifier: unsure (${pct}%)`));
349
+ else signals.push(chalk.red(`✗ classifier: reject (${pct}%)`));
350
+ } catch {
351
+ signals.push(chalk.gray(' classifier: embed failed (skip)'));
352
+ }
353
+ } else {
354
+ signals.push(chalk.gray(' classifier: no model (run `pg train`)'));
355
+ }
356
+
340
357
  if (signals.length) {
341
358
  signals.forEach(s => console.log(' ' + s));
342
359
  }
@@ -355,6 +372,22 @@ if (args[0] === 'validate') {
355
372
  }
356
373
  }
357
374
 
375
+ if (args[0] === 'train') {
376
+ const { train: trainModel } = await import('./src/filter/train.js');
377
+ const spin = (await import('./cli.js')).spinner('Training classifier...');
378
+ spin.start();
379
+ try {
380
+ const model = await trainModel();
381
+ spin.stop();
382
+ success(`Classifier trained (${model.counts.good} good, ${model.counts.bad} bad examples)`);
383
+ } catch (e) {
384
+ spin.stop();
385
+ error(`Training failed: ${e.message}`);
386
+ process.exit(1);
387
+ }
388
+ process.exit(0);
389
+ }
390
+
358
391
  if (args[0] === 'search') {
359
392
  const query = args.slice(1).join(' ');
360
393
  if (!query) { error('Usage: ' + bin + ' search <query>'); process.exit(1); }
package/indexer.js CHANGED
@@ -2,8 +2,8 @@ import { globSync } from 'glob';
2
2
  import { createHash } from 'crypto';
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
- import { parseSkillFile, isSkillFile } from './parser.js';
6
- import { embedBatch, BATCH_SIZE } from './embedder.js';
5
+ import { parseSkillFile, isSkillFile, filterWithClassifier } from './parser.js';
6
+ import { embedBatch, cosineSimilarity, BATCH_SIZE } from './embedder.js';
7
7
  import { getDb, skillId, vecToBlob } from './db.js';
8
8
  import { loadConfig } from './config.js';
9
9
  import { chunkText } from './chunker.js';
@@ -138,6 +138,7 @@ export async function indexAll({ fast = false } = {}) {
138
138
  let count = 0;
139
139
  let errors = 0;
140
140
  let skipped = 0;
141
+ let classifierRemoved = 0;
141
142
  let batch = [];
142
143
  const start = Date.now();
143
144
 
@@ -174,8 +175,10 @@ export async function indexAll({ fast = false } = {}) {
174
175
  batch.push({ ...parsed, hash });
175
176
 
176
177
  if (batch.length >= BATCH_SIZE) {
177
- await indexBatch(db, batch, { fast });
178
- count += batch.length;
178
+ const filtered = await filterWithClassifier(batch);
179
+ classifierRemoved += batch.length - filtered.length;
180
+ await indexBatch(db, filtered, { fast });
181
+ count += filtered.length;
179
182
  batch = [];
180
183
  const eta = count > 0 ? Math.round((total - count) * (Date.now() - start) / count / 1000) : '?';
181
184
  progress(count, total, { skipped, eta, errors });
@@ -196,8 +199,10 @@ export async function indexAll({ fast = false } = {}) {
196
199
  }
197
200
 
198
201
  if (batch.length > 0) {
199
- await indexBatch(db, batch, { fast });
200
- count += batch.length;
202
+ const filtered = await filterWithClassifier(batch);
203
+ classifierRemoved += batch.length - filtered.length;
204
+ await indexBatch(db, filtered, { fast });
205
+ count += filtered.length;
201
206
  }
202
207
 
203
208
  progress(total, total, { skipped, errors });
@@ -208,7 +213,9 @@ export async function indexAll({ fast = false } = {}) {
208
213
  await buildAnnIndex();
209
214
  spin.stop();
210
215
  }
211
- success(`Indexed ${chalk.white.bold(count)} skills ${chalk.gray(`(${errors} errors, ${skipped} skipped, ${removed} removed)`)}`);
216
+ const stats = [`${errors} errors`, `${skipped} skipped`, `${removed} removed`];
217
+ if (classifierRemoved > 0) stats.push(`${classifierRemoved} filtered`);
218
+ success(`Indexed ${chalk.white.bold(count)} skills ${chalk.gray(`(${stats.join(', ')})`)}`);
212
219
  if (fast) info(chalk.yellow('Fast mode: keyword search only. Run `pg reindex` for semantic search.'));
213
220
  const elapsed = ((Date.now() - start) / 1000).toFixed(1);
214
221
  info(chalk.gray(`Time: ${elapsed}s`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptgraph-mcp",
3
- "version": "2.4.6",
3
+ "version": "2.4.7",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "bin": {
package/parser.js CHANGED
@@ -1,136 +1,42 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import matter from 'gray-matter';
4
+ import { hardFilter } from './src/filter/hard-filter.js';
5
+ import { classify } from './src/filter/classifier.js';
6
+ import { loadModel } from './src/filter/train.js';
4
7
 
5
- // match /skill-name but not URLs (http://, https://, etc.)
6
8
  const SKILL_REF_RE = /(?<!https?:|ftp:)(?<![a-zA-Z0-9])\/([a-z0-9][a-z0-9-]{2,})/g;
7
9
 
8
- // Filenames that are never skills — docs, meta, legal files
9
- const SKIP_FILENAMES = new Set([
10
- 'readme', 'changelog', 'license', 'contributing', 'code-of-conduct',
11
- 'security', 'authors', 'credits', 'install', 'installation', 'usage',
12
- 'engagements', 'contributors', 'maintainers', 'acknowledgements',
13
- 'faq', 'glossary', 'index', 'overview', 'summary', 'roadmap', 'todo',
14
- 'notes', 'template', 'example', 'sample', 'demo', 'getting-started',
15
- 'quickstart', 'guide', 'tutorial', 'walkthrough', 'architecture',
16
- 'design', 'spec', 'specification', 'requirements', 'privacy', 'terms',
17
- 'disclaimer', 'notice', 'copying', 'warranty', 'codeofconduct',
18
- 'pull_request_template', 'issue_template', 'funding',
19
- ]);
20
-
21
- // Filename patterns that are never skills — readme* catches ALL variants (readme_de, readme_zh-CN, etc.)
22
- const SKIP_FILENAME_RE = /^(_|\.)|^v?\d+[\.\-]\d+|^\d{4}[\-_]\d{2}|^readme|^license|^changelog|^contributing|^code.of.conduct|^security|^authors|^credits|^disclaimer|^notice|^copying|^warranty|^promotion|^funding/i;
23
-
24
- // Path segments that indicate the file is NOT a skill
25
- const SKIP_DIRS = new Set([
26
- '.github', 'docs', 'doc', 'documentation', 'examples', 'example',
27
- 'tests', 'test', '__tests__', 'spec', 'fixtures', 'assets', 'images',
28
- 'img', 'screenshots', 'media', 'static', 'public', 'dist', 'build',
29
- 'node_modules', 'vendor', 'third_party',
30
- ]);
31
-
32
- // First-header values that signal documentation, not a skill
33
- const DOC_FIRST_HEADERS = /^(overview|introduction|about|background|welcome|getting started|what is|why |table of contents|toc|foreword|preface|readme)/i;
34
-
35
- // Imperative verbs commonly found in skill headers
36
- const IMPERATIVE_HEADERS = /\b(run|use|apply|execute|check|debug|fix|create|add|remove|deploy|test|write|generate|analyze|review|refactor|optimize|configure|setup|install|scan|audit|validate|search|find|extract|parse)\b/i;
37
-
38
- // Instructional section headers
39
- const INSTRUCTION_HEADERS = /^#{1,3}\s+(steps?|usage|instructions?|how\s+to|when\s+to\s+use|workflow|process|procedure|example|examples?|commands?|output|result)/i;
40
-
41
- // ── scoring ───────────────────────────────────────────────────────────────────
42
-
43
- function skillScore(raw, base) {
44
- let score = 0;
45
-
46
- // Fast path: frontmatter with name = definitely a skill
47
- try {
48
- const { data } = matter(raw);
49
- if (data.name && typeof data.name === 'string') return 10;
50
- } catch {}
51
-
52
- const lines = raw.split('\n');
53
- const nonEmpty = lines.filter(l => l.trim());
54
- const headers = nonEmpty.filter(l => /^#{1,3}\s/.test(l));
55
-
56
- // Minimum viable content
57
- if (raw.length < 150) return -99;
58
- if (headers.length < 1) return -99;
59
-
60
- // ── positive signals ──────────────────────────────────────────────────────
61
-
62
- // Instructional section names (## Steps, ## Usage, etc.)
63
- if (lines.some(l => INSTRUCTION_HEADERS.test(l))) score += 2;
64
-
65
- // Headers with imperative verbs
66
- if (headers.some(h => IMPERATIVE_HEADERS.test(h))) score += 2;
67
-
68
- // Code block
69
- if (raw.includes('```') || raw.includes(' ')) score += 1;
70
-
71
- // Numbered list (step-by-step)
72
- if (nonEmpty.some(l => /^\d+\.\s/.test(l))) score += 1;
73
-
74
- // Bullet list
75
- if (nonEmpty.some(l => /^[-*+]\s/.test(l))) score += 1;
76
-
77
- // Multiple headers (structure)
78
- if (headers.length >= 2) score += 1;
79
- if (headers.length >= 4) score += 1;
80
-
81
- // ── negative signals ──────────────────────────────────────────────────────
82
-
83
- // First header looks like documentation
84
- const firstHeader = headers[0]?.replace(/^#+\s*/, '') || '';
85
- if (DOC_FIRST_HEADERS.test(firstHeader)) score -= 3;
86
-
87
- // Content is mostly long prose paragraphs (narrative, not instructional)
88
- const paragraphs = raw.split(/\n\n+/).filter(p => p.trim() && !p.trim().startsWith('#'));
89
- const longProse = paragraphs.filter(p => p.split(' ').length > 60 && !/```/.test(p));
90
- if (longProse.length > paragraphs.length * 0.6 && paragraphs.length > 3) score -= 2;
91
-
92
- // Filename looks like a version, date, or index
93
- if (SKIP_FILENAME_RE.test(base)) score -= 3;
94
-
95
- // Very high word repetition (filler content)
96
- const words = raw.toLowerCase().split(/\s+/).filter(w => w.length > 3);
97
- if (words.length > 80) {
98
- const unique = new Set(words);
99
- if (unique.size / words.length < 0.22) score -= 2;
100
- }
101
-
102
- return score;
103
- }
104
-
105
- // ── public API ────────────────────────────────────────────────────────────────
106
-
107
10
  export function isSkillFile(filePath, raw) {
108
- const parts = filePath.replace(/\\/g, '/').split('/');
109
- const base = parts[parts.length - 1].replace(/\.md$/i, '').toLowerCase();
110
-
111
- // Hard-reject by filename
112
- if (SKIP_FILENAMES.has(base)) return false;
113
- if (SKIP_FILENAME_RE.test(base)) return false;
114
-
115
- // Hard-reject by parent directory
116
- for (const part of parts.slice(0, -1)) {
117
- if (SKIP_DIRS.has(part.toLowerCase())) return false;
118
- }
11
+ const result = hardFilter(filePath, raw);
12
+ if (!result.pass) return false;
119
13
 
120
14
  try {
121
15
  if (!raw) raw = fs.readFileSync(filePath, 'utf8');
122
-
123
- // Hard-reject: content starts with README header or badge lines
124
16
  const firstLines = raw.trimStart().slice(0, 300);
125
17
  if (/^#\s*readme\b/i.test(firstLines)) return false;
126
18
  if (/!\[.*\]\(https?:\/\/(img\.shields\.io|badge\.fury|travis-ci|github\.com\/[^)]+\/badge)/i.test(firstLines)) return false;
127
19
 
128
- return skillScore(raw, base) >= 3;
20
+ return true;
129
21
  } catch {
130
- return false;
22
+ return true;
131
23
  }
132
24
  }
133
25
 
26
+ export async function filterWithClassifier(skills) {
27
+ const centroids = loadModel();
28
+ if (!centroids) return skills;
29
+
30
+ const { embedBatch } = await import('./embedder.js');
31
+ const texts = skills.map(s => s.content || '');
32
+ const embeddings = await embedBatch(texts);
33
+
34
+ return skills.filter((_, i) => {
35
+ const decision = classify(embeddings[i], centroids, skills[i].content, skills[i].path);
36
+ return decision.label === 'skill' || decision.label === 'unsure';
37
+ });
38
+ }
39
+
134
40
  export function parseSkillFile(filePath, source, opts = {}) {
135
41
  const raw = opts.raw ?? fs.readFileSync(filePath, 'utf8');
136
42
 
@@ -0,0 +1,250 @@
1
+ 2.0.0 / 2024-08-31
2
+ ==================
3
+
4
+ * Drop node <18 support
5
+ * deps: mime-types@^3.0.0
6
+ * deps: negotiator@^1.0.0
7
+
8
+ 1.3.8 / 2022-02-02
9
+ ==================
10
+
11
+ * deps: mime-types@~2.1.34
12
+ - deps: mime-db@~1.51.0
13
+ * deps: negotiator@0.6.3
14
+
15
+ 1.3.7 / 2019-04-29
16
+ ==================
17
+
18
+ * deps: negotiator@0.6.2
19
+ - Fix sorting charset, encoding, and language with extra parameters
20
+
21
+ 1.3.6 / 2019-04-28
22
+ ==================
23
+
24
+ * deps: mime-types@~2.1.24
25
+ - deps: mime-db@~1.40.0
26
+
27
+ 1.3.5 / 2018-02-28
28
+ ==================
29
+
30
+ * deps: mime-types@~2.1.18
31
+ - deps: mime-db@~1.33.0
32
+
33
+ 1.3.4 / 2017-08-22
34
+ ==================
35
+
36
+ * deps: mime-types@~2.1.16
37
+ - deps: mime-db@~1.29.0
38
+
39
+ 1.3.3 / 2016-05-02
40
+ ==================
41
+
42
+ * deps: mime-types@~2.1.11
43
+ - deps: mime-db@~1.23.0
44
+ * deps: negotiator@0.6.1
45
+ - perf: improve `Accept` parsing speed
46
+ - perf: improve `Accept-Charset` parsing speed
47
+ - perf: improve `Accept-Encoding` parsing speed
48
+ - perf: improve `Accept-Language` parsing speed
49
+
50
+ 1.3.2 / 2016-03-08
51
+ ==================
52
+
53
+ * deps: mime-types@~2.1.10
54
+ - Fix extension of `application/dash+xml`
55
+ - Update primary extension for `audio/mp4`
56
+ - deps: mime-db@~1.22.0
57
+
58
+ 1.3.1 / 2016-01-19
59
+ ==================
60
+
61
+ * deps: mime-types@~2.1.9
62
+ - deps: mime-db@~1.21.0
63
+
64
+ 1.3.0 / 2015-09-29
65
+ ==================
66
+
67
+ * deps: mime-types@~2.1.7
68
+ - deps: mime-db@~1.19.0
69
+ * deps: negotiator@0.6.0
70
+ - Fix including type extensions in parameters in `Accept` parsing
71
+ - Fix parsing `Accept` parameters with quoted equals
72
+ - Fix parsing `Accept` parameters with quoted semicolons
73
+ - Lazy-load modules from main entry point
74
+ - perf: delay type concatenation until needed
75
+ - perf: enable strict mode
76
+ - perf: hoist regular expressions
77
+ - perf: remove closures getting spec properties
78
+ - perf: remove a closure from media type parsing
79
+ - perf: remove property delete from media type parsing
80
+
81
+ 1.2.13 / 2015-09-06
82
+ ===================
83
+
84
+ * deps: mime-types@~2.1.6
85
+ - deps: mime-db@~1.18.0
86
+
87
+ 1.2.12 / 2015-07-30
88
+ ===================
89
+
90
+ * deps: mime-types@~2.1.4
91
+ - deps: mime-db@~1.16.0
92
+
93
+ 1.2.11 / 2015-07-16
94
+ ===================
95
+
96
+ * deps: mime-types@~2.1.3
97
+ - deps: mime-db@~1.15.0
98
+
99
+ 1.2.10 / 2015-07-01
100
+ ===================
101
+
102
+ * deps: mime-types@~2.1.2
103
+ - deps: mime-db@~1.14.0
104
+
105
+ 1.2.9 / 2015-06-08
106
+ ==================
107
+
108
+ * deps: mime-types@~2.1.1
109
+ - perf: fix deopt during mapping
110
+
111
+ 1.2.8 / 2015-06-07
112
+ ==================
113
+
114
+ * deps: mime-types@~2.1.0
115
+ - deps: mime-db@~1.13.0
116
+ * perf: avoid argument reassignment & argument slice
117
+ * perf: avoid negotiator recursive construction
118
+ * perf: enable strict mode
119
+ * perf: remove unnecessary bitwise operator
120
+
121
+ 1.2.7 / 2015-05-10
122
+ ==================
123
+
124
+ * deps: negotiator@0.5.3
125
+ - Fix media type parameter matching to be case-insensitive
126
+
127
+ 1.2.6 / 2015-05-07
128
+ ==================
129
+
130
+ * deps: mime-types@~2.0.11
131
+ - deps: mime-db@~1.9.1
132
+ * deps: negotiator@0.5.2
133
+ - Fix comparing media types with quoted values
134
+ - Fix splitting media types with quoted commas
135
+
136
+ 1.2.5 / 2015-03-13
137
+ ==================
138
+
139
+ * deps: mime-types@~2.0.10
140
+ - deps: mime-db@~1.8.0
141
+
142
+ 1.2.4 / 2015-02-14
143
+ ==================
144
+
145
+ * Support Node.js 0.6
146
+ * deps: mime-types@~2.0.9
147
+ - deps: mime-db@~1.7.0
148
+ * deps: negotiator@0.5.1
149
+ - Fix preference sorting to be stable for long acceptable lists
150
+
151
+ 1.2.3 / 2015-01-31
152
+ ==================
153
+
154
+ * deps: mime-types@~2.0.8
155
+ - deps: mime-db@~1.6.0
156
+
157
+ 1.2.2 / 2014-12-30
158
+ ==================
159
+
160
+ * deps: mime-types@~2.0.7
161
+ - deps: mime-db@~1.5.0
162
+
163
+ 1.2.1 / 2014-12-30
164
+ ==================
165
+
166
+ * deps: mime-types@~2.0.5
167
+ - deps: mime-db@~1.3.1
168
+
169
+ 1.2.0 / 2014-12-19
170
+ ==================
171
+
172
+ * deps: negotiator@0.5.0
173
+ - Fix list return order when large accepted list
174
+ - Fix missing identity encoding when q=0 exists
175
+ - Remove dynamic building of Negotiator class
176
+
177
+ 1.1.4 / 2014-12-10
178
+ ==================
179
+
180
+ * deps: mime-types@~2.0.4
181
+ - deps: mime-db@~1.3.0
182
+
183
+ 1.1.3 / 2014-11-09
184
+ ==================
185
+
186
+ * deps: mime-types@~2.0.3
187
+ - deps: mime-db@~1.2.0
188
+
189
+ 1.1.2 / 2014-10-14
190
+ ==================
191
+
192
+ * deps: negotiator@0.4.9
193
+ - Fix error when media type has invalid parameter
194
+
195
+ 1.1.1 / 2014-09-28
196
+ ==================
197
+
198
+ * deps: mime-types@~2.0.2
199
+ - deps: mime-db@~1.1.0
200
+ * deps: negotiator@0.4.8
201
+ - Fix all negotiations to be case-insensitive
202
+ - Stable sort preferences of same quality according to client order
203
+
204
+ 1.1.0 / 2014-09-02
205
+ ==================
206
+
207
+ * update `mime-types`
208
+
209
+ 1.0.7 / 2014-07-04
210
+ ==================
211
+
212
+ * Fix wrong type returned from `type` when match after unknown extension
213
+
214
+ 1.0.6 / 2014-06-24
215
+ ==================
216
+
217
+ * deps: negotiator@0.4.7
218
+
219
+ 1.0.5 / 2014-06-20
220
+ ==================
221
+
222
+ * fix crash when unknown extension given
223
+
224
+ 1.0.4 / 2014-06-19
225
+ ==================
226
+
227
+ * use `mime-types`
228
+
229
+ 1.0.3 / 2014-06-11
230
+ ==================
231
+
232
+ * deps: negotiator@0.4.6
233
+ - Order by specificity when quality is the same
234
+
235
+ 1.0.2 / 2014-05-29
236
+ ==================
237
+
238
+ * Fix interpretation when header not in request
239
+ * deps: pin negotiator@0.4.5
240
+
241
+ 1.0.1 / 2014-01-18
242
+ ==================
243
+
244
+ * Identity encoding isn't always acceptable
245
+ * deps: negotiator@~0.4.0
246
+
247
+ 1.0.0 / 2013-12-27
248
+ ==================
249
+
250
+ * Genesis