promptgraph-mcp 2.4.7 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +3 -6
  2. package/ann.js +23 -51
  3. package/api.js +200 -0
  4. package/config.js +14 -0
  5. package/db.js +48 -0
  6. package/embedder.js +10 -0
  7. package/github-import.js +167 -27
  8. package/indexer.js +49 -12
  9. package/marketplace.js +130 -7
  10. package/package.json +9 -3
  11. package/search.js +86 -20
  12. package/src/filter/hard-filter.js +2 -0
  13. package/src/filter/train.js +3 -1
  14. package/src/reranker/reranker.js +27 -0
  15. package/src/store/flat-store.js +61 -0
  16. package/src/store/hnsw-store.js +187 -0
  17. package/src/store/index.js +19 -0
  18. package/src/store/vector-store.js +9 -0
  19. package/src/utils/rate-limiter.js +33 -0
  20. package/validator.js +31 -1
  21. package/registry/training/bad/accepts-HISTORY.md +0 -250
  22. package/registry/training/bad/argparse-CHANGELOG.md +0 -185
  23. package/registry/training/bad/balanced-match-LICENSE.md +0 -23
  24. package/registry/training/bad/better-sqlite3-README.md +0 -99
  25. package/registry/training/bad/bindings-LICENSE.md +0 -22
  26. package/registry/training/bad/bl-LICENSE.md +0 -13
  27. package/registry/training/bad/body-parser-README.md +0 -494
  28. package/registry/training/bad/bytes-HISTORY.md +0 -97
  29. package/registry/training/bad/camelcase-README.md +0 -135
  30. package/registry/training/bad/chai-README.md +0 -162
  31. package/registry/training/bad/cli-progress-LICENSE.md +0 -24
  32. package/registry/training/bad/content-type-HISTORY.md +0 -29
  33. package/registry/training/bad/cookie-SECURITY.md +0 -25
  34. package/registry/training/bad/cookie-signature-HISTORY.md +0 -70
  35. package/registry/training/bad/cors-README.md +0 -277
  36. package/registry/training/bad/cross-spawn-README.md +0 -89
  37. package/registry/training/bad/deep-extend-CHANGELOG.md +0 -46
  38. package/registry/training/bad/depd-HISTORY.md +0 -103
  39. package/registry/training/bad/esprima-README.md +0 -46
  40. package/registry/training/bad/etag-HISTORY.md +0 -83
  41. package/registry/training/bad/expect-type-SECURITY.md +0 -14
  42. package/registry/training/bad/finalhandler-HISTORY.md +0 -239
  43. package/registry/training/bad/fresh-HISTORY.md +0 -80
  44. package/registry/training/bad/glob-LICENSE.md +0 -63
  45. package/registry/training/bad/hono-README.md +0 -85
  46. package/registry/training/bad/http-errors-HISTORY.md +0 -186
  47. package/registry/training/bad/jose-LICENSE.md +0 -21
  48. package/registry/training/bad/jose-README.md +0 -153
  49. package/registry/training/bad/js-yaml-README.md +0 -299
  50. package/registry/training/bad/json-schema-typed-LICENSE.md +0 -57
  51. package/registry/training/bad/json-stringify-safe-CHANGELOG.md +0 -14
  52. package/registry/training/bad/lru-cache-LICENSE.md +0 -55
  53. package/registry/training/bad/media-typer-HISTORY.md +0 -50
  54. package/registry/training/bad/minimatch-LICENSE.md +0 -55
  55. package/registry/training/bad/minimist-CHANGELOG.md +0 -298
  56. package/registry/training/bad/minimist-README.md +0 -121
  57. package/registry/training/bad/ms-LICENSE.md +0 -21
  58. package/registry/training/bad/negotiator-HISTORY.md +0 -114
  59. package/registry/training/bad/on-finished-HISTORY.md +0 -98
  60. package/registry/training/bad/pkce-challenge-CHANGELOG.md +0 -114
  61. package/registry/training/bad/postcss-README.md +0 -28
  62. package/registry/training/bad/prebuild-install-CHANGELOG.md +0 -131
  63. package/registry/training/bad/proxy-addr-HISTORY.md +0 -161
  64. package/registry/training/bad/qs-CHANGELOG.md +0 -822
  65. package/registry/training/bad/rc-README.md +0 -227
  66. package/registry/training/bad/readable-stream-CONTRIBUTING.md +0 -38
  67. package/registry/training/bad/router-HISTORY.md +0 -228
  68. package/registry/training/bad/semver-README.md +0 -680
  69. package/registry/training/bad/send-README.md +0 -317
  70. package/registry/training/bad/serve-static-README.md +0 -253
  71. package/registry/training/bad/statuses-HISTORY.md +0 -87
  72. package/registry/training/bad/type-is-HISTORY.md +0 -292
  73. package/registry/training/bad/vary-HISTORY.md +0 -39
  74. package/registry/training/bad/vite-LICENSE.md +0 -2230
  75. package/registry/training/bad/which-CHANGELOG.md +0 -166
  76. package/registry/training/bad/zod-README.md +0 -191
  77. package/registry/training/good/skills-store-autopilot.md +0 -85
  78. package/registry/training/good/skills-store-bot-builder.md +0 -70
  79. package/registry/training/good/skills-store-chain.md +0 -136
  80. package/registry/training/good/skills-store-evolve.md +0 -100
  81. package/registry/training/good/skills-store-game.md +0 -27
  82. package/registry/training/good/skills-store-hunt.md +0 -102
  83. package/registry/training/good/skills-store-intel.md +0 -56
  84. package/registry/training/good/skills-store-memory-gc.md +0 -58
  85. package/registry/training/good/skills-store-pcsort.md +0 -207
  86. package/registry/training/good/skills-store-pickup.md +0 -60
  87. package/registry/training/good/skills-store-recon.md +0 -141
  88. package/registry/training/good/skills-store-remember.md +0 -64
  89. package/registry/training/good/skills-store-report.md +0 -117
  90. package/registry/training/good/skills-store-router.md +0 -225
  91. package/registry/training/good/skills-store-search.md +0 -168
  92. package/registry/training/good/skills-store-surface.md +0 -53
  93. package/registry/training/good/skills-store-token-scan.md +0 -141
  94. package/registry/training/good/skills-store-triage.md +0 -97
  95. package/registry/training/good/skills-store-unity.md +0 -733
  96. package/registry/training/good/skills-store-validate.md +0 -135
  97. package/registry/training/good/skills-store-web3-audit.md +0 -217
  98. package/src/filter/cluster.js +0 -52
  99. package/src/filter/dedup.js +0 -36
