sanity-plugin-transifex 3.0.0 → 4.0.1

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Sanity.io
3
+ Copyright (c) 2025 Sanity.io
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -6,10 +6,15 @@
6
6
  npm install sanity-plugin-transifex
7
7
  ```
8
8
 
9
+ ![2023-02-15 10 06 06](https://user-images.githubusercontent.com/3969996/219130146-8d34c5a6-f11c-4647-826d-e3c07eaf7144.gif)
10
+
11
+
9
12
  # Sanity + Transifex = 🌍
10
13
 
11
14
  This plugin provides an in-studio integration with [Transifex](https://transifex.com). It allows your editors to send any document to Transifex with the click of a button, monitor ongoing translations, and import partial or complete translations back into the studio.
12
15
 
16
+ _Recent updates for v4:_ We've added support for the new document internationalization plugin pattern. Please read the [Document level translations](#document-level-translations) section for more information.
17
+
13
18
  # Table of Contents
14
19
 
15
20
  - [Quickstart](#quickstart)
@@ -21,46 +26,69 @@ This plugin provides an in-studio integration with [Transifex](https://transifex
21
26
 
22
27
  ## Quickstart
23
28
 
24
- 1. In your studio folder, run `npm install sanity-plugin-transifex`.
25
- 2. Ensure the plugin has access to your Transifex secrets. You'll want to create a document that includes your project name, organization name, and a token with appropriate access. [Please refer to the Transifex documentation on creating a token if you don't have one already.](https://docs.transifex.com/account/authentication)
26
- - In your studio, create a file called `populateTransifexSecrets.js`.
27
- - Place the following in the file and fill out the correct values (those in all-caps).
29
+ 1. In your studio folder, run:
30
+
31
+ ```sh
32
+ npm install sanity-plugin-transifex
33
+ ```
34
+
35
+ 2. Ensure the plugin has access to your Transifex secrets. You'll want to create a document that includes your project name, organization name, and a token with appropriate access.
36
+
37
+ [Please refer to the Transifex documentation on creating a token if you don't have one already.](https://docs.transifex.com/account/authentication)
38
+
39
+ In your Studio folder, create a file called `populateTransifexSecrets.js` with the following contents:
28
40
 
29
41
  ```javascript
42
+ // ./populateTransifexSecrets.js
43
+ // Do not commit this file to your repository
44
+
30
45
  import {getCliClient} from 'sanity/cli'
31
46
 
32
47
  const client = getCliClient({apiVersion: '2023-02-15'})
33
48
 
34
49
  client.createOrReplace({
50
+ // The `.` in this _id will ensure the document is private
51
+ // even in a public dataset!
35
52
  _id: 'transifex.secrets',
36
53
  _type: 'transifexSettings',
54
+ // Replace these with your values
37
55
  organization: 'YOUR_TRANSIFEX_ORG_HERE',
38
56
  project: 'YOUR_TRANSIFEX_PROJECT_HERE',
39
57
  token: 'YOUR_TRANSIFEX_TOKEN_HERE'
40
58
  })
41
59
  ```
42
60
 
43
- - On the command line, run the file with `sanity exec populateTransifexSecrets.js --with-user-token`.
44
- Verify that everything went well by using Vision in the studio to query `*[_id == 'transifex.secrets']`. (NOTE: If you have multiple datasets, you'll have to do this across all of them, since it's a document!)
45
- - If everything looks good, go ahead and delete `populateTransifexSecrets.js` so you don't commit it.
46
- Because the document's `_id` is on a path (`transifex`), it won't be exposed to the outside world, even in a public dataset. If you have concerns about this being exposed to authenticated users of your studio, you can control access to this path with [role-based access control](https://www.sanity.io/docs/access-control).
61
+ On the command line, run the file:
62
+
63
+ ```sh
64
+ npx sanity exec populateTransifexSecrets.js --with-user-token
65
+ ```
66
+
67
+ Verify that the document was created using the Vision Tool in the Studio and query `*[_id == 'transifex.secrets']`. Note: If you have multiple datasets, you'll have to do this across all of them.
68
+
69
+ If the document was found in your dataset(s), delete `populateTransifexSecrets.js`.
47
70
 
48
- 3. Get the Transifex tab on your desired document type, using whatever pattern you like. You'll use the [desk structure](https://www.sanity.io/docs/structure-builder-introduction) for this. The options for translation will be nested under this desired document type's views. Here's an example:
71
+ If you have concerns about this being exposed to authenticated users of your studio, you can control access to this path with [role-based access control](https://www.sanity.io/docs/access-control).
72
+
73
+ 4. Get the Transifex tab on your desired document type, using whatever pattern you like. You'll use the [desk structure](https://www.sanity.io/docs/structure-builder-introduction) for this. The options for translation will be nested under this desired document type's views. Here's an example:
49
74
 
50
75
  ```javascript
51
76
  import {DefaultDocumentNodeResolver} from 'sanity/desk'
52
77
  //...your other desk structure imports...
53
- import {TranslationTab, defaultFieldLevelConfig} from 'sanity-plugin-transifex'
78
+ import {TranslationsTab, defaultDocumentLevelConfig} from 'sanity-plugin-transifex'
79
+ //if you are using field-level translations, you can import the field-level config instead:
80
+ //import {TranslationsTab, defaultFieldLevelConfig} from 'sanity-plugin-studio-smartling'
81
+ //if you're not sure which, please look at the document-level and field-level sections below
54
82
 
