quallaa-cli
Version:
Sets up core infrastructure services for AI-assisted development
266 lines ⢠9.83 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultSchemas = void 0;
exports.setupTypesense = setupTypesense;
exports.createTypesenseCollection = createTypesenseCollection;
const chalk_1 = __importDefault(require("chalk"));
const inquirer_1 = __importDefault(require("inquirer"));
const axios_1 = __importDefault(require("axios"));
const credentials_1 = require("../storage/credentials");
async function setupTypesense() {
try {
console.log(chalk_1.default.gray('\nTypesense provides fast, typo-tolerant search.'));
console.log(chalk_1.default.gray('This is optional but recommended for content-heavy applications.\n'));
const existing = await (0, credentials_1.getCredentials)('typesense');
if (existing) {
const { useExisting } = await inquirer_1.default.prompt([
{
type: 'confirm',
name: 'useExisting',
message: 'Existing Typesense credentials found. Use them?',
default: true,
},
]);
if (useExisting) {
return { success: true, service: 'typesense', message: 'Using existing credentials' };
}
}
const { setupChoice } = await inquirer_1.default.prompt([
{
type: 'list',
name: 'setupChoice',
message: 'How would you like to set up Typesense?',
choices: [
{ name: 'Typesense Cloud (recommended)', value: 'cloud' },
{ name: 'Self-hosted instance', value: 'self-hosted' },
{ name: 'Skip Typesense setup', value: 'skip' },
],
},
]);
if (setupChoice === 'skip') {
return {
success: true,
service: 'typesense',
message: 'Skipped Typesense setup',
};
}
let credentials;
if (setupChoice === 'cloud') {
credentials = await setupTypesenseCloud();
}
else {
credentials = await setupSelfHosted();
}
await (0, credentials_1.saveCredentials)('typesense', credentials);
await addToEnvFile(credentials);
return {
success: true,
service: 'typesense',
credentials: { typesense: credentials },
};
}
catch (error) {
return {
success: false,
service: 'typesense',
message: error instanceof Error ? error.message : 'Setup failed',
error: error,
};
}
}
async function setupTypesenseCloud() {
console.log(chalk_1.default.yellow('\nāļø Typesense Cloud Setup'));
console.log(chalk_1.default.gray('Note: Typesense Cloud requires manual setup through their website.'));
console.log(chalk_1.default.gray('1. Go to: https://cloud.typesense.org/'));
console.log(chalk_1.default.gray('2. Create a cluster'));
console.log(chalk_1.default.gray('3. Get your connection details\n'));
const { host, port, protocol, apiKey } = await inquirer_1.default.prompt([
{
type: 'input',
name: 'host',
message: 'Cluster hostname (e.g., xxx-1.a1.typesense.net):',
validate: (input) => input.length > 0 || 'Hostname is required',
},
{
type: 'input',
name: 'port',
message: 'Port:',
default: '443',
},
{
type: 'list',
name: 'protocol',
message: 'Protocol:',
choices: ['https', 'http'],
default: 'https',
},
{
type: 'password',
name: 'apiKey',
message: 'Admin API Key:',
validate: (input) => input.length > 0 || 'API key is required',
},
]);
await testConnection({ host, port, protocol, apiKey });
return { host, port, protocol, apiKey };
}
async function setupSelfHosted() {
console.log(chalk_1.default.yellow('\nš Self-hosted Typesense Setup'));
const { host, port, protocol, apiKey } = await inquirer_1.default.prompt([
{
type: 'input',
name: 'host',
message: 'Typesense host:',
default: 'localhost',
},
{
type: 'input',
name: 'port',
message: 'Port:',
default: '8108',
},
{
type: 'list',
name: 'protocol',
message: 'Protocol:',
choices: ['http', 'https'],
default: 'http',
},
{
type: 'input',
name: 'apiKey',
message: 'Admin API Key (from your Typesense config):',
validate: (input) => input.length > 0 || 'API key is required',
},
]);
await testConnection({ host, port, protocol, apiKey });
return { host, port, protocol, apiKey };
}
async function testConnection(config) {
try {
console.log(chalk_1.default.gray('Testing connection...'));
const response = await axios_1.default.get(`${config.protocol}://${config.host}:${config.port}/health`, {
headers: {
'X-TYPESENSE-API-KEY': config.apiKey,
},
timeout: 10000,
});
if (response.data.ok === true) {
console.log(chalk_1.default.green('ā Connection successful'));
}
else {
throw new Error('Health check failed');
}
}
catch (error) {
console.error(chalk_1.default.red('ā Connection failed'));
throw new Error('Could not connect to Typesense. Please check your settings.');
}
}
async function addToEnvFile(credentials) {
const envContent = `
# Typesense
TYPESENSE_HOST=${credentials.host}
TYPESENSE_PORT=${credentials.port}
TYPESENSE_PROTOCOL=${credentials.protocol}
TYPESENSE_API_KEY=${credentials.apiKey}
`;
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
try {
await fs.access('.env.local');
await fs.appendFile('.env.local', envContent);
}
catch {
await fs.writeFile('.env.local', envContent);
}
console.log(chalk_1.default.green('ā Environment variables added to .env.local'));
}
async function createTypesenseCollection(collectionName, schema) {
const credentials = await (0, credentials_1.getCredentials)('typesense');
if (!credentials) {
throw new Error('Typesense not configured. Run "quallaa setup typesense" first.');
}
const url = `${credentials.protocol}://${credentials.host}:${credentials.port}/collections`;
const response = await axios_1.default.post(url, {
name: collectionName,
...schema,
}, {
headers: {
'X-TYPESENSE-API-KEY': credentials.apiKey,
'Content-Type': 'application/json',
},
});
if (response.status === 201) {
console.log(chalk_1.default.green(`ā Collection '${collectionName}' created`));
}
}
exports.defaultSchemas = {
contacts: {
fields: [
{ name: 'name', type: 'string' },
{ name: 'email', type: 'string' },
{ name: 'company', type: 'string', optional: true },
{ name: 'role', type: 'string', optional: true },
{ name: 'created_at', type: 'int64' },
],
default_sorting_field: 'created_at',
},
posts: {
fields: [
{ name: 'title', type: 'string' },
{ name: 'content', type: 'string' },
{ name: 'author', type: 'string' },
{ name: 'tags', type: 'string[]', optional: true },
{ name: 'published_at', type: 'int64' },
],
default_sorting_field: 'published_at',
},
products: {
fields: [
{ name: 'name', type: 'string' },
{ name: 'description', type: 'string' },
{ name: 'price', type: 'float' },
{ name: 'category', type: 'string' },
{ name: 'tags', type: 'string[]', optional: true },
{ name: 'created_at', type: 'int64' },
],
default_sorting_field: 'created_at',
},
};
//# sourceMappingURL=typesense.js.map