waha-shared 1.0.481 → 1.0.483

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.
@@ -17,22 +17,8 @@
17
17
  "some": { "type": "string" },
18
18
  "none": { "type": "string" },
19
19
  "highlight_verse": { "type": "string" },
20
- "join_others_discovering": { "type": "string" },
21
20
  "people_shared_last_week": { "type": "string" },
22
- "onboarding_dbs_intro_heading": { "type": "string" },
23
- "onboarding_dbs_intro_body": { "type": "string" },
24
- "onboarding_invite_group_heading": { "type": "string" },
25
- "onboarding_invite_group_body": { "type": "string" },
26
- "popups_need_help_with_group_title": { "type": "string" },
27
- "popups_need_help_with_group_message": { "type": "string" },
28
21
  "popups_micro_lessons_banner": { "type": "string" },
29
- "question_in_a_group": { "type": "string" },
30
- "question_what_brings_you_question": { "type": "string" },
31
- "question_what_brings_you_body": { "type": "string" },
32
- "question_what_brings_you_option_1": { "type": "string" },
33
- "question_what_brings_you_option_2": { "type": "string" },
34
- "question_what_brings_you_option_3": { "type": "string" },
35
- "question_what_brings_you_option_4": { "type": "string" },
36
22
  "name": { "type": "string", "minLength": 1 },
37
23
  "version": { "type": "string", "minLength": 1 },