55
- export const getDefaultDocumentNode: DefaultDocumentNodeResolver = (S, {schemaType}) => {
83
+ export const defaultDocumentNode: DefaultDocumentNodeResolver = (S, {schemaType}) => {
56
84
  if (schemaType === 'myTranslatableDocumentType') {
57
85
  return S.document().views([
58
86
  S.view.form(),
59
- //...my other views -- for example, live preview, the i18n plugin, etc.,
60
- S.view.component(TranslationTab).title('Transifex').options(defaultDocumentLevelConfig)
87
+ //...my other views -- for example, live preview, document pane, etc.,
88
+ S.view.component(TranslationsTab).title('Transifex').options(defaultDocumentLevelConfig)
89
+ //again, if you're using field-level translations, you can use the field-level config instead:
61
90
  ])
62
91
  }
63
- return S.document()
64
92
  }
65
93
  ```
66
94
 
@@ -72,8 +100,9 @@ To use the default config mentioned above, we assume that you are following the
72
100
 
73
101
  ### Field-level translations
74
102
 
75
- If you are using field-level translation, we assume any fields you want translated exist in the multi-locale object form we recommend.
76
- For example, on a document you don't want to be translated, you may have a "title" field that's a flat string: `title: 'My title is here.'` For a field you want to include many languages for, your title may look like
103
+ If you are using field-level translation and the `defaultFieldLevelConfig` configuration, we assume any fields you want translated exist in the multi-locale object form we recommend.
104
+
105
+ For example, on a document you don't want to be translated, you may have a "title" field that's a flat string: `title: 'My title is here.'` For a field you want to include many languages for your title may look like:
77
106
 
78
107
  ```javascript
79
108
  {
@@ -88,7 +117,9 @@ For example, on a document you don't want to be translated, you may have a "titl
88
117
 
89
118
  ### Document level translations
90
119
 
91
- Since we often find users want to use the [Document internationalization plugin](https://www.sanity.io/plugins/document-internationalization) if they're using document-level translations, we assume that any documents you want in different languages will follow the pattern `{id-of-base-language-document}__i18n_{locale}`
120
+ Since we often find users want to use the [Document internationalization plugin](https://www.sanity.io/plugins/document-internationalization) if they're using document-level translations, we assume that any documents you want in different languages will be present in a `translation.metadata` document.
121
+
122
+ _Important_: The above is true if you are using the Document Internationalization Plugin at version 2 or above. If you are using version 1 please use the `legacyDocumentLevelConfig` configuration exported from this plugin. This configuration assumes your translations follow the pattern `{id-of-base-language-document}__i18n_{locale}`
92
123
 
93
124
  ### Final note
94
125
 
@@ -106,11 +137,13 @@ To personalize this configuration it's useful to know what arguments go into `Tr
106
137
  - `importTranslation`: a function that takes in `id` (your document id) `localeId` (the locale of the imported language) and `document` the translated HTML from Transifex. It will deserialize your document back into an object that can be patched into your Sanity data, and then executes that patch.
107
138
  - `Adapter`: An interface with methods to send things over to Transifex. You likely don't want to override this!
108
139
 
109
- There are a number of reasons to override these functions. More general cases are often around ensuring documents serialize and deserialize correctly. Since the serialization functions are used across all our translation plugins currently, you can find some frequently encountered scenarios at [their repository here](https://github.com/sanity-io/sanity-naive-html-serializer), along with code examples for new config.
140
+ There are several reasons to override these functions. More general cases are often around ensuring documents serialize and deserialize correctly. Since the serialization functions are used across all our translation plugins currently, you can find some frequently encountered scenarios at [their repository here](https://github.com/sanity-io/sanity-naive-html-serializer), along with code examples for new config.
141
+
142
+ ## Migrating to Sanity Studio v3
110
143
 
111
- ## Migrating to V3
144
+ You should not have to do anything to migrate to Sanity Studio v3. If you are using the default configs, you should be able to upgrade without any changes. If you are using custom serialization, you may need to update how `BaseDocumentSerializer` receives your schema.
112
145
 
113
- You should not have to do anything to migrate to V3. If you are using the default configs, you should be able to upgrade without any changes. If you are using custom serialization, you may need to update how `BaseDocumentSerializer` receives your schema. These is oulined in the serializer README [here](https://github.com/sanity-io/sanity-naive-html-serializer#v2-to-v3-changes).
146
+ These are outlined in the serializer README [here](https://github.com/sanity-io/sanity-naive-html-serializer#v2-to-v3-changes).
114
147
 
115
148
  ## License
116
149
 
@@ -0,0 +1,57 @@
1
+ import {Adapter} from 'sanity-translations-tab'
2
+ import {BaseDocumentDeserializer} from 'sanity-translations-tab'
3
+ import {BaseDocumentMerger} from 'sanity-translations-tab'
4
+ import {BaseDocumentSerializer} from 'sanity-translations-tab'
5
+ import {customSerializers} from 'sanity-translations-tab'
6
+ import {defaultStopTypes} from 'sanity-translations-tab'
7
+ import {documentLevelPatch} from 'sanity-translations-tab'
8
+ import {fieldLevelPatch} from 'sanity-translations-tab'
9
+ import {findLatestDraft} from 'sanity-translations-tab'
10
+ import {legacyDocumentLevelPatch} from 'sanity-translations-tab'
11
+ import {TranslationFunctionContext} from 'sanity-translations-tab'
12
+ import {TranslationsTab} from 'sanity-translations-tab'
13
+
14
+ export {BaseDocumentDeserializer}
15
+
16
+ export {BaseDocumentMerger}
17
+
18
+ export {BaseDocumentSerializer}
19
+
20
+ declare interface ConfigOptions {
21
+ adapter: Adapter
22
+ secretsNamespace: string | null
23
+ exportForTranslation: (
24
+ id: string,
25
+ context: TranslationFunctionContext,
26
+ ) => Promise<Record<string, any>>
27
+ importTranslation: (
28
+ id: string,
29
+ localeId: string,
30
+ doc: string,
31
+ context: TranslationFunctionContext,
32
+ ) => Promise<void>
33
+ }
34
+
35
+ export {customSerializers}
36
+
37
+ export declare const defaultDocumentLevelConfig: ConfigOptions
38
+
39
+ export declare const defaultFieldLevelConfig: ConfigOptions
40
+
41
+ export {defaultStopTypes}
42
+
43
+ export {documentLevelPatch}
44
+
45
+ export {fieldLevelPatch}
46
+
47
+ export {findLatestDraft}
48
+
49
+ export declare const legacyDocumentLevelConfig: ConfigOptions
50
+
51
+ export {legacyDocumentLevelPatch}
52
+
53
+ export declare const TransifexAdapter: Adapter
54
+
55
+ export {TranslationsTab}
56
+
57
+ export {}
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ import {defaultStopTypes} from 'sanity-translations-tab'
7
7
  import {documentLevelPatch} from 'sanity-translations-tab'
8
8
  import {fieldLevelPatch} from 'sanity-translations-tab'
9
9
  import {findLatestDraft} from 'sanity-translations-tab'
10
+ import {legacyDocumentLevelPatch} from 'sanity-translations-tab'
10
11
  import {TranslationFunctionContext} from 'sanity-translations-tab'
11
12
  import {TranslationsTab} from 'sanity-translations-tab'
12
13
 
@@ -21,13 +22,13 @@ declare interface ConfigOptions {
21
22
  secretsNamespace: string | null
22
23
  exportForTranslation: (
23
24
  id: string,
24
- context: TranslationFunctionContext
25
+ context: TranslationFunctionContext,
25
26
  ) => Promise<Record<string, any>>
26
27
  importTranslation: (
27
28
  id: string,
28
29
  localeId: string,
29
30
  doc: string,
30
- context: TranslationFunctionContext
31
+ context: TranslationFunctionContext,
31
32
  ) => Promise<void>
32
33
  }
33
34
 
@@ -45,6 +46,10 @@ export {fieldLevelPatch}
45
46
 
46
47
  export {findLatestDraft}
47
48
 
49
+ export declare const legacyDocumentLevelConfig: ConfigOptions
50
+
51
+ export {legacyDocumentLevelPatch}
52
+
48
53
  export declare const TransifexAdapter: Adapter
49
54
 
50
55
  export {TranslationsTab}
package/dist/index.js CHANGED
@@ -1,87 +1,63 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', {
4
- value: true
5
- });
6
- var sanityTranslationsTab = require('sanity-translations-tab');
7
- const baseTransifexUrl = "https://rest.api.transifex.com";
8
- const getHeaders = secrets => ({
9
- Authorization: "Bearer ".concat(secrets == null ? void 0 : secrets.token),
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: !0 });
3
+ var sanityTranslationsTab = require("sanity-translations-tab");
4
+ const baseTransifexUrl = "https://rest.api.transifex.com", getHeaders = (secrets) => ({
5
+ Authorization: `Bearer ${secrets?.token}`,
10
6
  "Content-Type": "application/vnd.api+json"
11
- });
12
- const projOrgSlug = secrets => "o:".concat(secrets == null ? void 0 : secrets.organization, ":p:").concat(secrets == null ? void 0 : secrets.project);
13
- const getLocales = async secrets => {
7
+ }), projOrgSlug = (secrets) => `o:${secrets?.organization}:p:${secrets?.project}`, getLocales = async (secrets) => {
14
8
  let locales = [];
15
- if (secrets) {
16
- locales = await fetch("".concat(baseTransifexUrl, "/projects/").concat(projOrgSlug(secrets), "/languages"), {
17
- headers: getHeaders(secrets)
18
- }).then(res => res.json()).then(res => res.data.map(lang => ({
19
- enabled: true,
9
+ return secrets && (locales = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {
10
+ headers: getHeaders(secrets)
11
+ }).then((res) => res.json()).then(
12
+ (res) => res.data.map((lang) => ({
13
+ enabled: !0,
20
14
  description: lang.attributes.name,
21
15
  localeId: lang.attributes.code
22
- })));
23
- }
24
- return locales;
25
- };
26
- const getTranslationTask = async (documentId, secrets) => {
27
- if (!documentId || !secrets) {
16
+ }))
17
+ )), locales;
18
+ }, getTranslationTask = async (documentId, secrets) => {
19
+ if (!documentId || !secrets)
28
20
  return {
29
21
  taskId: documentId,
30
22
  documentId,
31
23
  locales: []
32
24
  };
33
- }
34
- const projectFilter = "filter[project]=".concat(projOrgSlug(secrets));
35
- const resourceFilter = "filter[resource]=".concat(projOrgSlug(secrets), ":r:").concat(documentId);
36
- const task = await fetch("".concat(baseTransifexUrl, "/resource_language_stats?").concat(projectFilter, "&").concat(resourceFilter), {
37
- headers: getHeaders(secrets)
38
- }).then(res => {
39
- if (res.ok) {
25
+ const projectFilter = `filter[project]=${projOrgSlug(secrets)}`, resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`, task = await fetch(
26
+ `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,
27
+ { headers: getHeaders(secrets) }
28
+ ).then((res) => {
29
+ if (res.ok)
40
30
  return res.json();
41
- } else if (res.status === 404) {
42
- return {
43
- data: []
44
- };
45
- }
46
- throw Error("Failed to retrieve tasks from Transifex. Status: ".concat(res.status));
47
- }).then(res => ({
48
- taskId: "".concat(projOrgSlug(secrets), ":r:").concat(documentId),
31
+ if (res.status === 404)
32
+ return { data: [] };
33
+ throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`);
34
+ }).then((res) => ({
35
+ taskId: `${projOrgSlug(secrets)}:r:${documentId}`,
49
36
  documentId,
50
- locales: res.data.map(locale => ({
37
+ locales: res.data.map((locale) => ({
51
38
  localeId: locale.relationships.language.data.id.split(":")[1],
52
- progress: Math.floor(100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings)))
39
+ progress: Math.floor(
40
+ 100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings))
41
+ )
53
42
  }))
54
- }));
55
- const locales = await getLocales(secrets);
56
- const localeIds = locales.map(l => l.localeId);
57
- const validLocales = task.locales.filter(locale => localeIds.find(id => id === locale.localeId));
58
- task.locales = validLocales;
59
- return task;
60
- };
61
- const pollForFileDownloadLocation = async (resourceDownloadUrl, translationDownloadId, headers) => {
62
- const response = await fetch("".concat(resourceDownloadUrl, "/").concat(translationDownloadId), {
43
+ })), localeIds = (await getLocales(secrets)).map((l) => l.localeId), validLocales = task.locales.filter(
44
+ (locale) => localeIds.find((id) => id === locale.localeId)
45
+ );
46
+ return task.locales = validLocales, task;
47
+ }, pollForFileDownloadLocation = async (resourceDownloadUrl, translationDownloadId, headers) => {
48
+ const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {
63
49
  headers
64
50
  });
65
- if (response.status === 500) {
66
- console.info("Transifex plugin message: Received 500 for translation download ID ".concat(translationDownloadId, ". Trying to reconnect..."));
67
- await new Promise(resolve => setTimeout(resolve, 3e3));
68
- return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers);
69
- } else if (response.redirected) {
70
- console.info("Transifex plugin message: Received redirect for translation download ID ".concat(translationDownloadId, ". Following redirect now for file download."));
71
- return response.url;
72
- } else if (response.status === 200) {
73
- console.info("Transifex plugin message: Requested download location for translation download ID ".concat(translationDownloadId, ". Location is still pending, trying again."));
74
- await new Promise(resolve => setTimeout(resolve, 3e3));
75
- return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers);
76
- }
77
- console.error("Transifex plugin message: Requested download location for translation download ID ".concat(translationDownloadId, " but received error code ").concat(response.status, ". Waiting and trying again."));
78
- await new Promise(resolve => setTimeout(resolve, 3e3));
79
- return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers);
80
- };
81
- const handleFileDownload = url => {
82
- return fetch(url).then(res => res.text());
83
- };
84
- const getTranslation = async (taskId, localeId, secrets) => {
51
+ return response.status === 500 ? (console.info(
52
+ `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`
53
+ ), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)) : response.redirected ? (console.info(
54
+ `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`
55
+ ), response.url) : response.status === 200 ? (console.info(
56
+ `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`
57
+ ), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)) : (console.error(
58
+ `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`
59
+ ), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers));
60
+ }, handleFileDownload = (url) => fetch(url).then((res) => res.text()), getTranslation = async (taskId, localeId, secrets) => {
85
61
  const resourceDownloadBody = {
86
62
  data: {
87
63
  attributes: {
@@ -90,7 +66,7 @@ const getTranslation = async (taskId, localeId, secrets) => {
90
66
  relationships: {
91
67
  language: {
92
68
  data: {
93
- id: "l:".concat(localeId),
69
+ id: `l:${localeId}`,
94
70
  type: "languages"
95
71
  }
96
72
  },
@@ -103,22 +79,21 @@ const getTranslation = async (taskId, localeId, secrets) => {
103
79
  },
104
80
  type: "resource_translations_async_downloads"
105
81
  }
106
- };
107
- const resourceDownloadUrl = "".concat(baseTransifexUrl, "/resource_translations_async_downloads");
108
- const translationDownloadId = await fetch(resourceDownloadUrl, {
82
+ }, resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`, translationDownloadId = await fetch(resourceDownloadUrl, {
109
83
  headers: getHeaders(secrets),
110
84
  method: "POST",
111
85
  body: JSON.stringify(resourceDownloadBody)
112
- }).then(res => res.json()).then(res => res.data.id);
113
- const headers = getHeaders(secrets);
114
- const location = await pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers);
86
+ }).then((res) => res.json()).then((res) => res.data.id), headers = getHeaders(secrets), location = await pollForFileDownloadLocation(
87
+ resourceDownloadUrl,
88
+ translationDownloadId,
89
+ headers
90
+ );
115
91
  return handleFileDownload(location);
116
- };
117
- const createResource = (doc, documentId, secrets) => {
92
+ }, createResource = (doc, documentId, secrets) => {
118
93
  const resourceCreateBody = {
119
94
  data: {
120
95
  attributes: {
121
- accept_translations: true,
96
+ accept_translations: !0,
122
97
  name: doc.name,
123
98
  slug: documentId
124
99
  },
@@ -139,21 +114,18 @@ const createResource = (doc, documentId, secrets) => {
139
114
  type: "resources"
140
115
  }
141
116
  };
142
- return fetch("".concat(baseTransifexUrl, "/resources"), {
117
+ return fetch(`${baseTransifexUrl}/resources`, {
143
118
  headers: getHeaders(secrets),
144
119
  method: "POST",
145
120
  body: JSON.stringify(resourceCreateBody)
146
- }).then(res => res.json()).then(res => res.data.id);
147
- };
148
- const createTask = async (documentId, document, localeIds, secrets) => {
149
- let resourceId = await fetch("".concat(baseTransifexUrl, "/resources/").concat(projOrgSlug(secrets), ":r:").concat(documentId), {
150
- headers: getHeaders(secrets)
151
- }).then(res => res.json()).then(res => res.data ? res.data.id : null);
152
- if (!resourceId) {
153
- resourceId = await createResource(document, documentId, secrets);
154
- }
155
- const resourceUploadUrl = "".concat(baseTransifexUrl, "/resource_strings_async_uploads");
156
- const resourceUploadBody = {
121
+ }).then((res) => res.json()).then((res) => res.data.id);
122
+ }, createTask = async (documentId, document, localeIds, secrets) => {
123
+ let resourceId = await fetch(
124
+ `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,
125
+ { headers: getHeaders(secrets) }
126
+ ).then((res) => res.json()).then((res) => res.data ? res.data.id : null);
127
+ resourceId || (resourceId = await createResource(document, documentId, secrets));
128
+ const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`, resourceUploadBody = {
157
129
  data: {
158
130
  attributes: {
159
131
  content: document.content,
@@ -175,78 +147,86 @@ const createTask = async (documentId, document, localeIds, secrets) => {
175
147
  body: JSON.stringify(resourceUploadBody),
176
148
  headers: getHeaders(secrets)
177
149
  }).then(() => getTranslationTask(documentId, secrets));
178
- };
179
- const TransifexAdapter = {
150
+ }, TransifexAdapter = {
180
151
  getLocales,
181
152
  getTranslationTask,
182
153
  createTask,
183
154
  getTranslation
184
- };
185
- const defaultDocumentLevelConfig = {
155
+ }, defaultDocumentLevelConfig = {
186
156
  ...sanityTranslationsTab.baseDocumentLevelConfig,
187
157
  adapter: TransifexAdapter,
188
158
  secretsNamespace: "transifex"
189
- };
190
- const defaultFieldLevelConfig = {
159
+ }, legacyDocumentLevelConfig = {
160
+ ...sanityTranslationsTab.legacyDocumentLevelConfig,
161
+ adapter: TransifexAdapter,
162
+ secretsNamespace: "transifex"
163
+ }, defaultFieldLevelConfig = {
191
164
  ...sanityTranslationsTab.baseFieldLevelConfig,
192
165
  adapter: TransifexAdapter,
193
166
  secretsNamespace: "transifex"
194
167
  };
195
- Object.defineProperty(exports, 'BaseDocumentDeserializer', {
196
- enumerable: true,
197
- get: function () {
168
+ Object.defineProperty(exports, "BaseDocumentDeserializer", {
169
+ enumerable: !0,
170
+ get: function() {
198
171
  return sanityTranslationsTab.BaseDocumentDeserializer;
199
172
  }
200
173
  });
201
- Object.defineProperty(exports, 'BaseDocumentMerger', {
202
- enumerable: true,
203
- get: function () {
174
+ Object.defineProperty(exports, "BaseDocumentMerger", {
175
+ enumerable: !0,
176
+ get: function() {
204
177
  return sanityTranslationsTab.BaseDocumentMerger;
205
178
  }
206
179
  });
207
- Object.defineProperty(exports, 'BaseDocumentSerializer', {
208
- enumerable: true,
209
- get: function () {
180
+ Object.defineProperty(exports, "BaseDocumentSerializer", {
181
+ enumerable: !0,
182
+ get: function() {
210
183
  return sanityTranslationsTab.BaseDocumentSerializer;
211
184
  }
212
185
  });
213
- Object.defineProperty(exports, 'TranslationsTab', {
214
- enumerable: true,
215
- get: function () {
186
+ Object.defineProperty(exports, "TranslationsTab", {
187
+ enumerable: !0,
188
+ get: function() {
216
189
  return sanityTranslationsTab.TranslationsTab;
217
190
  }
218
191
  });
219
- Object.defineProperty(exports, 'customSerializers', {
220
- enumerable: true,
221
- get: function () {
192
+ Object.defineProperty(exports, "customSerializers", {
193
+ enumerable: !0,
194
+ get: function() {
222
195
  return sanityTranslationsTab.customSerializers;
223
196
  }
224
197
  });
225
- Object.defineProperty(exports, 'defaultStopTypes', {
226
- enumerable: true,
227
- get: function () {
198
+ Object.defineProperty(exports, "defaultStopTypes", {
199
+ enumerable: !0,
200
+ get: function() {
228
201
  return sanityTranslationsTab.defaultStopTypes;
229
202
  }
230
203
  });
231
- Object.defineProperty(exports, 'documentLevelPatch', {
232
- enumerable: true,
233
- get: function () {
204
+ Object.defineProperty(exports, "documentLevelPatch", {
205
+ enumerable: !0,
206
+ get: function() {
234
207
  return sanityTranslationsTab.documentLevelPatch;
235
208
  }
236
209
  });
237
- Object.defineProperty(exports, 'fieldLevelPatch', {
238
- enumerable: true,
239
- get: function () {
210
+ Object.defineProperty(exports, "fieldLevelPatch", {
211
+ enumerable: !0,
212
+ get: function() {
240
213
  return sanityTranslationsTab.fieldLevelPatch;
241
214
  }
242
215
  });
243
- Object.defineProperty(exports, 'findLatestDraft', {
244
- enumerable: true,
245
- get: function () {
216
+ Object.defineProperty(exports, "findLatestDraft", {
217
+ enumerable: !0,
218
+ get: function() {
246
219
  return sanityTranslationsTab.findLatestDraft;
247
220
  }
248
221
  });
222
+ Object.defineProperty(exports, "legacyDocumentLevelPatch", {
223
+ enumerable: !0,
224
+ get: function() {
225
+ return sanityTranslationsTab.legacyDocumentLevelPatch;
226
+ }
227
+ });
249
228
  exports.TransifexAdapter = TransifexAdapter;
250
229
  exports.defaultDocumentLevelConfig = defaultDocumentLevelConfig;
251
230
  exports.defaultFieldLevelConfig = defaultFieldLevelConfig;
231
+ exports.legacyDocumentLevelConfig = legacyDocumentLevelConfig;
252
232
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/transifexAdapter/helpers.ts","../src/transifexAdapter/getLocales.ts","../src/transifexAdapter/getTranslationTask.ts","../src/transifexAdapter/getTranslation.ts","../src/transifexAdapter/createTask.ts","../src/transifexAdapter/index.ts","../src/index.ts"],"sourcesContent":["import {Secrets} from 'sanity-translations-tab'\n\nexport const baseTransifexUrl = 'https://rest.api.transifex.com'\n\nexport const getHeaders = (secrets: Secrets | null): Record<string, string> => ({\n Authorization: `Bearer ${secrets?.token}`,\n 'Content-Type': 'application/vnd.api+json',\n})\n\nexport const projOrgSlug = (secrets: Secrets | null): string =>\n `o:${secrets?.organization}:p:${secrets?.project}`\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n let locales = []\n if (secrets) {\n locales = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {\n headers: getHeaders(secrets),\n })\n .then((res) => res.json())\n .then((res) =>\n res.data.map((lang: Record<string, any>) => ({\n enabled: true,\n description: lang.attributes.name,\n localeId: lang.attributes.code,\n }))\n )\n }\n return locales\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getLocales} from './getLocales'\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n documentId: string,\n secrets: Secrets | null\n) => {\n if (!documentId || !secrets) {\n return {\n taskId: documentId,\n documentId: documentId,\n locales: [],\n }\n }\n const projectFilter = `filter[project]=${projOrgSlug(secrets)}`\n const resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`\n const task = await fetch(\n `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => {\n if (res.ok) {\n return res.json()\n }\n //normal -- just means that this task doesn't exist yet.\n else if (res.status === 404) {\n return {data: []}\n }\n throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`)\n })\n .then((res) => ({\n taskId: `${projOrgSlug(secrets)}:r:${documentId}`,\n documentId: documentId,\n locales: res.data.map((locale: Record<string, any>) => ({\n localeId: locale.relationships.language.data.id.split(':')[1],\n progress: Math.floor(\n 100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings))\n ),\n })),\n }))\n\n const locales = await getLocales(secrets)\n const localeIds = locales.map((l: Record<string, any>) => l.localeId)\n const validLocales = task.locales.filter((locale: Record<string, any>) =>\n localeIds.find((id: string) => id === locale.localeId)\n )\n task.locales = validLocales\n\n return task\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, getHeaders} from './helpers'\n\nconst pollForFileDownloadLocation = async (\n resourceDownloadUrl: string,\n translationDownloadId: string,\n headers: Record<string, any>\n): Promise<string> => {\n const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {\n headers: headers,\n })\n\n if (response.status === 500) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n } else if (response.redirected) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`\n )\n return response.url\n } else if (response.status === 200) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n }\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.error(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n}\n\nconst handleFileDownload = (url: string) => {\n return fetch(url).then((res) => res.text())\n}\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n taskId: string,\n localeId: string,\n secrets: Secrets | null\n) => {\n const resourceDownloadBody = {\n data: {\n attributes: {\n content_encoding: 'text',\n },\n relationships: {\n language: {\n data: {\n id: `l:${localeId}`,\n type: 'languages',\n },\n },\n resource: {\n data: {\n id: taskId,\n type: 'resources',\n },\n },\n },\n type: 'resource_translations_async_downloads',\n },\n }\n\n const resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`\n const translationDownloadId = await fetch(resourceDownloadUrl, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceDownloadBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n\n const headers = getHeaders(secrets)\n const location = await pollForFileDownloadLocation(\n resourceDownloadUrl,\n translationDownloadId,\n headers\n )\n return handleFileDownload(location)\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getTranslationTask} from './getTranslationTask'\n\nconst createResource = (doc: Record<string, any>, documentId: string, secrets: Secrets | null) => {\n const resourceCreateBody = {\n data: {\n attributes: {\n accept_translations: true,\n name: doc.name,\n slug: documentId,\n },\n relationships: {\n i18n_format: {\n data: {\n id: 'HTML_FRAGMENT',\n type: 'i18n_formats',\n },\n },\n project: {\n data: {\n id: projOrgSlug(secrets),\n type: 'projects',\n },\n },\n },\n type: 'resources',\n },\n }\n\n return fetch(`${baseTransifexUrl}/resources`, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceCreateBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n}\n\n//@ts-ignore until we resolve the TranslationTask return type\nexport const createTask: Adapter['createTask'] = async (\n documentId: string,\n document: Record<string, any>,\n localeIds: string[],\n secrets: Secrets | null\n) => {\n let resourceId = await fetch(\n `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => res.json())\n .then((res) => (res.data ? res.data.id : null))\n\n if (!resourceId) {\n resourceId = await createResource(document, documentId, secrets)\n }\n\n const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`\n const resourceUploadBody = {\n data: {\n attributes: {\n content: document.content,\n content_encoding: 'text',\n },\n relationships: {\n resource: {\n data: {\n id: resourceId,\n type: 'resources',\n },\n },\n },\n type: 'resource_strings_async_uploads',\n },\n }\n\n return fetch(resourceUploadUrl, {\n method: 'POST',\n body: JSON.stringify(resourceUploadBody),\n headers: getHeaders(secrets),\n }).then(() => getTranslationTask(documentId, secrets))\n}\n","import {Adapter} from 'sanity-translations-tab'\n\nimport {getLocales} from './getLocales'\nimport {getTranslationTask} from './getTranslationTask'\nimport {getTranslation} from './getTranslation'\nimport {createTask} from './createTask'\n\nexport const TransifexAdapter: Adapter = {\n getLocales,\n getTranslationTask,\n createTask,\n getTranslation,\n}\n","import {\n baseDocumentLevelConfig,\n baseFieldLevelConfig,\n Adapter,\n TranslationFunctionContext,\n} from 'sanity-translations-tab'\nimport {TransifexAdapter} from './transifexAdapter'\n\nexport {\n findLatestDraft,\n BaseDocumentDeserializer,\n BaseDocumentSerializer,\n BaseDocumentMerger,\n defaultStopTypes,\n customSerializers,\n documentLevelPatch,\n fieldLevelPatch,\n TranslationsTab,\n} from 'sanity-translations-tab'\n\ninterface ConfigOptions {\n adapter: Adapter\n secretsNamespace: string | null\n exportForTranslation: (\n id: string,\n context: TranslationFunctionContext\n ) => Promise<Record<string, any>>\n importTranslation: (\n id: string,\n localeId: string,\n doc: string,\n context: TranslationFunctionContext\n ) => Promise<void>\n}\nconst defaultDocumentLevelConfig: ConfigOptions = {\n ...baseDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst defaultFieldLevelConfig: ConfigOptions = {\n ...baseFieldLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nexport {TransifexAdapter, defaultDocumentLevelConfig, defaultFieldLevelConfig}\n"],"names":["baseTransifexUrl","getHeaders","secrets","Authorization","token","projOrgSlug","organization","project","getLocales","locales","fetch","headers","then","res","json","data","map","lang","enabled","description","attributes","name","localeId","code","getTranslationTask","documentId","taskId","projectFilter","resourceFilter","task","ok","status","Error","locale","relationships","language","id","split","progress","Math","floor","reviewed_strings","parseFloat","total_strings","localeIds","l","validLocales","filter","find","pollForFileDownloadLocation","resourceDownloadUrl","translationDownloadId","response","console","info","Promise","resolve","setTimeout","redirected","url","error","handleFileDownload","text","getTranslation","resourceDownloadBody","content_encoding","type","resource","method","body","JSON","stringify","location","createResource","doc","resourceCreateBody","accept_translations","slug","i18n_format","createTask","document","resourceId","resourceUploadUrl","resourceUploadBody","content","TransifexAdapter","defaultDocumentLevelConfig","baseDocumentLevelConfig","adapter","secretsNamespace","defaultFieldLevelConfig","baseFieldLevelConfig"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/transifexAdapter/helpers.ts","../src/transifexAdapter/getLocales.ts","../src/transifexAdapter/getTranslationTask.ts","../src/transifexAdapter/getTranslation.ts","../src/transifexAdapter/createTask.ts","../src/transifexAdapter/index.ts","../src/index.ts"],"sourcesContent":["import {Secrets} from 'sanity-translations-tab'\n\nexport const baseTransifexUrl = 'https://rest.api.transifex.com'\n\nexport const getHeaders = (secrets: Secrets | null): Record<string, string> => ({\n Authorization: `Bearer ${secrets?.token}`,\n 'Content-Type': 'application/vnd.api+json',\n})\n\nexport const projOrgSlug = (secrets: Secrets | null): string =>\n `o:${secrets?.organization}:p:${secrets?.project}`\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n let locales = []\n if (secrets) {\n locales = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {\n headers: getHeaders(secrets),\n })\n .then((res) => res.json())\n .then((res) =>\n res.data.map((lang: Record<string, any>) => ({\n enabled: true,\n description: lang.attributes.name,\n localeId: lang.attributes.code,\n }))\n )\n }\n return locales\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getLocales} from './getLocales'\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n documentId: string,\n secrets: Secrets | null\n) => {\n if (!documentId || !secrets) {\n return {\n taskId: documentId,\n documentId: documentId,\n locales: [],\n }\n }\n const projectFilter = `filter[project]=${projOrgSlug(secrets)}`\n const resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`\n const task = await fetch(\n `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => {\n if (res.ok) {\n return res.json()\n }\n //normal -- just means that this task doesn't exist yet.\n else if (res.status === 404) {\n return {data: []}\n }\n throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`)\n })\n .then((res) => ({\n taskId: `${projOrgSlug(secrets)}:r:${documentId}`,\n documentId: documentId,\n locales: res.data.map((locale: Record<string, any>) => ({\n localeId: locale.relationships.language.data.id.split(':')[1],\n progress: Math.floor(\n 100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings))\n ),\n })),\n }))\n\n const locales = await getLocales(secrets)\n const localeIds = locales.map((l: Record<string, any>) => l.localeId)\n const validLocales = task.locales.filter((locale: Record<string, any>) =>\n localeIds.find((id: string) => id === locale.localeId)\n )\n task.locales = validLocales\n\n return task\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, getHeaders} from './helpers'\n\nconst pollForFileDownloadLocation = async (\n resourceDownloadUrl: string,\n translationDownloadId: string,\n headers: Record<string, any>\n): Promise<string> => {\n const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {\n headers: headers,\n })\n\n if (response.status === 500) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n } else if (response.redirected) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`\n )\n return response.url\n } else if (response.status === 200) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n }\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.error(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n}\n\nconst handleFileDownload = (url: string) => {\n return fetch(url).then((res) => res.text())\n}\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n taskId: string,\n localeId: string,\n secrets: Secrets | null\n) => {\n const resourceDownloadBody = {\n data: {\n attributes: {\n content_encoding: 'text',\n },\n relationships: {\n language: {\n data: {\n id: `l:${localeId}`,\n type: 'languages',\n },\n },\n resource: {\n data: {\n id: taskId,\n type: 'resources',\n },\n },\n },\n type: 'resource_translations_async_downloads',\n },\n }\n\n const resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`\n const translationDownloadId = await fetch(resourceDownloadUrl, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceDownloadBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n\n const headers = getHeaders(secrets)\n const location = await pollForFileDownloadLocation(\n resourceDownloadUrl,\n translationDownloadId,\n headers\n )\n return handleFileDownload(location)\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getTranslationTask} from './getTranslationTask'\n\nconst createResource = (doc: Record<string, any>, documentId: string, secrets: Secrets | null) => {\n const resourceCreateBody = {\n data: {\n attributes: {\n accept_translations: true,\n name: doc.name,\n slug: documentId,\n },\n relationships: {\n i18n_format: {\n data: {\n id: 'HTML_FRAGMENT',\n type: 'i18n_formats',\n },\n },\n project: {\n data: {\n id: projOrgSlug(secrets),\n type: 'projects',\n },\n },\n },\n type: 'resources',\n },\n }\n\n return fetch(`${baseTransifexUrl}/resources`, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceCreateBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n}\n\n//@ts-ignore until we resolve the TranslationTask return type\nexport const createTask: Adapter['createTask'] = async (\n documentId: string,\n document: Record<string, any>,\n localeIds: string[],\n secrets: Secrets | null\n) => {\n let resourceId = await fetch(\n `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => res.json())\n .then((res) => (res.data ? res.data.id : null))\n\n if (!resourceId) {\n resourceId = await createResource(document, documentId, secrets)\n }\n\n const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`\n const resourceUploadBody = {\n data: {\n attributes: {\n content: document.content,\n content_encoding: 'text',\n },\n relationships: {\n resource: {\n data: {\n id: resourceId,\n type: 'resources',\n },\n },\n },\n type: 'resource_strings_async_uploads',\n },\n }\n\n return fetch(resourceUploadUrl, {\n method: 'POST',\n body: JSON.stringify(resourceUploadBody),\n headers: getHeaders(secrets),\n }).then(() => getTranslationTask(documentId, secrets))\n}\n","import {Adapter} from 'sanity-translations-tab'\n\nimport {getLocales} from './getLocales'\nimport {getTranslationTask} from './getTranslationTask'\nimport {getTranslation} from './getTranslation'\nimport {createTask} from './createTask'\n\nexport const TransifexAdapter: Adapter = {\n getLocales,\n getTranslationTask,\n createTask,\n getTranslation,\n}\n","import {\n baseDocumentLevelConfig,\n legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,\n baseFieldLevelConfig,\n Adapter,\n TranslationFunctionContext,\n} from 'sanity-translations-tab'\nimport {TransifexAdapter} from './transifexAdapter'\n\nexport {\n findLatestDraft,\n BaseDocumentDeserializer,\n BaseDocumentSerializer,\n BaseDocumentMerger,\n defaultStopTypes,\n customSerializers,\n legacyDocumentLevelPatch,\n documentLevelPatch,\n fieldLevelPatch,\n TranslationsTab,\n} from 'sanity-translations-tab'\n\ninterface ConfigOptions {\n adapter: Adapter\n secretsNamespace: string | null\n exportForTranslation: (\n id: string,\n context: TranslationFunctionContext\n ) => Promise<Record<string, any>>\n importTranslation: (\n id: string,\n localeId: string,\n doc: string,\n context: TranslationFunctionContext\n ) => Promise<void>\n}\nconst defaultDocumentLevelConfig: ConfigOptions = {\n ...baseDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst legacyDocumentLevelConfig: ConfigOptions = {\n ...baseLegacyDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst defaultFieldLevelConfig: ConfigOptions = {\n ...baseFieldLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nexport {\n TransifexAdapter,\n defaultDocumentLevelConfig,\n defaultFieldLevelConfig,\n legacyDocumentLevelConfig,\n}\n"],"names":["baseDocumentLevelConfig","baseLegacyDocumentLevelConfig","baseFieldLevelConfig"],"mappings":"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"}
package/dist/index.mjs ADDED
@@ -0,0 +1,183 @@
1
+ import { baseDocumentLevelConfig, baseFieldLevelConfig, legacyDocumentLevelConfig as legacyDocumentLevelConfig$1 } from "sanity-translations-tab";
2
+ import { BaseDocumentDeserializer, BaseDocumentMerger, BaseDocumentSerializer, TranslationsTab, customSerializers, defaultStopTypes, documentLevelPatch, fieldLevelPatch, findLatestDraft, legacyDocumentLevelPatch } from "sanity-translations-tab";
3
+ const baseTransifexUrl = "https://rest.api.transifex.com", getHeaders = (secrets) => ({
4
+ Authorization: `Bearer ${secrets?.token}`,
5
+ "Content-Type": "application/vnd.api+json"
6
+ }), projOrgSlug = (secrets) => `o:${secrets?.organization}:p:${secrets?.project}`, getLocales = async (secrets) => {
7
+ let locales = [];
8
+ return secrets && (locales = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {
9
+ headers: getHeaders(secrets)
10
+ }).then((res) => res.json()).then(
11
+ (res) => res.data.map((lang) => ({
12
+ enabled: !0,
13
+ description: lang.attributes.name,
14
+ localeId: lang.attributes.code
15
+ }))
16
+ )), locales;
17
+ }, getTranslationTask = async (documentId, secrets) => {
18
+ if (!documentId || !secrets)
19
+ return {
20
+ taskId: documentId,
21
+ documentId,
22
+ locales: []
23
+ };
24
+ const projectFilter = `filter[project]=${projOrgSlug(secrets)}`, resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`, task = await fetch(
25
+ `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,
26
+ { headers: getHeaders(secrets) }
27
+ ).then((res) => {
28
+ if (res.ok)
29
+ return res.json();
30
+ if (res.status === 404)
31
+ return { data: [] };
32
+ throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`);
33
+ }).then((res) => ({
34
+ taskId: `${projOrgSlug(secrets)}:r:${documentId}`,
35
+ documentId,
36
+ locales: res.data.map((locale) => ({
37
+ localeId: locale.relationships.language.data.id.split(":")[1],
38
+ progress: Math.floor(
39
+ 100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings))
40
+ )
41
+ }))
42
+ })), localeIds = (await getLocales(secrets)).map((l) => l.localeId), validLocales = task.locales.filter(
43
+ (locale) => localeIds.find((id) => id === locale.localeId)
44
+ );
45
+ return task.locales = validLocales, task;
46
+ }, pollForFileDownloadLocation = async (resourceDownloadUrl, translationDownloadId, headers) => {
47
+ const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {
48
+ headers
49
+ });
50
+ return response.status === 500 ? (console.info(
51
+ `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`
52
+ ), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)) : response.redirected ? (console.info(
53
+ `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`
54
+ ), response.url) : response.status === 200 ? (console.info(
55
+ `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`
56
+ ), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)) : (console.error(
57
+ `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`
58
+ ), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers));
59
+ }, handleFileDownload = (url) => fetch(url).then((res) => res.text()), getTranslation = async (taskId, localeId, secrets) => {
60
+ const resourceDownloadBody = {
61
+ data: {
62
+ attributes: {
63
+ content_encoding: "text"
64
+ },
65
+ relationships: {
66
+ language: {
67
+ data: {
68
+ id: `l:${localeId}`,
69
+ type: "languages"
70
+ }
71
+ },
72
+ resource: {
73
+ data: {
74
+ id: taskId,
75
+ type: "resources"
76
+ }
77
+ }
78
+ },
79
+ type: "resource_translations_async_downloads"
80
+ }
81
+ }, resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`, translationDownloadId = await fetch(resourceDownloadUrl, {
82
+ headers: getHeaders(secrets),
83
+ method: "POST",
84
+ body: JSON.stringify(resourceDownloadBody)
85
+ }).then((res) => res.json()).then((res) => res.data.id), headers = getHeaders(secrets), location = await pollForFileDownloadLocation(
86
+ resourceDownloadUrl,
87
+ translationDownloadId,
88
+ headers
89
+ );
90
+ return handleFileDownload(location);
91
+ }, createResource = (doc, documentId, secrets) => {
92
+ const resourceCreateBody = {
93
+ data: {
94
+ attributes: {
95
+ accept_translations: !0,
96
+ name: doc.name,
97
+ slug: documentId
98
+ },
99
+ relationships: {
100
+ i18n_format: {
101
+ data: {
102
+ id: "HTML_FRAGMENT",
103
+ type: "i18n_formats"
104
+ }
105
+ },
106
+ project: {
107
+ data: {
108
+ id: projOrgSlug(secrets),
109
+ type: "projects"
110
+ }
111
+ }
112
+ },
113
+ type: "resources"
114
+ }
115
+ };
116
+ return fetch(`${baseTransifexUrl}/resources`, {
117
+ headers: getHeaders(secrets),
118
+ method: "POST",
119
+ body: JSON.stringify(resourceCreateBody)
120
+ }).then((res) => res.json()).then((res) => res.data.id);
121
+ }, createTask = async (documentId, document, localeIds, secrets) => {
122
+ let resourceId = await fetch(
123
+ `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,
124
+ { headers: getHeaders(secrets) }
125
+ ).then((res) => res.json()).then((res) => res.data ? res.data.id : null);
126
+ resourceId || (resourceId = await createResource(document, documentId, secrets));
127
+ const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`, resourceUploadBody = {
128
+ data: {
129
+ attributes: {
130
+ content: document.content,
131
+ content_encoding: "text"
132
+ },
133
+ relationships: {
134
+ resource: {
135
+ data: {
136
+ id: resourceId,
137
+ type: "resources"
138
+ }
139
+ }
140
+ },
141
+ type: "resource_strings_async_uploads"
142
+ }
143
+ };
144
+ return fetch(resourceUploadUrl, {
145
+ method: "POST",
146
+ body: JSON.stringify(resourceUploadBody),
147
+ headers: getHeaders(secrets)
148
+ }).then(() => getTranslationTask(documentId, secrets));
149
+ }, TransifexAdapter = {
150
+ getLocales,
151
+ getTranslationTask,
152
+ createTask,
153
+ getTranslation
154
+ }, defaultDocumentLevelConfig = {
155
+ ...baseDocumentLevelConfig,
156
+ adapter: TransifexAdapter,
157
+ secretsNamespace: "transifex"
158
+ }, legacyDocumentLevelConfig = {
159
+ ...legacyDocumentLevelConfig$1,
160
+ adapter: TransifexAdapter,
161
+ secretsNamespace: "transifex"
162
+ }, defaultFieldLevelConfig = {
163
+ ...baseFieldLevelConfig,
164
+ adapter: TransifexAdapter,
165
+ secretsNamespace: "transifex"
166
+ };
167
+ export {
168
+ BaseDocumentDeserializer,
169
+ BaseDocumentMerger,
170
+ BaseDocumentSerializer,
171
+ TransifexAdapter,
172
+ TranslationsTab,
173
+ customSerializers,
174
+ defaultDocumentLevelConfig,
175
+ defaultFieldLevelConfig,
176
+ defaultStopTypes,
177
+ documentLevelPatch,
178
+ fieldLevelPatch,
179
+ findLatestDraft,
180
+ legacyDocumentLevelConfig,
181
+ legacyDocumentLevelPatch
182
+ };
183
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../src/transifexAdapter/helpers.ts","../src/transifexAdapter/getLocales.ts","../src/transifexAdapter/getTranslationTask.ts","../src/transifexAdapter/getTranslation.ts","../src/transifexAdapter/createTask.ts","../src/transifexAdapter/index.ts","../src/index.ts"],"sourcesContent":["import {Secrets} from 'sanity-translations-tab'\n\nexport const baseTransifexUrl = 'https://rest.api.transifex.com'\n\nexport const getHeaders = (secrets: Secrets | null): Record<string, string> => ({\n Authorization: `Bearer ${secrets?.token}`,\n 'Content-Type': 'application/vnd.api+json',\n})\n\nexport const projOrgSlug = (secrets: Secrets | null): string =>\n `o:${secrets?.organization}:p:${secrets?.project}`\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n let locales = []\n if (secrets) {\n locales = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {\n headers: getHeaders(secrets),\n })\n .then((res) => res.json())\n .then((res) =>\n res.data.map((lang: Record<string, any>) => ({\n enabled: true,\n description: lang.attributes.name,\n localeId: lang.attributes.code,\n }))\n )\n }\n return locales\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getLocales} from './getLocales'\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n documentId: string,\n secrets: Secrets | null\n) => {\n if (!documentId || !secrets) {\n return {\n taskId: documentId,\n documentId: documentId,\n locales: [],\n }\n }\n const projectFilter = `filter[project]=${projOrgSlug(secrets)}`\n const resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`\n const task = await fetch(\n `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => {\n if (res.ok) {\n return res.json()\n }\n //normal -- just means that this task doesn't exist yet.\n else if (res.status === 404) {\n return {data: []}\n }\n throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`)\n })\n .then((res) => ({\n taskId: `${projOrgSlug(secrets)}:r:${documentId}`,\n documentId: documentId,\n locales: res.data.map((locale: Record<string, any>) => ({\n localeId: locale.relationships.language.data.id.split(':')[1],\n progress: Math.floor(\n 100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings))\n ),\n })),\n }))\n\n const locales = await getLocales(secrets)\n const localeIds = locales.map((l: Record<string, any>) => l.localeId)\n const validLocales = task.locales.filter((locale: Record<string, any>) =>\n localeIds.find((id: string) => id === locale.localeId)\n )\n task.locales = validLocales\n\n return task\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, getHeaders} from './helpers'\n\nconst pollForFileDownloadLocation = async (\n resourceDownloadUrl: string,\n translationDownloadId: string,\n headers: Record<string, any>\n): Promise<string> => {\n const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {\n headers: headers,\n })\n\n if (response.status === 500) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n } else if (response.redirected) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`\n )\n return response.url\n } else if (response.status === 200) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n }\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.error(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n}\n\nconst handleFileDownload = (url: string) => {\n return fetch(url).then((res) => res.text())\n}\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n taskId: string,\n localeId: string,\n secrets: Secrets | null\n) => {\n const resourceDownloadBody = {\n data: {\n attributes: {\n content_encoding: 'text',\n },\n relationships: {\n language: {\n data: {\n id: `l:${localeId}`,\n type: 'languages',\n },\n },\n resource: {\n data: {\n id: taskId,\n type: 'resources',\n },\n },\n },\n type: 'resource_translations_async_downloads',\n },\n }\n\n const resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`\n const translationDownloadId = await fetch(resourceDownloadUrl, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceDownloadBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n\n const headers = getHeaders(secrets)\n const location = await pollForFileDownloadLocation(\n resourceDownloadUrl,\n translationDownloadId,\n headers\n )\n return handleFileDownload(location)\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getTranslationTask} from './getTranslationTask'\n\nconst createResource = (doc: Record<string, any>, documentId: string, secrets: Secrets | null) => {\n const resourceCreateBody = {\n data: {\n attributes: {\n accept_translations: true,\n name: doc.name,\n slug: documentId,\n },\n relationships: {\n i18n_format: {\n data: {\n id: 'HTML_FRAGMENT',\n type: 'i18n_formats',\n },\n },\n project: {\n data: {\n id: projOrgSlug(secrets),\n type: 'projects',\n },\n },\n },\n type: 'resources',\n },\n }\n\n return fetch(`${baseTransifexUrl}/resources`, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceCreateBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n}\n\n//@ts-ignore until we resolve the TranslationTask return type\nexport const createTask: Adapter['createTask'] = async (\n documentId: string,\n document: Record<string, any>,\n localeIds: string[],\n secrets: Secrets | null\n) => {\n let resourceId = await fetch(\n `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => res.json())\n .then((res) => (res.data ? res.data.id : null))\n\n if (!resourceId) {\n resourceId = await createResource(document, documentId, secrets)\n }\n\n const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`\n const resourceUploadBody = {\n data: {\n attributes: {\n content: document.content,\n content_encoding: 'text',\n },\n relationships: {\n resource: {\n data: {\n id: resourceId,\n type: 'resources',\n },\n },\n },\n type: 'resource_strings_async_uploads',\n },\n }\n\n return fetch(resourceUploadUrl, {\n method: 'POST',\n body: JSON.stringify(resourceUploadBody),\n headers: getHeaders(secrets),\n }).then(() => getTranslationTask(documentId, secrets))\n}\n","import {Adapter} from 'sanity-translations-tab'\n\nimport {getLocales} from './getLocales'\nimport {getTranslationTask} from './getTranslationTask'\nimport {getTranslation} from './getTranslation'\nimport {createTask} from './createTask'\n\nexport const TransifexAdapter: Adapter = {\n getLocales,\n getTranslationTask,\n createTask,\n getTranslation,\n}\n","import {\n baseDocumentLevelConfig,\n legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,\n baseFieldLevelConfig,\n Adapter,\n TranslationFunctionContext,\n} from 'sanity-translations-tab'\nimport {TransifexAdapter} from './transifexAdapter'\n\nexport {\n findLatestDraft,\n BaseDocumentDeserializer,\n BaseDocumentSerializer,\n BaseDocumentMerger,\n defaultStopTypes,\n customSerializers,\n legacyDocumentLevelPatch,\n documentLevelPatch,\n fieldLevelPatch,\n TranslationsTab,\n} from 'sanity-translations-tab'\n\ninterface ConfigOptions {\n adapter: Adapter\n secretsNamespace: string | null\n exportForTranslation: (\n id: string,\n context: TranslationFunctionContext\n ) => Promise<Record<string, any>>\n importTranslation: (\n id: string,\n localeId: string,\n doc: string,\n context: TranslationFunctionContext\n ) => Promise<void>\n}\nconst defaultDocumentLevelConfig: ConfigOptions = {\n ...baseDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst legacyDocumentLevelConfig: ConfigOptions = {\n ...baseLegacyDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst defaultFieldLevelConfig: ConfigOptions = {\n ...baseFieldLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nexport {\n TransifexAdapter,\n defaultDocumentLevelConfig,\n defaultFieldLevelConfig,\n legacyDocumentLevelConfig,\n}\n"],"names":["baseLegacyDocumentLevelConfig"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-transifex",
3
- "version": "3.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "This plugin provides an in-studio integration with Transifex. It allows your editors to send any document to Transifex with the click of a button, monitor ongoing translations, and import partial or complete translations back into the studio.",
5
5
  "keywords": [
6
6
  "sanity",
@@ -18,17 +18,16 @@
18
18
  "author": "Sanity.io",
19
19
  "exports": {
20
20
  ".": {
21
- "types": "./dist/index.d.ts",
22
21
  "source": "./src/index.ts",
23
22
  "require": "./dist/index.js",
24
- "import": "./dist/index.esm.js",
25
- "default": "./dist/index.esm.js"
23
+ "import": "./dist/index.mjs",
24
+ "default": "./dist/index.js"
26
25
  },
27
26
  "./package.json": "./package.json"
28
27
  },
28
+ "type": "commonjs",
29
29
  "main": "./dist/index.js",
30
- "module": "./dist/index.esm.js",
31
- "source": "./src/index.ts",
30
+ "module": "./dist/index.mjs",
32
31
  "types": "./dist/index.d.ts",
33
32
  "files": [
34
33
  "dist",
@@ -37,32 +36,32 @@
37
36
  "v2-incompatible.js"
38
37
  ],
39
38
  "scripts": {
40
- "build": "run-s clean && plugin-kit verify-package --silent && pkg-utils build --strict && pkg-utils --strict",
39
+ "build": "plugin-kit verify-package --silent && pkg-utils build --strict --check --clean",
41
40
  "lint": "eslint .",
42
41
  "prepare": "husky install",
43
42
  "analyze": "size-limit --why",
44
43
  "clean": "rimraf dist",
45
44
  "format": "prettier --write --cache --ignore-unknown .",
46
45
  "link-watch": "plugin-kit link-watch",
47
- "prepublishOnly": "run-s build",
46
+ "prepublishOnly": "npm run build",
48
47
  "watch": "pkg-utils watch --strict"
49
48
  },
50
49
  "dependencies": {
51
- "@sanity/incompatible-plugin": "^1.0.4",
52
- "sanity-translations-tab": "^3.0.0"
50
+ "@sanity/incompatible-plugin": "^1.0.5",
51
+ "sanity-translations-tab": "^4.4.0"
53
52
  },
54
53
  "devDependencies": {
55
54
  "@commitlint/cli": "^17.4.3",
56
55
  "@commitlint/config-conventional": "^17.4.3",
57
- "@sanity/pkg-utils": "^2.2.5",
58
- "@sanity/plugin-kit": "^3.1.4",
56
+ "@sanity/pkg-utils": "^7.9.3",
57
+ "@sanity/plugin-kit": "^4.0.19",
59
58
  "@sanity/semantic-release-preset": "^4.0.0",
60
59
  "@types/react": "^18.0.28",
61
60
  "@typescript-eslint/eslint-plugin": "^5.52.0",
62
61
  "@typescript-eslint/parser": "^5.52.0",
63
62
  "eslint": "^8.34.0",
64
63
  "eslint-config-prettier": "^8.6.0",
65
- "eslint-config-react-app": "^6.0.0",
64
+ "eslint-config-react-app": "^7.0.1",
66
65
  "eslint-config-sanity": "^6.0.0",
67
66
  "eslint-plugin-prettier": "^4.2.1",
68
67
  "eslint-plugin-react": "^7.32.2",
@@ -72,23 +71,23 @@
72
71
  "npm-run-all": "^4.1.5",
73
72
  "prettier": "^2.8.4",
74
73
  "prettier-plugin-packagejson": "^2.4.3",
75
- "react": "^18.2.0",
76
- "react-dom": "^18.2.0",
77
- "react-is": "^18.2.0",
74
+ "react": "^18.3.0",
75
+ "react-dom": "^18.3.0",
76
+ "react-is": "^18.3.0",
78
77
  "rimraf": "^4.1.2",
79
- "sanity": "^3.3.1",
78
+ "sanity": "^3.98.1",
80
79
  "semantic-release": "^20.1.0",
81
80
  "size-limit": "^4.7.0",
82
- "styled-components": "^5.3.6",
83
81
  "tslib": "^2.0.3",
84
- "typescript": "^4.9.5"
82
+ "typescript": "5.8.3"
85
83
  },
86
84
  "peerDependencies": {
87
- "react": "^18",
88
- "sanity": "^3"
85
+ "react": "^18.3 || ^19",
86
+ "react-dom": "^18.3 || ^19",
87
+ "sanity": "^3.3.1 || ^4.0.0-0"
89
88
  },
90
89
  "engines": {
91
- "node": ">=14"
90
+ "node": ">=18"
92
91
  },
93
92
  "eslintConfig": {
94
93
  "extends": "react-app",
@@ -96,5 +95,10 @@
96
95
  "@typescript-eslint/no-redeclare": "off",
97
96
  "no-redeclare": "off"
98
97
  }
98
+ },
99
+ "sanityPlugin": {
100
+ "verifyPackage": {
101
+ "tsconfig": false
102
+ }
99
103
  }
100
104
  }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  baseDocumentLevelConfig,
3
+ legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,
3
4
  baseFieldLevelConfig,
4
5
  Adapter,
5
6
  TranslationFunctionContext,
@@ -13,6 +14,7 @@ export {
13
14
  BaseDocumentMerger,
14
15
  defaultStopTypes,
15
16
  customSerializers,
17
+ legacyDocumentLevelPatch,
16
18
  documentLevelPatch,
17
19
  fieldLevelPatch,
18
20
  TranslationsTab,
@@ -38,10 +40,21 @@ const defaultDocumentLevelConfig: ConfigOptions = {
38
40
  secretsNamespace: 'transifex',
39
41
  }
40
42
 
43
+ const legacyDocumentLevelConfig: ConfigOptions = {
44
+ ...baseLegacyDocumentLevelConfig,
45
+ adapter: TransifexAdapter,
46
+ secretsNamespace: 'transifex',
47
+ }
48
+
41
49
  const defaultFieldLevelConfig: ConfigOptions = {
42
50
  ...baseFieldLevelConfig,
43
51
  adapter: TransifexAdapter,
44
52
  secretsNamespace: 'transifex',
45
53
  }
46
54
 
47
- export {TransifexAdapter, defaultDocumentLevelConfig, defaultFieldLevelConfig}
55
+ export {
56
+ TransifexAdapter,
57
+ defaultDocumentLevelConfig,
58
+ defaultFieldLevelConfig,
59
+ legacyDocumentLevelConfig,
60
+ }
package/dist/index.esm.js DELETED
@@ -1,192 +0,0 @@
1
- import { baseDocumentLevelConfig, baseFieldLevelConfig } from 'sanity-translations-tab';
2
- export { BaseDocumentDeserializer, BaseDocumentMerger, BaseDocumentSerializer, TranslationsTab, customSerializers, defaultStopTypes, documentLevelPatch, fieldLevelPatch, findLatestDraft } from 'sanity-translations-tab';
3
- const baseTransifexUrl = "https://rest.api.transifex.com";
4
- const getHeaders = secrets => ({
5
- Authorization: "Bearer ".concat(secrets == null ? void 0 : secrets.token),
6
- "Content-Type": "application/vnd.api+json"
7
- });
8
- const projOrgSlug = secrets => "o:".concat(secrets == null ? void 0 : secrets.organization, ":p:").concat(secrets == null ? void 0 : secrets.project);
9
- const getLocales = async secrets => {
10
- let locales = [];
11
- if (secrets) {
12
- locales = await fetch("".concat(baseTransifexUrl, "/projects/").concat(projOrgSlug(secrets), "/languages"), {
13
- headers: getHeaders(secrets)
14
- }).then(res => res.json()).then(res => res.data.map(lang => ({
15
- enabled: true,
16
- description: lang.attributes.name,
17
- localeId: lang.attributes.code
18
- })));
19
- }
20
- return locales;
21
- };
22
- const getTranslationTask = async (documentId, secrets) => {
23
- if (!documentId || !secrets) {
24
- return {
25
- taskId: documentId,
26
- documentId,
27
- locales: []
28
- };
29
- }
30
- const projectFilter = "filter[project]=".concat(projOrgSlug(secrets));
31
- const resourceFilter = "filter[resource]=".concat(projOrgSlug(secrets), ":r:").concat(documentId);
32
- const task = await fetch("".concat(baseTransifexUrl, "/resource_language_stats?").concat(projectFilter, "&").concat(resourceFilter), {
33
- headers: getHeaders(secrets)
34
- }).then(res => {
35
- if (res.ok) {
36
- return res.json();
37
- } else if (res.status === 404) {
38
- return {
39
- data: []
40
- };
41
- }
42
- throw Error("Failed to retrieve tasks from Transifex. Status: ".concat(res.status));
43
- }).then(res => ({
44
- taskId: "".concat(projOrgSlug(secrets), ":r:").concat(documentId),
45
- documentId,
46
- locales: res.data.map(locale => ({
47
- localeId: locale.relationships.language.data.id.split(":")[1],
48
- progress: Math.floor(100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings)))
49
- }))
50
- }));
51
- const locales = await getLocales(secrets);
52
- const localeIds = locales.map(l => l.localeId);
53
- const validLocales = task.locales.filter(locale => localeIds.find(id => id === locale.localeId));
54
- task.locales = validLocales;
55
- return task;
56
- };
57
- const pollForFileDownloadLocation = async (resourceDownloadUrl, translationDownloadId, headers) => {
58
- const response = await fetch("".concat(resourceDownloadUrl, "/").concat(translationDownloadId), {
59
- headers
60
- });
61
- if (response.status === 500) {
62
- console.info("Transifex plugin message: Received 500 for translation download ID ".concat(translationDownloadId, ". Trying to reconnect..."));
63
- await new Promise(resolve => setTimeout(resolve, 3e3));
64
- return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers);
65
- } else if (response.redirected) {
66
- console.info("Transifex plugin message: Received redirect for translation download ID ".concat(translationDownloadId, ". Following redirect now for file download."));
67
- return response.url;
68
- } else if (response.status === 200) {
69
- console.info("Transifex plugin message: Requested download location for translation download ID ".concat(translationDownloadId, ". Location is still pending, trying again."));
70
- await new Promise(resolve => setTimeout(resolve, 3e3));
71
- return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers);
72
- }
73
- console.error("Transifex plugin message: Requested download location for translation download ID ".concat(translationDownloadId, " but received error code ").concat(response.status, ". Waiting and trying again."));
74
- await new Promise(resolve => setTimeout(resolve, 3e3));
75
- return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers);
76
- };
77
- const handleFileDownload = url => {
78
- return fetch(url).then(res => res.text());
79
- };
80
- const getTranslation = async (taskId, localeId, secrets) => {
81
- const resourceDownloadBody = {
82
- data: {
83
- attributes: {
84
- content_encoding: "text"
85
- },
86
- relationships: {
87
- language: {
88
- data: {
89
- id: "l:".concat(localeId),
90
- type: "languages"
91
- }
92
- },
93
- resource: {
94
- data: {
95
- id: taskId,
96
- type: "resources"
97
- }
98
- }
99
- },
100
- type: "resource_translations_async_downloads"
101
- }
102
- };
103
- const resourceDownloadUrl = "".concat(baseTransifexUrl, "/resource_translations_async_downloads");
104
- const translationDownloadId = await fetch(resourceDownloadUrl, {
105
- headers: getHeaders(secrets),
106
- method: "POST",
107
- body: JSON.stringify(resourceDownloadBody)
108
- }).then(res => res.json()).then(res => res.data.id);
109
- const headers = getHeaders(secrets);
110
- const location = await pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers);
111
- return handleFileDownload(location);
112
- };
113
- const createResource = (doc, documentId, secrets) => {
114
- const resourceCreateBody = {
115
- data: {
116
- attributes: {
117
- accept_translations: true,
118
- name: doc.name,
119
- slug: documentId
120
- },
121
- relationships: {
122
- i18n_format: {
123
- data: {
124
- id: "HTML_FRAGMENT",
125
- type: "i18n_formats"
126
- }
127
- },
128
- project: {
129
- data: {
130
- id: projOrgSlug(secrets),
131
- type: "projects"
132
- }
133
- }
134
- },
135
- type: "resources"
136
- }
137
- };
138
- return fetch("".concat(baseTransifexUrl, "/resources"), {
139
- headers: getHeaders(secrets),
140
- method: "POST",
141
- body: JSON.stringify(resourceCreateBody)
142
- }).then(res => res.json()).then(res => res.data.id);
143
- };
144
- const createTask = async (documentId, document, localeIds, secrets) => {
145
- let resourceId = await fetch("".concat(baseTransifexUrl, "/resources/").concat(projOrgSlug(secrets), ":r:").concat(documentId), {
146
- headers: getHeaders(secrets)
147
- }).then(res => res.json()).then(res => res.data ? res.data.id : null);
148
- if (!resourceId) {
149
- resourceId = await createResource(document, documentId, secrets);
150
- }
151
- const resourceUploadUrl = "".concat(baseTransifexUrl, "/resource_strings_async_uploads");
152
- const resourceUploadBody = {
153
- data: {
154
- attributes: {
155
- content: document.content,
156
- content_encoding: "text"
157
- },
158
- relationships: {
159
- resource: {
160
- data: {
161
- id: resourceId,
162
- type: "resources"
163
- }
164
- }
165
- },
166
- type: "resource_strings_async_uploads"
167
- }
168
- };
169
- return fetch(resourceUploadUrl, {
170
- method: "POST",
171
- body: JSON.stringify(resourceUploadBody),
172
- headers: getHeaders(secrets)
173
- }).then(() => getTranslationTask(documentId, secrets));
174
- };
175
- const TransifexAdapter = {
176
- getLocales,
177
- getTranslationTask,
178
- createTask,
179
- getTranslation
180
- };
181
- const defaultDocumentLevelConfig = {
182
- ...baseDocumentLevelConfig,
183
- adapter: TransifexAdapter,
184
- secretsNamespace: "transifex"
185
- };
186
- const defaultFieldLevelConfig = {
187
- ...baseFieldLevelConfig,
188
- adapter: TransifexAdapter,
189
- secretsNamespace: "transifex"
190
- };
191
- export { TransifexAdapter, defaultDocumentLevelConfig, defaultFieldLevelConfig };
192
- //# sourceMappingURL=index.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/transifexAdapter/helpers.ts","../src/transifexAdapter/getLocales.ts","../src/transifexAdapter/getTranslationTask.ts","../src/transifexAdapter/getTranslation.ts","../src/transifexAdapter/createTask.ts","../src/transifexAdapter/index.ts","../src/index.ts"],"sourcesContent":["import {Secrets} from 'sanity-translations-tab'\n\nexport const baseTransifexUrl = 'https://rest.api.transifex.com'\n\nexport const getHeaders = (secrets: Secrets | null): Record<string, string> => ({\n Authorization: `Bearer ${secrets?.token}`,\n 'Content-Type': 'application/vnd.api+json',\n})\n\nexport const projOrgSlug = (secrets: Secrets | null): string =>\n `o:${secrets?.organization}:p:${secrets?.project}`\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n let locales = []\n if (secrets) {\n locales = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {\n headers: getHeaders(secrets),\n })\n .then((res) => res.json())\n .then((res) =>\n res.data.map((lang: Record<string, any>) => ({\n enabled: true,\n description: lang.attributes.name,\n localeId: lang.attributes.code,\n }))\n )\n }\n return locales\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getLocales} from './getLocales'\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n documentId: string,\n secrets: Secrets | null\n) => {\n if (!documentId || !secrets) {\n return {\n taskId: documentId,\n documentId: documentId,\n locales: [],\n }\n }\n const projectFilter = `filter[project]=${projOrgSlug(secrets)}`\n const resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`\n const task = await fetch(\n `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => {\n if (res.ok) {\n return res.json()\n }\n //normal -- just means that this task doesn't exist yet.\n else if (res.status === 404) {\n return {data: []}\n }\n throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`)\n })\n .then((res) => ({\n taskId: `${projOrgSlug(secrets)}:r:${documentId}`,\n documentId: documentId,\n locales: res.data.map((locale: Record<string, any>) => ({\n localeId: locale.relationships.language.data.id.split(':')[1],\n progress: Math.floor(\n 100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings))\n ),\n })),\n }))\n\n const locales = await getLocales(secrets)\n const localeIds = locales.map((l: Record<string, any>) => l.localeId)\n const validLocales = task.locales.filter((locale: Record<string, any>) =>\n localeIds.find((id: string) => id === locale.localeId)\n )\n task.locales = validLocales\n\n return task\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, getHeaders} from './helpers'\n\nconst pollForFileDownloadLocation = async (\n resourceDownloadUrl: string,\n translationDownloadId: string,\n headers: Record<string, any>\n): Promise<string> => {\n const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {\n headers: headers,\n })\n\n if (response.status === 500) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n } else if (response.redirected) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`\n )\n return response.url\n } else if (response.status === 200) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n }\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.error(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n}\n\nconst handleFileDownload = (url: string) => {\n return fetch(url).then((res) => res.text())\n}\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n taskId: string,\n localeId: string,\n secrets: Secrets | null\n) => {\n const resourceDownloadBody = {\n data: {\n attributes: {\n content_encoding: 'text',\n },\n relationships: {\n language: {\n data: {\n id: `l:${localeId}`,\n type: 'languages',\n },\n },\n resource: {\n data: {\n id: taskId,\n type: 'resources',\n },\n },\n },\n type: 'resource_translations_async_downloads',\n },\n }\n\n const resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`\n const translationDownloadId = await fetch(resourceDownloadUrl, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceDownloadBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n\n const headers = getHeaders(secrets)\n const location = await pollForFileDownloadLocation(\n resourceDownloadUrl,\n translationDownloadId,\n headers\n )\n return handleFileDownload(location)\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getTranslationTask} from './getTranslationTask'\n\nconst createResource = (doc: Record<string, any>, documentId: string, secrets: Secrets | null) => {\n const resourceCreateBody = {\n data: {\n attributes: {\n accept_translations: true,\n name: doc.name,\n slug: documentId,\n },\n relationships: {\n i18n_format: {\n data: {\n id: 'HTML_FRAGMENT',\n type: 'i18n_formats',\n },\n },\n project: {\n data: {\n id: projOrgSlug(secrets),\n type: 'projects',\n },\n },\n },\n type: 'resources',\n },\n }\n\n return fetch(`${baseTransifexUrl}/resources`, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceCreateBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n}\n\n//@ts-ignore until we resolve the TranslationTask return type\nexport const createTask: Adapter['createTask'] = async (\n documentId: string,\n document: Record<string, any>,\n localeIds: string[],\n secrets: Secrets | null\n) => {\n let resourceId = await fetch(\n `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => res.json())\n .then((res) => (res.data ? res.data.id : null))\n\n if (!resourceId) {\n resourceId = await createResource(document, documentId, secrets)\n }\n\n const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`\n const resourceUploadBody = {\n data: {\n attributes: {\n content: document.content,\n content_encoding: 'text',\n },\n relationships: {\n resource: {\n data: {\n id: resourceId,\n type: 'resources',\n },\n },\n },\n type: 'resource_strings_async_uploads',\n },\n }\n\n return fetch(resourceUploadUrl, {\n method: 'POST',\n body: JSON.stringify(resourceUploadBody),\n headers: getHeaders(secrets),\n }).then(() => getTranslationTask(documentId, secrets))\n}\n","import {Adapter} from 'sanity-translations-tab'\n\nimport {getLocales} from './getLocales'\nimport {getTranslationTask} from './getTranslationTask'\nimport {getTranslation} from './getTranslation'\nimport {createTask} from './createTask'\n\nexport const TransifexAdapter: Adapter = {\n getLocales,\n getTranslationTask,\n createTask,\n getTranslation,\n}\n","import {\n baseDocumentLevelConfig,\n baseFieldLevelConfig,\n Adapter,\n TranslationFunctionContext,\n} from 'sanity-translations-tab'\nimport {TransifexAdapter} from './transifexAdapter'\n\nexport {\n findLatestDraft,\n BaseDocumentDeserializer,\n BaseDocumentSerializer,\n BaseDocumentMerger,\n defaultStopTypes,\n customSerializers,\n documentLevelPatch,\n fieldLevelPatch,\n TranslationsTab,\n} from 'sanity-translations-tab'\n\ninterface ConfigOptions {\n adapter: Adapter\n secretsNamespace: string | null\n exportForTranslation: (\n id: string,\n context: TranslationFunctionContext\n ) => Promise<Record<string, any>>\n importTranslation: (\n id: string,\n localeId: string,\n doc: string,\n context: TranslationFunctionContext\n ) => Promise<void>\n}\nconst defaultDocumentLevelConfig: ConfigOptions = {\n ...baseDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst defaultFieldLevelConfig: ConfigOptions = {\n ...baseFieldLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nexport {TransifexAdapter, defaultDocumentLevelConfig, defaultFieldLevelConfig}\n"],"names":["baseTransifexUrl","getHeaders","secrets","Authorization","token","projOrgSlug","organization","project","getLocales","locales","fetch","headers","then","res","json","data","map","lang","enabled","description","attributes","name","localeId","code","getTranslationTask","documentId","taskId","projectFilter","resourceFilter","task","ok","status","Error","locale","relationships","language","id","split","progress","Math","floor","reviewed_strings","parseFloat","total_strings","localeIds","l","validLocales","filter","find","pollForFileDownloadLocation","resourceDownloadUrl","translationDownloadId","response","console","info","Promise","resolve","setTimeout","redirected","url","error","handleFileDownload","text","getTranslation","resourceDownloadBody","content_encoding","type","resource","method","body","JSON","stringify","location","createResource","doc","resourceCreateBody","accept_translations","slug","i18n_format","createTask","document","resourceId","resourceUploadUrl","resourceUploadBody","content","TransifexAdapter","defaultDocumentLevelConfig","baseDocumentLevelConfig","adapter","secretsNamespace","defaultFieldLevelConfig","baseFieldLevelConfig"],"mappings":";;AAEO,MAAMA,gBAAmB,GAAA,gCAAA;AAEnB,MAAAC,UAAA,GAAcC,OAAqD,KAAA;EAC9EC,aAAA,mBAAyBD,OAAS,IAAA,IAAA,GAAA,KAAA,CAAA,GAAAA,OAAA,CAAAE,KAAA,CAAA;EAClC,cAAgB,EAAA;AAClB,CAAA,CAAA;AAEO,MAAMC,cAAeH,OAAA,gBACrBA,OAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAAA,OAAA,CAASI,4BAAkBJ,OAAS,IAAA,IAAA,GAAA,KAAA,CAAA,GAAAA,OAAA,CAAAK,OAAA,CAAA;ACP9B,MAAAC,UAAA,GAAoC,MAAON,OAA4B,IAAA;EAClF,IAAIO,UAAU,EAAC;EACf,IAAIP,OAAS,EAAA;IACXO,OAAA,GAAU,MAAMC,KAAM,WAAGV,gBAA6B,uBAAAK,WAAA,CAAYH,OAAO,CAAe,iBAAA;MACtFS,OAAA,EAASV,WAAWC,OAAO;IAAA,CAC5B,EACEU,IAAK,CAACC,OAAQA,GAAI,CAAAC,IAAA,EAAM,CACxB,CAAAF,IAAA,CAAMC,GACL,IAAAA,GAAA,CAAIE,IAAK,CAAAC,GAAA,CAAKC,IAA+B,KAAA;MAC3CC,OAAS,EAAA,IAAA;MACTC,WAAA,EAAaF,KAAKG,UAAW,CAAAC,IAAA;MAC7BC,QAAA,EAAUL,KAAKG,UAAW,CAAAG;IAAA,CAC1B,CAAA,CAAA,CACJ;EACJ;EACO,OAAAd,OAAA;AACT,CAAA;ACfa,MAAAe,kBAAA,GAAoD,OAC/DC,UAAA,EACAvB,OACG,KAAA;EACC,IAAA,CAACuB,UAAc,IAAA,CAACvB,OAAS,EAAA;IACpB,OAAA;MACLwB,MAAQ,EAAAD,UAAA;MACRA,UAAA;MACAhB,SAAS;IAAC,CACZ;EACF;EACM,MAAAkB,aAAA,6BAAmCtB,WAAA,CAAYH,OAAO,CAAA,CAAA;EAC5D,MAAM0B,cAAiB,8BAAoBvB,WAAY,CAAAH,OAAO,CAAO,gBAAAuB,UAAA,CAAA;EACrE,MAAMI,OAAO,MAAMnB,KAAA,WACdV,sDAA4C2B,aAAiB,cAAAC,cAAA,GAChE;IAACjB,OAAA,EAASV,UAAW,CAAAC,OAAO;EAAC,CAAA,CAC/B,CACGU,IAAK,CAACC,GAAQ,IAAA;IACb,IAAIA,IAAIiB,EAAI,EAAA;MACV,OAAOjB,IAAIC,IAAK,EAAA;IAAA,CAClB,MAAA,IAESD,GAAI,CAAAkB,MAAA,KAAW,GAAK,EAAA;MACpB,OAAA;QAAChB,IAAM,EAAA;OAAE;IAClB;IACM,MAAAiB,KAAA,4DAA0DnB,GAAA,CAAIkB,MAAQ,EAAA;EAAA,CAC7E,CAAA,CACAnB,IAAK,CAACC,GAAS,KAAA;IACda,MAAQ,YAAGrB,WAAY,CAAAH,OAAO,CAAO,gBAAAuB,UAAA,CAAA;IACrCA,UAAA;IACAhB,OAAS,EAAAI,GAAA,CAAIE,IAAK,CAAAC,GAAA,CAAKiB,MAAiC,KAAA;MACtDX,QAAA,EAAUW,OAAOC,aAAc,CAAAC,QAAA,CAASpB,KAAKqB,EAAG,CAAAC,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA;MAC5DC,UAAUC,IAAK,CAAAC,KAAA,CACb,OAAOP,MAAO,CAAAb,UAAA,CAAWqB,mBAAmBC,UAAW,CAAAT,MAAA,CAAOb,WAAWuB,aAAa,CAAA,CAAA;IACxF,CACA,CAAA;EACF,CAAA,CAAA,CAAA;EAEE,MAAAlC,OAAA,GAAU,MAAMD,UAAA,CAAWN,OAAO,CAAA;EACxC,MAAM0C,YAAYnC,OAAQ,CAAAO,GAAA,CAAK6B,CAAA,IAA2BA,EAAEvB,QAAQ,CAAA;EAC9D,MAAAwB,YAAA,GAAejB,KAAKpB,OAAQ,CAAAsC,MAAA,CAAQd,UACxCW,SAAU,CAAAI,IAAA,CAAMZ,EAAe,IAAAA,EAAA,KAAOH,OAAOX,QAAQ,CAAA,CACvD;EACAO,IAAA,CAAKpB,OAAU,GAAAqC,YAAA;EAER,OAAAjB,IAAA;AACT,CAAA;AC/CA,MAAMoB,2BAA8B,GAAA,OAClCC,mBACA,EAAAC,qBAAA,EACAxC,OACoB,KAAA;EACpB,MAAMyC,QAAW,GAAA,MAAM1C,KAAM,WAAGwC,iCAAuBC,qBAAyB,GAAA;IAC9ExC;EAAA,CACD,CAAA;EAEG,IAAAyC,QAAA,CAASrB,WAAW,GAAK,EAAA;IAEnBsB,OAAA,CAAAC,IAAA,8EACgEH,qBAAA,8BACxE;IACA,MAAM,IAAII,OAAQ,CAACC,WAAYC,UAAW,CAAAD,OAAA,EAAS,GAAI,CAAC,CAAA;IACjD,OAAAP,2BAAA,CAA4BC,mBAAqB,EAAAC,qBAAA,EAAuBxC,OAAO,CAAA;EAAA,CACxF,MAAA,IAAWyC,SAASM,UAAY,EAAA;IAEtBL,OAAA,CAAAC,IAAA,mFACqEH,qBAAA,iDAC7E;IACA,OAAOC,QAAS,CAAAO,GAAA;EAAA,CAClB,MAAA,IAAWP,QAAS,CAAArB,MAAA,KAAW,GAAK,EAAA;IAE1BsB,OAAA,CAAAC,IAAA,6FAC+EH,qBAAA,gDACvF;IACA,MAAM,IAAII,OAAQ,CAACC,WAAYC,UAAW,CAAAD,OAAA,EAAS,GAAI,CAAC,CAAA;IACjD,OAAAP,2BAAA,CAA4BC,mBAAqB,EAAAC,qBAAA,EAAuBxC,OAAO,CAAA;EACxF;EAEQ0C,OAAA,CAAAO,KAAA,6FAC+ET,2DAAiDC,QAAS,CAAArB,MAAA,iCACjJ;EACA,MAAM,IAAIwB,OAAQ,CAACC,WAAYC,UAAW,CAAAD,OAAA,EAAS,GAAI,CAAC,CAAA;EACjD,OAAAP,2BAAA,CAA4BC,mBAAqB,EAAAC,qBAAA,EAAuBxC,OAAO,CAAA;AACxF,CAAA;AAEA,MAAMkD,kBAAA,GAAsBF,GAAgB,IAAA;EACnC,OAAAjD,KAAA,CAAMiD,GAAG,CAAE,CAAA/C,IAAA,CAAMC,GAAQ,IAAAA,GAAA,CAAIiD,MAAM,CAAA;AAC5C,CAAA;AAEO,MAAMC,cAA4C,GAAA,OACvDrC,MACA,EAAAJ,QAAA,EACApB,OACG,KAAA;EACH,MAAM8D,oBAAuB,GAAA;IAC3BjD,IAAM,EAAA;MACJK,UAAY,EAAA;QACV6C,gBAAkB,EAAA;MACpB,CAAA;MACA/B,aAAe,EAAA;QACbC,QAAU,EAAA;UACRpB,IAAM,EAAA;YACJqB,gBAASd,QAAA,CAAA;YACT4C,IAAM,EAAA;UACR;QACF,CAAA;QACAC,QAAU,EAAA;UACRpD,IAAM,EAAA;YACJqB,EAAI,EAAAV,MAAA;YACJwC,IAAM,EAAA;UACR;QACF;MACF,CAAA;MACAA,IAAM,EAAA;IACR;EAAA,CACF;EAEA,MAAMhB,gCAAyBlD,gBAAA,2CAAA;EACzB,MAAAmD,qBAAA,GAAwB,MAAMzC,KAAA,CAAMwC,mBAAqB,EAAA;IAC7DvC,OAAA,EAASV,WAAWC,OAAO,CAAA;IAC3BkE,MAAQ,EAAA,MAAA;IACRC,IAAA,EAAMC,IAAK,CAAAC,SAAA,CAAUP,oBAAoB;EAC1C,CAAA,CAAA,CACEpD,IAAK,CAACC,OAAQA,GAAI,CAAAC,IAAA,EAAM,CAAA,CACxBF,IAAK,CAACC,GAAQ,IAAAA,GAAA,CAAIE,KAAKqB,EAAE,CAAA;EAEtB,MAAAzB,OAAA,GAAUV,WAAWC,OAAO,CAAA;EAClC,MAAMsE,WAAW,MAAMvB,2BAAA,CACrBC,mBAAA,EACAC,qBAAA,EACAxC,OAAA,CACF;EACA,OAAOkD,mBAAmBW,QAAQ,CAAA;AACpC,CAAA;ACrFA,MAAMC,cAAiB,GAAA,CAACC,GAA0B,EAAAjD,UAAA,EAAoBvB,OAA4B,KAAA;EAChG,MAAMyE,kBAAqB,GAAA;IACzB5D,IAAM,EAAA;MACJK,UAAY,EAAA;QACVwD,mBAAqB,EAAA,IAAA;QACrBvD,MAAMqD,GAAI,CAAArD,IAAA;QACVwD,IAAM,EAAApD;MACR,CAAA;MACAS,aAAe,EAAA;QACb4C,WAAa,EAAA;UACX/D,IAAM,EAAA;YACJqB,EAAI,EAAA,eAAA;YACJ8B,IAAM,EAAA;UACR;QACF,CAAA;QACA3D,OAAS,EAAA;UACPQ,IAAM,EAAA;YACJqB,EAAA,EAAI/B,YAAYH,OAAO,CAAA;YACvBgE,IAAM,EAAA;UACR;QACF;MACF,CAAA;MACAA,IAAM,EAAA;IACR;EAAA,CACF;EAEO,OAAAxD,KAAA,WAASV,gBAA8B,iBAAA;IAC5CW,OAAA,EAASV,WAAWC,OAAO,CAAA;IAC3BkE,MAAQ,EAAA,MAAA;IACRC,IAAA,EAAMC,IAAK,CAAAC,SAAA,CAAUI,kBAAkB;EACxC,CAAA,CAAA,CACE/D,IAAK,CAACC,OAAQA,GAAI,CAAAC,IAAA,EAAM,CAAA,CACxBF,IAAK,CAACC,GAAQ,IAAAA,GAAA,CAAIE,KAAKqB,EAAE,CAAA;AAC9B,CAAA;AAGO,MAAM2C,UAAoC,GAAA,OAC/CtD,UACA,EAAAuD,QAAA,EACApC,WACA1C,OACG,KAAA;EACH,IAAI+E,aAAa,MAAMvE,KAAA,WAClBV,gBAAA,wBAA8BK,WAAY,CAAAH,OAAO,CAAO,gBAAAuB,UAAA,GAC3D;IAACd,OAAA,EAASV,UAAW,CAAAC,OAAO;EAAC,CAAA,EAE5BU,IAAK,CAACC,GAAQ,IAAAA,GAAA,CAAIC,MAAM,CAAA,CACxBF,IAAK,CAACC,OAASA,GAAI,CAAAE,IAAA,GAAOF,GAAI,CAAAE,IAAA,CAAKqB,KAAK,IAAK,CAAA;EAEhD,IAAI,CAAC6C,UAAY,EAAA;IACfA,UAAA,GAAa,MAAMR,cAAA,CAAeO,QAAU,EAAAvD,UAAA,EAAYvB,OAAO,CAAA;EACjE;EAEA,MAAMgF,8BAAuBlF,gBAAA,oCAAA;EAC7B,MAAMmF,kBAAqB,GAAA;IACzBpE,IAAM,EAAA;MACJK,UAAY,EAAA;QACVgE,SAASJ,QAAS,CAAAI,OAAA;QAClBnB,gBAAkB,EAAA;MACpB,CAAA;MACA/B,aAAe,EAAA;QACbiC,QAAU,EAAA;UACRpD,IAAM,EAAA;YACJqB,EAAI,EAAA6C,UAAA;YACJf,IAAM,EAAA;UACR;QACF;MACF,CAAA;MACAA,IAAM,EAAA;IACR;EAAA,CACF;EAEA,OAAOxD,MAAMwE,iBAAmB,EAAA;IAC9Bd,MAAQ,EAAA,MAAA;IACRC,IAAA,EAAMC,IAAK,CAAAC,SAAA,CAAUY,kBAAkB,CAAA;IACvCxE,OAAA,EAASV,WAAWC,OAAO;EAAA,CAC5B,CAAE,CAAAU,IAAA,CAAK,MAAMY,kBAAmB,CAAAC,UAAA,EAAYvB,OAAO,CAAC,CAAA;AACvD,CAAA;AC1EO,MAAMmF,gBAA4B,GAAA;EACvC7E,UAAA;EACAgB,kBAAA;EACAuD,UAAA;EACAhB;AACF,CAAA;ACsBA,MAAMuB,0BAA4C,GAAA;EAChD,GAAGC,uBAAA;EACHC,OAAS,EAAAH,gBAAA;EACTI,gBAAkB,EAAA;AACpB,CAAA;AAEA,MAAMC,uBAAyC,GAAA;EAC7C,GAAGC,oBAAA;EACHH,OAAS,EAAAH,gBAAA;EACTI,gBAAkB,EAAA;AACpB,CAAA;"}
@@ -1 +0,0 @@
1
- declare module 'part:@sanity/base/client'