Skip to main content
Deno 2 is finally here πŸŽ‰οΈ
Learn more

Tuner

deno.land/x/tuner

Tuner - ΠΌΠΎΠ΄ΡƒΠ»ΡŒ для управлСния конфигурациями ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°. Π”Π°Π½Π½Ρ‹Π΅ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ ΠΎΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‚ΡΡ Π² Π²ΠΈΠ΄Π΅ .ts Ρ„Π°ΠΉΠ»Π°, содСрТащСго ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с полями ΠΈ значСниями. Π€Π°ΠΉΠ»Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒΡΡ ΠΊΠ°ΠΊ Π² самом ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅(local Ρ„Π°ΠΉΠ»Ρ‹), Ρ‚Π°ΠΊ ΠΈ ΡƒΠ΄Π°Π»Π΅Π½Π½ΠΎ (remote). ΠŸΡ€ΠΈΡ‚ΠΎΠΌ доступ ΠΊ ΡƒΠ΄Π°Π»Π΅Π½Π½Ρ‹ΠΌ Ρ„Π°ΠΉΠ»Π°ΠΌ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΡ‚ΡŒ нСпосрСдствСнно пСрСдавая ΠΏΡƒΡ‚ΡŒ, Π»ΠΈΠ±ΠΎ ΠΏΡ€ΠΎΠΏΠΈΡΠ°Ρ‚ΡŒ колбэк-Ρ„ΡƒΠΊΠ½Ρ†ΠΈΡŽ, которая ΠΈΠ·Π²Π»Π΅ΠΊΠ°Π΅Ρ‚ Π½ΡƒΠΆΠ½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅.

ЀактичСски, ΠΌΠΎΠ΄ΡƒΠ»ΡŒ прСдоставляСт класс для ΠΌΠ΅Π½Π΅Π΄ΠΆΠΌΠ΅Π½Ρ‚Π° Ρ„Π°ΠΉΠ»ΠΎΠ² ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° ΠΈ нСсколько Ρ„ΡƒΠΊΠ½Ρ†ΠΈΠΉ, ΠΎΠ±Π½ΠΎΠ²Π»ΡΡŽΡ‰ΠΈΠ΅ схСмы ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° Π² Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ для ΡƒΠ΄ΠΎΠ±Π½ΠΎΠΉ ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ.


ОглавлСниС

sprout ГСнСрация ΠΈ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ схСмы

Для Π±ΠΎΠ»Π΅Π΅ ΠΊΠΎΠΌΡ„ΠΎΡ€Ρ‚ΠΎΠ³ΠΎ обращСния с ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠ΅ΠΉ интСрфСйсы ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² с Π΄Π°Π½Π½Ρ‹ΠΌΠΈ ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Ρ‹. ΠšΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ, ΠΏΡ€ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈ ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° схСма Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ автоматичСски пСрСписана.

ΠŸΡƒΡΡ‚ΡŒ Π² ΠΏΠ°ΠΏΠΊΠ΅ config имССтся Ρ„Π°ΠΉΠ» localConfig.ts

Π’Ρ€Π΅Π±ΠΎΠ²Π°Π½ΠΈΠΉ ΠΊ названию Ρ„Π°ΠΉΠ»ΠΎΠ² ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° Π½Π΅Ρ‚.

Π‘Π°ΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ обязан ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΏΠΎΠ»Π΅ secrets с ΡƒΠΊΠ°Π·Π°Π½ΠΈΠ΅ΠΌ Π½Π°Π·Π²Π°Π½ΠΈΠΉ сСкрСтных ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ….

secrets : {name: string}[]

// config/localConfig.ts
const localSupabase = {
  name: 'local',
  secrets: [
    {
      name: 'API_KEY',
    },
    {
      name: 'URL',
    },
  ],
  timeoutToUpdate: 200,
  mainTable: 'Wallets',
  isSubscribtionOn: true,
};

export default localSupabase;

Для Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ схСмы ΠΈ Ρ‚ΠΈΠΏΠ° этого ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°, Π° Ρ‚Π°ΠΊΠΆΠ΅ отслСТивания ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ этого Ρ„Π°ΠΉΠ»Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ watchConfigFiles:

// config/watcher.ts
import { watchConfigFiles } from 'https://deno.land/x/tuner/mod.ts';

