@far-world-labs/verblets 0.2.0 → 0.3.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/README.md +86 -213
- package/dist/index.browser.js +74 -0
- package/dist/index.js +548 -0
- package/dist/shared-C6kPWghF.js +7806 -0
- package/package.json +32 -11
- package/.cursor/launch.json +0 -30
- package/.cursor/settings.json +0 -20
- package/.github/workflows/branch-protection.yml +0 -22
- package/.github/workflows/ci.yml +0 -165
- package/.husky/pre-commit +0 -4
- package/.prettierrc +0 -6
- package/.release-it.json +0 -12
- package/.vitest.config.examples.js +0 -12
- package/.vitest.config.js +0 -8
- package/.vscode/launch.json +0 -31
- package/AGENTS.md +0 -220
- package/DEVELOPING.md +0 -105
- package/docker-compose.yml +0 -7
- package/eslint.config.js +0 -80
- package/scripts/clear-redis.js +0 -74
- package/scripts/generate-chain/index.js +0 -111
- package/scripts/generate-lib/index.js +0 -68
- package/scripts/generate-test/index.js +0 -137
- package/scripts/generate-verblet/README.md +0 -17
- package/scripts/generate-verblet/index.js +0 -110
- package/scripts/run.sh +0 -15
- package/scripts/runner/index.js +0 -56
- package/scripts/simple-editor/README.md +0 -34
- package/scripts/simple-editor/index.js +0 -79
- package/scripts/summarize-files/index.js +0 -70
- package/src/chains/README.md +0 -30
- package/src/chains/anonymize/README.md +0 -21
- package/src/chains/anonymize/index.examples.js +0 -75
- package/src/chains/anonymize/index.js +0 -121
- package/src/chains/anonymize/index.spec.js +0 -78
- package/src/chains/bulk-central-tendency/index.examples.js +0 -138
- package/src/chains/bulk-central-tendency/index.js +0 -91
- package/src/chains/bulk-filter/README.md +0 -21
- package/src/chains/bulk-filter/index.examples.js +0 -22
- package/src/chains/bulk-filter/index.js +0 -58
- package/src/chains/bulk-filter/index.spec.js +0 -38
- package/src/chains/bulk-find/README.md +0 -16
- package/src/chains/bulk-find/index.examples.js +0 -20
- package/src/chains/bulk-find/index.js +0 -30
- package/src/chains/bulk-find/index.spec.js +0 -26
- package/src/chains/bulk-group/README.md +0 -23
- package/src/chains/bulk-group/index.examples.js +0 -18
- package/src/chains/bulk-group/index.js +0 -34
- package/src/chains/bulk-group/index.spec.js +0 -41
- package/src/chains/bulk-map/README.md +0 -43
- package/src/chains/bulk-map/index.examples.js +0 -17
- package/src/chains/bulk-map/index.js +0 -86
- package/src/chains/bulk-map/index.spec.js +0 -44
- package/src/chains/bulk-reduce/README.md +0 -12
- package/src/chains/bulk-reduce/index.examples.js +0 -15
- package/src/chains/bulk-reduce/index.js +0 -13
- package/src/chains/bulk-reduce/index.spec.js +0 -25
- package/src/chains/bulk-score/README.md +0 -16
- package/src/chains/bulk-score/bulk-score-result.json +0 -18
- package/src/chains/bulk-score/index.examples.js +0 -22
- package/src/chains/bulk-score/index.js +0 -133
- package/src/chains/bulk-score/index.spec.js +0 -30
- package/src/chains/category-samples/README.md +0 -61
- package/src/chains/category-samples/index.examples.js +0 -103
- package/src/chains/category-samples/index.js +0 -134
- package/src/chains/collect-terms/README.md +0 -12
- package/src/chains/collect-terms/index.examples.js +0 -16
- package/src/chains/collect-terms/index.js +0 -44
- package/src/chains/collect-terms/index.spec.js +0 -25
- package/src/chains/conversation/README.md +0 -26
- package/src/chains/conversation/index.examples.js +0 -398
- package/src/chains/conversation/index.js +0 -126
- package/src/chains/conversation/index.spec.js +0 -148
- package/src/chains/conversation/turn-policies.js +0 -93
- package/src/chains/conversation/turn-policies.md +0 -123
- package/src/chains/conversation/turn-policies.spec.js +0 -135
- package/src/chains/date/README.md +0 -12
- package/src/chains/date/index.examples.js +0 -47
- package/src/chains/date/index.js +0 -74
- package/src/chains/date/index.spec.js +0 -62
- package/src/chains/disambiguate/README.md +0 -22
- package/src/chains/disambiguate/disambiguate-meanings-result.json +0 -16
- package/src/chains/disambiguate/index.examples.js +0 -18
- package/src/chains/disambiguate/index.js +0 -92
- package/src/chains/disambiguate/index.spec.js +0 -25
- package/src/chains/dismantle/README.md +0 -67
- package/src/chains/dismantle/dismantle.examples.js +0 -27
- package/src/chains/dismantle/index.examples.js +0 -30
- package/src/chains/dismantle/index.js +0 -303
- package/src/chains/dismantle/index.spec.js +0 -32
- package/src/chains/expect/README.md +0 -171
- package/src/chains/expect/index.examples.js +0 -146
- package/src/chains/expect/index.js +0 -207
- package/src/chains/expect/index.spec.js +0 -324
- package/src/chains/filter-ambiguous/README.md +0 -11
- package/src/chains/filter-ambiguous/index.examples.js +0 -20
- package/src/chains/filter-ambiguous/index.js +0 -49
- package/src/chains/filter-ambiguous/index.spec.js +0 -31
- package/src/chains/glossary/README.md +0 -19
- package/src/chains/glossary/index.examples.js +0 -386
- package/src/chains/glossary/index.js +0 -75
- package/src/chains/glossary/index.spec.js +0 -19
- package/src/chains/intersections/README.md +0 -166
- package/src/chains/intersections/index.examples.js +0 -280
- package/src/chains/intersections/index.js +0 -218
- package/src/chains/intersections/intersection-result.json +0 -38
- package/src/chains/list/index.examples.js +0 -68
- package/src/chains/list/index.js +0 -214
- package/src/chains/list/index.spec.js +0 -67
- package/src/chains/list/list-result.json +0 -16
- package/src/chains/list/schema.json +0 -24
- package/src/chains/llm-logger/README.md +0 -366
- package/src/chains/llm-logger/index.js +0 -591
- package/src/chains/llm-logger/index.spec.js +0 -391
- package/src/chains/llm-logger/schema.json +0 -105
- package/src/chains/questions/index.examples.js +0 -69
- package/src/chains/questions/index.js +0 -135
- package/src/chains/questions/index.spec.js +0 -29
- package/src/chains/scan-js/index.js +0 -116
- package/src/chains/set-interval/README.md +0 -81
- package/src/chains/set-interval/index.examples.js +0 -64
- package/src/chains/set-interval/index.js +0 -152
- package/src/chains/set-interval/index.spec.js +0 -70
- package/src/chains/socratic/README.md +0 -17
- package/src/chains/socratic/index.js +0 -64
- package/src/chains/socratic/index.spec.js +0 -24
- package/src/chains/sort/index.examples.js +0 -36
- package/src/chains/sort/index.js +0 -163
- package/src/chains/sort/index.spec.js +0 -112
- package/src/chains/sort/sort-result.json +0 -16
- package/src/chains/summary-map/README.md +0 -41
- package/src/chains/summary-map/index.examples.js +0 -64
- package/src/chains/summary-map/index.js +0 -226
- package/src/chains/summary-map/index.spec.js +0 -153
- package/src/chains/test/index.js +0 -114
- package/src/chains/test-advice/index.js +0 -35
- package/src/chains/themes/README.md +0 -20
- package/src/chains/themes/index.examples.js +0 -17
- package/src/chains/themes/index.js +0 -28
- package/src/chains/themes/index.spec.js +0 -19
- package/src/chains/veiled-variants/index.examples.js +0 -18
- package/src/chains/veiled-variants/index.js +0 -107
- package/src/chains/veiled-variants/index.spec.js +0 -40
- package/src/constants/common.js +0 -13
- package/src/constants/messages.js +0 -3
- package/src/constants/models.js +0 -184
- package/src/index.js +0 -203
- package/src/json-schemas/README.md +0 -13
- package/src/json-schemas/cars-test.json +0 -11
- package/src/json-schemas/index.js +0 -12
- package/src/json-schemas/intent.json +0 -38
- package/src/json-schemas/schema-dot-org-photograph.json +0 -133
- package/src/json-schemas/schema-dot-org-place.json +0 -129
- package/src/lib/README.md +0 -26
- package/src/lib/any-signal/index.js +0 -28
- package/src/lib/assert/README.md +0 -84
- package/src/lib/assert/index.js +0 -50
- package/src/lib/bulk-filter/README.md +0 -22
- package/src/lib/bulk-filter/index.examples.js +0 -27
- package/src/lib/bulk-filter/index.js +0 -63
- package/src/lib/bulk-filter/index.spec.js +0 -38
- package/src/lib/bulk-find/README.md +0 -18
- package/src/lib/bulk-find/index.examples.js +0 -19
- package/src/lib/bulk-find/index.js +0 -30
- package/src/lib/bulk-find/index.spec.js +0 -41
- package/src/lib/chatgpt/index.js +0 -163
- package/src/lib/combinations/index.js +0 -30
- package/src/lib/combinations/index.spec.js +0 -23
- package/src/lib/editor/index.js +0 -31
- package/src/lib/functional/index.js +0 -28
- package/src/lib/logger-service/index.js +0 -32
- package/src/lib/parse-js-parts/index.js +0 -321
- package/src/lib/parse-js-parts/index.spec.js +0 -156
- package/src/lib/parse-llm-list/README.md +0 -39
- package/src/lib/parse-llm-list/index.js +0 -54
- package/src/lib/parse-llm-list/index.spec.js +0 -59
- package/src/lib/path-aliases/index.js +0 -37
- package/src/lib/path-aliases/index.spec.js +0 -64
- package/src/lib/pave/index.js +0 -34
- package/src/lib/pave/index.spec.js +0 -76
- package/src/lib/prompt-cache/index.js +0 -50
- package/src/lib/retry/index.js +0 -66
- package/src/lib/retry/index.spec.js +0 -86
- package/src/lib/ring-buffer/README.md +0 -82
- package/src/lib/ring-buffer/index.js +0 -235
- package/src/lib/ring-buffer/index.spec.js +0 -388
- package/src/lib/search-best-first/city-walk.spec.js +0 -37
- package/src/lib/search-best-first/index.js +0 -97
- package/src/lib/search-best-first/index.spec.js +0 -35
- package/src/lib/search-js-files/code-features-property-definitions.json +0 -123
- package/src/lib/search-js-files/index.examples.js +0 -22
- package/src/lib/search-js-files/index.js +0 -155
- package/src/lib/search-js-files/index.spec.js +0 -34
- package/src/lib/search-js-files/scan-file.js +0 -242
- package/src/lib/shorten-text/index.js +0 -25
- package/src/lib/shorten-text/index.spec.js +0 -68
- package/src/lib/strip-numeric/index.js +0 -5
- package/src/lib/strip-response/index.js +0 -30
- package/src/lib/template-replace/index.js +0 -23
- package/src/lib/template-replace/index.spec.js +0 -60
- package/src/lib/timed-abort-controller/index.js +0 -41
- package/src/lib/to-bool/index.js +0 -8
- package/src/lib/to-date/index.js +0 -11
- package/src/lib/to-enum/index.js +0 -14
- package/src/lib/to-number/index.js +0 -12
- package/src/lib/to-number-with-units/index.js +0 -51
- package/src/lib/transcribe/index.js +0 -78
- package/src/prompts/README.md +0 -17
- package/src/prompts/as-enum.js +0 -5
- package/src/prompts/as-json-schema.js +0 -9
- package/src/prompts/as-object-with-schema.js +0 -26
- package/src/prompts/as-schema-org-text.js +0 -25
- package/src/prompts/as-schema-org-type.js +0 -1
- package/src/prompts/blog-post.js +0 -7
- package/src/prompts/code-features.js +0 -24
- package/src/prompts/constants.js +0 -101
- package/src/prompts/features-json-schema.js +0 -27
- package/src/prompts/generate-collection.js +0 -26
- package/src/prompts/generate-list.js +0 -48
- package/src/prompts/generate-questions.js +0 -19
- package/src/prompts/index.js +0 -20
- package/src/prompts/intent.js +0 -60
- package/src/prompts/output-succinct-names.js +0 -3
- package/src/prompts/select-from-threshold.js +0 -17
- package/src/prompts/sort.js +0 -31
- package/src/prompts/style.js +0 -38
- package/src/prompts/summarize.js +0 -13
- package/src/prompts/token-budget.js +0 -3
- package/src/prompts/wrap-list.js +0 -11
- package/src/prompts/wrap-variable.js +0 -36
- package/src/services/llm-model/global-overrides.spec.js +0 -432
- package/src/services/llm-model/index.js +0 -308
- package/src/services/llm-model/model.js +0 -21
- package/src/services/llm-model/negotiate.spec.js +0 -447
- package/src/services/redis/index.js +0 -147
- package/src/test/setup.js +0 -20
- package/src/verblets/README.md +0 -26
- package/src/verblets/auto/index.examples.js +0 -31
- package/src/verblets/auto/index.js +0 -28
- package/src/verblets/auto/index.spec.js +0 -32
- package/src/verblets/bool/README.md +0 -36
- package/src/verblets/bool/index.examples.js +0 -80
- package/src/verblets/bool/index.js +0 -25
- package/src/verblets/bool/index.schema.json +0 -14
- package/src/verblets/bool/index.spec.js +0 -33
- package/src/verblets/central-tendency/README.md +0 -166
- package/src/verblets/central-tendency/central-tendency-result.json +0 -24
- package/src/verblets/central-tendency/index.examples.js +0 -196
- package/src/verblets/central-tendency/index.js +0 -171
- package/src/verblets/central-tendency/index.spec.js +0 -148
- package/src/verblets/conversation-turn/README.md +0 -33
- package/src/verblets/conversation-turn/index.examples.js +0 -218
- package/src/verblets/conversation-turn/index.js +0 -68
- package/src/verblets/conversation-turn/index.spec.js +0 -77
- package/src/verblets/conversation-turn-multi/README.md +0 -31
- package/src/verblets/conversation-turn-multi/index.examples.js +0 -160
- package/src/verblets/conversation-turn-multi/index.js +0 -104
- package/src/verblets/conversation-turn-multi/index.spec.js +0 -63
- package/src/verblets/enum/index.examples.js +0 -30
- package/src/verblets/enum/index.js +0 -18
- package/src/verblets/enum/index.spec.js +0 -35
- package/src/verblets/expect/README.md +0 -64
- package/src/verblets/expect/index.examples.js +0 -109
- package/src/verblets/expect/index.js +0 -75
- package/src/verblets/expect/index.spec.js +0 -127
- package/src/verblets/intent/index.examples.js +0 -139
- package/src/verblets/intent/index.js +0 -60
- package/src/verblets/intent/index.spec.js +0 -31
- package/src/verblets/intersection/README.md +0 -16
- package/src/verblets/intersection/index.examples.js +0 -89
- package/src/verblets/intersection/index.js +0 -125
- package/src/verblets/intersection/index.spec.js +0 -60
- package/src/verblets/intersection/intersection-result.json +0 -16
- package/src/verblets/list-expand/README.md +0 -10
- package/src/verblets/list-expand/index.examples.js +0 -14
- package/src/verblets/list-expand/index.js +0 -104
- package/src/verblets/list-expand/index.spec.js +0 -18
- package/src/verblets/list-expand/list-expand-result.json +0 -16
- package/src/verblets/list-filter/README.md +0 -22
- package/src/verblets/list-filter/index.examples.js +0 -26
- package/src/verblets/list-filter/index.js +0 -18
- package/src/verblets/list-filter/index.spec.js +0 -19
- package/src/verblets/list-find/README.md +0 -11
- package/src/verblets/list-find/index.examples.js +0 -15
- package/src/verblets/list-find/index.js +0 -17
- package/src/verblets/list-find/index.spec.js +0 -19
- package/src/verblets/list-group/README.md +0 -16
- package/src/verblets/list-group/index.examples.js +0 -16
- package/src/verblets/list-group/index.js +0 -112
- package/src/verblets/list-group/index.spec.js +0 -35
- package/src/verblets/list-group/list-group-result.json +0 -16
- package/src/verblets/list-map/README.md +0 -11
- package/src/verblets/list-map/index.examples.js +0 -15
- package/src/verblets/list-map/index.js +0 -26
- package/src/verblets/list-map/index.spec.js +0 -17
- package/src/verblets/list-reduce/README.md +0 -10
- package/src/verblets/list-reduce/index.examples.js +0 -14
- package/src/verblets/list-reduce/index.js +0 -21
- package/src/verblets/list-reduce/index.spec.js +0 -27
- package/src/verblets/list-reduce/index.spec.jsx +0 -27
- package/src/verblets/name/README.md +0 -15
- package/src/verblets/name/index.examples.js +0 -28
- package/src/verblets/name/index.js +0 -19
- package/src/verblets/name/index.spec.js +0 -33
- package/src/verblets/name-similar-to/README.md +0 -26
- package/src/verblets/name-similar-to/index.examples.js +0 -18
- package/src/verblets/name-similar-to/index.js +0 -20
- package/src/verblets/name-similar-to/index.spec.js +0 -13
- package/src/verblets/number/index.examples.js +0 -199
- package/src/verblets/number/index.js +0 -25
- package/src/verblets/number/index.spec.js +0 -33
- package/src/verblets/number-with-units/index.examples.js +0 -38
- package/src/verblets/number-with-units/index.js +0 -84
- package/src/verblets/number-with-units/index.spec.js +0 -46
- package/src/verblets/number-with-units/number-with-units-result.json +0 -23
- package/src/verblets/people-list/README.md +0 -28
- package/src/verblets/people-list/index.examples.js +0 -184
- package/src/verblets/people-list/index.js +0 -44
- package/src/verblets/people-list/index.spec.js +0 -49
- package/src/verblets/schema-org/index.examples.js +0 -51
- package/src/verblets/schema-org/index.js +0 -37
- package/src/verblets/schema-org/index.spec.js +0 -39
- package/src/verblets/sentiment/README.md +0 -10
- package/src/verblets/sentiment/index.examples.js +0 -20
- package/src/verblets/sentiment/index.js +0 -9
- package/src/verblets/sentiment/index.spec.js +0 -20
- package/src/verblets/to-object/README.md +0 -38
- package/src/verblets/to-object/index.examples.js +0 -29
- package/src/verblets/to-object/index.js +0 -131
- package/src/verblets/to-object/index.spec.js +0 -71
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import search from './index.js';
|
|
4
|
-
|
|
5
|
-
const examples = [
|
|
6
|
-
{
|
|
7
|
-
inputs: { filename: './src/lib/chatgpt/index.js' },
|
|
8
|
-
want: { foundFiles: 30 },
|
|
9
|
-
},
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
describe('Scan JS repo with best-first search', () => {
|
|
13
|
-
examples.forEach((example) => {
|
|
14
|
-
it(example.inputs.text, async () => {
|
|
15
|
-
const result = await search({ node: example.inputs });
|
|
16
|
-
|
|
17
|
-
if (example.want.foundFiles) {
|
|
18
|
-
expect(result.visited.size).toBeGreaterThan(example.want.foundFiles);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
import parseJSParts from '../parse-js-parts/index.js';
|
|
5
|
-
import search from '../search-best-first/index.js';
|
|
6
|
-
|
|
7
|
-
export class Node {
|
|
8
|
-
constructor(args) {
|
|
9
|
-
Object.assign(this, args);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
toString() {
|
|
13
|
-
return `${this.filename}:${this.functionName}`;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const processLocalImport = async (source) => {
|
|
18
|
-
const importedFile = await fs.readFile(source, 'utf-8');
|
|
19
|
-
const parsedImport = parseJSParts(source, importedFile);
|
|
20
|
-
return Object.entries(parsedImport.functionsMap).map(([importKey, importValue]) => ({
|
|
21
|
-
filename: source,
|
|
22
|
-
functionName: importValue?.functionName ?? importKey,
|
|
23
|
-
functionData: importValue,
|
|
24
|
-
}));
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const processNpmImport = async (source, includeNodeModules = false) => {
|
|
28
|
-
if (!includeNodeModules) return [];
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
// Find the project root by looking for package.json
|
|
32
|
-
let currentDir = process.cwd();
|
|
33
|
-
let packageJsonPath = path.join(currentDir, 'package.json');
|
|
34
|
-
|
|
35
|
-
// If not found in current directory, try to find it in parent directories
|
|
36
|
-
while (
|
|
37
|
-
!(await fs
|
|
38
|
-
.access(packageJsonPath)
|
|
39
|
-
.then(() => true)
|
|
40
|
-
.catch(() => false))
|
|
41
|
-
) {
|
|
42
|
-
const parentDir = path.dirname(currentDir);
|
|
43
|
-
if (parentDir === currentDir) break; // Reached filesystem root
|
|
44
|
-
currentDir = parentDir;
|
|
45
|
-
packageJsonPath = path.join(currentDir, 'package.json');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
49
|
-
if (packageJson.dependencies[source] || packageJson.devDependencies[source]) {
|
|
50
|
-
const nodeModulePath = path.join(currentDir, 'node_modules', source);
|
|
51
|
-
const npmPackageJson = JSON.parse(
|
|
52
|
-
await fs.readFile(path.join(nodeModulePath, 'package.json'), 'utf8')
|
|
53
|
-
);
|
|
54
|
-
const mainFilePath = npmPackageJson.main || 'index.js';
|
|
55
|
-
const importedFile = await fs.readFile(path.join(nodeModulePath, mainFilePath), 'utf-8');
|
|
56
|
-
const parsedImport = parseJSParts(mainFilePath, importedFile);
|
|
57
|
-
|
|
58
|
-
return Object.entries(parsedImport.functionsMap).map(([importKey, importValue]) => ({
|
|
59
|
-
filename: path.join(nodeModulePath, mainFilePath),
|
|
60
|
-
functionName: importKey,
|
|
61
|
-
functionData: importValue,
|
|
62
|
-
}));
|
|
63
|
-
}
|
|
64
|
-
} catch (error) {
|
|
65
|
-
console.error(`Process npm import [error]: ${error.message} (source: ${source})`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return [];
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const visitDefault = ({ state }) => {
|
|
72
|
-
if (process.env.NODE_ENV === 'development') {
|
|
73
|
-
// console.error(`Visiting: ${node.filename} - ${node.functionName}`);
|
|
74
|
-
}
|
|
75
|
-
return state;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const rank = ({ nodes }) => {
|
|
79
|
-
// Example: Rank by the length of the function name
|
|
80
|
-
return nodes.sort(
|
|
81
|
-
(a, b) => (a.functionName ?? a.filename).length - (b.functionName ?? b.filename).length
|
|
82
|
-
);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const prepareNext = async ({ node, includeNodeModules }) => {
|
|
86
|
-
const code = await fs.readFile(node.filename, 'utf-8');
|
|
87
|
-
const parsed = parseJSParts(node.filename, code);
|
|
88
|
-
|
|
89
|
-
const functionsFound = Object.entries(parsed.functionsMap).map(([, value]) => {
|
|
90
|
-
return new Node({
|
|
91
|
-
...value,
|
|
92
|
-
filename: node.filename,
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const importPromises = Object.values(parsed.importsMap).map((importData) => {
|
|
97
|
-
if (
|
|
98
|
-
importData.source.startsWith('./') ||
|
|
99
|
-
importData.source.startsWith('../') ||
|
|
100
|
-
importData.source.startsWith('/')
|
|
101
|
-
) {
|
|
102
|
-
const resolvedPath = path.resolve(path.dirname(node.filename), importData.source);
|
|
103
|
-
|
|
104
|
-
return processLocalImport(resolvedPath);
|
|
105
|
-
}
|
|
106
|
-
return processNpmImport(importData.source, includeNodeModules);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const importFunctions = await Promise.all(importPromises);
|
|
110
|
-
const importsFound = importFunctions.reduce((acc, importedFuncs) => {
|
|
111
|
-
return [
|
|
112
|
-
...acc,
|
|
113
|
-
...importedFuncs.map(
|
|
114
|
-
(f) =>
|
|
115
|
-
new Node({
|
|
116
|
-
...f,
|
|
117
|
-
...f.functionData,
|
|
118
|
-
functionData: undefined,
|
|
119
|
-
// not including function names in the imports knowing we will scan the file for them better
|
|
120
|
-
functionName: undefined,
|
|
121
|
-
})
|
|
122
|
-
),
|
|
123
|
-
];
|
|
124
|
-
}, []);
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
functions: functionsFound,
|
|
128
|
-
imports: importsFound,
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const nextDefault = ({ state }) => {
|
|
133
|
-
return state.jsElements.imports.concat(state.jsElements.functions);
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
export default ({
|
|
137
|
-
next: nextExternal = nextDefault,
|
|
138
|
-
node: nodeInitial,
|
|
139
|
-
visit: visitExternal = visitDefault,
|
|
140
|
-
includeNodeModules,
|
|
141
|
-
...options
|
|
142
|
-
}) => {
|
|
143
|
-
return search({
|
|
144
|
-
...options,
|
|
145
|
-
next: async ({ node, state }) => {
|
|
146
|
-
const jsElements = await prepareNext({ node, includeNodeModules });
|
|
147
|
-
return nextExternal({ node, state: { jsElements, ...state } });
|
|
148
|
-
},
|
|
149
|
-
node: new Node(nodeInitial),
|
|
150
|
-
rank,
|
|
151
|
-
visit: ({ node, state }) => {
|
|
152
|
-
return visitExternal({ node, state });
|
|
153
|
-
},
|
|
154
|
-
});
|
|
155
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import search from './index.js';
|
|
4
|
-
|
|
5
|
-
vi.mock('../../lib/chatgpt/index.js', () => ({
|
|
6
|
-
default: vi.fn().mockImplementation((text) => {
|
|
7
|
-
if (/prompt text to match/.test(text)) {
|
|
8
|
-
return 'True';
|
|
9
|
-
}
|
|
10
|
-
return 'undefined';
|
|
11
|
-
}),
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
const examples = [
|
|
15
|
-
{
|
|
16
|
-
name: 'Basic usage',
|
|
17
|
-
inputs: { filename: './src/lib/chatgpt/index.js' },
|
|
18
|
-
want: { result: true },
|
|
19
|
-
},
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
describe('Scan JS repo with best-first search', () => {
|
|
23
|
-
examples.forEach((example) => {
|
|
24
|
-
it(example.name, async () => {
|
|
25
|
-
const result = await search({
|
|
26
|
-
node: { filename: example.inputs.filename },
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
if (example.want.typeOfResult) {
|
|
30
|
-
expect(typeof result).toStrictEqual(example.want.typeOfResult);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import { parse } from 'acorn';
|
|
4
|
-
import * as walk from 'acorn-walk';
|
|
5
|
-
import dependencyTree from 'dependency-tree';
|
|
6
|
-
|
|
7
|
-
const stripRootDir = (pathStr, root = process.cwd()) => {
|
|
8
|
-
return pathStr.replace(new RegExp(`^${root}`), '');
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const convertImport = (filePath, importPath) => {
|
|
12
|
-
return stripRootDir(path.resolve(path.dirname(filePath), importPath));
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const flatten = (obj) => {
|
|
16
|
-
let result = Object.keys(obj).reduce(
|
|
17
|
-
(memo, key) => ({
|
|
18
|
-
...memo,
|
|
19
|
-
[key]: 1,
|
|
20
|
-
}),
|
|
21
|
-
{}
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
result = Object.keys(obj).reduce((acc, key) => {
|
|
25
|
-
if (typeof obj[key] === 'object') {
|
|
26
|
-
const flattenedValue = flatten(obj[key]);
|
|
27
|
-
return Object.entries(flattenedValue).reduce((innerAcc, [innerKey, innerVal]) => {
|
|
28
|
-
return {
|
|
29
|
-
...innerAcc,
|
|
30
|
-
[innerKey]: (innerAcc[innerKey] ?? 0) + innerVal,
|
|
31
|
-
};
|
|
32
|
-
}, acc);
|
|
33
|
-
}
|
|
34
|
-
return acc;
|
|
35
|
-
}, result);
|
|
36
|
-
|
|
37
|
-
return result;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const listFiles = (file, cwd) => {
|
|
41
|
-
const config = {
|
|
42
|
-
filename: file,
|
|
43
|
-
directory: cwd,
|
|
44
|
-
nodeModulesConfig: { entry: 'module' },
|
|
45
|
-
nonExistent: [],
|
|
46
|
-
filter: (p) => p.indexOf('node_modules') === -1,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
return dependencyTree(config);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const parseFiles = (files) => {
|
|
53
|
-
const filesMap = {};
|
|
54
|
-
for (const file of files) {
|
|
55
|
-
const functionsMap = {};
|
|
56
|
-
const functionsSeen = {};
|
|
57
|
-
const variablesMap = {};
|
|
58
|
-
const importsMap = {};
|
|
59
|
-
|
|
60
|
-
const code = fs.readFileSync(file, 'utf-8');
|
|
61
|
-
|
|
62
|
-
const comments = [];
|
|
63
|
-
|
|
64
|
-
const ast = parse(code, {
|
|
65
|
-
sourceType: 'module',
|
|
66
|
-
ecmaVersion: 'latest',
|
|
67
|
-
onComment: comments,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
const commentsMap = comments.reduce((acc, comment) => {
|
|
71
|
-
return { ...acc, [comment.start]: comment };
|
|
72
|
-
}, {});
|
|
73
|
-
|
|
74
|
-
filesMap[stripRootDir(file)] = {
|
|
75
|
-
functionsMap,
|
|
76
|
-
variablesMap,
|
|
77
|
-
importsMap,
|
|
78
|
-
commentsMap,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
walk.simple(ast, {
|
|
82
|
-
ImportDeclaration(node) {
|
|
83
|
-
const declaration = node.specifiers
|
|
84
|
-
.filter((s) => s.type === 'ImportDefaultSpecifier')
|
|
85
|
-
.map((s) => {
|
|
86
|
-
return s.local.name;
|
|
87
|
-
})?.[0];
|
|
88
|
-
const specifiers = node.specifiers
|
|
89
|
-
.filter((s) => s.type === 'ImportSpecifier')
|
|
90
|
-
.map((s) => {
|
|
91
|
-
return { ...s.imported }.name; // also has local name
|
|
92
|
-
});
|
|
93
|
-
const source = node.source.value;
|
|
94
|
-
const importKey = source.startsWith('.') ? convertImport(file, source) : source;
|
|
95
|
-
importsMap[importKey] = {
|
|
96
|
-
start: node.start,
|
|
97
|
-
end: node.end,
|
|
98
|
-
declaration,
|
|
99
|
-
specifiers,
|
|
100
|
-
source,
|
|
101
|
-
};
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
FunctionDeclaration(node) {
|
|
105
|
-
functionsMap[`${node.type}:${node.id.name}`] = {
|
|
106
|
-
start: node.start,
|
|
107
|
-
end: node.end,
|
|
108
|
-
name: node.id.name,
|
|
109
|
-
type: node.type,
|
|
110
|
-
async: node.async,
|
|
111
|
-
generator: node.generator,
|
|
112
|
-
};
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
VariableDeclaration(node) {
|
|
116
|
-
const arrowDeclarations = node.declarations.filter((d) => {
|
|
117
|
-
return (
|
|
118
|
-
d.id.type === 'Identifier' &&
|
|
119
|
-
d.init?.type === 'ArrowFunctionExpression' &&
|
|
120
|
-
d.init?.body.type === 'BlockStatement'
|
|
121
|
-
);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
arrowDeclarations.forEach((d) => {
|
|
125
|
-
functionsSeen[`${d.init.start}:${d.init.end}`] = true;
|
|
126
|
-
|
|
127
|
-
functionsMap[`ArrowFunctionExpression:${d.id.name}`] = {
|
|
128
|
-
start: d.start,
|
|
129
|
-
end: d.end,
|
|
130
|
-
name: d.id.name,
|
|
131
|
-
async: d.init.async,
|
|
132
|
-
generator: d.init.generator,
|
|
133
|
-
type: 'ArrowFunctionExpression',
|
|
134
|
-
};
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
const fnExpDeclarations = node.declarations.filter((d) => {
|
|
138
|
-
return (
|
|
139
|
-
d.id.type === 'Identifier' &&
|
|
140
|
-
d.init?.type === 'FunctionExpression' &&
|
|
141
|
-
d.init?.body.type === 'BlockStatement'
|
|
142
|
-
);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
fnExpDeclarations.forEach((d) => {
|
|
146
|
-
functionsSeen[`${d.init.start}:${d.init.end}`] = true;
|
|
147
|
-
|
|
148
|
-
functionsMap[`FunctionExpression:${d.id.name}`] = {
|
|
149
|
-
start: d.start,
|
|
150
|
-
end: d.end,
|
|
151
|
-
name: d.id.name,
|
|
152
|
-
async: d.init.async,
|
|
153
|
-
generator: d.init.generator,
|
|
154
|
-
type: 'FunctionExpression',
|
|
155
|
-
};
|
|
156
|
-
});
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
// Property Assignment
|
|
160
|
-
AssignmentExpression(node) {
|
|
161
|
-
if (
|
|
162
|
-
(node.right.type === 'FunctionExpression' ||
|
|
163
|
-
node.right.type === 'ArrowFunctionExpression') &&
|
|
164
|
-
node.value
|
|
165
|
-
) {
|
|
166
|
-
functionsSeen[`${node.right.start}:${node.right.end}`] = true;
|
|
167
|
-
|
|
168
|
-
functionsMap[`${node.value.type}:${node.left.property.name}`] = {
|
|
169
|
-
start: node.start,
|
|
170
|
-
end: node.end,
|
|
171
|
-
name: node.left.property.name,
|
|
172
|
-
async: node.right.async,
|
|
173
|
-
generator: node.right.generator,
|
|
174
|
-
type: node.right.type,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
// Object Definition
|
|
180
|
-
Property(node) {
|
|
181
|
-
if (
|
|
182
|
-
node.value.type === 'FunctionExpression' ||
|
|
183
|
-
node.value.type === 'ArrowFunctionExpression'
|
|
184
|
-
) {
|
|
185
|
-
functionsSeen[`${node.value.start}:${node.value.end}`] = true;
|
|
186
|
-
|
|
187
|
-
functionsMap[`Property:${node.key.name}`] = {
|
|
188
|
-
start: node.start,
|
|
189
|
-
end: node.end,
|
|
190
|
-
name: node.key.name,
|
|
191
|
-
async: node.value.async,
|
|
192
|
-
generator: node.value.generator,
|
|
193
|
-
type: node.value.type,
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
|
|
198
|
-
// Class Definition
|
|
199
|
-
ClassDeclaration(node) {
|
|
200
|
-
const className = node.id.name;
|
|
201
|
-
node.body.body.forEach((classElement) => {
|
|
202
|
-
if (classElement.type === 'MethodDefinition') {
|
|
203
|
-
functionsSeen[`${classElement.value.start}:${classElement.value.end}`] = true;
|
|
204
|
-
|
|
205
|
-
functionsMap[`MethodDefinition:${className}.${classElement.key.name}`] = {
|
|
206
|
-
start: node.start,
|
|
207
|
-
end: node.end,
|
|
208
|
-
className,
|
|
209
|
-
name: classElement.key.name,
|
|
210
|
-
async: classElement.value.async,
|
|
211
|
-
generator: classElement.value.generator,
|
|
212
|
-
type: 'MethodDefinition',
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
walk.simple(ast, {
|
|
220
|
-
FunctionExpression(node) {
|
|
221
|
-
if (!functionsSeen[`${node.start}:${node.end}`]) {
|
|
222
|
-
functionsMap[`${node.type}:${node.start}`] = {
|
|
223
|
-
start: node.start,
|
|
224
|
-
end: node.end,
|
|
225
|
-
type: node.type,
|
|
226
|
-
async: node.async,
|
|
227
|
-
generator: node.generator,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
},
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return filesMap;
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
export default (entryFile) => {
|
|
238
|
-
const filesTree = listFiles(entryFile, process.cwd());
|
|
239
|
-
const filesList = Object.keys(flatten(filesTree));
|
|
240
|
-
const results = parseFiles(filesList);
|
|
241
|
-
return results;
|
|
242
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import modelService from '../../services/llm-model/index.js';
|
|
2
|
-
|
|
3
|
-
export default (
|
|
4
|
-
text,
|
|
5
|
-
{ minCharsToRemove = 10, model = modelService.getBestPublicModel(), targetTokenCount }
|
|
6
|
-
) => {
|
|
7
|
-
const ellipsis = '...';
|
|
8
|
-
const textToTokenRatio = text.length / model.toTokens(text).length;
|
|
9
|
-
let trimmedText = text;
|
|
10
|
-
let tokenCount = model.toTokens(trimmedText).length;
|
|
11
|
-
|
|
12
|
-
while (tokenCount > targetTokenCount) {
|
|
13
|
-
const charsToRemove = Math.max(
|
|
14
|
-
Math.ceil((tokenCount - targetTokenCount) * textToTokenRatio),
|
|
15
|
-
minCharsToRemove
|
|
16
|
-
);
|
|
17
|
-
const middleIndex = Math.floor(trimmedText.length / 2);
|
|
18
|
-
const startIndex = middleIndex - Math.ceil(charsToRemove / 2);
|
|
19
|
-
const endIndex = middleIndex + Math.floor(charsToRemove / 2);
|
|
20
|
-
trimmedText = trimmedText.slice(0, startIndex) + ellipsis + trimmedText.slice(endIndex);
|
|
21
|
-
tokenCount = model.toTokens(trimmedText).length;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return trimmedText;
|
|
25
|
-
};
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import modelService from '../../services/llm-model/index.js';
|
|
3
|
-
|
|
4
|
-
import shortenText from './index.js';
|
|
5
|
-
|
|
6
|
-
const examples = [
|
|
7
|
-
{
|
|
8
|
-
name: 'Basic usage',
|
|
9
|
-
inputs: {
|
|
10
|
-
text: 'Hello, world! This is a long text for testing the shortenText function.',
|
|
11
|
-
targetTokenCount: 10,
|
|
12
|
-
},
|
|
13
|
-
want: {
|
|
14
|
-
start: /^Hello, world!/,
|
|
15
|
-
end: /Text function\.$/,
|
|
16
|
-
maxLength: 40,
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
name: 'No trimming needed',
|
|
21
|
-
inputs: {
|
|
22
|
-
text: 'This text is short enough.',
|
|
23
|
-
targetTokenCount: 8,
|
|
24
|
-
},
|
|
25
|
-
want: {
|
|
26
|
-
result: 'This text is short enough.',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: 'Minimum characters removal',
|
|
31
|
-
inputs: {
|
|
32
|
-
text: 'This is another test to check the minimum characters removal feature.',
|
|
33
|
-
targetTokenCount: 6,
|
|
34
|
-
minCharsToRemove: 5,
|
|
35
|
-
},
|
|
36
|
-
want: {
|
|
37
|
-
start: /^This is/,
|
|
38
|
-
end: /feature\.$/,
|
|
39
|
-
maxLength: 25,
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
describe('Shorten text', () => {
|
|
45
|
-
examples.forEach((example) => {
|
|
46
|
-
it(example.name, () => {
|
|
47
|
-
const got = shortenText(example.inputs.text, {
|
|
48
|
-
targetTokenCount: example.inputs.targetTokenCount,
|
|
49
|
-
minCharsToRemove: example.inputs.minCharsToRemove,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
if (example.want.result) {
|
|
53
|
-
expect(got).toEqual(example.want.result);
|
|
54
|
-
}
|
|
55
|
-
if (example.want.start) {
|
|
56
|
-
expect(example.want.start.test(got)).toBe(true);
|
|
57
|
-
}
|
|
58
|
-
if (example.want.start) {
|
|
59
|
-
expect(example.want.end.test(got)).toBe(true);
|
|
60
|
-
}
|
|
61
|
-
if (example.want.maxLength) {
|
|
62
|
-
expect(modelService.getBestPublicModel().toTokens(got).length).toBeLessThanOrEqual(
|
|
63
|
-
example.want.maxLength
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This regex pattern looks for combinations of:
|
|
3
|
-
*
|
|
4
|
-
* { followed by optional whitespace and ",
|
|
5
|
-
* [ followed by optional whitespace, {, ", a digit [0-9], or [.
|
|
6
|
-
*/
|
|
7
|
-
const jsonStartRegex = /(?:\s*[{[]|[{[]+\s*[{[])/;
|
|
8
|
-
|
|
9
|
-
export default (val) => {
|
|
10
|
-
const [questionPart = '', , answerPart = ''] = val.split(/(=){11,29}/);
|
|
11
|
-
|
|
12
|
-
const answerPartTrimmed = answerPart.trim() ?? '';
|
|
13
|
-
|
|
14
|
-
const answerSection = answerPartTrimmed.length ? answerPart.trim() : questionPart.trim();
|
|
15
|
-
|
|
16
|
-
const answerNoPrefix = answerSection.replace(/[aA]nswer:?/, '').trim();
|
|
17
|
-
|
|
18
|
-
const answerNoPunctuation = answerNoPrefix.replace(/[., ]+$/g, '').trim();
|
|
19
|
-
|
|
20
|
-
const answerNoQuotes = answerNoPunctuation.replace(/^['"]/, '').replace(/['"]$/, '').trim();
|
|
21
|
-
|
|
22
|
-
if (answerNoQuotes.startsWith('{') || answerNoQuotes.startsWith('[')) {
|
|
23
|
-
return answerNoQuotes;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const match = answerNoQuotes.match(jsonStartRegex);
|
|
27
|
-
const answerJSONStart = match ? answerNoQuotes.slice(match.index) : undefined;
|
|
28
|
-
|
|
29
|
-
return answerJSONStart ?? answerNoQuotes;
|
|
30
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple template replacement utility
|
|
3
|
-
* Replaces {key} placeholders with values from data object
|
|
4
|
-
*
|
|
5
|
-
* @param {string} template - Template string with {key} placeholders
|
|
6
|
-
* @param {object} data - Data object with key-value pairs
|
|
7
|
-
* @returns {string} - Template with placeholders replaced
|
|
8
|
-
*/
|
|
9
|
-
export default function templateReplace(template, data) {
|
|
10
|
-
if (!template || typeof template !== 'string') {
|
|
11
|
-
return template || '';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (data === undefined || data === null || typeof data !== 'object') {
|
|
15
|
-
return template;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Replace all {key} placeholders
|
|
19
|
-
return template.replace(/\{([^}]+)\}/g, (match, key) => {
|
|
20
|
-
const value = data[key];
|
|
21
|
-
return String(value != null ? value : '');
|
|
22
|
-
});
|
|
23
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import templateReplace from './index.js';
|
|
3
|
-
|
|
4
|
-
describe('templateReplace', () => {
|
|
5
|
-
it('replaces single placeholder', () => {
|
|
6
|
-
const result = templateReplace('Hello {name}!', { name: 'World' });
|
|
7
|
-
expect(result).toBe('Hello World!');
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('replaces multiple placeholders', () => {
|
|
11
|
-
const result = templateReplace('Hello {name}, you are {age} years old', {
|
|
12
|
-
name: 'John',
|
|
13
|
-
age: 30,
|
|
14
|
-
});
|
|
15
|
-
expect(result).toBe('Hello John, you are 30 years old');
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('replaces multiple instances of same placeholder', () => {
|
|
19
|
-
const result = templateReplace('{name} said "{name} is great"', { name: 'Alice' });
|
|
20
|
-
expect(result).toBe('Alice said "Alice is great"');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('handles missing data gracefully', () => {
|
|
24
|
-
const result = templateReplace('Hello {name}!', {});
|
|
25
|
-
expect(result).toBe('Hello !');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('handles null/undefined values', () => {
|
|
29
|
-
const result = templateReplace('Value: {value}', { value: null });
|
|
30
|
-
expect(result).toBe('Value: ');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('handles non-string values', () => {
|
|
34
|
-
const result = templateReplace('Count: {count}, Active: {active}', {
|
|
35
|
-
count: 42,
|
|
36
|
-
active: true,
|
|
37
|
-
});
|
|
38
|
-
expect(result).toBe('Count: 42, Active: true');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('returns original template when no data provided', () => {
|
|
42
|
-
const result = templateReplace('Hello {name}!');
|
|
43
|
-
expect(result).toBe('Hello {name}!');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('returns original template when data is not an object', () => {
|
|
47
|
-
const result = templateReplace('Hello {name}!', 'not an object');
|
|
48
|
-
expect(result).toBe('Hello {name}!');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('handles empty template', () => {
|
|
52
|
-
const result = templateReplace('', { name: 'test' });
|
|
53
|
-
expect(result).toBe('');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('handles template with no placeholders', () => {
|
|
57
|
-
const result = templateReplace('Just plain text', { name: 'test' });
|
|
58
|
-
expect(result).toBe('Just plain text');
|
|
59
|
-
});
|
|
60
|
-
});
|