@tandem-language-exchange/content-store 1.3.1 → 1.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/dist/{chunk-D723FMZ2.js → chunk-D2F7FQEM.js} +3 -3
- package/dist/{chunk-VBJ6LVMY.js → chunk-LZHYKLAU.js} +4 -2
- package/dist/chunk-LZHYKLAU.js.map +1 -0
- package/dist/{chunk-SDEERVPV.js → chunk-MOGVAQ2N.js} +2 -2
- package/dist/{chunk-U73PO7OV.js → chunk-NQHWG4XM.js} +18 -444
- package/dist/chunk-NQHWG4XM.js.map +1 -0
- package/dist/{chunk-RZLDRXNQ.js → chunk-SF7FCBR2.js} +2 -2
- package/dist/chunk-SF7FCBR2.js.map +1 -0
- package/dist/{chunk-OCAIIQZW.js → chunk-Y6HC4NYU.js} +2 -2
- package/dist/chunk-Y6HC4NYU.js.map +1 -0
- package/dist/{chunk-7I67676Y.js → chunk-YWUFALDR.js} +5 -55
- package/dist/chunk-YWUFALDR.js.map +1 -0
- package/dist/client/fetch-content-bundles.js +5 -5
- package/dist/client/fetch-merged-translation-bundles.js +5 -5
- package/dist/client/fetch-translation-bundles.js +5 -5
- package/dist/client/list-projects.js +1 -1
- package/dist/client/list-resources.js +1 -1
- package/dist/client/query-cms.js +3 -3
- package/dist/node.d.ts +2 -96
- package/dist/node.js +4 -216
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-7I67676Y.js.map +0 -1
- package/dist/chunk-OCAIIQZW.js.map +0 -1
- package/dist/chunk-RZLDRXNQ.js.map +0 -1
- package/dist/chunk-U73PO7OV.js.map +0 -1
- package/dist/chunk-VBJ6LVMY.js.map +0 -1
- /package/dist/{chunk-D723FMZ2.js.map → chunk-D2F7FQEM.js.map} +0 -0
- /package/dist/{chunk-SDEERVPV.js.map → chunk-MOGVAQ2N.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/config.ts","../src/server/adapters/contentful.ts","../src/server/sync/retry.ts","../src/server/adapters/sanity.ts","../src/server/adapters/index.ts","../src/server/adapters/lingohub.ts","../src/server/sync/engine.ts"],"sourcesContent":["import dotenv from 'dotenv';\nimport type { S3Config, CMSProvider } from '../shared/types';\nimport {SharedConfig, config as sharedConfig} from '../shared/config';\n\ndotenv.config({ path: '.env.local' });\ndotenv.config();\n\nexport type { CMSProvider, S3Config };\n\nexport interface ContentfulConfig {\n spaceId: string;\n accessToken: string;\n host: string;\n batchSize: number;\n /** Content types to sync. When empty, all content types in the space are synced. */\n contentTypes: string[];\n /** Max nesting depth when unwrapping resolved entries. Deeper references are dropped. */\n maxDepth: number;\n}\n\nexport interface SanityConfig {\n projectId: string;\n dataset: string;\n token: string;\n apiVersion: string;\n}\n\nexport interface LingohubConfig {\n authToken: string;\n workspace: string;\n}\n\nexport interface RetryConfig {\n maxRetries: number;\n baseDelayMs: number;\n maxDelayMs: number;\n}\n\nexport interface RestApiConfig {\n port: number;\n apiToken: string;\n}\n\n/** Background schedule (same behaviour as `content-store sync` in the CLI). */\nexport interface ScheduledCmsJobConfig {\n enabled: boolean;\n intervalMinutes: number;\n /** When true, run once when the server starts, then on every interval. */\n runOnStart: boolean;\n /** Job kind; only `sync` is implemented. */\n task: string;\n syncCms?: CMSProvider;\n syncTypes?: string[];\n}\n\nexport interface ScheduledTranslationJobConfig {\n enabled: boolean;\n intervalMinutes: number;\n /** When true, run once when the server starts, then on every interval. */\n runOnStart: boolean;\n /** Job kind; only `sync` is implemented. */\n task: string;\n syncProjects?: string[];\n}\n\nfunction parseScheduledCmsJobConfig(): ScheduledCmsJobConfig {\n const intervalMinutes = parseInt(process.env.SCHEDULE_INTERVAL_MINUTES ?? '0', 10);\n const enabled = Number.isFinite(intervalMinutes) && intervalMinutes > 0;\n const runOnStart =\n (process.env.SCHEDULE_RUN_ON_START ?? 'true').toLowerCase() !== 'false';\n const task = (process.env.SCHEDULE_TASK ?? 'sync').trim();\n const syncCms = (process.env.SCHEDULE_SYNC_CMS ?? '').trim() as CMSProvider;\n const rawTypes = process.env.SCHEDULE_SYNC_TYPES?.trim();\n const syncTypes = rawTypes\n ? rawTypes.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined;\n\n return {\n enabled,\n intervalMinutes,\n runOnStart,\n task,\n syncCms: syncCms || undefined,\n syncTypes,\n };\n}\n\nfunction parseScheduledTranslationJobConfig(): ScheduledTranslationJobConfig {\n const intervalMinutes = parseInt(process.env.SCHEDULE_INTERVAL_MINUTES ?? '0', 10);\n const enabled = Number.isFinite(intervalMinutes) && intervalMinutes > 0;\n const runOnStart =\n (process.env.SCHEDULE_RUN_ON_START ?? 'true').toLowerCase() !== 'false';\n const task = (process.env.SCHEDULE_TASK ?? 'sync').trim();\n const rawProjects = process.env.SCHEDULE_SYNC_TRANSLATION_PROJECTS?.trim();\n const syncProjects = rawProjects\n ? rawProjects.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined;\n\n return {\n enabled,\n intervalMinutes,\n runOnStart,\n task,\n syncProjects,\n };\n}\n\nexport interface SlackConfig {\n enabled: boolean;\n botToken: string;\n signingSecret: string;\n /** Socket Mode app-level token (xapp-…). Required when enabled. */\n appToken: string;\n /** Channel ID or name for async notifications (e.g. sync results). */\n notifyChannel: string;\n /** Slash command name for CMS sync (default `/sync-content`). */\n cmdSyncContent: string;\n /** Slash command name for translation sync (default `/sync-translations`). */\n cmdSyncTranslations: string;\n}\n\nexport interface ServerConfig {\n contentful: ContentfulConfig;\n sanity: SanityConfig;\n lingohub: LingohubConfig;\n retry: RetryConfig;\n api: RestApiConfig;\n scheduledCmsJob: ScheduledCmsJobConfig;\n scheduledTranslationJob: ScheduledTranslationJobConfig;\n slack: SlackConfig;\n}\n\nexport const config: ServerConfig & SharedConfig = {\n ...sharedConfig,\n contentful: {\n spaceId: process.env.CONTENTFUL_SPACE_ID ?? '',\n accessToken: process.env.CONTENTFUL_WEBSITE_TOKEN ?? '',\n host: process.env.CONTENTFUL_HOST ?? 'preview.contentful.com',\n batchSize: 1000,\n maxDepth: 4,\n contentTypes: [\n 'asset','page','longtailPage','customJson','banner','cookieBanner','downloadPage'\n // Add Contentful content type IDs here to limit sync scope.\n ],\n },\n\n sanity: {\n projectId: process.env.SANITY_PROJECT_ID ?? '',\n dataset: process.env.SANITY_DATASET ?? 'main',\n token: process.env.SANITY_API_TOKEN ?? '',\n apiVersion: '2024-01-01',\n },\n\n lingohub: {\n authToken : process.env.LINGOHUB_AUTH_TOKEN ?? '',\n workspace: process.env.LINGOHUB_WORKSPACE ?? '',\n },\n\n retry: {\n maxRetries: parseInt(process.env.RETRY_MAX_RETRIES ?? '5', 10),\n baseDelayMs: parseInt(process.env.RETRY_BASE_DELAY_MS ?? '1000', 10),\n maxDelayMs: parseInt(process.env.RETRY_MAX_DELAY_MS ?? '60000', 10),\n },\n\n api: {\n port: parseInt(process.env.PORT ?? '3030', 10),\n apiToken: process.env.CONTENT_STORE_API_TOKEN ?? '',\n },\n\n scheduledCmsJob: parseScheduledCmsJobConfig(),\n scheduledTranslationJob: parseScheduledTranslationJobConfig(),\n\n slack: {\n enabled: (process.env.SLACK_BOT_TOKEN ?? '').length > 0,\n botToken: process.env.SLACK_BOT_TOKEN ?? '',\n signingSecret: process.env.SLACK_SIGNING_SECRET ?? '',\n appToken: process.env.SLACK_APP_TOKEN ?? '',\n notifyChannel: process.env.SLACK_NOTIFY_CHANNEL ?? '',\n cmdSyncContent: process.env.SLACK_CMD_SYNC_CONTENT ?? '/sync-content',\n cmdSyncTranslations: process.env.SLACK_CMD_SYNC_TRANSLATIONS ?? '/sync-translations',\n },\n};\n","import {\n createClient,\n type ContentfulClientApi,\n type ContentTypeCollection,\n type AssetCollection,\n} from 'contentful';\nimport type { ContentfulConfig, RetryConfig } from '../config';\nimport type { CMSAdapter, FetchResult } from './types';\nimport { withRetry } from '../sync/retry';\n\ntype CfCollection = {\n items: CfItem[],\n total: number\n}\n\ntype CfItem = {\n metadata:{\n tags: string[],\n concepts: string[]\n }\n sys:{\n type: string,\n id: string\n space: {\n sys: {\n type: string\n linkType: string\n id: string\n }\n },\n environment: {\n sys: {\n id: string\n type: 'Link',\n linkType: 'Environment'\n }\n },\n contentType: {\n sys: {\n type: 'Link',\n linkType: 'ContentType',\n id: string\n }\n },\n createdBy: {\n sys: {\n type: 'Link',\n linkType: 'User',\n id: string,\n }\n },\n updatedBy: {\n sys: {\n type: 'Link',\n linkType: 'User',\n id: string\n }\n },\n 'revision': number,\n 'createdAt': string,\n 'updatedAt': string,\n 'publishedVersion': string\n },\n fields:{\n [key:string]: unknown\n }\n}\n\n\n/**\n * Recursively unwraps Contentful's { metadata, sys, fields } envelope.\n * `depth` tracks how many entry/asset envelopes deep we are — anything\n * beyond `maxDepth` is dropped to avoid blowing the call stack on\n * circular or extremely deep reference chains.\n *\n * `path` holds objects on the current recursion branch only. That way a\n * shared reference (e.g. the same asset on `image` and `mobileImage`) is\n * unwrapped for each sibling; only true cycles (an object recurring as a\n * descendant of itself) yield `undefined`.\n */\nfunction stripEnvelope(\n value: unknown,\n maxDepth: number,\n depth = 0,\n path = new WeakSet<object>(),\n): unknown {\n if (value === null || typeof value !== 'object') return value;\n\n const obj = value as CfItem;\n\n if (path.has(obj)) return undefined;\n path.add(obj);\n\n try {\n if (Array.isArray(value)) {\n return value.map((item) => stripEnvelope(item, maxDepth, depth, path));\n }\n\n const isEnvelope = 'sys' in obj && 'fields' in obj && typeof obj.fields === 'object';\n\n if (isEnvelope) {\n if (depth >= maxDepth) return undefined;\n\n const fields = obj.fields as Record<string, unknown>;\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(fields)) {\n result[k] = stripEnvelope(v, maxDepth, depth + 1, path);\n }\n\n const sys = obj.sys;\n if (sys) {\n const existingMeta = result.meta;\n const metaBase =\n typeof existingMeta === 'object' &&\n existingMeta !== null &&\n !Array.isArray(existingMeta)\n ? (existingMeta as Record<string, unknown>)\n : {};\n const _contentType = sys.contentType ? sys.contentType.sys.id : 'Asset'\n result.meta = { ...metaBase, _id: sys.id, _contentType, _updatedAt: sys.updatedAt };\n }\n\n return result;\n }\n\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n result[k] = stripEnvelope(v, maxDepth, depth, path);\n }\n return result;\n } finally {\n path.delete(obj);\n }\n}\n\nexport class ContentfulAdapter implements CMSAdapter {\n readonly name = 'contentful';\n private client: ContentfulClientApi<undefined>;\n private batchSize: number;\n private maxDepth: number;\n private allowedTypes: string[];\n private retryConfig: RetryConfig;\n\n constructor(cfg: ContentfulConfig, retryConfig: RetryConfig) {\n this.client = createClient({\n space: cfg.spaceId,\n accessToken: cfg.accessToken,\n host: cfg.host,\n });\n this.batchSize = cfg.batchSize;\n this.maxDepth = cfg.maxDepth;\n this.allowedTypes = cfg.contentTypes;\n this.retryConfig = retryConfig;\n }\n\n async getContentTypes(): Promise<string[]> {\n const response = await withRetry<ContentTypeCollection>(\n () => this.client.getContentTypes(),\n this.retryConfig,\n );\n\n const allTypes = response.items.map((ct) => ct.sys.id);\n\n if (this.allowedTypes.length > 0) {\n return allTypes.filter((t) => this.allowedTypes.includes(t));\n }\n return allTypes;\n }\n\n /**\n * Fetches every entry for a content type using batched pagination.\n * Contentful caps `getEntries` at 1 000 items per call, so we page through\n * with `skip` until all items are collected.\n *\n * The reserved content type `\"asset\"` fetches from `getAssets()` instead.\n */\n async fetchAll(contentType: string, includeLevels: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 = 4): Promise<FetchResult> {\n if (contentType === 'asset') {\n return this.fetchAllAssets();\n }\n\n const allItems: unknown[] = [];\n let skip = 0;\n let total = 0;\n\n do {\n const payload = {\n content_type: contentType,\n limit: this.batchSize,\n skip,\n include: includeLevels,\n };\n const response = await this.client.getEntries(payload) as unknown as CfCollection;\n total = response.total;\n allItems.push(...response.items);\n skip += response.items.length;\n\n if (total > this.batchSize) {\n console.log(\n ` [contentful] ${contentType}: fetched ${allItems.length}/${total}`,\n );\n }\n } while (skip < total);\n\n return {\n contentType,\n items: allItems.map((item) => stripEnvelope(item, this.maxDepth)),\n total,\n };\n }\n\n private async fetchAllAssets(): Promise<FetchResult> {\n const allItems: unknown[] = [];\n let skip = 0;\n let total = 0;\n\n do {\n const response = await withRetry<AssetCollection>(\n () => this.client.getAssets({ limit: this.batchSize, skip }),\n this.retryConfig,\n );\n total = response.total;\n allItems.push(...response.items);\n skip += response.items.length;\n\n if (total > this.batchSize) {\n console.log(\n ` [contentful] asset: fetched ${allItems.length}/${total}`,\n );\n }\n } while (skip < total);\n\n return {\n contentType: 'asset',\n items: allItems.map((item) => stripEnvelope(item, this.maxDepth)),\n total,\n };\n }\n}\n","import type { RetryConfig } from '../config';\n\n/**\n * Inspects an error to determine if it represents an API rate-limit (HTTP 429).\n * Returns the suggested wait time in ms when available, otherwise `0` to signal\n * that the caller should fall back to computed backoff. Returns `null` when the\n * error is *not* a rate-limit error.\n */\nfunction rateLimitDelayMs(err: unknown): number | null {\n const e = err as Record<string, unknown>;\n\n if (e?.status === 429 || e?.statusCode === 429) {\n const reset = (e?.headers as Record<string, string> | undefined)?.[\n 'x-contentful-ratelimit-reset'\n ];\n return reset ? parseFloat(reset) * 1000 : 0;\n }\n\n const resp = e?.response as Record<string, unknown> | undefined;\n if (resp?.status === 429) {\n const headers = resp?.headers as Record<string, string> | undefined;\n const reset = headers?.['x-contentful-ratelimit-reset'];\n return reset ? parseFloat(reset) * 1000 : 0;\n }\n\n return null;\n}\n\nfunction computeDelay(\n attempt: number,\n baseDelayMs: number,\n maxDelayMs: number,\n): number {\n const exponential = baseDelayMs * Math.pow(2, attempt);\n const jitter = Math.random() * baseDelayMs;\n return Math.min(exponential + jitter, maxDelayMs);\n}\n\n/**\n * Executes `fn` with automatic retry + exponential backoff.\n * Rate-limit (429) responses are handled specially: if the API provides a\n * Retry-After / reset header, that value is respected instead of computed backoff.\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n { maxRetries, baseDelayMs, maxDelayMs }: RetryConfig,\n): Promise<T> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n if (attempt === maxRetries) throw err;\n\n const rlDelay = rateLimitDelayMs(err);\n let delay: number;\n\n if (rlDelay !== null) {\n delay =\n rlDelay > 0 ? rlDelay : computeDelay(attempt, baseDelayMs, maxDelayMs);\n console.warn(\n ` Rate limited (attempt ${attempt + 1}/${maxRetries}). ` +\n `Waiting ${Math.round(delay)}ms…`,\n );\n } else {\n delay = computeDelay(attempt, baseDelayMs, maxDelayMs);\n console.warn(\n ` Request failed (attempt ${attempt + 1}/${maxRetries}): ` +\n `${(err as Error).message}. Retrying in ${Math.round(delay)}ms…`,\n );\n }\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw new Error('withRetry: unreachable');\n}\n","import { createClient, type SanityClient } from '@sanity/client';\nimport type { SanityConfig, RetryConfig } from '../config';\nimport type { CMSAdapter, FetchResult } from './types';\nimport { withRetry } from '../sync/retry';\n\nexport class SanityAdapter implements CMSAdapter {\n readonly name = 'sanity';\n private client: SanityClient;\n private retryConfig: RetryConfig;\n\n constructor(cfg: SanityConfig, retryConfig: RetryConfig) {\n this.client = createClient({\n projectId: cfg.projectId,\n dataset: cfg.dataset,\n token: cfg.token,\n apiVersion: cfg.apiVersion,\n useCdn: false,\n });\n this.retryConfig = retryConfig;\n }\n\n async getContentTypes(): Promise<string[]> {\n const types: string[] = await withRetry(\n () => this.client.fetch('array::unique(*[]._type)'),\n this.retryConfig,\n );\n return types.filter(\n (t) => !t.startsWith('system.') && !t.startsWith('sanity.'),\n );\n }\n\n async fetchAll(contentType: string): Promise<FetchResult> {\n const items: unknown[] = await withRetry(\n () => this.client.fetch('*[_type == $type]', { type: contentType }),\n this.retryConfig,\n );\n\n console.log(` [sanity] ${contentType}: fetched ${items.length} items`);\n return { contentType, items, total: items.length };\n }\n}\n","import { config, type CMSProvider } from '../config';\nimport type { CMSAdapter } from './types';\nimport { ContentfulAdapter } from './contentful';\nimport { SanityAdapter } from './sanity';\n\nexport function createAdapter(cms: CMSProvider): CMSAdapter {\n switch (cms) {\n case 'contentful':\n return new ContentfulAdapter(config.contentful, config.retry);\n case 'sanity':\n return new SanityAdapter(config.sanity, config.retry);\n default:\n throw new Error(`Unknown CMS provider: ${cms as string}`);\n }\n}\n\nexport type { CMSAdapter, FetchResult } from './types';\n","import { config } from '../config';\nimport type { LingohubResource } from '../../shared/lingohub';\n\nconst cfg = config.lingohub;\nconst apiUrl = 'https://api.lingohub.com/v1/' + cfg.workspace + '/projects/';\n\n/**\n * Downloads the raw Lingohub resource body (exact bytes as UTF-8 text).\n * Sync uploads this unmodified to S3; conversion happens at fetch time.\n */\nexport async function fetchLingohubResourceRaw(\n project: string,\n resource: LingohubResource,\n locale: string,\n): Promise<string> {\n const urlForResourceLocalised =\n `${apiUrl}${project}/resources/${resource.fileName}?auth_token=${cfg.authToken}`.replace(\n '[locale]',\n locale,\n );\n const res = await fetch(urlForResourceLocalised, { method: 'GET' });\n if (!res.ok) {\n throw new Error(`Failed to fetch resource \\`${resource.fileName}\\` (${locale}): ${res.status} - ${res.statusText}`);\n }\n return await res.text();\n}\n","import type { CMSProvider } from '../config';\nimport { config } from '../config';\nimport { createAdapter } from '../adapters';\nimport {buildCmsObjectKey, buildTranslationObjectKey, ContentStore} from '../../shared/s3';\nimport { allProjects, defaultLocales } from '../../shared/lingohub';\nimport { fetchLingohubResourceRaw } from '../adapters/lingohub';\nimport { contentTypeForTranslationKey } from '../../shared/translationResource';\nimport { withRetry } from './retry';\n\nexport interface CmsSyncResultEntry {\n contentType: string;\n itemCount: number;\n objectKey: string;\n}\n\nexport interface CmsSyncResult {\n cms: CMSProvider;\n timestamp: number;\n entries: CmsSyncResultEntry[];\n errors: Array<{ contentType: string; error: string }>;\n}\n\nexport interface TranslationSyncResultEntry {\n project: string;\n resource: string;\n locale: string;\n /** UTF-8 byte size of the raw Lingohub file uploaded to S3. */\n itemCount: number;\n objectKey: string;\n}\n\nexport interface TranslationSyncResult {\n timestamp: number;\n entries: TranslationSyncResultEntry[];\n errors: Array<{ locale: string; project: string; resource: string; error: string }>;\n}\n\nexport function summariseCmsEntries(result: CmsSyncResult): string[] {\n return result.entries.map(\n (e) => `• \\`${e.contentType}\\` — ${e.itemCount} items`,\n );\n}\n\nexport function summariseCmsErrors(result: CmsSyncResult): string[] {\n return result.errors.map(\n (e) => `• \\`${e.contentType}\\` — ${e.error}`,\n );\n}\n\n/** Group translation sync entries into one summary line per project/resource. */\nexport function summariseTranslationEntries(entries: TranslationSyncResultEntry[]): string[] {\n const grouped = new Map<string, { locales: number; bytes: number }>();\n for (const e of entries) {\n const key = `${e.project} / ${e.resource}`;\n const existing = grouped.get(key);\n if (existing) {\n existing.locales += 1;\n existing.bytes += e.itemCount;\n } else {\n grouped.set(key, { locales: 1, bytes: e.itemCount });\n }\n }\n return [...grouped.entries()].map(\n ([key, { locales, bytes }]) => `• \\`${key}\\` / [${locales} locale${locales === 1 ? '' : 's'}] — ${bytes} bytes`,\n );\n}\n\nexport async function runSync(cms: CMSProvider, contentTypes?: string[], includeLevels?: number){\n await syncCmsContent(cms, contentTypes, includeLevels )\n}\n\nexport async function syncCmsContent(\n cms: CMSProvider,\n contentTypes?: string[],\n includeLevels?: number\n): Promise<CmsSyncResult> {\n const adapter = createAdapter(cms);\n const store = new ContentStore(config.s3);\n const timestamp = Math.floor(Date.now() / 1000);\n\n console.log(`\\nStarting sync from ${cms} at ${new Date(timestamp * 1000).toISOString()}`);\n\n const typesToSync =\n contentTypes && contentTypes.length > 0\n ? contentTypes\n : await adapter.getContentTypes();\n\n console.log(`Content types to sync: ${typesToSync.join(', ')}\\n`);\n\n const entries: CmsSyncResultEntry[] = [];\n const errors: Array<{ contentType: string; error: string }> = [];\n\n for (const contentType of typesToSync) {\n try {\n const result = await adapter.fetchAll(contentType, includeLevels);\n const objectKey = buildCmsObjectKey(cms, contentType);\n await store.upload(objectKey, result.items);\n\n entries.push({\n contentType,\n itemCount: result.total,\n objectKey,\n });\n\n console.log(\n ` + ${contentType}: ${result.total} items -> ${objectKey}`,\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n errors.push({ contentType, error: message });\n console.error(` x ${contentType}: ${message}`);\n }\n }\n\n console.log(\n `\\nSync complete: ${entries.length} succeeded, ${errors.length} failed\\n`,\n );\n\n return { cms, timestamp, entries, errors };\n}\n\nexport async function syncTranslations(projects?: string[], locales?:string[]):Promise<TranslationSyncResult> {\n\n const store = new ContentStore(config.s3);\n const entries: TranslationSyncResultEntry[] = [];\n const errors: Array<{ project: string; locale: string; resource: string; error: string }> = [];\n const timestamp = Math.floor(Date.now() / 1000);\n if(!locales){\n locales = defaultLocales;\n }\n if(!projects){\n projects = Object.keys(allProjects);\n }\n\n for(const project of projects) {\n const resources = allProjects[project];\n if(!resources){\n console.error(`No resources found for ${project}`);\n continue;\n }\n for(const resource of resources) {\n for(const loc of locales){\n const locale = (resource.localeMapping && resource.localeMapping[loc]) ? resource.localeMapping[loc] : loc;\n try {\n const raw = await withRetry(\n () => fetchLingohubResourceRaw(project, resource, locale),\n config.retry,\n );\n const objectKey = buildTranslationObjectKey(\n project,\n resource.fileName,\n locale,\n );\n await store.uploadRaw(\n objectKey,\n raw,\n contentTypeForTranslationKey(objectKey),\n );\n const byteLength = Buffer.byteLength(raw, 'utf8');\n entries.push({\n project,\n resource: resource.resource,\n locale,\n itemCount: byteLength,\n objectKey,\n });\n\n console.log(\n ` + ${project} - ${locale}: ${byteLength} bytes -> ${objectKey}`,\n );\n }catch(err){\n const message = err instanceof Error ? err.message : String(err);\n errors.push({ project, locale, resource: resource.resource, error: message });\n console.error(` x ${project} / ${resource.resource} - ${locale}: ${message}`);\n }\n }\n }\n }\n\n return { timestamp, entries, errors };\n}\n"],"mappings":"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","names":["config","result","cfg","createClient","cfg","createClient","config","config","config"]}
|
|
@@ -54,7 +54,7 @@ var allProjects = {
|
|
|
54
54
|
},
|
|
55
55
|
{
|
|
56
56
|
resource: "ipad",
|
|
57
|
-
fileName: "
|
|
57
|
+
fileName: "MainiPad.[locale].strings",
|
|
58
58
|
type: "strings",
|
|
59
59
|
localeMapping: localeMapping.ios
|
|
60
60
|
},
|
|
@@ -242,4 +242,4 @@ export {
|
|
|
242
242
|
defaultLocales,
|
|
243
243
|
allProjects
|
|
244
244
|
};
|
|
245
|
-
//# sourceMappingURL=chunk-
|
|
245
|
+
//# sourceMappingURL=chunk-SF7FCBR2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/lingohub.ts"],"sourcesContent":["export const defaultLocales = ['en', 'fr', 'de', 'es', 'it', 'pt-br', 'ru', 'zh-hans', 'zh-hant', 'ko', 'ja'];\n\nconst localeMapping = {\n ios: {\n 'pt-br': 'pt',\n 'zh-hans': 'zh-Hans',\n 'zh-hant': 'zh-Hant',\n },\n android: {\n 'pt-br': 'pt',\n 'zh-hans': 'zh-Hans-CN',\n 'zh-hant': 'zh-TW',\n },\n};\n\nexport type LingohubResource = {\n resource: string,\n fileName: string;\n type: 'json'|'strings'|'xml';\n localeMapping?: Record<string, string>;\n}\n\nexport const allProjects: Record<string, LingohubResource[]> = {\n 'tandem-(new-website)': [\n {\n resource: 'main',\n fileName: 'Website.[locale].json',\n type: 'json'\n }\n ],\n 'tandem-(website)': [\n {\n resource: 'main',\n fileName: '[locale].json',\n type: 'json'\n },\n {\n resource: 'ai',\n fileName: 'AI.[locale].json',\n type: 'json'\n },\n {\n resource: 'languages',\n fileName: 'languages.[locale].json',\n type: 'json'\n }\n ],\n 'tandem': [\n {\n resource: 'infoplist',\n fileName: 'InfoPlist.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n },\n {\n resource: 'localizable',\n fileName: 'Localizable.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n },\n {\n resource: 'ipad',\n fileName: 'MainiPad.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n },\n {\n resource: 'main',\n fileName: 'Main.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n }\n ],\n 'tandem-(android)': [\n {\n resource: 'accessibility',\n fileName: 'accessibility_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'call',\n fileName: 'call_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'cert',\n fileName: 'cert_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'chat',\n fileName: 'chat_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'checklist',\n fileName: 'checklist_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'clubs',\n fileName: 'clubs_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'common',\n fileName: 'common_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'community',\n fileName: 'community_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'correction',\n fileName: 'correction_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'country_names',\n fileName: 'country_names.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'emoji',\n fileName: 'emoji_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'errors',\n fileName: 'errors.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'expressions',\n fileName: 'expressions_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'gif',\n fileName: 'gif_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'guidelines',\n fileName: 'guidelines_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'lanuguages',\n fileName: 'languages_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'localizable',\n fileName: 'localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'localizable2',\n fileName: 'localizable2.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'login',\n fileName: 'login_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'myprofile',\n fileName: 'myprofile_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'onb',\n fileName: 'onb_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'parties',\n fileName: 'parties_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'pro',\n fileName: 'pro_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'pro_screen',\n fileName: 'pro_screen_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'push_notification',\n fileName: 'push_notification_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'reporting',\n fileName: 'reporting_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n resource: 'translation',\n fileName: 'translation_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n }\n\n ],\n 'tandem-(web-invites)': [\n {\n resource: 'main',\n fileName: '[locale].json',\n type: 'json'\n }\n ]\n};\n"],"mappings":"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","names":[]}
|
|
@@ -5,7 +5,7 @@ import dotenv from "dotenv";
|
|
|
5
5
|
dotenv.config({ path: ".env.local" });
|
|
6
6
|
dotenv.config();
|
|
7
7
|
var config = {
|
|
8
|
-
environment: process.env.ENVIRONMENT
|
|
8
|
+
environment: process.env.ENVIRONMENT || "development",
|
|
9
9
|
s3: {
|
|
10
10
|
bucket: process.env.CONTENT_STORE_S3_BUCKET ?? "",
|
|
11
11
|
region: process.env.CONTENT_STORE_S3_REGION ?? "eu-central-1",
|
|
@@ -17,4 +17,4 @@ var config = {
|
|
|
17
17
|
export {
|
|
18
18
|
config
|
|
19
19
|
};
|
|
20
|
-
//# sourceMappingURL=chunk-
|
|
20
|
+
//# sourceMappingURL=chunk-Y6HC4NYU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/config.ts"],"sourcesContent":["import dotenv from 'dotenv';\nimport type { S3Config, CMSProvider } from './types';\n\ndotenv.config({ path: '.env.local' });\ndotenv.config();\n\nexport type { CMSProvider, S3Config };\n\nexport interface SharedConfig {\n environment: string;\n s3: S3Config;\n}\n\nexport const config: SharedConfig = {\n environment: process.env.ENVIRONMENT || 'development',\n s3: {\n bucket: process.env.CONTENT_STORE_S3_BUCKET ?? '',\n region: process.env.CONTENT_STORE_S3_REGION ?? 'eu-central-1',\n accessKeyId: process.env.CONTENT_STORE_AWS_ACCESS_KEY ?? '',\n secretAccessKey: process.env.CONTENT_STORE_AWS_SECRET_ACCESS_KEY ?? '',\n }\n};\n"],"mappings":";;;AAAA,OAAO,YAAY;AAGnB,OAAO,OAAO,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,OAAO;AASP,IAAM,SAAuB;AAAA,EAChC,aAAa,QAAQ,IAAI,eAAe;AAAA,EACxC,IAAI;AAAA,IACA,QAAQ,QAAQ,IAAI,2BAA2B;AAAA,IAC/C,QAAQ,QAAQ,IAAI,2BAA2B;AAAA,IAC/C,aAAa,QAAQ,IAAI,gCAAgC;AAAA,IACzD,iBAAiB,QAAQ,IAAI,uCAAuC;AAAA,EACxE;AACJ;","names":[]}
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
AZURE_DEVOPS_PROJECT_KEYS,
|
|
4
3
|
config,
|
|
5
|
-
formatPipelineRunSummary,
|
|
6
|
-
isAzureDevOpsProjectKey,
|
|
7
4
|
summariseCmsEntries,
|
|
8
5
|
summariseCmsErrors,
|
|
9
6
|
summariseTranslationEntries,
|
|
10
7
|
syncCmsContent,
|
|
11
|
-
syncTranslations
|
|
12
|
-
|
|
13
|
-
} from "./chunk-U73PO7OV.js";
|
|
8
|
+
syncTranslations
|
|
9
|
+
} from "./chunk-NQHWG4XM.js";
|
|
14
10
|
import {
|
|
15
11
|
allProjects
|
|
16
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-SF7FCBR2.js";
|
|
17
13
|
|
|
18
14
|
// src/server/slack.ts
|
|
19
15
|
import { App } from "@slack/bolt";
|
|
@@ -114,7 +110,7 @@ async function startSlackBot() {
|
|
|
114
110
|
const result = await syncTranslations(projects, locales);
|
|
115
111
|
const lines = summariseTranslationEntries(result.entries);
|
|
116
112
|
const errorLines = result.errors.map(
|
|
117
|
-
(e) => `\u2022 \`${e.project}\` / \`${e.locale}\` \u2014 ${e.error}`
|
|
113
|
+
(e) => `\u2022 \`${e.project}\` / \`${e.resource}\` / \`${e.locale}\` \u2014 ${e.error}`
|
|
118
114
|
);
|
|
119
115
|
const blocks = [
|
|
120
116
|
result.errors.length === 0 ? `:white_check_mark: *Translation sync complete* \u2014 ${result.entries.length} resource \xD7 locale uploads` : `:warning: *Translation sync complete with errors* \u2014 ${result.entries.length} succeeded, ${result.errors.length} failed`,
|
|
@@ -134,52 +130,6 @@ async function startSlackBot() {
|
|
|
134
130
|
});
|
|
135
131
|
}
|
|
136
132
|
});
|
|
137
|
-
app.command(slack.cmdTriggerBuild, async ({ command, ack, respond }) => {
|
|
138
|
-
await ack();
|
|
139
|
-
const args = command.text.trim().split(/\s+/).filter(Boolean);
|
|
140
|
-
const projectArg = (args[0] ?? config.azure.defaultProject).toLowerCase();
|
|
141
|
-
if (args.length > 1) {
|
|
142
|
-
const projects = AZURE_DEVOPS_PROJECT_KEYS.join("`, `");
|
|
143
|
-
await respond({
|
|
144
|
-
response_type: "ephemeral",
|
|
145
|
-
text: "Usage: `/trigger-build [project]`\nQueues the pipeline configured for this instance (`ENVIRONMENT=" + config.azure.instanceEnvironment + "`, Azure `" + config.azure.pipelineEnvironment + "`).\nDefault project: `" + config.azure.defaultProject + "`. Projects: `" + projects + "`"
|
|
146
|
-
});
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
if (!isAzureDevOpsProjectKey(projectArg)) {
|
|
150
|
-
await respond({
|
|
151
|
-
response_type: "ephemeral",
|
|
152
|
-
text: `Unknown project \`${projectArg}\`. Use one of: \`${AZURE_DEVOPS_PROJECT_KEYS.join("`, `")}\`.`
|
|
153
|
-
});
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
if (!config.azure.enabled) {
|
|
157
|
-
await respond({
|
|
158
|
-
response_type: "ephemeral",
|
|
159
|
-
text: "Azure DevOps is not configured on this server (set `AZURE_DEVOPS_ACCESS_TOKEN`)."
|
|
160
|
-
});
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
await respond({
|
|
164
|
-
response_type: "in_channel",
|
|
165
|
-
text: `:hourglass_flowing_sand: Queuing pipeline for \`${projectArg}\` (\`${config.azure.instanceEnvironment}\` / Azure \`${config.azure.pipelineEnvironment}\`)\u2026`
|
|
166
|
-
});
|
|
167
|
-
try {
|
|
168
|
-
const result = await triggerPipelineBuild(config.azure, projectArg);
|
|
169
|
-
await respond({
|
|
170
|
-
response_type: "in_channel",
|
|
171
|
-
text: `:rocket: Pipeline run queued
|
|
172
|
-
${formatPipelineRunSummary(result)}`
|
|
173
|
-
});
|
|
174
|
-
} catch (err) {
|
|
175
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
176
|
-
console.error("[slack] Pipeline trigger failed:", message);
|
|
177
|
-
await respond({
|
|
178
|
-
response_type: "ephemeral",
|
|
179
|
-
text: `:x: Failed to trigger build: ${message}`
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
133
|
app.command("/list-projects", async ({ ack }) => {
|
|
184
134
|
const projectNames = Object.keys(allProjects);
|
|
185
135
|
const lines = projectNames.map((name, i) => `${i + 1}. ${name}`);
|
|
@@ -230,4 +180,4 @@ export {
|
|
|
230
180
|
notifySlack,
|
|
231
181
|
startSlackBot
|
|
232
182
|
};
|
|
233
|
-
//# sourceMappingURL=chunk-
|
|
183
|
+
//# sourceMappingURL=chunk-YWUFALDR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/slack.ts"],"sourcesContent":["import { App } from '@slack/bolt';\nimport { config, type CMSProvider } from './config';\nimport { syncCmsContent, syncTranslations, summariseTranslationEntries, summariseCmsEntries, summariseCmsErrors } from './sync/engine';\nimport { allProjects } from '../shared/lingohub';\n\nconst VALID_CMS: CMSProvider[] = ['contentful', 'sanity'];\n\nlet app: App | null = null;\n\n/**\n * Post a message to the configured Slack notify channel.\n * Silently logs and returns if Slack is not configured or no channel is set.\n */\nexport async function notifySlack(text: string): Promise<void> {\n const { notifyChannel } = config.slack;\n if (!app || !notifyChannel) {\n console.log('[slack] Notification skipped (no app or channel configured)');\n return;\n }\n try {\n await app.client.chat.postMessage({ channel: notifyChannel, text: `[${config.environment}] ${text}` });\n } catch (err) {\n console.error('[slack] Failed to send notification:', err);\n }\n}\n\nexport async function startSlackBot(): Promise<void> {\n const { slack } = config;\n if (!slack.enabled) {\n console.log('[slack] Disabled (SLACK_BOT_TOKEN not set)');\n return;\n }\n\n app = new App({\n token: slack.botToken,\n signingSecret: slack.signingSecret,\n appToken: slack.appToken,\n socketMode: true,\n });\n\n app.command(slack.cmdSyncContent, async ({ command, ack, respond }) => {\n await ack();\n\n const args = command.text.trim().split(/\\s+/).filter(Boolean);\n const cms = (args[0] ?? 'contentful') as CMSProvider;\n const contentTypes = args.length > 1 ? args.slice(1) : undefined;\n\n if (!VALID_CMS.includes(cms)) {\n await respond({\n response_type: 'ephemeral',\n text: `Invalid CMS provider \\`${cms}\\`. Use \\`contentful\\` or \\`sanity\\`.`,\n });\n return;\n }\n\n const typesHint = contentTypes?.length ? ` (types: ${contentTypes.join(', ')})` : ' (all types)';\n await respond({\n response_type: 'in_channel',\n text: `:hourglass_flowing_sand: Sync started for *${cms}*${typesHint}…`,\n });\n\n try {\n const result = await syncCmsContent(cms, contentTypes);\n\n const blocks: string[] = [\n result.errors.length === 0\n ? `:white_check_mark: *Sync complete* — ${result.entries.length} content types synced`\n : `:warning: *Sync complete with errors* — ${result.entries.length} succeeded, ${result.errors.length} failed`,\n '',\n ...summariseCmsEntries(result),\n ];\n\n if (result.errors.length > 0) {\n blocks.push('', '*Errors:*', ...summariseCmsErrors(result));\n }\n\n await respond({ response_type: 'in_channel', text: blocks.join('\\n') });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error('[slack] Sync failed:', message);\n await respond({\n response_type: 'ephemeral',\n text: `:x: Sync failed: ${message}`,\n });\n }\n });\n\n app.command(slack.cmdSyncTranslations, async ({ command, ack, respond }) => {\n await ack();\n\n const raw = command.text.trim();\n if (!raw) {\n await respond({\n response_type: 'ephemeral',\n text:\n 'Usage: `/sync-translations <comma-separated projects> [<comma-separated locales>]`\\n' +\n 'Example: `/sync-translations tandem,tandem-(website) en,de,fr`\\n' +\n 'Omit locales to use the default locale list.',\n });\n return;\n }\n\n const spaceIdx = raw.indexOf(' ');\n const projectsPart = spaceIdx === -1 ? raw : raw.slice(0, spaceIdx).trim();\n const localesPart = spaceIdx === -1 ? '' : raw.slice(spaceIdx + 1).trim();\n\n const projects = projectsPart\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n if (projects.length === 0) {\n await respond({\n response_type: 'ephemeral',\n text:\n 'No valid projects. Pass a comma-separated list as the first argument, e.g. `tandem,tandem-(website)`.',\n });\n return;\n }\n\n let locales: string[] | undefined = localesPart.length\n ? localesPart\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\n if (locales && locales.length === 0) {\n locales = undefined;\n }\n\n const localesHint =\n locales?.length ? ` — locales: ${locales?.join(', ')}` : ' — default locales';\n await respond({\n response_type: 'in_channel',\n text: `:hourglass_flowing_sand: Translation sync started for projects: *${projects.join(', ')}*${localesHint}…`,\n });\n\n try {\n const result = await syncTranslations(projects, locales);\n\n const lines = summariseTranslationEntries(result.entries);\n const errorLines = result.errors.map(\n (e) =>\n `• \\`${e.project}\\` / \\`${e.resource}\\` / \\`${e.locale}\\` — ${e.error}`,\n );\n\n const blocks: string[] = [\n result.errors.length === 0\n ? `:white_check_mark: *Translation sync complete* — ${result.entries.length} resource × locale uploads`\n : `:warning: *Translation sync complete with errors* — ${result.entries.length} succeeded, ${result.errors.length} failed`,\n '',\n ...lines,\n ];\n\n if (errorLines.length > 0) {\n blocks.push('', '*Errors:*', ...errorLines);\n }\n\n await respond({ response_type: 'in_channel', text: blocks.join('\\n') });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error('[slack] Sync failed:', message);\n await respond({\n response_type: 'ephemeral',\n text: `:x: Sync failed: ${message}`,\n });\n }\n });\n\n app.command('/list-projects', async ({ ack }) => {\n const projectNames = Object.keys(allProjects);\n const lines = projectNames.map((name, i) => `${i + 1}. ${name}`);\n await ack({\n response_type: 'ephemeral',\n text: `*Available Lingohub projects:*\\n${lines.join('\\n')}`,\n });\n });\n\n app.command('/list-resources', async ({ command, ack }) => {\n const project = command.text.trim();\n if (!project) {\n const projectNames = Object.keys(allProjects);\n const lines = projectNames.map((name, i) => `${i + 1}. ${name}`);\n await ack({\n response_type: 'ephemeral',\n text: `Usage: \\`/list-resources <project-name>\\`\\n\\n*Available projects:*\\n${lines.join('\\n')}`,\n });\n return;\n }\n\n const resources = allProjects[project];\n if (!resources) {\n const available = Object.keys(allProjects).join(', ');\n await ack({\n response_type: 'ephemeral',\n text: `Unknown project \\`${project}\\`.\\nAvailable projects: ${available}`,\n });\n return;\n }\n\n const lines = resources.map(\n (r, i) => `${i + 1}. \\`${r.resource}\\` (${r.fileName})`,\n );\n await ack({\n response_type: 'ephemeral',\n text: `*Resources for \"${project}\":*\\n${lines.join('\\n')}`,\n });\n });\n\n await app.start();\n console.log('[slack] Bot connected via Socket Mode');\n}\n"],"mappings":"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","names":["lines"]}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
config
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-MOGVAQ2N.js";
|
|
5
5
|
import {
|
|
6
6
|
fetchCmsBundles
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-D2F7FQEM.js";
|
|
8
8
|
import "../chunk-EQ3DSPTJ.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-Y6HC4NYU.js";
|
|
10
10
|
import {
|
|
11
11
|
ContentStore
|
|
12
|
-
} from "../chunk-
|
|
13
|
-
import "../chunk-
|
|
12
|
+
} from "../chunk-LZHYKLAU.js";
|
|
13
|
+
import "../chunk-SF7FCBR2.js";
|
|
14
14
|
|
|
15
15
|
// src/client/fetch-content-bundles.ts
|
|
16
16
|
import { Command } from "commander";
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
config
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-MOGVAQ2N.js";
|
|
5
5
|
import {
|
|
6
6
|
fetchMergedTranslationBundles
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-D2F7FQEM.js";
|
|
8
8
|
import "../chunk-EQ3DSPTJ.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-Y6HC4NYU.js";
|
|
10
10
|
import {
|
|
11
11
|
ContentStore
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-LZHYKLAU.js";
|
|
13
13
|
import {
|
|
14
14
|
allProjects
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-SF7FCBR2.js";
|
|
16
16
|
|
|
17
17
|
// src/client/fetch-merged-translation-bundles.ts
|
|
18
18
|
import fs from "fs";
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
config
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-MOGVAQ2N.js";
|
|
5
5
|
import {
|
|
6
6
|
fetchTranslationBundles
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-D2F7FQEM.js";
|
|
8
8
|
import "../chunk-EQ3DSPTJ.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-Y6HC4NYU.js";
|
|
10
10
|
import {
|
|
11
11
|
ContentStore
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-LZHYKLAU.js";
|
|
13
13
|
import {
|
|
14
14
|
allProjects
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-SF7FCBR2.js";
|
|
16
16
|
|
|
17
17
|
// src/client/fetch-translation-bundles.ts
|
|
18
18
|
import fs from "fs";
|
package/dist/client/query-cms.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
queryCmsBundle
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-D2F7FQEM.js";
|
|
5
5
|
import "../chunk-EQ3DSPTJ.js";
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-LZHYKLAU.js";
|
|
7
|
+
import "../chunk-SF7FCBR2.js";
|
|
8
8
|
|
|
9
9
|
// src/client/query-cms.ts
|
|
10
10
|
import { Command } from "commander";
|
package/dist/node.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as SDKConfig, F as FetchCmsBundlesOptions, a as FetchTranslationBundlesOptions, T as TranslationBundleInfo, b as FetchMergedTranslationBundlesOptions, C as CMSProvider, Q as QueryOptions } from './index-DqDNlXSE.js';
|
|
2
2
|
export { B as BundleItem, c as CmsBundleInfo, d as ContentStore, e as S3Config, f as S3RetryConfig, g as TranslationFilterConfig, h as fetchCmsBundles, i as fetchMergedTranslationBundles, j as fetchTranslationBundles, k as getDefaultS3RetryConfig, q as queryCmsBundle } from './index-DqDNlXSE.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -11,65 +11,6 @@ export { B as BundleItem, c as CmsBundleInfo, d as ContentStore, e as S3Config,
|
|
|
11
11
|
*/
|
|
12
12
|
declare function trimDepth(value: unknown, remaining: number): unknown;
|
|
13
13
|
|
|
14
|
-
type ContentRefreshScope = 'cms' | 'translations' | 'all';
|
|
15
|
-
interface ContentRefreshRequest {
|
|
16
|
-
scope?: ContentRefreshScope;
|
|
17
|
-
cms?: CMSProvider;
|
|
18
|
-
content_types?: string[];
|
|
19
|
-
projects?: string[];
|
|
20
|
-
locales?: string[];
|
|
21
|
-
}
|
|
22
|
-
/** Defaults applied when the request omits fields (set by the host application). */
|
|
23
|
-
interface ContentRefreshDefaults {
|
|
24
|
-
scope?: ContentRefreshScope;
|
|
25
|
-
cms?: CMSProvider;
|
|
26
|
-
contentTypes?: string[];
|
|
27
|
-
translationProjects?: string[];
|
|
28
|
-
locales?: string[];
|
|
29
|
-
}
|
|
30
|
-
interface ContentRefreshError {
|
|
31
|
-
step: string;
|
|
32
|
-
message: string;
|
|
33
|
-
}
|
|
34
|
-
interface ContentRefreshResult {
|
|
35
|
-
ok: boolean;
|
|
36
|
-
scope: ContentRefreshScope;
|
|
37
|
-
cmsFiles?: Record<string, string>;
|
|
38
|
-
translationFiles?: TranslationBundleInfo;
|
|
39
|
-
errors: ContentRefreshError[];
|
|
40
|
-
durationMs: number;
|
|
41
|
-
}
|
|
42
|
-
interface ContentRefreshFetchers {
|
|
43
|
-
fetchCmsBundles: (options: FetchCmsBundlesOptions) => Promise<Record<string, string>>;
|
|
44
|
-
fetchTranslationBundles: (options: FetchTranslationBundlesOptions) => Promise<TranslationBundleInfo>;
|
|
45
|
-
}
|
|
46
|
-
/** Base64 of `username:password` (value only — prefix with `Basic ` in the header). */
|
|
47
|
-
declare function encodeBasicAuthCredentials(username: string, password: string): string;
|
|
48
|
-
/**
|
|
49
|
-
* Validates `Authorization: Basic <base64>` against the expected credentials
|
|
50
|
-
* (base64 of `user:pass`, same as staging site basic auth).
|
|
51
|
-
*/
|
|
52
|
-
declare function assertContentRefreshBasicAuth(authorizationHeader: string | undefined, expectedCredentialsBase64: string): void;
|
|
53
|
-
declare class ContentRefreshAuthError extends Error {
|
|
54
|
-
readonly statusCode: number;
|
|
55
|
-
constructor(message: string, statusCode: number);
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Pull the latest bundles from S3 into the host app's configured output directory.
|
|
59
|
-
*/
|
|
60
|
-
declare function executeContentRefresh(fetchers: ContentRefreshFetchers, request: ContentRefreshRequest, defaults?: ContentRefreshDefaults): Promise<ContentRefreshResult>;
|
|
61
|
-
interface PostContentRefreshResponse {
|
|
62
|
-
target: string;
|
|
63
|
-
ok: boolean;
|
|
64
|
-
status: number;
|
|
65
|
-
body?: unknown;
|
|
66
|
-
error?: string;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Ask a remote application (web-site, web-app, …) to refresh its local content cache.
|
|
70
|
-
*/
|
|
71
|
-
declare function postContentRefresh(target: string, url: string, basicAuth: string, request: ContentRefreshRequest): Promise<PostContentRefreshResponse>;
|
|
72
|
-
|
|
73
14
|
declare class ContentStoreSDK {
|
|
74
15
|
private store;
|
|
75
16
|
private outputDir;
|
|
@@ -98,41 +39,6 @@ declare class ContentStoreSDK {
|
|
|
98
39
|
* Queries a previously fetched bundle from the local filesystem.
|
|
99
40
|
*/
|
|
100
41
|
queryCmsBundle(cms: CMSProvider, contentType: string, options?: QueryOptions): Promise<unknown[]>;
|
|
101
|
-
/**
|
|
102
|
-
* Download the latest bundles from S3 (same as the hosted refresh HTTP endpoint).
|
|
103
|
-
*/
|
|
104
|
-
refreshContent(request?: ContentRefreshRequest, defaults?: ContentRefreshDefaults): Promise<ContentRefreshResult>;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** Minimal request shape for Next.js Pages API routes (`pages/api/*`). */
|
|
108
|
-
interface NextPagesContentRefreshRequest {
|
|
109
|
-
method?: string;
|
|
110
|
-
headers: {
|
|
111
|
-
authorization?: string | string[];
|
|
112
|
-
};
|
|
113
|
-
body?: ContentRefreshRequest;
|
|
114
42
|
}
|
|
115
|
-
/** Minimal response shape for Next.js Pages API routes. */
|
|
116
|
-
interface NextPagesContentRefreshResponse {
|
|
117
|
-
status(code: number): {
|
|
118
|
-
json(body: unknown): void;
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
interface NextContentRefreshHandlerConfig {
|
|
122
|
-
sdk: ContentStoreSDK;
|
|
123
|
-
/** Base64 of `username:password` (same value as `CONTENT_REFRESH_BASIC_AUTH`). */
|
|
124
|
-
basicAuth: string;
|
|
125
|
-
defaults?: ContentRefreshDefaults;
|
|
126
|
-
}
|
|
127
|
-
/** @deprecated Use {@link NextContentRefreshHandlerConfig}. */
|
|
128
|
-
type NextPagesContentRefreshHandlerConfig = NextContentRefreshHandlerConfig;
|
|
129
|
-
/**
|
|
130
|
-
* Handler for a Next.js Pages API route (e.g. pages/api/internal/content-refresh.ts).
|
|
131
|
-
*/
|
|
132
|
-
declare function createNextPagesApiContentRefreshHandler(config: NextContentRefreshHandlerConfig): (req: NextPagesContentRefreshRequest, res: NextPagesContentRefreshResponse) => Promise<void>;
|
|
133
|
-
/**
|
|
134
|
-
* Handler for a Next.js App Router route (e.g. app/api/internal/content-refresh/route.ts).
|
|
135
|
-
*/
|
|
136
|
-
declare function handleNextAppRouterContentRefresh(request: Request, config: NextContentRefreshHandlerConfig): Promise<Response>;
|
|
137
43
|
|
|
138
|
-
export { CMSProvider,
|
|
44
|
+
export { CMSProvider, ContentStoreSDK, FetchCmsBundlesOptions, FetchMergedTranslationBundlesOptions, FetchTranslationBundlesOptions, QueryOptions, SDKConfig, TranslationBundleInfo, trimDepth };
|