const configFilePaths: ConfigFilePaths = {
  filePaths: [
    'config/localConfig.ts',
    'config/prodConfig.ts',
  ],
  configType: 'supabaseConfig',
};

await watchConfigFiles(configFilePaths);

ΠŸΡ€ΠΈ запускС deno run –allow-all config/watcher.ts ΠΊΠ°ΠΆΠ΄ΠΎΠ΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΠ΅ любого ΠΈΠ· отслСТиваСмых Ρ„Π°ΠΉΠ»ΠΎΠ² измСняСт схСму ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°(ΠΈΠ»ΠΈ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ Π΅Π΅ ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π²ΠΎΠΌ запускС/отсутствии Ρ„Π°ΠΉΠ»Π° схСмы)

Π€Π°ΠΉΠ» схСмы ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° располоТСн Π² Ρ‚ΠΎΠΉ ΠΆΠ΅ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ с Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ ${configType}Schema.ts

Π’Ρ‹Π²Π΅Π΄Π΅Π½Π½Ρ‹ΠΉ Ρ‚ΠΈΠΏ ΠΈ Π΅Π³ΠΎ ΠΈΠΌΠΏΠΎΡ€Ρ‚ автоматичСски Π²ΠΏΠΈΡˆΠ΅Ρ‚ΡΡ Π² config/localConfig.ts, …

hammer Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ€Π° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΠΎΠ²

// config/manager.ts
import {
  SupabaseConfig,
  SupabaseConfigSchema,
} from '../config/supabaseConfigSchema.ts';
import {
  ConfigManager,
  getNotionConfig,
} from 'https://deno.land/x/tuner/mod.ts';

const manager = new ConfigManager<
  SupabaseConfig,
  typeof SupabaseConfigSchema
>(
  SupabaseConfigSchema,
);

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΡƒΠ΄Π°Π»Π΅Π½Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°
manager.addRemoteConfigUrl(
  'https://raw.githubusercontent.com/artpani4/configTest/main/configTest.ts',
);

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΡƒΠ΄Π°Π»Π΅Π½Π½Ρ‹Ρ… ΠΊΠΎΠ½Ρ„ΠΈΠ³ΠΎΠ²
manager.addRemoteConfigUrls([
  'http://example.com/supabaseConfigRu.ts',
  'http://example.com/supabaseConfigEn.ts',
]);

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ локального ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°
manager.addLocalConfigUrl('config/localConfig.ts');

// Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π΅ΡΠΎΠΊΠ»ΡŒΠΊΠΈΡ… Π»ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹Ρ… ΠΊΠΎΠ½Ρ„ΠΈΠ³ΠΎΠ²
manager.addLocalConfigUrls([
  'config/localConfig.ts',
  'config/prodConfig.ts',
]);

// ΠŸΠ΅Ρ€Π΅Π΄Π°Π΅ΠΌ callback, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π΅Ρ‚ строку с ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ
// Π’ Π΄Π°Π½Π½ΠΎΠΌ случаС getNotionConfig(key: string, blockId: string): Promise<string>
manager.addRemoteProSource(async () => {
  return await getNotionConfig(
    getSecret('NOTION_KEY'),
    'blockID',
  );
});

// Если ΠΏΡ€ΠΎΠΈΠΉΠ·ΠΎΠ΄Π΅Ρ‚ какая-Ρ‚ΠΎ ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³Ρ€ΡƒΠ·ΠΊΠ΅ ΠΊΠ°ΠΊΠΎΠ³ΠΎ-Ρ‚ΠΎ ΠΈΠ· ΠΊΠΎΠ½Ρ„ΠΈΠ³ΠΎΠ², Ρ‚ΠΎ Π΄Π°Π½Π½Ρ‹ΠΉ ΠΊΠΎΠ½Ρ„ΠΈΠ³ загрузится ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ
// Если ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π²ΠΎΠ·Π½ΠΈΠΊΠ½Π΅Ρ‚ ΠΈ с Π½ΠΈΠΌ(ΠΈΠ»ΠΈ ΠΆΠ΅ ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ Π½Π΅Ρ‚, Π° Ρ†Π΅Π»Π΅Π²ΠΎΠΉ ΠΊΠΎΠ½Ρ„ΠΈΠ³ нСдоступСн) ΡΡ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅.
await manager.setMainConfig('config/localConfig.ts', 'local');
export default manager;