@@ -0,0 +1,187 @@
1
+ import { VectorStore } from './vector-store.js';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ const DIMS = 384;
6
+ const EF_SEARCH = 100;
7
+
8
+ let HierarchicalNSW = null;
9
+ let moduleError = null;
10
+
11
+ async function getHnswModule() {
12
+ if (HierarchicalNSW) return HierarchicalNSW;
13
+ if (moduleError) throw moduleError;
14
+ try {
15
+ const mod = await import('hnswlib-node');
16
+ HierarchicalNSW = mod.default.HierarchicalNSW;
17
+ return HierarchicalNSW;
18
+ } catch (e) {
19
+ moduleError = new Error(
20
+ 'hnswlib-node not available. Run: npm install hnswlib-node'
21
+ );
22
+ throw moduleError;
23
+ }
24
+ }
25
+
26
+ function toArray(v) {
27
+ return Array.from(v);
28
+ }
29
+
30
+ export class HNSWVectorStore extends VectorStore {
31
+ constructor() {
32
+ super();
33
+ this._index = null;
34
+ this._vectors = [];
35
+ this._idMap = [];
36
+ this._itemCount = 0;
37
+ this._maxElements = 0;
38
+ }
39
+
40
+ async add(id, vector) {
41
+ const HNSW = await getHnswModule();
42
+ const vec = toArray(vector);
43
+ if (!this._index) {
44
+ this._maxElements = 10000;
45
+ this._index = new HNSW('cosine', DIMS);
46
+ this._index.initIndex(this._maxElements, 200);
47
+ }
48
+ if (this._itemCount >= this._maxElements) {
49
+ await this._rebuild(this._maxElements * 2);
50
+ }
51
+ this._index.addPoint(vec, this._itemCount);
52
+ this._vectors.push(vec);
53
+ this._idMap.push(id);
54
+ this._itemCount++;
55
+ }
56
+
57
+ async addBatch(entries) {
58
+ for (const { id, vector } of entries) {
59
+ await this.add(id, vector);
60
+ }
61
+ }
62
+
63
+ async remove(id) {
64
+ const remainingVectors = [];
65
+ const remainingIds = [];
66
+ for (let i = 0; i < this._itemCount; i++) {
67
+ if (this._idMap[i] !== id) {
68
+ remainingVectors.push(this._vectors[i]);
69
+ remainingIds.push(this._idMap[i]);
70
+ }
71
+ }
72
+ this._index = null;
73
+ this._vectors = remainingVectors;
74
+ this._idMap = remainingIds;
75
+ this._itemCount = remainingVectors.length;
76
+ this._maxElements = 0;
77
+ if (this._itemCount > 0) {
78
+ await this._rebuild(this._itemCount + 1000);
79
+ }
80
+ }
81
+
82
+ async search(vector, topK = 20) {
83
+ await getHnswModule();
84
+ if (!this._index || this._itemCount === 0) return [];
85
+
86
+ const efSearch = Math.max(topK * 4, EF_SEARCH);
87
+ this._index.setEf(efSearch);
88
+
89
+ const numCandidates = Math.min(topK * 8, this._itemCount);
90
+ const result = this._index.searchKnn(toArray(vector), numCandidates);
91
+
92
+ const bestBySkill = new Map();
93
+ const neighbors = result.neighbors;
94
+ const distances = result.distances;
95
+ for (let i = 0; i < neighbors.length; i++) {
96
+ const idx = neighbors[i];
97
+ const score = 1 - distances[i];
98
+ const skillId = this._idMap[idx];
99
+ const prev = bestBySkill.get(skillId);
100
+ if (!prev || score > prev) bestBySkill.set(skillId, score);
101
+ }
102
+
103
+ return [...bestBySkill.entries()]
104
+ .sort((a, b) => b[1] - a[1])
105
+ .slice(0, topK)
106
+ .map(([skill_id, score]) => ({ skill_id, score }));
107
+ }
108
+
109
+ async build(entries) {
110
+ const HNSW = await getHnswModule();
111
+ const count = entries.length;
112
+
113
+ this._vectors = [];
114
+ this._idMap = [];
115
+ this._itemCount = 0;
116
+ this._maxElements = 0;
117
+ this._index = null;
118
+
119
+ if (count === 0) return;
120
+
121
+ this._maxElements = count + 1000;
122
+ this._index = new HNSW('cosine', DIMS);
123
+ this._index.initIndex(this._maxElements, 200);
124
+
125
+ for (let i = 0; i < count; i++) {
126
+ const { skill_id, vector } = entries[i];
127
+ const vec = toArray(vector);
128
+ this._index.addPoint(vec, i);
129
+ this._vectors.push(vec);
130
+ this._idMap.push(skill_id);
131
+ this._itemCount++;
132
+ }
133
+ }
134
+
135
+ async clear() {
136
+ this._index = null;
137
+ this._vectors = [];
138
+ this._idMap = [];
139
+ this._itemCount = 0;
140
+ this._maxElements = 0;
141
+ }
142
+
143
+ get size() {
144
+ return this._itemCount;
145
+ }
146
+
147
+ async save(dir) {
148
+ const HNSW = await getHnswModule();
149
+ if (!this._index) return;
150
+ fs.mkdirSync(dir, { recursive: true });
151
+ this._index.writeIndexSync(path.join(dir, 'index.bin'));
152
+ fs.writeFileSync(path.join(dir, 'vectors.json'), JSON.stringify(this._vectors), 'utf8');
153
+ fs.writeFileSync(path.join(dir, 'idmap.json'), JSON.stringify(this._idMap), 'utf8');
154
+ }
155
+
156
+ async load(dir) {
157
+ const HNSW = await getHnswModule();
158
+ const indexPath = path.join(dir, 'index.bin');
159
+ const vectorsPath = path.join(dir, 'vectors.json');
160
+ const idmapPath = path.join(dir, 'idmap.json');
161
+ if (!fs.existsSync(indexPath)) return false;
162
+
163
+ this._index = new HNSW('cosine', DIMS);
164
+ this._index.readIndexSync(indexPath);
165
+ this._vectors = JSON.parse(fs.readFileSync(vectorsPath, 'utf8'));
166
+ this._idMap = JSON.parse(fs.readFileSync(idmapPath, 'utf8'));
167
+ this._itemCount = this._idMap.length;
168
+ this._maxElements = this._index.getMaxElements();
169
+ return true;
170
+ }
171
+
172
+ static async fromDir(dir) {
173
+ const store = new HNSWVectorStore();
174
+ await store.load(dir);
175
+ return store;
176
+ }
177
+
178
+ async _rebuild(newMax) {
179
+ const HNSW = await getHnswModule();
180
+ this._maxElements = newMax;
181
+ this._index = new HNSW('cosine', DIMS);
182
+ this._index.initIndex(this._maxElements, 200);
183
+ for (let i = 0; i < this._itemCount; i++) {
184
+ this._index.addPoint(this._vectors[i], i);
185
+ }
186
+ }
187
+ }
@@ -0,0 +1,19 @@
1
+ import { FlatVectorStore } from './flat-store.js';
2
+ import { HNSWVectorStore } from './hnsw-store.js';
3
+
4
+ let _store = null;
5
+
6
+ export function getStore(type = null) {
7
+ if (_store) return _store;
8
+
9
+ if (!type) {
10
+ type = process.env.PG_VECTOR_STORE || 'flat';
11
+ }
12
+
13
+ _store = type === 'hnsw' ? new HNSWVectorStore() : new FlatVectorStore();
14
+ return _store;
15
+ }
16
+
17
+ export function resetStore() {
18
+ _store = null;
19
+ }
@@ -0,0 +1,9 @@
1
+ export class VectorStore {
2
+ async add(id, vector) { throw new Error('not implemented'); }
3
+ async addBatch(entries) { throw new Error('not implemented'); }
4
+ async remove(id) { throw new Error('not implemented'); }
5
+ async search(vector, topK) { throw new Error('not implemented'); }
6
+ async build(entries) { throw new Error('not implemented'); }
7
+ async clear() { throw new Error('not implemented'); }
8
+ get size() { return 0; }
9
+ }
@@ -0,0 +1,33 @@
1
+ export class RateLimiter {
2
+ constructor({ maxRequests, windowMs }) {
3
+ this.maxRequests = maxRequests
4
+ this.windowMs = windowMs
5
+ this.timestamps = []
6
+ this._chain = Promise.resolve()
7
+ }
8
+
9
+ async acquire() {
10
+ const prev = this._chain
11
+ this._chain = prev.then(() => this._doAcquire())
12
+ return this._chain
13
+ }
14
+
15
+ async _doAcquire() {
16
+ while (!this.tryAcquire()) {
17
+ const oldest = this.timestamps[0]
18
+ const waitMs = oldest
19
+ ? Math.max(1, oldest + this.windowMs - Date.now())
20
+ : 100
21
+ await new Promise(r => setTimeout(r, Math.min(waitMs, this.windowMs)))
22
+ }
23
+ }
24
+
25
+ tryAcquire() {
26
+ const now = Date.now()
27
+ const cutoff = now - this.windowMs
28
+ this.timestamps = this.timestamps.filter(t => t > cutoff)
29
+ if (this.timestamps.length >= this.maxRequests) return false
30
+ this.timestamps.push(now)
31
+ return true
32
+ }
33
+ }
package/validator.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import fs from 'fs';
2
+ import path from 'path';
2
3
  import matter from 'gray-matter';
