lingo.dev 0.111.16 → 0.112.0

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/build/cli.mjs CHANGED
@@ -2713,6 +2713,133 @@ ${content}`;
2713
2713
  });
2714
2714
  }
2715
2715
 
2716
+ // src/cli/loaders/markdoc.ts
2717
+ import Markdoc from "@markdoc/markdoc";
2718
+ import YAML3 from "yaml";
2719
+ var FM_ATTR_PREFIX2 = "fm-attr-";
2720
+ function createMarkdocLoader() {
2721
+ return createLoader({
2722
+ async pull(locale, input2) {
2723
+ const ast = Markdoc.parse(input2);
2724
+ const result = {};
2725
+ const counters = {};
2726
+ traverseAndExtract(ast, "", result, counters);
2727
+ if (ast.attributes?.frontmatter) {
2728
+ const frontmatter = YAML3.parse(ast.attributes.frontmatter);
2729
+ Object.entries(frontmatter).forEach(([key, value]) => {
2730
+ if (typeof value === "string") {
2731
+ result[`${FM_ATTR_PREFIX2}${key}`] = value;
2732
+ }
2733
+ });
2734
+ }
2735
+ return result;
2736
+ },
2737
+ async push(locale, data, originalInput) {
2738
+ if (!originalInput) {
2739
+ throw new Error("Original input is required for push");
2740
+ }
2741
+ const ast = Markdoc.parse(originalInput);
2742
+ const counters = {};
2743
+ const pathMap = {};
2744
+ buildPathMap(ast, "", counters, pathMap);
2745
+ const frontmatterEntries = Object.entries(data).filter(([key]) => key.startsWith(FM_ATTR_PREFIX2)).map(([key, value]) => [key.replace(FM_ATTR_PREFIX2, ""), value]);
2746
+ if (frontmatterEntries.length > 0 && ast.attributes) {
2747
+ const frontmatter = Object.fromEntries(frontmatterEntries);
2748
+ ast.attributes.frontmatter = YAML3.stringify(frontmatter, {
2749
+ defaultStringType: "PLAIN"
2750
+ }).trim();
2751
+ }
2752
+ const contentData = Object.fromEntries(
2753
+ Object.entries(data).filter(([key]) => !key.startsWith(FM_ATTR_PREFIX2))
2754
+ );
2755
+ applyTranslations(ast, "", contentData, pathMap);
2756
+ return Markdoc.format(ast);
2757
+ }
2758
+ });
2759
+ }
2760
+ function getSemanticNodeType(node) {
2761
+ if (node.type === "tag") return node.tag || "tag";
2762
+ return node.type;
2763
+ }
2764
+ function traverseAndExtract(node, path19, result, counters, parentType) {
2765
+ if (!node || typeof node !== "object") {
2766
+ return;
2767
+ }
2768
+ let semanticType = parentType;
2769
+ const nodeSemanticType = getSemanticNodeType(node);
2770
+ if (nodeSemanticType && !["text", "strong", "em", "inline", "link"].includes(nodeSemanticType)) {
2771
+ semanticType = nodeSemanticType;
2772
+ }
2773
+ if (node.type === "text" && node.attributes?.content) {
2774
+ const content = node.attributes.content;
2775
+ if (typeof content === "string" && content.trim()) {
2776
+ if (semanticType) {
2777
+ const index = counters[semanticType] || 0;
2778
+ counters[semanticType] = index + 1;
2779
+ const semanticKey = `${semanticType}-${index}`;
2780
+ result[semanticKey] = content;
2781
+ }
2782
+ }
2783
+ }
2784
+ if (Array.isArray(node.children)) {
2785
+ node.children.forEach((child, index) => {
2786
+ const childPath = path19 ? `${path19}/children/${index}` : `children/${index}`;
2787
+ traverseAndExtract(child, childPath, result, counters, semanticType);
2788
+ });
2789
+ }
2790
+ }
2791
+ function buildPathMap(node, path19, counters, pathMap, parentType) {
2792
+ if (!node || typeof node !== "object") {
2793
+ return;
2794
+ }
2795
+ let semanticType = parentType;
2796
+ const nodeSemanticType = getSemanticNodeType(node);
2797
+ if (nodeSemanticType && !["text", "strong", "em", "inline", "link"].includes(nodeSemanticType)) {
2798
+ semanticType = nodeSemanticType;
2799
+ }
2800
+ if (node.type === "text" && node.attributes?.content) {
2801
+ const content = node.attributes.content;
2802
+ if (typeof content === "string" && content.trim()) {
2803
+ if (semanticType) {
2804
+ const index = counters[semanticType] || 0;
2805
+ counters[semanticType] = index + 1;
2806
+ const semanticKey = `${semanticType}-${index}`;
2807
+ const contentPath = path19 ? `${path19}/attributes/content` : "attributes/content";
2808
+ pathMap[semanticKey] = contentPath;
2809
+ }
2810
+ }
2811
+ }
2812
+ if (Array.isArray(node.children)) {
2813
+ node.children.forEach((child, index) => {
2814
+ const childPath = path19 ? `${path19}/children/${index}` : `children/${index}`;
2815
+ buildPathMap(child, childPath, counters, pathMap, semanticType);
2816
+ });
2817
+ }
2818
+ }
2819
+ function applyTranslations(node, path19, data, pathMap) {
2820
+ if (!node || typeof node !== "object") {
2821
+ return;
2822
+ }
2823
+ if (node.type === "text" && node.attributes?.content) {
2824
+ const content = node.attributes.content;
2825
+ if (typeof content === "string") {
2826
+ const contentPath = path19 ? `${path19}/attributes/content` : "attributes/content";
2827
+ const semanticKey = Object.keys(pathMap).find(
2828
+ (key) => pathMap[key] === contentPath
2829
+ );
2830
+ if (semanticKey && data[semanticKey] !== void 0) {
2831
+ node.attributes.content = data[semanticKey];
2832
+ }
2833
+ }
2834
+ }
2835
+ if (Array.isArray(node.children)) {
2836
+ node.children.forEach((child, index) => {
2837
+ const childPath = path19 ? `${path19}/children/${index}` : `children/${index}`;
2838
+ applyTranslations(child, childPath, data, pathMap);
2839
+ });
2840
+ }
2841
+ }
2842
+
2716
2843
  // src/cli/loaders/properties.ts
2717
2844
  function createPropertiesLoader() {
2718
2845
  return createLoader({
@@ -6656,7 +6783,7 @@ function _isLockedKey(key, lockedKeys) {
6656
6783
 
6657
6784
  // src/cli/loaders/mdx2/frontmatter-split.ts
6658
6785
  import matter2 from "gray-matter";
6659
- import YAML3 from "yaml";
6786
+ import YAML4 from "yaml";
6660
6787
  function createMdxFrontmatterSplitLoader() {
6661
6788
  const fmEngine = createFmEngine();
6662
6789
  return createLoader({
@@ -6677,8 +6804,8 @@ function createMdxFrontmatterSplitLoader() {
6677
6804
  }
6678
6805
  function createFmEngine() {
6679
6806
  const yamlEngine2 = {
6680
- parse: (str) => YAML3.parse(str),
6681
- stringify: (obj) => YAML3.stringify(obj, { defaultStringType: "PLAIN" })
6807
+ parse: (str) => YAML4.parse(str),
6808
+ stringify: (obj) => YAML4.stringify(obj, { defaultStringType: "PLAIN" })
6682
6809
  };
6683
6810
  return {
6684
6811
  parse: (input2) => matter2(input2, {
@@ -7286,6 +7413,15 @@ function createBucketLoader(bucketType, bucketPathPattern, options, lockedKeys,
7286
7413
  createSyncLoader(),
7287
7414
  createUnlocalizableLoader(options.returnUnlocalizedKeys)
7288
7415
  );
7416
+ case "markdoc":
7417
+ return composeLoaders(
7418
+ createTextFileLoader(bucketPathPattern),
7419
+ createMarkdocLoader(),
7420
+ createFlatLoader(),
7421
+ createEnsureKeyOrderLoader(),
7422
+ createSyncLoader(),
7423
+ createUnlocalizableLoader(options.returnUnlocalizedKeys)
7424
+ );
7289
7425
  case "mdx":
7290
7426
  return composeLoaders(
7291
7427
  createTextFileLoader(bucketPathPattern),
@@ -7517,7 +7653,7 @@ function createLingoLocalizer(params) {
7517
7653
  // src/cli/processor/basic.ts
7518
7654
  import { generateText } from "ai";
7519
7655
  import _29 from "lodash";
7520
- function createBasicTranslator(model, systemPrompt) {
7656
+ function createBasicTranslator(model, systemPrompt, settings = {}) {
7521
7657
  return async (input2, onProgress) => {
7522
7658
  const chunks = extractPayloadChunks(input2.processableData);
7523
7659
  const subResults = [];
@@ -7539,6 +7675,7 @@ function createBasicTranslator(model, systemPrompt) {
7539
7675
  }
7540
7676
  const response = await generateText({
7541
7677
  model,
7678
+ ...settings,
7542
7679
  messages: [
7543
7680
  {
7544
7681
  role: "system",
@@ -7629,7 +7766,8 @@ function createProcessor(provider, params) {
7629
7766
  return result;
7630
7767
  } else {
7631
7768
  const model = getPureModelProvider(provider);
7632
- const result = createBasicTranslator(model, provider.prompt);
7769
+ const settings = provider.settings || {};
7770
+ const result = createBasicTranslator(model, provider.prompt, settings);
7633
7771
  return result;
7634
7772
  }
7635
7773
  }
@@ -7832,7 +7970,7 @@ function checkIfFileExists(filePath) {
7832
7970
 
7833
7971
  // src/cli/utils/delta.ts
7834
7972
  import * as path15 from "path";
7835
- import YAML4 from "yaml";
7973
+ import YAML5 from "yaml";
7836
7974
  var LockSchema = z.object({
7837
7975
  version: z.literal(1).default(1),
7838
7976
  checksums: z.record(
@@ -7897,7 +8035,7 @@ function createDeltaProcessor(fileKey) {
7897
8035
  },
7898
8036
  async loadLock() {
7899
8037
  const lockfileContent = tryReadFile(lockfilePath, null);
7900
- const lockfileYaml = lockfileContent ? YAML4.parse(lockfileContent) : null;
8038
+ const lockfileYaml = lockfileContent ? YAML5.parse(lockfileContent) : null;
7901
8039
  const lockfileData = lockfileYaml ? LockSchema.parse(lockfileYaml) : {
7902
8040
  version: 1,
7903
8041
  checksums: {}
@@ -7905,7 +8043,7 @@ function createDeltaProcessor(fileKey) {
7905
8043
  return lockfileData;
7906
8044
  },
7907
8045
  async saveLock(lockData) {
7908
- const lockfileYaml = YAML4.stringify(lockData);
8046
+ const lockfileYaml = YAML5.stringify(lockData);
7909
8047
  writeFile(lockfilePath, lockfileYaml);
7910
8048
  },
7911
8049
  async loadChecksums() {
@@ -8587,7 +8725,7 @@ import Ora8 from "ora";
8587
8725
  import fs12 from "fs";
8588
8726
  import path16 from "path";
8589
8727
  import Z4 from "zod";
8590
- import YAML5 from "yaml";
8728
+ import YAML6 from "yaml";
8591
8729
  import { MD5 as MD52 } from "object-hash";
8592
8730
  import _32 from "lodash";
8593
8731
  function createLockfileHelper() {
@@ -8635,12 +8773,12 @@ function createLockfileHelper() {
8635
8773
  return LockfileSchema.parse({});
8636
8774
  }
8637
8775
  const content = fs12.readFileSync(lockfilePath, "utf-8");
8638
- const result = LockfileSchema.parse(YAML5.parse(content));
8776
+ const result = LockfileSchema.parse(YAML6.parse(content));
8639
8777
  return result;
8640
8778
  }
8641
8779
  function _saveLockfile(lockfile) {
8642
8780
  const lockfilePath = _getLockfilePath();
8643
- const content = YAML5.stringify(lockfile);
8781
+ const content = YAML6.stringify(lockfile);
8644
8782
  fs12.writeFileSync(lockfilePath, content);
8645
8783
  }
8646
8784
  function _getLockfilePath() {
@@ -8981,8 +9119,9 @@ function createLingoDotDevLocalizer(explicitApiKey) {
8981
9119
  authenticated: !!response,
8982
9120
  username: response?.email
8983
9121
  };
8984
- } catch {
8985
- return { authenticated: false };
9122
+ } catch (error) {
9123
+ const errorMessage = error instanceof Error ? error.message : String(error);
9124
+ return { authenticated: false, error: errorMessage };
8986
9125
  }
8987
9126
  },
8988
9127
  localize: async (input2, onProgress) => {
@@ -9019,6 +9158,7 @@ import { generateText as generateText2 } from "ai";
9019
9158
  import { jsonrepair as jsonrepair3 } from "jsonrepair";
9020
9159
  import { createOllama as createOllama2 } from "ollama-ai-provider";
9021
9160
  function createExplicitLocalizer(provider) {
9161
+ const settings = provider.settings || {};
9022
9162
  switch (provider.id) {
9023
9163
  default:
9024
9164
  throw new Error(
@@ -9042,7 +9182,8 @@ function createExplicitLocalizer(provider) {
9042
9182
  id: provider.id,
9043
9183
  prompt: provider.prompt,
9044
9184
  apiKeyName: "OPENAI_API_KEY",
9045
- baseUrl: provider.baseUrl
9185
+ baseUrl: provider.baseUrl,
9186
+ settings
9046
9187
  });
9047
9188
  case "anthropic":
9048
9189
  return createAiSdkLocalizer({
@@ -9050,7 +9191,8 @@ function createExplicitLocalizer(provider) {
9050
9191
  id: provider.id,
9051
9192
  prompt: provider.prompt,
9052
9193
  apiKeyName: "ANTHROPIC_API_KEY",
9053
- baseUrl: provider.baseUrl
9194
+ baseUrl: provider.baseUrl,
9195
+ settings
9054
9196
  });
9055
9197
  case "google":
9056
9198
  return createAiSdkLocalizer({
@@ -9058,7 +9200,8 @@ function createExplicitLocalizer(provider) {
9058
9200
  id: provider.id,
9059
9201
  prompt: provider.prompt,
9060
9202
  apiKeyName: "GOOGLE_API_KEY",
9061
- baseUrl: provider.baseUrl
9203
+ baseUrl: provider.baseUrl,
9204
+ settings
9062
9205
  });
9063
9206
  case "openrouter":
9064
9207
  return createAiSdkLocalizer({
@@ -9066,14 +9209,16 @@ function createExplicitLocalizer(provider) {
9066
9209
  id: provider.id,
9067
9210
  prompt: provider.prompt,
9068
9211
  apiKeyName: "OPENROUTER_API_KEY",
9069
- baseUrl: provider.baseUrl
9212
+ baseUrl: provider.baseUrl,
9213
+ settings
9070
9214
  });
9071
9215
  case "ollama":
9072
9216
  return createAiSdkLocalizer({
9073
9217
  factory: (_params) => createOllama2().languageModel(provider.model),
9074
9218
  id: provider.id,
9075
9219
  prompt: provider.prompt,
9076
- skipAuth: true
9220
+ skipAuth: true,
9221
+ settings
9077
9222
  });
9078
9223
  case "mistral":
9079
9224
  return createAiSdkLocalizer({
@@ -9081,7 +9226,8 @@ function createExplicitLocalizer(provider) {
9081
9226
  id: provider.id,
9082
9227
  prompt: provider.prompt,
9083
9228
  apiKeyName: "MISTRAL_API_KEY",
9084
- baseUrl: provider.baseUrl
9229
+ baseUrl: provider.baseUrl,
9230
+ settings
9085
9231
  });
9086
9232
  }
9087
9233
  }
@@ -9115,9 +9261,13 @@ function createAiSdkLocalizer(params) {
9115
9261
  return {
9116
9262
  id: params.id,
9117
9263
  checkAuth: async () => {
9264
+ return { authenticated: true, username: "anonymous" };
9265
+ },
9266
+ validateSettings: async () => {
9118
9267
  try {
9119
9268
  await generateText2({
9120
9269
  model,
9270
+ ...params.settings,
9121
9271
  messages: [
9122
9272
  { role: "system", content: "You are an echo server" },
9123
9273
  { role: "user", content: "OK" },
@@ -9125,9 +9275,10 @@ function createAiSdkLocalizer(params) {
9125
9275
  { role: "user", content: "OK" }
9126
9276
  ]
9127
9277
  });
9128
- return { authenticated: true, username: "anonymous" };
9278
+ return { valid: true };
9129
9279
  } catch (error) {
9130
- return { authenticated: false };
9280
+ const errorMessage = error instanceof Error ? error.message : String(error);
9281
+ return { valid: false, error: errorMessage };
9131
9282
  }
9132
9283
  },
9133
9284
  localize: async (input2) => {
@@ -9157,6 +9308,7 @@ function createAiSdkLocalizer(params) {
9157
9308
  };
9158
9309
  const response = await generateText2({
9159
9310
  model,
9311
+ ...params.settings,
9160
9312
  messages: [
9161
9313
  { role: "system", content: systemPrompt },
9162
9314
  { role: "user", content: "OK" },
@@ -9170,6 +9322,9 @@ function createAiSdkLocalizer(params) {
9170
9322
  ]
9171
9323
  });
9172
9324
  const result = JSON.parse(response.text);
9325
+ if (typeof result.data === "object" && result.data !== null) {
9326
+ return result.data;
9327
+ }
9173
9328
  const index = result.data.indexOf("{");
9174
9329
  const lastIndex = result.data.lastIndexOf("}");
9175
9330
  const trimmed = result.data.slice(index, lastIndex + 1);
@@ -9239,20 +9394,30 @@ async function setup(input2) {
9239
9394
  },
9240
9395
  {
9241
9396
  title: "Checking authentication",
9397
+ enabled: (ctx) => ctx.localizer?.id === "Lingo.dev",
9242
9398
  task: async (ctx, task) => {
9243
9399
  const authStatus = await ctx.localizer.checkAuth();
9244
9400
  if (!authStatus.authenticated) {
9245
- throw new Error(
9246
- `Failed to authenticate with ${chalk10.hex(colors.yellow)(
9247
- ctx.localizer.id
9248
- )} provider. Please check your API key and try again.`
9249
- );
9401
+ throw new Error(authStatus.error || "Authentication failed");
9250
9402
  }
9251
9403
  task.title = `Authenticated as ${chalk10.hex(colors.yellow)(
9252
9404
  authStatus.username
9253
9405
  )}`;
9254
9406
  }
9255
9407
  },
9408
+ {
9409
+ title: "Validating configuration",
9410
+ enabled: (ctx) => ctx.localizer?.id !== "Lingo.dev",
9411
+ task: async (ctx, task) => {
9412
+ const validationStatus = await ctx.localizer.validateSettings();
9413
+ if (!validationStatus.valid) {
9414
+ throw new Error(
9415
+ validationStatus.error || "Configuration validation failed"
9416
+ );
9417
+ }
9418
+ task.title = `Configuration validated`;
9419
+ }
9420
+ },
9256
9421
  {
9257
9422
  title: "Initializing localization provider",
9258
9423
  async task(ctx, task) {
@@ -11319,7 +11484,7 @@ async function renderHero2() {
11319
11484
  // package.json
11320
11485
  var package_default = {
11321
11486
  name: "lingo.dev",
11322
- version: "0.111.16",
11487
+ version: "0.112.0",
11323
11488
  description: "Lingo.dev CLI",
11324
11489
  private: false,
11325
11490
  publishConfig: {
@@ -11460,6 +11625,7 @@ var package_default = {
11460
11625
  "@lingo.dev/_react": "workspace:*",
11461
11626
  "@lingo.dev/_sdk": "workspace:*",
11462
11627
  "@lingo.dev/_spec": "workspace:*",
11628
+ "@markdoc/markdoc": "^0.5.4",
11463
11629
  "@modelcontextprotocol/sdk": "^1.5.0",
11464
11630
  "@openrouter/ai-sdk-provider": "^0.7.1",
11465
11631
  "@paralleldrive/cuid2": "^2.2.2",