ΠšΠΎΠ½Ρ„ΠΈΠ³ΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ΡΡ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ .addRemoteConfigUrl ΠΈ .addRemoteConfigUrls ΠΏΡ€ΠΎΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‚ΡΡ Ρ‚Π°ΠΊΠΈΠΌ ΠΆΠ΅ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, ΠΊΠ°ΠΊ ΠΈ Π»ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹Π΅ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΠΈ

ΠšΠΎΠ½Ρ„ΠΈΠ³ΠΈ, ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌΡ‹Π΅ Ρ‡Π΅Ρ€Π΅Π· .addRemoteProSource ΠΎΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‚ΡΡ Π±Π΅Π· const …, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ сам ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΊΠΎΠ½Ρ„ΠΈΠ³Π°.

// ΠšΠΎΠ½Ρ„ΠΈΠ³ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ Ρ‡Π΅Ρ€Π΅Π· addRemoteConfigUrl
const prodSupabase = {
  name: 'gitRaw',
  secrets: [
    {
      name: 'API_KEY',
    },
    {
      name: 'URL',
    },
  ],
  timeoutToUpdate: 100,
  mainTable: 'Invoices',
  isSubscribtionOn: false,
};

export default prodSupabase;

// ΠšΠΎΠ½Ρ„ΠΈΠ³ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ Ρ‡Π΅Ρ€Π΅Π· .addRemoteProSource
{
  name: 'notion',
  secrets: [
    {
      name: 'API_KEY',
    },
    {
      name: 'URL',
    },
  ],
  timeoutToUpdate: 3000,
  mainTable: 'Notion',
  isSubscribtionOn: true,
};

rocket ИспользованиС ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ€Π° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΠΎΠ²

// src/index.ts
import { SupabaseConfig } from '../config/supabaseConfigSchema.ts';
import manager from '../config/manager.ts';
import { getSecret } from 'https://deno.land/x/tuner/mod.ts';

try {
  // АргумСнтом являСтся ΠΏΡ€Π΅Π΄ΠΈΠΊΠ°Ρ‚, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ прСдставляСт собой Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ для Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΠΈ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΠΎΠ².
  // Π’ Π΄Π°Π½Π½ΠΎΠΌ случаС, функция провСряСт, Ρ€Π°Π²Π½ΠΎ Π»ΠΈ ΠΏΠΎΠ»Π΅ 'name' Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ Π·Π½Π°Ρ‡Π΅Π½ΠΈΡŽ,  ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½ΠΎΠΌΡƒ ΠΈΠ· ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ окруТСния 'name'.
  const config = await manager.loadConfig(
    (config: SupabaseConfig) => config.name === Deno.env.get('name'),
  );
  console.log(config);
  // Π˜Π·Π²Π»Π΅Ρ‡Π΅Π½ΠΈΠ΅ сикрСтов
  const dbApiKey = getSecret('API_KEY');
  const dbUrl = getSecret('URL');
} catch (e) {
  console.log(e);
}

ΠŸΡ€ΠΈ запускС сикрСты ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ нСпосрСдствСнно

API_KEY=your_key URL=https://your_db_url.supabase.co deno run --allow-all index.ts

Π›ΠΈΠ±ΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ» .env

API_KEY=your_key
URL=https://your_db_url.supabase.co

Если сикрСт Π½Π΅ находится, гСнСрируСтся ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅.

puzzle ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΡ‹Π΅ сСрвисы

Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ колбэка для ΠΏΠΎΠ΄Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° ситуативно, Π½ΠΎ Π±Ρ‹Π²Π°Π΅Ρ‚ ΠΏΠΎΠ»Π΅Π·Π½ΠΎ Π²ΠΎ ΠΌΠ½ΠΎΠ³ΠΈΡ… случаях.

На Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ доступны ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с:

  • Notion(Π±Π»ΠΎΠΊ /code)
  • GitHub(Ρ„Π°ΠΉΠ» Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ)
// Notion
// blockId располоТСн послС # Π² ссылкС Π½Π° Π±Π»ΠΎΠΊ
getNotionConfig(key: string, blockId: string)

// GihHub
getGitHubConfig(
  apiKey: string,
  owner: string,
  repo: string,
  filePath: string,
)