@neurcode-ai/cli 0.8.0 → 0.8.2
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.
- package/dist/api-client.d.ts +1 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +2 -1
- package/dist/api-client.js.map +1 -1
- package/dist/commands/apply.d.ts.map +1 -1
- package/dist/commands/apply.js +127 -1
- package/dist/commands/apply.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +104 -23
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/revert.d.ts.map +1 -1
- package/dist/commands/revert.js +72 -0
- package/dist/commands/revert.js.map +1 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +126 -13
- package/dist/commands/verify.js.map +1 -1
- package/dist/services/security/SecurityGuard.d.ts +9 -1
- package/dist/services/security/SecurityGuard.d.ts.map +1 -1
- package/dist/services/security/SecurityGuard.js +206 -40
- package/dist/services/security/SecurityGuard.js.map +1 -1
- package/dist/utils/RelevanceScorer.d.ts +18 -0
- package/dist/utils/RelevanceScorer.d.ts.map +1 -0
- package/dist/utils/RelevanceScorer.js +239 -0
- package/dist/utils/RelevanceScorer.js.map +1 -0
- package/dist/utils/tier.d.ts +21 -0
- package/dist/utils/tier.d.ts.map +1 -0
- package/dist/utils/tier.js +150 -0
- package/dist/utils/tier.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RelevanceScorer
|
|
4
|
+
*
|
|
5
|
+
* High-performance BM25-inspired keyword matching algorithm for filtering
|
|
6
|
+
* exported tools based on user intent. Runs in milliseconds without requiring
|
|
7
|
+
* vector databases or embeddings.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.getTopKTools = getTopKTools;
|
|
11
|
+
// Common stop words to filter from intent
|
|
12
|
+
const STOP_WORDS = new Set([
|
|
13
|
+
'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'for', 'from',
|
|
14
|
+
'has', 'he', 'in', 'is', 'it', 'its', 'of', 'on', 'that', 'the',
|
|
15
|
+
'to', 'was', 'will', 'with', 'the', 'this', 'but', 'they', 'have',
|
|
16
|
+
'had', 'what', 'said', 'each', 'which', 'their', 'time', 'if',
|
|
17
|
+
'up', 'out', 'many', 'then', 'them', 'these', 'so', 'some', 'her',
|
|
18
|
+
'would', 'make', 'like', 'into', 'him', 'has', 'two', 'more', 'very',
|
|
19
|
+
'after', 'words', 'long', 'than', 'first', 'been', 'call', 'who',
|
|
20
|
+
'oil', 'sit', 'now', 'find', 'down', 'day', 'did', 'get', 'come',
|
|
21
|
+
'made', 'may', 'part'
|
|
22
|
+
]);
|
|
23
|
+
/**
|
|
24
|
+
* Clean and tokenize intent text
|
|
25
|
+
*/
|
|
26
|
+
function tokenizeIntent(intent) {
|
|
27
|
+
return intent
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.replace(/[^\w\s]/g, ' ') // Remove punctuation
|
|
30
|
+
.split(/\s+/)
|
|
31
|
+
.filter(word => word.length > 2 && !STOP_WORDS.has(word));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Extract folder names from file path
|
|
35
|
+
* e.g., "src/services/auth/validator.ts" -> ["src", "services", "auth"]
|
|
36
|
+
*/
|
|
37
|
+
function extractFolderNames(filePath) {
|
|
38
|
+
const parts = filePath.split('/');
|
|
39
|
+
return parts.slice(0, -1); // Remove filename, keep folders
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if a word appears in export name (case-insensitive)
|
|
43
|
+
*/
|
|
44
|
+
function fuzzyMatch(word, exportName) {
|
|
45
|
+
const lowerExport = exportName.toLowerCase();
|
|
46
|
+
const lowerWord = word.toLowerCase();
|
|
47
|
+
// Direct substring match
|
|
48
|
+
if (lowerExport.includes(lowerWord))
|
|
49
|
+
return true;
|
|
50
|
+
// CamelCase split match (e.g., "validateAuth" matches "auth")
|
|
51
|
+
const camelCaseParts = exportName.split(/(?=[A-Z])/).map(p => p.toLowerCase());
|
|
52
|
+
return camelCaseParts.some(part => part.includes(lowerWord));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Calculate relevance score for an export item
|
|
56
|
+
*/
|
|
57
|
+
function calculateScore(exportItem, intentTokens) {
|
|
58
|
+
let score = 0;
|
|
59
|
+
const exportName = exportItem.name.toLowerCase();
|
|
60
|
+
const filePath = exportItem.filePath.toLowerCase();
|
|
61
|
+
// Direct Match: +10 if export name appears exactly in intent
|
|
62
|
+
for (const token of intentTokens) {
|
|
63
|
+
if (exportName === token) {
|
|
64
|
+
score += 10;
|
|
65
|
+
break; // Only count once
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Fuzzy Match: +5 if a word in intent matches part of export name
|
|
69
|
+
for (const token of intentTokens) {
|
|
70
|
+
if (fuzzyMatch(token, exportItem.name)) {
|
|
71
|
+
score += 5;
|
|
72
|
+
break; // Only count once per token
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Path Match: +3 if folder name matches intent
|
|
76
|
+
const folderNames = extractFolderNames(exportItem.filePath);
|
|
77
|
+
for (const folder of folderNames) {
|
|
78
|
+
const lowerFolder = folder.toLowerCase();
|
|
79
|
+
for (const token of intentTokens) {
|
|
80
|
+
if (lowerFolder.includes(token) || token.includes(lowerFolder)) {
|
|
81
|
+
score += 3;
|
|
82
|
+
break; // Only count once per folder
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Type Weighting: Functions > Classes > Interfaces/Types
|
|
87
|
+
switch (exportItem.type) {
|
|
88
|
+
case 'function':
|
|
89
|
+
score += 2; // Functions are most actionable
|
|
90
|
+
break;
|
|
91
|
+
case 'class':
|
|
92
|
+
score += 1; // Classes are actionable
|
|
93
|
+
break;
|
|
94
|
+
case 'interface':
|
|
95
|
+
case 'type':
|
|
96
|
+
score += 0; // Interfaces/types are less actionable
|
|
97
|
+
break;
|
|
98
|
+
case 'const':
|
|
99
|
+
case 'variable':
|
|
100
|
+
score += 0.5; // Constants can be useful
|
|
101
|
+
break;
|
|
102
|
+
default:
|
|
103
|
+
score += 0;
|
|
104
|
+
}
|
|
105
|
+
return score;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Core Guard: Always include these essential tools regardless of relevance
|
|
109
|
+
*/
|
|
110
|
+
const CORE_TOOLS = new Set([
|
|
111
|
+
'plan',
|
|
112
|
+
'verify',
|
|
113
|
+
'apply',
|
|
114
|
+
'check',
|
|
115
|
+
'generate',
|
|
116
|
+
'create',
|
|
117
|
+
'update',
|
|
118
|
+
'delete',
|
|
119
|
+
'save',
|
|
120
|
+
'load',
|
|
121
|
+
'init',
|
|
122
|
+
'config',
|
|
123
|
+
'help'
|
|
124
|
+
]);
|
|
125
|
+
/**
|
|
126
|
+
* Check if an export is a core tool that should always be included
|
|
127
|
+
*/
|
|
128
|
+
function isCoreTool(exportItem) {
|
|
129
|
+
const name = exportItem.name.toLowerCase();
|
|
130
|
+
// Check exact match
|
|
131
|
+
if (CORE_TOOLS.has(name))
|
|
132
|
+
return true;
|
|
133
|
+
// Check if name contains core tool (e.g., "generatePlan" contains "plan")
|
|
134
|
+
for (const coreTool of CORE_TOOLS) {
|
|
135
|
+
if (name.includes(coreTool) || coreTool.includes(name)) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get Top-K most relevant tools based on user intent
|
|
143
|
+
*
|
|
144
|
+
* @param intent - User's intent/query
|
|
145
|
+
* @param exports - All available exports
|
|
146
|
+
* @param k - Number of top results to return (default: 20)
|
|
147
|
+
* @returns Filtered and sorted array of ExportItems
|
|
148
|
+
*/
|
|
149
|
+
function getTopKTools(intent, exports, k = 20) {
|
|
150
|
+
if (!intent || intent.trim().length === 0) {
|
|
151
|
+
// If no intent, return top K by type priority (functions first)
|
|
152
|
+
return exports
|
|
153
|
+
.sort((a, b) => {
|
|
154
|
+
const typePriority = {
|
|
155
|
+
'function': 3,
|
|
156
|
+
'class': 2,
|
|
157
|
+
'const': 1,
|
|
158
|
+
'interface': 0,
|
|
159
|
+
'type': 0,
|
|
160
|
+
'variable': 0,
|
|
161
|
+
'enum': 0,
|
|
162
|
+
'namespace': 0,
|
|
163
|
+
'default': 0,
|
|
164
|
+
'unknown': 0
|
|
165
|
+
};
|
|
166
|
+
return (typePriority[b.type] || 0) - (typePriority[a.type] || 0);
|
|
167
|
+
})
|
|
168
|
+
.slice(0, k);
|
|
169
|
+
}
|
|
170
|
+
// Tokenize intent
|
|
171
|
+
const intentTokens = tokenizeIntent(intent);
|
|
172
|
+
if (intentTokens.length === 0) {
|
|
173
|
+
// If intent has no meaningful tokens, return top K by type
|
|
174
|
+
return exports
|
|
175
|
+
.sort((a, b) => {
|
|
176
|
+
const typePriority = {
|
|
177
|
+
'function': 3,
|
|
178
|
+
'class': 2,
|
|
179
|
+
'const': 1,
|
|
180
|
+
'interface': 0,
|
|
181
|
+
'type': 0
|
|
182
|
+
};
|
|
183
|
+
return (typePriority[b.type] || 0) - (typePriority[a.type] || 0);
|
|
184
|
+
})
|
|
185
|
+
.slice(0, k);
|
|
186
|
+
}
|
|
187
|
+
// Score all exports
|
|
188
|
+
const scoredExports = exports.map(exp => ({
|
|
189
|
+
export: exp,
|
|
190
|
+
score: calculateScore(exp, intentTokens),
|
|
191
|
+
isCore: isCoreTool(exp)
|
|
192
|
+
}));
|
|
193
|
+
// Separate core tools and regular exports
|
|
194
|
+
const coreTools = scoredExports.filter(item => item.isCore);
|
|
195
|
+
const regularExports = scoredExports.filter(item => !item.isCore);
|
|
196
|
+
// Sort regular exports by score (descending)
|
|
197
|
+
regularExports.sort((a, b) => {
|
|
198
|
+
if (b.score !== a.score) {
|
|
199
|
+
return b.score - a.score;
|
|
200
|
+
}
|
|
201
|
+
// Tie-breaker: prefer functions
|
|
202
|
+
const typePriority = {
|
|
203
|
+
'function': 3,
|
|
204
|
+
'class': 2,
|
|
205
|
+
'const': 1,
|
|
206
|
+
'interface': 0,
|
|
207
|
+
'type': 0
|
|
208
|
+
};
|
|
209
|
+
return (typePriority[b.export.type] || 0) - (typePriority[a.export.type] || 0);
|
|
210
|
+
});
|
|
211
|
+
// Combine: core tools first, then top-scoring regular exports
|
|
212
|
+
const coreToolItems = coreTools.map(item => item.export);
|
|
213
|
+
const topRegularExports = regularExports
|
|
214
|
+
.slice(0, Math.max(0, k - coreToolItems.length))
|
|
215
|
+
.map(item => item.export);
|
|
216
|
+
// Remove duplicates (in case a core tool also scored high)
|
|
217
|
+
const seen = new Set();
|
|
218
|
+
const result = [];
|
|
219
|
+
for (const exp of [...coreToolItems, ...topRegularExports]) {
|
|
220
|
+
const key = `${exp.filePath}:${exp.name}`;
|
|
221
|
+
if (!seen.has(key)) {
|
|
222
|
+
seen.add(key);
|
|
223
|
+
result.push(exp);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// If we still need more items, fill with highest-scoring remaining
|
|
227
|
+
if (result.length < k) {
|
|
228
|
+
const remaining = regularExports
|
|
229
|
+
.filter(item => {
|
|
230
|
+
const key = `${item.export.filePath}:${item.export.name}`;
|
|
231
|
+
return !seen.has(key);
|
|
232
|
+
})
|
|
233
|
+
.slice(0, k - result.length)
|
|
234
|
+
.map(item => item.export);
|
|
235
|
+
result.push(...remaining);
|
|
236
|
+
}
|
|
237
|
+
return result.slice(0, k);
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=RelevanceScorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RelevanceScorer.js","sourceRoot":"","sources":["../../src/utils/RelevanceScorer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier Utility
|
|
3
|
+
*
|
|
4
|
+
* Fetches and caches user tier to minimize API latency.
|
|
5
|
+
* Caches tier in .neurcode/config.json for the session.
|
|
6
|
+
*/
|
|
7
|
+
export type UserTier = 'FREE' | 'PRO';
|
|
8
|
+
/**
|
|
9
|
+
* Get user tier from API or cache
|
|
10
|
+
* Defaults to 'FREE' if tier cannot be determined
|
|
11
|
+
*/
|
|
12
|
+
export declare function getUserTier(): Promise<UserTier>;
|
|
13
|
+
/**
|
|
14
|
+
* Clear tier cache (useful after tier changes)
|
|
15
|
+
*/
|
|
16
|
+
export declare function clearTierCache(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Check if user has PRO tier
|
|
19
|
+
*/
|
|
20
|
+
export declare function isProUser(): Promise<boolean>;
|
|
21
|
+
//# sourceMappingURL=tier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tier.d.ts","sourceRoot":"","sources":["../../src/utils/tier.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AAUtC;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,CA+ErD;AAwCD;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAiBrC;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAGlD"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tier Utility
|
|
4
|
+
*
|
|
5
|
+
* Fetches and caches user tier to minimize API latency.
|
|
6
|
+
* Caches tier in .neurcode/config.json for the session.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getUserTier = getUserTier;
|
|
10
|
+
exports.clearTierCache = clearTierCache;
|
|
11
|
+
exports.isProUser = isProUser;
|
|
12
|
+
const fs_1 = require("fs");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
const config_1 = require("../config");
|
|
15
|
+
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
16
|
+
let memoryCache = null;
|
|
17
|
+
/**
|
|
18
|
+
* Get user tier from API or cache
|
|
19
|
+
* Defaults to 'FREE' if tier cannot be determined
|
|
20
|
+
*/
|
|
21
|
+
async function getUserTier() {
|
|
22
|
+
try {
|
|
23
|
+
// Check memory cache first
|
|
24
|
+
const now = Date.now();
|
|
25
|
+
if (memoryCache && (now - memoryCache.cachedAt) < CACHE_TTL) {
|
|
26
|
+
return memoryCache.tier;
|
|
27
|
+
}
|
|
28
|
+
// Check file cache
|
|
29
|
+
const neurcodeDir = (0, path_1.join)(process.cwd(), '.neurcode');
|
|
30
|
+
const configPath = (0, path_1.join)(neurcodeDir, 'config.json');
|
|
31
|
+
if ((0, fs_1.existsSync)(configPath)) {
|
|
32
|
+
try {
|
|
33
|
+
const fileContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
|
|
34
|
+
const config = JSON.parse(fileContent);
|
|
35
|
+
if (config.tier && config.tierCachedAt) {
|
|
36
|
+
const cacheAge = now - config.tierCachedAt;
|
|
37
|
+
if (cacheAge < CACHE_TTL) {
|
|
38
|
+
const tier = config.tier;
|
|
39
|
+
memoryCache = { tier, cachedAt: config.tierCachedAt };
|
|
40
|
+
return tier;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
// Ignore parse errors, continue to API fetch
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Fetch from API
|
|
49
|
+
const config = (0, config_1.loadConfig)();
|
|
50
|
+
if (!config.apiKey) {
|
|
51
|
+
// No API key, default to FREE
|
|
52
|
+
return 'FREE';
|
|
53
|
+
}
|
|
54
|
+
const apiUrl = (config.apiUrl || 'https://api.neurcode.com').replace(/\/$/, '');
|
|
55
|
+
const apiKey = config.apiKey;
|
|
56
|
+
// Try to get tier from subscription endpoint
|
|
57
|
+
try {
|
|
58
|
+
const response = await fetch(`${apiUrl}/api/v1/subscriptions/status`, {
|
|
59
|
+
method: 'GET',
|
|
60
|
+
headers: {
|
|
61
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
// If not authorized or subscription not found, default to FREE
|
|
67
|
+
return 'FREE';
|
|
68
|
+
}
|
|
69
|
+
const subscription = await response.json();
|
|
70
|
+
// PRO tier if: professional plan AND (active status OR trial status)
|
|
71
|
+
const tier = subscription.plan.slug === 'professional' &&
|
|
72
|
+
(subscription.status === 'active' || subscription.status === 'trial' || subscription.isTrial)
|
|
73
|
+
? 'PRO' : 'FREE';
|
|
74
|
+
// Cache the tier
|
|
75
|
+
cacheTier(tier);
|
|
76
|
+
return tier;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
// If subscription endpoint fails, default to FREE
|
|
80
|
+
// Don't log warning to avoid noise - this is expected for FREE users
|
|
81
|
+
return 'FREE';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
// Fail-safe: default to FREE
|
|
86
|
+
return 'FREE';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Cache tier in memory and file
|
|
91
|
+
*/
|
|
92
|
+
function cacheTier(tier) {
|
|
93
|
+
const now = Date.now();
|
|
94
|
+
memoryCache = { tier, cachedAt: now };
|
|
95
|
+
try {
|
|
96
|
+
const neurcodeDir = (0, path_1.join)(process.cwd(), '.neurcode');
|
|
97
|
+
const configPath = (0, path_1.join)(neurcodeDir, 'config.json');
|
|
98
|
+
// Ensure .neurcode directory exists
|
|
99
|
+
if (!(0, fs_1.existsSync)(neurcodeDir)) {
|
|
100
|
+
(0, fs_1.mkdirSync)(neurcodeDir, { recursive: true });
|
|
101
|
+
}
|
|
102
|
+
// Read existing config or create new
|
|
103
|
+
let config = {};
|
|
104
|
+
if ((0, fs_1.existsSync)(configPath)) {
|
|
105
|
+
try {
|
|
106
|
+
const fileContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
|
|
107
|
+
config = JSON.parse(fileContent);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
// If parse fails, start with empty config
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Update tier cache
|
|
114
|
+
config.tier = tier;
|
|
115
|
+
config.tierCachedAt = now;
|
|
116
|
+
// Write back to file
|
|
117
|
+
(0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
// Ignore file write errors - memory cache is still valid
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Clear tier cache (useful after tier changes)
|
|
125
|
+
*/
|
|
126
|
+
function clearTierCache() {
|
|
127
|
+
memoryCache = null;
|
|
128
|
+
try {
|
|
129
|
+
const neurcodeDir = (0, path_1.join)(process.cwd(), '.neurcode');
|
|
130
|
+
const configPath = (0, path_1.join)(neurcodeDir, 'config.json');
|
|
131
|
+
if ((0, fs_1.existsSync)(configPath)) {
|
|
132
|
+
const fileContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
|
|
133
|
+
const config = JSON.parse(fileContent);
|
|
134
|
+
delete config.tier;
|
|
135
|
+
delete config.tierCachedAt;
|
|
136
|
+
(0, fs_1.writeFileSync)(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
// Ignore errors
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Check if user has PRO tier
|
|
145
|
+
*/
|
|
146
|
+
async function isProUser() {
|
|
147
|
+
const tier = await getUserTier();
|
|
148
|
+
return tier === 'PRO';
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=tier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tier.js","sourceRoot":"","sources":["../../src/utils/tier.ts"],"names":[],"mappings":"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"}
|