@tandem-language-exchange/content-store 1.3.0 → 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/README.md +9 -9
- 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-ZX5KC4F5.js → chunk-NQHWG4XM.js} +18 -447
- 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-HD7E5M5O.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 -89
- package/dist/node.js +4 -212
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-HD7E5M5O.js.map +0 -1
- package/dist/chunk-OCAIIQZW.js.map +0 -1
- package/dist/chunk-RZLDRXNQ.js.map +0 -1
- package/dist/chunk-VBJ6LVMY.js.map +0 -1
- package/dist/chunk-ZX5KC4F5.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
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ On **staging** (pages rebuild on request), content-store can tell your app to pu
|
|
|
113
113
|
|
|
114
114
|
```http
|
|
115
115
|
POST /api/internal/content-refresh
|
|
116
|
-
Authorization:
|
|
116
|
+
Authorization: Basic <base64(username:password)>
|
|
117
117
|
Content-Type: application/json
|
|
118
118
|
|
|
119
119
|
{"scope":"cms","cms":"contentful","content_types":["page","banner"]}
|
|
@@ -157,7 +157,7 @@ import { contentRefreshDefaults, contentStoreSdk } from '../../lib/content-store
|
|
|
157
157
|
|
|
158
158
|
const handler = createNextPagesApiContentRefreshHandler({
|
|
159
159
|
sdk: contentStoreSdk,
|
|
160
|
-
|
|
160
|
+
basicAuthBase64: process.env.CONTENT_REFRESH_BASIC_AUTH!,
|
|
161
161
|
defaults: contentRefreshDefaults,
|
|
162
162
|
});
|
|
163
163
|
|
|
@@ -180,24 +180,24 @@ import { contentRefreshDefaults, contentStoreSdk } from '@/lib/content-store';
|
|
|
180
180
|
export async function POST(request: Request) {
|
|
181
181
|
return handleNextAppRouterContentRefresh(request, {
|
|
182
182
|
sdk: contentStoreSdk,
|
|
183
|
-
|
|
183
|
+
basicAuthBase64: process.env.CONTENT_REFRESH_BASIC_AUTH!,
|
|
184
184
|
defaults: contentRefreshDefaults,
|
|
185
185
|
});
|
|
186
186
|
}
|
|
187
187
|
```
|
|
188
188
|
|
|
189
189
|
Point content-store staging at the deployed URL, e.g.
|
|
190
|
-
`
|
|
190
|
+
`CONTENT_REFRESH_WEB_SITE_URL=https://staging.example.com/api/internal/content-refresh`
|
|
191
191
|
|
|
192
192
|
After refresh, load bundles from `outputDir` in `getStaticProps`, `getServerSideProps`, or Server Components on the next request.
|
|
193
193
|
|
|
194
194
|
### Configure content-store (staging instance)
|
|
195
195
|
|
|
196
|
-
| Variable
|
|
197
|
-
|
|
198
|
-
| `
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
196
|
+
| Variable | Description |
|
|
197
|
+
|--------------------------------| --- |
|
|
198
|
+
| `CONTENT_REFRESH_WEB_SITE_URL` | Full URL to web-site refresh endpoint |
|
|
199
|
+
| `CONTENT_REFRESH_WEB_APP_URL` | Full URL to web-app refresh endpoint |
|
|
200
|
+
| `CONTENT_REFRESH_BASIC_AUTH` | Base64 of staging site basic-auth `username:password` (no `Basic ` prefix). Generate with `encodeBasicAuthCredentials` from the package or `printf '%s' 'user:pass' \| base64` |
|
|
201
201
|
|
|
202
202
|
After `POST /syncCmsContent` completes successfully on a **beta/staging** instance, content-store notifies every configured URL automatically.
|
|
203
203
|
|
|
@@ -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, apiToken, request) {
|
|
195
|
-
try {
|
|
196
|
-
const response = await fetch(url, {
|
|
197
|
-
method: "POST",
|
|
198
|
-
headers: {
|
|
199
|
-
Authorization: `Bearer ${apiToken}`,
|
|
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.apiToken) {
|
|
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.apiToken,
|
|
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[`AZURE_${slug}_CONTENT_REFRESH_URL`]?.trim() || void 0;
|
|
455
|
-
}
|
|
456
|
-
function parseContentRefreshConfig(instanceEnvironment, contentStoreApiToken) {
|
|
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
|
-
apiToken: process.env.CONTENT_REFRESH_API_TOKEN?.trim() || contentStoreApiToken,
|
|
466
|
-
targets
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
52
|
var config2 = {
|
|
470
53
|
...config,
|
|
471
54
|
contentful: {
|
|
@@ -506,11 +89,6 @@ var config2 = {
|
|
|
506
89
|
},
|
|
507
90
|
scheduledCmsJob: parseScheduledCmsJobConfig(),
|
|
508
91
|
scheduledTranslationJob: parseScheduledTranslationJobConfig(),
|
|
509
|
-
azure: parseAzureDevOpsConfig(config.environment),
|
|
510
|
-
contentRefresh: parseContentRefreshConfig(
|
|
511
|
-
config.environment,
|
|
512
|
-
process.env.CONTENT_STORE_API_TOKEN ?? ""
|
|
513
|
-
),
|
|
514
92
|
slack: {
|
|
515
93
|
enabled: (process.env.SLACK_BOT_TOKEN ?? "").length > 0,
|
|
516
94
|
botToken: process.env.SLACK_BOT_TOKEN ?? "",
|
|
@@ -518,8 +96,7 @@ var config2 = {
|
|
|
518
96
|
appToken: process.env.SLACK_APP_TOKEN ?? "",
|
|
519
97
|
notifyChannel: process.env.SLACK_NOTIFY_CHANNEL ?? "",
|
|
520
98
|
cmdSyncContent: process.env.SLACK_CMD_SYNC_CONTENT ?? "/sync-content",
|
|
521
|
-
cmdSyncTranslations: process.env.SLACK_CMD_SYNC_TRANSLATIONS ?? "/sync-translations"
|
|
522
|
-
cmdTriggerBuild: process.env.SLACK_CMD_TRIGGER_BUILD ?? "/trigger-build"
|
|
99
|
+
cmdSyncTranslations: process.env.SLACK_CMD_SYNC_TRANSLATIONS ?? "/sync-translations"
|
|
523
100
|
}
|
|
524
101
|
};
|
|
525
102
|
|
|
@@ -875,8 +452,8 @@ async function syncTranslations(projects, locales) {
|
|
|
875
452
|
);
|
|
876
453
|
} catch (err) {
|
|
877
454
|
const message = err instanceof Error ? err.message : String(err);
|
|
878
|
-
errors.push({ project, locale, error: message });
|
|
879
|
-
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}`);
|
|
880
457
|
}
|
|
881
458
|
}
|
|
882
459
|
}
|
|
@@ -885,12 +462,6 @@ async function syncTranslations(projects, locales) {
|
|
|
885
462
|
}
|
|
886
463
|
|
|
887
464
|
export {
|
|
888
|
-
AZURE_DEVOPS_PROJECT_KEYS,
|
|
889
|
-
isAzureDevOpsProjectKey,
|
|
890
|
-
triggerPipelineBuild,
|
|
891
|
-
formatPipelineRunSummary,
|
|
892
|
-
notifyContentRefreshTargets,
|
|
893
|
-
nextCronFireAfter,
|
|
894
465
|
config2 as config,
|
|
895
466
|
summariseCmsEntries,
|
|
896
467
|
summariseCmsErrors,
|
|
@@ -898,4 +469,4 @@ export {
|
|
|
898
469
|
syncCmsContent,
|
|
899
470
|
syncTranslations
|
|
900
471
|
};
|
|
901
|
-
//# sourceMappingURL=chunk-
|
|
472
|
+
//# sourceMappingURL=chunk-NQHWG4XM.js.map
|