@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
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
parseTranslationResourceRaw,
|
|
11
11
|
toFlatStringMap,
|
|
12
12
|
translationJsonOutputPath
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-LZHYKLAU.js";
|
|
14
14
|
import {
|
|
15
15
|
allProjects,
|
|
16
16
|
defaultLocales
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-SF7FCBR2.js";
|
|
18
18
|
|
|
19
19
|
// src/shared/bundles.ts
|
|
20
20
|
import fs from "fs/promises";
|
|
@@ -286,4 +286,4 @@ export {
|
|
|
286
286
|
fetchMergedTranslationBundles,
|
|
287
287
|
queryCmsBundle
|
|
288
288
|
};
|
|
289
|
-
//# sourceMappingURL=chunk-
|
|
289
|
+
//# sourceMappingURL=chunk-D2F7FQEM.js.map
|
|
@@ -15,7 +15,9 @@ var ContentStore = class {
|
|
|
15
15
|
credentials: {
|
|
16
16
|
accessKeyId: cfg.accessKeyId,
|
|
17
17
|
secretAccessKey: cfg.secretAccessKey
|
|
18
|
-
}
|
|
18
|
+
},
|
|
19
|
+
requestChecksumCalculation: "WHEN_REQUIRED",
|
|
20
|
+
responseChecksumValidation: "WHEN_REQUIRED"
|
|
19
21
|
});
|
|
20
22
|
this.bucket = cfg.bucket;
|
|
21
23
|
}
|
|
@@ -179,4 +181,4 @@ export {
|
|
|
179
181
|
toFlatStringMap,
|
|
180
182
|
translationJsonOutputPath
|
|
181
183
|
};
|
|
182
|
-
//# sourceMappingURL=chunk-
|
|
184
|
+
//# sourceMappingURL=chunk-LZHYKLAU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/s3.ts","../src/shared/translationResource.ts","../src/shared/utils.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 requestChecksumCalculation: 'WHEN_REQUIRED',\n responseChecksumValidation: 'WHEN_REQUIRED',\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","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 (value !== null && typeof value === 'object' && !Array.isArray(value)) {\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 const strings = converted.resources.string;\n const items = Array.isArray(strings) ? strings : [strings];\n items.forEach((item) => {\n if (!item?._attributes?.name) return;\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 const eqIdx = line.indexOf(' = ');\n if (eqIdx === -1) return;\n const key = line.slice(1, eqIdx - 1);\n const value = line.slice(eqIdx + 3 + 1, -1);\n if (!key) return;\n parsedObj[key] = value;\n });\n\n return parsedObj;\n};"],"mappings":"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","names":["path","out"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
config
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y6HC4NYU.js";
|
|
5
5
|
|
|
6
6
|
// src/client/config.ts
|
|
7
7
|
import dotenv from "dotenv";
|
|
@@ -14,4 +14,4 @@ var config2 = {
|
|
|
14
14
|
export {
|
|
15
15
|
config2 as config
|
|
16
16
|
};
|
|
17
|
-
//# sourceMappingURL=chunk-
|
|
17
|
+
//# sourceMappingURL=chunk-MOGVAQ2N.js.map
|
|
@@ -1,471 +1,54 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
config
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y6HC4NYU.js";
|
|
5
5
|
import {
|
|
6
6
|
ContentStore,
|
|
7
7
|
buildCmsObjectKey,
|
|
8
8
|
buildTranslationObjectKey,
|
|
9
9
|
contentTypeForTranslationKey
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-LZHYKLAU.js";
|
|
11
11
|
import {
|
|
12
12
|
allProjects,
|
|
13
13
|
defaultLocales
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
|
|
16
|
-
// src/server/adapters/azure/types.ts
|
|
17
|
-
var AZURE_DEVOPS_PROJECT_KEYS = [
|
|
18
|
-
"web-site",
|
|
19
|
-
"web-app",
|
|
20
|
-
"web-invites"
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
// src/server/adapters/azure/client.ts
|
|
24
|
-
var AzureDevOpsClient = class {
|
|
25
|
-
constructor(config3) {
|
|
26
|
-
this.config = config3;
|
|
27
|
-
const org = config3.organization.replace(/^\/+|\/+$/g, "");
|
|
28
|
-
const project = encodeURIComponent(config3.project);
|
|
29
|
-
this.baseUrl = `https://dev.azure.com/${org}/${project}`;
|
|
30
|
-
}
|
|
31
|
-
baseUrl;
|
|
32
|
-
/**
|
|
33
|
-
* Call any Azure DevOps REST endpoint under the configured organization and project.
|
|
34
|
-
*/
|
|
35
|
-
async request(options) {
|
|
36
|
-
const {
|
|
37
|
-
method = "GET",
|
|
38
|
-
path,
|
|
39
|
-
query = {},
|
|
40
|
-
body,
|
|
41
|
-
apiVersion = this.config.apiVersion,
|
|
42
|
-
headers: extraHeaders = {}
|
|
43
|
-
} = options;
|
|
44
|
-
if (!this.config.pat) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
"Azure DevOps is not configured (set AZURE_DEVOPS_ACCESS_TOKEN)"
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
const url = this.buildUrl(path, { ...query, "api-version": apiVersion });
|
|
50
|
-
const headers = {
|
|
51
|
-
Authorization: this.basicAuthHeader(),
|
|
52
|
-
Accept: "application/json",
|
|
53
|
-
...extraHeaders
|
|
54
|
-
};
|
|
55
|
-
const init = { method, headers };
|
|
56
|
-
if (body !== void 0) {
|
|
57
|
-
headers["Content-Type"] = "application/json";
|
|
58
|
-
init.body = JSON.stringify(body);
|
|
59
|
-
}
|
|
60
|
-
const response = await fetch(url, init);
|
|
61
|
-
const text = await response.text();
|
|
62
|
-
let parsed;
|
|
63
|
-
if (text) {
|
|
64
|
-
try {
|
|
65
|
-
parsed = JSON.parse(text);
|
|
66
|
-
} catch {
|
|
67
|
-
parsed = text;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (!response.ok) {
|
|
71
|
-
const errBody = parsed;
|
|
72
|
-
const detail = errBody?.message ?? (typeof parsed === "string" ? parsed : JSON.stringify(parsed));
|
|
73
|
-
throw new Error(
|
|
74
|
-
`Azure DevOps ${method} ${path} failed (${response.status}): ${detail}`
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
return parsed;
|
|
78
|
-
}
|
|
79
|
-
buildUrl(path, query) {
|
|
80
|
-
const base = /^https?:\/\//i.test(path) ? path : `${this.baseUrl}${path}`;
|
|
81
|
-
const url = new URL(base);
|
|
82
|
-
for (const [key, value] of Object.entries(query)) {
|
|
83
|
-
if (value !== void 0) {
|
|
84
|
-
url.searchParams.set(key, String(value));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return url.toString();
|
|
88
|
-
}
|
|
89
|
-
basicAuthHeader() {
|
|
90
|
-
const encoded = Buffer.from(`:${this.config.pat}`).toString("base64");
|
|
91
|
-
return `Basic ${encoded}`;
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
function createAzureDevOpsClient(config3) {
|
|
95
|
-
return new AzureDevOpsClient(config3);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// src/server/adapters/azure/pipelines.ts
|
|
99
|
-
function isAzureDevOpsProjectKey(value) {
|
|
100
|
-
return AZURE_DEVOPS_PROJECT_KEYS.includes(value);
|
|
101
|
-
}
|
|
102
|
-
function resolveProjectConfig(azureConfig, project) {
|
|
103
|
-
const projectConfig = azureConfig.projects[project];
|
|
104
|
-
if (!projectConfig) {
|
|
105
|
-
throw new Error(`Unknown Azure DevOps project "${project}"`);
|
|
106
|
-
}
|
|
107
|
-
return projectConfig;
|
|
108
|
-
}
|
|
109
|
-
async function triggerPipelineBuild(azureConfig, project, variables = {}) {
|
|
110
|
-
const { pipeline } = resolveProjectConfig(azureConfig, project);
|
|
111
|
-
if (!pipeline?.id) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`No pipeline id configured for project "${project}" (instance ENVIRONMENT=${azureConfig.instanceEnvironment}, Azure=${azureConfig.pipelineEnvironment})`
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
const client = createAzureDevOpsClient({
|
|
117
|
-
organization: azureConfig.organization,
|
|
118
|
-
project,
|
|
119
|
-
pat: azureConfig.pat,
|
|
120
|
-
apiVersion: azureConfig.apiVersion
|
|
121
|
-
});
|
|
122
|
-
const run = await client.request({
|
|
123
|
-
method: "POST",
|
|
124
|
-
path: `/_apis/pipelines/${pipeline.id}/runs`,
|
|
125
|
-
body: {
|
|
126
|
-
resources: {
|
|
127
|
-
repositories: {
|
|
128
|
-
self: {
|
|
129
|
-
refName: pipeline.refName
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
variables
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
return {
|
|
137
|
-
project,
|
|
138
|
-
instanceEnvironment: azureConfig.instanceEnvironment,
|
|
139
|
-
pipelineEnvironment: azureConfig.pipelineEnvironment,
|
|
140
|
-
pipelineId: pipeline.id,
|
|
141
|
-
refName: pipeline.refName,
|
|
142
|
-
run
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
function formatPipelineRunSummary(result) {
|
|
146
|
-
const {
|
|
147
|
-
run,
|
|
148
|
-
project,
|
|
149
|
-
instanceEnvironment,
|
|
150
|
-
pipelineEnvironment,
|
|
151
|
-
pipelineId,
|
|
152
|
-
refName
|
|
153
|
-
} = result;
|
|
154
|
-
const webUrl = run._links?.web?.href ?? run.url;
|
|
155
|
-
const envLabel = instanceEnvironment === pipelineEnvironment ? `\`${pipelineEnvironment}\`` : `\`${instanceEnvironment}\` \u2192 Azure \`${pipelineEnvironment}\``;
|
|
156
|
-
const lines = [
|
|
157
|
-
`Environment ${envLabel} \u2014 project \`${project}\`, pipeline id \`${pipelineId}\`, ref \`${refName}\``,
|
|
158
|
-
`Run #${run.id}${run.state ? ` \u2014 state: \`${run.state}\`` : ""}`
|
|
159
|
-
];
|
|
160
|
-
if (webUrl) {
|
|
161
|
-
lines.push(`<${webUrl}|Open run in Azure DevOps>`);
|
|
162
|
-
}
|
|
163
|
-
return lines.join("\n");
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// src/server/adapters/azure/environment.ts
|
|
167
|
-
var PRODUCTION_ALIASES = /* @__PURE__ */ new Set(["production", "live", "prod"]);
|
|
168
|
-
var STAGING_ALIASES = /* @__PURE__ */ new Set(["staging", "beta", "development", "dev", "local"]);
|
|
169
|
-
function normalizeToAzureEnvironment(instanceEnvironment) {
|
|
170
|
-
const key = instanceEnvironment.trim().toLowerCase();
|
|
171
|
-
if (!key) {
|
|
172
|
-
console.warn(
|
|
173
|
-
"[azure] Empty ENVIRONMENT; defaulting Azure pipeline environment to staging"
|
|
174
|
-
);
|
|
175
|
-
return "staging";
|
|
176
|
-
}
|
|
177
|
-
if (PRODUCTION_ALIASES.has(key)) {
|
|
178
|
-
return "production";
|
|
179
|
-
}
|
|
180
|
-
if (STAGING_ALIASES.has(key)) {
|
|
181
|
-
return "staging";
|
|
182
|
-
}
|
|
183
|
-
console.warn(
|
|
184
|
-
`[azure] Unrecognized ENVIRONMENT="${instanceEnvironment}"; defaulting Azure pipeline environment to staging`
|
|
185
|
-
);
|
|
186
|
-
return "staging";
|
|
187
|
-
}
|
|
188
|
-
function pipelineRefForAzure(pipelineEnvironment) {
|
|
189
|
-
return `refs/heads/${pipelineEnvironment}`;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// src/shared/content-refresh.ts
|
|
193
|
-
import { timingSafeEqual } from "crypto";
|
|
194
|
-
async function postContentRefresh(target, url, basicAuth, request) {
|
|
195
|
-
try {
|
|
196
|
-
const response = await fetch(url, {
|
|
197
|
-
method: "POST",
|
|
198
|
-
headers: {
|
|
199
|
-
Authorization: `Basic ${basicAuth}`,
|
|
200
|
-
"Content-Type": "application/json",
|
|
201
|
-
Accept: "application/json"
|
|
202
|
-
},
|
|
203
|
-
body: JSON.stringify(request)
|
|
204
|
-
});
|
|
205
|
-
const text = await response.text();
|
|
206
|
-
let body;
|
|
207
|
-
if (text) {
|
|
208
|
-
try {
|
|
209
|
-
body = JSON.parse(text);
|
|
210
|
-
} catch {
|
|
211
|
-
body = text;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return {
|
|
215
|
-
target,
|
|
216
|
-
ok: response.ok,
|
|
217
|
-
status: response.status,
|
|
218
|
-
body,
|
|
219
|
-
error: response.ok ? void 0 : typeof body === "object" && body !== null && "error" in body ? String(body.error) : `HTTP ${response.status}`
|
|
220
|
-
};
|
|
221
|
-
} catch (err) {
|
|
222
|
-
return {
|
|
223
|
-
target,
|
|
224
|
-
ok: false,
|
|
225
|
-
status: 0,
|
|
226
|
-
error: err instanceof Error ? err.message : String(err)
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// src/server/content-refresh-notify.ts
|
|
232
|
-
function isContentRefreshEnabledForInstance(instanceEnvironment) {
|
|
233
|
-
return normalizeToAzureEnvironment(instanceEnvironment) === "staging";
|
|
234
|
-
}
|
|
235
|
-
async function notifyContentRefreshTargets(notifyConfig, request, projects) {
|
|
236
|
-
if (!notifyConfig.enabled || !notifyConfig.basicAuth) {
|
|
237
|
-
return [];
|
|
238
|
-
}
|
|
239
|
-
const keys = projects ?? AZURE_DEVOPS_PROJECT_KEYS.filter(
|
|
240
|
-
(p) => notifyConfig.targets[p]?.url
|
|
241
|
-
);
|
|
242
|
-
const results = [];
|
|
243
|
-
for (const project of keys) {
|
|
244
|
-
const url = notifyConfig.targets[project]?.url;
|
|
245
|
-
if (!url) {
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
const result = await postContentRefresh(
|
|
249
|
-
project,
|
|
250
|
-
url,
|
|
251
|
-
notifyConfig.basicAuth,
|
|
252
|
-
request
|
|
253
|
-
);
|
|
254
|
-
results.push(result);
|
|
255
|
-
if (result.ok) {
|
|
256
|
-
console.log(
|
|
257
|
-
`[content-refresh] Notified ${project} (${result.status})`
|
|
258
|
-
);
|
|
259
|
-
} else {
|
|
260
|
-
console.error(
|
|
261
|
-
`[content-refresh] Failed to notify ${project}: ${result.error ?? result.status}`
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return results;
|
|
266
|
-
}
|
|
14
|
+
} from "./chunk-SF7FCBR2.js";
|
|
267
15
|
|
|
268
16
|
// src/server/config.ts
|
|
269
17
|
import dotenv from "dotenv";
|
|
270
|
-
|
|
271
|
-
// src/server/restrictedCron.ts
|
|
272
|
-
var MAX_SEARCH_MINUTES = 366 * 24 * 60;
|
|
273
|
-
function tokenize(expr) {
|
|
274
|
-
return expr.trim().split(/\s+/).map((s) => s.trim()).filter(Boolean);
|
|
275
|
-
}
|
|
276
|
-
function validateScheduleCronExpression(expr) {
|
|
277
|
-
const parts = tokenize(expr);
|
|
278
|
-
if (parts.length !== 2) {
|
|
279
|
-
throw new Error(
|
|
280
|
-
`Schedule cron must be exactly two fields (minute hour), whitespace-separated; got ${parts.length} field(s): "${expr}"`
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
const minuteSpec = parts[0];
|
|
284
|
-
const hourSpec = parts[1];
|
|
285
|
-
parseField(minuteSpec, 0, 59, "minute");
|
|
286
|
-
parseField(hourSpec, 0, 23, "hour");
|
|
287
|
-
return `${minuteSpec} ${hourSpec}`;
|
|
288
|
-
}
|
|
289
|
-
function parseField(spec, lo, hi, fieldName) {
|
|
290
|
-
const subs = spec.split(",").map((s) => s.trim()).filter(Boolean);
|
|
291
|
-
if (subs.length === 0) {
|
|
292
|
-
throw new Error(`Empty ${fieldName} field in cron`);
|
|
293
|
-
}
|
|
294
|
-
const preds = subs.map((sub) => parseSubfield(sub, lo, hi, fieldName));
|
|
295
|
-
return (n) => preds.some((p) => p(n));
|
|
296
|
-
}
|
|
297
|
-
function parseSubfield(sub, lo, hi, fieldName) {
|
|
298
|
-
if (sub === "*") {
|
|
299
|
-
return () => true;
|
|
300
|
-
}
|
|
301
|
-
if (sub.startsWith("*/")) {
|
|
302
|
-
const step = parseInt(sub.slice(2), 10);
|
|
303
|
-
if (!Number.isFinite(step) || step < 1) {
|
|
304
|
-
throw new Error(`Invalid step in ${fieldName} field: "${sub}"`);
|
|
305
|
-
}
|
|
306
|
-
return (n) => n >= lo && n <= hi && n % step === 0;
|
|
307
|
-
}
|
|
308
|
-
if (sub.includes("-")) {
|
|
309
|
-
const [a, b] = sub.split("-").map((x) => parseInt(x.trim(), 10));
|
|
310
|
-
if (!Number.isFinite(a) || !Number.isFinite(b)) {
|
|
311
|
-
throw new Error(`Invalid range in ${fieldName} field: "${sub}"`);
|
|
312
|
-
}
|
|
313
|
-
if (a < lo || b > hi || a > b) {
|
|
314
|
-
throw new Error(`Range out of bounds in ${fieldName} field: "${sub}"`);
|
|
315
|
-
}
|
|
316
|
-
return (n) => n >= a && n <= b;
|
|
317
|
-
}
|
|
318
|
-
const v = parseInt(sub, 10);
|
|
319
|
-
if (!Number.isFinite(v) || v < lo || v > hi) {
|
|
320
|
-
throw new Error(`Invalid value in ${fieldName} field: "${sub}"`);
|
|
321
|
-
}
|
|
322
|
-
return (n) => n === v;
|
|
323
|
-
}
|
|
324
|
-
function floorToMinuteStart(d) {
|
|
325
|
-
const t = new Date(d.getTime());
|
|
326
|
-
t.setSeconds(0, 0);
|
|
327
|
-
return t;
|
|
328
|
-
}
|
|
329
|
-
function addOneMinute(d) {
|
|
330
|
-
const t = new Date(d.getTime());
|
|
331
|
-
t.setMinutes(t.getMinutes() + 1, 0, 0);
|
|
332
|
-
return t;
|
|
333
|
-
}
|
|
334
|
-
function matchesAtMinuteStart(minutePred, hourPred, d) {
|
|
335
|
-
return minutePred(d.getMinutes()) && hourPred(d.getHours());
|
|
336
|
-
}
|
|
337
|
-
function nextCronFireAfter(expr, after) {
|
|
338
|
-
const parts = tokenize(expr);
|
|
339
|
-
if (parts.length !== 2) {
|
|
340
|
-
throw new Error(
|
|
341
|
-
`nextCronFireAfter expects exactly two fields (minute hour); got ${parts.length}: "${expr}"`
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
const minutePred = parseField(parts[0], 0, 59, "minute");
|
|
345
|
-
const hourPred = parseField(parts[1], 0, 23, "hour");
|
|
346
|
-
let d = floorToMinuteStart(after);
|
|
347
|
-
if (d.getTime() <= after.getTime()) {
|
|
348
|
-
d = addOneMinute(d);
|
|
349
|
-
}
|
|
350
|
-
for (let i = 0; i < MAX_SEARCH_MINUTES; i++) {
|
|
351
|
-
if (matchesAtMinuteStart(minutePred, hourPred, d)) {
|
|
352
|
-
return d;
|
|
353
|
-
}
|
|
354
|
-
d = addOneMinute(d);
|
|
355
|
-
}
|
|
356
|
-
throw new Error(`No cron match within ${MAX_SEARCH_MINUTES} minutes for "${expr}"`);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// src/server/config.ts
|
|
360
18
|
dotenv.config({ path: ".env.local" });
|
|
361
19
|
dotenv.config();
|
|
362
|
-
function readScheduleCronEnv() {
|
|
363
|
-
return {
|
|
364
|
-
scheduleCron: (process.env.SCHEDULE_CRON ?? "").trim(),
|
|
365
|
-
cmsCronRaw: (process.env.SCHEDULE_CMS_CRON ?? "").trim(),
|
|
366
|
-
translationCronRaw: (process.env.SCHEDULE_TRANSLATION_CRON ?? "").trim()
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
function resolveScheduleCron(jobSpecific, globalCron, jobLabel) {
|
|
370
|
-
const raw = jobSpecific || globalCron;
|
|
371
|
-
if (!raw) return null;
|
|
372
|
-
try {
|
|
373
|
-
return validateScheduleCronExpression(raw);
|
|
374
|
-
} catch (e) {
|
|
375
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
376
|
-
console.error(`[config] Invalid ${jobLabel} schedule cron: ${msg}`);
|
|
377
|
-
return null;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
20
|
function parseScheduledCmsJobConfig() {
|
|
381
|
-
const
|
|
21
|
+
const intervalMinutes = parseInt(process.env.SCHEDULE_INTERVAL_MINUTES ?? "0", 10);
|
|
22
|
+
const enabled = Number.isFinite(intervalMinutes) && intervalMinutes > 0;
|
|
382
23
|
const runOnStart = (process.env.SCHEDULE_RUN_ON_START ?? "true").toLowerCase() !== "false";
|
|
24
|
+
const task = (process.env.SCHEDULE_TASK ?? "sync").trim();
|
|
383
25
|
const syncCms = (process.env.SCHEDULE_SYNC_CMS ?? "").trim();
|
|
384
|
-
const rawTypes = process.env.
|
|
26
|
+
const rawTypes = process.env.SCHEDULE_SYNC_TYPES?.trim();
|
|
385
27
|
const syncTypes = rawTypes ? rawTypes.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
386
|
-
const resolved = resolveScheduleCron(cmsCronRaw, scheduleCron, "CMS");
|
|
387
|
-
const cmsOk = !!syncCms && (syncCms === "contentful" || syncCms === "sanity");
|
|
388
|
-
if (resolved !== null && !cmsOk) {
|
|
389
|
-
console.warn(
|
|
390
|
-
'[config] CMS schedule cron is set but SCHEDULE_SYNC_CMS is missing or not "contentful"|"sanity"; scheduled CMS sync is disabled.'
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
const enabled = resolved !== null && cmsOk;
|
|
394
28
|
return {
|
|
395
29
|
enabled,
|
|
396
|
-
|
|
30
|
+
intervalMinutes,
|
|
397
31
|
runOnStart,
|
|
32
|
+
task,
|
|
398
33
|
syncCms: syncCms || void 0,
|
|
399
34
|
syncTypes
|
|
400
35
|
};
|
|
401
36
|
}
|
|
402
37
|
function parseScheduledTranslationJobConfig() {
|
|
403
|
-
const
|
|
38
|
+
const intervalMinutes = parseInt(process.env.SCHEDULE_INTERVAL_MINUTES ?? "0", 10);
|
|
39
|
+
const enabled = Number.isFinite(intervalMinutes) && intervalMinutes > 0;
|
|
404
40
|
const runOnStart = (process.env.SCHEDULE_RUN_ON_START ?? "true").toLowerCase() !== "false";
|
|
41
|
+
const task = (process.env.SCHEDULE_TASK ?? "sync").trim();
|
|
405
42
|
const rawProjects = process.env.SCHEDULE_SYNC_TRANSLATION_PROJECTS?.trim();
|
|
406
43
|
const syncProjects = rawProjects ? rawProjects.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
407
|
-
const resolved = resolveScheduleCron(
|
|
408
|
-
translationCronRaw,
|
|
409
|
-
scheduleCron,
|
|
410
|
-
"translation"
|
|
411
|
-
);
|
|
412
|
-
const enabled = resolved !== null;
|
|
413
44
|
return {
|
|
414
45
|
enabled,
|
|
415
|
-
|
|
46
|
+
intervalMinutes,
|
|
416
47
|
runOnStart,
|
|
48
|
+
task,
|
|
417
49
|
syncProjects
|
|
418
50
|
};
|
|
419
51
|
}
|
|
420
|
-
function projectEnvSlug(project) {
|
|
421
|
-
return project.toUpperCase().replace(/-/g, "_");
|
|
422
|
-
}
|
|
423
|
-
function readProjectPipeline(project, pipelineEnvironment) {
|
|
424
|
-
const slug = projectEnvSlug(project);
|
|
425
|
-
const id = parseInt(process.env[`AZURE_${slug}_PIPELINE_ID`]?.trim() ?? "0", 10);
|
|
426
|
-
return { id, refName: pipelineRefForAzure(pipelineEnvironment) };
|
|
427
|
-
}
|
|
428
|
-
function parseAzureDevOpsConfig(instanceEnvironment) {
|
|
429
|
-
const pipelineEnvironment = normalizeToAzureEnvironment(instanceEnvironment);
|
|
430
|
-
const pat = process.env.AZURE_DEVOPS_ACCESS_TOKEN?.trim() ?? "";
|
|
431
|
-
const defaultProjectRaw = (process.env.AZURE_DEVOPS_DEFAULT_PROJECT ?? "web-site").trim();
|
|
432
|
-
const defaultProject = AZURE_DEVOPS_PROJECT_KEYS.includes(
|
|
433
|
-
defaultProjectRaw
|
|
434
|
-
) ? defaultProjectRaw : "web-site";
|
|
435
|
-
const projects = Object.fromEntries(
|
|
436
|
-
AZURE_DEVOPS_PROJECT_KEYS.map((project) => [
|
|
437
|
-
project,
|
|
438
|
-
{ pipeline: readProjectPipeline(project, pipelineEnvironment) }
|
|
439
|
-
])
|
|
440
|
-
);
|
|
441
|
-
return {
|
|
442
|
-
enabled: pat.length > 0,
|
|
443
|
-
organization: process.env.AZURE_DEVOPS_ORGANIZATION?.trim() || "tripod-technology",
|
|
444
|
-
pat,
|
|
445
|
-
apiVersion: process.env.AZURE_DEVOPS_API_VERSION?.trim() || "7.1",
|
|
446
|
-
defaultProject,
|
|
447
|
-
instanceEnvironment,
|
|
448
|
-
pipelineEnvironment,
|
|
449
|
-
projects
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
function readContentRefreshUrl(project) {
|
|
453
|
-
const slug = projectEnvSlug(project);
|
|
454
|
-
return process.env[`CONTENT_REFRESH_${slug}_URL`]?.trim() || void 0;
|
|
455
|
-
}
|
|
456
|
-
function parseContentRefreshConfig(instanceEnvironment) {
|
|
457
|
-
const targets = Object.fromEntries(
|
|
458
|
-
AZURE_DEVOPS_PROJECT_KEYS.map((project) => [
|
|
459
|
-
project,
|
|
460
|
-
{ url: readContentRefreshUrl(project) }
|
|
461
|
-
])
|
|
462
|
-
);
|
|
463
|
-
return {
|
|
464
|
-
enabled: isContentRefreshEnabledForInstance(instanceEnvironment),
|
|
465
|
-
basicAuth: process.env.CONTENT_REFRESH_BASIC_AUTH?.trim() ?? "",
|
|
466
|
-
targets
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
52
|
var config2 = {
|
|
470
53
|
...config,
|
|
471
54
|
contentful: {
|
|
@@ -506,8 +89,6 @@ var config2 = {
|
|
|
506
89
|
},
|
|
507
90
|
scheduledCmsJob: parseScheduledCmsJobConfig(),
|
|
508
91
|
scheduledTranslationJob: parseScheduledTranslationJobConfig(),
|
|
509
|
-
azure: parseAzureDevOpsConfig(config.environment),
|
|
510
|
-
contentRefresh: parseContentRefreshConfig(config.environment),
|
|
511
92
|
slack: {
|
|
512
93
|
enabled: (process.env.SLACK_BOT_TOKEN ?? "").length > 0,
|
|
513
94
|
botToken: process.env.SLACK_BOT_TOKEN ?? "",
|
|
@@ -515,8 +96,7 @@ var config2 = {
|
|
|
515
96
|
appToken: process.env.SLACK_APP_TOKEN ?? "",
|
|
516
97
|
notifyChannel: process.env.SLACK_NOTIFY_CHANNEL ?? "",
|
|
517
98
|
cmdSyncContent: process.env.SLACK_CMD_SYNC_CONTENT ?? "/sync-content",
|
|
518
|
-
cmdSyncTranslations: process.env.SLACK_CMD_SYNC_TRANSLATIONS ?? "/sync-translations"
|
|
519
|
-
cmdTriggerBuild: process.env.SLACK_CMD_TRIGGER_BUILD ?? "/trigger-build"
|
|
99
|
+
cmdSyncTranslations: process.env.SLACK_CMD_SYNC_TRANSLATIONS ?? "/sync-translations"
|
|
520
100
|
}
|
|
521
101
|
};
|
|
522
102
|
|
|
@@ -872,8 +452,8 @@ async function syncTranslations(projects, locales) {
|
|
|
872
452
|
);
|
|
873
453
|
} catch (err) {
|
|
874
454
|
const message = err instanceof Error ? err.message : String(err);
|
|
875
|
-
errors.push({ project, locale, error: message });
|
|
876
|
-
console.error(` x ${project} - ${locale}: ${message}`);
|
|
455
|
+
errors.push({ project, locale, resource: resource.resource, error: message });
|
|
456
|
+
console.error(` x ${project} / ${resource.resource} - ${locale}: ${message}`);
|
|
877
457
|
}
|
|
878
458
|
}
|
|
879
459
|
}
|
|
@@ -882,12 +462,6 @@ async function syncTranslations(projects, locales) {
|
|
|
882
462
|
}
|
|
883
463
|
|
|
884
464
|
export {
|
|
885
|
-
AZURE_DEVOPS_PROJECT_KEYS,
|
|
886
|
-
isAzureDevOpsProjectKey,
|
|
887
|
-
triggerPipelineBuild,
|
|
888
|
-
formatPipelineRunSummary,
|
|
889
|
-
notifyContentRefreshTargets,
|
|
890
|
-
nextCronFireAfter,
|
|
891
465
|
config2 as config,
|
|
892
466
|
summariseCmsEntries,
|
|
893
467
|
summariseCmsErrors,
|
|
@@ -895,4 +469,4 @@ export {
|
|
|
895
469
|
syncCmsContent,
|
|
896
470
|
syncTranslations
|
|
897
471
|
};
|
|
898
|
-
//# sourceMappingURL=chunk-
|
|
472
|
+
//# sourceMappingURL=chunk-NQHWG4XM.js.map
|