@tandem-language-exchange/content-store 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -27
- package/dist/chunk-4DE47ZJD.js +19 -0
- package/dist/chunk-4DE47ZJD.js.map +1 -0
- package/dist/chunk-5WQPK6GZ.js +178 -0
- package/dist/chunk-5WQPK6GZ.js.map +1 -0
- package/dist/chunk-HXG3MIJG.js +245 -0
- package/dist/chunk-HXG3MIJG.js.map +1 -0
- package/dist/{chunk-PQJ2MGH7.js → chunk-TYJ2WIYA.js} +9 -5
- package/dist/{chunk-PQJ2MGH7.js.map → chunk-TYJ2WIYA.js.map} +1 -1
- package/dist/chunk-UPIQFNCR.js +17 -0
- package/dist/chunk-UPIQFNCR.js.map +1 -0
- package/dist/{chunk-LOCC2BXB.js → chunk-ZIFMNEBR.js} +20 -22
- package/dist/chunk-ZIFMNEBR.js.map +1 -0
- package/dist/client/fetch-content-bundles.js +7 -3
- package/dist/client/fetch-content-bundles.js.map +1 -1
- package/dist/client/fetch-merged-translation-bundles.js +20 -13
- package/dist/client/fetch-merged-translation-bundles.js.map +1 -1
- package/dist/client/fetch-translation-bundles.js +20 -15
- package/dist/client/fetch-translation-bundles.js.map +1 -1
- package/dist/client/list-projects.js +15 -0
- package/dist/client/list-projects.js.map +1 -0
- package/dist/client/list-resources.js +21 -0
- package/dist/client/list-resources.js.map +1 -0
- package/dist/client/query-cms.js +34 -0
- package/dist/client/query-cms.js.map +1 -0
- package/dist/{index-kfqHGgMO.d.ts → index-PQ7XN47c.d.ts} +23 -9
- package/dist/index.d.ts +1 -1
- package/dist/node.browser.js.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.js +50 -8
- package/dist/node.js.map +1 -1
- package/package.json +10 -5
- package/dist/chunk-LOCC2BXB.js.map +0 -1
- package/dist/chunk-OTZLCMZ6.js +0 -396
- package/dist/chunk-OTZLCMZ6.js.map +0 -1
- package/dist/client/cli.js +0 -82
- package/dist/client/cli.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tandem-language-exchange/content-store",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -35,7 +35,9 @@
|
|
|
35
35
|
"bin": {
|
|
36
36
|
"fetch-content-bundles": "dist/client/fetch-content-bundles.js",
|
|
37
37
|
"fetch-translation-bundles": "dist/client/fetch-translation-bundles.js",
|
|
38
|
-
"fetch-merged-translation-bundles": "dist/client/fetch-merged-translation-bundles.js"
|
|
38
|
+
"fetch-merged-translation-bundles": "dist/client/fetch-merged-translation-bundles.js",
|
|
39
|
+
"list-projects": "dist/client/list-projects.js",
|
|
40
|
+
"list-resources": "dist/client/list-resources.js"
|
|
39
41
|
},
|
|
40
42
|
"engines": {
|
|
41
43
|
"npm": "^11.3.0",
|
|
@@ -52,10 +54,12 @@
|
|
|
52
54
|
"sync:contentful": "tsx src/server/cli.ts sync-cms-content --cms contentful",
|
|
53
55
|
"sync:sanity": "tsx src/server/cli.ts sync-cms-content --cms sanity",
|
|
54
56
|
"sync:translations": "tsx src/server/cli.ts sync-translations",
|
|
55
|
-
"fetch:
|
|
56
|
-
"fetch:translations": "tsx src/client/
|
|
57
|
+
"fetch:content-bundles": "tsx src/client/fetch-content-bundles.ts",
|
|
58
|
+
"fetch:translations": "tsx src/client/fetch-translation-bundles.ts",
|
|
57
59
|
"fetch:merged-translations": "tsx src/client/fetch-merged-translation-bundles.ts",
|
|
58
|
-
"query:cms": "tsx src/client/
|
|
60
|
+
"query:cms": "tsx src/client/query-cms.ts",
|
|
61
|
+
"list:projects": "tsx src/client/list-projects.ts",
|
|
62
|
+
"list:resources": "tsx src/client/list-resources.ts",
|
|
59
63
|
"lint": "eslint .",
|
|
60
64
|
"lint:fix": "eslint --fix ."
|
|
61
65
|
},
|
|
@@ -77,6 +81,7 @@
|
|
|
77
81
|
},
|
|
78
82
|
"devDependencies": {
|
|
79
83
|
"@tandem-web/web-azure-appconfig-sync": "1.0.9",
|
|
84
|
+
"@tandem-language-exchange/content-store": "^1.2.1",
|
|
80
85
|
"@types/express": "^5.0.2",
|
|
81
86
|
"@types/lodash.merge": "^4.6.9",
|
|
82
87
|
"@types/lodash.set": "^4.3.9",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/config.ts","../src/shared/bundles.ts","../src/shared/trimDepth.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 const config:SharedConfig = {\n ...sharedConfig\n};\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { CMSProvider } from './types';\nimport { buildCmsObjectKey, buildTranslationObjectKey, ContentStore } from './s3';\nimport {\n downloadRawWithRetry,\n downloadWithRetry,\n getDefaultS3RetryConfig,\n type S3RetryConfig,\n} from './s3-retry';\nimport { trimDepth } from './trimDepth';\nimport {\n allProjects,\n defaultLocales,\n type LingohubResource,\n} from './lingohub';\nimport {\n parseTranslationResourceRaw,\n toFlatStringMap,\n translationJsonOutputPath,\n} from './translationResource';\n\nexport { trimDepth } from './trimDepth';\nexport type { S3RetryConfig } from './s3-retry';\nexport { getDefaultS3RetryConfig } from './s3-retry';\n\nexport interface CmsBundleInfo {\n [key: string]: string;\n}\n\nexport interface TranslationBundleInfo {\n [key: string] : Record<string, string>;\n}\n\nexport interface BundleItem {\n [key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any\n}\n\nexport interface FetchCmsBundlesOptions {\n cms: CMSProvider;\n contentTypes: string[];\n /** S3 GET retry; defaults via {@link getDefaultS3RetryConfig}. */\n retry?: S3RetryConfig;\n}\n\nexport interface FetchTranslationBundlesOptions {\n projects: string[];\n locales?: string[] | undefined;\n /** S3 GET retry; defaults via {@link getDefaultS3RetryConfig}. */\n retry?: S3RetryConfig;\n}\n\n/** One merged JSON file per catalog locale (`en.json`, …). Same options as {@link FetchTranslationBundlesOptions}. */\nexport type FetchMergedTranslationBundlesOptions = FetchTranslationBundlesOptions;\n\nexport interface QueryOptions {\n /**\n * Filter items by matching property values (exact equality, or array for IN).\n * Keys may use dot notation for nested paths, e.g. `{ 'meta._id': 'abc' }` matches\n * `item.meta._id`.\n *\n * Contentful-style operators on the path (before `[`):\n * - `{ 'field[exists]': true }` — field is present and non-empty (not null, undefined,\n * `''`, `[]`, or `{}`).\n * - `{ 'field[exists]': false }` — field is missing or empty (same emptiness rules).\n * Nested paths work, e.g. `{ 'blocks.hero[exists]': false }`.\n */\n fields?: Record<string, unknown>;\n /**\n * Properties to include in each result object. Omit to return all properties.\n * Keys may use dot notation; nested segments become nested objects in the result,\n * e.g. `['slug', 'meta._updatedAt']` → `{ slug, meta: { _updatedAt } }`.\n */\n select?: string[];\n /** Maximum number of items to return. */\n limit?: number;\n /**\n * How many levels deep to return.\n * - 1 = the item's own scalar properties only; nested objects/refs are nulled.\n * - 2 = the item including its direct references; refs inside those are nulled.\n * - 3 = three levels deep, and so on.\n * - Omit to return the full depth.\n */\n include?: number;\n}\n\n/** Read a value at a dotted path; returns whether every segment existed. */\nfunction getAtPath(\n obj: unknown,\n dottedPath: string,\n): { found: true; value: unknown } | { found: false } {\n const parts = dottedPath.split('.').filter((p) => p.length > 0);\n if (parts.length === 0) return { found: false };\n\n let cur: unknown = obj;\n for (const p of parts) {\n if (cur === null || typeof cur !== 'object' || Array.isArray(cur)) {\n return { found: false };\n }\n const rec = cur as Record<string, unknown>;\n if (!(p in rec)) return { found: false };\n cur = rec[p];\n }\n return { found: true, value: cur };\n}\n\nconst FIELD_OP_KEY = /^(.+)\\[([^\\]]+)]$/;\n\nfunction parseFieldKey(key: string): { path: string; operator?: string } {\n const m = FIELD_OP_KEY.exec(key);\n if (!m) return { path: key };\n return { path: m[1]!, operator: m[2]! };\n}\n\n/** True when a bundle value counts as “no content” (aligned with typical CMS “empty”). */\nfunction isEmptyValue(value: unknown): boolean {\n if (value === null || value === undefined) return true;\n if (value === '') return true;\n if (Array.isArray(value) && value.length === 0) {\n return true;\n }\n return typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.keys(value).length === 0;\n\n}\n\nfunction matchesFieldFilter(\n item: BundleItem,\n key: string,\n expected: unknown,\n): boolean {\n const { path, operator } = parseFieldKey(key);\n const at = getAtPath(item, path);\n\n if (operator === 'exists') {\n if (expected !== true && expected !== false) return false;\n const empty = !at.found || isEmptyValue(at.value);\n return !expected ? empty : !empty;\n }\n\n const actual = at.found ? at.value : undefined;\n if (Array.isArray(expected)) return expected.includes(actual);\n return actual === expected;\n}\n\n/** Set `value` on `target` at a dotted path, creating plain objects as needed. */\nfunction setNestedAt(\n target: Record<string, unknown>,\n dottedPath: string,\n value: unknown,\n): void {\n const parts = dottedPath.split('.').filter((p) => p.length > 0);\n if (parts.length === 0) return;\n\n if (parts.length === 1) {\n target[parts[0]!] = value;\n return;\n }\n\n const head = parts[0]!;\n const rest = parts.slice(1).join('.');\n let nested = target[head];\n if (\n nested === null ||\n typeof nested !== 'object' ||\n Array.isArray(nested)\n ) {\n nested = {};\n target[head] = nested;\n }\n setNestedAt(nested as Record<string, unknown>, rest, value);\n}\n\n/**\n * Downloads the latest CMS bundles from S3 and writes them as JSON files to `outputDir`.\n *\n * @returns A map of contentType to absolute file path.\n */\nexport async function fetchCmsBundles(\n store: ContentStore,\n outputDir: string,\n options: FetchCmsBundlesOptions,\n): Promise<Record<string, string>> {\n const { cms, contentTypes } = options;\n const retry = options.retry ?? getDefaultS3RetryConfig();\n await fs.mkdir(outputDir, { recursive: true });\n\n const result: CmsBundleInfo = {};\n\n await Promise.all(\n contentTypes.map(async (contentType) => {\n const key = buildCmsObjectKey(cms, contentType);\n const data = await downloadWithRetry(store, key, retry);\n const filePath = path.resolve(outputDir, key);\n await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');\n result[contentType] = filePath;\n }),\n );\n\n return result;\n}\n\n/**\n * Downloads the latest translation bundles from S3 and writes them as JSON files to `outputDir`.\n *\n * @returns For each project, a map of S3 object key to absolute file path on disk.\n */\nexport async function fetchTranslationBundles(\n store: ContentStore,\n outputDir: string,\n options: FetchTranslationBundlesOptions,\n): Promise<TranslationBundleInfo> {\n const { projects, locales } = options;\n const retry = options.retry ?? getDefaultS3RetryConfig();\n await fs.mkdir(outputDir, { recursive: true });\n\n const result: TranslationBundleInfo = {};\n const localesToSync = locales && locales.length ? locales : defaultLocales;\n\n await Promise.all(\n projects.map(async (project) => {\n const resources = allProjects[project];\n if (!resources?.length) {\n return;\n }\n\n const resourceTasks: { objectKey: string; resource: LingohubResource }[] =\n [];\n for (const resource of resources) {\n for (const loc of localesToSync) {\n const locale =\n resource.localeMapping && resource.localeMapping[loc]\n ? resource.localeMapping[loc]\n : loc;\n const objectKey = buildTranslationObjectKey(\n project,\n resource.fileName,\n locale,\n );\n resourceTasks.push({ objectKey, resource });\n }\n }\n\n await Promise.all(\n resourceTasks.map(async ({ objectKey, resource }) => {\n const raw = await downloadRawWithRetry(store, objectKey, retry);\n const parsed = parseTranslationResourceRaw(raw, resource);\n const filePath = translationJsonOutputPath(outputDir, objectKey);\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(\n filePath,\n JSON.stringify(parsed, null, 2),\n 'utf-8',\n );\n if (!result[project]) {\n result[project] = {};\n }\n result[project][objectKey] = filePath;\n }),\n );\n }),\n );\n\n return result;\n}\n\n/**\n * Downloads translation bundles from S3, parses each raw Lingohub file, and merges all\n * key/value pairs per **catalog** locale into a single JSON file (e.g. `en.json`).\n * Duplicate keys across resources or projects: **last write wins** (iteration order:\n * projects → resources → locales).\n *\n * @returns Map of locale code to absolute path of the merged `{locale}.json` file.\n */\nexport async function fetchMergedTranslationBundles(\n store: ContentStore,\n outputDir: string,\n options: FetchMergedTranslationBundlesOptions,\n): Promise<Record<string, string>> {\n const { projects, locales } = options;\n const retry = options.retry ?? getDefaultS3RetryConfig();\n await fs.mkdir(outputDir, { recursive: true });\n\n const localesToSync = locales && locales.length ? locales : defaultLocales;\n\n type Task = {\n catalogLocale: string;\n objectKey: string;\n resource: LingohubResource;\n };\n const tasks: Task[] = [];\n\n for (const project of projects) {\n const resources = allProjects[project];\n if (!resources?.length) continue;\n for (const resource of resources) {\n for (const loc of localesToSync) {\n const mappedLocale =\n resource.localeMapping && resource.localeMapping[loc]\n ? resource.localeMapping[loc]\n : loc;\n const objectKey = buildTranslationObjectKey(\n project,\n resource.fileName,\n mappedLocale,\n );\n tasks.push({ catalogLocale: loc, objectKey, resource });\n }\n }\n }\n\n const results = await Promise.all(\n tasks.map(async ({ catalogLocale, objectKey, resource }) => {\n const raw = await downloadRawWithRetry(store, objectKey, retry);\n const parsed = parseTranslationResourceRaw(raw, resource);\n const stringMap = toFlatStringMap(parsed);\n return { catalogLocale, stringMap };\n }),\n );\n\n const merged: Record<string, Record<string, string>> = {};\n for (const loc of localesToSync) {\n merged[loc] = {};\n }\n for (const { catalogLocale, stringMap } of results) {\n Object.assign(merged[catalogLocale]!, stringMap);\n }\n\n const out: Record<string, string> = {};\n for (const loc of localesToSync) {\n const filePath = path.resolve(outputDir, `${loc}.json`);\n await fs.writeFile(\n filePath,\n JSON.stringify(merged[loc], null, 2),\n 'utf-8',\n );\n out[loc] = filePath;\n }\n\n return out;\n}\n\n/**\n * Queries a previously fetched bundle from the local filesystem.\n */\nexport async function queryCmsBundle(\n outputDir: string,\n cms: CMSProvider,\n contentType: string,\n options: QueryOptions = {},\n): Promise<unknown[]> {\n const filePath = path.resolve(\n outputDir,\n `${cms}-${contentType}.json`,\n );\n const raw = await fs.readFile(filePath, 'utf-8');\n let items:BundleItem[] = JSON.parse(raw) as Record<string, unknown>[];\n\n if (options.fields) {\n const filters = options.fields;\n items = items.filter((item) =>\n Object.entries(filters).every(([key, expected]) =>\n matchesFieldFilter(item, key, expected),\n ),\n );\n }\n\n if (options.limit !== undefined && options.limit > 0) {\n items = items.slice(0, options.limit);\n }\n\n if (options.include !== undefined) {\n items = items.map(\n (item) => trimDepth(item, options.include!) as Record<string, unknown>,\n );\n }\n\n if (options.select?.length) {\n const keys = options.select;\n items = items.map((item) => {\n const picked: Record<string, unknown> = {};\n for (const k of keys) {\n const at = getAtPath(item, k);\n if (at.found) setNestedAt(picked, k, at.value);\n }\n return picked;\n });\n }\n\n return items;\n}\n","/**\n * Trims nested object depth.\n *\n * `remaining` represents how many levels the current object is allowed.\n * - Scalar properties are always kept.\n * - Nested objects / arrays-of-objects consume one level. When `remaining`\n * drops to 1 they are replaced with `null` (no budget left for refs).\n */\nexport function trimDepth(value: unknown, remaining: number): unknown {\n if (value === null || value === undefined || typeof value !== 'object') {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => trimDepth(item, remaining));\n }\n\n const obj = value as Record<string, unknown>;\n const result: Record<string, unknown> = {};\n\n for (const [k, v] of Object.entries(obj)) {\n if (v === null || v === undefined || typeof v !== 'object') {\n result[k] = v;\n } else if (remaining <= 1) {\n result[k] = null;\n } else if (Array.isArray(v)) {\n result[k] = v.map((item) => {\n if (item !== null && typeof item === 'object' && !Array.isArray(item)) {\n return trimDepth(item, remaining - 1);\n }\n return item;\n });\n } else {\n result[k] = trimDepth(v, remaining - 1);\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":["config","path"]}
|
package/dist/chunk-OTZLCMZ6.js
DELETED
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/shared/s3.ts
|
|
4
|
-
import {
|
|
5
|
-
S3Client,
|
|
6
|
-
PutObjectCommand,
|
|
7
|
-
GetObjectCommand
|
|
8
|
-
} from "@aws-sdk/client-s3";
|
|
9
|
-
var ContentStore = class {
|
|
10
|
-
client;
|
|
11
|
-
bucket;
|
|
12
|
-
constructor(cfg) {
|
|
13
|
-
this.client = new S3Client({
|
|
14
|
-
region: cfg.region,
|
|
15
|
-
credentials: {
|
|
16
|
-
accessKeyId: cfg.accessKeyId,
|
|
17
|
-
secretAccessKey: cfg.secretAccessKey
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
this.bucket = cfg.bucket;
|
|
21
|
-
}
|
|
22
|
-
async upload(key, data) {
|
|
23
|
-
await this.client.send(
|
|
24
|
-
new PutObjectCommand({
|
|
25
|
-
Bucket: this.bucket,
|
|
26
|
-
Key: key,
|
|
27
|
-
Body: JSON.stringify(data, null, 2),
|
|
28
|
-
ContentType: "application/json"
|
|
29
|
-
})
|
|
30
|
-
);
|
|
31
|
-
return key;
|
|
32
|
-
}
|
|
33
|
-
/** Raw UTF-8 body (e.g. Lingohub file bytes as text). */
|
|
34
|
-
async uploadRaw(key, body, contentType) {
|
|
35
|
-
await this.client.send(
|
|
36
|
-
new PutObjectCommand({
|
|
37
|
-
Bucket: this.bucket,
|
|
38
|
-
Key: key,
|
|
39
|
-
Body: body,
|
|
40
|
-
ContentType: contentType
|
|
41
|
-
})
|
|
42
|
-
);
|
|
43
|
-
return key;
|
|
44
|
-
}
|
|
45
|
-
async download(key) {
|
|
46
|
-
const response = await this.client.send(
|
|
47
|
-
new GetObjectCommand({ Bucket: this.bucket, Key: key })
|
|
48
|
-
);
|
|
49
|
-
const body = await response.Body?.transformToString();
|
|
50
|
-
if (!body) throw new Error(`Empty response for key: ${key}`);
|
|
51
|
-
return JSON.parse(body);
|
|
52
|
-
}
|
|
53
|
-
/** Raw UTF-8 body from S3 (no JSON.parse). */
|
|
54
|
-
async downloadRaw(key) {
|
|
55
|
-
const response = await this.client.send(
|
|
56
|
-
new GetObjectCommand({ Bucket: this.bucket, Key: key })
|
|
57
|
-
);
|
|
58
|
-
const body = await response.Body?.transformToString();
|
|
59
|
-
if (!body) throw new Error(`Empty response for key: ${key}`);
|
|
60
|
-
return body;
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
var buildCmsObjectKey = (cms, contentType) => {
|
|
64
|
-
return `${cms}-${contentType}.json`;
|
|
65
|
-
};
|
|
66
|
-
var buildTranslationObjectKey = (project, fileName, locale) => {
|
|
67
|
-
return `lingohub-${project}.${fileName.replaceAll("[locale]", locale)}`;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// src/shared/lingohub.ts
|
|
71
|
-
var defaultLocales = ["en", "fr", "de", "es", "it", "pt-br", "ru", "zh-hans", "zh-hant", "ko", "ja"];
|
|
72
|
-
var localeMapping = {
|
|
73
|
-
ios: {
|
|
74
|
-
"pt-br": "pt",
|
|
75
|
-
"zh-hans": "zh-Hans",
|
|
76
|
-
"zh-hant": "zh-Hant"
|
|
77
|
-
},
|
|
78
|
-
android: {
|
|
79
|
-
"pt-br": "pt",
|
|
80
|
-
"zh-hans": "zh-Hans-CN",
|
|
81
|
-
"zh-hant": "zh-TW"
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
var allProjects = {
|
|
85
|
-
"tandem-(new-website)": [
|
|
86
|
-
{
|
|
87
|
-
fileName: "Website.[locale].json",
|
|
88
|
-
type: "json"
|
|
89
|
-
}
|
|
90
|
-
],
|
|
91
|
-
"tandem-(website)": [
|
|
92
|
-
{
|
|
93
|
-
fileName: "[locale].json",
|
|
94
|
-
type: "json"
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
fileName: "AI.[locale].json",
|
|
98
|
-
type: "json"
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
fileName: "languages.[locale].json",
|
|
102
|
-
type: "json"
|
|
103
|
-
}
|
|
104
|
-
],
|
|
105
|
-
"tandem": [
|
|
106
|
-
{
|
|
107
|
-
fileName: "InfoPlist.[locale].strings",
|
|
108
|
-
type: "strings",
|
|
109
|
-
localeMapping: localeMapping.ios
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
fileName: "Localizable.[locale].strings",
|
|
113
|
-
type: "strings",
|
|
114
|
-
localeMapping: localeMapping.ios
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
fileName: "MainiPad.[locale].strings",
|
|
118
|
-
type: "strings",
|
|
119
|
-
localeMapping: localeMapping.ios
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
fileName: "Main.[locale].strings",
|
|
123
|
-
type: "strings",
|
|
124
|
-
localeMapping: localeMapping.ios
|
|
125
|
-
}
|
|
126
|
-
],
|
|
127
|
-
"tandem-(android)": [
|
|
128
|
-
{
|
|
129
|
-
fileName: "accessibility_localizable.[locale].xml",
|
|
130
|
-
type: "xml",
|
|
131
|
-
localeMapping: localeMapping.android
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
fileName: "call_localizable.[locale].xml",
|
|
135
|
-
type: "xml",
|
|
136
|
-
localeMapping: localeMapping.android
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
fileName: "cert_localizable.[locale].xml",
|
|
140
|
-
type: "xml",
|
|
141
|
-
localeMapping: localeMapping.android
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
fileName: "chat_localizable.[locale].xml",
|
|
145
|
-
type: "xml",
|
|
146
|
-
localeMapping: localeMapping.android
|
|
147
|
-
},
|
|
148
|
-
{
|
|
149
|
-
fileName: "checklist_localizable.[locale].xml",
|
|
150
|
-
type: "xml",
|
|
151
|
-
localeMapping: localeMapping.android
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
fileName: "clubs_localizable.[locale].xml",
|
|
155
|
-
type: "xml",
|
|
156
|
-
localeMapping: localeMapping.android
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
fileName: "common_localizable.[locale].xml",
|
|
160
|
-
type: "xml",
|
|
161
|
-
localeMapping: localeMapping.android
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
fileName: "community_localizable.[locale].xml",
|
|
165
|
-
type: "xml",
|
|
166
|
-
localeMapping: localeMapping.android
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
fileName: "correction_localizable.[locale].xml",
|
|
170
|
-
type: "xml",
|
|
171
|
-
localeMapping: localeMapping.android
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
fileName: "country_names.[locale].xml",
|
|
175
|
-
type: "xml",
|
|
176
|
-
localeMapping: localeMapping.android
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
fileName: "emoji_localizable.[locale].xml",
|
|
180
|
-
type: "xml",
|
|
181
|
-
localeMapping: localeMapping.android
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
fileName: "errors_localizable.[locale].xml",
|
|
185
|
-
type: "xml",
|
|
186
|
-
localeMapping: localeMapping.android
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
fileName: "expressions_localizable.[locale].xml",
|
|
190
|
-
type: "xml",
|
|
191
|
-
localeMapping: localeMapping.android
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
fileName: "gif_localizable.[locale].xml",
|
|
195
|
-
type: "xml",
|
|
196
|
-
localeMapping: localeMapping.android
|
|
197
|
-
},
|
|
198
|
-
{
|
|
199
|
-
fileName: "guidelines_localizable.[locale].xml",
|
|
200
|
-
type: "xml",
|
|
201
|
-
localeMapping: localeMapping.android
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
fileName: "languages_localizable.[locale].xml",
|
|
205
|
-
type: "xml",
|
|
206
|
-
localeMapping: localeMapping.android
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
fileName: "localizable.[locale].xml",
|
|
210
|
-
type: "xml",
|
|
211
|
-
localeMapping: localeMapping.android
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
fileName: "localizable2.[locale].xml",
|
|
215
|
-
type: "xml",
|
|
216
|
-
localeMapping: localeMapping.android
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
fileName: "login_localizable.[locale].xml",
|
|
220
|
-
type: "xml",
|
|
221
|
-
localeMapping: localeMapping.android
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
fileName: "myprofile_localizable.[locale].xml",
|
|
225
|
-
type: "xml",
|
|
226
|
-
localeMapping: localeMapping.android
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
fileName: "onb_localizable.[locale].xml",
|
|
230
|
-
type: "xml",
|
|
231
|
-
localeMapping: localeMapping.android
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
fileName: "parties_localizable.[locale].xml",
|
|
235
|
-
type: "xml",
|
|
236
|
-
localeMapping: localeMapping.android
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
fileName: "pro_localizable.[locale].xml",
|
|
240
|
-
type: "xml",
|
|
241
|
-
localeMapping: localeMapping.android
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
fileName: "pro_screen_localizable.[locale].xml",
|
|
245
|
-
type: "xml",
|
|
246
|
-
localeMapping: localeMapping.android
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
fileName: "push_notification_localizable.[locale].xml",
|
|
250
|
-
type: "xml",
|
|
251
|
-
localeMapping: localeMapping.android
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
fileName: "reporting_localizable.[locale].xml",
|
|
255
|
-
type: "xml",
|
|
256
|
-
localeMapping: localeMapping.android
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
fileName: "translation_localizable.[locale].xml",
|
|
260
|
-
type: "xml",
|
|
261
|
-
localeMapping: localeMapping.android
|
|
262
|
-
}
|
|
263
|
-
],
|
|
264
|
-
"tandem-(web-invites)": [
|
|
265
|
-
{
|
|
266
|
-
fileName: "[locale].json",
|
|
267
|
-
type: "json"
|
|
268
|
-
}
|
|
269
|
-
]
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
// src/shared/translationResource.ts
|
|
273
|
-
import path from "path";
|
|
274
|
-
|
|
275
|
-
// src/shared/utils.ts
|
|
276
|
-
import convert from "xml-js";
|
|
277
|
-
import set from "lodash.set";
|
|
278
|
-
import merge from "lodash.merge";
|
|
279
|
-
var transformObjectToFlat = (data) => {
|
|
280
|
-
const result = {};
|
|
281
|
-
const flatten = (obj, path2 = []) => {
|
|
282
|
-
Object.entries(obj).forEach(([key, value]) => {
|
|
283
|
-
if (typeof value === "object") {
|
|
284
|
-
flatten(value, path2.concat(key));
|
|
285
|
-
} else {
|
|
286
|
-
result[path2.concat(key).join(".")] = value;
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
};
|
|
290
|
-
flatten(data);
|
|
291
|
-
return result;
|
|
292
|
-
};
|
|
293
|
-
var convertXMLToJS = (xml) => {
|
|
294
|
-
const converted = convert.xml2js(xml, {
|
|
295
|
-
ignoreComment: true,
|
|
296
|
-
ignoreDeclaration: true,
|
|
297
|
-
ignoreInstruction: true,
|
|
298
|
-
compact: true
|
|
299
|
-
});
|
|
300
|
-
let mapped = {};
|
|
301
|
-
converted.resources.string.forEach((item) => {
|
|
302
|
-
mapped = {
|
|
303
|
-
...mapped,
|
|
304
|
-
[item._attributes.name]: item._text
|
|
305
|
-
};
|
|
306
|
-
});
|
|
307
|
-
return mapped;
|
|
308
|
-
};
|
|
309
|
-
var parseIOSStrings = (strings) => {
|
|
310
|
-
const parsedObj = {};
|
|
311
|
-
strings.split("\n").filter((line) => line.startsWith('"') && line.endsWith(";")).map((line) => line.trim().slice(0, -1)).forEach((line) => {
|
|
312
|
-
let [key, value] = line.split(" = ");
|
|
313
|
-
if (!key || !value) return;
|
|
314
|
-
key = key.slice(1, -1);
|
|
315
|
-
value = value.slice(1, -1);
|
|
316
|
-
parsedObj[key] = value;
|
|
317
|
-
});
|
|
318
|
-
return parsedObj;
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
// src/shared/translationResource.ts
|
|
322
|
-
function contentTypeForTranslationKey(objectKey) {
|
|
323
|
-
if (objectKey.endsWith(".json")) return "application/json; charset=utf-8";
|
|
324
|
-
if (objectKey.endsWith(".xml")) return "application/xml; charset=utf-8";
|
|
325
|
-
if (objectKey.endsWith(".strings")) return "text/plain; charset=utf-8";
|
|
326
|
-
return "application/octet-stream";
|
|
327
|
-
}
|
|
328
|
-
function parseTranslationResourceRaw(raw, resource) {
|
|
329
|
-
if (resource.type === "json") {
|
|
330
|
-
return JSON.parse(raw);
|
|
331
|
-
}
|
|
332
|
-
if (resource.type === "strings") {
|
|
333
|
-
return parseIOSStrings(raw);
|
|
334
|
-
}
|
|
335
|
-
if (resource.type === "xml") {
|
|
336
|
-
return convertXMLToJS(raw);
|
|
337
|
-
}
|
|
338
|
-
throw new Error(`Unsupported resource type: ${resource.type}`);
|
|
339
|
-
}
|
|
340
|
-
function toFlatStringMap(parsed) {
|
|
341
|
-
if (parsed === null || parsed === void 0) return {};
|
|
342
|
-
if (typeof parsed === "string") return { value: parsed };
|
|
343
|
-
if (typeof parsed !== "object") return { value: String(parsed) };
|
|
344
|
-
if (Array.isArray(parsed)) {
|
|
345
|
-
const out2 = {};
|
|
346
|
-
parsed.forEach((v, i) => {
|
|
347
|
-
out2[String(i)] = typeof v === "object" && v !== null ? JSON.stringify(v) : String(v);
|
|
348
|
-
});
|
|
349
|
-
return out2;
|
|
350
|
-
}
|
|
351
|
-
const flat = transformObjectToFlat(parsed);
|
|
352
|
-
const out = {};
|
|
353
|
-
for (const [k, v] of Object.entries(flat)) {
|
|
354
|
-
if (v === null || v === void 0) {
|
|
355
|
-
out[k] = "";
|
|
356
|
-
} else if (typeof v === "object") {
|
|
357
|
-
out[k] = JSON.stringify(v);
|
|
358
|
-
} else {
|
|
359
|
-
out[k] = String(v);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
return out;
|
|
363
|
-
}
|
|
364
|
-
function translationJsonOutputPath(outputDir, objectKey) {
|
|
365
|
-
if (objectKey.endsWith(".json")) {
|
|
366
|
-
return path.resolve(outputDir, objectKey);
|
|
367
|
-
}
|
|
368
|
-
return path.resolve(outputDir, `${objectKey}.json`);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// src/shared/config.ts
|
|
372
|
-
import dotenv from "dotenv";
|
|
373
|
-
dotenv.config({ path: ".env.local" });
|
|
374
|
-
dotenv.config();
|
|
375
|
-
var config = {
|
|
376
|
-
s3: {
|
|
377
|
-
bucket: process.env.CONTENT_STORE_S3_BUCKET ?? "",
|
|
378
|
-
region: process.env.CONTENT_STORE_S3_REGION ?? "eu-central-1",
|
|
379
|
-
accessKeyId: process.env.AWS_ACCESS_KEY ?? "",
|
|
380
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? ""
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
export {
|
|
385
|
-
config,
|
|
386
|
-
ContentStore,
|
|
387
|
-
buildCmsObjectKey,
|
|
388
|
-
buildTranslationObjectKey,
|
|
389
|
-
defaultLocales,
|
|
390
|
-
allProjects,
|
|
391
|
-
contentTypeForTranslationKey,
|
|
392
|
-
parseTranslationResourceRaw,
|
|
393
|
-
toFlatStringMap,
|
|
394
|
-
translationJsonOutputPath
|
|
395
|
-
};
|
|
396
|
-
//# sourceMappingURL=chunk-OTZLCMZ6.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/s3.ts","../src/shared/lingohub.ts","../src/shared/translationResource.ts","../src/shared/utils.ts","../src/shared/config.ts"],"sourcesContent":["import {\n S3Client,\n PutObjectCommand,\n GetObjectCommand,\n} from '@aws-sdk/client-s3';\nimport type { S3Config } from './types';\n\nexport class ContentStore {\n private client: S3Client;\n private bucket: string;\n\n constructor(cfg: S3Config) {\n this.client = new S3Client({\n region: cfg.region,\n credentials: {\n accessKeyId: cfg.accessKeyId,\n secretAccessKey: cfg.secretAccessKey,\n },\n });\n this.bucket = cfg.bucket;\n }\n\n async upload(key: string, data: unknown): Promise<string> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: JSON.stringify(data, null, 2),\n ContentType: 'application/json',\n }),\n );\n return key;\n }\n\n /** Raw UTF-8 body (e.g. Lingohub file bytes as text). */\n async uploadRaw(\n key: string,\n body: string,\n contentType: string,\n ): Promise<string> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n }),\n );\n return key;\n }\n\n async download(key: string): Promise<unknown> {\n const response = await this.client.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: key }),\n );\n const body = await response.Body?.transformToString();\n if (!body) throw new Error(`Empty response for key: ${key}`);\n return JSON.parse(body);\n }\n\n /** Raw UTF-8 body from S3 (no JSON.parse). */\n async downloadRaw(key: string): Promise<string> {\n const response = await this.client.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: key }),\n );\n const body = await response.Body?.transformToString();\n if (!body) throw new Error(`Empty response for key: ${key}`);\n return body;\n }\n}\n\n/** {cms}-{contentType}.json (always points at the latest version) */\nexport const buildCmsObjectKey = (cms: string, contentType: string): string => {\n return `${cms}-${contentType}.json`;\n}\n\nexport const buildTranslationObjectKey = (project:string, fileName: string, locale: string): string => {\n return `lingohub-${project}.${fileName.replaceAll('[locale]', locale)}`;\n}\n","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 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 fileName: 'Website.[locale].json',\n type: 'json'\n }\n ],\n 'tandem-(website)': [\n {\n fileName: '[locale].json',\n type: 'json'\n },\n {\n fileName: 'AI.[locale].json',\n type: 'json'\n },\n {\n fileName: 'languages.[locale].json',\n type: 'json'\n }\n ],\n 'tandem': [\n {\n fileName: 'InfoPlist.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n },\n {\n fileName: 'Localizable.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n },\n {\n fileName: 'MainiPad.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n },\n {\n fileName: 'Main.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n }\n ],\n 'tandem-(android)': [\n {\n fileName: 'accessibility_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'call_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'cert_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'chat_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'checklist_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'clubs_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'common_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'community_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'correction_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'country_names.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'emoji_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'errors_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'expressions_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'gif_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'guidelines_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'languages_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'localizable2.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'login_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'myprofile_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'onb_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'parties_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'pro_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'pro_screen_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'push_notification_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'reporting_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'translation_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n }\n\n ],\n 'tandem-(web-invites)': [\n {\n fileName: '[locale].json',\n type: 'json'\n }\n ]\n};\n","import path from 'node:path';\nimport { convertXMLToJS, parseIOSStrings, transformObjectToFlat } from './utils';\nimport type { LingohubResource } from './lingohub';\n\n/** Content-Type for raw Lingohub bodies stored in S3. */\nexport function contentTypeForTranslationKey(objectKey: string): string {\n if (objectKey.endsWith('.json')) return 'application/json; charset=utf-8';\n if (objectKey.endsWith('.xml')) return 'application/xml; charset=utf-8';\n if (objectKey.endsWith('.strings')) return 'text/plain; charset=utf-8';\n return 'application/octet-stream';\n}\n\n/**\n * Parses a raw Lingohub file body from S3 into structured data.\n */\nexport function parseTranslationResourceRaw(\n raw: string,\n resource: LingohubResource,\n): unknown {\n if (resource.type === 'json') {\n return JSON.parse(raw) as unknown;\n }\n\n if (resource.type === 'strings') {\n return parseIOSStrings(raw);\n }\n\n if (resource.type === 'xml') {\n return convertXMLToJS(raw);\n }\n\n throw new Error(`Unsupported resource type: ${resource.type}`);\n}\n\n/**\n * Normalizes parsed translation data to a flat string map for merging (duplicate keys: last wins).\n */\nexport function toFlatStringMap(parsed: unknown): Record<string, string> {\n if (parsed === null || parsed === undefined) return {};\n if (typeof parsed === 'string') return { value: parsed };\n if (typeof parsed !== 'object') return { value: String(parsed) };\n if (Array.isArray(parsed)) {\n const out: Record<string, string> = {};\n parsed.forEach((v, i) => {\n out[String(i)] =\n typeof v === 'object' && v !== null ? JSON.stringify(v) : String(v);\n });\n return out;\n }\n\n const flat = transformObjectToFlat(parsed as Record<string, unknown>);\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(flat)) {\n if (v === null || v === undefined) {\n out[k] = '';\n } else if (typeof v === 'object') {\n out[k] = JSON.stringify(v);\n } else {\n out[k] = String(v);\n }\n }\n return out;\n}\n\n/** Where to write normalized JSON for one translation object key (avoids `.json.json`). */\nexport function translationJsonOutputPath(\n outputDir: string,\n objectKey: string,\n): string {\n if (objectKey.endsWith('.json')) {\n return path.resolve(outputDir, objectKey);\n }\n return path.resolve(outputDir, `${objectKey}.json`);\n}\n","import convert from 'xml-js';\nimport set from 'lodash.set';\nimport merge from 'lodash.merge';\n\nexport const transformObjectToNested = (data: Record<string, unknown>): Record<string, unknown> => {\n const result: Record<string, unknown> = {};\n\n Object.entries(data).forEach(([key, value]) => {\n const tempObject = {};\n set(tempObject, key, value);\n merge(result, tempObject);\n });\n\n return result;\n};\n\nexport const transformObjectToFlat = (data: Record<string, any>): Record<string, any> => { // eslint-disable-line @typescript-eslint/no-explicit-any\n const result: Record<string, unknown> = {};\n\n const flatten = (obj: Record<string, any>, path: string[] = []) => { // eslint-disable-line @typescript-eslint/no-explicit-any\n Object.entries(obj).forEach(([key, value]) => {\n if (typeof value === 'object') {\n flatten(value, path.concat(key));\n } else {\n result[path.concat(key).join('.')] = value;\n }\n });\n };\n\n flatten(data);\n\n return result;\n}\n\nexport const convertXMLToJS = (xml: string): Record<string, string> => {\n const converted = convert.xml2js(xml, {\n ignoreComment: true,\n ignoreDeclaration: true,\n ignoreInstruction: true,\n compact: true,\n }) as {\n resources: {\n string: {\n _attributes: { name: string },\n _text: 'User does not exist'\n }[];\n }\n };\n\n let mapped = {};\n converted.resources.string.forEach((item) => {\n mapped = {\n ...mapped,\n [item._attributes.name]: item._text,\n };\n });\n\n return mapped;\n};\n\nexport const parseIOSStrings = (strings: string): Record<string, string> => {\n const parsedObj: Record<string, string> = {};\n strings\n .split('\\n')\n .filter((line) => line.startsWith('\"') && line.endsWith(';'))\n .map((line) => line.trim().slice(0, -1))\n .forEach((line) => {\n let [key, value] = line.split(' = ');\n if (!key || !value) return;\n key = key.slice(1, -1);\n value = value.slice(1, -1);\n parsedObj[key] = value;\n });\n\n return parsedObj;\n};","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 s3: S3Config;\n}\n\nexport const config: SharedConfig = {\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.AWS_ACCESS_KEY ?? '',\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? '',\n }\n};\n"],"mappings":"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","names":["path","out"]}
|
package/dist/client/cli.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
config,
|
|
4
|
-
fetchCmsBundles,
|
|
5
|
-
fetchTranslationBundles,
|
|
6
|
-
queryCmsBundle
|
|
7
|
-
} from "../chunk-LOCC2BXB.js";
|
|
8
|
-
import "../chunk-EQ3DSPTJ.js";
|
|
9
|
-
import {
|
|
10
|
-
ContentStore
|
|
11
|
-
} from "../chunk-OTZLCMZ6.js";
|
|
12
|
-
|
|
13
|
-
// src/client/cli.ts
|
|
14
|
-
import { Command } from "commander";
|
|
15
|
-
var program = new Command();
|
|
16
|
-
program.name("content-store").description("Sync CMS content to S3").version("1.0.0");
|
|
17
|
-
program.command("fetch-cms").description("Download latest CMS bundles from S3 to the local filesystem").requiredOption("--cms <provider>", "CMS provider: contentful | sanity").requiredOption("--types <types>", "Comma-separated content types to fetch").option("--output <directory>", "Output directory", "./content-cache").action(async (opts) => {
|
|
18
|
-
const cms = opts.cms;
|
|
19
|
-
if (!["contentful", "sanity"].includes(cms)) {
|
|
20
|
-
console.error(`Invalid CMS provider: ${cms}`);
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
const contentTypes = opts.types.split(",").map((s) => s.trim());
|
|
24
|
-
const store = new ContentStore(config.s3);
|
|
25
|
-
try {
|
|
26
|
-
const files = await fetchCmsBundles(store, opts.output, {
|
|
27
|
-
cms,
|
|
28
|
-
contentTypes
|
|
29
|
-
});
|
|
30
|
-
console.log("Fetched bundles:");
|
|
31
|
-
for (const [type, filePath] of Object.entries(files)) {
|
|
32
|
-
console.log(` ${type} -> ${filePath}`);
|
|
33
|
-
}
|
|
34
|
-
} catch (err) {
|
|
35
|
-
console.error("Fetch failed:", err);
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
program.command("fetch-translations").description("Download latest Translation bundles from S3 to the local filesystem").requiredOption("--projects <projects>", "Comma-separated projects to fetch").option("--locales <locales>", "Comma-separated locales to fetch (omit to fetch all)").option("--output <directory>", "Output directory", "./content-cache").action(async (opts) => {
|
|
40
|
-
const projects = opts.projects.split(",").map((s) => s.trim());
|
|
41
|
-
const locales = opts.locales ? opts.locales.split(",").map((s) => s.trim()) : void 0;
|
|
42
|
-
const store = new ContentStore(config.s3);
|
|
43
|
-
try {
|
|
44
|
-
const files = await fetchTranslationBundles(store, opts.output, {
|
|
45
|
-
projects,
|
|
46
|
-
locales
|
|
47
|
-
});
|
|
48
|
-
console.log("Fetched bundles:");
|
|
49
|
-
for (const [project, pathsByKey] of Object.entries(files)) {
|
|
50
|
-
console.log(` ${project}:`);
|
|
51
|
-
for (const [objectKey, filePath] of Object.entries(pathsByKey)) {
|
|
52
|
-
console.log(` ${objectKey} -> ${filePath}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
} catch (err) {
|
|
56
|
-
console.error("Fetch failed:", err);
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
program.command("query-cms").description("Query a previously fetched CMS bundle from the local filesystem").requiredOption("--cms <provider>", "CMS provider: contentful | sanity").requiredOption("--type <type>", "Content type to query").option("--output <directory>", "Directory where bundles are stored", "./content-cache").option("--fields <json>", `Filter by fields (JSON object, e.g. '{"columns":"2"}')`).option("--select <props>", "Comma-separated properties to include in results").option("--limit <n>", "Maximum number of results", parseInt).option("--include <n>", "Depth of nested references to include", parseInt).action(
|
|
61
|
-
async (opts) => {
|
|
62
|
-
const cms = opts.cms;
|
|
63
|
-
if (!["contentful", "sanity"].includes(cms)) {
|
|
64
|
-
console.error(`Invalid CMS provider: ${cms}`);
|
|
65
|
-
process.exit(1);
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
const results = await queryCmsBundle(opts.output, cms, opts.type, {
|
|
69
|
-
fields: opts.fields ? JSON.parse(opts.fields) : void 0,
|
|
70
|
-
select: opts.select ? opts.select.split(",").map((s) => s.trim()) : void 0,
|
|
71
|
-
limit: opts.limit,
|
|
72
|
-
include: opts.include
|
|
73
|
-
});
|
|
74
|
-
console.log(JSON.stringify(results, null, 2));
|
|
75
|
-
} catch (err) {
|
|
76
|
-
console.error("Query failed:", err);
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
);
|
|
81
|
-
program.parse();
|
|
82
|
-
//# sourceMappingURL=cli.js.map
|
package/dist/client/cli.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/cli.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { config, type CMSProvider } from './config';\nimport { ContentStore } from '../shared/s3';\nimport { fetchCmsBundles, fetchTranslationBundles, queryCmsBundle } from '../shared/bundles';\n\nconst program = new Command();\n\nprogram\n .name('content-store')\n .description('Sync CMS content to S3')\n .version('1.0.0');\n\nprogram\n .command('fetch-cms')\n .description('Download latest CMS bundles from S3 to the local filesystem')\n .requiredOption('--cms <provider>', 'CMS provider: contentful | sanity')\n .requiredOption('--types <types>', 'Comma-separated content types to fetch')\n .option('--output <directory>', 'Output directory', './content-cache')\n .action(async (opts: { cms: string; types: string; output: string }) => {\n const cms = opts.cms as CMSProvider;\n\n if (!['contentful', 'sanity'].includes(cms)) {\n console.error(`Invalid CMS provider: ${cms}`);\n process.exit(1);\n }\n\n const contentTypes = opts.types.split(',').map((s) => s.trim());\n const store = new ContentStore(config.s3);\n\n try {\n const files = await fetchCmsBundles(store, opts.output, {\n cms,\n contentTypes,\n });\n\n console.log('Fetched bundles:');\n for (const [type, filePath] of Object.entries(files)) {\n console.log(` ${type} -> ${filePath}`);\n }\n } catch (err) {\n console.error('Fetch failed:', err);\n process.exit(1);\n }\n });\n\nprogram\n .command('fetch-translations')\n .description('Download latest Translation bundles from S3 to the local filesystem')\n .requiredOption('--projects <projects>', 'Comma-separated projects to fetch')\n .option('--locales <locales>', 'Comma-separated locales to fetch (omit to fetch all)')\n .option('--output <directory>', 'Output directory', './content-cache')\n .action(async (opts: { projects: string; locales: string; output: string }) => {\n\n const projects = opts.projects.split(',').map((s) => s.trim());\n const locales = opts.locales ? opts.locales.split(',').map((s) => s.trim()) : undefined;\n const store = new ContentStore(config.s3);\n\n try {\n const files = await fetchTranslationBundles(store, opts.output, {\n projects,\n locales,\n });\n\n console.log('Fetched bundles:');\n for (const [project, pathsByKey] of Object.entries(files)) {\n console.log(` ${project}:`);\n for (const [objectKey, filePath] of Object.entries(pathsByKey)) {\n console.log(` ${objectKey} -> ${filePath}`);\n }\n }\n } catch (err) {\n console.error('Fetch failed:', err);\n process.exit(1);\n }\n });\n\nprogram\n .command('query-cms')\n .description('Query a previously fetched CMS bundle from the local filesystem')\n .requiredOption('--cms <provider>', 'CMS provider: contentful | sanity')\n .requiredOption('--type <type>', 'Content type to query')\n .option('--output <directory>', 'Directory where bundles are stored', './content-cache')\n .option('--fields <json>', 'Filter by fields (JSON object, e.g. \\'{\"columns\":\"2\"}\\')')\n .option('--select <props>', 'Comma-separated properties to include in results')\n .option('--limit <n>', 'Maximum number of results', parseInt)\n .option('--include <n>', 'Depth of nested references to include', parseInt)\n .action(\n async (opts: {\n cms: string;\n type: string;\n output: string;\n fields?: string;\n select?: string;\n limit?: number;\n include?: number;\n }) => {\n const cms = opts.cms as CMSProvider;\n\n if (!['contentful', 'sanity'].includes(cms)) {\n console.error(`Invalid CMS provider: ${cms}`);\n process.exit(1);\n }\n\n try {\n const results = await queryCmsBundle(opts.output, cms, opts.type, {\n fields: opts.fields ? JSON.parse(opts.fields) : undefined,\n select: opts.select\n ? opts.select.split(',').map((s) => s.trim())\n : undefined,\n limit: opts.limit,\n include: opts.include,\n });\n\n console.log(JSON.stringify(results, null, 2));\n } catch (err) {\n console.error('Query failed:', err);\n process.exit(1);\n }\n },\n );\n\nprogram.parse();\n"],"mappings":"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","names":[]}
|