@algolia/wizard 0.33.0 → 0.34.0-rc.125.244
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/main.js
CHANGED
|
@@ -2645,7 +2645,7 @@ async function ensureApplication() {
|
|
|
2645
2645
|
}
|
|
2646
2646
|
|
|
2647
2647
|
// src/workflows/default.ts
|
|
2648
|
-
import { z as
|
|
2648
|
+
import { z as z29 } from "zod";
|
|
2649
2649
|
|
|
2650
2650
|
// src/actions/listIndices.ts
|
|
2651
2651
|
import { z as z5 } from "zod";
|
|
@@ -2759,17 +2759,23 @@ async function hasSymlinkParent(ctx, target) {
|
|
|
2759
2759
|
// src/lib/tools/listFiles.ts
|
|
2760
2760
|
function listFilesTool(ctx) {
|
|
2761
2761
|
return tool({
|
|
2762
|
-
description:
|
|
2763
|
-
inputSchema: z6.object(
|
|
2764
|
-
|
|
2765
|
-
|
|
2762
|
+
description: 'List files in a directory (default: the current working directory). Pass path to list a subdirectory directly \u2014 e.g. "packages/api" \u2014 without first changeDirectory-ing into it.',
|
|
2763
|
+
inputSchema: z6.object({
|
|
2764
|
+
path: z6.string().optional().describe("Directory to list, relative to cwd (default: cwd)")
|
|
2765
|
+
}),
|
|
2766
|
+
execute: async ({ path = "." }) => {
|
|
2767
|
+
logger.info({ path }, "called listFiles tool");
|
|
2766
2768
|
if (++ctx.counts.list > ctx.limits.list) {
|
|
2767
2769
|
return `Refused: list limit (${ctx.limits.list}) reached. Stop listing and proceed with the information you already have.`;
|
|
2768
2770
|
}
|
|
2769
|
-
const resolved2 = resolveInRoot(ctx,
|
|
2771
|
+
const resolved2 = resolveInRoot(ctx, path);
|
|
2770
2772
|
if (!resolved2.ok) return resolved2.error;
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
+
try {
|
|
2774
|
+
const entries = await readdir(resolved2.target, { withFileTypes: true });
|
|
2775
|
+
return entries.map((e) => e.isDirectory() ? `${e.name}/` : e.name).join("\n");
|
|
2776
|
+
} catch (err) {
|
|
2777
|
+
return `Error listing ${path}: ${err.message}`;
|
|
2778
|
+
}
|
|
2773
2779
|
}
|
|
2774
2780
|
});
|
|
2775
2781
|
}
|
|
@@ -3079,8 +3085,7 @@ function resolveWriteKey(index, appId) {
|
|
|
3079
3085
|
`Algolia Wizard write key for ${index} index`
|
|
3080
3086
|
);
|
|
3081
3087
|
}
|
|
3082
|
-
async function resolveSearchOnlyKey(index, appId
|
|
3083
|
-
if (envKey) return { key: envKey, source: "env" };
|
|
3088
|
+
async function resolveSearchOnlyKey(index, appId) {
|
|
3084
3089
|
return resolveKey(
|
|
3085
3090
|
"search",
|
|
3086
3091
|
index,
|
|
@@ -3119,6 +3124,12 @@ function isIgnoredByRule(root, relPath) {
|
|
|
3119
3124
|
function isTracked(root, relPath) {
|
|
3120
3125
|
return gitSucceeds(root, ["ls-files", "--error-unmatch", "--", relPath]);
|
|
3121
3126
|
}
|
|
3127
|
+
async function gitIgnoreStatus(root, target) {
|
|
3128
|
+
const { ignoredByRule, tracked } = await inspect(root, target);
|
|
3129
|
+
if (ignoredByRule === void 0) return "unknown";
|
|
3130
|
+
if (tracked) return "tracked";
|
|
3131
|
+
return ignoredByRule ? "covered" : "needsRule";
|
|
3132
|
+
}
|
|
3122
3133
|
async function inspect(root, target) {
|
|
3123
3134
|
const relPath = relative2(root, target);
|
|
3124
3135
|
if (!relPath || relPath.startsWith("..")) {
|
|
@@ -3164,31 +3175,9 @@ async function ensureGitIgnored(root, target) {
|
|
|
3164
3175
|
}
|
|
3165
3176
|
|
|
3166
3177
|
// src/lib/tools/writeAlgoliaCredentials.ts
|
|
3167
|
-
var APP_ID_VAR = "
|
|
3168
|
-
var API_KEY_VAR = "
|
|
3178
|
+
var APP_ID_VAR = "ALGOLIA_APP_ID";
|
|
3179
|
+
var API_KEY_VAR = "ALGOLIA_WRITE_KEY";
|
|
3169
3180
|
var INDEX_NAME_VAR = "ALGOLIA_INDEX_NAME";
|
|
3170
|
-
var PUBLIC_APP_ID_SUFFIX = "ALGOLIA_APP_ID";
|
|
3171
|
-
var PUBLIC_SEARCH_KEY_SUFFIX = "ALGOLIA_SEARCH_KEY";
|
|
3172
|
-
var PUBLIC_INDEX_NAME_SUFFIX = "ALGOLIA_INDEX_NAME";
|
|
3173
|
-
function publicAppIdVar(prefix) {
|
|
3174
|
-
return `${prefix}${PUBLIC_APP_ID_SUFFIX}`;
|
|
3175
|
-
}
|
|
3176
|
-
function publicSearchKeyVar(prefix) {
|
|
3177
|
-
return `${prefix}${PUBLIC_SEARCH_KEY_SUFFIX}`;
|
|
3178
|
-
}
|
|
3179
|
-
function publicIndexNameVar(prefix) {
|
|
3180
|
-
return `${prefix}${PUBLIC_INDEX_NAME_SUFFIX}`;
|
|
3181
|
-
}
|
|
3182
|
-
function publicSearchEnvVars(prefix, index, appId, searchKey) {
|
|
3183
|
-
return [
|
|
3184
|
-
{ name: publicAppIdVar(prefix), value: appId ?? "<your-algolia-app-id>" },
|
|
3185
|
-
{
|
|
3186
|
-
name: publicSearchKeyVar(prefix),
|
|
3187
|
-
value: searchKey ?? "<your-algolia-search-only-api-key>"
|
|
3188
|
-
},
|
|
3189
|
-
{ name: publicIndexNameVar(prefix), value: index }
|
|
3190
|
-
];
|
|
3191
|
-
}
|
|
3192
3181
|
function appendEnv(content, entries) {
|
|
3193
3182
|
const prefix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
|
|
3194
3183
|
const lines = entries.map(([name, value]) => `${name}=${value}
|
|
@@ -3196,11 +3185,13 @@ function appendEnv(content, entries) {
|
|
|
3196
3185
|
return content + prefix + lines;
|
|
3197
3186
|
}
|
|
3198
3187
|
function hasEnv(content, name) {
|
|
3199
|
-
return new RegExp(`^(\\
|
|
3188
|
+
return new RegExp(`^([ \\t]*(?:export[ \\t]+)?${name})[ \\t]*=`, "m").test(
|
|
3189
|
+
content
|
|
3190
|
+
);
|
|
3200
3191
|
}
|
|
3201
3192
|
function readEnv(content, name) {
|
|
3202
3193
|
const found = content.match(
|
|
3203
|
-
new RegExp(
|
|
3194
|
+
new RegExp(`^[ \\t]*(?:export[ \\t]+)?${name}[ \\t]*=[ \\t]*(.*)$`, "m")
|
|
3204
3195
|
);
|
|
3205
3196
|
if (!found) return null;
|
|
3206
3197
|
const raw = found[1].trim();
|
|
@@ -3211,16 +3202,16 @@ function readEnv(content, name) {
|
|
|
3211
3202
|
function upsertEnv(content, name, value) {
|
|
3212
3203
|
if (!hasEnv(content, name)) return appendEnv(content, [[name, value]]);
|
|
3213
3204
|
return content.replace(
|
|
3214
|
-
new RegExp(
|
|
3205
|
+
new RegExp(`^[ \\t]*(?:export[ \\t]+)?${name}[ \\t]*=.*$`, "gm"),
|
|
3215
3206
|
() => `${name}=${value}`
|
|
3216
3207
|
);
|
|
3217
3208
|
}
|
|
3218
3209
|
function writeCredentialsTool(ctx) {
|
|
3219
3210
|
return tool6({
|
|
3220
|
-
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) and the target index name (${INDEX_NAME_VAR}) into the given env file. The credentials come from the selected Algolia application, with a write key scoped to the target index; you only pass the path to the env file. ${INDEX_NAME_VAR} is always set to this run's target index, replacing any value already there. An ${APP_ID_VAR} or ${API_KEY_VAR} the file already gives a value is left untouched; a missing or blank one is filled in when it can be paired with the selected application. The env file is added to .gitignore automatically; do not edit .gitignore yourself.`,
|
|
3211
|
+
description: `Write the active Algolia credentials (${APP_ID_VAR} and ${API_KEY_VAR}) and the target index name (${INDEX_NAME_VAR}) into the given env file. The credentials come from the selected Algolia application, with a write key scoped to the target index; you only pass the path to the env file. ${INDEX_NAME_VAR} is always set to this run's target index, replacing any value already there. An ${APP_ID_VAR} or ${API_KEY_VAR} the file already gives a value is left untouched; a missing or blank one is filled in when it can be paired with the selected application. The env file is added to .gitignore automatically; do not edit .gitignore yourself. If the script or app that reads these credentials lives in a subdirectory (e.g. a package in a monorepo), an env file at the repo root is the wrong default \u2014 a script only loads env vars from its own directory (or one it's explicitly configured to read), so check every directory from the script's own up to the repo root, not just those two: its own directory, each ancestor in between (a shared workspace-level directory above the immediate package is common), and the root. Use whichever of those already holds real credentials; only fall back to the repo root when none of them do. Never invent a brand-new file in one of those directories when a real one already exists in another \u2014 that leaves the real one stale and the new one wrong. Listing just the script's own directory and the very top-level root is not enough to find a workspace-level file in between; check the intermediate ones too. If instructions describe a location that doesn't match the project you actually find (e.g. a path outside the repo, or a convention the project doesn't follow), don't stop and ask before doing anything \u2014 call this tool on the real, in-repo file the script actually reads (that's always the safe default), then note the mismatch afterward. Ending your turn with only a question and no call to this tool leaves the project unconfigured.`,
|
|
3221
3212
|
inputSchema: z13.object({
|
|
3222
3213
|
filePath: z13.string().describe(
|
|
3223
|
-
'Path to the env file to write credentials into (e.g. ".env")'
|
|
3214
|
+
'Path to the env file to write credentials into, relative to the repo root (e.g. ".env", or "packages/api/.env" when the consuming script lives in that package)'
|
|
3224
3215
|
)
|
|
3225
3216
|
}),
|
|
3226
3217
|
execute: async ({ filePath }) => {
|
|
@@ -3974,6 +3965,7 @@ var detectLanguage = () => runAgent({
|
|
|
3974
3965
|
"Return the exact version",
|
|
3975
3966
|
"Exclude things like CSS frameworks, build tools, or testing frameworks",
|
|
3976
3967
|
`Determine publicEnvVarPrefix: check the project's own env var usage first (e.g. names already referenced in code, .env/.env.example); if none exists, fall back to the detected framework's known convention for exposing env vars to client-side code; use "" when the project has no such convention (e.g. a backend-only project).`,
|
|
3968
|
+
"A brand-new project has no existing env var usage to find \u2014 one or two targeted checks (e.g. .env/.env.example, or a grep for the bundler's public-prefix convention) are enough to confirm that. Do not keep searching once those turn up nothing; fall back to the framework convention immediately.",
|
|
3977
3969
|
'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
|
|
3978
3970
|
"When done, call reportStatus"
|
|
3979
3971
|
],
|
|
@@ -4069,7 +4061,7 @@ async function runAnalysis(mode, extraInstructions = []) {
|
|
|
4069
4061
|
// package.json
|
|
4070
4062
|
var package_default = {
|
|
4071
4063
|
name: "@algolia/wizard",
|
|
4072
|
-
version: "0.
|
|
4064
|
+
version: "0.34.0-rc.125.244",
|
|
4073
4065
|
description: "Magically implement Algolia functionality in your codebase",
|
|
4074
4066
|
type: "module",
|
|
4075
4067
|
engines: {
|
|
@@ -4474,12 +4466,12 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
|
|
|
4474
4466
|
};
|
|
4475
4467
|
|
|
4476
4468
|
// src/actions/implement.ts
|
|
4477
|
-
import
|
|
4478
|
-
import {
|
|
4469
|
+
import z28 from "zod";
|
|
4470
|
+
import { relative as relative6, join as join12 } from "node:path";
|
|
4479
4471
|
|
|
4480
4472
|
// src/lib/git.ts
|
|
4481
4473
|
import { execFile as execFile2 } from "node:child_process";
|
|
4482
|
-
import { copyFile, mkdir as mkdir6,
|
|
4474
|
+
import { copyFile, mkdir as mkdir6, stat as stat3 } from "node:fs/promises";
|
|
4483
4475
|
import { basename as basename2, dirname as dirname7, isAbsolute as isAbsolute2, join as join10, resolve as resolve3 } from "node:path";
|
|
4484
4476
|
var MAX_BUFFER = 32 * 1024 * 1024;
|
|
4485
4477
|
function git(args) {
|
|
@@ -4533,42 +4525,6 @@ async function copyUploadIntoProject(repoRoot, ingestDir, sourcePath) {
|
|
|
4533
4525
|
}
|
|
4534
4526
|
return { ok: true, relPath };
|
|
4535
4527
|
}
|
|
4536
|
-
function hasEnvVar(content, name) {
|
|
4537
|
-
return new RegExp(`^(\\s*(?:export\\s+)?${name})\\s*=`, "m").test(content);
|
|
4538
|
-
}
|
|
4539
|
-
async function readEnvVar(repoRoot, name) {
|
|
4540
|
-
let content;
|
|
4541
|
-
try {
|
|
4542
|
-
content = await readFile8(join10(repoRoot, ".env"), "utf8");
|
|
4543
|
-
} catch (err) {
|
|
4544
|
-
if (err.code !== "ENOENT") throw err;
|
|
4545
|
-
return void 0;
|
|
4546
|
-
}
|
|
4547
|
-
const match = new RegExp(
|
|
4548
|
-
`^[ \\t]*(?:export[ \\t]+)?${name}[ \\t]*=[ \\t]*(.*)$`,
|
|
4549
|
-
"m"
|
|
4550
|
-
).exec(content);
|
|
4551
|
-
if (!match) return void 0;
|
|
4552
|
-
const value = match[1].trim().replace(/^(['"])(.*)\1$/, "$2").trim();
|
|
4553
|
-
if (!value || value.startsWith("<")) return void 0;
|
|
4554
|
-
return value;
|
|
4555
|
-
}
|
|
4556
|
-
async function writeSearchEnvValues(repoRoot, vars) {
|
|
4557
|
-
const target = join10(repoRoot, ".env");
|
|
4558
|
-
let existing = "";
|
|
4559
|
-
try {
|
|
4560
|
-
existing = await readFile8(target, "utf8");
|
|
4561
|
-
} catch (err) {
|
|
4562
|
-
if (err.code !== "ENOENT") throw err;
|
|
4563
|
-
}
|
|
4564
|
-
const missing = vars.filter((v) => !hasEnvVar(existing, v.name));
|
|
4565
|
-
if (missing.length === 0) return [];
|
|
4566
|
-
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
4567
|
-
const lines = missing.map(({ name, value }) => `${name}=${value}
|
|
4568
|
-
`).join("");
|
|
4569
|
-
await writeFile7(target, existing + prefix + lines, "utf8");
|
|
4570
|
-
return missing.map((v) => v.name);
|
|
4571
|
-
}
|
|
4572
4528
|
function normalizeFindingPaths(findings) {
|
|
4573
4529
|
return {
|
|
4574
4530
|
...findings,
|
|
@@ -4649,46 +4605,36 @@ function getFrameworkSpecificDoc(frameworks) {
|
|
|
4649
4605
|
return loadAlgoliaDoc("js");
|
|
4650
4606
|
}
|
|
4651
4607
|
|
|
4652
|
-
// src/actions/resolveEnvVarPrefix.ts
|
|
4653
|
-
import z28 from "zod";
|
|
4654
|
-
var resolveEnvVarPrefixSchema = z28.object({
|
|
4655
|
-
publicEnvVarPrefix: detectLanguageSchema.shape.publicEnvVarPrefix
|
|
4656
|
-
});
|
|
4657
|
-
var resolveEnvVarPrefix = (frameworkName) => runAgent({
|
|
4658
|
-
instructions: [
|
|
4659
|
-
`The developer corrected the project's framework to "${frameworkName}".`,
|
|
4660
|
-
`Determine publicEnvVarPrefix for this framework: check the project's own env var usage first (e.g. names already referenced in code, .env/.env.example); if none exists, fall back to this framework's known convention for exposing env vars to client-side code; use "" when the framework has no such convention (e.g. a backend-only framework).`,
|
|
4661
|
-
'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
|
|
4662
|
-
"When done, call reportStatus"
|
|
4663
|
-
],
|
|
4664
|
-
tools: ["listFiles", "changeDirectory", "readFile", "searchFiles"],
|
|
4665
|
-
outputSchema: resolveEnvVarPrefixSchema,
|
|
4666
|
-
modelSize: "small"
|
|
4667
|
-
});
|
|
4668
|
-
|
|
4669
4608
|
// src/actions/implement.ts
|
|
4670
|
-
var implementSchema =
|
|
4671
|
-
summary:
|
|
4672
|
-
ingestCommand:
|
|
4673
|
-
ingestScriptRan:
|
|
4674
|
-
ingestRecordCount:
|
|
4675
|
-
ingestDurationMs:
|
|
4676
|
-
ingestionSource:
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4609
|
+
var implementSchema = z28.object({
|
|
4610
|
+
summary: z28.string(),
|
|
4611
|
+
ingestCommand: z28.string().optional(),
|
|
4612
|
+
ingestScriptRan: z28.boolean().optional(),
|
|
4613
|
+
ingestRecordCount: z28.number().optional(),
|
|
4614
|
+
ingestDurationMs: z28.number().optional(),
|
|
4615
|
+
ingestionSource: z28.enum(["local", "fileUpload", "generated"]),
|
|
4616
|
+
searchConfig: z28.object({
|
|
4617
|
+
filePath: z28.string().optional(),
|
|
4618
|
+
vars: z28.array(
|
|
4619
|
+
z28.object({
|
|
4620
|
+
name: z28.string(),
|
|
4621
|
+
value: z28.string()
|
|
4622
|
+
})
|
|
4623
|
+
)
|
|
4624
|
+
}).optional()
|
|
4683
4625
|
});
|
|
4684
|
-
var implementationOutputSchema =
|
|
4685
|
-
summary:
|
|
4686
|
-
ingestCommand:
|
|
4626
|
+
var implementationOutputSchema = z28.object({
|
|
4627
|
+
summary: z28.string(),
|
|
4628
|
+
ingestCommand: z28.string().optional(),
|
|
4629
|
+
// Only for the search use case: the path of whatever module the agent
|
|
4630
|
+
// defined the Algolia config constants in, so the wizard can check it
|
|
4631
|
+
// won't end up gitignored (it's public, meant to be committed).
|
|
4632
|
+
searchConfigFile: z28.string().optional()
|
|
4687
4633
|
});
|
|
4688
|
-
var verificationOutputSchema =
|
|
4689
|
-
summary:
|
|
4690
|
-
sufficient:
|
|
4691
|
-
additionalInstructions:
|
|
4634
|
+
var verificationOutputSchema = z28.object({
|
|
4635
|
+
summary: z28.string(),
|
|
4636
|
+
sufficient: z28.boolean(),
|
|
4637
|
+
additionalInstructions: z28.string().optional()
|
|
4692
4638
|
});
|
|
4693
4639
|
var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
|
|
4694
4640
|
var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
|
|
@@ -4702,6 +4648,10 @@ function isJsProject(language) {
|
|
|
4702
4648
|
(name) => JS_LANGUAGES.some((js) => name.includes(js))
|
|
4703
4649
|
);
|
|
4704
4650
|
}
|
|
4651
|
+
var SEARCH_CONFIG_APP_ID = "ALGOLIA_APP_ID";
|
|
4652
|
+
var SEARCH_CONFIG_SEARCH_KEY = "ALGOLIA_SEARCH_API_KEY";
|
|
4653
|
+
var SEARCH_CONFIG_INDEX_NAME = "ALGOLIA_INDEX_NAME";
|
|
4654
|
+
var SEARCH_KEY_PLACEHOLDER = "<your-algolia-search-only-api-key>";
|
|
4705
4655
|
var UI_FRAMEWORKS = [
|
|
4706
4656
|
{ match: ["vue", "nuxt"], target: "Vue", doc: "vue" },
|
|
4707
4657
|
{ match: ["react", "next"], target: "React", doc: "react" },
|
|
@@ -4772,6 +4722,7 @@ function ingestionInstructions(input) {
|
|
|
4772
4722
|
"After a successful ingest, the script must print exactly one line to stdout in the form `ALGOLIA_WIZARD_RECORD_COUNT=<n>`, where <n> is the total number of records pushed to Algolia. Print it last, on its own line, with no surrounding text.",
|
|
4773
4723
|
...algoliaClientDoc(input),
|
|
4774
4724
|
"Install the Algolia client with the project's own package manager via runShell, declaring it in whatever manifest the project uses (e.g. package.json, requirements.txt, Gemfile, go.mod, composer.json) so the dependency is not just installed ad hoc.",
|
|
4725
|
+
`If the script loads its env vars from a file (e.g. via dotenv or an equivalent for its language) rather than the process environment directly, decide that up front and call writeCredentials on that file before you finish the script \u2014 do not wait to discover the need for it by having a writeFile call refused.`,
|
|
4775
4726
|
"When the script is finished, call reviewScript with its path and wait: running it writes records to a live index, so the developer reads it first. Do not run it before that call returns.",
|
|
4776
4727
|
'Then run the script yourself via runShell, and report the command you ran as "ingestCommand" so the developer can re-run it. Its explanation must say that running it writes records to Algolia.',
|
|
4777
4728
|
"The summary should be extremely concise.",
|
|
@@ -4793,17 +4744,14 @@ function searchInstructions(input) {
|
|
|
4793
4744
|
`Create the search experience as its own component in a new file, following the project's existing component conventions (location, naming, styling approach). Do not write it inline into an existing file.`,
|
|
4794
4745
|
`Import and render that new component from ${input.searchLocation ? `"${input.searchLocation}"` : "the best shared, always-rendered layout location (e.g. a header/nav component)"} so it is reachable across the app \u2014 at least a working search input and results list against the target index.`,
|
|
4795
4746
|
"If a search box already exists, replace its usage with an import and render of your new component; remove the old implementation.",
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
`Add Algolia App ID "${input.appId}"; leave the search-only key as a placeholder. Do not create or edit .env \u2014 the wizard writes the resolved key there itself.`,
|
|
4802
|
-
// The wizard writes these exact names into .env right after this step.
|
|
4803
|
-
`Use exactly these env var names in the code: ${input.searchEnvVars.map(({ name }) => name).join(", ")}.`,
|
|
4747
|
+
"When rendering results with an existing shared component (e.g. a card), import and reuse that component rather than inlining its markup \u2014 inlining silently drops the styles and behavior its own file provides.",
|
|
4748
|
+
`Define ${SEARCH_CONFIG_APP_ID}, ${SEARCH_CONFIG_SEARCH_KEY}, and ${SEARCH_CONFIG_INDEX_NAME} as exported constants in a module that fits this project's existing conventions for shared client-side config \u2014 reuse an existing one if it already holds config like this, or add a small new one otherwise. These are PUBLIC values, safe to commit and expose client-side: never read them from an environment variable or a .env* file, and never hardcode them anywhere except in that one module (import them wherever the search client needs them).`,
|
|
4749
|
+
`Set ${SEARCH_CONFIG_APP_ID} to "${input.appId}" and ${SEARCH_CONFIG_INDEX_NAME} to "${input.targetIndex}".`,
|
|
4750
|
+
input.searchKey ? `Set ${SEARCH_CONFIG_SEARCH_KEY} to "${input.searchKey}".` : `A real search-only key could not be provisioned${input.searchKeyError ? ` (${input.searchKeyError})` : ""} \u2014 set ${SEARCH_CONFIG_SEARCH_KEY} to the placeholder "${SEARCH_KEY_PLACEHOLDER}" and add a prominent TODO for the developer to fill in a real one.`,
|
|
4751
|
+
'Report the repo-relative path of that module as "searchConfigFile" in your final status.',
|
|
4804
4752
|
"Install any Algolia packages you import with the project's own package manager via runShell, and declare them in the project's dependency manifest.",
|
|
4805
4753
|
"Match the styles of the application as closely as possible.",
|
|
4806
|
-
"The summary should be extremely concise; do not mention
|
|
4754
|
+
"The summary should be extremely concise; do not mention manual testing steps."
|
|
4807
4755
|
];
|
|
4808
4756
|
}
|
|
4809
4757
|
function verificationInstructions(input) {
|
|
@@ -4934,21 +4882,6 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
|
|
|
4934
4882
|
languages: ctx.getStepOutput("confirm-language")?.languages ?? scan.languages,
|
|
4935
4883
|
frameworks: ctx.getStepOutput("confirm-framework")?.frameworks ?? scan.frameworks
|
|
4936
4884
|
};
|
|
4937
|
-
const normalizeFrameworkName = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
4938
|
-
const confirmedPrimaryFramework = language.frameworks[0]?.name;
|
|
4939
|
-
const frameworkWasCorrected = confirmedPrimaryFramework !== void 0 && !scan.frameworks.some(
|
|
4940
|
-
(fw) => normalizeFrameworkName(fw.name) === normalizeFrameworkName(confirmedPrimaryFramework)
|
|
4941
|
-
);
|
|
4942
|
-
const publicEnvVarPrefixPromise = frameworkWasCorrected ? resolveEnvVarPrefix(confirmedPrimaryFramework).then(
|
|
4943
|
-
(r) => r.publicEnvVarPrefix,
|
|
4944
|
-
(err) => {
|
|
4945
|
-
logger.warn(
|
|
4946
|
-
{ err, framework: confirmedPrimaryFramework },
|
|
4947
|
-
"implement: could not re-resolve publicEnvVarPrefix after a framework correction; using the stale scan value"
|
|
4948
|
-
);
|
|
4949
|
-
return scan.publicEnvVarPrefix;
|
|
4950
|
-
}
|
|
4951
|
-
) : Promise.resolve(scan.publicEnvVarPrefix);
|
|
4952
4885
|
const selected = ctx.getStepOutput(
|
|
4953
4886
|
"select-index"
|
|
4954
4887
|
);
|
|
@@ -5029,49 +4962,42 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES) {
|
|
|
5029
4962
|
);
|
|
5030
4963
|
}
|
|
5031
4964
|
}
|
|
5032
|
-
const
|
|
4965
|
+
const summaries = [];
|
|
4966
|
+
if (uploadWarning) summaries.push(uploadWarning);
|
|
4967
|
+
let searchKey;
|
|
4968
|
+
let searchKeyError;
|
|
4969
|
+
if (useCases.includes("search") && appId) {
|
|
4970
|
+
try {
|
|
4971
|
+
const resolved2 = await resolveSearchOnlyKey(targetIndex, appId);
|
|
4972
|
+
searchKey = resolved2.key;
|
|
4973
|
+
summaries.push(
|
|
4974
|
+
resolved2.source === "created" ? `Created a new search-only Algolia API key for the "${targetIndex}" index in app ${appId} \u2014 safe to expose in frontend code.` : `Reused the existing search-only Algolia API key for the "${targetIndex}" index in app ${appId}.`
|
|
4975
|
+
);
|
|
4976
|
+
} catch (err) {
|
|
4977
|
+
searchKeyError = err.message;
|
|
4978
|
+
summaries.push(
|
|
4979
|
+
`Could not provision a search-only Algolia API key (${searchKeyError}) \u2014 the search agent will scaffold a placeholder with a TODO for you to fill in.`
|
|
4980
|
+
);
|
|
4981
|
+
logger.warn(
|
|
4982
|
+
{ err: searchKeyError },
|
|
4983
|
+
"implement: could not provision a search-only API key; the agent will scaffold a placeholder"
|
|
4984
|
+
);
|
|
4985
|
+
}
|
|
4986
|
+
}
|
|
5033
4987
|
const input = {
|
|
5034
4988
|
findings: normalized,
|
|
5035
4989
|
confirmed: confirmed2,
|
|
5036
4990
|
searchLocation,
|
|
5037
4991
|
targetIndex,
|
|
5038
4992
|
language,
|
|
5039
|
-
publicEnvVarPrefix,
|
|
5040
4993
|
appId,
|
|
5041
|
-
|
|
4994
|
+
searchKey,
|
|
4995
|
+
searchKeyError,
|
|
5042
4996
|
ingestDir: INGEST_DIR,
|
|
5043
4997
|
ingestionSource,
|
|
5044
4998
|
uploadFilePath,
|
|
5045
4999
|
searchUiTarget: searchUiTarget(language)
|
|
5046
5000
|
};
|
|
5047
|
-
const summaries = [];
|
|
5048
|
-
if (uploadWarning) summaries.push(uploadWarning);
|
|
5049
|
-
let envSearchKey;
|
|
5050
|
-
let envAppIdMismatch = false;
|
|
5051
|
-
if (useCases.includes("search") && appId) {
|
|
5052
|
-
const envAppId = await readEnvVar(
|
|
5053
|
-
repoRoot,
|
|
5054
|
-
publicAppIdVar(publicEnvVarPrefix)
|
|
5055
|
-
);
|
|
5056
|
-
if (envAppId === appId) {
|
|
5057
|
-
envSearchKey = await readEnvVar(
|
|
5058
|
-
repoRoot,
|
|
5059
|
-
publicSearchKeyVar(publicEnvVarPrefix)
|
|
5060
|
-
);
|
|
5061
|
-
} else if (envAppId) {
|
|
5062
|
-
envAppIdMismatch = true;
|
|
5063
|
-
const appIdVarName = publicAppIdVar(publicEnvVarPrefix);
|
|
5064
|
-
const searchKeyVarName = publicSearchKeyVar(publicEnvVarPrefix);
|
|
5065
|
-
summaries.push(
|
|
5066
|
-
`\u26A0\uFE0F .env already sets ${appIdVarName}=${envAppId}, but the active Algolia application is ${appId}. The wizard left those values alone \u2014 update ${appIdVarName} and ${searchKeyVarName} by hand, or searches will fail.`
|
|
5067
|
-
);
|
|
5068
|
-
logger.warn(
|
|
5069
|
-
{ envAppId, appId },
|
|
5070
|
-
"implement: .env holds credentials for a different Algolia application; not reusing its search key"
|
|
5071
|
-
);
|
|
5072
|
-
}
|
|
5073
|
-
}
|
|
5074
|
-
let finalSearchEnvVars = input.searchEnvVars;
|
|
5075
5001
|
let agentRuns = 0;
|
|
5076
5002
|
let ingestCommand;
|
|
5077
5003
|
let ingestScriptRan = false;
|
|
@@ -5181,6 +5107,7 @@ ${detail}` : ""}`
|
|
|
5181
5107
|
]
|
|
5182
5108
|
});
|
|
5183
5109
|
}
|
|
5110
|
+
let searchConfigFile;
|
|
5184
5111
|
if (useCases.includes("search")) {
|
|
5185
5112
|
let extraInstructions = [];
|
|
5186
5113
|
useWizard.getState().clearWrittenFiles();
|
|
@@ -5195,11 +5122,14 @@ ${detail}` : ""}`
|
|
|
5195
5122
|
"implement: retrying search implementation after failed verification"
|
|
5196
5123
|
);
|
|
5197
5124
|
}
|
|
5198
|
-
const
|
|
5125
|
+
const searchResult = await runImplementationUseCase(
|
|
5199
5126
|
"search",
|
|
5200
5127
|
extraInstructions
|
|
5201
5128
|
);
|
|
5202
|
-
summaries.push(formatSummary("search", summary));
|
|
5129
|
+
summaries.push(formatSummary("search", searchResult.summary));
|
|
5130
|
+
if (searchResult.searchConfigFile) {
|
|
5131
|
+
searchConfigFile = searchResult.searchConfigFile;
|
|
5132
|
+
}
|
|
5203
5133
|
const verification = await runVerificationUseCase();
|
|
5204
5134
|
summaries.push(formatSummary("verification", verification.summary));
|
|
5205
5135
|
if (verification.sufficient) {
|
|
@@ -5223,76 +5153,17 @@ ${detail}` : ""}`
|
|
|
5223
5153
|
}
|
|
5224
5154
|
extraInstructions = verificationRetryInstructions(verification);
|
|
5225
5155
|
}
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
if (appId) {
|
|
5229
|
-
try {
|
|
5230
|
-
const resolved2 = await resolveSearchOnlyKey(
|
|
5231
|
-
targetIndex,
|
|
5232
|
-
appId,
|
|
5233
|
-
envSearchKey
|
|
5234
|
-
);
|
|
5235
|
-
searchKey = resolved2.key;
|
|
5236
|
-
summaries.push(
|
|
5237
|
-
resolved2.source === "created" ? `Created a new search-only Algolia API key for the "${targetIndex}" index in app ${appId} \u2014 safe to expose in frontend code.` : `Reused the existing search-only Algolia API key for the "${targetIndex}" index in app ${appId}.`
|
|
5238
|
-
);
|
|
5239
|
-
} catch (err) {
|
|
5240
|
-
searchKeyError = err.message;
|
|
5241
|
-
logger.warn(
|
|
5242
|
-
{ err: searchKeyError },
|
|
5243
|
-
"implement: could not provision a search-only API key; the .env value stays a placeholder"
|
|
5244
|
-
);
|
|
5245
|
-
}
|
|
5246
|
-
}
|
|
5247
|
-
finalSearchEnvVars = publicSearchEnvVars(
|
|
5248
|
-
publicEnvVarPrefix,
|
|
5249
|
-
targetIndex,
|
|
5250
|
-
appId,
|
|
5251
|
-
searchKey
|
|
5252
|
-
);
|
|
5253
|
-
const resolvedSearchEnvVars = finalSearchEnvVars.filter(
|
|
5254
|
-
(v) => !v.value.startsWith("<")
|
|
5255
|
-
);
|
|
5256
|
-
if (resolvedSearchEnvVars.length > 0) {
|
|
5257
|
-
const written = await writeSearchEnvValues(
|
|
5156
|
+
if (searchConfigFile) {
|
|
5157
|
+
const ignoreStatus = await gitIgnoreStatus(
|
|
5258
5158
|
repoRoot,
|
|
5259
|
-
|
|
5159
|
+
join12(repoRoot, searchConfigFile)
|
|
5260
5160
|
);
|
|
5261
|
-
if (
|
|
5262
|
-
summaries.push(`Wrote ${written.join(", ")} to .env.`);
|
|
5263
|
-
}
|
|
5264
|
-
const ignored = await ensureGitIgnored(repoRoot, join12(repoRoot, ".env"));
|
|
5265
|
-
if (ignored === "added") {
|
|
5266
|
-
summaries.push("Added .env to .gitignore.");
|
|
5267
|
-
} else if (ignored === "tracked") {
|
|
5268
|
-
summaries.push(
|
|
5269
|
-
'\u26A0\uFE0F .env is tracked by git, so a .gitignore rule cannot un-stage it. Run "git rm --cached .env" before committing, or the credentials go into history.'
|
|
5270
|
-
);
|
|
5271
|
-
}
|
|
5272
|
-
const stale = [];
|
|
5273
|
-
for (const v of resolvedSearchEnvVars) {
|
|
5274
|
-
if (written.includes(v.name)) continue;
|
|
5275
|
-
const current = await readEnvVar(repoRoot, v.name);
|
|
5276
|
-
if (current && current !== v.value) stale.push(v);
|
|
5277
|
-
}
|
|
5278
|
-
if (stale.length > 0 && !envAppIdMismatch) {
|
|
5161
|
+
if (ignoreStatus === "covered") {
|
|
5279
5162
|
summaries.push(
|
|
5280
|
-
`\u26A0\uFE0F
|
|
5281
|
-
);
|
|
5282
|
-
logger.warn(
|
|
5283
|
-
{ vars: stale.map((v) => v.name) },
|
|
5284
|
-
"implement: .env holds different values for the resolved search credentials; not overwriting them"
|
|
5163
|
+
`\u26A0\uFE0F ${searchConfigFile} is gitignored, so this public, safe-to-share search config won't reach teammates or CI. Remove whatever .gitignore rule covers it.`
|
|
5285
5164
|
);
|
|
5286
5165
|
}
|
|
5287
5166
|
}
|
|
5288
|
-
const unresolvedSearchEnvVars = finalSearchEnvVars.filter(
|
|
5289
|
-
(v) => v.value.startsWith("<")
|
|
5290
|
-
);
|
|
5291
|
-
if (unresolvedSearchEnvVars.length > 0) {
|
|
5292
|
-
summaries.push(
|
|
5293
|
-
`Could not resolve a value for ${unresolvedSearchEnvVars.map((v) => v.name).join(", ")} \u2014 fill it in manually in .env.` + (searchKeyError ? ` Reason: ${searchKeyError}` : "")
|
|
5294
|
-
);
|
|
5295
|
-
}
|
|
5296
5167
|
} else {
|
|
5297
5168
|
ctx.setUserInput("implementation", "success");
|
|
5298
5169
|
}
|
|
@@ -5305,7 +5176,19 @@ ${detail}` : ""}`
|
|
|
5305
5176
|
...ingestRecordCount != null ? { ingestRecordCount } : {},
|
|
5306
5177
|
...ingestDurationMs != null ? { ingestDurationMs } : {}
|
|
5307
5178
|
} : {},
|
|
5308
|
-
...useCases.includes("search") ? {
|
|
5179
|
+
...useCases.includes("search") ? {
|
|
5180
|
+
searchConfig: {
|
|
5181
|
+
filePath: searchConfigFile,
|
|
5182
|
+
vars: [
|
|
5183
|
+
{ name: SEARCH_CONFIG_APP_ID, value: appId ?? "" },
|
|
5184
|
+
{
|
|
5185
|
+
name: SEARCH_CONFIG_SEARCH_KEY,
|
|
5186
|
+
value: searchKey ?? SEARCH_KEY_PLACEHOLDER
|
|
5187
|
+
},
|
|
5188
|
+
{ name: SEARCH_CONFIG_INDEX_NAME, value: targetIndex }
|
|
5189
|
+
]
|
|
5190
|
+
}
|
|
5191
|
+
} : {}
|
|
5309
5192
|
};
|
|
5310
5193
|
}
|
|
5311
5194
|
|
|
@@ -5345,8 +5228,8 @@ var defaultWorkflow = {
|
|
|
5345
5228
|
defineStep({
|
|
5346
5229
|
id: "select-index",
|
|
5347
5230
|
title: "Set up index",
|
|
5348
|
-
outputSchema:
|
|
5349
|
-
selection:
|
|
5231
|
+
outputSchema: z29.object({
|
|
5232
|
+
selection: z29.string()
|
|
5350
5233
|
}),
|
|
5351
5234
|
run: (ctx) => selectIndexStep(ctx)
|
|
5352
5235
|
}),
|
|
@@ -5454,10 +5337,14 @@ var confirmFramework2 = {
|
|
|
5454
5337
|
var search = {
|
|
5455
5338
|
summary: "Added an InstantSearch-powered search box and results list, mounted in the shared header component.",
|
|
5456
5339
|
ingestionSource: "generated",
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5340
|
+
searchConfig: {
|
|
5341
|
+
filePath: "src/algolia.config.ts",
|
|
5342
|
+
vars: [
|
|
5343
|
+
{ name: "ALGOLIA_APP_ID", value: "SEEDAPPID" },
|
|
5344
|
+
{ name: "ALGOLIA_SEARCH_API_KEY", value: "seedsearchkey" },
|
|
5345
|
+
{ name: "ALGOLIA_INDEX_NAME", value: "wizard_seed_products" }
|
|
5346
|
+
]
|
|
5347
|
+
}
|
|
5461
5348
|
};
|
|
5462
5349
|
var review = {
|
|
5463
5350
|
summaryPoints: [
|
|
@@ -4,13 +4,18 @@ Install `instantsearch.js` (v4)
|
|
|
4
4
|
|
|
5
5
|
## Search client: always use the lite client
|
|
6
6
|
|
|
7
|
-
Import the client from `algoliasearch/lite` and alias it to `algoliasearch
|
|
7
|
+
Import the client from `algoliasearch/lite` and alias it to `algoliasearch`.
|
|
8
|
+
`ALGOLIA_APP_ID`, `ALGOLIA_SEARCH_API_KEY`, and `ALGOLIA_INDEX_NAME` below are
|
|
9
|
+
the constants exported from the project's shared client-side config module —
|
|
10
|
+
these are public values, safe to commit; never hardcode them inline or read
|
|
11
|
+
them from an env var.
|
|
8
12
|
|
|
9
13
|
```ts
|
|
10
14
|
import { liteClient as algoliasearch } from 'algoliasearch/lite'
|
|
15
|
+
import { ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY } from '<the project's shared config module>'
|
|
11
16
|
|
|
12
17
|
// Instantiate ONCE, outside components, with a stable reference.
|
|
13
|
-
const searchClient = algoliasearch(
|
|
18
|
+
const searchClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY)
|
|
14
19
|
```
|
|
15
20
|
|
|
16
21
|
## Vanilla (`instantsearch.js` v4)
|
|
@@ -19,11 +24,16 @@ const searchClient = algoliasearch(APP_ID, SEARCH_ONLY_KEY)
|
|
|
19
24
|
import { liteClient as algoliasearch } from 'algoliasearch/lite'
|
|
20
25
|
import instantsearch from 'instantsearch.js'
|
|
21
26
|
import { searchBox, hits } from 'instantsearch.js/es/widgets'
|
|
27
|
+
import {
|
|
28
|
+
ALGOLIA_APP_ID,
|
|
29
|
+
ALGOLIA_SEARCH_API_KEY,
|
|
30
|
+
ALGOLIA_INDEX_NAME,
|
|
31
|
+
} from '<the project's shared config module>'
|
|
22
32
|
|
|
23
|
-
const searchClient = algoliasearch(
|
|
33
|
+
const searchClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY)
|
|
24
34
|
|
|
25
35
|
const search = instantsearch({
|
|
26
|
-
indexName:
|
|
36
|
+
indexName: ALGOLIA_INDEX_NAME,
|
|
27
37
|
searchClient,
|
|
28
38
|
insights: true,
|
|
29
39
|
})
|
|
@@ -53,7 +63,7 @@ case, install `search-insights` from npm and pass its default export as
|
|
|
53
63
|
import aa from 'search-insights'
|
|
54
64
|
|
|
55
65
|
const search = instantsearch({
|
|
56
|
-
indexName:
|
|
66
|
+
indexName: ALGOLIA_INDEX_NAME,
|
|
57
67
|
searchClient,
|
|
58
68
|
insights: { insightsClient: aa },
|
|
59
69
|
})
|
|
@@ -4,13 +4,18 @@ Install `react-instantsearch` (v7)
|
|
|
4
4
|
|
|
5
5
|
## Search client: always use the lite client
|
|
6
6
|
|
|
7
|
-
Import the client from `algoliasearch/lite` and alias it to `algoliasearch
|
|
7
|
+
Import the client from `algoliasearch/lite` and alias it to `algoliasearch`.
|
|
8
|
+
`ALGOLIA_APP_ID`, `ALGOLIA_SEARCH_API_KEY`, and `ALGOLIA_INDEX_NAME` below are
|
|
9
|
+
the constants exported from the project's shared client-side config module —
|
|
10
|
+
these are public values, safe to commit; never hardcode them inline or read
|
|
11
|
+
them from an env var.
|
|
8
12
|
|
|
9
13
|
```ts
|
|
10
14
|
import { liteClient as algoliasearch } from 'algoliasearch/lite'
|
|
15
|
+
import { ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY } from '<the project's shared config module>'
|
|
11
16
|
|
|
12
17
|
// Instantiate ONCE, outside components, with a stable reference.
|
|
13
|
-
const searchClient = algoliasearch(
|
|
18
|
+
const searchClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY)
|
|
14
19
|
```
|
|
15
20
|
|
|
16
21
|
## React (`react-instantsearch` v7)
|
|
@@ -18,19 +23,19 @@ const searchClient = algoliasearch(APP_ID, SEARCH_ONLY_KEY)
|
|
|
18
23
|
```tsx
|
|
19
24
|
import { liteClient as algoliasearch } from 'algoliasearch/lite'
|
|
20
25
|
import { InstantSearch, SearchBox, Hits } from 'react-instantsearch'
|
|
26
|
+
import {
|
|
27
|
+
ALGOLIA_APP_ID,
|
|
28
|
+
ALGOLIA_SEARCH_API_KEY,
|
|
29
|
+
ALGOLIA_INDEX_NAME,
|
|
30
|
+
} from '<the project's shared config module>'
|
|
21
31
|
|
|
22
|
-
|
|
23
|
-
// convention: NEXT_PUBLIC_* for Next.js, etc.)
|
|
24
|
-
const searchClient = algoliasearch(
|
|
25
|
-
import.meta.env.VITE_ALGOLIA_APP_ID,
|
|
26
|
-
import.meta.env.VITE_ALGOLIA_SEARCH_API_KEY,
|
|
27
|
-
)
|
|
32
|
+
const searchClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY)
|
|
28
33
|
|
|
29
34
|
export function Search() {
|
|
30
35
|
return (
|
|
31
36
|
<InstantSearch
|
|
32
37
|
searchClient={searchClient}
|
|
33
|
-
indexName=
|
|
38
|
+
indexName={ALGOLIA_INDEX_NAME}
|
|
34
39
|
insights={true}
|
|
35
40
|
>
|
|
36
41
|
<SearchBox />
|
|
@@ -4,13 +4,18 @@ Install `vue-instantsearch` (v4)
|
|
|
4
4
|
|
|
5
5
|
## Search client: always use the lite client
|
|
6
6
|
|
|
7
|
-
Import the client from `algoliasearch/lite` and alias it to `algoliasearch
|
|
7
|
+
Import the client from `algoliasearch/lite` and alias it to `algoliasearch`.
|
|
8
|
+
`ALGOLIA_APP_ID`, `ALGOLIA_SEARCH_API_KEY`, and `ALGOLIA_INDEX_NAME` below are
|
|
9
|
+
the constants exported from the project's shared client-side config module —
|
|
10
|
+
these are public values, safe to commit; never hardcode them inline or read
|
|
11
|
+
them from an env var.
|
|
8
12
|
|
|
9
13
|
```ts
|
|
10
14
|
import { liteClient as algoliasearch } from 'algoliasearch/lite'
|
|
15
|
+
import { ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY } from '<the project's shared config module>'
|
|
11
16
|
|
|
12
17
|
// Instantiate ONCE, outside components, with a stable reference.
|
|
13
|
-
const searchClient = algoliasearch(
|
|
18
|
+
const searchClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY)
|
|
14
19
|
```
|
|
15
20
|
|
|
16
21
|
## Vue (`vue-instantsearch` v4)
|
|
@@ -19,7 +24,7 @@ const searchClient = algoliasearch(APP_ID, SEARCH_ONLY_KEY)
|
|
|
19
24
|
<template>
|
|
20
25
|
<ais-instant-search
|
|
21
26
|
:search-client="searchClient"
|
|
22
|
-
index-name="
|
|
27
|
+
:index-name="ALGOLIA_INDEX_NAME"
|
|
23
28
|
:insights="true"
|
|
24
29
|
>
|
|
25
30
|
<ais-search-box />
|
|
@@ -29,11 +34,13 @@ const searchClient = algoliasearch(APP_ID, SEARCH_ONLY_KEY)
|
|
|
29
34
|
|
|
30
35
|
<script setup>
|
|
31
36
|
import { liteClient as algoliasearch } from 'algoliasearch/lite'
|
|
37
|
+
import {
|
|
38
|
+
ALGOLIA_APP_ID,
|
|
39
|
+
ALGOLIA_SEARCH_API_KEY,
|
|
40
|
+
ALGOLIA_INDEX_NAME,
|
|
41
|
+
} from '<the project's shared config module>'
|
|
32
42
|
|
|
33
|
-
const searchClient = algoliasearch(
|
|
34
|
-
import.meta.env.VITE_ALGOLIA_APP_ID,
|
|
35
|
-
import.meta.env.VITE_ALGOLIA_SEARCH_API_KEY,
|
|
36
|
-
)
|
|
43
|
+
const searchClient = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY)
|
|
37
44
|
</script>
|
|
38
45
|
```
|
|
39
46
|
|
|
@@ -5,10 +5,19 @@ route, or programmatic queries). For UI, prefer `instantsearch-setup-<framework>
|
|
|
5
5
|
|
|
6
6
|
## Client
|
|
7
7
|
|
|
8
|
+
`ALGOLIA_APP_ID`, `ALGOLIA_SEARCH_API_KEY`, and `ALGOLIA_INDEX_NAME` below are
|
|
9
|
+
the constants exported from the project's shared client-side config module —
|
|
10
|
+
these are public values, safe to commit; never hardcode them inline or read
|
|
11
|
+
them from an env var.
|
|
12
|
+
|
|
8
13
|
```ts
|
|
9
14
|
import { algoliasearch } from 'algoliasearch'
|
|
15
|
+
import {
|
|
16
|
+
ALGOLIA_APP_ID,
|
|
17
|
+
ALGOLIA_SEARCH_API_KEY,
|
|
18
|
+
} from '<the project's shared config module>'
|
|
10
19
|
|
|
11
|
-
const client = algoliasearch(
|
|
20
|
+
const client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_API_KEY)
|
|
12
21
|
```
|
|
13
22
|
|
|
14
23
|
In v5 there is no `client.initIndex(...)`. Index methods take the index name as a
|
|
@@ -18,7 +27,7 @@ parameter on the client. Every method takes a single options object.
|
|
|
18
27
|
|
|
19
28
|
```ts
|
|
20
29
|
const { hits, nbHits } = await client.searchSingleIndex({
|
|
21
|
-
indexName:
|
|
30
|
+
indexName: ALGOLIA_INDEX_NAME,
|
|
22
31
|
searchParams: { query: 'shoes', hitsPerPage: 20, page: 0 },
|
|
23
32
|
})
|
|
24
33
|
```
|
|
@@ -28,7 +37,7 @@ const { hits, nbHits } = await client.searchSingleIndex({
|
|
|
28
37
|
```ts
|
|
29
38
|
const { results } = await client.search({
|
|
30
39
|
requests: [
|
|
31
|
-
{ indexName:
|
|
40
|
+
{ indexName: ALGOLIA_INDEX_NAME, query: 'shoes' },
|
|
32
41
|
{ indexName: 'OTHER_INDEX', query: 'shoes' },
|
|
33
42
|
],
|
|
34
43
|
})
|