storyloco 1.13.0 → 1.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyloco",
3
- "version": "1.13.0",
3
+ "version": "1.13.2",
4
4
  "description": "A collection of slick Storyblok field plugins built with Svelte 5, TypeScript, and Tailwind CSS",
5
5
  "keywords": [
6
6
  "storyblok",
@@ -0,0 +1,475 @@
1
+ /**
2
+ * AUTO-GENERATED FILE. DO NOT EDIT.
3
+ * Generated by vite-storyblok-schema plugin on 2025-10-01T12:20:26.416Z.
4
+ * Any changes will be overwritten.
5
+ */
6
+ import type { StoryblokCustomPlugins } from "../../vite.config.js";
7
+ import type { SbBlokData } from "@storyblok/svelte";
8
+ export type Blok<T> = SbBlokData & T;
9
+ import type { ISbStoryData } from "@storyblok/js";
10
+ export interface Action {
11
+ appearance?: "default" | "alternate";
12
+ header_size?: number | string;
13
+ space_above?: "" | "0" | "2" | "4" | "6" | "8" | "10";
14
+ space_below?: number | string;
15
+ header?: string;
16
+ body?: StoryblokRichtext;
17
+ image?: StoryblokAsset;
18
+ link?: Exclude<StoryblokMultilink, {
19
+ linktype?: "asset";
20
+ }>;
21
+ button?: string;
22
+ component?: "Action";
23
+ _uid?: string;
24
+ }
25
+ export interface Author {
26
+ photo?: StoryblokAsset;
27
+ component?: "Author";
28
+ _uid?: string;
29
+ }
30
+ export interface Button {
31
+ title?: string;
32
+ link?: StoryblokMultilink;
33
+ align?: "" | "left" | "center" | "right";
34
+ variant?: "" | "ghost" | "secondary" | "primary" | "light";
35
+ component?: "Button";
36
+ _uid?: string;
37
+ }
38
+ export interface Checkboxes {
39
+ label?: string;
40
+ options?: Option[];
41
+ required?: boolean;
42
+ component?: "Checkboxes";
43
+ _uid?: string;
44
+ }
45
+ export interface Connect {
46
+ subtitle_size?: number | string;
47
+ space_above?: number | string;
48
+ space_below?: number | string;
49
+ title?: string;
50
+ subtitle?: string;
51
+ title_size?: number | string;
52
+ component?: "Connect";
53
+ _uid?: string;
54
+ }
55
+ export interface Embed {
56
+ code?: string;
57
+ component?: "Embed";
58
+ _uid?: string;
59
+ }
60
+ export interface Entry {
61
+ location?: string;
62
+ title?: string;
63
+ start_time?: string;
64
+ end_time?: string;
65
+ component?: "Entry";
66
+ _uid?: string;
67
+ }
68
+ export interface FeaturePages {
69
+ appearance?: "compact" | "large";
70
+ space_above?: number | string;
71
+ space_below?: number | string;
72
+ title?: string;
73
+ links?: Link[];
74
+ component?: "FeaturePages";
75
+ _uid?: string;
76
+ }
77
+ export interface Form {
78
+ title?: string;
79
+ fields?: (Checkboxes | Input | Radios | Select | Textarea)[];
80
+ submit_label?: string;
81
+ confirmation_message?: string;
82
+ space_below?: number | string;
83
+ space_above?: number | string;
84
+ title_size?: number | string;
85
+ to?: string;
86
+ from?: string;
87
+ subject?: string;
88
+ component?: "Form";
89
+ _uid?: string;
90
+ }
91
+ export interface Hero {
92
+ squish?: boolean;
93
+ asset?: StoryblokAsset;
94
+ title?: StoryblokRichtext;
95
+ button?: string;
96
+ button_link?: Exclude<StoryblokMultilink, {
97
+ linktype?: "email";
98
+ } | {
99
+ linktype?: "asset";
100
+ }>;
101
+ button2?: string;
102
+ button2_link?: Exclude<StoryblokMultilink, {
103
+ linktype?: "email";
104
+ } | {
105
+ linktype?: "asset";
106
+ }>;
107
+ component?: "Hero";
108
+ _uid?: string;
109
+ }
110
+ export interface Ident {
111
+ component?: "Ident";
112
+ _uid?: string;
113
+ }
114
+ export interface Image {
115
+ aspect?: "" | "16/9" | "4/3" | "1/1" | "3/2";
116
+ space_above?: number | string;
117
+ space_below?: number | string;
118
+ asset?: StoryblokAsset;
119
+ component?: "Image";
120
+ _uid?: string;
121
+ }
122
+ export interface ImageCarousel {
123
+ images?: StoryblokMultiasset;
124
+ space_between?: number | string;
125
+ component?: "ImageCarousel";
126
+ _uid?: string;
127
+ }
128
+ export interface Input {
129
+ label?: string;
130
+ placeholder?: string;
131
+ type?: "" | "text" | "email";
132
+ required?: boolean;
133
+ component?: "Input";
134
+ _uid?: string;
135
+ }
136
+ export interface IWouldLikeToContactStyleStudio {
137
+ component?: "I would like to contact Style Studio";
138
+ _uid?: string;
139
+ }
140
+ export interface Latest {
141
+ title_size?: number | string;
142
+ space_above?: number | string;
143
+ space_below?: number | string;
144
+ title?: string;
145
+ link?: Exclude<StoryblokMultilink, {
146
+ linktype?: "email";
147
+ } | {
148
+ linktype?: "asset";
149
+ }>;
150
+ button?: string;
151
+ component?: "Latest";
152
+ _uid?: string;
153
+ }
154
+ export interface Layout {
155
+ nav?: Link[];
156
+ about_links?: Link[];
157
+ locations?: LocationItem[];
158
+ nav_image?: StoryblokAsset;
159
+ meta_title?: string;
160
+ meta_description?: string;
161
+ meta_tagline?: string;
162
+ meta_image?: StoryblokAsset;
163
+ social_x?: string;
164
+ social_facebook?: string;
165
+ social_instagram?: string;
166
+ social_youtube?: string;
167
+ component?: "Layout";
168
+ _uid?: string;
169
+ }
170
+ export interface Link {
171
+ img?: StoryblokAsset;
172
+ title?: string;
173
+ excerpt?: string;
174
+ component?: "Link";
175
+ _uid?: string;
176
+ }
177
+ export interface LocationItem {
178
+ title?: number | string;
179
+ image?: StoryblokAsset;
180
+ link?: Exclude<StoryblokMultilink, {
181
+ linktype?: "email";
182
+ } | {
183
+ linktype?: "asset";
184
+ }>;
185
+ component?: "LocationItem";
186
+ _uid?: string;
187
+ }
188
+ export interface Locations {
189
+ title_size?: number | string;
190
+ space_above?: number | string;
191
+ space_below?: number | string;
192
+ title?: string;
193
+ blocks?: LocationItem[];
194
+ component?: "Locations";
195
+ _uid?: string;
196
+ }
197
+ export interface Map {
198
+ zoom?: string;
199
+ space_below?: number | string;
200
+ markers?: Marker[];
201
+ space_above?: number | string;
202
+ component?: "Map";
203
+ _uid?: string;
204
+ }
205
+ export interface Marker {
206
+ name?: string;
207
+ latitude?: string;
208
+ longitude?: string;
209
+ component?: "Marker";
210
+ _uid?: string;
211
+ }
212
+ export interface Modal {
213
+ id?: string;
214
+ title?: string;
215
+ body?: StoryblokRichtext;
216
+ component?: "Modal";
217
+ _uid?: string;
218
+ }
219
+ export interface Option {
220
+ name?: string;
221
+ disabled?: boolean;
222
+ selected?: boolean;
223
+ component?: "Option";
224
+ _uid?: string;
225
+ }
226
+ export interface Page {
227
+ image?: StoryblokAsset;
228
+ subtitle?: string;
229
+ blocks?: (Action | Author | Button | Checkboxes | Connect | Embed | Entry | FeaturePages | Form | Hero | Ident | Image | ImageCarousel | Input | IWouldLikeToContactStyleStudio | Latest | Layout | Link | LocationItem | Locations | Map | Marker | Modal | Option | Page | Post | Project | Projects | Quote | Radios | RichText | Select | Sidekick | Spacer | Sublink | Textarea | Vacancy | VacancyList | Video | Whatson)[];
230
+ meta_title?: string;
231
+ meta_description?: string;
232
+ meta_tagline?: string;
233
+ meta_image?: StoryblokAsset;
234
+ modals?: Modal[];
235
+ location?: {
236
+ [k: string]: unknown;
237
+ }[];
238
+ category?: {
239
+ [k: string]: unknown;
240
+ }[];
241
+ component?: "Page";
242
+ _uid?: string;
243
+ }
244
+ export interface Post {
245
+ author?: ISbStoryData<Author> | string;
246
+ meta_title?: string;
247
+ meta_description?: string;
248
+ meta_tagline?: string;
249
+ meta_image?: StoryblokAsset;
250
+ blocks?: (Action | Author | Button | Checkboxes | Connect | Embed | Entry | FeaturePages | Form | Hero | Ident | Image | ImageCarousel | Input | IWouldLikeToContactStyleStudio | Latest | Layout | Link | LocationItem | Locations | Map | Marker | Modal | Option | Page | Post | Project | Projects | Quote | Radios | RichText | Select | Sidekick | Spacer | Sublink | Textarea | Vacancy | VacancyList | Video | Whatson)[];
251
+ location?: (number | string)[];
252
+ image?: StoryblokAsset;
253
+ body?: StoryblokRichtext;
254
+ component?: "Post";
255
+ _uid?: string;
256
+ }
257
+ export interface Project {
258
+ location?: (number | string)[];
259
+ category?: (number | string)[];
260
+ blocks?: (Action | Author | Button | Checkboxes | Connect | Embed | Entry | FeaturePages | Form | Hero | Ident | Image | ImageCarousel | Input | IWouldLikeToContactStyleStudio | Latest | Layout | Link | LocationItem | Locations | Map | Marker | Modal | Option | Page | Post | Project | Projects | Quote | Radios | RichText | Select | Sidekick | Spacer | Sublink | Textarea | Vacancy | VacancyList | Video | Whatson)[];
261
+ image?: StoryblokAsset;
262
+ subtitle?: string;
263
+ meta_title?: string;
264
+ meta_tagline?: string;
265
+ meta_description?: string;
266
+ meta_image?: StoryblokAsset;
267
+ component?: "Project";
268
+ _uid?: string;
269
+ }
270
+ export interface Projects {
271
+ title?: string;
272
+ limit?: string;
273
+ categories?: (number | string)[];
274
+ more?: boolean;
275
+ component?: "Projects";
276
+ _uid?: string;
277
+ }
278
+ export interface Quote {
279
+ space_above?: number | string;
280
+ space_below?: number | string;
281
+ component?: "Quote";
282
+ _uid?: string;
283
+ }
284
+ export interface Radios {
285
+ label?: string;
286
+ options?: Option[];
287
+ required?: boolean;
288
+ component?: "Radios";
289
+ _uid?: string;
290
+ }
291
+ export interface RichText {
292
+ alignment?: "" | "left" | "center" | "right";
293
+ space_above?: number | string;
294
+ space_below?: number | string;
295
+ body?: StoryblokRichtext;
296
+ component?: "RichText";
297
+ _uid?: string;
298
+ }
299
+ export interface Select {
300
+ label?: string;
301
+ placeholder?: string;
302
+ options?: Option[];
303
+ required?: boolean;
304
+ component?: "Select";
305
+ _uid?: string;
306
+ }
307
+ export interface Sidekick {
308
+ flip?: boolean;
309
+ space_above?: number | string;
310
+ space_below?: number | string;
311
+ title_size?: number | string;
312
+ subtitle_size?: number | string;
313
+ background?: number | string;
314
+ title?: string;
315
+ subtitle?: string;
316
+ body?: StoryblokRichtext;
317
+ button?: string;
318
+ button_link?: Exclude<StoryblokMultilink, {
319
+ linktype?: "email";
320
+ } | {
321
+ linktype?: "asset";
322
+ }>;
323
+ image?: StoryblokAsset;
324
+ stamp?: StoryblokAsset;
325
+ component?: "Sidekick";
326
+ _uid?: string;
327
+ }
328
+ export interface Spacer {
329
+ size?: StoryblokCustomPlugins["storyblok-slider"];
330
+ component?: "Spacer";
331
+ _uid?: string;
332
+ }
333
+ export interface Sublink {
334
+ link?: Exclude<StoryblokMultilink, {
335
+ linktype?: "email";
336
+ } | {
337
+ linktype?: "asset";
338
+ }>;
339
+ title?: string;
340
+ component?: "Sublink";
341
+ _uid?: string;
342
+ }
343
+ export interface Textarea {
344
+ label?: string;
345
+ placeholder?: string;
346
+ required?: boolean;
347
+ component?: "Textarea";
348
+ _uid?: string;
349
+ }
350
+ export interface Vacancy {
351
+ location?: (number | string)[];
352
+ blocks?: (Action | Author | Button | Checkboxes | Connect | Embed | Entry | FeaturePages | Form | Hero | Ident | Image | ImageCarousel | Input | IWouldLikeToContactStyleStudio | Latest | Layout | Link | LocationItem | Locations | Map | Marker | Modal | Option | Page | Post | Project | Projects | Quote | Radios | RichText | Select | Sidekick | Spacer | Sublink | Textarea | Vacancy | VacancyList | Video | Whatson)[];
353
+ type?: "" | "Full Time" | "Part Time" | "Volunteer";
354
+ closing_date?: string;
355
+ term?: "" | "Permanent" | "Temporary" | "Contract" | "Volunteer";
356
+ salary?: string;
357
+ department?: string;
358
+ apply_email_address?: string;
359
+ specification?: StoryblokRichtext;
360
+ component?: "Vacancy";
361
+ _uid?: string;
362
+ }
363
+ export interface VacancyList {
364
+ type?: ("" | "all" | "paid" | "voluntary")[];
365
+ component?: "VacancyList";
366
+ _uid?: string;
367
+ }
368
+ export interface Video {
369
+ space_above?: number | string;
370
+ space_below?: number | string;
371
+ title_size?: number | string;
372
+ video?: StoryblokAsset;
373
+ embed?: string;
374
+ component?: "Video";
375
+ _uid?: string;
376
+ }
377
+ export interface Whatson {
378
+ location?: number | string;
379
+ monday?: Entry[];
380
+ tuesday?: Entry[];
381
+ wednesday?: Entry[];
382
+ thursday?: Entry[];
383
+ friday?: Entry[];
384
+ component?: "Whatson";
385
+ _uid?: string;
386
+ }
387
+ type StoryblokPropertyType = "asset" | "multiasset" | "multilink" | "table" | "richtext";
388
+ interface StoryblokAsset {
389
+ alt: string | null;
390
+ copyright: string | null;
391
+ fieldtype: "asset";
392
+ id: number;
393
+ filename: string | null;
394
+ name: string;
395
+ title: string | null;
396
+ focus: string | null;
397
+ meta_data: Record<string, unknown>;
398
+ source: string | null;
399
+ is_external_url: boolean;
400
+ is_private: boolean;
401
+ src: string;
402
+ updated_at: string;
403
+ width: number | null;
404
+ height: number | null;
405
+ aspect_ratio: number | null;
406
+ public_id: string | null;
407
+ content_type: string;
408
+ }
409
+ interface StoryblokMultiasset extends Array<StoryblokAsset> {
410
+ }
411
+ interface StoryblokMultilink {
412
+ fieldtype: "multilink";
413
+ id: string;
414
+ url: string;
415
+ cached_url: string;
416
+ target?: "_blank" | "_self";
417
+ anchor?: string;
418
+ rel?: string;
419
+ title?: string;
420
+ prep?: string;
421
+ linktype: "story" | "url" | "email" | "asset";
422
+ story?: {
423
+ name: string;
424
+ created_at: string;
425
+ published_at: string;
426
+ id: number;
427
+ uuid: string;
428
+ content: Record<string, unknown>;
429
+ slug: string;
430
+ full_slug: string;
431
+ sort_by_date?: string;
432
+ position?: number;
433
+ tag_list?: string[];
434
+ is_startpage?: boolean;
435
+ parent_id?: number | null;
436
+ meta_data?: Record<string, unknown> | null;
437
+ group_id?: string;
438
+ first_published_at?: string;
439
+ release_id?: number | null;
440
+ lang?: string;
441
+ path?: string | null;
442
+ alternates?: unknown[];
443
+ default_full_slug?: string | null;
444
+ translated_slugs?: unknown[] | null;
445
+ };
446
+ email?: string;
447
+ }
448
+ interface StoryblokTable {
449
+ fieldtype: "table";
450
+ thead: Array<{
451
+ _uid: string;
452
+ value: string;
453
+ component: "_table_head";
454
+ _editable?: string;
455
+ }>;
456
+ tbody: Array<{
457
+ _uid: string;
458
+ component: "_table_row";
459
+ _editable?: string;
460
+ body: Array<{
461
+ _uid: string;
462
+ value: string;
463
+ component: "_table_col";
464
+ _editable?: string;
465
+ }>;
466
+ }>;
467
+ }
468
+ interface StoryblokRichtext {
469
+ type: string;
470
+ content?: StoryblokRichtext[];
471
+ marks?: StoryblokRichtext[];
472
+ attrs?: Record<string, unknown>;
473
+ text?: string;
474
+ }
475
+ export type { StoryblokAsset, StoryblokMultiasset, StoryblokMultilink, StoryblokPropertyType, StoryblokRichtext, StoryblokTable, };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * AUTO-GENERATED FILE. DO NOT EDIT.
3
+ * Generated by vite-storyblok-schema plugin on 2025-10-01T12:20:26.416Z.
4
+ * Any changes will be overwritten.
5
+ */
6
+ export {};
@@ -1,5 +1,21 @@
1
- import { redirects_map } from "../../../.svelte-kit/storyblok/redirects.build.js";
2
1
  import { match, compile } from "path-to-regexp";
2
+ // Lazy load the redirects map to avoid import errors during dev
3
+ let redirects_map = null;
4
+ async function load_redirects_map() {
5
+ if (redirects_map)
6
+ return redirects_map;
7
+ try {
8
+ const { redirects_map: map } = await import("../../../.svelte-kit/storyblok/redirects.build.js");
9
+ redirects_map = map;
10
+ return map;
11
+ }
12
+ catch (error) {
13
+ // During dev or if file doesn't exist yet, return empty map
14
+ console.warn("Redirects file not found, using empty redirects map");
15
+ redirects_map = new Map();
16
+ return redirects_map;
17
+ }
18
+ }
3
19
  /** Normalise to a leading-slash path. Empty → '/' */
4
20
  function normalize_path(pathname) {
5
21
  if (!pathname)
@@ -22,22 +38,31 @@ function to_dst_pattern(glob) {
22
38
  return normalize_path(glob).replaceAll("*", () => `:w${i++}`);
23
39
  }
24
40
  /** Precompiled wildcard rules generated from the redirects map. */
25
- const wildcard_rules = [];
41
+ let wildcard_rules = [];
26
42
  /** Exact redirect lookups, with and without leading slash variants. */
27
- const exact_map = new Map();
28
- for (const [key, value] of redirects_map.entries()) {
29
- if (key.includes("*")) {
30
- const src_pattern = to_src_pattern(key);
31
- const dst_pattern = to_dst_pattern(value);
32
- wildcard_rules.push({
33
- matcher: match(src_pattern, { decode: decodeURIComponent, end: true }),
34
- builder: compile(dst_pattern, { encode: (x) => x }),
35
- });
36
- }
37
- else {
38
- exact_map.set(normalize_path(key), value);
39
- exact_map.set(key.startsWith("/") ? key.slice(1) : key, value);
43
+ let exact_map = new Map();
44
+ let rules_initialized = false;
45
+ async function initialize_rules() {
46
+ if (rules_initialized)
47
+ return;
48
+ const map = await load_redirects_map();
49
+ wildcard_rules = [];
50
+ exact_map = new Map();
51
+ for (const [key, value] of map.entries()) {
52
+ if (key.includes("*")) {
53
+ const src_pattern = to_src_pattern(key);
54
+ const dst_pattern = to_dst_pattern(value);
55
+ wildcard_rules.push({
56
+ matcher: match(src_pattern, { decode: decodeURIComponent, end: true }),
57
+ builder: compile(dst_pattern, { encode: (x) => x }),
58
+ });
59
+ }
60
+ else {
61
+ exact_map.set(normalize_path(key), value);
62
+ exact_map.set(key.startsWith("/") ? key.slice(1) : key, value);
63
+ }
40
64
  }
65
+ rules_initialized = true;
41
66
  }
42
67
  /**
43
68
  * Resolve a redirect for the given pathname.
@@ -45,7 +70,8 @@ for (const [key, value] of redirects_map.entries()) {
45
70
  * Returns a normalised absolute path ('/…') if a redirect applies, else null.
46
71
  * Self-redirects are ignored.
47
72
  */
48
- function resolve_redirect(url) {
73
+ async function resolve_redirect(url) {
74
+ await initialize_rules();
49
75
  try {
50
76
  const source = normalize_path(url.pathname + (url.search || ""));
51
77
  const source_plain = source.includes("?") ? source.split("?")[0] : source;
@@ -77,7 +103,7 @@ function resolve_redirect(url) {
77
103
  }
78
104
  }
79
105
  export const handle_redirects = async ({ event, resolve }) => {
80
- const target = resolve_redirect(event.url);
106
+ const target = await resolve_redirect(event.url);
81
107
  if (!target)
82
108
  return await resolve(event);
83
109
  return new Response(null, { status: 308, headers: { Location: target } });
@@ -3,7 +3,7 @@ import { writeFile, readFile, access, constants } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { Logger } from "../shared/logger.js";
5
5
  import * as prettier from "prettier";
6
- const name = "vite-redirects";
6
+ const name = "vite-storyblok-redirects";
7
7
  const logger = new Logger(name);
8
8
  async function get_redirects(datasource = "redirects", public_storyblok_access_token) {
9
9
  const env_token = loadEnv("", "", "").PUBLIC_STORYBLOK_ACCESS_TOKEN;
@@ -125,15 +125,29 @@ export default function schema({ output_path, interval_ms, storyblok_personal_ac
125
125
  await writeFile(components_file, JSON.stringify(new_components, null, 2));
126
126
  const here = (str) => process.env.SCHEMA_DEV
127
127
  ? path.join("src/lib/schema", str)
128
- : path.join("node_modules/storyloco/packages/vite/dist", str);
128
+ : path.join("node_modules/storyloco/packages/vite/dist/schema", str);
129
129
  // Generate types
130
130
  logger.start("Generating TypeScript definitions...");
131
131
  await $ `chmod u+w ${schema_ts_file}`.quiet().nothrow();
132
- await $ `${x} storyblok@4.6.6 ts generate -s ${storyblok_space_id} -p .svelte-kit/storyblok --compiler-options ${here("compiler_options.js")} --custom-fields-parser ${here("custom_fields_parser.js")} --strict`.quiet();
132
+ await $ `${x} storyblok@4.6.6 ts generate -s ${storyblok_space_id} -p .svelte-kit/storyblok --compiler-options ${here("compiler_options.js")} --custom-fields-parser ${here("custom_fields_parser.js")} --strict`;
133
133
  logger.succeed("TypeScript definitions generated.");
134
134
  // Get generated files
135
- const component_types = await readFile(`.svelte-kit/storyblok/types/${storyblok_space_id}/storyblok-components.d.ts`, "utf-8");
136
- const general = await readFile(".svelte-kit/storyblok/types/storyblok.d.ts", "utf-8");
135
+ let component_types;
136
+ let general;
137
+ try {
138
+ component_types = await readFile(`.svelte-kit/storyblok/types/${storyblok_space_id}/storyblok-components.d.ts`, "utf-8");
139
+ }
140
+ catch (error) {
141
+ logger.warn("Component types file not found, skipping type generation");
142
+ return;
143
+ }
144
+ try {
145
+ general = await readFile(".svelte-kit/storyblok/types/storyblok.d.ts", "utf-8");
146
+ }
147
+ catch (error) {
148
+ logger.warn("General types file not found, skipping type generation");
149
+ return;
150
+ }
137
151
  const general_replaced = general.replace(`// This file was generated by the Storyblok CLI.
138
152
  // DO NOT MODIFY THIS FILE BY HAND.
139
153
  import type { ISbStoryData } from '@storyblok/js';`, "");
@@ -7,4 +7,5 @@ export declare class Logger {
7
7
  start(message: string): void;
8
8
  succeed(message: string): void;
9
9
  fail(message: string): void;
10
+ warn(message: string): void;
10
11
  }
@@ -20,4 +20,7 @@ export class Logger {
20
20
  fail(message) {
21
21
  this.spinner.fail(message);
22
22
  }
23
+ warn(message) {
24
+ this.spinner.warn(message);
25
+ }
23
26
  }