4
+ import { MAX_DOWNLOAD_SIZE } from './config.js';
3
5
 
4
6
  // patterns that indicate malicious or junk skills
5
7
  const DANGEROUS_PATTERNS = [
@@ -15,7 +17,7 @@ const DANGEROUS_PATTERNS = [
15
17
  ];
16
18
 
17
19
  const MIN_CONTENT_LENGTH = 200; // chars of actual instruction
18
- const MAX_CONTENT_LENGTH = 100000; // 100KB cap
20
+ const MAX_CONTENT_LENGTH = 5242880; // 5MB cap
19
21
  const MIN_DESCRIPTION_LENGTH = 15;
20
22
  const NAME_RE = /^[a-z0-9][a-z0-9-]{1,63}$/;
21
23
 
@@ -80,6 +82,25 @@ export function validateSkill(filePath) {
80
82
 
81
83
  // junk filename heuristic
82
84
  const base = filePath.split(/[\\/]/).pop().toLowerCase();
85
+
86
+ // path traversal check (must be an actual path component, not substring match)
87
+ const pathParts = filePath.split(/[\\/]/);
88
+ if (pathParts.includes('..')) {
89
+ errors.push('Path traversal detected: file path contains ".."');
90
+ }
91
+
92
+ // extension whitelist — reject unexpected file types
93
+ const ALLOWED_EXTENSIONS = new Set(['.md', '.json', '.yaml', '.yml', '.txt', '.js', '.ts', '.py', '.rs', '.toml']);
94
+ const ext = path.extname(filePath).toLowerCase();
95
+ if (!ALLOWED_EXTENSIONS.has(ext)) {
96
+ errors.push(`File extension "${ext}" not allowed. Allowed: ${[...ALLOWED_EXTENSIONS].join(', ')}`);
97
+ }
98
+
99
+ // binary content detection — must be valid UTF-8 with no null bytes
100
+ if (raw.includes('\0')) {
101
+ errors.push('File contains null bytes (binary content)');
102
+ }
103
+
83
104
  if (['readme.md', 'changelog.md', 'license.md', 'contributing.md'].includes(base)) {
84
105
  warnings.push('Filename looks like a docs file, not a skill.');
85
106
  }
@@ -136,6 +157,15 @@ export function validateBundle(def) {
136
157
  return { ok: errors.length === 0, errors, warnings };
137
158
  }
138
159
 
160
+ export function sanitizeExternalContent(content) {
161
+ if (typeof content !== 'string') return ''
162
+ let sanitized = content.replace(/\0/g, '')
163
+ if (Buffer.byteLength(sanitized, 'utf8') > MAX_DOWNLOAD_SIZE) {
164
+ sanitized = Buffer.from(sanitized, 'utf8').subarray(0, MAX_DOWNLOAD_SIZE).toString('utf8')
165
+ }
166
+ return sanitized
167
+ }
168
+
139
169
  // CLI: node validator.js <file>
140
170
  if (import.meta.url === `file://${process.argv[1]}`) {
141
171
  const file = process.argv[2];
@@ -1,250 +0,0 @@
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