38
24
  "a11y_add_secondary_language_hint": {
@@ -519,11 +505,9 @@
519
505
  "article": { "type": "string", "minLength": 1 },
520
506
  "no_results": { "type": "string", "minLength": 1 },
521
507
  "no_notes": { "type": "string", "minLength": 1 },
522
- "not_yet": { "type": "string", "minLength": 1 },
523
508
  "micro_lesson": { "type": "string", "minLength": 1 },
524
509
  "micro_lessons": { "type": "string", "minLength": 1 },
525
510
  "filter_micro_lessons": { "type": "string", "minLength": 1 },
526
- "question_placeholder": { "type": "string", "minLength": 1 },
527
511
  "no_text_available": { "type": "string" },
528
512
  "share_story_audio": { "type": "string" },
529
513
  "read_along": { "type": "string" },
@@ -871,11 +855,9 @@
871
855
  "article",
872
856
  "no_results",
873
857
  "no_notes",
874
- "not_yet",
875
858
  "micro_lesson",
876
859
  "micro_lessons",
877
860
  "filter_micro_lessons",
878
- "question_placeholder",
879
861
  "no_text_available",
880
862
  "share_story_audio",
881
863
  "read_along",
@@ -13,22 +13,8 @@ export declare const TrickyStrings: z.ZodObject<{
13
13
  some: z.ZodOptional<z.ZodString>;
14
14
  none: z.ZodOptional<z.ZodString>;
15
15
  highlight_verse: z.ZodOptional<z.ZodString>;
16
- join_others_discovering: z.ZodOptional<z.ZodString>;
17
16
  people_shared_last_week: z.ZodOptional<z.ZodString>;
18
- onboarding_dbs_intro_heading: z.ZodOptional<z.ZodString>;
19
- onboarding_dbs_intro_body: z.ZodOptional<z.ZodString>;
20
- onboarding_invite_group_heading: z.ZodOptional<z.ZodString>;
21
- onboarding_invite_group_body: z.ZodOptional<z.ZodString>;
22
- popups_need_help_with_group_title: z.ZodOptional<z.ZodString>;
23
- popups_need_help_with_group_message: z.ZodOptional<z.ZodString>;
24
17
  popups_micro_lessons_banner: z.ZodOptional<z.ZodString>;
25
- question_in_a_group: z.ZodOptional<z.ZodString>;
26
- question_what_brings_you_question: z.ZodOptional<z.ZodString>;
27
- question_what_brings_you_body: z.ZodOptional<z.ZodString>;
28
- question_what_brings_you_option_1: z.ZodOptional<z.ZodString>;
29
- question_what_brings_you_option_2: z.ZodOptional<z.ZodString>;
30
- question_what_brings_you_option_3: z.ZodOptional<z.ZodString>;
31
- question_what_brings_you_option_4: z.ZodOptional<z.ZodString>;
32
18
  }, z.core.$strip>;
33
19
  export declare const LanguageTranslationsApp: z.ZodObject<{
34
20
  name: z.ZodString;
@@ -338,11 +324,9 @@ export declare const LanguageTranslationsApp: z.ZodObject<{
338
324
  article: z.ZodString;
339
325
  no_results: z.ZodString;
340
326
  no_notes: z.ZodString;
341
- not_yet: z.ZodString;
342
327
  micro_lesson: z.ZodString;
343
328
  micro_lessons: z.ZodString;
344
329
  filter_micro_lessons: z.ZodString;
345
- question_placeholder: z.ZodString;
346
330
  no_text_available: z.ZodString;
347
331
  share_story_audio: z.ZodString;
348
332
  read_along: z.ZodString;
@@ -387,22 +371,8 @@ export declare const LanguageTranslationsApp: z.ZodObject<{
387
371
  some: z.ZodOptional<z.ZodString>;
388
372
  none: z.ZodOptional<z.ZodString>;
389
373
  highlight_verse: z.ZodOptional<z.ZodString>;
390
- join_others_discovering: z.ZodOptional<z.ZodString>;
391
374
  people_shared_last_week: z.ZodOptional<z.ZodString>;
392
- onboarding_dbs_intro_heading: z.ZodOptional<z.ZodString>;
393
- onboarding_dbs_intro_body: z.ZodOptional<z.ZodString>;
394
- onboarding_invite_group_heading: z.ZodOptional<z.ZodString>;
395
- onboarding_invite_group_body: z.ZodOptional<z.ZodString>;
396
- popups_need_help_with_group_title: z.ZodOptional<z.ZodString>;
397
- popups_need_help_with_group_message: z.ZodOptional<z.ZodString>;
398
375
  popups_micro_lessons_banner: z.ZodOptional<z.ZodString>;
399
- question_in_a_group: z.ZodOptional<z.ZodString>;
400
- question_what_brings_you_question: z.ZodOptional<z.ZodString>;
401
- question_what_brings_you_body: z.ZodOptional<z.ZodString>;
402
- question_what_brings_you_option_1: z.ZodOptional<z.ZodString>;
403
- question_what_brings_you_option_2: z.ZodOptional<z.ZodString>;
404
- question_what_brings_you_option_3: z.ZodOptional<z.ZodString>;
405
- question_what_brings_you_option_4: z.ZodOptional<z.ZodString>;
406
376
  }, z.core.$strip>;
407
377
  export type LanguageTranslationsApp = z.infer<typeof LanguageTranslationsApp>;
408
378
  export declare const TranslationsApp: z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -713,11 +683,9 @@ export declare const TranslationsApp: z.ZodRecord<z.ZodString, z.ZodObject<{
713
683
  article: z.ZodString;
714
684
  no_results: z.ZodString;
715
685
  no_notes: z.ZodString;
716
- not_yet: z.ZodString;
717
686
  micro_lesson: z.ZodString;
718
687
  micro_lessons: z.ZodString;
719
688
  filter_micro_lessons: z.ZodString;
720
- question_placeholder: z.ZodString;
721
689
  no_text_available: z.ZodString;
722
690
  share_story_audio: z.ZodString;
723
691
  read_along: z.ZodString;
@@ -762,21 +730,7 @@ export declare const TranslationsApp: z.ZodRecord<z.ZodString, z.ZodObject<{
762
730
  some: z.ZodOptional<z.ZodString>;
763
731
  none: z.ZodOptional<z.ZodString>;
764
732
  highlight_verse: z.ZodOptional<z.ZodString>;
765
- join_others_discovering: z.ZodOptional<z.ZodString>;
766
733
  people_shared_last_week: z.ZodOptional<z.ZodString>;
767
- onboarding_dbs_intro_heading: z.ZodOptional<z.ZodString>;
768
- onboarding_dbs_intro_body: z.ZodOptional<z.ZodString>;
769
- onboarding_invite_group_heading: z.ZodOptional<z.ZodString>;
770
- onboarding_invite_group_body: z.ZodOptional<z.ZodString>;
771
- popups_need_help_with_group_title: z.ZodOptional<z.ZodString>;
772
- popups_need_help_with_group_message: z.ZodOptional<z.ZodString>;
773
734
  popups_micro_lessons_banner: z.ZodOptional<z.ZodString>;
774
- question_in_a_group: z.ZodOptional<z.ZodString>;
775
- question_what_brings_you_question: z.ZodOptional<z.ZodString>;
776
- question_what_brings_you_body: z.ZodOptional<z.ZodString>;
777
- question_what_brings_you_option_1: z.ZodOptional<z.ZodString>;
778
- question_what_brings_you_option_2: z.ZodOptional<z.ZodString>;
779
- question_what_brings_you_option_3: z.ZodOptional<z.ZodString>;
780
- question_what_brings_you_option_4: z.ZodOptional<z.ZodString>;
781
735
  }, z.core.$strip>>;
782
736
  export type TranslationsApp = z.infer<typeof TranslationsApp>;
@@ -19,23 +19,10 @@ exports.TrickyStrings = zod_1.z
19
19
  none: zod_1.z.string(),
20
20
  // Verse highlighting
21
21
  highlight_verse: zod_1.z.string(),
22
- // New onboarding
23
- join_others_discovering: zod_1.z.string(),
22
+ // Sharing social proof
24
23
  people_shared_last_week: zod_1.z.string(),
25
- onboarding_dbs_intro_heading: zod_1.z.string(),
26
- onboarding_dbs_intro_body: zod_1.z.string(),
27
- onboarding_invite_group_heading: zod_1.z.string(),
28
- onboarding_invite_group_body: zod_1.z.string(),
29
- popups_need_help_with_group_title: zod_1.z.string(),
30
- popups_need_help_with_group_message: zod_1.z.string(),
24
+ // Micro lessons
31
25
  popups_micro_lessons_banner: zod_1.z.string(),
32
- question_in_a_group: zod_1.z.string(),
33
- question_what_brings_you_question: zod_1.z.string(),
34
- question_what_brings_you_body: zod_1.z.string(),
35
- question_what_brings_you_option_1: zod_1.z.string(),
36
- question_what_brings_you_option_2: zod_1.z.string(),
37
- question_what_brings_you_option_3: zod_1.z.string(),
38
- question_what_brings_you_option_4: zod_1.z.string(),
39
26
  })
40
27
  .partial();
41
28
  exports.LanguageTranslationsApp = zod_1.z.object({
@@ -349,11 +336,9 @@ exports.LanguageTranslationsApp = zod_1.z.object({
349
336
  article: zod_1.z.string().min(1),
350
337
  no_results: zod_1.z.string().min(1),
351
338
  no_notes: zod_1.z.string().min(1),
352
- not_yet: zod_1.z.string().min(1),
353
339
  micro_lesson: zod_1.z.string().min(1),
354
340
  micro_lessons: zod_1.z.string().min(1),
355
341
  filter_micro_lessons: zod_1.z.string().min(1),
356
- question_placeholder: zod_1.z.string().min(1),
357
342
  no_text_available: zod_1.z.string(),
358
343
  // ---
359
344
  // Auth feature
@@ -1,3 +1,4 @@
1
+ import { type SourceFilesModel } from '@crowdin/crowdin-api-client';
1
2
  /**
2
3
  * Map of project names to their Crowdin ids. Can be retrieved by going to the
3
4
  * project's dashboard in Crowdin and looking on the right side panel for ID:
@@ -7,7 +8,14 @@ export declare const crowdinProjectIds: {
7
8
  website: string;
8
9
  app: string;
9
10
  dmc: string;
11
+ emails: string;
10
12
  };
13
+ /**
14
+ * Where a translated file lands, relative to the language folder. Every Waha
15
+ * project maps a language's Crowdin three-letter code onto its Waha id, which
16
+ * is why translations download straight into `crowdin/{wahaLanguageId}`.
17
+ */
18
+ export declare const CROWDIN_TRANSLATION_PATTERN = "%three_letters_code%/%original_file_name%";
11
19
  interface DownloadTranslationsParams {
12
20
  /**
13
21
  * The id of the project to download translations for. Can either be taken
@@ -17,14 +25,45 @@ interface DownloadTranslationsParams {
17
25
  projectId: string;
18
26
  /**
19
27
  * The folder where translations are stored. Source translations are assumed
20
- * to be stored in `translationsFolder/eng/*.json`.
28
+ * to be stored in `translationsFolder/eng/{sourcePattern}`.
21
29
  */
22
30
  translationsFolder: string;
23
31
  /** The Crowdin personal access token to use for authentication. */
24
32
  token: string;
33
+ /**
34
+ * Glob matching the source files to download translations for, relative to
35
+ * `translationsFolder/eng`. Defaults to the JSON files every other project
36
+ * uses; the drip emails are markdown.
37
+ */
38
+ sourcePattern?: string;
25
39
  }
26
40
  /**
27
41
  * Downloads translations from Crowdin for a given project ID and source path.
28
42
  */
29
- export declare function downloadTranslations({ projectId, translationsFolder, token, }: DownloadTranslationsParams): void;
43
+ export declare function downloadTranslations({ projectId, translationsFolder, token, sourcePattern, }: DownloadTranslationsParams): void;
44
+ interface UploadSourceParams {
45
+ /** The id of the project to upload to, from `crowdinProjectIds`. */
46
+ projectId: string;
47
+ /**
48
+ * Absolute path to the source file to upload, somewhere under a `crowdin/`
49
+ * directory (e.g. `/path/to/project/crowdin/eng/app_interface_text.json`,
50
+ * or `/path/to/project/crowdin/foo.md` for a project that keeps sources at
51
+ * the root). The path Crowdin sees is everything after `crowdin/`.
52
+ */
53
+ source: string;
54
+ /** The Crowdin personal access token to use for authentication. */
55
+ token: string;
56
+ /**
57
+ * Label to attach, created if Crowdin has not seen it before. Only applies
58
+ * to new keys, which is what makes version tagging work.
59
+ */
60
+ label?: string;
61
+ /** Parser and import settings, for files needing more than the defaults. */
62
+ fileSettings?: {
63
+ type?: SourceFilesModel.FileType;
64
+ importOptions?: SourceFilesModel.ImportOptions;
65
+ };
66
+ }
67
+ /** Uploads a source file to Crowdin, creating it or replacing its contents. */
68
+ export declare function uploadSource({ projectId, source, token, label, fileSettings, }: UploadSourceParams): Promise<void>;
30
69
  export {};
@@ -1,8 +1,15 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.crowdinProjectIds = void 0;
6
+ exports.CROWDIN_TRANSLATION_PATTERN = exports.crowdinProjectIds = void 0;
4
7
  exports.downloadTranslations = downloadTranslations;
8
+ exports.uploadSource = uploadSource;
9
+ const crowdin_api_client_1 = __importDefault(require("@crowdin/crowdin-api-client"));
10
+ const fs_1 = __importDefault(require("fs"));
5
11
  const node_child_process_1 = require("node:child_process");
12
+ const path_1 = __importDefault(require("path"));
6
13
  /**
7
14
  * Map of project names to their Crowdin ids. Can be retrieved by going to the
8
15
  * project's dashboard in Crowdin and looking on the right side panel for ID:
@@ -12,23 +19,133 @@ exports.crowdinProjectIds = {
12
19
  website: '698737',
13
20
  app: '549537',
14
21
  dmc: '663528',
22
+ emails: '929263',
15
23
  };
24
+ /**
25
+ * Where a translated file lands, relative to the language folder. Every Waha
26
+ * project maps a language's Crowdin three-letter code onto its Waha id, which
27
+ * is why translations download straight into `crowdin/{wahaLanguageId}`.
28
+ */
29
+ exports.CROWDIN_TRANSLATION_PATTERN = '%three_letters_code%/%original_file_name%';
30
+ /**
31
+ * Derives the Crowdin-relative path (what's shown in the dashboard, e.g.
32
+ * `eng/app_interface_text.json`) from an absolute filesystem path. Every
33
+ * Waha project keeps its Crowdin sources under a `crowdin/` directory, so
34
+ * that directory's name is the anchor: everything after the last `crowdin`
35
+ * segment is the path Crowdin knows about.
36
+ *
37
+ * This indirection exists because callers pass an absolute path rather than
38
+ * one relative to this file. `shared/` ships as a published npm package, so
39
+ * this file can end up executing from inside a consumer's `node_modules`,
40
+ * where a base path hardcoded relative to `__dirname` points nowhere useful.
41
+ */
42
+ function crowdinRelativePath(absolutePath) {
43
+ const segments = absolutePath.split(path_1.default.sep);
44
+ const crowdinIndex = segments.lastIndexOf('crowdin');
45
+ if (crowdinIndex === -1) {
46
+ throw new Error(`Expected an absolute path under a "crowdin" directory, got "${absolutePath}".`);
47
+ }
48
+ return segments.slice(crowdinIndex + 1).join('/');
49
+ }
16
50
  /**
17
51
  * Downloads translations from Crowdin for a given project ID and source path.
18
52
  */
19
- function downloadTranslations({ projectId, translationsFolder, token, }) {
53
+ function downloadTranslations({ projectId, translationsFolder, token, sourcePattern = '*.json', }) {
20
54
  (0, node_child_process_1.spawnSync)([
21
- 'npx @crowdin/cli download translations',
55
+ 'crowdin download translations',
22
56
  `-i ${projectId}`,
23
- `-s eng/*.json`,
57
+ `-s "eng/${sourcePattern}"`,
24
58
  // Add Crowdin token present in .env file.
25
59
  `-T ${token}`,
26
60
  // Set the base path for which the other paths will be relative to.
27
61
  `--base-path="${translationsFolder}"`,
28
62
  // Where to download translations to relative to the base path.
29
- '-t "/%three_letters_code%/%original_file_name%"',
63
+ `-t "/${exports.CROWDIN_TRANSLATION_PATTERN}"`,
30
64
  ].join(' '), {
31
65
  shell: true,
32
66
  stdio: 'inherit',
33
67
  });
34
68
  }
69
+ /** Uploads a source file to Crowdin, creating it or replacing its contents. */
70
+ async function uploadSource({ projectId, source, token, label, fileSettings, }) {
71
+ const client = new crowdin_api_client_1.default({ token });
72
+ const id = Number(projectId);
73
+ const relativePath = crowdinRelativePath(source);
74
+ const fileName = relativePath.split('/').pop();
75
+ const contents = fs_1.default.readFileSync(source, 'utf8');
76
+ const storage = await client.uploadStorageApi.addStorage(fileName, contents);
77
+ /*
78
+ * Replacing a file needs its numeric id, and the API has no by-path
79
+ * lookup, so this listing is how the id is obtained rather than a
80
+ * precaution. `filter` narrows it server-side; the path comparison still
81
+ * has to be exact because the filter matches on substrings.
82
+ */
83
+ const files = await client.sourceFilesApi.listProjectFiles(id, {
84
+ filter: fileName,
85
+ limit: 500,
86
+ });
87
+ const existing = files.data.find((file) => file.data.path === `/${relativePath}`);
88
+ const exportOptions = {
89
+ exportPattern: `/${exports.CROWDIN_TRANSLATION_PATTERN}`,
90
+ };
91
+ const attachLabelIds = label ? [await labelId(client, id, label)] : undefined;
92
+ /*
93
+ * The two requests are built separately rather than sharing a spread. A
94
+ * file's parser `type` is fixed at creation and the replace endpoint
95
+ * rejects it outright, and spreading into a typed parameter skips
96
+ * TypeScript's excess property check — so a shared object hides the
97
+ * mistake until Crowdin returns "Field 'type' is unexpected".
98
+ */
99
+ if (existing) {
100
+ const request = {
101
+ storageId: storage.data.id,
102
+ exportOptions,
103
+ // Keeps translations of revised strings but drops their approvals, so
104
+ // a reviewer looks again rather than stale copy staying green.
105
+ updateOption: 'keep_translations',
106
+ ...(fileSettings?.importOptions && {
107
+ importOptions: fileSettings.importOptions,
108
+ }),
109
+ ...(attachLabelIds && { attachLabelIds }),
110
+ };
111
+ await client.sourceFilesApi.updateOrRestoreFile(id, existing.data.id, request);
112
+ return;
113
+ }
114
+ const request = {
115
+ storageId: storage.data.id,
116
+ name: fileName,
117
+ exportOptions,
118
+ ...fileSettings,
119
+ ...(attachLabelIds && { attachLabelIds }),
120
+ ...(await directoryId(client, id, relativePath)),
121
+ };
122
+ await client.sourceFilesApi.createFile(id, request);
123
+ }
124
+ /** Resolves a label by title, creating it if Crowdin does not have it. */
125
+ async function labelId(client, projectId, title) {
126
+ const labels = await client.labelsApi.listLabels(projectId, { limit: 500 });
127
+ const existing = labels.data.find((label) => label.data.title === title);
128
+ if (existing)
129
+ return existing.data.id;
130
+ return (await client.labelsApi.addLabel(projectId, { title })).data.id;
131
+ }
132
+ /**
133
+ * Resolves the directory a new file belongs in, or nothing for projects that
134
+ * keep sources at the root.
135
+ *
136
+ * A missing directory is an error rather than something to create: a
137
+ * misspelled path that gets created silently uploads to a new file and
138
+ * orphans every translation attached to the real one.
139
+ */
140
+ async function directoryId(client, projectId, relativePath) {
141
+ const segments = relativePath.split('/').slice(0, -1);
142
+ if (segments.length === 0)
143
+ return {};
144
+ const wanted = `/${segments.join('/')}`;
145
+ const directories = await client.sourceFilesApi.listProjectDirectories(projectId, { limit: 500 });
146
+ const match = directories.data.find((entry) => entry.data.path === wanted);
147
+ if (!match) {
148
+ throw new Error(`Crowdin project ${projectId} has no ${wanted} directory. Create it before uploading.`);
149
+ }
150
+ return { directoryId: match.data.id };
151
+ }
@@ -0,0 +1,28 @@
1
+ import z from 'zod';
2
+ /** The two audiences a new signup can be enrolled in. */
3
+ export declare const DripSequenceId: z.ZodEnum<{
4
+ drip_believer: "drip_believer";
5
+ drip_seeker: "drip_seeker";
6
+ }>;
7
+ export type DripSequenceId = z.infer<typeof DripSequenceId>;
8
+ /** Languages the drip is provisioned for. */
9
+ export declare const DRIP_LANGUAGES: readonly ["eng", "ara", "swe"];
10
+ export declare const DripLanguageId: z.ZodEnum<{
11
+ eng: "eng";
12
+ ara: "ara";
13
+ swe: "swe";
14
+ }>;
15
+ export type DripLanguageId = z.infer<typeof DripLanguageId>;
16
+ export declare const SubscribeDripBody: z.ZodObject<{
17
+ sequence: z.ZodEnum<{
18
+ drip_believer: "drip_believer";
19
+ drip_seeker: "drip_seeker";
20
+ }>;
21
+ languageId: z.ZodEnum<{
22
+ eng: "eng";
23
+ ara: "ara";
24
+ swe: "swe";
25
+ }>;
26
+ firstName: z.ZodOptional<z.ZodString>;
27
+ }, z.core.$strip>;
28
+ export type SubscribeDripBody = z.infer<typeof SubscribeDripBody>;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SubscribeDripBody = exports.DripLanguageId = exports.DRIP_LANGUAGES = exports.DripSequenceId = void 0;
7
+ const zod_1 = __importDefault(require("zod"));
8
+ /** The two audiences a new signup can be enrolled in. */
9
+ exports.DripSequenceId = zod_1.default.enum(['drip_believer', 'drip_seeker']);
10
+ /** Languages the drip is provisioned for. */
11
+ exports.DRIP_LANGUAGES = ['eng', 'ara', 'swe'];
12
+ exports.DripLanguageId = zod_1.default.enum(exports.DRIP_LANGUAGES);
13
+ exports.SubscribeDripBody = zod_1.default.object({
14
+ sequence: exports.DripSequenceId,
15
+ languageId: exports.DripLanguageId,
16
+ firstName: zod_1.default.string().optional(),
17
+ });
@@ -291,8 +291,8 @@ export declare const MicroLessonSubmission: z.ZodObject<{
291
291
  timeSubmitted: z.ZodNumber;
292
292
  platform: z.ZodEnum<{
293
293
  web: "web";
294
- ios: "ios";
295
294
  android: "android";
295
+ ios: "ios";
296
296
  }>;
297
297
  }, z.core.$strip>;
298
298
  export type MicroLessonSubmission = z.infer<typeof MicroLessonSubmission>;
@@ -59,8 +59,8 @@ export type UserReduxState = z.infer<typeof UserReduxState>;
59
59
  export declare const WahaAppVersion: z.ZodString;
60
60
  export declare const DevicePlatform: z.ZodEnum<{
61
61
  web: "web";
62
- ios: "ios";
63
62
  android: "android";
63
+ ios: "ios";
64
64
  macos: "macos";
65
65
  windows: "windows";
66
66
  }>;
@@ -88,8 +88,8 @@ export declare const DeviceInfo: z.ZodObject<{
88
88
  }>>;
89
89
  platform: z.ZodEnum<{
90
90
  web: "web";
91
- ios: "ios";
92
91
  android: "android";
92
+ ios: "ios";
93
93
  macos: "macos";
94
94
  windows: "windows";
95
95
  }>;
@@ -210,8 +210,8 @@ export declare const LegacyWahaUser: z.ZodObject<{
210
210
  lastUpdate: z.ZodNumber;
211
211
  platform: z.ZodEnum<{
212
212
  web: "web";
213
- ios: "ios";
214
213
  android: "android";
214
+ ios: "ios";
215
215
  macos: "macos";
216
216
  windows: "windows";
217
217
  }>;
@@ -296,8 +296,8 @@ export declare const LegacyWahaUser: z.ZodObject<{
296
296
  timeSubmitted: z.ZodNumber;
297
297
  platform: z.ZodEnum<{
298
298
  web: "web";
299
- ios: "ios";
300
299
  android: "android";
300
+ ios: "ios";
301
301
  }>;
302
302
  }, z.core.$strip>>>;
303
303
  obedienceScore: z.ZodOptional<z.ZodNumber>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "waha-shared",
3
- "version": "1.0.481",
3
+ "version": "1.0.483",
4
4
  "author": "Waha",
5
5
  "dependencies": {
6
6
  "@types/signale": "^1.4.7",
@@ -13,6 +13,7 @@
13
13
  },
14
14
  "description": "Shared assets for Waha's various apps",
15
15
  "devDependencies": {
16
+ "@crowdin/crowdin-api-client": "^1.57.0",
16
17
  "@types/react": "^19.3.0",
17
18
  "copyfiles": "^2.4.1",
18
19
  "react": "^19.3.0",